I frequently find myself needing to teach my friends git. This is the second in a series of n posts to quickly get you up to speed with git.
Ok, so now you can add and commit things. This is great when you only have one thing to work on at a time. But what happens if you want to try something out and you're not sure if it'll work or not? Well, my friend, this is what branches are for.
To create a new branch, type
git branch new_branch_name. This will create a new branch. (To see a list of all your branches, just type
git branch. The one with the * next to it is your current branch.) To switch between branches, you must
git checkout branch_to_switch_to. Now, you can add and commit new changes to each branch in parallel.
At some time, you will want to incorporate the changes you've made back in to your "main" branch. (Called "master" in git by default.) to do this, you need to understand two tools:
git merge and
git merge functions a lot like
svn merge. You can use it to merge different revisions between branches. If you have two branches going, say, "foo" and "bar", and want to merge your changes from
bar, here's what you need to do:
git checkout foo- switch to
foo(not necessary if you're already working on
git merge bar- merge
Easy, huh? Now, let's backtrack a bit. There are two types of merges: fast-forward merges, and merges that create merge commits. If you'll remember from Part 1, every commit has a parent commit (the commit that came right before it). A fast-forward merge happens when the last commit on
foo is the parent of a commit in
bar. It's called a fast-forward merge because
foo is fast-forwarded to where
bar is. Fast-forward merges are generally desirable because you retain the commit history of
bar in an easy-to-follow way.
Merge commits happen when the last commit of
foo is not a parent of any commit in
bar. In this case, git will create a commit that is essentially equal to the diff of
bar. However, this is undesirable because the commit history has been messed up, because this commit doesn't reflect the work that was actually done, just the diff of two branches at any given time. How do we fix this? With
bar will have a commit that is a common "ancestor", that is, a commit that is the parent of commits in both
bar. (It might be theoretically possible for this not to happen, but you'd have to try really hard and do things like branch from an empty repository, so it won't happen by accident.) What
git rebase will do is replay all of the commits on
bar since the common ancestor on to the end of
foo. (This is also called "reparenting", since the first commit since the common ancestor on
bar is changed to have its parent be the latest commit on
foo.) Here's how to rebase:
git checkout bar- switch to
bar(not necessary if you're already working on
git rebase foo- reparent
Now, at this point, you may have some conflicts. You can use
git status to see which files have conflicts, and then you'll need to edit them so they don't anymore. Then use
git add to mark them for add, and
git rebase --continue to continue with the rebase. If you get to a situation where your rebase is stuck and you don't know what to do, you can always
git rebase --abort to stop rebasing and go back to where you were.
Now, you have a
bar that is reparented on to
foo, so you can
git checkout foo and
git merge bar to have a nice, clean fast-forward merge!
Up next: remote branches and labels.