GIT:如何将合并提交强制推送到祖先

14

我在GIT中有两个分支和两个提交:

 A(master)---B(branch "topic")
  • 分支'master'的HEAD是提交A
  • 分支'topic'的HEAD是提交B
  • 提交A是提交B的父提交

我想在“topic”分支中创建一个合并提交C(它将具有A和B作为父提交)。 (我知道这似乎很奇怪,合并提交将为空。)

 A(master)---B---C (branch "topic")
  \-------------/

我设法以过于复杂的方式创建了这个合并提交(请参见下文)。是否有更简单的方法来创建此合并提交?

感谢您的答案!


初始状态:

$ git init plop
Initialized empty Git repository in /tmp/plop/.git/
$ cd plop/
$ git commit -m "Initial commit (commit A)"  --allow-empty
[master (root-commit) a687d4e] Initial commit (commit A)
$ git checkout -b topic
Switched to a new branch 'topic'
$ git commit -m "Some work on my topic branch (commit B)" --allow-empty
[topic d4d1c71] Some work on my topic branch (commit B)
$ #OK, we now reached the initial state
一些尝试:
$ git merge master #Does not work
Already up-to-date.
$ git merge --no-ff -s ours master #Does not work
Already up-to-date.
有更简单的方法实现以下内容吗?
$ #Let's try another way (too complex!)
$ git checkout master
Switched to branch 'master'
$ git merge --no-ff topic
Already up-to-date!
Merge made by recursive.
$ git checkout topic
Switched to branch 'topic'
$ git merge master
Updating d4d1c71..641e7ae
Fast-forward
$ git checkout master
Switched to branch 'master'
$ git reset --hard HEAD^1
HEAD is now at a687d4e Initial commit
$ git checkout topic
Switched to branch 'topic'
$ git log #This is what I wanted to reach
commit 641e7aeb614d9b49796e8f11abd3a0290ac08b40
Merge: a687d4e d4d1c71
Author: xxx <yyy.zzz>
Date:   Sat Jul 23 12:52:41 2011 +0200

    Merge branch 'topic'

commit d4d1c71c87b94335c8852ab7675cbb663965ef7d
Author: xxx <yyy.zzz>
Date:   Sat Jul 23 12:50:11 2011 +0200

    Some work on my topic branch (commit B)

commit a687d4eb88b9f6d661122a5766dd632dd462fbaa
Author: xxx <yyy.zzz>
Date:   Sat Jul 23 12:49:52 2011 +0200

    Initial commit (commit A)

你为什么想这样做?这没有意义。在master上创建一个新的提交,将合并topic是可能的(而且也有一定的意义)。 - svick
2
以下是合并祖先提交的可能用例:假设分支包含对文件的一些更改(例如,它是某种下游分支),现在我们想要还原这些更改。简单地执行 git revert 会使得 git blame 将所有还原的行归因于还原提交,但将它们归因于上游会更有帮助。创建一个还原提交并将其标记为与上游合并可以实现这一点。 - Chortos-2
好问题。以下是为什么你想这样做的示例,对于每个人都在问“为什么”。我有一个gh-pages分支,其中包含已构建的文档和仅在该分支上检入的dist/prod文件,用于部署。我的dev分支领先于master,我将dev -> gh-pages合并并重新构建,然后部署。但后来我意识到我搞砸了,因为我想要部署我的主构建,而不是我的开发构建。所以我checkout gh-pages并尝试merge --no-commit master,但它会提示“已经是最新的”。在这种情况下,强制进行下游(空)合并将非常有帮助。 - chharvey
刚遇到了这个问题,主分支(master)刚合并到了话题分支(topic),而我希望在话题分支合并到主分支时再进行另一次提交...但是遇到了这个问题。这对于需要知道要跟随哪个合并祖先的工具非常重要。 当git log --graph将您的主分支显示在话题分支的右侧或中间时,也没有帮助。 - user377178
这是另一个用例:复制文件时(再次)保留 git blame 输出,而无需使用非默认的 blame -CC 选项。重命名文件,然后将其合并回去。(你喜欢当你通过谷歌回到 Stack Overflow 并看到自己已经对问题点赞和评论吗?) - Chortos-2
5个回答

11

更新:不用直接操作sha1,有更简洁的方法来实现同样的功能:

$ echo "merge commit" | git commit-tree topic^{tree} -p master -p topic
4201b6abae6bb06f929ea00fbc35019679d55535

$ git merge 4201b6abae6bb06f929ea00fbc35019679d55535
Updating b826a8e..4201b6a
Fast-forward

甚至可以使用一行命令:

$ git merge $(echo "merge commit" | git commit-tree topic^{tree} -p master -p topic)

关于那是做什么的细节 - 请阅读整个答案 :)


我完全同意其他人的看法,对我来说没有意义,但如果你真的想要它-可以使用低级别的管道命令来实现,如下所述。

首先,您应该知道父提交的SHA1值。我假设B有评论“从主题更改”,A有评论“从主分支更改”,而我们当前在B(主题分支)。

$ git log --format=oneline -2
b826a8e93ac8da0de5bfb5b70d5f4e7c352a01fa change from topic
8b7653a529fb3ce964fda79bfd57e645441ad893 change from master

那么你应该知道提交B的具体树对象的sha1:

$ git cat-file -p topic
tree 867f31c455a371756ec353b54d755f51d98d62c4
parent 8b7653a529fb3ce964fda79bfd57e645441ad893
author ivan-danilov <email@gmail.com> 1311518908 +0300
committer ivan-danilov <email@gmail.com> 1311518908 +0300

change from topic

所以它是867f31c455a371756ec353b54d755f51d98d62c4。最后,您应该执行git-commit-tree命令:

$ echo "merge commit" | git commit-tree 867f31c455a371756ec353b54d755f51d98d62c4 -p b826a8e93ac8da0de5bfb5b70d5f4e7c352a01fa -p 8b7653a529fb3ce964fda79bfd57e645441ad893
4201b6abae6bb06f929ea00fbc35019679d55535

请注意,我使用了管道重定向,因为git-commit-treestdin流中获取提交注释。第一个参数是我们用git cat-file得到的树的sha1,另外两个参数是我们用git log得到的提交的sha1。

命令的输出是新创建合并提交的sha1。现在你想将主题分支快进到它:

$ git merge 4201b6abae6bb06f929ea00fbc35019679d55535
Updating b826a8e..4201b6a
Fast-forward

就这样了。你拥有了你想要的。


谢谢,git commit-tree 是我所缺少的。如果它导致这种低级别的GIT命令,我同意我必须重新考虑我们的工作流程... - OLU
@Stefan 如果从主分支执行此操作 - 它将在主分支中创建合并提交,这不是 OP 想要的。如果从主题分支执行 git merge master --no-ff - git 将回复 'Already up-to-date.' - Ivan Danilov
哦,对了,抱歉。应该更仔细地阅读,OP想要反向合并。在这种情况下,我会使用(关于主题分支):cur = $(git rev-parse HEAD); git reset master; git merge --no-ff $ cur -m'将主题合并到主分支' - Stefan
我建议交换父级的顺序,将分支作为第一个父级,主分支作为第二个。如果不拒绝运行,这就是 git merge 的做法。 - Chortos-2

3
以相反的方式合并应该可行:
git branch tmp master    # tmp points to A
git checkout tmp
git merge --no-ff -m 'odd merge' topic    # merge B+A ==> C
git checkout topic
git reset --hard tmp     # topic now points to C
git branch -d tmp

2
只有当mastertopic之间存在差异时,即它们已经分叉,才有意义进行合并提交。在您的情况下,没有需要合并的内容 - topic已经包含了master的所有提交,因此git不允许您创建一个无用的合并。
如果master中有一个topic中没有的提交,则可以正常工作:
$ git init plop
Initialized empty Git repository in C:/Temp/plop/.git/
$ cd plop
$ git commit -m "Initial commit (commit A)" --allow-empty
[master (root-commit) b6e2e91] Initial commit (commit A)
$ git commit -m "master-only commit (commit C)" --allow-empty
[master 67b491e] master-only commit (commit C)
$ git checkout HEAD~ -b topic
Switched to a new branch 'topic'
$ git commit -m "Some work on my topic branch (commit B)" --allow-empty
[topic 2251f13] Some work on my topic branch (commit B)
$ git merge master
Already up-to-date!
Merge made by recursive.

导致...
*   592ad46 Merge branch 'master' into topic
|\
| * 67b491e master-only commit (commit C)
* | 2251f13 Some work on my topic branch (commit B)
|/
* b6e2e91 Initial commit (commit A)

谢谢您的回答。我知道要求一个空提交看起来很奇怪,但它可以符合我们的开发工作流程(也许有缺陷,这是另一个问题)。GIT允许空提交,所以我想GIT在这里应该不会成为问题…… - OLU

0

现在你正在主题分支上,由于它是主分支的直接后代,因此将其与主分支合并没有意义,因为主分支已经包含了所有主题分支的更改。然而,主分支没有主题分支所具有的任何更改。

如果你切换到主分支,然后将主题分支合并到主分支中,主分支应该快进并更新其HEAD。


这是我的做法。但是,我不想让主分支的HEAD移动(尽管我希望“topic”的HEAD移动),所以我必须重置主分支。有更简单的方法来实现这个吗? - OLU
哦不。我误解了你的问题。虽然我没有答案,但我也不明白为什么你想这样做。也许说出来会有帮助? - Jookia
我知道要求一个空提交看起来很奇怪,但它符合我们的开发工作流程(也许是有问题的,这是另一个问题)。GIT允许空提交,所以我想GIT在这里不应该是个问题。 - OLU

0

这个问题没有官方解决方案,但有一个变通方法。查看 master 分支所在的提交,使你处于分离 HEAD状态。然后提交一个空提交。接着切换到你的topic分支并将那个空提交合并进去。

$ git checkout 9123456 # (the latest commit on master)
Note: checking out '9123456'.
You are in 'detached HEAD' state...
$ git commit --allow-empty -m 'empty commit'
[detached HEAD 9123457] empty commit
$ git checkout topic
Warning: you are leaving 1 commit behind ... 9123457 empty commit
Switched to branch 'topic'
$ git merge 9123457

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接