1864

I accidentally amended my previous commit. The commit should have been separate to keep history of the changes I made to a particular file.

Is there a way to undo that last commit? If I do something like git reset --hard HEAD^, the first commit also is undone.

(I have not yet pushed to any remote directories)

alex
  • 479,566
  • 201
  • 878
  • 984
Jesper Rønn-Jensen
  • 106,591
  • 44
  • 118
  • 155
  • *if* you want to confirm the effects of each step you took (either before trying out the answers below or if your head starts to spin while executing one) try `git log --reflog -p -- {{name-of-the-dir-or-file-in-question}}`. It shows both the actual changes and the commit messages for each action. – Kay V Jan 27 '22 at 05:40

13 Answers13

3298

What you need to do is to create a new commit with the same details as the current HEAD commit, but with the parent as the previous version of HEAD. git reset --soft will move the branch pointer so that the next commit happens on top of a different commit from where the current branch head is now.

# Move the current head so that it's pointing at the old commit
# Leave the index intact for redoing the commit.
# HEAD@{1} gives you "the commit that HEAD pointed at before 
# it was moved to where it currently points at". Note that this is
# different from HEAD~1, which gives you "the commit that is the
# parent node of the commit that HEAD is currently pointing to."
git reset --soft HEAD@{1}

# commit the current tree using the commit details of the previous
# HEAD commit. (Note that HEAD@{1} is pointing somewhere different from the
# previous command. It's now pointing at the erroneously amended commit.)
# The -C option takes the given commit and reuses the log message and
# authorship information.
git commit -C HEAD@{1}
sudormrfbin
  • 618
  • 8
  • 16
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 52
    Very cool, +1. I even did it with the second last amend view into `git reflog` to find the correct number e.g. `{2}`. – JJD Jul 12 '11 at 12:19
  • 254
    Just to be clear, the first command is a true "undo". It produces the HEAD, working directory (unchanged), and index state prior to `git commit --amend`. The 2nd is a "redo" into a new commit. These work for any `git commit`, not just `--amend`. – cdunn2001 Dec 09 '11 at 21:54
  • 82
    So if you didn't amend with a new commit message that you need to salvage, the second part can just be a regular `git commit`. – Matt Montag May 19 '12 at 03:41
  • 18
    For some reason, I was getting an error when running `git reset --soft HEAD@{1}`: `fatal: ambiguous argument 'HEAD@1': unknown revision or path not in the working tree. Use '--' to separate paths from revisions`. When I replaced `HEAD@{1}` with the equivalent commit hash shown in `git reflog` (thanks JJD!), this answer worked wonderfully! – Tim Arnold Jul 31 '12 at 12:45
  • 31
    @TimArnold depending on your shell, you may need to put single or double quotes around `HEAD@{1}`. If I run `echo HEAD@{1}` in tcsh for example, the output is `HEAD@1` because the braces were interpreted by tcsh. If I use single quotes, the braces are preserved. – Kelvin Dec 27 '12 at 16:42
  • 1
    yeap, you need quotes around `HEAD@{1}` in `tcsh`; won't work otherwise – cnst Apr 24 '13 at 19:37
  • 2
    That first line saved my ass (git reset --soft HEAD@{1} to undo the commit --amend). Thanks! – Richard Connamacher Feb 17 '14 at 23:42
  • 4
    @demisx: No, `HEAD@{1}` and `HEAD^1` are NOT the same. Understanding `HEAD@{1}` and `reflog` is the crux of the solution here. Compare `git reflog` which `HEAD@{1}` is referencing VS. `git log` the commit history that `HEAD^1` is referencing. The reflog is actually a lot simpler (as it is linear or least the referencing of it is) vs. the the commit history which could be tree like with more complicated referencing. If you want to dig deeper down that rabbit hole checkout `HEAD~` vs `HEAD^`: http://stackoverflow.com/questions/2221658/whats-the-difference-between-head-and-head-in-git – Shwaydogg Mar 02 '15 at 21:16
  • 1
    I lost the changes I amended into the commit amend though. Help! – Dagrooms Jul 13 '15 at 21:32
  • 5
    doing `git reflog` will show you a list of things you had done - helpful if you have done some other things since the 'amend' - `HEAD@{1}` refers to the most recent action – m.kocikowski Aug 28 '15 at 19:17
  • 1
    If using powershell, you will have to escape the squiggly braces with back-ticks. – Seth Flowers Jan 15 '16 at 17:01
  • 1
    What if it was at rebase edit command? How to return original commit information, message, committer, etc? – Sergei Krivonos Jan 20 '16 at 12:19
  • reset is by default soft so `--soft` is not needed here. Also maybe add those ticks around the `HEAD@{1}` bit for shell safety. So `git reset 'HEAD@{1}'` – Timo Jul 29 '16 at 13:05
  • 1
    The default for `reset` is `--mixed`, not `--soft`. `HEAD@{1}` is Bourne shell safe. Bourne shell and its variants are the most popular shells used with Git, people using other shells may need to make adjustments as making an answer portable to every conceivable shell probably isn't practical. – CB Bailey Jul 29 '16 at 13:10
  • 2
    It might be helpful to follow up with a `git commit --amend --reset-author` to set the author of your commit to yourself. In my case I had by mistake ammended to the last commit by author A and when resetting and committing as explained above, the author of my commit was still A. – Kent Munthe Caspersen Jul 11 '17 at 10:04
  • Can someone explain why the second statement (git commit -C) is needed? – Jonathan Aug 08 '17 at 07:39
  • I had to also do a (git rebase) in the end – Yuval Rimar Feb 13 '18 at 11:14
  • I did commit --amend on the merge commit, resulting in the real merge commit hidden. I ran the two suggested commands but they didn't seem to have any effect. git log still shows the amended merge commit as though it were the merge commit. The real merge commit is there as a reference to remote branch (because I pushed) but it isn't referencing my local branch. How do I restore the real merge commit as part of my local branch? – Kuro Mar 25 '19 at 23:47
  • 1
    I just did the first command to restore to the state I needed. Thanks for this, realizing just how useful the reflog really is. – kevin11 Apr 10 '19 at 12:29
  • For future readers: if you get `error: unknown switch 'e'` you are probably runing in Powershell (Core/6?). EIther backtick the right stuff as mentioned above or switch to a legacy command prompt. – LosManos May 21 '19 at 19:20
  • @Jonathan `-C` in `git commit -C HEAD@{1}` reuses the message from the commit given. See man git-commit: `-C , --reuse-message= Take an existing commit object, and reuse the log message and the authorship information (including the timestamp) when creating the commit.` – martisj Oct 03 '19 at 09:55
  • 1
    @KentMuntheCaspersen I had the same problem, but followed up with `git reset HEAD~1`, `git add`, `git commit` instead. I suppose `--reset-author` is the way to go though. – Zyl Jan 09 '20 at 11:11
  • @Zyl for some reasons the command `git reset --soft HEAD@{1}` didn't work on my mac but `git reset HEAD~1` worked. thanks – misterone Jan 16 '21 at 00:59
  • This didn't work but @kenorb's answer (with `cherry-pick`) did. – Mark Jeronimus Jan 21 '22 at 14:43
  • 1
    Just wondering why this answer is way below when it has lot of votes, I thought Stackoverflow now sorts answers by score? – pinkpanther Feb 24 '22 at 19:20
  • In my opinion, the second command is introducing confusion into an otherwise great answer. The question is about undoing an accidental amend. That's what the first command addresses. Whether you'd want the new commit to have the same commit message is a separate issue (and I really don't know why you'd want that if you've accidentally amended). You can simply just `git commit` after the first command. – jacderida Jun 02 '22 at 20:57
  • I don't see why you would suggest using the `-C`-option here. It wasn't asked for if I understand correctly... – polynomial_donut Jul 08 '23 at 15:24
187

use the ref-log:

git branch fixing-things HEAD@{1}
git reset fixing-things

you should then have all your previously amended changes only in your working copy and can commit again

to see a full list of previous indices type git reflog

knittl
  • 246,190
  • 53
  • 318
  • 364
  • 8
    This wipes the index too -- still useful, but goes beyond a simple "undo". – cdunn2001 Dec 09 '11 at 21:56
  • 23
    @neaumusic: yes! `HEAD~1` is exactly the same as `HEAD^` and identifiers the _parent_ of the current commit. `HEAD@{1}` on the other hand refers to the commit which HEAD pointed to before this one, i.e. they mean different commits when you checkout a different branch or amend a commit. – knittl Apr 27 '15 at 20:26
  • @knittl ah no wonder i didnt think this was possible before, thanks again, good information – neaumusic Apr 27 '15 at 20:45
  • 14
    the fist step is redundant. Simple `git reset HEAD@{1}` is enough. – dwelle Aug 18 '16 at 19:04
  • @jamesdeath123 he's simply creating a new branch from a previous commit, and then resetting the main branch to the new branch. He gains nothing by this. – dwelle Oct 02 '18 at 08:13
  • 1
    Creating a temporary branch helps to give commits a name and to not mess up things further :) – knittl Oct 02 '18 at 15:43
  • 11
    WOW, `reflog` means `ref-log` and not `re-flog`? This makes so much more sense – byxor Aug 19 '20 at 07:30
170

None of these answers with the use of HEAD@{1} worked out for me, so here's my solution:

git reflog

d0c9f22 HEAD@{0}: commit (amend): [Feature] - ABC Commit Description 
c296452 HEAD@{1}: commit: [Feature] - ABC Commit Description 

git reset --soft c296452

Your staging environment will now contain all of the changes that you accidentally merged with the c296452 commit.

Oisín Foley
  • 2,317
  • 2
  • 22
  • 29
137

Find your amended commits by:

git log --reflog

Note: You may add --patch to see the body of the commits for clarity. Same as git reflog.

then reset your HEAD to any previous commit at the point it was fine by:

git reset SHA1 --hard

Note: Replace SHA1 with your real commit hash. Also note that this command will lose any uncommitted changes, so you may stash them before. Alternatively, use --soft instead to retain the latest changes and then commit them.

Then cherry-pick the other commit that you need on top of it:

git cherry-pick SHA1
kenorb
  • 155,785
  • 88
  • 678
  • 743
  • 48
    If you do `git reset SHA1 --soft`, you can retain the latest changes and then commit them. – pravj Dec 28 '17 at 19:10
  • and another handy trick: *if*, before messing around, you want to confirm the effects of each step you took, try `git log --reflog -p -- {{name-of-the-dir-or-file-in-question}}`. It shows both the actual changes and the commit messages. – Kay V Jan 27 '22 at 05:36
  • Just use `git reset SHA1`, which defaults to mode `--mixed` which doesn't reset the working tree, allowing you to commit right away. `git reset SHA1 && git commit -m "abc"` – Meuko Jul 13 '22 at 10:37
  • @Meuko but `reset --soft` has the benefit (as I see it in this scenario) of putting the changes back in the index (not in the working tree). This way you're sure, if you commit again, you'll be committing the same changes as the commit you just reset it. This is handy to me when I amend a commit by mistake. – Felipe Romero Jul 19 '22 at 23:24
  • 1
    Full command: `git reset --soft 'HEAD@{1}'` – Felipe Romero Sep 19 '22 at 22:50
35

If you have pushed the commit to remote and then erroneously amended changes to that commit this will fix your problem. Issue a git log to find the SHA before the commit. (this assumes remote is named origin). Now issue these command using that SHA.

git reset --soft <SHA BEFORE THE AMMEND>
#you now see all the changes in the commit and the amend undone

#save ALL the changes to the stash
git stash

git pull origin <your-branch> --ff-only
#if you issue git log you can see that you have the commit you didn't want to amend

git stash pop
#git status reveals only the changes you incorrectly amended

#now you can create your new unamended commit
David Sopko
  • 5,263
  • 2
  • 38
  • 42
28

Possibly worth noting that if you're still in your editor with the commit message, you can delete the commit message and it will abort the git commit --amend command.

Justin Schulz
  • 457
  • 5
  • 9
26

You can always split a commit, From the manual

  • Start an interactive rebase with git rebase -i commit^, where commit is the commit you want to split. In fact, any commit range will do, as long as it contains that commit.
  • Mark the commit you want to split with the action "edit".
  • When it comes to editing that commit, execute git reset HEAD^. The effect is that the HEAD is rewound by one, and the index follows suit. However, the working tree stays the same.
  • Now add the changes to the index that you want to have in the first commit. You can use git add (possibly interactively) or git-gui (or both) to do that.
  • Commit the now-current index with whatever commit message is appropriate now.
  • Repeat the last two steps until your working tree is clean.
  • Continue the rebase with git rebase --continue.
Arkaitz Jimenez
  • 22,500
  • 11
  • 75
  • 105
  • 32
    way too complicated. `git reflog` is all you need – knittl Sep 22 '09 at 10:14
  • 2
    Lots of steps yes, but each step is uncomplicated and easy to do. This worked for me and gets my vote. – OzBandit May 09 '12 at 16:03
  • 5
    additionally, this answer allows you to selectively pick the changes that you accidentally 'amended', to does provide some additional value to the git reset --soft HEAD@{1} approach (which did solve my problem BTW) – Wiebe Tijsma Apr 29 '13 at 16:08
  • 2
    You can selectively pick changes with the reflog method, too. Just do `git reset` instead of `git reset --soft`, then do `git add --patch`. – geekofalltrades Jun 26 '15 at 20:09
  • Way too complicated, and provides more control than needed to just undo a mistaken amend, which was what the question asked - not how to manually split a commit. If I mistakenly did an amend, I don't want to have to go through and select which changes I want in each commit! `git reflog` is where it's at. – corinnaerin Jun 08 '17 at 02:34
  • 2
    This still rewrites history and requires a force push. Depending on your situation that may or may not be a problem. – Pajn Jun 12 '17 at 14:38
23

Maybe can use git reflog to get two commit before amend and after amend.

Then use git diff before_commit_id after_commit_id > d.diff to get diff between before amend and after amend.

Next use git checkout before_commit_id to back to before commit

And last use git apply d.diff to apply the real change you did.

That solves my problem.

utzcoz
  • 649
  • 4
  • 15
15

You can do below to undo your git commit —amend

  1. git reset --soft HEAD^
  2. git checkout files_from_old_commit_on_branch
  3. git pull origin your_branch_name

====================================

Now your changes are as per previous. So you are done with the undo for git commit —amend

Now you can do git push origin <your_branch_name>, to push to the branch.

kenorb
  • 155,785
  • 88
  • 678
  • 743
Pratik
  • 666
  • 7
  • 9
  • +1. Simple solutions to undo "amend". Just for better understanding, I would like to add before Step 2. (git checkout files), run `git restore --staged ` to unstage changes. – Ravi Patel Jan 23 '23 at 06:14
8

Almost 9 years late to this but didn't see this variation mentioned accomplishing the same thing (it's kind of a combination of a few of these, similar to to top answer (https://stackoverflow.com/a/1459264/4642530).

Search all detached heads on branch

git reflog show origin/BRANCH_NAME --date=relative

Then find the SHA1 hash

Reset to old SHA1

git reset --hard SHA1

Then push it back up.

git push origin BRANCH_NAME

Done.

This will revert you back to the old commit entirely.

(Including the date of the prior overwritten detached commit head)

garrettmac
  • 8,417
  • 3
  • 41
  • 60
4
  1. Checkout to temporary branch with last commit

    git branch temp HEAD@{1}

  2. Reset last commit

    git reset temp

  3. Now, you'll have all files your commit as well as previous commit. Check status of all the files.

    git status

  4. Reset your commit files from git stage.

    git reset myfile1.js (so on)

  5. Reattach this commit

    git commit -C HEAD@{1}

  6. Add and commit your files to new commit.

Priyanshu Chauhan
  • 5,297
  • 5
  • 35
  • 34
4

Simple Solution Solution Works Given: If your HEAD commit is in sync with remote commit.

  • Create one more branch in your local workspace, and keep it in sync with your remote branch.
  • Cherry pick the HEAD commit from the branch (where git commit --amend) was performed onto the newly created branch.

The cherry-picked commit will only contain your latest changes, not the old changes. You can now just rename this commit.

Vasudeva H
  • 51
  • 2
  • Great idea, worked well for me. Also note you could use `master` / `main` if no one else has merged to it yet. Saved me today! – ragurney Feb 08 '22 at 01:24
2

This is how you can do it easily:

  • Soft reset the last commit which has old commit & amended changes

git reset --soft HEAD^

  • Now commit the staged changes in a new commit

git commit -m "new commit msg"

Now push the changes and rebase this commit on top of last commit on which you had amended your changes.

This will make sure that the new commit has only amended changes.

Sudesh Chaudhary
  • 395
  • 6
  • 13