#!/bin/sh # atgc-change-id-hook v1 # # Stamp a `Change-Id:` trailer on a commit that has none. The id is what a # stacked pull request is matched to its commits by, and it is the one thing # about a commit that survives an amend or a rebase — so it has to be written # when the commit is, not bolted on afterwards by rewriting the branch. # # Installed by `atgc repo configure`. Deleting this file is a supported way # to turn it off; atgc reinstalls it only when the slot is empty. # # Nothing here prompts, and nothing here fails a commit: every path that # cannot produce an id leaves the message exactly as it found it and exits 0. msg="$1" [ -n "$msg" ] || exit 0 [ -f "$msg" ] || exit 0 # Already carries one. Also covers a commit being amended, whose trailer is # already in the message being re-edited. if grep -qi '^Change-Id:[[:space:]]*I' "$msg"; then exit 0 fi # A fixup or squash belongs to the commit it names, and will be folded into # it: an id of its own would be one more id than there are changes. if head -n 1 "$msg" | grep -qE '^(fixup|squash|amend)! '; then exit 0 fi # An empty message aborts the commit. Stamping one would turn that abort into # a commit whose entire message is a trailer. if [ -z "$(sed -e 's/#.*//' -e '/^[[:space:]]*$/d' "$msg")" ]; then exit 0 fi # Unique, not reproducible: author, time, pid and the message itself. Cut to # the 40 hex digits a change-id is written with, since a repository using # sha256 hashes would otherwise hand back 64. id=$( { git var GIT_AUTHOR_IDENT 2>/dev/null echo "$$" date +%s 2>/dev/null cat "$msg" } | git hash-object --stdin 2>/dev/null | cut -c1-40 ) [ -n "$id" ] || exit 0 # `interpret-trailers` places it in the trailer block, after a Signed-off-by # and before nothing, and knows about comments and the scissors line — all # the reasons not to append a line by hand here. git interpret-trailers --if-exists doNothing --trailer "Change-Id: I$id" --in-place "$msg" 2>/dev/null exit 0