Home > Software engineering >  Blocking commits to branches with no-direct-commit policies
Blocking commits to branches with no-direct-commit policies

Time:02-10

I work with git repos hosted in Azure Devops (AzDO). Usually the master branch has AzDO policy that you can't push directly to it, and must use pull requests. However, some folks accidentally commit locally to master, and then their master branch gets out of sync with the origin, and they need to reset it with git reset --hard origin. I want to avoid this.

I know I can prevent commits to master using pre-commit hooks, with scripts or utils that will block (See answers to this question, and also this answer to a similar question). The richest util for this - https://pre-commit.com/hooks.html - only has a manually-configured no-commit-to-branch list.

Is there a solution that will block commits to any branch with policies in AzDO?

Edit in response to comments:

  • I disagree it's a training issue. A tool should do its best to guard users from mistake (just like saws have hand guard/etc.). IMO git fails at it, and git/AzDO could do better.
    • That said, maybe the git checkout master/git checkout -b mybranch is at fault here. So git fetch/git checkout -b mybranch origin/master would be safer.
  • I know one can reset/rebase, and that's the workaround for folks that got into this situation. However, I'd like to avoid that, especially for people with lower git skills.

CodePudding user response:

tl;dr: One possible solution is to sidestep the issue. Instruct your users (that are not super comfortable with Git yet) to delete their local copy of master and never checkout the master branch again. Instead they can update their copy of the remote branches often with git fetch, and they can use origin/master when making new branches or rebasing/merging to update their branches with the latest origin/master. I don't know that I would go so far as to prevent users from checking out master though, since more advanced users of Git might still wish to do so for some reason, and they would be perfectly comfortable resetting or renaming that local branch as needed.

I have recommended this approach to my co-workers and anecdotal evidence (of approximately 50 developers over the last few years) shows it seems to work fairly well. I also eat my own dog food most of the time; I still occasionally checkout out shared branches when I need to commit to one directly (and either bypass policies to push, or on very rare occasions, force push).

Additional Notes:

When you use remote tracking branches (such as origin/master) to create new branches, by default the new local branch will track that remote branch, which you normally don't want. So, when creating branches this way you'll probably want to use --no-track. In general, to make pushing for the first time slightly easier I recommend using the config setting of push.default to current. More info here.

Here are some recommended conventions for using origin/master:

# Update your local copy of the remote branches often,
#   especially before creating or updating branches
git fetch

# Create a new branch (newer syntax)
git switch -c my-branch origin/master --no-track
# Create a new branch (old syntax)
git checkout -b my-branch origin/master --no-track

# Pushing out changes for the first time
git push -u origin my-branch
# Pushing out changes for the first time with config set to current
git push -u
# Subsequent pushes
git push

# Update your feature branch using rebase
git switch my-branch
git fetch
git rebase origin/master
git push --force-with-lease

# Update your feature branch using merge
git switch my-branch
git fetch
git merge origin/master
git push

Also, you mentioned:

A tool should do its best to guard users from mistake...

I generally agree, though you do need to make sure your solution works for everyone. Ideally there would be a way to enable using training wheels for those that want/need them, without forcing them on those who can ride effectively without them. As I mentioned above I wouldn't want to be prevented from checking out (or committing to) a shared branch, since I sometimes actually need to. That being said, since using pre-commit hooks are at the user's discretion, I could simply not use the hook, or perhaps disable it temporarily when I need to bypass it, so I would not be too bothered by that sort of hook.

On the other hand, here's a really silly example that would bother me. In one of the Git repos I work out of we have a (strongly urged but not yet enforced) policy that all commit titles should begin with a capital letter. I recently demoed a pre-commit hook that warns you when you create a commit starting with a lowercase letter. Someone asked me if that hook could be changed to prevent from creating commits that don't begin with a capital letter, and I told them that I didn't want that, because I happen to purposefully make lots of local commits beginning with a lowercase letter. I'm referring to my WIP temporary commits, for which I use a lowercase letter as a note-to-self that I need to rewrite the commit before PR'ing it into a shared branch. If others wish to use the same mnemonic, they too would need that hook to be only a warning rather than a block. I would however support a block on the server side to prevent merging in commits that don't begin with a capital letter, as long as someone had the capability to override it, to cover the case where certain commits cannot (or should not) be re-written.

CodePudding user response:

However, some folks accidentally commit locally to master, and then their master branch gets out of sync with the origin, and they need to reset it with git reset --hard origin. I want to avoid this.

They don't need to do that. In Git, all ref names are local and completely, utterly arbitrary. Just rename the local branch.

git branch -M master whatIshouldhavecalledit

is all they need, rename their branch.

  • Related