How can I recover a lost commit in Git?
Asked Answered
P

9

533

First, got "your branch is ahead of origin/master by 3 commits" then my app has reverted to an earlier time with earlier changes.

How can I get what I spent the last 11 hours doing back?

Panelist answered 11/4, 2012 at 3:4 Comment(2)
Did you already look at #277577 or #2433079 or #2343118 ?Psychometry
If you are using SourceTree or other Git GUI apps, head down there.Countermove
M
1155

git reflog is your friend. Find the commit that you want to be on in that list and you can reset to it (for example:git reset --hard e870e41).

(If you didn't commit your changes... you might be in trouble - commit early, and commit often!)

Mesmerize answered 11/4, 2012 at 3:8 Comment(9)
So the problem actually occurred when I tried to commit... git reflog returns ": HEAD@{0}: checkout: moving from following-users to ma....HEAD@{1}: commit: yo.... HEAD@{2}: commit: updated a ton....HEAD@{3}: checkout: moving from master to following-u...how can I revert back to one of those last three?Panelist
Take a look at git log HEAD@{1}. If that looks like the right series of commits, then you can git reset HEAD@{1}.Mesmerize
Only if the codes are staged (using git add), they are kept in git and can be found back easily using commands like git fsck --lost-found.Abscise
Accidentally dropped a commit I should have kept when rebasing. This totally saved me from redo-ing a couple hours worth of work.Smearcase
Be careful using --hard. Uncommitted changes will be lost.Soliloquize
Do lost commits get auto-deleted after a certain period of time?Readership
@TheTechRobo36414519 git-gc eventually cleans up old reflog entries, and once nothing references a commit its objects will eventually be cleaned up too. The conditions under which the reflog is cleaned up can be set via git-gc's config options git-scm.com/docs/git-gc#Documentation/git-gc.txt-gcreflogExpire (at the time of this comment, the default expiry for reflog entries is 90 days for commits which are reachable, and 30 days for commits which are unreachable from current tips).Mesmerize
My particular issue was that I committed my changes to a feature branch I wasn't using anymore and then, after switching back to main and pulling an update of the repo, those commits were seemingly gone. For me, merging my committed changes from the feature branch into main fixed the issue.Doll
You saved my life, this command is OPAstrobiology
M
203

Before answering, let's add some background, explaining what this HEAD is.

First of all what is HEAD?

HEAD is simply a reference to the current commit (latest) on the current branch.
There can only be a single HEAD at any given time (excluding git worktree).

The content of HEAD is stored inside .git/HEAD and it contains the 40 bytes SHA-1 of the current commit.


detached HEAD

If you are not on the latest commit - meaning that HEAD is pointing to a prior commit in history it's called detached HEAD.

Enter image description here

On the command line, it will look like this - SHA-1 instead of the branch name since the HEAD is not pointing to the tip of the current branch:

Enter image description here

Enter image description here


A few options on how to recover from a detached HEAD:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

This will checkout new branch pointing to the desired commit.
This command will checkout to a given commit.
At this point, you can create a branch and start to work from this point on.

# Checkout a given commit.
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# Create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

You can always use the reflog as well.
git reflog will display any change which updated the HEAD and checking out the desired reflog entry will set the HEAD back to this commit.

Every time the HEAD is modified there will be a new entry in the reflog

git reflog
git checkout HEAD@{...}

This will get you back to your desired commit

Enter image description here


git reset --hard <commit_id>

"Move" your HEAD back to the desired commit.

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts if you've modified things which were
# changed since the commit you reset to.
  • Note: (Since Git 2.7) you can also use the git rebase --no-autostash as well.

git revert <sha-1>

"Undo" the given commit or commit range.
The reset command will "undo" any changes made in the given commit.
A new commit with the undo patch will be committed while the original commit will remain in the history as well.

# Add a new commit with the undo of the original one.
# The <sha-1> can be any commit(s) or commit range
git revert <sha-1>

This schema illustrates which command does what.
As you can see there, reset && checkout modify the HEAD.

Enter image description here

Mastigophoran answered 13/1, 2016 at 21:20 Comment(4)
That just saved me many hours of work. I used "git reflog --date=iso" to see the date/time for each entry since I couldn't tell for sure without the timestamp.Subhuman
for me, in git reset --hard <commit_id>, removing HEAD worked! +1 for graphical representation!!.Thecla
Just to mention: if you know the branch name: git reflog <branchname> can be quite useful, since you see the changes of just one branch.Wolter
Nice explanation. I tend to use git cherry-pick <dangling-commit-hash> on the branch where I want it to come to put it there.Abash
K
60

Another way to get to the deleted commit is with the git fsck command.

git fsck --lost-found

This will output something like at the last line:

dangling commit xyz

We can check that it is the same commit using reflog as suggested in other answers. Now we can do a git merge

git merge xyz

Note:
We cannot get the commit back with fsck if we have already run a git gc command which will remove the reference to the dangling commit.

Kress answered 12/1, 2016 at 19:38 Comment(1)
This is the only answer that works if you haven't recently pointed at the commit in question, such as when you fetch a branch and then accidentally reset that branch elsewhere.Idle
C
26

This happened to me just today, so I am writing what came out as a lifesaver for me. My answer is very similar to @Amber 's answer.

First, I did

git reflog

to search for that particular commit's hash, then just copied that hash and did

git cherry-pick <hash>

from that branch. This brought all the change from that lost commit to my current branch, and restored my faith on GIT.

Have a nice day!

Callie answered 13/2, 2021 at 16:3 Comment(1)
This is what I needed. To note, my lost commit was only seen in git reflog; I did not see it at all in git logs.Masera
A
17

First of all use git reflog to list all your commits even the lost commit

git reflog

Then use git log HEAD@{your_commit_number} to find the commit you are looking for. e.g

git log HEAD@{17}

Checkout into the commit after you find it with git checkout HEAD@{your_commit_number} e.g

git checkout HEAD@{17}

You may need to add and commit your changes

git add .
git commit -m"quuck_fix"

Then, you will have to create a temporary branch to restore the commit back to your branch

git branch temp

Finally, you will checkout into your existing branch and then merge the temporary branch.

#git checkout <your_existing_branch> e.g

git checkout main
git merge temp
Aesthete answered 14/10, 2022 at 23:13 Comment(0)
M
15

Try this, This will show all commits recorded in git for a period of time

git reflog

Find the commit you want with

git log HEAD@{3}

or

git log -p HEAD@{3}    

Then check it out if it's the right one:

git checkout HEAD@{3}

This will create a detached head for that commit. Add and commit any changes if needed

git status 
git add
git commit -m "temp_work" 

Now if want to restore commit back to a branch lets say master you will need to name this branch switch to master then merge to master.

git branch temp
git checkout master
git merge temp

Here's also a link specifically for reflog on a Git tutorial site: Atlassian Git Tutorial

Murial answered 4/8, 2020 at 14:57 Comment(0)
L
9

If you cannot find your commit with git reflog and it happen that you were using IntelliJ IDE you can right click on your project root folder -> Local History -> Show History and revert your changes from there.

I messed up doing git rebase with git push -f and this truly saved me since the commit was dropped from the local and remote repositories.

Hope that saves someone's day

Cheers

Leprosy answered 1/10, 2021 at 14:44 Comment(2)
Hey So I have VS running and I have the commit id of a commit ec101349 how can i filter my reflog with this id? is it possible?Blooded
I didn't know about this feature, this saved me a few hours of lost work!Ukrainian
C
2

The safest way is to

  1. Find commit head number for state you want to restore

git reflog or git reflog | grep your_phrase

  1. Create new branch off it

git checkout -b your-branch HEAD@{<your-number>}

Canossa answered 4/10, 2022 at 11:50 Comment(0)
C
0

I use this quite often and it's dumbfounding me how uncomfortable this still is in git GUI apps such as SourceTree, so I wrote a little utility to help me visualize the history and hidden commits directly in the UI.

These scripts will create (and delete) tags for n last items from the reflog.

Usage

# Create "HEAD-n" tags for n reflog items
./reflog-tag.sh 5

# With prompt
./reflog-tag.sh

# Delete all "HEAD-*" tags
./reflog-untag.sh

On Windows put the scripts somewhere in PATH, then in the repo

sh reflog-tag.sh [<number>]
sh reflot-untag.sh

And the result looks like this:

enter image description here

enter image description here

reflog-tag.sh

#!/bin/bash

# Usage:
# ./reflog-tag.sh <number>

# Usage with prompt:
# ./reflog-tag.sh
# Please provide the number of last commits you want to tag: <number>

# on Windows
# sh reflog-tag.sh <number>

# Number of last commits to tag
n=$1

if [ -z "$n" ]
then
  read -p "Please provide the number of last commits you want to tag: " n
fi

# Get the last n commit hashes along with their reflog selectors
entries=$(git reflog --pretty=format:'%h %gd' -n $n)

while read -r entry
do
  # Split the entry into commit hash and reflog selector
  commit=$(echo $entry | cut -d' ' -f1)
  ref=$(echo $entry | cut -d' ' -f2)

  # Extract the number from the reflog selector
  number=$(echo $ref | grep -o -E '[0-9]+')

  # Create a tag for the commit
  git tag "HEAD-$number" $commit
done < <(echo "$entries")

echo "Tags created successfully."

reflog-untag.sh

#!/bin/bash

# Get all tags that start with 'HEAD-'
tags=$(git tag -l 'HEAD-*')

for tag in $tags
do
    # Delete the tag
    git tag -d $tag
done

echo "Tags deleted successfully."

SourceTree integration

Right click in the graph view to view and execute Custom Actions.

enter image description here

Define custom actions

Open Tools > Options > Custom Actions > [Add]

  1. cmd /c sh reflog-tag.sh
  2. cmd /c sh reflog-untag.sh

enter image description here

Countermove answered 19/3 at 21:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.