squash`不会添加“合并”头来提交
我想知道所有的工具如何知道什么分支/提交合并,直到我找到提交中的“合并”标题。
我的问题是:为什么git merge --squash
不添加该头,而git merge
呢?
换句话说:为什么我在使用git merge
时看到合并边缘,而没有使用git merge --squash
?
谢谢。
一些修正的信息:
对于“merge-header”,我的意思是合并后git log
的第二行:
commit 7777777
Merge: 0123456 9876543
Author: Some Body <...>
Date: Fri ....
...当一个git merge --squash
不会产生该行,我的假设是,git gui工具读取的头可以绘制'合并边缘'看到下面的图像。
我的问题也是如此,见上文。
TL; DR: git merge --squash
合并(动词),但不合并(名词)。
您提到的“合并标题”不在这种形式的提交中。 相反,它只是当git log
遇到合并提交时打印的东西。 要真正巩固这个想法,让我们看看合并是什么 - “合并”意味着什么是动词 - 以及合并是什么。 更合适的是,让我们看看合并提交是什么,或者“合并”是指修饰单词“提交”的形容词。 然而,我们会很懒惰,就像好的Git用户一样,并将其从“合并提交”缩短为“合并”,从而使其成为名词。
合并为名词
我们首先看一下名词用法,因为这很简单。 在Git中,合并提交只是一个提交,至少有两个父提交。 这真的很简单。 每个提交都有一些父母。 一个根提交有零父母(并且大多数存储库只有一个根提交,这是第一次提交,显然不能有父提交)。 大多数普通的提交只有一个父代,其他所有提交都有两个或多个父代1,因此合并提交。 然后,我们将短语“合并提交”缩短为“合并”,将“合并”从形容词(修改“提交”)改为名词(意为“合并提交”)。
如果合并提交是一个提交两个父母的提交,那么git merge
必须进行合并提交,并且合并应该意味着“进行合并提交” - 并且它确实,但有时只是。 我们将很快看到什么时候,更重要的是为什么。
1其他VCS可能停在两个。 例如,Mercurial确实没有两个以上的父母。 然而,Git允许任何承诺拥有任何数量的父母。 有三个或更多父母的Git提交被称为章鱼合并,通常这些提交使用git merge -s octopus
或同等工具进行,例如, git merge topic1 topic2 topic3
:运行git merge
与额外提交说明符隐含-s octopus
。 这些做任何事情都无法通过一系列双亲合并来完成,所以Git的章鱼合并并不会比Mercurial更具实力,但章鱼合并有时很方便,并且很适合炫耀你的Git Fu。 :-)
合并为动词
合并的动词形式要复杂得多 - 或者至少在Git中。 我们可以区分两种主要的合并形式:合并源代码更改的行为,然后是合并分支的行为。 (这带来了“什么是分支”的问题,这个问题比你想象的要多得多,所以请看“分支”究竟是什么意思?)许多Git命令可以做动词类型的合并,包括git cherry-pick
和git revert
,这两者本质上都是一种git apply -3
3.2的形式。当然, git merge
是最明显的方法,当它合并时,会得到合并更改的动词形式。
合并更改
合并源变更更恰当地称为“三路合并”。 关于这方面的更多信息,请参阅Wikipedia article3或VonC的答案为什么3路合并优于2路合并? 细节可能会变得相当复杂,但是这个合并过程的目标非常简单:我们想要结合对某个共同基础所做的更改,即给定某个起始点B,我们可以找到C1和C2的更改并将它们合并为一个新的更改C3,然后将该更改添加到基准B以获得新版本。
在Git中,合并源代码的行为 - 使用Git的索引,也称为“暂存区域”。 通常,索引对于每个文件都只有一个条目,它们将进入下一个提交。 当你git add
一个文件时,你告诉Git更新文件的暂存版本,替换当前索引中的文件,或者如果文件以前是“未跟踪”的,则将该文件添加到索引中,以便现在跟踪它并也为下一次提交演出。 但是,在合并过程中,索引每个文件最多有三个条目:一个来自合并基础版本,一个文件版本条目被视为更改#1(也称为“我们”),另一个用于改变#2(“他们的”)。 如果Git能够自己组合这些更改,它将使用一个常规(stage-for-commit)条目来替换三个条目。 否则,它会因冲突而停止,并在文件的工作树版本中留下标记的冲突。 您必须自己解决冲突(大概是在编辑过程中的文件),并使用git add
将三个特殊冲突合并索引版本替换为一个正常分阶段版本。
一旦所有冲突得到解决,Git将能够做出新的提交。
进行合并提交
正常的合并git merge
所做的最后一件事是进行合并提交。 同样,如果没有冲突,Git可以自己完成: git merge
the changes, git add
s each merge-result to update the staged files,并且为你运行git commit
。 或者,如果有冲突,你修复它们,你运行git add
,然后运行git commit
。 在所有情况下,它实际上是git commit
,而不是git merge
本身,这使合并提交。
这最后一部分实际上非常简单,因为索引/分段区域都已设置。 Git只是像往常一样进行提交,不同之处在于,不是将当前( HEAD
)提交的ID作为其单个父进程提供,而是至少提供了两个父提交ID:第一个是像往常一样的HEAD
提交,其余来自git merge
( .git/MERGE
)留下的文件。
2 -3
在git apply -3
,可以拼写为--3way
,指示git apply
在需要时使用git diff
输出中的index
字符串构造合并基础。 当用git cherry-pick
和git revert
来做这件事时,为了把它们变成合并(而不是简单的补丁),Git使用樱桃选择或恢复提交的父提交结束。 这里值得注意的是,Git只在每个文件的基础上做这个,在对补丁进行处理之后,就像一个简单的补丁失败了一样。 只有当提交是当前(HEAD)提交的祖先时,使用父提交文件作为三向合并的基本版本才会有帮助。 如果它实际上不是这样一个祖先,那么将从“base”生成的差异与应用的补丁相结合可能没有帮助。 尽管如此,Git会作为后备之作。
3像维基百科一样,我发现它刚刚出现了一些小的不准确情况 - 例如,可能有两个以上的DAG LCA - 但没有时间去处理它,这不是一个糟糕的概述。
4通常,首先要做出冲突的条目是永远不会困扰的。 如果可能的话,Git会尽快完成git diff
阶段。 假设基础提交中包含四个文件: u.txt
在任一提交中one.txt
没有改变, one.txt
从基本更改为HEAD
但从基本更改为其他提交, two.txt
从基本更改为另一个提交但不是从base到HEAD
,并且three.txt
在两者中都被更改。 Git会简单地复制u.txt
直通,采取one.txt
从HEAD
,采取two.txt
从其他承诺,只有懒得产生差异列表,然后尝试将它们合并为three.txt
。 这个过程非常快,但是这意味着如果你有这些文件的专门三路合并程序,它永远不会为u.txt
, one.txt
和two.txt
,只能运行在three.txt
。
我不确定Git是否会在尝试合并差异之前或尝试失败之后创建多个索引条目。 但是,它必须在运行自定义合并驱动程序之前完成所有三项。
非合并“合并”
上面的序列检查了一些提交(通常是分支提示),在另一个提交(通常是其他分支提示)上运行git merge
,找到合适的合并库,创建两组差异,合并差异,并提交结果 -是正常合并如何工作,以及Git如何合并提交。 我们合并(作为动词)变化,并合并(形容词)提交(或“合并”,名词)。 但是,正如我们前面提到的, git merge
并不总是这样做。
快进
有时候git merge
说它是“做快速合并”。 这有点用词不当,因为在Git中,“快进”更准确地被认为是分支标签更改的属性。 还有两个其他命令使用这个属性, git fetch
和git push
,它们区分了正常(或“快进”)分支更新和“强制”更新。 对快速转发的适当讨论需要深入到提交图的细节中,所以我在这里要说的是,将分支标签从提交O(旧)移动到提交N(新)时发生,并且提交N has承诺O作为祖先。
当git merge
检测到你的合并参数是这些情况之一时, HEAD
是这个其他提交的祖先,它通常会调用这个快进操作。 在这种情况下,Git只使用您告诉它合并的提交。 根本没有新的提交,只是重用了一些现有的提交。 Git不会进行合并提交,也不会执行任何合并作为动词。 它只是改变你对新的提交,就像通过git reset --hard
:移动当前分支标签并更新工作树。
你可以使用--no-ff
.5来抑制这个快进行为.5在这种情况下,即使可以进行快进, git merge
也会进行一次新的合并提交。 你不会得到合并作为动词的动作(没有工作要做),但你得到一个新的合并提交,并且Git更新你的工作树来匹配。
壁球合并不是合并
请注意,我们在这里涵盖了三种情况中的两种:
git merge --no-ff
缺失的第三种情况是动词 - 无名词:我们如何得到合并的行为,结合变化,没有合并提交的名词/形容词形式? 这是“squash git merge --squash <commit-specifier>
”进来的地方。运行git merge --squash <commit-specifier>
告诉Git像往常一样执行merge操作,但不记录其他分支/ commit-ID,以便最终的git commit
使一个正常的,不合并的单亲提交。
这就是它 - 就是这样! 它只是在最后进行正常的非合并提交。 奇怪的是,它迫使你做出承诺,而不是自己承担责任。 (没有必要这样做的根本原因,我不知道为什么Git作者选择这样做。)但这些都是机制,而不是政策:它们告诉你如何制作各种承诺,但不是你应该做什么,或者什么时候,或者最重要的,为什么。
5你可以告诉git merge
,它应该只在它能快进时继续: git merge --ff-only
。 如果新提交可以快进, git merge
会将更新git merge
到它。 否则,它只是失败。 我做了一个别名, git mff
,这样做,因为通常我想要git fetch
,然后看看我是否需要合并,重新绑定,完全创建一个新的分支,或其他。 如果我可以快进,我不需要做其他任何事情,所以如果git mff
工作,我就完成了。
什么时候使用什么样的合并,为什么
为什么问题很难,像所有的哲学问题一样,没有一个正确的答案(但肯定是一堆错误的:-))。 考虑一下这样一个事实:每次你使用git merge
时,你都可以做一些不同的事情,并得到相同的源代码来处理你最新的提交。 git merge
有三个成功的结果(也就是说,你不git merge --abort
- 结束git merge --abort
,而是成功结束):
这三者之间的唯一区别(除了第一个明显的“没有新的提交”)是他们在提交图中留下的记录.6一个快进显然没有记录:该图与之前没有变化,因为你什么也没加。 如果这就是你想要的,那就是你应该使用的。 在一个你正在关注别人的工作并从未做过任何事情的仓库中,这可能就是你想要的。 这也是默认情况下你会得到的,Git会为你“工作”
如果您进行常规合并,则会留下合并记录。 所有现有的提交保持完全一样,Git增加了一个新的提交,有两个父母。 任何后来的人都会看到谁做了什么,什么时候,如何等等。如果这是你想要的,这就是你应该做的。 当然,有些工具(如git log
)会显示谁做了什么,什么时候做什么等等,通过显示所有历史的完整图片,可能会掩盖“大图片”视图以及所有小细节。 换句话说,这既是正面的也是负面的。
如果你做了一个壁球合并,那就不会留下合并记录。 你做了一个新的提交,它提取每个合并即动作动作,但是新提交不是合并成一个名词。 任何后来的人都会看到所有进入的工作,但不是从哪里来的。 像git log
这样的工具不能显示细节,而且你和其他人都只能得到一张大图。 再次,这既是正面的也是负面的。 但是缺点也许有点大,因为如果你发现你以后需要这些细节,他们就不在那里了。 它们不仅在git log
视图中不存在,它们也不适用于将来的git merge
。
如果你永远不会做一个未来的压缩变化的git merge
,那可能不是问题。 如果你打算彻底删除那个分支,放弃所有的个体变化作为个体,并且只保留单个集体git merge --squash
合并变化,做git merge --squash
的“坏”部分git merge --squash
基本上没有“不良价值”。 但是,如果您打算继续在该分支上工作,并在稍后再次合并,那么该特定的不良价值将大大增加。
如果你正在做squash合并,以使git log
输出“看起来更好”(显示更多的大图而不是用太多的细节来掩盖它),请注意,有各种git log
选项可以选择哪些提交显示。 特别是,-- --first-commit
避免完全遍历合并分支,只显示合并本身,然后继续执行提交图的“主线”。 例如,您还可以使用--simplify-by-decoration
来省略所有但标记的提交。
6Well,也在你的推荐日志里; 但是你的推荐日志是私人的,并且最终会过期,所以我们会忽略它们。
7假设他们 - 无论他们是谁 - 都不会“倒退”他们的提交图表,通过重新布署或删除已发布的提交。 如果他们确实删除了已发布的提交,那么Git默认会将这些提交合并回来,就好像它们是您自己的工作一样。 这是发布Git存储库的任何人都应该认真思考“倒回”这样的提交的原因之一。
假设没有奇特的章鱼合并。
Git合并 - 如果这些提交不在master [保留提交顺序]中,将合并所有从分支到提交的提交。 它还添加了一个新的提交ID,显示合并 - http:// repo-name的合并分支“分支名称”到“主”
现在Git合并--squash - 查找所有与主文件不同的文件并将它们标记为已修改。 现在,当您提交这些文件时,它们将使用您提供的消息进入新的提交ID,如正常提交。
链接地址: http://www.djcxy.com/p/45065.html