我使用master
命令将我的uat
分支合并到我的git merge --squash master
分支中,我解决了所有冲突并将我的更改推送到了repo中。现在,自从我合并后没有人触及uat
分支,但是当我在git merge --squash master
分支中运行uat
时,即使我修复了这些冲突并推送它们,我仍然会看到相同的冲突。
答案 0 :(得分:2)
这很正常。
问题是git merge --squash
是人们在分支上可能称之为终端操作:在执行git merge --squash branch
之后,你应该 1 < em>删除分支git branch -D branch
,或者至少将其重命名为“不再对此进行处理”#34;例如:git branch -m branch dead-branches/branch
。
1 &#34;宜&#34;这是一个价值判断。没有技术要求,你可能会忽略保留分支的原因:你只需要知道Git正在做什么以及保留分支的意义。
在考虑壁球&#34;合并&#34;之前,值得看看正常(非压缩)合并的作用,看看为什么我将单词merge合并为{{1的引号}} 案件。 Git的--squash
是关于结合工作,它使用的机制是找到一个共同的起点。这是一个提交 - 理想情况下是一次提交, 提交 - 位于两个分支上,最靠近每个分支的尖端。如果我们按照我喜欢的方式绘制它们,分支名称和提交看起来像这样:
git merge
您已检出的主线分支 - 由分支名称 C--D--E <-- mainline (HEAD)
/
A--B [common starting point]
\
F--G--H <-- branch
附加的单词HEAD
表示 - 表示实际上是&#34;提交mainline
& #34 ;.提交E
到A
的提交都在该分支上,因为Git从E
开始向后工作:E
的父级是E
; D
的父母是D
; C
的父母是C
; B
&#39; s是B
。 (A
是有史以来第一次提交,因此它没有父级:它是 root commit 。这在这里并不重要,但值得一提,因为你做的第一次提交在新的存储库中是这些根提交之一,通常只有 一个。)
同时,另一个分支A
实际上意味着&#34;提交branch
&#34;。提交H
,A
和B
到F
的所有内容都在分支H
上。
请注意,提交branch
在两个分支上!它的提交是最近的&#34;提交两个提示,B
和E
,因此它是合并基础。
在给出提示提交的情况下,通过查找合并基础开始真正的合并。实际上,Git会将保存为合并基础快照的内容与您H
之后的内容进行比较。当前提交git checkout <hash-of-B>
:
E
它用基数和你的其他分支提示重复这个:
git diff --find-renames <hash-of-B> <hash-of-E> # what we did
这是Git应该结合起来的工作。如果您更改了三个根本没有更改的文件,Git应该将更改应用到基础。如果他们更改了一些你根本没有改变的文件,Git应该将他们的更改应用到基础。如果你和他们都触及了相同的文件,Git应该将这些更改结合起来,如果可以的话,确保如果你们两个都做了完全相同的更改,Git只接受< em>该变更的一个副本。
将组合的更改应用于基础会产生新的快照(或者有合并冲突;然后Git通常会停止并让您清理混乱)。假设一切顺利,Git提交新快照,将第一个父级设置为当前提交,将其第二个父级设置为另一个提交:
git diff --find-renames <hash-of-B> <hash-of-H> # what they did
让我们以一种不那么弯曲的方式重新绘制它:
C--D--E
/ \
A--B I <-- mainline (HEAD)
\ /
F--G--H <-- branch
合并提交 A--B--C--D--E--I <-- mainline (HEAD)
\ /
F---G---H <-- branch
仅在一个分支上,在本例中为主线分支。让我们查看旁边分支I
并立即开展更多工作:
branch
如果我们A--B--C--D--E--I <-- mainline
\ /
F---G---H--J--K <-- branch (HEAD)
(将我们的HEAD附加到那里)并运行git checkout mainline
,Git现在必须再次找到上的最佳提交分支机构。我们从侧面分支的git merge branch
开始,然后查看K
,然后查看J
。同时,我们从主线H
开始,然后同时查看I
和E
。提交H
在我们的分支提交列表中,因此它是最接近两个分支的那个:它是新的合并基础。
Git现在将我们的更改与他们的更改结合起来。这些变化是:
H
请注意,这次从git diff --find-renames H I # what we did
git diff --find-renames H K # what they did
开始,Git 不是:由于合并提交B
,合并基础现为H
。< / p>
假设一切顺利,Git进行了一次新的提交,其中组合的更改应用于I
中的任何内容:
H
我们都完成了。
现在让我们看看A--B--C--D--E--I-----L <-- mainline (HEAD)
\ / /
F---G---H--J--K <-- branch
做了什么,从我们原来的同样开始:
git merge --squash
Git执行相同的合并基础查找,并进行相同的组合。我喜欢将其称为合并为动词:它是找到合并库,获得两个A--B--C--D--E <-- mainline (HEAD)
\
F---G---H <-- branch
并组合结果的动作。
但下一步,当一切顺利时,情况会有所不同。而不是将合并提交 - 合并作为形容词,修改 commit ,意味着提交 two (或更多)parents-Git使用一个父进行提交。好吧,事实上,它完全停止,好像你运行了git diff
,但是当你通过运行git merge --no-commit
完成了这个过程时, 进行了一次普通的非合并提交{ {1}}:
git commit
新提交I
与将三个提交A--B--C--D--E--I <-- mainline (HEAD)
\
F---G---H <-- branch
合并并将其应用于I
的内容具有相同的效果:内容方面,它是您将得到的内容如果您在F-G-H
上执行E
并将最后两个git rebase -i master
命令更改为branch
。但它不是合并提交!它不是一个合并为形容词,即使它做了合并动词动作。
所以,如果您现在pick
并进行更多提交然后切换回主线,那么您现在拥有的内容如下:
squash
如果你现在要求Git合并两个分支,Git首先找到合并基础。合并基础是两个分支上的第一个提交,由于git checkout branch
不是合并提交,因此再次提交A--B--C--D--E--I <-- mainline (HEAD)
\
F---G---H--J--K <-- branch
。
请注意I
在其内容中包含B
的所有更改(因为我们是通过merge-as-a-verb-ing制作的),但是Git不知道,所以下次合并必须查看I
vs F-G-H
,这意味着再次放入B
的所有内容。
如果Git能够自己上次解决所有冲突,那么它也可以自己解决所有冲突。这个新的合并动词,无论我们是否使用H
,都可以自动完成所有操作。如果Git上次不得不停下来得到帮助,那么这次也有可能不得不停下来寻求帮助:许多相同的冲突都会发生。它们甚至可能更糟糕,取决于我们解决它们做出提交F-G-H
时所做的事情。
正常的解决方案是当我们执行--squash
并生成提交I
时,我们完全抛弃侧支:
git merge --squash
如果我们想进行更多更改,我们会建立一个新的分支:
I
然后对其进行新的提交:
A--B--C--D--E--I <-- mainline (HEAD)
通过A--B--C--D--E--I <-- mainline, branch2 (HEAD)
提交A--B--C--D--E--I <-- mainline
\
J--K <-- branch2 (HEAD)
已被放弃(很快就会消失,特别是如果我们删除名称F
,因为这也会删除H
的reflog这会将剩余的提交保护减少到branch
reflog中的#34;无法访问&#34;提交的30天。但是,我们不再需要或不再需要它们:这三个旧的无聊快照现在表示为提交branch
的单个快照。