1950

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)

1
  • 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
    Commented Jan 27, 2022 at 5:40

14 Answers 14

3463

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}
26
  • 53
    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
    Commented Jul 12, 2011 at 12:19
  • 263
    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
    Commented Dec 9, 2011 at 21:54
  • 85
    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. Commented May 19, 2012 at 3:41
  • 21
    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
    Commented Jul 31, 2012 at 12:45
  • 32
    @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
    Commented Dec 27, 2012 at 16:42
229

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.

5
  • 13
    I executed git commit --amend on an already pushed commit and therefore the other suggestions did not work. But this did. Thanks. Commented Feb 23, 2021 at 12:17
  • Nice. So I just did git reset --soft HEAD@{1} to cancel the latest amend.
    – Noam Manos
    Commented Mar 23, 2023 at 13:32
  • 4
    If you're in powershell, git reset --soft "HEAD@{1}" needs to have quotes. {1} is special syntax it seems like Commented Mar 31, 2023 at 20:31
  • 1
    Thx, because the accepted solution did absolutely nothing in my repo
    – ymoreau
    Commented Nov 3, 2023 at 12:05
  • 2
    This is the most straightforward answer for me to understand. When doing potentially dangerous things in git, I prefer to use an explicit commit reference instead of relative reference like HEAD@{1}, which could be referencing something different than what I intended.
    – wisbucky
    Commented Dec 6, 2023 at 1:54
220

Use the ref-log:

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

You should then have all your previously amended changes in your working copy and can commit them to a new commit.

To see a full list of previous head commits type git reflog.

You might find my other answer helpful if you are looking for a single command to achieve this and are not afraid of low-level plumbing commands.

7
  • 9
    This wipes the index too -- still useful, but goes beyond a simple "undo".
    – cdunn2001
    Commented Dec 9, 2011 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
    Commented Apr 27, 2015 at 20:26
  • 17
    the fist step is redundant. Simple git reset HEAD@{1} is enough.
    – dwelle
    Commented Aug 18, 2016 at 19:04
  • 1
    Creating a temporary branch helps to give commits a name and to not mess up things further :)
    – knittl
    Commented Oct 2, 2018 at 15:43
  • 13
    WOW, reflog means ref-log and not re-flog? This makes so much more sense
    – byxor
    Commented Aug 19, 2020 at 7:30
142

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
5
  • 50
    If you do git reset SHA1 --soft, you can retain the latest changes and then commit them.
    – pravj
    Commented Dec 28, 2017 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
    Commented Jan 27, 2022 at 5: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"
    – mkdior
    Commented Jul 13, 2022 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. Commented Jul 19, 2022 at 23:24
  • 1
    Full command: git reset --soft 'HEAD@{1}' Commented Sep 19, 2022 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
2
  • 4
    This is a special case of the more general question, but it covered my immediate need exactly. Commented Aug 16, 2019 at 20:11
  • 1
    Same for me. I occasionally get conflicts during git rebase, and sometimes I do "amend" instead of "rebase --continue" ... and this here is just a life saver!
    – GhostCat
    Commented Nov 24, 2020 at 7:56
30

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.

0
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.
6
  • 32
    way too complicated. git reflog is all you need
    – knittl
    Commented Sep 22, 2009 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
    Commented May 9, 2012 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) Commented Apr 29, 2013 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. Commented Jun 26, 2015 at 20:09
  • 2
    This still rewrites history and requires a force push. Depending on your situation that may or may not be a problem.
    – Pajn
    Commented Jun 12, 2017 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.

0
17

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.

1
  • +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 <YOUR_FILES> to unstage changes.
    – Ravi Patel
    Commented Jan 23, 2023 at 6: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)

1
  • 4
    Yes, but I typically want to reset --soft to keep my changes. I just want it committed separately Commented Nov 2, 2018 at 13:11
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.

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.

1
  • 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
    Commented Feb 8, 2022 at 1:24
4

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.

1

You can use the low-level commit-tree command and the reflog to construct a new commit with your current head commit's tree, but the original (pre-amend) commit as parent. The branch is forwarded by resetting both working tree and index:

git reset --soft "$(git commit-tree HEAD^{tree} -p HEAD@{1} -m 'Commit message of new commit')"

Why does this work? Let's have a look at some ASCII drawings:

C     t:W    (HEAD@{1}) original commit with tree W
B     t:V
A     t:U

This is where we start, but after amending the commit, we are left with:

C'    t:X    (branch; HEAD) accidentally amended commit with tree X
| C   t:W    (HEAD@{1}) original commit with tree W
|/
B     t:V
A     t:U

Commit C is no longer reachable from the branch, but it still exists in Git's database and is listed in the reflog as HEAD@{1}.

What you want is to create the following new commit with the tree of the amended commit:

D     t:X    (branch; HEAD) separate commit with tree X
C     t:W    original commit with tree W
B     t:V
A     t:U

and git commit-tree HEAD^{tree} -p HEAD@{1} does exactly that: it creates commit D with tree X (same tree as of amended commit C'), but sets the parent to C (the commit before being amended). git reset is required to move the branch from the erroneous, amended commit to the correct, separate commit.

1
  • This one liner fixed my issue, no need to know more.
    – Seif
    Commented Jan 25 at 15:26

Not the answer you're looking for? Browse other questions tagged or ask your own question.