diff mbox series

[v2] diff: fix interaction between the "-s" option and other options

Message ID 20230505165952.335256-1-gitster@pobox.com (mailing list archive)
State Accepted
Commit 9d484b92ed38c2222b6880e0bc2b572fdc837dbd
Headers show
Series [v2] diff: fix interaction between the "-s" option and other options | expand

Commit Message

Junio C Hamano May 5, 2023, 4:59 p.m. UTC
Sergey Organov noticed and reported "--patch --no-patch --raw"
behaves differently from just "--raw".  It turns out that there are
a few interesting bugs in the implementation and documentation.

 * First, the documentation for "--no-patch" was unclear that it
   could be read to mean "--no-patch" countermands an earlier
   "--patch" but not other things.  The intention of "--no-patch"
   ever since it was introduced at d09cd15d (diff: allow --no-patch
   as synonym for -s, 2013-07-16) was to serve as a synonym for
   "-s", so "--raw --patch --no-patch" should have produced no
   output, but it can be (mis)read to allow showing only "--raw"
   output.

 * Then the interaction between "-s" and other format options were
   poorly implemented.  Modern versions of Git uses one bit each to
   represent formatting options like "--patch", "--stat" in a single
   output_format word, but for historical reasons, "-s" also is
   represented as another bit in the same word.  This allows two
   interesting bugs to happen, and we have both X-<.

   (1) After setting a format bit, then setting NO_OUTPUT with "-s",
       the code to process another "--<format>" option drops the
       NO_OUTPUT bit to allow output to be shown again.  However,
       the code to handle "-s" only set NO_OUTPUT without unsetting
       format bits set earlier, so the earlier format bit got
       revealed upon seeing the second "--<format>" option.  This is
       the problem Sergey observed.

   (2) After setting NO_OUTPUT with "-s", code to process
       "--<format>" option can forget to unset NO_OUTPUT, leaving
       the command still silent.

It is tempting to change the meaning of "--no-patch" to mean
"disable only the patch format output" and reimplement "-s" as "not
showing anything", but it would be an end-user visible change in
behavior.  Let's fix the interactions of these bits to first make
"-s" work as intended.

The fix is conceptually very simple.

 * Whenever we set DIFF_FORMAT_FOO because we saw the "--foo"
   option (e.g. DIFF_FORMAT_RAW is set when the "--raw" option is
   given), we make sure we drop DIFF_FORMAT_NO_OUTPUT.  We forgot to
   do so in some of the options and caused (2) above.

 * When processing "-s" option, we should not just set
   DIFF_FORMAT_NO_OUTPUT bit, but clear other DIFF_FORMAT_* bits.
   We didn't do so and retained format bits set by options
   previously seen, causing (1) above.

It is even more tempting to lose NO_OUTPUT bit and instead take
output_format word being 0 as its replacement, but that would break
the mechanism "git show" uses to default to "--patch" output, where
the distinction between telling the command to be silent with "-s"
and having no output format specified on the command line matters,
and an explicit output format given on the command line should not
be "combined" with the default "--patch" format.

So, while we cannot lose the NO_OUTPUT bit, as a follow-up work, we
may want to replace it with OPTION_GIVEN bit, and

 * make "--patch", "--raw", etc. set DIFF_FORMAT_$format bit and
   DIFF_FORMAT_OPTION_GIVEN bit on for each format.  "--no-raw",
   etc. will set off DIFF_FORMAT_$format bit but still record the
   fact that we saw an option from the command line by setting
   DIFF_FORMAT_OPTION_GIVEN bit.

 * make "-s" (and its synonym "--no-patch") clear all other bits
   and set only the DIFF_FORMAT_OPTION_GIVEN bit on.

which I suspect would make the code much cleaner without breaking
any end-user expectations.

Once that is in place, transitioning "--no-patch" to mean the
counterpart of "--patch", just like "--no-raw" only defeats an
earlier "--raw", would be quite simple at the code level.  The
social cost of migrating the end-user expectations might be too
great for it to be worth, but at least the "GIVEN" bit clean-up
alone may be worth it.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/diff-options.txt |  7 +++++--
 diff.c                         | 24 +++++++++++++-----------
 t/t4000-diff-format.sh         | 34 +++++++++++++++++++++++++++++++++-
 3 files changed, 51 insertions(+), 14 deletions(-)

Comments

Eric Sunshine May 5, 2023, 5:41 p.m. UTC | #1
On Fri, May 5, 2023 at 1:02 PM Junio C Hamano <gitster@pobox.com> wrote:
> Sergey Organov noticed and reported "--patch --no-patch --raw"
> behaves differently from just "--raw".  It turns out that there are
> a few interesting bugs in the implementation and documentation.
>
>  * First, the documentation for "--no-patch" was unclear that it
>    could be read to mean "--no-patch" countermands an earlier
>    "--patch" but not other things.  The intention of "--no-patch"
>    ever since it was introduced at d09cd15d (diff: allow --no-patch
>    as synonym for -s, 2013-07-16) was to serve as a synonym for
>    "-s", so "--raw --patch --no-patch" should have produced no
>    output, but it can be (mis)read to allow showing only "--raw"
>    output.
>
>  * Then the interaction between "-s" and other format options were
>    poorly implemented.  Modern versions of Git uses one bit each to
>    represent formatting options like "--patch", "--stat" in a single
>    output_format word, but for historical reasons, "-s" also is
>    represented as another bit in the same word.  This allows two
>    interesting bugs to happen, and we have both X-<.
>
>    (1) After setting a format bit, then setting NO_OUTPUT with "-s",
>        the code to process another "--<format>" option drops the
>        NO_OUTPUT bit to allow output to be shown again.  However,
>        the code to handle "-s" only set NO_OUTPUT without unsetting
>        format bits set earlier, so the earlier format bit got
>        revealed upon seeing the second "--<format>" option.  This is

Glad to see "THis" from v1 fixed.

>        the problem Sergey observed.
>
>    (2) After setting NO_OUTPUT with "-s", code to process
>        "--<format>" option can forget to unset NO_OUTPUT, leaving
>        the command still silent.
>
> It is tempting to change the meaning of "--no-patch" to mean
> "disable only the patch format output" and reimplement "-s" as "not
> showing anything", but it would be an end-user visible change in
> behavior.  Let's fix the interactions of these bits to first make
> "-s" work as intended.
>
> The fix is conceptually very simple.
>
>  * Whenever we set DIFF_FORMAT_FOO because we saw the "--foo"
>    option (e.g. DIFF_FORMAT_RAW is set when the "--raw" option is
>    given), we make sure we drop DIFF_FORMAT_NO_OUTPUT.  We forgot to
>    do so in some of the options and caused (2) above.
>
>  * When processing "-s" option, we should not just set
>    DIFF_FORMAT_NO_OUTPUT bit, but clear other DIFF_FORMAT_* bits.
>    We didn't do so and retained format bits set by options
>    previously seen, causing (1) above.

The above description is very clear and well stated, even to someone
like me who didn't follow the discussion which culminated in this
patch.

> It is even more tempting to lose NO_OUTPUT bit and instead take
> output_format word being 0 as its replacement, but that would break
> the mechanism "git show" uses to default to "--patch" output, where
> the distinction between telling the command to be silent with "-s"
> and having no output format specified on the command line matters,
> and an explicit output format given on the command line should not
> be "combined" with the default "--patch" format.
>
> So, while we cannot lose the NO_OUTPUT bit, as a follow-up work, we
> may want to replace it with OPTION_GIVEN bit, and
>
>  * make "--patch", "--raw", etc. set DIFF_FORMAT_$format bit and
>    DIFF_FORMAT_OPTION_GIVEN bit on for each format.  "--no-raw",
>    etc. will set off DIFF_FORMAT_$format bit but still record the
>    fact that we saw an option from the command line by setting
>    DIFF_FORMAT_OPTION_GIVEN bit.
>
>  * make "-s" (and its synonym "--no-patch") clear all other bits
>    and set only the DIFF_FORMAT_OPTION_GIVEN bit on.
>
> which I suspect would make the code much cleaner without breaking
> any end-user expectations.
>
> Once that is in place, transitioning "--no-patch" to mean the
> counterpart of "--patch", just like "--no-raw" only defeats an
> earlier "--raw", would be quite simple at the code level.  The
> social cost of migrating the end-user expectations might be too
> great for it to be worth, but at least the "GIVEN" bit clean-up

s/worth/worthwhile/

> alone may be worth it.

And this final part addresses the big question which v1 left dangling
(specifically, "why the proposed patch doesn't eliminate NO_OUTPUT
altogether). Good.

> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Junio C Hamano May 5, 2023, 7:01 p.m. UTC | #2
Eric Sunshine <sunshine@sunshineco.com> writes:

> On Fri, May 5, 2023 at 1:02 PM Junio C Hamano <gitster@pobox.com> wrote:
> ...
> Glad to see "THis" from v1 fixed.
> ...
> And this final part addresses the big question which v1 left dangling
> (specifically, "why the proposed patch doesn't eliminate NO_OUTPUT
> altogether). Good.

Thanks.
Felipe Contreras May 9, 2023, 12:38 a.m. UTC | #3
Junio C Hamano wrote:
> Sergey Organov noticed and reported "--patch --no-patch --raw"
> behaves differently from just "--raw".  It turns out that there are
> a few interesting bugs in the implementation and documentation.
> 
>  * First, the documentation for "--no-patch" was unclear that it
>    could be read to mean "--no-patch" countermands an earlier
>    "--patch" but not other things.  The intention of "--no-patch"
>    ever since it was introduced at d09cd15d (diff: allow --no-patch
>    as synonym for -s, 2013-07-16) was to serve as a synonym for
>    "-s", so "--raw --patch --no-patch" should have produced no
>    output, but it can be (mis)read to allow showing only "--raw"
>    output.

I would say that is orthogonal.

>  * Then the interaction between "-s" and other format options were
>    poorly implemented.  Modern versions of Git uses one bit each to
>    represent formatting options like "--patch", "--stat" in a single
>    output_format word, but for historical reasons, "-s" also is
>    represented as another bit in the same word.  This allows two
>    interesting bugs to happen, and we have both X-<.
> 
>    (1) After setting a format bit, then setting NO_OUTPUT with "-s",
>        the code to process another "--<format>" option drops the
>        NO_OUTPUT bit to allow output to be shown again.  However,
>        the code to handle "-s" only set NO_OUTPUT without unsetting

s/only set/only sets/

>        format bits set earlier, so the earlier format bit got
>        revealed upon seeing the second "--<format>" option.  This is
>        the problem Sergey observed.
> 
>    (2) After setting NO_OUTPUT with "-s", code to process

s/code/the code/

>        "--<format>" option can forget to unset NO_OUTPUT, leaving
>        the command still silent.

> It is tempting to change the meaning of "--no-patch" to mean
> "disable only the patch format output" and reimplement "-s" as "not
> showing anything", but it would be an end-user visible change in
> behavior.

Yes, it would be a change in behavior from what no reasonable user would
expect, to what most reasonable users would expct.

These are synonyms:

 1.a. git diff --patch-with-raw
 1.b. git diff --patch --raw

And so should these:

 2.a. git diff --raw
 2.b. git diff --no-patch --raw

But who on Earth would then think these are different?

 2.b. git diff --no-patch --raw
 2.c. git diff --raw --no-patch

Your patch is *already* an end-user visible change in behavior, so why
not do the end-user visible change in behavior that reasonable users
would expect?

> Let's fix the interactions of these bits to first make "-s" work as
> intended.

Is it though?

> The fix is conceptually very simple.
> 
>  * Whenever we set DIFF_FORMAT_FOO because we saw the "--foo"
>    option (e.g. DIFF_FORMAT_RAW is set when the "--raw" option is
>    given), we make sure we drop DIFF_FORMAT_NO_OUTPUT.  We forgot to
>    do so in some of the options and caused (2) above.
> 
>  * When processing "-s" option, we should not just set
>    DIFF_FORMAT_NO_OUTPUT bit, but clear other DIFF_FORMAT_* bits.
>    We didn't do so and retained format bits set by options
>    previously seen, causing (1) above.
> 
> It is even more tempting to lose NO_OUTPUT bit and instead take
> output_format word being 0 as its replacement, but that would break
> the mechanism "git show" uses to default to "--patch" output, where
> the distinction between telling the command to be silent with "-s"
> and having no output format specified on the command line matters,
> and an explicit output format given on the command line should not
> be "combined" with the default "--patch" format.

That's because the logic is not correct, the default should not be 0,
the default should be a different value, for example
DIFF_FORMAT_DEFAULT, that way each tool can update DIFF_FORMAT_DEFAULT
to whatever default is desired.

Then 0 doesn't mean default, it means NO_OUTPUT, and then removing all
the formats--including DIFF_FORMAT_DEFAULT--makes it clear what the user
intends to do.

> So, while we cannot lose the NO_OUTPUT bit, as a follow-up work, we
> may want to replace it with OPTION_GIVEN bit, and
> 
>  * make "--patch", "--raw", etc. set DIFF_FORMAT_$format bit and
>    DIFF_FORMAT_OPTION_GIVEN bit on for each format.  "--no-raw",
>    etc. will set off DIFF_FORMAT_$format bit but still record the
>    fact that we saw an option from the command line by setting
>    DIFF_FORMAT_OPTION_GIVEN bit.
> 
>  * make "-s" (and its synonym "--no-patch") clear all other bits
>    and set only the DIFF_FORMAT_OPTION_GIVEN bit on.
> 
> which I suspect would make the code much cleaner without breaking
> any end-user expectations.

Why DIFF_FORMAT_OPTION_GIVEN? DIFF_FORMAT_DEFAULT as the opposite is
much more understandable.

> Once that is in place, transitioning "--no-patch" to mean the
> counterpart of "--patch", just like "--no-raw" only defeats an
> earlier "--raw", would be quite simple at the code level.

It's not only simple, it's a no-op, as (~DIFF_FORMAT_PATCH |
DIFF_FORMAT_OPTION_GIVEN) becomes indistinguishible from
DIFF_FORMAT_NO_OUTPUT unless another optin like DIFF_FORMAT_RAW is
specified.

I'm sending a patch series that shows that to be the case.
Junio C Hamano May 9, 2023, 1:22 a.m. UTC | #4
Felipe Contreras <felipe.contreras@gmail.com> writes:

>> Let's fix the interactions of these bits to first make "-s" work as
>> intended.
>
> Is it though?

Yes.

If the proposed log message says "as intended", the author thinks it
is.  Throwing a rhetorical question and stopping at that is not
useful; you'd need to explain yourself if you think differently.
Unless the only effect you want is to be argumentative and annoy
others, that is.

I've dug the history and as I explained elsewhere in the earlier
discussion, I know that the "--no-patch" originally was added as a
synonym for "-s" that makes the output from the diff machinery
silent---I have a good reason to believe that it is making "-s" and
"--no-patch" both work as intended.

I would not say that we should *not* move further with a follow up
topic, but I think we should consider doing so only after the dust
settles from this round.
Felipe Contreras May 9, 2023, 3:50 a.m. UTC | #5
Junio C Hamano wrote:
> Felipe Contreras <felipe.contreras@gmail.com> writes:
> 
> >> Let's fix the interactions of these bits to first make "-s" work as
> >> intended.
> >
> > Is it though?
> 
> Yes.
> 
> If the proposed log message says "as intended", the author thinks it
> is.

The question is not if the author of the patch thinks this is the way
`-s` is intended to work, the question is if this is the way `-s` is
intended to work.

The way `-s` is intended to work is completely independent of what the
author of the patch thinks, as `-s` existed well before this patch.

A cursory search for `-s` in diff-tree.c shows:

  Author: Linus Torvalds <torvalds@ppc970.osdl.org>
  Date:   Fri May 6 10:56:35 2005 -0700

      git-diff-tree: clean up output
      
      This only shows the tree headers when something actually changed. Also,
      add a "silent" mode, which doesn't actually show the changes at all,
      just the commit information.

So presumably the original author of `-s` intended for it to not show
any changes at all, but that was before any of the non-patch options
were introduced.

So, 18 years later: what is the intention behind `-s`?

> Throwing a rhetorical question and stopping at that is not
> useful;

Who says this is a rhetorical question?

`-s` was introduced 18 years ago, before any of the non-patch options
were introduced.

I do not think the intention behind `-s` in 2023 is clear at all, and
the patch does not attempt to answer that.

> Unless the only effect you want is to be argumentative and annoy
> others, that is.

Assume good faith:
https://en.wikipedia.org/wiki/Wikipedia:Assume_good_faith

> I've dug the history and as I explained elsewhere in the earlier
> discussion, I know that the "--no-patch" originally was added as a
> synonym for "-s" that makes the output from the diff machinery
> silent---I have a good reason to believe that it is making "-s" and
> "--no-patch" both work as intended.

I don't think so.

`-s` might have been added to make all the diff machinery silent, but
`--no-patch` is a different question, as the commit message of d09cd15d
makes abundantly clear:

  diff: allow --no-patch as synonym for -s
  
  This follows the usual convention of having a --no-foo option to negate
  --foo.

Now we know `-s` is not an antonym of `--patch`, so the commit message
of d09cd15d cannot possibly be correct.

There's only three options now:

 1. `-s` doesn't turn all the diff machinery silent, only --patch
 2. `--no-patch` is decoupled from `--patch`
 3. `--no-patch` is decoupled from `-s`

I don't think think there's any other reasonable option, including the
status quo.

> I would not say that we should *not* move further with a follow up
> topic, but I think we should consider doing so only after the dust
> settles from this round.

But what is that dust?

Do you agree with the following?

 1. No reasonable user would consider the status quo to be expected.
 2. Any change to the status quo would incur in backwards-incompatible
    changes for end-users.

If so, the only question remaining is what backwards-incompatible
changes shall be implemented.
Junio C Hamano May 10, 2023, 4:26 a.m. UTC | #6
Felipe Contreras <felipe.contreras@gmail.com> writes:

>> > Is it though?
>> 
>> Yes.
>> 
>> If the proposed log message says "as intended", the author thinks it
>> is.
>
> The question is not if the author of the patch thinks this is the way
> `-s` is intended to work, the question is if this is the way `-s` is
> intended to work.

The "author" refers to the author of the "proposed log message" of
the patch in question, i.e. me in this case.  The author of the
patch under discussion thinks it is, so asking "Is it?", implying
you do not agree, is nothing but a rhetorical question, and doing
so, without explaining why, wastes time on both sides.

I am not interested in getting involved in unproductive arguments
with you (or with anybody else for that matter).  I've been giving
you benefit of doubt, but I'll go back to refrain from responding to
your message, unless it is a patch that I can say "I agree 100% with
what the proposed log message says and what the patch text does,
looking great, thanks. Will queue." to, which has been my default
stance.

Past experience tells me that to any review other than "100% good",
I would see responses in an unpleasant and hostile manner.  Anything
that asks clarification for something unclear in your patch, or
suggests alternatives or improvements.  And it led to unproductive
and irritating waste of time number of times, and eventually you
were asked to leave the development community for at least a few
times.
Felipe Contreras May 10, 2023, 11:16 p.m. UTC | #7
Junio C Hamano wrote:
> Felipe Contreras <felipe.contreras@gmail.com> writes:
> 
> >> > Is it though?
> >> 
> >> Yes.
> >> 
> >> If the proposed log message says "as intended", the author thinks it
> >> is.
> >
> > The question is not if the author of the patch thinks this is the way
> > `-s` is intended to work, the question is if this is the way `-s` is
> > intended to work.
> 
> The "author" refers to the author of the "proposed log message" of
> the patch in question, i.e. me in this case.  The author of the
> patch under discussion thinks it is, so asking "Is it?",

This is the full quote:

====
Let's fix the interactions of these bits to first make "-s" work as intended.
====

If instead you meant this:

====
Let's fix the interactions of these bits to first make "-s" work as I intend.
====

Then that's not a rationale, you are essentially saying "let's do X because I
want".

> I am not interested in getting involved in unproductive arguments with you
> (or with anybody else for that matter).

This is the way the review process works and all git developers have to go
trough it.

We all have to convince others our proposed change is desirable.

Your patch is implementing a backwards-incompatible change:

  git diff -s --raw master

That command used not produce any output and after your patch it now produces
output.

Your commit message does not provide a rationale as to why *we* want to
implement this backwards-incompatible change.

"This is the way *I* intend `-s` to work" is not a rationale.

> And it led to unproductive and irritating waste of time number of times, and
> eventually you were asked to leave the development community for at least a
> few times.

That is blatantly false. As a member of Git's Project Leadership Committee, you
should know precisely how many times the committee has excercised this power,
and it hasn't been "a few times", it has been one time.

And this is a smoke screen: your commit message still doesn't provide any
rationale as to why `-s` should work the way *you* intend.

Throwing personal attacks at a reviewer for merely pointing out an issue in the
commit message is far from productive.
Felipe Contreras May 10, 2023, 11:41 p.m. UTC | #8
Felipe Contreras wrote:
> Junio C Hamano wrote:

> > And it led to unproductive and irritating waste of time number of times, and
> > eventually you were asked to leave the development community for at least a
> > few times.
> 
> That is blatantly false. As a member of Git's Project Leadership Committee, you
> should know precisely how many times the committee has excercised this power,
> and it hasn't been "a few times", it has been one time.

And for the record: that one time I was asked by the committee to not interact
with certain members of the community for a few months.

The amount of times I was asked to "leave the development community" is *zero*.
Jeff King May 11, 2023, 1:25 a.m. UTC | #9
On Wed, May 10, 2023 at 05:41:57PM -0600, Felipe Contreras wrote:

> Felipe Contreras wrote:
> > Junio C Hamano wrote:
> 
> > > And it led to unproductive and irritating waste of time number of times, and
> > > eventually you were asked to leave the development community for at least a
> > > few times.
> > 
> > That is blatantly false. As a member of Git's Project Leadership Committee, you
> > should know precisely how many times the committee has excercised this power,
> > and it hasn't been "a few times", it has been one time.
> 
> And for the record: that one time I was asked by the committee to not interact
> with certain members of the community for a few months.
> 
> The amount of times I was asked to "leave the development community" is *zero*.

You're right, in the sense that the first time you were asked to leave
we did not have a CoC, and nor was the PLC expected to be part of such
conversations at that time. Likewise, many times during which your
behavior has been a problem on the list, people did not ask you to
leave, but simply said "I am not going to read your messages anymore".

For example, here's Junio asking you to leave in 2013:

  https://lore.kernel.org/git/7vsj0lvs8f.fsf@alter.siamese.dyndns.org/

Here's him explaining a few months later why your patches aren't getting
reviewed:

  https://lore.kernel.org/git/xmqqtxgjg35a.fsf@gitster.dls.corp.google.com/

Here's me addressing complaints about your behavior half a year after
that:

 https://lore.kernel.org/git/20140514202646.GE2715@sigill.intra.peff.net/

    That last one has some gmane links; if anyone truly wants to follow
    them (and I don't recommend that as being worth your time), the lore
    equivalents are:

      https://lore.kernel.org/git/20140425191236.GA31637@sigill.intra.peff.net/

      https://lore.kernel.org/git/480ACEB0-7629-44DF-805F-E9543E66241B@quendi.de/

      https://lore.kernel.org/git/7vfvl0htys.fsf@alter.siamese.dyndns.org/

      https://lore.kernel.org/git/20140502223612.GA11374@sigill.intra.peff.net/

I'm sure you will find reason to argue with all of that. But I think the
spirit of "it led to an unproductive and irritating waste of time a
number of times" is accurate. And this thread is one more example.

You can feel free to respond if you want to; I'm not planning to
participate further in this thread (and in case you were not aware, I'm
not on the PLC any more). I just didn't want Junio to think he was alone
in his view of the situation.

-Peff
Junio C Hamano May 11, 2023, 1:50 a.m. UTC | #10
Felipe Contreras <felipe.contreras@gmail.com> writes:

>> The "author" refers to the author of the "proposed log message" of
>> the patch in question, i.e. me in this case.  The author of the
>> patch under discussion thinks it is, so asking "Is it?",
>
> This is the full quote:
>
> ====
> Let's fix the interactions of these bits to first make "-s" work as intended.
> ====
>
> If instead you meant this:
>
> ====
> Let's fix the interactions of these bits to first make "-s" work as I intend.
> ====
>
> Then that's not a rationale, you are essentially saying "let's do X because I
> want".

This will be the last message from me on this.  I wouldn't have even
seen the message I am responding to, as I've already done my "once
every few days sweep the spam folder to find things to salvage", but
somebody notified me of it, so...

I didn't say and I didn't mean "as I intend", and you know that.

I, the author of the patch under discussion, know that it is the
intention of the author of the earlier commit that introduced
"--no-patch" to make it work identically as "-s".

I even had a quote from that earlier commit in the proposed log
message of the patch (look for d09cd15d) to substantiate the fact
that it was the intended way for the option "--no-patch" to work.
So, either you are arguing against the patch you didn't even read,
or you are playing your usual word twisting game just for the sake
of arguing.

>> And it led to unproductive and irritating waste of time number of times, and
>> eventually you were asked to leave the development community for at least a
>> few times.
>
> That is blatantly false. As a member of Git's Project Leadership Committee, you
> should know precisely how many times the committee has excercised this power,
> and it hasn't been "a few times", it has been one time.

You were asked to leave in May 2014, and according to that message
from May 2014 [*1*], apparently you were asked to leave after a big
"Felipe eruption" in the summer of 2013 [*2*].  These happened long
before the project adopted a formal CoC at 5cdf2301 (add a Code of
Conduct document, 2019-09-24).

But apparently the "fact" does not matter to you.  I know that your
next excuse will be "I said the committee never exercised this power
more than once, which is a FACT", which may let you keep arguing
further.


[References]

*1* https://lore.kernel.org/git/53709788.2050201@alum.mit.edu/
*2* https://public-inbox.org/git/7vsj0lvs8f.fsf@alter.siamese.dyndns.org/
Felipe Contreras May 13, 2023, 3:07 a.m. UTC | #11
Jeff King wrote:
> On Wed, May 10, 2023 at 05:41:57PM -0600, Felipe Contreras wrote:
> > Felipe Contreras wrote:
> > > Junio C Hamano wrote:

> > > > And it led to unproductive and irritating waste of time number of times, and
> > > > eventually you were asked to leave the development community for at least a
> > > > few times.
> > > 
> > > That is blatantly false. As a member of Git's Project Leadership Committee, you
> > > should know precisely how many times the committee has excercised this power,
> > > and it hasn't been "a few times", it has been one time.
> > 
> > And for the record: that one time I was asked by the committee to not interact
> > with certain members of the community for a few months.
> > 
> > The amount of times I was asked to "leave the development community" is *zero*.
> 
> You're right, in the sense that the first time you were asked to leave
> we did not have a CoC,

That is is once again: *false*.

The git community has *never* asked me to leave.

> Likewise, many times during which your behavior has been a problem on the
> list,

False: it was not a problem on the list, it was a problem *for some people* on
the list.

> people did not ask you to leave, but simply said "I am not going to read your
> messages anymore".

Yes, and for every person who has said "I am not going to read your messages"
publicly, I received a response saying "thank you for saying what we are all
thinking but cannot say aloud for fear of reprisals" privately.

You do understand that people have different opinions? Some people hate Donald
Trump, some people don't. And some people cannot express their honest opinion
at $dayjob.

Having a different opinion is OK. And the foundation of a functioning civilized
democracy is to tolerate the opinions of others.

> For example, here's Junio asking you to leave in 2013:
> 
>   https://lore.kernel.org/git/7vsj0lvs8f.fsf@alter.siamese.dyndns.org/

Read the thread.

My objective was to show that the code organization was wrong, and libgit.a was
not an actual library. If there ever was any hope of having an actual libgit
library, the code needed to be reorganized:

====
  The plan is simple; make libgit.a a proper library, starting by
  clarifying what goes into libgit.a, and what doesn't. If there's any
  hopes of ever having a public library, it's clear what code doesn't
  belong in libgit.a; code that is meant for builtins, that code belongs
  in builtins/lib.a, or similar.
====
Felipe Contreras: [1]

The whole libification project of Google proves I was right: git was not (and
is not) ready to be a library. libgit.a is not nearly close to be an actual
standalone library. Pretty far from it.

Just today Elijah Newren sent a 27-patch series [2] attempting to move in the
right direction, but doesn't even begin to tip the scales to make libgit.so
possible.

My proposal did receive positive feedback:

====
  Nice joke patch to illustrate your point ;)
====
Ramkumar Ramachandra: [3]

====
  This is a good example: yes, I'm convinced that the code does need to
  be reorganized.
====
Ramkumar Ramachandra: [4]

Even you yourself provided useful positive feedback based on my proposal:

====
  If we want to start caring, then we probably need to create a separate
  "kitchen sink"-like library, with the rule that things in libgit.a
  cannot depend on it. In other words, a support library for Git's
  commands, for the parts that are not appropriate to expose as part of a
  library API.
====
Jeff King: [5]

Junio also provided good feedback initially:

====
  Another thing to think about is looking at pieces of data and
  functions defined in each *.o files and moving things around within
  them.  For example, looking at the dependency chain I quoted earlier
  for sequencer.o to build upload-pack, which is about responding to
  "git fetch" on the sending side:

  ...

  It is already crazy. There is no reason for the pack sender to be
  linking with the sequencer interpreter machinery. If the function
  definition (and possibly other ones) are split into separate source
  files (still in libgit.a), git-upload-pack binary does not have to
  pull in the whole sequencer.c at all.
====
Junio C Hamano: [6]

Things started to turn south when I expressed the following opinion:

====
  But init_copy_notes_for_rewrite() can *not* be used by anything other
  than git builtins. Standalone binaries will never use such a function,
  therefore it doesn't belong in libgit.a. Another example is
  alias_lookup(). They belong in builtin/lib.a.
====
Felipe Contreras: [7]

Junio asked me for an example of a function that would not belong to libgit.so,
and I said `init_copy_notes_for_rewrite()` is an example of a function that
nothing outside the `git` binary would need.

====
  But that is not a good justification for closing door to others that
  come later who may want to have a standalone that would want to use
  it.  Think about rewriting filter-branch.sh in C but not as a
  built-in, for example.
====
Junio C Hamano: [8]

I argued nobody would actually do that, and I was right, as eventually
filter-branch.sh was rewritten in C, but as a builtin, as I said it would.

Junio then argued that there was no justification for my claim that certain
functions would only be used by git builtins, and therefore they should not
belong in a libgit.so library:

====
  >> You still haven't justified why we have to _forbid_ any outside
  >> callers from calling copy_notes_for_rewrite().
  >
  > Because only builtins _should_ use it.

  And there is no justification behind that "_should_" claim; you are
  not making any technical argument to explain it.
====
Junio C Hamano: [9]

Google's libification project proves I was right: some functions should not
belong in libgit.a.

If Junio had listened to me back in 2013, the changes Google developers are
working on now to make libgit.a something that remotely resembles an actual
library would not be as monumental as they are in 2023.

Instead of considering my argument, Junio chose to attack me personally:

====
  I do not see a point in continuing to discuss this (or any design
  level issues) with you.  You seem to go into a wrong direction to
  break the design of the overall system, not in a direction to
  improve anything.  I do not know, and at this point I do not care,
  if you are doing so deliberately to sabotage Git.  Just stop.
====
Junio C Hamano: [9]

Even if Junio's opinion was the correct one (it's not: as Google's libification
project proves), it's not OK to personally attack a contributor merely for
expressing an opinion that happens to differ from that of the maintainer.

I am entitled to have my own opinion.

I already know what you are going to argue back: you are going to argue that
Google's libification project is different from my argument, but it's not:
Emily Shaffer's introductory mail explained the same thing:

====
  In other words, for some modules which
  already have clear boundaries inside of Git - like config.[ch],
  strbuf.[ch], etc. - we want to remove some implicit dependencies, like
  references to globals, and make explicit other dependencies, like
  references to other modules within Git.
====
Emily Shaffer: [10]

Google developers clearly believe the boundaries between "modules" are not
clear, and they should be. Which is *exactly* what I argued back in 2013.

You say Junio asked me to leave, but you conveniently avoid explaining *why*:
because he didn't like my opinion.

Junio was not content with simply saying "let's agree to disagree", he threw yet
another personal attack:

====
  So I do not think this is not even a bikeshedding.  Just one side
  being right, and the other side continuing to repeat nonsense
  without listening.
====
Junio C Hamano: [11]

And then:

====
  But what followed was a nonsense, which ended up wastign everybody's
  time:
====
Junio C Hamano: [12]

This breaks the current code of conduct, as it clearly is a behavior that is
not:

 * Demonstrating empathy and kindness toward other people
 * Being respectful of differing opinions, viewpoints, and experiences

This is what I objectively did *not* do in that thread:

 * Denigrate the opinions of others
 * Personally attack anybody

It was Junio the one who did that, not me.

Junio asked me to leave because I expressed an *opinion* he did not like.

Junio asked me to leave because I said in my opinion `copy_notes_for_rewrite()`
does not belong in libgit.a, because only git builtins should use it.

That's it.

I think it's incredibly deceitful of you to claim "Junio asked you to leave"
and provide a link, without explaining *why*.

Fast-forward to 2023, and Google developers are using the same language as I
did in 2013:

====
  Strbuf is a widely used basic structure that should only interact with other
  primitives in strbuf.[ch].
====
Calvin Wan: [13]

Is Junio asking them to leave the project for merely daring to express an
opinion about what *should* be the direction the Git project takes?

Of course not.

Ironically, the link you shared is a perfect example the double standards of
the Git project, in which a normative statement from a Google employee is par
for the course, but a normative statement from an unaffiliated contributor
(i.e. me) is complete heresy.

All of this is of course, nothing more than a smoke screen from the topic at hand.

---

This is the topic:

Subject: Re: [PATCH v2] diff: fix interaction between the "-s" option and other options

All that matters here is this:

 1. Apply Junio's patch
 2. Run this command `git diff -s --raw @~`

Does the command produce the same output before and after the patch? Yes or no.

That is it.

Stop dragging personal drama between Junio and me from 2013 in which nobody
else participated--including you--and answer the *only* relevant question in
this thread.

Does Junio's patch change the current behavior?

 a. Yes
 b. No

Cheers.

[1] https://lore.kernel.org/git/CAMP44s0cozMsTo7KQAjnqkqmvMwMw9D3SZrVxg48MOXkH9UQJQ@mail.gmail.com/
[2] https://lore.kernel.org/git/pull.1525.v2.git.1683875068.gitgitgadget@gmail.com/
[3] https://lore.kernel.org/git/CALkWK0mA7MXQv1k5bFpZLARDOHxU5kzKFXzcyUfb6NLZZY-=FA@mail.gmail.com/
[4] https://lore.kernel.org/git/CALkWK0=7PRndNc7XQ-PCPbVCp9vck909bA561JhQG6uXXj1n4g@mail.gmail.com/
[5] https://lore.kernel.org/git/20130610220627.GB28345@sigill.intra.peff.net/
[6] https://lore.kernel.org/git/7vwqq1ct0g.fsf@alter.siamese.dyndns.org/
[7] https://lore.kernel.org/git/CAMP44s03iXPVnunBdFT8etvZ-ew-D15A7mCV3wAAFXMNCpRAgA@mail.gmail.com/
[8] https://lore.kernel.org/git/7vppvsbkc3.fsf@alter.siamese.dyndns.org/
[9] https://lore.kernel.org/git/7vobbca1sr.fsf@alter.siamese.dyndns.org/
[10] https://lore.kernel.org/git/CAJoAoZ=Cig_kLocxKGax31sU7Xe4==BGzC__Bg2_pr7krNq6MA@mail.gmail.com/
[11] https://lore.kernel.org/git/7vehc8a05n.fsf@alter.siamese.dyndns.org/
[12] https://lore.kernel.org/git/7vzjuv14ir.fsf@alter.siamese.dyndns.org/
[13] https://lore.kernel.org/git/20230503184849.1809304-1-calvinwan@google.com/
Felipe Contreras May 13, 2023, 5:32 a.m. UTC | #12
Junio C Hamano wrote:
> Felipe Contreras <felipe.contreras@gmail.com> writes:

> >> The "author" refers to the author of the "proposed log message" of
> >> the patch in question, i.e. me in this case.  The author of the
> >> patch under discussion thinks it is, so asking "Is it?",
> >
> > This is the full quote:
> >
> > ====
> > Let's fix the interactions of these bits to first make "-s" work as intended.
> > ====
> >
> > If instead you meant this:
> >
> > ====
> > Let's fix the interactions of these bits to first make "-s" work as I intend.
> > ====
> >
> > Then that's not a rationale, you are essentially saying "let's do X because I
> > want".
> 
> This will be the last message from me on this.  I wouldn't have even
> seen the message I am responding to, as I've already done my "once
> every few days sweep the spam folder to find things to salvage",

Comment that breaks the code of conduct:

 * Demonstrating empathy and kindness toward other people
 * Being respectful of differing opinions, viewpoints, and experiences

Is the maintainer exempt from following the code of conduct?

> I didn't say and I didn't mean "as I intend", and you know that.

No, I don't know that, because I don't make assumptions.

You said this:

====
  >> Let's fix the interactions of these bits to first make "-s" work as
  >> intended.
  >
  > Is it though?

  Yes.

  If the proposed log message says "as intended", the author thinks it
  is.
====
[1]

Since you are "the author", the above directly translates to "I think it is as
intended", but I responded directly with:

====
  The question is not if the author of the patch thinks this is the way
  `-s` is intended to work, the question is if this is the way `-s` is
  intended to work.
====
[2]

Which is a perfectly valid response, to which you replied:

====
  The "author" refers to the author of the "proposed log message" of
  the patch in question, i.e. me in this case.  The author of the
  patch under discussion thinks it is, so asking "Is it?", implying
  you do not agree, is nothing but a rhetorical question, and doing
  so, without explaining why, wastes time on both sides.
====
[3]

This is not a valid response, because the question was never "does the author
of the patch think this behavior is intended", the question was "is this
behavior intended", and I made that abundantly clear in [2].

So there's only two options:

 a. This is the behavior you intend, and you meant to say this is the
    behaviour you intend.
 b. This is the behavior you think is intended, in which case if you think so
    or not is irrelevant, instead you need to provide a rationale for why
    you think that is the case, which you never did.

If it is `a`: that's not a rationale. If it is `b`: you still need a rationale.
Either way no rationale was provided in the commit message (or anywhere else).

You chose to avoid this question and instead throw personal attacks in [3],
which is not productive.

Fortunately for the project I decided to investigate the whole history behind
the true intention behind `-s` in [4].

In that investigation it became exceedingly clear that the intention behind
`-s` is different from the intention behind `--no-patch`. And it also became
clear that after making `output_format` a bit field: the intention of `-s`
became unclear.

The culmination of that investigation is the thread in which `--no-patch` was
introduced [5]. In that thread Matthieu Moy explained the true purpose was to
make it more accessible to silence the output of `git show`.

Furthermore, Matthieu Moy happened to respond today, and make it even more
clear [6]:

====
  Looking more closely, it's rather clear to me 
  they are not, and that

     git show --raw --patch --no-patch

  should be equivalent to

     git show --raw
====

Which is *exactly* what I and Sergey argued, and to repeat and make it
unquestionably clear:

  `--raw --patch --no-patch` should be equivalent to `--raw`.

Period.

You can throw all the personal attacks you want, but what you think is the
intended behavior of `-s` is irrelevant, the fact is that the intended behavior
of `--no-patch` is independent from the intended behavior of `-s`.

History--and the explicit explanation of the original author--proves that.

So, when I asked "is it though?", that wasn't a rhetorical question intended to
waste time: the answer is clearly: NO.

This is not the way `-s` is intended to work.

> >> And it led to unproductive and irritating waste of time number of times, and
> >> eventually you were asked to leave the development community for at least a
> >> few times.
> >
> > That is blatantly false. As a member of Git's Project Leadership Committee, you
> > should know precisely how many times the committee has excercised this power,
> > and it hasn't been "a few times", it has been one time.
> 
> You were asked to leave in May 2014, and according to that message
> from May 2014 [*1*],

This is the worst kind of misrepresentation.

The fact that *one person* said something, doesn't mean *the community* said that.

Anybody who is the leader of any organization should understand that the
opinion of *one person* is not the same as the opinion of a whole community.

And this is--once again--a smoke screen.

Whatever one person said in 2014 is totally and completely irrelevant to the
topic at hand.

---

The commit message of the patch does not explain why the behavior of `-s`
should be changed in a backwards-incompatible way.

[1] https://lore.kernel.org/git/xmqqsfc62t8y.fsf@gitster.g/
[2] https://lore.kernel.org/git/6459c31038e81_7c68294ee@chronos.notmuch/
[3] https://lore.kernel.org/git/xmqqjzxgzua0.fsf@gitster.g/
[4] https://lore.kernel.org/git/645c5da0981c1_16961a29455@chronos.notmuch/
[5] https://lore.kernel.org/git/1373893639-13413-1-git-send-email-Matthieu.Moy@imag.fr/
[6] https://lore.kernel.org/git/4f713a29-1a34-2f71-ee54-c01020be903a@univ-lyon1.fr/
diff mbox series

Patch

diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 3674ac48e9..7d5bb65a49 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -29,8 +29,11 @@  endif::git-diff[]
 
 -s::
 --no-patch::
-	Suppress diff output. Useful for commands like `git show` that
-	show the patch by default, or to cancel the effect of `--patch`.
+	Suppress all output from the diff machinery.  Useful for
+	commands like `git show` that show the patch by default to
+	squelch their output, or to cancel the effect of options like
+	`--patch`, `--stat` earlier on the command line in an alias.
+
 endif::git-format-patch[]
 
 ifdef::git-log[]
diff --git a/diff.c b/diff.c
index 648f6717a5..5a2f096683 100644
--- a/diff.c
+++ b/diff.c
@@ -4868,6 +4868,7 @@  static int diff_opt_stat(const struct option *opt, const char *value, int unset)
 	} else
 		BUG("%s should not get here", opt->long_name);
 
+	options->output_format &= ~DIFF_FORMAT_NO_OUTPUT;
 	options->output_format |= DIFF_FORMAT_DIFFSTAT;
 	options->stat_name_width = name_width;
 	options->stat_graph_width = graph_width;
@@ -4887,6 +4888,7 @@  static int parse_dirstat_opt(struct diff_options *options, const char *params)
 	 * The caller knows a dirstat-related option is given from the command
 	 * line; allow it to say "return this_function();"
 	 */
+	options->output_format &= ~DIFF_FORMAT_NO_OUTPUT;
 	options->output_format |= DIFF_FORMAT_DIRSTAT;
 	return 1;
 }
@@ -5086,6 +5088,7 @@  static int diff_opt_compact_summary(const struct option *opt,
 		options->flags.stat_with_summary = 0;
 	} else {
 		options->flags.stat_with_summary = 1;
+		options->output_format &= ~DIFF_FORMAT_NO_OUTPUT;
 		options->output_format |= DIFF_FORMAT_DIFFSTAT;
 	}
 	return 0;
@@ -5404,9 +5407,8 @@  static void prep_parse_options(struct diff_options *options)
 		OPT_BITOP('p', "patch", &options->output_format,
 			  N_("generate patch"),
 			  DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT),
-		OPT_BIT_F('s', "no-patch", &options->output_format,
-			  N_("suppress diff output"),
-			  DIFF_FORMAT_NO_OUTPUT, PARSE_OPT_NONEG),
+		OPT_SET_INT('s', "no-patch", &options->output_format,
+			    N_("suppress diff output"), DIFF_FORMAT_NO_OUTPUT),
 		OPT_BITOP('u', NULL, &options->output_format,
 			  N_("generate patch"),
 			  DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT),
@@ -5415,9 +5417,9 @@  static void prep_parse_options(struct diff_options *options)
 			       PARSE_OPT_NONEG | PARSE_OPT_OPTARG, diff_opt_unified),
 		OPT_BOOL('W', "function-context", &options->flags.funccontext,
 			 N_("generate diffs with <n> lines context")),
-		OPT_BIT_F(0, "raw", &options->output_format,
+		OPT_BITOP(0, "raw", &options->output_format,
 			  N_("generate the diff in raw format"),
-			  DIFF_FORMAT_RAW, PARSE_OPT_NONEG),
+			  DIFF_FORMAT_RAW, DIFF_FORMAT_NO_OUTPUT),
 		OPT_BITOP(0, "patch-with-raw", &options->output_format,
 			  N_("synonym for '-p --raw'"),
 			  DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW,
@@ -5426,12 +5428,12 @@  static void prep_parse_options(struct diff_options *options)
 			  N_("synonym for '-p --stat'"),
 			  DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT,
 			  DIFF_FORMAT_NO_OUTPUT),
-		OPT_BIT_F(0, "numstat", &options->output_format,
+		OPT_BITOP(0, "numstat", &options->output_format,
 			  N_("machine friendly --stat"),
-			  DIFF_FORMAT_NUMSTAT, PARSE_OPT_NONEG),
-		OPT_BIT_F(0, "shortstat", &options->output_format,
+			  DIFF_FORMAT_NUMSTAT, DIFF_FORMAT_NO_OUTPUT),
+		OPT_BITOP(0, "shortstat", &options->output_format,
 			  N_("output only the last line of --stat"),
-			  DIFF_FORMAT_SHORTSTAT, PARSE_OPT_NONEG),
+			  DIFF_FORMAT_SHORTSTAT, DIFF_FORMAT_NO_OUTPUT),
 		OPT_CALLBACK_F('X', "dirstat", options, N_("<param1,param2>..."),
 			       N_("output the distribution of relative amount of changes for each sub-directory"),
 			       PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
@@ -5447,9 +5449,9 @@  static void prep_parse_options(struct diff_options *options)
 		OPT_BIT_F(0, "check", &options->output_format,
 			  N_("warn if changes introduce conflict markers or whitespace errors"),
 			  DIFF_FORMAT_CHECKDIFF, PARSE_OPT_NONEG),
-		OPT_BIT_F(0, "summary", &options->output_format,
+		OPT_BITOP(0, "summary", &options->output_format,
 			  N_("condensed summary such as creations, renames and mode changes"),
-			  DIFF_FORMAT_SUMMARY, PARSE_OPT_NONEG),
+			  DIFF_FORMAT_SUMMARY, DIFF_FORMAT_NO_OUTPUT),
 		OPT_BIT_F(0, "name-only", &options->output_format,
 			  N_("show only names of changed files"),
 			  DIFF_FORMAT_NAME, PARSE_OPT_NONEG),
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
index bfcaae390f..8d50331b8c 100755
--- a/t/t4000-diff-format.sh
+++ b/t/t4000-diff-format.sh
@@ -5,6 +5,9 @@ 
 
 test_description='Test built-in diff output engine.
 
+We happen to know that all diff plumbing and diff Porcelain share the
+same command line parser, so testing one should be sufficient; pick
+diff-files as a representative.
 '
 
 TEST_PASSES_SANITIZE_LEAK=true
@@ -16,9 +19,11 @@  Line 2
 line 3'
 cat path0 >path1
 chmod +x path1
+mkdir path2
+>path2/path3
 
 test_expect_success 'update-index --add two files with and without +x.' '
-	git update-index --add path0 path1
+	git update-index --add path0 path1 path2/path3
 '
 
 mv path0 path0-
@@ -91,4 +96,31 @@  test_expect_success 'git diff-files --patch --no-patch does not show the patch'
 	test_must_be_empty err
 '
 
+
+# Smudge path2/path3 so that dirstat has something to show
+date >path2/path3
+
+for format in stat raw numstat shortstat summary \
+	dirstat cumulative dirstat-by-file \
+	patch-with-raw patch-with-stat compact-summary
+do
+	test_expect_success "--no-patch in 'git diff-files --no-patch --$format' is a no-op" '
+		git diff-files --no-patch "--$format" >actual &&
+		git diff-files "--$format" >expect &&
+		test_cmp expect actual
+	'
+
+	test_expect_success "--no-patch clears all previous ones" '
+		git diff-files --$format -s -p >actual &&
+		git diff-files -p >expect &&
+		test_cmp expect actual
+	'
+
+	test_expect_success "--no-patch in 'git diff --no-patch --$format' is a no-op" '
+		git diff --no-patch "--$format" >actual &&
+		git diff "--$format" >expect &&
+		test_cmp expect actual
+	'
+done
+
 test_done