Disallow branching from and merge of specific branches
Asked Answered
G

7

8

Is there a git hook or some other way to prohibit branching from and merging of specific branches. We want to make sure that we don't merge a "dirty" integration branch into our clean deployment branch.

The major goal is that people cannot execute something like this:

git checkout integration
git checkout -b major_problem_branch

or

git checkout deployment_or_hotfix_or_feature_branch
git merge integration
Geisha answered 2/11, 2012 at 13:58 Comment(3)
fixed :) i have co as alias for checkoutGeisha
Have you gotten an answer you're satisfied with yet, or do you need to clarify your question?Dremadremann
it's a shame that i cannot split the bounty :)Geisha
A
5

You can use branch permissions on the server by adding this to the config file in your bare repository:

[hooks]
        allowedtomerge = user1,user2,user3
        protectedbranches = master

That will allow user1, user2, and user3 to merge to master branch, but no one else.

Or you could create a pre-commit hook:

#!/bin/bash

if [[ `git symbolic-ref HEAD` == "refs/heads/master" ] -a ["$USER" != "user1"]] then
    echo "You cannot commit in master!"
    exit 1
fi

Then you have people that evaluate and allow changes to go forward.

Ideally, I would use a system that you like, for instance gerrit or assembla or github. They have nice ways of controlling a master branch through Merge Requests.

Afternoon answered 9/11, 2012 at 2:45 Comment(3)
is there a way to get the command i'm currently executing in that pre-commit hook? so that i could check if the symbolic-ref matches my deploy branch and that the command isn't merge integration branch. also i would like to check if symbolic-ref returns the integration branch and that i do not branch from that?Geisha
guess i will give this way a try :)Geisha
I was wrong about arg array and $1 - check out git-scm.com/book/en/… for more information in general about how to implement hooks to enforce policy.Afternoon
D
3

In general, you can't reliably prevent anyone from making changes in their local repositories. Unless you trust your developers to never make any mistakes (in which case you wouldn't need this), you have to run your checks in hooks on your server.

That means you can't count on preventing people from branching, and I'm not sure why you'd really want to anyway. What you can do is keep people from pushing work that hasn't been somehow "approved" into the server's deployment branch (which I'm going to call deploy).


Now the first question is how to express that approval.

If you want a workflow where an authorized person has to review work before it's deployed, then I'd have the reviewer do these steps, if the work is currently living on a branch named review:

  1. Ensure deploy is fully merged into review, so that later deploy can be fast-forwarded to review.
  2. Do any appropriate verification on review.
  3. Create and push a signed tag (git tag -s), using an authorized key, for the contents of review.
  4. Fast-forward deploy to review (git checkout deploy, git merge --ff-only review) and push it.

If instead you want anyone to be able to deploy, but you want them to have to think about it first, you could do the same thing but relax the signature requirements. Or you could bless a particular branch (tested, say) and require that all work be merged and pushed to tested before being pushed to deploy.


How can the server verify that this approval has been given for the protected deploy branch? The basic plan is to use the update hook to accept or reject ref-updates for refs/heads/deploy according to the acceptance criteria you've decided on.

If you want a tag that satisfies particular criteria, like being signed by an approved key, then you can find all tags that point to the new object proposed for deployment using git for-each-ref refs/tags. Remember to accept the update if any tag meets the criteria, or else sooner or later somebody's going to push a bad tag that blocks deployment. Verifying that the right key was used is left as an exercise for the reader, but gpg --no-default-keyring --keyring=approved.gpg may help.

If you'll take any commit as long as somebody's tagged it, you can use git describe --exact-match <object>. If you want to limit to tags with a particular name pattern, add --match <pattern>. If you'll accept unannotated tags, add --tags.

If you want work merged to a blessed branch before being merged to deployment, you can check that the output of git rev-parse tested equals the proposed new object.

In all cases, you probably want to double-check that the push is a fast-forwarding push. You can check that git merge-base <old> <new> equals <old>.

Dremadremann answered 4/11, 2012 at 20:56 Comment(7)
This can be penetrated with git rebase -i just before the merge and a squash commit, actually doing a fast-forward.Myranda
@SergeyK., rebase will change the commit hash from the changed commit forward, because the hash of each commit is over material that includes the hash of the parent commit. This can only be "penetrated" if you've broken SHA-1, and in that case you'll have better things to do with your time.Dremadremann
That's true, but that new squashed and rebased commit will contains no information about the branch it came from.Myranda
@SergeyK., git commits never record what branch they came from, only what parent commits. Which of my recommendations are you concerned about, or do you believe there's a fundamental flaw?Dremadremann
After we do git pull --rebase and then rebase our branch on top of master the parent of this new commit will be the ORIG_HEAD. We will lose all information on where the changes came from, so we can push them and you won't be able to detect the forbidden branch.Myranda
@SergeyK., ah, I see. Indeed, you can't reliably detect that the code was ever on a forbidden branch. My proposal is to turn the question around: verify that the code has been approved, instead of checking that it hasn't ever been forbidden. This is surely what you want anyway: once the code has matured, why would you still keep it out of the release?Dremadremann
Anyway, it is just the matter of trust.Myranda
I
1

I don't think Git has support for branch grained permission, you should consider using gitolite for such requirement.

You even has a very cool gitolite web interface, called GitLab with a look similar to github.

Infold answered 2/11, 2012 at 14:7 Comment(2)
do u have more information about how i could do smth like this with gitolite?Geisha
No sorry, you can take a look at gitolite docs -> sitaramc.github.com/gitolite/admin.html#serverInfold
S
1

If you never, ever want to merge from the dirty integration branch to the clean deployment branch, you could clone the deployment repo, then perform the dirty integration work only on the clone (the non-deployment repo).

Sahib answered 2/11, 2012 at 16:13 Comment(0)
R
1

may I advise to use git flow by nvie.Model It keeps all your branches where they should be by using very simple commands. Here's a cheatsheet

Regret answered 8/11, 2012 at 23:7 Comment(1)
nvie is a great methodology - but it is complex for people to get their heads around. I find it simpler if you just go to a Continuous Delivery methodology.Afternoon
D
1

Note that if you are willing to control access right at a central repo level, Assembla is one git hosting service which now (March 2013) allows for protected branches:

See "Put Down Your Forks - Introducing Protected Branches":

A Protected Branch is a branch with limited write access.
Specify members (or groups) of your team that will be able to submit code to a branch:

group

Now, only these people will be able to push to the Master branch.
Everyone else will have to contribute code through merge requests. They will be able to push to any other branch in the repo, but not Master.

protected master branch

Damning answered 21/3, 2013 at 11:50 Comment(0)
S
0

With branch per feature (as explained here in my article: http://dymitruk.com/blog/2012/02/05/branch-per-feature/) you have the idea of a release candidate. You can add an update hook on the server to ensure that the merge-base of the release candidate and the development (aka integration branch) have a common base that is the iteration start tag. This ensures that all you have are the complete features themselves.

If you want to be even more sure, you can see if a feature is finished. You would have to have the hook script integrate with your issue tracking system. If the feature that is being integrated does not have a "complete" status, you can fail at that point too. Jira, for example, provides a way to interact with it via command line.

Submit answered 5/11, 2012 at 5:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.