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-pickgit 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 -3git apply -3 ,可以拼写为--3way ,指示git apply在需要时使用git diff输出中的index字符串构造合并基础。 当用git cherry-pickgit 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.txtHEAD ,采取two.txt从其他承诺,只有懒得产生差异列表,然后尝试将它们合并为three.txt 。 这个过程非常快,但是这意味着如果你有这些文件的专门三路合并程序,它永远不会为u.txtone.txttwo.txt ,只能运行在three.txt

我不确定Git是否会在尝试合并差异之前或尝试失败之后创建多个索引条目。 但是,它必须在运行自定义合并驱动程序之前完成所有三项。


非合并“合并”

上面的序列检查了一些提交(通常是分支提示),在另一个提交(通常是其他分支提示)上运行git merge ,找到合适的合并库,创建两组差异,合并差异,并提交结果 -是正常合并如何工作,以及Git如何合并提交。 我们合并(作为动词)变化,并合并(形容词)提交(或“合并”,名词)。 但是,正如我们前面提到的, git merge并不总是这样做。

快进

有时候git merge说它是“做快速合并”。 这有点用词不当,因为在Git中,“快进”更准确地被认为是分支标签更改的属性。 还有两个其他命令使用这个属性, git fetchgit 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

    上一篇: squash` does not add "Merge" header to commit

    下一篇: how to add option ' git merge