Thread (46 messages) flat view 46 messages, 5 authors, 2016-06-15

Re: [PATCH v2 1/2] Introduce config variable "diff.defaultOptions"

From: Jeff King <hidden>
Date: 2016-06-15 22:46:26

On Tue, Mar 17, 2009 at 09:05:18AM -0700, Keith Cascio wrote:
In order to answer your questions as convincingly as possible, I wrote up a 
one-page PDF document, downloadable here:
                http://preview.tinyurl.com/c769dd
Thanks for following up on this. And I appreciate that it is probably
nicer to compose in whatever you used to make this PDF due to enhanced
typography and linked footnotes, but posting a link to a PDF has a few
downsides:

  - the text in the PDF does not become part of the list archive; if
    your tinyurl or your hosted file ever goes away, the content of
    your message is permanently lost

  - it makes it very difficult for readers to reply inline to your
    comments

So please just send text emails in the future.

This time, however, I converted your PDF to text so I could reply.

Your PDF said:
No matter which implementation, we all agree the semantics are "pretend
as if the options in [diff.defaultOptions] were prepended to the command
line" [1]. That requirement is subject neither to confusion nor doubt.
An implementation is acceptable only if it delivers exactly that
behavior to the user.
OK, good, I think we are in agreement there.
Git's diff options processing scheme is a mature[2], versatile
instrument relied upon by at least 47 client codepaths. The API it
provides for recording user intent to an instance of struct diff_options
entails the following steps[3]:

1) to prepare the structure for recording, call diff_setup() once

2) to record user intention, write to the structure[4], calling
diff_opt_parse() as needed
I think there is a step 1.5, where callers can record their own
"default" intentions -- things that this particular caller will default
to, but which users can override via command-line options. E.g., "git
log" tweaks several options in cmd_log_init, such as turning on
ALLOW_TEXTCONV and RECURSIVE.
3) to prepare the structure for "playback", call diff_setup_done() once

4) to test user intention, read from the structure
OK, makes sense.
Git's code is already equipped to react to every kind of user intention
during step 2.
More or less. I think in our past discussion, it came about that there
are some things the user cannot say, like undoing certain options. This
could be a problem if a caller defaults options to something un-doable.
For example, I don't think there is a way to turn off OPT_RECURSIVE in
git-log.  In practice, this hasn't been a problem.
An attractive (but hopelessly flawed) strategy is to pre-load the
structure with defaultOptions before step 2. Step 1, diff_setup(), is
the only place to do that, since the client starts overwriting as soon
as diff_setup() returns.
I don't agree that it is hopelessly flawed. It requires a new call at
each callsite to say "I have set up my defaults, now take the user
defaults from the config, before I proceed to step 2 and parse the
user intentions". Which sounds awful to add a new call at each site,
but I am not sure that is not necessary anyway.

I don't know that all callsites will _want_ to respect such a config
option, especially not plumbing. So any callsite is going to have to opt
into this functionality anyway.
Some aspects of user intention dictate whether or not to perform the
pre-load at all, most notably: which Git command the user invoked (also
switches). But at step 1, inside diff_setup(), we don't know the user's
full intention yet, so we can't decide whether or not to perform the
pre-load.
I think I suggested last time that the idea of whether or not to perform
the pre-load doesn't _have_ to come from the same set of user intention.
That is, in the call

  git [git-options] diff [diff-options]

we actually parse [git-options] and [diff-options] at different times.
Pre-load intention can go into [git-options], which avoids this
dependency cycle.
This could tempt an undisciplined programmer to try to peek ahead at
part of the user's intention before the code sees it in its normal
course. That would be a disaster because it would undermine the
integrity, not to mention beauty, of Git's entire initialization scheme.
It would be a cheap hack; an inelegant, fragile, ugly, and utterly
half-baked attempt to bypass and circumvent the existing architecture.
Your poetry aside, I agree that way madness lies.
My proposal:

a) patiently accumulates user intention via Git's well-established
initialization scheme, never needing to peek ahead or misbehave in any
way, thus attaining harmony.

b) postpones the decision whether or not to load defaultOptions until
step 3, diff_setup_done(), after we've had every opportunity to examine
user intention, but loads them effectively underneath any explicit
command line options, thereby fulfilling the agreed upon semantic
obligation.

c) is inspired by a simple, powerful, easy-to-understand, and popular
metaphor: layer flattening.

Why, oh why do some people think there's an "easier" way[1]?!
Because I outlined it above?

Look, I am not opposed to layer flattening if that's what is required to
get it right. But consider the downside of layer flattening: we must
always record intent-to-change when making a change to the struct (i.e.,
the "mask" variable in your original patches). This is fine for members
hidden behind macros, but there are a lot of members that are assigned
to directly. We would need to:

  1. Introduce new infrastructure for assigning to these members.

  2. Fix existing locations by converting them to this infrastructure.

  3. Introduce some mechanism to help future callers get it right (since
     otherwise assigning directly is a subtle bug).

This is elementary encapsulation; in a language with better OO support,
you would hide all of your struct members behind accessors. But this is
C, and a dialect of C where that doesn't usually happen. So I think it
is going to introduce a lot of code changes, and the resulting code will
not look as much like the rest of git as it once did.

So what I am suggesting is that _if_ there is an easier way to do it,
then it is worth exploring.

-Peff
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help