When working in git, you most commonly trace a repository's history using commits' SHA-1 hashes. To revert to a previous commit, you might write something like this:
The problem is, we tend not to think in hashes. Rather, we wonder what changes we made yesterday, or remember that a now-broken piece of code worked perfectly a week ago. Although we can use
git log to track down the commit we want, there has to be an easier way. Luckily git understands our human quirks, and offers us just that.
Understanding dates in Git: author date vs. committer date & 'approxidate'
There are two kinds of timestamp in git: a
GIT_AUTHOR_DATE and a
GIT_COMMITTER_DATE. Although in most cases they both store the same value, they serve slightly different purposes. As the Pro Git Book explains:
The author is the person who originally wrote the work, whereas the committer is the person who last applied the work.
So if, for instance, you send in a patch to a project, the author date will be when you made the original commit but the committer date will be when the patch was applied. Another common scenario is rebasing: rebasing will alter the commit date, but not the author date.
This distinction is worth mentioning because of an inconsistency in git that Carl Worth points out:
git logdisplays author dates as “Date” but then uses commit dates when given a
--sinceoption. That seems like broken defaults to me.
In other words, all the methods listed below rely on the committer date, even though you're used to seeing the author date. As mentioned, most of the time they'll be the same, but to see committer dates in the log just use:
Date parsing with 'approxidate'
Git employs a kind of date parsing which will be familiar to any rubyists who've used Chronic. The parser, called 'approxidate' is very flexible, and allows both fixed dates in any format you can dream up (“10-11-1998”, “Fri Jun 4 15:46:55 2010 +0200”, “9/9/83”) and relative dates (“today”, “1 month 2 days ago”, “six minutes ago”). You can include days of the week (“last Tuesday”), timezones (“3PM GMT”) and 'named' times (“noon”, “tea time”).
Approxidate isn't really documented anywhere, but the code for the parser is very readable, so check it out to get an idea of the kind of formats git will accept.
log, whatchanged, –since and –until
OK, so how about if we want to look at all the commits made since yesterday? All we need is the
We can also use
--until to fetch all commits up to a certain date; or of course we can use both options in tandem:
--until can also be used with
Worth noting: instead of
--since you can use
--after, if that's more your style.
revert, diff and the @ construct
Not all git commands have options like
--until. So what if we wanted to revert back to our repo as it was a month ago? Luckily git provides us with a more generic way to reference commits using dates, with the @ construct:
(where master is the name of the branch you're working on).
This lets us do clever things, like:
which will compare the repo as it was yesterday, and as it was 1 year, 6 months ago.
Change history: amending and editing dates
There might be certain situations where you want to alter the timestamps git assigns to commits. There are a couple of ways that you can do this.
--date option allows you to specify the author date that git attaches to the commit. Here we can't use approxidate unfortunately, only fixed dates will work (YYYY.MM.DD, MM/DD/YYYY, DD.MM.YYYY, RFC 2822 and ISO 8601 are all valid).
We can also use
amend to change the timestamp of a previous commit:
--date will only change the
GIT_COMMITTER_DATE. If this is a problem, you may need to…
A word of warning - overriding
GIT_COMMITER_DATE is somewhat frowned upon:
It should be never overridden, unless you know you absolutely need to override it (to ensure the commit gets the same ID as another or when migrating history around)
The code above will alter both timestamps. Amending a commit in the past is more tricky, but the GitFAQ provides us with a handy bash script:
#!/bin/sh # # Rewrite all branches to modify the date of one specific commit in a repo. # # Sample date format: Fri Jan 2 21:38:53 2009 -0800 # ISO8601 and RFC822 dates will also work. # # Note: filter-branch is picky about the commit argument. As of 18.104.22.168, # a hex ID will work, the symbolic revision HEAD will fail silently, # and the usability of more exotic rev specs was not tested by the author. # # Copyright (c) Eric S. Raymond, 2010-08-01. BSD terms apply (if anybody really thinks that this # script is long and non-obvious enough to fall under copyright law). # commit="$1" date="$2" git filter-branch --env-filter \ "if test \$GIT_COMMIT = '$commit' then export GIT_AUTHOR_DATE export GIT_COMMITTER_DATE GIT_AUTHOR_DATE='$date' GIT_COMMITTER_DATE='$date' fi" && rm -fr "$(git rev-parse --git-dir)/refs/original/"
Instead of setting the date explicitly, we can also use
GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" ( source: this gist ) to match up the committer date to the author date.