Up a Level
Git tips: Getting the first commit date of a file

A few years ago (2012), I wrote a post which counted up how many words I wrote in eleven years and also broke it down by month.

While I haven't done a post on that again (I figured that most people don't care), I have been maintaining the metadata on the top of each chapter in case I ever do.

However, when I'm occasionally obsessing about writing, I don't put in the headers and I have to do it after the fact. When that comes, I have to go back and figure out when I actually started a chapter (which is my definition of the date header).

When there is only one or two files, it isn't too hard, but when it is thirty chapters of a commission, I usually try to find a program to help me figure out the dates.

I frequently use Perl for my one-off programs. The following Perl script takes one or more files and simply gives the last date in a semi-useful manner.


USAGE: git-first-commit-date [–bare|-b] file…



use strict; use warnings;


use Getopt::Long;


–bare means don't put the filename in the line. Otherwise it will

–put the filename, followed by a colon and a space.

my $bare = 0;

&GetOptions( “b|bare!” => $bare, );

Go through the input files.

while (@ARGV) { # Pull out the filename. my $filename = shift @ARGV; my $reason = "<missing>"; my $valid = 0;

if (-f $filename)
    # Get the date for the file. We tell Git to only give us the
    # ISO date (https://xkcd.com/1179/) for the files using
    # --pretty=format:%ad --date=short. We use --follow to handle
    # renames. Finally, we get the last one (the earliest
    # date). --reverse didn't seem to work, so we skip that.
    $reason = `git log --follow --pretty=format:%ad --date=short "$filename" | tail -n 1`;
    chomp $reason;

    # If we have a date, use it. Otherwise say it is untracked.
    $reason = "&lt;untracked&gt;" if $reason =~ /^\s*$/s;

# Write out the results.
if (!$bare)
    print "$filename: ";

# Print out the reason which will be &lt;untracked&gt;, &lt;missing&gt;, or a
# date.
print $reason, "\n";


When it runs, you get something like this:

$ git-first-commit-date untracked-file missing-file chapter-00.markdown 
untracked-file: <untracked>
missing-file: <missing>
chapter-00.markdown: 2014-02-20

It's a little one-off program, but it solves a very specific problem for me.