As requested in the discussion on "Trustable guix pull" [0], I've
recently started signing the commits I push to Savannah.

At first, I set "gpgsign = true" in my Guix repo's Git config. This
requires you to sign every commit you make. It's effective, but I found
it annoying to provide my signing key while doing exploratory hacking,
rebasing a branch on master, etc.

Instead, I want to sign after my final "self-review" and before pushing
to Savannah or sending patches to the list for final review.

So, I've attached a pre-push Git hook that should prevent unsigned
commits from being pushed to any remote [1]. I've also attached a shell
function that will sign commits besides HEAD (useful for signing a range
of commits). I didn't find a more Git-idiomatic way to sign an existing
commit besides HEAD.

Please let me know if you see any problems with this approach, or if you
can suggest some improvements.

[0]
http://debbugs.gnu.org/cgi/bugreport.cgi?bug=22883#16

[1] One could make it remote-specific if desired.
#!/gnu/store/b1yqjimbdh5bf9jnizd4h7yf110744j2-bash-4.3.42/bin/sh

# A hook script that prevents the user from pushing unsigned commits.

# Called by "git push" after it has checked the remote status, but before
# anything has been pushed.  If this script exits with a non-zero status nothing
# will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
#   <local ref> <local sha1> <remote ref> <remote sha1>

z40=0000000000000000000000000000000000000000

while read local_ref local_sha remote_ref remote_sha
do
        if [ "$local_sha" = $z40 ]
        then
                # Handle delete
                :
        else
                if [ "$remote_sha" = $z40 ]
                then
                        # New branch, examine all commits
                        range="$local_sha"
                else
                        # Update to existing branch, examine new commits
                        range="$remote_sha..$local_sha"
                fi


                # Check if push candidate commits are PGP signed.
                git verify-commit $(git rev-list $range) >/dev/null 2>&1

                if [ $? -ne 0 ]
                then
                        echo "error: Please sign these commits before pushing:" 
1>&2
                        echo $range 1>&2

                        exit 1
                fi

        fi
done

exit 0
git-sign() {
        case $# in
                "0") range=HEAD ;;
                "1") range=$1 ;;
                 * ) echo "too many arguments" 1>&2; return 1 ;;
        esac

        # In git-2.8.4, it should be possible to drop the -i option ,
        # along with the override of EDITOR:
        # 
https://git.kernel.org/cgit/git/git.git/commit/?h=next&id=78ec240020db4bdd773830f3d41f4b4bdf9a4e2d
        EDITOR=true git rebase -i "$range" --exec "git commit --amend --no-edit 
--gpg-sign" \
                || git rebase --abort
}

Attachment: signature.asc
Description: PGP signature

Reply via email to