Tuesday, March 12, 2013

Git Course Part III: Branches and Merging

We will go back to work on the tutorial project for this section.
Branches:
We already learned what is a commit. A branch is just a label that we can put to a commit.
It is used normally to reference it, just like the HEAD or the SHA-1 of the commit.
When you work on git, you're normally working on a branch. The first time you create your repository, this branch is called master by default.
However, the name master is not used by git in any way, and you can drop this name if you want to.

Listing and changing branches
To list which branches you have, you do git branch. The following output will appear:
weasley:git_tutorial rafael$ git branch
* master

Since we only have master, this is the only branch we can see for now. The star * tells you which branch you're using.
To create another branch, you do git checkout -b . For example:
weasley:git_tutorial rafael$ git checkout -b my_totally_cool_new_branch
Switched to a new branch 'my_totally_cool_new_branch'
weasley:git_tutorial rafael$ git branch
  master
* my_totally_cool_new_branch

You not only created a new branch, but you also now are on it. To go back to master, you type git checkout master.
Now you know how to create a new branch, but maybe you don't know what does it mean. The truth is, the checkout -b command can receive two parameters, the name of the new branch that you want to create, and the name of which branch you want this branch to be based.  If you omit the second parameter, this new branch will be based on your current branch. Since we were on master, my_totally_cool_new_branch is based on master, and the checkout we did was the equivalent of doing git checkout -b my_totally_cool_new_branch master.
When you create a branch based on another, the new branch will have exactly the same commit history and HEAD commit of the branch it's being based on. The picture bellows show that both master and my_totally_cool_new_branch point exactly to the same commit. In fact, they're just names that reference the same commit.
Creating a new commit.
Now, supposed you create another commit on this new branch, this is what happens:
weasley:git_tutorial rafael$ echo "another cool new line" >> a.txt
weasley:git_tutorial rafael$ git commit -a
[my_totally_cool_new_branch ad36d5a] Another cool new line.
 1 file changed, 1 insertion(+)

Now, master and my_totally_cool_new_branch don't point to the same commit anymore. Since we made the commit on my_totally_new_branch, the branch was updated to point to this new commit. master however was not changed and continues to point to the same commit it was previously pointing to.
Merging
Now imagine I'm done with my_totally_cool_new_branch and I want to get the alterations I made on it back to master.  git gives me two ways to do this. The simpler one is merging.
Merging takes the changes made to another branch and applies them on your current branch. So, in order to get the commit back to master, here is what we do:
weasley:git_tutorial rafael$ git checkout master
Switched to branch 'master'
weasley:git_tutorial rafael$ git merge my_totally_cool_new_branch
Updating ca93f24..ad36d5a
Fast-forward
 a.txt | 1 +
 1 file changed, 1 insertion(+)
weasley:git_tutorial rafael$ git log -n 2
commit ad36d5a48059bccf1ce53b86b9069b0693ef3116
Author: Rafael Adson <rafael@geekie.com.br>
Date:   Tue Mar 12 21:59:42 2013 -0300

    Another cool new line.

commit ca93f245d1f6ba6b9d9e5db0a404f9121b929173
Author: Rafael Adson <rafael@geekie.com.br>
Date:   Tue Mar 5 01:15:37 2013 -0300

    Another commit.

Now, master has the new commit, and was updated to point to the new HEAD commit, as we can see now.
After merging
Since my_totally_cool_new_branch was ahead of master, now both commits are equal.
Now let's simulate two people working on the same project. In order to do this, I created two new branches, where each one of them will work:
weasley:git_tutorial rafael$ git checkout -b b1
Switched to a new branch 'b1'
weasley:git_tutorial rafael$ git checkout master
Switched to branch 'master'
weasley:git_tutorial rafael$ git branch b2
weasley:git_tutorial rafael$ git branch
  b1
  b2
* master
  my_totally_cool_new_branch
Both branches were created based on master, and as of now master, b1 and b2 are exactly equal.
weasley:git_tutorial rafael$ git checkout b1
Switched to branch 'b1'
weasley:git_tutorial rafael$ echo "I'm developer 1" > dev1.txt
weasley:git_tutorial rafael$ git add dev1.txt
weasley:git_tutorial rafael$ git commit --author "Dev 1 <dev1@fiive.net>"
[b1 99d495e] Changes made by dev1.
 Author: Dev 1 <dev1@fiive.net>
 1 file changed, 1 insertion(+)
 create mode 100644 dev1.txt

And let us do some work as developer 2:
easley:git_tutorial rafael$ echo "I'm developer 2" > dev2.txt
weasley:git_tutorial rafael$ git add dev2.txt
weasley:git_tutorial rafael$ git commit
Aborting commit due to empty commit message.
weasley:git_tutorial rafael$ git commit --author "Dev 2 <dev2@fiive.net>"
[b2 d3fca00] Changes made by dev2.
 Author: Dev 2 <dev2@fiive.net>
 1 file changed, 1 insertion(+)
 create mode 100644 dev2.txt

Now if we look into the three commits, here is what we see:
Two developers working on different branches.
The branches have diverged. Now dev2 has things that dev1 has not, and dev1 has things that dev2 has not.
Now, let's say dev1 needs the changes dev2 has made to the code. How does he do? He can just merge from b2, the same way we merged master from my_totally_cool_new_branch:
weasley:git_tutorial rafael$ git checkout b1
Switched to branch 'b1'
weasley:git_tutorial rafael$ git merge b2
Merge made by the 'recursive' strategy.
 dev2.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 dev2.txt
If you're following this tutorial, you should have seen that an editor was opened asking for the message for a new commit. What happened?
Well, here's what you had before:Each branch with its own commits.

You had a commit referenced by b1, and it's parent was master. You also had a commit referenced by b2, and it's parent was also master.
When you tried to merge b2, into b1, this is what git tried to do:
git trying to do merge.
Merge won't change the commits, so now you have both commits that were pointed by the two branches, and they both have parent as their master.
However, this structure is not good, since you don't have a HEAD commit and you need one. That's why git creates another commit, that has two parents (b1 and b2 in the previous pictures) and then updates b1 to point to this new commit.
After merge
Notice that, since the commits done by b1 and b2 did not change, if b2 wants b1 changes it can get it by doing git merge b1 in its branch. The effect of this is that both branches will now point to the same commit (since no commit was changed).

Conflicts on Merging
Now imagine dev1 see the brand new dev2 file and wants to make some changes to it. So here's what he does:
weasley:git_tutorial rafael$ echo "some new changes" >> dev2.txt
weasley:git_tutorial rafael$ git add .
weasley:git_tutorial rafael$ git commit --author "Dev 1"
[b1 62de83b] Changes made on dev2 made by dev1.
 Author: Dev 1 <dev1@fiive.net>
 1 file changed, 1 insertion(+)
In the meantime, dev2 also has made his new changes on this file:
weasley:git_tutorial rafael$ git checkout b2
Switched to branch 'b2'
weasley:git_tutorial rafael$ echo "dev 2 changes" > dev2.txt
weasley:git_tutorial rafael$ git reset --hard
HEAD is now at d3fca00 Changes made by dev2.
weasley:git_tutorial rafael$ echo "dev 2 changes" >> dev2.txt
weasley:git_tutorial rafael$ git commit -a
[b2 aee8aa0] Futher changes.
 1 file changed, 1 insertion(+)

Now suppose dev1 wants dev2 changes to the file.  Since both changed the same file, it's possible that the changes will conflict. This will happen when git can't find out what it should do to the file. In this case:
weasley:git_tutorial rafael$ git merge b2
Auto-merging dev2.txt
CONFLICT (content): Merge conflict in dev2.txt
Automatic merge failed; fix conflicts and then commit the result.

There are two ways to fix this conflict. The first way is to edit both files manually and then generate a new commit. I won't teach you this way because I find it too cumbersome.
The second way is to use git merge tool. You can configure a tool for git to use to help you  decide which changes should be applied. There are a wide range of such tools available. I normally use either FileMerge when I am on Mac OS X or meld when I am using Linux, you're free to choose whichever one you like best. Although configuration of such tool falls out of the scope of this tutorial, I will teach you how to use it.
When a conflict happens, you can type git mergetool.
weasley:git_tutorial rafael$ git mergetool
Merging:
dev2.txt

Normal merge conflict for 'dev2.txt':
  {local}: modified file
  {remote}: modified file
Hit return to start merge resolution tool (opendiff):

git will then ask for you to mark a resolution in each of the files that conflicted. It will either suggest an action or will ask you to open your diff tool. By pressing enter, the merge tool should open:
opendiff showing conflicts.
Such tools will indicate to you where the problems happens, and will ask for you to take an action. In this case, I will select in the Action select box that I want both changes:
Resolving conflicts using opendiff
And then I'll just save my alterations and quit the merge tool. If there's another conflict, git will jump to that one. If there isn't, you just need now to create the merge commit.
weasley:git_tutorial rafael$ git commit
[b1 697b352] Merge branch 'b2' into b1
And now dev1 can work with his branch again in sync with b2.

Deleting a branch
To delete a branch, you do:
weasley:git_tutorial rafael$ git checkout master
Switched to branch 'master'
weasley:git_tutorial rafael$ git branch -d my_totally_cool_new_branch
Deleted branch my_totally_cool_new_branch (was ad36d5a).
weasley:git_tutorial rafael$ git branch
  b1
  b2
* master

Now my_totally_cool_new_branch does not exist anymore (ahhhhhh!).
This is not always work. git will try to prevent you from losing work if the result of deleting a branch would make that happen. So, for example, if I try to delete b2.
weasley:git_tutorial rafael$ git branch -d b2
error: The branch 'b2' is not fully merged.
If you are sure you want to delete it, run 'git branch -D b2'.

git didn't allow me to delete it, but it did tell me what I need to do if I really want to do that.

Coming up next:
I'll teach you another way of syncing branches called rebase.

Wednesday, March 6, 2013

Git Course Part II: Some useful commands and tools.

Since we need to illustrate things that require a bunch of commits here, I'll not use the repo I created yesterday for examples, but instead I'll use one of my personal repos: intern-objc https://github.com/rafaeladson/intern-objc
git gui:
If you don't like using the  command line so much, and want something more visual, there are a couple of tools that may or may not help you.
You can check them out here: http://git-scm.com/downloads/guis
Referencing Commits
As far as I know, there are three ways of referencing a commit.
The first way is to get the SHA-1 code that identifies the commit and use that. So for example if you run git log you will probably see this (the contents may change if I ever update this repo):

commit 4250aef93bfb207dd6f8a4fbd16f7b667c0408fd
Author: rafaeladson 
Date: Fri Nov 2 19:51:10 2012 -0200
 
    Updating build to ios6.0
    
    Previous ios5.1
 
commit 30e0256470b56f3c746510e60a416f12c0eede59
Author: rafael.adson 
Date: Wed Mar 14 23:17:36 2012 -0300
 
    Support for deleting items from BaseTableViewcontrolller
    
    Deleting items will be enabled by default on BaseTableViewController.
    
    If you don't want that behavior, you should override the method on your own implementation.
 
commit 3911e80ca914831afa8c02f218a9b2eabac08e52
Author: rafael.adson 
Date: Wed Mar 14 22:27:13 2012 -0300
 
    Creating a script to deploy/update intern in projects.
    
    Also updated the README file.
 
commit 6c81f997a428b9abdfd6522856c0e8d298da0c28
Author: rafael.adson 
Date: Wed Mar 14 21:24:31 2012 -0300
 
    Shell script to package intern.
    
    Created a shell script to automatically package intern.
 
commit 6367d0a913bdb80c444c33a9a98e3afb77089bd4
Author: rafael.adson 
Date: Wed Mar 14 08:47:38 2012 -0300
 
    Documentation for DataManagerDelegate
    
    Now it is commented.
 
commit 87c04c079e3942ac97abf0dced24feaccb215db1
Author: rafael.adson 
Date: Wed Mar 14 01:47:07 2012 -0300
 
    Changed implementation of DataManager to use a delegate instead of using Notification.
    
    Using notification was causing some initialization problems on iPhone applications.
    I found out that using delegate is both more stable and more elegant, so now I migrated the application to use delegates instead.
 
commit cf5adfa3b60853ebc42a2b361ac96e29435e2e90
Author: rafael.adson 
Date: Sat Mar 10 21:49:46 2012 -0300
 
    Taking DataManager out of InternIOSTest target
    
    If DataManager is present on InternIOSTest, I won't be able to import both InternIOS and InternIOSTest on the same project because b
    
    So far, I couldn't solve this problem.
 
commit 4beb746f41f62d80d1c29eadd4fcc97d46c885a5
Author: rafael.adson 
Date: Sat Mar 10 21:30:31 2012 -0300
 
    Added some utilities methods on DataManagerBaseTest
    
    - Added one method to get all instances of an object in the database if you give the entity name.
    - Added one method to delete all instances of a given entity.
    - Updated project configuration for iOS 5.1
Git log lists all commits in sequence. Each commit is shown like this:
commit 4250aef93bfb207dd6f8a4fbd16f7b667c0408fd
Author: rafaeladson 
Date: Fri Nov 2 19:51:10 2012 -0200
 
    Updating build to ios6.0
    
    Previous ios5.1
The first line contains the SHA-1 code, the second the author, the third the date when the commit was made.
Then comes the message telling the description the author did to this commit.
So, If I want to reference this commit, for example, I would use the code 4250aef93bfb207dd6f8a4fbd16f7b667c0408fd.
The second way to reference commit is by using HEAD. Head always points to the topmost commit of your branch (If you don't know  a branch is, I will explain in part III). So, in this case, HEAD is equal to 4250aef93bfb207dd6f8a4fbd16f7b667c0408fd.
However, although HEAD by itself is not very useful, HEAD can be used to reference other commits. There are two ways of doing that. In other two explain that, I will tell you how to reference the commit 3911e80ca914831afa8c02f218a9b2eabac08e52. There are two ways to do that:
1. use HEAD~2. When you use this syntax, you're referencing two commits below HEAD. HEAD~1 or HEAD~ can be used to reference the first commit below HEAD.
2. use HEAD^^, which is equal to HEAD~2. HEAD^^^ == HEAD~3 and HEAD^ == HEAD~ and so on.
The third way to reference a commit is to use the name of the branch or tag that points to that commit. If you don't know what a branch or tag is, don't worry, I'll explain them in my next article.

git reset
Git reset is a extremely useful command that lets you go to any point in history, keeping or discarding your state.
For example, let's say I don't want to support iOS 6.0 anymore, because Apple Maps sucks. So, I want to go back to HEAD^.
In its default mode, git reset will keep your changes, but they will not be in the index anymore, and it will rollback to the desire commit. So, for example:
weasley:intern-objc rafael$ git status
# On branch master
nothing to commit (working directory clean)
(geekie)weasley:intern-objc rafael$ git log -n 1
commit 4250aef93bfb207dd6f8a4fbd16f7b667c0408fd
Author: rafaeladson 
Date: Fri Nov 2 19:51:10 2012 -0200
 
    Updating build to ios6.0
    
    Previous ios5.1
weasley:intern-objc rafael$ git reset HEAD^
Unstaged changes after reset:
MMakefile
Mprepare_for_intern.sh
(geekie)weasley:intern-objc rafael$ git status
# On branch master
# Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
#
# Changes not staged for commit:
# (use "git add ..." to update what will be committed)
# (use "git checkout -- ..." to discard changes in working directory)
#
#modified: Makefile
#modified: prepare_for_intern.sh
#
no changes added to commit (use "git add" and/or "git commit -a")
weasley:intern-objc rafael$ git log -n 1
commit 30e0256470b56f3c746510e60a416f12c0eede59
Author: rafael.adson 
Date: Wed Mar 14 23:17:36 2012 -0300
 
    Support for deleting items from BaseTableViewcontrolller
    
    Deleting items will be enabled by default on BaseTableViewController.
    
    If you don't want that behavior, you should override the method on your own implementation.
 

Note that after git reset HEAD^ my HEAD is now 30e0256470b56f3c746510e60a416f12c0eede59, and the changes that were made by the previous HEAD are now untracked, but still in my working tree.
In soft mode, git reset will rollback to the desired commit, but will keep changes in the index. So, in this case, this is what happens.

 
weasley:intern-objc rafael$ git status
# On branch master
nothing to commit (working directory clean)
(geekie)weasley:intern-objc rafael$ git log -n 1
commit 4250aef93bfb207dd6f8a4fbd16f7b667c0408fd
Author: rafaeladson 
Date: Fri Nov 2 19:51:10 2012 -0200
 
    Updating build to ios6.0
    
    Previous ios5.1
weasley:intern-objc rafael$ git reset --soft HEAD~
weasley:intern-objc rafael$ git status
# On branch master
# Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
#
# Changes to be committed:
# (use "git reset HEAD ..." to upstage)
#
#modified: Makefile
#modified: prepare_for_intern.sh
#
weasley:intern-objc rafael$ git log -n 1
commit 30e0256470b56f3c746510e60a416f12c0eede59
Author: rafael.adson 
Date: Wed Mar 14 23:17:36 2012 -0300
 
    Support for deleting items from BaseTableViewcontrolller
    
    Deleting items will be enabled by default on BaseTableViewController.
    
    If you don't want that behavior, you should override the method on your own implementation.

In this case, note that it did the same thing as before, but this time it kept the changes in the index.
In hard mode, git reset will not keep changes at all. So for our example:
weasley:intern-objc rafael$ git status
# On branch master
nothing to commit (working directory clean)
weasley:intern-objc rafael$ git log -n 1
commit 4250aef93bfb207dd6f8a4fbd16f7b667c0408fd
Author: rafaeladson 
Date: Fri Nov 2 19:51:10 2012 -0200
 
    Updating build to ios6.0
    
    Previous ios5.1
weasley:intern-objc rafael$ git reset --hard HEAD~
HEAD is now at 30e0256 Support for deleting items from BaseTableViewcontrolller
weasley:intern-objc rafael$ git status
# On branch master
# Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
#
nothing to commit (working directory clean)
weasley:intern-objc rafael$ git log -n 1
commit 30e0256470b56f3c746510e60a416f12c0eede59
Author: rafael.adson 
Date: Wed Mar 14 23:17:36 2012 -0300
 
    Support for deleting items from BaseTableViewcontrolller
    
    Deleting items will be enabled by default on BaseTableViewController.
    
    If you don't want that behavior, you should override the method on your own implementation.
 
Note that this time, the changes are gone forever (not really, I still have them, just not in this branch).  Bye bye ie6.
git reset --hard is extremely powerful, but also dangerous. Be careful when using it.
You can also do git reset --hard to erase my current data. So for example, suppose I code a little and them I don't like what I did anymore. I can reset to HEAD by doing:
weasley:intern-objc rafael$ echo "Ooops, just erased my makefile" > Makefile
weasley:intern-objc rafael$ git status
# On branch master
# Changes not staged for commit:
# (use "git add ..." to update what will be committed)
# (use "git checkout -- ..." to discard changes in working directory)
#
#modified: Makefile
#
no changes added to commit (use "git add" and/or "git commit -a")
weasley:intern-objc rafael$ git reset --hard
HEAD is now at 4250aef Updating build to ios6.0
weasley:intern-objc rafael$ git status
# On branch master
nothing to commit (working directory clean)
 

The stash
The stash is a FIFO data-structure that will keep your alterations if you don't want them right now, but will need them later.
So suppose for example you did some alteration to the Makefile, and want to keep them, but you need to do another thing and will come back to solve this later.
This is how you would do it:

weasley:intern-objc rafael$ echo "Some alterations I want to keep" >> Makefile
weasley:intern-objc rafael$ git status
# On branch master
# Changes not staged for commit:
# (use "git add ..." to update what will be committed)
# (use "git checkout -- ..." to discard changes in working directory)
#
#modified: Makefile
#
no changes added to commit (use "git add" and/or "git commit -a")
weasley:intern-objc rafael$ git stash save "changes to makefile"
Saved working directory and index state On master: changes to makefile
HEAD is now at 4250aef Updating build to ios6.0
weasley:intern-objc rafael$ git status
# On branch master
nothing to commit (working directory clean)
weasley:intern-objc rafael$ git stash list
stash@{0}: On master: changes to makefile
Now the changes you made are in the position 0 of the stash. Suppose I want to make another change:
weasley:intern-objc rafael$ echo "Some more changes to Makefile" >> Makefile
weasley:intern-objc rafael$ git status
# On branch master
# Changes not staged for commit:
# (use "git add ..." to update what will be committed)
# (use "git checkout -- ..." to discard changes in working directory)
#
#modified: Makefile
#
no changes added to commit (use "git add" and/or "git commit -a")
weasley:intern-objc rafael$ git stash save "Some more changes"
Saved working directory and index state On master: Some more changes
HEAD is now at 4250aef Updating build to ios6.0
weasley:intern-objc rafael$ git stash list
stash@{0}: On master: Some more changes
stash@{1}: On master: changes to makefile

Now, the newer changes are in position 0 of the stash, the older ones are in position 1.
Then I go, do other things, eventually I come back and want my changes back.
This is how I apply the changes in position 0 to my working space:

weasley:intern-objc rafael$ git stash apply 
# On branch master
# Changes not staged for commit:
# (use "git add ..." to update what will be committed)
# (use "git checkout -- ..." to discard changes in working directory)
#
#modified: Makefile
#
no changes added to commit (use "git add" and/or "git commit -a")
weasley:intern-objc rafael$ git stash list
stash@{0}: On master: Some more changes
stash@{1}: On master: changes to makefile

apply puts the changes back in my workspace, but don't remove them from the stash. If I want to remove them, I could just use pop instead of apply.

Bisect
Bisect is a command that is useful when something went wrong somewhere, but I don't know which commit broke things.
In order to bisect to work, you need to identify a good and bad commit. It will then make a binary search to find out which commit is the culprit.
So, in our example, see that a file exists with the name README.pod. Since I don't like perl, I don't want my README in this format anymore.
So, I want to undo the commit that created the README.pod, so I can create another README in a format that does not come from perl.
The way to start a bisect is by using a git bisect start. I need to inform a good and a bad commit, like this:

git bisect start  

I know in this case that my HEAD is bad, since README.pod exists, and I know that my initial commit (c842b16fb354c3ff3da2ae45fdecf80c5e677f14) is good, since I remember creating README.pod afterwards.
So I run:
weasley:intern-objc rafael$ git bisect start HEAD c842b16fb354c3ff3da2ae45fdecf80c5e677f14
Bisecting: 17 revisions left to test after this (roughly 4 steps)
[b60de31dd42828132e8714c1de0b81dbb46b8b3d] Consertando alguns bugs no TableViewController

The bisect process has started, and git has taken me to the commit b60de31dd42828132e8714c1de0b81dbb46b8b3d, and it wants to know if it is good or bad.
If it's good, I tell git so by doing git bisect good. If it's bad, I do git bisect bad.
In this case, when I run ls I see that README.pod doesn't exist in this commit, so it's a good one. Following up the process:
weasley:intern-objc rafael$ ls
Intern InternIOSTest RunTests.sh intern-ios lib
InternIOS Makefile UnitTests intern-ios.xcodeproj
weasley:intern-objc rafael$ git bisect good
Bisecting: 8 revisions left to test after this (roughly 3 steps)
[492228c62385e97431ae2769fdc8bf656b04f0a1] Changing the appearance of the README file.
weasley:intern-objc rafael$ ls
Intern InternIOSTest Makefile RunTests.sh intern-ios lib
InternIOS LICENSE.txt README.pod UnitTests intern-ios.xcodeproj
weasley:intern-objc rafael$ git bisect bad
Bisecting: 4 revisions left to test after this (roughly 2 steps)
[cd4abe52cb20c90b1969a87a551f3b0873162d95] Changing all strings to Intern table.
weasley:intern-objc rafael$ ls
Intern InternIOSTest RunTests.sh intern-ios lib
InternIOS Makefile UnitTests intern-ios.xcodeproj
weasley:intern-objc rafael$ git bisect good
Bisecting: 2 revisions left to test after this (roughly 1 step)
[2a44f78507a753a16758f9b3d0dfdbb8bbbebc5c] Fixing tests on hudson
weasley:intern-objc rafael$ ls
Intern InternIOSTest RunTests.sh intern-ios lib
InternIOS Makefile UnitTests intern-ios.xcodeproj
weasley:intern-objc rafael$ git bisect good
Bisecting: 0 revisions left to test after this (roughly 1 step)
[2554a24272a6ab63b999f612303c3e86b4e5aa1c] README and LICENSE
weasley:intern-objc rafael$ ls
Intern InternIOSTest Makefile RunTests.sh intern-ios lib
InternIOS LICENSE.txt README.pod UnitTests intern-ios.xcodeproj
weasley:intern-objc rafael$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[90b6a085d048360f0e3906d8065c2a4b8e89dd27] Migrating to ios5.1
weasley:intern-objc rafael$ ls
Intern InternIOSTest RunTests.sh intern-ios lib
InternIOS Makefile UnitTests intern-ios.xcodeproj
weasley:intern-objc rafael$ git bisect good
2554a24272a6ab63b999f612303c3e86b4e5aa1c is the first bad commit
commit 2554a24272a6ab63b999f612303c3e86b4e5aa1c
Author: rafael.adson 
Date: Fri Mar 9 21:57:09 2012 -0300
 
    README and LICENSE
 
:000000 100644 0000000000000000000000000000000000000000 f25e17ec178047de8e901b4678bb7c7c6dff0202 ALICENSE.txt
:000000 100644 0000000000000000000000000000000000000000 5ad516520895e9443f77d62028d03cfdd48722db AREADME.pod

After the whole process, git is then able to tell me that the commit 2554a24272a6ab63b999f612303c3e86b4e5aa1c created the README file.
At any time, if I want to abort the  bisect, I can do:
git bisect reset
Now that I found my commit, how do I undo the changes made to it? Read the next section to find out!

Revert

So let's say I want to undo the alterations done by HEAD, since it puts support in iOS 6 that I don't like.
I don't want to modify my history using reset because there are other people with that commit, and I don't want to upset them. How do I do it?
git reverts help. To revert the changes in HEAD, do:
weasley:intern-objc rafael$ git revert HEAD
[master 9ef89b5] Revert "Updating build to ios6.0"
 1 file changed, 4 insertions(+), 4 deletions(-)
 mode change 100755 => 100644 prepare_for_intern.sh
weasley:intern-objc rafael$ git log -n 2
commit 9ef89b5c26acb907a6fe1218312a1b41847c6deb
Author: Rafael Adson 
Date: Wed Mar 6 00:54:01 2013 -0300
 
    Revert "Updating build to ios6.0"
    
    This reverts commit 4250aef93bfb207dd6f8a4fbd16f7b667c0408fd.
 
commit 4250aef93bfb207dd6f8a4fbd16f7b667c0408fd
Author: rafaeladson 
Date: Fri Nov 2 19:51:10 2012 -0200
 
    Updating build to ios6.0
    
    Previous ios5.1
 

It reverts everything that was done in HEAD, creating another commit that undo the alterations.
You don't need to revert only the last commit. For example, now that I found out which commit created the dreadful README.pod, I can revert it too.
weasley:intern-objc rafael$ git revert 2554a24272a6ab63b999f612303c3e86b4e5aa1c
error: could not revert 2554a24... README and LICENSE
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add ' or 'git rm '
hint: and commit the result with 'git commit'
weasley:intern-objc rafael$ 

Ooops! What happened? In this case, there were conflicts because a commit after 2554a24272a6ab63b999f612303c3e86b4e5aa1c changed the README.pod file.
What you needed to do here is to resolve the conflicts, then commit your alterations.
Since I haven't show you how to resolve conflicts yet, let's just move on.

git help
If you don't know what a command does, you can do git help .
For example: git help bisect will open a manual page that will explain to you how to do a bisect.
If you want a quick help, you can do:
weasley:intern-objc rafael$ git bisect help
Usage: git bisect [help|start|bad|good|skip|next|reset|visualize|replay|log|run]
 
git bisect help
 print this long help message.
git bisect start [--no-checkout] [ [...]] [--] […]
 reset bisect state and start bisection.
git bisect bad []
 mark  a known-bad revision.
git bisect good […]
 mark ... known-good revisions.
git bisect skip [(|)…]
 mark ... untestable revisions.
git bisect next
 find next bisection to test and check it out.
git bisect reset []
 finish bisection search and go back to commit.
git bisect visualize
 show bisect status in gitk.
git bisect replay 
 replay bisection log.
git bisect log
 show bisect log.
git bisect run …
 use ... to automatically bisect.
 
Please use "git help bisect" to get the full man page.
 You can also type git help if you don't know which commands git has.
Next Steps
Next we will work with branches, and we will learn how to join our work with the work of others, and how to resolve conflicts.