170

(For simplicity) I have a master branch and a dev in my Git repository. I want to ensure the master branch is always working, so all work I do should be in the dev branch.

However, when I merge my changes in with a --no-ff merge, I tend to stay in the master branch, and just continue working in it (because I forget to checkout my dev branch).

Can I put up a rule for the master branch, which states I can't do commits, and fast-forward merges, but only --no-ff merges from another branch?

This must work for private hosted repositories (ergo, not GitHub or Bitbucket).

1
  • 9
    "fast-forward commits" is not a thing. Commits are just commits, git commit makes a new one, there is no fast-forwarding happening. It sounds like you just want to prohibit ordinary commits when the current branch is master, in which case, look into the pre-commit hook. Commented Nov 7, 2016 at 10:23

3 Answers 3

282

Yes, it is possible. You must create a pre-commit hook which rejects commits to the master branch. Git doesn't call a pre-commit hook when you call the merge command, so this hook will be rejecting only regular commits.

  1. Go to your repository.

  2. Create a file, .git/hooks/pre-commit, with the following content:

    #!/bin/bash
    
    branch="$(git rev-parse --abbrev-ref HEAD)"
    
    if [ "$branch" = "master" ] || [ "$branch" = "main" ]; then
        echo "You can't commit directly to $branch branch"
        exit 1
    fi
    
  3. Make it executable (not required on Windows):

    chmod +x .git/hooks/pre-commit
    

To disable fast-forward merges, you must also add the following option to your .git/config file:

[branch "master"]
    mergeoptions = --no-ff

If you want also protect the master branch on your remote, check this answer: How to restrict access to master branch in Git

Sign up to request clarification or add additional context in comments.

11 Comments

This looks exactly like what I need - does this work in Windows as well?
@RasmusBækgaard yes it will: the bash script for the hook will be interpreted by the Git bash included in Git for Windows. (You just don't need the chmod step)
Nice, for anyone looking for a way to add these rules or other git hooks in the project repository, check this simple npm package: github.com/kilianc/shared-git-hooks, because you can't include anything that resides under the .git directory into the repository.
also, this is only for our local git repo, how can we enforce rules across different git repos for all developers without having them manually change the contents of .git directory?
found -n "This option bypasses the pre-commit and commit-msg hooks."
|
60

You can use the pre-commit utility to do this. It has a built-in no-commit-to-branch hook that can be used to prevent commits to one or more branches.

Setup

The basic setup process is:

  • Install using pip or Homebrew (instructions at https://pre-commit.com/#install)
  • Create a .pre-commit-config.yaml file in the root of your project (see below for a first draft)
  • Install the hooks into your Git configuration by running pre-commit install.

Basic configuration for protecting branches

Here is a basic configuration that includes just the no-commit-to-branch hook:

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
  rev: v3.3.0
  hooks:
    - id: no-commit-to-branch
      args: ['--branch', 'master']

If you want to protect multiple branches, you can use include multiple --branch arguments in the argument list:

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
  rev: v3.3.0
  hooks:
    - id: no-commit-to-branch
      args: ['--branch', 'master', '--branch', 'staging']

Isn't this all overkill?

Pre-commit has many other built-in hooks, and a large collection of community-built hooks that will transform the way you clean up and validate your commits. The reason I mention this is because, while this tool may be overkill for just preventing commits to a protected branch, it has many other features that make it a compelling and simple addition to any Git project.

2 Comments

Super useful! I will definitely try this out, thanks for the information @JGC
Thank you, a nice suggestion! Note that main and master are covered by default if there is no --branch.
27

It may make sense to install it globally via

git config --global core.hooksPath ~/githooks

and moving that pre-commit file into that directory

6 Comments

What if I have multiple repositories - won't it affect all of them?
and that is what you may what to do in most of the cases
Say I have this weird project, where they renamed master to Production - can exceptions be made?
Install what globally? Can you make it clearer in your answer? (But without "Edit:", "Update:", or similar - the answer should appear as if it was written today.)
@PeterMortensen I guess he meant to install the pre-commit hook globally for all local git repositories, since the .git folder lies inside each repo's folder. With the suggested action, there will be a global "githooks" folder, under user's home folder, where he/she can put the same pre-commit hook script and it will be active for commits to any repo.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.