Vilx-
Vilx-

Reputation: 106904

How to prevent a specific branch from being merged in git?

We have a master branch where the released production code lives, a dev branch where the code for the test server lives, and various feature branches (branched from master) as each developer sees fit.

Over the course of time the dev branch has diverged somewhat from master. In addition, there are some incorrect merges there that mess up parts of the code. Several times already we have tried to reset (force-push) dev to be the same as master. To start over with a clean slate, so to say.

Unfortunately this does not last long. Sooner or later someone merges the old dev into the new dev, bringing back all the mess with it. I suspect this might even happen automatically, where a naive git pull silently merges the old and new branch heads.

Is it possible to prevent this with a server-side commit hook? Something that would refuse to accept the git push if the wrong commit is merged in?

Upvotes: 17

Views: 21656

Answers (3)

kelvin
kelvin

Reputation: 1614

Sooner or later someone merges the old dev into the new dev, bringing back all the mess with it.

This is a common problem when using the default git pull behavior. To avoid it, it's possible to configure git pull to use rebase by default instead of merge. That is, to rebase the current branch onto the remote branch instead of merging it:

git config pull.rebase interactive

From the git-config man page:

pull.rebase

When true, rebase branches on top of the fetched branch, instead of merging the default branch from the default remote when "git pull" is run. See "branch..rebase" for setting this on a per-branch basis.

When preserve, also pass --preserve-merges along to git rebase so that locally committed merge commits will not be flattened by running git pull.

When the value is interactive, the rebase is run in interactive mode.

NOTE: this is a possibly dangerous operation; do not use it unless you understand the implications (see git-rebase(1) for details).

With that, whenever the remote branch is rewritten (with push -f), whoever is pulling is responsible for identifying and dropping the "old" commits during the rebase. This results in a clean history (i.e.: no merges of "old" versions) on each branch.

Upvotes: -1

Gasol
Gasol

Reputation: 2519

It's possible with Git Hooks. Put the following POC script to .git/hooks/pre-receive on your remote (server-side) repository and give it right permission to execute.

Configure the branch you want to protect, for example master

$ git config hooks.protected-branch.refs master

File: .git/hooks/pre-receive

#!/bin/sh

read old_value new_value ref_name

refs=$(git config hooks.protected-branch.refs)

for ref in $refs; do
    if [ "$ref_name" == "refs/heads/$ref" ]; then
        if [ "$old_value" == "0000000000000000000000000000000000000000" ]; then
            continue
        fi

        if ! git merge-base --is-ancestor "$ref_name" "$new_value"; then
            echo "$ref_name is protected branch"
            exit 1
        fi
    fi
done

When you try to reset master by force-push, You will get similar output like this:

Counting objects: 12, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (12/12), 920 bytes | 153.00 KiB/s, done.
Total 12 (delta 4), reused 0 (delta 0)
remote: refs/heads/master is protected branch
To ../demo
 ! [remote rejected]   master -> master (pre-receive hook declined)
error: failed to push some refs to '../demo

Upvotes: 11

Bhanu Prakash Ryaga
Bhanu Prakash Ryaga

Reputation: 300

GitHub has a feature called protected branches, which gives repository administrators the ability to disable force pushes to specific branches.

In addition to blocking force pushes, a protected branch can have required status checks.

for more information... Please check https://blog.github.com/2015-09-03-protected-branches-and-required-status-checks/

Upvotes: -3

Related Questions