Subject: Re: [PATCH 7/7] push: document --lockref

From: Jonathan Nieder <[email-masked]>
Date: Sun, 14 Jul 2013 14:28:00 -0700

Johannes Sixt wrote:
> Am 14.07.2013 22:34, schrieb Jonathan Nieder:

>> Would a '*' that acts like --lockref on a per ref basis address your
>> concerns?
>
> No, because I think that new syntax is not necessary.
>
> But admittedly, I haven't spent any time to think about push.default
> modes other than 'matching'. In particular, I wonder how Junio's last
> example with push.default=simple can work today:
>
>    $ git pull --rebase  # not a merge
>    $ git push
>
> because it is not a fast-forward.

Right, let's examine this example more closely.

If I run:

	(1) git pull --rebase
	(2) git push

then normally that push will be a fast-forward.  My changes are
on top of the new upstream changes, just as though I used format-patch
and send-email to submit the changes to a maintainer who would then
apply them.

However, someone else might have pushed to the same branch between
step (1) and (2), causing the fast-forward-only push to fail.

Usually that means other person made a valuable change and I can
simply repeat steps (1), and (2) and they will succeed.

But maybe that intervening push was a mistake.  To distinguish that
possibility I might do something like

	(3) git fetch origin
	(4) gitk @{u}@{1}..@{u}; # Is the change good?

	(5a) git pull --rebase; git push; # Yes, put my change on top of it
	(5b) git push --force; # No, my change is better!

So far so good.  But what if yet another change is made upstream
between step (3) and (5)?

If following approach (5a), that's fine.  We notice the new
intervening change and react accordingly, again.  There is a
possibility of starvation, but no other harm done.

In case (5b), it may be a serious problem.  I don't know about the
intervening change until I read the "git push" output, and in the
usual case I just won't notice.  The new lockref UI is meant to
address this problem.  So in the new world order, in case (5b) it
sounds like I should have instead used

	(5b') git push --allow-non-ff

Suppose I am writing a script that is meant to set the remote
repository to a known state.  Other contributors are only using
fast-forward updates so once my change goes in they will act
appropriately.  I just need to get my ref update in, without being
blocked by other ref updates.

Then I will use

	(5c) git push --force

which means not to use this new lockref trick that looks at my
remote-tracking branch and instead to just force the ref update.  This
would for example be the right semantics when pushing to a mirror from
a relay that also fetches from a canonical repository.  It avoids
needing to fetch from the target repo before every push.

Of course if ref updates are highly contended, even the current "git
push --force" will sometimes fail, since it internally *does* use a
compare-and-swap against the result of an ls-remote.  That's a (minor)
bug, imho.  Fixing it will require tweaking the protocol to make the
compare-and-swap optional.