在git合并过程中,所有差异是否从目标分支复制到源分支? [英] Do all diffs get copied to source branch from destination branch during git merge?
问题描述
我一直试图从一个dst分支获取所有差异到一个src分支。我通常遵循以下工作流程。
git checkout dst(切换到dst分支)
git pull dst(fetch将远程dst上的更改合并到本地dst)
git checkout src(切换到src分支)
git合并dst(将所有差异从dst拖到src?)
[如果合并冲突发生,我解决合并冲突,并做]
git commit
然而,合并后,我看到并非所有的差异都复制到我的src分支。例如 git diff src:somefile dst:somefile
显示src和dst之间的 somefile
中的添加,但是这些更改是不是复制到我期望在合并后发生的src中。
我在考虑合并的方式时是否存在根本性错误?如何获得从dst到src分支的所有差异(增加和删除)。
我在考虑合并的方式是否存在根本性错误?
是:
...
git diff src:somefile dst:somefile
...
这不是合并的操作。合并分支并不意味着使我的文件与他们的文件相同。
提交图
让我们从一些提交图绘制开始:
... <-B <-C <-D < ; - 分支
这里,大写字母表示提交ID,即其中一个像 f931ca0 ...
这样的大的丑陋的40个字符的SHA-1 ID。每个提交都会记录一个源代码树(例如,您可以使用< id>:path
来查看该文件的内容以及该提交ID)。它还记录了一些父提交ID:通常只有一个ID,但有时不止一个(合并提交),很少有零父ID(根提交)。
分支
就是您的其中一个分支的名称,它包含commit-ID D
,即分支名称指向该分支上的最小提交 D
。提交 D
指向 C
,它指向 B
,等等。
一个图形变成分支-y或分支 - 这个术语不幸与我们用来讨论分支的名称 - 当几个不同的提交指向同一个父代时。请记住,这里的箭头都指向左边,即使它们指向左右或左右,因为文本样式的图表没有箭头的空间,所以我们只画一条线,比如 -
或者 \ $ c $
... B - C - 或 /
D < - br1
\
E - F < - br2
在这里,分支 br1
的提示是commit D
,并且分支的尖端 br2
是提交 F
。使用 git rev-parse
:
<$ p可以向git请求实际的ID,具体的40个字符的东西$ p> $ git rev-parse br1
e59f6c2d348d465e3147b11098126d3965686098
for实例。我们再次说, br1
指向commit D
,它指向 C
,它指向 B
,依此类推。同时 br2
指向 F
,它指向 E
指向哪个点返回到 B
。
任何给定分支名称指向的提交是提交提交科。当你向一个分支添加一个新的提交时,git通过使新的提交存储上一个提示的ID作为它的父ID来做到这一点,并且一旦新的提交被确定并安全地存储在存储库中,改变存储在分支名称中的ID 。也就是说,如果您向 br1
添加新提交,新提交 G
将指向 D
,git将使 G
:
... B - C - D - G < - br1
\
E - F < - br2
无论我们随着时间的推移提交了多少新提示,提交 B
仍然特别有趣。在图论中,这里的概念是可达性:commit B
(总是)可以从两个分支提示中获得。
合并
所以,下面是 git merge
的确。你给它两个分支提示ID--其中一个是你现在的分支,另一个是你指定的分支 - 和它:
- 标识从两个分支提示(这是合并基础)到达的最近的提交;
- 执行从合并基础到当前分支提示的差异;
li>
- 完成从合并基础到另一个技巧的差异;
- 结合这两个差异来查看双方在哪里使相同
更改;
- 应用步骤3中找到的更改,而不重新应用步骤4中找到的组合更改;和 如果一切顺利,则使用两个父母进行新的提交,这两个父母都是分支提示。 / ol>
在步骤2和步骤3中发现的更改影响同一文件的相同区域但不完全相同的情况下会发生冲突(因此可以不会通过步骤4减去)。如果发生冲突,git会让你解决它们;当你做最后的
git commit
时,这仍然会产生相同类型的合并提交。
现在,假设在
B
-C
-D
-G
行,有人修改了路径README.txt
以在第一行包含感叹号。同时在B
-E
-F
这到README.txt
。最后的合并提交将保留这里所做的更改,但如果您比较br1:README
和br2:README
有区别:br2
中的第一行不会有感叹号。这是因为被合并的变更(base-to -br2
)不会改变这个;保留的变化(基于 - < - c $ c> br1 )。我们不希望基于 -br1
更改来取消 ,我们只想要尚未存在的更改(尚未完成在任何情况下,让我们在最后的合并提交中绘制H
:
... B - C - D - G - H < - br1
\ /
E ----- F < - br2
现在
br1:README
具有!
但br2:README
没有。
简而言之,在合并之后,没有理由期望任何两个文件在新的分支提示中匹配。如果所有文件必须完全匹配,那么您将清除
br1
上的所有工作,仅替换br2
。I have been trying to get all the diffs from a dst branch to a src branch. I usually follow the following workflow.
git checkout dst (switch to dst branch) git pull dst (fetch and merge changes on remote dst to local dst) git checkout src (switch to src branch) git merge dst (pull all diffs from dst to src?) [optionally if merge conflicts occur, I resolve merge conflicts and do] git commit
However, after merge, I am seeing that not all diffs are copied to my src branch. For instance
git diff src:somefile dst:somefile
shows additions insomefile
between the src and dst, however these changes are not copied over to src which I am expecting to happen after merge.Is there something fundamentally wrong in the way I am thinking about merge? How do I get all the diffs (additions and deletions) from dst to src branch.
解决方案Is there something fundamentally wrong in the way I am thinking about merge?
Yes:
...
git diff src:somefile dst:somefile
...This isn't what merge does. Merging a branch does not mean "make my files the same as theirs".
The commit graph
Let's start with a bit of commit-graph drawing:
... <- B <- C <- D <-- branch
Here, the uppercase letters stand in for a commit ID, i.e., one of those big ugly 40-character SHA-1 IDs like
f931ca0...
. Each commit records a source tree (this is how, for instance, you can use<id>:path
to see that file's contents as of that commit ID). It also records some parent commit ID(s): usually just one ID, but sometimes more than one (a "merge" commit) and rarely zero parent IDs (a "root" commit).The
branch
is just the name of one of your branches, and it contains commit-IDD
, i.e., the branch name "points to" the tip-most commitD
on that branch. CommitD
points back toC
, which points back toB
, and so on.A graph becomes "branch-y" or "branched"—this term unfortunately collides with the same term we use to talk about the name of a branch—when several different commits point to the same parent. Remember that the arrows here all point left-ish, even if they point up-and-left or down-and-left; and since text-style graphs don't have room for arrows, we'll just draw a line like
-
or\
or/
here:... B - C - D <-- br1 \ E - F <-- br2
Here the tip of branch
br1
is commitD
, and the tip of branchbr2
is commitF
. You can ask git for the actual ID, the concrete 40-character thing, usinggit rev-parse
:$ git rev-parse br1 e59f6c2d348d465e3147b11098126d3965686098
for instance. Again, we say that
br1
"points to" commitD
, which points back toC
, which points back toB
, and so on. Meanwhilebr2
points toF
which points toE
which points back toB
.The commit that any given branch-name points to is the "tip commit" of that branch. When you add a new commit to a branch, git does this by making the new commit store the previous tip's ID as its parent ID, and once the new commit is finalized and safely in the repository, changing the ID stored in the branch-name. That is, if you add a new commit to
br1
, the new commitG
will point back toD
, and git will makebr1
point toG
:... B - C - D - G <-- br1 \ E - F <-- br2
No matter how many new tip commits we make over time, commit
B
remains especially interesting. In graph theory terms, the concept here is "reachability": commitB
is (always) reachable from both branch-tips.Merge
So, here's what
git merge
does. You give it two branch tip IDs—one of these is the branch you're on now, the other is the one you name—and it:- identifies the nearest commit reachable from both branch tips (this is the "merge base");
- does a diff from the merge-base to the current-branch tip;
- does a diff from the merge-base to the other tip;
- combines these two diffs to see where "both sides" made the same changes;
- applies the changes found in step 3, without re-applying the combined changes found in step 4; and
- if all goes well, makes a new commit with two parents, those two parents being both branch-tips.
Conflicts occur wherever the changes found in steps 2 and 3 affect the same region of the same file, but are not exactly the same (hence can't be subtracted away via step 4). If conflicts occur, git makes you resolve them; when you do your final
git commit
this still makes the same kind of "merge commit".Now, suppose that somewhere along the way in the
B
-C
-D
-G
line, someone modified the pathREADME.txt
to include an exclamation point in the first line. Meanwhile inB
-E
-F
line, nobody did this toREADME.txt
. The final merge commit will retain the change made here, but if you comparebr1:README
tobr2:README
there will be differences: the first line inbr2
won't have the exclamation point. That's because the changes being merged-in (base-to-br2
) don't change this; the changes being retained (base-to-br1
) do. We don't want base-to-br1
changes to get removed, we only want changes that aren't already present (have not been made on "both side") to be added.In any case, let's draw in the final merge commit
H
:... B - C - D - G - H <-- br1 \ / E ----- F <-- br2
Now
br1:README
has the!
butbr2:README
doesn't.In short, there's no reason to expect, after a merge, any two files to match in the new branch-tips. If all the files had to match exactly, you'd wipe out all the work on
br1
, replacing it with only the work onbr2
.这篇关于在git合并过程中,所有差异是否从目标分支复制到源分支?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!