Skip to main content

Git for Authors

Section 2.2 Branches

The term branch gets used many ways in git, as a verb and a noun, likely because it is so fundamental. We will introduce you to this concept with an exercise, that will also give you some idea of the power of the idea. So even if you do not work the example right away, read through it, since it contains a lot of explanation.

Checkpoint 2.2.1. Hero and Heroine on Branches.

Make sure your practice repository has at least three files in it, with one typographic mistake. So go ahead and create a new file and a new commit if you need to. We will wait.
We are going to pretend that we are writing a novel and we want to design two possible endings. In one, the hero dies, and in the other, the heroine dies.
Make sure your working directory is clean and type git branch heroine. Not much will appear to have just happened. So here is a new diagnostic command you will want to use all the time. Type git show-branch. This shows all of your possible branches, with an asterisk (“*”) indicating the branch you are “on” and an exclamation mark indicating other branches (“!”). It is not very informative yes, since your repository is just beginning to take shape and you have only just created a branch. Be sure to keep this output visible in your terminal for an upcoming comparison.
Now we are going to move to you new branch. Ready? Type git checkout heroine. Now run git show-branch again and compare to the previous output. The big change is that the asterisk has moved to indicate the heroine branch. We say you are now on the heroine branch. If you have experience with other revision control systems, the word “checkout” is fraught with other meanings that are not accurate. Sorry, just get over it. In git the checkout command will change the files in your working directory to some possibly different state, based on a different sequence of changes in a sequence of commits constituting a different branch. (Remember, commits are collections of changes.)
Choose one of your two files without a typo and edit it to include some words about the heroine dying, and also remove some existing words at the same time. Use git diff, git add, git commit to form a new commit that is the changes you just made, and with a commit message about creating an ending where the heroine dies. You should have a clean working directory after the commit.
Now show-branch will show something interesting:
rob@lava:~/books/ppw$ git show-branch
* [heroine] Create an ending where the heroine dies
 ! [master] <message about last commit on master>
--
*  [heroine] Create an ending where the heroine dies
*+ [master] <message about last commit on master>
The top half lists all your branches, with color-coding and identifying symbols. The bottom half tries to describe to you how those branches relate. We will make it even more interesting shortly.
Close any files you have open in your editor, type git checkout master, and open the files again. Don’t panic. You have restored your working directory to the state of all the accumulated changes up to the tip of the master branch, so you do not see your most recent change to your novel. The changes that constitute the brilliant ending about the death of the heroine is saved by git in a place that it is hard for you to find but easy for you to manage. Would you like your ending back? Close your files, type git checkout heroine, and open your files. git has manipulated your files and applied the changes with the ending, so now your novel should contain the new ending.
Perhaps you now see why git can feel a bit foreign and why at some point you will have that “Oh, &\#*!” feeling. Don’t panic. git manipulates the state of the files in your working directory, so in a way those files are ethereal. git stores your commits (collections of changes) safely and can rewind and replay them in ways consistent with your writing while on various branches, using your working directory as a sort of sandbox or laboratory.
Switch back and forth several times between the master and heroine branches with git checkout. Close and open your files in your editor as you let git manipulate your working directory. Run git show-branch liberally. When you are ready, leave a file open when you know a checkout is going to change it. How does your editor react? We don’t know, so can’t help. But a good editor might say something like “File on disk changed, do you want to reload it?” We use an editor that just silently reloads the file, unless there are unsaved edits in it by mistake. If a git checkout results in a file not even being present, our editor leaves it in a state we find very confusing. Sometimes this sort of behavior can be configured in a editor. Experiment until this is not confusing and in the meantime as you are learning, close and reopen files as you instruct git to manipulate the state of your working directory.
Ready for another branch? Make sure you are on master (by running git checkout master). Then make a new branch with git branch hero, and switch to it with git checkout hero. Run git show-branch as you go, studying carefully how the output is changing.
Open the file that is different from the one you edited before, and that is not the one with the typo. Remove some words, and author an ending where the hero dies. Create a commit with these changes, using a commit message about the hero dying. (We might now use “commit” as a verb, and say commit your changes.) Now show-branch will produce something like:
rob@lava:~/books/ppw$ git show-branch
* [hero] Made an ending where the hero is dead
 ! [heroine] Create an ending where the heroine dies
  ! [master] <message about last commit on master>
---
*   [hero] Made an ending where the hero is dead
 +  [heroine] Create an ending where the heroine dies
*++ [master] <message about last commit on master>
Now this is interesting. You have three branches, you are on the hero branch, and the heroine and hero branches contain divergent storylines based on a common beginning. (Everything up to master is common and not reported in this output.) Experiment checking out different branches, run show-branch liberally, and do not forget that you can do things like git show hero even if you are not presently on the hero branch. Get comfortable with the current state of your repository. Notice that each of our two new branches could each be a sequence of many commits, but we just need one commit each for the purposes of this exercise.
As you looked around, did you notice that typographical error you made in the third file when you first start writing? (If not, play along.) Checkout the master branch, open the file with the typo and and edit to correct the mistake. Commit those changes. You should now see:
rob@lava:~/books/ppw$ git show-branch
! [hero] Made an ending where the hero is dead
 ! [heroine] Create an ending where the heroine dies
  * [master] Correct a misspelling
---
  * [master] Correct a misspelling
+   [hero] Made an ending where the hero is dead
 +  [heroine] Create an ending where the heroine dies
++* [master^] <message about last commit on master>
Notice that the new commit has resulted in the tip of the master branch moving to be the commit with the typo fix. Every commit we have made so far has also moved the branch pointer to a new tip, but this is the first time it was really interesting and significant. Notice that through all these pointer movements we no longer have a pointer to the commit where all the branches originated. Notice too that this report still shows the last of our pre-exercise commits, since that is where our three branches diverged from common material.
The good news is that we have two versions of our story and only fixed the typo once, while the bad news is that the master branch has advanced and left the hero and heroine branches behind. Do you see that in the output above? As you checkout the three branches, you will see the effect of the three editing sessions, but they are all disjoint. Said differently, switching to the hero branch results in git manipulating your working directory by applying a sequence of commits that do not contain the commit with the typo fix. And similarly for the heroine branch. But not all is lost.
Here is where you begin to learn how to leverage git. We are going to rewind the hero branch back to the commit where the branch began, and then we are going to replay our changes at the new tip of the master branch. And git knows just how far to rewind. (This would be slightly more interesting if our hero branch had multiple commits beyond the old branch point.)
Ready? Make sure your files are closed, and checkout the hero branch, since this is the branch we are changing. Run git log now and save the output some place, because we want to see how the commit hashes behave. Now run git rebase master. This tells git to rewind the current branch (hero), checkout master, branch off master, and replay the commits that were just rewound (in the opposite order), leaving the hero branch pointer at the tip of the branch. Remember, git manipulates the files in your working directory by applying (replaying) commits in sequence.
You might be aware that in replaying the commits, git might get confused by intermediate changes that had not been present when the commits were based on the original branch point. These situations are known as conflicts. git can be pretty smart about these, but sometimes they require intervention by the author. If you have followed our instructions and used three files, then this will not happen in this exercise. We address conflicts in Chapter 3 and Chapter 4.
Open your files and you should see the hero dying, and you should also see the typo fixed. When I need to run this command, I say to myself “rebase the current branch onto master” to make sure I get it right and do the right thing. Examine the result with git show-branch.
Repeat this procedure to move the heroine branch up to the tip of master. This is an exercise, so we will not hold your hand on this one. Think carefully about each step while reviewing how we rebased the hero branch. When you are done, you should see:
rob@lava:~/books/ppw$ git show-branch
! [hero] Made an ending where the hero is dead
 * [heroine] Create an ending where the heroine dies
  ! [master] Correct a misspelling
---
+   [hero] Made an ending where the hero is dead
 *  [heroine] Create an ending where the heroine dies
+*+ [master] Correct a misspelling
Notice that all of the commits for the beginning of the story, prior to our typo fix are not shown in this output because they are now common to all the branches.
Before you experiment too much, checkout the hero branch, and as beforehand, run git log. Compare with the output you save a few steps back. You should see that the first commit (hero dies) now has a totally different commit hash, the second commit is the typo fix (which was not evident beforehand), and the present third commit is the previous second commit, with the identical commit hash. We will discuss this outside of the exercise. Now, explore your repository thoroughly with the various commands and diagnostics you have learned.
That is it, we are done. Notice that you are maintaining two versions of your novel, can easily switch between working on one or the other, and you can edit common material just once in a way that is reflected in each version. If you made a mess of this (which might be a good thing), just start over with a new repository and have a do-over. Or maybe repeat the exercise with extra commits on each branch for a fuller experience and to solidify your new skills.
The previous exercise begs the question: what to do once we decide which ending we want? The next exercise has the answer.

Checkpoint 2.2.2. Heroine Meets Her End.

Having thoroughly explored the possibilities, we have decided to kill off the heroine as the ending of our novel. Briefly, we are going to take all the changes from the heroine branch and move them into the master branch permanently. Then we will deal with the hero branch.
First, checkout the master branch since that is the branch we will be changing. Then it is as simple as git merge heroine. Through this merge you have taken the changes from the heroine branch and brought them into the master branch. You should have output something like:
rob@lava:~/books/ppw$ git merge heroine
Updating 842f83c..c7fe82d
Fast-forward
 some-file.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
The file listed should be the one you edited with the story of the death of the heroine. For more diagnostics:
rob@lava:~/books/ppw$ git show-branch
! [hero] Made an ending where the hero is dead
 ! [heroine] Create an ending where the heroine dies
  * [master] Create an ending where the heroine dies
---
+   [hero] Made an ending where the hero is dead
 +* [heroine] Create an ending where the heroine dies
++* [hero^] Correct a misspelling
Now the heroine and master branches are identical, and the master pointer has advanced one commit, so that the hero branch still splits off prematurely from master, but the heroine branch does not.
Running git log you will see that the second commit (the typo fix) was reported by the merge as a short commit hash in the output (XXXX) and the first commit (the heroine ending) is also reported by the merge via a short commit hash (XXXX). This is the movement of the master branch pointer, indicated by .. between the hashes.
This is actually a very special type of merge, known as a fast-forward merge, as reported in the output. Since the heroine branch splits off from the tip of the master branch, it is trivial to rewind all the heroine commits back to master and then replay them onto master. Think about that for a minute. Not only is it trivial, it borders on silly.
Here is what really happens in a fast-forward merge. No commits are rewound and replayed. git simply moves the master branch pointer from its original location, so that it points to the same commit as the one that heroine points to. This has two implications. First, none of the commits on the heroine branch change in any way. Second, once the merge is completed master and heroine are redundant, as they point to the same commit, as you can see in the top half of the output from show-branch where the commit message is duplicated.
The previous paragraph is an important realization, but you might be wise to forget about it. In the long run, it is better to think of this merge as bringing (merging) the changes on heroine into master. That is the more general situation, and we remember how the simpler version of describing a fast-forward merge confused more general concepts later.
Now we have a bit of a mess on our hands. The heroine branch pointer is redundant, but really it is obsolete. Our trial death of the heroine is no longer experimental, we have decided that is the ending we want and now it is part of master. Let us kill the heroine branch pointer too.
rob@lava:~/books/ppw$ git branch -d heroine
Deleted branch heroine (was c7fe82d)
Note in the output the short-version commit hash for the commit that is the current tip of master, that is what heroine also pointed to. Most of your work will initially reside on branches, so deleting a branch pointer sounds like a dangerous thing to do, and it is. But git has your back. Try the following:
rob@lava:~/books/ppw$ git branch -d master
error: Cannot delete the branch 'master' which you are currently on.
That is good. You would not want to delete the branch you were on, as then you would be totally lost. So let us try cleaning up another part of our mess. We still have the hero branch, with the ending we did not choose. Let us delete that:
rob@lava:~/books/ppw$ git branch -d hero
error: The branch 'hero' is not fully merged.
If you are sure you want to delete it, run 'git branch -D hero'.
When we deleted the branch pointer to heroine, git knew it was redundant and happily deleted it at once with no questions asked. But the branch pointer hero lets us reference commits we cannot locate any other way, specifically the commit where we let the hero die. So git makes us be a bit more certain and we need the upper-case variant of the switch to get rid of it. Be careful, because if you lose track of commits, they can be hard to resurrect and eventually git will garbage collect them when it does some automatic spring cleaning of your repository. For us, there is no real harm in leaving the hero branch in place, and it is instructive to wait a while before killing it. It is doing no damage where it is, we do not ever need to check it out, though eventually we will grow tired of seeing it in all our diagnostic reports.
Realize that the decision above to merge heroine into master should not be taken lightly, as it is quite difficult to reverse it, and not really in the spirit of how you use git. Branches like hero and heroine are sometimes called topic branches. Or in a nod to software development they may be called feature branches, since they might be used to create and test a new feature for a larger piece of software. I like to refer to a branch like master as the mainline. An essential aspect of working with git is to always work on a branch, and realize that there is little cost to making a branch, merging it, and then deleting the (temporary) branch pointer. I have made branches that only had a lifetime of five minutes. That is a principle.