How can I undo git reset --hard HEAD~1?

后端 未结 18 3099
逝去的感伤
逝去的感伤 2020-11-22 00:21

Is it possible to undo the changes caused by the following command? If so, how?

git reset --hard HEAD~1
相关标签:
18条回答
  • 2020-11-22 00:30

    as far as i know, --hard will discards uncommitted changes. Since these aren't tracked by git. but you can undo the discarded commit.

    $ git reflog
    

    will lists:

    b0d059c HEAD@{0}: reset: moving to HEAD~1
    4bac331 HEAD@{1}: commit: added level introduction....
    ....
    

    where 4bac331 is the discarded commit.

    Now just move the head to that commit::

    $ git reset --hard 4bac331
    
    0 讨论(0)
  • 2020-11-22 00:30

    My problem is almost similar. I have uncommitted files before I enter git reset --hard.

    Thankfully. I managed to skip all these resources. After I noticed that I can just undo (ctrl-z).

    0 讨论(0)
  • 2020-11-22 00:32

    Before answering lets add some background, explaining what is this HEAD.

    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 its called detached HEAD.

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


    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


    git reset HEAD --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 commited while the original commit will remain in the history as well.

    # add 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 illustrate which command does what.
    As you can see there reset && checkout modify the HEAD.

    0 讨论(0)
  • 2020-11-22 00:33

    Pat Notz is correct. You can get the commit back so long as it's been within a few days. git only garbage collects after about a month or so unless you explicitly tell it to remove newer blobs.

    $ git init
    Initialized empty Git repository in .git/
    
    $ echo "testing reset" > file1
    $ git add file1
    $ git commit -m 'added file1'
    Created initial commit 1a75c1d: added file1
     1 files changed, 1 insertions(+), 0 deletions(-)
     create mode 100644 file1
    
    $ echo "added new file" > file2
    $ git add file2
    $ git commit -m 'added file2'
    Created commit f6e5064: added file2
     1 files changed, 1 insertions(+), 0 deletions(-)
     create mode 100644 file2
    
    $ git reset --hard HEAD^
    HEAD is now at 1a75c1d... added file1
    
    $ cat file2
    cat: file2: No such file or directory
    
    $ git reflog
    1a75c1d... HEAD@{0}: reset --hard HEAD^: updating HEAD
    f6e5064... HEAD@{1}: commit: added file2
    
    $ git reset --hard f6e5064
    HEAD is now at f6e5064... added file2
    
    $ cat file2
    added new file
    

    You can see in the example that the file2 was removed as a result of the hard reset, but was put back in place when I reset via the reflog.

    0 讨论(0)
  • 2020-11-22 00:36

    It is possible to recover it if Git hasn't garbage collected yet.

    Get an overview of dangling commits with fsck:

    $ git fsck --lost-found
    dangling commit b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf
    

    Recover the dangling commit with rebase:

    $ git rebase b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf
    
    0 讨论(0)
  • 2020-11-22 00:36

    If you have not yet garbage collected your repository (e.g. using git repack -d or git gc, but note that garbage collection can also happen automatically), then your commit is still there – it's just no longer reachable through the HEAD.

    You can try to find your commit by looking through the output of git fsck --lost-found.

    Newer versions of Git have something called the "reflog", which is a log of all changes that are made to the refs (as opposed to changes that are made to the repository contents). So, for example, every time you switch your HEAD (i.e. every time you do a git checkout to switch branches) that will be logged. And, of course, your git reset also manipulated the HEAD, so it was also logged. You can access older states of your refs in a similar way that you can access older states of your repository, by using an @ sign instead of a ~, like git reset HEAD@{1}.

    It took me a while to understand what the difference is between HEAD@{1} and HEAD~1, so here is a little explanation:

    git init
    git commit --allow-empty -mOne
    git commit --allow-empty -mTwo
    git checkout -b anotherbranch
    git commit --allow-empty -mThree
    git checkout master # This changes the HEAD, but not the repository contents
    git show HEAD~1 # => One
    git show HEAD@{1} # => Three
    git reflog
    

    So, HEAD~1 means "go to the commit before the commit that HEAD currently points at", while HEAD@{1} means "go to the commit that HEAD pointed at before it pointed at where it currently points at".

    That will easily allow you to find your lost commit and recover it.

    0 讨论(0)
提交回复
热议问题