git@vger.kernel.org list mirror (unofficial, one of many)
 help / color / mirror / code / Atom feed
* [PATCH] [GSOC]trailer: change $ARG to environment variable
@ 2021-03-23 14:53 ZheNing Hu via GitGitGadget
  2021-03-24 15:42 ` [PATCH v2] [GSOC]trailer: pass arg as positional parameter ZheNing Hu via GitGitGadget
  0 siblings, 1 reply; 60+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-23 14:53 UTC (permalink / raw)
  To: git; +Cc: Christian Couder, Junio C Hamano, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

In the original implementation of `trailer.<token>.command`,
use `strbuf_replace` to replace `$ARG` in the <value> of the trailer,
but it have a problem:`strbuf_replace` replace the `$ARG` in command
only once, If the user's trailer command have used more then one `$ARG`,
error will occur.

So pass `$ARG` as an environment variable to the trailer command,
all `$ARG` in the command will be replaced, which will fix this problem.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC] trailer: change $ARG to environment variable
    
    In https://lore.kernel.org/git/xmqqv99i4ck2.fsf@gitster.g/ Junio and
    Christian talked about the problem of using strbuf_replace() to replace
    $ARG.
    
    The current new solution is to pass $ARG as an environment variable into
    the command.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-913%2Fadlternative%2Ftrailer-pass-ARG-env-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-913/adlternative/trailer-pass-ARG-env-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/913

 Documentation/git-interpret-trailers.txt |  3 ++-
 t/t7513-interpret-trailers.sh            | 17 +++++++++++++++++
 trailer.c                                |  9 ++++++---
 3 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 96ec6499f001..7cf7c032a0e9 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -242,7 +242,8 @@ line, where <value> is taken to be the standard output of the
 specified command with any leading and trailing whitespace trimmed
 off.
 +
-If the command contains the `$ARG` string, this string will be
+If the command contains the `$ARG` string (`$ARG` is an exported
+environment variable), this string will be
 replaced with the <value> part of an existing trailer with the same
 <token>, if any, before the command is launched.
 +
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 6602790b5f4c..d303cf0e4b36 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -1291,6 +1291,23 @@ test_expect_success 'with command using $ARG' '
 	test_cmp expected actual
 '
 
+test_expect_success 'with command using more than one $ARG' '
+	git config trailer.fix.ifExists "replace" &&
+	git config trailer.fix.command "test -n $ARG && git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG || true" &&
+	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
+	cat complex_message_body >expected &&
+	sed -e "s/ Z\$/ /" >>expected <<-EOF &&
+		Fixes: $FIXED
+		Acked-by= Z
+		Reviewed-by:
+		Signed-off-by: Z
+		Signed-off-by: A U Thor <author@example.com>
+	EOF
+	git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+		<complex_message >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'with failing command using $ARG' '
 	git config trailer.fix.ifExists "replace" &&
 	git config trailer.fix.command "false \$ARG" &&
diff --git a/trailer.c b/trailer.c
index be4e9726421c..42e3b818327a 100644
--- a/trailer.c
+++ b/trailer.c
@@ -44,7 +44,7 @@ static char *separators = ":";
 
 static int configured;
 
-#define TRAILER_ARG_STRING "$ARG"
+#define TRAILER_ARG_STRING "ARG"
 
 static const char *git_generated_prefixes[] = {
 	"Signed-off-by: ",
@@ -222,13 +222,16 @@ static char *apply_command(const char *command, const char *arg)
 	struct strbuf buf = STRBUF_INIT;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	char *result;
+	const char *const *var;
 
 	strbuf_addstr(&cmd, command);
+	for (var = local_repo_env; *var; var++)
+		strvec_push(&cp.env_array, *var);
 	if (arg)
-		strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
+		strvec_pushf(&cp.env_array, "%s=%s", TRAILER_ARG_STRING, arg);
 
 	strvec_push(&cp.args, cmd.buf);
-	cp.env = local_repo_env;
+
 	cp.no_stdin = 1;
 	cp.use_shell = 1;
 

base-commit: 142430338477d9d1bb25be66267225fb58498d92
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v2] [GSOC]trailer: pass arg as positional parameter
  2021-03-23 14:53 [PATCH] [GSOC]trailer: change $ARG to environment variable ZheNing Hu via GitGitGadget
@ 2021-03-24 15:42 ` ZheNing Hu via GitGitGadget
  2021-03-24 20:18   ` Junio C Hamano
  2021-03-25 11:53   ` [PATCH v3] " ZheNing Hu via GitGitGadget
  0 siblings, 2 replies; 60+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-24 15:42 UTC (permalink / raw)
  To: git; +Cc: Christian Couder, Junio C Hamano, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

In the original implementation of `trailer.<token>.command`,
use `strbuf_replace` to replace $ARG in the <value> of the
trailer, but it have a problem: `strbuf_replace` replace the
$ARG in command only once, If the user's trailer command have
used more than one $ARG, error will occur.

If directly modify the implementation of the original
`trailer.<token>.command`, The user’s previous `'$ARG'` in
trailer command will not be replaced. So now add new
config "trailer.<token>.cmd", pass trailer's value as
positional parameter 1 to the user's command, users can
use $1 as trailer's value, to implement original variable
replacement.

Original `trailer.<token>.command` can still be used until git
completely abandoned it.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC]trailer: pass arg as positional parameter
    
    In https://lore.kernel.org/git/xmqqv99i4ck2.fsf@gitster.g/ Junio and
    Christian talked about the problem of using strbuf_replace() to replace
    $ARG.
    
    Now pass trailer value as $1 to the trailer command with another
    trailer.<token>.cmd config.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-913%2Fadlternative%2Ftrailer-pass-ARG-env-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-913/adlternative/trailer-pass-ARG-env-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/913

Range-diff vs v1:

 1:  abc5b04d152f ! 1:  185356d6fc90 [GSOC]trailer: change $ARG to environment variable
     @@ Metadata
      Author: ZheNing Hu <adlternative@gmail.com>
      
       ## Commit message ##
     -    [GSOC]trailer: change $ARG to environment variable
     +    [GSOC]trailer: pass arg as positional parameter
      
          In the original implementation of `trailer.<token>.command`,
     -    use `strbuf_replace` to replace `$ARG` in the <value> of the trailer,
     -    but it have a problem:`strbuf_replace` replace the `$ARG` in command
     -    only once, If the user's trailer command have used more then one `$ARG`,
     -    error will occur.
     +    use `strbuf_replace` to replace $ARG in the <value> of the
     +    trailer, but it have a problem: `strbuf_replace` replace the
     +    $ARG in command only once, If the user's trailer command have
     +    used more than one $ARG, error will occur.
      
     -    So pass `$ARG` as an environment variable to the trailer command,
     -    all `$ARG` in the command will be replaced, which will fix this problem.
     +    If directly modify the implementation of the original
     +    `trailer.<token>.command`, The user’s previous `'$ARG'` in
     +    trailer command will not be replaced. So now add new
     +    config "trailer.<token>.cmd", pass trailer's value as
     +    positional parameter 1 to the user's command, users can
     +    use $1 as trailer's value, to implement original variable
     +    replacement.
     +
     +    Original `trailer.<token>.command` can still be used until git
     +    completely abandoned it.
      
          Signed-off-by: ZheNing Hu <adlternative@gmail.com>
      
       ## Documentation/git-interpret-trailers.txt ##
     -@@ Documentation/git-interpret-trailers.txt: line, where <value> is taken to be the standard output of the
     - specified command with any leading and trailing whitespace trimmed
     - off.
     - +
     --If the command contains the `$ARG` string, this string will be
     -+If the command contains the `$ARG` string (`$ARG` is an exported
     -+environment variable), this string will be
     - replaced with the <value> part of an existing trailer with the same
     - <token>, if any, before the command is launched.
     - +
     +@@ Documentation/git-interpret-trailers.txt: also be executed for each of these arguments. And the <value> part of
     + these arguments, if any, will be used to replace the `$ARG` string in
     + the command.
     + 
     ++trailer.<token>.cmd::
     ++	Similar to 'trailer.<token>.command'. But the difference is that
     ++	`$1` is used in the command to replace the value of the trailer
     ++	instead of the original `$ARG`, which means that we can quote the
     ++	trailer value multiple times in the command.
     ++	E.g. `trailer.sign.cmd="test -n \"$1\" && echo \"$1\" || true "`
     ++
     + EXAMPLES
     + --------
     + 
      
       ## t/t7513-interpret-trailers.sh ##
     -@@ t/t7513-interpret-trailers.sh: test_expect_success 'with command using $ARG' '
     - 	test_cmp expected actual
     +@@ t/t7513-interpret-trailers.sh: test_expect_success 'setup a commit' '
     + 	git commit -m "Add file a.txt"
       '
       
     -+test_expect_success 'with command using more than one $ARG' '
     ++test_expect_success 'with cmd using $1' '
     ++	test_when_finished "git config --unset trailer.fix.cmd" &&
      +	git config trailer.fix.ifExists "replace" &&
     -+	git config trailer.fix.command "test -n $ARG && git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG || true" &&
     ++	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%s)\" \
     ++		--abbrev-commit --abbrev=14 \"\$1\" || true" &&
      +	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
     -+	cat complex_message_body >expected &&
     -+	sed -e "s/ Z\$/ /" >>expected <<-EOF &&
     ++	cat complex_message_body >expected2 &&
     ++	sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
      +		Fixes: $FIXED
      +		Acked-by= Z
      +		Reviewed-by:
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'with command using $ARG' '
      +		Signed-off-by: A U Thor <author@example.com>
      +	EOF
      +	git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
     -+		<complex_message >actual &&
     -+	test_cmp expected actual
     ++		<complex_message >actual2 &&
     ++	test_cmp expected2 actual2
      +'
      +
     - test_expect_success 'with failing command using $ARG' '
     + test_expect_success 'with command using $ARG' '
       	git config trailer.fix.ifExists "replace" &&
     - 	git config trailer.fix.command "false \$ARG" &&
     +-	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
     ++	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
     ++		--abbrev-commit --abbrev=14 \$ARG" &&
     + 	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
     + 	cat complex_message_body >expected &&
     + 	sed -e "s/ Z\$/ /" >>expected <<-EOF &&
      
       ## trailer.c ##
     -@@ trailer.c: static char *separators = ":";
     - 
     - static int configured;
     - 
     --#define TRAILER_ARG_STRING "$ARG"
     -+#define TRAILER_ARG_STRING "ARG"
     +@@ trailer.c: struct conf_info {
     + 	char *name;
     + 	char *key;
     + 	char *command;
     ++	int is_new_cmd;
     + 	enum trailer_where where;
     + 	enum trailer_if_exists if_exists;
     + 	enum trailer_if_missing if_missing;
     +@@ trailer.c: static int check_if_different(struct trailer_item *in_tok,
     + 	return 1;
     + }
       
     - static const char *git_generated_prefixes[] = {
     - 	"Signed-off-by: ",
     -@@ trailer.c: static char *apply_command(const char *command, const char *arg)
     +-static char *apply_command(const char *command, const char *arg)
     ++static char *apply_command(const char *command, int is_new_cmd , const char *arg)
     + {
     + 	struct strbuf cmd = STRBUF_INIT;
       	struct strbuf buf = STRBUF_INIT;
     - 	struct child_process cp = CHILD_PROCESS_INIT;
     +@@ trailer.c: static char *apply_command(const char *command, const char *arg)
       	char *result;
     -+	const char *const *var;
       
       	strbuf_addstr(&cmd, command);
     -+	for (var = local_repo_env; *var; var++)
     -+		strvec_push(&cp.env_array, *var);
     - 	if (arg)
     +-	if (arg)
      -		strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
     -+		strvec_pushf(&cp.env_array, "%s=%s", TRAILER_ARG_STRING, arg);
     - 
     +-
       	strvec_push(&cp.args, cmd.buf);
     --	cp.env = local_repo_env;
     -+
     ++	if (arg) {
     ++		if (is_new_cmd)
     ++			strvec_push(&cp.args, arg);
     ++		else
     ++			strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
     ++	}
     + 	cp.env = local_repo_env;
       	cp.no_stdin = 1;
       	cp.use_shell = 1;
     +@@ trailer.c: static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg
     + 			else
     + 				arg = xstrdup("");
     + 		}
     +-		arg_tok->value = apply_command(arg_tok->conf.command, arg);
     ++		arg_tok->value = apply_command(arg_tok->conf.command, arg_tok->conf.is_new_cmd, arg);
     + 		free((char *)arg);
     + 	}
     + }
     +@@ trailer.c: static struct arg_item *get_conf_item(const char *name)
     + 	return item;
     + }
     + 
     +-enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_WHERE,
     +-			 TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
     ++enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_CMD,
     ++			TRAILER_WHERE, TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
       
     + static struct {
     + 	const char *name;
     +@@ trailer.c: static struct {
     + } trailer_config_items[] = {
     + 	{ "key", TRAILER_KEY },
     + 	{ "command", TRAILER_COMMAND },
     ++	{ "cmd", TRAILER_CMD },
     + 	{ "where", TRAILER_WHERE },
     + 	{ "ifexists", TRAILER_IF_EXISTS },
     + 	{ "ifmissing", TRAILER_IF_MISSING }
     +@@ trailer.c: static int git_trailer_config(const char *conf_key, const char *value, void *cb)
     + 	case TRAILER_COMMAND:
     + 		if (conf->command)
     + 			warning(_("more than one %s"), conf_key);
     ++		conf->is_new_cmd = 0;
     ++		conf->command = xstrdup(value);
     ++		break;
     ++	case TRAILER_CMD:
     ++		if (conf->command)
     ++			warning(_("more than one %s"), conf_key);
     ++		conf->is_new_cmd = 1;
     + 		conf->command = xstrdup(value);
     + 		break;
     + 	case TRAILER_WHERE:


 Documentation/git-interpret-trailers.txt |  7 +++++++
 t/t7513-interpret-trailers.sh            | 22 +++++++++++++++++++-
 trailer.c                                | 26 +++++++++++++++++-------
 3 files changed, 47 insertions(+), 8 deletions(-)

diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 96ec6499f001..38656b1b3841 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -252,6 +252,13 @@ also be executed for each of these arguments. And the <value> part of
 these arguments, if any, will be used to replace the `$ARG` string in
 the command.
 
+trailer.<token>.cmd::
+	Similar to 'trailer.<token>.command'. But the difference is that
+	`$1` is used in the command to replace the value of the trailer
+	instead of the original `$ARG`, which means that we can quote the
+	trailer value multiple times in the command.
+	E.g. `trailer.sign.cmd="test -n \"$1\" && echo \"$1\" || true "`
+
 EXAMPLES
 --------
 
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 6602790b5f4c..7cb81201442a 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -1274,9 +1274,29 @@ test_expect_success 'setup a commit' '
 	git commit -m "Add file a.txt"
 '
 
+test_expect_success 'with cmd using $1' '
+	test_when_finished "git config --unset trailer.fix.cmd" &&
+	git config trailer.fix.ifExists "replace" &&
+	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \"\$1\" || true" &&
+	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
+	cat complex_message_body >expected2 &&
+	sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
+		Fixes: $FIXED
+		Acked-by= Z
+		Reviewed-by:
+		Signed-off-by: Z
+		Signed-off-by: A U Thor <author@example.com>
+	EOF
+	git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+		<complex_message >actual2 &&
+	test_cmp expected2 actual2
+'
+
 test_expect_success 'with command using $ARG' '
 	git config trailer.fix.ifExists "replace" &&
-	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \$ARG" &&
 	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-EOF &&
diff --git a/trailer.c b/trailer.c
index be4e9726421c..80f47657ff1a 100644
--- a/trailer.c
+++ b/trailer.c
@@ -14,6 +14,7 @@ struct conf_info {
 	char *name;
 	char *key;
 	char *command;
+	int is_new_cmd;
 	enum trailer_where where;
 	enum trailer_if_exists if_exists;
 	enum trailer_if_missing if_missing;
@@ -216,7 +217,7 @@ static int check_if_different(struct trailer_item *in_tok,
 	return 1;
 }
 
-static char *apply_command(const char *command, const char *arg)
+static char *apply_command(const char *command, int is_new_cmd , const char *arg)
 {
 	struct strbuf cmd = STRBUF_INIT;
 	struct strbuf buf = STRBUF_INIT;
@@ -224,10 +225,13 @@ static char *apply_command(const char *command, const char *arg)
 	char *result;
 
 	strbuf_addstr(&cmd, command);
-	if (arg)
-		strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
-
 	strvec_push(&cp.args, cmd.buf);
+	if (arg) {
+		if (is_new_cmd)
+			strvec_push(&cp.args, arg);
+		else
+			strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
+	}
 	cp.env = local_repo_env;
 	cp.no_stdin = 1;
 	cp.use_shell = 1;
@@ -257,7 +261,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg
 			else
 				arg = xstrdup("");
 		}
-		arg_tok->value = apply_command(arg_tok->conf.command, arg);
+		arg_tok->value = apply_command(arg_tok->conf.command, arg_tok->conf.is_new_cmd, arg);
 		free((char *)arg);
 	}
 }
@@ -454,8 +458,8 @@ static struct arg_item *get_conf_item(const char *name)
 	return item;
 }
 
-enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_WHERE,
-			 TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
+enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_CMD,
+			TRAILER_WHERE, TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
 
 static struct {
 	const char *name;
@@ -463,6 +467,7 @@ static struct {
 } trailer_config_items[] = {
 	{ "key", TRAILER_KEY },
 	{ "command", TRAILER_COMMAND },
+	{ "cmd", TRAILER_CMD },
 	{ "where", TRAILER_WHERE },
 	{ "ifexists", TRAILER_IF_EXISTS },
 	{ "ifmissing", TRAILER_IF_MISSING }
@@ -540,6 +545,13 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
 	case TRAILER_COMMAND:
 		if (conf->command)
 			warning(_("more than one %s"), conf_key);
+		conf->is_new_cmd = 0;
+		conf->command = xstrdup(value);
+		break;
+	case TRAILER_CMD:
+		if (conf->command)
+			warning(_("more than one %s"), conf_key);
+		conf->is_new_cmd = 1;
 		conf->command = xstrdup(value);
 		break;
 	case TRAILER_WHERE:

base-commit: 142430338477d9d1bb25be66267225fb58498d92
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v2] [GSOC]trailer: pass arg as positional parameter
  2021-03-24 15:42 ` [PATCH v2] [GSOC]trailer: pass arg as positional parameter ZheNing Hu via GitGitGadget
@ 2021-03-24 20:18   ` Junio C Hamano
  2021-03-25  1:43     ` ZheNing Hu
  2021-03-25 11:53   ` [PATCH v3] " ZheNing Hu via GitGitGadget
  1 sibling, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2021-03-24 20:18 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget; +Cc: git, Christian Couder, ZheNing Hu

"ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: ZheNing Hu <adlternative@gmail.com>
>
> In the original implementation of `trailer.<token>.command`,
> use `strbuf_replace` to replace $ARG in the <value> of the
> trailer, but it have a problem: `strbuf_replace` replace the
> $ARG in command only once, If the user's trailer command have
> used more than one $ARG, error will occur.
>
> If directly modify the implementation of the original
> `trailer.<token>.command`, The user’s previous `'$ARG'` in
> trailer command will not be replaced. So now add new
> config "trailer.<token>.cmd", pass trailer's value as
> positional parameter 1 to the user's command, users can
> use $1 as trailer's value, to implement original variable
> replacement.
>
> Original `trailer.<token>.command` can still be used until git
> completely abandoned it.

Sorry, but that's quite an ungrammatical mess X-<.

>  1:  abc5b04d152f ! 1:  185356d6fc90 [GSOC]trailer: change $ARG to environment variable
>      @@ Metadata
>       Author: ZheNing Hu <adlternative@gmail.com>

As this is completely a different design and does not share anything
with the earlier round, the range-diff is merely distracting and
useless.

>  Documentation/git-interpret-trailers.txt |  7 +++++++
>  t/t7513-interpret-trailers.sh            | 22 +++++++++++++++++++-
>  trailer.c                                | 26 +++++++++++++++++-------
>  3 files changed, 47 insertions(+), 8 deletions(-)
>
> diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
> index 96ec6499f001..38656b1b3841 100644
> --- a/Documentation/git-interpret-trailers.txt
> +++ b/Documentation/git-interpret-trailers.txt
> @@ -252,6 +252,13 @@ also be executed for each of these arguments. And the <value> part of
>  these arguments, if any, will be used to replace the `$ARG` string in
>  the command.
>  
> +trailer.<token>.cmd::
> +	Similar to 'trailer.<token>.command'. But the difference is that
> +	`$1` is used in the command to replace the value of the trailer
> +	instead of the original `$ARG`, which means that we can quote the

"quote"?

> +	trailer value multiple times in the command.
> +	E.g. `trailer.sign.cmd="test -n \"$1\" && echo \"$1\" || true "`

This needs to explain what happens if the user gives both .cmd and
.command to the same token.  Is it an error?  Is the newly invented
.cmd takes precedence?  Something else?

Whatever the answer is, the reasoning behind reaching the design
must be explained and defended in the proposed log message.


> diff --git a/trailer.c b/trailer.c
> index be4e9726421c..80f47657ff1a 100644
> --- a/trailer.c
> +++ b/trailer.c
> @@ -14,6 +14,7 @@ struct conf_info {
>  	char *name;
>  	char *key;
>  	char *command;
> +	int is_new_cmd;

Poor naming.  The .cmd thing may be "new" right now in your mind,
but how would you transition out of it when design flaws are
discovered in it and replace it with yet another mechanism?

Add a new "char *cmd" field, and at the using site, define the
precedence between the two when both cmd and command members of the
structure are populated, perhaps?

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v2] [GSOC]trailer: pass arg as positional parameter
  2021-03-24 20:18   ` Junio C Hamano
@ 2021-03-25  1:43     ` ZheNing Hu
  0 siblings, 0 replies; 60+ messages in thread
From: ZheNing Hu @ 2021-03-25  1:43 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: ZheNing Hu via GitGitGadget, Git List, Christian Couder

Junio C Hamano <gitster@pobox.com> 于2021年3月25日周四 上午4:18写道:
>
> "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > In the original implementation of `trailer.<token>.command`,
> > use `strbuf_replace` to replace $ARG in the <value> of the
> > trailer, but it have a problem: `strbuf_replace` replace the
> > $ARG in command only once, If the user's trailer command have
> > used more than one $ARG, error will occur.
> >
> > If directly modify the implementation of the original
> > `trailer.<token>.command`, The user’s previous `'$ARG'` in
> > trailer command will not be replaced. So now add new
> > config "trailer.<token>.cmd", pass trailer's value as
> > positional parameter 1 to the user's command, users can
> > use $1 as trailer's value, to implement original variable
> > replacement.
> >
> > Original `trailer.<token>.command` can still be used until git
> > completely abandoned it.
>
> Sorry, but that's quite an ungrammatical mess X-<.
>

Somewhat embarrassing. I have tried to fix it...

> >  1:  abc5b04d152f ! 1:  185356d6fc90 [GSOC]trailer: change $ARG to environment variable
> >      @@ Metadata
> >       Author: ZheNing Hu <adlternative@gmail.com>
>
> As this is completely a different design and does not share anything
> with the earlier round, the range-diff is merely distracting and
> useless.
>

I thought the designs of the two were very similar.

> >  Documentation/git-interpret-trailers.txt |  7 +++++++
> >  t/t7513-interpret-trailers.sh            | 22 +++++++++++++++++++-
> >  trailer.c                                | 26 +++++++++++++++++-------
> >  3 files changed, 47 insertions(+), 8 deletions(-)
> >
> > diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
> > index 96ec6499f001..38656b1b3841 100644
> > --- a/Documentation/git-interpret-trailers.txt
> > +++ b/Documentation/git-interpret-trailers.txt
> > @@ -252,6 +252,13 @@ also be executed for each of these arguments. And the <value> part of
> >  these arguments, if any, will be used to replace the `$ARG` string in
> >  the command.
> >
> > +trailer.<token>.cmd::
> > +     Similar to 'trailer.<token>.command'. But the difference is that
> > +     `$1` is used in the command to replace the value of the trailer
> > +     instead of the original `$ARG`, which means that we can quote the
>
> "quote"?
>

parse.

> > +     trailer value multiple times in the command.
> > +     E.g. `trailer.sign.cmd="test -n \"$1\" && echo \"$1\" || true "`
>
> This needs to explain what happens if the user gives both .cmd and
> .command to the same token.  Is it an error?  Is the newly invented
> .cmd takes precedence?  Something else?
>

For the time being, if I make "cmd" and "command" equivalent, it will
only trigger a warning.

> Whatever the answer is, the reasoning behind reaching the design
> must be explained and defended in the proposed log message.
>

OK.

>
> > diff --git a/trailer.c b/trailer.c
> > index be4e9726421c..80f47657ff1a 100644
> > --- a/trailer.c
> > +++ b/trailer.c
> > @@ -14,6 +14,7 @@ struct conf_info {
> >       char *name;
> >       char *key;
> >       char *command;
> > +     int is_new_cmd;
>
> Poor naming.  The .cmd thing may be "new" right now in your mind,
> but how would you transition out of it when design flaws are
> discovered in it and replace it with yet another mechanism?
>

I thought if the "command" will need to be replaced in later releases,
 "is_new_cmd" will be removed at the same time, now I think
 "is_new_cmd" may cause misunderstandings.

> Add a new "char *cmd" field, and at the using site, define the
> precedence between the two when both cmd and command members of the
> structure are populated, perhaps?

It sounds feasible, I will try.

Thanks.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v3] [GSOC]trailer: pass arg as positional parameter
  2021-03-24 15:42 ` [PATCH v2] [GSOC]trailer: pass arg as positional parameter ZheNing Hu via GitGitGadget
  2021-03-24 20:18   ` Junio C Hamano
@ 2021-03-25 11:53   ` ZheNing Hu via GitGitGadget
  2021-03-25 22:28     ` Junio C Hamano
  2021-03-26 16:13     ` [PATCH v4] " ZheNing Hu via GitGitGadget
  1 sibling, 2 replies; 60+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-25 11:53 UTC (permalink / raw)
  To: git; +Cc: Christian Couder, Junio C Hamano, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

Original implementation of `trailer.<token>.command` use
`strbuf_replace` to replace $ARG in command with the <value>
of the trailer, but it have a problem: `strbuf_replace`
replace the $ARG only once, If the user's trailer command
have used more than one $ARG, the remaining replacement will
fail.

If directly modify the implementation of the original
`trailer.<token>.command`, The user’s previous `'$ARG'` in
trailer command will not be replaced. So now add new config
"trailer.<token>.cmd", pass trailer's value as positional
parameter 1 to the user's command, the user can use $1 as
trailer's value, to implement original variable replacement.

If the user has these two configuration: "trailer.<token>.cmd"
and "trailer.<token>.command", "cmd" will execute and "command"
will not executed.

Original `trailer.<token>.command` can still be used until git
completely abandoned it.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC]trailer: pass arg as positional parameter
    
    In https://lore.kernel.org/git/xmqqv99i4ck2.fsf@gitster.g/ Junio and
    Christian talked about the problem of using strbuf_replace() to replace
    $ARG.
    
    Now pass trailer value as $1 to the trailer command with another
    trailer.<token>.cmd config.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-913%2Fadlternative%2Ftrailer-pass-ARG-env-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-913/adlternative/trailer-pass-ARG-env-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/913

Range-diff vs v2:

 1:  185356d6fc90 ! 1:  b268ecd7b395 [GSOC]trailer: pass arg as positional parameter
     @@ Metadata
       ## Commit message ##
          [GSOC]trailer: pass arg as positional parameter
      
     -    In the original implementation of `trailer.<token>.command`,
     -    use `strbuf_replace` to replace $ARG in the <value> of the
     -    trailer, but it have a problem: `strbuf_replace` replace the
     -    $ARG in command only once, If the user's trailer command have
     -    used more than one $ARG, error will occur.
     +    Original implementation of `trailer.<token>.command` use
     +    `strbuf_replace` to replace $ARG in command with the <value>
     +    of the trailer, but it have a problem: `strbuf_replace`
     +    replace the $ARG only once, If the user's trailer command
     +    have used more than one $ARG, the remaining replacement will
     +    fail.
      
          If directly modify the implementation of the original
          `trailer.<token>.command`, The user’s previous `'$ARG'` in
     -    trailer command will not be replaced. So now add new
     -    config "trailer.<token>.cmd", pass trailer's value as
     -    positional parameter 1 to the user's command, users can
     -    use $1 as trailer's value, to implement original variable
     -    replacement.
     +    trailer command will not be replaced. So now add new config
     +    "trailer.<token>.cmd", pass trailer's value as positional
     +    parameter 1 to the user's command, the user can use $1 as
     +    trailer's value, to implement original variable replacement.
     +
     +    If the user has these two configuration: "trailer.<token>.cmd"
     +    and "trailer.<token>.command", "cmd" will execute and "command"
     +    will not executed.
      
          Original `trailer.<token>.command` can still be used until git
          completely abandoned it.
     @@ Documentation/git-interpret-trailers.txt: also be executed for each of these arg
      +trailer.<token>.cmd::
      +	Similar to 'trailer.<token>.command'. But the difference is that
      +	`$1` is used in the command to replace the value of the trailer
     -+	instead of the original `$ARG`, which means that we can quote the
     ++	instead of the original `$ARG`, which means that we can pass the
      +	trailer value multiple times in the command.
     -+	E.g. `trailer.sign.cmd="test -n \"$1\" && echo \"$1\" || true "`
     ++	E.g. `git config trailer.sign.cmd "test -n \"$1\" && echo \"$1\" || true "`.
     ++	If the user has these two configuration: "trailer.<token>.cmd"
     ++	and "trailer.<token>.command", "cmd" will be executed and "command"
     ++	will not be executed.
      +
       EXAMPLES
       --------
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup a commit' '
       	git commit -m "Add file a.txt"
       '
       
     -+test_expect_success 'with cmd using $1' '
     ++test_expect_success 'with cmd and $1' '
      +	test_when_finished "git config --unset trailer.fix.cmd" &&
      +	git config trailer.fix.ifExists "replace" &&
      +	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%s)\" \
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup a commit' '
      +		<complex_message >actual2 &&
      +	test_cmp expected2 actual2
      +'
     ++
     ++test_expect_success 'cmd takes precedence over command' '
     ++	test_when_finished "git config --unset trailer.fix.cmd" &&
     ++	git config trailer.fix.ifExists "replace" &&
     ++	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
     ++		--abbrev-commit --abbrev=14 \"\$1\" || true" &&
     ++	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
     ++		--abbrev-commit --abbrev=14 \$ARG" &&
     ++	FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
     ++	cat complex_message_body >expected2 &&
     ++	sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
     ++		Fixes: $FIXED
     ++		Acked-by= Z
     ++		Reviewed-by:
     ++		Signed-off-by: Z
     ++		Signed-off-by: A U Thor <author@example.com>
     ++	EOF
     ++	git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
     ++		<complex_message >actual2 &&
     ++	test_cmp expected2 actual2
     ++'
      +
       test_expect_success 'with command using $ARG' '
       	git config trailer.fix.ifExists "replace" &&
     @@ trailer.c: struct conf_info {
       	char *name;
       	char *key;
       	char *command;
     -+	int is_new_cmd;
     ++	char *cmd;
       	enum trailer_where where;
       	enum trailer_if_exists if_exists;
       	enum trailer_if_missing if_missing;
     +@@ trailer.c: static void free_arg_item(struct arg_item *item)
     + 	free(item->conf.name);
     + 	free(item->conf.key);
     + 	free(item->conf.command);
     ++	free(item->conf.cmd);
     + 	free(item->token);
     + 	free(item->value);
     + 	free(item);
      @@ trailer.c: static int check_if_different(struct trailer_item *in_tok,
       	return 1;
       }
       
      -static char *apply_command(const char *command, const char *arg)
     -+static char *apply_command(const char *command, int is_new_cmd , const char *arg)
     ++static char *apply_command(const char *command, const char *cmd_, const char *arg)
       {
       	struct strbuf cmd = STRBUF_INIT;
       	struct strbuf buf = STRBUF_INIT;
     -@@ trailer.c: static char *apply_command(const char *command, const char *arg)
     + 	struct child_process cp = CHILD_PROCESS_INIT;
       	char *result;
       
     - 	strbuf_addstr(&cmd, command);
     +-	strbuf_addstr(&cmd, command);
      -	if (arg)
      -		strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
      -
     - 	strvec_push(&cp.args, cmd.buf);
     -+	if (arg) {
     -+		if (is_new_cmd)
     +-	strvec_push(&cp.args, cmd.buf);
     ++	if (cmd_) {
     ++		strbuf_addstr(&cmd, cmd_);
     ++		strvec_push(&cp.args, cmd.buf);
     ++		if (arg)
      +			strvec_push(&cp.args, arg);
     -+		else
     ++	} else if (command) {
     ++		strbuf_addstr(&cmd, command);
     ++		strvec_push(&cp.args, cmd.buf);
     ++		if (arg)
      +			strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
      +	}
       	cp.env = local_repo_env;
       	cp.no_stdin = 1;
       	cp.use_shell = 1;
     +@@ trailer.c: static char *apply_command(const char *command, const char *arg)
     + 
     + static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)
     + {
     +-	if (arg_tok->conf.command) {
     ++	if (arg_tok->conf.command || arg_tok->conf.cmd) {
     + 		const char *arg;
     + 		if (arg_tok->value && arg_tok->value[0]) {
     + 			arg = arg_tok->value;
      @@ trailer.c: static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg
       			else
       				arg = xstrdup("");
       		}
      -		arg_tok->value = apply_command(arg_tok->conf.command, arg);
     -+		arg_tok->value = apply_command(arg_tok->conf.command, arg_tok->conf.is_new_cmd, arg);
     ++		arg_tok->value = apply_command(arg_tok->conf.command, arg_tok->conf.cmd, arg);
       		free((char *)arg);
       	}
       }
     +@@ trailer.c: static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
     + 	dst->name = xstrdup_or_null(src->name);
     + 	dst->key = xstrdup_or_null(src->key);
     + 	dst->command = xstrdup_or_null(src->command);
     ++	dst->cmd = xstrdup_or_null(src->cmd);
     + }
     + 
     + static struct arg_item *get_conf_item(const char *name)
      @@ trailer.c: static struct arg_item *get_conf_item(const char *name)
       	return item;
       }
     @@ trailer.c: static struct {
       	{ "ifexists", TRAILER_IF_EXISTS },
       	{ "ifmissing", TRAILER_IF_MISSING }
      @@ trailer.c: static int git_trailer_config(const char *conf_key, const char *value, void *cb)
     - 	case TRAILER_COMMAND:
     - 		if (conf->command)
       			warning(_("more than one %s"), conf_key);
     -+		conf->is_new_cmd = 0;
     -+		conf->command = xstrdup(value);
     -+		break;
     -+	case TRAILER_CMD:
     -+		if (conf->command)
     -+			warning(_("more than one %s"), conf_key);
     -+		conf->is_new_cmd = 1;
       		conf->command = xstrdup(value);
       		break;
     ++	case TRAILER_CMD:
     ++		if (conf->cmd)
     ++			warning(_("more than one %s"), conf_key);
     ++		conf->cmd = xstrdup(value);
     ++		break;
       	case TRAILER_WHERE:
     + 		if (trailer_set_where(&conf->where, value))
     + 			warning(_("unknown value '%s' for key '%s'"), value, conf_key);
     +@@ trailer.c: static void process_command_line_args(struct list_head *arg_head,
     + 	/* Add an arg item for each configured trailer with a command */
     + 	list_for_each(pos, &conf_head) {
     + 		item = list_entry(pos, struct arg_item, list);
     +-		if (item->conf.command)
     ++		if (item->conf.cmd || item->conf.command)
     + 			add_arg_item(arg_head,
     + 				     xstrdup(token_from_item(item, NULL)),
     + 				     xstrdup(""),


 Documentation/git-interpret-trailers.txt | 10 ++++++
 t/t7513-interpret-trailers.sh            | 43 +++++++++++++++++++++++-
 trailer.c                                | 37 ++++++++++++++------
 3 files changed, 78 insertions(+), 12 deletions(-)

diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 96ec6499f001..f796041514bf 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -252,6 +252,16 @@ also be executed for each of these arguments. And the <value> part of
 these arguments, if any, will be used to replace the `$ARG` string in
 the command.
 
+trailer.<token>.cmd::
+	Similar to 'trailer.<token>.command'. But the difference is that
+	`$1` is used in the command to replace the value of the trailer
+	instead of the original `$ARG`, which means that we can pass the
+	trailer value multiple times in the command.
+	E.g. `git config trailer.sign.cmd "test -n \"$1\" && echo \"$1\" || true "`.
+	If the user has these two configuration: "trailer.<token>.cmd"
+	and "trailer.<token>.command", "cmd" will be executed and "command"
+	will not be executed.
+
 EXAMPLES
 --------
 
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 6602790b5f4c..059beec0c0de 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -1274,9 +1274,50 @@ test_expect_success 'setup a commit' '
 	git commit -m "Add file a.txt"
 '
 
+test_expect_success 'with cmd and $1' '
+	test_when_finished "git config --unset trailer.fix.cmd" &&
+	git config trailer.fix.ifExists "replace" &&
+	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \"\$1\" || true" &&
+	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
+	cat complex_message_body >expected2 &&
+	sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
+		Fixes: $FIXED
+		Acked-by= Z
+		Reviewed-by:
+		Signed-off-by: Z
+		Signed-off-by: A U Thor <author@example.com>
+	EOF
+	git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+		<complex_message >actual2 &&
+	test_cmp expected2 actual2
+'
+
+test_expect_success 'cmd takes precedence over command' '
+	test_when_finished "git config --unset trailer.fix.cmd" &&
+	git config trailer.fix.ifExists "replace" &&
+	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
+		--abbrev-commit --abbrev=14 \"\$1\" || true" &&
+	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \$ARG" &&
+	FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
+	cat complex_message_body >expected2 &&
+	sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
+		Fixes: $FIXED
+		Acked-by= Z
+		Reviewed-by:
+		Signed-off-by: Z
+		Signed-off-by: A U Thor <author@example.com>
+	EOF
+	git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+		<complex_message >actual2 &&
+	test_cmp expected2 actual2
+'
+
 test_expect_success 'with command using $ARG' '
 	git config trailer.fix.ifExists "replace" &&
-	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \$ARG" &&
 	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-EOF &&
diff --git a/trailer.c b/trailer.c
index be4e9726421c..634d3f1ff04a 100644
--- a/trailer.c
+++ b/trailer.c
@@ -14,6 +14,7 @@ struct conf_info {
 	char *name;
 	char *key;
 	char *command;
+	char *cmd;
 	enum trailer_where where;
 	enum trailer_if_exists if_exists;
 	enum trailer_if_missing if_missing;
@@ -127,6 +128,7 @@ static void free_arg_item(struct arg_item *item)
 	free(item->conf.name);
 	free(item->conf.key);
 	free(item->conf.command);
+	free(item->conf.cmd);
 	free(item->token);
 	free(item->value);
 	free(item);
@@ -216,18 +218,24 @@ static int check_if_different(struct trailer_item *in_tok,
 	return 1;
 }
 
-static char *apply_command(const char *command, const char *arg)
+static char *apply_command(const char *command, const char *cmd_, const char *arg)
 {
 	struct strbuf cmd = STRBUF_INIT;
 	struct strbuf buf = STRBUF_INIT;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	char *result;
 
-	strbuf_addstr(&cmd, command);
-	if (arg)
-		strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
-
-	strvec_push(&cp.args, cmd.buf);
+	if (cmd_) {
+		strbuf_addstr(&cmd, cmd_);
+		strvec_push(&cp.args, cmd.buf);
+		if (arg)
+			strvec_push(&cp.args, arg);
+	} else if (command) {
+		strbuf_addstr(&cmd, command);
+		strvec_push(&cp.args, cmd.buf);
+		if (arg)
+			strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
+	}
 	cp.env = local_repo_env;
 	cp.no_stdin = 1;
 	cp.use_shell = 1;
@@ -247,7 +255,7 @@ static char *apply_command(const char *command, const char *arg)
 
 static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)
 {
-	if (arg_tok->conf.command) {
+	if (arg_tok->conf.command || arg_tok->conf.cmd) {
 		const char *arg;
 		if (arg_tok->value && arg_tok->value[0]) {
 			arg = arg_tok->value;
@@ -257,7 +265,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg
 			else
 				arg = xstrdup("");
 		}
-		arg_tok->value = apply_command(arg_tok->conf.command, arg);
+		arg_tok->value = apply_command(arg_tok->conf.command, arg_tok->conf.cmd, arg);
 		free((char *)arg);
 	}
 }
@@ -430,6 +438,7 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
 	dst->name = xstrdup_or_null(src->name);
 	dst->key = xstrdup_or_null(src->key);
 	dst->command = xstrdup_or_null(src->command);
+	dst->cmd = xstrdup_or_null(src->cmd);
 }
 
 static struct arg_item *get_conf_item(const char *name)
@@ -454,8 +463,8 @@ static struct arg_item *get_conf_item(const char *name)
 	return item;
 }
 
-enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_WHERE,
-			 TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
+enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_CMD,
+			TRAILER_WHERE, TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
 
 static struct {
 	const char *name;
@@ -463,6 +472,7 @@ static struct {
 } trailer_config_items[] = {
 	{ "key", TRAILER_KEY },
 	{ "command", TRAILER_COMMAND },
+	{ "cmd", TRAILER_CMD },
 	{ "where", TRAILER_WHERE },
 	{ "ifexists", TRAILER_IF_EXISTS },
 	{ "ifmissing", TRAILER_IF_MISSING }
@@ -542,6 +552,11 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
 			warning(_("more than one %s"), conf_key);
 		conf->command = xstrdup(value);
 		break;
+	case TRAILER_CMD:
+		if (conf->cmd)
+			warning(_("more than one %s"), conf_key);
+		conf->cmd = xstrdup(value);
+		break;
 	case TRAILER_WHERE:
 		if (trailer_set_where(&conf->where, value))
 			warning(_("unknown value '%s' for key '%s'"), value, conf_key);
@@ -708,7 +723,7 @@ static void process_command_line_args(struct list_head *arg_head,
 	/* Add an arg item for each configured trailer with a command */
 	list_for_each(pos, &conf_head) {
 		item = list_entry(pos, struct arg_item, list);
-		if (item->conf.command)
+		if (item->conf.cmd || item->conf.command)
 			add_arg_item(arg_head,
 				     xstrdup(token_from_item(item, NULL)),
 				     xstrdup(""),

base-commit: 142430338477d9d1bb25be66267225fb58498d92
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3] [GSOC]trailer: pass arg as positional parameter
  2021-03-25 11:53   ` [PATCH v3] " ZheNing Hu via GitGitGadget
@ 2021-03-25 22:28     ` Junio C Hamano
  2021-03-26 13:29       ` ZheNing Hu
  2021-03-26 16:13     ` [PATCH v4] " ZheNing Hu via GitGitGadget
  1 sibling, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2021-03-25 22:28 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget; +Cc: git, Christian Couder, ZheNing Hu

"ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: ZheNing Hu <adlternative@gmail.com>
>
> Original implementation of `trailer.<token>.command` use

uses

> `strbuf_replace` to replace $ARG in command with the <value>
> of the trailer, but it have a problem: `strbuf_replace`

has

> replace the $ARG only once, If the user's trailer command

replaces the $ARG only once.

> have used more than one $ARG, the remaining replacement will
> fail.

"will fail" is quite vague.  It is just left unreplaced, and if the
user expects all of them to be replaced, then the expectation and
reality would not match, but all of that you have already said by
"replaces the $ARG only once.", so I think this sentence should be
removed.

> If directly modify the implementation of the original
> `trailer.<token>.command`, The user’s previous `'$ARG'` in
> trailer command will not be replaced.

That statement does not make much sense.  Depending on the way how
the implementation is "directly" modified, you can fix the "replaces
only once" problem without introducing such a problem.  Just look
for '$ARG' in the string and replace all of them, not just the first
one.  It's not too difficult.

This confusion primarily comes from the fact that you forgot to
explain the other problem you are fixing, I think.  Even though the
trailer.<token>.command documentation implies that the user is
expected to give a shell script or some sort as the command and the
use of $ARG makes it look like a shell variable, the original
implementation does not treat it as a shell variable at all.  And
the textual replacement is done without making sure the value being
replaced has characters with special meaning in the shell language.

So existing .command may incorrectly use $ARG inside a single-quote
pair and expect it to be replaced to a string inside a single-quote
pair.  A malformed, or worse, malicious, value would escape out of
the single-quote pair (remember, the '; rm -fr .' example?) and
execute arbitrary code in an unexpected way.  The (ungrammatical)
"if directly modify the implementation" refers to a potential way to
fix these two problems at the same time by doing the $ARG thing
without using textual replacement, namely, exporting the value as an
environment variable ARG.  If that approach was taken, then, $ARG
enclosed in a single-quote pair will no longer be replaced, which
makes it a backward incompatible change.

But without describing what solution you are talking about, your
three-line description does not make much sense.

> So now add new config
> "trailer.<token>.cmd", pass trailer's value as positional
> parameter 1 to the user's command, the user can use $1 as
> trailer's value, to implement original variable replacement.
>
> If the user has these two configuration: "trailer.<token>.cmd"
> and "trailer.<token>.command", "cmd" will execute and "command"
> will not executed.
>
> Original `trailer.<token>.command` can still be used until git
> completely abandoned it.
>
> Signed-off-by: ZheNing Hu <adlternative@gmail.com>

Let's rewrite it completely.

	The `trailer.<token>.command` configuration variable
	specifies a command (run via the shall, so it does not have
	to be a single name of or path to the command, but can be a
	shell script), and the first occurrence of substring $ARG is
	replaced with the value given to the `interpret-trailer`
	command for the token.  This has two downsides:

	* The use of $ARG in the mechanism misleads the users that
          the value is passed in the shell variable, and tempt them
          to use $ARG more than once, but that would not work, as
          the second and subsequent $ARG are not replaced.

	* Because $ARG is textually replaced without regard to the
          shell language syntax, even '$ARG' (inside a single-quote
          pair), which a user would expect to stay intact, would be
          replaced, and worse, if the value had an unmatching single
          quote (imagine a name like "O'Connor", substituted into
          NAME='$ARG' to make it NAME='O'Connor), it would result in
          a broken command that is not syntactically correct (or
          worse).

	Introduce a new `trailer.<token>.cmd` configuration that
	takes higher precedence to deprecate and eventually remove
	`trailer.<token>.command`, which passes the value as a
	parameter to the command.  Instead of "$ARG", the users will
	refer to the value as positional argument, $1, in their
	scripts.

I tried to cover everything we need to tell the reviewers about this
change with the above.  Did I miss anything?

> +trailer.<token>.cmd::
> +	Similar to 'trailer.<token>.command'. But the difference is that
> +	`$1` is used in the command to replace the value of the trailer
> +	instead of the original `$ARG`, which means that we can pass the
> +	trailer value multiple times in the command.

We eventually want to deprecate the .command, so we'd prefer not to
rely on its description too much (e.g. try to find a way to say what
you want to say without "instead of the original `$ARG`").

	The command specified by this configuration variable is run
	with a single parameter, which is the <value> part of an
	existing trailer with the same <token>.  The output from the
	command is then used as the value for the <token> in the
	resulting trailer.

would be the replacement for the part that talks about $ARG in the
description of trailer.<token>.command.

The original description for `trailer.<token>.command` is so jumbled
(not your failure at all) that I had a hard time to understand what
it is trying to say (e.g. what does "as if a special <token>=<value>
argument were added at the beginning of the command line" mean?  Is
it making a one-shot export of environment variable to run the
command???), so the above may need further adjustment.  Christian?
Care to help out?

> +	E.g. `git config trailer.sign.cmd "test -n \"$1\" && echo \"$1\" || true "`.

An example is good.  There is a whole EXAMPLES section in this
manual page, and the one that uses $ARG may be a good candidate to
look at and change to use .cmd (instead of .command).

> +	If the user has these two configuration: "trailer.<token>.cmd"
> +	and "trailer.<token>.command", "cmd" will be executed and "command"
> +	will not be executed.

	When both .cmd and .command are given for the same <token>,
	.cmd is used and .command is ignored.

> diff --git a/trailer.c b/trailer.c
> index be4e9726421c..634d3f1ff04a 100644
> --- a/trailer.c
> +++ b/trailer.c
> @@ -14,6 +14,7 @@ struct conf_info {
>  	char *name;
>  	char *key;
>  	char *command;
> +	char *cmd;
>  	enum trailer_where where;
>  	enum trailer_if_exists if_exists;
>  	enum trailer_if_missing if_missing;
> @@ -127,6 +128,7 @@ static void free_arg_item(struct arg_item *item)
>  	free(item->conf.name);
>  	free(item->conf.key);
>  	free(item->conf.command);
> +	free(item->conf.cmd);
>  	free(item->token);
>  	free(item->value);
>  	free(item);
> @@ -216,18 +218,24 @@ static int check_if_different(struct trailer_item *in_tok,
>  	return 1;
>  }
>  
> -static char *apply_command(const char *command, const char *arg)
> +static char *apply_command(const char *command, const char *cmd_, const char *arg)
>  {
>  	struct strbuf cmd = STRBUF_INIT;
>  	struct strbuf buf = STRBUF_INIT;
>  	struct child_process cp = CHILD_PROCESS_INIT;
>  	char *result;
>  
> -	strbuf_addstr(&cmd, command);
> -	if (arg)
> -		strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
> -
> -	strvec_push(&cp.args, cmd.buf);
> +	if (cmd_) {
> +		strbuf_addstr(&cmd, cmd_);
> +		strvec_push(&cp.args, cmd.buf);
> +		if (arg)
> +			strvec_push(&cp.args, arg);
> +	} else if (command) {
> +		strbuf_addstr(&cmd, command);
> +		strvec_push(&cp.args, cmd.buf);
> +		if (arg)
> +			strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
> +	}

OK.  it is clear cmd_ takes precedence this way.

Later (not as part of this patch, but a few releases down the road),
we may want to add a warning() about using a deprecated feature when
"else if (command)" block is taken.

> @@ -247,7 +255,7 @@ static char *apply_command(const char *command, const char *arg)
>  
>  static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)
>  {
> -	if (arg_tok->conf.command) {
> +	if (arg_tok->conf.command || arg_tok->conf.cmd) {
>  		const char *arg;
>  		if (arg_tok->value && arg_tok->value[0]) {
>  			arg = arg_tok->value;
> @@ -257,7 +265,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg
>  			else
>  				arg = xstrdup("");
>  		}
> -		arg_tok->value = apply_command(arg_tok->conf.command, arg);
> +		arg_tok->value = apply_command(arg_tok->conf.command, arg_tok->conf.cmd, arg);

It might be cleaner to just pass arg_tok->conf to apply_command()
and hide "cmd takes precedence over command" as an implementation
detail of that helper function.

The implementation looks as good as the original "command" with that
change at this point.  Documentation may need a bit more polishing.

Thanks.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3] [GSOC]trailer: pass arg as positional parameter
  2021-03-25 22:28     ` Junio C Hamano
@ 2021-03-26 13:29       ` ZheNing Hu
  0 siblings, 0 replies; 60+ messages in thread
From: ZheNing Hu @ 2021-03-26 13:29 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: ZheNing Hu via GitGitGadget, Git List, Christian Couder

Junio C Hamano <gitster@pobox.com> 于2021年3月26日周五 上午6:28写道:
>
> "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > Original implementation of `trailer.<token>.command` use
>
> uses
>
> > `strbuf_replace` to replace $ARG in command with the <value>
> > of the trailer, but it have a problem: `strbuf_replace`
>
> has
>
> > replace the $ARG only once, If the user's trailer command
>
> replaces the $ARG only once.
>

Okay... singular and plural problems.

> > have used more than one $ARG, the remaining replacement will
> > fail.
>
> "will fail" is quite vague.  It is just left unreplaced, and if the
> user expects all of them to be replaced, then the expectation and
> reality would not match, but all of that you have already said by
> "replaces the $ARG only once.", so I think this sentence should be
> removed.
>

Indeed so.

> > If directly modify the implementation of the original
> > `trailer.<token>.command`, The user’s previous `'$ARG'` in
> > trailer command will not be replaced.
>
> That statement does not make much sense.  Depending on the way how
> the implementation is "directly" modified, you can fix the "replaces
> only once" problem without introducing such a problem.  Just look
> for '$ARG' in the string and replace all of them, not just the first
> one.  It's not too difficult.
>
> This confusion primarily comes from the fact that you forgot to
> explain the other problem you are fixing, I think.  Even though the
> trailer.<token>.command documentation implies that the user is
> expected to give a shell script or some sort as the command and the
> use of $ARG makes it look like a shell variable, the original
> implementation does not treat it as a shell variable at all.  And
> the textual replacement is done without making sure the value being
> replaced has characters with special meaning in the shell language.
>

Yes! The accidental injection problem caused should have been the focus
of my explanation.

> So existing .command may incorrectly use $ARG inside a single-quote
> pair and expect it to be replaced to a string inside a single-quote
> pair.  A malformed, or worse, malicious, value would escape out of
> the single-quote pair (remember, the '; rm -fr .' example?) and
> execute arbitrary code in an unexpected way.  The (ungrammatical)
> "if directly modify the implementation" refers to a potential way to
> fix these two problems at the same time by doing the $ARG thing
> without using textual replacement, namely, exporting the value as an
> environment variable ARG.  If that approach was taken, then, $ARG
> enclosed in a single-quote pair will no longer be replaced, which
> makes it a backward incompatible change.
>

Oh, I remeber it: terrible shell injection. Specifically, it looks like this:

$ git config trailer.sign.command "git log --author='\$ARG'"
$ git interpret-trailers --trailer "sign = adl' && rm -rf ./repo/'"

now I know that should be "backward incompatible change" as you said.

> But without describing what solution you are talking about, your
> three-line description does not make much sense.
>
> > So now add new config
> > "trailer.<token>.cmd", pass trailer's value as positional
> > parameter 1 to the user's command, the user can use $1 as
> > trailer's value, to implement original variable replacement.
> >
> > If the user has these two configuration: "trailer.<token>.cmd"
> > and "trailer.<token>.command", "cmd" will execute and "command"
> > will not executed.
> >
> > Original `trailer.<token>.command` can still be used until git
> > completely abandoned it.
> >
> > Signed-off-by: ZheNing Hu <adlternative@gmail.com>
>
> Let's rewrite it completely.
>
>         The `trailer.<token>.command` configuration variable
>         specifies a command (run via the shall, so it does not have
>         to be a single name of or path to the command, but can be a
>         shell script), and the first occurrence of substring $ARG is
>         replaced with the value given to the `interpret-trailer`
>         command for the token.  This has two downsides:
>
>         * The use of $ARG in the mechanism misleads the users that
>           the value is passed in the shell variable, and tempt them
>           to use $ARG more than once, but that would not work, as
>           the second and subsequent $ARG are not replaced.
>
>         * Because $ARG is textually replaced without regard to the
>           shell language syntax, even '$ARG' (inside a single-quote
>           pair), which a user would expect to stay intact, would be
>           replaced, and worse, if the value had an unmatching single
>           quote (imagine a name like "O'Connor", substituted into
>           NAME='$ARG' to make it NAME='O'Connor), it would result in
>           a broken command that is not syntactically correct (or
>           worse).
>
>         Introduce a new `trailer.<token>.cmd` configuration that
>         takes higher precedence to deprecate and eventually remove
>         `trailer.<token>.command`, which passes the value as a
>         parameter to the command.  Instead of "$ARG", the users will
>         refer to the value as positional argument, $1, in their
>         scripts.
>
> I tried to cover everything we need to tell the reviewers about this
> change with the above.  Did I miss anything?

Nothing to blame, feature of the old command, 2 problems, 1 solution,
this is what the log should look like.

>
> > +trailer.<token>.cmd::
> > +     Similar to 'trailer.<token>.command'. But the difference is that
> > +     `$1` is used in the command to replace the value of the trailer
> > +     instead of the original `$ARG`, which means that we can pass the
> > +     trailer value multiple times in the command.
>
> We eventually want to deprecate the .command, so we'd prefer not to
> rely on its description too much (e.g. try to find a way to say what
> you want to say without "instead of the original `$ARG`").
>

Oh! here I can’t rely on the documentation of the old `.command`, otherwise it’s
not easy to delete the old documentation.

>         The command specified by this configuration variable is run
>         with a single parameter, which is the <value> part of an
>         existing trailer with the same <token>.  The output from the
>         command is then used as the value for the <token> in the
>         resulting trailer.
>
> would be the replacement for the part that talks about $ARG in the
> description of trailer.<token>.command.
>
> The original description for `trailer.<token>.command` is so jumbled
> (not your failure at all) that I had a hard time to understand what
> it is trying to say (e.g. what does "as if a special <token>=<value>
> argument were added at the beginning of the command line" mean?  Is
> it making a one-shot export of environment variable to run the
> command???), so the above may need further adjustment.  Christian?
> Care to help out?
>
> > +     E.g. `git config trailer.sign.cmd "test -n \"$1\" && echo \"$1\" || true "`.
>
> An example is good.  There is a whole EXAMPLES section in this
> manual page, and the one that uses $ARG may be a good candidate to
> look at and change to use .cmd (instead of .command).
>

Okay, I will modify the paragraphs containing `.command` in these examples.

> > +     If the user has these two configuration: "trailer.<token>.cmd"
> > +     and "trailer.<token>.command", "cmd" will be executed and "command"
> > +     will not be executed.
>
>         When both .cmd and .command are given for the same <token>,
>         .cmd is used and .command is ignored.
>
> > diff --git a/trailer.c b/trailer.c
> > index be4e9726421c..634d3f1ff04a 100644
> > --- a/trailer.c
> > +++ b/trailer.c
> > @@ -14,6 +14,7 @@ struct conf_info {
> >       char *name;
> >       char *key;
> >       char *command;
> > +     char *cmd;
> >       enum trailer_where where;
> >       enum trailer_if_exists if_exists;
> >       enum trailer_if_missing if_missing;
> > @@ -127,6 +128,7 @@ static void free_arg_item(struct arg_item *item)
> >       free(item->conf.name);
> >       free(item->conf.key);
> >       free(item->conf.command);
> > +     free(item->conf.cmd);
> >       free(item->token);
> >       free(item->value);
> >       free(item);
> > @@ -216,18 +218,24 @@ static int check_if_different(struct trailer_item *in_tok,
> >       return 1;
> >  }
> >
> > -static char *apply_command(const char *command, const char *arg)
> > +static char *apply_command(const char *command, const char *cmd_, const char *arg)
> >  {
> >       struct strbuf cmd = STRBUF_INIT;
> >       struct strbuf buf = STRBUF_INIT;
> >       struct child_process cp = CHILD_PROCESS_INIT;
> >       char *result;
> >
> > -     strbuf_addstr(&cmd, command);
> > -     if (arg)
> > -             strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
> > -
> > -     strvec_push(&cp.args, cmd.buf);
> > +     if (cmd_) {
> > +             strbuf_addstr(&cmd, cmd_);
> > +             strvec_push(&cp.args, cmd.buf);
> > +             if (arg)
> > +                     strvec_push(&cp.args, arg);
> > +     } else if (command) {
> > +             strbuf_addstr(&cmd, command);
> > +             strvec_push(&cp.args, cmd.buf);
> > +             if (arg)
> > +                     strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
> > +     }
>
> OK.  it is clear cmd_ takes precedence this way.
>
> Later (not as part of this patch, but a few releases down the road),
> we may want to add a warning() about using a deprecated feature when
> "else if (command)" block is taken.
>

Fine, I will keep this version of "cmd priority execution" for now.

> > @@ -247,7 +255,7 @@ static char *apply_command(const char *command, const char *arg)
> >
> >  static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)
> >  {
> > -     if (arg_tok->conf.command) {
> > +     if (arg_tok->conf.command || arg_tok->conf.cmd) {
> >               const char *arg;
> >               if (arg_tok->value && arg_tok->value[0]) {
> >                       arg = arg_tok->value;
> > @@ -257,7 +265,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg
> >                       else
> >                               arg = xstrdup("");
> >               }
> > -             arg_tok->value = apply_command(arg_tok->conf.command, arg);
> > +             arg_tok->value = apply_command(arg_tok->conf.command, arg_tok->conf.cmd, arg);
>
> It might be cleaner to just pass arg_tok->conf to apply_command()
> and hide "cmd takes precedence over command" as an implementation
> detail of that helper function.
>
> The implementation looks as good as the original "command" with that
> change at this point.  Documentation may need a bit more polishing.
>

you're right.

> Thanks.

Thanks, Junio.
You and the people in the git community are very enthusiastic,
You have patiently explained these small mistakes that I made,
and taught me a lot of problems that I didn't notice.

Grateful.

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-25 11:53   ` [PATCH v3] " ZheNing Hu via GitGitGadget
  2021-03-25 22:28     ` Junio C Hamano
@ 2021-03-26 16:13     ` ZheNing Hu via GitGitGadget
  2021-03-27 18:04       ` Junio C Hamano
  2021-03-31 10:05       ` [PATCH v5 0/2] " ZheNing Hu via GitGitGadget
  1 sibling, 2 replies; 60+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-26 16:13 UTC (permalink / raw)
  To: git; +Cc: Christian Couder, Junio C Hamano, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name of or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token.  This has two downsides:

* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.

* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatching single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor), it would result in
a broken command that is not syntactically correct (or
worse).

Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as a
parameter to the command.  Instead of "$ARG", the users will
refer to the value as positional argument, $1, in their
scripts.

Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC]trailer: pass arg as positional parameter
    
    In https://lore.kernel.org/git/xmqqv99i4ck2.fsf@gitster.g/ Junio and
    Christian talked about the problem of using strbuf_replace() to replace
    $ARG.
    
    Now pass trailer value as $1 to the trailer command with another
    trailer.<token>.cmd config.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-913%2Fadlternative%2Ftrailer-pass-ARG-env-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-913/adlternative/trailer-pass-ARG-env-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/913

Range-diff vs v3:

 1:  b268ecd7b395 ! 1:  e2bbdcb943c2 [GSOC]trailer: pass arg as positional parameter
     @@ Metadata
       ## Commit message ##
          [GSOC]trailer: pass arg as positional parameter
      
     -    Original implementation of `trailer.<token>.command` use
     -    `strbuf_replace` to replace $ARG in command with the <value>
     -    of the trailer, but it have a problem: `strbuf_replace`
     -    replace the $ARG only once, If the user's trailer command
     -    have used more than one $ARG, the remaining replacement will
     -    fail.
     +    The `trailer.<token>.command` configuration variable
     +    specifies a command (run via the shell, so it does not have
     +    to be a single name of or path to the command, but can be a
     +    shell script), and the first occurrence of substring $ARG is
     +    replaced with the value given to the `interpret-trailer`
     +    command for the token.  This has two downsides:
      
     -    If directly modify the implementation of the original
     -    `trailer.<token>.command`, The user’s previous `'$ARG'` in
     -    trailer command will not be replaced. So now add new config
     -    "trailer.<token>.cmd", pass trailer's value as positional
     -    parameter 1 to the user's command, the user can use $1 as
     -    trailer's value, to implement original variable replacement.
     +    * The use of $ARG in the mechanism misleads the users that
     +    the value is passed in the shell variable, and tempt them
     +    to use $ARG more than once, but that would not work, as
     +    the second and subsequent $ARG are not replaced.
      
     -    If the user has these two configuration: "trailer.<token>.cmd"
     -    and "trailer.<token>.command", "cmd" will execute and "command"
     -    will not executed.
     +    * Because $ARG is textually replaced without regard to the
     +    shell language syntax, even '$ARG' (inside a single-quote
     +    pair), which a user would expect to stay intact, would be
     +    replaced, and worse, if the value had an unmatching single
     +    quote (imagine a name like "O'Connor", substituted into
     +    NAME='$ARG' to make it NAME='O'Connor), it would result in
     +    a broken command that is not syntactically correct (or
     +    worse).
      
     -    Original `trailer.<token>.command` can still be used until git
     -    completely abandoned it.
     +    Introduce a new `trailer.<token>.cmd` configuration that
     +    takes higher precedence to deprecate and eventually remove
     +    `trailer.<token>.command`, which passes the value as a
     +    parameter to the command.  Instead of "$ARG", the users will
     +    refer to the value as positional argument, $1, in their
     +    scripts.
      
     +    Helped-by: Junio C Hamano <gitster@pobox.com>
          Signed-off-by: ZheNing Hu <adlternative@gmail.com>
      
       ## Documentation/git-interpret-trailers.txt ##
     @@ Documentation/git-interpret-trailers.txt: also be executed for each of these arg
       the command.
       
      +trailer.<token>.cmd::
     -+	Similar to 'trailer.<token>.command'. But the difference is that
     -+	`$1` is used in the command to replace the value of the trailer
     -+	instead of the original `$ARG`, which means that we can pass the
     -+	trailer value multiple times in the command.
     -+	E.g. `git config trailer.sign.cmd "test -n \"$1\" && echo \"$1\" || true "`.
     -+	If the user has these two configuration: "trailer.<token>.cmd"
     -+	and "trailer.<token>.command", "cmd" will be executed and "command"
     -+	will not be executed.
     ++	The command specified by this configuration variable is run
     ++	with a single parameter, which is the <value> part of an
     ++	existing trailer with the same <token>.  The output from the
     ++	command is then used as the value for the <token> in the
     ++	resulting trailer.
     ++	The command is expected to replace `trailer.<token>.cmd`.
     ++	When both .cmd and .command are given for the same <token>,
     ++        .cmd is used and .command is ignored.
      +
       EXAMPLES
       --------
       
     +@@ Documentation/git-interpret-trailers.txt: $ git format-patch -1
     + $ git interpret-trailers --trailer 'Cc: Alice <alice@example.com>' --trailer 'Reviewed-by: Bob <bob@example.com>' 0001-foo.patch >0001-bar.patch
     + ------------
     + 
     +-* Configure a 'sign' trailer with a command to automatically add a
     ++* Configure a 'sign' trailer with a cmd to automatically add a
     +   'Signed-off-by: ' with the author information only if there is no
     +   'Signed-off-by: ' already, and show how it works:
     + +
     +@@ Documentation/git-interpret-trailers.txt: $ git interpret-trailers --trailer 'Cc: Alice <alice@example.com>' --trailer 'Re
     + $ git config trailer.sign.key "Signed-off-by: "
     + $ git config trailer.sign.ifmissing add
     + $ git config trailer.sign.ifexists doNothing
     +-$ git config trailer.sign.command 'echo "$(git config user.name) <$(git config user.email)>"'
     ++$ git config trailer.sign.cmd 'echo "$(git config user.name) <$(git config user.email)>"'
     + $ git interpret-trailers <<EOF
     + > EOF
     + 
     +@@ Documentation/git-interpret-trailers.txt: subject
     + Fix #42
     + ------------
     + 
     +-* Configure a 'see' trailer with a command to show the subject of a
     ++* Configure a 'see' trailer with a cmd to show the subject of a
     +   commit that is related, and show how it works:
     + +
     + ------------
     + $ git config trailer.see.key "See-also: "
     + $ git config trailer.see.ifExists "replace"
     + $ git config trailer.see.ifMissing "doNothing"
     +-$ git config trailer.see.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG"
     ++$ git config trailer.see.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \"\$1\"|| true "
     + $ git interpret-trailers <<EOF
     + > subject
     + > 
      
       ## t/t7513-interpret-trailers.sh ##
      @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup a commit' '
     @@ trailer.c: static int check_if_different(struct trailer_item *in_tok,
       }
       
      -static char *apply_command(const char *command, const char *arg)
     -+static char *apply_command(const char *command, const char *cmd_, const char *arg)
     ++static char *apply_command(struct conf_info *conf, const char *arg)
       {
       	struct strbuf cmd = STRBUF_INIT;
       	struct strbuf buf = STRBUF_INIT;
     @@ trailer.c: static int check_if_different(struct trailer_item *in_tok,
      -		strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
      -
      -	strvec_push(&cp.args, cmd.buf);
     -+	if (cmd_) {
     -+		strbuf_addstr(&cmd, cmd_);
     ++	if (conf->cmd) {
     ++		strbuf_addstr(&cmd, conf->cmd);
      +		strvec_push(&cp.args, cmd.buf);
      +		if (arg)
      +			strvec_push(&cp.args, arg);
     -+	} else if (command) {
     -+		strbuf_addstr(&cmd, command);
     ++	} else if (conf->command) {
     ++		strbuf_addstr(&cmd, conf->command);
      +		strvec_push(&cp.args, cmd.buf);
      +		if (arg)
      +			strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
     @@ trailer.c: static void apply_item_command(struct trailer_item *in_tok, struct ar
       				arg = xstrdup("");
       		}
      -		arg_tok->value = apply_command(arg_tok->conf.command, arg);
     -+		arg_tok->value = apply_command(arg_tok->conf.command, arg_tok->conf.cmd, arg);
     ++		arg_tok->value = apply_command(&arg_tok->conf, arg);
       		free((char *)arg);
       	}
       }


 Documentation/git-interpret-trailers.txt | 18 +++++++---
 t/t7513-interpret-trailers.sh            | 43 +++++++++++++++++++++++-
 trailer.c                                | 37 ++++++++++++++------
 3 files changed, 82 insertions(+), 16 deletions(-)

diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 96ec6499f001..b73f9c8d71eb 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -252,6 +252,16 @@ also be executed for each of these arguments. And the <value> part of
 these arguments, if any, will be used to replace the `$ARG` string in
 the command.
 
+trailer.<token>.cmd::
+	The command specified by this configuration variable is run
+	with a single parameter, which is the <value> part of an
+	existing trailer with the same <token>.  The output from the
+	command is then used as the value for the <token> in the
+	resulting trailer.
+	The command is expected to replace `trailer.<token>.cmd`.
+	When both .cmd and .command are given for the same <token>,
+        .cmd is used and .command is ignored.
+
 EXAMPLES
 --------
 
@@ -301,7 +311,7 @@ $ git format-patch -1
 $ git interpret-trailers --trailer 'Cc: Alice <alice@example.com>' --trailer 'Reviewed-by: Bob <bob@example.com>' 0001-foo.patch >0001-bar.patch
 ------------
 
-* Configure a 'sign' trailer with a command to automatically add a
+* Configure a 'sign' trailer with a cmd to automatically add a
   'Signed-off-by: ' with the author information only if there is no
   'Signed-off-by: ' already, and show how it works:
 +
@@ -309,7 +319,7 @@ $ git interpret-trailers --trailer 'Cc: Alice <alice@example.com>' --trailer 'Re
 $ git config trailer.sign.key "Signed-off-by: "
 $ git config trailer.sign.ifmissing add
 $ git config trailer.sign.ifexists doNothing
-$ git config trailer.sign.command 'echo "$(git config user.name) <$(git config user.email)>"'
+$ git config trailer.sign.cmd 'echo "$(git config user.name) <$(git config user.email)>"'
 $ git interpret-trailers <<EOF
 > EOF
 
@@ -333,14 +343,14 @@ subject
 Fix #42
 ------------
 
-* Configure a 'see' trailer with a command to show the subject of a
+* Configure a 'see' trailer with a cmd to show the subject of a
   commit that is related, and show how it works:
 +
 ------------
 $ git config trailer.see.key "See-also: "
 $ git config trailer.see.ifExists "replace"
 $ git config trailer.see.ifMissing "doNothing"
-$ git config trailer.see.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG"
+$ git config trailer.see.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \"\$1\"|| true "
 $ git interpret-trailers <<EOF
 > subject
 > 
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 6602790b5f4c..059beec0c0de 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -1274,9 +1274,50 @@ test_expect_success 'setup a commit' '
 	git commit -m "Add file a.txt"
 '
 
+test_expect_success 'with cmd and $1' '
+	test_when_finished "git config --unset trailer.fix.cmd" &&
+	git config trailer.fix.ifExists "replace" &&
+	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \"\$1\" || true" &&
+	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
+	cat complex_message_body >expected2 &&
+	sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
+		Fixes: $FIXED
+		Acked-by= Z
+		Reviewed-by:
+		Signed-off-by: Z
+		Signed-off-by: A U Thor <author@example.com>
+	EOF
+	git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+		<complex_message >actual2 &&
+	test_cmp expected2 actual2
+'
+
+test_expect_success 'cmd takes precedence over command' '
+	test_when_finished "git config --unset trailer.fix.cmd" &&
+	git config trailer.fix.ifExists "replace" &&
+	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
+		--abbrev-commit --abbrev=14 \"\$1\" || true" &&
+	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \$ARG" &&
+	FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
+	cat complex_message_body >expected2 &&
+	sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
+		Fixes: $FIXED
+		Acked-by= Z
+		Reviewed-by:
+		Signed-off-by: Z
+		Signed-off-by: A U Thor <author@example.com>
+	EOF
+	git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+		<complex_message >actual2 &&
+	test_cmp expected2 actual2
+'
+
 test_expect_success 'with command using $ARG' '
 	git config trailer.fix.ifExists "replace" &&
-	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \$ARG" &&
 	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-EOF &&
diff --git a/trailer.c b/trailer.c
index be4e9726421c..278e40974a4c 100644
--- a/trailer.c
+++ b/trailer.c
@@ -14,6 +14,7 @@ struct conf_info {
 	char *name;
 	char *key;
 	char *command;
+	char *cmd;
 	enum trailer_where where;
 	enum trailer_if_exists if_exists;
 	enum trailer_if_missing if_missing;
@@ -127,6 +128,7 @@ static void free_arg_item(struct arg_item *item)
 	free(item->conf.name);
 	free(item->conf.key);
 	free(item->conf.command);
+	free(item->conf.cmd);
 	free(item->token);
 	free(item->value);
 	free(item);
@@ -216,18 +218,24 @@ static int check_if_different(struct trailer_item *in_tok,
 	return 1;
 }
 
-static char *apply_command(const char *command, const char *arg)
+static char *apply_command(struct conf_info *conf, const char *arg)
 {
 	struct strbuf cmd = STRBUF_INIT;
 	struct strbuf buf = STRBUF_INIT;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	char *result;
 
-	strbuf_addstr(&cmd, command);
-	if (arg)
-		strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
-
-	strvec_push(&cp.args, cmd.buf);
+	if (conf->cmd) {
+		strbuf_addstr(&cmd, conf->cmd);
+		strvec_push(&cp.args, cmd.buf);
+		if (arg)
+			strvec_push(&cp.args, arg);
+	} else if (conf->command) {
+		strbuf_addstr(&cmd, conf->command);
+		strvec_push(&cp.args, cmd.buf);
+		if (arg)
+			strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
+	}
 	cp.env = local_repo_env;
 	cp.no_stdin = 1;
 	cp.use_shell = 1;
@@ -247,7 +255,7 @@ static char *apply_command(const char *command, const char *arg)
 
 static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)
 {
-	if (arg_tok->conf.command) {
+	if (arg_tok->conf.command || arg_tok->conf.cmd) {
 		const char *arg;
 		if (arg_tok->value && arg_tok->value[0]) {
 			arg = arg_tok->value;
@@ -257,7 +265,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg
 			else
 				arg = xstrdup("");
 		}
-		arg_tok->value = apply_command(arg_tok->conf.command, arg);
+		arg_tok->value = apply_command(&arg_tok->conf, arg);
 		free((char *)arg);
 	}
 }
@@ -430,6 +438,7 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
 	dst->name = xstrdup_or_null(src->name);
 	dst->key = xstrdup_or_null(src->key);
 	dst->command = xstrdup_or_null(src->command);
+	dst->cmd = xstrdup_or_null(src->cmd);
 }
 
 static struct arg_item *get_conf_item(const char *name)
@@ -454,8 +463,8 @@ static struct arg_item *get_conf_item(const char *name)
 	return item;
 }
 
-enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_WHERE,
-			 TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
+enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_CMD,
+			TRAILER_WHERE, TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
 
 static struct {
 	const char *name;
@@ -463,6 +472,7 @@ static struct {
 } trailer_config_items[] = {
 	{ "key", TRAILER_KEY },
 	{ "command", TRAILER_COMMAND },
+	{ "cmd", TRAILER_CMD },
 	{ "where", TRAILER_WHERE },
 	{ "ifexists", TRAILER_IF_EXISTS },
 	{ "ifmissing", TRAILER_IF_MISSING }
@@ -542,6 +552,11 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
 			warning(_("more than one %s"), conf_key);
 		conf->command = xstrdup(value);
 		break;
+	case TRAILER_CMD:
+		if (conf->cmd)
+			warning(_("more than one %s"), conf_key);
+		conf->cmd = xstrdup(value);
+		break;
 	case TRAILER_WHERE:
 		if (trailer_set_where(&conf->where, value))
 			warning(_("unknown value '%s' for key '%s'"), value, conf_key);
@@ -708,7 +723,7 @@ static void process_command_line_args(struct list_head *arg_head,
 	/* Add an arg item for each configured trailer with a command */
 	list_for_each(pos, &conf_head) {
 		item = list_entry(pos, struct arg_item, list);
-		if (item->conf.command)
+		if (item->conf.cmd || item->conf.command)
 			add_arg_item(arg_head,
 				     xstrdup(token_from_item(item, NULL)),
 				     xstrdup(""),

base-commit: 142430338477d9d1bb25be66267225fb58498d92
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-26 16:13     ` [PATCH v4] " ZheNing Hu via GitGitGadget
@ 2021-03-27 18:04       ` Junio C Hamano
  2021-03-27 19:53         ` Christian Couder
  2021-03-31 10:05       ` [PATCH v5 0/2] " ZheNing Hu via GitGitGadget
  1 sibling, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2021-03-27 18:04 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget; +Cc: git, Christian Couder, ZheNing Hu

"ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:

> @@ -252,6 +252,16 @@ also be executed for each of these arguments. And the <value> part of
>  these arguments, if any, will be used to replace the `$ARG` string in
>  the command.
>  
> +trailer.<token>.cmd::
> +	The command specified by this configuration variable is run
> +	with a single parameter, which is the <value> part of an
> +	existing trailer with the same <token>.  The output from the
> +	command is then used as the value for the <token> in the
> +	resulting trailer.
> +	The command is expected to replace `trailer.<token>.cmd`.
> +	When both .cmd and .command are given for the same <token>,
> +        .cmd is used and .command is ignored.

Christian, because ".cmd" is trying to eventually replace it, I find
it a bit disturbing that the description we give here looks a lot
smaller compared to the one for ".command".  I am afraid that we may
have failed to reproduce something important from the description of
the ".command" for the above; care to rend a hand or two here to
complete the description?

As I cannot grok what the description for ".command" is trying to
say, especially around this part:

    When this option is specified, the behavior is as if a special
    '<token>=<value>' argument were added at the beginning of the command
    line, where <value> is ...

and

    If some '<token>=<value>' arguments are also passed on the command
    line, when a 'trailer.<token>.command' is configured, the command will
    also be executed for each of these arguments.

I cannot quite judge if what we came up with in the above
description is sufficient.

> -* Configure a 'sign' trailer with a command to automatically add a
> +* Configure a 'sign' trailer with a cmd to automatically add a
>    'Signed-off-by: ' with the author information only if there is no
>    'Signed-off-by: ' already, and show how it works:
>  +
> @@ -309,7 +319,7 @@ $ git interpret-trailers --trailer 'Cc: Alice <alice@example.com>' --trailer 'Re
>  $ git config trailer.sign.key "Signed-off-by: "
>  $ git config trailer.sign.ifmissing add
>  $ git config trailer.sign.ifexists doNothing
> -$ git config trailer.sign.command 'echo "$(git config user.name) <$(git config user.email)>"'
> +$ git config trailer.sign.cmd 'echo "$(git config user.name) <$(git config user.email)>"'
>  $ git interpret-trailers <<EOF
>  > EOF

This change would definitely be needed when the support for
".command" is removed after deprecation period.  As it does not take
any argument, .cmd and .command should behave identically, so making
this change now, without waiting, may make sense.

> @@ -333,14 +343,14 @@ subject
>  Fix #42
>  ------------
>  
> -* Configure a 'see' trailer with a command to show the subject of a
> +* Configure a 'see' trailer with a cmd to show the subject of a
>    commit that is related, and show how it works:
>  +
>  ------------
>  $ git config trailer.see.key "See-also: "
>  $ git config trailer.see.ifExists "replace"
>  $ git config trailer.see.ifMissing "doNothing"
> -$ git config trailer.see.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG"
> +$ git config trailer.see.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \"\$1\"|| true "
>  $ git interpret-trailers <<EOF
>  > subject

This, too, but until ".command" is removed, wouldn't it be better
for readers to keep both variants, as the distinction between $ARG
and $1 needs to be illustrated?

Besides, the examples given here are not equivalent.  The original
assumes that ARG is there, or it is OK to default to HEAD; the new
one gives no output when $ARG/$1 is not supplied.  It would confuse
readers to give two too-similar-but-subtly-different examles, as
they will be forced to wonder if the difference is something needed
to transition from .command to .cmd (and I am guessing that it is
not).

Rewriting both to use "--pretty=reference" may be worth doing.  As
can be seen in these examples:

git show -s --pretty=reference \$1
git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$1

that it makes the result much easier to read.

Thanks.  Do not send a reroll prematurely; I want to see area
expert's input at this point.



^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-27 18:04       ` Junio C Hamano
@ 2021-03-27 19:53         ` Christian Couder
  2021-03-28 10:46           ` ZheNing Hu
  0 siblings, 1 reply; 60+ messages in thread
From: Christian Couder @ 2021-03-27 19:53 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: ZheNing Hu via GitGitGadget, git, ZheNing Hu

On Sat, Mar 27, 2021 at 7:04 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
> > @@ -252,6 +252,16 @@ also be executed for each of these arguments. And the <value> part of
> >  these arguments, if any, will be used to replace the `$ARG` string in
> >  the command.
> >
> > +trailer.<token>.cmd::
> > +     The command specified by this configuration variable is run
> > +     with a single parameter, which is the <value> part of an
> > +     existing trailer with the same <token>.  The output from the
> > +     command is then used as the value for the <token> in the
> > +     resulting trailer.
> > +     The command is expected to replace `trailer.<token>.cmd`.

s/trailer.<token>.cmd/trailer.<token>.command/

> > +     When both .cmd and .command are given for the same <token>,
> > +        .cmd is used and .command is ignored.
>
> Christian, because ".cmd" is trying to eventually replace it, I find
> it a bit disturbing that the description we give here looks a lot
> smaller compared to the one for ".command".  I am afraid that we may
> have failed to reproduce something important from the description of
> the ".command" for the above; care to rend a hand or two here to
> complete the description?

Yeah, sure. I just saw that you already asked about this in this
thread. Sorry for not answering earlier.

> As I cannot grok what the description for ".command" is trying to
> say, especially around this part:
>
>     When this option is specified, the behavior is as if a special
>     '<token>=<value>' argument were added at the beginning of the command
>     line, where <value> is ...

This is because when a number of trailers are passed on the command
line, and some other trailers are in the input file, the order in
which the different trailers are processed and their priorities can be
important. So by saying the above, people can get an idea about at
which point and with which priority a trailer coming from such a
config option will be processed.

> and
>
>     If some '<token>=<value>' arguments are also passed on the command
>     line, when a 'trailer.<token>.command' is configured, the command will
>     also be executed for each of these arguments.

Yeah, this means that when a 'trailer.foo.command' is configured, it
is always executed at least once. The first time it is executed, it is
passed nothing ($ARG is replaced with the empty string). Then for each
'foo=<value>' argument passed on the command line, it is executed once
more with $ARG replaced by <value>.

The reason it is always executed first with $ARG replaced with the
empty string is that this way it makes it possible to set up commands
that will always be executed when `git interpret-trailers` is run.
This makes it possible to automatically add some trailers if they are
missing for example.

Another way to do it would be to have another config option called
`trailer.<token>.alwaysRunCmd` to tell if the cmd specified by
`trailer.<token>.cmd` should be run even if no '<token>=<value>'
argument is passed on the command line. As we are introducing
`trailer.<token>.cmd`, it's a good time to wonder if this would be a
better design. But this issue is quite complex, because of the fact
that 'trailer.<token>.ifMissing' and 'trailer.<token>.ifExists' also
take a part in deciding if the command will be run.

This mechanism is the reason why a trick, when setting up a
'trailer.foo.command' trailer, is to also set 'trailer.foo.ifexists'
to "replace", so that the first time the command is run (with $ARG
replaced with the empty string) it will add a foo trailer with a
default value, and if it is run another time, because a 'foo=bar'
argument is passed on the command line, then the trailer with the
default value will be replaced by the value computed from running the
command again with $ARG replaced with "bar".

Another trick is to have the command output nothing when $ARG is the
empty string along with using --trim-empty. This way the command will
create an empty trailer, when it is run the first time, and if it's
not another time, then this empty trailer will be removed because of
--trim-empty.

> I cannot quite judge if what we came up with in the above
> description is sufficient.

I don't think it's sufficient. I think that, while we are at it, a bit
more thinking/discussion is required to make sure we want to keep the
same design as 'trailer.<token>.command'.

> > -* Configure a 'sign' trailer with a command to automatically add a
> > +* Configure a 'sign' trailer with a cmd to automatically add a
> >    'Signed-off-by: ' with the author information only if there is no
> >    'Signed-off-by: ' already, and show how it works:
> >  +
> > @@ -309,7 +319,7 @@ $ git interpret-trailers --trailer 'Cc: Alice <alice@example.com>' --trailer 'Re
> >  $ git config trailer.sign.key "Signed-off-by: "
> >  $ git config trailer.sign.ifmissing add
> >  $ git config trailer.sign.ifexists doNothing
> > -$ git config trailer.sign.command 'echo "$(git config user.name) <$(git config user.email)>"'
> > +$ git config trailer.sign.cmd 'echo "$(git config user.name) <$(git config user.email)>"'
> >  $ git interpret-trailers <<EOF
> >  > EOF
>
> This change would definitely be needed when the support for
> ".command" is removed after deprecation period.  As it does not take
> any argument, .cmd and .command should behave identically, so making
> this change now, without waiting, may make sense.

By the way the above example is an example of why we might want any
configured command to be executed at least once, even when no
corresponding '<token>=<value>' argument is passed on the command
line.

> > @@ -333,14 +343,14 @@ subject
> >  Fix #42
> >  ------------
> >
> > -* Configure a 'see' trailer with a command to show the subject of a
> > +* Configure a 'see' trailer with a cmd to show the subject of a
> >    commit that is related, and show how it works:
> >  +
> >  ------------
> >  $ git config trailer.see.key "See-also: "
> >  $ git config trailer.see.ifExists "replace"
> >  $ git config trailer.see.ifMissing "doNothing"
> > -$ git config trailer.see.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG"
> > +$ git config trailer.see.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \"\$1\"|| true "
> >  $ git interpret-trailers <<EOF
> >  > subject
>
> This, too, but until ".command" is removed, wouldn't it be better
> for readers to keep both variants, as the distinction between $ARG
> and $1 needs to be illustrated?
>
> Besides, the examples given here are not equivalent.  The original
> assumes that ARG is there, or it is OK to default to HEAD; the new
> one gives no output when $ARG/$1 is not supplied.

Yeah, I agree they are not equivalent.

> It would confuse
> readers to give two too-similar-but-subtly-different examles, as
> they will be forced to wonder if the difference is something needed
> to transition from .command to .cmd (and I am guessing that it is
> not).

I agree.

> Rewriting both to use "--pretty=reference" may be worth doing.  As
> can be seen in these examples:
>
> git show -s --pretty=reference \$1
> git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$1
>
> that it makes the result much easier to read.

Yeah, thanks for the good suggestion.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-27 19:53         ` Christian Couder
@ 2021-03-28 10:46           ` ZheNing Hu
  2021-03-29  9:04             ` Christian Couder
  0 siblings, 1 reply; 60+ messages in thread
From: ZheNing Hu @ 2021-03-28 10:46 UTC (permalink / raw)
  To: Christian Couder; +Cc: Junio C Hamano, ZheNing Hu via GitGitGadget, git

Christian Couder <christian.couder@gmail.com> 于2021年3月28日周日 上午3:53写道:
>
> On Sat, Mar 27, 2021 at 7:04 PM Junio C Hamano <gitster@pobox.com> wrote:
> >
> > "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
> >
> > > @@ -252,6 +252,16 @@ also be executed for each of these arguments. And the <value> part of
> > >  these arguments, if any, will be used to replace the `$ARG` string in
> > >  the command.
> > >
> > > +trailer.<token>.cmd::
> > > +     The command specified by this configuration variable is run
> > > +     with a single parameter, which is the <value> part of an
> > > +     existing trailer with the same <token>.  The output from the
> > > +     command is then used as the value for the <token> in the
> > > +     resulting trailer.
> > > +     The command is expected to replace `trailer.<token>.cmd`.
>
> s/trailer.<token>.cmd/trailer.<token>.command/
>
> > > +     When both .cmd and .command are given for the same <token>,
> > > +        .cmd is used and .command is ignored.
> >
> > Christian, because ".cmd" is trying to eventually replace it, I find
> > it a bit disturbing that the description we give here looks a lot
> > smaller compared to the one for ".command".  I am afraid that we may
> > have failed to reproduce something important from the description of
> > the ".command" for the above; care to rend a hand or two here to
> > complete the description?
>
> Yeah, sure. I just saw that you already asked about this in this
> thread. Sorry for not answering earlier.
>
> > As I cannot grok what the description for ".command" is trying to
> > say, especially around this part:
> >
> >     When this option is specified, the behavior is as if a special
> >     '<token>=<value>' argument were added at the beginning of the command
> >     line, where <value> is ...
>
> This is because when a number of trailers are passed on the command
> line, and some other trailers are in the input file, the order in
> which the different trailers are processed and their priorities can be
> important. So by saying the above, people can get an idea about at
> which point and with which priority a trailer coming from such a
> config option will be processed.
>

This shows that .command itself has the characteristic of alwaysRun:
even if <token> <value> is not specified, the shell in .command will be
executed at least once, $ARG is empty by default. This is why I asked
`log --author=$ARG -1` will show the last commit identity when `--trailer`
 is not used.

> > and
> >
> >     If some '<token>=<value>' arguments are also passed on the command
> >     line, when a 'trailer.<token>.command' is configured, the command will
> >     also be executed for each of these arguments.
>
> Yeah, this means that when a 'trailer.foo.command' is configured, it
> is always executed at least once. The first time it is executed, it is
> passed nothing ($ARG is replaced with the empty string). Then for each
> 'foo=<value>' argument passed on the command line, it is executed once
> more with $ARG replaced by <value>.
>
> The reason it is always executed first with $ARG replaced with the
> empty string is that this way it makes it possible to set up commands
> that will always be executed when `git interpret-trailers` is run.
> This makes it possible to automatically add some trailers if they are
> missing for example.
>

Yes, $ARG or $1 are always exist because of:

               arg = xstrdup("");

so I think maybe we don't even need this judge in `apply_command`?
+               if (arg)
+                       strvec_push(&cp.args, arg);

> Another way to do it would be to have another config option called
> `trailer.<token>.alwaysRunCmd` to tell if the cmd specified by
> `trailer.<token>.cmd` should be run even if no '<token>=<value>'
> argument is passed on the command line. As we are introducing
> `trailer.<token>.cmd`, it's a good time to wonder if this would be a
> better design. But this issue is quite complex, because of the fact
> that 'trailer.<token>.ifMissing' and 'trailer.<token>.ifExists' also
> take a part in deciding if the command will be run.
>

In fact, I would prefer this design, because if I don’t add any trailers,
the trailer.<token>.command I set will be executed, which may be very
distressing sometimes, and `alwayRunCmd` is the user I hope that "trailers"
can be added automatically, and other trailers.<token>.command will not be
executed automatically. This allows the user to reasonably configure the
commands that need to be executed. This must be a very comfortable thing.

But as you said, to disable the automatic addition in the original .command
and use the new .alwaysRunCmd, I’m afraid there are a lot of things to consider.
Perhaps future series of patches can be considered to do it.

> This mechanism is the reason why a trick, when setting up a
> 'trailer.foo.command' trailer, is to also set 'trailer.foo.ifexists'
> to "replace", so that the first time the command is run (with $ARG
> replaced with the empty string) it will add a foo trailer with a
> default value, and if it is run another time, because a 'foo=bar'
> argument is passed on the command line, then the trailer with the
> default value will be replaced by the value computed from running the
> command again with $ARG replaced with "bar".
>
> Another trick is to have the command output nothing when $ARG is the
> empty string along with using --trim-empty. This way the command will
> create an empty trailer, when it is run the first time, and if it's
> not another time, then this empty trailer will be removed because of
> --trim-empty.
>

It looks very practical indeed.

> > I cannot quite judge if what we came up with in the above
> > description is sufficient.
>
> I don't think it's sufficient. I think that, while we are at it, a bit
> more thinking/discussion is required to make sure we want to keep the
> same design as 'trailer.<token>.command'.

Sure. I agree that more discussion is needed.
I think if the documents that once belonged to .command are copied to .cmd,
will the readers be too burdensome to read them? Will it be better to migrate
its documentation until we completely delete .command?

>
> > > -* Configure a 'sign' trailer with a command to automatically add a
> > > +* Configure a 'sign' trailer with a cmd to automatically add a
> > >    'Signed-off-by: ' with the author information only if there is no
> > >    'Signed-off-by: ' already, and show how it works:
> > >  +
> > > @@ -309,7 +319,7 @@ $ git interpret-trailers --trailer 'Cc: Alice <alice@example.com>' --trailer 'Re
> > >  $ git config trailer.sign.key "Signed-off-by: "
> > >  $ git config trailer.sign.ifmissing add
> > >  $ git config trailer.sign.ifexists doNothing
> > > -$ git config trailer.sign.command 'echo "$(git config user.name) <$(git config user.email)>"'
> > > +$ git config trailer.sign.cmd 'echo "$(git config user.name) <$(git config user.email)>"'
> > >  $ git interpret-trailers <<EOF
> > >  > EOF
> >
> > This change would definitely be needed when the support for
> > ".command" is removed after deprecation period.  As it does not take
> > any argument, .cmd and .command should behave identically, so making
> > this change now, without waiting, may make sense.
>
> By the way the above example is an example of why we might want any
> configured command to be executed at least once, even when no
> corresponding '<token>=<value>' argument is passed on the command
> line.

Already noticed that.

>
> > > @@ -333,14 +343,14 @@ subject
> > >  Fix #42
> > >  ------------
> > >
> > > -* Configure a 'see' trailer with a command to show the subject of a
> > > +* Configure a 'see' trailer with a cmd to show the subject of a
> > >    commit that is related, and show how it works:
> > >  +
> > >  ------------
> > >  $ git config trailer.see.key "See-also: "
> > >  $ git config trailer.see.ifExists "replace"
> > >  $ git config trailer.see.ifMissing "doNothing"
> > > -$ git config trailer.see.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG"
> > > +$ git config trailer.see.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \"\$1\"|| true "
> > >  $ git interpret-trailers <<EOF
> > >  > subject
> >
> > This, too, but until ".command" is removed, wouldn't it be better
> > for readers to keep both variants, as the distinction between $ARG
> > and $1 needs to be illustrated?

So the correct solution should be to keep the original .command Examples,
and then give the .cmd examples again.

> >
> > Besides, the examples given here are not equivalent.  The original
> > assumes that ARG is there, or it is OK to default to HEAD; the new
> > one gives no output when $ARG/$1 is not supplied.
>
> Yeah, I agree they are not equivalent.
>
> > It would confuse
> > readers to give two too-similar-but-subtly-different examles, as
> > they will be forced to wonder if the difference is something needed
> > to transition from .command to .cmd (and I am guessing that it is
> > not).
>
> I agree.

OK...I will modify it.

>
> > Rewriting both to use "--pretty=reference" may be worth doing.  As
> > can be seen in these examples:
> >
> > git show -s --pretty=reference \$1
> > git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$1
> >
> > that it makes the result much easier to read.
>
> Yeah, thanks for the good suggestion.

Yes, `--pretty=reference` is similar to `--format="%h(%s)"` and provides better
readability.

Thanks,Junio and Christian!

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-28 10:46           ` ZheNing Hu
@ 2021-03-29  9:04             ` Christian Couder
  2021-03-29 13:43               ` ZheNing Hu
  0 siblings, 1 reply; 60+ messages in thread
From: Christian Couder @ 2021-03-29  9:04 UTC (permalink / raw)
  To: ZheNing Hu; +Cc: Junio C Hamano, ZheNing Hu via GitGitGadget, git

On Sun, Mar 28, 2021 at 12:46 PM ZheNing Hu <adlternative@gmail.com> wrote:
>
> Christian Couder <christian.couder@gmail.com> 于2021年3月28日周日 上午3:53写道:
> >
> > On Sat, Mar 27, 2021 at 7:04 PM Junio C Hamano <gitster@pobox.com> wrote:

> > > As I cannot grok what the description for ".command" is trying to
> > > say, especially around this part:
> > >
> > >     When this option is specified, the behavior is as if a special
> > >     '<token>=<value>' argument were added at the beginning of the command
> > >     line, where <value> is ...
> >
> > This is because when a number of trailers are passed on the command
> > line, and some other trailers are in the input file, the order in
> > which the different trailers are processed and their priorities can be
> > important. So by saying the above, people can get an idea about at
> > which point and with which priority a trailer coming from such a
> > config option will be processed.
>
> This shows that .command itself has the characteristic of alwaysRun:
> even if <token> <value> is not specified, the shell in .command will be
> executed at least once, $ARG is empty by default. This is why I asked
> `log --author=$ARG -1` will show the last commit identity when `--trailer`
>  is not used.

Yeah, that's the reason.

> > > and
> > >
> > >     If some '<token>=<value>' arguments are also passed on the command
> > >     line, when a 'trailer.<token>.command' is configured, the command will
> > >     also be executed for each of these arguments.
> >
> > Yeah, this means that when a 'trailer.foo.command' is configured, it
> > is always executed at least once. The first time it is executed, it is
> > passed nothing ($ARG is replaced with the empty string). Then for each
> > 'foo=<value>' argument passed on the command line, it is executed once
> > more with $ARG replaced by <value>.
> >
> > The reason it is always executed first with $ARG replaced with the
> > empty string is that this way it makes it possible to set up commands
> > that will always be executed when `git interpret-trailers` is run.
> > This makes it possible to automatically add some trailers if they are
> > missing for example.
>
> Yes, $ARG or $1 are always exist because of:
>
>                arg = xstrdup("");
>
> so I think maybe we don't even need this judge in `apply_command`?
> +               if (arg)
> +                       strvec_push(&cp.args, arg);

Yeah, I haven't looked at the code, but that might be a good
simplification. If you work on this, please submit it in a separate
commit.

> > Another way to do it would be to have another config option called
> > `trailer.<token>.alwaysRunCmd` to tell if the cmd specified by
> > `trailer.<token>.cmd` should be run even if no '<token>=<value>'
> > argument is passed on the command line. As we are introducing
> > `trailer.<token>.cmd`, it's a good time to wonder if this would be a
> > better design. But this issue is quite complex, because of the fact
> > that 'trailer.<token>.ifMissing' and 'trailer.<token>.ifExists' also
> > take a part in deciding if the command will be run.

Actually after thinking about it, I think it might be better, instead
of `trailer.<token>.alwaysRunCmd`, to add something like
`trailer.<token>.runMode` that could take multiple values like:

- "beforeCLI": would make it run once, like ".command" does now before
any CLI trailer are processed

- "forEachCLIToken": would make it run once for each trailer that has
the token, like ".command" also does now, the difference would be that
the value for the token would be passed in the $1 argument

- "afterCLI": would make it run once after all the CLI trailers have
been processed and it could pass the different values for the token if
any in different arguments: $1, $2, $3, ...

This would make it possible to extend later if the need arises for
more different times or ways to run configured commands.

> In fact, I would prefer this design, because if I don’t add any trailers,
> the trailer.<token>.command I set will be executed, which may be very
> distressing sometimes, and `alwayRunCmd` is the user I hope that "trailers"
> can be added automatically, and other trailers.<token>.command will not be
> executed automatically. This allows the user to reasonably configure the
> commands that need to be executed. This must be a very comfortable thing.

I agree that it should be easier and more straightforward, than it is
now, to configure this.

> But as you said, to disable the automatic addition in the original .command
> and use the new .alwaysRunCmd, I’m afraid there are a lot of things to consider.
> Perhaps future series of patches can be considered to do it.

Yeah, support for `trailer.<token>.runMode` might be added in
different commits at least and possibly later in a different patch
series. There are the following issues to resolve, though, if we want
to focus only on a new ".cmd" config option:

- how and when should it run by default,
- how to explain that in the doc, and maybe
- how to improve the current description of what happens for ".command"

> > This mechanism is the reason why a trick, when setting up a
> > 'trailer.foo.command' trailer, is to also set 'trailer.foo.ifexists'
> > to "replace", so that the first time the command is run (with $ARG
> > replaced with the empty string) it will add a foo trailer with a
> > default value, and if it is run another time, because a 'foo=bar'
> > argument is passed on the command line, then the trailer with the
> > default value will be replaced by the value computed from running the
> > command again with $ARG replaced with "bar".
> >
> > Another trick is to have the command output nothing when $ARG is the
> > empty string along with using --trim-empty. This way the command will
> > create an empty trailer, when it is run the first time, and if it's
> > not another time, then this empty trailer will be removed because of
> > --trim-empty.
> >
>
> It looks very practical indeed.
>
> > > I cannot quite judge if what we came up with in the above
> > > description is sufficient.
> >
> > I don't think it's sufficient. I think that, while we are at it, a bit
> > more thinking/discussion is required to make sure we want to keep the
> > same design as 'trailer.<token>.command'.
>
> Sure. I agree that more discussion is needed.
> I think if the documents that once belonged to .command are copied to .cmd,
> will the readers be too burdensome to read them? Will it be better to migrate
> its documentation until we completely delete .command?

My opinion (if we focus only on adding ".cmd") is that:

- for simplicity for now it should run at the same time as ".command",
the only difference being how the argument is passed (using $1 instead
of textually replacing $ARG)
- the doc for ".command" should be first improved if possible, and
then moved over to ".cmd" saying for ".command" that ".command" is
deprecated in favor of ".cmd" but otherwise works as ".cmd" except
that instead using $1 the value is passed by textually replacing $ARG
which could be a safety and correctness issue.

Another way to work on all this, would be to first work on adding
support for `trailer.<token>.runMode` and on improving existing
documentation, and then to add ".cmd", which could then by default use
a different ".runMode" than ".command".

> > > This, too, but until ".command" is removed, wouldn't it be better
> > > for readers to keep both variants, as the distinction between $ARG
> > > and $1 needs to be illustrated?
>
> So the correct solution should be to keep the original .command Examples,
> and then give the .cmd examples again.

Maybe we could take advantage of ".cmd" to show other nice
possibilities to use all of this. Especially if support for `git
commit --trailer ...` is already merged, we might be able to use it in
those examples, or perhaps add some examples to the git commit doc.

Best,
Christian.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-29  9:04             ` Christian Couder
@ 2021-03-29 13:43               ` ZheNing Hu
  2021-03-30  8:45                 ` Christian Couder
  0 siblings, 1 reply; 60+ messages in thread
From: ZheNing Hu @ 2021-03-29 13:43 UTC (permalink / raw)
  To: Christian Couder; +Cc: Junio C Hamano, ZheNing Hu via GitGitGadget, git

Christian Couder <christian.couder@gmail.com> 于2021年3月29日周一 下午5:05写道:
>
> >
> > Yes, $ARG or $1 are always exist because of:
> >
> >                arg = xstrdup("");
> >
> > so I think maybe we don't even need this judge in `apply_command`?
> > +               if (arg)
> > +                       strvec_push(&cp.args, arg);
>
> Yeah, I haven't looked at the code, but that might be a good
> simplification. If you work on this, please submit it in a separate
> commit.
>

Well, if necessary, I'll put it in another commit, maybe I should double check
to see if there's anything special going on.

> > > Another way to do it would be to have another config option called
> > > `trailer.<token>.alwaysRunCmd` to tell if the cmd specified by
> > > `trailer.<token>.cmd` should be run even if no '<token>=<value>'
> > > argument is passed on the command line. As we are introducing
> > > `trailer.<token>.cmd`, it's a good time to wonder if this would be a
> > > better design. But this issue is quite complex, because of the fact
> > > that 'trailer.<token>.ifMissing' and 'trailer.<token>.ifExists' also
> > > take a part in deciding if the command will be run.
>
> Actually after thinking about it, I think it might be better, instead
> of `trailer.<token>.alwaysRunCmd`, to add something like
> `trailer.<token>.runMode` that could take multiple values like:
>

If really can achieve it is certainly better than 'alwaysRunCmd'.
The following three small configuration options look delicious.
But I think it needs to be discussed in more detail:

> - "beforeCLI": would make it run once, like ".command" does now before
> any CLI trailer are processed
>

Does "beforeCLI" handle all trailers? Or is it just doing something to add empty
value trailers?

> - "forEachCLIToken": would make it run once for each trailer that has
> the token, like ".command" also does now, the difference would be that
> the value for the token would be passed in the $1 argument
>

This is exactly same as before.

> - "afterCLI": would make it run once after all the CLI trailers have
> been processed and it could pass the different values for the token if
> any in different arguments: $1, $2, $3, ...
>

I might get a little confused here: What's the input for $1,$2,$3?
Is users more interested in dealing with trailers value or a line of the
trailer?

> This would make it possible to extend later if the need arises for
> more different times or ways to run configured commands.
>
> > In fact, I would prefer this design, because if I don’t add any trailers,
> > the trailer.<token>.command I set will be executed, which may be very
> > distressing sometimes, and `alwayRunCmd` is the user I hope that "trailers"
> > can be added automatically, and other trailers.<token>.command will not be
> > executed automatically. This allows the user to reasonably configure the
> > commands that need to be executed. This must be a very comfortable thing.
>
> I agree that it should be easier and more straightforward, than it is
> now, to configure this.
>
> > But as you said, to disable the automatic addition in the original .command
> > and use the new .alwaysRunCmd, I’m afraid there are a lot of things to consider.
> > Perhaps future series of patches can be considered to do it.
>
> Yeah, support for `trailer.<token>.runMode` might be added in
> different commits at least and possibly later in a different patch
> series. There are the following issues to resolve, though, if we want
> to focus only on a new ".cmd" config option:
>
> - how and when should it run by default,

Do you mean that ".cmd" can get rid of the ".command" auto-add problem
in this patch series?
This might be a good idea if I can add the three modes you mentioned above
in the later patch series.

> - how to explain that in the doc, and maybe
> - how to improve the current description of what happens for ".command"
>
> > > This mechanism is the reason why a trick, when setting up a
> > > 'trailer.foo.command' trailer, is to also set 'trailer.foo.ifexists'
> > > to "replace", so that the first time the command is run (with $ARG
> > > replaced with the empty string) it will add a foo trailer with a
> > > default value, and if it is run another time, because a 'foo=bar'
> > > argument is passed on the command line, then the trailer with the
> > > default value will be replaced by the value computed from running the
> > > command again with $ARG replaced with "bar".
> > >
> > > Another trick is to have the command output nothing when $ARG is the
> > > empty string along with using --trim-empty. This way the command will
> > > create an empty trailer, when it is run the first time, and if it's
> > > not another time, then this empty trailer will be removed because of
> > > --trim-empty.
> > >
> >
> > It looks very practical indeed.
> >
> > > > I cannot quite judge if what we came up with in the above
> > > > description is sufficient.
> > >
> > > I don't think it's sufficient. I think that, while we are at it, a bit
> > > more thinking/discussion is required to make sure we want to keep the
> > > same design as 'trailer.<token>.command'.
> >
> > Sure. I agree that more discussion is needed.
> > I think if the documents that once belonged to .command are copied to .cmd,
> > will the readers be too burdensome to read them? Will it be better to migrate
> > its documentation until we completely delete .command?
>
> My opinion (if we focus only on adding ".cmd") is that:
>
> - for simplicity for now it should run at the same time as ".command",
> the only difference being how the argument is passed (using $1 instead
> of textually replacing $ARG)
> - the doc for ".command" should be first improved if possible, and
> then moved over to ".cmd" saying for ".command" that ".command" is
> deprecated in favor of ".cmd" but otherwise works as ".cmd" except
> that instead using $1 the value is passed by textually replacing $ARG
> which could be a safety and correctness issue.
>

I agree with you. There may be need some discretion.

> Another way to work on all this, would be to first work on adding
> support for `trailer.<token>.runMode` and on improving existing
> documentation, and then to add ".cmd", which could then by default use
> a different ".runMode" than ".command".
>

I think the task can be put off until April.
Deal with the easier ".cmd" first.

> > > > This, too, but until ".command" is removed, wouldn't it be better
> > > > for readers to keep both variants, as the distinction between $ARG
> > > > and $1 needs to be illustrated?
> >
> > So the correct solution should be to keep the original .command Examples,
> > and then give the .cmd examples again.
>
> Maybe we could take advantage of ".cmd" to show other nice
> possibilities to use all of this. Especially if support for `git
> commit --trailer ...` is already merged, we might be able to use it in
> those examples, or perhaps add some examples to the git commit doc.
>

Oh, the 'commit --trailer' may still be queuing, It may take a while.

> Best,
> Christian.

Thanks.

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-29 13:43               ` ZheNing Hu
@ 2021-03-30  8:45                 ` Christian Couder
  2021-03-30 11:22                   ` ZheNing Hu
  0 siblings, 1 reply; 60+ messages in thread
From: Christian Couder @ 2021-03-30  8:45 UTC (permalink / raw)
  To: ZheNing Hu; +Cc: Junio C Hamano, ZheNing Hu via GitGitGadget, git

On Mon, Mar 29, 2021 at 3:44 PM ZheNing Hu <adlternative@gmail.com> wrote:
>
> Christian Couder <christian.couder@gmail.com> 于2021年3月29日周一 下午5:05写道:
> >
> > >
> > > Yes, $ARG or $1 are always exist because of:
> > >
> > >                arg = xstrdup("");
> > >
> > > so I think maybe we don't even need this judge in `apply_command`?
> > > +               if (arg)
> > > +                       strvec_push(&cp.args, arg);
> >
> > Yeah, I haven't looked at the code, but that might be a good
> > simplification. If you work on this, please submit it in a separate
> > commit.
>
> Well, if necessary, I'll put it in another commit, maybe I should double check
> to see if there's anything special going on.
>
> > > > Another way to do it would be to have another config option called
> > > > `trailer.<token>.alwaysRunCmd` to tell if the cmd specified by
> > > > `trailer.<token>.cmd` should be run even if no '<token>=<value>'
> > > > argument is passed on the command line. As we are introducing
> > > > `trailer.<token>.cmd`, it's a good time to wonder if this would be a
> > > > better design. But this issue is quite complex, because of the fact
> > > > that 'trailer.<token>.ifMissing' and 'trailer.<token>.ifExists' also
> > > > take a part in deciding if the command will be run.
> >
> > Actually after thinking about it, I think it might be better, instead
> > of `trailer.<token>.alwaysRunCmd`, to add something like
> > `trailer.<token>.runMode` that could take multiple values like:
>
> If really can achieve it is certainly better than 'alwaysRunCmd'.
> The following three small configuration options look delicious.
> But I think it needs to be discussed in more detail:
>
> > - "beforeCLI": would make it run once, like ".command" does now before
> > any CLI trailer are processed
>
> Does "beforeCLI" handle all trailers? Or is it just doing something to add empty
> value trailers?

I am not sure what you mean by "handle all trailers". What I mean is
that it would just work like ".command" does right now before the
"--trailers ..." options are processed.

Let's suppose the "trailer.foo.command" config option is set to "bar".
Then the "bar" command will be run just before the "--trailers ..."
options are processed and the output of that, let's say "baz" will be
used to add a new "foo: baz" trailer to the ouput of `git
interpret-trailers`.

For example:

-------
$ git -c trailer.foo.command='echo baz' interpret-trailers<<EOF
EOF

foo: baz
-------

In other words an empty value trailer is just a special case when the
command that is run does not output anything. But such commands are
expected to output something not trivial at least in some cases.

See also the example in the doc that uses:

$ git config trailer.sign.command 'echo "$(git config user.name)
<$(git config user.email)>"'

> > - "forEachCLIToken": would make it run once for each trailer that has
> > the token, like ".command" also does now, the difference would be that
> > the value for the token would be passed in the $1 argument
>
> This is exactly same as before.

Yeah it is the same as before when the "--trailers ..." options are
processed, but not before that.

To get exactly the same as before one would need to configure both
"beforeCLI" _and_ "forEachCLIToken", for example like this (note that
we use "--add" when adding "forEachCLIToken"):

$ git config trailer.foo.runMode beforeCLI
$ git config --add trailer.foo.runMode forEachCLIToken
$ git config -l | grep foo
trailer.foo.runmode=beforeCLI
trailer.foo.runmode=forEachCLIToken

> > - "afterCLI": would make it run once after all the CLI trailers have
> > been processed and it could pass the different values for the token if
> > any in different arguments: $1, $2, $3, ...
>
> I might get a little confused here: What's the input for $1,$2,$3?

The input would be the different values that are used for the token in
the "--trailer ..." CLI arguments.

For (an hypothetical) example:

------
$ git config trailer.foo.runMode afterCLI
$ git config trailer.foo.cmd 'echo $@'
$ git interpret-trailers --trailer foo=a --trailer foo=b --trailer foo=c<<EOF
EOF

foo: a b c
$ git interpret-trailers<<EOF
EOF

foo:
------

I am not sure "afterCLI" would be useful, but we might not want to
implement it right now. It's just an example to show that we could add
other modes to run the configured ".cmd" (and maybe ".command" too).

> Is users more interested in dealing with trailers value or a line of the
> trailer?

I am not sure what you mean here. If "a line of the trailer" means a
trailer that is already in the input file that is passed to `git
interpret-trailers`, and if "trailers value" means a "--trailer ..."
argument, then I would say that users could be interested in dealing
with both.

It's true that right now the command configured by a ".command" is not
run when `git interpret-trailers` processes in input file that
contains a trailer with the corresponding token. So new values for
".runMode" could be implemented to make that happen.

> > > But as you said, to disable the automatic addition in the original .command
> > > and use the new .alwaysRunCmd, I’m afraid there are a lot of things to consider.
> > > Perhaps future series of patches can be considered to do it.
> >
> > Yeah, support for `trailer.<token>.runMode` might be added in
> > different commits at least and possibly later in a different patch
> > series. There are the following issues to resolve, though, if we want
> > to focus only on a new ".cmd" config option:
> >
> > - how and when should it run by default,
>
> Do you mean that ".cmd" can get rid of the ".command" auto-add problem
> in this patch series?

I am not sure what you mean with "auto-add". Do you mean that fact
that the ".command" runs once before the CLI "--trailer ..." options
are processed?

> This might be a good idea if I can add the three modes you mentioned above
> in the later patch series.

I like that your are interested in improving trailer handling in Git,
but I must say that if you intend to apply for the GSoC, you might
want to work on your application document first, as it will need to be
discussed on the mailing list too and it will take some time. You are
also free to work on this too, but that shouldn't be your priority.

By the way if this (or another Git related) subject is more
interesting to you than the project ideas we propose on
https://git.github.io/SoC-2021-Ideas/, then you are welcome to write a
proposal about working on this (improving trailer handling) rather
than on a project idea from that page. You might want to make sure
that some people would be willing to (co-)mentor you working on it
though.

[...]

> > Another way to work on all this, would be to first work on adding
> > support for `trailer.<token>.runMode` and on improving existing
> > documentation, and then to add ".cmd", which could then by default use
> > a different ".runMode" than ".command".
>
> I think the task can be put off until April.
> Deal with the easier ".cmd" first.

Ok for me, but see above about GSoC application.


> > > > > This, too, but until ".command" is removed, wouldn't it be better
> > > > > for readers to keep both variants, as the distinction between $ARG
> > > > > and $1 needs to be illustrated?
> > >
> > > So the correct solution should be to keep the original .command Examples,
> > > and then give the .cmd examples again.
> >
> > Maybe we could take advantage of ".cmd" to show other nice
> > possibilities to use all of this. Especially if support for `git
> > commit --trailer ...` is already merged, we might be able to use it in
> > those examples, or perhaps add some examples to the git commit doc.
>
> Oh, the 'commit --trailer' may still be queuing, It may take a while.

You might want to check if it needs another reroll or if there are
other reasons (like no reviews) why it's not listed in the last
"What's cooking ..." email from Junio. If you think it is ready and
has been forgotten, you can ping reviewers (including me), to ask them
to review it one more time, or Junio if the last version you sent has
already been reviewed.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-30  8:45                 ` Christian Couder
@ 2021-03-30 11:22                   ` ZheNing Hu
  2021-03-30 15:07                     ` ZheNing Hu
  0 siblings, 1 reply; 60+ messages in thread
From: ZheNing Hu @ 2021-03-30 11:22 UTC (permalink / raw)
  To: Christian Couder; +Cc: Junio C Hamano, ZheNing Hu via GitGitGadget, git

Christian Couder <christian.couder@gmail.com> 于2021年3月30日周二 下午4:45写道:
>
> On Mon, Mar 29, 2021 at 3:44 PM ZheNing Hu <adlternative@gmail.com> wrote:
> >
> > Christian Couder <christian.couder@gmail.com> 于2021年3月29日周一 下午5:05写道:
> > >
> > > >
> > > > Yes, $ARG or $1 are always exist because of:
> > > >
> > > >                arg = xstrdup("");
> > > >
> > > > so I think maybe we don't even need this judge in `apply_command`?
> > > > +               if (arg)
> > > > +                       strvec_push(&cp.args, arg);
> > >
> > > Yeah, I haven't looked at the code, but that might be a good
> > > simplification. If you work on this, please submit it in a separate
> > > commit.
> >
> > Well, if necessary, I'll put it in another commit, maybe I should double check
> > to see if there's anything special going on.
> >
> > > > > Another way to do it would be to have another config option called
> > > > > `trailer.<token>.alwaysRunCmd` to tell if the cmd specified by
> > > > > `trailer.<token>.cmd` should be run even if no '<token>=<value>'
> > > > > argument is passed on the command line. As we are introducing
> > > > > `trailer.<token>.cmd`, it's a good time to wonder if this would be a
> > > > > better design. But this issue is quite complex, because of the fact
> > > > > that 'trailer.<token>.ifMissing' and 'trailer.<token>.ifExists' also
> > > > > take a part in deciding if the command will be run.
> > >
> > > Actually after thinking about it, I think it might be better, instead
> > > of `trailer.<token>.alwaysRunCmd`, to add something like
> > > `trailer.<token>.runMode` that could take multiple values like:
> >
> > If really can achieve it is certainly better than 'alwaysRunCmd'.
> > The following three small configuration options look delicious.
> > But I think it needs to be discussed in more detail:
> >
> > > - "beforeCLI": would make it run once, like ".command" does now before
> > > any CLI trailer are processed
> >
> > Does "beforeCLI" handle all trailers? Or is it just doing something to add empty
> > value trailers?
>
> I am not sure what you mean by "handle all trailers". What I mean is
> that it would just work like ".command" does right now before the
> "--trailers ..." options are processed.
>
> Let's suppose the "trailer.foo.command" config option is set to "bar".
> Then the "bar" command will be run just before the "--trailers ..."
> options are processed and the output of that, let's say "baz" will be
> used to add a new "foo: baz" trailer to the ouput of `git
> interpret-trailers`.
>
> For example:
>
> -------
> $ git -c trailer.foo.command='echo baz' interpret-trailers<<EOF
> EOF
>
> foo: baz
> -------
>
> In other words an empty value trailer is just a special case when the
> command that is run does not output anything. But such commands are
> expected to output something not trivial at least in some cases.
>
> See also the example in the doc that uses:
>
> $ git config trailer.sign.command 'echo "$(git config user.name)
> <$(git config user.email)>"'
>

I see what you mean, which is to provide a default value for any
trailers that haven't been run command yet.

> > > - "forEachCLIToken": would make it run once for each trailer that has
> > > the token, like ".command" also does now, the difference would be that
> > > the value for the token would be passed in the $1 argument
> >
> > This is exactly same as before.
>
> Yeah it is the same as before when the "--trailers ..." options are
> processed, but not before that.
>
> To get exactly the same as before one would need to configure both
> "beforeCLI" _and_ "forEachCLIToken", for example like this (note that
> we use "--add" when adding "forEachCLIToken"):
>
> $ git config trailer.foo.runMode beforeCLI
> $ git config --add trailer.foo.runMode forEachCLIToken
> $ git config -l | grep foo
> trailer.foo.runmode=beforeCLI
> trailer.foo.runmode=forEachCLIToken
>
> > > - "afterCLI": would make it run once after all the CLI trailers have
> > > been processed and it could pass the different values for the token if
> > > any in different arguments: $1, $2, $3, ...
> >
> > I might get a little confused here: What's the input for $1,$2,$3?
>
> The input would be the different values that are used for the token in
> the "--trailer ..." CLI arguments.
>
> For (an hypothetical) example:
>
> ------
> $ git config trailer.foo.runMode afterCLI
> $ git config trailer.foo.cmd 'echo $@'
> $ git interpret-trailers --trailer foo=a --trailer foo=b --trailer foo=c<<EOF
> EOF
>
> foo: a b c
> $ git interpret-trailers<<EOF
> EOF
>
> foo:
> ------
>
> I am not sure "afterCLI" would be useful, but we might not want to
> implement it right now. It's just an example to show that we could add
> other modes to run the configured ".cmd" (and maybe ".command" too).
>

Yes, not so useful.

> > Is users more interested in dealing with trailers value or a line of the
> > trailer?
>
> I am not sure what you mean here. If "a line of the trailer" means a
> trailer that is already in the input file that is passed to `git
> interpret-trailers`, and if "trailers value" means a "--trailer ..."
> argument, then I would say that users could be interested in dealing
> with both.
>

Sorry, I mean after we running those command, a line trailer is
"foo: bar" and trailers value will be "bar".

> It's true that right now the command configured by a ".command" is not
> run when `git interpret-trailers` processes in input file that
> contains a trailer with the corresponding token. So new values for
> ".runMode" could be implemented to make that happen.
>

Sure.

> > > > But as you said, to disable the automatic addition in the original .command
> > > > and use the new .alwaysRunCmd, I’m afraid there are a lot of things to consider.
> > > > Perhaps future series of patches can be considered to do it.
> > >
> > > Yeah, support for `trailer.<token>.runMode` might be added in
> > > different commits at least and possibly later in a different patch
> > > series. There are the following issues to resolve, though, if we want
> > > to focus only on a new ".cmd" config option:
> > >
> > > - how and when should it run by default,
> >
> > Do you mean that ".cmd" can get rid of the ".command" auto-add problem
> > in this patch series?
>
> I am not sure what you mean with "auto-add". Do you mean that fact
> that the ".command" runs once before the CLI "--trailer ..." options
> are processed?
>

I'm talking about the empty values $ARG passing to the user's command,
those command  at least run once, You say "how and when should it run by
default", I was wondering if I could not run .cmd without passing trailer.

> > This might be a good idea if I can add the three modes you mentioned above
> > in the later patch series.
>
> I like that your are interested in improving trailer handling in Git,
> but I must say that if you intend to apply for the GSoC, you might
> want to work on your application document first, as it will need to be
> discussed on the mailing list too and it will take some time. You are
> also free to work on this too, but that shouldn't be your priority.
>

In fact, I had written the proposal carefully.
I have been studying what went wrong with OIga's improvement of cat-file
recently.

I may have thought of some ideas, and has been written in Proposal,
I will submit it in about two days :)

> By the way if this (or another Git related) subject is more
> interesting to you than the project ideas we propose on
> https://git.github.io/SoC-2021-Ideas/, then you are welcome to write a
> proposal about working on this (improving trailer handling) rather
> than on a project idea from that page. You might want to make sure
> that some people would be willing to (co-)mentor you working on it
> though.
>

Aha, for the time being, you are the most suitable mentor,
But I might just take improvement of `interpret-tarilers` as my interest to
do something. I will choice the project of "git cat-file" .

> [...]
>
> > > Another way to work on all this, would be to first work on adding
> > > support for `trailer.<token>.runMode` and on improving existing
> > > documentation, and then to add ".cmd", which could then by default use
> > > a different ".runMode" than ".command".
> >
> > I think the task can be put off until April.
> > Deal with the easier ".cmd" first.
>
> Ok for me, but see above about GSoC application.
>
>
> > > > > > This, too, but until ".command" is removed, wouldn't it be better
> > > > > > for readers to keep both variants, as the distinction between $ARG
> > > > > > and $1 needs to be illustrated?
> > > >
> > > > So the correct solution should be to keep the original .command Examples,
> > > > and then give the .cmd examples again.
> > >
> > > Maybe we could take advantage of ".cmd" to show other nice
> > > possibilities to use all of this. Especially if support for `git
> > > commit --trailer ...` is already merged, we might be able to use it in
> > > those examples, or perhaps add some examples to the git commit doc.
> >
> > Oh, the 'commit --trailer' may still be queuing, It may take a while.
>
> You might want to check if it needs another reroll or if there are
> other reasons (like no reviews) why it's not listed in the last
> "What's cooking ..." email from Junio. If you think it is ready and
> has been forgotten, you can ping reviewers (including me), to ask them
> to review it one more time, or Junio if the last version you sent has
> already been reviewed.

It should still be in "seen" inheritance, Junio is advancing it.
Maybe you think it has something to improve, please feel free to tell me.

In addition, I now found a small bug in ".cmd",

git config -l |grep bug
trailer.bug.key=bug-descibe:
trailer.bug.ifexists=replace
trailer.bug.cmd=echo 123

see what will happen:

git interpret-trailers --trailer="bug:text" <<-EOF
`heredocd> EOF

bug-descibe:123 text

"text" seem print to stdout.

I'm looking at what's going on here.

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-30 11:22                   ` ZheNing Hu
@ 2021-03-30 15:07                     ` ZheNing Hu
  2021-03-30 17:14                       ` Junio C Hamano
  0 siblings, 1 reply; 60+ messages in thread
From: ZheNing Hu @ 2021-03-30 15:07 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Christian Couder, ZheNing Hu via GitGitGadget, git

Hi, Junio,

ZheNing Hu <adlternative@gmail.com> 于2021年3月30日周二 下午7:22写道:
>
> In addition, I now found a small bug in ".cmd",
>
> git config -l |grep bug
> trailer.bug.key=bug-descibe:
> trailer.bug.ifexists=replace
> trailer.bug.cmd=echo 123
>
> see what will happen:
>
> git interpret-trailers --trailer="bug:text" <<-EOF
> `heredocd> EOF
>
> bug-descibe:123 text
>
> "text" seem print to stdout.
>
> I'm looking at what's going on here.
>

Here I may need to think with you whether it is reasonable to pass "$1".

I found that we passed the parameters in the above situation like this:

(gdb) print cp.args.v[0]
$7 = 0x5555558f4e20 "echo \"123\""
(gdb) print cp.args.v[1]
$8 = 0x5555558ee150 "text"

At this time, our idea is base on that v[0] will be the content of the shell,
and v[1] will be the $1 of the shell.

But in fact, git handles shell subprocesses in a special way:

The `prepare_shell_cmd()` in "run-command.c" seem to use "$@" to pass
shell args.

Before exec:

(gdb) print argv.v[1]
$22 = 0x5555558edfd0 "/bin/sh"
(gdb) print argv.v[2]
$23 = 0x5555558f4c80 "-c"
(gdb) print argv.v[3]
$24 = 0x5555558ed4b0 "echo \"123\" \"$@\""
(gdb) print argv.v[4]
$25 = 0x5555558f5980 "echo \"123\""
(gdb) print argv.v[5]
$26 = 0x5555558edab0 "abc"
(gdb) print argv.v[6]
$27 = 0x0

Some unexpected things happened here.
Maybe "abc" was wrongly used as the parameter of "echo"?
Looking forward to your reply.

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-30 15:07                     ` ZheNing Hu
@ 2021-03-30 17:14                       ` Junio C Hamano
  2021-03-31  5:14                         ` ZheNing Hu
  0 siblings, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2021-03-30 17:14 UTC (permalink / raw)
  To: ZheNing Hu; +Cc: Christian Couder, ZheNing Hu via GitGitGadget, git

ZheNing Hu <adlternative@gmail.com> writes:

> The `prepare_shell_cmd()` in "run-command.c" seem to use "$@" to pass
> shell args.

Yes. "$@" is a way to write "$1" "$2" "$3"...
Since you are passing only one, 

	echo "$@"

and

	echo "$1"

would be the equivalent.

I am not sure what program you fed to the gdb (and remote debugging
over e-mail is not my forte ;-), but let's see.

> Before exec:
>
> (gdb) print argv.v[1]
> $22 = 0x5555558edfd0 "/bin/sh"
> (gdb) print argv.v[2]
> $23 = 0x5555558f4c80 "-c"
> (gdb) print argv.v[3]
> $24 = 0x5555558ed4b0 "echo \"123\" \"$@\""
> (gdb) print argv.v[4]
> $25 = 0x5555558f5980 "echo \"123\""
> (gdb) print argv.v[5]
> $26 = 0x5555558edab0 "abc"
> (gdb) print argv.v[6]
> $27 = 0x0
>
> Some unexpected things happened here.
> Maybe "abc" was wrongly used as the parameter of "echo"?
> Looking forward to your reply.

Observe

	$ sh -c '
		echo "\$0 == $0"
		count=0
		for arg in "$@"
		do
			count=$(( $count + 1 ))
			echo "\$$count == $arg"
		done
	' 0 1 2
	$0 == 0
	$1 == 1
	$2 == 2

i.e. the first arg after

	argv[1] = "/bin/sh"
        argv[2] = "-c"
	argv[3] = "script"

is used to give the script the name of the program ($0).  Are we
getting hit by this common confusion?

It is customery to write such an invocation with '-' as the "name of
the program" thing, so that ordinary positional parameters are
available starting at $1, not $0, like so:

	sh -c 'script' - arg1 arg2 ...

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-30 17:14                       ` Junio C Hamano
@ 2021-03-31  5:14                         ` ZheNing Hu
  2021-03-31 18:19                           ` Junio C Hamano
  0 siblings, 1 reply; 60+ messages in thread
From: ZheNing Hu @ 2021-03-31  5:14 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Christian Couder, ZheNing Hu via GitGitGadget, git

Junio C Hamano <gitster@pobox.com> 于2021年3月31日周三 上午1:14写道:
>
> ZheNing Hu <adlternative@gmail.com> writes:
>
> > The `prepare_shell_cmd()` in "run-command.c" seem to use "$@" to pass
> > shell args.
>
> Yes. "$@" is a way to write "$1" "$2" "$3"...
> Since you are passing only one,
>
>         echo "$@"
>
> and
>
>         echo "$1"
>
> would be the equivalent.
>
> I am not sure what program you fed to the gdb (and remote debugging
> over e-mail is not my forte ;-), but let's see.
>



> > Before exec:
> >
> > (gdb) print argv.v[1]
> > $22 = 0x5555558edfd0 "/bin/sh"
> > (gdb) print argv.v[2]
> > $23 = 0x5555558f4c80 "-c"
> > (gdb) print argv.v[3]
> > $24 = 0x5555558ed4b0 "echo \"123\" \"$@\""
> > (gdb) print argv.v[4]
> > $25 = 0x5555558f5980 "echo \"123\""
> > (gdb) print argv.v[5]
> > $26 = 0x5555558edab0 "abc"
> > (gdb) print argv.v[6]
> > $27 = 0x0
> >
> > Some unexpected things happened here.
> > Maybe "abc" was wrongly used as the parameter of "echo"?
> > Looking forward to your reply.
>
> Observe
>
>         $ sh -c '
>                 echo "\$0 == $0"
>                 count=0
>                 for arg in "$@"
>                 do
>                         count=$(( $count + 1 ))
>                         echo "\$$count == $arg"
>                 done
>         ' 0 1 2
>         $0 == 0
>         $1 == 1
>         $2 == 2
>
> i.e. the first arg after
>
>         argv[1] = "/bin/sh"
>         argv[2] = "-c"
>         argv[3] = "script"
>
> is used to give the script the name of the program ($0).  Are we
> getting hit by this common confusion?
>
> It is customery to write such an invocation with '-' as the "name of
> the program" thing, so that ordinary positional parameters are
> available starting at $1, not $0, like so:
>
>         sh -c 'script' - arg1 arg2 ...

The configuration is like this:
trailer.bug.key=BUG:
trailer.bug.ifexists=add
trailer.bug.cmd=echo "123"

And use:

$ git interpret-trailers --trailer="bug:456" --trailer="bug:789"<<-EOF
EOF

BUG: 123
BUG: 123 456
BUG: 123 789

I just want three "BUG: 123", but "456" and "789" appeared...

In fact, I think about this problem like this way:
When we execute a child process that runs the shell,
the function`prepare_shell_cmd()` will actively add "$@" to the end of our
shell command when we have more than zero args ,

e.g.

"echo \"123\"" "abc"

will turn to

 "echo \"123\" \"$@\"" "echo \"123\"" "abc"

Normally, $@ should not cause any problems because it passes arguments
to the script what we provide.

But now, what we actually want is take any $1 that appears in the script as an
argument, the automatically added $@ causes $1 to be implicitly included.
And the original $ARG does not have this problem, Or if we pass environment
variables, this kind of problem will not occur.

Or If we want to avoid this problem, should we add one new options in
`struct child_process` , such as: "shell_no_implicit_args" , let git not add
 extra "$@" before we run the shell script?

Thanks.

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v5 0/2] [GSOC]trailer: pass arg as positional parameter
  2021-03-26 16:13     ` [PATCH v4] " ZheNing Hu via GitGitGadget
  2021-03-27 18:04       ` Junio C Hamano
@ 2021-03-31 10:05       ` ZheNing Hu via GitGitGadget
  2021-03-31 10:05         ` [PATCH v5 1/2] [GSOC] run-command: add shell_no_implicit_args option ZheNing Hu via GitGitGadget
                           ` (3 more replies)
  1 sibling, 4 replies; 60+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-31 10:05 UTC (permalink / raw)
  To: git; +Cc: Christian Couder, Junio C Hamano, ZheNing Hu

In https://lore.kernel.org/git/xmqqv99i4ck2.fsf@gitster.g/ Junio and
Christian talked about the problem of using strbuf_replace() to replace
$ARG.

Now pass trailer value as $1 to the trailer command with another
trailer.<token>.cmd config.

ZheNing Hu (2):
  [GSOC] run-command: add shell_no_implicit_args option
  [GSOC]trailer: pass arg as positional parameter

 Documentation/git-interpret-trailers.txt | 75 ++++++++++++++++++++----
 run-command.c                            |  8 +--
 run-command.h                            |  1 +
 t/t7513-interpret-trailers.sh            | 61 ++++++++++++++++++-
 trailer.c                                | 38 ++++++++----
 5 files changed, 157 insertions(+), 26 deletions(-)


base-commit: 142430338477d9d1bb25be66267225fb58498d92
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-913%2Fadlternative%2Ftrailer-pass-ARG-env-v5
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-913/adlternative/trailer-pass-ARG-env-v5
Pull-Request: https://github.com/gitgitgadget/git/pull/913

Range-diff vs v4:

 -:  ------------ > 1:  4c59cab53a0d [GSOC] run-command: add shell_no_implicit_args option
 1:  e2bbdcb943c2 ! 2:  5894d8c4b364 [GSOC]trailer: pass arg as positional parameter
     @@ Commit message
          scripts.
      
          Helped-by: Junio C Hamano <gitster@pobox.com>
     +    Helped-by: Christian Couder <christian.couder@gmail.com>
          Signed-off-by: ZheNing Hu <adlternative@gmail.com>
      
       ## Documentation/git-interpret-trailers.txt ##
     -@@ Documentation/git-interpret-trailers.txt: also be executed for each of these arguments. And the <value> part of
     - these arguments, if any, will be used to replace the `$ARG` string in
     - the command.
     - 
     +@@ Documentation/git-interpret-trailers.txt: trailer.<token>.command::
     + 	be called to automatically add or modify a trailer with the
     + 	specified <token>.
     + +
     +-When this option is specified, the behavior is as if a special
     +-'<token>=<value>' argument were added at the beginning of the command
     +-line, where <value> is taken to be the standard output of the
     +-specified command with any leading and trailing whitespace trimmed
     +-off.
     ++When this option is specified, the first occurrence of substring $ARG is
     ++replaced with the value given to the `interpret-trailer` command for the
     ++same token.
     + +
     +-If the command contains the `$ARG` string, this string will be
     +-replaced with the <value> part of an existing trailer with the same
     +-<token>, if any, before the command is launched.
     ++".command" has been deprecated due to the $ARG in the user's command can
     ++only be replaced once and the original way of replacing $ARG was not safe.
     ++Now the preferred option is using "trailer.<token>.cmd", which use position
     ++argument to pass the value.
     +++
     ++When both .cmd and .command are given for the same <token>,
     ++.cmd is used and .command is ignored.
     ++
      +trailer.<token>.cmd::
      +	The command specified by this configuration variable is run
      +	with a single parameter, which is the <value> part of an
      +	existing trailer with the same <token>.  The output from the
      +	command is then used as the value for the <token> in the
      +	resulting trailer.
     -+	The command is expected to replace `trailer.<token>.cmd`.
     -+	When both .cmd and .command are given for the same <token>,
     -+        .cmd is used and .command is ignored.
     -+
     - EXAMPLES
     - --------
     - 
     -@@ Documentation/git-interpret-trailers.txt: $ git format-patch -1
     - $ git interpret-trailers --trailer 'Cc: Alice <alice@example.com>' --trailer 'Reviewed-by: Bob <bob@example.com>' 0001-foo.patch >0001-bar.patch
     - ------------
     - 
     --* Configure a 'sign' trailer with a command to automatically add a
     -+* Configure a 'sign' trailer with a cmd to automatically add a
     -   'Signed-off-by: ' with the author information only if there is no
     -   'Signed-off-by: ' already, and show how it works:
     +++
     ++When this option is specified, If there is no trailer with same <token>,
     ++the behavior is as if a special '<token>=<value>' argument were added at
     ++the beginning of the command, <value> will be passed to the user's
     ++command as an empty value.
       +
     -@@ Documentation/git-interpret-trailers.txt: $ git interpret-trailers --trailer 'Cc: Alice <alice@example.com>' --trailer 'Re
     - $ git config trailer.sign.key "Signed-off-by: "
     - $ git config trailer.sign.ifmissing add
     - $ git config trailer.sign.ifexists doNothing
     --$ git config trailer.sign.command 'echo "$(git config user.name) <$(git config user.email)>"'
     -+$ git config trailer.sign.cmd 'echo "$(git config user.name) <$(git config user.email)>"'
     - $ git interpret-trailers <<EOF
     - > EOF
     + If some '<token>=<value>' arguments are also passed on the command
     + line, when a 'trailer.<token>.command' is configured, the command will
     + also be executed for each of these arguments. And the <value> part of
     +-these arguments, if any, will be used to replace the `$ARG` string in
     +-the command.
     ++these arguments, if any, will be passed to the command as first parameter.
       
     + EXAMPLES
     + --------
      @@ Documentation/git-interpret-trailers.txt: subject
       Fix #42
       ------------
       
     --* Configure a 'see' trailer with a command to show the subject of a
      +* Configure a 'see' trailer with a cmd to show the subject of a
     ++  commit that is related, and show how it works:
     +++
     ++------------
     ++$ git config trailer.see.key "See-also: "
     ++$ git config trailer.see.ifExists "replace"
     ++$ git config trailer.see.ifMissing "doNothing"
     ++$ git config trailer.see.cmd "git show -s --pretty=reference \"\$1\""
     ++$ git interpret-trailers <<EOF
     ++> subject
     ++> 
     ++> message
     ++> 
     ++> see: HEAD~2
     ++> EOF
     ++subject
     ++
     ++message
     ++
     ++See-also: fe3187489d69c4 (subject of related commit, 2021-3-20)
     ++------------
     ++
     ++* Configure a 'bug' trailer with a cmd to show when and where
     ++  was the bug introduced, and show how it works:
     +++
     ++------------
     ++$ git config trailer.bug.key "Bug-from: "
     ++$ git config trailer.bug.ifExists "replace"
     ++$ git config trailer.bug.cmd "git log --grep \"\$1\" -1 --pretty=\"%h %aD\""
     ++$ git interpret-trailers --trailer="bug:the information manager from hell" <<EOF
     ++> subject
     ++> 
     ++> message
     ++> 
     ++> EOF
     ++subject
     ++
     ++message
     ++
     ++Bug-from: 57d84f8d93 Mon, 6 Aug 2012 18:27:09 +0700
     ++------------
     ++
     + * Configure a 'see' trailer with a command to show the subject of a
         commit that is related, and show how it works:
       +
     - ------------
     - $ git config trailer.see.key "See-also: "
     - $ git config trailer.see.ifExists "replace"
     - $ git config trailer.see.ifMissing "doNothing"
     --$ git config trailer.see.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG"
     -+$ git config trailer.see.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \"\$1\"|| true "
     - $ git interpret-trailers <<EOF
     - > subject
     - > 
      
       ## t/t7513-interpret-trailers.sh ##
     +@@ t/t7513-interpret-trailers.sh: test_expect_success 'setup' '
     + 	EOF
     + '
     + 
     ++test_expect_success 'with cmd' '
     ++	test_when_finished "git config --unset trailer.bug.key && \
     ++	git config --unset trailer.bug.ifExists && \
     ++	git config --unset trailer.bug.cmd" &&
     ++	git config trailer.bug.key "Bug-maker: " &&
     ++	git config trailer.bug.ifExists "add" &&
     ++	git config trailer.bug.cmd "echo \"\$@\"" &&
     ++	cat >>expected2 <<-EOF &&
     ++
     ++	Bug-maker: 
     ++	Bug-maker: jocker
     ++	Bug-maker: batman
     ++	EOF
     ++	git interpret-trailers --trailer "bug: jocker" --trailer "bug:batman" \
     ++		>actual2 &&
     ++	test_cmp expected2 actual2
     ++'
     ++
     + test_expect_success 'without config' '
     + 	sed -e "s/ Z\$/ /" >expected <<-\EOF &&
     + 
      @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup a commit' '
       	git commit -m "Add file a.txt"
       '
     @@ trailer.c: static int check_if_different(struct trailer_item *in_tok,
      -
      -	strvec_push(&cp.args, cmd.buf);
      +	if (conf->cmd) {
     ++		cp.shell_no_implicit_args = 1;
      +		strbuf_addstr(&cmd, conf->cmd);
      +		strvec_push(&cp.args, cmd.buf);
      +		if (arg)
     @@ trailer.c: static int check_if_different(struct trailer_item *in_tok,
       	cp.env = local_repo_env;
       	cp.no_stdin = 1;
       	cp.use_shell = 1;
     +-	cp.shell_no_implicit_args = 1;
     + 
     + 	if (capture_command(&cp, &buf, 1024)) {
     + 		error(_("running trailer command '%s' failed"), cmd.buf);
      @@ trailer.c: static char *apply_command(const char *command, const char *arg)
       
       static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)

-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v5 1/2] [GSOC] run-command: add shell_no_implicit_args option
  2021-03-31 10:05       ` [PATCH v5 0/2] " ZheNing Hu via GitGitGadget
@ 2021-03-31 10:05         ` ZheNing Hu via GitGitGadget
  2021-04-01  7:22           ` Christian Couder
  2021-03-31 10:05         ` [PATCH v5 2/2] [GSOC]trailer: pass arg as positional parameter ZheNing Hu via GitGitGadget
                           ` (2 subsequent siblings)
  3 siblings, 1 reply; 60+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-31 10:05 UTC (permalink / raw)
  To: git; +Cc: Christian Couder, Junio C Hamano, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

When we use subprocess to run a shell-script, if we have any
args, git will add extra $@ to the end of the shell-script,
This can pass positional parameters correctly, But if we just
want to use some of these passed parameters, git will still
add an extra "$@", which contains all positional parameters we
passed. This does not meet our expectations.

E.g. our shell-script is:
"echo \"\$1\""
and pass $1 "abc", git will change our script to:
"echo \"\$1\" \"$@\""

The positional parameters we entered will be printed
repeatedly. So let add a new `shell_no_implicit_args`
to `struct child_process`, which can suppress the
joining of $@ if `shell_no_implicit_args` is set to 1,
this will allow us to use only few of positional args
in multi-parameter shell script, instead of using all
of them.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
 run-command.c | 8 ++++----
 run-command.h | 1 +
 trailer.c     | 1 +
 3 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/run-command.c b/run-command.c
index be6bc128cd9d..a2cf6177f522 100644
--- a/run-command.c
+++ b/run-command.c
@@ -264,7 +264,7 @@ int sane_execvp(const char *file, char * const argv[])
 	return -1;
 }
 
-static const char **prepare_shell_cmd(struct strvec *out, const char **argv)
+static const char **prepare_shell_cmd(struct strvec *out, const char **argv, int shell_no_implicit_args)
 {
 	if (!argv[0])
 		BUG("shell command is empty");
@@ -281,7 +281,7 @@ static const char **prepare_shell_cmd(struct strvec *out, const char **argv)
 		 * If we have no extra arguments, we do not even need to
 		 * bother with the "$@" magic.
 		 */
-		if (!argv[1])
+		if (!argv[1] || shell_no_implicit_args)
 			strvec_push(out, argv[0]);
 		else
 			strvec_pushf(out, "%s \"$@\"", argv[0]);
@@ -416,7 +416,7 @@ static int prepare_cmd(struct strvec *out, const struct child_process *cmd)
 	if (cmd->git_cmd) {
 		prepare_git_cmd(out, cmd->argv);
 	} else if (cmd->use_shell) {
-		prepare_shell_cmd(out, cmd->argv);
+		prepare_shell_cmd(out, cmd->argv, cmd->shell_no_implicit_args);
 	} else {
 		strvec_pushv(out, cmd->argv);
 	}
@@ -929,7 +929,7 @@ int start_command(struct child_process *cmd)
 	if (cmd->git_cmd)
 		cmd->argv = prepare_git_cmd(&nargv, cmd->argv);
 	else if (cmd->use_shell)
-		cmd->argv = prepare_shell_cmd(&nargv, cmd->argv);
+		cmd->argv = prepare_shell_cmd(&nargv, cmd->argv, cmd->shell_no_implicit_args);
 
 	cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, (char**) cmd->env,
 			cmd->dir, fhin, fhout, fherr);
diff --git a/run-command.h b/run-command.h
index d08414a92e73..9597c987c5bb 100644
--- a/run-command.h
+++ b/run-command.h
@@ -133,6 +133,7 @@ struct child_process {
 	 * argv[1], etc, do not need to be shell-quoted.
 	 */
 	unsigned use_shell:1;
+	unsigned shell_no_implicit_args:1;
 
 	unsigned stdout_to_stderr:1;
 	unsigned clean_on_exit:1;
diff --git a/trailer.c b/trailer.c
index be4e9726421c..35dd0f4c8512 100644
--- a/trailer.c
+++ b/trailer.c
@@ -231,6 +231,7 @@ static char *apply_command(const char *command, const char *arg)
 	cp.env = local_repo_env;
 	cp.no_stdin = 1;
 	cp.use_shell = 1;
+	cp.shell_no_implicit_args = 1;
 
 	if (capture_command(&cp, &buf, 1024)) {
 		error(_("running trailer command '%s' failed"), cmd.buf);
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v5 2/2] [GSOC]trailer: pass arg as positional parameter
  2021-03-31 10:05       ` [PATCH v5 0/2] " ZheNing Hu via GitGitGadget
  2021-03-31 10:05         ` [PATCH v5 1/2] [GSOC] run-command: add shell_no_implicit_args option ZheNing Hu via GitGitGadget
@ 2021-03-31 10:05         ` ZheNing Hu via GitGitGadget
  2021-04-01  7:28         ` [PATCH v5 0/2] " Christian Couder
  2021-04-02 13:26         ` [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option ZheNing Hu via GitGitGadget
  3 siblings, 0 replies; 60+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-03-31 10:05 UTC (permalink / raw)
  To: git; +Cc: Christian Couder, Junio C Hamano, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name of or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token.  This has two downsides:

* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.

* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatching single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor), it would result in
a broken command that is not syntactically correct (or
worse).

Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as a
parameter to the command.  Instead of "$ARG", the users will
refer to the value as positional argument, $1, in their
scripts.

Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
 Documentation/git-interpret-trailers.txt | 75 ++++++++++++++++++++----
 t/t7513-interpret-trailers.sh            | 61 ++++++++++++++++++-
 trailer.c                                | 39 ++++++++----
 3 files changed, 152 insertions(+), 23 deletions(-)

diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 96ec6499f001..bbd1c9bfd65e 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -236,21 +236,34 @@ trailer.<token>.command::
 	be called to automatically add or modify a trailer with the
 	specified <token>.
 +
-When this option is specified, the behavior is as if a special
-'<token>=<value>' argument were added at the beginning of the command
-line, where <value> is taken to be the standard output of the
-specified command with any leading and trailing whitespace trimmed
-off.
+When this option is specified, the first occurrence of substring $ARG is
+replaced with the value given to the `interpret-trailer` command for the
+same token.
 +
-If the command contains the `$ARG` string, this string will be
-replaced with the <value> part of an existing trailer with the same
-<token>, if any, before the command is launched.
+".command" has been deprecated due to the $ARG in the user's command can
+only be replaced once and the original way of replacing $ARG was not safe.
+Now the preferred option is using "trailer.<token>.cmd", which use position
+argument to pass the value.
++
+When both .cmd and .command are given for the same <token>,
+.cmd is used and .command is ignored.
+
+trailer.<token>.cmd::
+	The command specified by this configuration variable is run
+	with a single parameter, which is the <value> part of an
+	existing trailer with the same <token>.  The output from the
+	command is then used as the value for the <token> in the
+	resulting trailer.
++
+When this option is specified, If there is no trailer with same <token>,
+the behavior is as if a special '<token>=<value>' argument were added at
+the beginning of the command, <value> will be passed to the user's
+command as an empty value.
 +
 If some '<token>=<value>' arguments are also passed on the command
 line, when a 'trailer.<token>.command' is configured, the command will
 also be executed for each of these arguments. And the <value> part of
-these arguments, if any, will be used to replace the `$ARG` string in
-the command.
+these arguments, if any, will be passed to the command as first parameter.
 
 EXAMPLES
 --------
@@ -333,6 +346,48 @@ subject
 Fix #42
 ------------
 
+* Configure a 'see' trailer with a cmd to show the subject of a
+  commit that is related, and show how it works:
++
+------------
+$ git config trailer.see.key "See-also: "
+$ git config trailer.see.ifExists "replace"
+$ git config trailer.see.ifMissing "doNothing"
+$ git config trailer.see.cmd "git show -s --pretty=reference \"\$1\""
+$ git interpret-trailers <<EOF
+> subject
+> 
+> message
+> 
+> see: HEAD~2
+> EOF
+subject
+
+message
+
+See-also: fe3187489d69c4 (subject of related commit, 2021-3-20)
+------------
+
+* Configure a 'bug' trailer with a cmd to show when and where
+  was the bug introduced, and show how it works:
++
+------------
+$ git config trailer.bug.key "Bug-from: "
+$ git config trailer.bug.ifExists "replace"
+$ git config trailer.bug.cmd "git log --grep \"\$1\" -1 --pretty=\"%h %aD\""
+$ git interpret-trailers --trailer="bug:the information manager from hell" <<EOF
+> subject
+> 
+> message
+> 
+> EOF
+subject
+
+message
+
+Bug-from: 57d84f8d93 Mon, 6 Aug 2012 18:27:09 +0700
+------------
+
 * Configure a 'see' trailer with a command to show the subject of a
   commit that is related, and show how it works:
 +
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 6602790b5f4c..aec240f1dc05 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -51,6 +51,24 @@ test_expect_success 'setup' '
 	EOF
 '
 
+test_expect_success 'with cmd' '
+	test_when_finished "git config --unset trailer.bug.key && \
+	git config --unset trailer.bug.ifExists && \
+	git config --unset trailer.bug.cmd" &&
+	git config trailer.bug.key "Bug-maker: " &&
+	git config trailer.bug.ifExists "add" &&
+	git config trailer.bug.cmd "echo \"\$@\"" &&
+	cat >>expected2 <<-EOF &&
+
+	Bug-maker: 
+	Bug-maker: jocker
+	Bug-maker: batman
+	EOF
+	git interpret-trailers --trailer "bug: jocker" --trailer "bug:batman" \
+		>actual2 &&
+	test_cmp expected2 actual2
+'
+
 test_expect_success 'without config' '
 	sed -e "s/ Z\$/ /" >expected <<-\EOF &&
 
@@ -1274,9 +1292,50 @@ test_expect_success 'setup a commit' '
 	git commit -m "Add file a.txt"
 '
 
+test_expect_success 'with cmd and $1' '
+	test_when_finished "git config --unset trailer.fix.cmd" &&
+	git config trailer.fix.ifExists "replace" &&
+	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \"\$1\" || true" &&
+	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
+	cat complex_message_body >expected2 &&
+	sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
+		Fixes: $FIXED
+		Acked-by= Z
+		Reviewed-by:
+		Signed-off-by: Z
+		Signed-off-by: A U Thor <author@example.com>
+	EOF
+	git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+		<complex_message >actual2 &&
+	test_cmp expected2 actual2
+'
+
+test_expect_success 'cmd takes precedence over command' '
+	test_when_finished "git config --unset trailer.fix.cmd" &&
+	git config trailer.fix.ifExists "replace" &&
+	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
+		--abbrev-commit --abbrev=14 \"\$1\" || true" &&
+	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \$ARG" &&
+	FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
+	cat complex_message_body >expected2 &&
+	sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
+		Fixes: $FIXED
+		Acked-by= Z
+		Reviewed-by:
+		Signed-off-by: Z
+		Signed-off-by: A U Thor <author@example.com>
+	EOF
+	git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+		<complex_message >actual2 &&
+	test_cmp expected2 actual2
+'
+
 test_expect_success 'with command using $ARG' '
 	git config trailer.fix.ifExists "replace" &&
-	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \$ARG" &&
 	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-EOF &&
diff --git a/trailer.c b/trailer.c
index 35dd0f4c8512..a000293d6e7e 100644
--- a/trailer.c
+++ b/trailer.c
@@ -14,6 +14,7 @@ struct conf_info {
 	char *name;
 	char *key;
 	char *command;
+	char *cmd;
 	enum trailer_where where;
 	enum trailer_if_exists if_exists;
 	enum trailer_if_missing if_missing;
@@ -127,6 +128,7 @@ static void free_arg_item(struct arg_item *item)
 	free(item->conf.name);
 	free(item->conf.key);
 	free(item->conf.command);
+	free(item->conf.cmd);
 	free(item->token);
 	free(item->value);
 	free(item);
@@ -216,22 +218,28 @@ static int check_if_different(struct trailer_item *in_tok,
 	return 1;
 }
 
-static char *apply_command(const char *command, const char *arg)
+static char *apply_command(struct conf_info *conf, const char *arg)
 {
 	struct strbuf cmd = STRBUF_INIT;
 	struct strbuf buf = STRBUF_INIT;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	char *result;
 
-	strbuf_addstr(&cmd, command);
-	if (arg)
-		strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
-
-	strvec_push(&cp.args, cmd.buf);
+	if (conf->cmd) {
+		cp.shell_no_implicit_args = 1;
+		strbuf_addstr(&cmd, conf->cmd);
+		strvec_push(&cp.args, cmd.buf);
+		if (arg)
+			strvec_push(&cp.args, arg);
+	} else if (conf->command) {
+		strbuf_addstr(&cmd, conf->command);
+		strvec_push(&cp.args, cmd.buf);
+		if (arg)
+			strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
+	}
 	cp.env = local_repo_env;
 	cp.no_stdin = 1;
 	cp.use_shell = 1;
-	cp.shell_no_implicit_args = 1;
 
 	if (capture_command(&cp, &buf, 1024)) {
 		error(_("running trailer command '%s' failed"), cmd.buf);
@@ -248,7 +256,7 @@ static char *apply_command(const char *command, const char *arg)
 
 static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)
 {
-	if (arg_tok->conf.command) {
+	if (arg_tok->conf.command || arg_tok->conf.cmd) {
 		const char *arg;
 		if (arg_tok->value && arg_tok->value[0]) {
 			arg = arg_tok->value;
@@ -258,7 +266,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg
 			else
 				arg = xstrdup("");
 		}
-		arg_tok->value = apply_command(arg_tok->conf.command, arg);
+		arg_tok->value = apply_command(&arg_tok->conf, arg);
 		free((char *)arg);
 	}
 }
@@ -431,6 +439,7 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
 	dst->name = xstrdup_or_null(src->name);
 	dst->key = xstrdup_or_null(src->key);
 	dst->command = xstrdup_or_null(src->command);
+	dst->cmd = xstrdup_or_null(src->cmd);
 }
 
 static struct arg_item *get_conf_item(const char *name)
@@ -455,8 +464,8 @@ static struct arg_item *get_conf_item(const char *name)
 	return item;
 }
 
-enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_WHERE,
-			 TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
+enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_CMD,
+			TRAILER_WHERE, TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
 
 static struct {
 	const char *name;
@@ -464,6 +473,7 @@ static struct {
 } trailer_config_items[] = {
 	{ "key", TRAILER_KEY },
 	{ "command", TRAILER_COMMAND },
+	{ "cmd", TRAILER_CMD },
 	{ "where", TRAILER_WHERE },
 	{ "ifexists", TRAILER_IF_EXISTS },
 	{ "ifmissing", TRAILER_IF_MISSING }
@@ -543,6 +553,11 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
 			warning(_("more than one %s"), conf_key);
 		conf->command = xstrdup(value);
 		break;
+	case TRAILER_CMD:
+		if (conf->cmd)
+			warning(_("more than one %s"), conf_key);
+		conf->cmd = xstrdup(value);
+		break;
 	case TRAILER_WHERE:
 		if (trailer_set_where(&conf->where, value))
 			warning(_("unknown value '%s' for key '%s'"), value, conf_key);
@@ -709,7 +724,7 @@ static void process_command_line_args(struct list_head *arg_head,
 	/* Add an arg item for each configured trailer with a command */
 	list_for_each(pos, &conf_head) {
 		item = list_entry(pos, struct arg_item, list);
-		if (item->conf.command)
+		if (item->conf.cmd || item->conf.command)
 			add_arg_item(arg_head,
 				     xstrdup(token_from_item(item, NULL)),
 				     xstrdup(""),
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-31  5:14                         ` ZheNing Hu
@ 2021-03-31 18:19                           ` Junio C Hamano
  2021-03-31 18:29                             ` Junio C Hamano
  2021-04-01  3:39                             ` ZheNing Hu
  0 siblings, 2 replies; 60+ messages in thread
From: Junio C Hamano @ 2021-03-31 18:19 UTC (permalink / raw)
  To: ZheNing Hu; +Cc: Christian Couder, ZheNing Hu via GitGitGadget, git

ZheNing Hu <adlternative@gmail.com> writes:

> The configuration is like this:
> trailer.bug.key=BUG:
> trailer.bug.ifexists=add
> trailer.bug.cmd=echo "123"
>
> And use:
>
> $ git interpret-trailers --trailer="bug:456" --trailer="bug:789"<<-EOF
> EOF
>
> BUG: 123
> BUG: 123 456
> BUG: 123 789

I think that is quite expected.  You said the command to run is
'echo 123', and that is not "pick a directory $D on $PATH where
there is an executable '$D/echo 123' exists, and run that".  It
runs the given command with the shell, and in general that is
what we want for end-user supplied commands specified in the
configuration file [*1*].

So we form a shell command whose beginning is 'echo 123' and tuck
the argument after that command line, so it is understandable that
"echo 123 456" gets executed for "--trailer=bug:456".

I wasn't following the discussion between you and Christian closely
but I recall seeing him saying that the command is executed one
extra time without any arg before it is run for actual --trailer
requests with the value?  I am guessing that is where the first
output "BUG: 123" (without anything else) is coming from.


*1* Imagine .editor set to 'emacs -nw' or 'vim -f'; we do not want
    Git to find a directory on $PATH that has an executable whose
    name is 'emacs -nw' and run that file (i.e. give 'emacs -nw' as
    the first argument to execlp()).  Instead, you'd want to behave
    as if the user typed "emacs -nw", followed by any arguments we
    want to give to it (in .editor's case, the name of the file to
    be edited) properly quoted for the shell.

    And the way we do so is to form a moral equivalent of

	execlp("sh", "-c", "emacs -nw $@", ...);

    and put the arguments at the end where I wrote ... (we actually
    do so with execvp(), but illustrating with execlp() is easier to
    read and write---hence "a moral equivalent of").

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-31 18:19                           ` Junio C Hamano
@ 2021-03-31 18:29                             ` Junio C Hamano
  2021-04-01  3:56                               ` ZheNing Hu
  2021-04-01  3:39                             ` ZheNing Hu
  1 sibling, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2021-03-31 18:29 UTC (permalink / raw)
  To: ZheNing Hu; +Cc: Christian Couder, ZheNing Hu via GitGitGadget, git

Junio C Hamano <gitster@pobox.com> writes:

> ZheNing Hu <adlternative@gmail.com> writes:
>
>> The configuration is like this:
>> trailer.bug.key=BUG:
>> trailer.bug.ifexists=add
>> trailer.bug.cmd=echo "123"
>>
>> And use:
>>
>> $ git interpret-trailers --trailer="bug:456" --trailer="bug:789"<<-EOF
>> EOF
>>
>> BUG: 123
>> BUG: 123 456
>> BUG: 123 789
>
> I think that is quite expected.  You said the command to run is
> 'echo 123', and that is not "pick a directory $D on $PATH where
> there is an executable '$D/echo 123' exists, and run that".  It
> runs the given command with the shell, and in general that is
> what we want for end-user supplied commands specified in the
> configuration file [*1*].
> ...
> *1* Imagine .editor set to 'emacs -nw' or 'vim -f'; we do not want
>     Git to find a directory on $PATH that has an executable whose
>     name is 'emacs -nw' and run that file (i.e. give 'emacs -nw' as
>     the first argument to execlp()).  Instead, you'd want to behave
>     as if the user typed "emacs -nw", followed by any arguments we
>     want to give to it (in .editor's case, the name of the file to
>     be edited) properly quoted for the shell.
>
>     And the way we do so is to form a moral equivalent of
>
> 	execlp("sh", "-c", "emacs -nw \"$@\"", ...);
>
>     and put the arguments at the end where I wrote ... (we actually
>     do so with execvp(), but illustrating with execlp() is easier to
>     read and write---hence "a moral equivalent of").

So, learning from that .editor example, what you can do when you do
not want to take any parameter is to explicitly ignore them.  

Let's take the very basic form first.  Imagine you wrote a little
script and wanted to see three "123", ignoring end-user input after
"--trailer=bug:".

    .cmd = my-script 123

would run 'my-script "$@"'.  What should you write in my-script to
cause that happen?  Here is an example solution:

    #!/bin/sh
    echo 123

Notice that "$1" is completely ignored, even if the machinery that
drives .cmd makes three calls?

	sh -c 'my-script 123 "$@"'
	sh -c 'my-script 123 "$@"' 456
	sh -c 'my-script 123 "$@"' 789

The way to do the same without an extra script on disk is for you to
use sh-c yourself.

    .cmd = sh -c 'echo 123'

And if you do want to use $1, you can do the same.  E.g. if you want
to double them in the output, you'd probably do something like this:

    .cmd = sh -c 'echo "<$1 - $1>"'

You'd need to quote the value appropriately for the config file,
though.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-31 18:19                           ` Junio C Hamano
  2021-03-31 18:29                             ` Junio C Hamano
@ 2021-04-01  3:39                             ` ZheNing Hu
  1 sibling, 0 replies; 60+ messages in thread
From: ZheNing Hu @ 2021-04-01  3:39 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Christian Couder, ZheNing Hu via GitGitGadget, git

Junio C Hamano <gitster@pobox.com> 于2021年4月1日周四 上午2:20写道:
>
> ZheNing Hu <adlternative@gmail.com> writes:
>
> > The configuration is like this:
> > trailer.bug.key=BUG:
> > trailer.bug.ifexists=add
> > trailer.bug.cmd=echo "123"
> >
> > And use:
> >
> > $ git interpret-trailers --trailer="bug:456" --trailer="bug:789"<<-EOF
> > EOF
> >
> > BUG: 123
> > BUG: 123 456
> > BUG: 123 789
>
> I think that is quite expected.  You said the command to run is
> 'echo 123', and that is not "pick a directory $D on $PATH where
> there is an executable '$D/echo 123' exists, and run that".  It
> runs the given command with the shell, and in general that is
> what we want for end-user supplied commands specified in the
> configuration file [*1*].
>

I agree that if you want to use execv directly to execute a terminal
command, if arg[0] is something like "emacs -nw", error will be
reported:"No such file or directory". But by wrapping a layer of
"sh" "-c", The program name 'emacs' in 'emacs -nw' can be
found and executed normally.

> So we form a shell command whose beginning is 'echo 123' and tuck
> the argument after that command line, so it is understandable that
> "echo 123 456" gets executed for "--trailer=bug:456".
>
> I wasn't following the discussion between you and Christian closely
> but I recall seeing him saying that the command is executed one
> extra time without any arg before it is run for actual --trailer
> requests with the value?  I am guessing that is where the first
> output "BUG: 123" (without anything else) is coming from.
>

Exactly.
Each .command/.cmd willl executes this 'beforeCLI' operation  once,
I use ifexists='add' here just for seeing the effect, In general, we will
use ifexists='replace'.

>
> *1* Imagine .editor set to 'emacs -nw' or 'vim -f'; we do not want
>     Git to find a directory on $PATH that has an executable whose
>     name is 'emacs -nw' and run that file (i.e. give 'emacs -nw' as
>     the first argument to execlp()).  Instead, you'd want to behave
>     as if the user typed "emacs -nw", followed by any arguments we
>     want to give to it (in .editor's case, the name of the file to
>     be edited) properly quoted for the shell.
>
>     And the way we do so is to form a moral equivalent of
>
>         execlp("sh", "-c", "emacs -nw $@", ...);
>
>     and put the arguments at the end where I wrote ... (we actually
>     do so with execvp(), but illustrating with execlp() is easier to
>     read and write---hence "a moral equivalent of").

I can see the benefits of this.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-03-31 18:29                             ` Junio C Hamano
@ 2021-04-01  3:56                               ` ZheNing Hu
  2021-04-01 19:49                                 ` Junio C Hamano
  0 siblings, 1 reply; 60+ messages in thread
From: ZheNing Hu @ 2021-04-01  3:56 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Christian Couder, ZheNing Hu via GitGitGadget, git

Junio C Hamano <gitster@pobox.com> 于2021年4月1日周四 上午2:29写道:
>
> Junio C Hamano <gitster@pobox.com> writes:
>
> > ZheNing Hu <adlternative@gmail.com> writes:
> >
> >> The configuration is like this:
> >> trailer.bug.key=BUG:
> >> trailer.bug.ifexists=add
> >> trailer.bug.cmd=echo "123"
> >>
> >> And use:
> >>
> >> $ git interpret-trailers --trailer="bug:456" --trailer="bug:789"<<-EOF
> >> EOF
> >>
> >> BUG: 123
> >> BUG: 123 456
> >> BUG: 123 789
> >
> > I think that is quite expected.  You said the command to run is
> > 'echo 123', and that is not "pick a directory $D on $PATH where
> > there is an executable '$D/echo 123' exists, and run that".  It
> > runs the given command with the shell, and in general that is
> > what we want for end-user supplied commands specified in the
> > configuration file [*1*].
> > ...
> > *1* Imagine .editor set to 'emacs -nw' or 'vim -f'; we do not want
> >     Git to find a directory on $PATH that has an executable whose
> >     name is 'emacs -nw' and run that file (i.e. give 'emacs -nw' as
> >     the first argument to execlp()).  Instead, you'd want to behave
> >     as if the user typed "emacs -nw", followed by any arguments we
> >     want to give to it (in .editor's case, the name of the file to
> >     be edited) properly quoted for the shell.
> >
> >     And the way we do so is to form a moral equivalent of
> >
> >       execlp("sh", "-c", "emacs -nw \"$@\"", ...);
> >
> >     and put the arguments at the end where I wrote ... (we actually
> >     do so with execvp(), but illustrating with execlp() is easier to
> >     read and write---hence "a moral equivalent of").
>
> So, learning from that .editor example, what you can do when you do
> not want to take any parameter is to explicitly ignore them.
>
> Let's take the very basic form first.  Imagine you wrote a little
> script and wanted to see three "123", ignoring end-user input after
> "--trailer=bug:".
>
>     .cmd = my-script 123
>
> would run 'my-script "$@"'.  What should you write in my-script to
> cause that happen?  Here is an example solution:
>
>     #!/bin/sh
>     echo 123
>
> Notice that "$1" is completely ignored, even if the machinery that
> drives .cmd makes three calls?
>
>         sh -c 'my-script 123 "$@"'
>         sh -c 'my-script 123 "$@"' 456
>         sh -c 'my-script 123 "$@"' 789
>
> The way to do the same without an extra script on disk is for you to
> use sh-c yourself.
>
>     .cmd = sh -c 'echo 123'
>

This is indeed a viable solution, But the extra "sh -c" seems to put an
unnecessary burden on the user.
Sometimes I wonder, why not recommend using environment variables
like $ARG?

> And if you do want to use $1, you can do the same.  E.g. if you want
> to double them in the output, you'd probably do something like this:
>
>     .cmd = sh -c 'echo "<$1 - $1>"'
>
> You'd need to quote the value appropriately for the config file,
> though.

In fact, In the following example, trailer <value> contains whitespaces ,

$ git interpret-trailers --trailer="bug:the information manager from hell"

which can make it to work properly (But it's a little bit tedious):

$ git config trailer.bug.cmd "sh -c \"echo \\\"\$1\"\\\""
$ git interpret-trailers --trailer="bug:the information manager from hell"

Bug-from:
Bug-from: the information manager from hell

Is there an easier way?
Or can make the user ignore the details of "sh -c"?

Thanks.

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v5 1/2] [GSOC] run-command: add shell_no_implicit_args option
  2021-03-31 10:05         ` [PATCH v5 1/2] [GSOC] run-command: add shell_no_implicit_args option ZheNing Hu via GitGitGadget
@ 2021-04-01  7:22           ` Christian Couder
  2021-04-01  9:58             ` ZheNing Hu
  0 siblings, 1 reply; 60+ messages in thread
From: Christian Couder @ 2021-04-01  7:22 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget; +Cc: git, Junio C Hamano, ZheNing Hu

On Wed, Mar 31, 2021 at 12:05 PM ZheNing Hu via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> From: ZheNing Hu <adlternative@gmail.com>
>
> When we use subprocess to run a shell-script, if we have any

Maybe: s/subprocess/a subprocess/

> args, git will add extra $@ to the end of the shell-script,
> This can pass positional parameters correctly, But if we just
> want to use some of these passed parameters, git will still
> add an extra "$@", which contains all positional parameters we
> passed. This does not meet our expectations.

I am not sure explaining things using $@ is the best way to make this
as clear as possible. I don't have a clear alternative right now
though.

> E.g. our shell-script is:
> "echo \"\$1\""
> and pass $1 "abc",

Maybe: s/pass $1 "abc"/we pass "abc" as $1/

> git will change our script to:
> "echo \"\$1\" \"$@\""

Where will "abc" appear then?

> The positional parameters we entered will be printed
> repeatedly.

If you take us passing "abc" in $1 as an example, then I think it's a
good idea to show us the result of that.

> So let add a new `shell_no_implicit_args`

Maybe: s/`shell_no_implicit_args`/`shell_no_implicit_args` flag/

> to `struct child_process`, which can suppress the
> joining of $@ if `shell_no_implicit_args` is set to 1,
> this will allow us to use only few of positional args
> in multi-parameter shell script, instead of using all
> of them.

I think our goal is more to have each argument we pass be passed just once.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v5 0/2] [GSOC]trailer: pass arg as positional parameter
  2021-03-31 10:05       ` [PATCH v5 0/2] " ZheNing Hu via GitGitGadget
  2021-03-31 10:05         ` [PATCH v5 1/2] [GSOC] run-command: add shell_no_implicit_args option ZheNing Hu via GitGitGadget
  2021-03-31 10:05         ` [PATCH v5 2/2] [GSOC]trailer: pass arg as positional parameter ZheNing Hu via GitGitGadget
@ 2021-04-01  7:28         ` Christian Couder
  2021-04-01 10:02           ` ZheNing Hu
  2021-04-02 13:26         ` [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option ZheNing Hu via GitGitGadget
  3 siblings, 1 reply; 60+ messages in thread
From: Christian Couder @ 2021-04-01  7:28 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget; +Cc: git, Junio C Hamano, ZheNing Hu

On Wed, Mar 31, 2021 at 12:05 PM ZheNing Hu via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> In https://lore.kernel.org/git/xmqqv99i4ck2.fsf@gitster.g/ Junio and
> Christian talked about the problem of using strbuf_replace() to replace
> $ARG.

It's better to sum up a bit the discussions. It's nice to provide a
link to the discussions though.

> Now pass trailer value as $1 to the trailer command with another
> trailer.<token>.cmd config.

If this patch series introduces a new trailer.<token>.cmd config
option, then I would expect one of the patch in the series to have a
subject like "trailer: add new trailer.<token>.cmd config option".

> ZheNing Hu (2):
>   [GSOC] run-command: add shell_no_implicit_args option
>   [GSOC]trailer: pass arg as positional parameter

I guess the "trailer: pass arg as positional parameter" is the one
introducing the new trailer.<token>.cmd config option.

Also it seems strange that there is no space between "[GSOC]" and "trailer".

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v5 1/2] [GSOC] run-command: add shell_no_implicit_args option
  2021-04-01  7:22           ` Christian Couder
@ 2021-04-01  9:58             ` ZheNing Hu
  0 siblings, 0 replies; 60+ messages in thread
From: ZheNing Hu @ 2021-04-01  9:58 UTC (permalink / raw)
  To: Christian Couder; +Cc: ZheNing Hu via GitGitGadget, git, Junio C Hamano

Christian Couder <christian.couder@gmail.com> 于2021年4月1日周四 下午3:22写道:
>
> On Wed, Mar 31, 2021 at 12:05 PM ZheNing Hu via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
> >
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > When we use subprocess to run a shell-script, if we have any
>
> Maybe: s/subprocess/a subprocess/
>
> > args, git will add extra $@ to the end of the shell-script,
> > This can pass positional parameters correctly, But if we just
> > want to use some of these passed parameters, git will still
> > add an extra "$@", which contains all positional parameters we
> > passed. This does not meet our expectations.
>
> I am not sure explaining things using $@ is the best way to make this
> as clear as possible. I don't have a clear alternative right now
> though.
>
> > E.g. our shell-script is:
> > "echo \"\$1\""
> > and pass $1 "abc",
>
> Maybe: s/pass $1 "abc"/we pass "abc" as $1/
>
> > git will change our script to:
> > "echo \"\$1\" \"$@\""
>
> Where will "abc" appear then?
>
> > The positional parameters we entered will be printed
> > repeatedly.
>
> If you take us passing "abc" in $1 as an example, then I think it's a
> good idea to show us the result of that.
>
> > So let add a new `shell_no_implicit_args`
>
> Maybe: s/`shell_no_implicit_args`/`shell_no_implicit_args` flag/
>

Thanks for these grammar corrections.

> > to `struct child_process`, which can suppress the
> > joining of $@ if `shell_no_implicit_args` is set to 1,
> > this will allow us to use only few of positional args
> > in multi-parameter shell script, instead of using all
> > of them.
>
> I think our goal is more to have each argument we pass be passed just once.

More accurately, we only want those explicit positional
parameters to be replaced.
But Junio probably thinks it's OK to put on a layer of
"sh -c"  to "absorb" the "$@". I think it works, but it may
cause some trouble for users.

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v5 0/2] [GSOC]trailer: pass arg as positional parameter
  2021-04-01  7:28         ` [PATCH v5 0/2] " Christian Couder
@ 2021-04-01 10:02           ` ZheNing Hu
  0 siblings, 0 replies; 60+ messages in thread
From: ZheNing Hu @ 2021-04-01 10:02 UTC (permalink / raw)
  To: Christian Couder; +Cc: ZheNing Hu via GitGitGadget, git, Junio C Hamano

Christian Couder <christian.couder@gmail.com> 于2021年4月1日周四 下午3:28写道:
>
> On Wed, Mar 31, 2021 at 12:05 PM ZheNing Hu via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
> >
> > In https://lore.kernel.org/git/xmqqv99i4ck2.fsf@gitster.g/ Junio and
> > Christian talked about the problem of using strbuf_replace() to replace
> > $ARG.
>
> It's better to sum up a bit the discussions. It's nice to provide a
> link to the discussions though.
>
> > Now pass trailer value as $1 to the trailer command with another
> > trailer.<token>.cmd config.
>
> If this patch series introduces a new trailer.<token>.cmd config
> option, then I would expect one of the patch in the series to have a
> subject like "trailer: add new trailer.<token>.cmd config option".
>
> > ZheNing Hu (2):
> >   [GSOC] run-command: add shell_no_implicit_args option
> >   [GSOC]trailer: pass arg as positional parameter
>
> I guess the "trailer: pass arg as positional parameter" is the one
> introducing the new trailer.<token>.cmd config option.
>
> Also it seems strange that there is no space between "[GSOC]" and "trailer".

Thanks, I'll fix them.

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-04-01  3:56                               ` ZheNing Hu
@ 2021-04-01 19:49                                 ` Junio C Hamano
  2021-04-02  2:08                                   ` ZheNing Hu
  0 siblings, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2021-04-01 19:49 UTC (permalink / raw)
  To: ZheNing Hu; +Cc: Christian Couder, ZheNing Hu via GitGitGadget, git

ZheNing Hu <adlternative@gmail.com> writes:

>> The way to do the same without an extra script on disk is for you to
>> use sh-c yourself.
>>
>>     .cmd = sh -c 'echo 123'
>
> This is indeed a viable solution, But the extra "sh -c" seems to put an
> unnecessary burden on the user.

Nobody forces you to write long script in the configuration file.
In fact, the "find author from history" is so useful that I'd think
people have an alias or a script in ~/bin/ already for their own
interactive use.  E.g.

    $ cat ~/bin/git-who
    #!/bin/sh
    git log -1 --format="%an <%ae>" --author="$1"
    $ cat ~/bin/git-one
    #!/bin/sh
    git show -s --pretty=reference "$1"

and with them:

	trailer.key.cmd = git who

that is internally wrapped into 

	sh -c 'git who "$@"'

and fed "gitster@" as the first parameter when "--trailer=key:gitster@"
is given would work just fine.

> Sometimes I wonder, why not recommend using environment variables
> like $ARG?

I am also fine with that; when we discovered the design flaw of
.command, I think I suggested either would make a viable choice.
The only downside is that it would squat on a good name $ARG and
forbids end-users from using the symbol for other purpose, but as
long as the application is limited in scope, that would be fine.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter
  2021-04-01 19:49                                 ` Junio C Hamano
@ 2021-04-02  2:08                                   ` ZheNing Hu
  0 siblings, 0 replies; 60+ messages in thread
From: ZheNing Hu @ 2021-04-02  2:08 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Christian Couder, ZheNing Hu via GitGitGadget, git

Junio C Hamano <gitster@pobox.com> 于2021年4月2日周五 上午3:49写道:
>
> ZheNing Hu <adlternative@gmail.com> writes:
>
> >> The way to do the same without an extra script on disk is for you to
> >> use sh-c yourself.
> >>
> >>     .cmd = sh -c 'echo 123'
> >
> > This is indeed a viable solution, But the extra "sh -c" seems to put an
> > unnecessary burden on the user.
>
> Nobody forces you to write long script in the configuration file.
> In fact, the "find author from history" is so useful that I'd think
> people have an alias or a script in ~/bin/ already for their own
> interactive use.  E.g.
>
>     $ cat ~/bin/git-who
>     #!/bin/sh
>     git log -1 --format="%an <%ae>" --author="$1"
>     $ cat ~/bin/git-one
>     #!/bin/sh
>     git show -s --pretty=reference "$1"
>
> and with them:
>
>         trailer.key.cmd = git who
>
> that is internally wrapped into
>
>         sh -c 'git who "$@"'
>
> and fed "gitster@" as the first parameter when "--trailer=key:gitster@"
> is given would work just fine.
>

Okay, now I get it. This allows us to run some common scripts or just use
"sh -c". I might need some additional tests to illustrate those changes made
by using '.cmd'.

> > Sometimes I wonder, why not recommend using environment variables
> > like $ARG?
>
> I am also fine with that; when we discovered the design flaw of
> .command, I think I suggested either would make a viable choice.
> The only downside is that it would squat on a good name $ARG and
> forbids end-users from using the symbol for other purpose, but as
> long as the application is limited in scope, that would be fine.

I agree with you. This may indeed be a minor drawback.

Thanks, Junio.

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-03-31 10:05       ` [PATCH v5 0/2] " ZheNing Hu via GitGitGadget
                           ` (2 preceding siblings ...)
  2021-04-01  7:28         ` [PATCH v5 0/2] " Christian Couder
@ 2021-04-02 13:26         ` ZheNing Hu via GitGitGadget
  2021-04-02 20:48           ` Junio C Hamano
                             ` (2 more replies)
  3 siblings, 3 replies; 60+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-04-02 13:26 UTC (permalink / raw)
  To: git; +Cc: Christian Couder, Junio C Hamano, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name of or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token.  This has two downsides:

* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.

* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatching single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor), it would result in
a broken command that is not syntactically correct (or
worse).

Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as a
parameter to the command.  Instead of "$ARG", the users will
refer to the value as positional argument, $1, in their
scripts.

Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC] trailer: add new trailer..cmd config option
    
    In https://lore.kernel.org/git/xmqqv99i4ck2.fsf@gitster.g/ Junio and
    Christian talked about the problem of using strbuf_replace() to replace
    $ARG:
    
     1. if user's script have more than one $ARG, only the first one will be
        replaced, which is incorrected.
     2. $ARG is textually replaced without shell syntax, which may result a
        broken command when $ARG include some unmatching single quote, very
        unsafe.
    
    Now pass trailer value as $1 to the trailer command with another
    trailer.<token>.cmd config, to solve these above two problems,

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-913%2Fadlternative%2Ftrailer-pass-ARG-env-v6
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-913/adlternative/trailer-pass-ARG-env-v6
Pull-Request: https://github.com/gitgitgadget/git/pull/913

Range-diff vs v5:

 1:  4c59cab53a0d < -:  ------------ [GSOC] run-command: add shell_no_implicit_args option
 2:  5894d8c4b364 ! 1:  3aed77d077b9 [GSOC]trailer: pass arg as positional parameter
     @@ Metadata
      Author: ZheNing Hu <adlternative@gmail.com>
      
       ## Commit message ##
     -    [GSOC]trailer: pass arg as positional parameter
     +    [GSOC] trailer: add new trailer.<token>.cmd config option
      
          The `trailer.<token>.command` configuration variable
          specifies a command (run via the shell, so it does not have
     @@ Documentation/git-interpret-trailers.txt: subject
       Fix #42
       ------------
       
     -+* Configure a 'see' trailer with a cmd to show the subject of a
     -+  commit that is related, and show how it works:
     ++* Configure a 'see' trailer with a cmd use a global script `git-one`
     ++  to show the subject of a commit that is related, and show how it works:
      ++
      +------------
     ++$ cat ~/bin/git-one
     ++#!/bin/sh
     ++git show -s --pretty=reference "$1"
      +$ git config trailer.see.key "See-also: "
      +$ git config trailer.see.ifExists "replace"
      +$ git config trailer.see.ifMissing "doNothing"
     -+$ git config trailer.see.cmd "git show -s --pretty=reference \"\$1\""
     ++$ git config trailer.see.cmd "~/bin/git-one"
      +$ git interpret-trailers <<EOF
      +> subject
      +> 
     @@ Documentation/git-interpret-trailers.txt: subject
      +
      +message
      +
     -+See-also: fe3187489d69c4 (subject of related commit, 2021-3-20)
     ++See-also: fe3187e (subject of related commit, 2021-4-2)
      +------------
      +
     -+* Configure a 'bug' trailer with a cmd to show when and where
     -+  was the bug introduced, and show how it works:
     ++* Configure a 'who' trailer with a cmd use a global script `git-who`
     ++  to find the recent matching "author <mail>" pair in git log and
     ++  show how it works:
      ++
      +------------
     -+$ git config trailer.bug.key "Bug-from: "
     -+$ git config trailer.bug.ifExists "replace"
     -+$ git config trailer.bug.cmd "git log --grep \"\$1\" -1 --pretty=\"%h %aD\""
     -+$ git interpret-trailers --trailer="bug:the information manager from hell" <<EOF
     ++$ cat ~/bin/git-who
     ++ #!/bin/sh
     ++    git log -1 --format="%an <%ae>" --author="$1"
     ++$ git config trailer.help.key "Helped-by: "
     ++$ git config trailer.help.ifExists "replace"
     ++$ git config trailer.help.cmd "~/bin/git-who"
     ++$ git interpret-trailers --trailer="help:gitster@" <<EOF
      +> subject
      +> 
      +> message
     @@ Documentation/git-interpret-trailers.txt: subject
      +
      +message
      +
     -+Bug-from: 57d84f8d93 Mon, 6 Aug 2012 18:27:09 +0700
     ++Helped-by: Junio C Hamano <gitster@pobox.com>
      +------------
      +
       * Configure a 'see' trailer with a command to show the subject of a
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup' '
       
      +test_expect_success 'with cmd' '
      +	test_when_finished "git config --unset trailer.bug.key && \
     -+	git config --unset trailer.bug.ifExists && \
     -+	git config --unset trailer.bug.cmd" &&
     ++			    git config --unset trailer.bug.ifExists && \
     ++			    git config --unset trailer.bug.cmd" &&
      +	git config trailer.bug.key "Bug-maker: " &&
      +	git config trailer.bug.ifExists "add" &&
     -+	git config trailer.bug.cmd "echo \"\$@\"" &&
     -+	cat >>expected2 <<-EOF &&
     ++	git config trailer.bug.cmd "echo \"maybe is\"" &&
     ++	cat >expected2 <<-EOF &&
      +
     -+	Bug-maker: 
     -+	Bug-maker: jocker
     -+	Bug-maker: batman
     ++	Bug-maker: maybe is
     ++	Bug-maker: maybe is him
     ++	Bug-maker: maybe is me
      +	EOF
     -+	git interpret-trailers --trailer "bug: jocker" --trailer "bug:batman" \
     ++	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
     ++		>actual2 &&
     ++	test_cmp expected2 actual2
     ++'
     ++
     ++test_expect_success 'with cmd and $1' '
     ++	test_when_finished "git config --unset trailer.bug.key && \
     ++			    git config --unset trailer.bug.ifExists && \
     ++			    git config --unset trailer.bug.cmd" &&
     ++	git config trailer.bug.key "Bug-maker: " &&
     ++	git config trailer.bug.ifExists "replace" &&
     ++	git config trailer.bug.cmd "echo \"\$1\" is" &&
     ++	cat >expected2 <<-EOF &&
     ++
     ++	Bug-maker: me is me
     ++	EOF
     ++	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
     ++		>actual2 &&
     ++	test_cmp expected2 actual2
     ++'
     ++
     ++test_expect_success 'with cmd and $1 with sh -c' '
     ++	test_when_finished "git config --unset trailer.bug.key && \
     ++			    git config --unset trailer.bug.ifExists && \
     ++			    git config --unset trailer.bug.cmd" &&
     ++	git config trailer.bug.key "Bug-maker: " &&
     ++	git config trailer.bug.ifExists "replace" &&
     ++	git config trailer.bug.cmd "sh -c \"echo who is \"\$1\"\"" &&
     ++	cat >expected2 <<-EOF &&
     ++
     ++	Bug-maker: who is me
     ++	EOF
     ++	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
     ++		>actual2 &&
     ++	test_cmp expected2 actual2
     ++'
     ++
     ++test_expect_success 'with cmd and $1 with shell script' '
     ++	test_when_finished "git config --unset trailer.bug.key && \
     ++			    git config --unset trailer.bug.ifExists && \
     ++			    git config --unset trailer.bug.cmd" &&
     ++	git config trailer.bug.key "Bug-maker: " &&
     ++	git config trailer.bug.ifExists "replace" &&
     ++	git config trailer.bug.cmd "./echoscript" &&
     ++	cat >expected2 <<-EOF &&
     ++
     ++	Bug-maker: who is me
     ++	EOF
     ++	cat >echoscript <<-EOF &&
     ++	#!/bin/sh
     ++	echo who is "\$1"
     ++	EOF
     ++	chmod +x echoscript &&
     ++	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
      +		>actual2 &&
      +	test_cmp expected2 actual2
      +'
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup a commit' '
       	git commit -m "Add file a.txt"
       '
       
     -+test_expect_success 'with cmd and $1' '
     -+	test_when_finished "git config --unset trailer.fix.cmd" &&
     -+	git config trailer.fix.ifExists "replace" &&
     -+	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%s)\" \
     -+		--abbrev-commit --abbrev=14 \"\$1\" || true" &&
     -+	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
     -+	cat complex_message_body >expected2 &&
     -+	sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
     -+		Fixes: $FIXED
     -+		Acked-by= Z
     -+		Reviewed-by:
     -+		Signed-off-by: Z
     -+		Signed-off-by: A U Thor <author@example.com>
     -+	EOF
     -+	git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
     -+		<complex_message >actual2 &&
     -+	test_cmp expected2 actual2
     -+'
     -+
      +test_expect_success 'cmd takes precedence over command' '
      +	test_when_finished "git config --unset trailer.fix.cmd" &&
      +	git config trailer.fix.ifExists "replace" &&
     @@ trailer.c: static int check_if_different(struct trailer_item *in_tok,
      -
      -	strvec_push(&cp.args, cmd.buf);
      +	if (conf->cmd) {
     -+		cp.shell_no_implicit_args = 1;
     ++		// cp.shell_no_implicit_args = 1;
      +		strbuf_addstr(&cmd, conf->cmd);
      +		strvec_push(&cp.args, cmd.buf);
      +		if (arg)
     @@ trailer.c: static int check_if_different(struct trailer_item *in_tok,
       	cp.env = local_repo_env;
       	cp.no_stdin = 1;
       	cp.use_shell = 1;
     --	cp.shell_no_implicit_args = 1;
     - 
     - 	if (capture_command(&cp, &buf, 1024)) {
     - 		error(_("running trailer command '%s' failed"), cmd.buf);
      @@ trailer.c: static char *apply_command(const char *command, const char *arg)
       
       static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)


 Documentation/git-interpret-trailers.txt | 82 +++++++++++++++++---
 t/t7513-interpret-trailers.sh            | 95 +++++++++++++++++++++++-
 trailer.c                                | 38 +++++++---
 3 files changed, 193 insertions(+), 22 deletions(-)

diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 96ec6499f001..67649ec6134c 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -236,21 +236,34 @@ trailer.<token>.command::
 	be called to automatically add or modify a trailer with the
 	specified <token>.
 +
-When this option is specified, the behavior is as if a special
-'<token>=<value>' argument were added at the beginning of the command
-line, where <value> is taken to be the standard output of the
-specified command with any leading and trailing whitespace trimmed
-off.
+When this option is specified, the first occurrence of substring $ARG is
+replaced with the value given to the `interpret-trailer` command for the
+same token.
 +
-If the command contains the `$ARG` string, this string will be
-replaced with the <value> part of an existing trailer with the same
-<token>, if any, before the command is launched.
+".command" has been deprecated due to the $ARG in the user's command can
+only be replaced once and the original way of replacing $ARG was not safe.
+Now the preferred option is using "trailer.<token>.cmd", which use position
+argument to pass the value.
++
+When both .cmd and .command are given for the same <token>,
+.cmd is used and .command is ignored.
+
+trailer.<token>.cmd::
+	The command specified by this configuration variable is run
+	with a single parameter, which is the <value> part of an
+	existing trailer with the same <token>.  The output from the
+	command is then used as the value for the <token> in the
+	resulting trailer.
++
+When this option is specified, If there is no trailer with same <token>,
+the behavior is as if a special '<token>=<value>' argument were added at
+the beginning of the command, <value> will be passed to the user's
+command as an empty value.
 +
 If some '<token>=<value>' arguments are also passed on the command
 line, when a 'trailer.<token>.command' is configured, the command will
 also be executed for each of these arguments. And the <value> part of
-these arguments, if any, will be used to replace the `$ARG` string in
-the command.
+these arguments, if any, will be passed to the command as first parameter.
 
 EXAMPLES
 --------
@@ -333,6 +346,55 @@ subject
 Fix #42
 ------------
 
+* Configure a 'see' trailer with a cmd use a global script `git-one`
+  to show the subject of a commit that is related, and show how it works:
++
+------------
+$ cat ~/bin/git-one
+#!/bin/sh
+git show -s --pretty=reference "$1"
+$ git config trailer.see.key "See-also: "
+$ git config trailer.see.ifExists "replace"
+$ git config trailer.see.ifMissing "doNothing"
+$ git config trailer.see.cmd "~/bin/git-one"
+$ git interpret-trailers <<EOF
+> subject
+> 
+> message
+> 
+> see: HEAD~2
+> EOF
+subject
+
+message
+
+See-also: fe3187e (subject of related commit, 2021-4-2)
+------------
+
+* Configure a 'who' trailer with a cmd use a global script `git-who`
+  to find the recent matching "author <mail>" pair in git log and
+  show how it works:
++
+------------
+$ cat ~/bin/git-who
+ #!/bin/sh
+    git log -1 --format="%an <%ae>" --author="$1"
+$ git config trailer.help.key "Helped-by: "
+$ git config trailer.help.ifExists "replace"
+$ git config trailer.help.cmd "~/bin/git-who"
+$ git interpret-trailers --trailer="help:gitster@" <<EOF
+> subject
+> 
+> message
+> 
+> EOF
+subject
+
+message
+
+Helped-by: Junio C Hamano <gitster@pobox.com>
+------------
+
 * Configure a 'see' trailer with a command to show the subject of a
   commit that is related, and show how it works:
 +
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 6602790b5f4c..923923e57573 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -51,6 +51,77 @@ test_expect_success 'setup' '
 	EOF
 '
 
+test_expect_success 'with cmd' '
+	test_when_finished "git config --unset trailer.bug.key && \
+			    git config --unset trailer.bug.ifExists && \
+			    git config --unset trailer.bug.cmd" &&
+	git config trailer.bug.key "Bug-maker: " &&
+	git config trailer.bug.ifExists "add" &&
+	git config trailer.bug.cmd "echo \"maybe is\"" &&
+	cat >expected2 <<-EOF &&
+
+	Bug-maker: maybe is
+	Bug-maker: maybe is him
+	Bug-maker: maybe is me
+	EOF
+	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+		>actual2 &&
+	test_cmp expected2 actual2
+'
+
+test_expect_success 'with cmd and $1' '
+	test_when_finished "git config --unset trailer.bug.key && \
+			    git config --unset trailer.bug.ifExists && \
+			    git config --unset trailer.bug.cmd" &&
+	git config trailer.bug.key "Bug-maker: " &&
+	git config trailer.bug.ifExists "replace" &&
+	git config trailer.bug.cmd "echo \"\$1\" is" &&
+	cat >expected2 <<-EOF &&
+
+	Bug-maker: me is me
+	EOF
+	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+		>actual2 &&
+	test_cmp expected2 actual2
+'
+
+test_expect_success 'with cmd and $1 with sh -c' '
+	test_when_finished "git config --unset trailer.bug.key && \
+			    git config --unset trailer.bug.ifExists && \
+			    git config --unset trailer.bug.cmd" &&
+	git config trailer.bug.key "Bug-maker: " &&
+	git config trailer.bug.ifExists "replace" &&
+	git config trailer.bug.cmd "sh -c \"echo who is \"\$1\"\"" &&
+	cat >expected2 <<-EOF &&
+
+	Bug-maker: who is me
+	EOF
+	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+		>actual2 &&
+	test_cmp expected2 actual2
+'
+
+test_expect_success 'with cmd and $1 with shell script' '
+	test_when_finished "git config --unset trailer.bug.key && \
+			    git config --unset trailer.bug.ifExists && \
+			    git config --unset trailer.bug.cmd" &&
+	git config trailer.bug.key "Bug-maker: " &&
+	git config trailer.bug.ifExists "replace" &&
+	git config trailer.bug.cmd "./echoscript" &&
+	cat >expected2 <<-EOF &&
+
+	Bug-maker: who is me
+	EOF
+	cat >echoscript <<-EOF &&
+	#!/bin/sh
+	echo who is "\$1"
+	EOF
+	chmod +x echoscript &&
+	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+		>actual2 &&
+	test_cmp expected2 actual2
+'
+
 test_expect_success 'without config' '
 	sed -e "s/ Z\$/ /" >expected <<-\EOF &&
 
@@ -1274,9 +1345,31 @@ test_expect_success 'setup a commit' '
 	git commit -m "Add file a.txt"
 '
 
+test_expect_success 'cmd takes precedence over command' '
+	test_when_finished "git config --unset trailer.fix.cmd" &&
+	git config trailer.fix.ifExists "replace" &&
+	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
+		--abbrev-commit --abbrev=14 \"\$1\" || true" &&
+	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \$ARG" &&
+	FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
+	cat complex_message_body >expected2 &&
+	sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
+		Fixes: $FIXED
+		Acked-by= Z
+		Reviewed-by:
+		Signed-off-by: Z
+		Signed-off-by: A U Thor <author@example.com>
+	EOF
+	git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+		<complex_message >actual2 &&
+	test_cmp expected2 actual2
+'
+
 test_expect_success 'with command using $ARG' '
 	git config trailer.fix.ifExists "replace" &&
-	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \$ARG" &&
 	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-EOF &&
diff --git a/trailer.c b/trailer.c
index be4e9726421c..6aeff6a1bd33 100644
--- a/trailer.c
+++ b/trailer.c
@@ -14,6 +14,7 @@ struct conf_info {
 	char *name;
 	char *key;
 	char *command;
+	char *cmd;
 	enum trailer_where where;
 	enum trailer_if_exists if_exists;
 	enum trailer_if_missing if_missing;
@@ -127,6 +128,7 @@ static void free_arg_item(struct arg_item *item)
 	free(item->conf.name);
 	free(item->conf.key);
 	free(item->conf.command);
+	free(item->conf.cmd);
 	free(item->token);
 	free(item->value);
 	free(item);
@@ -216,18 +218,25 @@ static int check_if_different(struct trailer_item *in_tok,
 	return 1;
 }
 
-static char *apply_command(const char *command, const char *arg)
+static char *apply_command(struct conf_info *conf, const char *arg)
 {
 	struct strbuf cmd = STRBUF_INIT;
 	struct strbuf buf = STRBUF_INIT;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	char *result;
 
-	strbuf_addstr(&cmd, command);
-	if (arg)
-		strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
-
-	strvec_push(&cp.args, cmd.buf);
+	if (conf->cmd) {
+		// cp.shell_no_implicit_args = 1;
+		strbuf_addstr(&cmd, conf->cmd);
+		strvec_push(&cp.args, cmd.buf);
+		if (arg)
+			strvec_push(&cp.args, arg);
+	} else if (conf->command) {
+		strbuf_addstr(&cmd, conf->command);
+		strvec_push(&cp.args, cmd.buf);
+		if (arg)
+			strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
+	}
 	cp.env = local_repo_env;
 	cp.no_stdin = 1;
 	cp.use_shell = 1;
@@ -247,7 +256,7 @@ static char *apply_command(const char *command, const char *arg)
 
 static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)
 {
-	if (arg_tok->conf.command) {
+	if (arg_tok->conf.command || arg_tok->conf.cmd) {
 		const char *arg;
 		if (arg_tok->value && arg_tok->value[0]) {
 			arg = arg_tok->value;
@@ -257,7 +266,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg
 			else
 				arg = xstrdup("");
 		}
-		arg_tok->value = apply_command(arg_tok->conf.command, arg);
+		arg_tok->value = apply_command(&arg_tok->conf, arg);
 		free((char *)arg);
 	}
 }
@@ -430,6 +439,7 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
 	dst->name = xstrdup_or_null(src->name);
 	dst->key = xstrdup_or_null(src->key);
 	dst->command = xstrdup_or_null(src->command);
+	dst->cmd = xstrdup_or_null(src->cmd);
 }
 
 static struct arg_item *get_conf_item(const char *name)
@@ -454,8 +464,8 @@ static struct arg_item *get_conf_item(const char *name)
 	return item;
 }
 
-enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_WHERE,
-			 TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
+enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_CMD,
+			TRAILER_WHERE, TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
 
 static struct {
 	const char *name;
@@ -463,6 +473,7 @@ static struct {
 } trailer_config_items[] = {
 	{ "key", TRAILER_KEY },
 	{ "command", TRAILER_COMMAND },
+	{ "cmd", TRAILER_CMD },
 	{ "where", TRAILER_WHERE },
 	{ "ifexists", TRAILER_IF_EXISTS },
 	{ "ifmissing", TRAILER_IF_MISSING }
@@ -542,6 +553,11 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
 			warning(_("more than one %s"), conf_key);
 		conf->command = xstrdup(value);
 		break;
+	case TRAILER_CMD:
+		if (conf->cmd)
+			warning(_("more than one %s"), conf_key);
+		conf->cmd = xstrdup(value);
+		break;
 	case TRAILER_WHERE:
 		if (trailer_set_where(&conf->where, value))
 			warning(_("unknown value '%s' for key '%s'"), value, conf_key);
@@ -708,7 +724,7 @@ static void process_command_line_args(struct list_head *arg_head,
 	/* Add an arg item for each configured trailer with a command */
 	list_for_each(pos, &conf_head) {
 		item = list_entry(pos, struct arg_item, list);
-		if (item->conf.command)
+		if (item->conf.cmd || item->conf.command)
 			add_arg_item(arg_head,
 				     xstrdup(token_from_item(item, NULL)),
 				     xstrdup(""),

base-commit: 142430338477d9d1bb25be66267225fb58498d92
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-02 13:26         ` [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option ZheNing Hu via GitGitGadget
@ 2021-04-02 20:48           ` Junio C Hamano
  2021-04-03  5:08             ` ZheNing Hu
                               ` (2 more replies)
  2021-04-02 23:44           ` Junio C Hamano
  2021-04-04 13:11           ` [PATCH v7] " ZheNing Hu via GitGitGadget
  2 siblings, 3 replies; 60+ messages in thread
From: Junio C Hamano @ 2021-04-02 20:48 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget; +Cc: git, Christian Couder, ZheNing Hu

"ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:

> quote (imagine a name like "O'Connor", substituted into
> NAME='$ARG' to make it NAME='O'Connor), it would result in

You inherited a typo from my review comments here.  This line should
have said

    NAME='$ARG' to make it NAME='O'Connor'), it would result in

I will tweak locally while queuing (read: no need to send an update
only to fix this line---but please do not forget to change it if you
are going to send an update to fix or improve other things).


>  Documentation/git-interpret-trailers.txt | 82 +++++++++++++++++---
>  t/t7513-interpret-trailers.sh            | 95 +++++++++++++++++++++++-
>  trailer.c                                | 38 +++++++---
>  3 files changed, 193 insertions(+), 22 deletions(-)
>
> diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
> index 96ec6499f001..67649ec6134c 100644
> --- a/Documentation/git-interpret-trailers.txt
> +++ b/Documentation/git-interpret-trailers.txt
> @@ -236,21 +236,34 @@ trailer.<token>.command::
>  	be called to automatically add or modify a trailer with the
>  	specified <token>.
>  +
> -When this option is specified, the behavior is as if a special
> -'<token>=<value>' argument were added at the beginning of the command
> -line, where <value> is taken to be the standard output of the
> -specified command with any leading and trailing whitespace trimmed
> -off.
> +When this option is specified, the first occurrence of substring $ARG is
> +replaced with the value given to the `interpret-trailer` command for the
> +same token.
>  +
> -If the command contains the `$ARG` string, this string will be
> -replaced with the <value> part of an existing trailer with the same
> -<token>, if any, before the command is launched.
> +".command" has been deprecated due to the $ARG in the user's command can
> +only be replaced once and the original way of replacing $ARG was not safe.
> +Now the preferred option is using "trailer.<token>.cmd", which use position
> +argument to pass the value.
> ++
> +When both .cmd and .command are given for the same <token>,
> +.cmd is used and .command is ignored.

Warning about unsafe replacement is a good idea.  OK.

> +trailer.<token>.cmd::
> +	The command specified by this configuration variable is run
> +	with a single parameter, which is the <value> part of an
> +	existing trailer with the same <token>.  The output from the
> +	command is then used as the value for the <token> in the
> +	resulting trailer.
> ++
> +When this option is specified, If there is no trailer with same <token>,

s/If/if/ (downcase).

> +the behavior is as if a special '<token>=<value>' argument were added at
> +the beginning of the command, <value> will be passed to the user's
> +command as an empty value.

Do the two occurrences of the word "command" in the sentence refer
to different things?  I do not think this is an existing problem
inherited from the original, but as we are trying to improve the
description, I wonder if we can clarify them a bit.

	... as if a '<token>=<value>' argument were added at the
	beginning of the "git interpret-trailers" command, the
	command specified by this configuration variable will be
	called with an empty string as the argument.

is my attempt, but I am not still sure what that "as if" part is
trying to say.  Does it mean with

	[trailer "Foo"] cmd = foo-cmd

and the 'input-file' does not have "Foo: <some existing value>"
trailer in it, the command "git interpret-trailers input-file"
would behave as if this command was run

	$ Foo= git interpret-trailers input-file

(as there is no <value>, I am not sure what <value> is used when
<token>=<value> is prefixed to the command)?

Puzzled and confused utterly am I...  Help, Christian?

>  +
>  If some '<token>=<value>' arguments are also passed on the command
>  line, when a 'trailer.<token>.command' is configured, the command will
>  also be executed for each of these arguments. And the <value> part of

This talks about 'trailer.<token>.command'.  Should this be changed
to '.cmd'? 

Or does everything after "When this option is specified, if there is
no trailer with ..." apply to both the old .command and new .cmd?
If so, that was not clear at all---we'd need to clarify this part.

> -these arguments, if any, will be used to replace the `$ARG` string in
> -the command.
> +these arguments, if any, will be passed to the command as first parameter.
>  
>  EXAMPLES
>  --------
> @@ -333,6 +346,55 @@ subject
>  Fix #42
>  ------------
>  
> +* Configure a 'see' trailer with a cmd use a global script `git-one`
> +  to show the subject of a commit that is related, and show how it works:
> ++
> +------------
> +$ cat ~/bin/git-one
> +#!/bin/sh
> +git show -s --pretty=reference "$1"
> +$ git config trailer.see.key "See-also: "
> +$ git config trailer.see.ifExists "replace"
> +$ git config trailer.see.ifMissing "doNothing"
> +$ git config trailer.see.cmd "~/bin/git-one"
> +$ git interpret-trailers <<EOF
> +> subject
> +> 
> +> message
> +> 
> +> see: HEAD~2
> +> EOF
> +subject
> +
> +message
> +
> +See-also: fe3187e (subject of related commit, 2021-4-2)
> +------------
> +
> +* Configure a 'who' trailer with a cmd use a global script `git-who`
> +  to find the recent matching "author <mail>" pair in git log and
> +  show how it works:
> ++
> +------------
> +$ cat ~/bin/git-who
> + #!/bin/sh
> +    git log -1 --format="%an <%ae>" --author="$1"

Unusual indentation here.  But more importantly, I am not sure if 
having both 'see' and 'help' examples is worth it---they are similar
enough that the second one does not teach anything new to those who
studied the first one already, aren't they?

> +$ git config trailer.help.key "Helped-by: "
> +$ git config trailer.help.ifExists "replace"
> +$ git config trailer.help.cmd "~/bin/git-who"
> +$ git interpret-trailers --trailer="help:gitster@" <<EOF
> +> subject
> +> 
> +> message
> +> 
> +> EOF
> +subject
> +
> +message
> +
> +Helped-by: Junio C Hamano <gitster@pobox.com>
> +------------
> +
>  * Configure a 'see' trailer with a command to show the subject of a
>    commit that is related, and show how it works:
>  +



> diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
> index 6602790b5f4c..923923e57573 100755
> --- a/t/t7513-interpret-trailers.sh
> +++ b/t/t7513-interpret-trailers.sh
> @@ -51,6 +51,77 @@ test_expect_success 'setup' '
>  	EOF
>  '
>  
> +test_expect_success 'with cmd' '
> +	test_when_finished "git config --unset trailer.bug.key && \
> +			    git config --unset trailer.bug.ifExists && \
> +			    git config --unset trailer.bug.cmd" &&

It is unwise to use && between these three "git config" invocations,
I suspect.  "git config --unset" exits with non-zero status when you
attempt to remove with an non-existent key, but you would remove the
.ifExists and .cmd even if .key is not defined.  Perhaps

	test_when_finished "git config --unset-all trailer.bug.key
			    git config --unset-all trailer.bug.ifExists
			    git config --unset-all trailer.bug.cmd" &&

would be more sensible.  Or if we just want to remove everything
under the trailer.bug.* section, this might be even better:

	test_when_finished "git config --remove-section trailer.bug" &&

as we can add new trailer.bug.* to the system and use them in this
test, but removing the entire section would still be a good way to
clean after ourselves.

> diff --git a/trailer.c b/trailer.c
> index be4e9726421c..6aeff6a1bd33 100644
> --- a/trailer.c
> +++ b/trailer.c
> ...
> +static char *apply_command(struct conf_info *conf, const char *arg)
>  {
>  	struct strbuf cmd = STRBUF_INIT;
>  	struct strbuf buf = STRBUF_INIT;
>  	struct child_process cp = CHILD_PROCESS_INIT;
>  	char *result;
>  
> -	strbuf_addstr(&cmd, command);
> -	if (arg)
> -		strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
> -
> -	strvec_push(&cp.args, cmd.buf);
> +	if (conf->cmd) {
> +		// cp.shell_no_implicit_args = 1;

Do not add new code that is commented out.  Besides we do not use // comment.

> +		strbuf_addstr(&cmd, conf->cmd);
> +		strvec_push(&cp.args, cmd.buf);
> +		if (arg)
> +			strvec_push(&cp.args, arg);

Thanks.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-02 13:26         ` [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option ZheNing Hu via GitGitGadget
  2021-04-02 20:48           ` Junio C Hamano
@ 2021-04-02 23:44           ` Junio C Hamano
  2021-04-03  3:22             ` ZheNing Hu
  2021-04-04 13:11           ` [PATCH v7] " ZheNing Hu via GitGitGadget
  2 siblings, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2021-04-02 23:44 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget; +Cc: git, Christian Couder, ZheNing Hu

"ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: ZheNing Hu <adlternative@gmail.com>
>
> The `trailer.<token>.command` configuration variable
> specifies a command (run via the shell, so it does not have
> to be a single name of or path to the command, but can be a
> shell script), and the first occurrence of substring $ARG is
> replaced with the value given to the `interpret-trailer`
> command for the token.  This has two downsides:
> ...
>
>  Documentation/git-interpret-trailers.txt | 82 +++++++++++++++++---
>  t/t7513-interpret-trailers.sh            | 95 +++++++++++++++++++++++-
>  trailer.c                                | 38 +++++++---
>  3 files changed, 193 insertions(+), 22 deletions(-)

Merging this to anything that has zh/commit-trailer (which extends
the tests in t7502) seems to break t7502.  Running the test with the
"-i -v" option ends like so.

expecting success of 7502.24 'commit --trailer with -c and command': 
	trailer_commit_base &&
	cat >expected <<-\EOF &&
	hello

	Signed-off-by: C O Mitter <committer@example.com>
	Signed-off-by: C1 E1
	Helped-by: C2 E2
	Mentored-by: C4 E4
	Reported-by: A U Thor <author@example.com>
	EOF
	git -c trailer.report.key="Reported-by: " \
		-c trailer.report.ifexists="replace" \
		-c trailer.report.command="NAME=\"\$ARG\"; test -n \"\$NAME\" && \
		git log --author=\"\$NAME\" -1 --format=\"format:%aN <%aE>\" || true" \
		commit --trailer "report = author" --amend &&
	git cat-file commit HEAD >commit.msg &&
	sed -e "1,/^\$/d" commit.msg >actual &&
	test_cmp expected actual

[main 6b1e5e9] hello
 Author: A U Thor <author@example.com>
 1 file changed, 1 insertion(+)
[main 97c7a39] hello
 Author: A U Thor <author@example.com>
 Date: Thu Apr 7 15:22:13 2005 -0700
 1 file changed, 1 insertion(+)
--- expected	2021-04-02 23:43:10.649082950 +0000
+++ actual	2021-04-02 23:43:10.673085111 +0000
@@ -4,4 +4,4 @@
 Signed-off-by: C1 E1
 Helped-by: C2 E2
 Mentored-by: C4 E4
-Reported-by: A U Thor <author@example.com>
+Reported-by:
not ok 24 - commit --trailer with -c and command
#	
#		trailer_commit_base &&
#		cat >expected <<-\EOF &&
#		hello
#	
#		Signed-off-by: C O Mitter <committer@example.com>
#		Signed-off-by: C1 E1
#		Helped-by: C2 E2
#		Mentored-by: C4 E4
#		Reported-by: A U Thor <author@example.com>
#		EOF
#		git -c trailer.report.key="Reported-by: " \
#			-c trailer.report.ifexists="replace" \
#			-c trailer.report.command="NAME=\"\$ARG\"; test -n \"\$NAME\" && \
#			git log --author=\"\$NAME\" -1 --format=\"format:%aN <%aE>\" || true" \
#			commit --trailer "report = author" --amend &&
#		git cat-file commit HEAD >commit.msg &&
#		sed -e "1,/^\$/d" commit.msg >actual &&
#		test_cmp expected actual
#	

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-02 23:44           ` Junio C Hamano
@ 2021-04-03  3:22             ` ZheNing Hu
  2021-04-03  4:31               ` Junio C Hamano
  0 siblings, 1 reply; 60+ messages in thread
From: ZheNing Hu @ 2021-04-03  3:22 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: ZheNing Hu via GitGitGadget, Git List, Christian Couder

Junio C Hamano <gitster@pobox.com> 于2021年4月3日周六 上午7:44写道:
>
> "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > The `trailer.<token>.command` configuration variable
> > specifies a command (run via the shell, so it does not have
> > to be a single name of or path to the command, but can be a
> > shell script), and the first occurrence of substring $ARG is
> > replaced with the value given to the `interpret-trailer`
> > command for the token.  This has two downsides:
> > ...
> >
> >  Documentation/git-interpret-trailers.txt | 82 +++++++++++++++++---
> >  t/t7513-interpret-trailers.sh            | 95 +++++++++++++++++++++++-
> >  trailer.c                                | 38 +++++++---
> >  3 files changed, 193 insertions(+), 22 deletions(-)
>
> Merging this to anything that has zh/commit-trailer (which extends
> the tests in t7502) seems to break t7502.  Running the test with the
> "-i -v" option ends like so.
>
> expecting success of 7502.24 'commit --trailer with -c and command':
>         trailer_commit_base &&
>         cat >expected <<-\EOF &&
>         hello
>
>         Signed-off-by: C O Mitter <committer@example.com>
>         Signed-off-by: C1 E1
>         Helped-by: C2 E2
>         Mentored-by: C4 E4
>         Reported-by: A U Thor <author@example.com>
>         EOF
>         git -c trailer.report.key="Reported-by: " \
>                 -c trailer.report.ifexists="replace" \
>                 -c trailer.report.command="NAME=\"\$ARG\"; test -n \"\$NAME\" && \
>                 git log --author=\"\$NAME\" -1 --format=\"format:%aN <%aE>\" || true" \
>                 commit --trailer "report = author" --amend &&
>         git cat-file commit HEAD >commit.msg &&
>         sed -e "1,/^\$/d" commit.msg >actual &&
>         test_cmp expected actual
>
> [main 6b1e5e9] hello
>  Author: A U Thor <author@example.com>
>  1 file changed, 1 insertion(+)
> [main 97c7a39] hello
>  Author: A U Thor <author@example.com>
>  Date: Thu Apr 7 15:22:13 2005 -0700
>  1 file changed, 1 insertion(+)
> --- expected    2021-04-02 23:43:10.649082950 +0000
> +++ actual      2021-04-02 23:43:10.673085111 +0000
> @@ -4,4 +4,4 @@
>  Signed-off-by: C1 E1
>  Helped-by: C2 E2
>  Mentored-by: C4 E4
> -Reported-by: A U Thor <author@example.com>
> +Reported-by:
> not ok 24 - commit --trailer with -c and command
> #
> #               trailer_commit_base &&
> #               cat >expected <<-\EOF &&
> #               hello
> #
> #               Signed-off-by: C O Mitter <committer@example.com>
> #               Signed-off-by: C1 E1
> #               Helped-by: C2 E2
> #               Mentored-by: C4 E4
> #               Reported-by: A U Thor <author@example.com>
> #               EOF
> #               git -c trailer.report.key="Reported-by: " \
> #                       -c trailer.report.ifexists="replace" \
> #                       -c trailer.report.command="NAME=\"\$ARG\"; test -n \"\$NAME\" && \
> #                       git log --author=\"\$NAME\" -1 --format=\"format:%aN <%aE>\" || true" \
> #                       commit --trailer "report = author" --amend &&
> #               git cat-file commit HEAD >commit.msg &&
> #               sed -e "1,/^\$/d" commit.msg >actual &&
> #               test_cmp expected actual
> #

Little bug, Change it like this will work:

        } else if (conf->command) {
                strbuf_addstr(&cmd, conf->command);
-               strvec_push(&cp.args, cmd.buf);
                if (arg)
                        strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
+               strvec_push(&cp.args, cmd.buf);
        }

thanks.
--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-03  3:22             ` ZheNing Hu
@ 2021-04-03  4:31               ` Junio C Hamano
  2021-04-03  5:15                 ` ZheNing Hu
  0 siblings, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2021-04-03  4:31 UTC (permalink / raw)
  To: ZheNing Hu; +Cc: ZheNing Hu via GitGitGadget, Git List, Christian Couder

ZheNing Hu <adlternative@gmail.com> writes:

> Little bug, Change it like this will work:
>
>         } else if (conf->command) {
>                 strbuf_addstr(&cmd, conf->command);
> -               strvec_push(&cp.args, cmd.buf);
>                 if (arg)
>                         strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
> +               strvec_push(&cp.args, cmd.buf);
>         }

It means the coverage of the test included in this patch was not
sufficient, right?

I queued this separately from the "commit --trailer" topic in
tonight's pushout, but it may make sense to queue this step
immediately on top of the "commit --trailer" patch.

In any case, I suspect we would not hear from Christian right away
due to timezone differences, so perhaps let this v6 simmer on the
list, and then hopefully update the documentation part with his
input.

Thanks.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-02 20:48           ` Junio C Hamano
@ 2021-04-03  5:08             ` ZheNing Hu
  2021-04-04  5:34               ` Junio C Hamano
  2021-04-03  5:51             ` Christian Couder
  2021-04-04  5:43             ` ZheNing Hu
  2 siblings, 1 reply; 60+ messages in thread
From: ZheNing Hu @ 2021-04-03  5:08 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: ZheNing Hu via GitGitGadget, Git List, Christian Couder

Junio C Hamano <gitster@pobox.com> 于2021年4月3日周六 上午4:49写道:

> > +the behavior is as if a special '<token>=<value>' argument were added at
> > +the beginning of the command, <value> will be passed to the user's
> > +command as an empty value.
>
> Do the two occurrences of the word "command" in the sentence refer
> to different things?  I do not think this is an existing problem
> inherited from the original, but as we are trying to improve the
> description, I wonder if we can clarify them a bit.
>
>         ... as if a '<token>=<value>' argument were added at the
>         beginning of the "git interpret-trailers" command, the
>         command specified by this configuration variable will be
>         called with an empty string as the argument.
>
> is my attempt, but I am not still sure what that "as if" part is
> trying to say.  Does it mean with
>
>         [trailer "Foo"] cmd = foo-cmd
>
> and the 'input-file' does not have "Foo: <some existing value>"
> trailer in it, the command "git interpret-trailers input-file"
> would behave as if this command was run
>
>         $ Foo= git interpret-trailers input-file
>
> (as there is no <value>, I am not sure what <value> is used when
> <token>=<value> is prefixed to the command)?
>
> Puzzled and confused utterly am I...  Help, Christian?
>

If we don’t want a input-file without any trailers to execute this command,
 I’m thinking that we maybe need "trailer.<token>.ifmissing=doNothing"
or something else, otherwise execute this 'alwaysRunCmd' for create a
empty trailer like "Signed-off-by" and users can use it to add content.

> >  +
> >  If some '<token>=<value>' arguments are also passed on the command
> >  line, when a 'trailer.<token>.command' is configured, the command will
> >  also be executed for each of these arguments. And the <value> part of
>
> This talks about 'trailer.<token>.command'.  Should this be changed
> to '.cmd'?
>
> Or does everything after "When this option is specified, if there is
> no trailer with ..." apply to both the old .command and new .cmd?
> If so, that was not clear at all---we'd need to clarify this part.
>

Because ".command" will be eliminated, may be only leave those
description Information to ".cmd" is better.

> > -these arguments, if any, will be used to replace the `$ARG` string in
> > -the command.
> > +these arguments, if any, will be passed to the command as first parameter.
> >
> >  EXAMPLES
> >  --------
> > @@ -333,6 +346,55 @@ subject
> >  Fix #42
> >  ------------
> >
> > +* Configure a 'see' trailer with a cmd use a global script `git-one`
> > +  to show the subject of a commit that is related, and show how it works:
> > ++
> > +------------
> > +$ cat ~/bin/git-one
> > +#!/bin/sh
> > +git show -s --pretty=reference "$1"
> > +$ git config trailer.see.key "See-also: "
> > +$ git config trailer.see.ifExists "replace"
> > +$ git config trailer.see.ifMissing "doNothing"
> > +$ git config trailer.see.cmd "~/bin/git-one"
> > +$ git interpret-trailers <<EOF
> > +> subject
> > +>
> > +> message
> > +>
> > +> see: HEAD~2
> > +> EOF
> > +subject
> > +
> > +message
> > +
> > +See-also: fe3187e (subject of related commit, 2021-4-2)
> > +------------
> > +
> > +* Configure a 'who' trailer with a cmd use a global script `git-who`
> > +  to find the recent matching "author <mail>" pair in git log and
> > +  show how it works:
> > ++
> > +------------
> > +$ cat ~/bin/git-who
> > + #!/bin/sh
> > +    git log -1 --format="%an <%ae>" --author="$1"
>
> Unusual indentation here.  But more importantly, I am not sure if
> having both 'see' and 'help' examples is worth it---they are similar
> enough that the second one does not teach anything new to those who
> studied the first one already, aren't they?
>

Ok, I will think about other examples.

> > +$ git config trailer.help.key "Helped-by: "
> > +$ git config trailer.help.ifExists "replace"
> > +$ git config trailer.help.cmd "~/bin/git-who"
> > +$ git interpret-trailers --trailer="help:gitster@" <<EOF
> > +> subject
> > +>
> > +> message
> > +>
> > +> EOF
> > +subject
> > +
> > +message
> > +
> > +Helped-by: Junio C Hamano <gitster@pobox.com>
> > +------------
> > +
> >  * Configure a 'see' trailer with a command to show the subject of a
> >    commit that is related, and show how it works:
> >  +
>
>
>
> > diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
> > index 6602790b5f4c..923923e57573 100755
> > --- a/t/t7513-interpret-trailers.sh
> > +++ b/t/t7513-interpret-trailers.sh
> > @@ -51,6 +51,77 @@ test_expect_success 'setup' '
> >       EOF
> >  '
> >
> > +test_expect_success 'with cmd' '
> > +     test_when_finished "git config --unset trailer.bug.key && \
> > +                         git config --unset trailer.bug.ifExists && \
> > +                         git config --unset trailer.bug.cmd" &&
>
> It is unwise to use && between these three "git config" invocations,
> I suspect.  "git config --unset" exits with non-zero status when you
> attempt to remove with an non-existent key, but you would remove the
> .ifExists and .cmd even if .key is not defined.  Perhaps
>
>         test_when_finished "git config --unset-all trailer.bug.key
>                             git config --unset-all trailer.bug.ifExists
>                             git config --unset-all trailer.bug.cmd" &&
>
> would be more sensible.  Or if we just want to remove everything
> under the trailer.bug.* section, this might be even better:
>
>         test_when_finished "git config --remove-section trailer.bug" &&
>
> as we can add new trailer.bug.* to the system and use them in this
> test, but removing the entire section would still be a good way to
> clean after ourselves.
>

Good idea, This removing is also more efficient.

> > diff --git a/trailer.c b/trailer.c
> > index be4e9726421c..6aeff6a1bd33 100644
> > --- a/trailer.c
> > +++ b/trailer.c
> > ...
> > +static char *apply_command(struct conf_info *conf, const char *arg)
> >  {
> >       struct strbuf cmd = STRBUF_INIT;
> >       struct strbuf buf = STRBUF_INIT;
> >       struct child_process cp = CHILD_PROCESS_INIT;
> >       char *result;
> >
> > -     strbuf_addstr(&cmd, command);
> > -     if (arg)
> > -             strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
> > -
> > -     strvec_push(&cp.args, cmd.buf);
> > +     if (conf->cmd) {
> > +             // cp.shell_no_implicit_args = 1;
>
> Do not add new code that is commented out.  Besides we do not use // comment.
>
> > +             strbuf_addstr(&cmd, conf->cmd);
> > +             strvec_push(&cp.args, cmd.buf);
> > +             if (arg)
> > +                     strvec_push(&cp.args, arg);
>
> Thanks.

Thanks.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-03  4:31               ` Junio C Hamano
@ 2021-04-03  5:15                 ` ZheNing Hu
  0 siblings, 0 replies; 60+ messages in thread
From: ZheNing Hu @ 2021-04-03  5:15 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: ZheNing Hu via GitGitGadget, Git List, Christian Couder

Junio C Hamano <gitster@pobox.com> 于2021年4月3日周六 下午12:31写道:
>
> ZheNing Hu <adlternative@gmail.com> writes:
>
> > Little bug, Change it like this will work:
> >
> >         } else if (conf->command) {
> >                 strbuf_addstr(&cmd, conf->command);
> > -               strvec_push(&cp.args, cmd.buf);
> >                 if (arg)
> >                         strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
> > +               strvec_push(&cp.args, cmd.buf);
> >         }
>
> It means the coverage of the test included in this patch was not
> sufficient, right?
>

Indeed, I might have to think what tests need to be added to the ".cmd".

> I queued this separately from the "commit --trailer" topic in
> tonight's pushout, but it may make sense to queue this step
> immediately on top of the "commit --trailer" patch.
>

Maybe I can rebase this "trailer .cmd" to "master" which already have
include "commit --trailer" later, this will cover "commit --trailer" tests.

> In any case, I suspect we would not hear from Christian right away
> due to timezone differences, so perhaps let this v6 simmer on the
> list, and then hopefully update the documentation part with his
> input.
>

Yes, it's worth waiting patiently,

> Thanks.

Thanks.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-02 20:48           ` Junio C Hamano
  2021-04-03  5:08             ` ZheNing Hu
@ 2021-04-03  5:51             ` Christian Couder
  2021-04-04 23:26               ` Junio C Hamano
  2021-04-04  5:43             ` ZheNing Hu
  2 siblings, 1 reply; 60+ messages in thread
From: Christian Couder @ 2021-04-03  5:51 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: ZheNing Hu via GitGitGadget, git, ZheNing Hu

On Fri, Apr 2, 2021 at 10:49 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:

> > +trailer.<token>.cmd::
> > +     The command specified by this configuration variable is run
> > +     with a single parameter, which is the <value> part of an

I prefer "argument" over "parameter". In the next paragraph you use
"argument" (in "as if a special '<token>=<value>' argument"), so it
would be more consistent and easier to read if "argument" was used
here too.

> > +     existing trailer with the same <token>.

It would be better to say something like "which is the <value> part of
a `--trailer <token>=<value>` on the command line". (Yeah, the
existing doc might be bad about this.)

Also it might be better to explicitly say that the command is run once
for each `--trailer <token>=<value>` on the command line with the same
<token>.

> > The output from the
> > +     command is then used as the value for the <token> in the
> > +     resulting trailer.
> > ++
> > +When this option is specified, If there is no trailer with same <token>,

As above, it's better to be more explicit and say something like "if
there is no `--trailer <token>=<value>` with the same <token> on the
command line". That's because there could already be such trailers in
the input file, but those don't trigger the command.

Anyway see below as you might need to change the whole paragraph anyway.

> s/If/if/ (downcase).
>
> > +the behavior is as if a special '<token>=<value>' argument were added at
> > +the beginning of the command, <value> will be passed to the user's
> > +command as an empty value.

The problem with this is that people could understand that the command
will run with an empty argument only if there is no trailer with the
same <token>. The current situation though is that the command will
run once with an empty argument whether there are `--trailer
<token>=<value>` options with the same <token> passed on the command
line or not.

> Do the two occurrences of the word "command" in the sentence refer
> to different things?  I do not think this is an existing problem
> inherited from the original, but as we are trying to improve the
> description, I wonder if we can clarify them a bit.
>
>         ... as if a '<token>=<value>' argument were added at the
>         beginning of the "git interpret-trailers" command, the
>         command specified by this configuration variable will be
>         called with an empty string as the argument.
>
> is my attempt,

It looks better to me.

> but I am not still sure what that "as if" part is
> trying to say.  Does it mean with
>
>         [trailer "Foo"] cmd = foo-cmd
>
> and the 'input-file' does not have "Foo: <some existing value>"
> trailer in it, the command "git interpret-trailers input-file"
> would behave as if this command was run
>
>         $ Foo= git interpret-trailers input-file

I would say it would behave as if:

$ git interpret-trailers --trailer Foo= input-file

> (as there is no <value>, I am not sure what <value> is used when
> <token>=<value> is prefixed to the command)?

Nothing is done when such things are prefixed to the command.

> Puzzled and confused utterly am I...  Help, Christian?

I hope the above helps.

> >  If some '<token>=<value>' arguments are also passed on the command
> >  line, when a 'trailer.<token>.command' is configured, the command will
> >  also be executed for each of these arguments. And the <value> part of
>
> This talks about 'trailer.<token>.command'.  Should this be changed
> to '.cmd'?

I think so.

> Or does everything after "When this option is specified, if there is
> no trailer with ..." apply to both the old .command and new .cmd?
> If so, that was not clear at all---we'd need to clarify this part.

Yeah, in the doc about ".command" I think we should say that it
behaves in a similar way as ".cmd" except for the $ARG vs $1 issue.

> > -these arguments, if any, will be used to replace the `$ARG` string in
> > -the command.
> > +these arguments, if any, will be passed to the command as first parameter.

s/as first parameter/as its first argument/

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-03  5:08             ` ZheNing Hu
@ 2021-04-04  5:34               ` Junio C Hamano
  0 siblings, 0 replies; 60+ messages in thread
From: Junio C Hamano @ 2021-04-04  5:34 UTC (permalink / raw)
  To: ZheNing Hu; +Cc: ZheNing Hu via GitGitGadget, Git List, Christian Couder

ZheNing Hu <adlternative@gmail.com> writes:

>> >  If some '<token>=<value>' arguments are also passed on the command
>> >  line, when a 'trailer.<token>.command' is configured, the command will
>> >  also be executed for each of these arguments. And the <value> part of
>>
>> This talks about 'trailer.<token>.command'.  Should this be changed
>> to '.cmd'?
>>
>> Or does everything after "When this option is specified, if there is
>> no trailer with ..." apply to both the old .command and new .cmd?
>> If so, that was not clear at all---we'd need to clarify this part.
>>
>
> Because ".command" will be eliminated, may be only leave those
> description Information to ".cmd" is better.

Not really.

Until .command goes away, people who want to migrate a config file
written in the .command days to .cmd would need to know what rules
govern .command variant.  Otherwise they would not know what the
original they inherited, using .command, wanted to do, and have no
way to emulate it with the new .cmd approach.  If something is
shared between the two, at least you need to mention that it applies
to both.

Until .command actually gets removed, that is.

>> Unusual indentation here.  But more importantly, I am not sure if
>> having both 'see' and 'help' examples is worth it---they are similar
>> enough that the second one does not teach anything new to those who
>> studied the first one already, aren't they?
>>
>
> Ok, I will think about other examples.

Or just use one, and let somebody else in the future encounter real
world example that is different enough to come up with a follow up
patch to describe that example.

>> > diff --git a/trailer.c b/trailer.c
>> > index be4e9726421c..6aeff6a1bd33 100644
>> > --- a/trailer.c
>> > +++ b/trailer.c
>> > ...
>> > -     strvec_push(&cp.args, cmd.buf);
>> > +     if (conf->cmd) {
>> > +             // cp.shell_no_implicit_args = 1;
>>
>> Do not add new code that is commented out.  Besides we do not use // comment.
>>
>> > +             strbuf_addstr(&cmd, conf->cmd);
>> > +             strvec_push(&cp.args, cmd.buf);
>> > +             if (arg)
>> > +                     strvec_push(&cp.args, arg);
>>
>> Thanks.
>
> Thanks.


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-02 20:48           ` Junio C Hamano
  2021-04-03  5:08             ` ZheNing Hu
  2021-04-03  5:51             ` Christian Couder
@ 2021-04-04  5:43             ` ZheNing Hu
  2021-04-04  8:52               ` Christian Couder
  2 siblings, 1 reply; 60+ messages in thread
From: ZheNing Hu @ 2021-04-04  5:43 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: ZheNing Hu via GitGitGadget, Git List, Christian Couder

Hi, Junio and Christian,

> > +------------
> > +$ cat ~/bin/git-one
> > +#!/bin/sh
> > +git show -s --pretty=reference "$1"
> > +$ git config trailer.see.key "See-also: "
> > +$ git config trailer.see.ifExists "replace"
> > +$ git config trailer.see.ifMissing "doNothing"
> > +$ git config trailer.see.cmd "~/bin/git-one"
> > +$ git interpret-trailers <<EOF
> > +> subject
> > +>
> > +> message
> > +>
> > +> see: HEAD~2
> > +> EOF
> > +subject
> > +
> > +message
> > +
> > +See-also: fe3187e (subject of related commit, 2021-4-2)
> > +------------
> > +
> > +* Configure a 'who' trailer with a cmd use a global script `git-who`
> > +  to find the recent matching "author <mail>" pair in git log and
> > +  show how it works:
> > ++
> > +------------
> > +$ cat ~/bin/git-who
> > + #!/bin/sh
> > +    git log -1 --format="%an <%ae>" --author="$1"
>
> Unusual indentation here.  But more importantly, I am not sure if
> having both 'see' and 'help' examples is worth it---they are similar
> enough that the second one does not teach anything new to those who
> studied the first one already, aren't they?
>

This may be an off-topic question:
I wanted to use `shortlog -s` instead of the document example,
But I found a very strange place:
Here we have two shell scripts:

$ cat ~/bin/gcount
#!/bin/sh
if test "$1" != ""
then
git log -1 --author="$1"
else
echo "hello there"
fi

cat ~/bin/gcount2
#!/bin/sh
if test "$1" != ""
then
git shortlog -1 --author="$1"
else
echo "hello there"
fi

If I use them in the terminal, there is no problem with them,

$ ~/bin/gcount gitster
commit 142430338477d9d1bb25be66267225fb58498d92
(interpret-trailers-command-arg, abc5b)
Author: Junio C Hamano <gitster@pobox.com>
Date:   Mon Mar 22 14:00:00 2021 -0700

    The second batch

    Signed-off-by: Junio C Hamano <gitster@pobox.com>

$ ~/bin/gcount2 gitster
Junio C Hamano (1):
      The second batch

if I use .cmd to run these scripts, the situation is totally different:

$ git config -l | grep trailer
trailer.cnt.ifexists=add
trailer.cnt.key=Cnt:
trailer.cnt.cmd=~/bin/gcount

$ git interpret-trailers --trailer="cnt:gitster" <<EOF
EOF

Cnt: hello there
Cnt: commit 142430338477d9d1bb25be66267225fb58498d92
Author: Junio C Hamano <gitster@pobox.com>
Date:   Mon Mar 22 14:00:00 2021 -0700

    The second batch

    Signed-off-by: Junio C Hamano <gitster@pobox.com>

And if I turn to use gcount2:
$ git config trailer.cnt.cmd "~/bin/gcount2"
$ git interpret-trailers --trailer="cnt:gitster" <<EOF
EOF

Cnt: hello there
Cnt:

It looks like `shortlog` does not write to standard output.
Note that in `short_log.c`, log will be output to `log->file`.
Does it make the above behavior different?
Is there a good solution?


> > +$ git config trailer.help.key "Helped-by: "
> > +$ git config trailer.help.ifExists "replace"
> > +$ git config trailer.help.cmd "~/bin/git-who"
> > +$ git interpret-trailers --trailer="help:gitster@" <<EOF
> > +> subject
> > +>
> > +> message
> > +>
> > +> EOF
> > +subject
> > +
> > +message
> > +
> > +Helped-by: Junio C Hamano <gitster@pobox.com>
> > +------------
> > +
> >  * Configure a 'see' trailer with a command to show the subject of a
> >    commit that is related, and show how it works:
> >  +

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-04  5:43             ` ZheNing Hu
@ 2021-04-04  8:52               ` Christian Couder
  2021-04-04  9:53                 ` ZheNing Hu
  0 siblings, 1 reply; 60+ messages in thread
From: Christian Couder @ 2021-04-04  8:52 UTC (permalink / raw)
  To: ZheNing Hu; +Cc: Junio C Hamano, ZheNing Hu via GitGitGadget, Git List

On Sun, Apr 4, 2021 at 7:44 AM ZheNing Hu <adlternative@gmail.com> wrote:

> This may be an off-topic question:
> I wanted to use `shortlog -s` instead of the document example,
> But I found a very strange place:
> Here we have two shell scripts:
>
> $ cat ~/bin/gcount
> #!/bin/sh
> if test "$1" != ""

It's better to use `test -n "$1"` instead of `test "$1" != ""`.

> then
> git log -1 --author="$1"
> else
> echo "hello there"
> fi
>
> cat ~/bin/gcount2
> #!/bin/sh
> if test "$1" != ""
> then
> git shortlog -1 --author="$1"
> else
> echo "hello there"
> fi
>
> If I use them in the terminal, there is no problem with them,
>
> $ ~/bin/gcount gitster
> commit 142430338477d9d1bb25be66267225fb58498d92
> (interpret-trailers-command-arg, abc5b)
> Author: Junio C Hamano <gitster@pobox.com>
> Date:   Mon Mar 22 14:00:00 2021 -0700
>
>     The second batch
>
>     Signed-off-by: Junio C Hamano <gitster@pobox.com>
>
> $ ~/bin/gcount2 gitster
> Junio C Hamano (1):
>       The second batch
>
> if I use .cmd to run these scripts, the situation is totally different:
>
> $ git config -l | grep trailer
> trailer.cnt.ifexists=add
> trailer.cnt.key=Cnt:
> trailer.cnt.cmd=~/bin/gcount

The script/command used in ".cmd" or ".command" should really return a
short string with no newline or line feed char in it. Here your script
can return multiple lines which could mess things up and be difficult
to understand.

> $ git interpret-trailers --trailer="cnt:gitster" <<EOF
> EOF
>
> Cnt: hello there
> Cnt: commit 142430338477d9d1bb25be66267225fb58498d92
> Author: Junio C Hamano <gitster@pobox.com>
> Date:   Mon Mar 22 14:00:00 2021 -0700
>
>     The second batch
>
>     Signed-off-by: Junio C Hamano <gitster@pobox.com>
>
> And if I turn to use gcount2:
> $ git config trailer.cnt.cmd "~/bin/gcount2"
> $ git interpret-trailers --trailer="cnt:gitster" <<EOF
> EOF
>
> Cnt: hello there
> Cnt:
>
> It looks like `shortlog` does not write to standard output.

In shortlog's doc there is:

"If no revisions are passed on the command line and either standard
input is not a terminal or there is no current branch, git shortlog
will output a summary of the log read from standard input, without
reference to the current repository."

I guess that's what's happening here.

> Note that in `short_log.c`, log will be output to `log->file`.
> Does it make the above behavior different?
> Is there a good solution?

I would try to pass a revision on the command line.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-04  8:52               ` Christian Couder
@ 2021-04-04  9:53                 ` ZheNing Hu
  0 siblings, 0 replies; 60+ messages in thread
From: ZheNing Hu @ 2021-04-04  9:53 UTC (permalink / raw)
  To: Christian Couder; +Cc: Junio C Hamano, ZheNing Hu via GitGitGadget, Git List

Christian Couder <christian.couder@gmail.com> 于2021年4月4日周日 下午4:52写道:
>
> On Sun, Apr 4, 2021 at 7:44 AM ZheNing Hu <adlternative@gmail.com> wrote:
>
> > This may be an off-topic question:
> > I wanted to use `shortlog -s` instead of the document example,
> > But I found a very strange place:
> > Here we have two shell scripts:
> >
> > $ cat ~/bin/gcount
> > #!/bin/sh
> > if test "$1" != ""
>
> It's better to use `test -n "$1"` instead of `test "$1" != ""`.
>
> > then
> > git log -1 --author="$1"
> > else
> > echo "hello there"
> > fi
> >
> > cat ~/bin/gcount2
> > #!/bin/sh
> > if test "$1" != ""
> > then
> > git shortlog -1 --author="$1"
> > else
> > echo "hello there"
> > fi
> >
> > If I use them in the terminal, there is no problem with them,
> >
> > $ ~/bin/gcount gitster
> > commit 142430338477d9d1bb25be66267225fb58498d92
> > (interpret-trailers-command-arg, abc5b)
> > Author: Junio C Hamano <gitster@pobox.com>
> > Date:   Mon Mar 22 14:00:00 2021 -0700
> >
> >     The second batch
> >
> >     Signed-off-by: Junio C Hamano <gitster@pobox.com>
> >
> > $ ~/bin/gcount2 gitster
> > Junio C Hamano (1):
> >       The second batch
> >
> > if I use .cmd to run these scripts, the situation is totally different:
> >
> > $ git config -l | grep trailer
> > trailer.cnt.ifexists=add
> > trailer.cnt.key=Cnt:
> > trailer.cnt.cmd=~/bin/gcount
>
> The script/command used in ".cmd" or ".command" should really return a
> short string with no newline or line feed char in it. Here your script
> can return multiple lines which could mess things up and be difficult
> to understand.
>

Yes, it's just a small test, I want use `git shortlog -s --author="$1"` to show
commit count of one author.

> > $ git interpret-trailers --trailer="cnt:gitster" <<EOF
> > EOF
> >
> > Cnt: hello there
> > Cnt: commit 142430338477d9d1bb25be66267225fb58498d92
> > Author: Junio C Hamano <gitster@pobox.com>
> > Date:   Mon Mar 22 14:00:00 2021 -0700
> >
> >     The second batch
> >
> >     Signed-off-by: Junio C Hamano <gitster@pobox.com>
> >
> > And if I turn to use gcount2:
> > $ git config trailer.cnt.cmd "~/bin/gcount2"
> > $ git interpret-trailers --trailer="cnt:gitster" <<EOF
> > EOF
> >
> > Cnt: hello there
> > Cnt:
> >
> > It looks like `shortlog` does not write to standard output.
>
> In shortlog's doc there is:
>
> "If no revisions are passed on the command line and either standard
> input is not a terminal or there is no current branch, git shortlog
> will output a summary of the log read from standard input, without
> reference to the current repository."
>
> I guess that's what's happening here.
>

This solved my confusion ;-)

> > Note that in `short_log.c`, log will be output to `log->file`.
> > Does it make the above behavior different?
> > Is there a good solution?
>
> I would try to pass a revision on the command line.

Thanks, Christian.
--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v7] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-02 13:26         ` [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option ZheNing Hu via GitGitGadget
  2021-04-02 20:48           ` Junio C Hamano
  2021-04-02 23:44           ` Junio C Hamano
@ 2021-04-04 13:11           ` ZheNing Hu via GitGitGadget
  2021-04-06 16:23             ` Christian Couder
  2021-04-09 13:37             ` [PATCH v8 0/2] [GSOC] trailer: add new .cmd " ZheNing Hu via GitGitGadget
  2 siblings, 2 replies; 60+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-04-04 13:11 UTC (permalink / raw)
  To: git; +Cc: Christian Couder, Junio C Hamano, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name of or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token.  This has two downsides:

* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.

* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatching single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor'), it would result in
a broken command that is not syntactically correct (or
worse).

Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as a
parameter to the command.  Instead of "$ARG", the users will
refer to the value as positional argument, $1, in their
scripts.

Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
    [GSOC] trailer: add new trailer..cmd config option
    
    In https://lore.kernel.org/git/xmqqv99i4ck2.fsf@gitster.g/ Junio and
    Christian talked about the problem of using strbuf_replace() to replace
    $ARG:
    
     1. if user's script have more than one $ARG, only the first one will be
        replaced, which is incorrected.
     2. $ARG is textually replaced without shell syntax, which may result a
        broken command when $ARG include some unmatching single quote, very
        unsafe.
    
    Now pass trailer value as $1 to the trailer command with another
    trailer.<token>.cmd config, to solve these above two problems,
    
    We are now writing documents that are more readable and correct than
    before.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-913%2Fadlternative%2Ftrailer-pass-ARG-env-v7
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-913/adlternative/trailer-pass-ARG-env-v7
Pull-Request: https://github.com/gitgitgadget/git/pull/913

Range-diff vs v6:

 1:  3aed77d077b9 ! 1:  1e9a6572ac6f [GSOC] trailer: add new trailer.<token>.cmd config option
     @@ Commit message
          pair), which a user would expect to stay intact, would be
          replaced, and worse, if the value had an unmatching single
          quote (imagine a name like "O'Connor", substituted into
     -    NAME='$ARG' to make it NAME='O'Connor), it would result in
     +    NAME='$ARG' to make it NAME='O'Connor'), it would result in
          a broken command that is not syntactically correct (or
          worse).
      
     @@ Documentation/git-interpret-trailers.txt: trailer.<token>.command::
      -off.
      +When this option is specified, the first occurrence of substring $ARG is
      +replaced with the value given to the `interpret-trailer` command for the
     -+same token.
     ++same token. This option behaves in a similar way as ".cmd", however, it
     ++passes the value through $ARG.
       +
      -If the command contains the `$ARG` string, this string will be
      -replaced with the <value> part of an existing trailer with the same
     @@ Documentation/git-interpret-trailers.txt: trailer.<token>.command::
      +
      +trailer.<token>.cmd::
      +	The command specified by this configuration variable is run
     -+	with a single parameter, which is the <value> part of an
     -+	existing trailer with the same <token>.  The output from the
     -+	command is then used as the value for the <token> in the
     -+	resulting trailer.
     ++	with a single argument, which is the <value> part of a
     ++	`--trailer <token>=<value>` on the command line. The output
     ++	from the command is then used as the value for the <token>
     ++	in the resulting trailer.
      ++
     -+When this option is specified, If there is no trailer with same <token>,
     -+the behavior is as if a special '<token>=<value>' argument were added at
     -+the beginning of the command, <value> will be passed to the user's
     -+command as an empty value.
     ++When this option is specified, the behavior is as if a '<token>=<value>'
     ++argument were added at the beginning of the "git interpret-trailers"
     ++command, the command specified by this configuration variable will be
     ++called with an empty string as the argument.
       +
       If some '<token>=<value>' arguments are also passed on the command
     - line, when a 'trailer.<token>.command' is configured, the command will
     - also be executed for each of these arguments. And the <value> part of
     +-line, when a 'trailer.<token>.command' is configured, the command will
     +-also be executed for each of these arguments. And the <value> part of
      -these arguments, if any, will be used to replace the `$ARG` string in
      -the command.
     -+these arguments, if any, will be passed to the command as first parameter.
     ++line, when a 'trailer.<token>.cmd' is configured, the command is run
     ++once for each `--trailer <token>=<value>` on the command line with the
     ++same <token>. And the <value> part of these arguments, if any, will be
     ++passed to the command as its first argument.
       
       EXAMPLES
       --------
     @@ Documentation/git-interpret-trailers.txt: subject
       Fix #42
       ------------
       
     -+* Configure a 'see' trailer with a cmd use a global script `git-one`
     -+  to show the subject of a commit that is related, and show how it works:
     ++* Configure a 'cnt' trailer with a cmd use a global script `gcount`
     ++to record commit counts of a specified author and show how it works:
      ++
      +------------
     -+$ cat ~/bin/git-one
     ++$ cat ~/bin/gcount
      +#!/bin/sh
     -+git show -s --pretty=reference "$1"
     -+$ git config trailer.see.key "See-also: "
     -+$ git config trailer.see.ifExists "replace"
     -+$ git config trailer.see.ifMissing "doNothing"
     -+$ git config trailer.see.cmd "~/bin/git-one"
     -+$ git interpret-trailers <<EOF
     ++test -n "$1" && git shortlog -s --author="$1" HEAD || true
     ++$ git config trailer.cnt.key "Commit-count: "
     ++$ git config trailer.cnt.ifExists "replace"
     ++$ git config trailer.cnt.cmd "~/bin/gcount"
     ++$ git interpret-trailers --trailer="cnt:Junio" <<EOF
      +> subject
      +> 
      +> message
      +> 
     -+> see: HEAD~2
      +> EOF
      +subject
      +
      +message
      +
     -+See-also: fe3187e (subject of related commit, 2021-4-2)
     ++Commit-count: 22484     Junio C Hamano
      +------------
      +
     -+* Configure a 'who' trailer with a cmd use a global script `git-who`
     -+  to find the recent matching "author <mail>" pair in git log and
     -+  show how it works:
     ++* Configure a 'ref' trailer with a cmd use a global script `glog-grep`
     ++  to grep last relevant commit from git log in the git repository
     ++  and show how it works:
      ++
      +------------
     -+$ cat ~/bin/git-who
     -+ #!/bin/sh
     -+    git log -1 --format="%an <%ae>" --author="$1"
     -+$ git config trailer.help.key "Helped-by: "
     -+$ git config trailer.help.ifExists "replace"
     -+$ git config trailer.help.cmd "~/bin/git-who"
     -+$ git interpret-trailers --trailer="help:gitster@" <<EOF
     ++$ cat ~/bin/glog-grep
     ++#!/bin/sh
     ++test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
     ++$ git config trailer.ref.key "Reference-to: "
     ++$ git config trailer.ref.ifExists "replace"
     ++$ git config trailer.ref.cmd "~/bin/glog-grep"
     ++$ git interpret-trailers --trailer="ref:Add copyright notices." <<EOF
      +> subject
      +> 
      +> message
     @@ Documentation/git-interpret-trailers.txt: subject
      +
      +message
      +
     -+Helped-by: Junio C Hamano <gitster@pobox.com>
     ++Reference-to: 8bc9a0c769 (Add copyright notices., 2005-04-07)
      +------------
      +
       * Configure a 'see' trailer with a command to show the subject of a
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup' '
       '
       
      +test_expect_success 'with cmd' '
     -+	test_when_finished "git config --unset trailer.bug.key && \
     -+			    git config --unset trailer.bug.ifExists && \
     -+			    git config --unset trailer.bug.cmd" &&
     ++	test_when_finished "git config --remove-section trailer.bug" &&
      +	git config trailer.bug.key "Bug-maker: " &&
      +	git config trailer.bug.ifExists "add" &&
      +	git config trailer.bug.cmd "echo \"maybe is\"" &&
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup' '
      +'
      +
      +test_expect_success 'with cmd and $1' '
     -+	test_when_finished "git config --unset trailer.bug.key && \
     -+			    git config --unset trailer.bug.ifExists && \
     -+			    git config --unset trailer.bug.cmd" &&
     ++	test_when_finished "git config --remove-section trailer.bug" &&
      +	git config trailer.bug.key "Bug-maker: " &&
      +	git config trailer.bug.ifExists "replace" &&
      +	git config trailer.bug.cmd "echo \"\$1\" is" &&
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup' '
      +'
      +
      +test_expect_success 'with cmd and $1 with sh -c' '
     -+	test_when_finished "git config --unset trailer.bug.key && \
     -+			    git config --unset trailer.bug.ifExists && \
     -+			    git config --unset trailer.bug.cmd" &&
     ++	test_when_finished "git config --remove-section trailer.bug" &&
      +	git config trailer.bug.key "Bug-maker: " &&
      +	git config trailer.bug.ifExists "replace" &&
      +	git config trailer.bug.cmd "sh -c \"echo who is \"\$1\"\"" &&
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup' '
      +'
      +
      +test_expect_success 'with cmd and $1 with shell script' '
     -+	test_when_finished "git config --unset trailer.bug.key && \
     -+			    git config --unset trailer.bug.ifExists && \
     -+			    git config --unset trailer.bug.cmd" &&
     ++	test_when_finished "git config --remove-section trailer.bug" &&
      +	git config trailer.bug.key "Bug-maker: " &&
      +	git config trailer.bug.ifExists "replace" &&
      +	git config trailer.bug.cmd "./echoscript" &&
     @@ trailer.c: static int check_if_different(struct trailer_item *in_tok,
      -
      -	strvec_push(&cp.args, cmd.buf);
      +	if (conf->cmd) {
     -+		// cp.shell_no_implicit_args = 1;
      +		strbuf_addstr(&cmd, conf->cmd);
      +		strvec_push(&cp.args, cmd.buf);
      +		if (arg)
      +			strvec_push(&cp.args, arg);
      +	} else if (conf->command) {
      +		strbuf_addstr(&cmd, conf->command);
     -+		strvec_push(&cp.args, cmd.buf);
      +		if (arg)
      +			strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
     ++		strvec_push(&cp.args, cmd.buf);
      +	}
       	cp.env = local_repo_env;
       	cp.no_stdin = 1;


 Documentation/git-interpret-trailers.txt | 86 +++++++++++++++++++----
 t/t7513-interpret-trailers.sh            | 87 +++++++++++++++++++++++-
 trailer.c                                | 37 +++++++---
 3 files changed, 186 insertions(+), 24 deletions(-)

diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 96ec6499f001..83600e93390d 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -236,21 +236,36 @@ trailer.<token>.command::
 	be called to automatically add or modify a trailer with the
 	specified <token>.
 +
-When this option is specified, the behavior is as if a special
-'<token>=<value>' argument were added at the beginning of the command
-line, where <value> is taken to be the standard output of the
-specified command with any leading and trailing whitespace trimmed
-off.
+When this option is specified, the first occurrence of substring $ARG is
+replaced with the value given to the `interpret-trailer` command for the
+same token. This option behaves in a similar way as ".cmd", however, it
+passes the value through $ARG.
 +
-If the command contains the `$ARG` string, this string will be
-replaced with the <value> part of an existing trailer with the same
-<token>, if any, before the command is launched.
+".command" has been deprecated due to the $ARG in the user's command can
+only be replaced once and the original way of replacing $ARG was not safe.
+Now the preferred option is using "trailer.<token>.cmd", which use position
+argument to pass the value.
++
+When both .cmd and .command are given for the same <token>,
+.cmd is used and .command is ignored.
+
+trailer.<token>.cmd::
+	The command specified by this configuration variable is run
+	with a single argument, which is the <value> part of a
+	`--trailer <token>=<value>` on the command line. The output
+	from the command is then used as the value for the <token>
+	in the resulting trailer.
++
+When this option is specified, the behavior is as if a '<token>=<value>'
+argument were added at the beginning of the "git interpret-trailers"
+command, the command specified by this configuration variable will be
+called with an empty string as the argument.
 +
 If some '<token>=<value>' arguments are also passed on the command
-line, when a 'trailer.<token>.command' is configured, the command will
-also be executed for each of these arguments. And the <value> part of
-these arguments, if any, will be used to replace the `$ARG` string in
-the command.
+line, when a 'trailer.<token>.cmd' is configured, the command is run
+once for each `--trailer <token>=<value>` on the command line with the
+same <token>. And the <value> part of these arguments, if any, will be
+passed to the command as its first argument.
 
 EXAMPLES
 --------
@@ -333,6 +348,53 @@ subject
 Fix #42
 ------------
 
+* Configure a 'cnt' trailer with a cmd use a global script `gcount`
+to record commit counts of a specified author and show how it works:
++
+------------
+$ cat ~/bin/gcount
+#!/bin/sh
+test -n "$1" && git shortlog -s --author="$1" HEAD || true
+$ git config trailer.cnt.key "Commit-count: "
+$ git config trailer.cnt.ifExists "replace"
+$ git config trailer.cnt.cmd "~/bin/gcount"
+$ git interpret-trailers --trailer="cnt:Junio" <<EOF
+> subject
+> 
+> message
+> 
+> EOF
+subject
+
+message
+
+Commit-count: 22484     Junio C Hamano
+------------
+
+* Configure a 'ref' trailer with a cmd use a global script `glog-grep`
+  to grep last relevant commit from git log in the git repository
+  and show how it works:
++
+------------
+$ cat ~/bin/glog-grep
+#!/bin/sh
+test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
+$ git config trailer.ref.key "Reference-to: "
+$ git config trailer.ref.ifExists "replace"
+$ git config trailer.ref.cmd "~/bin/glog-grep"
+$ git interpret-trailers --trailer="ref:Add copyright notices." <<EOF
+> subject
+> 
+> message
+> 
+> EOF
+subject
+
+message
+
+Reference-to: 8bc9a0c769 (Add copyright notices., 2005-04-07)
+------------
+
 * Configure a 'see' trailer with a command to show the subject of a
   commit that is related, and show how it works:
 +
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 6602790b5f4c..6d26a2606604 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -51,6 +51,69 @@ test_expect_success 'setup' '
 	EOF
 '
 
+test_expect_success 'with cmd' '
+	test_when_finished "git config --remove-section trailer.bug" &&
+	git config trailer.bug.key "Bug-maker: " &&
+	git config trailer.bug.ifExists "add" &&
+	git config trailer.bug.cmd "echo \"maybe is\"" &&
+	cat >expected2 <<-EOF &&
+
+	Bug-maker: maybe is
+	Bug-maker: maybe is him
+	Bug-maker: maybe is me
+	EOF
+	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+		>actual2 &&
+	test_cmp expected2 actual2
+'
+
+test_expect_success 'with cmd and $1' '
+	test_when_finished "git config --remove-section trailer.bug" &&
+	git config trailer.bug.key "Bug-maker: " &&
+	git config trailer.bug.ifExists "replace" &&
+	git config trailer.bug.cmd "echo \"\$1\" is" &&
+	cat >expected2 <<-EOF &&
+
+	Bug-maker: me is me
+	EOF
+	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+		>actual2 &&
+	test_cmp expected2 actual2
+'
+
+test_expect_success 'with cmd and $1 with sh -c' '
+	test_when_finished "git config --remove-section trailer.bug" &&
+	git config trailer.bug.key "Bug-maker: " &&
+	git config trailer.bug.ifExists "replace" &&
+	git config trailer.bug.cmd "sh -c \"echo who is \"\$1\"\"" &&
+	cat >expected2 <<-EOF &&
+
+	Bug-maker: who is me
+	EOF
+	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+		>actual2 &&
+	test_cmp expected2 actual2
+'
+
+test_expect_success 'with cmd and $1 with shell script' '
+	test_when_finished "git config --remove-section trailer.bug" &&
+	git config trailer.bug.key "Bug-maker: " &&
+	git config trailer.bug.ifExists "replace" &&
+	git config trailer.bug.cmd "./echoscript" &&
+	cat >expected2 <<-EOF &&
+
+	Bug-maker: who is me
+	EOF
+	cat >echoscript <<-EOF &&
+	#!/bin/sh
+	echo who is "\$1"
+	EOF
+	chmod +x echoscript &&
+	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+		>actual2 &&
+	test_cmp expected2 actual2
+'
+
 test_expect_success 'without config' '
 	sed -e "s/ Z\$/ /" >expected <<-\EOF &&
 
@@ -1274,9 +1337,31 @@ test_expect_success 'setup a commit' '
 	git commit -m "Add file a.txt"
 '
 
+test_expect_success 'cmd takes precedence over command' '
+	test_when_finished "git config --unset trailer.fix.cmd" &&
+	git config trailer.fix.ifExists "replace" &&
+	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
+		--abbrev-commit --abbrev=14 \"\$1\" || true" &&
+	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \$ARG" &&
+	FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
+	cat complex_message_body >expected2 &&
+	sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
+		Fixes: $FIXED
+		Acked-by= Z
+		Reviewed-by:
+		Signed-off-by: Z
+		Signed-off-by: A U Thor <author@example.com>
+	EOF
+	git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+		<complex_message >actual2 &&
+	test_cmp expected2 actual2
+'
+
 test_expect_success 'with command using $ARG' '
 	git config trailer.fix.ifExists "replace" &&
-	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \$ARG" &&
 	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
 	cat complex_message_body >expected &&
 	sed -e "s/ Z\$/ /" >>expected <<-EOF &&
diff --git a/trailer.c b/trailer.c
index be4e9726421c..bd384befe15b 100644
--- a/trailer.c
+++ b/trailer.c
@@ -14,6 +14,7 @@ struct conf_info {
 	char *name;
 	char *key;
 	char *command;
+	char *cmd;
 	enum trailer_where where;
 	enum trailer_if_exists if_exists;
 	enum trailer_if_missing if_missing;
@@ -127,6 +128,7 @@ static void free_arg_item(struct arg_item *item)
 	free(item->conf.name);
 	free(item->conf.key);
 	free(item->conf.command);
+	free(item->conf.cmd);
 	free(item->token);
 	free(item->value);
 	free(item);
@@ -216,18 +218,24 @@ static int check_if_different(struct trailer_item *in_tok,
 	return 1;
 }
 
-static char *apply_command(const char *command, const char *arg)
+static char *apply_command(struct conf_info *conf, const char *arg)
 {
 	struct strbuf cmd = STRBUF_INIT;
 	struct strbuf buf = STRBUF_INIT;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	char *result;
 
-	strbuf_addstr(&cmd, command);
-	if (arg)
-		strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
-
-	strvec_push(&cp.args, cmd.buf);
+	if (conf->cmd) {
+		strbuf_addstr(&cmd, conf->cmd);
+		strvec_push(&cp.args, cmd.buf);
+		if (arg)
+			strvec_push(&cp.args, arg);
+	} else if (conf->command) {
+		strbuf_addstr(&cmd, conf->command);
+		if (arg)
+			strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
+		strvec_push(&cp.args, cmd.buf);
+	}
 	cp.env = local_repo_env;
 	cp.no_stdin = 1;
 	cp.use_shell = 1;
@@ -247,7 +255,7 @@ static char *apply_command(const char *command, const char *arg)
 
 static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)
 {
-	if (arg_tok->conf.command) {
+	if (arg_tok->conf.command || arg_tok->conf.cmd) {
 		const char *arg;
 		if (arg_tok->value && arg_tok->value[0]) {
 			arg = arg_tok->value;
@@ -257,7 +265,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg
 			else
 				arg = xstrdup("");
 		}
-		arg_tok->value = apply_command(arg_tok->conf.command, arg);
+		arg_tok->value = apply_command(&arg_tok->conf, arg);
 		free((char *)arg);
 	}
 }
@@ -430,6 +438,7 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
 	dst->name = xstrdup_or_null(src->name);
 	dst->key = xstrdup_or_null(src->key);
 	dst->command = xstrdup_or_null(src->command);
+	dst->cmd = xstrdup_or_null(src->cmd);
 }
 
 static struct arg_item *get_conf_item(const char *name)
@@ -454,8 +463,8 @@ static struct arg_item *get_conf_item(const char *name)
 	return item;
 }
 
-enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_WHERE,
-			 TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
+enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_CMD,
+			TRAILER_WHERE, TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
 
 static struct {
 	const char *name;
@@ -463,6 +472,7 @@ static struct {
 } trailer_config_items[] = {
 	{ "key", TRAILER_KEY },
 	{ "command", TRAILER_COMMAND },
+	{ "cmd", TRAILER_CMD },
 	{ "where", TRAILER_WHERE },
 	{ "ifexists", TRAILER_IF_EXISTS },
 	{ "ifmissing", TRAILER_IF_MISSING }
@@ -542,6 +552,11 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
 			warning(_("more than one %s"), conf_key);
 		conf->command = xstrdup(value);
 		break;
+	case TRAILER_CMD:
+		if (conf->cmd)
+			warning(_("more than one %s"), conf_key);
+		conf->cmd = xstrdup(value);
+		break;
 	case TRAILER_WHERE:
 		if (trailer_set_where(&conf->where, value))
 			warning(_("unknown value '%s' for key '%s'"), value, conf_key);
@@ -708,7 +723,7 @@ static void process_command_line_args(struct list_head *arg_head,
 	/* Add an arg item for each configured trailer with a command */
 	list_for_each(pos, &conf_head) {
 		item = list_entry(pos, struct arg_item, list);
-		if (item->conf.command)
+		if (item->conf.cmd || item->conf.command)
 			add_arg_item(arg_head,
 				     xstrdup(token_from_item(item, NULL)),
 				     xstrdup(""),

base-commit: 142430338477d9d1bb25be66267225fb58498d92
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-03  5:51             ` Christian Couder
@ 2021-04-04 23:26               ` Junio C Hamano
  2021-04-06  3:47                 ` Christian Couder
  0 siblings, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2021-04-04 23:26 UTC (permalink / raw)
  To: Christian Couder; +Cc: ZheNing Hu via GitGitGadget, git, ZheNing Hu

Christian Couder <christian.couder@gmail.com> writes:

> On Fri, Apr 2, 2021 at 10:49 PM Junio C Hamano <gitster@pobox.com> wrote:
>> ...
>>         ... as if a '<token>=<value>' argument were added at the
>>         beginning of the "git interpret-trailers" command, the
>>         command specified by this configuration variable will be
>>         called with an empty string as the argument.
>>
>> is my attempt,
>
> It looks better to me.
>
>> but I am not still sure what that "as if" part is
>> trying to say.  Does it mean with
>>
>>         [trailer "Foo"] cmd = foo-cmd
>>
>> and the 'input-file' does not have "Foo: <some existing value>"
>> trailer in it, the command "git interpret-trailers input-file"
>> would behave as if this command was run
>>
>>         $ Foo= git interpret-trailers input-file
>
> I would say it would behave as if:
>
> $ git interpret-trailers --trailer Foo= input-file

Hmmm.  That means that the descrition in the original is quite
misleading, no?  If it said

	... as if "--trailer" "<token>=<value>" arguments were given
	to "git interpret-trailers" command near the beginning of
	its command line

then that may be closer description of the command line you are
forming, but as its written (with or without my attempt to clarify
above), it was impossible to infer that you are behaving as if
another --trailer option (with <token>=<value> as its value) was
given.



^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-04 23:26               ` Junio C Hamano
@ 2021-04-06  3:47                 ` Christian Couder
  2021-04-06  3:52                   ` Christian Couder
  0 siblings, 1 reply; 60+ messages in thread
From: Christian Couder @ 2021-04-06  3:47 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: ZheNing Hu via GitGitGadget, git, ZheNing Hu

On Mon, Apr 5, 2021 at 1:26 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Christian Couder <christian.couder@gmail.com> writes:
>
> > On Fri, Apr 2, 2021 at 10:49 PM Junio C Hamano <gitster@pobox.com> wrote:
> >> ...
> >>         ... as if a '<token>=<value>' argument were added at the
> >>         beginning of the "git interpret-trailers" command, the
> >>         command specified by this configuration variable will be
> >>         called with an empty string as the argument.
> >>
> >> is my attempt,
> >
> > It looks better to me.
> >
> >> but I am not still sure what that "as if" part is
> >> trying to say.  Does it mean with
> >>
> >>         [trailer "Foo"] cmd = foo-cmd
> >>
> >> and the 'input-file' does not have "Foo: <some existing value>"
> >> trailer in it, the command "git interpret-trailers input-file"
> >> would behave as if this command was run
> >>
> >>         $ Foo= git interpret-trailers input-file
> >
> > I would say it would behave as if:
> >
> > $ git interpret-trailers --trailer Foo= input-file
>
> Hmmm.  That means that the descrition in the original is quite
> misleading, no?

Yeah, I agree it is misleading and difficult to understand.

> If it said
>
>         ... as if "--trailer" "<token>=<value>" arguments were given
>         to "git interpret-trailers" command near the beginning of
>         its command line
>
> then that may be closer description of the command line you are
> forming, but as its written (with or without my attempt to clarify
> above), it was impossible to infer that you are behaving as if
> another --trailer option (with <token>=<value> as its value) was
> given.

I agree.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-06  3:47                 ` Christian Couder
@ 2021-04-06  3:52                   ` Christian Couder
  2021-04-06  5:16                     ` ZheNing Hu
  0 siblings, 1 reply; 60+ messages in thread
From: Christian Couder @ 2021-04-06  3:52 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: ZheNing Hu via GitGitGadget, git, ZheNing Hu

On Tue, Apr 6, 2021 at 5:47 AM Christian Couder
<christian.couder@gmail.com> wrote:
>
> On Mon, Apr 5, 2021 at 1:26 AM Junio C Hamano <gitster@pobox.com> wrote:
> >
> > Christian Couder <christian.couder@gmail.com> writes:

> > > I would say it would behave as if:
> > >
> > > $ git interpret-trailers --trailer Foo= input-file
> >
> > Hmmm.  That means that the descrition in the original is quite
> > misleading, no?
>
> Yeah, I agree it is misleading and difficult to understand.
>
> > If it said
> >
> >         ... as if "--trailer" "<token>=<value>" arguments were given
> >         to "git interpret-trailers" command near the beginning of
> >         its command line
> >
> > then that may be closer description of the command line you are
> > forming, but as its written (with or without my attempt to clarify
> > above), it was impossible to infer that you are behaving as if
> > another --trailer option (with <token>=<value> as its value) was
> > given.
>
> I agree.

By the way it might be better to first have a patch that clarifies the
existing documentation of ".command", before the patch that adds
".cmd". This way the first patch that only clarifies the documentation
could be backported to maintenance branches of old releases.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-06  3:52                   ` Christian Couder
@ 2021-04-06  5:16                     ` ZheNing Hu
  2021-04-06  5:34                       ` Junio C Hamano
  2021-04-06  5:37                       ` Junio C Hamano
  0 siblings, 2 replies; 60+ messages in thread
From: ZheNing Hu @ 2021-04-06  5:16 UTC (permalink / raw)
  To: Christian Couder; +Cc: Junio C Hamano, ZheNing Hu via GitGitGadget, git

Hi, Junio and Christian,

Christian Couder <christian.couder@gmail.com> 于2021年4月6日周二 上午11:52写道:
>
> On Tue, Apr 6, 2021 at 5:47 AM Christian Couder
> <christian.couder@gmail.com> wrote:
> >
> > On Mon, Apr 5, 2021 at 1:26 AM Junio C Hamano <gitster@pobox.com> wrote:
> > >
> > > Christian Couder <christian.couder@gmail.com> writes:
>
> > > > I would say it would behave as if:
> > > >
> > > > $ git interpret-trailers --trailer Foo= input-file
> > >
> > > Hmmm.  That means that the descrition in the original is quite
> > > misleading, no?
> >
> > Yeah, I agree it is misleading and difficult to understand.
> >
> > > If it said
> > >
> > >         ... as if "--trailer" "<token>=<value>" arguments were given
> > >         to "git interpret-trailers" command near the beginning of
> > >         its command line
> > >
> > > then that may be closer description of the command line you are
> > > forming, but as its written (with or without my attempt to clarify
> > > above), it was impossible to infer that you are behaving as if
> > > another --trailer option (with <token>=<value> as its value) was
> > > given.
> >
> > I agree.
>
> By the way it might be better to first have a patch that clarifies the
> existing documentation of ".command", before the patch that adds
> ".cmd". This way the first patch that only clarifies the documentation
> could be backported to maintenance branches of old releases.

This first patch should explain errors of ".command", and then explain that
we will no longer use it, right?

I find in the status update description:
"Seems to break new tests for .command variant in zh/commit-trailer"
I have tryed to merge my local branch 1e9a657(trailer-cmd) and
2daae3d(upstream/zh/commit-trailer)

I can't find any tests errors after merging, is there something unexpected
happened?

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-06  5:16                     ` ZheNing Hu
@ 2021-04-06  5:34                       ` Junio C Hamano
  2021-04-06  5:37                       ` Junio C Hamano
  1 sibling, 0 replies; 60+ messages in thread
From: Junio C Hamano @ 2021-04-06  5:34 UTC (permalink / raw)
  To: ZheNing Hu; +Cc: Christian Couder, ZheNing Hu via GitGitGadget, git

ZheNing Hu <adlternative@gmail.com> writes:

> I find in the status update description:
> "Seems to break new tests for .command variant in zh/commit-trailer"
> I have tryed to merge my local branch 1e9a657(trailer-cmd) and
> 2daae3d(upstream/zh/commit-trailer)

Thanks for noticing---the description is stale.  Remember that after
I pointed out the breakage, you responded that the conversion for
the .command codepath was wrong?  What is queued in my tree has a
version with fix of that bug, so the comment does not apply.




^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-06  5:16                     ` ZheNing Hu
  2021-04-06  5:34                       ` Junio C Hamano
@ 2021-04-06  5:37                       ` Junio C Hamano
  1 sibling, 0 replies; 60+ messages in thread
From: Junio C Hamano @ 2021-04-06  5:37 UTC (permalink / raw)
  To: ZheNing Hu; +Cc: Christian Couder, ZheNing Hu via GitGitGadget, git

ZheNing Hu <adlternative@gmail.com> writes:

>> By the way it might be better to first have a patch that clarifies the
>> existing documentation of ".command", before the patch that adds
>> ".cmd". This way the first patch that only clarifies the documentation
>> could be backported to maintenance branches of old releases.
>
> This first patch should explain errors of ".command", and then explain that
> we will no longer use it, right?

If I am reading what Christian wrote correctly, I think it is not
"errors of .command" but to clarify the misleading description
(especially around the "as if <token>=<value> were added at the
beginning").  As if nothing is planned for its deprecation.



^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v7] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-04 13:11           ` [PATCH v7] " ZheNing Hu via GitGitGadget
@ 2021-04-06 16:23             ` Christian Couder
  2021-04-07  4:51               ` ZheNing Hu
  2021-04-09 13:37             ` [PATCH v8 0/2] [GSOC] trailer: add new .cmd " ZheNing Hu via GitGitGadget
  1 sibling, 1 reply; 60+ messages in thread
From: Christian Couder @ 2021-04-06 16:23 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget; +Cc: git, Junio C Hamano, ZheNing Hu

On Sun, Apr 4, 2021 at 3:11 PM ZheNing Hu via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> From: ZheNing Hu <adlternative@gmail.com>
>
> The `trailer.<token>.command` configuration variable
> specifies a command (run via the shell, so it does not have
> to be a single name of or path to the command, but can be a
> shell script), and the first occurrence of substring $ARG is
> replaced with the value given to the `interpret-trailer`
> command for the token.  This has two downsides:
>
> * The use of $ARG in the mechanism misleads the users that
> the value is passed in the shell variable, and tempt them
> to use $ARG more than once, but that would not work, as
> the second and subsequent $ARG are not replaced.
>
> * Because $ARG is textually replaced without regard to the
> shell language syntax, even '$ARG' (inside a single-quote
> pair), which a user would expect to stay intact, would be
> replaced, and worse, if the value had an unmatching single

s/unmatching/unmatched/

> quote (imagine a name like "O'Connor", substituted into
> NAME='$ARG' to make it NAME='O'Connor'), it would result in
> a broken command that is not syntactically correct (or
> worse).

Good explanation of the issues with ".command"!

> Introduce a new `trailer.<token>.cmd` configuration that
> takes higher precedence to deprecate and eventually remove
> `trailer.<token>.command`, which passes the value as a
> parameter to the command.  Instead of "$ARG", the users will

s/a parameter/an argument/
s/the users will/users can/

> refer to the value as positional argument, $1, in their
> scripts.
>
> Helped-by: Junio C Hamano <gitster@pobox.com>
> Helped-by: Christian Couder <christian.couder@gmail.com>
> Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> ---
>     [GSOC] trailer: add new trailer..cmd config option

Maybe <token> has been removed from "trailer.<token>.cmd" above
because it has been interpreted as an HTML or XML tag?

>     In https://lore.kernel.org/git/xmqqv99i4ck2.fsf@gitster.g/ Junio and
>     Christian talked about the problem of using strbuf_replace() to replace
>     $ARG:
>
>      1. if user's script have more than one $ARG, only the first one will be

s/user's script have/the user's script has/

>         replaced, which is incorrected.
>      2. $ARG is textually replaced without shell syntax, which may result a
>         broken command when $ARG include some unmatching single quote, very
>         unsafe.

Yeah, good summary of the issues with ".command"

>     Now pass trailer value as $1 to the trailer command with another
>     trailer.<token>.cmd config, to solve these above two problems,
>
>     We are now writing documents that are more readable and correct than
>     before.

Yeah, correcting the doc is a good thing to do. By the way, as I said
to Junio, it might be better to make the doc for ".command" more
readable and correct in a first patch separate from the patch
introducing ".cmd".

If you really want to do both in the same patch you should tell that
in the commit message too, not just here after the "---" line.

>  Documentation/git-interpret-trailers.txt | 86 +++++++++++++++++++----
>  t/t7513-interpret-trailers.sh            | 87 +++++++++++++++++++++++-
>  trailer.c                                | 37 +++++++---
>  3 files changed, 186 insertions(+), 24 deletions(-)
>
> diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
> index 96ec6499f001..83600e93390d 100644
> --- a/Documentation/git-interpret-trailers.txt
> +++ b/Documentation/git-interpret-trailers.txt
> @@ -236,21 +236,36 @@ trailer.<token>.command::
>         be called to automatically add or modify a trailer with the
>         specified <token>.
>  +
> -When this option is specified, the behavior is as if a special
> -'<token>=<value>' argument were added at the beginning of the command
> -line, where <value> is taken to be the standard output of the
> -specified command with any leading and trailing whitespace trimmed
> -off.
> +When this option is specified, the first occurrence of substring $ARG is
> +replaced with the value given to the `interpret-trailer` command for the
> +same token. This option behaves in a similar way as ".cmd", however, it
> +passes the value through $ARG.

Maybe this last sentence could be replaced with "Otherwise this option
behaves in the same way as 'trailer.<token>.cmd'."

> -If the command contains the `$ARG` string, this string will be
> -replaced with the <value> part of an existing trailer with the same
> -<token>, if any, before the command is launched.
> +".command" has been deprecated due to the $ARG in the user's command can

s/".command"/The 'trailer.<token>.command' option/
s/to the $ARG/to the fact that `$ARG`/

> +only be replaced once and the original way of replacing $ARG was not safe.

s/only be/can only be/
s/and the/and that the/

> +Now the preferred option is using "trailer.<token>.cmd", which use position

s/using//
s/use/uses/
s/position/a positional/

Also please make sure that trailer.<token>.cmd and
trailer.<token>.command are always quoted in the same way. I think
single quotes are used in the current doc, so please keep using single
quotes.

> +argument to pass the value.
> ++
> +When both .cmd and .command are given for the same <token>,
> +.cmd is used and .command is ignored.

Please spell and quote ".cmd" and ".command" consistently, so for
example like: 'trailer.<token>.cmd'

> +trailer.<token>.cmd::
> +       The command specified by this configuration variable is run
> +       with a single argument, which is the <value> part of a
> +       `--trailer <token>=<value>` on the command line. The output
> +       from the command is then used as the value for the <token>
> +       in the resulting trailer.
> ++
> +When this option is specified, the behavior is as if a '<token>=<value>'

s/'<token>=<value>'/`--trailer <token>=<value>`/  (let's try to be as
explicit as possible)

> +argument were added at the beginning of the "git interpret-trailers"

s/were/was/

> +command, the command specified by this configuration variable will be
> +called with an empty string as the argument.
> +
>  If some '<token>=<value>' arguments are also passed on the command
> -line, when a 'trailer.<token>.command' is configured, the command will
> -also be executed for each of these arguments. And the <value> part of
> -these arguments, if any, will be used to replace the `$ARG` string in
> -the command.
> +line, when a 'trailer.<token>.cmd' is configured, the command is run
> +once for each `--trailer <token>=<value>` on the command line with the
> +same <token>. And the <value> part of these arguments, if any, will be
> +passed to the command as its first argument.

Yeah, it's much better than it was, but I think we can do better. I
will try to come up with something soon.

Also as I said above and in reply to Junio, I think it might be better
to split this in 2 patches.

>  EXAMPLES
>  --------
> @@ -333,6 +348,53 @@ subject
>  Fix #42
>  ------------
>
> +* Configure a 'cnt' trailer with a cmd use a global script `gcount`
> +to record commit counts of a specified author and show how it works:
> ++
> +------------
> +$ cat ~/bin/gcount
> +#!/bin/sh
> +test -n "$1" && git shortlog -s --author="$1" HEAD || true
> +$ git config trailer.cnt.key "Commit-count: "
> +$ git config trailer.cnt.ifExists "replace"
> +$ git config trailer.cnt.cmd "~/bin/gcount"
> +$ git interpret-trailers --trailer="cnt:Junio" <<EOF
> +> subject
> +>
> +> message
> +>
> +> EOF
> +subject
> +
> +message
> +
> +Commit-count: 22484     Junio C Hamano
> +------------
> +
> +* Configure a 'ref' trailer with a cmd use a global script `glog-grep`
> +  to grep last relevant commit from git log in the git repository
> +  and show how it works:
> ++
> +------------
> +$ cat ~/bin/glog-grep
> +#!/bin/sh
> +test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
> +$ git config trailer.ref.key "Reference-to: "
> +$ git config trailer.ref.ifExists "replace"
> +$ git config trailer.ref.cmd "~/bin/glog-grep"
> +$ git interpret-trailers --trailer="ref:Add copyright notices." <<EOF
> +> subject
> +>
> +> message
> +>
> +> EOF
> +subject
> +
> +message
> +
> +Reference-to: 8bc9a0c769 (Add copyright notices., 2005-04-07)

The added examples look good!

> +test_expect_success 'with cmd' '
> +       test_when_finished "git config --remove-section trailer.bug" &&
> +       git config trailer.bug.key "Bug-maker: " &&
> +       git config trailer.bug.ifExists "add" &&
> +       git config trailer.bug.cmd "echo \"maybe is\"" &&
> +       cat >expected2 <<-EOF &&
> +
> +       Bug-maker: maybe is
> +       Bug-maker: maybe is him
> +       Bug-maker: maybe is me
> +       EOF
> +       git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
> +               >actual2 &&
> +       test_cmp expected2 actual2
> +'

I guess this shows that the command is called multiple times, the
first time with an empty first arg.

> +test_expect_success 'with cmd and $1' '
> +       test_when_finished "git config --remove-section trailer.bug" &&
> +       git config trailer.bug.key "Bug-maker: " &&
> +       git config trailer.bug.ifExists "replace" &&
> +       git config trailer.bug.cmd "echo \"\$1\" is" &&
> +       cat >expected2 <<-EOF &&
> +
> +       Bug-maker: me is me
> +       EOF
> +       git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
> +               >actual2 &&
> +       test_cmp expected2 actual2
> +'

I guess this shows that the argument is also available as "$1".

> +test_expect_success 'with cmd and $1 with sh -c' '
> +       test_when_finished "git config --remove-section trailer.bug" &&
> +       git config trailer.bug.key "Bug-maker: " &&
> +       git config trailer.bug.ifExists "replace" &&
> +       git config trailer.bug.cmd "sh -c \"echo who is \"\$1\"\"" &&
> +       cat >expected2 <<-EOF &&
> +
> +       Bug-maker: who is me
> +       EOF
> +       git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
> +               >actual2 &&
> +       test_cmp expected2 actual2
> +'

Ok, this shows how `sh -c ...` can be used in ".cmd".

> +test_expect_success 'with cmd and $1 with shell script' '
> +       test_when_finished "git config --remove-section trailer.bug" &&
> +       git config trailer.bug.key "Bug-maker: " &&
> +       git config trailer.bug.ifExists "replace" &&
> +       git config trailer.bug.cmd "./echoscript" &&
> +       cat >expected2 <<-EOF &&
> +
> +       Bug-maker: who is me
> +       EOF
> +       cat >echoscript <<-EOF &&
> +       #!/bin/sh
> +       echo who is "\$1"
> +       EOF
> +       chmod +x echoscript &&
> +       git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
> +               >actual2 &&
> +       test_cmp expected2 actual2
> +'

Ok.

>  test_expect_success 'without config' '
>         sed -e "s/ Z\$/ /" >expected <<-\EOF &&
>
> @@ -1274,9 +1337,31 @@ test_expect_success 'setup a commit' '
>         git commit -m "Add file a.txt"
>  '
>
> +test_expect_success 'cmd takes precedence over command' '
> +       test_when_finished "git config --unset trailer.fix.cmd" &&
> +       git config trailer.fix.ifExists "replace" &&
> +       git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
> +               --abbrev-commit --abbrev=14 \"\$1\" || true" &&
> +       git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
> +               --abbrev-commit --abbrev=14 \$ARG" &&
> +       FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
> +       cat complex_message_body >expected2 &&
> +       sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
> +               Fixes: $FIXED
> +               Acked-by= Z
> +               Reviewed-by:
> +               Signed-off-by: Z
> +               Signed-off-by: A U Thor <author@example.com>
> +       EOF
> +       git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
> +               <complex_message >actual2 &&
> +       test_cmp expected2 actual2
> +'

Ok.

>  test_expect_success 'with command using $ARG' '
>         git config trailer.fix.ifExists "replace" &&
> -       git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
> +       git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
> +               --abbrev-commit --abbrev=14 \$ARG" &&

This is just an indent change. I am not sure it's worth doing in this
patch. If you think that the file needs better indentation though,
then you might do it in a separate preparatory patch at the beginning
of the current patch series.

If there is only this place in the file where such an indentation
improvement is needed, this might be ok, but please mention in the
commit message that while at it you are also doing this small change.

The other parts of the patch look good to me.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v7] [GSOC] trailer: add new trailer.<token>.cmd config option
  2021-04-06 16:23             ` Christian Couder
@ 2021-04-07  4:51               ` ZheNing Hu
  0 siblings, 0 replies; 60+ messages in thread
From: ZheNing Hu @ 2021-04-07  4:51 UTC (permalink / raw)
  To: Christian Couder; +Cc: ZheNing Hu via GitGitGadget, git, Junio C Hamano

Christian Couder <christian.couder@gmail.com> 于2021年4月7日周三 上午12:23写道:
>
> On Sun, Apr 4, 2021 at 3:11 PM ZheNing Hu via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
> >
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > The `trailer.<token>.command` configuration variable
> > specifies a command (run via the shell, so it does not have
> > to be a single name of or path to the command, but can be a
> > shell script), and the first occurrence of substring $ARG is
> > replaced with the value given to the `interpret-trailer`
> > command for the token.  This has two downsides:
> >
> > * The use of $ARG in the mechanism misleads the users that
> > the value is passed in the shell variable, and tempt them
> > to use $ARG more than once, but that would not work, as
> > the second and subsequent $ARG are not replaced.
> >
> > * Because $ARG is textually replaced without regard to the
> > shell language syntax, even '$ARG' (inside a single-quote
> > pair), which a user would expect to stay intact, would be
> > replaced, and worse, if the value had an unmatching single
>
> s/unmatching/unmatched/
>
> > quote (imagine a name like "O'Connor", substituted into
> > NAME='$ARG' to make it NAME='O'Connor'), it would result in
> > a broken command that is not syntactically correct (or
> > worse).
>
> Good explanation of the issues with ".command"!
>

Credit for Junio.

> > Introduce a new `trailer.<token>.cmd` configuration that
> > takes higher precedence to deprecate and eventually remove
> > `trailer.<token>.command`, which passes the value as a
> > parameter to the command.  Instead of "$ARG", the users will
>
> s/a parameter/an argument/
> s/the users will/users can/
>
> > refer to the value as positional argument, $1, in their
> > scripts.
> >
> > Helped-by: Junio C Hamano <gitster@pobox.com>
> > Helped-by: Christian Couder <christian.couder@gmail.com>
> > Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> > ---
> >     [GSOC] trailer: add new trailer..cmd config option
>
> Maybe <token> has been removed from "trailer.<token>.cmd" above
> because it has been interpreted as an HTML or XML tag?
>

Aha, It may well be so.
Maybe change to "[GSOC] trailer add .cmd config option"?

> >     In https://lore.kernel.org/git/xmqqv99i4ck2.fsf@gitster.g/ Junio and
> >     Christian talked about the problem of using strbuf_replace() to replace
> >     $ARG:
> >
> >      1. if user's script have more than one $ARG, only the first one will be
>
> s/user's script have/the user's script has/
>
> >         replaced, which is incorrected.
> >      2. $ARG is textually replaced without shell syntax, which may result a
> >         broken command when $ARG include some unmatching single quote, very
> >         unsafe.
>
> Yeah, good summary of the issues with ".command"
>
> >     Now pass trailer value as $1 to the trailer command with another
> >     trailer.<token>.cmd config, to solve these above two problems,
> >
> >     We are now writing documents that are more readable and correct than
> >     before.
>
> Yeah, correcting the doc is a good thing to do. By the way, as I said
> to Junio, it might be better to make the doc for ".command" more
> readable and correct in a first patch separate from the patch
> introducing ".cmd".
>
> If you really want to do both in the same patch you should tell that
> in the commit message too, not just here after the "---" line.
>

I actually tried to write it yesterday, but...

diff --git a/Documentation/git-interpret-trailers.txt
b/Documentation/git-interpret-trailers.txt
index 96ec6499f0..39f742b3dc 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -237,20 +237,20 @@ trailer.<token>.command::
        specified <token>.
 +
 When this option is specified, the behavior is as if a special
-'<token>=<value>' argument were added at the beginning of the command
-line, where <value> is taken to be the standard output of the
-specified command with any leading and trailing whitespace trimmed
-off.
+'<token>=<value>' argument were added at the beginning of the
+"git interpret-trailers" command, where <value> is taken to be the
+standard output of the specified command with any leading and
+trailing whitespace trimmed off.
 +
-If the command contains the `$ARG` string, this string will be
-replaced with the <value> part of an existing trailer with the same
-<token>, if any, before the command is launched.
+The first occurrence of substring $ARG will be replaced with the
+<value> part of an existing trailer with the same <token>, if any,
+before the command is launched.
 +
 If some '<token>=<value>' arguments are also passed on the command
-line, when a 'trailer.<token>.command' is configured, the command will
-also be executed for each of these arguments. And the <value> part of
-these arguments, if any, will be used to replace the `$ARG` string in
-the command.
+line, when a 'trailer.<token>.cmd' is configured, the command is run
+once for each `--trailer <token>=<value>` on the command line with the
+same <token>. And the <value> part of these arguments, if any, will be
+used to replace the fist $ARG string in the command.

Since I am based on the document in the second patch, you have also
uncovered some points worthy of modification below,
so temporarily what I wrote is not accurate enough.

> >  Documentation/git-interpret-trailers.txt | 86 +++++++++++++++++++----
> >  t/t7513-interpret-trailers.sh            | 87 +++++++++++++++++++++++-
> >  trailer.c                                | 37 +++++++---
> >  3 files changed, 186 insertions(+), 24 deletions(-)
> >
> > diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
> > index 96ec6499f001..83600e93390d 100644
> > --- a/Documentation/git-interpret-trailers.txt
> > +++ b/Documentation/git-interpret-trailers.txt
> > @@ -236,21 +236,36 @@ trailer.<token>.command::
> >         be called to automatically add or modify a trailer with the
> >         specified <token>.
> >  +
> > -When this option is specified, the behavior is as if a special
> > -'<token>=<value>' argument were added at the beginning of the command
> > -line, where <value> is taken to be the standard output of the
> > -specified command with any leading and trailing whitespace trimmed
> > -off.
> > +When this option is specified, the first occurrence of substring $ARG is
> > +replaced with the value given to the `interpret-trailer` command for the
> > +same token. This option behaves in a similar way as ".cmd", however, it
> > +passes the value through $ARG.
>
> Maybe this last sentence could be replaced with "Otherwise this option
> behaves in the same way as 'trailer.<token>.cmd'."
>
> > -If the command contains the `$ARG` string, this string will be
> > -replaced with the <value> part of an existing trailer with the same
> > -<token>, if any, before the command is launched.
> > +".command" has been deprecated due to the $ARG in the user's command can
>
> s/".command"/The 'trailer.<token>.command' option/
> s/to the $ARG/to the fact that `$ARG`/
>
> > +only be replaced once and the original way of replacing $ARG was not safe.
>
> s/only be/can only be/
> s/and the/and that the/
>
> > +Now the preferred option is using "trailer.<token>.cmd", which use position
>
> s/using//
> s/use/uses/
> s/position/a positional/
>
> Also please make sure that trailer.<token>.cmd and
> trailer.<token>.command are always quoted in the same way. I think
> single quotes are used in the current doc, so please keep using single
> quotes.
>
> > +argument to pass the value.
> > ++
> > +When both .cmd and .command are given for the same <token>,
> > +.cmd is used and .command is ignored.
>
> Please spell and quote ".cmd" and ".command" consistently, so for
> example like: 'trailer.<token>.cmd'
>

OK.

> > +trailer.<token>.cmd::
> > +       The command specified by this configuration variable is run
> > +       with a single argument, which is the <value> part of a
> > +       `--trailer <token>=<value>` on the command line. The output
> > +       from the command is then used as the value for the <token>
> > +       in the resulting trailer.
> > ++
> > +When this option is specified, the behavior is as if a '<token>=<value>'
>
> s/'<token>=<value>'/`--trailer <token>=<value>`/  (let's try to be as
> explicit as possible)
>
> > +argument were added at the beginning of the "git interpret-trailers"
>
> s/were/was/
>
> > +command, the command specified by this configuration variable will be
> > +called with an empty string as the argument.
> > +
> >  If some '<token>=<value>' arguments are also passed on the command
> > -line, when a 'trailer.<token>.command' is configured, the command will
> > -also be executed for each of these arguments. And the <value> part of
> > -these arguments, if any, will be used to replace the `$ARG` string in
> > -the command.
> > +line, when a 'trailer.<token>.cmd' is configured, the command is run
> > +once for each `--trailer <token>=<value>` on the command line with the
> > +same <token>. And the <value> part of these arguments, if any, will be
> > +passed to the command as its first argument.
>
> Yeah, it's much better than it was, but I think we can do better. I
> will try to come up with something soon.
>
> Also as I said above and in reply to Junio, I think it might be better
> to split this in 2 patches.
>
> >  EXAMPLES
> >  --------
> > @@ -333,6 +348,53 @@ subject
> >  Fix #42
> >  ------------
> >
> > +* Configure a 'cnt' trailer with a cmd use a global script `gcount`
> > +to record commit counts of a specified author and show how it works:
> > ++
> > +------------
> > +$ cat ~/bin/gcount
> > +#!/bin/sh
> > +test -n "$1" && git shortlog -s --author="$1" HEAD || true
> > +$ git config trailer.cnt.key "Commit-count: "
> > +$ git config trailer.cnt.ifExists "replace"
> > +$ git config trailer.cnt.cmd "~/bin/gcount"
> > +$ git interpret-trailers --trailer="cnt:Junio" <<EOF
> > +> subject
> > +>
> > +> message
> > +>
> > +> EOF
> > +subject
> > +
> > +message
> > +
> > +Commit-count: 22484     Junio C Hamano
> > +------------
> > +
> > +* Configure a 'ref' trailer with a cmd use a global script `glog-grep`
> > +  to grep last relevant commit from git log in the git repository
> > +  and show how it works:
> > ++
> > +------------
> > +$ cat ~/bin/glog-grep
> > +#!/bin/sh
> > +test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
> > +$ git config trailer.ref.key "Reference-to: "
> > +$ git config trailer.ref.ifExists "replace"
> > +$ git config trailer.ref.cmd "~/bin/glog-grep"
> > +$ git interpret-trailers --trailer="ref:Add copyright notices." <<EOF
> > +> subject
> > +>
> > +> message
> > +>
> > +> EOF
> > +subject
> > +
> > +message
> > +
> > +Reference-to: 8bc9a0c769 (Add copyright notices., 2005-04-07)
>
> The added examples look good!
>

Thanks.

> > +test_expect_success 'with cmd' '
> > +       test_when_finished "git config --remove-section trailer.bug" &&
> > +       git config trailer.bug.key "Bug-maker: " &&
> > +       git config trailer.bug.ifExists "add" &&
> > +       git config trailer.bug.cmd "echo \"maybe is\"" &&
> > +       cat >expected2 <<-EOF &&
> > +
> > +       Bug-maker: maybe is
> > +       Bug-maker: maybe is him
> > +       Bug-maker: maybe is me
> > +       EOF
> > +       git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
> > +               >actual2 &&
> > +       test_cmp expected2 actual2
> > +'
>
> I guess this shows that the command is called multiple times, the
> first time with an empty first arg.
>
> > +test_expect_success 'with cmd and $1' '
> > +       test_when_finished "git config --remove-section trailer.bug" &&
> > +       git config trailer.bug.key "Bug-maker: " &&
> > +       git config trailer.bug.ifExists "replace" &&
> > +       git config trailer.bug.cmd "echo \"\$1\" is" &&
> > +       cat >expected2 <<-EOF &&
> > +
> > +       Bug-maker: me is me
> > +       EOF
> > +       git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
> > +               >actual2 &&
> > +       test_cmp expected2 actual2
> > +'
>
> I guess this shows that the argument is also available as "$1".
>
> > +test_expect_success 'with cmd and $1 with sh -c' '
> > +       test_when_finished "git config --remove-section trailer.bug" &&
> > +       git config trailer.bug.key "Bug-maker: " &&
> > +       git config trailer.bug.ifExists "replace" &&
> > +       git config trailer.bug.cmd "sh -c \"echo who is \"\$1\"\"" &&
> > +       cat >expected2 <<-EOF &&
> > +
> > +       Bug-maker: who is me
> > +       EOF
> > +       git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
> > +               >actual2 &&
> > +       test_cmp expected2 actual2
> > +'
>
> Ok, this shows how `sh -c ...` can be used in ".cmd".
>
> > +test_expect_success 'with cmd and $1 with shell script' '
> > +       test_when_finished "git config --remove-section trailer.bug" &&
> > +       git config trailer.bug.key "Bug-maker: " &&
> > +       git config trailer.bug.ifExists "replace" &&
> > +       git config trailer.bug.cmd "./echoscript" &&
> > +       cat >expected2 <<-EOF &&
> > +
> > +       Bug-maker: who is me
> > +       EOF
> > +       cat >echoscript <<-EOF &&
> > +       #!/bin/sh
> > +       echo who is "\$1"
> > +       EOF
> > +       chmod +x echoscript &&
> > +       git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
> > +               >actual2 &&
> > +       test_cmp expected2 actual2
> > +'
>
> Ok.
>
> >  test_expect_success 'without config' '
> >         sed -e "s/ Z\$/ /" >expected <<-\EOF &&
> >
> > @@ -1274,9 +1337,31 @@ test_expect_success 'setup a commit' '
> >         git commit -m "Add file a.txt"
> >  '
> >
> > +test_expect_success 'cmd takes precedence over command' '
> > +       test_when_finished "git config --unset trailer.fix.cmd" &&
> > +       git config trailer.fix.ifExists "replace" &&
> > +       git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
> > +               --abbrev-commit --abbrev=14 \"\$1\" || true" &&
> > +       git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
> > +               --abbrev-commit --abbrev=14 \$ARG" &&
> > +       FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
> > +       cat complex_message_body >expected2 &&
> > +       sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
> > +               Fixes: $FIXED
> > +               Acked-by= Z
> > +               Reviewed-by:
> > +               Signed-off-by: Z
> > +               Signed-off-by: A U Thor <author@example.com>
> > +       EOF
> > +       git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
> > +               <complex_message >actual2 &&
> > +       test_cmp expected2 actual2
> > +'
>
> Ok.
>
> >  test_expect_success 'with command using $ARG' '
> >         git config trailer.fix.ifExists "replace" &&
> > -       git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
> > +       git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
> > +               --abbrev-commit --abbrev=14 \$ARG" &&
>
> This is just an indent change. I am not sure it's worth doing in this
> patch. If you think that the file needs better indentation though,
> then you might do it in a separate preparatory patch at the beginning
> of the current patch series.
>
> If there is only this place in the file where such an indentation
> improvement is needed, this might be ok, but please mention in the
> commit message that while at it you are also doing this small change.
>

Well, I may often think that these small changes are irrelevant.
Since you said that, I will cancel the modification of this place.

> The other parts of the patch look good to me.

Thanks, Christian.
--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v8 0/2] [GSOC] trailer: add new .cmd config option
  2021-04-04 13:11           ` [PATCH v7] " ZheNing Hu via GitGitGadget
  2021-04-06 16:23             ` Christian Couder
@ 2021-04-09 13:37             ` ZheNing Hu via GitGitGadget
  2021-04-09 13:37               ` [PATCH v8 1/2] [GSOC] docs: correct descript of trailer.<token>.command ZheNing Hu via GitGitGadget
                                 ` (2 more replies)
  1 sibling, 3 replies; 60+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-04-09 13:37 UTC (permalink / raw)
  To: git; +Cc: Christian Couder, Junio C Hamano, ZheNing Hu

In https://lore.kernel.org/git/xmqqv99i4ck2.fsf@gitster.g/ Junio and
Christian talked about the problem of using strbuf_replace() to replace
$ARG:

 1. if the user's script has more than one $ARG, only the first one will be
    replaced, which is incorrected.
 2. $ARG is textually replaced without shell syntax, which may result a
    broken command when $ARG include some unmatching single quote, very
    unsafe.

Now pass trailer value as $1 to the trailer command with another
trailer.<token>.cmd config, to solve these above two problems,

We are now writing documents that are more readable and correct than before.

ZheNing Hu (2):
  [GSOC] docs: correct descript of trailer.<token>.command
  [GSOC] trailer: add new .cmd config option

 Documentation/git-interpret-trailers.txt | 90 ++++++++++++++++++++----
 t/t7513-interpret-trailers.sh            | 84 ++++++++++++++++++++++
 trailer.c                                | 37 +++++++---
 3 files changed, 187 insertions(+), 24 deletions(-)


base-commit: 142430338477d9d1bb25be66267225fb58498d92
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-913%2Fadlternative%2Ftrailer-pass-ARG-env-v8
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-913/adlternative/trailer-pass-ARG-env-v8
Pull-Request: https://github.com/gitgitgadget/git/pull/913

Range-diff vs v7:

 -:  ------------ > 1:  505903811df8 [GSOC] docs: correct descript of trailer.<token>.command
 1:  1e9a6572ac6f ! 2:  3dc8983a4702 [GSOC] trailer: add new trailer.<token>.cmd config option
     @@ Metadata
      Author: ZheNing Hu <adlternative@gmail.com>
      
       ## Commit message ##
     -    [GSOC] trailer: add new trailer.<token>.cmd config option
     +    [GSOC] trailer: add new .cmd config option
      
          The `trailer.<token>.command` configuration variable
          specifies a command (run via the shell, so it does not have
     @@ Commit message
          * Because $ARG is textually replaced without regard to the
          shell language syntax, even '$ARG' (inside a single-quote
          pair), which a user would expect to stay intact, would be
     -    replaced, and worse, if the value had an unmatching single
     +    replaced, and worse, if the value had an unmatched single
          quote (imagine a name like "O'Connor", substituted into
          NAME='$ARG' to make it NAME='O'Connor'), it would result in
          a broken command that is not syntactically correct (or
     @@ Commit message
      
          Introduce a new `trailer.<token>.cmd` configuration that
          takes higher precedence to deprecate and eventually remove
     -    `trailer.<token>.command`, which passes the value as a
     -    parameter to the command.  Instead of "$ARG", the users will
     +    `trailer.<token>.command`, which passes the value as an
     +    argument to the command.  Instead of "$ARG", users can
          refer to the value as positional argument, $1, in their
          scripts.
      
     @@ Documentation/git-interpret-trailers.txt: trailer.<token>.command::
       	specified <token>.
       +
      -When this option is specified, the behavior is as if a special
     --'<token>=<value>' argument were added at the beginning of the command
     --line, where <value> is taken to be the standard output of the
     --specified command with any leading and trailing whitespace trimmed
     --off.
     +-'--trailer <token>=<value>' argument was added at the beginning of
     +-the "git interpret-trailers" command, where <value> is taken to be the
     +-standard output of the specified command with any leading and trailing
     +-whitespace trimmed off.
      +When this option is specified, the first occurrence of substring $ARG is
      +replaced with the value given to the `interpret-trailer` command for the
     -+same token. This option behaves in a similar way as ".cmd", however, it
     -+passes the value through $ARG.
     - +
     --If the command contains the `$ARG` string, this string will be
     --replaced with the <value> part of an existing trailer with the same
     --<token>, if any, before the command is launched.
     -+".command" has been deprecated due to the $ARG in the user's command can
     -+only be replaced once and the original way of replacing $ARG was not safe.
     -+Now the preferred option is using "trailer.<token>.cmd", which use position
     -+argument to pass the value.
     ++same token. It passes the value through `$ARG`, otherwise this option behaves
     ++in the same way as 'trailer.<token>.cmd'.
      ++
     -+When both .cmd and .command are given for the same <token>,
     -+.cmd is used and .command is ignored.
     ++The 'trailer.<token>.command' option has been deprecated due to the fact
     ++that $ARG in the user's command can only be replaced once and that the
     ++original way of replacing $ARG was not safe. Now the preferred option is
     ++'trailer.<token>.cmd', which uses a positional argument to pass the value.
     + +
     +-The first occurrence of substring `$ARG` will be replaced with the
     +-<value> part of an existing trailer with the same <token>, if any,
     +-before the command is launched.
     ++When both 'trailer.<token>.cmd' and 'trailer.<token>.command' are given
     ++for the same <token>, 'trailer.<token>.cmd' is used and
     ++'trailer.<token>.command' is ignored.
      +
      +trailer.<token>.cmd::
      +	The command specified by this configuration variable is run
     @@ Documentation/git-interpret-trailers.txt: trailer.<token>.command::
      +	from the command is then used as the value for the <token>
      +	in the resulting trailer.
      ++
     -+When this option is specified, the behavior is as if a '<token>=<value>'
     -+argument were added at the beginning of the "git interpret-trailers"
     -+command, the command specified by this configuration variable will be
     -+called with an empty string as the argument.
     ++When this option is specified, the behavior is as if a
     ++'--trailer <token>=<value>' argument was added at the beginning of
     ++the "git interpret-trailers" command, the command specified by this
     ++configuration variable will be called with an empty string as the
     ++argument.
       +
     - If some '<token>=<value>' arguments are also passed on the command
     --line, when a 'trailer.<token>.command' is configured, the command will
     --also be executed for each of these arguments. And the <value> part of
     --these arguments, if any, will be used to replace the `$ARG` string in
     --the command.
     -+line, when a 'trailer.<token>.cmd' is configured, the command is run
     -+once for each `--trailer <token>=<value>` on the command line with the
     -+same <token>. And the <value> part of these arguments, if any, will be
     -+passed to the command as its first argument.
     +-If some '<token>=<value>' arguments are also passed on the command
     +-line, when a 'trailer.<token>.command' is configured, the command is run
     +-once for each these arguments with the same <token>. And the <value>
     +-part of these arguments, if any, will be used to replace the first `$ARG`
     +-string in the command.
     ++If some '--trailer <token>=<value>' arguments are also passed on the
     ++command line, when a 'trailer.<token>.cmd' is configured, the command
     ++is run once for each `--trailer <token>=<value>` on the command line
     ++with the same <token>. And the <value> part of these arguments, if any,
     ++will be passed to the command as its first argument.
       
       EXAMPLES
       --------
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup a commit' '
      +	test_when_finished "git config --unset trailer.fix.cmd" &&
      +	git config trailer.fix.ifExists "replace" &&
      +	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
     -+		--abbrev-commit --abbrev=14 \"\$1\" || true" &&
     ++	--abbrev-commit --abbrev=14 \"\$1\" || true" &&
      +	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
      +		--abbrev-commit --abbrev=14 \$ARG" &&
      +	FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup a commit' '
      +
       test_expect_success 'with command using $ARG' '
       	git config trailer.fix.ifExists "replace" &&
     --	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
     -+	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
     -+		--abbrev-commit --abbrev=14 \$ARG" &&
     - 	FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
     - 	cat complex_message_body >expected &&
     - 	sed -e "s/ Z\$/ /" >>expected <<-EOF &&
     + 	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
      
       ## trailer.c ##
      @@ trailer.c: struct conf_info {

-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v8 1/2] [GSOC] docs: correct descript of trailer.<token>.command
  2021-04-09 13:37             ` [PATCH v8 0/2] [GSOC] trailer: add new .cmd " ZheNing Hu via GitGitGadget
@ 2021-04-09 13:37               ` ZheNing Hu via GitGitGadget
  2021-04-09 19:02                 ` Christian Couder
  2021-04-09 13:37               ` [PATCH v8 2/2] [GSOC] trailer: add new .cmd config option ZheNing Hu via GitGitGadget
  2021-04-09 19:59               ` [PATCH v8 0/2] " Christian Couder
  2 siblings, 1 reply; 60+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-04-09 13:37 UTC (permalink / raw)
  To: git; +Cc: Christian Couder, Junio C Hamano, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

In the original documentation of `trailer.<token>.command`,
some descriptions are easily misunderstood. So let's modify
it to increase its readability.

In addition, clarify that `$ARG` in command can only be
replaced once since `$ARG` is text replacement.

Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
 Documentation/git-interpret-trailers.txt | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 96ec6499f001..3e5aa3a65ae9 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -237,20 +237,20 @@ trailer.<token>.command::
 	specified <token>.
 +
 When this option is specified, the behavior is as if a special
-'<token>=<value>' argument were added at the beginning of the command
-line, where <value> is taken to be the standard output of the
-specified command with any leading and trailing whitespace trimmed
-off.
+'--trailer <token>=<value>' argument was added at the beginning of
+the "git interpret-trailers" command, where <value> is taken to be the
+standard output of the specified command with any leading and trailing
+whitespace trimmed off.
 +
-If the command contains the `$ARG` string, this string will be
-replaced with the <value> part of an existing trailer with the same
-<token>, if any, before the command is launched.
+The first occurrence of substring `$ARG` will be replaced with the
+<value> part of an existing trailer with the same <token>, if any,
+before the command is launched.
 +
 If some '<token>=<value>' arguments are also passed on the command
-line, when a 'trailer.<token>.command' is configured, the command will
-also be executed for each of these arguments. And the <value> part of
-these arguments, if any, will be used to replace the `$ARG` string in
-the command.
+line, when a 'trailer.<token>.command' is configured, the command is run
+once for each these arguments with the same <token>. And the <value>
+part of these arguments, if any, will be used to replace the first `$ARG`
+string in the command.
 
 EXAMPLES
 --------
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v8 2/2] [GSOC] trailer: add new .cmd config option
  2021-04-09 13:37             ` [PATCH v8 0/2] [GSOC] trailer: add new .cmd " ZheNing Hu via GitGitGadget
  2021-04-09 13:37               ` [PATCH v8 1/2] [GSOC] docs: correct descript of trailer.<token>.command ZheNing Hu via GitGitGadget
@ 2021-04-09 13:37               ` ZheNing Hu via GitGitGadget
  2021-04-09 20:18                 ` Christian Couder
  2021-04-09 19:59               ` [PATCH v8 0/2] " Christian Couder
  2 siblings, 1 reply; 60+ messages in thread
From: ZheNing Hu via GitGitGadget @ 2021-04-09 13:37 UTC (permalink / raw)
  To: git; +Cc: Christian Couder, Junio C Hamano, ZheNing Hu, ZheNing Hu

From: ZheNing Hu <adlternative@gmail.com>

The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name of or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token.  This has two downsides:

* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.

* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatched single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor'), it would result in
a broken command that is not syntactically correct (or
worse).

Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as an
argument to the command.  Instead of "$ARG", users can
refer to the value as positional argument, $1, in their
scripts.

Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
---
 Documentation/git-interpret-trailers.txt | 90 ++++++++++++++++++++----
 t/t7513-interpret-trailers.sh            | 84 ++++++++++++++++++++++
 trailer.c                                | 37 +++++++---
 3 files changed, 187 insertions(+), 24 deletions(-)

diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 3e5aa3a65ae9..1a874a93f49b 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -236,21 +236,38 @@ trailer.<token>.command::
 	be called to automatically add or modify a trailer with the
 	specified <token>.
 +
-When this option is specified, the behavior is as if a special
-'--trailer <token>=<value>' argument was added at the beginning of
-the "git interpret-trailers" command, where <value> is taken to be the
-standard output of the specified command with any leading and trailing
-whitespace trimmed off.
+When this option is specified, the first occurrence of substring $ARG is
+replaced with the value given to the `interpret-trailer` command for the
+same token. It passes the value through `$ARG`, otherwise this option behaves
+in the same way as 'trailer.<token>.cmd'.
++
+The 'trailer.<token>.command' option has been deprecated due to the fact
+that $ARG in the user's command can only be replaced once and that the
+original way of replacing $ARG was not safe. Now the preferred option is
+'trailer.<token>.cmd', which uses a positional argument to pass the value.
 +
-The first occurrence of substring `$ARG` will be replaced with the
-<value> part of an existing trailer with the same <token>, if any,
-before the command is launched.
+When both 'trailer.<token>.cmd' and 'trailer.<token>.command' are given
+for the same <token>, 'trailer.<token>.cmd' is used and
+'trailer.<token>.command' is ignored.
+
+trailer.<token>.cmd::
+	The command specified by this configuration variable is run
+	with a single argument, which is the <value> part of a
+	`--trailer <token>=<value>` on the command line. The output
+	from the command is then used as the value for the <token>
+	in the resulting trailer.
++
+When this option is specified, the behavior is as if a
+'--trailer <token>=<value>' argument was added at the beginning of
+the "git interpret-trailers" command, the command specified by this
+configuration variable will be called with an empty string as the
+argument.
 +
-If some '<token>=<value>' arguments are also passed on the command
-line, when a 'trailer.<token>.command' is configured, the command is run
-once for each these arguments with the same <token>. And the <value>
-part of these arguments, if any, will be used to replace the first `$ARG`
-string in the command.
+If some '--trailer <token>=<value>' arguments are also passed on the
+command line, when a 'trailer.<token>.cmd' is configured, the command
+is run once for each `--trailer <token>=<value>` on the command line
+with the same <token>. And the <value> part of these arguments, if any,
+will be passed to the command as its first argument.
 
 EXAMPLES
 --------
@@ -333,6 +350,53 @@ subject
 Fix #42
 ------------
 
+* Configure a 'cnt' trailer with a cmd use a global script `gcount`
+to record commit counts of a specified author and show how it works:
++
+------------
+$ cat ~/bin/gcount
+#!/bin/sh
+test -n "$1" && git shortlog -s --author="$1" HEAD || true
+$ git config trailer.cnt.key "Commit-count: "
+$ git config trailer.cnt.ifExists "replace"
+$ git config trailer.cnt.cmd "~/bin/gcount"
+$ git interpret-trailers --trailer="cnt:Junio" <<EOF
+> subject
+> 
+> message
+> 
+> EOF
+subject
+
+message
+
+Commit-count: 22484     Junio C Hamano
+------------
+
+* Configure a 'ref' trailer with a cmd use a global script `glog-grep`
+  to grep last relevant commit from git log in the git repository
+  and show how it works:
++
+------------
+$ cat ~/bin/glog-grep
+#!/bin/sh
+test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
+$ git config trailer.ref.key "Reference-to: "
+$ git config trailer.ref.ifExists "replace"
+$ git config trailer.ref.cmd "~/bin/glog-grep"
+$ git interpret-trailers --trailer="ref:Add copyright notices." <<EOF
+> subject
+> 
+> message
+> 
+> EOF
+subject
+
+message
+
+Reference-to: 8bc9a0c769 (Add copyright notices., 2005-04-07)
+------------
+
 * Configure a 'see' trailer with a command to show the subject of a
   commit that is related, and show how it works:
 +
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 6602790b5f4c..68353af3e62e 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -51,6 +51,69 @@ test_expect_success 'setup' '
 	EOF
 '
 
+test_expect_success 'with cmd' '
+	test_when_finished "git config --remove-section trailer.bug" &&
+	git config trailer.bug.key "Bug-maker: " &&
+	git config trailer.bug.ifExists "add" &&
+	git config trailer.bug.cmd "echo \"maybe is\"" &&
+	cat >expected2 <<-EOF &&
+
+	Bug-maker: maybe is
+	Bug-maker: maybe is him
+	Bug-maker: maybe is me
+	EOF
+	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+		>actual2 &&
+	test_cmp expected2 actual2
+'
+
+test_expect_success 'with cmd and $1' '
+	test_when_finished "git config --remove-section trailer.bug" &&
+	git config trailer.bug.key "Bug-maker: " &&
+	git config trailer.bug.ifExists "replace" &&
+	git config trailer.bug.cmd "echo \"\$1\" is" &&
+	cat >expected2 <<-EOF &&
+
+	Bug-maker: me is me
+	EOF
+	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+		>actual2 &&
+	test_cmp expected2 actual2
+'
+
+test_expect_success 'with cmd and $1 with sh -c' '
+	test_when_finished "git config --remove-section trailer.bug" &&
+	git config trailer.bug.key "Bug-maker: " &&
+	git config trailer.bug.ifExists "replace" &&
+	git config trailer.bug.cmd "sh -c \"echo who is \"\$1\"\"" &&
+	cat >expected2 <<-EOF &&
+
+	Bug-maker: who is me
+	EOF
+	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+		>actual2 &&
+	test_cmp expected2 actual2
+'
+
+test_expect_success 'with cmd and $1 with shell script' '
+	test_when_finished "git config --remove-section trailer.bug" &&
+	git config trailer.bug.key "Bug-maker: " &&
+	git config trailer.bug.ifExists "replace" &&
+	git config trailer.bug.cmd "./echoscript" &&
+	cat >expected2 <<-EOF &&
+
+	Bug-maker: who is me
+	EOF
+	cat >echoscript <<-EOF &&
+	#!/bin/sh
+	echo who is "\$1"
+	EOF
+	chmod +x echoscript &&
+	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
+		>actual2 &&
+	test_cmp expected2 actual2
+'
+
 test_expect_success 'without config' '
 	sed -e "s/ Z\$/ /" >expected <<-\EOF &&
 
@@ -1274,6 +1337,27 @@ test_expect_success 'setup a commit' '
 	git commit -m "Add file a.txt"
 '
 
+test_expect_success 'cmd takes precedence over command' '
+	test_when_finished "git config --unset trailer.fix.cmd" &&
+	git config trailer.fix.ifExists "replace" &&
+	git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
+	--abbrev-commit --abbrev=14 \"\$1\" || true" &&
+	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+		--abbrev-commit --abbrev=14 \$ARG" &&
+	FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
+	cat complex_message_body >expected2 &&
+	sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
+		Fixes: $FIXED
+		Acked-by= Z
+		Reviewed-by:
+		Signed-off-by: Z
+		Signed-off-by: A U Thor <author@example.com>
+	EOF
+	git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+		<complex_message >actual2 &&
+	test_cmp expected2 actual2
+'
+
 test_expect_success 'with command using $ARG' '
 	git config trailer.fix.ifExists "replace" &&
 	git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
diff --git a/trailer.c b/trailer.c
index be4e9726421c..bd384befe15b 100644
--- a/trailer.c
+++ b/trailer.c
@@ -14,6 +14,7 @@ struct conf_info {
 	char *name;
 	char *key;
 	char *command;
+	char *cmd;
 	enum trailer_where where;
 	enum trailer_if_exists if_exists;
 	enum trailer_if_missing if_missing;
@@ -127,6 +128,7 @@ static void free_arg_item(struct arg_item *item)
 	free(item->conf.name);
 	free(item->conf.key);
 	free(item->conf.command);
+	free(item->conf.cmd);
 	free(item->token);
 	free(item->value);
 	free(item);
@@ -216,18 +218,24 @@ static int check_if_different(struct trailer_item *in_tok,
 	return 1;
 }
 
-static char *apply_command(const char *command, const char *arg)
+static char *apply_command(struct conf_info *conf, const char *arg)
 {
 	struct strbuf cmd = STRBUF_INIT;
 	struct strbuf buf = STRBUF_INIT;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	char *result;
 
-	strbuf_addstr(&cmd, command);
-	if (arg)
-		strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
-
-	strvec_push(&cp.args, cmd.buf);
+	if (conf->cmd) {
+		strbuf_addstr(&cmd, conf->cmd);
+		strvec_push(&cp.args, cmd.buf);
+		if (arg)
+			strvec_push(&cp.args, arg);
+	} else if (conf->command) {
+		strbuf_addstr(&cmd, conf->command);
+		if (arg)
+			strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
+		strvec_push(&cp.args, cmd.buf);
+	}
 	cp.env = local_repo_env;
 	cp.no_stdin = 1;
 	cp.use_shell = 1;
@@ -247,7 +255,7 @@ static char *apply_command(const char *command, const char *arg)
 
 static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)
 {
-	if (arg_tok->conf.command) {
+	if (arg_tok->conf.command || arg_tok->conf.cmd) {
 		const char *arg;
 		if (arg_tok->value && arg_tok->value[0]) {
 			arg = arg_tok->value;
@@ -257,7 +265,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg
 			else
 				arg = xstrdup("");
 		}
-		arg_tok->value = apply_command(arg_tok->conf.command, arg);
+		arg_tok->value = apply_command(&arg_tok->conf, arg);
 		free((char *)arg);
 	}
 }
@@ -430,6 +438,7 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
 	dst->name = xstrdup_or_null(src->name);
 	dst->key = xstrdup_or_null(src->key);
 	dst->command = xstrdup_or_null(src->command);
+	dst->cmd = xstrdup_or_null(src->cmd);
 }
 
 static struct arg_item *get_conf_item(const char *name)
@@ -454,8 +463,8 @@ static struct arg_item *get_conf_item(const char *name)
 	return item;
 }
 
-enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_WHERE,
-			 TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
+enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_CMD,
+			TRAILER_WHERE, TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
 
 static struct {
 	const char *name;
@@ -463,6 +472,7 @@ static struct {
 } trailer_config_items[] = {
 	{ "key", TRAILER_KEY },
 	{ "command", TRAILER_COMMAND },
+	{ "cmd", TRAILER_CMD },
 	{ "where", TRAILER_WHERE },
 	{ "ifexists", TRAILER_IF_EXISTS },
 	{ "ifmissing", TRAILER_IF_MISSING }
@@ -542,6 +552,11 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
 			warning(_("more than one %s"), conf_key);
 		conf->command = xstrdup(value);
 		break;
+	case TRAILER_CMD:
+		if (conf->cmd)
+			warning(_("more than one %s"), conf_key);
+		conf->cmd = xstrdup(value);
+		break;
 	case TRAILER_WHERE:
 		if (trailer_set_where(&conf->where, value))
 			warning(_("unknown value '%s' for key '%s'"), value, conf_key);
@@ -708,7 +723,7 @@ static void process_command_line_args(struct list_head *arg_head,
 	/* Add an arg item for each configured trailer with a command */
 	list_for_each(pos, &conf_head) {
 		item = list_entry(pos, struct arg_item, list);
-		if (item->conf.command)
+		if (item->conf.cmd || item->conf.command)
 			add_arg_item(arg_head,
 				     xstrdup(token_from_item(item, NULL)),
 				     xstrdup(""),
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v8 1/2] [GSOC] docs: correct descript of trailer.<token>.command
  2021-04-09 13:37               ` [PATCH v8 1/2] [GSOC] docs: correct descript of trailer.<token>.command ZheNing Hu via GitGitGadget
@ 2021-04-09 19:02                 ` Christian Couder
  2021-04-10 13:40                   ` ZheNing Hu
  0 siblings, 1 reply; 60+ messages in thread
From: Christian Couder @ 2021-04-09 19:02 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget; +Cc: git, Junio C Hamano, ZheNing Hu

On Fri, Apr 9, 2021 at 3:37 PM ZheNing Hu via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> From: ZheNing Hu <adlternative@gmail.com>
>
> In the original documentation of `trailer.<token>.command`,
> some descriptions are easily misunderstood. So let's modify
> it to increase its readability.
>
> In addition, clarify that `$ARG` in command can only be
> replaced once since `$ARG` is text replacement.

I think you can remove the "since `$ARG` is text replacement" part.
Otherwise this looks fine.

> Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> ---
>  Documentation/git-interpret-trailers.txt | 22 +++++++++++-----------
>  1 file changed, 11 insertions(+), 11 deletions(-)
>
> diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
> index 96ec6499f001..3e5aa3a65ae9 100644
> --- a/Documentation/git-interpret-trailers.txt
> +++ b/Documentation/git-interpret-trailers.txt
> @@ -237,20 +237,20 @@ trailer.<token>.command::
>         specified <token>.

The beginning of the doc for trailer.<token>.command could already be
improved. It is:

       This option can be used to specify a shell command that will
       be called to automatically add or modify a trailer with the
       specified <token>.

Instead we could say for example:

       This option can be used to specify a shell command that will
       be called:
         - once to automatically add a trailer with the specified
<token>, and then
         - each time a '--trailer <token>=<value>' argument to modify
the <value> of the trailer that this option would produce

>  When this option is specified, the behavior is as if a special

I would rather say:

When the specified command is first called to add a trailer with the
specified <token>, the behavior is as if a special

> -'<token>=<value>' argument were added at the beginning of the command
> -line, where <value> is taken to be the standard output of the
> -specified command with any leading and trailing whitespace trimmed
> -off.
> +'--trailer <token>=<value>' argument was added at the beginning of
> +the "git interpret-trailers" command, where <value> is taken to be the
> +standard output of the specified command with any leading and trailing

Here we can remove "specified" as we now use it at the beginning of
the sentence.

> +whitespace trimmed off.
>  +
> -If the command contains the `$ARG` string, this string will be
> -replaced with the <value> part of an existing trailer with the same
> -<token>, if any, before the command is launched.
> +The first occurrence of substring `$ARG` will be replaced with the
> +<value> part of an existing trailer with the same <token>, if any,
> +before the command is launched.

I think we should not talk about `$ARG` at this point, let's remove
this and see what we can do below in the next paragraph or after it.

>  If some '<token>=<value>' arguments are also passed on the command

s/'<token>=<value>'/'--trailer <token>=<value>'/

> -line, when a 'trailer.<token>.command' is configured, the command will
> -also be executed for each of these arguments. And the <value> part of
> -these arguments, if any, will be used to replace the `$ARG` string in
> -the command.
> +line, when a 'trailer.<token>.command' is configured, the command is run

s/when a 'trailer.<token>.command' is configured, //
s/the command is run/the command is called again/

> +once for each these arguments with the same <token>.

s/each these/each of these/

> And the <value>
> +part of these arguments, if any, will be used to replace the first `$ARG`
> +string in the command.

s/first `$ARG` string/first occurrence of substring `$ARG`/

Let's also add something to explain the purpose of this, for example
we could add:

"This way the command can produce a <value> computed from the <value>
passed in the '--trailer <token>=<value>' argument."

And then let's also explain what happens when the command is called
the first time, with for example:

"For consistency, the first occurrence of substring `$ARG` is also
replaced, this time with the empty string, in the command when the
command is first called to add a trailer with the specified <token>."

To sum up this would give the following (not properly formatted) description:

-------------------
trailer.<token>.command::
       This option can be used to specify a shell command that will be called:
         - once to automatically add a trailer with the specified
<token>, and then
         - each time a '--trailer <token>=<value>' argument to modify
the <value> of the trailer that this option would produce

       When the specified command is first called to add a trailer
with the specified <token>, the behavior is as if a special
       '--trailer <token>=<value>' argument was added at the beginning
of the "git interpret-trailers" command, where <value>
       is taken to be the standard output of the command with any
leading and trailing whitespace trimmed off.

       If some '--trailer <token>=<value>' arguments are also passed
on the command
       line, the command is called again once for each of these
arguments with the same <token>.
       And the <value> part of these arguments, if any, will be used
to replace the first occurrence
       of substring `$ARG` in the command. This way the command can
produce a <value>
       computed from the <value> passed in the '--trailer
<token>=<value>' argument.

       For consistency, the first occurrence of substring `$ARG` is
also replaced, this time with the
       empty string, in the command when the command is first called
to add a trailer with the
       specified <token>.
-------------------

Thanks!

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v8 0/2] [GSOC] trailer: add new .cmd config option
  2021-04-09 13:37             ` [PATCH v8 0/2] [GSOC] trailer: add new .cmd " ZheNing Hu via GitGitGadget
  2021-04-09 13:37               ` [PATCH v8 1/2] [GSOC] docs: correct descript of trailer.<token>.command ZheNing Hu via GitGitGadget
  2021-04-09 13:37               ` [PATCH v8 2/2] [GSOC] trailer: add new .cmd config option ZheNing Hu via GitGitGadget
@ 2021-04-09 19:59               ` Christian Couder
  2 siblings, 0 replies; 60+ messages in thread
From: Christian Couder @ 2021-04-09 19:59 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget; +Cc: git, Junio C Hamano, ZheNing Hu

On Fri, Apr 9, 2021 at 3:37 PM ZheNing Hu via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> In https://lore.kernel.org/git/xmqqv99i4ck2.fsf@gitster.g/ Junio and
> Christian talked about the problem of using strbuf_replace() to replace
> $ARG:
>
>  1. if the user's script has more than one $ARG, only the first one will be
>     replaced, which is incorrected.

Maybe: s/is incorrected/can be puzzling/

>  2. $ARG is textually replaced without shell syntax, which may result a

Not sure what "without shell syntax" means here.

Also: s/may result a/may result in a/

>     broken command when $ARG include some unmatching single quote, very

s/when/if/
s/include/includes/
s/some unmatching/an unmatched/

>     unsafe.
>
> Now pass trailer value as $1 to the trailer command with another
> trailer.<token>.cmd config, to solve these above two problems,

I think the important thing here is to explain that we want to
introduce a new 'trailer.<token>.cmd' config option, so that we can
start deprecating 'trailer.<token>.command' when people have started
using the new 'trailer.<token>.cmd' config option. Maybe something
like:

"To address these issues, let's introduce a new 'trailer.<token>.cmd'
config option that behaves in the same way as
'trailer.<token>.command' except that it passes the trailer value as
$1 to the configured command instead of textually replacing the first
occurence of '$ARG' in it. This will let us slowly deprecate
'trailer.<token>.command' in favor of 'trailer.<token>.cmd' in the
future."

> We are now writing documents that are more readable and correct than before.

I would suggest removing this sentence as I don't think it adds much
to the above.

> ZheNing Hu (2):
>   [GSOC] docs: correct descript of trailer.<token>.command

By the way the following title might be a bit better and shorter:

"[GSOC] docs: improve 'trailer.<token>.command' doc"

>   [GSOC] trailer: add new .cmd config option

Maybe we can afford: s/.cmd/'trailer.<token>.cmd'/

>      -    [GSOC] trailer: add new trailer.<token>.cmd config option
>      +    [GSOC] trailer: add new .cmd config option

Was the previous title too long? Or is there an issue with
GitGitGadget because it contains <token>?

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v8 2/2] [GSOC] trailer: add new .cmd config option
  2021-04-09 13:37               ` [PATCH v8 2/2] [GSOC] trailer: add new .cmd config option ZheNing Hu via GitGitGadget
@ 2021-04-09 20:18                 ` Christian Couder
  2021-04-10 14:09                   ` ZheNing Hu
  0 siblings, 1 reply; 60+ messages in thread
From: Christian Couder @ 2021-04-09 20:18 UTC (permalink / raw)
  To: ZheNing Hu via GitGitGadget; +Cc: git, Junio C Hamano, ZheNing Hu

On Fri, Apr 9, 2021 at 3:37 PM ZheNing Hu via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> From: ZheNing Hu <adlternative@gmail.com>
>
> The `trailer.<token>.command` configuration variable
> specifies a command (run via the shell, so it does not have
> to be a single name of or path to the command, but can be a

s/of or/or/

> shell script), and the first occurrence of substring $ARG is
> replaced with the value given to the `interpret-trailer`
> command for the token.  This has two downsides:

Maybe: s/for the token/for the token in a '--trailer <token>=<value>' argument/

> * The use of $ARG in the mechanism misleads the users that
> the value is passed in the shell variable, and tempt them
> to use $ARG more than once, but that would not work, as
> the second and subsequent $ARG are not replaced.
>
> * Because $ARG is textually replaced without regard to the
> shell language syntax, even '$ARG' (inside a single-quote
> pair), which a user would expect to stay intact, would be
> replaced, and worse, if the value had an unmatched single
> quote (imagine a name like "O'Connor", substituted into
> NAME='$ARG' to make it NAME='O'Connor'), it would result in
> a broken command that is not syntactically correct (or
> worse).
>
> Introduce a new `trailer.<token>.cmd` configuration that
> takes higher precedence to deprecate and eventually remove
> `trailer.<token>.command`, which passes the value as an
> argument to the command.  Instead of "$ARG", users can
> refer to the value as positional argument, $1, in their
> scripts.

Good!

> Helped-by: Junio C Hamano <gitster@pobox.com>
> Helped-by: Christian Couder <christian.couder@gmail.com>
> Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> ---
>  Documentation/git-interpret-trailers.txt | 90 ++++++++++++++++++++----
>  t/t7513-interpret-trailers.sh            | 84 ++++++++++++++++++++++
>  trailer.c                                | 37 +++++++---
>  3 files changed, 187 insertions(+), 24 deletions(-)
>
> diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
> index 3e5aa3a65ae9..1a874a93f49b 100644
> --- a/Documentation/git-interpret-trailers.txt
> +++ b/Documentation/git-interpret-trailers.txt
> @@ -236,21 +236,38 @@ trailer.<token>.command::
>         be called to automatically add or modify a trailer with the
>         specified <token>.
>  +
> -When this option is specified, the behavior is as if a special
> -'--trailer <token>=<value>' argument was added at the beginning of
> -the "git interpret-trailers" command, where <value> is taken to be the
> -standard output of the specified command with any leading and trailing
> -whitespace trimmed off.
> +When this option is specified, the first occurrence of substring $ARG is
> +replaced with the value given to the `interpret-trailer` command for the
> +same token. It passes the value through `$ARG`, otherwise this option behaves
> +in the same way as 'trailer.<token>.cmd'.

Actually I think that we should say first that this behaves in the
same way as the 'trailer.<token>.cmd'.

And this should also replace the first paragraph in the description of
'trailer.<token>.command', not just the second one.

Maybe:

"This option behaves in the same way as 'trailer.<token>.cmd', except
that it doesn't pass anything as argument to the specified command.
Instead the first occurrence of substring $ARG is replaced by the
value that would be passed as argument."

> +The 'trailer.<token>.command' option has been deprecated due to the fact

s/deprecated/deprecated in favor of 'trailer.<token>.cmd'/

> +that $ARG in the user's command can only be replaced once and that the

s/can only be/is only/

> +original way of replacing $ARG was not safe.

s/was/is/

> Now the preferred option is 'trailer.<token>.cmd', which uses a positional argument to pass the value.

I think we can remove this sentence especially if we say "deprecated
in favor of 'trailer.<token>.cmd'" above.

> -The first occurrence of substring `$ARG` will be replaced with the
> -<value> part of an existing trailer with the same <token>, if any,
> -before the command is launched.
> +When both 'trailer.<token>.cmd' and 'trailer.<token>.command' are given
> +for the same <token>, 'trailer.<token>.cmd' is used and
> +'trailer.<token>.command' is ignored.

Ok.

> +trailer.<token>.cmd::

I think we should base the description of this option on what I
suggest in patch 1/2. So let's agree on patch 1/2 before discussing
this.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v8 1/2] [GSOC] docs: correct descript of trailer.<token>.command
  2021-04-09 19:02                 ` Christian Couder
@ 2021-04-10 13:40                   ` ZheNing Hu
  0 siblings, 0 replies; 60+ messages in thread
From: ZheNing Hu @ 2021-04-10 13:40 UTC (permalink / raw)
  To: Christian Couder; +Cc: ZheNing Hu via GitGitGadget, git, Junio C Hamano

Christian Couder <christian.couder@gmail.com> 于2021年4月10日周六 上午3:02写道:
>
> On Fri, Apr 9, 2021 at 3:37 PM ZheNing Hu via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
> >
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > In the original documentation of `trailer.<token>.command`,
> > some descriptions are easily misunderstood. So let's modify
> > it to increase its readability.
> >
> > In addition, clarify that `$ARG` in command can only be
> > replaced once since `$ARG` is text replacement.
>
> I think you can remove the "since `$ARG` is text replacement" part.
> Otherwise this looks fine.
>

Ok.

> > Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> > ---
> >  Documentation/git-interpret-trailers.txt | 22 +++++++++++-----------
> >  1 file changed, 11 insertions(+), 11 deletions(-)
> >
> > diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
> > index 96ec6499f001..3e5aa3a65ae9 100644
> > --- a/Documentation/git-interpret-trailers.txt
> > +++ b/Documentation/git-interpret-trailers.txt
> > @@ -237,20 +237,20 @@ trailer.<token>.command::
> >         specified <token>.
>
> The beginning of the doc for trailer.<token>.command could already be
> improved. It is:
>
>        This option can be used to specify a shell command that will
>        be called to automatically add or modify a trailer with the
>        specified <token>.
>
> Instead we could say for example:
>
>        This option can be used to specify a shell command that will
>        be called:
>          - once to automatically add a trailer with the specified
> <token>, and then
>          - each time a '--trailer <token>=<value>' argument to modify
> the <value> of the trailer that this option would produce
>
> >  When this option is specified, the behavior is as if a special
>
> I would rather say:
>
> When the specified command is first called to add a trailer with the
> specified <token>, the behavior is as if a special
>
> > -'<token>=<value>' argument were added at the beginning of the command
> > -line, where <value> is taken to be the standard output of the
> > -specified command with any leading and trailing whitespace trimmed
> > -off.
> > +'--trailer <token>=<value>' argument was added at the beginning of
> > +the "git interpret-trailers" command, where <value> is taken to be the
> > +standard output of the specified command with any leading and trailing
>
> Here we can remove "specified" as we now use it at the beginning of
> the sentence.
>
> > +whitespace trimmed off.
> >  +
> > -If the command contains the `$ARG` string, this string will be
> > -replaced with the <value> part of an existing trailer with the same
> > -<token>, if any, before the command is launched.
> > +The first occurrence of substring `$ARG` will be replaced with the
> > +<value> part of an existing trailer with the same <token>, if any,
> > +before the command is launched.
>
> I think we should not talk about `$ARG` at this point, let's remove
> this and see what we can do below in the next paragraph or after it.
>

Here I may want to keep the content similar to the original paragraph.
But if you say so, it will be fine.

> >  If some '<token>=<value>' arguments are also passed on the command
>
> s/'<token>=<value>'/'--trailer <token>=<value>'/
>
> > -line, when a 'trailer.<token>.command' is configured, the command will
> > -also be executed for each of these arguments. And the <value> part of
> > -these arguments, if any, will be used to replace the `$ARG` string in
> > -the command.
> > +line, when a 'trailer.<token>.command' is configured, the command is run
>
> s/when a 'trailer.<token>.command' is configured, //
> s/the command is run/the command is called again/
>
> > +once for each these arguments with the same <token>.
>
> s/each these/each of these/
>
> > And the <value>
> > +part of these arguments, if any, will be used to replace the first `$ARG`
> > +string in the command.
>
> s/first `$ARG` string/first occurrence of substring `$ARG`/
>
> Let's also add something to explain the purpose of this, for example
> we could add:
>
> "This way the command can produce a <value> computed from the <value>
> passed in the '--trailer <token>=<value>' argument."
>
> And then let's also explain what happens when the command is called
> the first time, with for example:
>
> "For consistency, the first occurrence of substring `$ARG` is also
> replaced, this time with the empty string, in the command when the
> command is first called to add a trailer with the specified <token>."
>
> To sum up this would give the following (not properly formatted) description:
>
> -------------------
> trailer.<token>.command::
>        This option can be used to specify a shell command that will be called:
>          - once to automatically add a trailer with the specified
> <token>, and then
>          - each time a '--trailer <token>=<value>' argument to modify
> the <value> of the trailer that this option would produce
>

Maybe I am a little confused: this two "-" meaning? Shouldn't it be the
same paragraph?

-       This option can be used to specify a shell command that will
-       be called to automatically add or modify a trailer with the
-       specified <token>.
+       This option can be used to specify a shell command that will be called:
+       - once to automatically add a trailer with the specified
<token>, and then
+       - each time a '--trailer <token>=<value>' argument to modify
the <value> of
+       the trailer that this option would produce.

Wouldn't it be weird?

>        When the specified command is first called to add a trailer
> with the specified <token>, the behavior is as if a special
>        '--trailer <token>=<value>' argument was added at the beginning
> of the "git interpret-trailers" command, where <value>
>        is taken to be the standard output of the command with any
> leading and trailing whitespace trimmed off.
>
>        If some '--trailer <token>=<value>' arguments are also passed
> on the command
>        line, the command is called again once for each of these
> arguments with the same <token>.
>        And the <value> part of these arguments, if any, will be used
> to replace the first occurrence
>        of substring `$ARG` in the command. This way the command can
> produce a <value>
>        computed from the <value> passed in the '--trailer
> <token>=<value>' argument.
>
>        For consistency, the first occurrence of substring `$ARG` is
> also replaced, this time with the
>        empty string, in the command when the command is first called
> to add a trailer with the
>        specified <token>.
> -------------------
>
> Thanks!

Thanks a lot!
It looks more professional than what I wrote.

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v8 2/2] [GSOC] trailer: add new .cmd config option
  2021-04-09 20:18                 ` Christian Couder
@ 2021-04-10 14:09                   ` ZheNing Hu
  0 siblings, 0 replies; 60+ messages in thread
From: ZheNing Hu @ 2021-04-10 14:09 UTC (permalink / raw)
  To: Christian Couder; +Cc: ZheNing Hu via GitGitGadget, git, Junio C Hamano

Christian Couder <christian.couder@gmail.com> 于2021年4月10日周六 上午4:19写道:
>
> On Fri, Apr 9, 2021 at 3:37 PM ZheNing Hu via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
> >
> > From: ZheNing Hu <adlternative@gmail.com>
> >
> > The `trailer.<token>.command` configuration variable
> > specifies a command (run via the shell, so it does not have
> > to be a single name of or path to the command, but can be a
>
> s/of or/or/
>
> > shell script), and the first occurrence of substring $ARG is
> > replaced with the value given to the `interpret-trailer`
> > command for the token.  This has two downsides:
>
> Maybe: s/for the token/for the token in a '--trailer <token>=<value>' argument/
>
> > * The use of $ARG in the mechanism misleads the users that
> > the value is passed in the shell variable, and tempt them
> > to use $ARG more than once, but that would not work, as
> > the second and subsequent $ARG are not replaced.
> >
> > * Because $ARG is textually replaced without regard to the
> > shell language syntax, even '$ARG' (inside a single-quote
> > pair), which a user would expect to stay intact, would be
> > replaced, and worse, if the value had an unmatched single
> > quote (imagine a name like "O'Connor", substituted into
> > NAME='$ARG' to make it NAME='O'Connor'), it would result in
> > a broken command that is not syntactically correct (or
> > worse).
> >
> > Introduce a new `trailer.<token>.cmd` configuration that
> > takes higher precedence to deprecate and eventually remove
> > `trailer.<token>.command`, which passes the value as an
> > argument to the command.  Instead of "$ARG", users can
> > refer to the value as positional argument, $1, in their
> > scripts.
>
> Good!
>
> > Helped-by: Junio C Hamano <gitster@pobox.com>
> > Helped-by: Christian Couder <christian.couder@gmail.com>
> > Signed-off-by: ZheNing Hu <adlternative@gmail.com>
> > ---
> >  Documentation/git-interpret-trailers.txt | 90 ++++++++++++++++++++----
> >  t/t7513-interpret-trailers.sh            | 84 ++++++++++++++++++++++
> >  trailer.c                                | 37 +++++++---
> >  3 files changed, 187 insertions(+), 24 deletions(-)
> >
> > diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
> > index 3e5aa3a65ae9..1a874a93f49b 100644
> > --- a/Documentation/git-interpret-trailers.txt
> > +++ b/Documentation/git-interpret-trailers.txt
> > @@ -236,21 +236,38 @@ trailer.<token>.command::
> >         be called to automatically add or modify a trailer with the
> >         specified <token>.
> >  +
> > -When this option is specified, the behavior is as if a special
> > -'--trailer <token>=<value>' argument was added at the beginning of
> > -the "git interpret-trailers" command, where <value> is taken to be the
> > -standard output of the specified command with any leading and trailing
> > -whitespace trimmed off.
> > +When this option is specified, the first occurrence of substring $ARG is
> > +replaced with the value given to the `interpret-trailer` command for the
> > +same token. It passes the value through `$ARG`, otherwise this option behaves
> > +in the same way as 'trailer.<token>.cmd'.
>
> Actually I think that we should say first that this behaves in the
> same way as the 'trailer.<token>.cmd'.
>
> And this should also replace the first paragraph in the description of
> 'trailer.<token>.command', not just the second one.
>

Make sence. After all, the primary purpose of this patch is to show that
".command" has been deprecated.

> Maybe:
>
> "This option behaves in the same way as 'trailer.<token>.cmd', except
> that it doesn't pass anything as argument to the specified command.
> Instead the first occurrence of substring $ARG is replaced by the
> value that would be passed as argument."
>
> > +The 'trailer.<token>.command' option has been deprecated due to the fact
>
> s/deprecated/deprecated in favor of 'trailer.<token>.cmd'/
>
> > +that $ARG in the user's command can only be replaced once and that the
>
> s/can only be/is only/
>
> > +original way of replacing $ARG was not safe.
>
> s/was/is/
>
> > Now the preferred option is 'trailer.<token>.cmd', which uses a positional argument to pass the value.
>
> I think we can remove this sentence especially if we say "deprecated
> in favor of 'trailer.<token>.cmd'" above.
>

I agree.

> > -The first occurrence of substring `$ARG` will be replaced with the
> > -<value> part of an existing trailer with the same <token>, if any,
> > -before the command is launched.
> > +When both 'trailer.<token>.cmd' and 'trailer.<token>.command' are given
> > +for the same <token>, 'trailer.<token>.cmd' is used and
> > +'trailer.<token>.command' is ignored.
>
> Ok.
>
> > +trailer.<token>.cmd::
>
> I think we should base the description of this option on what I
> suggest in patch 1/2. So let's agree on patch 1/2 before discussing
> this.

Yes, The description of the new .cmd is best approximated by
the .command in 1/2 .

I should @Junio, I don't know if he has any other opinions.

--
ZheNing Hu

^ permalink raw reply	[flat|nested] 60+ messages in thread

end of thread, other threads:[~2021-04-10 14:10 UTC | newest]

Thread overview: 60+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-23 14:53 [PATCH] [GSOC]trailer: change $ARG to environment variable ZheNing Hu via GitGitGadget
2021-03-24 15:42 ` [PATCH v2] [GSOC]trailer: pass arg as positional parameter ZheNing Hu via GitGitGadget
2021-03-24 20:18   ` Junio C Hamano
2021-03-25  1:43     ` ZheNing Hu
2021-03-25 11:53   ` [PATCH v3] " ZheNing Hu via GitGitGadget
2021-03-25 22:28     ` Junio C Hamano
2021-03-26 13:29       ` ZheNing Hu
2021-03-26 16:13     ` [PATCH v4] " ZheNing Hu via GitGitGadget
2021-03-27 18:04       ` Junio C Hamano
2021-03-27 19:53         ` Christian Couder
2021-03-28 10:46           ` ZheNing Hu
2021-03-29  9:04             ` Christian Couder
2021-03-29 13:43               ` ZheNing Hu
2021-03-30  8:45                 ` Christian Couder
2021-03-30 11:22                   ` ZheNing Hu
2021-03-30 15:07                     ` ZheNing Hu
2021-03-30 17:14                       ` Junio C Hamano
2021-03-31  5:14                         ` ZheNing Hu
2021-03-31 18:19                           ` Junio C Hamano
2021-03-31 18:29                             ` Junio C Hamano
2021-04-01  3:56                               ` ZheNing Hu
2021-04-01 19:49                                 ` Junio C Hamano
2021-04-02  2:08                                   ` ZheNing Hu
2021-04-01  3:39                             ` ZheNing Hu
2021-03-31 10:05       ` [PATCH v5 0/2] " ZheNing Hu via GitGitGadget
2021-03-31 10:05         ` [PATCH v5 1/2] [GSOC] run-command: add shell_no_implicit_args option ZheNing Hu via GitGitGadget
2021-04-01  7:22           ` Christian Couder
2021-04-01  9:58             ` ZheNing Hu
2021-03-31 10:05         ` [PATCH v5 2/2] [GSOC]trailer: pass arg as positional parameter ZheNing Hu via GitGitGadget
2021-04-01  7:28         ` [PATCH v5 0/2] " Christian Couder
2021-04-01 10:02           ` ZheNing Hu
2021-04-02 13:26         ` [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option ZheNing Hu via GitGitGadget
2021-04-02 20:48           ` Junio C Hamano
2021-04-03  5:08             ` ZheNing Hu
2021-04-04  5:34               ` Junio C Hamano
2021-04-03  5:51             ` Christian Couder
2021-04-04 23:26               ` Junio C Hamano
2021-04-06  3:47                 ` Christian Couder
2021-04-06  3:52                   ` Christian Couder
2021-04-06  5:16                     ` ZheNing Hu
2021-04-06  5:34                       ` Junio C Hamano
2021-04-06  5:37                       ` Junio C Hamano
2021-04-04  5:43             ` ZheNing Hu
2021-04-04  8:52               ` Christian Couder
2021-04-04  9:53                 ` ZheNing Hu
2021-04-02 23:44           ` Junio C Hamano
2021-04-03  3:22             ` ZheNing Hu
2021-04-03  4:31               ` Junio C Hamano
2021-04-03  5:15                 ` ZheNing Hu
2021-04-04 13:11           ` [PATCH v7] " ZheNing Hu via GitGitGadget
2021-04-06 16:23             ` Christian Couder
2021-04-07  4:51               ` ZheNing Hu
2021-04-09 13:37             ` [PATCH v8 0/2] [GSOC] trailer: add new .cmd " ZheNing Hu via GitGitGadget
2021-04-09 13:37               ` [PATCH v8 1/2] [GSOC] docs: correct descript of trailer.<token>.command ZheNing Hu via GitGitGadget
2021-04-09 19:02                 ` Christian Couder
2021-04-10 13:40                   ` ZheNing Hu
2021-04-09 13:37               ` [PATCH v8 2/2] [GSOC] trailer: add new .cmd config option ZheNing Hu via GitGitGadget
2021-04-09 20:18                 ` Christian Couder
2021-04-10 14:09                   ` ZheNing Hu
2021-04-09 19:59               ` [PATCH v8 0/2] " Christian Couder

git@vger.kernel.org list mirror (unofficial, one of many)

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://public-inbox.org/git
	git clone --mirror http://ou63pmih66umazou.onion/git
	git clone --mirror http://czquwvybam4bgbro.onion/git
	git clone --mirror http://hjrcffqmbrq6wope.onion/git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V1 git git/ https://public-inbox.org/git \
		git@vger.kernel.org
	public-inbox-index git

Example config snippet for mirrors.
Newsgroups are available over NNTP:
	nntp://news.public-inbox.org/inbox.comp.version-control.git
	nntp://ou63pmih66umazou.onion/inbox.comp.version-control.git
	nntp://czquwvybam4bgbro.onion/inbox.comp.version-control.git
	nntp://hjrcffqmbrq6wope.onion/inbox.comp.version-control.git
	nntp://news.gmane.io/gmane.comp.version-control.git
 note: .onion URLs require Tor: https://www.torproject.org/

code repositories for project(s) associated with this inbox:

	https://80x24.org/mirrors/git.git

AGPL code for this site: git clone https://public-inbox.org/public-inbox.git