So over the past couple of weeks I've decided to note down my billable times in git commits. This allows a client to see how much they're spending but also it means I don't have to make it all up at the end of the week. It's good for both of us.

I've been leaving little coded messages at the end of my commits. Here's a sample:

Flushed out the flux capacitor, drank some BRAWNDO.


That means I spent ~20 minutes doing something. 1 is an hour.

So I have these littered throughout my projects. The question now is, how do I pick these out from a certain date, and add them up so I can bill the client?

I should add that these are not single-user projects. Some are collaborative other developer may wish to follow my lead and handle their billing this way. I need to filter on just my commits.


We can limit what logs git outputs based on who (--author: can be a name or email) and when (--since: takes a bizarre number of formats and fuzzy strings).

Then we just need to find lines that start with T=, add plus symbols between all the numbers and feed them into the command line calculator bc.

$ git log --all --author=Oli --since="1 week" | grep -oP '(?<=T=)[\d.]+' | paste -sd+ | bc

Or with an awk:

$ git log --all --author=Oli --since="1 week" | awk -F= '/T\=[0-9.]+/{t+=$2} END{print t}'

Urgh, only 30.5 hours?! It feels like 60.

In a bigger project with millions of commits, it might be wise to add on a --grep '^T=' to the git log command. This will do a quick filter down internally before the external grep conditions the data.

A problem occurred after starting to use this. I needed to reset the clock. I've been through several iterations of script (lots of clever piping) but thanks to git rebase and other tree-messing-up operations, the best way to sort commits by date, and so they only show up once is to do it manually.

Therefore I present the ugliest functional script I think I've ever written.


function dfmt {
    date -d$1 +%Y-%m-%d

while read DATE HASH; do
    [[ -z $STARTD ]] && STARTD=$DATE
    while read M; do
        if [[ $M == "RESET" ]]; then
            echo -e "$(dfmt $STARTD) to $(dfmt $DATE)\t$TOTAL"
            TOTAL=$(bc <<< "$TOTAL + $M")
    done < <(git show -s $HASH | grep -oP '(?<=T=)([\d.]+|RESET)')
done < <(git log --author=Oli --all --pretty="%aI %H" | sort -u -k1,1)

echo -e "$(dfmt $STARTD) to $(dfmt now)*\t$TOTAL\t" $(bc <<< "scale=2; $TOTAL / 8.0") " days"

This parses every commit and outputs every billing chunk (between T=RESET tokens). It's much better. It's much slower. It seems inevitable that I'll rewrite this in Python one day but I have to do paying work for now :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.