Preventing Commits to the master Branch in OS X Mojave
Published 4 years, 9 months pastWhat happened was, I was preparing to roll out new designs for the News section and event pages of An Event Apart, and I had each rollout in its own branch. Somewhere in the process of bringing both into the master branch, I managed to create a merge conflict that rapidly led to more and more conflicts. I very nearly had to take off and nuke the entire site from orbit, just to start over. (A couple of branches, including dev, did have to get erased and re-pulled.)
Part of what made it worse was that at one point, I accidentally committed a quick edit to master, because I’d forgotten to check out the branch I was trying to edit, and my attempts to undo that mistake just compounded whatever other mistakes already existed. Once all the dust settled and things were back into good shape, I said to myself, “Self, I bet there’s a way to prevent commits to the master branch, because git is second only to emacs in the number of things you can do to/with it.” So I went looking, and yes, there is a way: add the following to your .git/hooks/pre-commit file.
#!/bin/sh
branch="$(git rev-parse --abbrev-ref HEAD)"
if [ "$branch" = "master" ]; then
echo "You can't commit directly to master branch"
exit 1
fi
I got that from this StackOverflow answer, and it was perfect for me, since I use the bash shell. So I created the pre-commit file, made a trivial README.md edit, and tried to commit to master. That’s when OS X Mojave’s Terminal spit back:
fatal: cannot exec '.git/hooks/pre-commit': Operation not permitted
Huh. I mean, it prevented me from committing to master, but not in a useful way. Once I verified that it happened in all branches, not just master, I knew there was trouble.
I checked permissions and all the rest, but I was still getting the error. If I went into .git/hooks and ran the script from the command line by ./pre-commit, I got a slightly different error:
-bash: ./pre-commit: /bin/bash: bad interpreter: Operation not permitted
So I submitted my own StackOverflow question, detailing what I’d done and the file and directory permissions and all the rest. I was stunned to find out the answer was that Mojave itself was blocking things, through its System Integrity Protection feature. Why did this simple file trigger SIP? I don’t know.
The fix, shared by both Jeff and Rich, was to go into .git/hooks and then type the following to check for SIP status:
xattr -l pre-commit
It showed a com.apple.quarantine value, so I then typed:
xattr -d com.apple.quarantine pre-commit
And that was it! Now if I try to commit a change to the master branch, the commit is rejected and I get a warning message. At that point, I can git stash the changes, check out the proper branch (or a new one), and then git stash pop to bring the changes into that branch, where I can commit them and then merge the changes in properly.
I may modify the script to reject commits to the dev branch as well, but I’m holding off on that for now, since the dev branch is often where merge conflicts are worked out before going to master. Either way, at least I’ll be less likely to accidentally foul up master when I’m hip-deep in other problems.