Thread (26 messages) 26 messages, 4 authors, 2022-10-18

Re: [PATCH v2 00/10] run-command API: add run_command_{l,sv}_opt()

From: Jeff King <hidden>
Date: 2022-10-18 20:43:25

On Tue, Oct 18, 2022 at 03:28:43PM +0200, Ævar Arnfjörð Bjarmason wrote:
quoted
Hmph...  I somehow thought that the concensus is rather try the
complete opposite approach shown by René's patch to lose the use of
run_command_v_opt() by replacing it with run_command(&args), with
args.v populated using strvec_pushl() and other strvec API
functions.

One of the reasons I would prefer to see us moving in that direction
was because the first iteration of this series was a good
demonstration of the relatively limited usefulness of _l_opt()
variant and also it seemed to be error prone to use it.
I'm getting somewhat mixed messages. Jeff seemed to like the end-to-end
safety of run_command_l_opt() before I wrote it. I think the
run_command_l_opt() still really shines for the simple cases.
Sorry if I gave that impression. I like the safety of strvec_pushl(),
but I think using it with a "struct child_process" is just fine. It's
more flexible, and as René's patch showed, doesn't really make the
callers more complex (that definitely _wasn't_ the case long ago, when
most of these callers were written).

I do think Junio saying "consensus" may have been premature. I expressed
my opinion and he agreed, but I think that is as far as it got. :)
I don't see how *_l_opt() is particularly error prone, I just had a
stupid think-o in v1 of this, but that if/else if bug is something that
could have snuck in with run_command() given the same stupidity :)
I don't think it's error-prone. It just seems like it complicates an API
for little gain, and causes us to have a lot of boilerplate mapping
RUN_* flags into cmd.* fields.
I wonder if a run_command() that just had the prototype (struct
child_process *cmd, ...) might not be the best of both worlds (or a
run_commandl() alternative). I.e. to do away with the whole custom way
of specifying the flag(s), and just take the passed-in arguments and
append them to "&cmd.args".
That would work, but is it buying much? You still have to declare the
child_process at that point, which means you have:

  struct child_process cmd = CHILD_PROCESS_INIT;
  cmd.git_cmd = 1;
  run_command(&cmd, "log", "--", "HEAD", NULL);

instead of:

  struct child_process cmd = CHILD_PROCESS_INIT;
  cmd.git_cmd = 1;
  strvec_pushl(&cmd.args, "log", "--", "HEAD", NULL);
  run_command(&cmd);

Saving one line doesn't seem worth complicating the API to me. Plus
existing users have to say "run_command(&cmd, NULL)", or we need to
ignore varargs when "cmd.args.nr > 0" (which papers over errors).

And most of the time run_command() is inside an if() anyway. Just
style-wise, keeping the long command name on its own line is a
readability improvement (IMHO, anyway).
It's also interesting to consider adding a --noop option to be supported
by parse-options.c. The reason we can't use a run_command_l_opt() in
some cases is because we're doing e.g.:

	if (progress)
		strvec_push(&args, "--progress");

We have a --no-progress, but in those cases the recipient at the end
often cares about a default of -1 for a bool variable, or similar. So if
we could do:

	run_command_l_opt(0, command,
		(progress ? "--progress" : "--noop"),
		...,
		NULL
	);
I dunno. That does not seem more readable to me, and would end up with
GIT_TRACE lines like:

  git foo --noop --noop --real-option --noop
We could benefit from compile-time checks, and wouldn't have to allocate
a strvec just for building up the arguments in some cases. Just food for
thought...
Keep in mind we allocate a strvec either way. And IMHO seeing:

  if (foo)
          strvec_push(&cmd.args, "foo");

is the most readable form. Not to mention that it is more flexible. Many
cases use "pushf" for the same thing.

-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