如何退出(而不是中止)正在进行的Git合并,使更改未提交?

时间:2019-04-26 01:02:32

标签: git git-merge

当Git合并由于冲突或由于用户使用--no-commit选项要求而停止时,则认为合并仍在进行中,文件{{1 }}。

此状态通常在提交合并结果或中止合并时结束。在后一种情况下,合并所引入的更改将被撤销。

是否有任何方法可以完成“正在进行的合并” 状态而不产生提交,也不会丢失合并中的更改?让我们假设所有可能的冲突已解决。 1 这类似于为进行樱桃采摘,还原和变基提供的$GIT_DIR/MERGE_HEAD选项。

一种明显的方法是--quit,但是在进行合并时,这会出错。

对于这个问题,我没有特定的用例,但是我想知道关于Git UI的完整性。


1 就像torek精心阐述的answer所示,未解决的冲突表示在索引槽1到3中的条目,这仅在正在进行的合并中才有意义,严格来说甚至构成一个。这意味着:只要存在未解决的冲突,就可以进行合并。因此,要使该问题反映一个恰当的问题,就必须将其解决所有可能的冲突问题。

3 个答案:

答案 0 :(得分:2)

  

有什么方法可以完成“正在进行的合并”状态而无需生成提交?

不要打扰。提交合并git reset --soft @~,您就完成了。

答案 1 :(得分:2)

使用Git 2.23(2019年第三季度),这将变得更加容易(不再使用git reset),因为“ git merge”学会了“ --quit”选项,可以清除正在进行的合并而使工作树和索引仍然混乱不堪。

请参见commit f3f8311之前的commit b643355(2019年5月18日)和Nguyễn Thái Ngọc Duy (pclouds)(2019年5月9日)。
(由Junio C Hamano -- gitster --commit c4a38d1中合并,2019年6月13日)

  

merge:添加--quit

     

这允许取消当前合并,而无需重置工作树/索引,这就是--abort的用途。
  与其他--quit一样,当您忘记自己正处于合并过程中并且已经离开并执行其他操作时,通常会使用此方法。
  到您意识到的时候,您甚至无法继续合并。

     

这还将使所有正在进行的命令ammergerebaserevert和   cherry-pick,取全部三个--abort--continue--quitbisect有一个   不同的用户界面)。


文档位于commit 437591aPhillip Wood (phillipwood)(2019年6月17日)中。
(由Junio C Hamano -- gitster --commit 0af6d5d中合并,2019年7月9日)

git merge --quit

  

忘记当前正在进行的合并。
  保持索引和工作树不变。

答案 2 :(得分:1)

您主要必须执行git reset,该操作会重置--mixed。那确实会丢失一些信息,这意味着答案是肯定的。如果没有冲突要解决,则可以执行git add -ugit add .或类似操作来恢复丢失的信息;但是无论如何,要使这个答案有意义,还有一些其他事情要实现。

首先,Git根本没有真正使用您的工作树。可以肯定的是,它确实将文件放入您的工作树中,并且将在各种条件下(例如,与 index 的差异)检查它们的更改。 git add -u,或者在由于另一个git checkout而破坏工作树文件之前。而且,当然,git merge以熟悉的形式将合并冲突写入工作树文件:

$ cat file
some contents
a few lines
of contents just
<<<<<<< HEAD
to make some
||||||| merged common ancestors
to make 
=======
to make
>>>>>>> branch
things more
interesting

(这是merge.conflictStyle设置为diff3的一部分,用于添加||||||| merged common ancestors部分,以便您可以看到发生合并冲突之前的情况-在这种情况下,之前是尾随空格,我通过移除branch来固定空格,但在master上通过添加更多单词来固定空格。

我上面提到的

index 是真正的动作所在。这个东西-这个索引,Git也称它为登台区域缓存,这取决于谁/哪个部分的Git进行了调用-包含版本将进入 next 提交的文件的数量。

就像工作树一样,索引纯粹是临时的。但是,从某种意义上说,它对Git的影响要大于工作树。您可以建立一个--bare储存库;这样的存储库没有 工作树,但是它仍然具有提交,并且仍然具有索引。 1 Git可以将提交读入索引,并写入新的提交从索引开始,所有这些都不需要工作树。

通常情况下,索引中的内容非常简单。 Git中的每次提交都会保存每个文件的完整副本。它以特殊的,只读的,压缩的,仅Git的形式保存该文件。我喜欢将这些文件称为“冻干”。每个这样的文件都有一个唯一的blob哈希值,即文件内容唯一的哈希值。索引中的内容恰好是这些相同的冻干文件,或更确切地说是blob散列。提交中的文件和索引中的文件之间的主要区别在于,索引中的文件可以用新的不同的冻干文件(不同的blob散列)覆盖。好了,您可以再次添加新文件或删除现有文件,都以这种冻干形式进行。

无论如何,在Git中执行新提交的动作如此之快 ,因为它真正要做的就是获取索引中已经存在的所有冻干文件,并记录其blob哈希值在提交中(通过更多名为 trees 的Git对象,但这只是实现细节)。新提交将获得一个新的唯一哈希ID。新提交会记住先前当前提交的哈希ID;然后,通过将哈希ID写入HEAD或直通到分支名称的行为,新的提交将成为当前的提交,并成为当前分支的头(如果您在分支上)。

因此,在通常的情况下,在一个非裸露的存储库中,每个文件总是有三个个活动副本。如果您有一个名为README.md的文件,则实际上有:

  • HEAD:README.md:这是当前提交中的冻结副本。您无法更改它,Git也无法更改。 (您可以HEAD移至另一个提交,但这不会影响提交的README.md。)
  • :README.md:这是索引中的副本。它采用的是冻干格式,但是您可以随时将其替换为新副本。
  • README.md:这是您可以看到,品尝和闻到的唯一文件,或与计算机上的文件有关的任何文件。它不是不是冻干格式。它在您的工作树中,因此您可以处理任何其他文件。

一次同时影响这三个文件的主要命令(忽略明显的git checkout <branch>等一次影响许多文件的命令)是

  • git checkout [commit] [--] paths:从某个提交中取出单个文件,将它们复制到索引中,然后再复制到工作树中。
  • git reset [commit] [--] paths:将某些提交中的单个文件复制到索引中,而不接触工作树。
  • git add [--] paths:将单个文件从工作树复制到索引,并在复制过程中将其冻干。

但是,当您开始合并时,索引将扮演新的扩大角色。现在,索引中不仅有每个文件的一个副本,而且最多可以有三个个副本。上例中的file的三个副本是:

  • :1:file:这是来自合并基础提交的文件,是工作树冲突文件中提到的合并的共同祖先
  • :2:file:这是来自HEAD提交的文件。
  • :3:file:这是另一个提交的文件,在这种情况下,该提交位于branch的尖端。

目前没有:0:file,名称:file简写为:0:file,因为存在这三个非零的阶段编号的文件

当您最终解决所有冲突并运行git mergegit commit时,git merge --continue进行合并提交的另一件事是另一次提交的原始哈希ID。因此,Git已将其保存到.git(如您所述的.git/MERGE_HEAD)中的文件中。因此,合并正在进行的事实记录在两个位置:此.git/MERGE_HEAD文件,以及存在未合并的索引条目的事实。

要停止合并,必须必须将所有索引条目放回零阶段。这意味着您必须选择一些文件以从阶段1、2和/或3移到阶段0,或使用工作树副本(通过git add)放入阶段0,或使用文件。 HEAD复制到第零阶段。

现在,如果它们已经是所有处于零阶段的 ,要么是因为您解决了所有冲突,要么是因为您没有冲突,而仅使用git merge --no-commit进入了这种状态,这会有一个非常不利的副作用:git reset --mixedHEAD提交读入索引,这样您就失去了所有已经添加的已解决冲突。但是那些添加的已解决冲突现在也在您的工作树中,因此您可以(可能)将它们放回去(但请在脚注之前查看最后一段)。

从定义上讲,任何在零阶段没有的条目都意味着存在正在进行的未解决的合并。如果不销毁一些正在进行的未解决的合并,就无法解决。

git reset --mixed具有删除.git/MERGE_HEAD的附加副作用,因此不再有正在进行的合并。这样就解决了另一个问题。最大的问题是如何处理更高级别的索引条目,而该特定问题只能通过破坏信息来解决。无论您要保留的是信息,还是必须完成。

如果出于某种原因,您巧妙地上演了file的某个版本,而该版本不在工作树的HEAD提交 nor 中,则此{{1 }}将破坏其哈希值。因此,您可能想使用git reset --mixed将其提取到某个位置的临时文件中,或者至少记录其blob哈希并使用git checkout-index进行恢复。除此之外,答案主要只是git update-index --index-info


1 确实,它也可能也没有索引。毕竟,当您将工作树添加到非裸仓库时,实际上是同时添加了索引和工作树。但是Git的某些内部部分坚持或曾经坚持锁定索引,因此它必须有一个索引才能锁定。