Prevent commits in master branch
Asked Answered
F

3

158

(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).

Fettle answered 7/11, 2016 at 9:47 Comment(1)
"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.Percussion
S
262

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

Somerville answered 7/11, 2016 at 12:40 Comment(11)
This looks exactly like what I need - does this work in Windows as well?Berezniki
@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)Feeney
note: you can also prevent yourself from pushing to a remote master branch in the pre-push hook. ex: gist.github.com/aaronhoffman/ffbfd36928f9336be2436cffe39feaecHf
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.Circumnavigate
Instead of making one my teammates (or my own) day harder when we accidentally make changes to master and then try to commit, and then get rejected, is there any way to tell Git to create a warning when it sees any changes in master?Terle
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?Terle
This should only work for one user right? Ideally this rule should apply to the whole repoBesiege
Note to those new to bash scripting (like myself): the space between the square brackets and double quotes in the if line are required. I found that out the hard way... glad I figured it out eventually. Took me a few minutes though. ;)Rifleman
thanks, this works perfectly, except when there are merge conflicts and need to commit the merge result, there's any way to allow this?Croak
found -n "This option bypasses the pre-commit and commit-msg hooks."Croak
Hey, @Somerville could we make that a global pre-commit hook? So that we always will have that same filw when we initialize a new git repository?Trawick
P
50

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.

Prandial answered 19/3, 2020 at 17:22 Comment(2)
Super useful! I will definitely try this out, thanks for the information @PrandialDepredation
Thank you, a nice suggestion! Note that main and master are covered by default if there is no --branch.Nun
A
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

Aalto answered 22/10, 2019 at 9:14 Comment(6)
What if I have multiple repositories - won't it affect all of them?Berezniki
and that is what you may what to do in most of the casesAalto
Say I have this weird project, where they renamed master to Production - can exceptions be made?Berezniki
you can use or operator in bash to specify more branches you want to protect on client sideAalto
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.)Demarche
@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.Colucci

© 2022 - 2024 — McMap. All rights reserved.