mdcq
mdcq

Reputation: 2026

Move commits into new branch with merge commits

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).

Upvotes: 1

Views: 554

Answers (3)

mdcq
mdcq

Reputation: 2026

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

Upvotes: 1

matt
matt

Reputation: 534893

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.

Upvotes: 2

eftshift0
eftshift0

Reputation: 30156

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).

Upvotes: 2

Related Questions