Search code examples
gitgithubbranchrebasebranching-and-merging

Forgot to create a new branch before pushing to remote


I was working on a feature locally and made a couple of commits on my master branch (instead of making a separate branch and working there). Then, I pushed those commits to my remote master branch on GitHub (which is actually a fork of another repository). Some time passed before I realized the error and more commits were added to the remote master branch. Now, I want to go back and make a separate branch for that initial feature. How would I do that?

To illustrate my problem, this is what I have:


master A - B - C - D - E - F

This is what I want:


newbranch     C - D
             /     \
master A - B  ----  E - F

where commits C, D are the initial feature that I forgot to make a new branch for.


Solution

  • Caveat: it's generally considered bad practice to rewrite shared branches such as master. You'll want to make sure everyone who uses the repo is aware that you're doing this.

    That being said, as asked, you could do this:

    # NOTE - this assumes your remote is called "origin"
    
    git fetch
    
    # create a branch called "newbranch" that points to commit hash D
    git branch newbranch D
    
    # checkout master so you can fix it up
    git switch master
    
    # change master to point to B
    git reset --hard B
    
    # Replay E through F (or any new commits if they exist!) onto master
    git cherry-pick E..origin/master
    
    # replace the remote master (make sure everyone is OK with it!)
    git push --force-with-lease
    

    The results of these commands will make your two branches look like:

    newbranch: A-B-C-D
    master: A-B-E'-F'
    

    Note the E' and F' notation indicates that those commits will be similar to E and F, but with different commit IDs (because anytime you rewrite a commit you are literally making a new commit, and therefore the ID will change.)

    Some Thoughts:

    1. It's possible you'll get conflicts at the cherry-pick command. This will happen if any of the commits after D depend on either of the commits you are pulling out of master. If that happens you may want to reconsider this action altogether.

    2. In Git there are oftentimes many different ways to do the same thing. Note you can actually swap out the reset and cherry-pick commands and do it all in one shot with a single "fancy" rebase --onto. But conceptually I think doing a reset and then cherry-picking a range of commits is easier to understand explicitly. The rebase just does both behind the scenes.

    3. In the cherry-pick command, the reason I suggest going from commit E to origin/master instead of the obvious E to F, is simply because you mentioned: "more commits were added to the remote master branch." In case even more are added that you don't know about until your initial git fetch command at the start of this answer, this way you will keep any new unknown commits as well.