1

I have a linear history like so

----a--b--c--d--e--f----    main

I want to move a series of commits into a new branch and create a merge commit into the old branch, creating a non-linear history like so

----a-------x--d'--e'--f'-- main
      \    /
       b--c

where x is the new merge commit.

How do I do this?

Note, the moved commits aren't the most recent ones (see here).

0

3 Answers 3

2

This is what I would do:

git checkout a
git merge --no-ff c
git cherry-pick c..main
# at this point you are on a revision that has a history like what you asked for
# now it's time to move main
git branch -f main
git checkout main

And if you have pushed that branch into other repos, you will have to force-push new because you rewrote its history. (there are other considerations out of you rewriting history, just in case).

Sign up to request clarification or add additional context in comments.

Comments

2

You can do it easily enough, but you will be rewriting the history of main which is not recommended and might have to be force-pushed to the remote.

Anyway, here's your initial situation, more or less (I'm using master instead of main and I only went up to e):

$ git log --oneline --graph --all
* d5de97b (HEAD -> master) e
* 97f47d1 d
* 7c33da9 c
* 295b3c7 b
* 18622aa a

OK, I propose to turn b and c into commits on a branch feature that will be merged into master before d. Ready? (Some of these steps, such as some of the checkout moves, may be unnecessary, but I didn't pause to worry about that.)

$ git branch holdmyplace        # prevent losing any commits
$ git branch feature 7c33da9    # call `c` by a branch name `feature`
$ git reset --hard 18622        # slide `master` all the way back to `a`...
$ git merge --no-ff feature     # ...and merge `feature` with a merge commit!
$ git checkout holdmyplace      # okay, now return to the placeholder branch...
$ git rebase --onto master feature holdmyplace 
                                # ...and append it to `master`
$ git checkout master           # finally, return to master...
$ git reset --hard holdmyplace  # ...and slide it back up to the end!
$ git branch -d holdmyplace     # we no longer need the placeholder branch

And now we have:

$ git log --oneline --graph --all
* 0d58dd1 (HEAD -> master) e
* 7831073 d
*   2bd7688 Merge branch 'feature'
|\  
| * 7c33da9 (feature) c
| * 295b3c7 b
|/  
* 18622aa a

Which is what you said you wanted.

Comments

1

Let's start by creating a few pointers to make things easier.

git branch base <a>
git branch feat <c>

The diagram looks like this.

----a--b--c--d--e--f----    main
    ^     ^
  base  feat

Now we merge feat into base, creating the merge commit.

git switch base
git merge --no-ff feat

The diagram now looks like this.

----a------x          base
     \    /
      b--c--d--e--f-- main

Now we rebase main onto base, moving it onto the new merge commit.

git switch main
git rebase --onto base feat

Finally, we can clean up the pointers, which we don't need anymore.

git branch -d base
git branch -d feat

And we end up with the diagram that looks like this.

----a------x--d'--e'--f'-- main
     \    /
      b--c

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.