threads / patch / 56487

patch, 2 partsparse-options.c: remove OPT_ARGUMENT

Subject: [PATCH 0/2] parse-options.c: remove OPT_ARGUMENT

## tl;dr

17 messages between Sep 11, 2021 and Sep 13, 2021. Diffs are folded; open one to read it.

replies: 16people: 3as markdown or json

Ævar Arnfjörð Bjarmason· Sep 11, 2021, 18:21 UTC · lore

We've only ever used parse-option.c's OPT_ARGUMENT() in one place, as it turns out we can use an OPT_BOOL there instead. and get a net reduction in code & complexity.

Ævar Arnfjörð Bjarmason (2):
  difftool: use "struct strvec" API in run_{dir,file}_diff()
  parse-options API: remove OPTION_ARGUMENT feature
 Documentation/technical/api-parse-options.txt |  5 --
 builtin/difftool.c                            | 50 +++++++++++--------
 parse-options.c                               | 13 -----
 parse-options.h                               |  3 --
 t/helper/test-parse-options.c                 |  1 -
 t/t0040-parse-options.sh                      |  5 --
 6 files changed, 28 insertions(+), 49 deletions(-)
-- 
2.33.0.995.ga5ea46173a2
Ævar Arnfjörð Bjarmason· Sep 11, 2021, 18:21 UTC · re: Ævar Arnfjörð Bjarmason · lore

[PATCH 1/2] difftool: use "struct strvec" API in run_{dir,file}_diff()

The underlying run_command() API can take either the "struct strvec args", or a "const char **argv". Let's move to the former to use the more "native" version of run_command() in both of these functions.

This change probably isn't worth in on its own, but sets us up to simplify API use even more in a subsequent commit.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/difftool.c | 46 +++++++++++++++++++++++++---------------------
 1 file changed, 25 insertions(+), 21 deletions(-)
Show changes to builtin/difftool.c +25 −21
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 6a9242a8032..e656514bcac 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -331,7 +331,7 @@ static int checkout_path(unsigned mode, struct object_id *oid,
 }
 
 static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
-			int argc, const char **argv)
+			struct strvec *args)
 {
 	char tmpdir[PATH_MAX];
 	struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT;
@@ -393,10 +393,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
 	child.clean_on_exit = 1;
 	child.dir = prefix;
 	child.out = -1;
-	strvec_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z",
-		     NULL);
-	for (i = 0; i < argc; i++)
-		strvec_push(&child.args, argv[i]);
+	child.argv = args->v;
+
 	if (start_command(&child))
 		die("could not obtain raw diff");
 	fp = xfdopen(child.out, "r");
@@ -663,30 +661,30 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
 	strbuf_release(&rdir);
 	strbuf_release(&wtdir);
 	strbuf_release(&buf);
+	strvec_clear(args);
 
 	return ret;
 }
 
-static int run_file_diff(int prompt, const char *prefix,
-			 int argc, const char **argv)
+static int run_file_diff(int prompt, const char *prefix, struct strvec *args)
 {
-	struct strvec args = STRVEC_INIT;
-	const char *env[] = {
-		"GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL,
-		NULL
-	};
-	int i;
+	struct child_process cmd = CHILD_PROCESS_INIT;
+	int ret;
 
+	strvec_pushl(&cmd.env_array, "GIT_PAGER=",
+		     "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL);
 	if (prompt > 0)
-		env[2] = "GIT_DIFFTOOL_PROMPT=true";
+		strvec_push(&cmd.env_array, "GIT_DIFFTOOL_PROMPT=true");
 	else if (!prompt)
-		env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";
+		strvec_push(&cmd.env_array, "GIT_DIFFTOOL_NO_PROMPT=true");
 
+	cmd.git_cmd = 1;
+	cmd.dir = prefix;
+	cmd.argv = args->v;
 
-	strvec_push(&args, "diff");
-	for (i = 0; i < argc; i++)
-		strvec_push(&args, argv[i]);
-	return run_command_v_opt_cd_env(args.v, RUN_GIT_CMD, prefix, env);
+	ret = run_command(&cmd);
+	strvec_clear(args);
+	return ret;
 }
 
 int cmd_difftool(int argc, const char **argv, const char *prefix)
@@ -719,6 +717,7 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
 		OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")),
 		OPT_END()
 	};
+	struct strvec args = STRVEC_INIT;
 
 	git_config(difftool_config, NULL);
 	symlinks = has_symlinks;
@@ -768,7 +767,12 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
 	 * will invoke a separate instance of 'git-difftool--helper' for
 	 * each file that changed.
 	 */
+	strvec_push(&args, "diff");
+	if (dir_diff)
+		strvec_pushl(&args, "--raw", "--no-abbrev", "-z", NULL);
+	strvec_pushv(&args, argv);
+
 	if (dir_diff)
-		return run_dir_diff(extcmd, symlinks, prefix, argc, argv);
-	return run_file_diff(prompt, prefix, argc, argv);
+		return run_dir_diff(extcmd, symlinks, prefix, &args);
+	return run_file_diff(prompt, prefix, &args);
 }
-- 
2.33.0.995.ga5ea46173a2
Jeff King· Sep 12, 2021, 22:39 UTC · re: Ævar Arnfjörð Bjarmason · lore

Re: [PATCH 1/2] difftool: use "struct strvec" API in run_{dir,file}_diff()

On Sat, Sep 11, 2021 at 08:21:11PM +0200, Ævar Arnfjörð Bjarmason wrote:
> The underlying run_command() API can take either the "struct strvec
> args", or a "const char **argv". Let's move to the former to use the
> more "native" version of run_command() in both of these functions.

It sounds like we're moving to use child.args (the strvec interface) instead of child.argv (the const char one). Which I support; I'd like to eventually get rid of the argv interface entirely because it has memory-ownership semantics that are easy to get wrong.

But this...
Show 10 quoted lines
> @@ -393,10 +393,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
>  	child.clean_on_exit = 1;
>  	child.dir = prefix;
>  	child.out = -1;
> -	strvec_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z",
> -		     NULL);
> -	for (i = 0; i < argc; i++)
> -		strvec_push(&child.args, argv[i]);
> +	child.argv = args->v;
> +
...is going in the opposite direction.
I'd much rather see us continue to use child.args here, like:
  strvec_pushv(&child.args, args->v);

Though really I do think passing the strvec into run_dir_diff() is questionable in the first place. The caller depends on us to free the memory in the strvec for them, which is...subtle.

It does let you immediately return here:
Show 5 quoted lines
>  	if (dir_diff)
> -		return run_dir_diff(extcmd, symlinks, prefix, argc, argv);
> -	return run_file_diff(prompt, prefix, argc, argv);
> +		return run_dir_diff(extcmd, symlinks, prefix, &args);
> +	return run_file_diff(prompt, prefix, &args);
without doing any cleanup. But I think just:
  if (dir_diff)
	ret = run_dir_diff(...);
  else
	ret = run_file_diff(...);
  strvec_clear(&args);
  return ret;
would be a lot more obvious.

I almost suggested that this could be done even simpler by having the caller pass in one of two pre-made argv lists (in addition to what's in the original argv). But the _actual_ benefit you want here is making it easy to construct those lists in the shared code of the caller. Your commit message didn't at all make that clear (so to me it looked like your "cleanup" was actively making things worse).

It only becomes apparent with the second patch. I would have found it much easier to understand with something like the patch below. And then a further patch to use strvec_pushv instead of manually looping (even getting rid of the argc parameters entirely!), and one to convert run_file_diff() to use a struct child_process (which fixes its memory leak).

-- >8 -- difftool: prepare "diff" cmdline in cmd_difftool()

We call into either run_dir_diff() or run_file_diff(), each of which sets up a child argv starting with "diff" and some hard-coded options (depending on which mode we're using). Let's extract that logic into the caller, which will make it easier to modify the options for cases which affect both functions.

Signed-off-by: Jeff King <peff@peff.net>
---
 builtin/difftool.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)
Show changes to builtin/difftool.c +8 −5
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 6a9242a803..91a8e51b0c 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -393,8 +393,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
 	child.clean_on_exit = 1;
 	child.dir = prefix;
 	child.out = -1;
-	strvec_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z",
-		     NULL);
 	for (i = 0; i < argc; i++)
 		strvec_push(&child.args, argv[i]);
 	if (start_command(&child))
@@ -683,7 +681,6 @@ static int run_file_diff(int prompt, const char *prefix,
 		env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";
 
 
-	strvec_push(&args, "diff");
 	for (i = 0; i < argc; i++)
 		strvec_push(&args, argv[i]);
 	return run_command_v_opt_cd_env(args.v, RUN_GIT_CMD, prefix, env);
@@ -719,6 +716,7 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
 		OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")),
 		OPT_END()
 	};
+	struct strvec args = STRVEC_INIT;
 
 	git_config(difftool_config, NULL);
 	symlinks = has_symlinks;
@@ -768,7 +766,12 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
 	 * will invoke a separate instance of 'git-difftool--helper' for
 	 * each file that changed.
 	 */
+	strvec_push(&args, "diff");
+	if (dir_diff)
+		strvec_pushl(&args, "--raw", "--no-abbrev", "-z", NULL);
+	strvec_pushv(&args, argv);
+
 	if (dir_diff)
-		return run_dir_diff(extcmd, symlinks, prefix, argc, argv);
-	return run_file_diff(prompt, prefix, argc, argv);
+		return run_dir_diff(extcmd, symlinks, prefix, args.nr, args.v);
+	return run_file_diff(prompt, prefix, args.nr, args.v);
 }
-- 
2.33.0.811.g40f7f3a89c
Jeff King· Sep 12, 2021, 22:41 UTC · re: Jeff King · lore

Re: [PATCH 1/2] difftool: use "struct strvec" API in run_{dir,file}_diff()

On Sun, Sep 12, 2021 at 06:39:26PM -0400, Jeff King wrote:
Show 9 quoted lines
> It only becomes apparent with the second patch. I would have found it
> much easier to understand with something like the patch below. And then
> a further patch to use strvec_pushv instead of manually looping (even
> getting rid of the argc parameters entirely!), and one to convert
> run_file_diff() to use a struct child_process (which fixes its memory
> leak).
> 
> -- >8 --
> difftool: prepare "diff" cmdline in cmd_difftool()

Note that this actually introduces a new leak of the strvec in the caller. So it would probably want the "ret =" think I suggested to be squashed in.

(I wasn't really planning to make a finished patch, but just trying to illustrate what had confused me in your original. I ended up closer than I had planned, though).

-Peff
Junio C Hamano· Sep 13, 2021, 00:10 UTC · re: Jeff King · lore

Re: [PATCH 1/2] difftool: use "struct strvec" API in run_{dir,file}_diff()

Jeff King <peff@peff.net> writes:
Show 45 quoted lines
> On Sat, Sep 11, 2021 at 08:21:11PM +0200, Ævar Arnfjörð Bjarmason wrote:
>
>> The underlying run_command() API can take either the "struct strvec
>> args", or a "const char **argv". Let's move to the former to use the
>> more "native" version of run_command() in both of these functions.
>
> It sounds like we're moving to use child.args (the strvec interface)
> instead of child.argv (the const char one). Which I support; I'd like to
> eventually get rid of the argv interface entirely because it has
> memory-ownership semantics that are easy to get wrong.
>
> But this...
>
>> @@ -393,10 +393,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
>>  	child.clean_on_exit = 1;
>>  	child.dir = prefix;
>>  	child.out = -1;
>> -	strvec_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z",
>> -		     NULL);
>> -	for (i = 0; i < argc; i++)
>> -		strvec_push(&child.args, argv[i]);
>> +	child.argv = args->v;
>> +
>
> ...is going in the opposite direction.
>
> I'd much rather see us continue to use child.args here, like:
>
>   strvec_pushv(&child.args, args->v);
>
> Though really I do think passing the strvec into run_dir_diff() is
> questionable in the first place. The caller depends on us to free the
> memory in the strvec for them, which is...subtle.
> ...
> +	strvec_push(&args, "diff");
> +	if (dir_diff)
> +		strvec_pushl(&args, "--raw", "--no-abbrev", "-z", NULL);
> +	strvec_pushv(&args, argv);
> +
>  	if (dir_diff)
> -		return run_dir_diff(extcmd, symlinks, prefix, argc, argv);
> -	return run_file_diff(prompt, prefix, argc, argv);
> +		return run_dir_diff(extcmd, symlinks, prefix, args.nr, args.v);
> +	return run_file_diff(prompt, prefix, args.nr, args.v);
>  }

Yes, I have to say that the end result of not having to rely on the strvec type, in order to just call a main()- like function, makes it much more pleasant read.

Ævar Arnfjörð Bjarmason· Sep 11, 2021, 18:21 UTC · re: Ævar Arnfjörð Bjarmason · lore

[PATCH 2/2] parse-options API: remove OPTION_ARGUMENT feature

As was noted in 1a85b49b87a (parse-options: make OPT_ARGUMENT() more useful, 2019-03-14) there's only ever been one user of the OPT_ARGUMENT(), that user was added in 20de316e334 (difftool: allow running outside Git worktrees with --no-index, 2019-03-14).

The OPT_ARGUMENT() feature itself was added way back in 580d5bffdea (parse-options: new option type to treat an option-like parameter as an argument., 2008-03-02), but as discussed in 1a85b49b87a wasn't used until 20de316e334 in 2019.

Now that the preceding commit has migrated this code over to using "struct strvec" to manage the "args" member of a "struct child_process", we can just use that directly instead of relying on OPT_ARGUMENT.

This has a minor change in behavior in that if we'll pass --no-index we'll now always pass it as the first argument, before we'd pass it in whatever position the caller did. Preserving this was the real value of OPT_ARGUMENT(), but as it turns out we didn't need that either. We can always inject it as the first argument, the other end will parse it just the same.

Note that we cannot remove the "out" and "cpidx" members of "struct parse_opt_ctx_t" added in 580d5bffdea, while they were introduced with OPT_ARGUMENT() we since used them for other things.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Documentation/technical/api-parse-options.txt |  5 -----
 builtin/difftool.c                            |  4 +++-
 parse-options.c                               | 13 -------------
 parse-options.h                               |  3 ---
 t/helper/test-parse-options.c                 |  1 -
 t/t0040-parse-options.sh                      |  5 -----
 6 files changed, 3 insertions(+), 28 deletions(-)
Show changes to 6 files +3 −28

Documentation/technical/api-parse-options.txt, builtin/difftool.c, parse-options.c, parse-options.h, t/helper/test-parse-options.c, t/t0040-parse-options.sh

diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 5a60bbfa7f4..acfd5dc1d8b 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -198,11 +198,6 @@ There are some macros to easily define options:
 	The filename will be prefixed by passing the filename along with
 	the prefix argument of `parse_options()` to `prefix_filename()`.
 
-`OPT_ARGUMENT(long, &int_var, description)`::
-	Introduce a long-option argument that will be kept in `argv[]`.
-	If this option was seen, `int_var` will be set to one (except
-	if a `NULL` pointer was passed).
-
 `OPT_NUMBER_CALLBACK(&var, description, func_ptr)`::
 	Recognize numerical options like -123 and feed the integer as
 	if it was an argument to the function given by `func_ptr`.
diff --git a/builtin/difftool.c b/builtin/difftool.c
index e656514bcac..9aa77a91043 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -714,7 +714,7 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
 			    "tool returns a non - zero exit code")),
 		OPT_STRING('x', "extcmd", &extcmd, N_("command"),
 			   N_("specify a custom command for viewing diffs")),
-		OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")),
+		OPT_BOOL(0, "no-index", &no_index, N_("passed to `diff`")),
 		OPT_END()
 	};
 	struct strvec args = STRVEC_INIT;
@@ -768,6 +768,8 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
 	 * each file that changed.
 	 */
 	strvec_push(&args, "diff");
+	if (no_index)
+		strvec_push(&args, "--no-index");
 	if (dir_diff)
 		strvec_pushl(&args, "--raw", "--no-abbrev", "-z", NULL);
 	strvec_pushv(&args, argv);
diff --git a/parse-options.c b/parse-options.c
index 2abff136a17..55c5821b08d 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -310,19 +310,6 @@ static enum parse_opt_result parse_long_opt(
 again:
 		if (!skip_prefix(arg, long_name, &rest))
 			rest = NULL;
-		if (options->type == OPTION_ARGUMENT) {
-			if (!rest)
-				continue;
-			if (*rest == '=')
-				return error(_("%s takes no value"),
-					     optname(options, flags));
-			if (*rest)
-				continue;
-			if (options->value)
-				*(int *)options->value = options->defval;
-			p->out[p->cpidx++] = arg - 2;
-			return PARSE_OPT_DONE;
-		}
 		if (!rest) {
 			/* abbreviated? */
 			if (!(p->flags & PARSE_OPT_KEEP_UNKNOWN) &&
diff --git a/parse-options.h b/parse-options.h
index a845a9d9527..39d90882548 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -8,7 +8,6 @@
 enum parse_opt_type {
 	/* special types */
 	OPTION_END,
-	OPTION_ARGUMENT,
 	OPTION_GROUP,
 	OPTION_NUMBER,
 	OPTION_ALIAS,
@@ -155,8 +154,6 @@ struct option {
 #define OPT_INTEGER_F(s, l, v, h, f)     { OPTION_INTEGER, (s), (l), (v), N_("n"), (h), (f) }
 
 #define OPT_END()                   { OPTION_END }
-#define OPT_ARGUMENT(l, v, h)       { OPTION_ARGUMENT, 0, (l), (v), NULL, \
-				      (h), PARSE_OPT_NOARG, NULL, 1 }
 #define OPT_GROUP(h)                { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
 #define OPT_BIT(s, l, v, h, b)      OPT_BIT_F(s, l, v, h, b, 0)
 #define OPT_BITOP(s, l, v, h, set, clear) { OPTION_BITOP, (s), (l), (v), NULL, (h), \
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 2051ce57db7..a282b6ff13e 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -134,7 +134,6 @@ int cmd__parse_options(int argc, const char **argv)
 		OPT_NOOP_NOARG(0, "obsolete"),
 		OPT_STRING_LIST(0, "list", &list, "str", "add str to list"),
 		OPT_GROUP("Magic arguments"),
-		OPT_ARGUMENT("quux", NULL, "means --quux"),
 		OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
 			number_callback),
 		{ OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b",
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index ad4746d899a..da310ed29b1 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -37,7 +37,6 @@ String options
     --list <str>          add str to list
 
 Magic arguments
-    --quux                means --quux
     -NUM                  set integer to NUM
     +                     same as -b
     --ambiguous           positive ambiguity
@@ -263,10 +262,6 @@ test_expect_success 'detect possible typos' '
 	test_cmp typo.err output.err
 '
 
-test_expect_success 'keep some options as arguments' '
-	test-tool parse-options --expect="arg 00: --quux" --quux
-'
-
 cat >expect <<\EOF
 Callback: "four", 0
 boolean: 5
-- 
2.33.0.995.ga5ea46173a2
Jeff King· Sep 12, 2021, 22:43 UTC · re: Ævar Arnfjörð Bjarmason · lore

Re: [PATCH 2/2] parse-options API: remove OPTION_ARGUMENT feature

On Sat, Sep 11, 2021 at 08:21:12PM +0200, Ævar Arnfjörð Bjarmason wrote:
Show 14 quoted lines
> As was noted in 1a85b49b87a (parse-options: make OPT_ARGUMENT() more
> useful, 2019-03-14) there's only ever been one user of the
> OPT_ARGUMENT(), that user was added in 20de316e334 (difftool: allow
> running outside Git worktrees with --no-index, 2019-03-14).
> 
> The OPT_ARGUMENT() feature itself was added way back in
> 580d5bffdea (parse-options: new option type to treat an option-like
> parameter as an argument., 2008-03-02), but as discussed in
> 1a85b49b87a wasn't used until 20de316e334 in 2019.
> 
> Now that the preceding commit has migrated this code over to using
> "struct strvec" to manage the "args" member of a "struct
> child_process", we can just use that directly instead of relying on
> OPT_ARGUMENT.

Yeah, the change in difftool here looks fine (regardless of how the cleanup in the first patch gets there). And I'm happy to see this somewhat weird OPT_ macro go away if nobody is using it.

-Peff
Ævar Arnfjörð Bjarmason· Sep 13, 2021, 03:35 UTC · re: Ævar Arnfjörð Bjarmason · lore

[PATCH v2 0/4] difftool refactoring + remove OPT_ARGUMENT() macro

We've only ever used parse-option.c's OPT_ARGUMENT() in one place, as it turns out we can use an OPT_BOOL there instead. and get a net reduction in code & complexity.

I think this v2 should address the comments Jeff King had in https://lore.kernel.org/git/YT6BnnXeAWn8BycF@coredump.intra.peff.net/; there's now an amended version of his proposed patch as part of this, but I prepended it with another one to prepare the "struct child_process" in cmd_difftool().

Doing so nicely gets around the question of the strvec memory management, since we can trust the run-command.c API to do that for us, but couldn't in my v1 when we'd copy our own "struct strvec *" to its "args".

Jeff King (1):
  difftool: prepare "diff" cmdline in cmd_difftool()
Ævar Arnfjörð Bjarmason (3):
  difftool: prepare "struct child_process" in cmd_difftool()
  difftool: use run_command() API in run_file_diff()
  parse-options API: remove OPTION_ARGUMENT feature
 Documentation/technical/api-parse-options.txt |  5 --
 builtin/difftool.c                            | 51 ++++++++++---------
 parse-options.c                               | 13 -----
 parse-options.h                               |  3 --
 t/helper/test-parse-options.c                 |  1 -
 t/t0040-parse-options.sh                      |  5 --
 6 files changed, 26 insertions(+), 52 deletions(-)
Range-diff against v1:
1:  e7481eb0c0c ! 1:  f57c6c9b069 difftool: use "struct strvec" API in run_{dir,file}_diff()
    @@ Metadata
     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## Commit message ##
    -    difftool: use "struct strvec" API in run_{dir,file}_diff()
    +    difftool: prepare "struct child_process" in cmd_difftool()
     
    -    The underlying run_command() API can take either the "struct strvec
    -    args", or a "const char **argv". Let's move to the former to use the
    -    more "native" version of run_command() in both of these functions.
    -
    -    This change probably isn't worth in on its own, but sets us up to
    -    simplify API use even more in a subsequent commit.
    +    Move the preparation of the "struct child_process" from run_dir_diff()
    +    to its only caller, cmd_difftool(). This is in preparation for
    +    migrating run_file_diff() to using the run_command() API directly, and
    +    to move more of the shared setup of the two to cmd_difftool().
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
    @@ builtin/difftool.c: static int checkout_path(unsigned mode, struct object_id *oi
      
      static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
     -			int argc, const char **argv)
    -+			struct strvec *args)
    ++			int argc, const char **argv,
    ++			struct child_process *child)
      {
      	char tmpdir[PATH_MAX];
      	struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT;
     @@ builtin/difftool.c: static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
    - 	child.clean_on_exit = 1;
    - 	child.dir = prefix;
    - 	child.out = -1;
    + 	struct index_state wtindex;
    + 	struct checkout lstate, rstate;
    + 	int rc, flags = RUN_GIT_CMD, err = 0;
    +-	struct child_process child = CHILD_PROCESS_INIT;
    + 	const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL };
    + 	struct hashmap wt_modified, tmp_modified;
    + 	int indices_loaded = 0;
    +@@ builtin/difftool.c: static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
    + 	rdir_len = rdir.len;
    + 	wtdir_len = wtdir.len;
    + 
    +-	child.no_stdin = 1;
    +-	child.git_cmd = 1;
    +-	child.use_shell = 0;
    +-	child.clean_on_exit = 1;
    +-	child.dir = prefix;
    +-	child.out = -1;
     -	strvec_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z",
    --		     NULL);
    --	for (i = 0; i < argc; i++)
    ++	child->no_stdin = 1;
    ++	child->git_cmd = 1;
    ++	child->use_shell = 0;
    ++	child->clean_on_exit = 1;
    ++	child->dir = prefix;
    ++	child->out = -1;
    ++	strvec_pushl(&child->args, "diff", "--raw", "--no-abbrev", "-z",
    + 		     NULL);
    + 	for (i = 0; i < argc; i++)
     -		strvec_push(&child.args, argv[i]);
    -+	child.argv = args->v;
    -+
    - 	if (start_command(&child))
    +-	if (start_command(&child))
    ++		strvec_push(&child->args, argv[i]);
    ++	if (start_command(child))
      		die("could not obtain raw diff");
    - 	fp = xfdopen(child.out, "r");
    -@@ builtin/difftool.c: static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
    - 	strbuf_release(&rdir);
    - 	strbuf_release(&wtdir);
    - 	strbuf_release(&buf);
    -+	strvec_clear(args);
    - 
    - 	return ret;
    - }
    +-	fp = xfdopen(child.out, "r");
    ++	fp = xfdopen(child->out, "r");
      
    --static int run_file_diff(int prompt, const char *prefix,
    --			 int argc, const char **argv)
    -+static int run_file_diff(int prompt, const char *prefix, struct strvec *args)
    - {
    --	struct strvec args = STRVEC_INIT;
    --	const char *env[] = {
    --		"GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL,
    --		NULL
    --	};
    --	int i;
    -+	struct child_process cmd = CHILD_PROCESS_INIT;
    -+	int ret;
    - 
    -+	strvec_pushl(&cmd.env_array, "GIT_PAGER=",
    -+		     "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL);
    - 	if (prompt > 0)
    --		env[2] = "GIT_DIFFTOOL_PROMPT=true";
    -+		strvec_push(&cmd.env_array, "GIT_DIFFTOOL_PROMPT=true");
    - 	else if (!prompt)
    --		env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";
    -+		strvec_push(&cmd.env_array, "GIT_DIFFTOOL_NO_PROMPT=true");
    - 
    -+	cmd.git_cmd = 1;
    -+	cmd.dir = prefix;
    -+	cmd.argv = args->v;
    - 
    --	strvec_push(&args, "diff");
    --	for (i = 0; i < argc; i++)
    --		strvec_push(&args, argv[i]);
    --	return run_command_v_opt_cd_env(args.v, RUN_GIT_CMD, prefix, env);
    -+	ret = run_command(&cmd);
    -+	strvec_clear(args);
    -+	return ret;
    - }
    + 	/* Build index info for left and right sides of the diff */
    + 	i = 0;
    +@@ builtin/difftool.c: static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
      
    - int cmd_difftool(int argc, const char **argv, const char *prefix)
    + 	fclose(fp);
    + 	fp = NULL;
    +-	if (finish_command(&child)) {
    ++	if (finish_command(child)) {
    + 		ret = error("error occurred running diff --raw");
    + 		goto finish;
    + 	}
     @@ builtin/difftool.c: int cmd_difftool(int argc, const char **argv, const char *prefix)
      		OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")),
      		OPT_END()
      	};
    -+	struct strvec args = STRVEC_INIT;
    ++	struct child_process child = CHILD_PROCESS_INIT;
      
      	git_config(difftool_config, NULL);
      	symlinks = has_symlinks;
     @@ builtin/difftool.c: int cmd_difftool(int argc, const char **argv, const char *prefix)
    - 	 * will invoke a separate instance of 'git-difftool--helper' for
      	 * each file that changed.
      	 */
    -+	strvec_push(&args, "diff");
    -+	if (dir_diff)
    -+		strvec_pushl(&args, "--raw", "--no-abbrev", "-z", NULL);
    -+	strvec_pushv(&args, argv);
    -+
      	if (dir_diff)
     -		return run_dir_diff(extcmd, symlinks, prefix, argc, argv);
    --	return run_file_diff(prompt, prefix, argc, argv);
    -+		return run_dir_diff(extcmd, symlinks, prefix, &args);
    -+	return run_file_diff(prompt, prefix, &args);
    ++		return run_dir_diff(extcmd, symlinks, prefix, argc, argv, &child);
    + 	return run_file_diff(prompt, prefix, argc, argv);
      }
-:  ----------- > 2:  1c2794115c7 difftool: prepare "diff" cmdline in cmd_difftool()
-:  ----------- > 3:  2b093bd71fc difftool: use run_command() API in run_file_diff()
2:  28b43789b11 ! 4:  4fddce0a38d parse-options API: remove OPTION_ARGUMENT feature
    @@ builtin/difftool.c: int cmd_difftool(int argc, const char **argv, const char *pr
     +		OPT_BOOL(0, "no-index", &no_index, N_("passed to `diff`")),
      		OPT_END()
      	};
    - 	struct strvec args = STRVEC_INIT;
    + 	struct child_process child = CHILD_PROCESS_INIT;
     @@ builtin/difftool.c: int cmd_difftool(int argc, const char **argv, const char *prefix)
      	 * each file that changed.
      	 */
    - 	strvec_push(&args, "diff");
    + 	strvec_push(&child.args, "diff");
     +	if (no_index)
    -+		strvec_push(&args, "--no-index");
    ++		strvec_push(&child.args, "--no-index");
      	if (dir_diff)
    - 		strvec_pushl(&args, "--raw", "--no-abbrev", "-z", NULL);
    - 	strvec_pushv(&args, argv);
    + 		strvec_pushl(&child.args, "--raw", "--no-abbrev", "-z", NULL);
    + 	strvec_pushv(&child.args, argv);
     
      ## parse-options.c ##
     @@ parse-options.c: static enum parse_opt_result parse_long_opt(
-- 
2.33.0.999.ga5f89b684e9
Ævar Arnfjörð Bjarmason· Sep 13, 2021, 03:35 UTC · re: Ævar Arnfjörð Bjarmason · lore

[PATCH v2 2/4] difftool: prepare "diff" cmdline in cmd_difftool()

From: Jeff King <peff@peff.net>

We call into either run_dir_diff() or run_file_diff(), each of which sets up a child argv starting with "diff" and some hard-coded options (depending on which mode we're using). Let's extract that logic into the caller, which will make it easier to modify the options for cases which affect both functions.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/difftool.c | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)
Show changes to builtin/difftool.c +7 −8
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 9f08a8f3fd2..f8fcc67640f 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -331,7 +331,6 @@ static int checkout_path(unsigned mode, struct object_id *oid,
 }
 
 static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
-			int argc, const char **argv,
 			struct child_process *child)
 {
 	char tmpdir[PATH_MAX];
@@ -393,10 +392,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
 	child->clean_on_exit = 1;
 	child->dir = prefix;
 	child->out = -1;
-	strvec_pushl(&child->args, "diff", "--raw", "--no-abbrev", "-z",
-		     NULL);
-	for (i = 0; i < argc; i++)
-		strvec_push(&child->args, argv[i]);
 	if (start_command(child))
 		die("could not obtain raw diff");
 	fp = xfdopen(child->out, "r");
@@ -683,7 +678,6 @@ static int run_file_diff(int prompt, const char *prefix,
 		env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";
 
 
-	strvec_push(&args, "diff");
 	for (i = 0; i < argc; i++)
 		strvec_push(&args, argv[i]);
 	return run_command_v_opt_cd_env(args.v, RUN_GIT_CMD, prefix, env);
@@ -769,7 +763,12 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
 	 * will invoke a separate instance of 'git-difftool--helper' for
 	 * each file that changed.
 	 */
+	strvec_push(&child.args, "diff");
+	if (dir_diff)
+		strvec_pushl(&child.args, "--raw", "--no-abbrev", "-z", NULL);
+	strvec_pushv(&child.args, argv);
+
 	if (dir_diff)
-		return run_dir_diff(extcmd, symlinks, prefix, argc, argv, &child);
-	return run_file_diff(prompt, prefix, argc, argv);
+		return run_dir_diff(extcmd, symlinks, prefix, &child);
+	return run_file_diff(prompt, prefix, child.args.nr, child.args.v);
 }
-- 
2.33.0.999.ga5f89b684e9
Ævar Arnfjörð Bjarmason· Sep 13, 2021, 03:35 UTC · re: Ævar Arnfjörð Bjarmason · lore

[PATCH v2 1/4] difftool: prepare "struct child_process" in cmd_difftool()

Move the preparation of the "struct child_process" from run_dir_diff() to its only caller, cmd_difftool(). This is in preparation for migrating run_file_diff() to using the run_command() API directly, and to move more of the shared setup of the two to cmd_difftool().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/difftool.c | 29 +++++++++++++++--------------
 1 file changed, 15 insertions(+), 14 deletions(-)
Show changes to builtin/difftool.c +15 −14
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 6a9242a8032..9f08a8f3fd2 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -331,7 +331,8 @@ static int checkout_path(unsigned mode, struct object_id *oid,
 }
 
 static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
-			int argc, const char **argv)
+			int argc, const char **argv,
+			struct child_process *child)
 {
 	char tmpdir[PATH_MAX];
 	struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT;
@@ -352,7 +353,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
 	struct index_state wtindex;
 	struct checkout lstate, rstate;
 	int rc, flags = RUN_GIT_CMD, err = 0;
-	struct child_process child = CHILD_PROCESS_INIT;
 	const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL };
 	struct hashmap wt_modified, tmp_modified;
 	int indices_loaded = 0;
@@ -387,19 +387,19 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
 	rdir_len = rdir.len;
 	wtdir_len = wtdir.len;
 
-	child.no_stdin = 1;
-	child.git_cmd = 1;
-	child.use_shell = 0;
-	child.clean_on_exit = 1;
-	child.dir = prefix;
-	child.out = -1;
-	strvec_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z",
+	child->no_stdin = 1;
+	child->git_cmd = 1;
+	child->use_shell = 0;
+	child->clean_on_exit = 1;
+	child->dir = prefix;
+	child->out = -1;
+	strvec_pushl(&child->args, "diff", "--raw", "--no-abbrev", "-z",
 		     NULL);
 	for (i = 0; i < argc; i++)
-		strvec_push(&child.args, argv[i]);
-	if (start_command(&child))
+		strvec_push(&child->args, argv[i]);
+	if (start_command(child))
 		die("could not obtain raw diff");
-	fp = xfdopen(child.out, "r");
+	fp = xfdopen(child->out, "r");
 
 	/* Build index info for left and right sides of the diff */
 	i = 0;
@@ -525,7 +525,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
 
 	fclose(fp);
 	fp = NULL;
-	if (finish_command(&child)) {
+	if (finish_command(child)) {
 		ret = error("error occurred running diff --raw");
 		goto finish;
 	}
@@ -719,6 +719,7 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
 		OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")),
 		OPT_END()
 	};
+	struct child_process child = CHILD_PROCESS_INIT;
 
 	git_config(difftool_config, NULL);
 	symlinks = has_symlinks;
@@ -769,6 +770,6 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
 	 * each file that changed.
 	 */
 	if (dir_diff)
-		return run_dir_diff(extcmd, symlinks, prefix, argc, argv);
+		return run_dir_diff(extcmd, symlinks, prefix, argc, argv, &child);
 	return run_file_diff(prompt, prefix, argc, argv);
 }
-- 
2.33.0.999.ga5f89b684e9
Ævar Arnfjörð Bjarmason· Sep 13, 2021, 03:35 UTC · re: Ævar Arnfjörð Bjarmason · lore

[PATCH v2 3/4] difftool: use run_command() API in run_file_diff()

Change the run_file_diff() function to use the run_command() API directly, instead of invoking the run_command_v_opt_cd_env() wrapper.

This allows it, like run_dir_diff(), to use the "args" from "struct strvec", instead of the "const char **argv" passed into cmd_difftool(). This will be used in the subsequent commit to get rid of OPT_ARGUMENT() from cmd_difftool().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/difftool.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)
Show changes to builtin/difftool.c +6 −7
diff --git a/builtin/difftool.c b/builtin/difftool.c
index f8fcc67640f..de2e5545c81 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -663,24 +663,23 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
 }
 
 static int run_file_diff(int prompt, const char *prefix,
-			 int argc, const char **argv)
+			 struct child_process *child)
 {
-	struct strvec args = STRVEC_INIT;
 	const char *env[] = {
 		"GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL,
 		NULL
 	};
-	int i;
 
 	if (prompt > 0)
 		env[2] = "GIT_DIFFTOOL_PROMPT=true";
 	else if (!prompt)
 		env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";
 
+	child->git_cmd = 1;
+	child->dir = prefix;
+	strvec_pushv(&child->env_array, env);
 
-	for (i = 0; i < argc; i++)
-		strvec_push(&args, argv[i]);
-	return run_command_v_opt_cd_env(args.v, RUN_GIT_CMD, prefix, env);
+	return run_command(child);
 }
 
 int cmd_difftool(int argc, const char **argv, const char *prefix)
@@ -770,5 +769,5 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
 
 	if (dir_diff)
 		return run_dir_diff(extcmd, symlinks, prefix, &child);
-	return run_file_diff(prompt, prefix, child.args.nr, child.args.v);
+	return run_file_diff(prompt, prefix, &child);
 }
-- 
2.33.0.999.ga5f89b684e9
Jeff King· Sep 13, 2021, 18:04 UTC · re: Ævar Arnfjörð Bjarmason · lore

Re: [PATCH v2 3/4] difftool: use run_command() API in run_file_diff()

On Mon, Sep 13, 2021 at 05:35:39AM +0200, Ævar Arnfjörð Bjarmason wrote:
Show 7 quoted lines
> Change the run_file_diff() function to use the run_command() API
> directly, instead of invoking the run_command_v_opt_cd_env() wrapper.
> 
> This allows it, like run_dir_diff(), to use the "args" from "struct
> strvec", instead of the "const char **argv" passed into
> cmd_difftool(). This will be used in the subsequent commit to get rid
> of OPT_ARGUMENT() from cmd_difftool().
It also fixes the existing leak of its "args" strvec.

I like the general direction here of putting the child_process in the parent. There's a few opportunities for further cleanup it opens, but I'm happy whether you want to pursue them or not (I'm also happy to do them as real patches on top myself, but don't want to de-rail your series).

Show 15 quoted lines
>  static int run_file_diff(int prompt, const char *prefix,
> -			 int argc, const char **argv)
> +			 struct child_process *child)
>  {
> -	struct strvec args = STRVEC_INIT;
>  	const char *env[] = {
>  		"GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL,
>  		NULL
>  	};
> -	int i;
>  
>  	if (prompt > 0)
>  		env[2] = "GIT_DIFFTOOL_PROMPT=true";
>  	else if (!prompt)
>  		env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";

This "save a NULL in env where we might stick something in later" is ugly. Now that we have a child_process, it might be nicer as:

  strvec_push(&env.args, "GIT_PAGER=");
  strvec_push(&env.args, "GIT_EXTERNAL_DIFF=git-difftool--helper");
  if (prompt > 0)
	strvec_push(&env.args, "GIT_DIFFTOOL_PROMPT=true");
  else if (!prompt)
	strvec_push(&env.args, "GIT_DIFFTOOL_NO_PROMPT=true");
> +	child->git_cmd = 1;
> +	child->dir = prefix;

These are the same in run_dir_diff(). We could do more of the shared prep of the child_process in the caller. Likewise, we might want to pick up run_dir_diff()'s no_stdin and clean_on_exit settings (and unsetting use_shell, but I think that is already pointless; it defaults to false in the first place, and git_cmd takes precedence over it anyway).

-Peff
Ævar Arnfjörð Bjarmason· Sep 13, 2021, 19:13 UTC · re: Jeff King · lore

Re: [PATCH v2 3/4] difftool: use run_command() API in run_file_diff()

On Mon, Sep 13 2021, Jeff King wrote:
Show 17 quoted lines
> On Mon, Sep 13, 2021 at 05:35:39AM +0200, Ævar Arnfjörð Bjarmason wrote:
>
>> Change the run_file_diff() function to use the run_command() API
>> directly, instead of invoking the run_command_v_opt_cd_env() wrapper.
>> 
>> This allows it, like run_dir_diff(), to use the "args" from "struct
>> strvec", instead of the "const char **argv" passed into
>> cmd_difftool(). This will be used in the subsequent commit to get rid
>> of OPT_ARGUMENT() from cmd_difftool().
>
> It also fixes the existing leak of its "args" strvec.
>
> I like the general direction here of putting the child_process in the
> parent. There's a few opportunities for further cleanup it opens, but
> I'm happy whether you want to pursue them or not (I'm also happy to do
> them as real patches on top myself, but don't want to de-rail your
> series).
*nod*
Show 25 quoted lines
>>  static int run_file_diff(int prompt, const char *prefix,
>> -			 int argc, const char **argv)
>> +			 struct child_process *child)
>>  {
>> -	struct strvec args = STRVEC_INIT;
>>  	const char *env[] = {
>>  		"GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL,
>>  		NULL
>>  	};
>> -	int i;
>>  
>>  	if (prompt > 0)
>>  		env[2] = "GIT_DIFFTOOL_PROMPT=true";
>>  	else if (!prompt)
>>  		env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";
>
> This "save a NULL in env where we might stick something in later" is
> ugly. Now that we have a child_process, it might be nicer as:
>
>   strvec_push(&env.args, "GIT_PAGER=");
>   strvec_push(&env.args, "GIT_EXTERNAL_DIFF=git-difftool--helper");
>   if (prompt > 0)
> 	strvec_push(&env.args, "GIT_DIFFTOOL_PROMPT=true");
>   else if (!prompt)
> 	strvec_push(&env.args, "GIT_DIFFTOOL_NO_PROMPT=true");

Yes, I had something like that in the earlier round, but decided to try to keep the diff minimal, churn and all that. So it would be nice, but let's do it as some later cleanup if someone's interested.

Show 8 quoted lines
>> +	child->git_cmd = 1;
>> +	child->dir = prefix;
>
> These are the same in run_dir_diff(). We could do more of the shared
> prep of the child_process in the caller. Likewise, we might want to pick
> up run_dir_diff()'s no_stdin and clean_on_exit settings (and unsetting
> use_shell, but I think that is already pointless; it defaults to false
> in the first place, and git_cmd takes precedence over it anyway).
*nod*, but can be left for some other time.
Aside: I did most of that removal of "argv" from the child_process
struct you suggested, it's in
avar/run-command-use-less-argv-use-args-instead in my repo if you're
curious / wanted to pick any of that up. I won't be submitting it any
time soon for the reasons noted upthread.

In any case it has rather large semantic and textual conflicts with the outstanding hook.[ch] topic, but with that branch + that applied + a few changes on top I was able to get the whole tree compiling & testing successfully earlier.

Jeff King· Sep 13, 2021, 19:27 UTC · re: Ævar Arnfjörð Bjarmason · lore

Re: [PATCH v2 3/4] difftool: use run_command() API in run_file_diff()

On Mon, Sep 13, 2021 at 09:13:43PM +0200, Ævar Arnfjörð Bjarmason wrote:
> Yes, I had something like that in the earlier round, but decided to try
> to keep the diff minimal, churn and all that. So it would be nice, but
> let's do it as some later cleanup if someone's interested.
OK, let's leave it for now, then.
Show 5 quoted lines
> Aside: I did most of that removal of "argv" from the child_process
> struct you suggested, it's in
> avar/run-command-use-less-argv-use-args-instead in my repo if you're
> curious / wanted to pick any of that up. I won't be submitting it any
> time soon for the reasons noted upthread.

Yeah, it's rather far-reaching, which is why I didn't do a mass-conversion when I introduced "args" in c460c0ecdc (run-command: store an optional argv_array, 2014-05-15). My plan was to clean up spots over time as we touched them, but of course that's quite a slow process.

Mostly I just didn't want to see any sites going the _other_ direction, as in your original version of the series. :)

I'm quite happy with what you ended up with here.
-Peff
Ævar Arnfjörð Bjarmason· Sep 13, 2021, 03:35 UTC · re: Ævar Arnfjörð Bjarmason · lore

[PATCH v2 4/4] parse-options API: remove OPTION_ARGUMENT feature

As was noted in 1a85b49b87a (parse-options: make OPT_ARGUMENT() more useful, 2019-03-14) there's only ever been one user of the OPT_ARGUMENT(), that user was added in 20de316e334 (difftool: allow running outside Git worktrees with --no-index, 2019-03-14).

The OPT_ARGUMENT() feature itself was added way back in 580d5bffdea (parse-options: new option type to treat an option-like parameter as an argument., 2008-03-02), but as discussed in 1a85b49b87a wasn't used until 20de316e334 in 2019.

Now that the preceding commit has migrated this code over to using "struct strvec" to manage the "args" member of a "struct child_process", we can just use that directly instead of relying on OPT_ARGUMENT.

This has a minor change in behavior in that if we'll pass --no-index we'll now always pass it as the first argument, before we'd pass it in whatever position the caller did. Preserving this was the real value of OPT_ARGUMENT(), but as it turns out we didn't need that either. We can always inject it as the first argument, the other end will parse it just the same.

Note that we cannot remove the "out" and "cpidx" members of "struct parse_opt_ctx_t" added in 580d5bffdea, while they were introduced with OPT_ARGUMENT() we since used them for other things.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Documentation/technical/api-parse-options.txt |  5 -----
 builtin/difftool.c                            |  4 +++-
 parse-options.c                               | 13 -------------
 parse-options.h                               |  3 ---
 t/helper/test-parse-options.c                 |  1 -
 t/t0040-parse-options.sh                      |  5 -----
 6 files changed, 3 insertions(+), 28 deletions(-)
Show changes to 6 files +3 −28

Documentation/technical/api-parse-options.txt, builtin/difftool.c, parse-options.c, parse-options.h, t/helper/test-parse-options.c, t/t0040-parse-options.sh

diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 5a60bbfa7f4..acfd5dc1d8b 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -198,11 +198,6 @@ There are some macros to easily define options:
 	The filename will be prefixed by passing the filename along with
 	the prefix argument of `parse_options()` to `prefix_filename()`.
 
-`OPT_ARGUMENT(long, &int_var, description)`::
-	Introduce a long-option argument that will be kept in `argv[]`.
-	If this option was seen, `int_var` will be set to one (except
-	if a `NULL` pointer was passed).
-
 `OPT_NUMBER_CALLBACK(&var, description, func_ptr)`::
 	Recognize numerical options like -123 and feed the integer as
 	if it was an argument to the function given by `func_ptr`.
diff --git a/builtin/difftool.c b/builtin/difftool.c
index de2e5545c81..bb9fe7245a4 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -709,7 +709,7 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
 			    "tool returns a non - zero exit code")),
 		OPT_STRING('x', "extcmd", &extcmd, N_("command"),
 			   N_("specify a custom command for viewing diffs")),
-		OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")),
+		OPT_BOOL(0, "no-index", &no_index, N_("passed to `diff`")),
 		OPT_END()
 	};
 	struct child_process child = CHILD_PROCESS_INIT;
@@ -763,6 +763,8 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
 	 * each file that changed.
 	 */
 	strvec_push(&child.args, "diff");
+	if (no_index)
+		strvec_push(&child.args, "--no-index");
 	if (dir_diff)
 		strvec_pushl(&child.args, "--raw", "--no-abbrev", "-z", NULL);
 	strvec_pushv(&child.args, argv);
diff --git a/parse-options.c b/parse-options.c
index 2abff136a17..55c5821b08d 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -310,19 +310,6 @@ static enum parse_opt_result parse_long_opt(
 again:
 		if (!skip_prefix(arg, long_name, &rest))
 			rest = NULL;
-		if (options->type == OPTION_ARGUMENT) {
-			if (!rest)
-				continue;
-			if (*rest == '=')
-				return error(_("%s takes no value"),
-					     optname(options, flags));
-			if (*rest)
-				continue;
-			if (options->value)
-				*(int *)options->value = options->defval;
-			p->out[p->cpidx++] = arg - 2;
-			return PARSE_OPT_DONE;
-		}
 		if (!rest) {
 			/* abbreviated? */
 			if (!(p->flags & PARSE_OPT_KEEP_UNKNOWN) &&
diff --git a/parse-options.h b/parse-options.h
index a845a9d9527..39d90882548 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -8,7 +8,6 @@
 enum parse_opt_type {
 	/* special types */
 	OPTION_END,
-	OPTION_ARGUMENT,
 	OPTION_GROUP,
 	OPTION_NUMBER,
 	OPTION_ALIAS,
@@ -155,8 +154,6 @@ struct option {
 #define OPT_INTEGER_F(s, l, v, h, f)     { OPTION_INTEGER, (s), (l), (v), N_("n"), (h), (f) }
 
 #define OPT_END()                   { OPTION_END }
-#define OPT_ARGUMENT(l, v, h)       { OPTION_ARGUMENT, 0, (l), (v), NULL, \
-				      (h), PARSE_OPT_NOARG, NULL, 1 }
 #define OPT_GROUP(h)                { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
 #define OPT_BIT(s, l, v, h, b)      OPT_BIT_F(s, l, v, h, b, 0)
 #define OPT_BITOP(s, l, v, h, set, clear) { OPTION_BITOP, (s), (l), (v), NULL, (h), \
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 2051ce57db7..a282b6ff13e 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -134,7 +134,6 @@ int cmd__parse_options(int argc, const char **argv)
 		OPT_NOOP_NOARG(0, "obsolete"),
 		OPT_STRING_LIST(0, "list", &list, "str", "add str to list"),
 		OPT_GROUP("Magic arguments"),
-		OPT_ARGUMENT("quux", NULL, "means --quux"),
 		OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
 			number_callback),
 		{ OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b",
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index ad4746d899a..da310ed29b1 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -37,7 +37,6 @@ String options
     --list <str>          add str to list
 
 Magic arguments
-    --quux                means --quux
     -NUM                  set integer to NUM
     +                     same as -b
     --ambiguous           positive ambiguity
@@ -263,10 +262,6 @@ test_expect_success 'detect possible typos' '
 	test_cmp typo.err output.err
 '
 
-test_expect_success 'keep some options as arguments' '
-	test-tool parse-options --expect="arg 00: --quux" --quux
-'
-
 cat >expect <<\EOF
 Callback: "four", 0
 boolean: 5
-- 
2.33.0.999.ga5f89b684e9
Junio C Hamano· Sep 13, 2021, 06:27 UTC · re: Ævar Arnfjörð Bjarmason · lore

Re: [PATCH v2 4/4] parse-options API: remove OPTION_ARGUMENT feature

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
> Now that the preceding commit has migrated this code over to using
> "struct strvec" to manage the "args" member of a "struct
> child_process", we can just use that directly instead of relying on
> OPT_ARGUMENT.
Nice.
Jeff King· Sep 13, 2021, 18:04 UTC · re: Ævar Arnfjörð Bjarmason · lore

Re: [PATCH v2 0/4] difftool refactoring + remove OPT_ARGUMENT() macro

On Mon, Sep 13, 2021 at 05:35:36AM +0200, Ævar Arnfjörð Bjarmason wrote:
Show 10 quoted lines
> I think this v2 should address the comments Jeff King had in
> https://lore.kernel.org/git/YT6BnnXeAWn8BycF@coredump.intra.peff.net/;
> there's now an amended version of his proposed patch as part of this,
> but I prepended it with another one to prepare the "struct
> child_process" in cmd_difftool().
> 
> Doing so nicely gets around the question of the strvec memory
> management, since we can trust the run-command.c API to do that for
> us, but couldn't in my v1 when we'd copy our own "struct strvec *" to
> its "args".

Thanks for taking my suggestions into account. I like this approach, and I don't see any bugs. I replied separately to patch 3 with some further opportunities for cleanup, but with or without them, this is all a strict improvement.

-Peff

← back to recent threads