Subject: Re: [PATCH] update-ref: add forward command to safely fast-forward refs

From: Junio C Hamano <[email-masked]>
Date: Thu, 23 Feb 2023 09:14:38 -0800

Ronan Pigott <[email-masked]> writes:

> forward is an update-ref command that behaves similarly to update, but
> takes an additional argument, <ancestor>, and verifies that the new
> value is a descendent of ancestor before updating. This is useful for
> fast-forwarding prefetched refs.

Why is this necessary?

 * Do you expect that you may not know the ancestry relationship
   between the <newvalue> and <ancestor> values when you need to
   compute them, in order to formulate the 'forward' command?

 * Is there a case where the relationship between <newvalue> and
   <ancestor> that was fast-forward when you formulated the
   'forward' command changes by the time the 'forward' command gets
   executed?

I do not see the reason why this new command is needed, unless one
or both of the above is what you are trying to address.

For example, existing "delete SP <ref> SP <oldvalue>" is to protect
the ref you want to delete, that used to have the oldvalue back when
you created the 'delete' command, from getting deleted when somebody
else changed it from the sidelines.  We can do

	oldvalue=$(git rev-parse refs/to/be/deleted^{object})
	echo delete refs/to/be/deleted $oldvalue | ...

and let the command notice if somebody else changed
refs/to/be/deleted in between.  It is similar to the second one
between the two I cited above, to make sure that your command does
not overwrite what somebody else did.

It may make some sense if the new <ancestor> thing is to replace the
<oldvalue> thing, though.  That is, if there were a three-commit chain

	A---B---C

where the ref you are trying to update currently points at A and you
want to update it to C.  You would observe that the current value is
A, and formulate the command line:

	update ref/to/be/updated C A

with the current system, and updating the ref is allowed only when
nobody touched the ref in the meantime.  It is _conceivable_ to say
that we are OK as long as the ref points at a decendant of A
(instead of pointing exactly at A), and is an ancestor of C (the
value we are updating to), with

	fast-forward ref/to/be/fast-forwarded C A

Then somebody else _could_ update the ref to B from the sideline,
but we notice that it is a descendant of A and an ancestor of C, and
we are still allowed to update it to C.

Even in that case, I am not sure how useful it would be, but at
least that use case I can see why it may make sense.

If you can write <newvalue> and <ancestor> on the command line, you
certainly should be able to compute "git merge-base".  And because
the commits are immutable, it won't change in the middle.

So, I am not very impressed.  Unless I am missing something, this
does not seem to be adding anything we cannot already do.

I wonder if

    git fetch . "+refs/prefetch/remotes/origin/*:refs/remotes/origin/*"

(with or without the '+' prefix, depending) what you are really
going after, though.