Git

Git是版本控制系统(VCSs)的标准。

以下为自底向上介绍Git,从数据模型开始,再到接口,并理解接口是如何操作数据模型的。

 快照(Snapshots)

 Git通过一系列快照来管理 顶级目录中的文件和文件夹 的历史记录。

文件被称为Blob(数据对象,也就是一组数据)。

目录被称为tree,将名字和Blob对象或树对象进行映射(使得目录中可以包含其他目录)。

快照是被追踪的最顶层的树。

树例:

<root> (tree)
|
+- foo (tree)
|  |
|  + bar.txt (blob, contents = "hello world")
|
+- baz.txt (blob, contents = "git is wonderful")

这个顶层的树包含了两个元素:一个名为foo的树(它本身包含了一个blob对象bar.txt),以及一个blob对象baz.txt。

关联快照

在Git中,历史记录是一个由快照组成的有向无环图(代表Git中的每个快照都有一系列“父辈(之前的一系列快照)”)。

某个快照可能由多个父辈而来。例如,经过合并后的两条分支。

在Git中,这些快照被称为“提交(commit)”。可视化表示这些历史提交记录可能是这样的:

o <-- o <-- o <-- o
            ^  
             \
              --- o <-- o

其中的o表示一次提交(快照)。箭头指向了当前提交的父辈。

上面的分岔在开发完成后可能会合并并创建一个新的提交,会同时包含这些特性。

例如(粗体为最新的合并):

o <-- o <-- o <-- o <---- o
            ^            /
             \          v
              --- o <-- o

Git中的提交不可改变,但不代表错误不能被修改(会提交为一个新的提交记录)。而引用(reference)则被更新为指向这些新的提交。

数据模型

(伪代码表示,一种简洁的历史模型)

// 文件就是一组数据
type blob = array<byte>

// 一个包含文件和目录的目录
type tree = map<string, tree | blob>

// 每个提交都包含一个父辈,元数据和顶层树
type commit = struct {
    parent: array<commit>
    author: string
    message: string
    snapshot: tree
}

对象和内存寻址

Git中的对象可以是blob、树或提交。当它们引用其他对象时,它们并没有真正的在硬盘上保存这些对象,而是仅仅保存了它们的哈希值作为引用。

例如,上面例子中的树可以通过 git cat-file -p 698281bc680d1995c5f4caaf3359721a5a58d48d 来进行可视化。

100644 blob 4448adbf7ecd394f42ae135bbeed9676e894af85    baz.txt
040000 tree c68d233a33c5c06e0340e4c224f0afca87c8ce87    foo

树本身也会包含一些指向其他内容的指针例如 baz.txt (blob) 和 foo (树)。如果我们用 git cat-file -p 4448adbf7ecd394f42ae135bbeed9676e894af85,即通过哈希值查看 baz.txt 的内容,会得到以下信息:

git is wonderful

引用

快照可以用哈希值来标记,但难以记忆。针对这一问题,Git的解决方法是给这些哈希值赋予人类可读的名字,也就是引用(references)。引用是指向提交的指针,可以被更新(指向新的提交)。eg. master引用通常会指向主分支的最新一次提交。

references = map<string, string>

def update_reference(name, id):
    references[name] = id

def read_reference(name):
    return references[name]

def load_reference(name_or_id):
    if name_or_id in references:
        return load(references[name_or_id])
    else:
        return load(name_or_id)

Git可以使用诸如master这样可读的名称来表示历史记录中某个特定的提交。

标记当前位置(这样可以在创建新的快照的时候知道它的相对位置,设置它的父辈)有一个特殊的索引HEAD。

仓库(repositories)

给出Git仓库的定义:对象(objects)和引用(references)。

在硬盘上,Git仅存储对象和引用:因为其数据模型仅包含这些东西。所有的git命令都对应着对提交树的操作,例如增加对象、增加或删除引用。

暂存区

允许指定下次快照中要包括哪些改动。

Git的命令行接口

基础

git help <command> 获取git命令的帮助信息

git init 创建一个新的git仓库,其数据会存放在一个名为.git的目录下

git status 显示当前的仓库状态

git add <filename> 添加文件到暂存区

git commit 创建一个新的提交

git log 显示历史日志

git log --all --graph --decorate 可视化历史记录(有向无环图)

git diff <filename> 显示与暂存区文件的差异

git diff <revision> <filename> 显示某个文件两个版本之间的差异

git checkout <revision> 更新HEAD和目前的分支

分支和合并

git branch 显示分支

git branch <name> 创建分支

git checkout -b <name> 创建分支并切换到该分支(相当于git branch <name>;git checkout <name>)

git merge <revision> 合并到当前分支

git mergetool 使用工具来处理合并冲突

git rebase 将一系列补丁变基(rebase)为新的基线

远端操作

git remote 列出远端

git remote add <name> <url> 添加一个远端

git push <remote> <local branch>:<remote branch> 将对象传送至远端并更新远端引用

git branch --set-upstream-to=<remote>/<remote branch> 创建本地与远端分支的关联关系

git fetch 从远端获取对象/索引

git pull 相当于git fetch;git merge

git clone 从远端下载仓库

撤销

git commit --amend 编辑提交的内容或信息

git reset HEAD <file> 恢复暂存的文件

git checkout -- <file> 丢弃修改

git restore git2.32版本后取代git reset进行许多撤销操作

Git 高级操作

git config: Git 是一个 高度可定制的工具

git clone --depth=1: 浅克隆(shallow clone),不包括完整的版本历史信息

git add -p: 交互式暂存

git rebase -i: 交互式变基

git blame: 查看最后修改某行的人

git stash: 暂时移除工作目录下的修改内容

git bisect: 通过二分查找搜索历史记录

.gitignore: 指定故意不追踪的文件

资源推荐

pro git 中文版

posted @ 2023-11-23 21:22  yhish  阅读(51)  评论(0)    收藏  举报