Scu_laji

git pull --rebase真正做了什么

git pull —rebase 真正做了什么

前情提要:

​ 为了明白这篇文章你需要首先明白git reflog是什么,git rebase做了什么。我强烈建议你先去阅读完整的git rebase command 指南。

​ 首先,让我们假定我们有一个远端分支叫做origin/master,我们的本地分支叫做 master.

​ 有些时候我们有一些zz的合作伙伴在master分支上做了rebase,这时候就有可能产生大问题。这通常能在我们fetch远端分支导致复杂的冲突问题。

​ 通常的git pull,不严谨的说。它就相当于

1
2
3
# assume current checked out branch is "master"
git fetch origin
git merge origin/master

​ 而根据上一条, 你可能会认为, git pull --rebase 相当于

1
2
git fetch origin
git rebase origin/master

但是事实上并不是,上述代码对origin/master 被重新rebase并加入了squashing的commit无效。下面我将一步一步复现这个过程。

首先,创建一个仓库

1
2
3
4
5
6
7
mkdir test
git init
touch test.txt
echo "a" > test.txt
git add test.txt
git commit -m "a"
# 重复5次 abcde

因此, 我应该能获得一个类似这样的commit 记录。

这是我的master分支。 然后我将这些代码推送到了一个github仓库中。

于是我的zz的合作伙伴(由你扮演)上线了,首先,他觉得abcde这5次提交太zz了。假定你决定将bc两次提交合并成一次。de两次提交合并成一次。并提交了一次f。

你可以clone一个新仓库下来做这个实验

1
2
3
git rebase -i HEAD~4
# 然后将c和e的pick改成squash
保存,退出

于是提交记录变成了这样

并且bd的提交记录的commitid也有了变化。

你非常开心的将你的提交推送了上去。

1
git push -f

与此同时,我本地并不知道你远端干了什么事,于是我本地继续开发,提交了新记录p与q。

那么我得到了新的commit tree类似这样

1
a -- b -- c -- d -- e -- p -- q

此时我想将我的提交提交到远程分支。

此时无论是git pull还是git fetch; git rebase origin/master都不会成功。

但是git pull --rebase可以,在这个例子中,它相当于

1
2
git fetch origin
git rebase --onto origin/master e master

这个命令会给予我们如下的提交结果。

1
a -- b(b+c) -- d(d+e) -- f -- p -- q

你也许现在还是糊涂的,但是事实就是如此,git pull --rebase做了一件天才的事,将b+c与d+e这两次squashing可能引起的冲突给搞定了。

所以它到底是怎么做到的


how

这个command尝试去找出哪个commit是真正你本地的提交,哪个commit是从远端fetch下来的。

为了做到这一点,它查看远端分支(origin/master)的reflog。这里的reflog按照最近优先的顺序拥有连续的git fetch操作信息。

对于每一个reflog信息(举个🌰,origin/[email protected]{1}, 接下来…{2}如此循环),git pull --rebase检查这个commit是不是当前branch的祖先,当找到第一个节点是。它选择这个节点作为rebase的起始点。