Git’s guts: Branches, HEAD, and fast-forwards

Lets get some learning done. There are a few questions that keep cropping up when I introduce people to Git, so I thought I’d post some answers as a mini-series of blog posts. I’ll cover some fundamentals, while trying not to retread too much ground that the fantastic Git community book already covers so well. Instead I’m going to talk about things that should help you understand what you and Git are doing day-to-day.

What’s a branch?

I know what you’re thinking. “C’mon, we know what a branch is”. A branch is a copy of a source tree, that’s maintained separately from it’s parent; that’s what we perceive a branch to be, and that’s how we’re used to dealing with them. Sometimes they’re physical copies (VSS and TFS), other times they’re lightweight copies (SVN), but they’re copies non-the-less. Or are they?

Lets look at it a different way. The Git way.

Git works a little differently than most other version control systems. It doesn’t store changes using delta encoding, where complete files are built up by stacking differences contained in each commit. Instead, in Git each commit stores a snapshot of how the repository looked when the commit occurred; a commit also contains a bit of meta-data, author, date, but more importantly a reference to the parent of the commit (the previous commit, usually).

That’s a bit weird, I know, but bare with me.

So what is a branch? Nothing more than a pointer to a commit (with a name). There’s nothing physical about it, nothing is created, moved, copied, nothing. A branch contains no history, and has no idea of what it consists of beyond the reference to a single commit.

Given a stack of commits:

The branch references the newest commit. If you were to make another commit in this branch, the branch’s reference would be updated to point at the new commit.

The history is built up by recursing over the commits through each’s parent.

What’s HEAD?

Now that you know what a branch is, this one is easy. HEAD is a reference to the latest commit in the branch you’re in.

Given these two branches:

If you had master checked out, HEAD would reference e34fa33, the exact same commit that the master branch itself references. If you had feature checked out, HEAD would reference dde3e1. With that in mind, as both HEAD and a branch is just a reference to a commit, it is sometimes said that HEAD points to the current branch you’re on; while this is not strictly true, in most circumstances it’s close enough.

What’s a fast-forward?

A fast-forward is what Git does when you merge or rebase against a branch that is simply ahead the one you have checked-out.

Given the following branch setup:

You’ve got both branches referencing the same commit. They’ve both got exactly the same history. Now commit something to feature.

The master branch is still referencing 7ddac6c, while feature has advanced by two commits. The feature branch can now be considered ahead of master.

It’s now quite easy to see what’ll happen when Git does a fast-forward. It simply updates the master branch to reference the same commit that feature does. No changes are made to the repository itself, as the commits from feature already contain all the necessary changes.

Your repository history would now look like this:

When doesn’t a fast-forward happen?

Fast-forwards don’t happen in situations where changes have been made in the original branch and the new branch.

If you were to merge or rebase feature onto master, Git would be unable to do a fast-forward because the trees have both diverged. Considering Git commits are immutable, there’s no way for Git to get the commits from feature into master without changing their parent references.

For more info on all this object malarky, I’d recommend reading the Git community book.

If there’s anything that you’re not sure about ask in the comments and I’ll try get it into my next post.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

This entry was posted in git. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Adriaan

    Nice post. Would be nice to see how a merge of the last example would look like.

  • http://www.lostechies.com/members/jagregory/default.aspx James Gregory

    @Adriaan Thanks. I think merging will be the topic of my next post, you’re not the first to have requested it.

  • Sean Stapleton

    ” while feature has advanced by two commits.”

    The graphic above this reuses the id from one of the previous graphics (c66d9ed). I assume this is just a copy-paste error, but it hurts clarity. The reused id also appears in the graphic below, where you have c66d9ed listed twice in the commit chain.

  • http://www.lostechies.com/members/jagregory/default.aspx James Gregory

    @Sean: Well spotted. Corrected, sorry about that.