Reputation: 1607
I have a scenario in which I need to create, add and commit a file to the repo for every .hbs file that is added or changes. I do all that in a post-receive
hook because the requirements state that it must be done server-side.
In order to do that, I keep a working copy of the repo on the server (Gitlab) so I can add the file to the repo. In my hook, I generate the file then do git add
, git commit
and git push
.
What worries me with that approach is what happens if many people push at the same time? If the push operations run concurrently, I think it might be problematic. But if they are run sequentially, then I see no problem.
So my question is: Can I assume that git push
(particularly the post-receive
hook) run sequentially on the server?
Upvotes: 2
Views: 217
Reputation: 1324937
Quarantine areas were new in some post-2.0 Git version. These details mostly don't matter; they're only really important if a pre-receive or update hook rejects some name-change.
The problem is: before Git 2.36, a failed push might not clear the quanrantine area, which could after other concurrent push.
With Git 2.36 (Q2 2022), "receive-pack
" checks if it will do any ref updates (various conditions could reject a push) before received objects are taken out of the temporary directory used for quarantine purposes, so that a push that is known-to-fail will not leave crufts that a future "gc
" needs to clean up.
See commit 5407764 (29 Jan 2022) by Chen Bojun (cbj-bojun
).
(Merged by Junio C Hamano -- gitster
-- in commit 867b520, 18 Feb 2022)
receive-pack
: purge temporary data if no command is ready to runHelped-by: Jiang Xin
Helped-by: Teng Long
Signed-off-by: Chen Bojun
When pushing a hidden ref, e.g.:
$ git push origin HEAD:refs/hidden/foo
"
receive-pack
" will reject our request with an error message like this:! [remote rejected] HEAD -> refs/hidden/foo (deny updating a hidden ref)
The remote side ("
git-receive-pack
") will not create the hidden ref as expected, but the pack file sent by "git-send-pack" is left inside the remote repository.
I.e. the quarantine directory is not purged as it should be.Add a checkpoint before calling
"tmp_objdir_migrate()
" and after calling the "pre-receive
" hook to purge that temporary data in the quarantine area when there is no command ready to run.The reason we do not add the checkpoint before the "
pre-receive
" hook, but after it, is that the "pre-receive
" hook is called with a switch-off "skip_broken
" flag, and all commands, even broken ones, should be fed by calling "feed_receive_hook()
".
Upvotes: 0
Reputation: 488453
The short answer is no: you cannot make this assumption. You can make some others, though.
Any Git repository will lock any given reference while updating it. This is true even on a purely-local Git operation, without any client/server stuff going on: if you run one git commit
in one window, and another git commit
in another window, at the same time, on the same branch, only one at a time can actually update that branch name.
That said, the actual updating occurs before the post-receive hook runs. All locks (on branch names, and on the index itself) are released after the pre-receive
and update
hooks run, before the post-update
hook runs. So we can construct some race scenarios like this:
git push
(from Alice's computer) to send commit a123456
to the server and to ask the server to set the server's master
branch to identify commit a123456
.git push
(from Bob's computer) to send commit b789abc
to the server and to ask the server to set the server's feature
to identify commit b789abc
.git push
to send commit fedcba9
to the server and to ask the server to set the server's master
to identify commit fedcba9
.One of these three will "win the race" to start updating. Let's assume, without (I hope) loss of generality, that it's Alice, and that Bob will go next and Carol will go third. Alice's commit goes into the quarantine area,1 and the server's pre-receive and update hooks get a chance to decide whether making the server's master
identify a123456
is a good idea. If her git push
is not forced, the server Git will first ensure that the current value of master
is an ancestor of this proposed new commit, too—i.e., that the git push
is a fast-forward operation.
If all goes well, Alice's commit is moved from quarantine to the main repository database and master
now identifies a123456
on the server. The server releases all the locks and begins running the post-receive
hook. Since Bob is queued up, the server now (almost immediately—it's a separate process that has been waiting) takes Bob's commit b789abc
and puts that in quarantine and starts the vetting process for the server's refs/heads/feature
. If all goes well, the commit moves from quarantine to the main database, refs/heads/feature
now identifies b789abc
, and Git spins off the post-receive hook. (If the push is rejected, I think Git still spins off the post-receive hook, but now there are no updates in its stdin stream. Let's assume the push is accepted.)
At this point Carol's push can run. Her commit fedcba9
almost certainly does not have a123456
as an ancestor—the only way it could have it is if Carol got this from Alice earlier, after all—so after her commit goes into quarantine, her attempt to set master
will be rejected unless she used --force
. The server Git then runs the post-receive hook, but with no updates in its stdin stream. (Possibly, the post-receive hook doesn't run at all.)
If the first post-receive hook is still running, there are, at this point, three post-receive hooks running. One has, in its stdin, an update to refs/heads/master
from whatever hash ID it held before, to a123456
. One has, in its stdin, an update to refs/heads/feature
, from whatever hash ID it held before, to b789abc
. The third has an empty stdin stream: there are no updates to perform.
What happens from here on out is, of course, up to you, the author of the post-receive hook.
1Quarantine areas were new in some post-2.0 Git version. These details mostly don't matter; they're only really important if a pre-receive or update hook rejects some name-change.
Upvotes: 3