简而言之: 在基于master
重命名feature
之前,请使用git pull
和git pull --rebase
更新master
和feature
。在您将feature
分支重命名到master
之后,无需再进行git pull
操作。
根据您当前的工作流程,git status
告诉您以下原因:
您的分支和 'origin/feature' 分别有27个和2个不同的提交记录。
这是由于重命名后的feature
分支包含了 25个新的提交记录,这些提交记录无法通过origin/feature
访问(因为它们来自于master
的重新命名),另外还有2个提交记录可以通过origin/feature
访问,但由于与origin/feature
上的提交记录不同,则具有不同的SHA-1哈希值。这些提交记录包含相同的更改(即它们是“修补等效”的),但是因为它们基于本地仓库中不同的提交而具有不同的SHA-1哈希值。
下面是一个示例。假设这是您在对master
运行git pull
操作之前的历史记录:
A - B - C (master)
\
D - E (feature)
在执行了
git pull
之后,
master
分支得到了提交记录
F
:
A - B - C - F (master, origin/master)
\
D - E (feature)
这时,您将feature
在master
的基础上进行变基,这会应用D
和E
:
A - B - C - F (master, origin/master)
\
D' - E' (feature)
与此同时,远程分支
origin/feature
仍然基于提交记录
C
:
A - B - C - F (master, origin/master)
\ \
\ D' - E' (feature)
\
D - E (origin/feature)
如果你在
feature
上执行
git status
,Git 会告诉你
feature
分支相对于
origin/feature
分支有
3个(
F
,
D'
,
E'
)和
2个(
D
,
E
)提交版本存在差异。
请注意,D'
和E'
包含与D
和E
相同的更改,但具有不同的提交ID,因为它们已经被重新基于F
。
解决方案是在将
feature
分支重新基于
master
之前,在
master
和
feature
分支上都执行
git pull
。然而,由于你可能在
feature
上有尚未推送到
origin
的提交版本,因此你需要执行以下操作:
git checkout feature && git pull --rebase
为了避免在 `origin/feature` 和本地 `feature` 之间创建一个合并提交(`merge commit`),请使用变基操作(`rebasing`)。
关于变基操作的影响更新:
根据
这条评论,我扩展了分叉分支的原因。在变基操作后,为什么 `git status` 报告 `feature` 和 `origin/feature` 分叉是因为变基操作会将新的提交插入到 `feature` 中并且重写之前推送到 `origin/feature` 的提交。
考虑在拉取(`pull`)之后但在变基操作之前的情况。
A - B - C - F (master)
\
D - E (feature, origin/feature)
在这一点上,feature
和 origin/feature
指向相同的提交记录 E
——换句话说,它们是“同步”的。在将 feature
重新基于 master
后,历史记录将如下所示:
A - B - C - F (master)
\ \
\ D' - E' (feature)
\
D - E (origin/feature)
正如您所看到的,
feature
和
origin/feature
已经
分叉,它们的共同祖先是提交
C
。这是因为
feature
现在包含了从
master
获取的新提交
F
以及
D'
和
E'
(读作“
D prime”和“
E prime”),它们是基于
F
的提交
D
和
E
。尽管它们包含相同的更改,但Git认为它们不同,因为它们具有不同的提交ID。与此同时,
origin/feature
仍然引用
D
和
E
。
在这一点上,您已经
重写历史记录:通过重新设置它们来修改现有的提交,从而有效地创建了“新”的提交。
现在,如果您在
feature
上运行
git pull
,将会发生以下情况:
A - B - C - F (master)
\ \
\ D' - E'- M (feature)
\ /
D - E - (origin/feature)
git pull
命令会执行git fetch
和git merge
操作,因此会创建合并提交M
,它的父提交是E'
和E
。
相反,如果您运行git pull --rebase
(也就是git fetch
+ git rebase
),则Git会执行以下操作:
- 将
feature
移动到提交C
(即feature
和origin/feature
的公共祖先)
- 应用
origin/feature
中的D
和E
- 应用
F
、D'
和E'
但是,由于D'
和E'
包含与D
和E
相同的更改,因此Git会直接丢弃它们,从而导致历史记录如下所示:
A - B - C - F (master)
\
D - E - F' (feature)
^
(origin/feature)
注意观察提交记录中的提交
F
,它先前是从分支
feature
可到达的,现在它被应用在
origin/feature
分支之上,产生了一个新的提交记录
F'
。此时,
git status
命令会提示您以下信息:
您的分支领先于 'origin/feature' 分支 1 个提交。
当然,这个提交就是
F'
。