GIT-op 3: Specifying revisions

October 17th, 2009
by squidgit

Now for the great time-saver of this series of tutes.  You’re going to spend a lot of time specifying revisions when working with GIT, but it does a great job of easing this for you.

The way most people get revision specifiers is by using git-log to find the commit they’re looking for and cut-and-pasting the SHA1 hash associated with it.  This hash is the basis of all git specification methods.

Note that the examples I give here relate to a particular command but will work with pretty much any of them.  Most GIT commands pipe their given specifiers through git-rev-list to get the commit IDs out and the docco for that command is a good place to get more info on what I’m going to say.

Tags

The most common form of easy revision specification is tagging.  Tag a version simply by

git tag -a tag_name [SHA1 hash of target commit]

or just

git tag -a tag_name

if you’ve currently got the target commit checked out.

You can use -u <key-id> instead of -a to sign the tag and -v to verify a signed tag.  Once you’ve got a revision tagged, that tag name simply acts as a synonym for the SHA1 has it’s linked to.

Relative

Relative specification is useful if you’re working with commits very close to HEAD.  Drop back 1 or 2 revisions with

git log HEAD^

or

git log HEAD^^

When going further back this can of course be a pain so instead of

git log HEAD^^^^^^^

you can use

git log HEAD~7

to go back 7 commits at once.

Time-based

Now GIT starts to get cool.  It keeps a track of at what your tree looked like at different points in time and you can access this information in a jiff.  Say you’ve just pulled in a bunch of changes and want to look at everything which wasn’t there when you left work; git does a pretty good job of parsing English-like time specifications so you can simply do

git log @{6pm.yesterday}

How about what was on some other branch the day before yesterday but not in your branch now

gitk other_branch@{day.before.yesterday} ^HEAD

Note that a carat before a revision name is logical not, a carat after is one commit before.

As you can see, the @{} specification is very powerful, allowing you to get the status of a tree at a particular time either in normal date specifications or more English-like phrasing.  This is not, however, the same thing as the commits since a particular time.  For example, if you pull in a big old bunch of commits, @{5.minutes.ago} will point to the tree before the pull, not a tree with all but the commits timestamped in the last 5 minutes.  For this you need the –since specifier

gitk --since="5th October"

Other neat specifiers

There are other things you can use to select commits too.

gitk --author="Ben Nizette" --committer="Haavard Skinnemoen"

will show all the patches I wrote which went through Haavard.

gitk --since="yesterday" --grep="gpio"

Will show all patches in the last day which claimed to have something to do with gpio.  We can limit this further

gitk --since="yesterday" drivers/gpio/

will simply show the last day’s commits which touched something in the drivers/gpio/ directory.

gitk --extended-regexp="^foo.*[0-9]*$"

Well that does pretty much as you’d expect really.

gitk --author="Linus Torvalds" --no-merges

Shows you how much actual coding (non-merge) work Linus actually does.

The dots

There can be some confusion when using GIT as to what a series of dots actually does.  2 dots (“..”) is a range specifier

git log HEAD~5..HEAD~2

shows all commits between the 5th and 2nd newest.  It’s a shorthand for

git log HEAD~2 ^HEAD~5

3 dots (“…”) is a symmetric difference, that is

git log HEAD~5...HEAD~2

will show all commits that are in HEAD~5 or HEAD~2 but not both.  In the case where they’re just different points on the same branch 2 and 3 dots do the same thing, but suppose you’re about to merge a pair of branches and you want to see what might conflict,

gitk merge_1...merge_2

shows the commits which are in one branch or the other but not both – i.e. it just lists the commits which could possibly conflict.

You can use this during a merge as well to narrow down which commits introduced the conflict, read their changelog and hopefully get a better understanding of the correct conflict resolution.  Say you’re out at sea with a conflict in complex/subsystem.c; simply

gitk merge_1...merge_2 complex/subsystem.c

To view all patches which could possibly cause that conflict.

Anyway, that’s a quick overview of the specifiers that make my life easier.  You can refer to “man git-rev-list” for other cool options to try out too.

Posted in Uncategorized | Comments (0)