1943

In one of my development branches, I made some changes to my codebase. Before I was able to complete the features I was working on, I had to switch my current branch to master to demo some features. But just using a "git checkout master" preserved the changes I also made in my development branch, thus breaking some of the functionality in master. So what I did was commit the changes on my development branch with a commit message "temporary commit" and then checkout master for the demo.

Now that I'm done with the demo and back to work on my development branch, I would like to remove the "temporary commit" that I made while still preserving the changes I made. Is that possible?

Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
tanookiben
  • 22,575
  • 8
  • 27
  • 25
  • 104
    Next time: [`git stash`](http://git-scm.com/book/en/Git-Tools-Stashing) – Matt Ball Apr 02 '13 at 18:57
  • 86
    @MattBall, not necessarily. While `git stash` is a good tool, "work in progress" throw-away commits are quite a legitimate device, too. – kostix Apr 02 '13 at 21:03
  • 4
    This is a great resource strait from Github: [How to undo \(almost\) anything with Git](https://github.com/blog/2019-how-to-undo-almost-anything-with-git) – jasonleonhard Feb 03 '17 at 20:55
  • 26
    @MattBall @kostix Yeah, stash is particularly ill-suited for "long-term" stashing, given that it's one global stack for the whole repo. I want to be able to stash changes on a branch and then go to other branches, do whatever else, come back days later without worrying that I might have used `git stash` on some other branch in the interim. – alecbz Dec 19 '17 at 18:37
  • 13
    One thing worth noting about `stash` is that it is completely local and will be prone to code loss from repo-deletion or recreation or, hardware failure or loss. IMO, it really should be used only for very short term WIP. Love a WIP commit before going on holiday :P .. call it a brain dump commit! – ϹοδεMεδιϲ Jul 23 '18 at 06:41
  • 2
    I would just `git checkout -b temp-need-to-run-demo-now` a temporary branch immediately, and then `git commit` but of course this is not meant to be an answer :) – user776686 Apr 07 '19 at 09:53
  • A supplementary trick when you make temporary commits and branches is to set the push-url to an invalid value to avoid accidentally pushing: `git remote set-url --push origin nopush` – Åsmund Jan 17 '22 at 15:15
  • @kostix not for branches other people are working on. You can always just create a new branch with those changes, keep it local or push it to a remote somewhere if you want to. AFAIK no hosted solution charges based on number of branches. – Aspiring Dev Apr 07 '22 at 13:37

13 Answers13

2979

It's as simple as this:

git reset HEAD^

Note: some shells treat ^ as a special character (for example some Windows shells or ZSH with globbing enabled), so you may have to quote "HEAD^" or use HEAD~1 in those cases.

git reset without a --hard or --soft moves your HEAD to point to the specified commit, without changing any files. HEAD^ refers to the (first) parent commit of your current commit, which in your case is the commit before the temporary one.

Note that another option is to carry on as normal, and then at the next commit point instead run:

git commit --amend [-m … etc]

which will instead edit the most recent commit, having the same effect as above.

Note that this (as with nearly every git answer) can cause problems if you've already pushed the bad commit to a place where someone else may have pulled it from. Try to avoid that

Svish
  • 152,914
  • 173
  • 462
  • 620
Gareth
  • 133,157
  • 36
  • 148
  • 157
  • 71
    This is by far the simplest, but if you've already pushed your commit to a remote and someone else pulled it, I'd be very hesitant to do anything except apologize. – atw13 Apr 02 '13 at 19:00
  • 49
    I get `More?` after doing this. Whatever I type in at that prompt gives me `fatal: ambiguous argument 'HEADwhateverItypedIn': unknown revision or path not in the working tree.` – DaAwesomeP Jan 27 '15 at 03:20
  • 72
    @DaAwesomeP sounds like you're using a shell which treats `^` as a special character. You could either quote the reference `"HEAD^"`, or use the alternative syntax `HEAD~1` unquoted – Gareth Jan 27 '15 at 10:58
  • @Gareth just to note, this is on Windows 8.1 in the normal Command Prompt (not the git bash/shell that is also included). – DaAwesomeP Jan 29 '15 at 04:56
  • 14
    Worked for me, had to escape character though `git reset HEAD\^` – cevaris Feb 02 '15 at 20:05
  • 3
    On Windows I had to use `git reset "HEAD^"` – serverpunk Mar 29 '16 at 16:09
  • 4
    Damn! i did not read the comments and did `git reset parent commit`, after reading `"HEAD^ refers to the (first) parent commit of your current commit, which in your case is the commit before the temporary one."` and lost all my changes. luckily they werent a lot. – Polynomial Proton Jul 15 '16 at 17:04
  • In the case that this doesn't work, see http://stackoverflow.com/questions/14203952/git-reset-asks-more – danwellman Oct 10 '16 at 07:54
  • 1
    @TheUknown what do you mean lost? This doesn't loose any of your changes? You can just go back to the commit you reset? (after 30+ days it can be deleted though if no branch points to it or any of it's children) – Johny Skovdal Dec 06 '16 at 22:04
  • 9
    @kostix FWIW that link has changed to https://git-scm.com/book/en/v2/Git-Tools-Reset-Demystified – hughjdavey Oct 10 '18 at 10:55
  • 1
    If you're like me and forgot to grab a copy of the commit message, the commit actually still exists and you can do `git show ` to see and copy your awesome commit message. (In my case for moving the commit out of one branch and into another.) – cedricdlb Oct 26 '18 at 00:02
  • 1
    For anyone doing this to remote, you can do a `git push -f ` afterwards to remove the commit from the remote – Garth Oct 25 '19 at 11:22
  • With the Windows default shell command ("cmd.exe"), you need to use double ^ characters: "git reset HEAD^^" This is because Windows cmd uses ^ as escape character. Therefore the first ^ escapes the second ^ Hence, the ^ as the last non-space character in the line, only escapes the newline character, resulting in 'More?'. So in Windows cmd "git reset HEAD^^^^" is the equivalent to bash "git reset HEAD^^" – TheUnseen May 11 '22 at 16:51
  • In my case, I just wanted to revert the changes to a single file, but following this method I could not commit unless I did a forced push (which sounded a bit too sketchy for me). The answer that helped me the most was: https://stackoverflow.com/a/22041320/13281260 – roomrys Feb 13 '23 at 22:22
  • 1
    @roomrys That makes sense! If you only make _new_ commits (even ones that reverse previous changes) then you won't need to force push them. That sounds different to this question which is specifically trying to "un-commit" a commit they made that they don't want to keep. – Gareth Feb 16 '23 at 10:23
345

There are two ways of handling this. Which is easier depends on your situation.

Reset

If the commit you want to get rid of was the last commit, and you have not done any additional work you can simply use git-reset

git reset HEAD^

Takes your branch back to the commit just before your current HEAD. However, it doesn't actually change the files in your working tree. As a result, the changes that were in that commit show up as modified - its like an 'uncommit' command. In fact, I have an alias to do just that.

git config --global alias.uncommit 'reset HEAD^'

Then you can just used git uncommit in the future to back up one commit.

Squashing

Squashing a commit means combining two or more commits into one. I do this quite often. In your case you have a half done feature commited, and then you would finish it off and commit again with the proper, permanent commit message.

git rebase -i <ref>

I say above because I want to make it clear this could be any number of commits back. Run git log and find the commit you want to get rid of, copy its SHA1 and use it in place of <ref>. Git will take you into interactive rebase mode. It will show all the commits between your current state and whatever you put in place of <ref>. So if <ref> is 10 commits ago, it will show you all 10 commits.

In front of each commit, it will have the word pick. Find the commit you want to get rid of and change it from pick to fixup or squash. Using fixup simply discards that commits message and merges the changes into its immediate predecessor in the list. The squash keyword does the same thing, but allows you to edit the commit message of the newly combined commit.

Note that the commits will be re-committed in the order they show up on the list when you exit the editor. So if you made a temporary commit, then did other work on the same branch, and completed the feature in a later commit, then using rebase would allow you to re-sort the commits and squash them.

WARNING:

Rebasing modifies history - DONT do this to any commits you have already shared with other developers.

Stashing

In the future, to avoid this problem consider using git stash to temporarily store uncommitted work.

git stash save 'some message'

This will store your current changes off to the side in your stash list. Above is the most explicit version of the stash command, allowing for a comment to describe what you are stashing. You can also simply run git stash and nothing else, but no message will be stored.

You can browse your stash list with...

git stash list

This will show you all your stashes, what branches they were done on, and the message and at the beginning of each line, and identifier for that stash which looks like this stash@{#} where # is its position in the array of stashes.

To restore a stash (which can be done on any branch, regardless of where the stash was originally created) you simply run...

git stash apply stash@{#}

Again, there # is the position in the array of stashes. If the stash you want to restore is in the 0 position - that is, if it was the most recent stash. Then you can just run the command without specifying the stash position, git will assume you mean the last one: git stash apply.

So, for example, if I find myself working on the wrong branch - I may run the following sequence of commands.

git stash
git checkout <correct_branch>
git stash apply

In your case you moved around branches a bit more, but the same idea still applies.

starball
  • 20,030
  • 7
  • 43
  • 238
eddiemoya
  • 6,713
  • 1
  • 24
  • 34
  • 6
    `git config --global alias.uncommit reset HEAD^` just aliases uncommit to reset. Instead, do `git config --global alias.uncommit 'reset HEAD^'` – mernst Apr 14 '15 at 23:23
  • 5
    Note that you will have to use ^^ instead of ^ if using in a windows command prompt. – Pramod B R Apr 01 '19 at 13:55
277

I think you are looking for this

git reset --soft HEAD~1

It undoes the most recent commit whilst keeping the changes made in that commit to staging.

myselfmiqdad
  • 2,518
  • 2
  • 18
  • 33
Sudhanshu Jain
  • 2,941
  • 1
  • 9
  • 4
  • 16
    Thanks. This worked for me. Calling `git reset HEAD^` on Windows just prompts "More?" - whatever that means – Tyron Oct 21 '18 at 11:00
  • 21
    @Tyron `^` is an escape character in DOS. When paired with a new line, it serves as a continuation prompt for the preceding command. Typing `git reset HEAD^^` should work on Windows. – trk Jan 09 '19 at 14:16
122

Yes, you can delete your commit without deleting the changes:

git reset @~
vallentin
  • 23,478
  • 6
  • 59
  • 81
DURGESH
  • 2,373
  • 1
  • 15
  • 13
88

You're looking for either git reset HEAD^ --soft or git reset HEAD^ --mixed.

There are 3 modes to the reset command as stated in the docs:

git reset HEAD^ --soft

undo the git commit. Changes still exist in the working tree(the project folder) + the index (--cached)

git reset HEAD^ --mixed

undo git commit + git add. Changes still exist in the working tree

git reset HEAD^ --hard

Like you never made these changes to the codebase. Changes are gone from the working tree.

Bar Horing
  • 5,337
  • 3
  • 30
  • 26
28

2020 Simple way :

git reset <commit_hash>

(The commit hash of the last commit you want to keep).

If the commit was pushed, you can then do :

git push -f

You will keep the now uncommitted changes locally

Xys
  • 8,486
  • 2
  • 38
  • 56
  • 4
    When you say this is the '2020 Simple way' is that implying this method wasn't possible in previous versions? If so which version of Git is required to use this method? – tomhughes Nov 24 '20 at 13:20
13

In my case, I already pushed to the repo. Ouch!

You can revert a specific commit while keeping the changes in your local files by doing:

git revert -n <sha>

This way I was able to keep the changes which I needed and undid a commit which had already been pushed.

Wim Feijen
  • 788
  • 7
  • 9
  • 4
    In my case, I needed to revert something i already pushed to the remote repository. Even more ouch! Best way was to `git revert bad-commit-sha`, then `git revert -n revert-commit-just-created-sha` and then repair from there. You got me halfway. Thanks! – TinkerTenorSoftwareGuy Jul 20 '17 at 22:39
  • This seems to do the opposite, no? It creates new changes which correspond to a revert of the changes done in the selected commit. If you were to commit these new changes, you will have undone the work you wanted actually to keep. – svaens Apr 27 '18 at 08:56
  • 1
    Hence the `-n` option, which leaves the changes that the reversion would do staged but not committed. Then you can pick and choose which of those reversion changes to keep or discard – youngmit Jun 15 '20 at 17:16
12

For those using zsh, you'll have to use the following:

git reset --soft HEAD\^

Explained here: https://github.com/robbyrussell/oh-my-zsh/issues/449

In case the URL becomes dead, the important part is:

Escape the ^ in your command

You can alternatively can use HEAD~ so that you don't have to escape it each time.

Community
  • 1
  • 1
Greg Hilston
  • 2,397
  • 2
  • 24
  • 35
4

Using git 2.9 (precisely 2.9.2.windows.1) git reset HEAD^ prompts for more; not sure what is expected input here. Please refer below screenshot

enter image description here

Found other solution git reset HEAD~#numberOfCommits using which we can choose to select number of local commits you want to reset by keeping your changes intact. Hence, we get an opportunity to throw away all local commits as well as limited number of local commits.

Refer below screenshots showing git reset HEAD~1 in action: enter image description here

enter image description here

nkharche
  • 917
  • 9
  • 10
2

One more way to do it.

Add commit on the top of temporary commit and then do:

git rebase -i

To merge two commits into one (command will open text file with explicit instructions, edit it).

Ruslan Osipov
  • 5,655
  • 4
  • 29
  • 44
  • 2
    Technically correct but nowhere near as elegant as simply doing `git reset HEAD^`. Git rebase has lots of room for error here. – TheBuzzSaw Apr 02 '13 at 21:33
1

In some case, I want only to undo the changes on specific files on the first commit to add them to a second commit and have a cleaner git log.

In this case, what I do is the following:

git checkout HEAD~1 <path_to_file_to_put_in_different_commit>
git add -u
git commit --amend --no-edit
git checkout HEAD@{1} <path_to_file_to_put_in_different_commit>
git commit -m "This is the new commit"

Of course, this works well even in the middle of a rebase -i with an edit option on the commit to split.

Louis Caron
  • 1,043
  • 1
  • 11
  • 17
1

Use this code if you want to update the history of your commit or want to add new commit and remove a previous one

  1. This command will print the last commit hash e.g. fa386d8eaa4bde4a4a2912ef96aad6df31f4d4cc

    git log

  2. Reset your last commit. Note: It will not delete any changes and keep new and old changes. Removes only the last commit message

    git reset 629f9dfb47620a1794f5edf816762f8e30668498

  3. Add all the changed files

    git add .

  4. Commit your message

    git commit -m "Your_new_commit_message_here"

  5. Force push as you already pushed earlier and check with same git log command

    git push -f origin Your_Branch_Name

Sunil
  • 3,785
  • 1
  • 32
  • 43
-1

First you can see log with following command -

git reflog

this shows all commits, then find out your commit that you want to undo and the Head number associated it with and then enter following command

git reset HEAD@{#NumberOfCommitYouWantToUndo} 

e.g. git reset HEAD@{3}

git reset HEAD@{#} shows all Unstaged changes after reset

Waaheeda
  • 394
  • 3
  • 14