1

I am currently working on a branch that looks like this

--A------B (master)
        / \
----C--D   E--F (feature_branch, HEAD)

I've been trying my best to fix it without success :( git-rebase <A_SHA1> doesn't seem to work at all. There are currently only two branches: master and feature_branch.

Is there even a way to make it look like this?

--A------B (master)
   \    / \
    C--D   E--F (feature_branch, HEAD)
natty_papi
  • 65
  • 4

1 Answers1

2

Note that existing commits cannot be changed, so given:

...--α--A------B   <-- master
              / \
 ....--γ--C--D   E--F   <-- feature_branch (HEAD)

what you'll inevitably get from a rebase is, instead:

            C'-D'  E'-F'  <-- feature_branch (HEAD)
           /    \ /
          /___---B'
         //
...--α--A------B   <-- master
              / \
 ....--γ--C--D   E--F   [abandoned]

You can then forcibly move master to point to B' instead of B so that you have:

            C'-D'  E'-F'  <-- feature_branch (HEAD)
           /    \ /
          /___---B'  <-- master
         //
...--α--A------B
              / \
 ....--γ--C--D   E--F   [abandoned]

It's now possible to ignore the presence of B, γ, C, and so on entirely and pretend that C' is C, for instance. Note that commit γ has become unreachable unless α and γ are really the same commit.

To achieve this using git rebase, you will want the somewhat-newfangled -r or --rebase-merges option:

git checkout feature_branch   # if needed - you've drawn a detached HEAD
git rebase -i -r --onto master <hash-of-γ>

after which you will need to delete commits such as α and A from the list of commits to be pick-ed, as these commits are all currently on feature_branch as well as on master, through merge commit B. (Note that -r was new in Git 2.18. The -r option uses the interactive machinery to instruct Git to re-perform merges, which is what we will do below.)

Overall, though, it's probably easier to achieve this using separate git cherry-pick and git merge commands:

git checkout --detach <hash-of-A>    # note: master~1 probably finds commit A
git cherry-pick <hash-of-C>          # make C'
git cherry-pick <hash-of-D>          # make D'
git merge --no-ff <hash-of-A>        # make new merge B'
git branch -f master HEAD            # forcibly update master now
git cherry-pick feature_branch~2     # make E'
git cherry-pick feature_branch~1     # make F'
git checkout -B feature_branch       # forcibly move feature_branch and re-attach HEAD

If master~1 does identify commit A, you can use that in place of each literal hash here, and in that case, master^2^ will identify commit C and master^2 will identify commit D, so you can use that in place of those two literal hash IDs.

torek
  • 448,244
  • 59
  • 642
  • 775
  • 1
    Not so "newfangled " anymore indeed: `--rebase-merges` comes from [Git 2.18 (Q2 2018)](https://stackoverflow.com/a/50555740/6309), and the legacy option it replaces, `--preserve-merge`, was officially deprecated with Git 2.22, Q2 2019. – VonC Sep 08 '20 at 05:44
  • Thanks for the explanation :) I ended up using `--rebase-merges`. – natty_papi Sep 08 '20 at 12:40