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.

#!/usr/bin/perl

# USAGE: git-first-commit-date [--bare|-b] file...

#
# Setup
#

# Directives
use strict;
use warnings;

# Modules
use Getopt::Long;

#
# Options
#

# --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 = "<untracked>" if $reason =~ /^\s*$/s;
    }

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

    # Print out the reason which will be <untracked>, <missing>, 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.

2014-04-20