Subscribe to XML Feed
25 Aug 2010

Note to Self

One of the cool things about Git is that it has strong cryptographic integrity. If you change any bit in the commit data or any of the files it keeps, all the checksums change, including the commit SHA and every commit SHA since that one. However, that means that in order to amend the commit in any way, for instance to add some comments on something or even sign off on a commit, you have to change the SHA of the commit itself.

Wouldn’t it be nice if you could add data to a commit without changing it’s SHA? If only there existed an external mechanism to attach data to a commit without modifying the commit message itself. Happy day! It turns out there exists just such a feature in newer versions of Git! As we can see from the Git 1.6.6 release notes where this new functionality was first introduced:

* "git notes" command to annotate existing commits.

Need any more be said? Well, maybe. How do you use it? What does it do? How can it be useful? I’m not sure I can answer all of these questions, but let’s give it a try. First of all, how does one use it?

Well, to add a note to a specific commit, you only need to run git notes add [commit], like this:

$ git notes add HEAD

This will open up your editor to write your commit message. You can also use the -m option to provide the note right on the command line:

$ git notes add -m 'I approve - Scott' master~1

That will add a note to the first parent on the last commit on the master branch. Now, how to view these notes? The easiest way is with the git log command.

$ git log master
commit 0385bcc3bc66d1b1ec07346c237061574335c3b8
Author: Ryan Tomayko <rtomayko@gmail.com>
Date:   Tue Jun 22 20:09:32 2010 -0700

  yield to run block right before accepting connections

commit 06ca03a20bb01203e2d6b8996e365f46cb6d59bd
Author: Ryan Tomayko <rtomayko@gmail.com>
Date:   Wed May 12 06:47:15 2010 -0700

  no need to delete these header names now

Notes:
  I approve - Scott

You can see the notes appended automatically in the log output. You can only have one note per commit in a namespace though (I will explain namespaces in the next section), so if you want to add a note to that commit, you have to instead edit the existing one. You can either do this by running:

$ git notes edit master~1

Which will open a text editor with the existing note so you can edit it:

I approve - Scott

#
# Write/edit the notes for the following object:
#
# commit 06ca03a20bb01203e2d6b8996e365f46cb6d59bd
# Author: Ryan Tomayko <rtomayko@gmail.com>
# Date:   Wed May 12 06:47:15 2010 -0700
# 
#     no need to delete these header names now
# 
#  kidgloves.rb |    2 --
#  1 files changed, 0 insertions(+), 2 deletions(-)
~                                                                               
~                                                                               
~                                                                               
".git/NOTES_EDITMSG" 13L, 338C

Sort of weird, but it works. If you just want to add something to the end of an existing note, you can run git notes append SHA, but only in newer versions of Git (I think 1.7.1 and above).

Notes Namespaces

Since you can only have one note per commit, Git allows you to have multiple namespaces for your notes. The default namespace is called ‘commits’, but you can change that. Let’s say we’re using the ‘commits’ notes namespace to store general comments but we want to also store bugzilla information for our commits. We can also have a ‘bugzilla’ namespace. Here is how we would add a bug number to a commit under the bugzilla namespace:

$ git notes --ref=bugzilla add -m 'bug #15' 0385bcc3

However, now you have to tell Git to specifically look in that namespace:

$ git log --show-notes=bugzilla
commit 0385bcc3bc66d1b1ec07346c237061574335c3b8
Author: Ryan Tomayko <rtomayko@gmail.com>
Date:   Tue Jun 22 20:09:32 2010 -0700

  yield to run block right before accepting connections

Notes (bugzilla):
  bug #15

commit 06ca03a20bb01203e2d6b8996e365f46cb6d59bd
Author: Ryan Tomayko <rtomayko@gmail.com>
Date:   Wed May 12 06:47:15 2010 -0700

  no need to delete these header names now

Notes:
  I approve - Scott

Notice that it also will show your normal notes. You can actually have it show notes from all your namespaces by running git log --show-notes=* - if you have a lot of them, you may want to just alias that. Here is what your log output might look like if you have a number of notes namespaces:

$ git log -1 --show-notes=*
commit 0385bcc3bc66d1b1ec07346c237061574335c3b8
Author: Ryan Tomayko <rtomayko@gmail.com>
Date:   Tue Jun 22 20:09:32 2010 -0700

    yield to run block right before accepting connections

Notes:
    I approve of this, too - Scott

Notes (bugzilla):
    bug #15

Notes (build):
    build successful (8/13/10)

You can also switch the current namespace you’re using so that the default for writing and showing notes is not ‘commits’ but, say, ‘bugzilla’ instead. If you export the variable GIT_NOTES_REF to point to something different, then the --ref and --show-notes options are not neccesary. For example:

$ export GIT_NOTES_REF=refs/notes/bugzilla

That will set your default to ‘bugzilla’ instead. It has to start with the ‘refs/notes/’ though.

Sharing Notes

Now, here is where the general usability of this really breaks down. I am hoping that this will be improved in the future and I put off writing this post because of my concern with this phase of the process, but I figured it has interesting enough functionality as-is that someone might want to play with it.

So, the notes (as you may have noticed in the previous section) are stored as references, just like branches and tags. This means you can push them to a server. However, Git has a bit of magic built in to expand a branch name like ‘master’ to what it really is, which is ‘refs/heads/master’. Unfortunately, Git has no such magic built in for notes. So, to push your notes to a server, you cannot simply run something like git push origin bugzilla. Git will do this:

$ git push origin bugzilla
error: src refspec bugzilla does not match any.
error: failed to push some refs to 'git@github.com:schacon/kidgloves.git'

However, you can push anything under ‘refs/’ to a server, you just need to be more explicit about it. If you run this it will work fine:

$ git push origin refs/notes/bugzilla
Counting objects: 3, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 263 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:schacon/kidgloves.git
 * [new branch]      refs/notes/bugzilla -> refs/notes/bugzilla

In fact, you may want to just make that git push origin refs/notes/* which will push all your notes. This is what Git does normally for something like tags. When you run git push origin --tags it basically expands to git push origin refs/tags/*.

Getting Notes

Unfortunately, getting notes is even more difficult. Not only is there no git fetch --notes or something, you have to specify both sides of the refspec (as far as I can tell).

$ git fetch origin refs/notes/*:refs/notes/*
remote: Counting objects: 12, done.
remote: Compressing objects: 100% (8/8), done.
remote: Total 12 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (12/12), done.
From github.com:schacon/kidgloves
 * [new branch]      refs/notes/bugzilla -> refs/notes/bugzilla

That is basically the only way to get them into your repository from the server. Yay. If you want to, you can setup your Git config file to automatically pull them down though. If you look at your .git/config file you should have a section that looks like this:

[remote "origin"]
  fetch = +refs/heads/*:refs/remotes/origin/*
  url = git@github.com:schacon/kidgloves.git

The ‘fetch’ line is the refspec of what Git will try to do if you run just git fetch origin. It contains the magic formula of what Git will fetch and store local references to. For instance, in this case it will take every branch on the server and give you a local branch under ‘remotes/origin/’ so you can reference the ‘master’ branch on the server as ‘remotes/origin/master’ or just ‘origin/master’ (it will look under ‘remotes’ when it’s trying to figure out what you’re doing). If you change that line to fetch = +refs/heads/*:refs/remotes/manamana/* then even though your remote is named ‘origin’, the master branch from your ‘origin’ server will be under ‘manamana/master’.

Anyhow, you can use this to make your notes fetching easier. If you add multiple fetch lines, it will do them all. So in addition to the current fetch line, you can add a line that looks like this:

  fetch = +refs/notes/*:refs/notes/*

Which says also get all the notes references on the server and store them as though they were local notes. Or you can namespace them if you want, but that can cause issues when you try to push them back again.

Collaborating on Notes

Now, this is where the main problem is. Merging notes is super difficult. This means that if you pull down someone’s notes, you edit any note in a namespace locally and the other developer edits any note in that same namespace, you’re going to have a hard time getting them back in sync. When the second person tries to push their notes it will look like a non-fast-forward just like a normal branch update, but unline a normal branch you can’t just run git pull and then try again. You have to check out your notes ref as if it were a normal branch, which will look ridiculously confusing and then do the merge and then switch back. It is do-able, but probably not something you really want to do.

Because of this, it’s probably best to namespace your notes or better just have an automated process create them (like build statuses or bugzilla artifacts). If only one entity is updating your notes, you won’t have merge issues. However, if you want to use them to comment on commits within a team, it is going to be a bit painful.

So far, I’ve heard of people using them to have their ticketing system attach metadata automatically or have a system attach associated mailing list emails to commits they concern. Other people just use them entirely locally without pushing them anywhere to store reminders for themselves and whatnot.
Probably a good start, but the ambitious among you may come up with something else interesting to do. Let me know!

View Comments
09 Jun 2010

Pro Git 简体中文版

The amazing Chunzi, in addition to translating and helping to coordinate the translation of the Chinese version of Pro Git, has just sent me a draft epub of the book in Chinese.

As he says:

“下载地址:http://cl.ly/da7a450319adfac01108

此书基本完成翻译,但仍有大半尚未审阅。今心血来潮,做了 epub 版本,可在 iPad 上阅读。生成代码在 http://github.com/chunzi/progit/tree/master/epub-zh。

既然是开源图书,所以优先级比较低。后续更新时再作新版。”

I have also uploaded the file to S3 in addition to his cl.ly link. If you want Pro Git in Chinese on your iPad, you can download it here.

祝你好运!

View Comments
06 Jun 2010

Kindle Version Available

When Pro Git was first released, I asked about being able to get it on my Kindle. In fact, one of the very first people to read the book was @adelcambre with a mobi file I generated myself. It was horrible looking, because I didn’t do it very well, but it did work. My editor at Apress wanted to get a professional Kindle version produced, but wasn’t sure if it was going to get done anytime soon.

Well, I just randomly found out that it did in fact finally get done. About 9 months after it was first published, I saw on my Amazon referal account that I had sold a Kindle version, which confused me. However, I went to Amazon and there it was:

I assume it looks much, much better than the version I originally did for Andy. (I should probably get myself a copy). So, if you feel like reading it on the Kindle, have at it. There have also been a number of people who have asked me how they can support the book without getting a dead-tree version, so here’s your chance. You can even get a Kindle reader for your computer, so you can search through it and whatnot. Enjoy!

View Comments
17 May 2010

Pro Git on your iPad

The awesome guys at Media Temple have converted the Pro Git book into ePub format that looks great on the iBook reader on the iPad.

You can download it here and just drop the ePub file onto iTunes to upload it into your iPad. For reading Pro Git on the road, this is a great format and MT did an amazing job at making it look good on that platform.

Check it out - here is Pro Git in your bookshelf:

The title page:

And a chapter with illustrations:

Sweet - thanks guys!

Update: I’ve added a Stanza Atom catalog feed for those of you who want to load the epub book onto your Android running Aldiko or iPhone running Stanza. To download the book, simply add a custom catalog with this URL:

  http://progit.org/stanza.atom
View Comments

Older Posts

Pro Git CliffNotes 22 Apr 2010 Comments
Git Loves the Environment 11 Apr 2010 Comments
Replace Kicker 17 Mar 2010 Comments
Git's Little Bundle of Joy 10 Mar 2010 Comments
Rerere Your Boat... 08 Mar 2010 Comments
Smart HTTP Transport 04 Mar 2010 Comments
Undoing Merges 02 Mar 2010 Comments
Translate This 19 Aug 2009 Comments
The Gory Details 28 Jul 2009 Comments
Welcome to the Pro Git website 11 Feb 2009 Comments