Skip to content

Instantly share code, notes, and snippets.

@rsvp
Last active Aug 14, 2017
Embed
What would you like to do?
git-lu :: Git script gets LAST COMMIT DATE / TIMES (since modification time is unreliable), given path/filename(s) -- supports wildcards, SHA hash ref.
#!/usr/bin/env bash
# bash 4.3.11(1) Linux 3.13.0 git 1.9.1 Date : 2017-08-14
#
# _______________| git-lu : get last commit date for file(s)
# since git can change OS file modification times!
#
# Usage: git lu [filename(s), wildcards optional]
#
# Example: $ git lu *.ipynb # SAMPLE OUTPUT FORMATTING
# 0000-00-00 _Not_committed _No_SHA 00-tpl-v4.ipynb
# 2014-12-07 12:02:38 -0800 362d82c fred-wage-capital.ipynb
# 2015-01-17 13:38:57 -0800 1530474 fred-debt-pop.ipynb
#
# Dependencies: git-log (built-in)
#
# CHANGE LOG
# 2017-08-14 Error code 117 for get_data fail.
# 2017-08-11 First version.
# _____ PREAMBLE_v3: settings, variables, and error handling.
#
LC_ALL=POSIX
# locale means "ASCII, US English, no special rules,
# output per ISO and RFC standards."
# Esp. use ASCII encoding for glob and sorting characters.
shopt -s extglob
# ^set extended glob for pattern matching.
shopt -s failglob
# ^failed pattern matching signals error.
set -e
# ^errors checked: immediate exit if a command has non-zero status.
set -o pipefail
# ^exit status on fail within pipe, not (default) last command.
set -u
# ^unassigned variables shall be errors.
# Example of default VARIABLE ASSIGNMENT: arg1=${1:-'foo'}
arg1=${1:-'NULL'}
program=${0##*/} # similar to using basename
memf=$( mktemp /dev/shm/88_${program}_tmp.XXXXXXXXXX )
mem2=$( mktemp /dev/shm/88_${program}_tmp.XXXXXXXXXX )
errf=$( mktemp /dev/shm/88_${program}_tmp.XXXXXXXXXX )
cleanup () {
# Delete temporary files, then optionally exit given status.
local status=${1:-'0'}
rm -f $memf $mem2 $errf
[ $status = '-1' ] || exit $status # thus -1 prevents exit.
} #--------------------------------------------------------------------
warn () {
# Message with basename to stderr. Usage: warn "message"
echo -e "\n !! ${program}: $1 " >&2
} #--------------------------------------------------------------------
die () {
# Exit with status of most recent command or custom status, after
# cleanup and warn. Usage: command || die "message" [status]
local status=${2:-"$?"}
# cat $errf >&2
cleanup -1 && warn "$1" && exit $status
} #--------------------------------------------------------------------
trap "die 'SIG disruption, but cleanup finished.' 114" 1 2 3 15
# Cleanup after INTERRUPT: 1=SIGHUP, 2=SIGINT, 3=SIGQUIT, 15=SIGTERM
trap "die 'unhandled ERR via trap, but cleanup finished.' 116" ERR
# Cleanup after command failure unless it's part of a test clause.
#
# _______________ :: BEGIN Script ::::::::::::::::::::::::::::::::::::::::
# __________ PRE-QUALIFY command-line parameters now
# because git-log error message is ugly.
[ "$arg1" = 'NULL' ] && die "SPECIFY filename(s), wildcards optional." 113
# ___ since "in" is omitted, positional parameters assumed, i.e. "$@"
for file ; do
[ -e "$file" ] || die "INVALID file specified: $file" 115
done
# __________ MAIN function
get_date () {
# ^Expects a single filename as $1 argument...
git log -1 --pretty="%ci %h" -- "$1" > $mem2 2>> $errf
# %ci is commit date ISO style: 2017-08-04 09:30:06 -0700
# %cd would be commit date without time portion.
# %h gives short SHA hash (%H for full hash)
[ -s $mem2 ] && cat $mem2 || echo "0000-00-00 _Not_committed _No_SHA"
# UNCOMMITTED file gets ZEROS date and _No_SHA reference.
}
# ___ since "in" is omitted, positional parameters assumed, i.e. "$@"
for file ; do
echo "$(get_date "$file") $file"
done > $memf
[ -s $errf ] && die "get_date FAIL. Must be within a git repository." 117
# __________ Post-production
# [ ] - Possibly add colors for readability...
sort $memf
# Dates sorted from 0000-00-00 to most recent commit date.
# Minor discrepancies possible if commits come from different time zones.
cleanup # Instead of: trap arg EXIT
# _______________ EOS :: END of Script ::::::::::::::::::::::::::::::::::::::::
# vim: set fileencoding=utf-8 ff=unix tw=78 ai syn=sh :
@rsvp

This comment has been minimized.

Copy link
Owner Author

@rsvp rsvp commented Aug 13, 2017

Installation

The script should be placed within the scope of your $PATH.
By convention, git itself should be able to access this shell script
by the way it is named (see Example).

The script properly handles ignored and uncommitted files as well.

Story

For my Linux shell: ls -l -A -t -r is aliased to lu which is time-sorted.
The script above is the git analogue since OS modification times cannot be
relied upon as last commit times.

By the way, lu is French for "read" (past tense).

Shortcut to this gist: https://git.io/git-lu

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment