-1

After merging a branch into master, I realized that I ideally should have made a minor commit before the branch was merged, because it is directly related to the commit before the merge. I have tried to clarify what the desired outcome looks like in the simplified example below, where

  • B is the merge commit of X and Y into master,
  • C is a commit that relates to the merged content,
  • D does not touch any of the newly merged content and is directly related to A.

Current branch structure:

A----B-C-D
    /
 X-Y

The desired outcome is to move D right after A, while leaving the rest as is:

A-D----B-C
      /
   X-Y

Rebase after picking all commits and moving D after A:

A-D-X-Y-B-C

Rebase after dropping X and Y, and moving D after A:

A-D-B-C

How can I keep the branch in my history? Is rebase the wrong tool here? I have looked at cherry-pick, but I am not sure how I would use it to achieve the desired outcome.

In case it matters, the branch did not origin from master, but from an unrelated repo. It added a couple of new files while retaining their commit history from the previous repo. This is for a local repo where I am the only contributor.

joelostblom
  • 43,590
  • 17
  • 150
  • 159

2 Answers2

2

I don't know if that can be achieved with a single run of git rebase -i. But you can do it like this:

git checkout A
git cherry-pick D
git merge Y # this will create kind-of a B', it won't be the same, though cause you brought D before it
git cherry-pick C

Then you could consider moving branches around if you liked the results:

git branch -f master
git checkout master
eftshift0
  • 26,375
  • 3
  • 36
  • 60
  • I too would have gone for something like this rather than rebasing. Much clearer, seems to me. – Romain Valeri Dec 10 '19 at 14:45
  • 1
    In all honesty, I don't understand why people are so fond of `git rebase -i`. It's very powerful, don't get me wrong, but there are situations when it just isn't the tool for the job. – eftshift0 Dec 10 '19 at 14:54
  • Couldn't agree more. So far, no other git command has caused me to go help a coworker as often as this one. – Romain Valeri Dec 10 '19 at 15:17
1

You might want to do something like this:

git rebase -i --rebase-merges A

The rebase todo instructions will initially look like this

label onto

# Branch branch-name/master
reset [new root]
pick X
pick Y
label branch-name/master

reset onto
merge -C pick B
pick C
pick D

Reorder the bottom part as in a standard rebase:

reset onto
pick D
merge -C pick B
pick C

By adding --rebase-merges, you are asking Git to try to recreate any existing branches and maintain the merge structure when the rebase takes place.

From the man pages:

--rebase-merges[=(rebase-cousins|no-rebase-cousins)]

By default, a rebase will simply drop merge commits from the todo list, and put the rebased commits into a single, linear branch. With --rebase-merges, the rebase will instead try to preserve the branching structure within the commits that are to be rebased, by recreating the merge commits. Any resolved merge conflicts or manual amendments in these merge commits will have to be resolved/re-applied manually.

By default, or when no-rebase-cousins was specified, commits which do not have <upstream> as direct ancestor will keep their original branch point, i.e. commits that would be excluded by git-log2's --ancestry-path option will keep their original ancestry by default. If the rebase-cousins mode is turned on, such commits are instead rebased onto <upstream> (or <onto>, if specified).

The --rebase-merges mode is similar in spirit to the deprecated --preserve-merges, but in contrast to that option works well in interactive rebases: commits can be reordered, inserted and dropped at will.

It is currently only possible to recreate the merge commits using the recursive merge strategy; Different merge strategies can be used only via explicit exec git merge -s <strategy> [...] commands.

See related StackOverflow topic here.

joelostblom
  • 43,590
  • 17
  • 150
  • 159
Dykotomee
  • 728
  • 6
  • 20