160

(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.
    – torek
    Commented Nov 7, 2016 at 10:23

3 Answers 3

265

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" ]; then
      echo "You can't commit directly to master 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

11
  • 2
    This looks exactly like what I need - does this work in Windows as well? Commented Nov 8, 2016 at 7:48
  • 4
    @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)
    – VonC
    Commented Nov 8, 2016 at 8:04
  • 4
    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. Commented Oct 24, 2017 at 7:44
  • 19
    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? Commented Nov 27, 2017 at 23:42
  • 3
    found -n "This option bypasses the pre-commit and commit-msg hooks."
    – Lluís
    Commented Jun 10, 2019 at 11:55
53

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
  • 5
    Super useful! I will definitely try this out, thanks for the information @JGC Commented Aug 28, 2021 at 19:48
  • 4
    Thank you, a nice suggestion! Note that main and master are covered by default if there is no --branch. Commented Jun 17, 2022 at 22:39
20

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
  • 1
    What if I have multiple repositories - won't it affect all of them? Commented Nov 5, 2019 at 12:32
  • 3
    and that is what you may what to do in most of the cases Commented Nov 8, 2019 at 11:24
  • Say I have this weird project, where they renamed master to Production - can exceptions be made? Commented Nov 8, 2019 at 15:21
  • 1
    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.) Commented Nov 24, 2021 at 21:24
  • 1
    @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.
    – Alexandre
    Commented Dec 14, 2021 at 2:46

Not the answer you're looking for? Browse other questions tagged or ask your own question.