How do I find the date of a git commit using only plumbing commands?
Asked Answered
H

3

3

There is already a question asking how to find the date of a commit in Git. However, all the answers to that question make use of the git show or git log commands, which are both porcelain commands; the standard wisdom on porcelain commands seems to be that their output format may change and that using them in scripts should therefore be avoided wherever possible.

Is there a way to obtain the timestamp of a git commit using only plumbing commands?

Hamnet answered 8/10, 2018 at 19:1 Comment(4)
Is there any reason to require a plumbing command rather than simply enforce a format to a porcelain command? E.g. git log --pretty=format:%aD | head -1 or git show --format="format:%aD" --no-patch HEAD.Faradmeter
@Faradmeter Only that, as I understand it, there's no guarantee that those commands will continue to give identical output in future versions of git. For instance, git 2.7.4 gives me Mon, 8 Oct 2018 for the date in the %aD format, but I don't know whether I can reasonably expect that some future version won't produce Mon, 08 Oct 2018 for the same commit.Hamnet
Well, since the %aD format is specified as "author date, RFC2822 style", the format should remain the same. To get another fixed format, you could use %aI or add a dedicated --date=raw. Neither of these should change with a larger probability than the "plumbing" version.Faradmeter
@Faradmeter RFC2822 permits but doesn't require a leading zero, so ‘RFC2822 style’ doesn't guarantee either variant. I agree it seems unlikely that %aI will change, but since the git docs explicitly say that porcelain commands are subject to change and shouldn't be used in scripts (except of course in cases where they offer a --porcelain option), I would prefer not to rely on second-guessing their future development.Hamnet
S
5

The format of a commit object is as follows:

tree SP SHA_1_NAME_AS_HEX LF
parent SP SHA_1_NAME_AS_HEX LF
...
author SP any number of words SP <email_addr> SP TIMESTAMP SP TZ_OFFSET LF
committer SP any number of words SP <email_addr> SP TIMESTAMP SP TZ_OFFSET LF
LF
commit message LF
LF
extensive commit message

(Where SP is an ASCII space character, 0x20, and LF is an ASCII line-feed character, 0x0a).

So basically to achieve what you want, you:

  1. Call git cat-file commit <commitish_of_interest>
  2. Iterate over what that program dumps to its stdout by LF-terminated lines.
  3. Stop at the line which starts with author SP.
  4. Split it on spaces, and take the last two fields. The first one is the number of seconds since 00:00 in the timezone defined by the second field, which has the format SHHMM where S is a sign, - or +, whcih defines where the offset if to the West of, or to the East of Greenwich, correspondingly, and HHMM is the offset, in HH hours and MM minutes, of the timezone.
  5. Process the values of these two fields to format the timestamp.

Note that the contents of the author and committer fields are the same only on "normal" commits. On rebased commits at least timestamps may differ, and if a commit was rebased by a person other than who actually authored the commit (its author), the fields will be different. The same is also true for commits created from patches via git am.

Spatiotemporal answered 8/10, 2018 at 20:19 Comment(0)
S
2

git cat-file -p HASH_OF_COMMIT will return the content of the commit blob. So you will have access to all the Metadata and so, the author and creation dates.

Solicitous answered 8/10, 2018 at 19:44 Comment(0)
D
1

Git is a little lax on properly distinguishing between plumbing and porcelain. The advice to use plumbing commands in scripts is good, but sometimes a command—such as git log—doesn't have an appropriate plumbing variant, but does have options that make it "plumby", if I can make up a word.

In this case, an appropriate command is (edited, 21 Mar 2019, per comment from Josh):

git -c log.showSignature=false log --pretty=format:%ad --no-walk <hash>

(or the same with %cd as the format directive for committer date; add various date formatting options, to git log itself, or as %aD for instance, to change the date format). Nothing exactly promises that git log might not add new features, such as the log.decorate setting, that might disrupt this, and Git should get with itself and add --porcelain options to the commands that behave like git status does. (Note that git status now has --porcelain and --porcelain=v2, both of which convert it to plumbing. Don't ask me why this isn't spelled --plumbing...)

Dredge answered 8/10, 2018 at 20:36 Comment(4)
Thanks. I've noticed the --porcelain option for status (and for the --word-diff option, but no others, of show and log), but of course this just makes me more wary of using porcelain commands that don't have a --porcelain option :). I suppose some things (e.g. --pretty=format:%at) are particularly unlikely to change, but it would be nice to have it in writing...Hamnet
It would be easy for the Git folks to add now, too, since git log --porcelain could just imply ignore log.decorate and ui.color settings or something close to that... (oops, ui.color is Mercurial, color.* for Git. Too many VCSes...)Dredge
This is not resilient in the face of unusual user configuration! If the user has set log.showSignature, then this will also output several more lines from gpg, which tends to break whatever's expecting a single date.Kirit
@Josh: ugh. So git -c log.showSignature=false log --pretty=..., then. This is why git log should have a plumbing equivalent.Dredge

© 2022 - 2024 — McMap. All rights reserved.