4

I'm investigating the possibility of implementing a "pre-commit" verification flow, enforced on the central SCM server side. By "pre-commit" in this paragraph I don't mean the git pre-commit hook, I really mean before the change becomes part of the integration branch, visible to all developers working on the branch.

For git in particular, which is a distributed SCM, the commit phase happens on the local server, which is not what I need. So I can't use the git pre-commit hook.

From what I gather the pre-receive hook would be what I'm after:

This hook is invoked by 'git-receive-pack' on the remote repository, which happens when a 'git push' is done on a local repository. Just before starting to update refs on the remote repository, the pre-receive hook is invoked. Its exit status determines the success or failure of the update.

This hook executes once for the receive operation. [snip]

If the hook exits with non-zero status, none of the refs will be updated. If the hook exits with zero, updating of individual refs can still be prevented by the <<update,'update'>> hook.

Or maybe the related update hook:

This hook is invoked by 'git-receive-pack' on the remote repository, which happens when a 'git push' is done on a local repository. Just before updating the ref on the remote repository, the update hook is invoked. Its exit status determines the success or failure of the ref update.

The hook executes once for each ref to be updated, [snip]

The complication is that the verification itself can take a while.

My question: is the execution of these hooks serialized (by git)? Meaning will another hook invocation (due to a subsequent push operation performed before the previous hook execution completes):

  • fail,
  • block until the previous, still running execution finishes,

or

  • simply run in parallel with the previous hook execution?
2

In general git allows concurrent operations because one of them will eventually fail. Some specific database updates will have file locks (like updating the index files) but multiple receives can be in flight at once I think. In this case whichever verification completed first would be allowed in, and the second would fail due to having an invalid history (i.e. not being a fast-forward push).

In general this kind of operation is not well suited to git's server-side hooks since if something does fail, the person will then have to do some surgery on their branches to get things back in a valid state. These kinds of checks are generally run on the workstation side, though then you run into the "fun" that is distributing git hooks as they can't actually be checked in directly.

Another common solution is to have a CI service like Jenkins running verifications (tests) and updating a second branch or a utility tag showing which commits are acceptable.

  • Yeah, this confirms my suspicion - exactly what I was afraid of. And both suggestions (private dev verifications and post-commit CI verifications) have drawbacks which are serious obstacles to large scale integration - which is what I'm trying to address in the 1st place :) More thinking required. – Dan Cornilescu Mar 8 '17 at 18:09
3

Each Git hook is executed when a particular Git command is run. See the Git documentation on hooks for specifics. If you have two pushes updating the same ref, whichever one completes the "update" hook first gets to update the ref, and the other will get an error. It doesn't matter which client started to push first. It's all about which got to update the ref first.

When a push occurs, this runs a number of hooks. These three are run before a ref can be updated.

  • pre-push - Client Side, called by git push and can be used to prevent a push from taking place.
  • pre-receive - Server side, invoked by git-receive-pack on the remote repository. Just before starting to update refs on the remote repository, the pre-receive hook is invoked. Its exit status determines the success or failure of the update. If the hook exits with non-zero status, none of the refs will be updated. If the hook exits with zero, updating of individual refs can still be prevented by the update hook.
  • update - Server side, invoked by git-receive-pack on the remote repository. Just before updating the ref on the remote repository, the update hook is invoked. Its exit status determines the success or failure of the ref update. The hook executes once for each ref to be updated.

My 2 Cents: I think you're asking the wrong question. Any use of a Git hook should be quick and simple. If your verification requires more time than a second or two, it's too long and would be misplaced in a Git hook. A long running hook will frustrate users because they'll think Git is unresponsive. My suggestion is to apply the FAIL FAST, FAIL OFTEN principle.

In your case of a long running verification, use of a tool to perform the verification outside of Git is ideal, such as a unit test for example, runnable from CI or locally. Proper use of pull requests (i.e. merging short lived branches) should check that your verification passed and then allow incorporation of such a change.

  • The long-running verification server-side might have been acceptable if multiple of them could have been executed in parallel and still all succeed if verification was OK. But that's not happening, so can't use this as shortcut - fallback to candidate-only submissions on client side and automated commits later on, server side. Checking git format-patch workflow next :) – Dan Cornilescu Mar 8 '17 at 23:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.