[英]Unexpected behaviour when git cherry-pick and merge between branches
I'm surprised that changes done after cherry-picking in git become obsolete when merging.我很惊讶在 git 中挑选后所做的更改在合并时变得过时。 Here is a full example.
这是一个完整的例子。
The following is business as usual.以下是照常营业。
Mac:git user1$ mkdir myrepo; cd myrepo; git init
Initialized empty Git repository in /Users/user1/tmp/git/myrepo/.git/
Mac:myrepo user1$ echo "rotums kanoner och krut" > rotum.txt
Mac:myrepo user1$ git add rotum.txt
Mac:myrepo user1$ git commit -m "Added file"
[master (root-commit) 1044abb] Added file
1 file changed, 1 insertion(+)
create mode 100644 rotum.txt
Mac:myrepo user1$ git checkout -b mybranch
Switched to a new branch 'mybranch'
Mac:myrepo user1$ echo "mutors kanoner och krut" >> rotum.txt
Mac:myrepo user1$ git commit -am "Added mutor"
[mybranch 19afeba] Added mutor
1 file changed, 1 insertion(+)
Mac:myrepo user1$ git checkout master
Switched to branch 'master'
Mac:myrepo user1$ git cherry-pick 19af
[master cce2ca5] Added mutor
Date: Wed May 19 16:12:04 2021 +0200
1 file changed, 1 insertion(+)
Mac:myrepo user1$ cat rotum.txt
rotums kanoner och krut
mutors kanoner och krut
Now is when the unexpected behaviour occurs.现在是发生意外行为的时候。
Mac:myrepo user1$ echo "rotums kanoner och krut" > rotum.txt
Mac:myrepo user1$ cat rotum.txt
rotums kanoner och krut
Mac:myrepo user1$ git commit -am "Removed mutor"
[master f63dc50] Removed mutor
1 file changed, 1 deletion(-)
Mac:myrepo user1$ git merge mybranch
Merge made by the 'recursive' strategy.
rotum.txt | 1 +
1 file changed, 1 insertion(+)
Mac:myrepo user1$ cat rotum.txt
rotums kanoner och krut
mutors kanoner och krut
Is this expected behaviour or a bug?这是预期的行为还是错误?
The key here is that git does not record the fact that a commit was cherry-picked from one branch to another;这里的关键是 git 没有记录提交是从一个分支到另一个分支的事实; it just creates a new commit based on the one you specify (the same applies to "git rebase").
它只是根据您指定的提交创建一个新提交(同样适用于“git rebase”)。
As far as git is concerned, you have these commits:就 git 而言,您有以下提交:
Note that I haven't described these commits as "on" one branch or the other, because strictly that has no meaning in git;请注意,我没有将这些提交描述为“在”一个分支或另一个分支上,因为严格来说,这在 git 中没有任何意义; a branch points at a commit, and other commits are reachable via "parent" pointers.
分支指向提交,其他提交可通过“父”指针访问。
At the point when you merge the two branches:在合并两个分支时:
When you merge, git determines a "merge base" based on the most recent commit reachable from both branches;合并时,git 根据两个分支可访问的最新提交确定“合并基础”; in this case, that is 1044abb.
在这种情况下,即 1044abb。 It then looks at the differences between that commit and the two branches being merged:
然后它会查看该提交与被合并的两个分支之间的差异:
It then combines these two changes, and applies them to produce the new version of the file.然后它结合这两个更改,并应用它们来生成文件的新版本。 Since one side of the merge wants to add the line, and the other wants to do nothing, the resolution is to add the line.
由于合并的一侧要添加行,而另一侧什么都不做,因此解决方法是添加行。
The result is:结果是:
Or to put it more succinctly: you've added the line, removed it, and then added it again.或者更简洁地说:您已经添加了该行,将其删除,然后再次添加它。
With a diagram:带图:
Initial commit: create file
| Add a line in file (cherry-pick b)
| | Remove line, return file to its state in a.
v v v
a----c----d <- master
\
b <- mybranch
^
Add a line in file
When you merge mybranch
into master
:当您将
mybranch
合并到master
时:
a.
in the diagram), a.
),master
(commit d
) and sees that, when compared to a.
master
(commit d
) 并看到,与a.
, the file is left unchanged mybranch
(commit b
) and sees that, when compared to a.
mybranch
(提交b
)并看到,与a.
, a line should be added so the merge succeeds without conflicts, and "brings in" the changes from mybranch
.因此合并成功而没有冲突,并“引入”来自
mybranch
的更改。
You reach this situation because:您遇到这种情况是因为:
git merge
does not inspect the intermediate commits in the history of master
and mybranch
, git merge
不检查master
和mybranch
历史中的中间提交,git cherry-pick
creates new, unrelated commits, and nothing is kept in the commit graph to indicate "these changes were already included", git cherry-pick
创建新的、不相关的提交,并且提交图中没有保留任何内容以指示“这些更改已包含在内”, To give further perspective:提供进一步的视角:
git rebase
:git rebase
:git checkout mybranch
git rebase master
unlike git merge
, git rebase
does compare the list of commits, and if a rebased commit introduces the exact same changes as another commit in the target branch, that commit is dropped.与
git merge
不同, git rebase
确实比较了提交列表,并且如果重新定位的提交引入了与目标分支中的另一个提交完全相同的更改,则该提交将被删除。
In your example: b
wouldn't be reapplied, because it introduces the exact same changes as c
which is already on master
.在您的示例中:
b
不会被重新应用,因为它引入了与已经在master
上的c
完全相同的更改。
b
instead of cherry-picking:b
而不是挑选樱桃: merge 'mybranch'
v
a---c----d <- master
\ /
b <- mybranch
then git merge mybranch
would have said already merged
, and wouldn't have re-applied commit b
然后
git merge mybranch
会说already merged
,并且不会重新应用提交b
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.