commit-msg: Ignore all kinds of fixup/squash/amend temp commits

There are also "fixup!" and "amend!". During the review process of
https://gerrit-review.googlesource.com/c/gerrit/+/339555 fixup was
also ignored, but it was suggested to not ignore it, since it will
be eventually discarded anyway.

Executing the hook has a cost (especially for Windows, where shell
scripts are slow), and there is no reason to run the hook just for
discarding it shortly afterwards.

Moreover, *not* having Change-Id prevents accidental push of those
commits. It also enables the user to mark a commit that should not
be pushed by using a prefix such as "temp!".

Change the regex to ignore any word that is lowercase and followed
by an exclamation mark.

Add a new value to gerrit.createChangeId configuration that forces
Change-Id creation even for temp commits.

Amends commit 490149df4d93c0597429d4174dcdb71d6664c105.

Release-Notes: The commit-msg hook now operates as a no-op for temporary commits, which are identified by a lowercase word followed by an exclamation mark (e.g., fixup!, squash!). Configure gerrit.createChangeId to 'always' to force creation on these cases.
Change-Id: I8aa5384f23f5a119379950209083473e594d6e8d
diff --git a/Documentation/cmd-hook-commit-msg.txt b/Documentation/cmd-hook-commit-msg.txt
index e547822..ccb6d58 100644
--- a/Documentation/cmd-hook-commit-msg.txt
+++ b/Documentation/cmd-hook-commit-msg.txt
@@ -56,6 +56,12 @@
 The `Change-Id` will not be added if `gerrit.createChangeId` is set
 to `false` in the git config.
 
+The `Change-Id` will not be added to temporary commits created by
+`git commit --fixup` or `git commit --squash`, as well as commits
+with a subject line that begins with a lowercase word followed by
+an exclamation mark (e.g., `nopush!`). To override this behavior,
+set `gerrit.createChangeId` to `always` in the git config.
+
 If `gerrit.reviewUrl` is set to the base URL of the Gerrit server that
 changes are uploaded to (e.g. `https://gerrit-review.googlesource.com/`)
 in the git config, then instead of adding a `Change-Id` trailer, a `Link`
diff --git a/resources/com/google/gerrit/server/commit-msg_test.sh b/resources/com/google/gerrit/server/commit-msg_test.sh
index 1772eb7..80e40bd 100755
--- a/resources/com/google/gerrit/server/commit-msg_test.sh
+++ b/resources/com/google/gerrit/server/commit-msg_test.sh
@@ -135,9 +135,9 @@
   fi
 }
 
-function test_suppress_squash {
+function suppress_squash_like {
   cat << EOF > input
-squash! bla bla
+$1! bla bla
 EOF
 
   ${hook} input || fail "failed hook execution"
@@ -147,6 +147,30 @@
   fi
 }
 
+function test_suppress_squash {
+  # test for standard git prefixes
+  suppress_squash_like squash
+  suppress_squash_like fixup
+  suppress_squash_like amend
+  # test for custom prefixes
+  suppress_squash_like temp
+  suppress_squash_like nopush
+}
+
+function test_always_create {
+  cat << EOF > input
+squash! bla bla
+EOF
+
+  git config gerrit.createChangeId always
+  ${hook} input || fail "failed hook execution"
+  git config --unset gerrit.createChangeId
+  found=$(grep -c '^Change-Id' input || true)
+  if [[ "${found}" != "1" ]]; then
+    fail "got ${found} Change-Ids, want 1"
+  fi
+}
+
 # gerrit.reviewUrl causes us to create Link instead of Change-Id.
 function test_link {
   cat << EOF > input
diff --git a/resources/com/google/gerrit/server/tools/root/hooks/commit-msg b/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
index 5c7dffa..13aa86c 100755
--- a/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
+++ b/resources/com/google/gerrit/server/tools/root/hooks/commit-msg
@@ -30,14 +30,20 @@
 fi
 
 # Do not create a change id if requested
-if test "false" = "$(git config --bool --get gerrit.createChangeId)" ; then
-  exit 0
-fi
+case "$(git config --get gerrit.createChangeId)" in
+  false)
+    exit 0
+    ;;
+  always)
+    ;;
+  *)
+    # Do not create a change id for squash/fixup commits.
+    if head -n1 "$1" | LC_ALL=C grep -q '^[a-z][a-z]*! '; then
+      exit 0
+    fi
+    ;;
+esac
 
-# Do not create a change id for squash commits.
-if head -n1 "$1" | grep -q '^squash! '; then
-  exit 0
-fi
 
 if git rev-parse --verify HEAD >/dev/null 2>&1; then
   refhash="$(git rev-parse HEAD)"