618

I've done a fair bit of work ("Your branch is ahead of 'origin/master' by 37 commits.") which really should have gone into its own branch rather than into master. These commits only exist on my local machine and have not been pushed to origin, but the situation is complicated somewhat in that other devs have been pushing to origin/master and I've pulled those changes.

How do I retroactively move my 37 local commits onto a new branch? Based on the docs, it appears that git rebase --onto my-new-branch master or ...origin/master should do this, but both just give me the error "fatal: Needed a single revision". man git-rebase says nothing about providing a revision to rebase and its examples do not do so, so I have no idea how to resolve this error.

(Note that this is not a duplicate of Move existing, uncommited work to a new branch in Git or How to merge my local uncommitted changes into another Git branch? as those questions deal with uncommitted changes in the local working tree, not changes which have been committed locally.)

Robin Green
  • 32,079
  • 16
  • 104
  • 187
Dave Sherohman
  • 45,363
  • 14
  • 64
  • 102
  • 2
    Check out this [solution](https://stackoverflow.com/a/1628584/1972597). Seems to be easy and clean. – Tony Oct 07 '19 at 07:24

12 Answers12

627

This should be fine, since you haven't pushed your commits anywhere else yet, and you're free to rewrite the history of your branch after origin/master. First I would run a git fetch origin to make sure that origin/master is up to date. Assuming that you're currently on master, you should be able to do:

git rebase origin/master

... which will replay all of your commits that aren't in origin/master onto origin/master. The default action of rebase is to ignore merge commits (e.g. those that your git pulls probably introduced) and it'll just try to apply the patch introduced by each of your commits onto origin/master. (You may have to resolve some conflicts along the way.) Then you can create your new branch based on the result:

git branch new-work

... and then reset your master back to origin/master:

# Use with care - make sure "git status" is clean and you're still on master:
git reset --hard origin/master

When doing this kind of manipulating branches with git branch, git reset, etc. I find it useful to frequently look at the commit graph with gitk --all or a similar tool, just to check that I understand where all the different refs are pointing.

Alternatively, you could have just created a topic branch based on where your master is at in the first place (git branch new-work-including-merges) and then reset master as above. However, since your topic branch will include merges from origin/master and you've not pushed your changes yet, I'd suggest doing a rebase so that the history is tidier. (Also, when you eventually merge your topic branch back to master, the changes will be more obvious.)

Mark Longair
  • 446,582
  • 72
  • 411
  • 327
  • If you go with the second approach, which leaves you with your new branch pointing at your merge commit, you can always reset that branch to point at the correct parent of that commit -- after all, a branch is just a pointer to a particular commit. However, if you're going to rebase it anyway, it probably makes sense just to do the rebase. – Andrew Aylett Feb 21 '11 at 13:40
  • @Andrew: I read the question as having suggested that the questioner had pulled multiple times in the mean time, and that it wasn't necessarily the case that he was currently at the merge commit. (Reading again, I see that the former isn't actually clear.) Anyway, if my assumption were right it wouldn't make much difference whether the topic branch was pointing to a merge commit or not, given there would be previously unpushed merges anyway. But I take your point :) – Mark Longair Feb 21 '11 at 14:05
  • @Mark: Good point, I'd obviously not read it like that. One of the things I really like about git is that it's flexible enough that things like this are fixable at all, let alone in many different ways :). Also, +1 for suggesting using `gitk`. – Andrew Aylett Feb 21 '11 at 14:21
  • To clarify, yes, you understood correctly, Mark: I had pulled multiple times since I started diverging from master. – Dave Sherohman Mar 22 '11 at 14:35
  • @DaveSherohman did you use the alternative or the first solution with `git rebase...` when you said worked like a charm. I'm in the same situation. – pjammer Dec 10 '12 at 13:28
  • @DaveSherohman you are right, the first option works like a charm! and to Mark, thanks for this answer. i wish i could upvote more! – pjammer Dec 10 '12 at 14:43
  • I started following these steps, but got to the `reset --hard` part and couldn't see how my commits had been moved from `master` onto `newBranch`. It appears that, if I do that reset, I'll lose my commits. Don't I need some command to move my commits over to newBranch? `status` tells me that I'm on branch `master`, and ahead of `origin/master` by # commits. Maybe I want your option-B, but I'm not understanding it as a set of ordered steps. Could you edit? Thanks! – Olie Aug 26 '14 at 18:53
  • Also, in the (main, not alternate) steps above, should not `git branch new-work` be `git checkout -b new-work` ? Isn't that the step that moves all the current work onto a new branch? It seems that, as written, we create a new branch, but never put any work onto it. Sorry if I'm being ignorant/naive; I'm just not following the "committed changes get moved onto `new-work` branch" part. – Olie Aug 26 '14 at 19:03
  • @Olie indeed! In fact I just seemed to have lost 2 days of work because of this >< Let's see what my Eclipse history is able to give me. Grrr – Quintesse Jan 21 '15 at 21:13
  • 8
    @Olie: No, the answer is correct under the assumptions in the question and those I set out at the top of the answer. The commits that should be on a separate new branch are already in `master`; the rebase rewrites the `master` branch so that the new commits are linearly on top of `origin/master`, then `git branch new-work` creates a `new-work` branch pointing at the tip of `master` (the current branch) *without* switching the current branch to `new-work`. So now `new-work` contains all the new commits. Then the reset moves the current branch (still `master`) back to `origin/master`. – Mark Longair Jan 21 '15 at 22:01
  • 1
    @Quintesse: I'm very sorry if you've lost work, but I'm sure this answer is correct for the situation described by the original questioner. (I've just replied to Olie's remarks hopefully clarifying.) Anyway, just in case it helps to get your work back, I should say that if your work was committed within the last few days (one of the assumption in this question and answer) you should be easily able to retrieve it via the git reflog. – Mark Longair Jan 21 '15 at 22:06
  • 6
    @Olie: perhaps a better way of explaining: branches in git are just like labels that point at a particular commit; they're automatically moved to new commits if you create them while on that branch or can be moved with `git reset` and various other ways. The `git branch new-work` is just saying "create a branch pointing at this commit while I remain on my current branch (which is master in this case)". So there's no need to have a command that *moves* the commits from master to the new branch - you just create a new branch there and when you reset master the new branch is left where master was – Mark Longair Jan 21 '15 at 22:12
  • Would it be similar to do the rebase, then reset soft, then checkout -b newbranch? – Niall Connaughton Mar 18 '15 at 05:56
  • 1
    a bit late to the party but @Olie, just because git status when on the new branch doesn't show the commits ahead of master doesn't mean they are not actually there (assuming that's why you were worried). Try pushing the new branch to origin: you will see the commits are there – Félix Adriyel Gagnon-Grenier Nov 23 '15 at 22:16
  • 2
    @FélixGagnon-Grenier, don't worry about "late" -- there are always people looking up old questions and every clarification helps. Thanks! :) – Olie Nov 24 '15 at 02:49
  • @FélixGagnon-Grenier - actually the commits are there locally as well, you just need to switch to the branch you created using `git checkout new-work`. I also had a moment of dread before realizing this. – Eli Iser Jan 06 '16 at 13:12
  • to make sure `git status` is clean before `git reset`, one could use `git stash` and then `git stash pop after the reset is done with. – steadyfish Oct 07 '16 at 13:31
228

If you have a low # of commits and you don't care if these are combined into one mega-commit, this works well and isn't as scary as doing git rebase:

unstage the files (replace 1 with # of commits)

git reset --soft HEAD~1

create a new branch

git checkout -b NewBranchName

add the changes

git add -A

make a commit

git commit -m "Whatever"
Stachu
  • 5,677
  • 3
  • 30
  • 34
  • 6
    To show an easy-to-understand graph, please use `git log --all --decorate --oneline --graph`. – EliuX Mar 28 '18 at 22:30
  • 1
    Hey @EliuX - I'm missing the relevancy here. Can you expand? – Stachu Mar 29 '18 at 15:26
  • This is something useful to check if you got the wanted result in what you did – EliuX Apr 02 '18 at 04:48
  • 8
    Thank u!! this is a very simple solution and it worked perfectly!! – Chris Sim Oct 18 '18 at 09:44
  • Not sure what went wrong but I just lost some work doing this. My work was lost when I switched to the new branch - all changes were gone at this point. – Noobie3001 Oct 20 '20 at 10:44
  • I'm sorry to hear that, @Noobie3001. If you're in a disaster situation, I'd recommend `git reflog` and seeing if you can stumble upon some of your lost work. Additionally, depending on the IDE/editor you're using, it may track changes locally - maybe you can recover some work there! I've done my above steps dozens of times without fail - not sure what could have gone wrong for you. – Stachu Oct 20 '20 at 14:02
  • this is a very simple solution. Thank you! – pritampanhale Apr 28 '21 at 05:36
224

I stuck with the same issue. I have found easiest solution which I like to share.

1) Create new branch with your changes.

git checkout -b mybranch

2) (Optional) Push new branch code on remote server.

git push origin mybranch

3) Checkout back to master branch.

git checkout master

4) Reset master branch code with remote server and remove local commit.

git reset --hard origin/master
Ariel
  • 25,995
  • 5
  • 59
  • 69
NiRmaL
  • 2,866
  • 1
  • 18
  • 15
  • 4
    You can leave out step 2. I assume that in the time since the top answer, git has changed and this prcedure was not allowed before. – Sebastian Dec 19 '18 at 12:59
  • 11
    Wanted to expand on why this works. You have a set of commits that were committed to your local master but not updated on the origin. Then you create a branch from your local update, which reflects all the new changes. When you switch back to the master and hard reset it, it resets back to the origin's master. Your new branch still has the changes it inherited from the local master prior to reset. The new branch doesn't get reset along with the local master. If you're doing this for the first time, you might consider backing up your repo and confirm your changes are in your backup. – rocking_ellipse Sep 24 '20 at 13:38
  • This was the clearest and what felt like the safest option since it creates the branch first. I wouldn't have realized that you can push under the branch without pushing the same commit under master but sure enough it works. You can then verify that your changes are safely pushed to the remote and can start a pr. From there it's just a matter of going back to master and resetting back to the last commit before the outgoing (--hard). – b_levitt Jun 27 '23 at 20:36
36

One more way assume branch1 - is branch with committed changes branch2 - is desirable branch

git fetch && git checkout branch1
git log

select commit ids that you need to move

git fetch && git checkout branch2
git cherry-pick commit_id_first..commit_id_last
git push

Now revert unpushed commits from initial branch

git fetch && git checkout branch1
git reset --soft HEAD~1
Andriy
  • 973
  • 8
  • 13
  • 5
    Cherry-pick is really the best "copy/move a single commit" command, esp. when history is baggage for your purposes. – John Neuhaus May 14 '18 at 15:24
  • This is so far the most convenient answer to the question. Thank you for the command! – Farah Jul 18 '18 at 22:51
  • can you update your last comment where 1 or n is the number of unpushed commits? This is still a really good solution for this question. – chAlexey Jun 12 '19 at 13:45
17

Alternatively, right after you commit to the wrong branch, perform these steps:

  1. git log
  2. git diff {previous to last commit} {latest commit} > your_changes.patch
  3. git reset --hard origin/{your current branch}
  4. git checkout -b {new branch}
  5. git apply your_changes.patch

I can imagine that there is a simpler approach for steps one and two.

testing
  • 19,681
  • 50
  • 236
  • 417
Andreas B
  • 377
  • 3
  • 10
  • I did this but I replaced step 3 with `git reset --hard {commit_hash}` to go back to the commit where I wanted to separate my branches, then apply my changes onto a new branch. The downside is you have to and write your commit messages again. – Jonno_FTW Dec 01 '22 at 00:17
  • Just did this and it worked perfectly, thanks for sharing. – Sitnik Dec 08 '22 at 22:00
6

What about:

  1. Branch from the current HEAD.
  2. Make sure you are on master, not your new branch.
  3. git reset back to the last commit before you started making changes.
  4. git pull to re-pull just the remote changes you threw away with the reset.

Or will that explode when you try to re-merge the branch?

Tim Keating
  • 6,443
  • 4
  • 47
  • 53
5

Here is a much simpler way:

  1. Create a new branch

  2. On your new branch do a git merge master- this will merge your committed (not pushed) changes to your new branch

  3. Delete you local master branch git branch -D master Use -D instead of -d because you want to force delete the branch.

  4. Just do a git fetch on your master branch and do a git pull on your master branch to ensure you have your teams latest code.

A Kok
  • 45
  • 1
  • 5
  • The merge from the local master branch to the target branch is a great tip. Deleting and re-pulling the master branch is unnecessary. Just `git reset --hard origin/master` to discard the unpushed change in your master branch. – Matt Hulse May 11 '21 at 18:09
2

A simpler approach, which I have been using (assuming you want to move 4 commits):

git format-patch HEAD~4

(Look in the directory from which you executed the last command for the 4 .patch files)

git reset HEAD~4 --hard

git checkout -b tmp/my-new-branch

Then:

git apply /path/to/patch.patch

In whatever order you wanted.

user1429980
  • 6,872
  • 2
  • 43
  • 53
1

The git reset --hard origin/master solutions don't appeal to me because they unnecessarily mess with the working tree; I prefer

git branch new-work
git switch new-work
git branch --force master origin/master

That (1) creates a new branch pointing at the current commit; (2) makes that branch the current branch (and won't touch the working tree since it's the same commit, since that branch was just created); (3) re-points the master branch back to origin/master, without touching the working tree (as we're no longer on the master branch).

Robert Tupelo-Schneck
  • 10,047
  • 4
  • 47
  • 58
  • This worked great for me. I was actually on a branch (not master) already, but same conceptual steps worked perfectly. Totally agree with the idea of NOT messing with the working tree. – MemeDeveloper Jul 19 '23 at 17:52
0

For me this was the best way:

  1. Check for changes and merge conflicts git fetch
  2. Create a new branch git branch my-changes and push to remote
  3. Change upstream to new created branch git master -u upstream-branch remotes/origin/my-changes
  4. Push your commits to the new upstream branch.
  5. Switch back to previous upstream git branch master --set-upstream-to remotes/origin/master
Sebastian
  • 1,109
  • 4
  • 17
  • 33
0
  1. Checkout fresh copy of you sources

    git clone ........

  2. Make branch from desired position

    git checkout {position} git checkout -b {branch-name}

  3. Add remote repository

    git remote add shared ../{original sources location}.git

  4. Get remote sources

    git fetch shared

  5. Checkout desired branch

    git checkout {branch-name}

  6. Merge sources

    git merge shared/{original branch from shared repository}

John Conde
  • 217,595
  • 99
  • 455
  • 496
0

Simply merge the original local branch in a new one?

git checkout -b new_branch
git merge original_branch
Carlos
  • 1