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.