Re: [PATCH 2/2] built-in rebase: reinstate `checkout -q` behavior where appropriate

2018-11-12 Thread Junio C Hamano
Junio C Hamano  writes:

> Johannes Schindelin  writes:
>
>>> On the other hand, for a file-scope static that is likely to stay as a
>>> non-API function but a local helper, it may not be worth it.
>>
>> Oh, do you think that `reset_head()` is likely to stay as non-API
>> function? I found myself in the need of repeating this tedious
>> unpack_trees() dance quite a few times over the past two years, and it is
>> *always* daunting because the API is *that* unintuitive.
>>
>> So I *do* hope that `reset_head()` will actually be moved to reset.[ch]
>> eventually, and callsites e.g. in `sequencer.c` will be converted from
>> calling `unpack_trees()` to calling `reset_head()` instead.
>
> As long as the public API function is well thought out, of course it
> is OK.  Ideally, builtin/reset.c can detect when it is making a hard
> reset to HEAD and call that same function.  Which may or may not
> mean you would also want to tell it to record ORIG_HEAD and remove
> MERGE_HEAD and others), perhaps with another bit in that flag word.

Nah, forget about that one.  It sometimes does branch switching and
sometimes does hard reset, which looks like a strange chimera.  We
shouldn't burden it further by adding "while at it remove cruft, as
'reset --hard' needs to do that" to it.


Re: [PATCH 2/2] built-in rebase: reinstate `checkout -q` behavior where appropriate

2018-11-12 Thread Junio C Hamano
Johannes Schindelin  writes:

>> On the other hand, for a file-scope static that is likely to stay as a
>> non-API function but a local helper, it may not be worth it.
>
> Oh, do you think that `reset_head()` is likely to stay as non-API
> function? I found myself in the need of repeating this tedious
> unpack_trees() dance quite a few times over the past two years, and it is
> *always* daunting because the API is *that* unintuitive.
>
> So I *do* hope that `reset_head()` will actually be moved to reset.[ch]
> eventually, and callsites e.g. in `sequencer.c` will be converted from
> calling `unpack_trees()` to calling `reset_head()` instead.

As long as the public API function is well thought out, of course it
is OK.  Ideally, builtin/reset.c can detect when it is making a hard
reset to HEAD and call that same function.  Which may or may not
mean you would also want to tell it to record ORIG_HEAD and remove
MERGE_HEAD and others), perhaps with another bit in that flag word.


Re: [PATCH 2/2] built-in rebase: reinstate `checkout -q` behavior where appropriate

2018-11-12 Thread Johannes Schindelin
Hi Junio,

On Mon, 12 Nov 2018, Junio C Hamano wrote:

> Johannes Schindelin  writes:
> 
> >> >  static int reset_head(struct object_id *oid, const char *action,
> >> > -  const char *switch_to_branch, int detach_head,
> >> > +  const char *switch_to_branch,
> >> > +  int detach_head, int reset_hard,
> >> 
> >> It might be worth switching to a single flag variable here. It would
> >> make calls like this:
> >> 
> >> > -if (reset_head(>object.oid, "checkout", NULL, 1,
> >> > +if (reset_head(>object.oid, "checkout", NULL, 1, 
> >> > 0,
> >> >  NULL, msg.buf))
> >> 
> >> a little more self-documenting (if a little more verbose).
> >
> > I thought about that, but for just two flags? Well, let me try it and see
> > whether I like the result ;-)
> 
> My rule of thumb is that repeating three times is definitely when we
> should consolidate separate ones into a single flag word, and twice
> is a borderline.
> 
> For two-time repetition, it is often worth fixing when somebody
> notices it during the review, as that is a sign that repetition,
> even a minor one, disturbed a reader enough to point out.

That's my thought exactly, hence I looked into it. The end result is
actually easier to read, in particular the commit that re-introduces the
`reset --hard` behavior: it no longer touches *all* callsites of
`reset_head()` but only the relevant ones.

> On the other hand, for a file-scope static that is likely to stay as a
> non-API function but a local helper, it may not be worth it.

Oh, do you think that `reset_head()` is likely to stay as non-API
function? I found myself in the need of repeating this tedious
unpack_trees() dance quite a few times over the past two years, and it is
*always* daunting because the API is *that* unintuitive.

So I *do* hope that `reset_head()` will actually be moved to reset.[ch]
eventually, and callsites e.g. in `sequencer.c` will be converted from
calling `unpack_trees()` to calling `reset_head()` instead.

v2 on the way,
Dscho

> So I am OK either way, slightly in favor of fixing it while we
> remember.
> 
> 
> >> This one could actually turn into:
> >> 
> >>   ret = error(...);
> >>   goto leave_reset_head;
> >> 
> >> now. We don't have to worry about an uninitialized desc.buffer anymore
> >> (as I mentioned in the previous email), because "nr" would be 0.
> >> 
> >> It doesn't save any lines, though (but maybe having a single
> >> cleanup/exit point would make things easier to read; I dunno).
> >
> > But you're right, of course. Consistency makes for easier-to-read code.
> 
> Yup, that does sound good.
> 
> Thanks.
> 


Re: [PATCH 2/2] built-in rebase: reinstate `checkout -q` behavior where appropriate

2018-11-12 Thread Johannes Schindelin
Hi Peff,

On Fri, 9 Nov 2018, Jeff King wrote:

> On Fri, Nov 09, 2018 at 06:21:41PM +0100, Johannes Schindelin wrote:
> 
> > Actually, you got me thinking about the desc.buffer. And I think there is
> > one corner case where it could cause a problem: `struct tree_desc desc[2]`
> > does not initialize the buffers to NULL. And what if
> > fill_tree_descriptor() function returns NULL? Then the buffer is still
> > uninitialized.
> > 
> > In practice, our *current* version of fill_tree_descriptor() never returns
> > NULL if the oid parameter is non-NULL. It would die() in the error case
> > instead (bad!). So to prepare for a less bad version, I'd rather
> > initialize the `desc` array and be on the safe (and easier to reason
> > about) side.
> 
> Yeah, I agree with all of that.
> 
> One solution would just be to increment only after success:
> 
>   if (fill_tree_descriptor([nr], ..) < 0)
>   goto error;
>   nr++; /* now we know it's valid! */
> 
> But there are lots of alternatives.  :)

True. I simply prefer to initialize it and be done with it. ;-)

Ciao,
Dscho


Re: [PATCH 2/2] built-in rebase: reinstate `checkout -q` behavior where appropriate

2018-11-11 Thread Junio C Hamano
Johannes Schindelin  writes:

>> >  static int reset_head(struct object_id *oid, const char *action,
>> > -const char *switch_to_branch, int detach_head,
>> > +const char *switch_to_branch,
>> > +int detach_head, int reset_hard,
>> 
>> It might be worth switching to a single flag variable here. It would
>> make calls like this:
>> 
>> > -  if (reset_head(>object.oid, "checkout", NULL, 1,
>> > +  if (reset_head(>object.oid, "checkout", NULL, 1, 0,
>> >NULL, msg.buf))
>> 
>> a little more self-documenting (if a little more verbose).
>
> I thought about that, but for just two flags? Well, let me try it and see
> whether I like the result ;-)

My rule of thumb is that repeating three times is definitely when we
should consolidate separate ones into a single flag word, and twice
is a borderline.

For two-time repetition, it is often worth fixing when somebody
notices it during the review, as that is a sign that repetition,
even a minor one, disturbed a reader enough to point out.  On the
other hand, for a file-scope static that is likely to stay as a
non-API function but a local helper, it may not be worth it.

So I am OK either way, slightly in favor of fixing it while we
remember.


>> This one could actually turn into:
>> 
>>   ret = error(...);
>>   goto leave_reset_head;
>> 
>> now. We don't have to worry about an uninitialized desc.buffer anymore
>> (as I mentioned in the previous email), because "nr" would be 0.
>> 
>> It doesn't save any lines, though (but maybe having a single
>> cleanup/exit point would make things easier to read; I dunno).
>
> But you're right, of course. Consistency makes for easier-to-read code.

Yup, that does sound good.

Thanks.


Re: [PATCH 2/2] built-in rebase: reinstate `checkout -q` behavior where appropriate

2018-11-09 Thread Jeff King
On Fri, Nov 09, 2018 at 06:21:41PM +0100, Johannes Schindelin wrote:

> Actually, you got me thinking about the desc.buffer. And I think there is
> one corner case where it could cause a problem: `struct tree_desc desc[2]`
> does not initialize the buffers to NULL. And what if
> fill_tree_descriptor() function returns NULL? Then the buffer is still
> uninitialized.
> 
> In practice, our *current* version of fill_tree_descriptor() never returns
> NULL if the oid parameter is non-NULL. It would die() in the error case
> instead (bad!). So to prepare for a less bad version, I'd rather
> initialize the `desc` array and be on the safe (and easier to reason
> about) side.

Yeah, I agree with all of that.

One solution would just be to increment only after success:

  if (fill_tree_descriptor([nr], ..) < 0)
goto error;
  nr++; /* now we know it's valid! */

But there are lots of alternatives.  :)

-Peff


Re: [PATCH 2/2] built-in rebase: reinstate `checkout -q` behavior where appropriate

2018-11-09 Thread Johannes Schindelin
Hi Peff,

On Fri, 9 Nov 2018, Jeff King wrote:

> On Fri, Nov 09, 2018 at 01:34:19AM -0800, Johannes Schindelin via 
> GitGitGadget wrote:
> 
> > From: Johannes Schindelin 
> > 
> > When we converted a `git checkout -q $onto^0` call to use
> > `reset_head()`, we inadvertently incurred a change from a twoway_merge
> > to a oneway_merge, as if we wanted a `git reset --hard` instead.
> > 
> > This has performance ramifications under certain, though, as the
> > oneway_merge needs to lstat() every single index entry whereas
> > twoway_merge does not.
> > 
> > So let's go back to the old behavior.
> 
> Makes sense. I didn't think too hard about any possible gotchas with the
> twoway/oneway switch, but if that's what git-checkout was doing before,
> it seems obviously safe.

Right.

> > diff --git a/builtin/rebase.c b/builtin/rebase.c
> > index 6f6d7de156..c1cc50f3f8 100644
> > --- a/builtin/rebase.c
> > +++ b/builtin/rebase.c
> > @@ -523,11 +523,12 @@ finished_rebase:
> >  #define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
> >  
> >  static int reset_head(struct object_id *oid, const char *action,
> > - const char *switch_to_branch, int detach_head,
> > + const char *switch_to_branch,
> > + int detach_head, int reset_hard,
> 
> It might be worth switching to a single flag variable here. It would
> make calls like this:
> 
> > -   if (reset_head(>object.oid, "checkout", NULL, 1,
> > +   if (reset_head(>object.oid, "checkout", NULL, 1, 0,
> > NULL, msg.buf))
> 
> a little more self-documenting (if a little more verbose).

I thought about that, but for just two flags? Well, let me try it and see
whether I like the result ;-)

> > -   if (!oid) {
> > -   if (get_oid("HEAD", _oid)) {
> > -   rollback_lock_file();
> > -   return error(_("could not determine HEAD revision"));
> > -   }
> > -   oid = _oid;
> > +   if (get_oid("HEAD", _oid)) {
> > +   rollback_lock_file();
> > +   return error(_("could not determine HEAD revision"));
> > }
> 
> This one could actually turn into:
> 
>   ret = error(...);
>   goto leave_reset_head;
> 
> now. We don't have to worry about an uninitialized desc.buffer anymore
> (as I mentioned in the previous email), because "nr" would be 0.
> 
> It doesn't save any lines, though (but maybe having a single
> cleanup/exit point would make things easier to read; I dunno).

But you're right, of course. Consistency makes for easier-to-read code.

> Take all my comments as observations, not objections. This looks OK to
> me either way.

Actually, you got me thinking about the desc.buffer. And I think there is
one corner case where it could cause a problem: `struct tree_desc desc[2]`
does not initialize the buffers to NULL. And what if
fill_tree_descriptor() function returns NULL? Then the buffer is still
uninitialized.

In practice, our *current* version of fill_tree_descriptor() never returns
NULL if the oid parameter is non-NULL. It would die() in the error case
instead (bad!). So to prepare for a less bad version, I'd rather
initialize the `desc` array and be on the safe (and easier to reason
about) side.

Thank you for your helpful review,
Dscho


Re: [PATCH 2/2] built-in rebase: reinstate `checkout -q` behavior where appropriate

2018-11-09 Thread Jeff King
On Fri, Nov 09, 2018 at 01:34:19AM -0800, Johannes Schindelin via GitGitGadget 
wrote:

> From: Johannes Schindelin 
> 
> When we converted a `git checkout -q $onto^0` call to use
> `reset_head()`, we inadvertently incurred a change from a twoway_merge
> to a oneway_merge, as if we wanted a `git reset --hard` instead.
> 
> This has performance ramifications under certain, though, as the
> oneway_merge needs to lstat() every single index entry whereas
> twoway_merge does not.
> 
> So let's go back to the old behavior.

Makes sense. I didn't think too hard about any possible gotchas with the
twoway/oneway switch, but if that's what git-checkout was doing before,
it seems obviously safe.

> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index 6f6d7de156..c1cc50f3f8 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -523,11 +523,12 @@ finished_rebase:
>  #define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
>  
>  static int reset_head(struct object_id *oid, const char *action,
> -   const char *switch_to_branch, int detach_head,
> +   const char *switch_to_branch,
> +   int detach_head, int reset_hard,

It might be worth switching to a single flag variable here. It would
make calls like this:

> - if (reset_head(>object.oid, "checkout", NULL, 1,
> + if (reset_head(>object.oid, "checkout", NULL, 1, 0,
>   NULL, msg.buf))

a little more self-documenting (if a little more verbose).

> - if (!oid) {
> - if (get_oid("HEAD", _oid)) {
> - rollback_lock_file();
> - return error(_("could not determine HEAD revision"));
> - }
> - oid = _oid;
> + if (get_oid("HEAD", _oid)) {
> + rollback_lock_file();
> + return error(_("could not determine HEAD revision"));
>   }

This one could actually turn into:

  ret = error(...);
  goto leave_reset_head;

now. We don't have to worry about an uninitialized desc.buffer anymore
(as I mentioned in the previous email), because "nr" would be 0.

It doesn't save any lines, though (but maybe having a single
cleanup/exit point would make things easier to read; I dunno).

Take all my comments as observations, not objections. This looks OK to
me either way.

-Peff