简体   繁体   中英

Git rewriting history with rebase

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)

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.

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