{"thread":{"id":"50852","subject":"[PATCH 1/2] praise: a culturally sensitive wrapper for 'blame'","startedAt":"2019-04-01T10:13:02Z","lastAt":"2019-04-01T15:38:04Z","messageCount":3,"participants":["Ævar Arnfjörð Bjarmason","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"372867","messageId":"20190401101246.21418-1-avarab@gmail.com","threadId":"50852","inReplyTo":null,"subject":"[PATCH 1/2] praise: a culturally sensitive wrapper for 'blame'","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2019-04-01T10:12:45Z","receivedAt":"2019-04-01T10:13:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a wrapper for \"blame\" for use in blameless cultural\nsettings. Users in such cultures have hitherto had to fall back on the\nlegacy \"annotate\" command and its inferior output if they wanted to\navoid culturally inappropriate behavior.\n\nNow such users have the option of using this wrapper. Organizations\nthat wish to engage in cultural enforcement can set\n\"blame.culture=blameless\" in their configuration to make any direct\nuse of the \"blame\" command an error. Those wishing to centrally report\nsuch transgressions can do so with the trace2 logging facility[1].\n\n1. See Documentation/technical/api-trace2.txt --\n   https://github.com/git/git/blob/master/Documentation/technical/api-trace2.txt\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config/blame.txt | 10 ++++++++++\n Documentation/git-blame.txt    |  3 ++-\n Documentation/git-praise.txt   | 20 ++++++++++++++++++++\n builtin/blame.c                |  9 +++++++++\n git.c                          |  1 +\n t/t8002-blame.sh               | 15 +++++++++++++++\n 6 files changed, 57 insertions(+), 1 deletion(-)\n create mode 100644 Documentation/git-praise.txt\n\ndiff --git a/Documentation/config/blame.txt b/Documentation/config/blame.txt\nindex 67b5c1d1e0..c85b35de17 100644\n--- a/Documentation/config/blame.txt\n+++ b/Documentation/config/blame.txt\n@@ -1,3 +1,13 @@\n+blame.culture::\n+\tSet to 'blameless' to enforce the use of the functionality of\n+\tlinkgit:git-blame[1] in blameless cultural settings. This will\n+\tmake invocations of linkgit:git-blame[1] an error, forcing the\n+\tuse of linkgit:git-praise[1]. Defaults to 'blameful', which is\n+\tthe default.\n++\n+Note that the `--porcelain` format for machine consumption is exempt\n+from this enforcement to avoid breaking existing scripts.\n+\n blame.blankBoundary::\n \tShow blank commit object name for boundary commits in\n \tlinkgit:git-blame[1]. This option defaults to false.\ndiff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt\nindex 16323eb80e..9c1bff7389 100644\n--- a/Documentation/git-blame.txt\n+++ b/Documentation/git-blame.txt\n@@ -228,7 +228,8 @@ include::mailmap.txt[]\n \n SEE ALSO\n --------\n-linkgit:git-annotate[1]\n+linkgit:git-annotate[1],\n+linkgit:git-praise[1]\n \n GIT\n ---\ndiff --git a/Documentation/git-praise.txt b/Documentation/git-praise.txt\nnew file mode 100644\nindex 0000000000..d34b9dbc61\n--- /dev/null\n+++ b/Documentation/git-praise.txt\n@@ -0,0 +1,20 @@\n+git-praise(1)\n+============\n+\n+NAME\n+----\n+git-praise - A culturally sensitive alias for git-blame\n+\n+DESCRIPTION\n+-----------\n+\n+Annotates each line in the given file without compromising the\n+cultural integrity of blameless cultures.\n+\n+The functionality of this command is equivalent to\n+linkgit:git-blame[1]. Its use can be enforced by setting\n+`blame.culture=blameless` in the config. See linkgit:git-config[1].\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 177c1022a0..238b19db48 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -37,6 +37,7 @@ static const char *blame_opt_usage[] = {\n \tNULL\n };\n \n+static int blameless_culture;\n static int longest_file;\n static int longest_author;\n static int max_orig_digits;\n@@ -681,6 +682,10 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n \t\tblank_boundary = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(var, \"blame.culture\")) {\n+\t\tblameless_culture = !strcmp(value, \"blameless\");\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(var, \"blame.showemail\")) {\n \t\tint *output_option = cb;\n \t\tif (git_config_bool(var, value))\n@@ -828,6 +833,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n \tstruct parse_opt_ctx_t ctx;\n \tint cmd_is_annotate = !strcmp(argv[0], \"annotate\");\n+\tint cmd_is_praise = !strcmp(argv[0], \"praise\");\n \tstruct range_set ranges;\n \tunsigned int range_i;\n \tlong anchor;\n@@ -889,6 +895,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tif (cmd_is_annotate) {\n \t\toutput_option |= OUTPUT_ANNOTATE_COMPAT;\n \t\tblame_date_mode.type = DATE_ISO8601;\n+\t} else if (!cmd_is_praise && blameless_culture &&\n+\t\t   !(output_option & OUTPUT_PORCELAIN)) {\n+\t\tdie(_(\"must be invoked as 'git praise' with 'blame.culture=blameless' set!\"));\n \t} else {\n \t\tblame_date_mode = revs.date_mode;\n \t}\ndiff --git a/git.c b/git.c\nindex 2014aab6b8..77640f8cd9 100644\n--- a/git.c\n+++ b/git.c\n@@ -536,6 +536,7 @@ static struct cmd_struct commands[] = {\n \t{ \"pack-refs\", cmd_pack_refs, RUN_SETUP },\n \t{ \"patch-id\", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT },\n \t{ \"pickaxe\", cmd_blame, RUN_SETUP },\n+\t{ \"praise\", cmd_blame, RUN_SETUP },\n \t{ \"prune\", cmd_prune, RUN_SETUP },\n \t{ \"prune-packed\", cmd_prune_packed, RUN_SETUP },\n \t{ \"pull\", cmd_pull, RUN_SETUP | NEED_WORK_TREE },\ndiff --git a/t/t8002-blame.sh b/t/t8002-blame.sh\nindex eea048e52c..2d59b856d1 100755\n--- a/t/t8002-blame.sh\n+++ b/t/t8002-blame.sh\n@@ -48,6 +48,21 @@ test_expect_success 'blame with no options and no config' '\n \ttest_cmp expected_n result\n '\n \n+test_expect_success 'praise' '\n+\tgit praise --show-email one >praise1 &&\n+\tfind_blame <praise1 >result &&\n+\ttest_cmp expected_e result &&\n+\n+\tgit praise --no-show-email one >praise2 &&\n+\tfind_blame <praise2 >result &&\n+\ttest_cmp expected_n result\n+'\n+\n+test_expect_success 'enforced praise' '\n+\ttest_must_fail git -c blame.culture=blameless blame one 2>err &&\n+\ttest_i18ngrep \"must be.*git praise\" err\n+'\n+\n test_expect_success 'blame with showemail options' '\n \tgit blame --show-email one >blame1 &&\n \tfind_blame <blame1 >result &&\n-- \n2.21.0.392.gf8f6787159e\n\n"},{"id":"372868","messageId":"20190401101246.21418-2-avarab@gmail.com","threadId":"50852","inReplyTo":"20190401101246.21418-1-avarab@gmail.com","subject":"[PATCH 2/2] praise: make 'blameless' cultural enforcement configurable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2019-04-01T10:12:46Z","receivedAt":"2019-04-01T10:13:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The culture shock of having a 'blameless' culture from day one might\nbe too much for some, so let's allow for setting\n\"blame.culture.enforcement=warning\" to allow for easing into the\ndefault of \"error\".\n\nAlso allow for excluding non-interactive users of \"blame\". There are\nsome automated users who use \"blame\" but don't use the \"--porcelain\"\nformat (which was already excluded). Those can set\ne.g. \"error:interactive\" to only emit errors when \"blame\" is\ninteracting with a TTY.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config/blame.txt | 12 ++++++++++++\n builtin/blame.c                | 27 ++++++++++++++++++++++++++-\n t/t8002-blame.sh               | 28 ++++++++++++++++++++++++++++\n 3 files changed, 66 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config/blame.txt b/Documentation/config/blame.txt\nindex c85b35de17..13570192cf 100644\n--- a/Documentation/config/blame.txt\n+++ b/Documentation/config/blame.txt\n@@ -7,6 +7,18 @@ blame.culture::\n +\n Note that the `--porcelain` format for machine consumption is exempt\n from this enforcement to avoid breaking existing scripts.\n++\n+See `blame.culture.enforcement` below for tweaking the error behavior.\n+\n+blame.culture.enforcement::\n+\tWhen `blame.culture=blameless` is set invoking\n+\tlinkgit:git-blame[1] becomes an `error` This variable can also\n+\tbe set to `warning` to only warn, and to either\n+\t`error:interactive` or `warning:interactive` to only error out\n+\tor warn if stderr is connected to a TTY.\n++\n+This allows for enforcing a blameless culture on interactive users,\n+while leaving any automated use alone.\n \n blame.blankBoundary::\n \tShow blank commit object name for boundary commits in\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 238b19db48..9f62950559 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -59,6 +59,12 @@ static size_t blame_date_width;\n \n static struct string_list mailmap = STRING_LIST_INIT_NODUP;\n \n+static enum {\n+\tBLAME_ENFORCE_ERROR\t\t= 1<<0,\n+\tBLAME_ENFORCE_WARNING\t\t= 1<<1,\n+\tBLAME_ENFORCE_INTERACTIVE\t= 1<<2\n+} blame_culture_enforcement = BLAME_ENFORCE_ERROR;\n+\n #ifndef DEBUG\n #define DEBUG 0\n #endif\n@@ -686,6 +692,19 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n \t\tblameless_culture = !strcmp(value, \"blameless\");\n \t\treturn 0;\n \t}\n+\tif (!strcmp(var, \"blame.culture.enforcement\")) {\n+\t\tif (!strcmp(value, \"error\"))\n+\t\t\tblame_culture_enforcement = BLAME_ENFORCE_ERROR;\n+\t\telse if (!strcmp(value, \"error:interactive\"))\n+\t\t\tblame_culture_enforcement = (BLAME_ENFORCE_ERROR |\n+\t\t\t\t\t\t     BLAME_ENFORCE_INTERACTIVE);\n+\t\telse if (!strcmp(value, \"warning\"))\n+\t\t\tblame_culture_enforcement = BLAME_ENFORCE_WARNING;\n+\t\telse if (!strcmp(value, \"warning:interactive\"))\n+\t\t\tblame_culture_enforcement = (BLAME_ENFORCE_WARNING |\n+\t\t\t\t\t\t     BLAME_ENFORCE_INTERACTIVE);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(var, \"blame.showemail\")) {\n \t\tint *output_option = cb;\n \t\tif (git_config_bool(var, value))\n@@ -897,7 +916,13 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tblame_date_mode.type = DATE_ISO8601;\n \t} else if (!cmd_is_praise && blameless_culture &&\n \t\t   !(output_option & OUTPUT_PORCELAIN)) {\n-\t\tdie(_(\"must be invoked as 'git praise' with 'blame.culture=blameless' set!\"));\n+\t\tif (!(blame_culture_enforcement & BLAME_ENFORCE_INTERACTIVE) ||\n+\t\t    isatty(2)) {\n+\t\t\tif (blame_culture_enforcement & BLAME_ENFORCE_ERROR)\n+\t\t\t\tdie(_(\"must be invoked as 'git praise' with 'blame.culture=blameless' set!\"));\n+\t\t\telse if (blame_culture_enforcement & BLAME_ENFORCE_WARNING)\n+\t\t\t\twarning(_(\"should be invoked as 'git praise' with 'blame.culture=blameless' set!\"));\n+\t\t}\n \t} else {\n \t\tblame_date_mode = revs.date_mode;\n \t}\ndiff --git a/t/t8002-blame.sh b/t/t8002-blame.sh\nindex 2d59b856d1..09ef0bc440 100755\n--- a/t/t8002-blame.sh\n+++ b/t/t8002-blame.sh\n@@ -2,6 +2,7 @@\n \n test_description='git blame'\n . ./test-lib.sh\n+. \"$TEST_DIRECTORY/lib-terminal.sh\"\n \n PROG='git blame -c'\n . \"$TEST_DIRECTORY\"/annotate-tests.sh\n@@ -60,9 +61,36 @@ test_expect_success 'praise' '\n \n test_expect_success 'enforced praise' '\n \ttest_must_fail git -c blame.culture=blameless blame one 2>err &&\n+\ttest_i18ngrep \"must be.*git praise\" err &&\n+\ttest_must_fail git -c blame.culture=blameless \\\n+\t\t-c blame.culture.enforcement=error blame one 2>err &&\n \ttest_i18ngrep \"must be.*git praise\" err\n '\n \n+test_expect_success 'recommended praise' '\n+\tgit -c blame.culture=blameless \\\n+\t\t-c blame.culture.enforcement=warning blame one 2>err &&\n+\ttest_i18ngrep \"should be.*git praise\" err\n+'\n+\n+test_expect_success TTY 'interactive: praise blame.culture.enforcement=*:interactive' '\n+\ttest_must_fail test_terminal git -c blame.culture=blameless \\\n+\t\t-c blame.culture.enforcement=error:interactive blame one 2>err &&\n+\ttest_i18ngrep \"must be.*git praise\" err &&\n+\ttest_terminal git -c blame.culture=blameless \\\n+\t\t-c blame.culture.enforcement=warning:interactive blame one 2>err &&\n+\ttest_i18ngrep \"should be.*git praise\" err\n+'\n+\n+test_expect_success TTY 'non-interactive: praise blame.culture.enforcement=*:interactive' '\n+\tgit -c blame.culture=blameless \\\n+\t\t-c blame.culture.enforcement=error:interactive blame one 2>err &&\n+\ttest_i18ngrep ! \"must be.*git praise\" err &&\n+\tgit -c blame.culture=blameless \\\n+\t\t-c blame.culture.enforcement=warning:interactive blame one 2>err &&\n+\ttest_i18ngrep ! \"should be.*git praise\" err\n+'\n+\n test_expect_success 'blame with showemail options' '\n \tgit blame --show-email one >blame1 &&\n \tfind_blame <blame1 >result &&\n-- \n2.21.0.392.gf8f6787159e\n\n"},{"id":"372896","messageId":"nycvar.QRO.7.76.6.1904011737050.41@tvgsbejvaqbjf.bet","threadId":"50852","inReplyTo":"20190401101246.21418-2-avarab@gmail.com","subject":"Re: [PATCH 2/2] praise: make 'blameless' cultural enforcement configurable","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-04-01T15:37:46Z","receivedAt":"2019-04-01T15:38:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 1 Apr 2019, Ævar Arnfjörð Bjarmason wrote:\n\n> The culture shock of having a 'blameless' culture from day one might\n> be too much for some, so let's allow for setting\n> \"blame.culture.enforcement=warning\" to allow for easing into the\n> default of \"error\".\n>\n> Also allow for excluding non-interactive users of \"blame\". There are\n> some automated users who use \"blame\" but don't use the \"--porcelain\"\n> format (which was already excluded). Those can set\n> e.g. \"error:interactive\" to only emit errors when \"blame\" is\n> interacting with a TTY.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nI reviewed both patches, and they look fine to me. So they are\n\nBlessed-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n\n:-D\n\n> ---\n>  Documentation/config/blame.txt | 12 ++++++++++++\n>  builtin/blame.c                | 27 ++++++++++++++++++++++++++-\n>  t/t8002-blame.sh               | 28 ++++++++++++++++++++++++++++\n>  3 files changed, 66 insertions(+), 1 deletion(-)\n>\n> diff --git a/Documentation/config/blame.txt b/Documentation/config/blame.txt\n> index c85b35de17..13570192cf 100644\n> --- a/Documentation/config/blame.txt\n> +++ b/Documentation/config/blame.txt\n> @@ -7,6 +7,18 @@ blame.culture::\n>  +\n>  Note that the `--porcelain` format for machine consumption is exempt\n>  from this enforcement to avoid breaking existing scripts.\n> ++\n> +See `blame.culture.enforcement` below for tweaking the error behavior.\n> +\n> +blame.culture.enforcement::\n> +\tWhen `blame.culture=blameless` is set invoking\n> +\tlinkgit:git-blame[1] becomes an `error` This variable can also\n> +\tbe set to `warning` to only warn, and to either\n> +\t`error:interactive` or `warning:interactive` to only error out\n> +\tor warn if stderr is connected to a TTY.\n> ++\n> +This allows for enforcing a blameless culture on interactive users,\n> +while leaving any automated use alone.\n>\n>  blame.blankBoundary::\n>  \tShow blank commit object name for boundary commits in\n> diff --git a/builtin/blame.c b/builtin/blame.c\n> index 238b19db48..9f62950559 100644\n> --- a/builtin/blame.c\n> +++ b/builtin/blame.c\n> @@ -59,6 +59,12 @@ static size_t blame_date_width;\n>\n>  static struct string_list mailmap = STRING_LIST_INIT_NODUP;\n>\n> +static enum {\n> +\tBLAME_ENFORCE_ERROR\t\t= 1<<0,\n> +\tBLAME_ENFORCE_WARNING\t\t= 1<<1,\n> +\tBLAME_ENFORCE_INTERACTIVE\t= 1<<2\n> +} blame_culture_enforcement = BLAME_ENFORCE_ERROR;\n> +\n>  #ifndef DEBUG\n>  #define DEBUG 0\n>  #endif\n> @@ -686,6 +692,19 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n>  \t\tblameless_culture = !strcmp(value, \"blameless\");\n>  \t\treturn 0;\n>  \t}\n> +\tif (!strcmp(var, \"blame.culture.enforcement\")) {\n> +\t\tif (!strcmp(value, \"error\"))\n> +\t\t\tblame_culture_enforcement = BLAME_ENFORCE_ERROR;\n> +\t\telse if (!strcmp(value, \"error:interactive\"))\n> +\t\t\tblame_culture_enforcement = (BLAME_ENFORCE_ERROR |\n> +\t\t\t\t\t\t     BLAME_ENFORCE_INTERACTIVE);\n> +\t\telse if (!strcmp(value, \"warning\"))\n> +\t\t\tblame_culture_enforcement = BLAME_ENFORCE_WARNING;\n> +\t\telse if (!strcmp(value, \"warning:interactive\"))\n> +\t\t\tblame_culture_enforcement = (BLAME_ENFORCE_WARNING |\n> +\t\t\t\t\t\t     BLAME_ENFORCE_INTERACTIVE);\n> +\t\treturn 0;\n> +\t}\n>  \tif (!strcmp(var, \"blame.showemail\")) {\n>  \t\tint *output_option = cb;\n>  \t\tif (git_config_bool(var, value))\n> @@ -897,7 +916,13 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n>  \t\tblame_date_mode.type = DATE_ISO8601;\n>  \t} else if (!cmd_is_praise && blameless_culture &&\n>  \t\t   !(output_option & OUTPUT_PORCELAIN)) {\n> -\t\tdie(_(\"must be invoked as 'git praise' with 'blame.culture=blameless' set!\"));\n> +\t\tif (!(blame_culture_enforcement & BLAME_ENFORCE_INTERACTIVE) ||\n> +\t\t    isatty(2)) {\n> +\t\t\tif (blame_culture_enforcement & BLAME_ENFORCE_ERROR)\n> +\t\t\t\tdie(_(\"must be invoked as 'git praise' with 'blame.culture=blameless' set!\"));\n> +\t\t\telse if (blame_culture_enforcement & BLAME_ENFORCE_WARNING)\n> +\t\t\t\twarning(_(\"should be invoked as 'git praise' with 'blame.culture=blameless' set!\"));\n> +\t\t}\n>  \t} else {\n>  \t\tblame_date_mode = revs.date_mode;\n>  \t}\n> diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh\n> index 2d59b856d1..09ef0bc440 100755\n> --- a/t/t8002-blame.sh\n> +++ b/t/t8002-blame.sh\n> @@ -2,6 +2,7 @@\n>\n>  test_description='git blame'\n>  . ./test-lib.sh\n> +. \"$TEST_DIRECTORY/lib-terminal.sh\"\n>\n>  PROG='git blame -c'\n>  . \"$TEST_DIRECTORY\"/annotate-tests.sh\n> @@ -60,9 +61,36 @@ test_expect_success 'praise' '\n>\n>  test_expect_success 'enforced praise' '\n>  \ttest_must_fail git -c blame.culture=blameless blame one 2>err &&\n> +\ttest_i18ngrep \"must be.*git praise\" err &&\n> +\ttest_must_fail git -c blame.culture=blameless \\\n> +\t\t-c blame.culture.enforcement=error blame one 2>err &&\n>  \ttest_i18ngrep \"must be.*git praise\" err\n>  '\n>\n> +test_expect_success 'recommended praise' '\n> +\tgit -c blame.culture=blameless \\\n> +\t\t-c blame.culture.enforcement=warning blame one 2>err &&\n> +\ttest_i18ngrep \"should be.*git praise\" err\n> +'\n> +\n> +test_expect_success TTY 'interactive: praise blame.culture.enforcement=*:interactive' '\n> +\ttest_must_fail test_terminal git -c blame.culture=blameless \\\n> +\t\t-c blame.culture.enforcement=error:interactive blame one 2>err &&\n> +\ttest_i18ngrep \"must be.*git praise\" err &&\n> +\ttest_terminal git -c blame.culture=blameless \\\n> +\t\t-c blame.culture.enforcement=warning:interactive blame one 2>err &&\n> +\ttest_i18ngrep \"should be.*git praise\" err\n> +'\n> +\n> +test_expect_success TTY 'non-interactive: praise blame.culture.enforcement=*:interactive' '\n> +\tgit -c blame.culture=blameless \\\n> +\t\t-c blame.culture.enforcement=error:interactive blame one 2>err &&\n> +\ttest_i18ngrep ! \"must be.*git praise\" err &&\n> +\tgit -c blame.culture=blameless \\\n> +\t\t-c blame.culture.enforcement=warning:interactive blame one 2>err &&\n> +\ttest_i18ngrep ! \"should be.*git praise\" err\n> +'\n> +\n>  test_expect_success 'blame with showemail options' '\n>  \tgit blame --show-email one >blame1 &&\n>  \tfind_blame <blame1 >result &&\n> --\n> 2.21.0.392.gf8f6787159e\n>\n>\n"}]}