简体   繁体   中英

GIT: how to force a merge commit to an ancestor

In GIT, I have two branches and two commits:

 A(master)---B(branch "topic")
  • the HEAD of the branch 'master' is commit A
  • the HEAD of the branch 'topic' is commit B
  • commit A is the parent of commit B

I would like to create a merge commit C in the "topic" branch (it would have A and B as parents). (I know that this seems odd, and that the merge commit would be empty.)

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

I managed to create this merge commit in a too complex way (see below). Is there any easier way to create this merge commit?

Thanks for your answers!


Initial state:

$ 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

Some tries:

$ git merge master #Does not work
Already up-to-date.
$ git merge --no-ff -s ours master #Does not work
Already up-to-date.

Is there an easier way to achieve the following?

$ #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)

UPD : Cleaner way to do same thing without messing with sha1 directly:

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

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

Or even one-line command:

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

For details of what that's doing - read entire answer :)


I'm totally agree with others in that it has no sense for me, but if you really want it - it is possible with low-level plumbing commands as described below.

First of all, you should know sha1 of your parent commits. I suppose B has comment 'change from topic', A has comment 'change from master' and we're currently at B (topic branch).

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

Then you should know sha1 of concrete tree object of commit B:

$ 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

So it is 867f31c455a371756ec353b54d755f51d98d62c4 . And finally you should execute git-commit-tree command:

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

Note that I used pipeline redirection as git-commit-tree takes commit comment from stdin stream. First param is tree's sha1 we got with git cat-file and two others are commits' sha1 we got with git log .

Output from command is the sha1 of newly created merge commit. Now you want to fast-forward topic branch to it:

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

That's all. You have what you wanted.

Merging the other way around should work:

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

It only makes sense to make a merge commit if there's a difference between master and topic - if they have diverged. In your case, there's nothing to merge - topic already has all of master 's commits, so git won't allow you to create a merge that does nothing.

It works fine if you have a commit in master that's not in 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.

Resulting in...

*   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)

There is no official solution to this problem, there is a workaround. Check out the commit that master is on, so that you are in detached HEAD state. Then commit an empty commit. Then check out your topic branch and merge in that empty commit.

$ 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

Right now you're on the topic branch, and since it's a direct descendant of master, merging with master makes no sense as topic contains all the changes master has. However, master has none of the changes topic has.

If you checkout master and then merge topic in to master, master should fast forward and update its HEAD.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM