How to return the project to a specific commit?

33

Assuming a condition where, I have a project with 20 commits, and I decide for unexplained reasons to return some commits independently if I want to go back 4, 5 or 10, assuming the description of my commits are clear, I view my last commits using the command $ git log , and after located, how do I revert to a specific commit? What are the possible problems I can encounter? And how to heal them?

    
asked by anonymous 06.06.2014 / 04:15

3 answers

39

Your question is very broad. There are many, many possible scenarios, with several alternative solutions, one with advantages and disadvantages. Still, I see three important scenarios to consider:

  • you want to return your local repository to a specific point;
  • you want to return a public repository to a specific point
  • You want to go back to a specific point in history to take a test, but you do not want to change the story.

Let's see them, then.

Rewind local repository

Consider this repository on GitHub . Let's make some improvements in the program. First, we cloned the repository locally and edited it.

  • First, let's add a shebang :

    $ git diff -U1
    diff --git a/add.py b/add.py
    index 77d557c..2bb03ed 100644
    --- a/add.py
    +++ b/add.py
    @@ -1,3 +1,4 @@
    +#!/usr/bin/env python
     import sys
    $
    $  git com -am "Adding shebang line"
    [master 3c1dd69] Adding shebang line
     1 file changed, 1 insertion(+)
    
  • Then let's make the output more explanatory:

    $ git diff -U1
    diff --git a/add.py b/add.py
    index 2bb03ed..3287cc7 100644
    --- a/add.py
    +++ b/add.py
    @@ -6,4 +6,4 @@ def add(a, b):
     a2 = int(sys.argv[2])
    -print add(a1, a2)
    +print "%d + %d = %d" % (a1, a2, add(a1, a2))
    $
    $ git com -am "Making output readable"
    [master 3cba3f9] Making output readable
     1 file changed, 1 insertion(+), 1 deletion(-)
    
  • Finally, we added a greeting:

    $ git diff -U1
    diff --git a/add.py b/add.py
    index 3287cc7..676b124 100644
    --- a/add.py
    +++ b/add.py
    @@ -3,2 +3,4 @@ import sys
    
    +print "This is the lame calculator. Hello!"
    +
     def add(a, b):
    $
    $ git com -am "Greeting"
    [master a41916f] Greeting
     1 file changed, 2 insertions(+)
    
  • We are ready to send the changes to GitHub but it is decided that the second and third changes are a bad idea. To undo it, as it only exists in your local repository , we can use git reset . First, we have this history:

    $ git log --oneline -n4
    a41916f Greeting
    f69d7d0 Making output readable
    3c1dd69 Adding shebang line
    cf629bc Removing spurious line
    

    Then we use git reset --hard :

    $ git reset --hard cf629bc
    HEAD is now at cf629bc Removing spurious line
    

    The result will be:

    $ git log --oneline -n4
    cf629bc Removing spurious line
    e4eccaf Using better names
    b0d6449 Using proper name
    e0e3713 Using variables
    

    Now, yes! We can make our push happy.

    Rewind Public Repository

    Well, before doing our push , we notice that there are already wrong commits in GitHub! The commit 0b75b1 renames b to a2 within the add() function, where this should not be done . Committing e88170 is even worse, turning addition into subtraction meaningless!

    When we rewind public repositories almost we can never use git reset . If we used git reset , a person who cloned our repository would have serious problems synchronizing with it again. The solution in this case is git revert . To do this, simply go through the interval of commits to be reverted, starting from the last commit to be removed and ending at the first commit to not be removed (e88170). That is, you should pass the f187f6..e88170 :

    $ git revert --no-edit HEAD..e88170
    [master b77fd51] Revert "Using subtraction"
     1 file changed, 1 insertion(+), 1 deletion(-)
    [master bf5d6e0] Revert "Using better names"
     1 file changed, 4 insertions(+), 4 deletions(-)
    

    What's the difference for git reset ? Well, instead of extirpating the commits, it adds new commits undoing the old changes. If you look at the last commit, it will be the reverse version of the first committed commit:

    $ git show -U1
    commit bf5d6e0a64916f9b4a40975c6e8ae519e682d37d
    Author: Adam Victor Nazareth Brandizzi <[email protected]>
    Date:   Fri Jun 6 13:01:01 2014 -0300
    
        Revert "Using better names"
    
        This reverts commit 0b75b1fbbf6858bd5fe3c46a51dda7ea424bd84b.
    
    diff --git a/lamecalc.py b/lamecalc.py
    index 3edf375..9c3a009 100644
    --- a/lamecalc.py
    +++ b/lamecalc.py
    @@ -3,7 +3,7 @@ import sys
     def add(a, b):
    -    return a+a2
    +    return a+b
    
    
    -a1 = int(sys.argv[1])
    -a2 = int(sys.argv[2])
    -print add(a1, a2)
    +a = int(sys.argv[1])
    +b = int(sys.argv[2])
    +print add(a, b)
    

    Now, yes! We can do the push of reversals. Our past mistakes will remain visible, but we will not break our followers' repositories.

    One cool thing about git revert is that you do not have to use it only in the latest commands. If you wanted to revert only the commit 0b75b1 , you could do so:

    $ git revert 0b75b1
    

    Of course it could conflict, but in that case it was only to resolve it, as it does with any conflict.

    Return to the past to do an experiment

    git reset returns the repository behind and causes the current branch to point to the chosen commit . We do not always want this. For example, suppose we do not know which commit causes our program to show subtraction instead of addition. We suspect, however, that the problem did not exist in the f187f6 revision. In this case, just checkout this review and test:

    $ git co f187f6
    Note: checking out 'f187f6'.
    
    You are in 'detached HEAD' state. You can look around, make experimental
    changes and commit them, and you can discard any commits you make in this
    state without impacting any branches by performing another checkout.
    
    If you want to create a new branch to retain commits you create, you may
    do so (now or later) by using -b with the checkout command again. Example:
    
      git checkout -b new_branch_name
    
    HEAD is now at f187f65... Using variables
    
    Ready! In our current state, there is no longer the commit weird:

    $ git log --oneline -n5
    f187f65 Using variables
    f8f7118 Reading from parameters
    e70e491 Moving to function
    bbfb15d Adding operation
    dd958e3 Initial commit
    $
    [13h13 adam@adam:~/sandbox/lamecalc] $ git branch
    * (detached from f187f65)
      master
    

    We tested and found that the problem really did not exist then:

    $ python lamecalc.py 2 2
    4
    

    On the other hand, our master branch has not changed:

    $ git log --oneline -n5 master
    e881705 Using subtraction
    0b75b1f Using better names
    f187f65 Using variables
    f8f7118 Reading from parameters
    e70e491 Moving to function
    

    But now that we know which version works, we can go back to the master branch with git co master and then apply git revert to the wrong commit.

    The sky is the limit

    This answer is just the tip of the iceberg. There are other ways to return to a point - git rebase iterative, checkout with -b etc. - each with its challenges. Boy, not even the ones I presented here are fully explained! Still, I think I can give you an idea. Anything, we can raise the question later too, right? :)

        
    06.06.2014 / 18:25
    24

    You can give git checkout <hash-do-seu-commit> . Example:

    git checkout c8ccd1c
    

    Then you will return to the state that was when you did this commit .

        
    06.06.2014 / 04:28
    6

    I had this same problem, to resolve it just use the command below:

    git reset --hard {hash-do-commit-desejado}
    

    Your branch goes straight to the desired hash .

        
    16.11.2015 / 22:07