#!/bin/bash # Bash unofficial strict mode set -euo pipefail # http://www.dwheeler.com/essays/filenames-in-shell.html IFS=$'\n\t' function define() { IFS='\n' read -r -d '' ${1} || true; } define TMPL << 'EOF' { "commit": "%H", "tree": "%T", "parent": "%P", "refs": "%D", "encoding": "%e", "message": "MSG", "commit_notes": "%N", "verification_flag": "%G?", "signer": "%GS", "signer_key": "%GK", "author": { "name": "AUTHOR", "email": "%aE", "date": "%ai" }, "commiter": { "name": "COMMITER", "email": "%cE", "date": "%ci" } } EOF function strip () { # strip newlines then strip all whitespace sed -e :a -e '/./,$!d;/^\n*$/{$d;N;};/\n$/ba' | sed -E 's/^\s+|\s+$//g' } function sanitize () { # escape newline, escape quotes, escape backslash, escape tabs, fix double escapes, delete invalid characters sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/"/\\"/g' | sed 's/\\\\/\\/g' | sed 's/\t/\\t/g' | sed -E 's/\\([^nt"])/\\\\\1/g' | tr -d '\r\0\t' | sed -E 's/[\x00-\x1f]//g'; } function field () { git show -s --format="$1" "$2" | strip | sanitize } git log --pretty=format:'%H' | while IFS='' read -r hash; do TMP="$TMPL" TMP="${TMP/MSG/$(field "%B" $hash)}" TMP="${TMP/AUTHOR/$(field "%aN" $hash)}" TMP="${TMP/COMMITER/$(field "%cN" $hash)}" git show $hash -s --format="$TMP" done # | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/\x0\x0\x0/\0\0\n/g' | sed 's/\"/\\\"/g' | sed 's/\x0\x0/"/g' | sed 's/\x0/" "/g' | tr -d '\000' \ # | xargs printf '{"commit": "%s","abbreviated_commit": "%s","tree": "%s","abbreviated_tree": "%s","parent": "%s","abbreviated_parent": "%s","refs": "%s","encoding": "%s","subject": "%s","sanitized_subject_line": "%s","body": "%s","commit_notes": "%s","verification_flag": "%s","signer": "%s","signer_key": "%s","author": {"name": "%s","email": "%s","date": "%s"},"commiter": {"name": "%s","email": "%s","date": "%s"}}\n' # | sed "$ s/,$//" | sed ':a;N;$!ba;s/\r\n\([^{]\)/\\n\1/g'| awk 'BEGIN { print("[") } { print(%s) } END { print("]") }' | sed ':a;N;$!ba;s/\n/ /g'