1

We have a bitbucket server, we have introduced the usage of Pull Requests to protect two branches: Master and Develop.

This works well and people cannot push anymore to those branch on our bitbucket server.

But the issue is that sometimes when we took the develop version to test some behavior, we forget to change the branch for doing our modification, and we do some commit locally that we cannot push to the server.

My question is: Is there a way to prevent users from committing on those two branche with a shared rules or something?

Some important requirement:

  1. It should be shared accross the team
  2. It should work in our windows environment
  3. This should not prevent our pull request to be merged into the develop/master

Thank you very much

Not a duplicate of How to restrict access to master branch on git since this other question is for the server to reject the commit, which is already in place. It doesn't prevent the dev to commit by error on develop instead of their branch and make them loose time to move their changes to another branch. If the commit was not possible, it would help us see this error quickly.

J4N
  • 19,480
  • 39
  • 187
  • 340
  • Have you protected/locked master and develop? If so, you shouldn’t need to force people to never to commit to them as they can’t ever share those commits. – evolutionxbox Mar 15 '18 at 08:50
  • @evolutionxbox Yes, they are protected. And from a server point of view, it's 100% safe, it just make some developer loose some time for a check that I think we can do locally to prevent this kind of error. – J4N Mar 15 '18 at 08:55
  • Maybe a pre-commit hook? Although you’d have to share the script between developers. – evolutionxbox Mar 15 '18 at 08:56
  • Possible duplicate of [How to restrict access to master branch on git](https://stackoverflow.com/questions/38864405/how-to-restrict-access-to-master-branch-on-git) – Karol Dowbecki Mar 15 '18 at 09:00
  • @KarolDowbecki This is not the same thing. I've already achieved what you mention and it works fine. This doesn't prevent commits on the branch, this just prevent them to being pushed. – J4N Mar 15 '18 at 10:32
  • @evolutionxbox What do you mean by "share the script"? It can ben commited? Do you know how to do it? – J4N Mar 15 '18 at 10:33
  • Git hooks, like the pre-commit-hook @evolutionxbox is talking about, are located in the `.git/hooks` folder. The whole `.git` folder is not tracked, thus each developer has to copy the hook into their respective `.git/hooks` folder. – kowsky Mar 15 '18 at 10:58
  • I do not really see the problem here, however: If you accidentally worked on `develop` and try to push, which is rejected, you can just `git checkout -b newbranch` to create a new branch with all your changes and push that. – kowsky Mar 15 '18 at 11:01
  • 1
    @kowsky has a good point. Once the branches are protected, I kinda doesn't matter what happens locally. Otherwise a git hook should be enough. – evolutionxbox Mar 15 '18 at 11:18
  • @kowsky: Maybe, but then you have the develop that has changes and does not represent correctly the remote develop. – J4N Mar 15 '18 at 15:05
  • That is correct. So, when someone realized he worked on develop, he would have to create a new branch to work on (`git checkout -b newbranch`) and then reset their local develop to the remote one (for example by `git update-ref refs/heads/develop refs/remotes/origin/develop`). – kowsky Mar 16 '18 at 08:37
  • @kowsky Agreed, but we are not much familiarized with git command line, so it means that we have to search where to execute it, which command, ... – J4N Mar 16 '18 at 10:45

1 Answers1

1

There is one thing you can do, but it requires active participation from your users. They must install a pre-commit hook.

The pre-commit hook can be very simple:

#! /bin/sh
# pre-commit: fail if current branch is master or develop
case "$(git rev-parse --abbrev-ref HEAD)" in
master|develop) echo "make a new branch first" 1>&2; exit 1;;
esac
exit 0

You would include this file (be sure to chmod +x it or otherwise make it executable) in your repository, and they would use the "magic incantation" of:

$ git clone <url>
$ cd <new-clone>
$ cp <path-to-file> .git/hooks/pre-commit

when cloning. However, if they forget to do the cp step, they will not have a hook. (You can give them a script that does the clone-and-install-hooks, or a more general script that installs hooks for them that they should run after cloning. You can even set up a template directory with hooks—or have them do this themselves, on their systems—and then use git clone --template=<template-dir>.)


Opinion alert: The whole idea is ultimately futile, because those who know what they are doing can easily get around anything you do here (e.g., git commit --no-verify); and for those who know what they are doing, there's no point, as branch names are essentially meaningless anyway.1 For those who are totally clueless, you can't provide this automatically. So all you can do is provide hints to users who are willing to take action, but don't understand the action they are taking.


1Consider that after making several commits on my branch named master, I can run:

git branch -m master feature/xyz

to rename my master to my feature/xyz, then:

git checkout master

to create a new master whose upstream is origin/master; the new master points to the same commit as origin/master. Then:

git checkout feature/xyz

puts me back on my feature/xyz. My feature/xyz now probably has its upstream set to origin/master, but maybe that is what I want anyway.

The branch names in my repository are all just ephemera here: I create, rename, and/or destroy them at will and whim. What matter are the commits. That's not the case for your server-side branch: the branch names there get copied into other users' remote-tracking names when they clone that repository, and those other users are probably naïve about how branch names and remote-tracking names work, so those names do matter. But once you know how names work in Git, they're just for convenience, and easily changed.

torek
  • 448,244
  • 59
  • 642
  • 775
  • The server is protected for this branch, so it's not about security, it's just about helping the user to see quickly they are not on their own branch. I think your solution will fit well in our context(and if the guy want to commit with '--no-verify', good for him, he will not being able to push anyway. – J4N Mar 15 '18 at 15:34
  • @J4N: understood—but the thing is, while this *seems* helpful, those people who install the hook but don't understand it will (through no fault of their own) eventually trip over something the hook *doesn't* protect against and probably wind up even more confused. The only thing that really helps is to learn how Git works, after which you don't need the protection. But that's an opinion with only programming-experience to back it up; if your users are doing something different, maybe their experience will differ. – torek Mar 15 '18 at 16:42
  • Yes I know, the protection side is done by the branch protection of bitbucket. It's just that since we introduced it, we got some errors, people will eventually learn, but in the meantime if we can make those errors costing less to the project ;) – J4N Mar 16 '18 at 06:42
  • Is it possible that this would work in the update hook? Or would it also restrict you from doing a git pull in master? – MrsPop88 Jun 20 '19 at 09:07
  • 1
    @JasonBruce: an `update` hook is run on a Git that is receiving a `git push` request. Its job is to vet one name-update (e.g., a request to change `refs/heads/master` from `a123456...` to `fedcba9...`, or to create `refs/tags/v1.2` at commit `7654321...`, or to delete some name). If it rejects the name create/delete/update request, the push continues for any *other* names requested, but that particular one gets a `rejected` status and the overall push fails for the person doing the push. The hook has no effect on `git fetch` or `git merge` and therefore no effect on `git pull`. – torek Jun 20 '19 at 16:17
  • Is there a way to enforce this at the global repository level? So instead of keeping the hook locally can we add it somewhere to global repo so that it come automatically when clone or pull is done (Something similar to adding .gitignore at repository level) – user85 Aug 12 '20 at 06:48
  • @user85: there is no global repository: each repository is an island unto itself. That said, when `git init` (including the one implied by `git clone`) creates a *new* repository, it does read a *template*. The default template is installed when you install Git itself. You can make your own template and add the `--template` option to your `git init` or `git clone` to use your own personal template, including any hooks you like: these will be in the new repository just created. Once that happens, though, it's up to you, the owner of the repository, to make any changes to the hooks directory. – torek Aug 12 '20 at 14:33
  • @user85: A common method used for providing *helpful* hooks is to have some file(s) committed into the repository along with a README or similar, that says something like: *If you want to install helpful hook X, copy or symbolic-link hooks/X to .git/hooks/X*. **Git** won't do this for you, because if it did, someone could put a malicious hook into a repository, which would then be run just by cloning the repository. – torek Aug 12 '20 at 14:35