介绍

网上存在太多介绍 Git 的文章,也有很多的书籍,如 ProGit - Book,它们都没有从一个设计层面介绍 Git 的原理。

提交

Git 将提交信息与提交内容分离处理。

  • 提交信息:作者、时间、父提交等等。
  • 提交内容:文件修改。

这一点是学习使用 Git 的误区,很多人没有理解这一点,导致遇到问题不知道如何解决,不知道如何设计工作流。

只看提交信息中提交与其父提交的话,可以将提交看作图的结点,每一个提交都有零个到多个父提交的连接,从而组合成了一个有向无环图。

Git 很多操作都是在处理这张图,例如:mergecherry-pickrebase 等等。

  • merge:创建一个新结点连接两个或更多结点。
  • cherry-pick:将一个结点的父结点修改为其他结点。
  • rebase:将一连串结点中第一个结点的父结点修改为其他结点。

快照

提交内容在 Git 中就是快照,是所有文件的当前状态。

由于提交大部分都会修改文件内容,因此需要减少自动生成的提交,例如 merge 操作。如果本地与远程都有提交,那么执行 pull 时会相当于 fetch + merge,然后在本地就会自动生成一个合并提交。如果存在冲突就需要在此次合并提交中解决。

具体详细信息可以参考:

merge 操作本身有多种算法,可以根据实际需要选择使用。这其实带来一个问题,合并时无法保证所有的改动就是对的,因此合并时需要仔细检查所有改动,有可能算法会计算错误,导致合并出了错误的结果。

因此最好的办法就是减少类似 merge 操作引入的自动提交。

实例

当理解以上概念后,可以用来解决实际问题。

一个功能分支合并到主干时未进行正确的操作,导致丢失了一部分内容。发现错误后重新合并 Git 会直接提示已合并过。

假设其中功能分支为 feature,主分支为 master,合并提交为 M,那么主分支上合并提交前的提交是 M^1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 检出到主分支中合并前的提交
git checkout -b new-feature M^1
# 重新合并功能分支,修复之间的合并错误
git merge feature
# 切换回现在的主分支
git checkout master
# 合并新功能分支上正确的结果
git merge new-feature
# 删除新功能分支
git branch -d new-feature

原理非常简单:通过重新生成一个包含正确快照的提交,然后合并此提交到主分支中的图上。