如何退出(而不是中止)正在进行的 Git 合并以保持未提交的更改?
How to quit (not abort) a Git merge in progress keeping the changes uncommitted?
当 Git 合并由于冲突或用户使用 --no-commit
选项请求停止时,合并被认为仍在进行中,存在表示什么文件 $GIT_DIR/MERGE_HEAD
.
此状态通常在合并结果提交或合并中止时结束。在后一种情况下,合并引入的更改将被倒带。
有什么方法可以完成 "merge in progress" 状态而不生成提交并且不会丢失合并中的更改? 让我们假设所有可能的冲突都已解决。1 这类似于 --quit
提供的用于樱桃采摘、恢复和变基的选项。
一个明显的方法是 git reset --soft
,但是当合并正在进行时会出现错误。
对于这个问题,我没有任何具体的用例,但我想知道 Git UI 在这一点上的完整性。
1正如 torek 非常详尽的 所述,未解决的冲突意味着索引槽 1 到 3 中的条目,这仅在正在进行的合并期间才有意义,并且严格来说甚至构成一个。这意味着:只要存在未解决的冲突,就会有一个正在进行的合并。因此,为了使这个问题反映出一个适定的问题,它必须限制在所有可能的冲突都得到解决的情况下。
您主要需要执行 git reset
,这会执行 --mixed
重置。那确实丢失了一些信息,这意味着实际上,答案是否定的。如果没有冲突需要解决,您可以执行 git add -u
或 git 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
存储库;这样的存储库有 no 工作树,但它仍然有提交,并且仍然有一个索引。1 Git 可以读取一个提交到索引中,并从索引中写入新的提交,所有这些都不需要工作树。
在正常情况下,索引中的内容非常简单。 Git 中的每次提交都会保存每个文件的完整副本。它以特殊的、只读的、压缩的、仅 Git 的形式保存该文件。我喜欢称这些文件为冻干。每个这样的文件都有一个唯一的 blob 散列 — 也就是说,对于文件的内容是唯一的。索引中的正是这些相同的冻干文件,或者更准确地说,是 blob 哈希。提交中的文件和索引中的文件之间的主要区别在于,索引中的文件可以 覆盖 新的、不同的冻干文件(不同的 blob 哈希值)。嗯,那,你可以添加新文件,或删除现有文件,同样都是这种冻干形式。
无论如何,在 Git 中进行新提交的动作是如此之快,因为 它真正做的就是获取所有已经冻结的文件在索引中,并在提交中记录它们的 blob 哈希(通过更多 Git 对象称为 trees 但这仅仅是一个实现细节)。新提交获得一个新的、唯一的哈希 ID;新提交会记住之前当前提交的哈希 ID;然后新提交成为当前提交,如果您在分支上,则成为当前分支的负责人,方法是将哈希 ID 写入 HEAD
或写入分支名称。
因此,在正常使用中,在非裸存储库中,每个文件总是有 三个 个活动副本。如果你有一个名为 README.md
的文件,你实际上有:
HEAD:README.md
:这是当前提交中的冻结副本。你不能改变它,Git 也不能。 (您 可以 将 HEAD
移动到另一个提交,但这不会影响 this 提交的 README.md
。)
:README.md
:这是索引中的副本。它是冻干的格式,但您可以随时用新的副本替换它。
README.md
:这是您唯一可以看到、尝到和闻到的文件——或者您对计算机上的文件所做的任何操作。 不是冻干形式。它在您的工作树中,因此您可以对任何其他文件执行任何操作。
一次影响这三个文件的主要命令——忽略明显的 git checkout <branch>
等一次影响多个文件——是:
git 结帐 [<em>提交</em>] [--] <em>路径</em>
:从一些提交中获取单个文件,将它们复制到索引中,然后再复制到工作树中。
git 重置 [<em>commit</em>] [--] <em>paths</em>
:将单个文件从某些提交复制到索引,而不触及工作树。
git add [--] <em>paths</em>
: 将单个文件从工作树复制到索引,冻结- 在复印过程中将它们晾干。
不过,当您开始合并时,索引会承担一个新的、扩大的角色。索引中的每个文件不再只有 一个 个副本,现在最多有 三个 个副本。上面例子中的三个file
分别是:
:1:file
:这是来自合并基础提交的文件,工作树冲突文件中提到的合并共同祖先。
:2:file
:这是来自 HEAD
提交的文件。
:3:file
:这是来自其他提交的文件,在本例中是 branch
末尾的提交。
暂时没有:0:file
——名字:file
是:0:file
的缩写——因为有这三个非零的阶段编号 个文件。
git merge
需要的另一件事是原始哈希 ID,当您最终解决所有冲突和 运行 git commit
或 git merge --continue
时进行合并提交另一个提交。所以 Git 已将其保存到 .git
中的文件中(.git/MERGE_HEAD
,如您所述)。因此,合并正在进行的事实记录在 两个 位置:这个 .git/MERGE_HEAD
文件,以及存在未合并索引条目的事实。
要停止合并,您必须将所有索引条目放回到阶段零。这意味着您必须选择一些文件从阶段 1、2、and/or 3 移动到阶段零,或者使用工作树副本(通过 git add
)放入阶段零,或者使用 HEAD
副本放入阶段零。
现在,如果它们都已经 在 阶段零,要么是因为您已经解决了所有冲突,要么是因为您没有冲突,只是使用了 git merge --no-commit
要进入这种状态,这有一个相当糟糕的副作用:git reset --mixed
将 HEAD
提交读取到索引中,这样你就失去了所有已经添加的、已解决的冲突。但是那些添加的已解决冲突现在 也 在您的工作树中,因此您可以(可能)将它们放回去(但请参阅脚注前的最后一段)。
任何 不 处于零阶段的条目都意味着,根据定义,存在正在进行的未解决合并。如果不破坏一些正在进行的未解决的合并,就无法解决这个问题。
git reset --mixed
具有删除 .git/MERGE_HEAD
的额外副作用,因此不再有正在进行的合并。这样就解决了另一个问题。最重要的是如何处理更高阶段的索引条目,这个特定问题只能通过销毁信息来解决。无论您是否打算保留这些信息,都必须完成。
如果您出于某种原因巧妙地暂存了某个版本的 file
,该版本既不在工作树中的 HEAD
提交 也不在 中,这个 git reset --mixed
会破坏它的散列。所以你可能想使用 git checkout-index
将它提取到某个地方的临时文件,或者至少记录它的 blob 哈希并使用 git update-index --index-info
来恢复它。不过,除此之外,答案主要只是 git reset
.
1真的,它可能也不应该有索引。毕竟,当您将工作树添加到非裸存储库时,您实际上同时添加了索引和工作树。但是Git的一些内部部分坚持,或者曾经坚持,锁定索引,所以它必须有一个索引来锁定。
Is there any way to finish the "merge in progress" state without generating a commit
不客气。提交合并,git reset --soft @~
,大功告成。
使用 Git 2.23(2019 年第 3 季度),这会更容易(不再 git reset
),因为“git merge
”学习了“--quit
”清理选项向上 in-progress 合并,同时留下工作树和索引仍然一团糟。
参见 commit f3f8311 (18 May 2019), and commit b643355 (09 May 2019) by Nguyễn Thái Ngọc Duy (pclouds
)。
(由 Junio C Hamano -- gitster
-- in commit c4a38d1 合并,2019 年 6 月 13 日)
merge
: add --quit
This allows to cancel the current merge without resetting worktree/index, which is what --abort is for.
Like other --quit
(s), this is often used when you forgot that you're in the middle of a merge and already switched away, doing different things.
By the time you've realized, you can't even continue the merge anymore.
This also makes all in-progress commands, am
, merge
, rebase
, revert
and
cherry-pick
, take all three --abort
, --continue
and --quit
(bisect
has a
different UI).
文档在 commit 437591a (17 Jun 2019) by Phillip Wood (phillipwood
).
(由 Junio C Hamano -- gitster
-- in commit 0af6d5d 合并,2019 年 7 月 9 日)
Forget about the current merge in progress.
Leave the index and the working tree as-is.
当 Git 合并由于冲突或用户使用 --no-commit
选项请求停止时,合并被认为仍在进行中,存在表示什么文件 $GIT_DIR/MERGE_HEAD
.
此状态通常在合并结果提交或合并中止时结束。在后一种情况下,合并引入的更改将被倒带。
有什么方法可以完成 "merge in progress" 状态而不生成提交并且不会丢失合并中的更改? 让我们假设所有可能的冲突都已解决。1 这类似于 --quit
提供的用于樱桃采摘、恢复和变基的选项。
一个明显的方法是 git reset --soft
,但是当合并正在进行时会出现错误。
对于这个问题,我没有任何具体的用例,但我想知道 Git UI 在这一点上的完整性。
1正如 torek 非常详尽的
您主要需要执行 git reset
,这会执行 --mixed
重置。那确实丢失了一些信息,这意味着实际上,答案是否定的。如果没有冲突需要解决,您可以执行 git add -u
或 git 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
存储库;这样的存储库有 no 工作树,但它仍然有提交,并且仍然有一个索引。1 Git 可以读取一个提交到索引中,并从索引中写入新的提交,所有这些都不需要工作树。
在正常情况下,索引中的内容非常简单。 Git 中的每次提交都会保存每个文件的完整副本。它以特殊的、只读的、压缩的、仅 Git 的形式保存该文件。我喜欢称这些文件为冻干。每个这样的文件都有一个唯一的 blob 散列 — 也就是说,对于文件的内容是唯一的。索引中的正是这些相同的冻干文件,或者更准确地说,是 blob 哈希。提交中的文件和索引中的文件之间的主要区别在于,索引中的文件可以 覆盖 新的、不同的冻干文件(不同的 blob 哈希值)。嗯,那,你可以添加新文件,或删除现有文件,同样都是这种冻干形式。
无论如何,在 Git 中进行新提交的动作是如此之快,因为 它真正做的就是获取所有已经冻结的文件在索引中,并在提交中记录它们的 blob 哈希(通过更多 Git 对象称为 trees 但这仅仅是一个实现细节)。新提交获得一个新的、唯一的哈希 ID;新提交会记住之前当前提交的哈希 ID;然后新提交成为当前提交,如果您在分支上,则成为当前分支的负责人,方法是将哈希 ID 写入 HEAD
或写入分支名称。
因此,在正常使用中,在非裸存储库中,每个文件总是有 三个 个活动副本。如果你有一个名为 README.md
的文件,你实际上有:
HEAD:README.md
:这是当前提交中的冻结副本。你不能改变它,Git 也不能。 (您 可以 将HEAD
移动到另一个提交,但这不会影响 this 提交的README.md
。):README.md
:这是索引中的副本。它是冻干的格式,但您可以随时用新的副本替换它。README.md
:这是您唯一可以看到、尝到和闻到的文件——或者您对计算机上的文件所做的任何操作。 不是冻干形式。它在您的工作树中,因此您可以对任何其他文件执行任何操作。
一次影响这三个文件的主要命令——忽略明显的 git checkout <branch>
等一次影响多个文件——是:
git 结帐 [<em>提交</em>] [--] <em>路径</em>
:从一些提交中获取单个文件,将它们复制到索引中,然后再复制到工作树中。git 重置 [<em>commit</em>] [--] <em>paths</em>
:将单个文件从某些提交复制到索引,而不触及工作树。git add [--] <em>paths</em>
: 将单个文件从工作树复制到索引,冻结- 在复印过程中将它们晾干。
不过,当您开始合并时,索引会承担一个新的、扩大的角色。索引中的每个文件不再只有 一个 个副本,现在最多有 三个 个副本。上面例子中的三个file
分别是:
:1:file
:这是来自合并基础提交的文件,工作树冲突文件中提到的合并共同祖先。:2:file
:这是来自HEAD
提交的文件。:3:file
:这是来自其他提交的文件,在本例中是branch
末尾的提交。
暂时没有:0:file
——名字:file
是:0:file
的缩写——因为有这三个非零的阶段编号 个文件。
git merge
需要的另一件事是原始哈希 ID,当您最终解决所有冲突和 运行 git commit
或 git merge --continue
时进行合并提交另一个提交。所以 Git 已将其保存到 .git
中的文件中(.git/MERGE_HEAD
,如您所述)。因此,合并正在进行的事实记录在 两个 位置:这个 .git/MERGE_HEAD
文件,以及存在未合并索引条目的事实。
要停止合并,您必须将所有索引条目放回到阶段零。这意味着您必须选择一些文件从阶段 1、2、and/or 3 移动到阶段零,或者使用工作树副本(通过 git add
)放入阶段零,或者使用 HEAD
副本放入阶段零。
现在,如果它们都已经 在 阶段零,要么是因为您已经解决了所有冲突,要么是因为您没有冲突,只是使用了 git merge --no-commit
要进入这种状态,这有一个相当糟糕的副作用:git reset --mixed
将 HEAD
提交读取到索引中,这样你就失去了所有已经添加的、已解决的冲突。但是那些添加的已解决冲突现在 也 在您的工作树中,因此您可以(可能)将它们放回去(但请参阅脚注前的最后一段)。
任何 不 处于零阶段的条目都意味着,根据定义,存在正在进行的未解决合并。如果不破坏一些正在进行的未解决的合并,就无法解决这个问题。
git reset --mixed
具有删除 .git/MERGE_HEAD
的额外副作用,因此不再有正在进行的合并。这样就解决了另一个问题。最重要的是如何处理更高阶段的索引条目,这个特定问题只能通过销毁信息来解决。无论您是否打算保留这些信息,都必须完成。
如果您出于某种原因巧妙地暂存了某个版本的 file
,该版本既不在工作树中的 HEAD
提交 也不在 中,这个 git reset --mixed
会破坏它的散列。所以你可能想使用 git checkout-index
将它提取到某个地方的临时文件,或者至少记录它的 blob 哈希并使用 git update-index --index-info
来恢复它。不过,除此之外,答案主要只是 git reset
.
1真的,它可能也不应该有索引。毕竟,当您将工作树添加到非裸存储库时,您实际上同时添加了索引和工作树。但是Git的一些内部部分坚持,或者曾经坚持,锁定索引,所以它必须有一个索引来锁定。
Is there any way to finish the "merge in progress" state without generating a commit
不客气。提交合并,git reset --soft @~
,大功告成。
使用 Git 2.23(2019 年第 3 季度),这会更容易(不再 git reset
),因为“git merge
”学习了“--quit
”清理选项向上 in-progress 合并,同时留下工作树和索引仍然一团糟。
参见 commit f3f8311 (18 May 2019), and commit b643355 (09 May 2019) by Nguyễn Thái Ngọc Duy (pclouds
)。
(由 Junio C Hamano -- gitster
-- in commit c4a38d1 合并,2019 年 6 月 13 日)
merge
: add--quit
This allows to cancel the current merge without resetting worktree/index, which is what --abort is for.
Like other--quit
(s), this is often used when you forgot that you're in the middle of a merge and already switched away, doing different things.
By the time you've realized, you can't even continue the merge anymore.This also makes all in-progress commands,
am
,merge
,rebase
,revert
andcherry-pick
, take all three--abort
,--continue
and--quit
(bisect
has a different UI).
文档在 commit 437591a (17 Jun 2019) by Phillip Wood (phillipwood
).
(由 Junio C Hamano -- gitster
-- in commit 0af6d5d 合并,2019 年 7 月 9 日)
Forget about the current merge in progress.
Leave the index and the working tree as-is.