Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
106 lines (91 sloc) 3.29 KB
#!/usr/bin/env python3
# file:
# vim:fileencoding=utf-8:fdm=marker:ft=python
# Copyright © 2012-2018 R.F. Smith <>.
# SPDX-License-Identifier: MIT
# Created: 2012-10-28T14:07:21+01:00
# Last modified: 2018-07-07T13:10:21+0200
"""Get the short hash and most recent commit date for files."""
from concurrent.futures import ThreadPoolExecutor
import logging
import os
import subprocess
import sys
import time
def main():
"""Entry point for gitdates."""
logging.basicConfig(level='WARNING', format='%(levelname)s: %(message)s')
checkfor(['git', '--version'])
# Get a list of all files
allfiles = []
# Get a list of excluded files.
if '.git' not in os.listdir('.'):
logging.error('This directory is not managed by git.')
exargs = ['git', 'ls-files', '-i', '-o', '--exclude-standard']
exc = subprocess.check_output(exargs).split()
for root, dirs, files in os.walk('.'):
for d in ['.git', '__pycache__']:
except ValueError:
tmp = [os.path.join(root, f) for f in files if f not in exc]
allfiles += tmp
# Gather the files' data using a ThreadPoolExecutor.
with ThreadPoolExecutor(max_workers=os.cpu_count()) as tp:
res =, allfiles)
filedata = [r for r in res if r is not None]
# Sort the data (latest modified first) and print it
filedata.sort(key=lambda a: a[2], reverse=True)
dfmt = '%Y-%m-%d %H:%M:%S %Z'
for name, tag, date in filedata:
ds = time.strftime(dfmt, date)
def checkfor(args, rv=0):
Ensure that a program necessary for using this script is available.
If the required utility is not found, this function will exit the program.
args: String or list of strings of commands. A single string may not
contain spaces.
rv: Expected return value from evoking the command.
if isinstance(args, str):
if ' ' in args:
raise ValueError('no spaces in single command allowed')
args = [args]
rc =, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
if rc != rv:
raise OSError'found required program "{args[0]}"')
except OSError as oops:
logging.error(f'required program "{args[0]}" not found: {oops.strerror}.')
def filecheck(fname):
Start a git process to get file info.
Return a string containing the filename, the abbreviated commit hash and the
author date in ISO 8601 format.
fname: Name of the file to check.
A 3-tuple containing the file name, latest short hash and latest
commit date.
args = ['git', '--no-pager', 'log', '-1', '--format=%h|%at', fname]
b = subprocess.check_output(args)
if len(b) == 0:
return None
data = b.decode()[:-1]
h, t = data.split('|')
out = (fname[2:], h, time.gmtime(float(t)))
except (subprocess.CalledProcessError, ValueError):
logging.error('git log failed for "{}"'.format(fname))
return None
return out
if __name__ == '__main__':