15

Assume I have the following history in my repository:

     E--F
    /
A--B--C---D

And I want to modify it to be this:

     E--F
    /    \
A--B--C---D

I don't want to modify file content of revisions, I just want to "draw a merge arrow". How can I do this?

I tried to do this by the following commands:

git checkout D
git merge F -s ours --no-ff --no-commit
git commit --amend

But I got the following error message:

fatal: You are in the middle of a merge -- cannot amend.

I want to leave the commit information unchanged (author, date, message). The only thing I want to change is to add pseudo merge-info to the commit.

anton_rh
  • 8,226
  • 7
  • 45
  • 73

2 Answers2

13

This worked for me:

git checkout D~1
git merge F -s ours --no-ff --no-commit
git cherry-pick D -n
git commit -C D
git branch -f D
git checkout D
anton_rh
  • 8,226
  • 7
  • 45
  • 73
  • What is `D` and `F`? Branch name or commit? – abhiarora Apr 27 '20 at 05:52
  • 1
    @abhiarora, `F` can be any of them (branch name or commit). `D` is supposed to be branch name. But `D` can also be commit, but in this case the last two commands (_branch_ and _checkout_) won't work. You'll have to create branch for new commit by yourself. – anton_rh Apr 27 '20 at 08:42
  • " You'll have to create branch for new commit by yourself." Didn't get this. Can you add more details in your answer? – abhiarora Apr 27 '20 at 08:43
  • @abhiarora, in case when `D` is commit (not branch), you'll get commits structure like it is shown on pic#2, but instead of commit `D` you'll have commit `D'`. And no branch will point to this commit. So you'll to create branch for this commit manually. If it's difficult for you, just use `D` as branch name. Then all commands in the answer will work fine. – anton_rh Apr 27 '20 at 08:52
6

raul.vila's comment contains the answer: you cannot modify existing commit D. There is no power on earth that can modify an existing commit: git commit --amend is a lie. :-)

There are several things you can do, though:

  • You can make a merge commit that has D as one of its parents, and F as the other, using the content from commit D. That is what this two-command sequence:

    git checkout D
    git merge F -s ours --no-ff --no-commit
    

    prepares to do (the final commit has been suppressed by --no-commit but the merge is recorded as being in progress).

    The problem, as you have seen, comes in when you go to use git commit --amend: --amend means use D's parent(s) instead of D itself as the parents and this conflicts with the ongoing recorded merge of F.

  • You can make a merge commit that has C as one of its parents, F as the other, and uses the contents (the tree) from commit D. This might be what you want. Note that there is no front end command for this, so you must build the new commit rather manually; and the result is what many call an evil merge.

  • You can use git replace to make an additional commit that resembles D but has different parents, that Git will use in place of D, but without actually deleting D at all. That is, the original graph remains in place (and can be viewed and used using git --no-replace-objects), but most commands, when they are about to use D, use the replacement instead.

    The advantage to this method is that it does not affect any existing clones. The disadvantage is that no one else will see this replacement commit unless they explicitly ask to copy it to their own repository: the reference to the replacement exists under refs/replace/, which is not normally transferred on git fetch and git push.

Raymond Chen's comment contains a command that makes a new commit with parents C and F but using D's tree.

torek
  • 448,244
  • 59
  • 642
  • 775