bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: automatically preventing merge commits on master


From: Jim Meyering
Subject: Re: automatically preventing merge commits on master
Date: Mon, 13 Oct 2008 21:41:46 +0200

Simon Josefsson <address@hidden> wrote:
> Jim Meyering <address@hidden> writes:
>
>> Does anyone object to my installing a server-side hook
>> that would prevent pushing merge commits on master?
>> In our experience here (with gnulib.git), pushing a merge
>> commit is always unintentional.
>
> +1 from me.  How would you do that?  I'd like to do it in some other
> projects.  It is easy to inadvertently send a merge commit.

Apply the patch below to git.git on its "maint" branch,
then copy templates/hooks--update.sample into your server-side
repository as an executable repo.git/hooks/update.

Finally, server-side, set the config variable for each repo/branch pair:

  git --git-dir=/path/to/your-proj.git config --bool \
    hooks.denymerge.master true

The only tricky part was the is_merge_commit function,
and I learned that by asking on #git/irc.freenode.
[Thanks to doener (Björn Steinbrink)]

However, beware.
I'm pretty sure that some parts of this patch depend on having
a relatively recent version of git.  I'm using it with git-1.5.6.4.

Jim

P.S., as you can see, the naming of the config options
is not consistent.  Still not sure which approach I prefer.

>From c02efe37e975094f7fbc7938f29c38819355f995 Mon Sep 17 00:00:00 2001
From: Jim Meyering <address@hidden>
Date: Tue, 29 Jul 2008 18:16:50 +0200
Subject: [PATCH] add hooks: deny-bad-whitespace, per-branch: deny-push 
deny-merge-commit

Reject any attempt to push a change that adds "bad" whitespace.
Override with this:
  git config hooks.allowbadwhitespace true

Disable push-access on a per-branch basis.

Prohibit pushing merge commits on a per-branch basis.
---
 templates/hooks--update.sample |   60 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 60 insertions(+), 0 deletions(-)

diff --git a/templates/hooks--update.sample b/templates/hooks--update.sample
index 93c6055..0c89f30 100755
--- a/templates/hooks--update.sample
+++ b/templates/hooks--update.sample
@@ -17,12 +17,35 @@
 #   This boolean sets whether deleting branches will be allowed in the
 #   repository.  By default they won't be.
 #
+# hooks.allowbadwhitespace
+#   This boolean sets whether you may push a commit that adds bad whitespace.
+#   By default, you may not.
+#
+# hooks.denypush.branch.BRANCH_NAME
+#   If defined to a string that looks like an email address, this option
+#   disables push access to the specified branch.  When a push fails as
+#   a result of this option, the resulting diagnostic includes the specified
+#   email address.  For example, run this on the server to deny all push
+#   access to "master":
+#
+#   For example, enable it with this:
+#   git config hooks.denypush.branch.master address@hidden
+#
+# hooks.denymerge.BRANCH_NAME
+#   When this boolean is true, you may not push a merge commit to BRANCH_NAME.
+#   By default, you may.
+#

 # --- Command line
 refname="$1"
 oldrev="$2"
 newrev="$3"

+is_merge_commit()
+{
+       git rev-parse --verify --quiet $1^2 > /dev/null
+}
+
 # --- Safety check
 if [ -z "$GIT_DIR" ]; then
        echo "Don't run this script from the command line." >&2
@@ -56,6 +79,7 @@ else
        newrev_type=$(git-cat-file -t $newrev)
 fi

+check_diff=no
 case "$refname","$newrev_type" in
        refs/tags/*,commit)
                # un-annotated tag
@@ -78,6 +102,32 @@ case "$refname","$newrev_type" in
                ;;
        refs/heads/*,commit)
                # branch
+               check_diff=yes
+               branch=${1##refs/heads/}
+               deny_push_email=$(git config "hooks.denypush.branch.$branch")
+               case $deny_push_email in
+                       '') ;;
+                       *) printf "error: *** %s\n" \
+                             "commit on branch '$branch'" \
+                             "locked by $deny_push_email" >&2
+                        exit 1;;
+               esac
+
+               # When enabled, this prohibits pushing a merge commit.
+               # Enable this hook for branch "next" with e.g.,
+               # git config --bool hooks.denymerge.next true
+               deny_merge=$(git config --bool "hooks.denymerge.$branch")
+               case $deny_merge in
+                       true)
+                         is_merge_commit $newrev && {
+                         printf "error: *** %s\n" \
+                         "You may not push merge commits to branch $branch." \
+                         "Did you forget to rebase? ($newrev)" >&2
+                         exit 1
+                       }
+                       ;;
+               esac
+
                ;;
        refs/heads/*,delete)
                # delete branch
@@ -88,6 +138,7 @@ case "$refname","$newrev_type" in
                ;;
        refs/remotes/*,commit)
                # tracking branch
+               check_diff=yes
                ;;
        refs/remotes/*,delete)
                # delete tracking branch
@@ -103,5 +154,14 @@ case "$refname","$newrev_type" in
                ;;
 esac

+if [ $check_diff = yes ]; then
+       allow_bad_whitespace=$(git config --bool hooks.allowbadwhitespace)
+       if [ "$allow_bad_whitespace" != "true" ]; then
+               test "$oldrev" = 0000000000000000000000000000000000000000 \
+                       && exit 0
+               exec git diff --check $oldrev $newrev --
+       fi
+fi
+
 # --- Finished
 exit 0
--
1.6.0.2.514.g23abd3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]