在保留对 master 的引用的同时,使用 git 将 master 中的一系列小合并压缩到我的分支中?
Squashing a sequence of small merges from master into my branch with git while keeping reference to master?
我有一个非常复杂的合并要做。部分问题在于我让太多时间过去了,所以合并到我的分支中的更改量是巨大的。
为了让事情更简单,我选择了 git merge origin/master~20
、git merge origin/master~17
、git merge origin/master~15
等,这样我就可以零碎地解决冲突,而不必一次性全部拿走
问题是这会导致我想删除的日志历史记录污染。合并所有这些提交的最佳方法是什么,同时仍然保持结果提交指向我的分支和主分支?
我通常使用 git reset --soft
压缩,但这不会留下对 master 分支的引用。我也尝试 git rebase -i --preserve-merges
但我收到 "Refusing to squash a merge"
错误消息。
我应该如何进行?
让我这样描述你的情况:你有你想要的合并结果——源代码树——但没有你想要的历史想要导致这个结果。
作为 VonC has put it elsewhere,并且您自己尝试过,git reset --soft
通常 是 的答案。您进行软重置,然后进行新的提交。如果此时您可以进行 merge 提交,它仍然是答案。
无需 git rerere
即可通过三种简单的方法完成此操作。一种是作弊,其中一种方法是 "documented cheating",因此可能是正确的方法。 (我更喜欢中间的方法,因为它又短又甜,但显然是在作弊,所以有一天它可能会失效。)
方法 1:笨重,但使用所有普通工具(无作弊)
请注意,此处的命令序列假设您位于存储库的顶层(特别是下面的 git rm
和 git checkout
步骤引用 .
表示 "everything").我还使用 $startpoint
作为您想要合并的提交,并使用 $other
来引用另一个 b运行ch-name 或提交 ID(您想要 git merge
).
保存最终结果的ID(我们想要树,但是标准工具最容易只引用提交,这也有效很好):
$ git tag temp-save-result
(或使用剪切和粘贴或 reflog 来保存它;为了简单起见,我在这里显示一个标签)。
重置。这也可能是 --hard
,而不是 --soft
:
$ git reset --hard $startpoint
运行 合并,将因冲突而失败。忽略冲突并 删除整个索引和工作树。 我们不希望有冲突的合并,或者到目前为止的任何临时结果,因为我们有 正确的 其他地方的结果。
$ git merge $other
$ git rm -r .
(如果您有一些自定义合并工具留下了粪便,您可能还想在这里将它们从工作树中清除,尽管它们不会影响任何重要的事情:它们只会让您的工作变得混乱工作树)。
提取步骤1中保存的工作树,并提交结果:
$ git checkout temp-save-result -- .
$ git commit
此提交结束合并,其 tree 来自您在步骤 1 中保存的树。您现在可以删除标签:
$ git tag -d temp-save-result
方法二:作弊
当 git commit
进行新提交时,如果 .git/MERGE_HEAD
存在,它会进行合并提交。 MERGE_HEAD
文件包含第二次提交的 ID,即正在提交的 other 或 remote 或 --theirs
合并。
因此,我们只需像往常一样进行软重置,然后添加合并 ID,然后提交。 (注意:我最近没有尝试过这个,Git 可能也想要 .git/MERGE_MSG
。准备好需要调整作弊,或者继续使用方法 3。)
$ git reset --soft $startpoint
$ git rev-parse $other > .git/MERGE_HEAD
$ git commit
第一个命令是我们通常的 git reset --soft
步骤,第二个命令 Git 表示我们正在解决冲突合并(并且索引已全部解决,所以我们必须完成执行该步骤),git commit
现在提交合并。
方法 3:使用管道命令 ("documented cheating")
创建实际提交对象的命令——git commit
无疑曾经只是一个 shell 脚本,运行 它接近尾声——是 git commit-tree
。它需要:
- 包含所需树的树对象
- 一条提交消息(它将从标准输入中读取一条,但您可能应该使用
-m
或 -F
)
- 新提交的父项的父项 ID
并将新的提交对象写入存储库,并打印出对象的哈希 ID。
我们已经这棵树了!因为它是当前提交,所以它的 ID 是 HEAD^{tree}
(使用 gitrevisions
syntax)。我们也有两个父 ID。我们只需要消息:
$ tmp=$(git commit-tree -p $startpoint -p $other -m 'merge msg' HEAD^{tree})
一旦我们有了提交 ID,我们只需要 git reset --hard
我们当前的 b运行ch 指向它:
$ git reset --hard $tmp
(当然,您可以在第二个命令中使用 $(...)
而不是 $tmp
将两者组合成一个大命令,尽管这假设您的 git commit-tree
命令会起作用:它是为了个人舒适,最好执行两个步骤。如果您更喜欢,可以通过剪切和粘贴哈希 ID 来省略 $tmp
变量和相应的 shell $(...)
语法。而且,您可以输入一个俗气的临时提交消息,然后在重置后使用 git commit --amend
对其进行编辑:--amend
选项也适用于合并提交。)
我有一个非常复杂的合并要做。部分问题在于我让太多时间过去了,所以合并到我的分支中的更改量是巨大的。
为了让事情更简单,我选择了 git merge origin/master~20
、git merge origin/master~17
、git merge origin/master~15
等,这样我就可以零碎地解决冲突,而不必一次性全部拿走
问题是这会导致我想删除的日志历史记录污染。合并所有这些提交的最佳方法是什么,同时仍然保持结果提交指向我的分支和主分支?
我通常使用 git reset --soft
压缩,但这不会留下对 master 分支的引用。我也尝试 git rebase -i --preserve-merges
但我收到 "Refusing to squash a merge"
错误消息。
我应该如何进行?
让我这样描述你的情况:你有你想要的合并结果——源代码树——但没有你想要的历史想要导致这个结果。
作为 VonC has put it elsewhere,并且您自己尝试过,git reset --soft
通常 是 的答案。您进行软重置,然后进行新的提交。如果此时您可以进行 merge 提交,它仍然是答案。
无需 git rerere
即可通过三种简单的方法完成此操作。一种是作弊,其中一种方法是 "documented cheating",因此可能是正确的方法。 (我更喜欢中间的方法,因为它又短又甜,但显然是在作弊,所以有一天它可能会失效。)
方法 1:笨重,但使用所有普通工具(无作弊)
请注意,此处的命令序列假设您位于存储库的顶层(特别是下面的 git rm
和 git checkout
步骤引用 .
表示 "everything").我还使用 $startpoint
作为您想要合并的提交,并使用 $other
来引用另一个 b运行ch-name 或提交 ID(您想要 git merge
).
保存最终结果的ID(我们想要树,但是标准工具最容易只引用提交,这也有效很好):
$ git tag temp-save-result
(或使用剪切和粘贴或 reflog 来保存它;为了简单起见,我在这里显示一个标签)。
重置。这也可能是
--hard
,而不是--soft
:$ git reset --hard $startpoint
运行 合并,将因冲突而失败。忽略冲突并 删除整个索引和工作树。 我们不希望有冲突的合并,或者到目前为止的任何临时结果,因为我们有 正确的 其他地方的结果。
$ git merge $other $ git rm -r .
(如果您有一些自定义合并工具留下了粪便,您可能还想在这里将它们从工作树中清除,尽管它们不会影响任何重要的事情:它们只会让您的工作变得混乱工作树)。
提取步骤1中保存的工作树,并提交结果:
$ git checkout temp-save-result -- . $ git commit
此提交结束合并,其 tree 来自您在步骤 1 中保存的树。您现在可以删除标签:
$ git tag -d temp-save-result
方法二:作弊
当 git commit
进行新提交时,如果 .git/MERGE_HEAD
存在,它会进行合并提交。 MERGE_HEAD
文件包含第二次提交的 ID,即正在提交的 other 或 remote 或 --theirs
合并。
因此,我们只需像往常一样进行软重置,然后添加合并 ID,然后提交。 (注意:我最近没有尝试过这个,Git 可能也想要 .git/MERGE_MSG
。准备好需要调整作弊,或者继续使用方法 3。)
$ git reset --soft $startpoint
$ git rev-parse $other > .git/MERGE_HEAD
$ git commit
第一个命令是我们通常的 git reset --soft
步骤,第二个命令 Git 表示我们正在解决冲突合并(并且索引已全部解决,所以我们必须完成执行该步骤),git commit
现在提交合并。
方法 3:使用管道命令 ("documented cheating")
创建实际提交对象的命令——git commit
无疑曾经只是一个 shell 脚本,运行 它接近尾声——是 git commit-tree
。它需要:
- 包含所需树的树对象
- 一条提交消息(它将从标准输入中读取一条,但您可能应该使用
-m
或-F
) - 新提交的父项的父项 ID
并将新的提交对象写入存储库,并打印出对象的哈希 ID。
我们已经这棵树了!因为它是当前提交,所以它的 ID 是 HEAD^{tree}
(使用 gitrevisions
syntax)。我们也有两个父 ID。我们只需要消息:
$ tmp=$(git commit-tree -p $startpoint -p $other -m 'merge msg' HEAD^{tree})
一旦我们有了提交 ID,我们只需要 git reset --hard
我们当前的 b运行ch 指向它:
$ git reset --hard $tmp
(当然,您可以在第二个命令中使用 $(...)
而不是 $tmp
将两者组合成一个大命令,尽管这假设您的 git commit-tree
命令会起作用:它是为了个人舒适,最好执行两个步骤。如果您更喜欢,可以通过剪切和粘贴哈希 ID 来省略 $tmp
变量和相应的 shell $(...)
语法。而且,您可以输入一个俗气的临时提交消息,然后在重置后使用 git commit --amend
对其进行编辑:--amend
选项也适用于合并提交。)