bypass pre-commit hook for merge commits
Asked Answered
D

3

32

I setup some git hooks to run some gulp commands on pre-commit. I basically run jshint/plato. I basically want to bypass these for two cases:

  1. hotfix branches (master / hotfix)
  2. git merge (or find a way to do it in a way that doesn't crash on the merge commit case)

The plato gulp command runs analysis on the source and produces a /reports/ directory that tracks complexity over time. If we do this on the hotfix branch it will result in merge conflicts when merging them back into development. Enough talking here is the simple hook:

#!/bin/sh

if git diff --cached --name-only --diff-filter=ACM | grep '.js$' >/dev/null 2>&1
then
  git stash -q --keep-index
  ./node_modules/.bin/gulp jshint
  RESULT=$?
  git stash pop -q
  [ $RESULT -ne 0 ] && exit 1
  git stash -q --keep-index
  ./node_modules/.bin/gulp plato
  git add report/
  git stash pop -q
fi

exit 0

Issue right now is if i have a merge conflict on "reports" and I resolve the merge All conflicts fixed but you are still merging. and then commit it runs the analysis again and stages the commit and when it commits it throws an error:

/Users/Nix/work/project/.git/modules/somesubmodule/MERGE_HEAD' for reading: No such file or directory.

The directory does exist but there is no merge head...

Discounter answered 6/1, 2015 at 14:14 Comment(0)
D
41

So I just found a command that I think i can use to detect the "merge_head"

 git rev-parse -q --verify MERGE_HEAD

If rev-parse returns a hash that means we are currently in a merge state. I can use that to bypass this logic. But will wait for some better advice from more experienced individuals.

Discounter answered 6/1, 2015 at 14:16 Comment(5)
Works for me. Thanks!Typescript
If you are rebasing, you can do about the same with the following: git rev-parse -q --verify REBASE_HEADGuenzi
Sorry for the newbie question, but how could we have the exact opposite effect? i.e, if we want to error out in case we're in a merge state.Juan
@FlavioWuensche For skipping on merge: (git rev-parse -q --verify MERGE_HEAD) || (skip_this_if_merging). For running on merge: (git rev-parse -q --verify MERGE_HEAD) && (only_run_this_if_merging)Timberwork
Strange, but git rev-parse -q --verify REBASE_HEAD evaluates to true event after rebase was finished. I tried (git rev-parse -q --verify MERGE_HEAD || git rev-parse -q --verify REBASE_HEAD) || (skip_this_if_merging_or_rebasing)Carolinacaroline
P
5

As mentioned in this related answer you could test for the existence of $GIT_DIR/MERGE_HEAD to detect a merge commit:

Here's what you do get:

  • If you're using git commit --amend to amend a merge commit, the pre-commit hook is run as usual, but it can't really detect that this is happening. The new commit will be a merge, but you can't tell.

  • If you're using regular old git commit to create a non-merge commit, the file MERGE_HEAD will not exist in the git directory, and you can tell that this is not going to create a merge commit.

  • If you're using git commit to finish off a conflicted merge, the file MERGE_HEAD will exist, and you can tell that this is going to create a merge commit.

  • If you're running git merge and it succeeds on its own, it makes a new commit without using the pre-commit hook, so you don't even get invoked here.

Hence, if you're willing to allow git commit --amend on merges to misfire, you can get close to what you want: just test for the existence of $GIT_DIR/MERGE_HEAD to see if this is a git commit that is finishing off a conflicted merge. (The use of $GIT_DIR is a trick to make this work even if the commands are run outside the git tree. Git sets $GIT_DIR so that in-hook git commands will work right.)

Precedent answered 3/5, 2018 at 14:13 Comment(0)
P
2
########################
# Setup Some Variables #
########################

REPO_ROOT_REL=$( git rev-parse --show-toplevel )
REPO_ROOT="$( cd "$REPO_ROOT_REL" >/dev/null 2>&1 ; pwd -P )"

####################################################
# Check for merge or rebase commit and abort if so #
####################################################

set +e
MERGE_HASH=$( git rev-parse -q --verify MERGE_HEAD 2> /dev/null)
set -e
if [ ! -z "$MERGE_HASH" ]; then
    echo "This appears to be a merge commit ($MERGE_HASH). Avoiding pre-commit steps."
    echo ""
    echo "EXITING ${0##*/}"
    exit 0
fi

REBASE_APPLY_PATH="${REPO_ROOT}/.git/rebase-apply"
REBASE_MERGE_PATH="${REPO_ROOT}/.git/rebase-merge"
if [ -e "${REBASE_APPLY_PATH}" ] || [ -e "${REBASE_MERGE_PATH}" ]; then
    echo "This appears to be a rebase commit (.git/rebase-apply or .git/rebase-merge exists). Avoiding pre-commit steps."
    echo ""
    echo "EXITING ${0##*/}"
    exit 0
fi
Pahari answered 24/11, 2023 at 18:21 Comment(2)
Wouldn't you want to exit 0 so that the merge/rebase can continue?Standoff
@Standoff yes you're correct, I have edited my answer accordingly. ThanksPahari

© 2022 - 2024 — McMap. All rights reserved.