How to prevent devs from merging master into a Release branch?

455 Views Asked by At

Our team uses a release branching strategy. A week before each release, we will create a branch off of master, e.g. "Release_1.2.3". We send this branch to our quality engineers for verification, and use the week prior to the release to merge in critical bug fixes before releasing our software to our users. During this time, feature development continues on master. Once Release 1.2.3 is officially released, we merge Release_1.2.3 back into master.

This approach generally works for us, except for one flaw: When working on changes targeting Release_1.2.3, some devs have incorrectly merged master into their development branch and then manually deleted the unnecessary changes. This causes issues when merging Release_1.2.3 back into master, since git views the deletions made as being a part of the history, and will try to make the same deletions on master.

We've tried to educate devs about this issue, but we have a large team contributing to the codebase (including many devs without much experience in git). Instead, I'm looking for an automated way to prevent these sorts of harmful merges. We use Github's Branch Protection Rules to prevent direct commits to master; can we use a similar approach here?

Alternatively, is there a programmatic way of detecting that a branch contains a harmful merge? We could potentially incorporate that into our existing CI system.

1

There are 1 best solutions below

5
VonC On

If you can convince your developer to install a client-side hook in their repo/.git/hooks folder, that allows for a control as close as possible of the action.

With Git 2.24+ (Q4 2019), you can use a pre-merge-commit similar to this script:

#!/bin/sh

# Get the commit hash of the head of the branch being merged
MERGE_HEAD=$(cat .git/MERGE_HEAD)

# Retrieve the branch name using the commit hash
merge_from=$(git branch --contains $MERGE_HEAD | grep -v '^\*' | sed 's/^ *//')

if [ "$merge_from" != "master" ]
then
  exit 0
fi

echo "You cannot merge master branch into the current branch."

exit 1

Or, if .git/MERGE_HEAD is no longer there when the pre-merge-commit is invoked, a possible option would be to use the reflog:

#!/bin/sh

# Get the commit hash of the previous HEAD
PREVIOUS_HEAD=$(git reflog -1 --pretty=%H HEAD@{1})

# Retrieve the branch name using the commit hash
merge_from=$(git branch --contains $PREVIOUS_HEAD | grep -v '^\*' | sed 's/^ *//')

if [ "$merge_from" != "master" ]
then
  exit 0
fi

echo "You cannot merge master branch into the current branch."

exit 1

That is less reliable than a server-side hook, but it can help.