Skip to contentSkip to conversation

trailer: change $ARG to environment variable

PATCH New ZheNing Hu via GitGitGadget 4 participants 101 messages Mar 23, 2021 – May 3, 2021 Documentation

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 trailer: change $ARG to environment variable

From: ZheNing Hu <[email protected]>

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.

---
[GSOC] trailer: change $ARG to environment variable

In https://lore.kernel.org/git/[email protected]/ 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(-)
Documentation/git-interpret-trailers.txt +2 -1
index 96ec6499f001..7cf7c032a0e9 100644
@@ -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.
 +
t/t7513-interpret-trailers.sh +17 -0
index 6602790b5f4c..d303cf0e4b36 100755
@@ -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 <[email protected]>
+	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" &&
trailer.c +6 -3
index be4e9726421c..42e3b818327a 100644
@@ -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

Signature · 1 line
gitgitgadget
#1 lore raw

PATCH v2 1/1 trailer: pass arg as positional parameter

From: ZheNing Hu <[email protected]>

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.

---
[GSOC]trailer: pass arg as positional parameter

In https://lore.kernel.org/git/[email protected]/ 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 <[email protected]>

## 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.

## 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 <[email protected]>
+ 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(-)
Documentation/git-interpret-trailers.txt +7 -0
index 96ec6499f001..38656b1b3841 100644
@@ -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
 --------
 
t/t7513-interpret-trailers.sh +21 -1
index 6602790b5f4c..7cb81201442a 100755
@@ -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 <[email protected]>
+	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 &&
trailer.c +19 -7
index be4e9726421c..80f47657ff1a 100644
@@ -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

Signature · 1 line
gitgitgadget
#2 lore raw

Re: [PATCH v2] [GSOC]trailer: pass arg as positional parameter

Quoted 16 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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.

Quoted 2 lines

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

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

Quoted 3 lines

1: abc5b04d152f ! 1: 185356d6fc90 [GSOC]trailer: change $ARG to environment variable
@@ Metadata
Author: ZheNing Hu <[email protected]>

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

Quoted 10 lines
 Documentation/git-interpret-trailers.txt |  7 +++++++
 t/t7513-interpret-trailers.sh            | 22 +++++++++++++++++++-
 trailer.c                                | 26 +++++++++++++++++-------
 3 files changed, 47 insertions(+), 8 deletions(-)
Documentation/git-interpret-trailers.txt +0 -0
index 96ec6499f001..38656b1b3841 100644

Documentation/git-interpret-trailers.txt also be executed for each of these arguments. And the <value> part of · line 258

Quoted 7 lines
Documentation/git-interpret-trailers.txt +4 -0
@@ -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"?

Documentation/git-interpret-trailers.txt also be executed for each of these arguments. And the <value> part of · line 260

Quoted 2 lines
Documentation/git-interpret-trailers.txt +2 -0
+	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.

trailer.c struct conf_info { · line 17

Quoted 9 lines
trailer.c +1 -0
index be4e9726421c..80f47657ff1a 100644
@@ -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?

#3 lore raw

Re: [PATCH v2] [GSOC]trailer: pass arg as positional parameter

Quoted 19 lines · Junio C Hamano <[email protected]> 于2021年3月25日周四 上午4:18写道:
Quoted 16 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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.

Quoted 5 lines
Quoted 2 lines

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...

Quoted 8 lines
Quoted 3 lines

1: abc5b04d152f ! 1: 185356d6fc90 [GSOC]trailer: change $ARG to environment variable
@@ Metadata
Author: ZheNing Hu <[email protected]>

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.

Quoted 13 lines
Quoted 13 lines
 Documentation/git-interpret-trailers.txt |  7 +++++++
 t/t7513-interpret-trailers.sh            | 22 +++++++++++++++++++-
 trailer.c                                | 26 +++++++++++++++++-------
 3 files changed, 47 insertions(+), 8 deletions(-)
Documentation/git-interpret-trailers.txt +0 -0
index 96ec6499f001..38656b1b3841 100644
@@ -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.
 
Quoted 7 lines

Documentation/git-interpret-trailers.txt also be executed for each of these arguments. And the <value> part of · line 258

Quoted 4 lines
Documentation/git-interpret-trailers.txt +4 -0
+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.

Quoted 7 lines

Documentation/git-interpret-trailers.txt also be executed for each of these arguments. And the <value> part of · line 260

Quoted 2 lines
Documentation/git-interpret-trailers.txt +2 -0
+     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.

Quoted 3 lines

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

OK.

Quoted 15 lines

trailer.c struct conf_info { · line 17

Quoted 10 lines
trailer.c +1 -0
index be4e9726421c..80f47657ff1a 100644
@@ -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.

Quoted 3 lines

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.

#4 lore raw

PATCH v3 1/1 trailer: pass arg as positional parameter

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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.

---
[GSOC]trailer: pass arg as positional parameter

In https://lore.kernel.org/git/[email protected]/ 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 <[email protected]>
     ++	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(-)
Documentation/git-interpret-trailers.txt +10 -0
index 96ec6499f001..f796041514bf 100644
@@ -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
 --------
 
t/t7513-interpret-trailers.sh +42 -1

51 lines not on this page · raw message

trailer.c +26 -11
index be4e9726421c..634d3f1ff04a 100644
@@ -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

Signature · 1 line
gitgitgadget
#5 lore raw

Re: [PATCH v3] [GSOC]trailer: pass arg as positional parameter

Quoted 3 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Original implementation of trailer.<token>.command use

uses

Quoted 2 lines

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

has

Quoted 1 line

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

replaces the $ARG only once.

Quoted 2 lines

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.

Quoted 3 lines

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.

Quoted 5 lines

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.

Quoted 8 lines

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.

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?

Documentation/git-interpret-trailers.txt also be executed for each of these arguments. And the <value> part of · line 259

Quoted 5 lines
Documentation/git-interpret-trailers.txt +5 -0
+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?

Documentation/git-interpret-trailers.txt also be executed for each of these arguments. And the <value> part of · line 260

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+	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).

Documentation/git-interpret-trailers.txt also be executed for each of these arguments. And the <value> part of · line 263

Quoted 3 lines
Documentation/git-interpret-trailers.txt +3 -0
+	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.

Quoted 38 lines
trailer.c +4 -6
index be4e9726421c..634d3f1ff04a 100644
@@ -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_) {

trailer.c static int check_if_different(struct trailer_item *in_tok, · line 238

Quoted 10 lines
trailer.c +10 -0
+		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.

Quoted 10 lines
trailer.c +1 -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;

trailer.c static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg · line 268

Quoted 5 lines
trailer.c +1 -1
@@ -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.

#6 lore raw

Re: [PATCH v3] [GSOC]trailer: pass arg as positional parameter

Quoted 9 lines · Junio C Hamano <[email protected]> 于2021年3月26日周五 上午6:28写道:
Quoted 3 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

Original implementation of trailer.<token>.command use

uses

Quoted 9 lines
Quoted 2 lines

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

has

Quoted 1 line

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

replaces the $ARG only once.

Okay... singular and plural problems.

Quoted 9 lines
Quoted 2 lines

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.

Quoted 10 lines
Quoted 3 lines

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.

Quoted 9 lines

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.

Quoted 12 lines

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.

Quoted 47 lines

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

Quoted 5 lines

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.

Quoted 8 lines

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.

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.

Quoted 2 lines

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.

Quoted 11 lines

Documentation/git-interpret-trailers.txt also be executed for each of these arguments. And the <value> part of · line 259

Quoted 6 lines
Documentation/git-interpret-trailers.txt +5 -0
+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.

Quoted 17 lines

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?

Quoted 6 lines
Quoted 1 line

+ 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.

Quoted 58 lines

Documentation/git-interpret-trailers.txt also be executed for each of these arguments. And the <value> part of · line 263

Quoted 3 lines
Documentation/git-interpret-trailers.txt +3 -0
+     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.

Quoted 38 lines
trailer.c +4 -6
index be4e9726421c..634d3f1ff04a 100644
@@ -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_) {

trailer.c static int check_if_different(struct trailer_item *in_tok, · line 238

Quoted 10 lines
trailer.c +10 -0
+             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.

Quoted 4 lines

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.

Quoted 20 lines
Quoted 10 lines
trailer.c +1 -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;

trailer.c static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg · line 268

Quoted 5 lines
trailer.c +1 -1
@@ -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.

Quoted 3 lines

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.

Quoted 1 line

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

#7 lore raw

PATCH v4 1/1 trailer: pass arg as positional parameter

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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.

---
[GSOC]trailer: pass arg as positional parameter

In https://lore.kernel.org/git/[email protected]/ 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 <[email protected]>

## 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 <[email protected]>' --trailer 'Reviewed-by: Bob <[email protected]>' 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 <[email protected]>' --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(-)
Documentation/git-interpret-trailers.txt +14 -4
index 96ec6499f001..b73f9c8d71eb 100644
@@ -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 <[email protected]>' --trailer 'Reviewed-by: Bob <[email protected]>' 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 <[email protected]>' --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
 > 
t/t7513-interpret-trailers.sh +42 -1

51 lines not on this page · raw message

trailer.c +26 -11
index be4e9726421c..278e40974a4c 100644
@@ -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

Signature · 1 line
gitgitgadget
#8 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Documentation/git-interpret-trailers.txt also be executed for each of these arguments. And the <value> part of · line 263

Quoted 13 lines · ZheNing Hu via GitGitGadget
Documentation/git-interpret-trailers.txt +9 -0
@@ -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.

Documentation/git-interpret-trailers.txt $ git interpret-trailers --trailer 'Cc: Alice <[email protected]>' --trailer 'Re · line 324

Quoted 13 lines
Documentation/git-interpret-trailers.txt +2 -2
-* 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 <[email protected]>' --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.

Quoted 6 lines
Documentation/git-interpret-trailers.txt +1 -1
@@ -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

Documentation/git-interpret-trailers.txt subject · line 355

Quoted 10 lines
Documentation/git-interpret-trailers.txt +1 -1
   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.

#9 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Quoted 7 lines · Junio C Hamano
Quoted 4 lines · ZheNing Hu via GitGitGadget
Documentation/git-interpret-trailers.txt +0 -0
@@ -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.
 
Quoted 7 lines

Documentation/git-interpret-trailers.txt also be executed for each of these arguments. And the <value> part of · line 261

Quoted 7 lines
Documentation/git-interpret-trailers.txt +7 -0
+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/

Quoted 9 lines

Documentation/git-interpret-trailers.txt also be executed for each of these arguments. And the <value> part of · line 263

Quoted 2 lines
Documentation/git-interpret-trailers.txt +2 -0
+     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.

Quoted 6 lines

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.

Quoted 5 lines

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.

Quoted 2 lines

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'.

Quoted 10 lines
Quoted 10 lines
Documentation/git-interpret-trailers.txt +1 -2
-* 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 <[email protected]>' --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)>"'
Quoted 8 lines

Documentation/git-interpret-trailers.txt $ git interpret-trailers --trailer 'Cc: Alice <[email protected]>' --trailer 'Re · line 324

Quoted 3 lines
Documentation/git-interpret-trailers.txt +1 -0
+$ 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.

Quoted 21 lines
Quoted 6 lines
Documentation/git-interpret-trailers.txt +1 -1
@@ -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

Documentation/git-interpret-trailers.txt subject · line 355

Quoted 10 lines
Documentation/git-interpret-trailers.txt +1 -1
   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?

Quoted 3 lines

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.

Quoted 5 lines

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.

Quoted 7 lines

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.

#10 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Quoted 39 lines · Christian Couder <[email protected]> 于2021年3月28日周日 上午3:53写道:
Quoted 7 lines · Junio C Hamano
Quoted 4 lines · ZheNing Hu via GitGitGadget
Documentation/git-interpret-trailers.txt +0 -0
@@ -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.
 
Quoted 7 lines

Documentation/git-interpret-trailers.txt also be executed for each of these arguments. And the <value> part of · line 261

Quoted 7 lines
Documentation/git-interpret-trailers.txt +7 -0
+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/

Quoted 9 lines

Documentation/git-interpret-trailers.txt also be executed for each of these arguments. And the <value> part of · line 263

Quoted 2 lines
Documentation/git-interpret-trailers.txt +2 -0
+     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.

Quoted 6 lines

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 ...

Quoted 7 lines

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.

Quoted 12 lines
Quoted 5 lines

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>.

Quoted 6 lines

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);

Quoted 9 lines

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.

Quoted 9 lines

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".

Quoted 6 lines

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.

Quoted 6 lines
Quoted 2 lines

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?

Quoted 20 lines
Quoted 11 lines
Quoted 11 lines
Documentation/git-interpret-trailers.txt +1 -2
-* 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 <[email protected]>' --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)>"'
Quoted 8 lines

Documentation/git-interpret-trailers.txt $ git interpret-trailers --trailer 'Cc: Alice <[email protected]>' --trailer 'Re · line 324

Quoted 3 lines
Documentation/git-interpret-trailers.txt +1 -0
+$ 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.

Quoted 4 lines

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.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Documentation/git-interpret-trailers.txt +0 -0
@@ -333,14 +343,14 @@ subject
 Fix #42
 ------------
 
Quoted 16 lines
Quoted 8 lines
Quoted 8 lines
Documentation/git-interpret-trailers.txt +1 -1
-* 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"
Quoted 8 lines

Documentation/git-interpret-trailers.txt subject · line 355

Quoted 4 lines
Documentation/git-interpret-trailers.txt +1 -1
-$ 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.

Quoted 7 lines
Quoted 4 lines

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.

Quoted 7 lines
Quoted 5 lines

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.

Quoted 10 lines
Quoted 8 lines

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

#11 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Quoted 19 lines · ZheNing Hu
Quoted 10 lines · Christian Couder <[email protected]> 于2021年3月28日周日 上午3:53写道:
Quoted 6 lines · Junio C Hamano

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 ...

Quoted 6 lines

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.

Quoted 5 lines

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.

Quoted 23 lines
Quoted 12 lines
Quoted 5 lines

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>.

Quoted 5 lines

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?

trailer.c static int check_if_different(struct trailer_item *in_tok, · line 232

Quoted 2 lines
trailer.c +2 -0
+               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.

Quoted 8 lines
Quoted 8 lines

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.

Quoted 6 lines

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.

Quoted 3 lines

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"

Quoted 21 lines
Quoted 9 lines

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".

Quoted 6 lines

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.

Quoted 3 lines
Quoted 2 lines

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

Quoted 8 lines
Quoted 3 lines

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".

Quoted 6 lines
Quoted 3 lines
Quoted 3 lines

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.

#12 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Quoted 6 lines · Christian Couder <[email protected]> 于2021年3月29日周一 下午5:05写道:
Quoted 6 lines

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

               arg = xstrdup("");
Quoted 8 lines

trailer.c static int check_if_different(struct trailer_item *in_tok, · line 232

Quoted 3 lines

so I think maybe we don't even need this judge in apply_command?

trailer.c +2 -0
+               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.

Quoted 9 lines
Quoted 8 lines
Quoted 8 lines

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.

Quoted 4 lines

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:

Quoted 3 lines

- "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?

Quoted 4 lines

- "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.

Quoted 4 lines

- "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?

Quoted 17 lines

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

Quoted 6 lines

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.

Quoted 3 lines

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.

Quoted 6 lines

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.

Quoted 35 lines

- how to explain that in the doc, and maybe
- how to improve the current description of what happens for ".command"

Quoted 21 lines
Quoted 9 lines

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".

Quoted 6 lines

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.

Quoted 3 lines
Quoted 2 lines

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

Quoted 8 lines
Quoted 3 lines

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:

Quoted 9 lines

- 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.

Quoted 5 lines

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.

Quoted 4 lines
Quoted 4 lines
Quoted 3 lines
Quoted 3 lines

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?

Quoted 8 lines
Quoted 2 lines

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.

Quoted 2 lines

Best,
Christian.

Thanks.

--
ZheNing Hu

#13 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Quoted 36 lines · ZheNing Hu
Quoted 6 lines · Christian Couder <[email protected]> 于2021年3月29日周一 下午5:05写道:
Quoted 6 lines

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

               arg = xstrdup("");
Quoted 7 lines

trailer.c static int check_if_different(struct trailer_item *in_tok, · line 232

Quoted 3 lines

so I think maybe we don't even need this judge in apply_command?

trailer.c +2 -0
+               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.

Quoted 9 lines
Quoted 8 lines
Quoted 8 lines

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.

Quoted 3 lines

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:

Quoted 5 lines
Quoted 2 lines

- "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)>"'
Quoted 5 lines
Quoted 3 lines

- "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
Quoted 5 lines
Quoted 3 lines

- "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).

Quoted 2 lines

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.

Quoted 9 lines
Quoted 9 lines
Quoted 3 lines

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:

Quoted 4 lines
Quoted 1 line

- 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?

Quoted 2 lines

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.

[...]

Quoted 7 lines
Quoted 4 lines

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.

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines

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.

Quoted 6 lines
Quoted 4 lines

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.

#14 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Quoted 63 lines · Christian Couder <[email protected]> 于2021年3月30日周二 下午4:45写道:
Quoted 36 lines · ZheNing Hu
Quoted 6 lines · Christian Couder <[email protected]> 于2021年3月29日周一 下午5:05写道:
Quoted 6 lines

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

               arg = xstrdup("");
Quoted 7 lines

Review

Quoted 3 lines

so I think maybe we don't even need this judge in apply_command?

(quoted diff) +2 -0
+               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.

Quoted 9 lines
Quoted 8 lines
Quoted 8 lines

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.

Quoted 3 lines

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:

Quoted 5 lines
Quoted 2 lines

- "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
-------

Quoted 9 lines

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.

Quoted 40 lines
Quoted 5 lines
Quoted 3 lines

- "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
Quoted 5 lines
Quoted 3 lines

- "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
Quoted 7 lines

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.

Quoted 9 lines
Quoted 2 lines

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".

Quoted 5 lines

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.

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 3 lines

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:

Quoted 9 lines
Quoted 4 lines
Quoted 1 line

- 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.

Quoted 9 lines
Quoted 2 lines

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 :)

Quoted 8 lines

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" .

Quoted 25 lines

[...]

Quoted 7 lines
Quoted 4 lines

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.

Quoted 12 lines
Quoted 11 lines
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines

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.

Quoted 8 lines
Quoted 1 line

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

#15 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Hi, Junio,

Quoted 10 lines · ZheNing Hu <[email protected]> 于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:

Quoted 9 lines

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

#16 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Quoted 2 lines · ZheNing Hu

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.

Quoted 15 lines

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

Quoted 3 lines

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 ...
#17 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Quoted 63 lines · Junio C Hamano <[email protected]> 于2021年3月31日周三 上午1:14写道:
Quoted 2 lines · ZheNing Hu

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.

Quoted 15 lines

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

Quoted 3 lines

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"
Quoted 8 lines

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 functionprepare_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

#18 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Quoted 5 lines · ZheNing Hu

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

Quoted 8 lines

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").

#19 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Quoted 31 lines · Junio C Hamano
Quoted 5 lines · ZheNing Hu

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

Quoted 8 lines

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.

Quoted 7 lines

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.

#20 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Quoted 60 lines · Junio C Hamano <[email protected]> 于2021年4月1日周四 上午2:29写道:
Quoted 31 lines · Junio C Hamano
Quoted 5 lines · ZheNing Hu

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

Quoted 8 lines

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.

Quoted 7 lines

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?

Quoted 9 lines
        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?

Quoted 7 lines

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

#21 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Quoted 7 lines · ZheNing Hu
Quoted 4 lines

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.

Quoted 2 lines

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.

#22 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

Quoted 25 lines · Junio C Hamano <[email protected]> 于2021年4月2日周五 上午3:49写道:
Quoted 7 lines · ZheNing Hu
Quoted 4 lines

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:

Quoted 9 lines

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'.

Quoted 8 lines
Quoted 2 lines

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

#23 lore raw

Re: [PATCH v4] [GSOC]trailer: pass arg as positional parameter

In reply to Junio C Hamano
Quoted 17 lines · Junio C Hamano <[email protected]> 于2021年4月1日周四 上午2:20写道:
Quoted 5 lines · ZheNing Hu

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

Quoted 8 lines

And use:

$ git interpret-trailers --trailer="bug:456" --trailer="bug:789"<<-EOF
EOF
BUG: 123
BUG: 123 456
BUG: 123 789
Quoted 7 lines

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.

Quoted 10 lines

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'.

Quoted 9 lines

*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.

Quoted 7 lines

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.

#24 lore raw

v5 cover trailer: pass arg as positional parameter

In reply to ZheNing Hu via GitGitGadget

In https://lore.kernel.org/git/[email protected]/ 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: Christian Couder <[email protected]>

## 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 <[email protected]>' --trailer 'Reviewed-by: Bob <[email protected]>' 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 <[email protected]>' --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)

Signature · 1 line
gitgitgadget
#25 lore raw

PATCH v5 1/2 run-command: add shell_no_implicit_args option

From: ZheNing Hu <[email protected]>

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.

---
 run-command.c | 8 ++++----
 run-command.h | 1 +
 trailer.c     | 1 +
 3 files changed, 6 insertions(+), 4 deletions(-)
run-command.c +4 -4
index be6bc128cd9d..a2cf6177f522 100644
@@ -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);
run-command.h +1 -0
index d08414a92e73..9597c987c5bb 100644
@@ -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;
trailer.c +1 -0
index be4e9726421c..35dd0f4c8512 100644
@@ -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);
Signature · 1 line
gitgitgadget
#26 lore raw

Re: [PATCH v5 1/2] [GSOC] run-command: add shell_no_implicit_args option

Quoted 4 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

When we use subprocess to run a shell-script, if we have any

Maybe: s/subprocess/a subprocess/

Quoted 5 lines

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.

Quoted 3 lines

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

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

Quoted 2 lines

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

Where will "abc" appear then?

Quoted 2 lines

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.

Quoted 1 line

So let add a new shell_no_implicit_args

Maybe: s/shell_no_implicit_args/shell_no_implicit_args flag/

Quoted 5 lines

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.

#27 lore raw

Re: [PATCH v5 1/2] [GSOC] run-command: add shell_no_implicit_args option

Quoted 34 lines · Christian Couder <[email protected]> 于2021年4月1日周四 下午3:22写道:
Quoted 4 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

When we use subprocess to run a shell-script, if we have any

Maybe: s/subprocess/a subprocess/

Quoted 5 lines

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.

Quoted 3 lines

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

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

Quoted 2 lines

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

Where will "abc" appear then?

Quoted 2 lines

The positional parameters we entered will be printed repeatedly.

Quoted 7 lines

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.

Quoted 1 line

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.

Quoted 7 lines
Quoted 5 lines

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

#28 lore raw

PATCH v5 2/2 trailer: pass arg as positional parameter

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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.

---
 Documentation/git-interpret-trailers.txt | 75 ++++++++++++++++++++----
 t/t7513-interpret-trailers.sh            | 61 ++++++++++++++++++-
 trailer.c                                | 39 ++++++++----
 3 files changed, 152 insertions(+), 23 deletions(-)
Documentation/git-interpret-trailers.txt +65 -10
index 96ec6499f001..bbd1c9bfd65e 100644
@@ -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:
 +
t/t7513-interpret-trailers.sh +60 -1

75 lines not on this page · raw message

trailer.c +27 -12
index 35dd0f4c8512..a000293d6e7e 100644
@@ -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(""),
Signature · 1 line
gitgitgadget
#29 lore raw

Re: [PATCH v5 0/2] [GSOC]trailer: pass arg as positional parameter

In reply to ZheNing Hu via GitGitGadget
Quoted 4 lines · ZheNing Hu via GitGitGadget

In https://lore.kernel.org/git/[email protected]/ 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.

Quoted 2 lines

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".

Quoted 3 lines

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".

#30 lore raw

Re: [PATCH v5 0/2] [GSOC]trailer: pass arg as positional parameter

Quoted 18 lines · Christian Couder <[email protected]> 于2021年4月1日周四 下午3:28写道:
Quoted 4 lines · ZheNing Hu via GitGitGadget

In https://lore.kernel.org/git/[email protected]/ 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.

Quoted 2 lines

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".

Quoted 8 lines
Quoted 3 lines

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

#31 lore raw

PATCH v6 1/1 trailer: add new trailer.<token>.cmd config option

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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.

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

In https://lore.kernel.org/git/[email protected]/ 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 <[email protected]>
      
       ## 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 <[email protected]>
      +------------
      +
       * 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 <[email protected]>
     -+	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(-)
Documentation/git-interpret-trailers.txt +72 -10
index 96ec6499f001..67649ec6134c 100644
@@ -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 <[email protected]>
+------------
+
 * Configure a 'see' trailer with a command to show the subject of a
   commit that is related, and show how it works:
 +
t/t7513-interpret-trailers.sh +94 -1

109 lines not on this page · raw message

trailer.c +27 -11
index be4e9726421c..6aeff6a1bd33 100644
@@ -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

Signature · 1 line
gitgitgadget
#32 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

Quoted 2 lines · ZheNing Hu via GitGitGadget

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).

Quoted 22 lines
 Documentation/git-interpret-trailers.txt | 82 +++++++++++++++++---
 t/t7513-interpret-trailers.sh            | 95 +++++++++++++++++++++++-
 trailer.c                                | 38 +++++++---
 3 files changed, 193 insertions(+), 22 deletions(-)
Documentation/git-interpret-trailers.txt +3 -5
index 96ec6499f001..67649ec6134c 100644
@@ -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.
 +

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 249

Quoted 10 lines
Documentation/git-interpret-trailers.txt +7 -3
-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.

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 258

Quoted 8 lines
Documentation/git-interpret-trailers.txt +8 -0
+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).

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 261

Quoted 3 lines
Documentation/git-interpret-trailers.txt +3 -0
+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?

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 265

Quoted 4 lines
Documentation/git-interpret-trailers.txt +0 -0
 +
 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.

Quoted 33 lines
Documentation/git-interpret-trailers.txt +24 -2
-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)

Documentation/git-interpret-trailers.txt subject · line 381

Quoted 10 lines
Documentation/git-interpret-trailers.txt +10 -0
+------------
+
+* 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?

Quoted 27 lines
Documentation/git-interpret-trailers.txt +16 -0
+$ 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 <[email protected]>
+------------
+
 * Configure a 'see' trailer with a command to show the subject of a
   commit that is related, and show how it works:
 +
t/t7513-interpret-trailers.sh +0 -0
index 6602790b5f4c..923923e57573 100755

t/t7513-interpret-trailers.sh test_expect_success 'setup' ' · line 57

Quoted 7 lines
t/t7513-interpret-trailers.sh +4 -0
@@ -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.

Quoted 9 lines
trailer.c +1 -0
index be4e9726421c..6aeff6a1bd33 100644
...
+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, · line 229

Quoted 10 lines
trailer.c +2 -5
 	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.

trailer.c static int check_if_different(struct trailer_item *in_tok, · line 233

Quoted 4 lines
trailer.c +4 -0
+		strbuf_addstr(&cmd, conf->cmd);
+		strvec_push(&cp.args, cmd.buf);
+		if (arg)
+			strvec_push(&cp.args, arg);

Thanks.

#33 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

Quoted 23 lines · Junio C Hamano <[email protected]> 于2021年4月3日周六 上午4:49写道:

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 261

Quoted 3 lines
Documentation/git-interpret-trailers.txt +3 -0
+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

Quoted 7 lines
        $ 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.

Quoted 5 lines
Quoted 4 lines

+
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

Quoted 7 lines

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.

Quoted 40 lines
Quoted 40 lines
Documentation/git-interpret-trailers.txt +31 -2
-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:
++
+------------
Quoted 9 lines

Documentation/git-interpret-trailers.txt subject · line 381

Quoted 3 lines
Documentation/git-interpret-trailers.txt +3 -0
+$ 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.

Quoted 44 lines
Quoted 27 lines
Documentation/git-interpret-trailers.txt +16 -0
+$ 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 <[email protected]>
+------------
+
 * Configure a 'see' trailer with a command to show the subject of a
   commit that is related, and show how it works:
 +
t/t7513-interpret-trailers.sh +0 -0
index 6602790b5f4c..923923e57573 100755

t/t7513-interpret-trailers.sh test_expect_success 'setup' ' · line 57

Quoted 7 lines
t/t7513-interpret-trailers.sh +4 -0
@@ -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" &&
Quoted 9 lines

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.

Quoted 22 lines
Quoted 9 lines
trailer.c +1 -0
index be4e9726421c..6aeff6a1bd33 100644
...
+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, · line 229

Quoted 10 lines
trailer.c +2 -5
      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.

Quoted 6 lines

trailer.c static int check_if_different(struct trailer_item *in_tok, · line 233

Quoted 4 lines
trailer.c +4 -0
+             strbuf_addstr(&cmd, conf->cmd);
+             strvec_push(&cp.args, cmd.buf);
+             if (arg)
+                     strvec_push(&cp.args, arg);

Thanks.

Thanks.

#34 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

Quoted 7 lines · ZheNing Hu
Quoted 7 lines
Quoted 3 lines

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'?

Quoted 7 lines
Quoted 4 lines

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.

Quoted 7 lines
Quoted 5 lines

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.

Quoted 19 lines
Quoted 11 lines

trailer.c static int check_if_different(struct trailer_item *in_tok, · line 229

Quoted 8 lines
trailer.c +2 -1
index be4e9726421c..6aeff6a1bd33 100644
...
-     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.

Quoted 6 lines

trailer.c static int check_if_different(struct trailer_item *in_tok, · line 233

Quoted 4 lines
trailer.c +4 -0
+             strbuf_addstr(&cmd, conf->cmd);
+             strvec_push(&cp.args, cmd.buf);
+             if (arg)
+                     strvec_push(&cp.args, arg);

Thanks.

Thanks.

#35 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

In reply to Junio C Hamano
Quoted 6 lines · Junio C Hamano

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 253

Quoted 3 lines · ZheNing Hu via GitGitGadget
Documentation/git-interpret-trailers.txt +3 -0
+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.

Quoted 1 line
Quoted 1 line

+ 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>.

Quoted 5 lines

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 258

Quoted 5 lines

The output from the

Documentation/git-interpret-trailers.txt +4 -0
+     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.

Quoted 5 lines

s/If/if/ (downcase).

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 261

Quoted 3 lines
Documentation/git-interpret-trailers.txt +3 -0
+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.

Quoted 11 lines

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.

Quoted 10 lines

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
Quoted 2 lines

(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.

Quoted 1 line

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

I hope the above helps.

Quoted 6 lines
Quoted 3 lines

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.

Quoted 3 lines

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.

Quoted 3 lines

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 266

Quoted 3 lines
Documentation/git-interpret-trailers.txt +1 -2
-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/

#36 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

Quoted 20 lines · Christian Couder
Quoted 7 lines · Junio C Hamano

...
... 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.

Quoted 9 lines

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

Quoted 5 lines
Quoted 1 line
        $ 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.

#37 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

Quoted 23 lines · Junio C Hamano
Quoted 20 lines · Christian Couder
Quoted 7 lines · Junio C Hamano

...
... 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.

Quoted 9 lines

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

Quoted 8 lines
Quoted 5 lines
Quoted 1 line
        $ 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.

Quoted 11 lines

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.

#38 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

Quoted 20 lines · Christian Couder
Quoted 9 lines · Junio C Hamano
Quoted 3 lines · Christian Couder

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.

Quoted 6 lines

If it said

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

Quoted 7 lines
Quoted 5 lines

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.

#39 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

Hi, Junio and Christian,

Quoted 29 lines · Christian Couder <[email protected]> 于2021年4月6日周二 上午11:52写道:
Quoted 26 lines · Christian Couder
Quoted 9 lines · Junio C Hamano
Quoted 3 lines · Christian Couder

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.

Quoted 11 lines

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.

Quoted 6 lines
Quoted 1 line

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

#40 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

Quoted 4 lines · ZheNing Hu

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.

#41 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

In reply to ZheNing Hu
Quoted 7 lines · ZheNing Hu
Quoted 4 lines

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.

#42 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

In reply to Junio C Hamano

Hi, Junio and Christian,

Quoted 27 lines
Quoted 27 lines

+------------
+$ cat ~/bin/git-one

Documentation/git-interpret-trailers.txt +25 -0
+#!/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:
++
+------------
Quoted 9 lines
Quoted 3 lines

+$ 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 <[email protected]>
Date:   Mon Mar 22 14:00:00 2021 -0700

The second batch

$ ~/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 <[email protected]>
Date: Mon Mar 22 14:00:00 2021 -0700

The second batch

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?

Quoted 19 lines
Quoted 19 lines

+$ 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

Documentation/git-interpret-trailers.txt +7 -0
+subject
+
+message
+
+Helped-by: Junio C Hamano <[email protected]>
+------------
+
 * Configure a 'see' trailer with a command to show the subject of a
   commit that is related, and show how it works:
 +
#43 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

Quoted 8 lines · ZheNing Hu

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" != "".

Quoted 31 lines

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 <[email protected]>
Date:   Mon Mar 22 14:00:00 2021 -0700

The second batch

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

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.

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

Cnt: hello there
Cnt: commit 142430338477d9d1bb25be66267225fb58498d92
Author: Junio C Hamano <[email protected]>
Date: Mon Mar 22 14:00:00 2021 -0700

The second batch

And if I turn to use gcount2:

Quoted 8 lines
$ 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.

Quoted 3 lines

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.

#44 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

Quoted 52 lines · Christian Couder <[email protected]> 于2021年4月4日周日 下午4:52写道:
Quoted 8 lines · ZheNing Hu

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" != "".

Quoted 31 lines

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 <[email protected]>
Date:   Mon Mar 22 14:00:00 2021 -0700

The second batch

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

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
Quoted 5 lines

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.

Quoted 22 lines
Quoted 13 lines
$ git interpret-trailers --trailer="cnt:gitster" <<EOF
EOF

Cnt: hello there
Cnt: commit 142430338477d9d1bb25be66267225fb58498d92
Author: Junio C Hamano <[email protected]>
Date: Mon Mar 22 14:00:00 2021 -0700

The second batch

And if I turn to use gcount2:

Quoted 8 lines
$ 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.

Quoted 9 lines

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 ;-)

Quoted 5 lines
Quoted 3 lines

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

#45 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

In reply to ZheNing Hu via GitGitGadget
Quoted 10 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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:
...

Quoted 4 lines
 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 <[email protected]>
	Signed-off-by: C1 E1
	Helped-by: C2 E2
	Mentored-by: C4 E4
	Reported-by: A U Thor <[email protected]>
	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 <[email protected]>

 1 file changed, 1 insertion(+)
[main 97c7a39] hello
 Author: A U Thor <[email protected]>
 Date: Thu Apr 7 15:22:13 2005 -0700
 1 file changed, 1 insertion(+)
actual renamed from expected +1 -1
@@ -4,4 +4,4 @@
 Signed-off-by: C1 E1
 Helped-by: C2 E2
 Mentored-by: C4 E4
-Reported-by: A U Thor <[email protected]>
+Reported-by:

not ok 24 - commit --trailer with -c and command
#
# trailer_commit_base &&
# cat >expected <<-\EOF &&
# hello
#
# Signed-off-by: C O Mitter <[email protected]>
# Signed-off-by: C1 E1
# Helped-by: C2 E2
# Mentored-by: C4 E4
# Reported-by: A U Thor <[email protected]>
# 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
#

#46 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

Quoted 77 lines · Junio C Hamano <[email protected]> 于2021年4月3日周六 上午7:44写道:
Quoted 10 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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:
...

Quoted 4 lines
 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 <[email protected]>
        Signed-off-by: C1 E1
        Helped-by: C2 E2
        Mentored-by: C4 E4
        Reported-by: A U Thor <[email protected]>
        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 <[email protected]>

 1 file changed, 1 insertion(+)
[main 97c7a39] hello
 Author: A U Thor <[email protected]>
 Date: Thu Apr 7 15:22:13 2005 -0700
 1 file changed, 1 insertion(+)
actual 2021-04-02 23:43:10.673085111 +0000 renamed from expected 2021-04-02 23:43:10.649082950 +0000 +1 -1
@@ -4,4 +4,4 @@
 Signed-off-by: C1 E1
 Helped-by: C2 E2
 Mentored-by: C4 E4
-Reported-by: A U Thor <[email protected]>
+Reported-by:

not ok 24 - commit --trailer with -c and command
#

#               trailer_commit_base &&
#               cat >expected <<-\EOF &&
#               hello
#
#               Signed-off-by: C O Mitter <[email protected]>
#               Signed-off-by: C1 E1
#               Helped-by: C2 E2
#               Mentored-by: C4 E4
#               Reported-by: A U Thor <[email protected]>
#               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
Quoted 1 line

#

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

#47 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

Review

Quoted 9 lines · ZheNing Hu

Little bug, Change it like this will work:

        } else if (conf->command) {
                strbuf_addstr(&cmd, conf->command);
(quoted diff) +1 -1
-               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.

#48 lore raw

Re: [PATCH v6] [GSOC] trailer: add new trailer.<token>.cmd config option

Quoted 13 lines · Junio C Hamano <[email protected]> 于2021年4月3日周六 下午12:31写道:

Review

Quoted 9 lines · ZheNing Hu

Little bug, Change it like this will work:

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

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".

Quoted 4 lines

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.

Quoted 5 lines

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,

Quoted 1 line

Thanks.

Thanks.

#49 lore raw

PATCH v7 1/1 trailer: add new trailer.<token>.cmd config option

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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.

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

In https://lore.kernel.org/git/[email protected]/ 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 <[email protected]>
     ++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(-)
Documentation/git-interpret-trailers.txt +74 -12
index 96ec6499f001..83600e93390d 100644
@@ -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:
 +
t/t7513-interpret-trailers.sh +86 -1

101 lines not on this page · raw message

trailer.c +26 -11
index be4e9726421c..bd384befe15b 100644
@@ -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

Signature · 1 line
gitgitgadget
#50 lore raw

Re: [PATCH v7] [GSOC] trailer: add new trailer.<token>.cmd config option

Quoted 15 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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.

Quoted 4 lines

* 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/

Quoted 4 lines

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"!

Quoted 4 lines

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/

Quoted 8 lines

refer to the value as positional argument, $1, in their scripts.

---
[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?

Quoted 5 lines

In https://lore.kernel.org/git/[email protected]/ 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/

Quoted 4 lines

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"

Quoted 5 lines

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.

Quoted 12 lines
 Documentation/git-interpret-trailers.txt | 86 +++++++++++++++++++----
 t/t7513-interpret-trailers.sh            | 87 +++++++++++++++++++++++-
 trailer.c                                | 37 +++++++---
 3 files changed, 186 insertions(+), 24 deletions(-)
Documentation/git-interpret-trailers.txt +0 -0
index 96ec6499f001..83600e93390d 100644
@@ -236,21 +236,36 @@ trailer.<token>.command::
        be called to automatically add or modify a trailer with the
        specified <token>.

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 242

Quoted 10 lines
Documentation/git-interpret-trailers.txt +4 -5
 +
-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'."

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 244

Quoted 4 lines
Documentation/git-interpret-trailers.txt +1 -3
-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/

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 245

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+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/

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 246

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+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.

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 250

Quoted 4 lines
Documentation/git-interpret-trailers.txt +4 -0
+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'

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 259

Quoted 8 lines
Documentation/git-interpret-trailers.txt +8 -0
+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)

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 260

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+argument were added at the beginning of the "git interpret-trailers"

s/were/was/

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 268

Quoted 12 lines
Documentation/git-interpret-trailers.txt +7 -4
+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.

Quoted 41 lines
Documentation/git-interpret-trailers.txt +35 -0
 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

Documentation/git-interpret-trailers.txt subject · line 395

Quoted 10 lines
Documentation/git-interpret-trailers.txt +10 -0
+> subject
+>
+> message
+>
+> EOF
+subject
+
+message
+
+Reference-to: 8bc9a0c769 (Add copyright notices., 2005-04-07)

The added examples look good!

Quoted 5 lines
Documentation/git-interpret-trailers.txt +5 -0
+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\"" &&

Documentation/git-interpret-trailers.txt subject

Quoted 10 lines
Documentation/git-interpret-trailers.txt +10 -0
+       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.

Documentation/git-interpret-trailers.txt

Quoted 13 lines
Documentation/git-interpret-trailers.txt +13 -0
+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".

Documentation/git-interpret-trailers.txt

Quoted 13 lines
Documentation/git-interpret-trailers.txt +13 -0
+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".

Quoted 8 lines
Documentation/git-interpret-trailers.txt +8 -0
+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

Documentation/git-interpret-trailers.txt

Quoted 10 lines
Documentation/git-interpret-trailers.txt +10 -0
+       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.

Quoted 17 lines

test_expect_success 'without config' '

        sed -e "s/ Z\$/ /" >expected <<-\EOF &&
Documentation/git-interpret-trailers.txt +10 -0
@@ -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 &&

Documentation/git-interpret-trailers.txt test_expect_success 'setup a commit' ' · line 1359

Quoted 10 lines
Documentation/git-interpret-trailers.txt +10 -0
+               Fixes: $FIXED
+               Acked-by= Z
+               Reviewed-by:
+               Signed-off-by: Z
+               Signed-off-by: A U Thor <[email protected]>
+       EOF
+       git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+               <complex_message >actual2 &&
+       test_cmp expected2 actual2
+'

Ok.

Documentation/git-interpret-trailers.txt test_expect_success 'setup a commit' ' · line 1363

Quoted 5 lines
Documentation/git-interpret-trailers.txt +2 -1
 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.

#51 lore raw

Re: [PATCH v7] [GSOC] trailer: add new trailer.<token>.cmd config option

Quoted 23 lines · Christian Couder <[email protected]> 于2021年4月7日周三 上午12:23写道:
Quoted 15 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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.

Quoted 4 lines

* 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

Quoted 9 lines

s/unmatching/unmatched/

Quoted 4 lines

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.

Quoted 11 lines
Quoted 4 lines

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/

Quoted 3 lines

refer to the value as positional argument, $1, in their scripts.

Quoted 9 lines
Quoted 5 lines

---
[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"?

Quoted 21 lines
Quoted 5 lines

In https://lore.kernel.org/git/[email protected]/ 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/

Quoted 4 lines

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"

Quoted 5 lines

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.

Quoted 8 lines

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...

(quoted diff) +0 -0

b/Documentation/git-interpret-trailers.txt
index 96ec6499f0..39f742b3dc 100644

Documentation/git-interpret-trailers.txt +11 -11
@@ -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.

Quoted 50 lines
Quoted 12 lines
 Documentation/git-interpret-trailers.txt | 86 +++++++++++++++++++----
 t/t7513-interpret-trailers.sh            | 87 +++++++++++++++++++++++-
 trailer.c                                | 37 +++++++---
 3 files changed, 186 insertions(+), 24 deletions(-)
Documentation/git-interpret-trailers.txt +0 -0
index 96ec6499f001..83600e93390d 100644
@@ -236,21 +236,36 @@ trailer.<token>.command::
        be called to automatically add or modify a trailer with the
        specified <token>.

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 242

Quoted 10 lines
Documentation/git-interpret-trailers.txt +4 -5
 +
-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'."

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 243

Quoted 4 lines
Documentation/git-interpret-trailers.txt +1 -3
-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/

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 244

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+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/

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 245

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+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.

Quoted 8 lines

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 249

Quoted 4 lines
Documentation/git-interpret-trailers.txt +4 -0
+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.

Quoted 80 lines

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 257

Quoted 8 lines
Documentation/git-interpret-trailers.txt +8 -0
+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)

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 258

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+argument were added at the beginning of the "git interpret-trailers"

s/were/was/

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 266

Quoted 12 lines
Documentation/git-interpret-trailers.txt +7 -4
+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.

Quoted 45 lines
Documentation/git-interpret-trailers.txt +39 -0
 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
+>
Quoted 9 lines

Documentation/git-interpret-trailers.txt subject · line 395

Quoted 6 lines
Documentation/git-interpret-trailers.txt +6 -0
+> EOF
+subject
+
+message
+
+Reference-to: 8bc9a0c769 (Add copyright notices., 2005-04-07)

The added examples look good!

Thanks.

Quoted 108 lines
Quoted 5 lines
Documentation/git-interpret-trailers.txt +5 -0
+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\"" &&

Documentation/git-interpret-trailers.txt subject

Quoted 10 lines
Documentation/git-interpret-trailers.txt +10 -0
+       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.

Documentation/git-interpret-trailers.txt

Quoted 13 lines
Documentation/git-interpret-trailers.txt +13 -0
+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".

Documentation/git-interpret-trailers.txt

Quoted 13 lines
Documentation/git-interpret-trailers.txt +13 -0
+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".

Quoted 8 lines
Documentation/git-interpret-trailers.txt +8 -0
+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

Documentation/git-interpret-trailers.txt

Quoted 10 lines
Documentation/git-interpret-trailers.txt +10 -0
+       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.

Quoted 17 lines

test_expect_success 'without config' '

        sed -e "s/ Z\$/ /" >expected <<-\EOF &&
Documentation/git-interpret-trailers.txt +10 -0
@@ -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 &&

Documentation/git-interpret-trailers.txt test_expect_success 'setup a commit' ' · line 1359

Quoted 10 lines
Documentation/git-interpret-trailers.txt +10 -0
+               Fixes: $FIXED
+               Acked-by= Z
+               Reviewed-by:
+               Signed-off-by: Z
+               Signed-off-by: A U Thor <[email protected]>
+       EOF
+       git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+               <complex_message >actual2 &&
+       test_cmp expected2 actual2
+'

Ok.

Documentation/git-interpret-trailers.txt test_expect_success 'setup a commit' ' · line 1363

Quoted 5 lines
Documentation/git-interpret-trailers.txt +2 -1
 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" &&
Quoted 9 lines

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.

Quoted 1 line

The other parts of the patch look good to me.

Thanks, Christian.
--
ZheNing Hu

#52 lore raw

v8 cover trailer: add new .cmd config option

In reply to ZheNing Hu via GitGitGadget

In https://lore.kernel.org/git/[email protected]/ 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 <[email protected]>

## 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 {

Signature · 1 line
gitgitgadget
#53 lore raw

PATCH v8 1/2 docs: correct descript of trailer.<token>.command

From: ZheNing Hu <[email protected]>

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.

---
 Documentation/git-interpret-trailers.txt | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)
Documentation/git-interpret-trailers.txt +11 -11
index 96ec6499f001..3e5aa3a65ae9 100644
@@ -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
 --------
Signature · 1 line
gitgitgadget
#54 lore raw

Re: [PATCH v8 1/2] [GSOC] docs: correct descript of trailer.<token>.command

Quoted 9 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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.

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 237

Quoted 11 lines
---
 Documentation/git-interpret-trailers.txt | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)
Documentation/git-interpret-trailers.txt +0 -0
index 96ec6499f001..3e5aa3a65ae9 100644
@@ -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

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 239

Quoted 1 line
Documentation/git-interpret-trailers.txt +0 -0
 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

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 242

Quoted 7 lines
Documentation/git-interpret-trailers.txt +3 -4
-'<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.

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 247

Quoted 8 lines
Documentation/git-interpret-trailers.txt +4 -3
+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.

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 249

Quoted 1 line
Documentation/git-interpret-trailers.txt +0 -0
 If some '<token>=<value>' arguments are also passed on the command

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

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 250

Quoted 5 lines
Documentation/git-interpret-trailers.txt +1 -4
-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/

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 249

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+once for each these arguments with the same <token>.

s/each these/each of these/

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 253

Quoted 3 lines

And the <value>

Documentation/git-interpret-trailers.txt +2 -0
+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!

#55 lore raw

Re: [PATCH v8 1/2] [GSOC] docs: correct descript of trailer.<token>.command

Quoted 10 lines · Christian Couder <[email protected]> 于2021年4月10日周六 上午3:02写道:
Quoted 7 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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

Quoted 6 lines
Quoted 2 lines

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.

Quoted 46 lines

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 237

Quoted 11 lines
---
 Documentation/git-interpret-trailers.txt | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)
Documentation/git-interpret-trailers.txt +0 -0
index 96ec6499f001..3e5aa3a65ae9 100644
@@ -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

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 239

Quoted 1 line
Documentation/git-interpret-trailers.txt +0 -0
 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

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 242

Quoted 7 lines
Documentation/git-interpret-trailers.txt +3 -4
-'<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.

Quoted 12 lines

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 247

Quoted 8 lines
Documentation/git-interpret-trailers.txt +4 -3
+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.

Quoted 38 lines

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 249

Quoted 1 line
Documentation/git-interpret-trailers.txt +0 -0
 If some '<token>=<value>' arguments are also passed on the command

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

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 250

Quoted 5 lines
Documentation/git-interpret-trailers.txt +1 -4
-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/

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 249

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+once for each these arguments with the same <token>.

s/each these/each of these/

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 253

Quoted 3 lines

And the <value>

Documentation/git-interpret-trailers.txt +2 -0
+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:

Quoted 8 lines

-------------------
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?

Quoted 18 lines

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.

Quoted 8 lines

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

#56 lore raw

PATCH v8 2/2 trailer: add new .cmd config option

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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.

---
 Documentation/git-interpret-trailers.txt | 90 ++++++++++++++++++++----
 t/t7513-interpret-trailers.sh            | 84 ++++++++++++++++++++++
 trailer.c                                | 37 +++++++---
 3 files changed, 187 insertions(+), 24 deletions(-)
Documentation/git-interpret-trailers.txt +77 -13
index 3e5aa3a65ae9..1a874a93f49b 100644
@@ -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:
 +
t/t7513-interpret-trailers.sh +84 -0

96 lines not on this page · raw message

trailer.c +26 -11
index be4e9726421c..bd384befe15b 100644
@@ -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(""),
Signature · 1 line
gitgitgadget
#57 lore raw

Re: [PATCH v8 2/2] [GSOC] trailer: add new .cmd config option

Quoted 6 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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/

Quoted 3 lines

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/

Quoted 14 lines

* 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).

Quoted 6 lines

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!

Quoted 16 lines
---
 Documentation/git-interpret-trailers.txt | 90 ++++++++++++++++++++----
 t/t7513-interpret-trailers.sh            | 84 ++++++++++++++++++++++
 trailer.c                                | 37 +++++++---
 3 files changed, 187 insertions(+), 24 deletions(-)
Documentation/git-interpret-trailers.txt +0 -0
index 3e5aa3a65ae9..1a874a93f49b 100644
@@ -236,21 +236,38 @@ trailer.<token>.command::
        be called to automatically add or modify a trailer with the
        specified <token>.

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 242

Quoted 10 lines
Documentation/git-interpret-trailers.txt +4 -5
 +
-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."

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 244

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+The 'trailer.<token>.command' option has been deprecated due to the fact

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

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 245

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+that $ARG in the user's command can only be replaced once and that the

s/can only be/is only/

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 245

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+original way of replacing $ARG was not safe.

s/was/is/

Quoted 1 line

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.

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 251

Quoted 6 lines
Documentation/git-interpret-trailers.txt +3 -3
-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.

Quoted 1 line

+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.

#58 lore raw

Re: [PATCH v8 2/2] [GSOC] trailer: add new .cmd config option

Quoted 71 lines · Christian Couder <[email protected]> 于2021年4月10日周六 上午4:19写道:
Quoted 6 lines · ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

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/

Quoted 3 lines

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/

Quoted 14 lines

* 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).

Quoted 6 lines

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!

Quoted 16 lines
---
 Documentation/git-interpret-trailers.txt | 90 ++++++++++++++++++++----
 t/t7513-interpret-trailers.sh            | 84 ++++++++++++++++++++++
 trailer.c                                | 37 +++++++---
 3 files changed, 187 insertions(+), 24 deletions(-)
Documentation/git-interpret-trailers.txt +0 -0
index 3e5aa3a65ae9..1a874a93f49b 100644
@@ -236,21 +236,38 @@ trailer.<token>.command::
        be called to automatically add or modify a trailer with the
        specified <token>.

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 242

Quoted 10 lines
Documentation/git-interpret-trailers.txt +4 -5
 +
-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'.

Quoted 3 lines

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.

Quoted 15 lines

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."

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 244

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+The 'trailer.<token>.command' option has been deprecated due to the fact

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

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 245

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+that $ARG in the user's command can only be replaced once and that the

s/can only be/is only/

Quoted 9 lines

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 245

Quoted 1 line
Documentation/git-interpret-trailers.txt +1 -0
+original way of replacing $ARG was not safe.

s/was/is/

Quoted 1 line

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.

Quoted 9 lines

Documentation/git-interpret-trailers.txt trailer.<token>.command:: · line 251

Quoted 6 lines
Documentation/git-interpret-trailers.txt +3 -3
-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.

Quoted 5 lines
Quoted 1 line

+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

#59 lore raw

Re: [PATCH v8 0/2] [GSOC] trailer: add new .cmd config option

In reply to ZheNing Hu via GitGitGadget
Quoted 7 lines · ZheNing Hu via GitGitGadget

In https://lore.kernel.org/git/[email protected]/ 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/

Quoted 1 line

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/

Quoted 1 line

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

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

Quoted 4 lines

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."

Quoted 1 line

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.

Quoted 2 lines

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"

Quoted 1 line

[GSOC] trailer: add new .cmd config option

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

Quoted 2 lines

- [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>?

#60 lore raw

v9 cover trailer: add new .cmd config option

In reply to ZheNing Hu via GitGitGadget

In https://lore.kernel.org/git/[email protected]/ 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 | 96 ++++++++++++++++++++----
 t/t7513-interpret-trailers.sh            | 84 +++++++++++++++++++++
 trailer.c                                | 37 ++++++---
 3 files changed, 190 insertions(+), 27 deletions(-)

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

Range-diff vs v8:

1: 505903811df8 ! 1: 8129ef6c476b [GSOC] docs: correct descript of trailer.<token>.command
@@ Commit message
it to increase its readability.

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

## Documentation/git-interpret-trailers.txt ##
-@@ Documentation/git-interpret-trailers.txt: trailer.<token>.command::
- specified <token>.
+@@ Documentation/git-interpret-trailers.txt: trailer.<token>.ifmissing::
+ that option for trailers with the specified <token>.
+
+ trailer.<token>.command::
+- 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.
+
- When this option is specified, the behavior is as if a special +-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 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 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 '--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.
+
- If some '<token>=<value>' arguments are also passed on the command +-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.
++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>.

EXAMPLES
--------
2: 3dc8983a4702 ! 2: 7f645ec95f48 [GSOC] trailer: add new .cmd config option
@@ Commit message

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
+ to be a single name 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:
+ command for the token in a '--trailer <token>=<value>' argument.
+
+ 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
@@ Commit message

## Documentation/git-interpret-trailers.txt ##
-@@ 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 --'--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'.
+@@ Documentation/git-interpret-trailers.txt: trailer.<token>.ifmissing::
+ that option for trailers with the specified <token>.
+
+ trailer.<token>.command::
++ 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 in favor of ++'trailer.<token>.cmd' due to the fact that $ARG in the user's command is ++only replaced once and that the original way of replacing $ARG is not safe.
++
-+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.
+ 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
+@@ Documentation/git-interpret-trailers.txt: 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.
++of these arguments, if any, will be passed to the command as its
++first argument. This way the command can produce a <value> computed ++from the <value> passed in the '--trailer <token>=<value>' 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.
+-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>.
++For consistency, the $1 is also passed, this time with the empty string, ++in the command when the command is first called to add a trailer with ++the specified <token>.

EXAMPLES
--------

Signature · 1 line
gitgitgadget
#61 lore raw

PATCH v9 1/2 docs: correct descript of trailer.<token>.command

From: ZheNing Hu <[email protected]>

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.

---
 Documentation/git-interpret-trailers.txt | 37 ++++++++++++++----------
 1 file changed, 21 insertions(+), 16 deletions(-)
Documentation/git-interpret-trailers.txt +21 -16
index 96ec6499f001..6f2a7a130464 100644
@@ -232,25 +232,30 @@ trailer.<token>.ifmissing::
 	that option for trailers with the specified <token>.
 
 trailer.<token>.command::
-	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.
 +
-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 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 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.
+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.
 +
-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.
+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>.
 
 EXAMPLES
 --------
Signature · 1 line
gitgitgadget
#62 lore raw

Re: [PATCH v9 1/2] [GSOC] docs: correct descript of trailer.<token>.command

Documentation/git-interpret-trailers.txt trailer.<token>.ifmissing:: · line 245

Quoted 13 lines · ZheNing Hu via GitGitGadget

trailer.<token>.command::

Documentation/git-interpret-trailers.txt +10 -1
+	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
+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.

So, with

[trailer "foo"] command = "echo $ARG"

in your .git/config

    $ git interpret-trailers </dev/null

gives 'foo:'.

Documentation/git-interpret-trailers.txt trailer.<token>.ifmissing:: · line 258

Quoted 12 lines
Documentation/git-interpret-trailers.txt +11 -0
+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>.

And then

    $ git interpret-trailers --trailer=foo:F </dev/null

would give you

foo:
foo: F

The above is quite an easy to read explanation of the behaviour.

I somehow wonder if this "run with empty even without anything on the command line" a misfeature, and I'd prefer to iron it out before we add .cmd, because we may not want to inherit it to the new .cmd, just like we avoided '$ARG' that does not properly quote and replaces only once from getting inherited by .cmd mechanism.

The reason why I suspect this may be a misfeature is because I do not see any way to avoid 'foo' trailer once trailer.foo.command is set. Which means I cannot use this mechanism to emulate "commit -s", which would hopefully be something like

	[trailer "signoff"]
		command = "git var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/"
		ifexists = addIfDifferentNeighbor

And trailer.signoff.ifmissing=donothing would not help in this case, either, I am afraid, as that would not just suppress the automatic empty one, which is fairly useless, but also suppresses the one that is made in response to the option from the command line.

Christian? What's your thought on this?

I can understand that it sometimes may be useful to unconditionally be able to add a trailer without doing anything from the command line, but it feels fairly useless that an empty one is automatically added, that the only way to hide that empty one from the end result is to use ifexists=replace, and that there is no apparent way to remove the empty one.

The --trim-empty option is a bit too crude a band-aid to use, as the existing log message may have an unrelated trailer for which it is perfectly valid not to have any value.

The fix we'd do when introducing .cmd should also get rid of this initial "run with an empty even when not asked"?

Or if the execution without any input from the command line were truly useful sometimes, a configuration variable that disables this behaviour, i.e.

[trailer "signoff"]
command = "git who"
ifexists = addIfDifferentNeighbor
implicitExecution = false

so that

	git commit --trailer=signoff:Couder --trailer=signoff:gitster@

would give us two sign-offs without the empty one, perhaps?

In any case, the documentation update in this step looks reasonably well written.

Thanks.

#63 lore raw

Re: [PATCH v9 1/2] [GSOC] docs: correct descript of trailer.<token>.command

Sorry for the late answer.

Quoted 19 lines · Junio C Hamano

Documentation/git-interpret-trailers.txt trailer.<token>.ifmissing:: · line 245

Quoted 13 lines · ZheNing Hu via GitGitGadget

trailer.<token>.command::

Documentation/git-interpret-trailers.txt +10 -1
+     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
+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.

So, with

Quoted 7 lines

[trailer "foo"] command = "echo $ARG"

in your .git/config

    $ git interpret-trailers </dev/null

gives 'foo:'.

Yeah:

------

$ git -c trailer.foo.command='echo $ARG' interpret-trailers </dev/null

foo:
------

Quoted 15 lines

Documentation/git-interpret-trailers.txt trailer.<token>.ifmissing:: · line 258

Quoted 12 lines
Documentation/git-interpret-trailers.txt +11 -0
+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>.

And then

Quoted 6 lines
    $ git interpret-trailers --trailer=foo:F </dev/null

would give you

foo:
foo: F

Yeah:

------
git -c trailer.foo.command='echo $ARG' interpret-trailers --trailer=foo:F </dev/null

foo:
foo: F
------

Quoted 7 lines

The above is quite an easy to read explanation of the behaviour.

I somehow wonder if this "run with empty even without anything on the command line" a misfeature, and I'd prefer to iron it out before we add .cmd, because we may not want to inherit it to the new .cmd, just like we avoided '$ARG' that does not properly quote and replaces only once from getting inherited by .cmd mechanism.

I don't think it's a misfeature. I think it can be very useful in some cases like with:

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

My opinion is that we should have added a trailer.<token>.runMode config option along with trailer.<token>.command. This was not discussed unfortunately when ".command" was implemented, but it seems to be a good idea now.

Quoted 3 lines

The reason why I suspect this may be a misfeature is because I do not see any way to avoid 'foo' trailer once trailer.foo.command is set.

It can be avoided when the --trim-empty CLI option can be used. A hook to remove empty trailers (which might call git interpret-trailers --trim-empty itself) could also be used when --trim-empty cannot be used directly.

Quoted 11 lines

Which means I cannot use this mechanism to emulate "commit -s", which would hopefully be something like

[trailer "signoff"]
command = "git var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/" ifexists = addIfDifferentNeighbor

And trailer.signoff.ifmissing=donothing would not help in this case, either, I am afraid, as that would not just suppress the automatic empty one, which is fairly useless, but also suppresses the one that is made in response to the option from the command line.

I agree that the current mechanism cannot easily emulate "commit -s".

Quoted 9 lines

Christian? What's your thought on this?

I can understand that it sometimes may be useful to unconditionally be able to add a trailer without doing anything from the command line, but it feels fairly useless that an empty one is automatically added, that the only way to hide that empty one from the end result is to use ifexists=replace, and that there is no apparent way to remove the empty one.

Quoted 3 lines

The --trim-empty option is a bit too crude a band-aid to use, as the existing log message may have an unrelated trailer for which it is perfectly valid not to have any value.

I agree that --trim-empty is not usable sometimes. Another idea would be to add 'trailer.<token>.trimEmpty' to be able to do things like:

------
git -c trailer.foo.trimEmpty=true -c trailer.foo.command='echo $ARG' interpret-trailers --trailer=foo:F </dev/null

foo: F
------

It could also be used along with git commit --trailer=foo:F.

Quoted 2 lines

The fix we'd do when introducing .cmd should also get rid of this initial "run with an empty even when not asked"?

I don't think it's a good idea to make ".cmd" behave in a different way as ".command" regarding this. Yes, there could be a short term gain because people could use ".cmd" when they don't want the initial "run with an empty value", but, as some people might still want this initial run, long term it looks like a burden that might:

- make it more difficult to understand how ".cmd" and ".command" both work
- make it more difficult to migrate from ".command" to ".cmd"
- prevent us from deprecating ".command" in favor of ".cmd"

Quoted 9 lines

Or if the execution without any input from the command line were truly useful sometimes, a configuration variable that disables this behaviour, i.e.

[trailer "signoff"]
command = "git who"
ifexists = addIfDifferentNeighbor
implicitExecution = false

Quoted 5 lines

so that

        git commit --trailer=signoff:Couder --trailer=signoff:gitster@

would give us two sign-offs without the empty one, perhaps?

Yeah, that's basically the same idea that we previously discussed with 'tailer.<token>.runMode' (or 'trailer.<token>.alwaysRunCmd') in:

https://lore.kernel.org/git/CAP8UFD0OMJfkuX_JoDros7h0B20D8sm0ZbtkVpL3dCYRV_M=OA@mail.gmail.com/

Quoted 2 lines

In any case, the documentation update in this step looks reasonably well written.

Thanks!

#64 lore raw

Re: [PATCH v9 1/2] [GSOC] docs: correct descript of trailer.<token>.command

Quoted 7 lines · Christian Couder
$ git config trailer.sign.command 'echo "$(git config user.name)
<$(git config user.email)>"'

My opinion is that we should have added a trailer.<token>.runMode config option along with trailer.<token>.command. This was not discussed unfortunately when ".command" was implemented, but it seems to be a good idea now.

Yes, without a knob to diable/enable, the "feature" is pretty much useless.

Quoted 4 lines

It can be avoided when the --trim-empty CLI option can be used. A hook to remove empty trailers (which might call git interpret-trailers --trim-empty itself) could also be used when --trim-empty cannot be used directly.

And as you know, --trim-empty that applies to all trailer keys would destroy other trailers and trailer.<token>.trimEmpty, even if it were available, would not work at all to remove it for the case you cited above, to add "user.name <user.email>", which is not an empty string.

Quoted 4 lines

I agree that the current mechanism cannot easily emulate "commit -s".
...
I agree that --trim-empty is not usable sometimes. Another idea would be to add 'trailer.<token>.trimEmpty' to be able to do things like:

"easily"? "sometimes"?

"--trim-empty" is unusable, trailer.<token>.trimEmpty would not work even if it were added because the reason why this is broken is not because it gives an empty value, but because it runs even when it is not asked and there is no way to turn it off.

This shows that even the only plausibly-useful use case we've seen so far is not very well supported, and necessary options/knobs to make it usable have not been invented and implemented.

It is time for us to admit that this is a misfeature that is not well thought out. Recognising that .command is broken is the first step to remedy it with a better alternative in .cmd; otherwise we would inherit the same breakage in the replacement.

The only reasonable way out I would think of is to hide the unconditional execution behind trailer.<token>.runMode, and make the default for runMode to "do not run when --trailer=<token>[:<value>] is not asked from the command line" for .cmd; it is OK for .command not to honor the knob, as we will get rid of it once we see .cmd works well.

Thanks.

#65 lore raw

PATCH v9 2/2 trailer: add new .cmd config option

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

The trailer.<token>.command configuration variable specifies a command (run via the shell, so it does not have to be a single name 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 in a '--trailer <token>=<value>' argument.

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.

---
 Documentation/git-interpret-trailers.txt | 75 ++++++++++++++++++---
 t/t7513-interpret-trailers.sh            | 84 ++++++++++++++++++++++++
 trailer.c                                | 37 +++++++----
 3 files changed, 177 insertions(+), 19 deletions(-)
Documentation/git-interpret-trailers.txt +67 -8
index 6f2a7a130464..dc1f69fa8b67 100644
@@ -232,6 +232,20 @@ trailer.<token>.ifmissing::
 	that option for trailers with the specified <token>.
 
 trailer.<token>.command::
+	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 in favor of
+'trailer.<token>.cmd' due to the fact that $ARG in the user's command is
+only replaced once and that the original way of replacing $ARG is not safe.
++
+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::
 	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
@@ -247,15 +261,13 @@ 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.
+of these arguments, if any, will be passed to the command as its
+first argument. 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>.
+For consistency, the $1 is also passed, this time with the empty string,
+in the command when the command is first called to add a trailer with
+the specified <token>.
 
 EXAMPLES
 --------
@@ -338,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:
 +
t/t7513-interpret-trailers.sh +84 -0

96 lines not on this page · raw message

trailer.c +26 -11
index be4e9726421c..bd384befe15b 100644
@@ -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(""),
Signature · 1 line
gitgitgadget
#66 lore raw

Re: [PATCH v9 2/2] [GSOC] trailer: add new .cmd config option

Documentation/git-interpret-trailers.txt leading and trailing whitespace trimmed off. · line 270

Quoted 3 lines · ZheNing Hu via GitGitGadget
Documentation/git-interpret-trailers.txt +3 -0
+For consistency, the $1 is also passed, this time with the empty string,
+in the command when the command is first called to add a trailer with
+the specified <token>.

I guess the same question as 1/2 applies to this part. I am not sure what "consistency" the behaviour of calling the configured command with no argument is trying to achieve. To me, .cmd doing this may be for consistency with .command but I am not sure why the consistency is even desiable.

Quoted 8 lines

+$ cat ~/bin/gcount

Documentation/git-interpret-trailers.txt +7 -0
+#!/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

Documentation/git-interpret-trailers.txt subject · line 374

Quoted 10 lines
Documentation/git-interpret-trailers.txt +10 -0
+> 
+> message
+> 
+> EOF
+subject
+
+message
+
+Commit-count: 22484     Junio C Hamano
+------------

This and the other (omitted) example demonstrates how the initial "empty" invocation is useless by using "replace". Which also means that you cannot add more than one trailer of the same <key> with the mechanism (since the older ones are replaced with the latest).

The code change and the test change are consistent with the design, though.

Thanks.

#67 lore raw

Re: [PATCH v9 2/2] [GSOC] trailer: add new .cmd config option

Quoted 12 lines · Junio C Hamano

Documentation/git-interpret-trailers.txt leading and trailing whitespace trimmed off. · line 270

Quoted 3 lines · ZheNing Hu via GitGitGadget
Documentation/git-interpret-trailers.txt +3 -0
+For consistency, the $1 is also passed, this time with the empty string,
+in the command when the command is first called to add a trailer with
+the specified <token>.

I guess the same question as 1/2 applies to this part. I am not sure what "consistency" the behaviour of calling the configured command with no argument is trying to achieve. To me, .cmd doing this may be for consistency with .command but I am not sure why the consistency is even desiable.

It might be desirable to make it easier for people to migrate from ".command" to ".cmd". I agree this is debatable, but I don't see a big downside in it. Maybe, if no argument was passed at all the first time the command is called instead of the empty string, the command could then know that it's called for the first time. I am not sure this would be very helpful in practice though.

Quoted 15 lines
Quoted 15 lines

+$ cat ~/bin/gcount

Documentation/git-interpret-trailers.txt +14 -0
+#!/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
Quoted 8 lines

Documentation/git-interpret-trailers.txt subject · line 374

Quoted 3 lines
Documentation/git-interpret-trailers.txt +3 -0
+
+Commit-count: 22484     Junio C Hamano
+------------

This and the other (omitted) example demonstrates how the initial "empty" invocation is useless by using "replace". Which also means that you cannot add more than one trailer of the same <key> with the mechanism (since the older ones are replaced with the latest).

You can add more than one trailer with the same key if you don't use "replace" but use "--trim-empty" on the command line, so that an empty trailer added by the initial invocation is removed. And we can later add a trailer.<token>.runMode to remove the initial invocation.

Quoted 2 lines

The code change and the test change are consistent with the design, though.

Yeah, this patch looks good to me now.

Thanks!

#68 lore raw

Re: [PATCH v9 2/2] [GSOC] trailer: add new .cmd config option

Hi, Christian and Junio,

Quoted 15 lines · Christian Couder <[email protected]> 于2021年4月13日周二 下午3:33写道:
Quoted 12 lines · Junio C Hamano

Documentation/git-interpret-trailers.txt leading and trailing whitespace trimmed off. · line 270

Quoted 3 lines · ZheNing Hu via GitGitGadget
Documentation/git-interpret-trailers.txt +3 -0
+For consistency, the $1 is also passed, this time with the empty string,
+in the command when the command is first called to add a trailer with
+the specified <token>.

I guess the same question as 1/2 applies to this part. I am not sure what "consistency" the behaviour of calling the configured command with no argument is trying to achieve. To me, .cmd doing this may be for consistency with .command but I am not sure why the consistency is even desirable.

Quoted 7 lines

It might be desirable to make it easier for people to migrate from ".command" to ".cmd". I agree this is debatable, but I don't see a big downside in it. Maybe, if no argument was passed at all the first time the command is called instead of the empty string, the command could then know that it's called for the first time. I am not sure this would be very helpful in practice though.

If i'm not wrong, Christan meant that this command must run so it's "consistency", and Junio thinks this "consistency" is not needed.

It is true that there is not much harm in keeping .cmd at the behavior of .command now. But I remember the trailer.<token>.runmode that Christan said before, perhaps adding it in the subsequent iterations can solve Junio's doubts. Sometimes I also confirm that the behavior of git interpret-trailers is very strange too.

Quoted 24 lines
Quoted 15 lines
Quoted 15 lines

+$ cat ~/bin/gcount

Documentation/git-interpret-trailers.txt +14 -0
+#!/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
Quoted 8 lines

Documentation/git-interpret-trailers.txt subject · line 374

Quoted 3 lines
Documentation/git-interpret-trailers.txt +3 -0
+
+Commit-count: 22484     Junio C Hamano
+------------

This and the other (omitted) example demonstrates how the initial "empty" invocation is useless by using "replace". Which also means that you cannot add more than one trailer of the same <key> with the mechanism (since the older ones are replaced with the latest).

Quoted 5 lines

You can add more than one trailer with the same key if you don't use "replace" but use "--trim-empty" on the command line, so that an empty trailer added by the initial invocation is removed. And we can later add a trailer.<token>.runMode to remove the initial invocation.

Yes, something like:

trailer.see.command=echo "$ARG"

git interpret-trailers --trim-empty --trailer="see = lll" --trailer="see:jj"</dev/null

see: lll
see: jj

Quoted 6 lines
Quoted 2 lines

The code change and the test change are consistent with the design, though.

Yeah, this patch looks good to me now.

Thanks!

So is there anything else should I improve?

Thanks.
--
ZheNing Hu

#69 lore raw

Re: [PATCH v9 2/2] [GSOC] trailer: add new .cmd config option

Quoted 10 lines · ZheNing Hu
Quoted 7 lines

It might be desirable to make it easier for people to migrate from ".command" to ".cmd". I agree this is debatable, but I don't see a big downside in it. Maybe, if no argument was passed at all the first time the command is called instead of the empty string, the command could then know that it's called for the first time. I am not sure this would be very helpful in practice though.

If i'm not wrong, Christan meant that this command must run so it's "consistency", and Junio thinks this "consistency" is not needed.

My stance actually is a bit stronger than that. I suspect that running the command without argument once even when no --trailer on the command line asks for that <key> is a misfeature, if not a bug (only because it is now documented by 1/2 as such---before that, at least I did not read the document that way). And unless it is shown that it is not a misfeature but is a useful behaviour with an example use case that benefits from it, I would prefer not to replicate it in the ".cmd".

Quoted 2 lines

It is true that there is not much harm in keeping .cmd at the behavior of .command now.

It is far from "there is not much harm". It misses the whole point of the exercise.

Only replacing the first occurrence and not the second and subsequent occurrence is not what we are keeping in ".cmd".

Replacing in an unsafe way with respect to the command line syntax is not what we are keeping in ".cmd".

That is because these two are misfeatures if not bugs (only because they are documented).

In fact, the only reason why we are introducing .cmd is so that we can deprecate .command and get rid of its misfeatures while keeping only good bits.

So I am waiting to hear why it is not a misfeature. If it is not, then surely I am fine to keep it for now and add a workaround later, but until that happens, I do not think "commit --trailer" can be used as a way to allow end-users emulate "-s" for their favorite trailer like helped-by, acked-by, etc.

#70 lore raw

Re: [PATCH v9 2/2] [GSOC] trailer: add new .cmd config option

Quoted 42 lines · Junio C Hamano <[email protected]> 于2021年4月14日周三 上午3:18写道:
Quoted 10 lines · ZheNing Hu
Quoted 7 lines

It might be desirable to make it easier for people to migrate from ".command" to ".cmd". I agree this is debatable, but I don't see a big downside in it. Maybe, if no argument was passed at all the first time the command is called instead of the empty string, the command could then know that it's called for the first time. I am not sure this would be very helpful in practice though.

If i'm not wrong, Christan meant that this command must run so it's "consistency", and Junio thinks this "consistency" is not needed.

My stance actually is a bit stronger than that. I suspect that running the command without argument once even when no --trailer on the command line asks for that <key> is a misfeature, if not a bug (only because it is now documented by 1/2 as such---before that, at least I did not read the document that way). And unless it is shown that it is not a misfeature but is a useful behaviour with an example use case that benefits from it, I would prefer not to replicate it in the ".cmd".

Quoted 2 lines

It is true that there is not much harm in keeping .cmd at the behavior of .command now.

It is far from "there is not much harm". It misses the whole point of the exercise.

Only replacing the first occurrence and not the second and subsequent occurrence is not what we are keeping in ".cmd".

Replacing in an unsafe way with respect to the command line syntax is not what we are keeping in ".cmd".

That is because these two are misfeatures if not bugs (only because they are documented).

In fact, the only reason why we are introducing .cmd is so that we can deprecate .command and get rid of its misfeatures while keeping only good bits.

Quoted 6 lines

So I am waiting to hear why it is not a misfeature. If it is not, then surely I am fine to keep it for now and add a workaround later, but until that happens, I do not think "commit --trailer" can be used as a way to allow end-users emulate "-s" for their favorite trailer like helped-by, acked-by, etc.

If it is really necessary to solve this "empty execution" in .cmd, Maybe we need to consider two points:
* Do we need a new config flag as you said [implicitExecution = false] or just drop it? Has anyone been relying on the "empty execution" of .command before? This may be worthy of concern.
* Do we need trailer.<token>.runMode as Christan said before? I rejected his this suggestion before, and now I regret it a bit.

--
ZheNing Hu

#71 lore raw

Re: [PATCH v9 2/2] [GSOC] trailer: add new .cmd config option

Quoted 7 lines · ZheNing Hu
Quoted 6 lines

So I am waiting to hear why it is not a misfeature. If it is not, then surely I am fine to keep it for now and add a workaround later, but until that happens, I do not think "commit --trailer" can be used as a way to allow end-users emulate "-s" for their favorite trailer like helped-by, acked-by, etc.

Quoted 6 lines

If it is really necessary to solve this "empty execution" in .cmd,

Maybe we need to consider two points:
* Do we need a new config flag as you said [implicitExecution = false] or just drop it? Has anyone been relying on the "empty execution" of .command before? This may be worthy of concern.

Yes, if it is useful sometimes to run the .command or .cmd with empty <value> even when nobody asks for it on the command line with a "--trailer=<key>:<value>" option, then I agree that the users should be able to choose between running and not running [*].

Quoted 2 lines

* Do we need trailer.<token>.runMode as Christan said before? I rejected his this suggestion before, and now I regret it a bit.

So far, I haven't heard anything that indicates it is a useful behaviour for .command, so my preference is to get rid of the behaviour when we introduce .cmd to deprecate .command; yes, until we get rid of .command, the behaviour between the two would be inconsistent but that is unavoidable when making a bugfix that is backward incompatible.

When (and only when) anybody finds a credible use case, we can add a mechanism to optionally turn it on (e.g. .runMode).

That is my thinking right at this moment, but that's of course subject to change when a use case that would be helped by having this extra execution.

[Footnote]

* Right now, all I know is that not being able to turn it off makes it impossible to use "git commit --trailer" as a more general substitute for "git commit --signoff" without breaking other trailers (e.g. --trim-empty may get rid of the result of the extra execution, but would remove other trailers that can be legitimately empty). And making it on by default with configuration would mean that even though we designed .cmd as a better version of the .command feature with its misdesign corrected, we'd inherit one misdesign from it, which defeats one third of the point of introducing the .cmd in the first place ;-)

#72 lore raw

Re: [PATCH v9 2/2] [GSOC] trailer: add new .cmd config option

Quoted 42 lines · Junio C Hamano <[email protected]> 于2021年4月15日周四 上午4:33写道:
Quoted 7 lines · ZheNing Hu
Quoted 6 lines

So I am waiting to hear why it is not a misfeature. If it is not, then surely I am fine to keep it for now and add a workaround later, but until that happens, I do not think "commit --trailer" can be used as a way to allow end-users emulate "-s" for their favorite trailer like helped-by, acked-by, etc.

Quoted 6 lines

If it is really necessary to solve this "empty execution" in .cmd,

Maybe we need to consider two points:
* Do we need a new config flag as you said [implicitExecution = false] or just drop it? Has anyone been relying on the "empty execution" of .command before? This may be worthy of concern.

Yes, if it is useful sometimes to run the .command or .cmd with empty <value> even when nobody asks for it on the command line with a "--trailer=<key>:<value>" option, then I agree that the users should be able to choose between running and not running [*].

Quoted 2 lines

* Do we need trailer.<token>.runMode as Christan said before? I rejected his this suggestion before, and now I regret it a bit.

So far, I haven't heard anything that indicates it is a useful behaviour for .command, so my preference is to get rid of the behaviour when we introduce .cmd to deprecate .command; yes, until we get rid of .command, the behaviour between the two would be inconsistent but that is unavoidable when making a bugfix that is backward incompatible.

When (and only when) anybody finds a credible use case, we can add a mechanism to optionally turn it on (e.g. .runMode).

That is my thinking right at this moment, but that's of course subject to change when a use case that would be helped by having this extra execution.

[Footnote]

Quoted 10 lines

* Right now, all I know is that not being able to turn it off makes it impossible to use "git commit --trailer" as a more general substitute for "git commit --signoff" without breaking other trailers (e.g. --trim-empty may get rid of the result of the extra execution, but would remove other trailers that can be legitimately empty). And making it on by default with configuration would mean that even though we designed .cmd as a better version of the .command feature with its misdesign corrected, we'd inherit one misdesign from it, which defeats one third of the point of introducing the .cmd in the first place ;-)

Perhaps such a modification can meet our temporary needs!

@@ -721,9 +738,10 @@ static void process_command_line_args(struct list_head *arg_head,

        char *cl_separators = xstrfmt("=%s", separators);

        /* 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.cmd || item->conf.command)
+ if (item->conf.command)

I'm so busy today that I probably haven't had time to put this into a patch today,
If this solution is reasonable, I will send a new version of the patch tomorrow. As you said, first solve the misfeature, and we can add [trailer.runMode] later.

Thanks, Junio!
--
ZheNing Hu

#73 lore raw

Re: [PATCH v9 2/2] [GSOC] trailer: add new .cmd config option

Quoted 24 lines · ZheNing Hu
Quoted 10 lines · Junio C Hamano <[email protected]> 于2021年4月15日周四 上午4:33写道:

So far, I haven't heard anything that indicates it is a useful behaviour for .command, so my preference is to get rid of the behaviour when we introduce .cmd to deprecate .command; yes, until we get rid of .command, the behaviour between the two would be inconsistent but that is unavoidable when making a bugfix that is backward incompatible.

When (and only when) anybody finds a credible use case, we can add a mechanism to optionally turn it on (e.g. .runMode).

...
Perhaps such a modification can meet our temporary needs!

(quoted diff) +1 -1
@@ -721,9 +738,10 @@ static void process_command_line_args(struct
 list_head *arg_head,
        char *cl_separators = xstrfmt("=%s", separators);
 
        /* 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.cmd || item->conf.command)
+               if (item->conf.command)
 
Quoted 4 lines

I'm so busy today that I probably haven't had time to put this into a patch today,
If this solution is reasonable, I will send a new version of the patch tomorrow. As you said, first solve the misfeature, and we can add [trailer.runMode] later.

I am perfectly fine (or rather I'd be happy) to wait for a while to see such a patch, as it would be a good idea to give time to those who do have need for this extra/empty execution to chime in. So, there is no need to rush.

Thanks.

#74 lore raw

Re: [PATCH v9 2/2] [GSOC] trailer: add new .cmd config option

In reply to Junio C Hamano
Quoted 22 lines · Junio C Hamano
Quoted 7 lines · ZheNing Hu
Quoted 6 lines

So I am waiting to hear why it is not a misfeature. If it is not, then surely I am fine to keep it for now and add a workaround later, but until that happens, I do not think "commit --trailer" can be used as a way to allow end-users emulate "-s" for their favorite trailer like helped-by, acked-by, etc.

Quoted 6 lines

If it is really necessary to solve this "empty execution" in .cmd,

Maybe we need to consider two points:
* Do we need a new config flag as you said [implicitExecution = false] or just drop it? Has anyone been relying on the "empty execution" of .command before? This may be worthy of concern.

Yes, if it is useful sometimes to run the .command or .cmd with empty <value> even when nobody asks for it on the command line with a "--trailer=<key>:<value>" option, then I agree that the users should be able to choose between running and not running [*].

Quoted 5 lines
Quoted 2 lines

* Do we need trailer.<token>.runMode as Christan said before? I rejected his this suggestion before, and now I regret it a bit.

So far, I haven't heard anything that indicates it is a useful behaviour for .command,

Well the git config trailer.sign.command 'echo "$(git config user.name) <$(git config user.email)>"' has been documented in the EXAMPLES section of the git interpret-trailers doc since when ".command" was implemented, and I believe that reviewers thought that it was a good feature to have then.

Quoted 6 lines

so my preference is to get rid of the
behaviour when we introduce .cmd to deprecate .command; yes, until we get rid of .command, the behaviour between the two would be inconsistent but that is unavoidable when making a bugfix that is backward incompatible.

Quoted 6 lines

When (and only when) anybody finds a credible use case, we can add a mechanism to optionally turn it on (e.g. .runMode).

That is my thinking right at this moment, but that's of course subject to change when a use case that would be helped by having this extra execution.

Such use cases and some of this were discussed when git interpret-trailers was initially implemented.

For example in https://lore.kernel.org/git/CAP8UFD2oXpW9QEkSh+vpNGRAxRFp0zJF39ZZ8sUZLTcKB9mHWQ@mail.gmail.com/
I suggested the following:

         [trailer "m-o-b"]
                 key = "Made-on-branch: "
                 command = "git name-rev --name-only HEAD"

when someone wanted a way to always add "a trailer for the branch that the commit was checked in to at the time"

In https://lore.kernel.org/git/[email protected]/ Michael Haggerty suggested:

"Maybe there should be a trailer.<token>.trimEmpty config option."

I haven't fully checked the discussions, so there might be other examples.

#75 lore raw

Re: [PATCH v9 2/2] [GSOC] trailer: add new .cmd config option

In reply to Christian Couder
Quoted 6 lines · Christian Couder

It might be desirable to make it easier for people to migrate from ".command" to ".cmd". I agree this is debatable, but I don't see a big downside in it. Maybe, if no argument was passed at all the first time the command is called instead of the empty string, the command could then know that it's called for the first time. I am not sure this would be very helpful in practice though.

An integration of the trailer mechanism into the "git commit" command, which cannot emulate "-s" at the end-user level, is something I would call a big failure, though.

Quoted 9 lines
Quoted 4 lines

This and the other (omitted) example demonstrates how the initial "empty" invocation is useless by using "replace". Which also means that you cannot add more than one trailer of the same <key> with the mechanism (since the older ones are replaced with the latest).

You can add more than one trailer with the same key if you don't use "replace" but use "--trim-empty" on the command line, so that an empty trailer added by the initial invocation is removed. And we can later add a trailer.<token>.runMode to remove the initial invocation.

As I said in the other message (not the one you are responding to), trim-empty is not a viable workaround. Imagine what happens when you want to always add a trailer (unrelated to signed-off-by), for which it is perfectly valid not to have any value. An attempt to work around the problem "extra call without being asked from the command line" behaviour has on our emulated "signed-off-by" example with --trim-empty would nuke such an unrelated trailer.

Besides, if the command produced non-empty value, e.g.

[trailer "baz"] command = "echo B $ARG Z"

the extra call without being asked from the command line will still give "baz: B Z" that is not empty.

So... I am very curious to learn what the intended use case is for this puzzling feature. If it turns out to be that it behaves only because it was coded that way without any real benefit, I would strongly prefer not to carry it over to the new .cmd thing, which is an attempt to deprecate .command to get rid of its broken parts (so far, we identified two---only the first occurrence is replaced, and the replacement can break command line syntax) while salvaging its good parts (being able to programmatically decide the value). And so far what I heard hasn't convinced me that this behaviour falls into the latter category.

Thanks.

#76 lore raw

v10 cover trailer: add new .cmd config option

In reply to ZheNing Hu via GitGitGadget

In https://lore.kernel.org/git/[email protected]/ 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.

At the same time, the trailer.<token>.command has another design
disadvantages: It will automatically execute with a empty value in <token> <value> pair, which will create a trailer that we don’t expect.

Now pass trailer value as $1 to the trailer command with another trailer.<token>.cmd config, to solve these above 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 | 93 ++++++++++++++++++++----
 t/t7513-interpret-trailers.sh            | 84 +++++++++++++++++++++
 trailer.c                                | 35 ++++++---
 3 files changed, 186 insertions(+), 26 deletions(-)

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

Range-diff vs v9:

 1:  8129ef6c476b = 1:  8129ef6c476b [GSOC] docs: correct descript of trailer.<token>.command
 2:  7f645ec95f48 ! 2:  daa889bd0ade [GSOC] trailer: add new .cmd config option

@@ Commit message
replaced with the value given to the interpret-trailer command for the token in a '--trailer <token>=<value>' argument.

- This has two downsides:
+ This has three downsides:

* The use of $ARG in the mechanism misleads the users that the value is passed in the shell variable, and tempt them
@@ Commit message
a broken command that is not syntactically correct (or worse).

+ * The first occurrence of substring $ARG will be replaced
+ with the empty string, in the command when the command is
+ first called to add a trailer with the specified <token>.
+ This is a bad design, the nature of automatic execution
+ causes it to add a trailer that we don't expect.
+
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.
+ scripts. At the same time, in order to allow
+ git interpret-trailers to better simulate the behavior
+ of git command -s, 'trailer.<token>.cmd' will not
+ automatically execute.

@@ Documentation/git-interpret-trailers.txt: leading and trailing whitespace trimme
-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.
-+of these arguments, if any, will be passed to the command as its -+first argument. 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>.
-+For consistency, the $1 is also passed, this time with the empty string, -+in the command when the command is first called to add a trailer with -+the specified <token>.
++of these arguments, if any, will be passed to the command as its ++first argument. This way the command can produce a <value> computed ++from the <value> passed in the '--trailer <token>=<value>' argument.

EXAMPLES
--------
@@ Documentation/git-interpret-trailers.txt: subject
+#!/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.ifExists "addIfDifferentNeighbor"
+$ git config trailer.cnt.cmd "~/bin/gcount"
-+$ git interpret-trailers --trailer="cnt:Junio" <<EOF
++$ git interpret-trailers --trailer="cnt:Junio" --trailer="cnt:Linus Torvalds"<<EOF
+> subject
+>
+> message
@@ Documentation/git-interpret-trailers.txt: subject
+message
+
+Commit-count: 22484 Junio C Hamano
++Commit-count: 1117 Linus Torvalds
+------------
+
+* Configure a 'ref' trailer with a cmd use a global script glog-grep
@@ t/t7513-interpret-trailers.sh: test_expect_success 'setup' '
+ 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
@@ t/t7513-interpret-trailers.sh: test_expect_success 'setup' '
+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.ifExists "add" &&
+ git config trailer.bug.cmd "echo \"\$1\" is" &&
+ cat >expected2 <<-EOF &&
+
++ Bug-maker: him is him
+ Bug-maker: me is me
+ EOF
+ git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
@@ trailer.c: static int git_trailer_config(const char *conf_key, const char *value
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(""),

Signature · 1 line
gitgitgadget
#77 lore raw

PATCH v10 1/2 docs: correct descript of trailer.<token>.command

From: ZheNing Hu <[email protected]>

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.

---
 Documentation/git-interpret-trailers.txt | 37 ++++++++++++++----------
 1 file changed, 21 insertions(+), 16 deletions(-)
Documentation/git-interpret-trailers.txt +21 -16
index 96ec6499f001..6f2a7a130464 100644
@@ -232,25 +232,30 @@ trailer.<token>.ifmissing::
 	that option for trailers with the specified <token>.
 
 trailer.<token>.command::
-	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.
 +
-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 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 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.
+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.
 +
-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.
+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>.
 
 EXAMPLES
 --------
Signature · 1 line
gitgitgadget
#78 lore raw

Re: [PATCH v10 1/2] [GSOC] docs: correct descript of trailer.<token>.command

Quoted 1 line · ZheNing Hu via GitGitGadget

Subject: Re: [PATCH v10 1/2] [GSOC] docs: correct descript of trailer.<token>.command

s/descript/&ion/ (no need to resend only to fix this).

Quoted 18 lines
Documentation/git-interpret-trailers.txt +9 -0
index 96ec6499f001..6f2a7a130464 100644
@@ -232,25 +232,30 @@ trailer.<token>.ifmissing::
 	that option for trailers with the specified <token>.
 
 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

Documentation/git-interpret-trailers.txt trailer.<token>.ifmissing:: · line 254

Quoted 10 lines
Documentation/git-interpret-trailers.txt +8 -0
+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.
 +

Makes quite a lot of sense. I wouldn't have got confused by the behaviour of .command if it were documented like the above from day one. Very nice.

Documentation/git-interpret-trailers.txt trailer.<token>.ifmissing:: · line 258

Quoted 4 lines
Documentation/git-interpret-trailers.txt +4 -0
+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>.

OK, so "for consistency" is about consistency between the "execute once first without being asked" case (where there is no plausible source of $ARG), and "execute because we were asked by a command line option (where we are given <value> to replace $ARG).

Makes sense.

Will queue. Thanks.

#79 lore raw

PATCH v10 2/2 trailer: add new .cmd config option

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

The trailer.<token>.command configuration variable specifies a command (run via the shell, so it does not have to be a single name 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 in a '--trailer <token>=<value>' argument.

This has three 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).

* The first occurrence of substring $ARG will be replaced with the empty string, in the command when the command is first called to add a trailer with the specified <token>. This is a bad design, the nature of automatic execution causes it to add a trailer that we don't expect.

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. At the same time, in order to allow
git interpret-trailers to better simulate the behavior of git command -s, 'trailer.<token>.cmd' will not automatically execute.

---
 Documentation/git-interpret-trailers.txt | 74 ++++++++++++++++++---
 t/t7513-interpret-trailers.sh            | 84 ++++++++++++++++++++++++
 trailer.c                                | 35 +++++++---
 3 files changed, 174 insertions(+), 19 deletions(-)
Documentation/git-interpret-trailers.txt +65 -9
index 6f2a7a130464..88084ca59654 100644
@@ -232,6 +232,20 @@ trailer.<token>.ifmissing::
 	that option for trailers with the specified <token>.
 
 trailer.<token>.command::
+	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 in favor of
+'trailer.<token>.cmd' due to the fact that $ARG in the user's command is
+only replaced once and that the original way of replacing $ARG is not safe.
++
+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::
 	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
@@ -247,15 +261,9 @@ 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>.
+of these arguments, if any, will be passed to the command as its
+first argument. This way the command can produce a <value> computed
+from the <value> passed in the '--trailer <token>=<value>' argument.
 
 EXAMPLES
 --------
@@ -338,6 +346,54 @@ 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 "addIfDifferentNeighbor"
+$ git config trailer.cnt.cmd "~/bin/gcount"
+$ git interpret-trailers --trailer="cnt:Junio" --trailer="cnt:Linus Torvalds"<<EOF
+> subject
+> 
+> message
+> 
+> EOF
+subject
+
+message
+
+Commit-count: 22484     Junio C Hamano
+Commit-count: 1117      Linus Torvalds
+------------
+
+* 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:
 +
t/t7513-interpret-trailers.sh +84 -0

96 lines not on this page · raw message

trailer.c +25 -10
index be4e9726421c..7c7cb61a945c 100644
@@ -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);
Signature · 1 line
gitgitgadget
#80 lore raw

Re: [PATCH v10 2/2] [GSOC] trailer: add new .cmd config option

Hmph, I am getting the following while applying.

.git/rebase-apply/patch:67: trailing whitespace.

Quoted 1 line

.git/rebase-apply/patch:69: trailing whitespace.

Quoted 1 line

.git/rebase-apply/patch:92: trailing whitespace.

Quoted 1 line

.git/rebase-apply/patch:94: trailing whitespace.

Quoted 1 line

warning: 4 lines applied after fixing whitespace errors.
Applying: trailer: add new .cmd config option

#81 lore raw

Re: [PATCH v10 2/2] [GSOC] trailer: add new .cmd config option

In reply to ZheNing Hu via GitGitGadget
Quoted 9 lines · ZheNing Hu via GitGitGadget

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. At the same time, in order to allow
git interpret-trailers to better simulate the behavior of git command -s, 'trailer.<token>.cmd' will not automatically execute.

OK. I think there still will be disagreement on this last point between Christian and I, but I'd be happy with this as the first cut for newly introduced .cmd and then when it becomes needed add something like the attached patch on top to optionally run the given command when configured.

 trailer.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
trailer.c +2 -1
index 7c7cb61a94..39132211cc 100644
@@ -723,7 +723,8 @@ 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.run_implicitly && item->conf.cmd) ||
+		    item->conf.command)
 			add_arg_item(arg_head,
 				     xstrdup(token_from_item(item, NULL)),
 				     xstrdup(""),
#82 lore raw

Re: [PATCH v10 2/2] [GSOC] trailer: add new .cmd config option

Quoted 15 lines · Junio C Hamano

OK. I think there still will be disagreement on this last point between Christian and I, but I'd be happy with this as the first cut for newly introduced .cmd and then when it becomes needed add something like the attached patch on top to optionally run the given command when configured.

 trailer.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
trailer.c +0 -0
index 7c7cb61a94..39132211cc 100644

trailer.c static void process_command_line_args(struct list_head *arg_head, · line 730

Quoted 9 lines
trailer.c +2 -1
@@ -723,7 +723,8 @@ 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.run_implicitly && item->conf.cmd) ||
+		    item->conf.command)
 			add_arg_item(arg_head,
 				     xstrdup(token_from_item(item, NULL)),
 				     xstrdup(""),

Actually, if we were to do this, I actually suspect that, unlike the textual replacement of $ARG in the old .command configuration, we should take advantage of the fact that the command can tell the cases between an empty string given as $1 and no positional argument was given for $1. So what add_arg_item() does here for .command (which has to give an empty string, because it will textually replace an occurrence of $ARG) and for .cmd may have to be different. Perhaps record NULL here, so that when the command line is formed, we can tell that we do not want add an extra "" that becomes $1, when we are dealing with .cmd in this codepath.

Thanks.

#83 lore raw

Re: [PATCH v10 2/2] [GSOC] trailer: add new .cmd config option

Quoted 29 lines · Junio C Hamano
Quoted 15 lines · Junio C Hamano

OK. I think there still will be disagreement on this last point between Christian and I, but I'd be happy with this as the first cut for newly introduced .cmd and then when it becomes needed add something like the attached patch on top to optionally run the given command when configured.

 trailer.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
trailer.c +0 -0
index 7c7cb61a94..39132211cc 100644

trailer.c static void process_command_line_args(struct list_head *arg_head, · line 730

Quoted 9 lines
trailer.c +2 -1
@@ -723,7 +723,8 @@ 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.run_implicitly && item->conf.cmd) ||
+		    item->conf.command)
 			add_arg_item(arg_head,
 				     xstrdup(token_from_item(item, NULL)),
 				     xstrdup(""),

Actually, if we were to do this, I actually suspect that, unlike the textual replacement of $ARG in the old .command configuration, we

Quoted 8 lines

should take advantage of the fact that the command can tell the cases between an empty string given as $1 and no positional argument was given for $1. So what add_arg_item() does here for .command (which has to give an empty string, because it will textually replace an occurrence of $ARG) and for .cmd may have to be different. Perhaps record NULL here, so that when the command line is formed, we can tell that we do not want add an extra "" that becomes $1, when we are dealing with .cmd in this codepath.

And continuing this line of thought, I think it would be a perfectly fine extension to allow the script/program that is launched by the .command or .cmd mechanism to signal interpret-trailers that it does not want it to add a trailer as the result of this invocation by exiting with non-zero. And that would be a reasonable way forward without having to add yet another ugly workaround .runMode.

For example, trailer.signoff.cmd could be this script:

	#!/bin/sh
	if test $# != 1
	then
		exit 1
	else
		git log -1 --author="$1" --format='"%aN" <$aE>'
	fi

where the "implicit" invocation is signalled by not passing any argument, to which the script reacts by exiting with 1, and the interpret-trailers would discard the result of the unasked-for invocation.

#84 lore raw

Re: [PATCH v10 2/2] [GSOC] trailer: add new .cmd config option

Quoted 17 lines · Junio C Hamano

And continuing this line of thought, I think it would be a perfectly fine extension to allow the script/program that is launched by the .command or .cmd mechanism to signal interpret-trailers that it does not want it to add a trailer as the result of this invocation by exiting with non-zero. And that would be a reasonable way forward without having to add yet another ugly workaround .runMode.

For example, trailer.signoff.cmd could be this script:

	#!/bin/sh
	if test $# != 1
	then
		exit 1
	else
		git log -1 --author="$1" --format='"%aN" <$aE>'
	fi
Quoted 4 lines

where the "implicit" invocation is signalled by not passing any argument, to which the script reacts by exiting with 1, and the interpret-trailers would discard the result of the unasked-for invocation.

The beauty of this approach, compared to say .runMode, is that the program specified by .cmd (and .command, except that it cannot tell if the invocation is in response to explicit --trailer=<key>:<value> request, or the extra one that is done even without being asked) have even better control in squelching the trailer output. Not just to silence the extra unasked-for invocation, it can inspect the value given to each of --trailer=<key>:<value> option and decide not to add a trailer in response to it.

#85 lore raw

Re: [PATCH v10 2/2] [GSOC] trailer: add new .cmd config option

Quoted 25 lines · Junio C Hamano <[email protected]> 于2021年4月17日周六 上午11:36写道:
Quoted 17 lines · Junio C Hamano

And continuing this line of thought, I think it would be a perfectly fine extension to allow the script/program that is launched by the .command or .cmd mechanism to signal interpret-trailers that it does not want it to add a trailer as the result of this invocation by exiting with non-zero. And that would be a reasonable way forward without having to add yet another ugly workaround .runMode.

For example, trailer.signoff.cmd could be this script:

      #!/bin/sh
      if test $# != 1
      then
              exit 1
      else
              git log -1 --author="$1" --format='"%aN" <$aE>'
      fi
Quoted 4 lines

where the "implicit" invocation is signalled by not passing any argument, to which the script reacts by exiting with 1, and the interpret-trailers would discard the result of the unasked-for invocation.

Quoted 9 lines

The beauty of this approach, compared to say .runMode, is that the program specified by .cmd (and .command, except that it cannot tell if the invocation is in response to explicit --trailer=<key>:<value> request, or the extra one that is done even without being asked) have even better control in squelching the trailer output. Not just to silence the extra unasked-for invocation, it can inspect the value given to each of --trailer=<key>:<value> option and decide not to add a trailer in response to it.

If I understand correctly, Your approach may be like this:

First, Those <token> <value> pairs we passed on the command line will use one positional parameter in the shell-script.

Second, if it is the trailer implicitly added, originally it was <token> "", now we turn it to <token> NULL, This will make the shell-script not pass positional parameters.

Then our shell script can distinguish them by the value of $#.

If we want shell-script to execute implicitly anyway, We can do nothing. the shell-script can be like as Christian mention:

#!/bin/sh
echo "$(git config user.name) <$(git config user.email)>"

But If we want shell-script to reject implicit execution, We can judge the value of $# : 0 exit , 1 normal as you mention:

#!/bin/sh
if test $# != 1
then
exit 0
else

    git log -1 --author="$1" --format='%aN <%aE>'

fi

I agree with your approach.
But when I am reproducing your solution, I may be in trouble:
If we let shell-script exit(1), capture_command() will output "running trailer command '%s' failed" which is its origin strategy in order to catch the user's command error.

But If we use exit(0) , The headache is coming again:

Signed-off-by:

the empty value trailer still output. What we want is that those "<token> NULL" are rejected here, we don’t want any other output, do we?

Thanks.
--
ZheNing Hu

#86 lore raw

Re: [PATCH v10 2/2] [GSOC] trailer: add new .cmd config option

Quoted 4 lines · ZheNing Hu

But when I am reproducing your solution, I may be in trouble:
If we let shell-script exit(1), capture_command() will output "running trailer command '%s' failed" which is its origin strategy in order to catch the user's command error.

Yes, if we were to take that route, the trailer.c code needs to be taught to conditionally not to pass $1, and capture_command() needs to be taught to conditionally be silent on non-zero exit, as it now is a perfectly sane for the program to exit with non-zero status in some cases (like this one).

#87 lore raw

v11 cover trailer: add new .cmd config option

In reply to ZheNing Hu via GitGitGadget

In https://lore.kernel.org/git/[email protected]/ 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 problems.

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

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

 Documentation/git-interpret-trailers.txt | 111 ++++++++++++++++++---
 t/t7513-interpret-trailers.sh            | 122 +++++++++++++++++++++++
 trailer.c                                |  79 ++++++++++++---
 3 files changed, 280 insertions(+), 32 deletions(-)

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

Range-diff vs v10:

1: 8129ef6c476b ! 1: 34210e5bd3da [GSOC] docs: correct descript of trailer.<token>.command
@@ Metadata
Author: ZheNing Hu <[email protected]>

## Commit message ##
- [GSOC] docs: correct descript of trailer.<token>.command
+ [GSOC] docs: correct description of .command

In the original documentation of trailer.<token>.command, some descriptions are easily misunderstood. So let's modify
2: daa889bd0ade ! 2: 9c0fc91aba24 [GSOC] trailer: add new .cmd config option

     @@ Commit message
          replaced with the value given to the `interpret-trailer`
          command for the token in a '--trailer <token>=<value>' argument.
      
     -    This has three downsides:
     +    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
     @@ Commit message
          a broken command that is not syntactically correct (or
          worse).
      
     -    * The first occurrence of substring `$ARG` will be replaced
     -    with the empty string, in the command when the command is
     -    first called to add a trailer with the specified <token>.
     -    This is a bad design, the nature of automatic execution
     -    causes it to add a trailer that we don't expect.
     -
          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
     @@ Commit message
          refer to the value as positional argument, $1, in their
          scripts. At the same time, in order to allow
          `git interpret-trailers` to better simulate the behavior
     -    of `git command -s`, 'trailer.<token>.cmd' will not
     -    automatically execute.
     +    of `git command -s`, the first implicitly executed command
     +    will not pass positional parameters, users can use this
     +    feature to suppress its output.
      
          Helped-by: Junio C Hamano <[email protected]>
          Helped-by: Christian Couder <[email protected]>
     @@ Documentation/git-interpret-trailers.txt: leading and trailing whitespace trimme
      -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.
     --+
     ++of these arguments, if any, will be passed to the command as its
     ++first argument. 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>.
     -+of these arguments, if any, will be passed to the command as its
     -+first argument. This way the command can produce a <value> computed
     -+from the <value> passed in the '--trailer <token>=<value>' argument.
     ++It is worth mentioning that the command is first called to add a
     ++trailer with the specified <token> and without positional argument.
     ++Users can make use of this output when they need automatically add
     ++some trailers. On the other hand, users can use a trick to suppress
     ++this output by judging whether the number of positional parameters
     ++is equal to one, if it is true, execute the commands, otherwise exit
     ++with non-zero to suppress the output.
       
       EXAMPLES
       --------
     @@ Documentation/git-interpret-trailers.txt: subject
      +------------
      +$ cat ~/bin/gcount
      +#!/bin/sh
     -+test -n "$1" && git shortlog -s --author="$1" HEAD || true
     ++if test "$#" != 1
     ++then
     ++	exit 1
     ++else
     ++	test -n "$1" && git shortlog -s --author="$1" HEAD || true
     ++fi
      +$ git config trailer.cnt.key "Commit-count: "
      +$ git config trailer.cnt.ifExists "addIfDifferentNeighbor"
      +$ git config trailer.cnt.cmd "~/bin/gcount"
      +$ git interpret-trailers --trailer="cnt:Junio" --trailer="cnt:Linus Torvalds"<<EOF
      +> subject
     -+> 
     ++>
      +> message
     -+> 
     ++>
      +> EOF
      +subject
      +
     @@ Documentation/git-interpret-trailers.txt: subject
      +------------
      +$ cat ~/bin/glog-grep
      +#!/bin/sh
     -+test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
     ++if test "$#" != 1
     ++then
     ++	exit 1
     ++else
     ++	test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
     ++fi
      +$ 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
      +
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup' '
      +	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
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup' '
      +	git config trailer.bug.cmd "echo \"\$1\" is" &&
      +	cat >expected2 <<-EOF &&
      +
     ++	Bug-maker: is
      +	Bug-maker: him is him
      +	Bug-maker: me is me
      +	EOF
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup' '
      +	EOF
      +	cat >echoscript <<-EOF &&
      +	#!/bin/sh
     -+	echo who is "\$1"
     ++	if test "\$#" != 1
     ++	then
     ++		exit 1
     ++	else
     ++		echo who is "\$1"
     ++	fi
      +	EOF
      +	chmod +x echoscript &&
      +	git interpret-trailers --trailer "bug: him" --trailer "bug:me" \
      +		>actual2 &&
      +	test_cmp expected2 actual2
      +'
     ++
     ++test_expect_success 'with cmd, $1 and without --trailer' '
     ++	test_when_finished "git config --remove-section trailer.bug" &&
     ++	test_when_finished "git config --remove-section trailer.gub" &&
     ++	git config trailer.bug.key "Bug-maker: " &&
     ++	git config trailer.bug.ifExists "replace" &&
     ++	git config trailer.bug.cmd "./echoscript" &&
     ++	git config trailer.gub.key "Gub-maker: " &&
     ++	git config trailer.gub.ifExists "replace" &&
     ++	git config trailer.gub.cmd "./echoscript2" &&
     ++	cat >expected2 <<-EOF &&
     ++
     ++	Gub-maker: si ohw
     ++	EOF
     ++	cat >echoscript <<-EOF &&
     ++	#!/bin/sh
     ++	if test "\$#" != 1
     ++	then
     ++		exit 1
     ++	else
     ++		echo who is "\$1"
     ++	fi
     ++	EOF
     ++	cat >echoscript2 <<-EOF &&
     ++		echo si ohw "\$1"
     ++	EOF
     ++	chmod +x echoscript &&
     ++	chmod +x echoscript2 &&
     ++	git interpret-trailers >actual2 &&
     ++	test_cmp expected2 actual2
     ++'
      +
       test_expect_success 'without config' '
       	sed -e "s/ Z\$/ /" >expected <<-\EOF &&
     @@ trailer.c: static void free_arg_item(struct arg_item *item)
       	free(item->conf.command);
      +	free(item->conf.cmd);
       	free(item->token);
     - 	free(item->value);
     +-	free(item->value);
     ++	if (item->value)
     ++		FREE_AND_NULL(item->value);
       	free(item);
     + }
     + 
      @@ trailer.c: static int check_if_different(struct trailer_item *in_tok,
       	return 1;
       }
     @@ 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;
     + 
     + 	if (capture_command(&cp, &buf, 1024)) {
     +-		error(_("running trailer command '%s' failed"), cmd.buf);
     + 		strbuf_release(&buf);
     +-		result = xstrdup("");
     ++		if (!conf->cmd || arg) {
     ++			error(_("running trailer command '%s' failed"), cmd.buf);
     ++			result = xstrdup("");
     ++		} else
     ++			result = NULL;
     + 	} else {
     + 		strbuf_trim(&buf);
     + 		result = strbuf_detach(&buf, NULL);
      @@ 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) {
     +-		const char *arg;
     +-		if (arg_tok->value && arg_tok->value[0]) {
      +	if (arg_tok->conf.command || arg_tok->conf.cmd) {
     - 		const char *arg;
     - 		if (arg_tok->value && arg_tok->value[0]) {
     ++		const char *arg = NULL;
     ++
     ++		if ((arg_tok->value && arg_tok->value[0]) ||
     ++		   (arg_tok->conf.cmd && !arg_tok->value)) {
       			arg = arg_tok->value;
     + 		} else {
     + 			if (in_tok && in_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);
     +-		free((char *)arg);
      +		arg_tok->value = apply_command(&arg_tok->conf, arg);
     - 		free((char *)arg);
     ++		if (arg)
     ++			free((char *)arg);
       	}
       }
     + 
     +@@ trailer.c: static void apply_arg_if_exists(struct trailer_item *in_tok,
     + 		break;
     + 	case EXISTS_REPLACE:
     + 		apply_item_command(in_tok, arg_tok);
     ++		if (!arg_tok->value) {
     ++			free_arg_item(arg_tok);
     ++			return;
     ++		}
     + 		add_arg_to_input_list(on_tok, arg_tok);
     + 		list_del(&in_tok->list);
     + 		free_trailer_item(in_tok);
     + 		break;
     + 	case EXISTS_ADD:
     + 		apply_item_command(in_tok, arg_tok);
     ++		if (!arg_tok->value) {
     ++			free_arg_item(arg_tok);
     ++			return;
     ++		}
     + 		add_arg_to_input_list(on_tok, arg_tok);
     + 		break;
     + 	case EXISTS_ADD_IF_DIFFERENT:
     + 		apply_item_command(in_tok, arg_tok);
     ++		if (!arg_tok->value) {
     ++			free_arg_item(arg_tok);
     ++			return;
     ++		}
     + 		if (check_if_different(in_tok, arg_tok, 1, head))
     + 			add_arg_to_input_list(on_tok, arg_tok);
     + 		else
     +@@ trailer.c: static void apply_arg_if_exists(struct trailer_item *in_tok,
     + 		break;
     + 	case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR:
     + 		apply_item_command(in_tok, arg_tok);
     ++		if (!arg_tok->value) {
     ++			free_arg_item(arg_tok);
     ++			return;
     ++		}
     + 		if (check_if_different(on_tok, arg_tok, 0, head))
     + 			add_arg_to_input_list(on_tok, arg_tok);
     + 		else
     +@@ trailer.c: static void apply_arg_if_missing(struct list_head *head,
     + 	case MISSING_ADD:
     + 		where = arg_tok->conf.where;
     + 		apply_item_command(NULL, arg_tok);
     ++		if (!arg_tok->value) {
     ++			free_arg_item(arg_tok);
     ++			return;
     ++		}
     + 		to_add = trailer_from_arg(arg_tok);
     + 		if (after_or_end(where))
     + 			list_add_tail(&to_add->list, head);
      @@ 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);
     @@ trailer.c: static int git_trailer_config(const char *conf_key, const char *value
       	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,
     + 				     xstrdup(token_from_item(item, NULL)),
     + 				     xstrdup(""),
     + 				     &item->conf, NULL);
     ++		else if (item->conf.cmd)
     ++			add_arg_item(arg_head,
     ++				     xstrdup(token_from_item(item, NULL)),
     ++				     NULL,
     ++				     &item->conf, NULL);
     + 	}
     + 
     + 	/* Add an arg item for each trailer on the command line */
Signature · 1 line
gitgitgadget
#88 lore raw

PATCH v11 1/2 docs: correct description of .command

From: ZheNing Hu <[email protected]>

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.

---
 Documentation/git-interpret-trailers.txt | 37 ++++++++++++++----------
 1 file changed, 21 insertions(+), 16 deletions(-)
Documentation/git-interpret-trailers.txt +21 -16
index 96ec6499f001..6f2a7a130464 100644
@@ -232,25 +232,30 @@ trailer.<token>.ifmissing::
 	that option for trailers with the specified <token>.
 
 trailer.<token>.command::
-	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.
 +
-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 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 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.
+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.
 +
-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.
+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>.
 
 EXAMPLES
 --------
Signature · 1 line
gitgitgadget
#89 lore raw

PATCH v11 2/2 trailer: add new .cmd config option

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

The trailer.<token>.command configuration variable specifies a command (run via the shell, so it does not have to be a single name 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 in a '--trailer <token>=<value>' argument.

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. At the same time, in order to allow
git interpret-trailers to better simulate the behavior of git command -s, the first implicitly executed command will not pass positional parameters, users can use this feature to suppress its output.

---
 Documentation/git-interpret-trailers.txt |  90 +++++++++++++++--
 t/t7513-interpret-trailers.sh            | 122 +++++++++++++++++++++++
 trailer.c                                |  79 ++++++++++++---
 3 files changed, 267 insertions(+), 24 deletions(-)
Documentation/git-interpret-trailers.txt +82 -8
index 6f2a7a130464..3fcc721bb3a2 100644
@@ -232,6 +232,20 @@ trailer.<token>.ifmissing::
 	that option for trailers with the specified <token>.
 
 trailer.<token>.command::
+	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 in favor of
+'trailer.<token>.cmd' due to the fact that $ARG in the user's command is
+only replaced once and that the original way of replacing $ARG is not safe.
++
+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::
 	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
@@ -247,15 +261,17 @@ 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.
+of these arguments, if any, will be passed to the command as its
+first argument. 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>.
+It is worth mentioning that the command is first called to add a
+trailer with the specified <token> and without positional argument.
+Users can make use of this output when they need automatically add
+some trailers. On the other hand, users can use a trick to suppress
+this output by judging whether the number of positional parameters
+is equal to one, if it is true, execute the commands, otherwise exit
+with non-zero to suppress the output.
 
 EXAMPLES
 --------
@@ -338,6 +354,64 @@ 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
+if test "$#" != 1
+then
+	exit 1
+else
+	test -n "$1" && git shortlog -s --author="$1" HEAD || true
+fi
+$ git config trailer.cnt.key "Commit-count: "
+$ git config trailer.cnt.ifExists "addIfDifferentNeighbor"
+$ git config trailer.cnt.cmd "~/bin/gcount"
+$ git interpret-trailers --trailer="cnt:Junio" --trailer="cnt:Linus Torvalds"<<EOF
+> subject
+>
+> message
+>
+> EOF
+subject
+
+message
+
+Commit-count: 22484     Junio C Hamano
+Commit-count: 1117      Linus Torvalds
+------------
+
+* 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
+if test "$#" != 1
+then
+	exit 1
+else
+	test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
+fi
+$ 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:
 +
t/t7513-interpret-trailers.sh +122 -0

134 lines not on this page · raw message

trailer.c +63 -16

180 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#90 lore raw

Re: [PATCH v11 0/2] [GSOC] trailer: add new .cmd config option

In reply to ZheNing Hu via GitGitGadget
Quoted 10 lines · ZheNing Hu via GitGitGadget

In https://lore.kernel.org/git/[email protected]/ 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.

Quoted 4 lines

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

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

Here is a good spot to summarize what changed since the previous round.

It seems that this now has "exit non-zero to tell the caller not to add the trailer for this execuation". Is that the only change you made?

I was hoping that we'd declare victory with what was in v10 (with possibly typos and minor stylistic fixes if needed---I no longer remember details), let it go through the usual course of cooking in 'next' and merged down to 'master', and then after the dust settles, we'd be adding this "by exiting with non-zero status, scripts can signal a trailer not to be added for a particular invocation" as a new feature, if it turns out to be necessary.

But let's see what's new in this iteration.

Quoted 8 lines
      +#!/bin/sh
     -+test -n "$1" && git shortlog -s --author="$1" HEAD || true
     ++if test "$#" != 1
     ++then
     ++	exit 1
     ++else
     ++	test -n "$1" && git shortlog -s --author="$1" HEAD || true
     ++fi

I find this dubious. Why not

	if test "$#" != 1 || test -z "$1"
	then
		exit 1
	else
		git shortlog -s --author="$1" HEAD
	fi

That is, if you happened to give an empty string, your version gives "" to <value> and returns success, letting a trailer "cnt:" with empty value. Is that what we really want?

Quoted 16 lines
      +$ git config trailer.cnt.key "Commit-count: "
      +$ git config trailer.cnt.ifExists "addIfDifferentNeighbor"
      +$ git config trailer.cnt.cmd "~/bin/gcount"
      +$ git interpret-trailers --trailer="cnt:Junio" --trailer="cnt:Linus Torvalds"<<EOF
      +> subject
     -+> 
     ++>
      +> message
     -+> 
     ++>
      +> EOF
      +subject
      +
     @@ Documentation/git-interpret-trailers.txt: subject
      +------------
      +$ cat ~/bin/glog-grep
Quoted 8 lines
      +#!/bin/sh
     -+test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
     ++if test "$#" != 1
     ++then
     ++	exit 1
     ++else
     ++	test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
     ++fi

Ditto.

Quoted 6 lines
     + 	if (capture_command(&cp, &buf, 1024)) {
     +-		error(_("running trailer command '%s' failed"), cmd.buf);
     + 		strbuf_release(&buf);
     +-		result = xstrdup("");
     ++		if (!conf->cmd || arg) {
     ++			error(_("running trailer command '%s' failed"), cmd.buf);

I am not sure about this part. If .cmd (the new style) exits with a non-zero status for user-supplied --trailer=<token>:<value> (because it did not like the <value>), is that "running failed"? The script is expected to express yes/no with its exit status, so I would say it is not failing, but successfully expressed its displeasure and vetoed the particular trailer from getting added. IOW, "|| arg" part in the condition feels iffy to me.

Quoted 6 lines

++ result = xstrdup("");
++ } else
++ result = NULL;
+ } else {
+ strbuf_trim(&buf);
+ result = strbuf_detach(&buf, NULL);

OK.

#91 lore raw

Re: [PATCH v11 0/2] [GSOC] trailer: add new .cmd config option

Quoted 16 lines · Junio C Hamano <[email protected]> 于2021年4月18日周日 上午6:26写道:
Quoted 13 lines · ZheNing Hu via GitGitGadget

In https://lore.kernel.org/git/[email protected]/ 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 problems.

Quoted 9 lines
Quoted 1 line

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

Here is a good spot to summarize what changed since the previous round.

It seems that this now has "exit non-zero to tell the caller not to add the trailer for this execuation". Is that the only change you made?

Yes, I think the more accurate one should be "exit non-zero to tell the caller not to add the trailer for this implicit execuation", You also said below, it may not be so good.

Quoted 8 lines

I was hoping that we'd declare victory with what was in v10 (with possibly typos and minor stylistic fixes if needed---I no longer remember details), let it go through the usual course of cooking in 'next' and merged down to 'master', and then after the dust settles, we'd be adding this "by exiting with non-zero status, scripts can signal a trailer not to be added for a particular invocation" as a new feature, if it turns out to be necessary.

Thanks for your and Christian's help this month!

OK, I understand, then I can wait for a while until trailer_cmd merge to master.

Quoted 21 lines

But let's see what's new in this iteration.

Quoted 8 lines
      +#!/bin/sh
     -+test -n "$1" && git shortlog -s --author="$1" HEAD || true
     ++if test "$#" != 1
     ++then
     ++       exit 1
     ++else
     ++       test -n "$1" && git shortlog -s --author="$1" HEAD || true
     ++fi

I find this dubious. Why not

        if test "$#" != 1 || test -z "$1"
        then
                exit 1
        else
                git shortlog -s --author="$1" HEAD
        fi
Quoted 3 lines

That is, if you happened to give an empty string, your version gives "" to <value> and returns success, letting a trailer "cnt:" with empty value. Is that what we really want?

If it's the user use --trailer="cnt:" instread of command implict running, I think keep it is right.

In fact, it should be said that it is equivalent to exit(0) if the user use --trailer="cnt:".

Quoted 35 lines
Quoted 17 lines
      +$ git config trailer.cnt.key "Commit-count: "
      +$ git config trailer.cnt.ifExists "addIfDifferentNeighbor"
      +$ git config trailer.cnt.cmd "~/bin/gcount"
      +$ git interpret-trailers --trailer="cnt:Junio" --trailer="cnt:Linus Torvalds"<<EOF
      +> subject
     -+>
     ++>
      +> message
     -+>
     ++>
      +> EOF
      +subject
      +
     @@ Documentation/git-interpret-trailers.txt: subject
      +------------
      +$ cat ~/bin/glog-grep
Quoted 8 lines
      +#!/bin/sh
     -+test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
     ++if test "$#" != 1
     ++then
     ++       exit 1
     ++else
     ++       test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
     ++fi

Ditto.

Quoted 6 lines
     +        if (capture_command(&cp, &buf, 1024)) {
     +-               error(_("running trailer command '%s' failed"), cmd.buf);
     +                strbuf_release(&buf);
     +-               result = xstrdup("");
     ++               if (!conf->cmd || arg) {
     ++                       error(_("running trailer command '%s' failed"), cmd.buf);
Quoted 8 lines

I am not sure about this part. If .cmd (the new style) exits with a non-zero status for user-supplied --trailer=<token>:<value> (because it did not like the <value>), is that "running failed"? The script is expected to express yes/no with its exit status, so I would say it is not failing, but successfully expressed its displeasure and vetoed the particular trailer from getting added. IOW, "|| arg" part in the condition feels iffy to me.

Well, you mean we can take advantage of non-zero exits instead of just removing implicitly executed content. I argee with you, this place is worth change.

Quoted 8 lines
Quoted 6 lines

++ result = xstrdup("");
++ } else
++ result = NULL;
+ } else {
+ strbuf_trim(&buf);
+ result = strbuf_detach(&buf, NULL);

OK.

Thanks.
--
ZheNing Hu

#92 lore raw

Re: [PATCH v11 0/2] [GSOC] trailer: add new .cmd config option

Quoted 24 lines · ZheNing Hu

OK, I understand, then I can wait for a while until trailer_cmd merge to master.

Quoted 21 lines

But let's see what's new in this iteration.

Quoted 8 lines
      +#!/bin/sh
     -+test -n "$1" && git shortlog -s --author="$1" HEAD || true
     ++if test "$#" != 1
     ++then
     ++       exit 1
     ++else
     ++       test -n "$1" && git shortlog -s --author="$1" HEAD || true
     ++fi

I find this dubious. Why not

        if test "$#" != 1 || test -z "$1"
        then
                exit 1
        else
                git shortlog -s --author="$1" HEAD
        fi
Quoted 6 lines
Quoted 3 lines

That is, if you happened to give an empty string, your version gives "" to <value> and returns success, letting a trailer "cnt:" with empty value. Is that what we really want?

If it's the user use --trailer="cnt:" instread of command implict running, I think keep it is right.

No, if you give an empty string, you'd end up running "shortlog" with --author="" and give whatever random number it comes up with, which I do not think is what you would want.

That is why --trailer=cnt: without name to match --author can be rejected with "exit 1" to demonstrate the feature. The .cmd can squelch not just the "unasked for extra invocation", but invocation from the command line whose <value> was bogus, unlike the .runmode feature we've seen proposed earlier.

Quoted 15 lines
Quoted 7 lines
Quoted 6 lines
     +        if (capture_command(&cp, &buf, 1024)) {
     +-               error(_("running trailer command '%s' failed"), cmd.buf);
     +                strbuf_release(&buf);
     +-               result = xstrdup("");
     ++               if (!conf->cmd || arg) {
     ++                       error(_("running trailer command '%s' failed"), cmd.buf);
Quoted 7 lines

I am not sure about this part. If .cmd (the new style) exits with a non-zero status for user-supplied --trailer=<token>:<value> (because it did not like the <value>), is that "running failed"? The script is expected to express yes/no with its exit status, so I would say it is not failing, but successfully expressed its displeasure and vetoed the particular trailer from getting added. IOW, "|| arg" part in the condition feels iffy to me.

Quoted 3 lines

Well, you mean we can take advantage of non-zero exits instead of just removing implicitly executed content. I argee with you, this place is worth change.

Yup, that is what I meant.

In any case, let's see how well the base topic fares.

Thanks.

#93 lore raw

Re: [PATCH v11 0/2] [GSOC] trailer: add new .cmd config option

Quoted 38 lines · Junio C Hamano <[email protected]> 于2021年4月21日周三 上午8:09写道:
Quoted 24 lines · ZheNing Hu

OK, I understand, then I can wait for a while until trailer_cmd merge to master.

Quoted 21 lines

But let's see what's new in this iteration.

Quoted 8 lines
      +#!/bin/sh
     -+test -n "$1" && git shortlog -s --author="$1" HEAD || true
     ++if test "$#" != 1
     ++then
     ++       exit 1
     ++else
     ++       test -n "$1" && git shortlog -s --author="$1" HEAD || true
     ++fi

I find this dubious. Why not

        if test "$#" != 1 || test -z "$1"
        then
                exit 1
        else
                git shortlog -s --author="$1" HEAD
        fi
Quoted 6 lines
Quoted 3 lines

That is, if you happened to give an empty string, your version gives "" to <value> and returns success, letting a trailer "cnt:" with empty value. Is that what we really want?

If it's the user use --trailer="cnt:" instread of command implict running, I think keep it is right.

No, if you give an empty string, you'd end up running "shortlog" with --author="" and give whatever random number it comes up with, which I do not think is what you would want.

Quoted 6 lines

That is why --trailer=cnt: without name to match --author can be rejected with "exit 1" to demonstrate the feature. The .cmd can squelch not just the "unasked for extra invocation", but invocation from the command line whose <value> was bogus, unlike the .runmode feature we've seen proposed earlier.

I admit that your idea makes sense, but we actually have another requirement:
Construct a trailer with an empty value.

The Commit-Count example above may not be good, Let’s take a look at the Signed-off-by.

e.g. --trailer="sign:", we expect to output a "Signed-off-by: ", then we can fill it
with the "name <email>" pair we want, this is when we shouldn't return non-zero and suppress its output.

Quoted 15 lines
Quoted 15 lines
Quoted 14 lines
Quoted 6 lines
     +        if (capture_command(&cp, &buf, 1024)) {
     +-               error(_("running trailer command '%s' failed"), cmd.buf);
     +                strbuf_release(&buf);
     +-               result = xstrdup("");
     ++               if (!conf->cmd || arg) {
     ++                       error(_("running trailer command '%s' failed"), cmd.buf);

I am not sure about this part. If .cmd (the new style) exits with a non-zero status for user-supplied --trailer=<token>:<value> (because it did not like the <value>), is that "running failed"? The script is expected to express yes/no with its exit status, so I would say it is not failing, but successfully expressed its displeasure and vetoed the particular trailer from getting added. IOW, "|| arg" part in the condition feels iffy to me.

Quoted 8 lines
Quoted 3 lines

Well, you mean we can take advantage of non-zero exits instead of just removing implicitly executed content. I argee with you, this place is worth change.

Yup, that is what I meant.

In any case, let's see how well the base topic fares.

Yes, I don't know if Christian agrees with temporary situation.

Quoted 1 line

Thanks.

Thanks.
--
ZheNing Hu

#94 lore raw

Re: [PATCH v11 0/2] [GSOC] trailer: add new .cmd config option

Quoted 2 lines · ZheNing Hu

I admit that your idea makes sense, but we actually have another requirement:
Construct a trailer with an empty value.

It can be done with a different script given to .cmd, which would say "exit 0" when allowing an empty string given as its input to appear in the output.

I was reacting what the "count" example does, and found that counting commits by all authors (that is what an empty string would match when given to --author="") somewhat illogical in the context of that example.

After all, these examples are to pique readers' interest by demonstrating what the mechanism can do and how it can be used, and for this feature, I think showing that

(1) we can squelch the output from unasked-for execution;

(2) we can reject --trailer=<key>:<value> when we do not like the

     given <value>;

(3) we can insert the trailer with the value we compute (and it is OK for the computed result happens to be an empty string).

should be plenty sufficient.

#95 lore raw

Re: [PATCH v11 0/2] [GSOC] trailer: add new .cmd config option

Quoted 10 lines · Junio C Hamano <[email protected]> 于2021年4月22日周四 上午7:40写道:
Quoted 2 lines · ZheNing Hu

I admit that your idea makes sense, but we actually have another requirement:
Construct a trailer with an empty value.

It can be done with a different script given to .cmd, which would say "exit 0" when allowing an empty string given as its input to appear in the output.

Now I think that we should keep those trailers which ask for a "name <email>" pair, like "Helped-by", "Signed-off-by", when we provide a "help:","sign:" in command line, This allows the user to dynamically fill in the "name <email>" pair of other people in the commit message later. It is worthwhile for users to exit with exit(0).

But those dispensable things like "Commit-Count", It must depend on a person's statistics in the git repository. So "cnt:" is meaningless, users' script can let it exit(1).

Quoted 5 lines

I was reacting what the "count" example does, and found that counting commits by all authors (that is what an empty string would match when given to --author="") somewhat illogical in the context of that example.

The "Commit-Count" in the example here can only target a specific person, which has great limitations.

I have a bold idea:

Our current --trailer can only fill in one data item, and we don't expect it to fill
in multiple rows. something like "Commit-Count", we hope to count the number of commits from everyone. But as we can see:

Commit-count: 7 Linus Arver
1117 Linus Torvalds

So If we have the opportunity to capture every line or every "block" of content, and feed it to git interpret-trailer, maybe we can get something like:

Commit-count: 7 Linus Arver
Commit-count: 1117 Linus Torvalds

This will definitely make it easy for us to generate a lot of trailer at once. For example, a newbie like me, after making a patch, want to --cc all authors of one file, maybe I only need a command to get it.

I don't know if it's a bit whimsical.

Quoted 6 lines

After all, these examples are to pique readers' interest by demonstrating what the mechanism can do and how it can be used, and for this feature, I think showing that

(1) we can squelch the output from unasked-for execution;

Quoted 7 lines

(2) we can reject --trailer=<key>:<value> when we do not like the

     given <value>;

(3) we can insert the trailer with the value we compute (and it is OK for the computed result happens to be an empty string).

should be plenty sufficient.

OK. I will add these three examples in the new patch (when .cmd merge to master).

Thanks.
--
ZheNing Hu

#96 lore raw

Re: [PATCH v11 0/2] [GSOC] trailer: add new .cmd config option

Quoted 9 lines · ZheNing Hu

Now I think that we should keep those trailers which ask for a "name <email>" pair, like "Helped-by", "Signed-off-by", when we provide a "help:","sign:" in command line, This allows the user to dynamically fill in the "name <email>" pair of other people in the commit message later. It is worthwhile for users to exit with exit(0).

But those dispensable things like "Commit-Count", It must depend on a person's statistics in the git repository. So "cnt:" is meaningless, users' script can let it exit(1).

Perhaps, but at this point what you think (or what I think) does not matter. That was the whole point of letting .cmd script signal Git if the result from the invocation should be kept or discarded with its exit status. What would be sufficient here for us to do is to agree that it would be good to have a minimal set (perhaps a pair) of examples to demonstrate that the script can choose to keep or discard a meaningless trailer entry with its exit status.

#97 lore raw

Re: [PATCH v11 0/2] [GSOC] trailer: add new .cmd config option

Quoted 13 lines · Junio C Hamano <[email protected]> 于2021年4月27日周二 下午2:49写道:
Quoted 9 lines · ZheNing Hu

Now I think that we should keep those trailers which ask for a "name <email>" pair, like "Helped-by", "Signed-off-by", when we provide a "help:","sign:" in command line, This allows the user to dynamically fill in the "name <email>" pair of other people in the commit message later. It is worthwhile for users to exit with exit(0).

But those dispensable things like "Commit-Count", It must depend on a person's statistics in the git repository. So "cnt:" is meaningless, users' script can let it exit(1).

Quoted 7 lines

Perhaps, but at this point what you think (or what I think) does not matter. That was the whole point of letting .cmd script signal Git if the result from the invocation should be kept or discarded with its exit status. What would be sufficient here for us to do is to agree that it would be good to have a minimal set (perhaps a pair) of examples to demonstrate that the script can choose to keep or discard a meaningless trailer entry with its exit status.

Yes, I argee.
Due to previous attempts, it seems that such an example is well given:
"Commit-Count" is the trailer that should be discarded. "Signed-off-by" is the trailer worth be kept.

Thanks.
--
ZheNing Hu

#98 lore raw

v12 cover trailer: add new .cmd config option

In reply to ZheNing Hu via GitGitGadget

In https://lore.kernel.org/git/[email protected]/ 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 problems.

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

Change from last version: Change docs example "Count-count" to "Helped-by".

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

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

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

Range-diff vs v11:

1: 34210e5bd3da ! 1: 8129ef6c476b [GSOC] docs: correct description of .command
@@ Metadata
Author: ZheNing Hu <[email protected]>

## Commit message ##
- [GSOC] docs: correct description of .command
+ [GSOC] docs: correct descript of trailer.<token>.command

In the original documentation of trailer.<token>.command, some descriptions are easily misunderstood. So let's modify
2: 9c0fc91aba24 ! 2: edb7f1961ddb [GSOC] trailer: add new .cmd config option

     @@ Commit message
          replaced with the value given to the `interpret-trailer`
          command for the token in a '--trailer <token>=<value>' argument.
      
     -    This has two downsides:
     +    This has three downsides:
      
          * The use of $ARG in the mechanism misleads the users that
          the value is passed in the shell variable, and tempt them
     @@ Commit message
          a broken command that is not syntactically correct (or
          worse).
      
     +    * The first occurrence of substring `$ARG` will be replaced
     +    with the empty string, in the command when the command is
     +    first called to add a trailer with the specified <token>.
     +    This is a bad design, the nature of automatic execution
     +    causes it to add a trailer that we don't expect.
     +
          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
     @@ Commit message
          refer to the value as positional argument, $1, in their
          scripts. At the same time, in order to allow
          `git interpret-trailers` to better simulate the behavior
     -    of `git command -s`, the first implicitly executed command
     -    will not pass positional parameters, users can use this
     -    feature to suppress its output.
     +    of `git command -s`, 'trailer.<token>.cmd' will not
     +    automatically execute.
      
          Helped-by: Junio C Hamano <[email protected]>
          Helped-by: Christian Couder <[email protected]>
     @@ Documentation/git-interpret-trailers.txt: leading and trailing whitespace trimme
      -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.
     -+of these arguments, if any, will be passed to the command as its
     -+first argument. 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>.
     -+It is worth mentioning that the command is first called to add a
     -+trailer with the specified <token> and without positional argument.
     -+Users can make use of this output when they need automatically add
     -+some trailers. On the other hand, users can use a trick to suppress
     -+this output by judging whether the number of positional parameters
     -+is equal to one, if it is true, execute the commands, otherwise exit
     -+with non-zero to suppress the output.
     ++of these arguments, if any, will be passed to the command as its
     ++first argument. This way the command can produce a <value> computed
     ++from the <value> passed in the '--trailer <token>=<value>' argument.
       
       EXAMPLES
       --------
     @@ Documentation/git-interpret-trailers.txt: 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:
     ++* Configure a 'help' trailer with a cmd use a script `glog-find-author`
     ++  which search specified author identity from git log in git repository
     ++  and show how it works:
      ++
      +------------
     -+$ cat ~/bin/gcount
     ++$ cat ~/bin/glog-find-author
      +#!/bin/sh
     -+if test "$#" != 1
     -+then
     -+	exit 1
     -+else
     -+	test -n "$1" && git shortlog -s --author="$1" HEAD || true
     -+fi
     -+$ git config trailer.cnt.key "Commit-count: "
     -+$ git config trailer.cnt.ifExists "addIfDifferentNeighbor"
     -+$ git config trailer.cnt.cmd "~/bin/gcount"
     -+$ git interpret-trailers --trailer="cnt:Junio" --trailer="cnt:Linus Torvalds"<<EOF
     ++test -n "$1" && git log --author="$1" --pretty="%an <%ae>" -1 || true
     ++$ git config trailer.help.key "Helped-by: "
     ++$ git config trailer.help.ifExists "addIfDifferentNeighbor"
     ++$ git config trailer.help.cmd "~/bin/glog-find-author"
     ++$ git interpret-trailers --trailer="help:Junio" --trailer="help:Couder" <<EOF
      +> subject
     -+>
     ++> 
      +> message
     -+>
     ++> 
      +> EOF
      +subject
      +
      +message
      +
     -+Commit-count: 22484     Junio C Hamano
     -+Commit-count: 1117      Linus Torvalds
     ++Helped-by: Junio C Hamano <[email protected]>
     ++Helped-by: Christian Couder <[email protected]>
      +------------
      +
     -+* Configure a 'ref' trailer with a cmd use a global script `glog-grep`
     ++* Configure a 'ref' trailer with a cmd use a 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
     -+if test "$#" != 1
     -+then
     -+	exit 1
     -+else
     -+	test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
     -+fi
     ++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
      +
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup' '
      +	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
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup' '
      +	git config trailer.bug.cmd "echo \"\$1\" is" &&
      +	cat >expected2 <<-EOF &&
      +
     -+	Bug-maker: is
      +	Bug-maker: him is him
      +	Bug-maker: me is me
      +	EOF
     @@ t/t7513-interpret-trailers.sh: test_expect_success 'setup' '
      +	EOF
      +	cat >echoscript <<-EOF &&
      +	#!/bin/sh
     -+	if test "\$#" != 1
     -+	then
     -+		exit 1
     -+	else
     -+		echo who is "\$1"
     -+	fi
     ++	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 'with cmd, $1 and without --trailer' '
     -+	test_when_finished "git config --remove-section trailer.bug" &&
     -+	test_when_finished "git config --remove-section trailer.gub" &&
     -+	git config trailer.bug.key "Bug-maker: " &&
     -+	git config trailer.bug.ifExists "replace" &&
     -+	git config trailer.bug.cmd "./echoscript" &&
     -+	git config trailer.gub.key "Gub-maker: " &&
     -+	git config trailer.gub.ifExists "replace" &&
     -+	git config trailer.gub.cmd "./echoscript2" &&
     -+	cat >expected2 <<-EOF &&
     -+
     -+	Gub-maker: si ohw
     -+	EOF
     -+	cat >echoscript <<-EOF &&
     -+	#!/bin/sh
     -+	if test "\$#" != 1
     -+	then
     -+		exit 1
     -+	else
     -+		echo who is "\$1"
     -+	fi
     -+	EOF
     -+	cat >echoscript2 <<-EOF &&
     -+		echo si ohw "\$1"
     -+	EOF
     -+	chmod +x echoscript &&
     -+	chmod +x echoscript2 &&
     -+	git interpret-trailers >actual2 &&
     -+	test_cmp expected2 actual2
     -+'
      +
       test_expect_success 'without config' '
       	sed -e "s/ Z\$/ /" >expected <<-\EOF &&
     @@ trailer.c: static void free_arg_item(struct arg_item *item)
       	free(item->conf.command);
      +	free(item->conf.cmd);
       	free(item->token);
     --	free(item->value);
     -+	if (item->value)
     -+		FREE_AND_NULL(item->value);
     + 	free(item->value);
       	free(item);
     - }
     - 
      @@ trailer.c: static int check_if_different(struct trailer_item *in_tok,
       	return 1;
       }
     @@ 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;
     - 
     - 	if (capture_command(&cp, &buf, 1024)) {
     --		error(_("running trailer command '%s' failed"), cmd.buf);
     - 		strbuf_release(&buf);
     --		result = xstrdup("");
     -+		if (!conf->cmd || arg) {
     -+			error(_("running trailer command '%s' failed"), cmd.buf);
     -+			result = xstrdup("");
     -+		} else
     -+			result = NULL;
     - 	} else {
     - 		strbuf_trim(&buf);
     - 		result = strbuf_detach(&buf, NULL);
      @@ 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) {
     --		const char *arg;
     --		if (arg_tok->value && arg_tok->value[0]) {
      +	if (arg_tok->conf.command || arg_tok->conf.cmd) {
     -+		const char *arg = NULL;
     -+
     -+		if ((arg_tok->value && arg_tok->value[0]) ||
     -+		   (arg_tok->conf.cmd && !arg_tok->value)) {
     + 		const char *arg;
     + 		if (arg_tok->value && arg_tok->value[0]) {
       			arg = arg_tok->value;
     - 		} else {
     - 			if (in_tok && in_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);
     --		free((char *)arg);
      +		arg_tok->value = apply_command(&arg_tok->conf, arg);
     -+		if (arg)
     -+			free((char *)arg);
     + 		free((char *)arg);
       	}
       }
     - 
     -@@ trailer.c: static void apply_arg_if_exists(struct trailer_item *in_tok,
     - 		break;
     - 	case EXISTS_REPLACE:
     - 		apply_item_command(in_tok, arg_tok);
     -+		if (!arg_tok->value) {
     -+			free_arg_item(arg_tok);
     -+			return;
     -+		}
     - 		add_arg_to_input_list(on_tok, arg_tok);
     - 		list_del(&in_tok->list);
     - 		free_trailer_item(in_tok);
     - 		break;
     - 	case EXISTS_ADD:
     - 		apply_item_command(in_tok, arg_tok);
     -+		if (!arg_tok->value) {
     -+			free_arg_item(arg_tok);
     -+			return;
     -+		}
     - 		add_arg_to_input_list(on_tok, arg_tok);
     - 		break;
     - 	case EXISTS_ADD_IF_DIFFERENT:
     - 		apply_item_command(in_tok, arg_tok);
     -+		if (!arg_tok->value) {
     -+			free_arg_item(arg_tok);
     -+			return;
     -+		}
     - 		if (check_if_different(in_tok, arg_tok, 1, head))
     - 			add_arg_to_input_list(on_tok, arg_tok);
     - 		else
     -@@ trailer.c: static void apply_arg_if_exists(struct trailer_item *in_tok,
     - 		break;
     - 	case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR:
     - 		apply_item_command(in_tok, arg_tok);
     -+		if (!arg_tok->value) {
     -+			free_arg_item(arg_tok);
     -+			return;
     -+		}
     - 		if (check_if_different(on_tok, arg_tok, 0, head))
     - 			add_arg_to_input_list(on_tok, arg_tok);
     - 		else
     -@@ trailer.c: static void apply_arg_if_missing(struct list_head *head,
     - 	case MISSING_ADD:
     - 		where = arg_tok->conf.where;
     - 		apply_item_command(NULL, arg_tok);
     -+		if (!arg_tok->value) {
     -+			free_arg_item(arg_tok);
     -+			return;
     -+		}
     - 		to_add = trailer_from_arg(arg_tok);
     - 		if (after_or_end(where))
     - 			list_add_tail(&to_add->list, head);
      @@ 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);
     @@ trailer.c: static int git_trailer_config(const char *conf_key, const char *value
       	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,
     - 				     xstrdup(token_from_item(item, NULL)),
     - 				     xstrdup(""),
     - 				     &item->conf, NULL);
     -+		else if (item->conf.cmd)
     -+			add_arg_item(arg_head,
     -+				     xstrdup(token_from_item(item, NULL)),
     -+				     NULL,
     -+				     &item->conf, NULL);
     - 	}
     - 
     - 	/* Add an arg item for each trailer on the command line */
Signature · 1 line
gitgitgadget
#99 lore raw

PATCH v12 1/2 docs: correct descript of trailer.<token>.command

From: ZheNing Hu <[email protected]>

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.

---
 Documentation/git-interpret-trailers.txt | 37 ++++++++++++++----------
 1 file changed, 21 insertions(+), 16 deletions(-)
Documentation/git-interpret-trailers.txt +21 -16
index 96ec6499f001..6f2a7a130464 100644
@@ -232,25 +232,30 @@ trailer.<token>.ifmissing::
 	that option for trailers with the specified <token>.
 
 trailer.<token>.command::
-	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.
 +
-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 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 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.
+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.
 +
-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.
+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>.
 
 EXAMPLES
 --------
Signature · 1 line
gitgitgadget
#100 lore raw

PATCH v12 2/2 trailer: add new .cmd config option

In reply to ZheNing Hu via GitGitGadget

From: ZheNing Hu <[email protected]>

The trailer.<token>.command configuration variable specifies a command (run via the shell, so it does not have to be a single name 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 in a '--trailer <token>=<value>' argument.

This has three 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).

* The first occurrence of substring $ARG will be replaced with the empty string, in the command when the command is first called to add a trailer with the specified <token>. This is a bad design, the nature of automatic execution causes it to add a trailer that we don't expect.

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. At the same time, in order to allow
git interpret-trailers to better simulate the behavior of git command -s, 'trailer.<token>.cmd' will not automatically execute.

---
 Documentation/git-interpret-trailers.txt | 75 ++++++++++++++++++---
 t/t7513-interpret-trailers.sh            | 84 ++++++++++++++++++++++++
 trailer.c                                | 35 +++++++---
 3 files changed, 175 insertions(+), 19 deletions(-)
Documentation/git-interpret-trailers.txt +66 -9
index 6f2a7a130464..1f76e0270053 100644
@@ -232,6 +232,20 @@ trailer.<token>.ifmissing::
 	that option for trailers with the specified <token>.
 
 trailer.<token>.command::
+	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 in favor of
+'trailer.<token>.cmd' due to the fact that $ARG in the user's command is
+only replaced once and that the original way of replacing $ARG is not safe.
++
+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::
 	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
@@ -247,15 +261,9 @@ 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>.
+of these arguments, if any, will be passed to the command as its
+first argument. This way the command can produce a <value> computed
+from the <value> passed in the '--trailer <token>=<value>' argument.
 
 EXAMPLES
 --------
@@ -338,6 +346,55 @@ subject
 Fix #42
 ------------
 
+* Configure a 'help' trailer with a cmd use a script `glog-find-author`
+  which search specified author identity from git log in git repository
+  and show how it works:
++
+------------
+$ cat ~/bin/glog-find-author
+#!/bin/sh
+test -n "$1" && git log --author="$1" --pretty="%an <%ae>" -1 || true
+$ git config trailer.help.key "Helped-by: "
+$ git config trailer.help.ifExists "addIfDifferentNeighbor"
+$ git config trailer.help.cmd "~/bin/glog-find-author"
+$ git interpret-trailers --trailer="help:Junio" --trailer="help:Couder" <<EOF
+> subject
+> 
+> message
+> 
+> EOF
+subject
+
+message
+
+Helped-by: Junio C Hamano <[email protected]>
+Helped-by: Christian Couder <[email protected]>
+------------
+
+* Configure a 'ref' trailer with a cmd use a 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:
 +
t/t7513-interpret-trailers.sh +84 -0

96 lines not on this page · raw message

trailer.c +25 -10
index be4e9726421c..7c7cb61a945c 100644
@@ -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);
Signature · 1 line
gitgitgadget