Search code examples
gitundogit-reset

How to return a repository back to the state it was in three commits ago?


Consider this Git repository:

$ git log -4

commit d7a65d3243259300ff133a28e952fd967df8ff24
Merge: 5572899 65d0d65
Date:   Sun Jul 20 08:23:09 2014 -0400

    Merge branch 'master' of ssh://foo/bar

commit 5572899e00f24f1d6ebc4435ef167508be2dbb47
Date:   Sun Jul 20 07:55:48 2014 -0400

    Commit changes done on server

commit 65d0d65b50a28388ff9073dd6973ee5d115fa141
Date:   Sun Jul 20 14:31:01 2014 +0300

    conflict 21

commit a3c6295de542ae510623921b188be1ef5bb25428
Date:   Sun Jul 20 13:12:39 2014 +0300

    Update spam filter

I need to return the code of the repository to the state that it was in at commit a3c6295de542ae510623921b188be1ef5bb25428. The repository is shared among other developers, all have pulled. What is the proper way to return the repo to the state of the desired commit?

Simply doing git reset --hard a3c629 will not work as I cannot then push the repo:

$ git push
To ssh://foo/bar.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'ssh://foo/bar.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again.  See the 'Note about
fast-forwards' section of 'git push --help' for details.

I don't want to rebase as there are other developers. What else can I do?


Solution

  • You can try a "forced" revert, meaning creating a new commit, except that new commit would reflect a3c6295d, instead of trying to cancel the subsequent commits:

    # move HEAD, reset index
    git reset a3c6295de542ae510623921b188be1ef5bb25428
    
    # force the files to be like the index
    git checkout -- .
    
    # move HEAD back to its previous place
    git reset --soft @{1}
    
    # commit the current index (which should reflect a3c6295d)
    git commit -m "restore a3c6295de542ae510623921b188be1ef5bb25428"
    
    git push
    

    That way, no need to force the push.


    The question "Revert to a previous Git commit" proposes a simpler solution, without having to move HEAD from your most recent commit:

    # restore directly the right commit
    git checkout a3c6295de542ae510623921b188be1ef5bb25428 -- .
    
    # commit the current index (which should reflect a3c6295d)
    git commit -m "restore a3c6295de542ae510623921b188be1ef5bb25428"
    

    Both solutions do the same: create a new commit on top of HEAD, which is useful considering HEAD was already pushed to a remote repo and its history shouldn't be changed.