在保留对master的引用的同时,使用git从master将一系列小合并压缩到我的分支中? [英] Squashing a sequence of small merges from master into my branch with git while keeping reference to master?
问题描述
我有一个非常复杂的合并工作.问题的部分原因是我花了太多时间,所以要合并到我的分支中的更改量真是太可怕了.
I had a very complicated merge to do. Part of the problem lay that I let too much time pass by so the amount of changes to incorporate into my branch was monstrous.
为了使事情变得更容易,我选择先执行git merge origin/master~20
,然后执行git merge origin/master~17
,git merge origin/master~15
等,这样我就可以零碎地解决冲突,而不必一次全部解决.
To make things easier, I opted by doing git merge origin/master~20
, then git merge origin/master~17
, git merge origin/master~15
, etc, so I could do the conflict resolutions in a piecemeal fashion instead of having to take it all at once.
问题在于这会导致我想摆脱的日志历史记录受到污染.合并所有这些提交同时仍使生成的提交同时指向我的分支和主服务器的最佳方法是什么?
The problem is tha this lead to a pollution of the log history that I would like to get rid of. What is the best approach to merge all these commits while still keeping the resulting commit pointing to both my branch and master?
我通常使用git reset --soft
进行压缩,但这不会留下对master分支的引用.我也尝试git rebase -i --preserve-merges
,但收到了"Refusing to squash a merge"
错误消息.
I usually squash by using git reset --soft
but that would not leave a reference to the master branch. I also tried to git rebase -i --preserve-merges
but I got "Refusing to squash a merge"
error messages.
我应该如何进行?
推荐答案
让我这样描述您的情况:您具有想要的合并结果-源树,但没有您想要的历史记录会导致此结果.
Let me describe your situation this way: You have the merge result you want—the source tree—but not the history you want that leads to this result.
正如 VonC将其放在其他地方,而您尝试自己尝试时,git reset --soft
通常是 >答案.您进行软重置,然后进行新的提交.如果您现在只能进行 merge 提交,那仍然是答案.
As VonC has put it elsewhere, and you attempted yourself, git reset --soft
usually is the answer. You do the soft reset, then make a new commit. If only you could make a merge commit at this point, it would still be the answer.
不使用git rerere
可以通过三种简单的方法来执行此操作.一种是通过作弊,一种方式是记录在案的作弊",因此可能是正确的方式. (我的首选方法是中间方法,因为它又短又甜,但是显然是作弊的,所以有一天可能会停止工作.)
There are three easy ways to do this without git rerere
. One is by cheating, and one of the ways is "documented cheating" and is therefore probably the right way. (My preferred method is middle one, as it's short and sweet, but it clearly is cheating, so it might stop working someday.)
请注意,此处的命令序列假定您位于存储库的顶层(特别是下面的git rm
和git checkout
步骤是指.
表示一切").我还使用$startpoint
进行提交,之后要进行合并,并使用$other
引用另一个分支名称或提交ID(您要进行git merge
的提交).
Note that the command sequence here assumes you are in the top level of your repository (in particular the git rm
and git checkout
steps below refer to .
to mean "everything"). I also use $startpoint
for the commit after which you want to have your merge, and $other
to refer to the other branch-name or commit ID (the one you want to git merge
).
-
保存最终结果的ID(我们需要 tree ,但是标准工具使其最简单地引用提交,这也很好用):
Save the ID of the final result (we want the tree, but the standard tools make it easiest to just refer to the commit, which also works fine):
$ git tag temp-save-result
(或使用剪切粘贴或引用日志保存它;在这里我只是为了简单起见显示一个标签).
(or use cut-and-paste or the reflog to save it; I show a tag just for simplicity here).
重置.最好是--hard
,而不是--soft
:
Reset. This might as well be --hard
, rather than --soft
:
$ git reset --hard $startpoint
运行合并,该合并将因冲突而失败.忽略冲突,并删除整个索引和工作树.我们不希望发生冲突的合并或到目前为止的任何临时结果,因为我们拥有正确的 结果在其他地方.
Run the merge, which will fail with conflicts. Ignore the conflicts and remove the entire index and work-tree. We don't want the conflicted merge, or any of the temporary results so far, because we have the proper results elsewhere.
$ git merge $other
$ git rm -r .
((如果您有一些自定义的合并工具会留下小滴,您可能也希望从这里将其清除到工作树中,尽管它们不会影响任何重要的事情:它们只会使您的工作树变得混乱不堪) ).
(if you have some custom merge tools that leave droppings behind, you might want to clean those out of the work tree here as well, although they won't affect anything important: they'll just be cluttering up your work tree).
提取步骤1中保存的工作树,并提交结果:
Extract the work-tree saved in step 1, and commit the result:
$ git checkout temp-save-result -- .
$ git commit
此提交结束了合并,合并的 tree 来自您在步骤1中保存的树.您现在可以删除标记:
This commit concludes the merge, whose tree comes from the tree you saved in step 1. You can now delete the tag:
$ git tag -d temp-save-result
方法2:作弊
当git commit
进行新提交时,如果.git/MERGE_HEAD
存在,则进行合并提交. MERGE_HEAD
文件包含第二个提交的ID,即正在合并的 other 或 remote 或--theirs
.
Method 2: cheat
When git commit
makes a new commit, it makes a merge commit if .git/MERGE_HEAD
exists. The MERGE_HEAD
file contains the ID of the second commit, i.e., the other or remote or --theirs
that is being merged-in.
因此,我们只需像往常一样简单地进行软重置,然后添加合并ID,然后提交. (注意:我最近还没有尝试过,Git可能还想要.git/MERGE_MSG
.准备调整作弊技巧,或者直接进入方法3.)
So, we simply do a soft reset as usual, then add the merge ID, then commit. (NB: I have not tried this lately, and Git might also want .git/MERGE_MSG
. Be prepared to need to tweak the cheat, or just move on to method 3.)
$ git reset --soft $startpoint
$ git rev-parse $other > .git/MERGE_HEAD
$ git commit
第一个命令是我们通常的git reset --soft
步骤,第二个命令是Git,它表示我们正在解决一个有冲突的合并(索引已全部解析,因此我们必须在该步骤中完成),而git commit
现在提交合并.
The first command is our usual git reset --soft
step, the second lies to Git to say that we're resolving a conflicted merge (and the index is all resolved, so we must be done with that step), and the git commit
now commits the merge.
创建一个实际提交对象的命令git commit
毫无疑问,只是一个在末尾运行它的shell脚本-git commit-tree
.它要求:
The command that makes an actual commit object—git commit
was no doubt once just a shell script that ran it near the end—is git commit-tree
. It requires:
- 包含所需树的树对象
- 提交消息(它将从stdin中读取一条,但是您可能应该使用
-m
或-F
) - 新提交的父代的父代ID
- a tree object that contains the desired tree
- a commit message (it will read one from stdin, but probably you should use
-m
or-F
) - the parent IDs for the parents of the new commit
它将新的提交对象写入存储库,并打印出对象的哈希ID.
and it writes the new commit object to the repository, and prints out the object's hash ID.
我们已经有树了!由于是当前提交,因此其ID为HEAD^{tree}
(使用 gitrevisions
语法).我们也有两个父ID.我们需要的只是消息:
We already have the tree! Since it's the current commit, its ID is HEAD^{tree}
(using gitrevisions
syntax). We have the two parent IDs as well. All we need is the message:
$ tmp=$(git commit-tree -p $startpoint -p $other -m 'merge msg' HEAD^{tree})
一旦有了提交ID,我们只需在当前分支中git reset --hard
指向它即可:
Once we have the commit ID we just need to git reset --hard
our current branch to point to it:
$ git reset --hard $tmp
(当然,您可以使用$(...)
而不是第二个命令中的$tmp
将两者合并为一个大命令,尽管这假定您的git commit-tree
命令可以工作:最好针对个人情况执行两个步骤如果愿意,可以通过剪切和粘贴哈希ID来省略$tmp
变量和相应的shell $(...)
语法,还可以放入俗气的临时提交消息,然后使用git commit --amend
重置后:--amend
选项也适用于合并提交.)
(Of course, you can combine the two into one big command using $(...)
instead of $tmp
in the second command, although that assumes your git commit-tree
command will work: it's probably better to do two steps for personal comfort. You can omit the $tmp
variable, and the corresponding shell $(...)
syntax, by cutting and pasting the hash ID, if you like that better. And, you can put in a cheesy temporary commit message, then edit it with git commit --amend
once you have reset to it: the --amend
option works on merge commits too.)
这篇关于在保留对master的引用的同时,使用git从master将一系列小合并压缩到我的分支中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!