Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

block push of trivial merge to git server

A while back I asked our developers to use rebase instead of merge before pushing. Eliminating trivial merges makes for a much easier to follow commit graph (ie: gitk, git log).

Sometimes folks still accidentally do trivial merges, then push. Does anyone have handy or have tips for writing a server-side hook that blocks trivial merges?

By "trivial merge", I mean a merge without conflicts. Here's an example, and here's a better explanation of a trivial merge in git.

Update Wed Nov 10 01:26:41 UTC 2010: great comments, all! Thank you.

  • Consider the following: all I'm really asking folks to do is this:
    • if git pull --ff-only fails, do git pull --rebase instead of git pull
  • git.git only has one or two committers, right? In theory, it should be easy to follow the commit graph, but it looks pretty messy to me.

Update Thu Nov 11 23:49:35 UTC 2010:

  • here's a gist with some in-progress work
  • another idea is to prevent trivial merges on the client end, with a "git push" wrapper

Update Wed Dec 15 18:34:52 UTC 2010:

  • adymitruk is close! Just one case is still unresolved: non-trivial merges must still work.
  • A rather complete test suite is available, check it out.
  • I asked for help on a (the?) git mailing list.
like image 433
Adam Monsen Avatar asked Nov 09 '10 20:11

Adam Monsen


People also ask

What is a trivial merge in git?

This basically means there were no conflicts and nothing magic was added in the merge commit.

How do I stop GitHub from merging?

Using GitHub's settings, you can only block merging by requiring either pull request reviews, status checks to pass, signed commits or linear history as shown under the branch protection settings. Apart from the above, there is no other way currently to block self merging PRs on GitHub.

How do I stop current merge?

Use git-reset or git merge --abort to cancel a merge that had conflicts. Please note that all the changes will be reset, and this operation cannot be reverted, so make sure to commit or git-stash all your changes before you start a merge.


2 Answers

I came across this piece of code, while trying to find a solution. It doesn't do exactly what you want, but it should be ez to add extra branch names on the if statement.

Works for me, so far. it forces pull --rebase for the same branch and lets regular merges with other branches go through.

All credits go to the original author.

#!/bin/bash # # This git update hook will refuse unnecessary merge commits caused by pulling # from being pushed to a shared repository. These commits make following the # history of a project difficult and are always avoidable with rebasing. # # by Scott Kyle (appden) # modified by Olivier Refalo (orefalo)  refname="$1" oldrev="$2" newrev="$3"  # if this is not run as a hook, you may have messed up if [ -z "$GIT_DIR" -o -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then     echo "Usage: GIT_DIR=<path> $0 <ref> <oldrev> <newrev>" >&2     exit 1 fi  # if the new revision is all 0's then it's a commit to delete a ref zero="0000000000000000000000000000000000000000" # also check if the new revision is not a commit or is not a fast forward # detect branch deletion, branch creation... and more if [ "${refname#refs/heads/}" = "master" ] || [ "$newrev" = "$zero" ] || [ "$oldrev" = "$zero" ] || [ $(git cat-file -t $newrev) != "co mmit" ] || [ $(git merge-base $oldrev $newrev) != "$oldrev" ]; then     exit 0 fi  # loop through merges in the push only following first parents for merge in $(git rev-list --first-parent --merges $oldrev..$newrev --); do     # lazily create the revision list of this branch prior to the push     [ -z "$revlist" ] && revlist=$(git rev-list $oldrev)     # check if the second parent of the merge is already in this branch     if grep -q $(git rev-parse $merge^2) <<< "$revlist"; then         cat >&2 <<-EOF             *** PUSH REJECTED ***             *** TRIVIAL merge detected on local branch ${refname#refs/heads/}             *** To fix: git rebase origin/${refname#refs/heads/}             ***             *** Next time use: git pull --rebase             ***             *** Permanent fix: git config [--global] branch.autosetuprebase always             *** Then for existing branches: git config branch.<name>.rebase true         EOF         exit 1     fi done  echo -Info- Clean history successfully preserved! exit 0 
like image 127
Olivier Refalo Avatar answered Sep 23 '22 13:09

Olivier Refalo


This update hook will check if you are pushing to specific branches (it allows trivial merges in wip, topic and other branches).

This does not bother with the rest of the parents on octopus merges as it only references the 2nd parent in each merge commit being pushed. Please feel free to update the script.

UPDATE: Reserved branches are required to exist on the remote.

#!/bin/bash
refname="$1"
oldrev="$2"
newrev="$3"
branches="refs/heads/hotfixes refs/heads/dev refs/heads/qa refs/heads/master"
cont="no"
for branch in $branches ; do
  if [[ $refname == $branch ]] ; then
    cont="yes"
  fi
done
if [[ $cont == "no" ]] ; then
  exit 0
fi
echo "inspecting branch $refname for trivial merges" >&2
hashes="$(git log --format=%H --merges $oldrev..$newrev)"
for hash in $hashes ; do
  echo "checking merge commit $hash" >&2
  cont="no"
  for branch in $branches ; do
    if [[ $refname == $branch ]] ; then
      continue
    fi
    # if [[ "$(git log --format=%H $hash^2 ^$branch | wc -l)" == "0" ]] ; then
    if [[ "$(git log --format=%H $hash^2 ^$branch | wc -l)" == "    0" ]] ; then
      cont="yes"
    fi
  done
  if [[ $cont == "no" ]] ; then
    echo "No trivial merges allowed. Please rebase and push again." >&2
    exit 1
  fi
done
exit 0
like image 42
Adam Dymitruk Avatar answered Sep 22 '22 13:09

Adam Dymitruk