Detecting push --force

762 views
Skip to first unread message

Irfan Adilovic

unread,
Jun 30, 2016, 5:52:20 AM6/30/16
to gitolite
I'd like to perform some custom restrictions on tag pushes, like "tag name must match a regex", "tag must be annotated", but allow these actions if the user supplies --force when pushing. Something similar to git's default behavior when trying to push a modified tag that exists in the remote -- this only works if --force is supplied.

Googling for the issue seems that the only way to detect this solely with git is to try to detect a situation which would have been impossible/unlikely without a push (i.e. detect that a branch push is non-fast-forward, or that a tag is being moved -- things that can't happen without --force).

As my hook would be rejecting otherwise perfectly normal pushes, that approach would not work.

Can gitolite help with this in any way?

Regards, Irfan

Sitaram Chamarty

unread,
Jun 30, 2016, 5:57:26 AM6/30/16
to Irfan Adilovic, gitolite
The "--force" is not passed to the remote, at least not at the level
that gitolite operates (i.e., it may well be part of the git protocol
communication for all I know).

So... no sorry!

regards
sitaram

Irfan Adilovic

unread,
Jun 30, 2016, 6:07:57 AM6/30/16
to Sitaram Chamarty, gitolite
Bummer. But thanks a lot for the quick feedback!

-- Irfan

Irfan Adilovic

unread,
Jun 30, 2016, 9:37:03 AM6/30/16
to Sitaram Chamarty, gitolite
This itched me a bit more, so I dove in to see if it can be hacked into git easily, and am sharing what I found: it absolutely can't.

The remote has no *explicit* information about the --force flag (or + prefix to a ref) used by the client -- one would have to amend the packfile transfer protocol [https://www.kernel.org/pub/software/scm/git/docs/technical/pack-protocol.htmlto pass the force flag to the remote. It is the pushing client who decides to bail out when a --force is needed, not the server.

This can be verified with GIT_TRACE_PACKET to see the exact protocol lines being exchanged:

# new tag without --force
$ git tag foo; GIT_TRACE_PACKET=1 git push --tags |& egrep -o 'push> .{5,}'
push> 0000000000000000000000000000000000000000 9edbf952c0cb944073b454b713e15f09e70f1aef refs/tags/foo\0 report-status side-band-64k quiet agent=git/2.8.3

# new tag with --force
$ git tag bar; GIT_TRACE_PACKET=1 git push --tags --force |& egrep -o 'push> .{5,}'
push> 0000000000000000000000000000000000000000 9edbf952c0cb944073b454b713e15f09e70f1aef refs/tags/bar\0 report-status side-band-64k quiet agent=git/2.8.3

As can be seen, there is no way to detect this kind of --force on the remote, which is what I was originally looking for.

# move remote tag without --force => no push was ever attempted by the client
$ git tag -fm "" foo >/dev/null; GIT_TRACE_PACKET=1 git push --tags |& egrep -o 'push> .{5,}'

# move remote tag with --force
$ git tag -fm "" foo >/dev/null; GIT_TRACE_PACKET=1 git push --tags --force |& egrep -o 'push> .{5,}'
push> 9edbf952c0cb944073b454b713e15f09e70f1aef d7c2313f0b67fe13ab57f632108e49c524559898 refs/tags/foo\0 report-status side-band-64k quiet agent=git/2.8.3

This kind of --force is trivial to detect by checking that neither sha is all zeros.

Regards, Irfan

Sitaram Chamarty

unread,
Jun 30, 2016, 9:53:00 AM6/30/16
to Irfan Adilovic, gitolite
On 06/30/2016 07:06 PM, Irfan Adilovic wrote:
> This itched me a bit more, so I dove in to see if it can be hacked
> into git easily, and am sharing what I found: it absolutely can't.

On the other hand, it should be fairly easy to do something like this:

git push origin bad-name # rejected

git push origin some-temp-good-name # accepted
ssh git@host rename some-temp-good-name bad-name

The explicit run of 'rename' is the moral equivalent of '--force'.

Not sure how that would work for annotated tag check though; maybe
something similar can be worked out.

Tim Nordell

unread,
Jun 30, 2016, 9:55:39 AM6/30/16
to Sitaram Chamarty, Irfan Adilovic, gitolite
Or one could do a separate operation to whitelist a specific name for your script, then do the git push.  E.g.:

ssh push origin bad-name # rejected
ssh git@host allow path/to/repo bad-name # accepted
ssh push origin bad-name # accepted

Same number of steps though for the end user.

- Tim


--
You received this message because you are subscribed to the Google Groups "gitolite" group.
To unsubscribe from this group and stop receiving emails from it, send an email to gitolite+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Sitaram Chamarty

unread,
Jun 30, 2016, 10:10:55 AM6/30/16
to Tim Nordell, Irfan Adilovic, gitolite
On 06/30/2016 07:25 PM, Tim Nordell wrote:
> Or one could do a separate operation to whitelist a specific name for your script, then do the git push. E.g.:
>
> ssh push origin bad-name # rejected
> ssh git@host allow path/to/repo bad-name # accepted
> ssh push origin bad-name # accepted
>
> Same number of steps though for the end user.

much more complicated to implement though. You need to store the whitelisted name somewhere, then the push's VREF must read it.

the rename thing was more like "you have what you have, just add this command" :)

regards
sitaram

Reply all
Reply to author
Forward
0 new messages