Move the most recent commit(s) to a new branch with Git

Created 27.10.2009 03:07
Viewed 1.22M times
5480 votes

I'd like to move the last several commits I've committed to master to a new branch and take master back to before those commits were made. Unfortunately, my Git-fu is not strong enough yet, any help?

I.e. How can I go from this

master A - B - C - D - E

to this?

newbranch     C - D - E
             /
master A - B 
4
Comments
Note: I asked the opposite question here by Benjol, 16.12.2010 08:56
eddmann.com/posts/… this one works by Sagar Naliyapara, 25.04.2017 06:35
Were the comments here purged? I ask because during my bimonthly visit to this question, I always scroll by that comment. by Tejas Kale, 19.03.2018 14:02
Side-comment: The question is about a very simple case. Reading the answers and all the "don't do this because..." and "a better solution is..." and "warning with version n+..." just after the answers (possibly when it's too late), it seems to me even very simple operations have no straight solutions in git. A graphical version manager where you would just add a tag for the new branch without dealing with what seems to me obscure and archaic syntax would be such a relief. My kingdom and my gold badges to the first one who "forks" git and starts a new approach ;-) it's urgent. by mins, 15.06.2020 10:57
Answers 15
35
7146

Moving to an existing branch

If you want to move your commits to an existing branch, it will look like this:

git checkout existingbranch
git merge master
git checkout master
git reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work.
git checkout existingbranch

You can store uncommitted edits to your stash before doing this, using git stash. Once complete, you can retrieve the stashed uncommitted edits with git stash pop

Moving to a new branch

WARNING: This method works because you are creating a new branch with the first command: git branch newbranch. If you want to move commits to an existing branch you need to merge your changes into the existing branch before executing git reset --hard HEAD~3 (see Moving to an existing branch above). If you don't merge your changes first, they will be lost.

Unless there are other circumstances involved, this can be easily done by branching and rolling back.

# Note: Any changes not committed will be lost.
git branch newbranch      # Create a new branch, saving the desired commits
git reset --hard HEAD~3   # Move master back by 3 commits (Make sure you know how many commits you need to go back)
git checkout newbranch    # Go to the new branch that still has the desired commits

But do make sure how many commits to go back. Alternatively, you can instead of HEAD~3, simply provide the hash of the commit (or the reference like origin/master) you want to "revert back to" on the master (/current) branch, e.g:

git reset --hard a1b2c3d4

*1 You will only be "losing" commits from the master branch, but don't worry, you'll have those commits in newbranch!

WARNING: With Git version 2.0 and later, if you later git rebase the new branch upon the original (master) branch, you may need an explicit --no-fork-point option during the rebase to avoid losing the carried-over commits. Having branch.autosetuprebase always set makes this more likely. See John Mellor's answer for details.

27.10.2009 03:15
Comments
And in particular, don't try to go back further than the point where you last pushed commits to another repository from which somebody else might have pulled. by Greg Hewgill, 27.10.2009 03:23
Wondering if you can explain WHY this works. To me you're creating a new branch, removing 3 commits from the old branch you are still on, and then checking out the branch you made. So how do the commits you removed magically show up in the new branch? by Jonathan Dumaine, 03.08.2010 18:28
@Jonathan Dumaine: Because I created the new branch before removing the commits from the old branch. They're still there in the new branch. by sykora, 04.08.2010 08:28
I see. Branching makes of clone of the state it is currently in. For some reason I was under the assumption that those commits would not get cloned because they were not on a remote server (too much git-svn haha). Thank you. by Jonathan Dumaine, 04.08.2010 18:44
branches in git are just markers which point to commits in history, there is nothing being cloned, created or deleted (except the markers) by knittl, 16.08.2010 11:32
But then how would you push this to another repository? I get rejected when I do git push after git reset --hard HEAD~3. by andrewrk, 15.12.2010 19:41
I'd change 'git reset --hard HEAD~3' into 'git reset --hard origin/master'. Then you don't have to know how many commits you are ahead of master. It will sync your local master with the master on your server. by Tom Maeckelberghe, 09.06.2011 15:31
Also note: Don't do this with uncommitted changes in your working copy! This just bit me! :( by Adam Tuttle, 25.10.2011 03:59
To take some of the risk out of resetting master back to the wrong commit, this one sets the branch back more precisely. "git reset --hard 1a0c9b", where "1a0c9b" is the start of the commit hash you wish to get back to. by Mazrick, 01.10.2012 23:12
Is there any reason why Mazrick's comment hasn't been upvoted more? This sounds like a far more reliable method than counting the number of commits. by Michael Bylstra, 16.10.2012 23:54
Why is git reset --hard recommended so often? Why not git checkout head~3 . (Note the . or you'll end up on a headless branch)? (of course head~3 could be replaced with alternatives mentioned above). Seems checkout is much safer since it won't destroy your history. Did I miss something? by cboettig, 24.01.2013 13:54
yes you did, most likely people accidentally committed to the wrong branch, they want to HARD remove those commits after putting them on another branch, the other branch will most likely eventually be merged in to the accidentally committed one, after review & testing by Dominic, 04.07.2013 09:23
"If you don't merge your changes first, they will be lost." -- This isn't entirely true. The commits only become detached. Garbage Collection takes a very long time (Something like >1000 commits or so maybe 10000?) So you can still recover them. Either by remembering the SHAs or finding them with git reflog or git fsck --lost-found. More info on lost commits by Sukima, 19.10.2013 14:20
By very long time I mean 30 days, NOT >1000 commits. by Sukima, 19.10.2013 14:34
Why would you do unnecessary checkouts and trash your worktree? git checkout -b newbranch; git branch -f master master~3 (or ...origin/master). by jthill, 19.10.2013 15:05
If I then went to the newbranch and did a git rebase master would the newbranch get that commit removed though? If I used merge instead of rebase would I avoid this? by Dustin, 03.04.2014 16:41
If you want to push master branch then (or original branch your rolled back) use git push --force origin master. In case you don't type force git will reject push and will ask you to pull rolled back commits form remote. by mazikwyry, 07.08.2014 07:30
This is what I used: git push --force -u origin master and then git push -u origin newbranch. by aliteralmind, 06.11.2014 19:44
sykora maybe you want to check out the meta-discussion over there: meta.stackoverflow.com/questions/298067/… - maybe you want to extend your answer to reference the very neat version @aragaer gave below (very underrated contribution as I think) by santa, 30.06.2015 10:29
I'd like to know how to do that with the new GitHub Desktop software, if possible. I know my way around the command line for some things, but git is way too complicated in a text-based environment. Now that an accessible, free, GUI-based official solution is available, I'd like to use that. by Fabio Bracht, 01.09.2015 15:24
Thanks Worked for me very well, only then reset a git stash apply, I changed to the correct branch and apply git stash pop! with that I get my changes and where I corresponded! by NHTorres, 25.11.2015 20:52
After you're dun effin up git push origin oldBranch --force by MLProgrammer-CiM, 10.02.2016 14:47
This answer causes commits to be lost: next time you git rebase, the 3 commits will be silently discarded from newbranch. See my answer for details and safer alternatives. by John Mellor, 06.04.2016 22:47
I tested the procedure with the new branch way. But this doesn't move the commit. as someone pointed out push --force is necessary and will require to re-attach the HEAD to the commit tail. wouldn't it? (sorry if I missed something from the huge amount of comments :) ) by dragonmnl, 06.02.2017 20:50
Following these instructions caused me to lose all the commits I was trying to move. Now I have to do the work all over again. Resetting the commits from master had the effect of changing the fork point of the branch, which had no commits of its own. by Throw Away Account, 18.10.2017 02:31
git push --force -u origin for both master and the new branch should be part of the answer by Andras Hatvani, 17.04.2018 12:40
I followed these instructions and thought I'd lost all my changes, then changed to the new branch and they were there. Kind of blew my mind for a moment until I realized why it worked. I'm dumb. Thanks. by Nathan Beach, 07.06.2018 14:20
worked for me only with creating a new branch. Moving to an existing branch wasn't possible by chainstair, 10.05.2019 14:17
Thats not a good solution, because in the git history it is still visible that you merged from master... by Black, 21.11.2019 14:58
I have used this post so many times because I often accidentally commit to a local master branch and can never remember how to do this. Wish I could upvote ten times. Thanks yet again for saving me... by Nathan Beach, 13.12.2019 15:57
Once you've removed the commits from master you can switch to the last branch using git checkout - by cpinamtz, 16.06.2020 10:56
I rolled back @Rennex's changes, since --keep option keeps the changes in the moved commits as uncommited changes, which is not the desired effect. Instead it makes a lot of mess if you move multiple commits with many changes. If you want to keep uncommited changes, stash them before. by Елин Й., 10.07.2020 08:16
Use "git reset --soft" to keep the changes locally by Martin H., 11.12.2020 16:37
after cherry-pick, one can use the interactive rebase and change pick to drop if you want to remove the commits from the original branch. especially useful if your commits are jumbled up and you only want to move some specific commits. You will have to force push though. by Ayush Mandowara, 13.01.2021 12:10
Incase you are doing this in an existing branch as I did: To be able to rebase head~3 or in IDE(e.g IntelliJ) "rebase to here" It's important NOT to take a pull from remote repo. If you do that, there would be commits from others into your own. I wonder what could be done in that case? Individual commit deletes? Also, I assume OP did not push his changes. If that were done a reset would delete the commits that were already pulled by others, and break their code, wouldn't it? Any good resource to know what could be done if items had been already pushed? by veritas, 01.04.2021 10:12
Show remaining 30 comments
9
1124

For those wondering why it works (as I was at first):

You want to go back to C, and move D and E to the new branch. Here's what it looks like at first:

A-B-C-D-E (HEAD)
        ↑
      master

After git branch newBranch:

    newBranch
        ↓
A-B-C-D-E (HEAD)
        ↑
      master

After git reset --hard HEAD~2:

    newBranch
        ↓
A-B-C-D-E (HEAD)
    ↑
  master

Since a branch is just a pointer, master pointed to the last commit. When you made newBranch, you simply made a new pointer to the last commit. Then using git reset you moved the master pointer back two commits. But since you didn't move newBranch, it still points to the commit it originally did.

22.07.2011 22:37
Comments
@Andrew Without --hard it leaves the changes in the working directory. In that case we can create a new branch and commmit those changes on the new branch. Would this approach remove the need of ensuring that others have not pulled your changes? by HRJ, 04.04.2013 08:18
I also needed to do a git push origin master --force for the change to show up in main repository. by Dženan, 26.11.2014 19:20
Probably a good idea to use --force-with-lease in case someone has already committed based on top of your old chain. by Jacob Wang, 16.11.2015 06:50
This answer causes commits to be lost: next time you git rebase, the 3 commits will be silently discarded from newbranch. See my answer for details and safer alternatives. by John Mellor, 06.04.2016 22:47
@John, that's nonsense. Rebasing without knowing what you're doing causes commits to be lost. If you lost commits, I'm sorry for you, but this answer didn't lose your commits. Note that origin/master doesn't appear in the above diagram. If you pushed to origin/master and then made the changes above, sure, things would go funny. But that's a "Doctor, it hurts when I do this" kind of problem. And it's out of scope for what the original question asked. I suggest you write your own question to explore your scenario instead of hijacking this one. by Ryan Lundy, 07.04.2016 03:20
@Kyralessa: The question asked for newbranch to be based off their existing local branch (which happens to be called master), implying that it should track it as a local upstream. After splitting the 3 commits off into newbranch, if the existing local branch is later changed (e.g. new commits are added locally, or pulled in from origin/master), then git rebase is a correct and reasonable way to incorporate those changes from the existing local branch into newbranch. (Merging is a valid alternative, but leads to more complicated history). by John Mellor, 07.04.2016 12:20
@John, in your answer, you said "Don't do this! git branch -t newbranch". Go back and read the answers again. Nobody suggested doing that. by Ryan Lundy, 07.04.2016 15:02
@Kyralessa, sure, but if you look at the diagram in the question, it's clear that they want newbranch to be based off their existing local master branch. After performing the accepted answer, when the user gets around to running git rebase in newbranch, git will remind them that they forgot to set the upstream branch, so they'll run git branch --set-upstream-to=master then git rebase and have the same problem. They may as well use git branch -t newbranch in the first place. by John Mellor, 07.04.2016 15:32
@JohnMellor, you are correct that calling git rebase with no arguments enables the --fork-point option, which can cause commits to be lost. In my opinion this is a defect in git rebase. It has nothing to do with my answer above, though. There are many circumstances in which git rebase with no arguments can cause trouble. The moral of the story (unfortunately) is not to call git rebase without passing in a branch, even if that branch is already the upstream. Git's usability has improved quite a lot in the past eight years or so, but this is one area wherre it could use more work. by Ryan Lundy, 01.06.2018 11:54
Show remaining 4 comments
19
506

In General...

The method exposed by sykora is the best option in this case. But sometimes is not the easiest and it's not a general method. For a general method use git cherry-pick:

To achieve what OP wants, its a 2-step process:

Step 1 - Note which commits from master you want on a newbranch

Execute

git checkout master
git log

Note the hashes of (say 3) commits you want on newbranch. Here I shall use:
C commit: 9aa1233
D commit: 453ac3d
E commit: 612ecb3

Note: You can use the first seven characters or the whole commit hash

Step 2 - Put them on the newbranch

git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233

OR (on Git 1.7.2+, use ranges)

git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233

git cherry-pick applies those three commits to newbranch.

07.02.2012 16:58
Comments
if the cherry picking is the last N commits then you can just move your master HEAD back w/ git reset --hard HEAD~N by Mike Graf, 07.12.2013 04:01
This works very well if you accidentally commit the wrong, non-master branch, when you should have created a new feature branch. by julianc, 27.02.2014 16:47
The information on git cherry-pick is nice, but the commands in this post don't work. 1) the 'git checkout newbranch' should be 'git checkout -b newbranch' since newbranch doesn't already exist; 2) if you checkout newbranch from the existing master branch it ALREADY has those three commits included in it, so there's no use in picking them. At the end of the day to get what the OP wanted, you'll still have to do some form of reset --hard HEAD. by JESii, 24.05.2014 09:08
+1 for a useful approach in some situations. This is good if you only want to pull your own commits (which are interspersed with others) into a new branch. by Tyler V., 01.10.2014 17:11
It's better answer. This way you can move commits to any branch. by skywinder, 05.11.2014 08:32
Is the order of cherry picking important? by kon psych, 23.04.2015 22:04
Didn't work for me. Error message, "nothing added to commit". Previous cherry-pick is now empty. Allow empty or continue. In any case, it didn't seem to work, commits didn't get moved over. by David, 02.09.2015 15:45
I was not able to use the cherry-pick range, got "error: could not apply c682e4b..." on one of the commits, but when used cherry-pick on each commit one at a time, it worked fine (I had to do git cherry-pick --abort after the error). Commenting because I thought those would be equivalent. This is on git version 2.3.8 (Apple Git-58) by Hudson, 31.10.2015 14:33
For the refs, you can usually use 3-5 hexits; however many it takes Git to uniquely identify the commit. Also, you can use master~3..master to refer to the last 3 commits on master instead of hashes. by Zaz, 16.04.2016 13:56
@konpsych I believe the order is important. You want to do the oldest commits first. by Kevin, 28.12.2016 20:00
The second cherry-pick example is incorrect. 612ecb3..9aa1233 means all commits after but excluding 612ecb3 up to and including 9aa1233. To get all three commits, you need cherry-pick 612ecb3~1..9aa1233. by Jason R. Coombs, 12.08.2017 14:54
Right @JasonR.Coombs I have edited the example. Thanks by Ivan, 28.02.2018 18:14
Doesn't cherry picking create new commits? Why would you do this when you can simply create a new branch from master and reset master's head back. It would be much faster and cheaper operation that way. by MIWMIB, 06.11.2018 22:16
This works great if you want to reply the commits ontop of a existing branch instead of creating a new one with the misplaced commits on. A situation I've sometimes gotten myself into when working on many features in parallel. by Hultner, 05.04.2019 15:06
These failed for me, but doing them in the "right" order, ie. in the chronological order of commits, worked for me: git cherry-pick 9aa1233 453ac3d 612ecb3 by PhiLho, 01.10.2019 14:54
This is an amazing thing that I somehow didn't know up until now. Thank you, my good Sir. by Shashank Saxena, 05.02.2020 11:51
I actually wanted to move commits to a branch that is actually many commits behind the current branch. This answer helps in that case. by Mihir, 01.03.2020 15:07
Once you copy them with cherry-pick, what's the best way to remove them from their original branch? I did it via dropping the commits thru git rebase -i but wondering if there is a preferred way. by sparc_spread, 14.07.2020 16:53
@PhiLho Thanks! I used your syntax - a little more explicit, a little safer by Dogweather, 03.08.2020 04:03
Show remaining 14 comments
23
371

Most previous answers are dangerously wrong!

Do NOT do this:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

As the next time you run git rebase (or git pull --rebase) those 3 commits would be silently discarded from newbranch! (see explanation below)

Instead do this:

git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
  • First it discards the 3 most recent commits (--keep is like --hard, but safer, as fails rather than throw away uncommitted changes).
  • Then it forks off newbranch.
  • Then it cherry-picks those 3 commits back onto newbranch. Since they're no longer referenced by a branch, it does that by using git's reflog: HEAD@{2} is the commit that HEAD used to refer to 2 operations ago, i.e. before we 1. checked out newbranch and 2. used git reset to discard the 3 commits.

Warning: the reflog is enabled by default, but if you've manually disabled it (e.g. by using a "bare" git repository), you won't be able to get the 3 commits back after running git reset --keep HEAD~3.

An alternative that doesn't rely on the reflog is:

# newbranch will omit the 3 most recent commits.
git checkout -b newbranch HEAD~3
git branch --set-upstream-to=oldbranch
# Cherry-picks the extra commits from oldbranch.
git cherry-pick ..oldbranch
# Discards the 3 most recent commits from oldbranch.
git branch --force oldbranch oldbranch~3

(if you prefer you can write @{-1} - the previously checked out branch - instead of oldbranch).


Technical explanation

Why would git rebase discard the 3 commits after the first example? It's because git rebase with no arguments enables the --fork-point option by default, which uses the local reflog to try to be robust against the upstream branch being force-pushed.

Suppose you branched off origin/master when it contained commits M1, M2, M3, then made three commits yourself:

M1--M2--M3  <-- origin/master
         \
          T1--T2--T3  <-- topic

but then someone rewrites history by force-pushing origin/master to remove M2:

M1--M3'  <-- origin/master
 \
  M2--M3--T1--T2--T3  <-- topic

Using your local reflog, git rebase can see that you forked from an earlier incarnation of the origin/master branch, and hence that the M2 and M3 commits are not really part of your topic branch. Hence it reasonably assumes that since M2 was removed from the upstream branch, you no longer want it in your topic branch either once the topic branch is rebased:

M1--M3'  <-- origin/master
     \
      T1'--T2'--T3'  <-- topic (rebased)

This behavior makes sense, and is generally the right thing to do when rebasing.

So the reason that the following commands fail:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

is because they leave the reflog in the wrong state. Git sees newbranch as having forked off the upstream branch at a revision that includes the 3 commits, then the reset --hard rewrites the upstream's history to remove the commits, and so next time you run git rebase it discards them like any other commit that has been removed from the upstream.

But in this particular case we want those 3 commits to be considered as part of the topic branch. To achieve that, we need to fork off the upstream at the earlier revision that doesn't include the 3 commits. That's what my suggested solutions do, hence they both leave the reflog in the correct state.

For more details, see the definition of --fork-point in the git rebase and git merge-base docs.

06.04.2016 22:38
Comments
I think I'd be more satisfied with this answer if you could demonstrate what the "wrong" state of reflog would be as opposed to the right state in this circumstance. by Makoto, 06.04.2016 22:49
In your scenario, if I keep M2 and M3 in my topic branch and then try to merge my topic branch back into master, then don't I reintroduce the edits someone else tried to delete (i.e. M2)? It would seem that this is undesired behavior, no? To me it makes sense that if someone is going to re-write history, you will want your topic branch to track the new history so you don't un-re-write history when you merge. by Gordon Bean, 06.04.2016 23:27
This answer says "Do NOT do this!" above something that no one suggested doing. by Ryan Lundy, 16.04.2016 21:41
Is there any way to fix it if I already did this method stackoverflow.com/a/1628584/2423194 and doing git push --force to delete the commits that I already moved to the new branch? by Rock Lee, 18.05.2016 20:42
Most people don't rewrite published history, especially on master. So no, they are not dangerously wrong. by Walf, 14.09.2016 06:59
@Makoto, after performing the "Do NOT do this" steps git reflog newbranch will have a single entry db8f2fe newbranch@{0}: branch: Created from oldbranch where db8f2fe is the hash of T3, and more importantly git reflog oldbranch will list the exact hashes of T1, T2 and T3. Together, it's clear that commits T1,T2,T3 were already part of oldbranch when newbranch forked off from it, hence they will be unexpectedly removed from newbranch by the next git rebase. Hence also git merge-base --fork-point newbranch oldbranch will fail to find the fork-point (instead of printing M3's hash). by John Mellor, 16.09.2016 02:32
@Makoto, if instead you perform either of my alternative steps, git reflog newbranch will start with 4b242c2 newbranch@{3}: branch: Created from HEAD where 4b242c2 is the hash of M3, followed by 3 entries for the cherry-picks. And git reflog oldbranch will list T1, T2 and T3, but they will have different hashes there than they do in newbranch, since they were copied to newbranch by cherry-picking (in particular, cherry-picking updates the timestamp - assuming it has been longer than a second since the commit was created - causing it to get a new hash even if it's a fast-forward). by John Mellor, 16.09.2016 02:32
@GordonBean, yes, it is good that git rebase removes M2 from your topic branch when M2 is removed by rewriting from origin/master. It just means you have to be careful in this particular case (splitting existing commits from a local branch onto a dependent local branch), since git can't distinguish the two cases. by John Mellor, 16.09.2016 02:33
@Kyralessa, the -t you are referring to in git branch happens implicitly if you have git config --global branch.autosetuprebase always set. Even if you don't, I already explained to you that the same problem occurs if you setup tracking after performing these commands, as the OP likely intends to do given their question. by John Mellor, 16.09.2016 02:36
@RockLee, yes, the general the way to fix such situations is to create a fresh branch (newbranch2) from a safe starting point then cherry-pick all the commits you want to keep (from badnewbranch to newbranch2). Cherry-picking will give the commits new hashes, so you'll be able to safely rebase newbranch2 (and can now delete badnewbranch). by John Mellor, 16.09.2016 02:36
@Walf, you misunderstood: git rebase is designed to be robust against upstreams having their history rewritten. Unfortunately, the side-effects of that robustness affect everyone, even if neither they nor their upstream ever rewrite history. by John Mellor, 16.09.2016 02:37
Okay, I do prefer the cherry-pick method since it's impossible to lose the commits (always visible in git log). However, if history is never rewritten, how does this lost commit situation occur? by Walf, 16.09.2016 07:04
Note: to check which commits to include, you can use git reflog which gives the HEAD@{xxx} reference. by Dan Smart, 06.10.2016 15:14
Considering that many folks have autosetuprebase always set, your warnings are quite wecome. Thanks John Mellor. @sykora and Kyralessa, I think its worth putting a note on the accepted answer about when it is dangerous (always if autosetuprebase is set in your .gitconfig, which many folks do!). There's at least one comment up above from Rock Lee who followed the accepted answer and appears to have been burned. by aggieNick02, 14.01.2017 00:25
@Walf, history is rewritten in this case by git reset --keep. So you're rewriting history yourself in this example by dropping commits, and that's how they're "lost" when rebasing. by mgiuffrida, 24.03.2017 01:11
You can also use --no-fork-point when you run git rebase. by torek, 24.03.2017 01:43
The danger that is being avoided here is that commits on newbranch will be lost in a rebase. If newbranch is merged into oldbranch before a rebase happens, is that still a danger? by Nathan Hinchey, 16.06.2017 17:51
I've tried this with to put a merge on another branch, but then the cherrypick doesn't choose the other commits in the merge (only the top one), at least I think so. It seems however that after the reset, I can just "git checkout <commit>" (with the commit obtained from the reflog), and then create a new branch from there. whhat do you think? by krthie, 06.09.2017 09:52
if you use the cherry-pick method, you need to then reset the remote & merge of newbranch. I don't know the git commands to do that, I find it easier to just edit .git/config by Michael Johnston, 22.09.2017 23:57
Too bad this isn't the accepted answer. I followed the steps in the accepted answer and lost 6 commits, just as you described! by Throw Away Account, 18.10.2017 20:02
Instead of using the relative revision format, just take the current HEAD revision e.g. aaaaaa, the new HEAD for master bbbbbb, and the revision after bbbbbb, e.g. cccccc, then do git reset --keep bbbbbb ; git checkout -t -b newbranch ; git cherry-pick cccccc..aaaaaa by char101, 15.11.2017 07:39
Your 'alternative that doesn't rely on the reflog' does not interoperate with git push: it gives errors no matter which branch I try to push to remote repo: either old or new. Thumbs down by Eugene Gr. Philippov, 05.07.2018 09:27
@Eugene Gr. Philippov Same here, git kept telling me that I need to git push - which in turn tells me that everything is up to date already and consequently does nothing. Next, git tells me to git push, cycle repeats. I am sure it is named "git" for a reason. I had to use git push origin to coerce cooperation. by Klaws, 02.12.2019 13:21
Show remaining 18 comments
16
351

Yet another way to do this, using just 2 commands. Also keeps your current working tree intact.

git checkout -b newbranch # switch to a new branch
git branch -f master HEAD~3 # make master point to some older commit

Old version - before I learned about git branch -f

git checkout -b newbranch # switch to a new branch
git push . +HEAD~3:master # make master point to some older commit 

Being able to push to . is a nice trick to know.

26.03.2014 08:13
Comments
Current directory. I guess this would work only if you are in a top directory. by aragaer, 28.03.2014 05:35
The local push is grin-inducing, but on reflection, how is it different to git branch -f here? by jthill, 05.08.2014 00:15
@GerardSexton . is current director. git can push to REMOTES or GIT URLs. path to local directory is supported Git URLs syntax. See the GIT URLS section in git help clone. by weakish, 25.11.2014 10:24
I don't know why this is not rated higher. Dead simple, and without the small but potential danger of git reset --hard. by Godsmith, 06.02.2015 14:56
@Godsmith My guess is people prefer three simple commands to two slightly more obscure commands. Also, top voted answers get more upvotes by nature of being displayed first. by JS_Riddler, 22.10.2015 15:43
I agree with @Godsmith! Why isn't this rated higher? The part about forcing master to point to a former commit is definitely the best option I have seen in here. Thank you @aragaer by hasse, 25.11.2015 15:48
Great answer! You can also use @~3 in place of HEAD~3. by Zaz, 16.04.2016 13:58
Worked for me… But can one of you explain you prefer this to the reset --hard method? Thanks! by henry, 10.07.2016 18:42
It doesn't alter the working tree at all - whatever modifications you have made to your code stay untouched - you don't even have to commit anything. by aragaer, 11.07.2016 07:41
I agree, very simple. And again as @Dženan said in previous answer don't forget to do git push origin master --force do save the changes remotely too. by Rotem, 09.12.2018 06:01
Here is the link to git branch command git-scm.com/docs/git-branch. And to clarify, with the command git branch -f master HEAD~3, it doesn't have to be master. by rince, 16.01.2019 20:18
A small warning: git branch -f master <something> will reset the tracking information on the master branch. So if you had set the upstream of master to anything other than origin/master, that setting will now be lost. by joeytwiddle, 26.09.2019 10:50
+1 this perfectly worked for me after I additionally did git push origin master --force and git push --set-upstream origin <newbranch> by Huioon Kim, 03.12.2019 03:56
Worked great for me. Did have to git push origin ___ --force - actually I was a develop branch and it worked great on there by Prasanth, 29.07.2020 14:54
git push --force is necessary to replicate at the repo by Tarmac, 06.04.2021 20:43
+ in front of refspec works as --force for that refspec. by aragaer, 07.04.2021 08:36
Show remaining 11 comments
9
202

Much simpler solution using git stash

Here's a far simpler solution for commits to the wrong branch. Starting on branch master that has three mistaken commits:

git reset HEAD~3
git stash
git checkout newbranch
git stash pop

When to use this?

  • If your primary purpose is to roll back master
  • You want to keep file changes
  • You don't care about the messages on the mistaken commits
  • You haven't pushed yet
  • You want this to be easy to memorize
  • You don't want complications like temporary/new branches, finding and copying commit hashes, and other headaches

What this does, by line number

  1. Undoes the last three commits (and their messages) to master, yet leaves all working files intact
  2. Stashes away all the working file changes, making the master working tree exactly equal to the HEAD~3 state
  3. Switches to an existing branch newbranch
  4. Applies the stashed changes to your working directory and clears the stash

You can now use git add and git commit as you normally would. All new commits will be added to newbranch.

What this doesn't do

  • It doesn't leave random temporary branches cluttering your tree
  • It doesn't preserve the mistaken commit messages, so you'll need to add a new commit message to this new commit
  • Update! Use up-arrow to scroll through your command buffer to reapply the prior commit with its commit message (thanks @ARK)

Goals

The OP stated the goal was to "take master back to before those commits were made" without losing changes and this solution does that.

I do this at least once a week when I accidentally make new commits to master instead of develop. Usually I have only one commit to rollback in which case using git reset HEAD^ on line 1 is a simpler way to rollback just one commit.

Don't do this if you pushed master's changes upstream

Someone else may have pulled those changes. If you are only rewriting your local master there's no impact when it's pushed upstream, but pushing a rewritten history to collaborators can cause headaches.

01.05.2018 07:50
Comments
Thanks, am so glad I read past/through so much to get to here, cause it's a pretty common use case for me as well. Are we so atypical? by Jim Mack, 10.09.2018 17:58
I think we're totally typical and "oops I commited to master by mistake" is the most common use-case for need to revert a handful or less of commits. Lucky this solution is so simple I have it memorized now. by Slam, 11.09.2018 19:58
This should be the accepted answer. It's straightforward, easy to understand and easy to remember by Sina Madani, 28.11.2018 00:53
It can be a solution but it does not do exactly what was asked because it loses the last 3 commits. by Jean Paul, 24.06.2019 16:17
OP did not specific the commit messages had to be preserved. No code is lost. by Slam, 16.08.2019 00:54
I don't event think the stashing is necessary. I just did it without and worked well. by A Campos, 20.08.2019 11:40
You can easily get your commit messages back, too, if you happen have them in your CLI (command line) history. I happened to have both the git add and git commit commands that I used so all I had to do was hit up arrow and enter a few times and boom! Everything was back, but on the right branch now. by Luke Gedeon, 19.09.2019 14:57
Thanks, it worked perfectly for me, because I only want to move the last commit, without the previous commits, to the other branch. Basically I made the mistake of committing to master instead of develop and master is ahead of develop with changes that I am not interested in merge right now. by quarac, 19.12.2019 13:06
To keep the commit messages too, you can just to git log before and copy them to a text file. Then later add all of that to the new message :) by ARK, 31.01.2020 16:12
Show remaining 4 comments
2
31

This doesn't "move" them in the technical sense but it has the same effect:

A--B--C  (branch-foo)
 \    ^-- I wanted them here!
  \
   D--E--F--G  (branch-bar)
      ^--^--^-- Opps wrong branch!

While on branch-bar:
$ git reset --hard D # remember the SHAs for E, F, G (or E and G for a range)

A--B--C  (branch-foo)
 \
  \
   D-(E--F--G) detached
   ^-- (branch-bar)

Switch to branch-foo
$ git cherry-pick E..G

A--B--C--E'--F'--G' (branch-foo)
 \   E--F--G detached (This can be ignored)
  \ /
   D--H--I (branch-bar)

Now you won't need to worry about the detached branch because it is basically
like they are in the trash can waiting for the day it gets garbage collected.
Eventually some time in the far future it will look like:

A--B--C--E'--F'--G'--L--M--N--... (branch-foo)
 \
  \
   D--H--I--J--K--.... (branch-bar)
19.10.2013 14:12
Comments
Can't you use rebase for the same thing? by Bergi, 29.06.2015 17:37
Yes you could alternatively use rebase on the detached branch in the scenario above. by Sukima, 30.06.2015 01:32
4
25

To do this without rewriting history (i.e. if you've already pushed the commits):

git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>

Both branches can then be pushed without force!

21.01.2016 16:10
Comments
But then you have to deal with the revert scenario, which, depending on your circumstance, can be a lot trickier. If you revert a commit on the branch, Git will still see those commits as have taken place, so in order to undo that, you have to revert the revert. This burns quite a few people, especially when they revert a merge and try to merge the branch back, only to find that Git believes that it's already merged that branch in (which is entirely true). by Makoto, 07.04.2016 16:15
That's why I cherry-pick the commits at the end, onto a new branch. That way git sees them as new commits, which solves your issue. by teh_senaus, 07.04.2016 17:18
This is more dangerous than it first seems, since you're changing the state of the repository's history without really understanding the implications of this state. by Makoto, 07.04.2016 19:25
I don't follow your argument - the point of this answer is that you're not changing history, simply adding new commits (which effectively undo the redo the changes). These new commits can be pushed and merged as normal. by teh_senaus, 08.04.2016 10:33
2
15

Simplest way to do this:

1. Rename master branch to your newbranch (assuming you are on master branch):

git branch -m newbranch

2. Create master branch from the commit that you wish:

git checkout -b master <seven_char_commit_id>

e.g. git checkout -b master a34bc22

21.03.2020 07:24
Comments
Love this solution, because you do not have to rewrite the git commit title/description. by Vulpo, 12.06.2020 08:34
Doesn't this mess up the remote upstream branches? Isn't newbranch now pointing to origin/master? by kraxor, 30.04.2021 15:46
0
13

Had just this situation:

Branch one: A B C D E F     J   L M  
                       \ (Merge)
Branch two:             G I   K     N

I performed:

git branch newbranch 
git reset --hard HEAD~8 
git checkout newbranch

I expected that commit I would be the HEAD, but commit L is it now...

To be sure to land on the right spot in the history its easier to work with the hash of the commit

git branch newbranch 
git reset --hard #########
git checkout newbranch
20.09.2013 10:17
2
12

How can I go from this

A - B - C - D - E 
                |
                master

to this?

A - B - C - D - E 
    |           |
    master      newbranch

With two commands

  • git branch -m master newbranch

giving

A - B - C - D - E 
                |
                newbranch

and

  • git branch master B

giving

A - B - C - D - E
    |           |
    master      newbranch
16.05.2019 12:00
Comments
Yep, this works and is quite easy. Sourcetree GUI is a little confused about the changes made in the git shell, but after a fetch it's all right again. by lars k., 24.05.2019 15:05
Yes, they are as in the question. The first couple of diagrams are intended to be equivalent to those in the question, just redrawn the way I would like for the purpose of illustration in the answer. Basically rename the master branch as newbranch and create a new master branch where you want it. by Ivan, 16.07.2019 15:22
0
7

If you just need to move all your unpushed commits to a new branch, then you just need to,

  1. create a new branch from the current one :git branch new-branch-name

  2. push your new branch: git push origin new-branch-name

  3. revert your old(current) branch to the last pushed/stable state: git reset --hard origin/old-branch-name

Some people also have other upstreams rather than origin, they should use appropriate upstream

17.06.2019 07:38
0
3

You can do this is just 3 simple step that i used.

1) make new branch where you want to commit you recent update.

git branch <branch name>

2) Find Recent Commit Id for commit on new branch.

git log

3) Copy that commit id note that Most Recent commit list take place on top. so you can find your commit. you also find this via message.

git cherry-pick d34bcef232f6c...

you can also provide some rang of commit id.

git cherry-pick d34bcef...86d2aec

Now your job done. If you picked correct id and correct branch then you will success. So before do this be careful. else another problem can occur.

Now you can push your code

git push

25.05.2018 14:14
0
3

1) Create a new branch, which moves all your changes to new_branch.

git checkout -b new_branch

2) Then go back to old branch.

git checkout master

3) Do git rebase

git rebase -i <short-hash-of-B-commit>

4) Then the opened editor contains last 3 commit information.

...
pick <C's hash> C
pick <D's hash> D
pick <E's hash> E
...

5) Change pick to drop in all those 3 commits. Then save and close the editor.

...
drop <C's hash> C
drop <D's hash> D
drop <E's hash> E
...

6) Now last 3 commits are removed from current branch (master). Now push the branch forcefully, with + sign before branch name.

git push origin +master
01.06.2018 05:40
0
1

Most of the solutions here count the amount of commits you'd like to go back. I think this is an error prone methodology. Counting would require recounting.

You can simply pass the commit hash of the commit you want to be at HEAD or in other words, the commit you'd like to be the last commit via:

(Notice see commit hash)

To avoid this:

1) git checkout master

2) git branch <feature branch> master

3) git reset --hard <commit hash>

4) git push -f origin master
24.11.2020 21:49