【问题标题】:How to interpret a merge commit in git?如何解释 git 中的合并提交?
【发布时间】:2018-04-19 16:39:04
【问题描述】:

这是一个提交历史的示例,其中添加了一些文件(abc):

* 51c3dd7 (HEAD -> master) add c
| * ded49bb (my_branch) add b
|/
* f3cea38 add a

假设我使用 git merge --no-commit --no-ff my_branchmy_branch 开始合并到当前分支 (HEAD),如所述,例如here.

现在,当处于 MERGING 状态时,我取消暂存所有内容,然后通过 git commit 完成合并。

结果我仍然得到一个合并提交,即使 HEAD 实际上没有任何变化:

*   5990dce (HEAD -> master) Merge branch 'my_branch'
|\
| * ded49bb (my_branch) add b
* | 51c3dd7 add c
|/
* f3cea38 a

我觉得这很令人困惑。合并不应该自动中止吗?

当然,这是一个简单的例子,但是在“选择性”合并之后可能会出现类似的混淆,如前文所述。 here.

是否应该将合并提交解释为:“某些内容可能已从该分支添加,也可能未添加?”

【问题讨论】:

  • 您合并了my_branch 的历史,而master 没有。
  • 你根本不应该合并这些,恕我直言。 Git 跟踪更改,它不在乎您是否将某些内容更改回原来的样子,历史应该保留。
  • 我个人更喜欢平面历史(没有任何分支),但你的情况对我来说毫无意义:你为什么要取消分支上演的所有内容?
  • 如果您想保留(即将撤消的)历史更改,您的方式是可以的。如果没有,您应该重写分支(例如git rebase -i)以完全不包含它,然后合并。

标签: git merge git-merge


【解决方案1】:

我觉得这很混乱。

很多人经常这样说 Git。 :-)

在我进一步讨论之前,让我先做一下这个术语说明。这里的词 merge 既可以是形容词——a merge commit——也可以是动词:merge another commit。当 a merge commit 缩短为 a merge 时,形容词形式可以成为名词。区分动词 to merge 和形容词(或名词)a merge commit 很重要。 merge commit 就是任何包含两个或多个父提交的提交。我们通常使用git merge 命令来启动作为动词的合并过程,该过程通过将这些作为名词合并的事物之一结束。

(git merge 命令可以被告知在不作为名词合并的情况下进行合并作为动词。Git 中有更多命令也可以在不作为动词的情况下进行合并做一个名词合并。不过,我们不需要在这里担心它们。)

合并不应该自动中止吗?

否:Git 假定您是认真的。

你说:开始合并的过程,但不要完成它,即使它看起来有效。让我对索引大惊小怪,稍后自己完成合并,通过运行 git commit1 要理解这一点,您需要了解 索引的角色,也称为暂存区,有时也称为缓存。

简单地说,索引是你构建下一个提交的地方。无论下一次提交是否将是合并提交,这都是正确的。对于普通的非合并提交,索引只保存每个文件的副本:它从 当前 提交中的每个文件的副本开始,然后您使用 git add 覆盖其中的一些以复制工作树版本到索引中,替换旧的当前提交版本。如果您git add 一个完全 文件,所采取的操作是相同的,只是该文件是新文件而不是被替换。如果您 git rm 一个文件(无论有无 --cached),您都将从索引中删除该文件,以便该文件不会在下一次提交中。

在合并为动词的过程中,索引的作用相当大,至少在合并冲突的情况下是这样。在这里,索引最终保存(最多)三个每个文件的副本。这些用“更高阶段”的数字编号。 Stage slot 0 用于普通文件;插槽 1-3 用于合并冲突状态文件。虽然这些较高阶段编号中有任何条目,但该索引不能用于提交。 Git 会强制你先解决冲突。

现在git add 有一个新功能:它告诉 Git 它应该删除文件的三个更高阶段的版本,并将正常的零阶段条目写入索引。你在这里没有做任何这些,但值得一提。

请注意,git rm 只是删除文件,而不考虑阶段插槽编号。同时,git resetgit add 一样,获得了一个新功能:git reset 像往常一样将文件从当前提交复制到阶段槽零,但像 git add 一样,更高的阶段槽条目(如果存在)。如果文件在当前提交中不存在,git reset 会从索引中删除该文件。

(事实上,git addgit reset 总是 做这些额外的工作。当没有更高的阶段编号时它什么都不做。如果文件 foo.txt 仅存在于阶段零为 :0:foo.txt,删除 :1:foo.txt(同一文件的 stage-slot-1 条目)无效。)

无论如何,在此合并过程结束时,索引具有将进入新合并提交的所有文件,就像它具有将进入正常非合并提交的所有文件一样。最后的git commit 步骤注意到git merge 留给它的信息——特别是用于使新提交成为合并提交的额外父级(在.git/MERGE_HEAD 中)。因此,git commit 进行新的提交,就像任何提交一样使用索引内容,但设置至少一个额外的父级,如该MERGE_HEAD 文件中记录的那样。 重要的是索引处于可提交状态。无论其内容是什么,这些文件都会成为提交中的文件。您在git merge --no-commitgit commit 之间运行的每个(Git)命令的真正最终目的是对索引中的文件进行一些修改。


1在相对较新的 Git 版本中,您可以运行 git merge --continue 以让 git merge 为您运行 git commit。如果您打算停止合并,请使用git merge --abort,这是肯定古老的git reset --merge 的同义词。 (每个人都应该有一个足够新的 Git 来使用 git merge --abort;一旦 git merge --continue 足够普遍,我建议在这里也使用它。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-21
    • 2013-06-20
    • 2016-05-22
    • 2017-04-20
    • 2018-01-24
    • 2018-10-17
    • 1970-01-01
    相关资源
    最近更新 更多