Subject: Re: [PATCH v4 1/3] push: add reflog check for "--force-if-includes"

From: Srinidhi Kaushik <[email-masked]>
Date: Mon, 21 Sep 2020 14:12:31 +0530

Hi Junio,

On 09/19/2020 13:03, Junio C Hamano wrote:
> Srinidhi Kaushik <[email-masked]> writes:
> 
> > Adds a check to verify if the remote-tracking ref of the local branch
> > is reachable from one of its "reflog" entries.
> 
> s/Adds/Add/

Gotcha, I will reword the commit messages for all three commits.
in the patch series.

> > When "--force-with-includes" is used along with "--force-with-lease",
> 
> A misspelt name for the new option is found here.

*Facepalm.* Thanks, will update.

> > [...]
> Makes me wonder, if in_merge_bases() is so expensive that it makes
> sense to split the "were we exactly at the tip?" and "is one of the
> commits we were at a descendant of the tip?" into separate phases,
> if this part should be calling in_merge_bases() one by one.
> 
> Would it make more sense to iterate over reflog entries from newer
> to older, collect them in an array of pointers to "struct commit" in
> a batch of say 8 commits or less, and then ask in_merge_bases_many()
> if the remote_commit is an ancestor of one of these local commits?
>
> The traversal cost to start from one "local commit" to see if
> remote_commit is an ancestor of it using in_merge_bases() and
> in_merge_bases_many() should be the same and an additional traversal
> cost to start from more local commits should be negligible compared
> to the traversal itself, so making a call to in_merge_bases() for
> each local_commit smells somewhat suboptimal.
> 
> If we were talking about older parts of the history, optional
> generation numbers could change the equation somewhat, but the
> common case for the workflow this series is trying to help is that
> these local commits ane the remote tip are relatively new and it is
> not unlikely that the generation numbers have not been computed for
> them, which is why I suspect that in_merges_many may be a win.

Nice! We can definitely try batching commits from the reflog and
pass it along to "in_merge_bases_many()". As for being faster than
calling "in_merge_bases()" for each commit entry in the reflog --
I am not familiar with how the former works. Do we still keep the
"reflog_entry_exists()" part? It might still be faster to go through
the entries once to check with "oideq()" in the first run.

Also, I was wondering if it is worth considering this:
  - check if the reflog of the HEAD has the remote ref
  - check if the reflog of the local branch has the remote ref
  - check if the remote ref is reachable from any of the local ref's
    "reflog" entries using "in_merge_bases_many()" in batches as
    suggested here.

The first two (we can even skip the second one) runs are relatively
fast, and the third one might be faster than checking "in_merge_bases()"
for each reflog entry. I suppose adding these three steps would make
the process slower overall, though. For context, I was referring to
your message [1] on the other thread regarding checking the HEAD's
reflog.

> > [...]
> > +		/*
> > +		 * If "compare-and-swap" is in "use_tracking[_for_rest]"
> > +		 * mode, and if "--foce-if-includes" was specified, run
> > +		 * the check.
> > +		 */
> > +		if (ref->if_includes)
> > +			check_if_includes_upstream(ref);
> 
> s/foce/force/; 

Yes, sorry about that; will update.
 
> I can see that the code is checking "and if force-if-includes was
> specified" part, but it is not immediately clear where the code
> checks if "--force-with-lease" is used with "tracking" and not with
> "the other side must be exactly this commit" mode here.
> 
>     ... goes and looks ...
> 
> Ah, ok, I found out. 
> 
> The field name "if_includes", and the comment for the field in
> remote.h, are both misleading.  It gives an impression that the
> field being true means "--force-if-included is in use", but in
> reality the field means a lot more.  When it is true, it signals
> that "--force-if-included" is in use *and* for this ref we were told
> to use the "--force-with-lease" without an exact object name.  And
> that logic is not here, but has already happened in apply_cas().
> 
> Which makes the above comment correct.  We however need a better
> name for this field and/or an explanation for the field in the
> header file, or both, to avoid misleading readers.
>
> > diff --git a/remote.h b/remote.h
> > index 5e3ea5a26d..38ab8539e2 100644
> > --- a/remote.h
> > +++ b/remote.h
> > @@ -104,7 +104,9 @@ struct ref {
> >  		forced_update:1,
> >  		expect_old_sha1:1,
> >  		exact_oid:1,
> > -		deletion:1;
> > +		deletion:1,
> > +		if_includes:1, /* If "--force-with-includes" was specified. */
> 
> The description needs to be tightened.
> 
> > +		unreachable:1; /* For "if_includes"; unreachable in reflog. */

OK, you're right. Perhaps, we could rename it to something like
"if_includes_for_tracking" and update the comment description
with saying something along the lines of:

+  /*
+   * Set when "--force-if-includes" is enabled, and
+   * if "compare-and-swap" is not provided with the
+   * exact commit to be expected on the remote (in
+   * "use_tracking" or use_tracking_for_rest" mode).
+   */

[1]: https://public-inbox.org/git/[email-masked]

Thanks again, for taking the time to review this.
-- 
Srinidhi Kaushik