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? :)