{"thread":{"id":"51667","subject":"[PATCH 00/14] Hash-independent tests, part 5","startedAt":"2019-08-18T19:20:46Z","lastAt":"2021-04-16T18:33:02Z","messageCount":229,"participants":["brian m. carlson","Eric Sunshine","Ævar Arnfjörð Bjarmason","Junio C Hamano","Jeff King","SZEDER Gábor","Jonathan Nieder","Matheus Tavares Bernardino"],"isPatch":true,"patchVersion":1,"patchTotal":14},"messages":[{"id":"380635","messageId":"20190818191646.868106-1-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":null,"subject":"[PATCH 00/14] Hash-independent tests, part 5","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:32Z","receivedAt":"2019-08-18T19:20:46Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"This is the fifth series of test fixes for SHA-256 compatibility.  It\nconsists of 15 patches fixing various tests from t3201 through t4009 and\nhas only test fixes: no test helper changes are included.\n\nThere are approximately 75 remaining test patches (plus some additional\ncode changes) required for the test suite to pass successfully with\nSHA-256, which does not include the patches required to replace SHA-1\nnear-collisions in tests with actual SHA-256 near-collisions.\n\nbrian m. carlson (14):\n  t3201: abstract away SHA-1-specific constants\n  t3206: abstract away hash size constants\n  t3301: abstract away SHA-1-specific constants\n  t3305: make hash size independent\n  t3306: abstract away SHA-1-specific constants\n  t3404: abstract away SHA-1-specific constants\n  t3430: avoid hard-coded object IDs\n  t3506: make hash independent\n  t3600: make hash size independent\n  t3800: make hash-size independent\n  t3903: abstract away SHA-1-specific constants\n  t4000: make hash size independent\n  t4002: make hash independent\n  t4009: make hash size independent\n\n t/t3201-branch-contains.sh    |   8 +-\n t/t3206-range-diff.sh         | 227 +++++++++++++++------\n t/t3301-notes.sh              | 140 ++++++++-----\n t/t3305-notes-fanout.sh       |  22 +-\n t/t3306-notes-prune.sh        |  45 ++---\n t/t3404-rebase-interactive.sh |  22 +-\n t/t3430-rebase-merges.sh      |  23 ++-\n t/t3506-cherry-pick-ff.sh     |   8 +-\n t/t3600-rm.sh                 |   4 +-\n t/t3800-mktag.sh              |  49 ++---\n t/t3903-stash.sh              |  32 ++-\n t/t4000-diff-format.sh        |   2 +-\n t/t4002-diff-basic.sh         | 367 ++++++++++++++++++++++++----------\n t/t4009-diff-rename-4.sh      |  19 +-\n 14 files changed, 640 insertions(+), 328 deletions(-)\n\n"},{"id":"380636","messageId":"20190818191646.868106-2-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH 01/14] t3201: abstract away SHA-1-specific constants","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:33Z","receivedAt":"2019-08-18T19:20:48Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Adjust the test so that it computes variables for object IDs instead of\nusing hard-coded hashes.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3201-branch-contains.sh | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh\nindex 0ea4fc4694..40251c9f8f 100755\n--- a/t/t3201-branch-contains.sh\n+++ b/t/t3201-branch-contains.sh\n@@ -192,10 +192,10 @@ test_expect_success 'branch --merged with --verbose' '\n \tEOF\n \ttest_cmp expect actual &&\n \tgit branch --verbose --merged topic >actual &&\n-\tcat >expect <<-\\EOF &&\n-\t  master c77a0a9 second on master\n-\t* topic  2c939f4 [ahead 1] foo\n-\t  zzz    c77a0a9 second on master\n+\tcat >expect <<-EOF &&\n+\t  master $(git rev-parse --short master) second on master\n+\t* topic  $(git rev-parse --short topic ) [ahead 1] foo\n+\t  zzz    $(git rev-parse --short zzz   ) second on master\n \tEOF\n \ttest_i18ncmp expect actual\n '\n"},{"id":"380637","messageId":"20190818191646.868106-3-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH 02/14] t3206: abstract away hash size constants","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:34Z","receivedAt":"2019-08-18T19:20:52Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"The various short object IDs in the range-diff output differ between\nhash algorithms.  Use test_oid_cache to look up values for both SHA-1\nand SHA-256.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3206-range-diff.sh | 227 +++++++++++++++++++++++++++++++-----------\n 1 file changed, 167 insertions(+), 60 deletions(-)\n\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex ec548654ce..0120f769f1 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -8,17 +8,124 @@ test_description='range-diff tests'\n # harm than good.  We need some real history.\n \n test_expect_success 'setup' '\n-\tgit fast-import < \"$TEST_DIRECTORY\"/t3206/history.export\n+\tgit fast-import < \"$TEST_DIRECTORY\"/t3206/history.export &&\n+\ttest_oid_cache <<-EOF\n+\t# topic\n+\tt1 sha1:4de457d\n+\tt2 sha1:fccce22\n+\tt3 sha1:147e64e\n+\tt4 sha1:a63e992\n+\tt1 sha256:b89f8b9\n+\tt2 sha256:5f12aad\n+\tt3 sha256:ea8b273\n+\tt4 sha256:14b7336\n+\n+\t# unmodified\n+\tu1 sha1:35b9b25\n+\tu2 sha1:de345ab\n+\tu3 sha1:9af6654\n+\tu4 sha1:2901f77\n+\tu1 sha256:e3731be\n+\tu2 sha256:14fadf8\n+\tu3 sha256:736c4bc\n+\tu4 sha256:673e77d\n+\n+\t# reordered\n+\tr1 sha1:aca177a\n+\tr2 sha1:14ad629\n+\tr3 sha1:ee58208\n+\tr4 sha1:307b27a\n+\tr1 sha256:f59d3aa\n+\tr2 sha256:fb261a8\n+\tr3 sha256:cb2649b\n+\tr4 sha256:958577e\n+\n+\t# removed (deleted)\n+\td1 sha1:7657159\n+\td2 sha1:43d84d3\n+\td3 sha1:a740396\n+\td1 sha256:e312513\n+\td2 sha256:eb19258\n+\td3 sha256:1ccb3c1\n+\n+\t# added\n+\ta1 sha1:2716022\n+\ta2 sha1:b62accd\n+\ta3 sha1:df46cfa\n+\ta4 sha1:3e64548\n+\ta5 sha1:12b4063\n+\ta1 sha256:d724f4d\n+\ta2 sha256:1de7762\n+\ta3 sha256:e159431\n+\ta4 sha256:b3e483c\n+\ta5 sha256:90866a7\n+\n+\t# rebased\n+\tb1 sha1:cc9c443\n+\tb2 sha1:c5d9641\n+\tb3 sha1:28cc2b6\n+\tb4 sha1:5628ab7\n+\tb5 sha1:a31b12e\n+\tb1 sha256:a1a8717\n+\tb2 sha256:20a5862\n+\tb3 sha256:587172a\n+\tb4 sha256:2721c5d\n+\tb5 sha256:7b57864\n+\n+\t# changed\n+\tc1 sha1:a4b3333\n+\tc2 sha1:f51d370\n+\tc3 sha1:0559556\n+\tc4 sha1:d966c5c\n+\tc1 sha256:f8c2b9d\n+\tc2 sha256:3fb6318\n+\tc3 sha256:168ab68\n+\tc4 sha256:3526539\n+\n+\t# changed-message\n+\tm1 sha1:f686024\n+\tm2 sha1:4ab067d\n+\tm3 sha1:b9cb956\n+\tm4 sha1:8add5f1\n+\tm1 sha256:31e6281\n+\tm2 sha256:a06bf1b\n+\tm3 sha256:82dc654\n+\tm4 sha256:48470c5\n+\n+\t# renamed\n+\tn1 sha1:f258d75\n+\tn2 sha1:017b62d\n+\tn3 sha1:3ce7af6\n+\tn4 sha1:1e6226b\n+\tn1 sha256:ad52114\n+\tn2 sha256:3b54c8f\n+\tn3 sha256:3b0a644\n+\tn4 sha256:e461653\n+\n+\t# added and removed\n+\ts1 sha1:096b1ba\n+\ts2 sha1:d92e698\n+\ts3 sha1:9a1db4d\n+\ts4 sha1:fea3b5c\n+\ts1 sha256:a7f9134\n+\ts2 sha256:b4c2580\n+\ts3 sha256:1d62aa2\n+\ts4 sha256:48160e8\n+\n+\t# Empty delimiter (included so lines match neatly)\n+\t__ sha1:-------\n+\t__ sha256:-------\n+\tEOF\n '\n \n test_expect_success 'simple A..B A..C (unmodified)' '\n \tgit range-diff --no-color master..topic master..unmodified \\\n \t\t>actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  35b9b25 s/5/A/\n-\t2:  fccce22 = 2:  de345ab s/4/A/\n-\t3:  147e64e = 3:  9af6654 s/11/B/\n-\t4:  a63e992 = 4:  2901f77 s/12/B/\n+\t1:  $(test_oid t1) = 1:  $(test_oid u1) s/5/A/\n+\t2:  $(test_oid t2) = 2:  $(test_oid u2) s/4/A/\n+\t3:  $(test_oid t3) = 3:  $(test_oid u3) s/11/B/\n+\t4:  $(test_oid t4) = 4:  $(test_oid u4) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -38,10 +145,10 @@ test_expect_success 'simple A B C (unmodified)' '\n test_expect_success 'trivial reordering' '\n \tgit range-diff --no-color master topic reordered >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  aca177a s/5/A/\n-\t3:  147e64e = 2:  14ad629 s/11/B/\n-\t4:  a63e992 = 3:  ee58208 s/12/B/\n-\t2:  fccce22 = 4:  307b27a s/4/A/\n+\t1:  $(test_oid t1) = 1:  $(test_oid r1) s/5/A/\n+\t3:  $(test_oid t3) = 2:  $(test_oid r2) s/11/B/\n+\t4:  $(test_oid t4) = 3:  $(test_oid r3) s/12/B/\n+\t2:  $(test_oid t2) = 4:  $(test_oid r4) s/4/A/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -49,10 +156,10 @@ test_expect_success 'trivial reordering' '\n test_expect_success 'removed a commit' '\n \tgit range-diff --no-color master topic removed >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  7657159 s/5/A/\n-\t2:  fccce22 < -:  ------- s/4/A/\n-\t3:  147e64e = 2:  43d84d3 s/11/B/\n-\t4:  a63e992 = 3:  a740396 s/12/B/\n+\t1:  $(test_oid t1) = 1:  $(test_oid d1) s/5/A/\n+\t2:  $(test_oid t2) < -:  $(test_oid __) s/4/A/\n+\t3:  $(test_oid t3) = 2:  $(test_oid d2) s/11/B/\n+\t4:  $(test_oid t4) = 3:  $(test_oid d3) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -60,11 +167,11 @@ test_expect_success 'removed a commit' '\n test_expect_success 'added a commit' '\n \tgit range-diff --no-color master topic added >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  2716022 s/5/A/\n-\t2:  fccce22 = 2:  b62accd s/4/A/\n-\t-:  ------- > 3:  df46cfa s/6/A/\n-\t3:  147e64e = 4:  3e64548 s/11/B/\n-\t4:  a63e992 = 5:  12b4063 s/12/B/\n+\t1:  $(test_oid t1) = 1:  $(test_oid a1) s/5/A/\n+\t2:  $(test_oid t2) = 2:  $(test_oid a2) s/4/A/\n+\t-:  $(test_oid __) > 3:  $(test_oid a3) s/6/A/\n+\t3:  $(test_oid t3) = 4:  $(test_oid a4) s/11/B/\n+\t4:  $(test_oid t4) = 5:  $(test_oid a5) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -72,10 +179,10 @@ test_expect_success 'added a commit' '\n test_expect_success 'new base, A B C' '\n \tgit range-diff --no-color master topic rebased >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  cc9c443 s/5/A/\n-\t2:  fccce22 = 2:  c5d9641 s/4/A/\n-\t3:  147e64e = 3:  28cc2b6 s/11/B/\n-\t4:  a63e992 = 4:  5628ab7 s/12/B/\n+\t1:  $(test_oid t1) = 1:  $(test_oid b1) s/5/A/\n+\t2:  $(test_oid t2) = 2:  $(test_oid b2) s/4/A/\n+\t3:  $(test_oid t3) = 3:  $(test_oid b3) s/11/B/\n+\t4:  $(test_oid t4) = 4:  $(test_oid b4) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -84,11 +191,11 @@ test_expect_success 'new base, B...C' '\n \t# this syntax includes the commits from master!\n \tgit range-diff --no-color topic...rebased >actual &&\n \tcat >expected <<-EOF &&\n-\t-:  ------- > 1:  a31b12e unrelated\n-\t1:  4de457d = 2:  cc9c443 s/5/A/\n-\t2:  fccce22 = 3:  c5d9641 s/4/A/\n-\t3:  147e64e = 4:  28cc2b6 s/11/B/\n-\t4:  a63e992 = 5:  5628ab7 s/12/B/\n+\t-:  $(test_oid __) > 1:  $(test_oid b5) unrelated\n+\t1:  $(test_oid t1) = 2:  $(test_oid b1) s/5/A/\n+\t2:  $(test_oid t2) = 3:  $(test_oid b2) s/4/A/\n+\t3:  $(test_oid t3) = 4:  $(test_oid b3) s/11/B/\n+\t4:  $(test_oid t4) = 5:  $(test_oid b4) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -96,9 +203,9 @@ test_expect_success 'new base, B...C' '\n test_expect_success 'changed commit' '\n \tgit range-diff --no-color topic...changed >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  a4b3333 s/5/A/\n-\t2:  fccce22 = 2:  f51d370 s/4/A/\n-\t3:  147e64e ! 3:  0559556 s/11/B/\n+\t1:  $(test_oid t1) = 1:  $(test_oid c1) s/5/A/\n+\t2:  $(test_oid t2) = 2:  $(test_oid c2) s/4/A/\n+\t3:  $(test_oid t3) ! 3:  $(test_oid c3) s/11/B/\n \t    @@ file: A\n \t      9\n \t      10\n@@ -108,7 +215,7 @@ test_expect_success 'changed commit' '\n \t      12\n \t      13\n \t      14\n-\t4:  a63e992 ! 4:  d966c5c s/12/B/\n+\t4:  $(test_oid t4) ! 4:  $(test_oid c4) s/12/B/\n \t    @@ file\n \t     @@ file: A\n \t      9\n@@ -125,10 +232,10 @@ test_expect_success 'changed commit' '\n test_expect_success 'changed commit with --no-patch diff option' '\n \tgit range-diff --no-color --no-patch topic...changed >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  a4b3333 s/5/A/\n-\t2:  fccce22 = 2:  f51d370 s/4/A/\n-\t3:  147e64e ! 3:  0559556 s/11/B/\n-\t4:  a63e992 ! 4:  d966c5c s/12/B/\n+\t1:  $(test_oid t1) = 1:  $(test_oid c1) s/5/A/\n+\t2:  $(test_oid t2) = 2:  $(test_oid c2) s/4/A/\n+\t3:  $(test_oid t3) ! 3:  $(test_oid c3) s/11/B/\n+\t4:  $(test_oid t4) ! 4:  $(test_oid c4) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -136,16 +243,16 @@ test_expect_success 'changed commit with --no-patch diff option' '\n test_expect_success 'changed commit with --stat diff option' '\n \tgit range-diff --no-color --stat topic...changed >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  a4b3333 s/5/A/\n+\t1:  $(test_oid t1) = 1:  $(test_oid c1) s/5/A/\n \t     a => b | 0\n \t     1 file changed, 0 insertions(+), 0 deletions(-)\n-\t2:  fccce22 = 2:  f51d370 s/4/A/\n+\t2:  $(test_oid t2) = 2:  $(test_oid c2) s/4/A/\n \t     a => b | 0\n \t     1 file changed, 0 insertions(+), 0 deletions(-)\n-\t3:  147e64e ! 3:  0559556 s/11/B/\n+\t3:  $(test_oid t3) ! 3:  $(test_oid c3) s/11/B/\n \t     a => b | 0\n \t     1 file changed, 0 insertions(+), 0 deletions(-)\n-\t4:  a63e992 ! 4:  d966c5c s/12/B/\n+\t4:  $(test_oid t4) ! 4:  $(test_oid c4) s/12/B/\n \t     a => b | 0\n \t     1 file changed, 0 insertions(+), 0 deletions(-)\n \tEOF\n@@ -155,9 +262,9 @@ test_expect_success 'changed commit with --stat diff option' '\n test_expect_success 'changed commit with sm config' '\n \tgit range-diff --no-color --submodule=log topic...changed >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  a4b3333 s/5/A/\n-\t2:  fccce22 = 2:  f51d370 s/4/A/\n-\t3:  147e64e ! 3:  0559556 s/11/B/\n+\t1:  $(test_oid t1) = 1:  $(test_oid c1) s/5/A/\n+\t2:  $(test_oid t2) = 2:  $(test_oid c2) s/4/A/\n+\t3:  $(test_oid t3) ! 3:  $(test_oid c3) s/11/B/\n \t    @@ file: A\n \t      9\n \t      10\n@@ -167,7 +274,7 @@ test_expect_success 'changed commit with sm config' '\n \t      12\n \t      13\n \t      14\n-\t4:  a63e992 ! 4:  d966c5c s/12/B/\n+\t4:  $(test_oid t4) ! 4:  $(test_oid c4) s/12/B/\n \t    @@ file\n \t     @@ file: A\n \t      9\n@@ -184,8 +291,8 @@ test_expect_success 'changed commit with sm config' '\n test_expect_success 'renamed file' '\n \tgit range-diff --no-color --submodule=log topic...renamed-file >actual &&\n \tsed s/Z/\\ /g >expected <<-EOF &&\n-\t1:  4de457d = 1:  f258d75 s/5/A/\n-\t2:  fccce22 ! 2:  017b62d s/4/A/\n+\t1:  $(test_oid t1) = 1:  $(test_oid n1) s/5/A/\n+\t2:  $(test_oid t2) ! 2:  $(test_oid n2) s/4/A/\n \t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n@@ -198,7 +305,7 @@ test_expect_success 'renamed file' '\n \t    Z@@\n \t    Z 1\n \t    Z 2\n-\t3:  147e64e ! 3:  3ce7af6 s/11/B/\n+\t3:  $(test_oid t3) ! 3:  $(test_oid n3) s/11/B/\n \t    @@ Metadata\n \t    Z ## Commit message ##\n \t    Z    s/11/B/\n@@ -210,7 +317,7 @@ test_expect_success 'renamed file' '\n \t    Z 8\n \t    Z 9\n \t    Z 10\n-\t4:  a63e992 ! 4:  1e6226b s/12/B/\n+\t4:  $(test_oid t4) ! 4:  $(test_oid n4) s/12/B/\n \t    @@ Metadata\n \t    Z ## Commit message ##\n \t    Z    s/12/B/\n@@ -229,8 +336,8 @@ test_expect_success 'renamed file' '\n test_expect_success 'file added and later removed' '\n \tgit range-diff --no-color --submodule=log topic...added-removed >actual &&\n \tsed s/Z/\\ /g >expected <<-EOF &&\n-\t1:  4de457d = 1:  096b1ba s/5/A/\n-\t2:  fccce22 ! 2:  d92e698 s/4/A/\n+\t1:  $(test_oid t1) = 1:  $(test_oid s1) s/5/A/\n+\t2:  $(test_oid t2) ! 2:  $(test_oid s2) s/4/A/\n \t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n@@ -246,7 +353,7 @@ test_expect_success 'file added and later removed' '\n \t    Z 7\n \t    +\n \t    + ## new-file (new) ##\n-\t3:  147e64e ! 3:  9a1db4d s/11/B/\n+\t3:  $(test_oid t3) ! 3:  $(test_oid s3) s/11/B/\n \t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n@@ -262,7 +369,7 @@ test_expect_success 'file added and later removed' '\n \t    Z 14\n \t    +\n \t    + ## new-file (deleted) ##\n-\t4:  a63e992 = 4:  fea3b5c s/12/B/\n+\t4:  $(test_oid t4) = 4:  $(test_oid s4) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -275,8 +382,8 @@ test_expect_success 'no commits on one side' '\n test_expect_success 'changed message' '\n \tgit range-diff --no-color topic...changed-message >actual &&\n \tsed s/Z/\\ /g >expected <<-EOF &&\n-\t1:  4de457d = 1:  f686024 s/5/A/\n-\t2:  fccce22 ! 2:  4ab067d s/4/A/\n+\t1:  $(test_oid t1) = 1:  $(test_oid m1) s/5/A/\n+\t2:  $(test_oid t2) ! 2:  $(test_oid m2) s/4/A/\n \t    @@ Metadata\n \t    Z ## Commit message ##\n \t    Z    s/4/A/\n@@ -286,16 +393,16 @@ test_expect_success 'changed message' '\n \t    Z ## file ##\n \t    Z@@\n \t    Z 1\n-\t3:  147e64e = 3:  b9cb956 s/11/B/\n-\t4:  a63e992 = 4:  8add5f1 s/12/B/\n+\t3:  $(test_oid t3) = 3:  $(test_oid m3) s/11/B/\n+\t4:  $(test_oid t4) = 4:  $(test_oid m4) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'dual-coloring' '\n-\tsed -e \"s|^:||\" >expect <<-\\EOF &&\n-\t:<YELLOW>1:  a4b3333 = 1:  f686024 s/5/A/<RESET>\n-\t:<RED>2:  f51d370 <RESET><YELLOW>!<RESET><GREEN> 2:  4ab067d<RESET><YELLOW> s/4/A/<RESET>\n+\tsed -e \"s|^:||\" >expect <<-EOF &&\n+\t:<YELLOW>1:  $(test_oid c1) = 1:  $(test_oid m1) s/5/A/<RESET>\n+\t:<RED>2:  $(test_oid c2) <RESET><YELLOW>!<RESET><GREEN> 2:  $(test_oid m2)<RESET><YELLOW> s/4/A/<RESET>\n \t:    <REVERSE><CYAN>@@<RESET> <RESET>Metadata<RESET>\n \t:      ## Commit message ##<RESET>\n \t:         s/4/A/<RESET>\n@@ -305,7 +412,7 @@ test_expect_success 'dual-coloring' '\n \t:      ## file ##<RESET>\n \t:    <CYAN> @@<RESET>\n \t:      1<RESET>\n-\t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n+\t:<RED>3:  $(test_oid c3) <RESET><YELLOW>!<RESET><GREEN> 3:  $(test_oid m3)<RESET><YELLOW> s/11/B/<RESET>\n \t:    <REVERSE><CYAN>@@<RESET> <RESET>file: A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n@@ -315,7 +422,7 @@ test_expect_success 'dual-coloring' '\n \t:      12<RESET>\n \t:      13<RESET>\n \t:      14<RESET>\n-\t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n+\t:<RED>4:  $(test_oid c4) <RESET><YELLOW>!<RESET><GREEN> 4:  $(test_oid m4)<RESET><YELLOW> s/12/B/<RESET>\n \t:    <REVERSE><CYAN>@@<RESET> <RESET>file<RESET>\n \t:    <CYAN> @@ file: A<RESET>\n \t:      9<RESET>\n"},{"id":"380638","messageId":"20190818191646.868106-5-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH 04/14] t3305: make hash size independent","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:36Z","receivedAt":"2019-08-18T19:20:53Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Instead of hard-coding 40-character shell patterns, use grep to\ndetermine if all of the paths have either zero or one levels of fanout,\nas appropriate.\n\nNote that the final test is implicitly dependent on the hash algorithm.\nDepending on the algorithm in use, the fanout may or may not completely\ncompress.  In its current state, this is not a problem, but it could be\nif the hash algorithm changes again.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3305-notes-fanout.sh | 22 ++++++----------------\n 1 file changed, 6 insertions(+), 16 deletions(-)\n\ndiff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh\nindex 54460beec4..831f83d211 100755\n--- a/t/t3305-notes-fanout.sh\n+++ b/t/t3305-notes-fanout.sh\n@@ -35,15 +35,10 @@ test_expect_success 'many notes created with git-notes triggers fanout' '\n \tgit ls-tree -r --name-only refs/notes/commits |\n \twhile read path\n \tdo\n-\t\tcase \"$path\" in\n-\t\t??/??????????????????????????????????????)\n-\t\t\t: true\n-\t\t\t;;\n-\t\t*)\n+\t\techo $path | grep \"^../[0-9a-f]*$\" || {\n \t\t\techo \"Invalid path \\\"$path\\\"\" &&\n-\t\t\treturn 1\n-\t\t\t;;\n-\t\tesac\n+\t\t\treturn 1;\n+\t\t}\n \tdone\n '\n \n@@ -77,15 +72,10 @@ test_expect_success 'deleting most notes triggers fanout consolidation' '\n \tgit ls-tree -r --name-only refs/notes/commits |\n \twhile read path\n \tdo\n-\t\tcase \"$path\" in\n-\t\t????????????????????????????????????????)\n-\t\t\t: true\n-\t\t\t;;\n-\t\t*)\n+\t\techo $path | grep -v \"^../.*\" || {\n \t\t\techo \"Invalid path \\\"$path\\\"\" &&\n-\t\t\treturn 1\n-\t\t\t;;\n-\t\tesac\n+\t\t\treturn 1;\n+\t\t}\n \tdone\n '\n \n"},{"id":"380639","messageId":"20190818191646.868106-4-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH 03/14] t3301: abstract away SHA-1-specific constants","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:35Z","receivedAt":"2019-08-18T19:20:54Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Adjust the test so that it computes variables for object IDs instead of\nusing hard-coded hashes.  Move some invocations of test_commit around so\nthat we can compute the object IDs for these commits.\n\nCompute several object IDs in the tests instead of using hard-coded\nvalues so that the test works with any hash algorithm.  Since the actual\nvalues are sorted by the object ID of the object being annotated, sort\nthe expected values accordingly as well.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3301-notes.sh | 140 ++++++++++++++++++++++++++++++-----------------\n 1 file changed, 90 insertions(+), 50 deletions(-)\n\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex 704bbc6541..d3fa298c6a 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -66,8 +66,9 @@ test_expect_success 'show notes entry with %N' '\n '\n \n test_expect_success 'create reflog entry' '\n+\tref=$(git rev-parse --short refs/notes/commits) &&\n \tcat <<-EOF >expect &&\n-\t\ta1d8fa6 refs/notes/commits@{0}: notes: Notes added by '\\''git notes add'\\''\n+\t\t$ref refs/notes/commits@{0}: notes: Notes added by '\\''git notes add'\\''\n \tEOF\n \tgit reflog show refs/notes/commits >actual &&\n \ttest_cmp expect actual\n@@ -134,8 +135,9 @@ test_expect_success 'can overwrite existing note with \"git notes add -f\"' '\n '\n \n test_expect_success 'show notes' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 7a4ca6ee52a974a66cbaa78e33214535dff1d691\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:14:13 2005 -0700\n \n@@ -152,8 +154,9 @@ test_expect_success 'show notes' '\n test_expect_success 'show multi-line notes' '\n \ttest_commit 3rd &&\n \tMSG=\"b3${LF}c3c3c3c3${LF}d3d3d3\" git notes add &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect-multiline <<-EOF &&\n-\t\tcommit d07d62e5208f22eb5695e7eb47667dc8b9860290\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:15:13 2005 -0700\n \n@@ -174,8 +177,9 @@ test_expect_success 'show -F notes' '\n \ttest_commit 4th &&\n \techo \"xyzzy\" >note5 &&\n \tgit notes add -F note5 &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect-F <<-EOF &&\n-\t\tcommit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:16:13 2005 -0700\n \n@@ -198,10 +202,13 @@ test_expect_success 'Re-adding -F notes without -f fails' '\n '\n \n test_expect_success 'git log --pretty=raw does not show notes' '\n+\tcommit=$(git rev-parse HEAD) &&\n+\ttree=$(git rev-parse HEAD^{tree}) &&\n+\tparent=$(git rev-parse HEAD^) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11\n-\t\ttree 05ac65288c4c4b3b709a020ae94b2ece2f2201ae\n-\t\tparent d07d62e5208f22eb5695e7eb47667dc8b9860290\n+\t\tcommit $commit\n+\t\ttree $tree\n+\t\tparent $parent\n \t\tauthor A U Thor <author@example.com> 1112912173 -0700\n \t\tcommitter C O Mitter <committer@example.com> 1112912173 -0700\n \n@@ -291,8 +298,9 @@ test_expect_success 'git log --no-notes resets ref list' '\n test_expect_success 'show -m notes' '\n \ttest_commit 5th &&\n \tgit notes add -m spam -m \"foo${LF}bar${LF}baz\" &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect-m <<-EOF &&\n-\t\tcommit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:17:13 2005 -0700\n \n@@ -313,8 +321,9 @@ test_expect_success 'show -m notes' '\n \n test_expect_success 'remove note with add -f -F /dev/null' '\n \tgit notes add -f -F /dev/null &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect-rm-F <<-EOF &&\n-\t\tcommit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:17:13 2005 -0700\n \n@@ -356,14 +365,16 @@ test_expect_success 'create note with combination of -m and -F' '\n test_expect_success 'remove note with \"git notes remove\"' '\n \tgit notes remove HEAD^ &&\n \tgit notes remove &&\n+\tcommit=$(git rev-parse HEAD) &&\n+\tparent=$(git rev-parse HEAD^) &&\n \tcat >expect-rm-remove <<-EOF &&\n-\t\tcommit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:17:13 2005 -0700\n \n \t\t${indent}5th\n \n-\t\tcommit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11\n+\t\tcommit $parent\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:16:13 2005 -0700\n \n@@ -459,9 +470,11 @@ test_expect_success 'removing with --stdin --ignore-missing' '\n '\n \n test_expect_success 'list notes with \"git notes list\"' '\n-\tcat >expect <<-EOF &&\n-\t\tc9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 7a4ca6ee52a974a66cbaa78e33214535dff1d691\n-\t\tc18dc024e14f08d18d14eea0d747ff692d66d6a3 d07d62e5208f22eb5695e7eb47667dc8b9860290\n+\tcommit_2=$(git rev-parse 2nd) &&\n+\tcommit_3=$(git rev-parse 3rd) &&\n+\tsort -t\" \" -k2 >expect <<-EOF &&\n+\t\t$(git rev-parse refs/notes/commits:$commit_2) $commit_2\n+\t\t$(git rev-parse refs/notes/commits:$commit_3) $commit_3\n \tEOF\n \tgit notes list >actual &&\n \ttest_cmp expect actual\n@@ -474,7 +487,7 @@ test_expect_success 'list notes with \"git notes\"' '\n \n test_expect_success 'list specific note with \"git notes list <object>\"' '\n \tcat >expect <<-EOF &&\n-\t\tc18dc024e14f08d18d14eea0d747ff692d66d6a3\n+\t\t$(git rev-parse refs/notes/commits:$commit_3)\n \tEOF\n \tgit notes list HEAD^^ >actual &&\n \ttest_cmp expect actual\n@@ -498,10 +511,11 @@ test_expect_success 'append to existing note with \"git notes append\"' '\n '\n \n test_expect_success '\"git notes list\" does not expand to \"git notes list HEAD\"' '\n-\tcat >expect_list <<-EOF &&\n-\t\tc9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 7a4ca6ee52a974a66cbaa78e33214535dff1d691\n-\t\t4b6ad22357cc8a1296720574b8d2fbc22fab0671 7f9ad8836c775acb134c0a055fc55fb4cd1ba361\n-\t\tc18dc024e14f08d18d14eea0d747ff692d66d6a3 d07d62e5208f22eb5695e7eb47667dc8b9860290\n+\tcommit_5=$(git rev-parse 5th) &&\n+\tsort -t\" \" -k2 >expect_list <<-EOF &&\n+\t\t$(git rev-parse refs/notes/commits:$commit_2) $commit_2\n+\t\t$(git rev-parse refs/notes/commits:$commit_3) $commit_3\n+\t\t$(git rev-parse refs/notes/commits:$commit_5) $commit_5\n \tEOF\n \tgit notes list >actual &&\n \ttest_cmp expect_list actual\n@@ -531,8 +545,9 @@ test_expect_success 'appending empty string to non-existing note does not create\n test_expect_success 'create other note on a different notes ref (setup)' '\n \ttest_commit 6th &&\n \tGIT_NOTES_REF=\"refs/notes/other\" git notes add -m \"other note\" &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect-not-other <<-EOF &&\n-\t\tcommit 2c125331118caba0ff8238b7f4958ac6e93fe39c\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:18:13 2005 -0700\n \n@@ -569,8 +584,10 @@ test_expect_success 'Do not show note when core.notesRef is overridden' '\n '\n \n test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '\n+\tcommit=$(git rev-parse HEAD) &&\n+\tparent=$(git rev-parse HEAD^) &&\n \tcat >expect-both <<-EOF &&\n-\t\tcommit 2c125331118caba0ff8238b7f4958ac6e93fe39c\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:18:13 2005 -0700\n \n@@ -582,7 +599,7 @@ test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '\n \t\tNotes (other):\n \t\t${indent}other note\n \n-\t\tcommit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361\n+\t\tcommit $parent\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:17:13 2005 -0700\n \n@@ -616,8 +633,9 @@ test_expect_success 'notes.displayRef can be given more than once' '\n '\n \n test_expect_success 'notes.displayRef respects order' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect-both-reversed <<-EOF &&\n-\t\tcommit 2c125331118caba0ff8238b7f4958ac6e93fe39c\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:18:13 2005 -0700\n \n@@ -642,14 +660,16 @@ test_expect_success 'GIT_NOTES_DISPLAY_REF works' '\n '\n \n test_expect_success 'GIT_NOTES_DISPLAY_REF overrides config' '\n+\tcommit=$(git rev-parse HEAD) &&\n+\tparent=$(git rev-parse HEAD^) &&\n \tcat >expect-none <<-EOF &&\n-\t\tcommit 2c125331118caba0ff8238b7f4958ac6e93fe39c\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:18:13 2005 -0700\n \n \t\t${indent}6th\n \n-\t\tcommit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361\n+\t\tcommit $parent\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:17:13 2005 -0700\n \n@@ -666,8 +686,9 @@ test_expect_success '--show-notes=* adds to GIT_NOTES_DISPLAY_REF' '\n '\n \n test_expect_success '--no-standard-notes' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect-commits <<-EOF &&\n-\t\tcommit 2c125331118caba0ff8238b7f4958ac6e93fe39c\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:18:13 2005 -0700\n \n@@ -712,8 +733,10 @@ test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '\n '\n \n test_expect_success 'create note from other note with \"git notes add -C\"' '\n+\ttest_commit 7th &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit fb01e0ca8c33b6cc0c6451dde747f97df567cb5c\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:19:13 2005 -0700\n \n@@ -722,7 +745,6 @@ test_expect_success 'create note from other note with \"git notes add -C\"' '\n \t\tNotes:\n \t\t${indent}order test\n \tEOF\n-\ttest_commit 7th &&\n \tgit notes add -C $(git notes list HEAD^) &&\n \tgit log -1 >actual &&\n \ttest_cmp expect actual &&\n@@ -744,8 +766,9 @@ test_expect_success 'create note from non-blob with \"git notes add -C\" fails' '\n '\n \n test_expect_success 'create note from blob with \"git notes add -C\" reuses blob id' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 9a4c31c7f722b5d517e92c64e932dd751e1413bf\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:20:13 2005 -0700\n \n@@ -762,8 +785,10 @@ test_expect_success 'create note from blob with \"git notes add -C\" reuses blob i\n '\n \n test_expect_success 'create note from other note with \"git notes add -c\"' '\n+\ttest_commit 9th &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 2e0db4bc649e174d667a1cde19e725cf897a5bd2\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:21:13 2005 -0700\n \n@@ -772,7 +797,6 @@ test_expect_success 'create note from other note with \"git notes add -c\"' '\n \t\tNotes:\n \t\t${indent}yet another note\n \tEOF\n-\ttest_commit 9th &&\n \tMSG=\"yet another note\" git notes add -c $(git notes list HEAD^^) &&\n \tgit log -1 >actual &&\n \ttest_cmp expect actual\n@@ -785,8 +809,9 @@ test_expect_success 'create note from non-existing note with \"git notes add -c\"\n '\n \n test_expect_success 'append to note from other note with \"git notes append -C\"' '\n+\tcommit=$(git rev-parse HEAD^) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 2e0db4bc649e174d667a1cde19e725cf897a5bd2\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:21:13 2005 -0700\n \n@@ -803,8 +828,9 @@ test_expect_success 'append to note from other note with \"git notes append -C\"'\n '\n \n test_expect_success 'create note from other note with \"git notes append -c\"' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 7c3b87ab368f81e11b1ea87b2ab99a71ccd25406\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:22:13 2005 -0700\n \n@@ -819,8 +845,9 @@ test_expect_success 'create note from other note with \"git notes append -c\"' '\n '\n \n test_expect_success 'append to note from other note with \"git notes append -c\"' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 7c3b87ab368f81e11b1ea87b2ab99a71ccd25406\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:22:13 2005 -0700\n \n@@ -837,8 +864,10 @@ test_expect_success 'append to note from other note with \"git notes append -c\"'\n '\n \n test_expect_success 'copy note with \"git notes copy\"' '\n+\ttest_commit 11th &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit a446fff8777efdc6eb8f4b7c8a5ff699484df0d5\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:23:13 2005 -0700\n \n@@ -849,7 +878,6 @@ test_expect_success 'copy note with \"git notes copy\"' '\n \t\t${indent}\n \t\t${indent}yet another note\n \tEOF\n-\ttest_commit 11th &&\n \tgit notes copy HEAD^ HEAD &&\n \tgit log -1 >actual &&\n \ttest_cmp expect actual &&\n@@ -864,8 +892,9 @@ test_expect_success 'prevent overwrite with \"git notes copy\"' '\n '\n \n test_expect_success 'allow overwrite with \"git notes copy -f\"' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit a446fff8777efdc6eb8f4b7c8a5ff699484df0d5\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:23:13 2005 -0700\n \n@@ -889,8 +918,10 @@ test_expect_success 'cannot copy note from object without notes' '\n '\n \n test_expect_success 'git notes copy --stdin' '\n+\tcommit=$(git rev-parse HEAD) &&\n+\tparent=$(git rev-parse HEAD^) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit e871aa61182b1d95d0a6fb75445d891722863b6b\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:25:13 2005 -0700\n \n@@ -901,7 +932,7 @@ test_expect_success 'git notes copy --stdin' '\n \t\t${indent}\n \t\t${indent}yet another note\n \n-\t\tcommit 65e263ded02ae4e8839bc151095113737579dc12\n+\t\tcommit $parent\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:24:13 2005 -0700\n \n@@ -922,21 +953,23 @@ test_expect_success 'git notes copy --stdin' '\n '\n \n test_expect_success 'git notes copy --for-rewrite (unconfigured)' '\n+\ttest_commit 14th &&\n+\ttest_commit 15th &&\n+\tcommit=$(git rev-parse HEAD) &&\n+\tparent=$(git rev-parse HEAD^) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 4acf42e847e7fffbbf89ee365c20ac7caf40de89\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:27:13 2005 -0700\n \n \t\t${indent}15th\n \n-\t\tcommit 07c85d77059393ed0154b8c96906547a59dfcddd\n+\t\tcommit $parent\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:26:13 2005 -0700\n \n \t\t${indent}14th\n \tEOF\n-\ttest_commit 14th &&\n-\ttest_commit 15th &&\n \t(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&\n \techo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |\n \tgit notes copy --for-rewrite=foo &&\n@@ -945,8 +978,10 @@ test_expect_success 'git notes copy --for-rewrite (unconfigured)' '\n '\n \n test_expect_success 'git notes copy --for-rewrite (enabled)' '\n+\tcommit=$(git rev-parse HEAD) &&\n+\tparent=$(git rev-parse HEAD^) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 4acf42e847e7fffbbf89ee365c20ac7caf40de89\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:27:13 2005 -0700\n \n@@ -957,7 +992,7 @@ test_expect_success 'git notes copy --for-rewrite (enabled)' '\n \t\t${indent}\n \t\t${indent}yet another note\n \n-\t\tcommit 07c85d77059393ed0154b8c96906547a59dfcddd\n+\t\tcommit $parent\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:26:13 2005 -0700\n \n@@ -986,8 +1021,9 @@ test_expect_success 'git notes copy --for-rewrite (disabled)' '\n '\n \n test_expect_success 'git notes copy --for-rewrite (overwrite)' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 4acf42e847e7fffbbf89ee365c20ac7caf40de89\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:27:13 2005 -0700\n \n@@ -1015,8 +1051,9 @@ test_expect_success 'git notes copy --for-rewrite (ignore)' '\n '\n \n test_expect_success 'git notes copy --for-rewrite (append)' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 4acf42e847e7fffbbf89ee365c20ac7caf40de89\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:27:13 2005 -0700\n \n@@ -1037,8 +1074,9 @@ test_expect_success 'git notes copy --for-rewrite (append)' '\n '\n \n test_expect_success 'git notes copy --for-rewrite (append two to one)' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 4acf42e847e7fffbbf89ee365c20ac7caf40de89\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:27:13 2005 -0700\n \n@@ -1075,8 +1113,9 @@ test_expect_success 'git notes copy --for-rewrite (append empty)' '\n '\n \n test_expect_success 'GIT_NOTES_REWRITE_MODE works' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 4acf42e847e7fffbbf89ee365c20ac7caf40de89\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:27:13 2005 -0700\n \n@@ -1095,8 +1134,9 @@ test_expect_success 'GIT_NOTES_REWRITE_MODE works' '\n '\n \n test_expect_success 'GIT_NOTES_REWRITE_REF works' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 4acf42e847e7fffbbf89ee365c20ac7caf40de89\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:27:13 2005 -0700\n \n"},{"id":"380640","messageId":"20190818191646.868106-6-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH 05/14] t3306: abstract away SHA-1-specific constants","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:37Z","receivedAt":"2019-08-18T19:20:56Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Adjust the test so that it computes variables for object IDs instead of\nusing hard-coded hashes.  Convert some single-line heredocs into inline\nuses of echo now that they can be expressed succinctly.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3306-notes-prune.sh | 45 ++++++++++++++++++++----------------------\n 1 file changed, 21 insertions(+), 24 deletions(-)\n\ndiff --git a/t/t3306-notes-prune.sh b/t/t3306-notes-prune.sh\nindex 61748088eb..8f4102ff9e 100755\n--- a/t/t3306-notes-prune.sh\n+++ b/t/t3306-notes-prune.sh\n@@ -11,23 +11,26 @@ test_expect_success 'setup: create a few commits with notes' '\n \ttest_tick &&\n \tgit commit -m 1st &&\n \tgit notes add -m \"Note #1\" &&\n+\tfirst=$(git rev-parse HEAD) &&\n \t: > file2 &&\n \tgit add file2 &&\n \ttest_tick &&\n \tgit commit -m 2nd &&\n \tgit notes add -m \"Note #2\" &&\n+\tsecond=$(git rev-parse HEAD) &&\n \t: > file3 &&\n \tgit add file3 &&\n \ttest_tick &&\n \tgit commit -m 3rd &&\n-\tCOMMIT_FILE=.git/objects/5e/e1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&\n+\tthird=$(git rev-parse HEAD) &&\n+\tCOMMIT_FILE=$(echo $third | sed \"s!^..!.git/objects/&/!\") &&\n \ttest -f $COMMIT_FILE &&\n \ttest-tool chmtime =+0 $COMMIT_FILE &&\n \tgit notes add -m \"Note #3\"\n '\n \n cat > expect <<END_OF_LOG\n-commit 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29\n+commit $third\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:15:13 2005 -0700\n \n@@ -36,7 +39,7 @@ Date:   Thu Apr 7 15:15:13 2005 -0700\n Notes:\n     Note #3\n \n-commit 08341ad9e94faa089d60fd3f523affb25c6da189\n+commit $second\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:14:13 2005 -0700\n \n@@ -45,7 +48,7 @@ Date:   Thu Apr 7 15:14:13 2005 -0700\n Notes:\n     Note #2\n \n-commit ab5f302035f2e7aaf04265f08b42034c23256e1f\n+commit $first\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:13:13 2005 -0700\n \n@@ -70,16 +73,16 @@ test_expect_success 'remove some commits' '\n \n test_expect_success 'verify that commits are gone' '\n \n-\ttest_must_fail git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&\n-\tgit cat-file -p 08341ad9e94faa089d60fd3f523affb25c6da189 &&\n-\tgit cat-file -p ab5f302035f2e7aaf04265f08b42034c23256e1f\n+\ttest_must_fail git cat-file -p $third &&\n+\tgit cat-file -p $second &&\n+\tgit cat-file -p $first\n '\n \n test_expect_success 'verify that notes are still present' '\n \n-\tgit notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&\n-\tgit notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&\n-\tgit notes show ab5f302035f2e7aaf04265f08b42034c23256e1f\n+\tgit notes show $third &&\n+\tgit notes show $second &&\n+\tgit notes show $first\n '\n \n test_expect_success 'prune -n does not remove notes' '\n@@ -90,13 +93,10 @@ test_expect_success 'prune -n does not remove notes' '\n \ttest_cmp expect actual\n '\n \n-cat > expect <<EOF\n-5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29\n-EOF\n \n test_expect_success 'prune -n lists prunable notes' '\n \n-\n+\techo $third >expect &&\n \tgit notes prune -n > actual &&\n \ttest_cmp expect actual\n '\n@@ -109,9 +109,9 @@ test_expect_success 'prune notes' '\n \n test_expect_success 'verify that notes are gone' '\n \n-\ttest_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&\n-\tgit notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&\n-\tgit notes show ab5f302035f2e7aaf04265f08b42034c23256e1f\n+\ttest_must_fail git notes show $third &&\n+\tgit notes show $second &&\n+\tgit notes show $first\n '\n \n test_expect_success 'remove some commits' '\n@@ -121,21 +121,18 @@ test_expect_success 'remove some commits' '\n \tgit gc --prune=now\n '\n \n-cat > expect <<EOF\n-08341ad9e94faa089d60fd3f523affb25c6da189\n-EOF\n-\n test_expect_success 'prune -v notes' '\n \n+\techo $second >expect &&\n \tgit notes prune -v > actual &&\n \ttest_cmp expect actual\n '\n \n test_expect_success 'verify that notes are gone' '\n \n-\ttest_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&\n-\ttest_must_fail git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&\n-\tgit notes show ab5f302035f2e7aaf04265f08b42034c23256e1f\n+\ttest_must_fail git notes show $third &&\n+\ttest_must_fail git notes show $second &&\n+\tgit notes show $first\n '\n \n test_done\n"},{"id":"380641","messageId":"20190818191646.868106-7-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH 06/14] t3404: abstract away SHA-1-specific constants","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:38Z","receivedAt":"2019-08-18T19:20:57Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Adjust the test so that it computes variables for object IDs instead of\nusing hard-coded hashes.  Add a use of $EMPTY_TREE instead of a\nhard-coded value.  Remove a comment about hard-coded hashes which is no\nlonger applicable.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3404-rebase-interactive.sh | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 461dd539ff..87ee69f78e 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -29,9 +29,6 @@ Initial setup:\n \n . \"$TEST_DIRECTORY\"/lib-rebase.sh\n \n-# WARNING: Modifications to the initial repository can change the SHA ID used\n-# in the expect2 file for the 'stop on conflicting pick' test.\n-\n test_expect_success 'setup' '\n \ttest_commit A file1 &&\n \ttest_commit B file1 &&\n@@ -233,25 +230,28 @@ test_expect_success 'exchange two commits' '\n \tset_fake_editor &&\n \tFAKE_LINES=\"2 1\" git rebase -i HEAD~2 &&\n \ttest H = $(git cat-file commit HEAD^ | sed -ne \\$p) &&\n-\ttest G = $(git cat-file commit HEAD | sed -ne \\$p)\n+\ttest G = $(git cat-file commit HEAD | sed -ne \\$p) &&\n+\tblob1=$(git rev-parse --short HEAD^:file1) &&\n+\tblob2=$(git rev-parse --short HEAD:file1) &&\n+\tcommit=$(git rev-parse --short HEAD)\n '\n \n test_expect_success 'stop on conflicting pick' '\n-\tcat >expect <<-\\EOF &&\n+\tcat >expect <<-EOF &&\n \tdiff --git a/file1 b/file1\n-\tindex f70f10e..fd79235 100644\n+\tindex $blob1..$blob2 100644\n \t--- a/file1\n \t+++ b/file1\n \t@@ -1 +1 @@\n \t-A\n \t+G\n \tEOF\n-\tcat >expect2 <<-\\EOF &&\n+\tcat >expect2 <<-EOF &&\n \t<<<<<<< HEAD\n \tD\n \t=======\n \tG\n-\t>>>>>>> 5d18e54... G\n+\t>>>>>>> $commit... G\n \tEOF\n \tgit tag new-branch1 &&\n \tset_fake_editor &&\n@@ -1003,7 +1003,7 @@ test_expect_success 'rebase -i --root temporary sentinel commit' '\n \tgit checkout B &&\n \tset_fake_editor &&\n \ttest_must_fail env FAKE_LINES=\"2\" git rebase -i --root &&\n-\tgit cat-file commit HEAD | grep \"^tree 4b825dc642cb\" &&\n+\tgit cat-file commit HEAD | grep \"^tree $EMPTY_TREE\" &&\n \tgit rebase --abort\n '\n \n@@ -1161,7 +1161,7 @@ test_expect_success 'rebase -i error on commits with \\ in message' '\n \ttest_expect_code 1 grep  \"\temp\" error\n '\n \n-test_expect_success 'short SHA-1 setup' '\n+test_expect_success SHA1 'short SHA-1 setup' '\n \ttest_when_finished \"git checkout master\" &&\n \tgit checkout --orphan collide &&\n \tgit rm -rf . &&\n@@ -1173,7 +1173,7 @@ test_expect_success 'short SHA-1 setup' '\n \t)\n '\n \n-test_expect_success 'short SHA-1 collide' '\n+test_expect_success SHA1 'short SHA-1 collide' '\n \ttest_when_finished \"reset_rebase && git checkout master\" &&\n \tgit checkout collide &&\n \t(\n"},{"id":"380642","messageId":"20190818191646.868106-8-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH 07/14] t3430: avoid hard-coded object IDs","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:39Z","receivedAt":"2019-08-18T19:20:58Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Compute the object IDs used in the todo list instead of hard-coding\nthem.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3430-rebase-merges.sh | 23 +++++++++++++++--------\n 1 file changed, 15 insertions(+), 8 deletions(-)\n\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex 7b6c4847ad..c1ea2ee297 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -37,20 +37,27 @@ test_expect_success 'setup' '\n \ttest_commit A &&\n \tgit checkout -b first &&\n \ttest_commit B &&\n+\tb=$(git rev-parse --short HEAD) &&\n \tgit checkout master &&\n \ttest_commit C &&\n+\tc=$(git rev-parse --short HEAD) &&\n \ttest_commit D &&\n+\td=$(git rev-parse --short HEAD) &&\n \tgit merge --no-commit B &&\n \ttest_tick &&\n \tgit commit -m E &&\n \tgit tag -m E E &&\n+\te=$(git rev-parse --short HEAD) &&\n \tgit checkout -b second C &&\n \ttest_commit F &&\n+\tf=$(git rev-parse --short HEAD) &&\n \ttest_commit G &&\n+\tg=$(git rev-parse --short HEAD) &&\n \tgit checkout master &&\n \tgit merge --no-commit G &&\n \ttest_tick &&\n \tgit commit -m H &&\n+\th=$(git rev-parse --short HEAD) &&\n \tgit tag -m H H &&\n \tgit checkout A &&\n \ttest_commit conflicting-G G.t\n@@ -93,24 +100,24 @@ test_expect_success 'create completely different structure' '\n '\n \n test_expect_success 'generate correct todo list' '\n-\tcat >expect <<-\\EOF &&\n+\tcat >expect <<-EOF &&\n \tlabel onto\n \n \treset onto\n-\tpick d9df450 B\n+\tpick $b B\n \tlabel E\n \n \treset onto\n-\tpick 5dee784 C\n+\tpick $c C\n \tlabel branch-point\n-\tpick ca2c861 F\n-\tpick 088b00a G\n+\tpick $f F\n+\tpick $g G\n \tlabel H\n \n \treset branch-point # C\n-\tpick 12bd07b D\n-\tmerge -C 2051b56 E # E\n-\tmerge -C 233d48a H # H\n+\tpick $d D\n+\tmerge -C $e E # E\n+\tmerge -C $h H # H\n \n \tEOF\n \n"},{"id":"380643","messageId":"20190818191646.868106-9-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH 08/14] t3506: make hash independent","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:40Z","receivedAt":"2019-08-18T19:21:01Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"This test uses a hard-coded object ID to ensure that the result of\ncherry-pick --ff is correct.  Use test_oid to make this work for both\nSHA-1 and SHA-256.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3506-cherry-pick-ff.sh | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh\nindex 127dd0082f..9d5adbc130 100755\n--- a/t/t3506-cherry-pick-ff.sh\n+++ b/t/t3506-cherry-pick-ff.sh\n@@ -16,7 +16,11 @@ test_expect_success setup '\n \tgit add file1 &&\n \ttest_tick &&\n \tgit commit -m \"second\" &&\n-\tgit tag second\n+\tgit tag second &&\n+\ttest_oid_cache <<-EOF\n+\tcp_ff sha1:1df192cd8bc58a2b275d842cede4d221ad9000d1\n+\tcp_ff sha256:e70d6b7fc064bddb516b8d512c9057094b96ce6ff08e12080acc4fe7f1d60a1d\n+\tEOF\n '\n \n test_expect_success 'cherry-pick using --ff fast forwards' '\n@@ -102,7 +106,7 @@ test_expect_success 'cherry pick a root commit with --ff' '\n \tgit add file2 &&\n \tgit commit --amend -m \"file2\" &&\n \tgit cherry-pick --ff first &&\n-\ttest \"$(git rev-parse --verify HEAD)\" = \"1df192cd8bc58a2b275d842cede4d221ad9000d1\"\n+\ttest \"$(git rev-parse --verify HEAD)\" = \"$(test_oid cp_ff)\"\n '\n \n test_expect_success 'cherry-pick --ff on unborn branch' '\n"},{"id":"380644","messageId":"20190818191646.868106-11-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH 10/14] t3800: make hash-size independent","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:42Z","receivedAt":"2019-08-18T19:21:06Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Replace references to several hard-coded object IDs with a variable\nreferring to the generated commit.  Avoid matching on exact character\npositions, which will be different depending on the hash in use.  In the\ntest for a valid object ID, use an obviously invalid one from the lookup\ntable.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3800-mktag.sh | 49 ++++++++++++++++++++++++------------------------\n 1 file changed, 25 insertions(+), 24 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 8eb47942e2..64dcc5ec28 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -23,6 +23,7 @@ check_verify_failure () {\n # first create a commit, so we have a valid object/type\n # for the tag.\n test_expect_success 'setup' '\n+\ttest_oid_init &&\n \techo Hello >A &&\n \tgit update-index --add A &&\n \tgit commit -m \"Initial commit\" &&\n@@ -69,28 +70,28 @@ check_verify_failure '\"object\" line SHA1 check' '^error: char7: .*SHA1 hash$'\n #  4. type line label check\n \n cat >tag.sig <<EOF\n-object 779e9b33986b1c2670fff52c5067603117b3e895\n+object $head\n xxxx tag\n tag mytag\n tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"type\" line label check' '^error: char47: .*\"\\\\ntype \"$'\n+check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n \n ############################################################\n #  5. type line eol check\n \n-echo \"object 779e9b33986b1c2670fff52c5067603117b3e895\" >tag.sig\n+echo \"object $head\" >tag.sig\n printf \"type tagsssssssssssssssssssssssssssssss\" >>tag.sig\n \n-check_verify_failure '\"type\" line eol check' '^error: char48: .*\"\\\\n\"$'\n+check_verify_failure '\"type\" line eol check' '^error: char.*: .*\"\\\\n\"$'\n \n ############################################################\n #  6. tag line label check #1\n \n cat >tag.sig <<EOF\n-object 779e9b33986b1c2670fff52c5067603117b3e895\n+object $head\n type tag\n xxx mytag\n tagger . <> 0 +0000\n@@ -98,37 +99,37 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure '\"tag\" line label check #1' \\\n-\t'^error: char57: no \"tag \" found$'\n+\t'^error: char.*: no \"tag \" found$'\n \n ############################################################\n #  7. tag line label check #2\n \n cat >tag.sig <<EOF\n-object 779e9b33986b1c2670fff52c5067603117b3e895\n+object $head\n type taggggggggggggggggggggggggggggggg\n tag\n EOF\n \n check_verify_failure '\"tag\" line label check #2' \\\n-\t'^error: char87: no \"tag \" found$'\n+\t'^error: char.*: no \"tag \" found$'\n \n ############################################################\n #  8. type line type-name length check\n \n cat >tag.sig <<EOF\n-object 779e9b33986b1c2670fff52c5067603117b3e895\n+object $head\n type taggggggggggggggggggggggggggggggg\n tag mytag\n EOF\n \n check_verify_failure '\"type\" line type-name length check' \\\n-\t'^error: char53: type too long$'\n+\t'^error: char.*: type too long$'\n \n ############################################################\n #  9. verify object (SHA1/type) check\n \n cat >tag.sig <<EOF\n-object 779e9b33986b1c2670fff52c5067603117b3e895\n+object $(test_oid deadbeef)\n type tagggg\n tag mytag\n tagger . <> 0 +0000\n@@ -150,7 +151,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify tag-name check' \\\n-\t'^error: char67: could not verify tag name$'\n+\t'^error: char.*: could not verify tag name$'\n \n ############################################################\n # 11. tagger line label check #1\n@@ -164,7 +165,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #1' \\\n-\t'^error: char70: could not find \"tagger \"$'\n+\t'^error: char.*: could not find \"tagger \"$'\n \n ############################################################\n # 12. tagger line label check #2\n@@ -179,7 +180,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #2' \\\n-\t'^error: char70: could not find \"tagger \"$'\n+\t'^error: char.*: could not find \"tagger \"$'\n \n ############################################################\n # 13. disallow missing tag author name\n@@ -194,7 +195,7 @@ This is filler\n EOF\n \n check_verify_failure 'disallow missing tag author name' \\\n-\t'^error: char77: missing tagger name$'\n+\t'^error: char.*: missing tagger name$'\n \n ############################################################\n # 14. disallow missing tag author name\n@@ -209,7 +210,7 @@ tagger T A Gger <\n EOF\n \n check_verify_failure 'disallow malformed tagger' \\\n-\t'^error: char77: malformed tagger field$'\n+\t'^error: char.*: malformed tagger field$'\n \n ############################################################\n # 15. allow empty tag email\n@@ -238,7 +239,7 @@ tagger T A Gger <tag ger@example.com> 0 +0000\n EOF\n \n check_verify_failure 'disallow spaces in tag email' \\\n-\t'^error: char77: malformed tagger field$'\n+\t'^error: char.*: malformed tagger field$'\n \n ############################################################\n # 17. disallow missing tag timestamp\n@@ -252,7 +253,7 @@ tagger T A Gger <tagger@example.com>__\n EOF\n \n check_verify_failure 'disallow missing tag timestamp' \\\n-\t'^error: char107: missing tag timestamp$'\n+\t'^error: char.*: missing tag timestamp$'\n \n ############################################################\n # 18. detect invalid tag timestamp1\n@@ -266,7 +267,7 @@ tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008\n EOF\n \n check_verify_failure 'detect invalid tag timestamp1' \\\n-\t'^error: char107: missing tag timestamp$'\n+\t'^error: char.*: missing tag timestamp$'\n \n ############################################################\n # 19. detect invalid tag timestamp2\n@@ -280,7 +281,7 @@ tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500\n EOF\n \n check_verify_failure 'detect invalid tag timestamp2' \\\n-\t'^error: char111: malformed tag timestamp$'\n+\t'^error: char.*: malformed tag timestamp$'\n \n ############################################################\n # 20. detect invalid tag timezone1\n@@ -294,7 +295,7 @@ tagger T A Gger <tagger@example.com> 1206478233 GMT\n EOF\n \n check_verify_failure 'detect invalid tag timezone1' \\\n-\t'^error: char118: malformed tag timezone$'\n+\t'^error: char.*: malformed tag timezone$'\n \n ############################################################\n # 21. detect invalid tag timezone2\n@@ -308,7 +309,7 @@ tagger T A Gger <tagger@example.com> 1206478233 +  30\n EOF\n \n check_verify_failure 'detect invalid tag timezone2' \\\n-\t'^error: char118: malformed tag timezone$'\n+\t'^error: char.*: malformed tag timezone$'\n \n ############################################################\n # 22. detect invalid tag timezone3\n@@ -322,7 +323,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -1430\n EOF\n \n check_verify_failure 'detect invalid tag timezone3' \\\n-\t'^error: char118: malformed tag timezone$'\n+\t'^error: char.*: malformed tag timezone$'\n \n ############################################################\n # 23. detect invalid header entry\n@@ -337,7 +338,7 @@ this line should not be here\n EOF\n \n check_verify_failure 'detect invalid header entry' \\\n-\t'^error: char124: trailing garbage in tag header$'\n+\t'^error: char.*: trailing garbage in tag header$'\n \n ############################################################\n # 24. create valid tag\n"},{"id":"380646","messageId":"20190818191646.868106-12-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH 11/14] t3903: abstract away SHA-1-specific constants","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:43Z","receivedAt":"2019-08-18T19:21:06Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Abstract away the SHA-1-specific constants by sanitizing diff output to\nremove the index lines, since it's clear from the assertions in question\nthat we are not interested in the specific object IDs.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3903-stash.sh | 32 ++++++++++++++++++++++----------\n 1 file changed, 22 insertions(+), 10 deletions(-)\n\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex b8e337893f..8e3e0992df 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -7,6 +7,18 @@ test_description='Test git stash'\n \n . ./test-lib.sh\n \n+diff_cmp () {\n+\tfor i in \"$1\" \"$2\"\n+\tdo\n+\t\tsed -e 's/^index 0000000\\.\\.[0-9a-f]*/index 0000000..1234567/' \\\n+\t\t-e 's/^index [0-9a-f]*\\.\\.[0-9a-f]*/index 1234567..89abcde/' \\\n+\t\t-e 's/^index [0-9a-f]*,[0-9a-f]*\\.\\.[0-9a-f]*/index 1234567,7654321..89abcde/' \\\n+\t\t\"$i\" > \"$i.compare\"\n+\tdone &&\n+\ttest_cmp \"$1.compare\" \"$2.compare\" &&\n+\trm -f \"$1.compare\" \"$2.compare\"\n+}\n+\n test_expect_success 'stash some dirty working directory' '\n \techo 1 >file &&\n \tgit add file &&\n@@ -36,7 +48,7 @@ EOF\n test_expect_success 'parents of stash' '\n \ttest $(git rev-parse stash^) = $(git rev-parse HEAD) &&\n \tgit diff stash^2..stash >output &&\n-\ttest_cmp expect output\n+\tdiff_cmp expect output\n '\n \n test_expect_success 'applying bogus stash does nothing' '\n@@ -210,13 +222,13 @@ test_expect_success 'stash branch' '\n \ttest refs/heads/stashbranch = $(git symbolic-ref HEAD) &&\n \ttest $(git rev-parse HEAD) = $(git rev-parse master^) &&\n \tgit diff --cached >output &&\n-\ttest_cmp expect output &&\n+\tdiff_cmp expect output &&\n \tgit diff >output &&\n-\ttest_cmp expect1 output &&\n+\tdiff_cmp expect1 output &&\n \tgit add file &&\n \tgit commit -m alternate\\ second &&\n \tgit diff master..stashbranch >output &&\n-\ttest_cmp output expect2 &&\n+\tdiff_cmp output expect2 &&\n \ttest 0 = $(git stash list | wc -l)\n '\n \n@@ -577,7 +589,7 @@ test_expect_success 'stash show -p - stashes on stack, stash-like argument' '\n \t+bar\n \tEOF\n \tgit stash show -p ${STASH_ID} >actual &&\n-\ttest_cmp expected actual\n+\tdiff_cmp expected actual\n '\n \n test_expect_success 'stash show - no stashes on stack, stash-like argument' '\n@@ -609,7 +621,7 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' '\n \t+foo\n \tEOF\n \tgit stash show -p ${STASH_ID} >actual &&\n-\ttest_cmp expected actual\n+\tdiff_cmp expected actual\n '\n \n test_expect_success 'stash show --patience shows diff' '\n@@ -627,7 +639,7 @@ test_expect_success 'stash show --patience shows diff' '\n \t+foo\n \tEOF\n \tgit stash show --patience ${STASH_ID} >actual &&\n-\ttest_cmp expected actual\n+\tdiff_cmp expected actual\n '\n \n test_expect_success 'drop: fail early if specified stash is not a stash ref' '\n@@ -791,7 +803,7 @@ test_expect_success 'stash where working directory contains \"HEAD\" file' '\n \tgit diff-index --cached --quiet HEAD &&\n \ttest \"$(git rev-parse stash^)\" = \"$(git rev-parse HEAD)\" &&\n \tgit diff stash^..stash >output &&\n-\ttest_cmp expect output\n+\tdiff_cmp expect output\n '\n \n test_expect_success 'store called with invalid commit' '\n@@ -847,7 +859,7 @@ test_expect_success 'stash list implies --first-parent -m' '\n \t+working\n \tEOF\n \tgit stash list --format=%gd -p >actual &&\n-\ttest_cmp expect actual\n+\tdiff_cmp expect actual\n '\n \n test_expect_success 'stash list --cc shows combined diff' '\n@@ -864,7 +876,7 @@ test_expect_success 'stash list --cc shows combined diff' '\n \t++working\n \tEOF\n \tgit stash list --format=%gd -p --cc >actual &&\n-\ttest_cmp expect actual\n+\tdiff_cmp expect actual\n '\n \n test_expect_success 'stash is not confused by partial renames' '\n"},{"id":"380645","messageId":"20190818191646.868106-13-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH 12/14] t4000: make hash size independent","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:44Z","receivedAt":"2019-08-18T19:21:07Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Use $ZERO_OID instead of hard-coding a fixed size all-zeros object ID.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t4000-diff-format.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh\nindex 8de36b7d12..e5116a76a1 100755\n--- a/t/t4000-diff-format.sh\n+++ b/t/t4000-diff-format.sh\n@@ -78,7 +78,7 @@ test_expect_success 'git diff-files --no-patch --patch shows the patch' '\n \n test_expect_success 'git diff-files --no-patch --patch-with-raw shows the patch and raw data' '\n \tgit diff-files --no-patch --patch-with-raw >actual &&\n-\tgrep -q \"^:100644 100755 .* 0000000000000000000000000000000000000000 M\tpath0\\$\" actual &&\n+\tgrep -q \"^:100644 100755 .* $ZERO_OID M\tpath0\\$\" actual &&\n \ttail -n +4 actual >actual-patch &&\n \tcompare_diff_patch expected actual-patch\n '\n"},{"id":"380647","messageId":"20190818191646.868106-14-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH 13/14] t4002: make hash independent","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:45Z","receivedAt":"2019-08-18T19:21:10Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Refactor out the hard-coded object IDs and use test_oid to provide\nvalues for both SHA-1 and SHA-256.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t4002-diff-basic.sh | 367 +++++++++++++++++++++++++++++-------------\n 1 file changed, 258 insertions(+), 109 deletions(-)\n\ndiff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh\nindex 3a6c21e825..cbcdd10464 100755\n--- a/t/t4002-diff-basic.sh\n+++ b/t/t4002-diff-basic.sh\n@@ -7,123 +7,272 @@ test_description='Test diff raw-output.\n \n '\n . ./test-lib.sh\n+\n . \"$TEST_DIRECTORY\"/lib-read-tree-m-3way.sh\n \n-cat >.test-plain-OA <<\\EOF\n-:000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A\tAA\n-:000000 100644 0000000000000000000000000000000000000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 A\tAN\n-:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D\tDD\n-:000000 040000 0000000000000000000000000000000000000000 6d50f65d3bdab91c63444294d38f08aeff328e42 A\tDF\n-:100644 000000 141c1f1642328e4bc46a7d801a71da392e66791e 0000000000000000000000000000000000000000 D\tDM\n-:100644 000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 0000000000000000000000000000000000000000 D\tDN\n-:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A\tLL\n-:100644 100644 03f24c8c4700babccfd28b654e7e8eac402ad6cd 103d9f89b50b9aad03054b579be5e7aa665f2d57 M\tMD\n-:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 M\tMM\n-:100644 100644 bd084b0c27c7b6cc34f11d6d0509a29be3caf970 a716d58de4a570e0038f5c307bd8db34daea021f M\tMN\n-:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M\tSS\n-:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 4c86f9a85fbc5e6804ee2e17a797538fbe785bca M\tTT\n-:040000 040000 7d670fdcdb9929f6c7dac196ff78689cd1c566a1 5e5f22072bb39f6e12cf663a57cb634c76eefb49 M\tZ\n+test_oid_init\n+\n+test_oid_cache <<\\EOF\n+aa_1 sha1:ccba72ad3888a3520b39efcf780b9ee64167535d\n+aa_1 sha256:9febfbf18197819b2735c45291f138525d2476d59470f98239647544586ba403\n+\n+aa_2 sha1:6aa2b5335b16431a0ef71e5c0a28be69183cf6a2\n+aa_2 sha256:6eaa3437de83f145a4aaa6ba355303075ade547b128ec6a2cd00a81ff7ce7a56\n+\n+an_1 sha1:7e426fb079479fd67f6d81f984e4ec649a44bc25\n+an_1 sha256:8f92a0bec99e399a38e3bd0e1bf19fbf121e0160efb29b857df79d439f1c4536\n+\n+dd_1 sha1:bcc68ef997017466d5c9094bcf7692295f588c9a\n+dd_1 sha256:07e17428b00639b85485d2b01083d219e2f3e3ba8579e9ca44e9cc8dd554d952\n+\n+df_1 sha1:6d50f65d3bdab91c63444294d38f08aeff328e42\n+df_1 sha256:e367cecc27e9bf5451b1c65828cb21938d36a5f8e39c1b03ad6509cc36bb8e9d\n+\n+df_2 sha1:71420ab81e254145d26d6fc0cddee64c1acd4787\n+df_2 sha256:0f0a86d10347ff6921d03a3c954679f3f1d14fa3d5cd82f57b32c09755f3a47d\n+\n+dfd1 sha1:68a6d8b91da11045cf4aa3a5ab9f2a781c701249\n+dfd1 sha256:f3bd3265b02b6978ce86490d8ad026c573639c974b3de1d9faf30d8d5a77d3d5\n+\n+dm_1 sha1:141c1f1642328e4bc46a7d801a71da392e66791e\n+dm_1 sha256:c89f8656e7b94e21ee5fbaf0e2149bbf783c51edbe2ce110349cac13059ee7ed\n+\n+dm_2 sha1:3c4d8de5fbad08572bab8e10eef8dbb264cf0231\n+dm_2 sha256:83a572e37e0c94086294dae2cecc43d9131afd6f6c906e495c78972230b54988\n+\n+dn_1 sha1:35abde1506ddf806572ff4d407bd06885d0f8ee9\n+dn_1 sha256:775d5852582070e620be63327bfa515fab8f71c7ac3e4f0c3cd6267b4377ba28\n+\n+ll_2 sha1:1d41122ebdd7a640f29d3c9cc4f9d70094374762\n+ll_2 sha256:7917b4948a883cfed0a77d3d5a625dc8577d6ddcc3c6c3bbc56c4d4226a2246d\n+\n+md_1 sha1:03f24c8c4700babccfd28b654e7e8eac402ad6cd\n+md_1 sha256:fc9f30369b978595ad685ba11ca9a17de0af16d79cd4b629975f4f1590033902\n+\n+md_2 sha1:103d9f89b50b9aad03054b579be5e7aa665f2d57\n+md_2 sha256:fc78ec75275628762fe520479a6b2398dec295ce7aabcb1d15e5963c7b4e9317\n+\n+mm_1 sha1:b258508afb7ceb449981bd9d63d2d3e971bf8d34\n+mm_1 sha256:a4b7847d228e900e3000285e240c20fd96f9dd41ce1445305f6eada126d4a04a\n+\n+mm_2 sha1:b431b272d829ff3aa4d1a5085f4394ab4d3305b6\n+mm_2 sha256:3f8b83ea36aacf689bcf1a1290a9a8ed341564d32682ea6f76fea9a979186782\n+\n+mm_3 sha1:19989d4559aae417fedee240ccf2ba315ea4dc2b\n+mm_3 sha256:71b3bfc5747ac033fff9ea0ab39ee453a3af2969890e75d6ef547b87544e2681\n+\n+mn_1 sha1:bd084b0c27c7b6cc34f11d6d0509a29be3caf970\n+mn_1 sha256:47a67450583d7a329eb01a7c4ba644945af72c0ed2c7c95eb5a00d6e46d4d483\n+\n+mn_2 sha1:a716d58de4a570e0038f5c307bd8db34daea021f\n+mn_2 sha256:f95104c1ebe27acb84bac25a7be98c71f6b8d3054b21f357a5be0c524ad97e08\n+\n+nm_1 sha1:c8f25781e8f1792e3e40b74225e20553041b5226\n+nm_1 sha256:09baddc7afaa62e62e152c23c9c3ab94bf15a3894031e227e9be7fe68e1f4e49\n+\n+nm_2 sha1:cdb9a8c3da571502ac30225e9c17beccb8387983\n+nm_2 sha256:58b5227956ac2d2a08d0efa513c0ae37430948b16791ea3869a1308dbf05536d\n+\n+na_1 sha1:15885881ea69115351c09b38371f0348a3fb8c67\n+na_1 sha256:18e4fdd1670cd7968ee23d35bfd29e5418d56fb190c840094c1c57ceee0aad8f\n+\n+nd_1 sha1:a4e179e4291e5536a5e1c82e091052772d2c5a93\n+nd_1 sha256:07dac9b01d00956ea0c65bd993d7de4864aeef2ed3cbb1255d9f1d949fcd6df6\n+\n+ss_1 sha1:40c959f984c8b89a2b02520d17f00d717f024397\n+ss_1 sha256:50fc1b5df74d9910db2f9270993484235f15b69b75b01bcfb53e059289d14af9\n+\n+ss_2 sha1:2ac547ae9614a00d1b28275de608131f7a0e259f\n+ss_2 sha256:a90f02e6044f1497d13db587d22ab12f90150a7d1e084afcf96065fab35ae2bc\n+\n+tt_1 sha1:4ac13458899ab908ef3b1128fa378daefc88d356\n+tt_1 sha256:c53113c7dd5060e86b5b251428bd058f6726f66273c6a24bff1c61a04f498dd3\n+\n+tt_2 sha1:4c86f9a85fbc5e6804ee2e17a797538fbe785bca\n+tt_2 sha256:0775f2a296129a7cf2862b46bc0e88c14d593f2773a3e3fb1c5193db6f5a7e77\n+\n+tt_3 sha1:c4e4a12231b9fa79a0053cb6077fcb21bb5b135a\n+tt_3 sha256:47860f93cdd211f96443e0560f21c57ab6c2f4b0ac27ff03651a352e53fe8484\n+\n+z__1 sha1:7d670fdcdb9929f6c7dac196ff78689cd1c566a1\n+z__1 sha256:44d0f37aff5e51cfcfdd1134c93a6419bcca7b9964f792ffcd5f9b4fcba1ee63\n+\n+z__2 sha1:5e5f22072bb39f6e12cf663a57cb634c76eefb49\n+z__2 sha256:d29de162113190fed104eb5f010820cef4e315f89b9326e8497f7219fb737894\n+\n+z__3 sha1:1ba523955d5160681af65cb776411f574c1e8155\n+z__3 sha256:07422d772b07794ab4369a5648e617719f89c2d2212cbeab05d97214b6471636\n+\n+zaa1 sha1:8acb8e9750e3f644bf323fcf3d338849db106c77\n+zaa1 sha256:e79b029282c8abec2d9f3f7faceaf2a1405e02d1f368e66450ae66cf5b68d1f4\n+\n+zaa2 sha1:6c0b99286d0bce551ac4a7b3dff8b706edff3715\n+zaa2 sha256:c82bd78c3e69ea1796e6b1a7a3ba45bb106c50e819296475b862123d3f5cc5a0\n+\n+zan1 sha1:087494262084cefee7ed484d20c8dc0580791272\n+zan1 sha256:4b159eb3804d05599023dd074f771d06d02870f4ab24a7165add8ac3d703b8d3\n+\n+zdd1 sha1:879007efae624d2b1307214b24a956f0a8d686a8\n+zdd1 sha256:eecfdd4d8092dd0363fb6d4548b54c6afc8982c3ed9b34e393f1d6a921d8eaa3\n+\n+zdm1 sha1:9b541b2275c06e3a7b13f28badf5294e2ae63df4\n+zdm1 sha256:ab136e88e19a843c4bf7713d2090d5a2186ba16a6a80dacc12eeddd256a8e556\n+\n+zdm2 sha1:d77371d15817fcaa57eeec27f770c505ba974ec1\n+zdm2 sha256:1c1a5f57363f46a15d95ce8527b3c2c158d88d16853b4acbf81bd20fd2c89a46\n+\n+zdn1 sha1:beb5d38c55283d280685ea21a0e50cfcc0ca064a\n+zdn1 sha256:0f0eca66183617b0aa5ad74b256540329f841470922ca6760263c996d825eb18\n+\n+zmd1 sha1:d41fda41b7ec4de46b43cb7ea42a45001ae393d5\n+zmd1 sha256:1ed32d481852eddf31a0ce12652a0ad14bf5b7a842667b5dbb0b50f35bf1c80a\n+\n+zmd2 sha1:a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9\n+zmd2 sha256:b238da211b404f8917df2d9c6f7030535e904b2186131007a3c292ec6902f933\n+\n+zmm1 sha1:4ca22bae2527d3d9e1676498a0fba3b355bd1278\n+zmm1 sha256:072b1d85b5f34fabc99dfa46008c5418df68302d3e317430006f49b32d244226\n+\n+zmm2 sha1:61422ba9c2c873416061a88cd40a59a35b576474\n+zmm2 sha256:81dd5d2b3c5cda16fef552256aed4e2ea0802a8450a08f308a92142112ff6dda\n+\n+zmm3 sha1:697aad7715a1e7306ca76290a3dd4208fbaeddfa\n+zmm3 sha256:8b10fab49e9be3414aa5e9a93d0e46f9569053440138a7c19a5eb5536d8e95bf\n+\n+zmn1 sha1:b16d7b25b869f2beb124efa53467d8a1550ad694\n+zmn1 sha256:609e4f75d1295e844c826feeba213acb0b6cfc609adfe8ff705b19e3829ae3e9\n+\n+zmn2 sha1:a5c544c21cfcb07eb80a4d89a5b7d1570002edfd\n+zmn2 sha256:d6d03edf2dc1a3b267a8205de5f41a2ff4b03def8c7ae02052b543fb09d589fc\n+\n+zna1 sha1:d12979c22fff69c59ca9409e7a8fe3ee25eaee80\n+zna1 sha256:b37b80e789e8ea32aa323f004628f02013f632124b0282c7fe00a127d3c64c3c\n+\n+znd1 sha1:a18393c636b98e9bd7296b8b437ea4992b72440c\n+znd1 sha256:af92a22eee8c38410a0c9d2b5135a10aeb052cbc7cf675541ed9a67bfcaf7cf9\n+\n+znm1 sha1:3fdbe17fd013303a2e981e1ca1c6cd6e72789087\n+znm1 sha256:f75aeaa0c11e76918e381c105f0752932c6150e941fec565d24fa31098a13dc1\n+\n+znm2 sha1:7e09d6a3a14bd630913e8c75693cea32157b606d\n+znm2 sha256:938d73cfbaa1c902a84fb5b3afd9736aa0590367fb9bd59c6c4d072ce70fcd6d\n+EOF\n+\n+cat >.test-plain-OA <<EOF\n+:000000 100644 $(test_oid zero) $(test_oid aa_1) A\tAA\n+:000000 100644 $(test_oid zero) $(test_oid an_1) A\tAN\n+:100644 000000 $(test_oid dd_1) $(test_oid zero) D\tDD\n+:000000 040000 $(test_oid zero) $(test_oid df_1) A\tDF\n+:100644 000000 $(test_oid dm_1) $(test_oid zero) D\tDM\n+:100644 000000 $(test_oid dn_1) $(test_oid zero) D\tDN\n+:000000 100644 $(test_oid zero) $(test_oid ll_2) A\tLL\n+:100644 100644 $(test_oid md_1) $(test_oid md_2) M\tMD\n+:100644 100644 $(test_oid mm_1) $(test_oid mm_2) M\tMM\n+:100644 100644 $(test_oid mn_1) $(test_oid mn_2) M\tMN\n+:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M\tSS\n+:100644 100644 $(test_oid tt_1) $(test_oid tt_2) M\tTT\n+:040000 040000 $(test_oid z__1) $(test_oid z__2) M\tZ\n EOF\n \n-cat >.test-recursive-OA <<\\EOF\n-:000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A\tAA\n-:000000 100644 0000000000000000000000000000000000000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 A\tAN\n-:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D\tDD\n-:000000 100644 0000000000000000000000000000000000000000 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 A\tDF/DF\n-:100644 000000 141c1f1642328e4bc46a7d801a71da392e66791e 0000000000000000000000000000000000000000 D\tDM\n-:100644 000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 0000000000000000000000000000000000000000 D\tDN\n-:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A\tLL\n-:100644 100644 03f24c8c4700babccfd28b654e7e8eac402ad6cd 103d9f89b50b9aad03054b579be5e7aa665f2d57 M\tMD\n-:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 M\tMM\n-:100644 100644 bd084b0c27c7b6cc34f11d6d0509a29be3caf970 a716d58de4a570e0038f5c307bd8db34daea021f M\tMN\n-:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M\tSS\n-:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 4c86f9a85fbc5e6804ee2e17a797538fbe785bca M\tTT\n-:000000 100644 0000000000000000000000000000000000000000 8acb8e9750e3f644bf323fcf3d338849db106c77 A\tZ/AA\n-:000000 100644 0000000000000000000000000000000000000000 087494262084cefee7ed484d20c8dc0580791272 A\tZ/AN\n-:100644 000000 879007efae624d2b1307214b24a956f0a8d686a8 0000000000000000000000000000000000000000 D\tZ/DD\n-:100644 000000 9b541b2275c06e3a7b13f28badf5294e2ae63df4 0000000000000000000000000000000000000000 D\tZ/DM\n-:100644 000000 beb5d38c55283d280685ea21a0e50cfcc0ca064a 0000000000000000000000000000000000000000 D\tZ/DN\n-:100644 100644 d41fda41b7ec4de46b43cb7ea42a45001ae393d5 a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 M\tZ/MD\n-:100644 100644 4ca22bae2527d3d9e1676498a0fba3b355bd1278 61422ba9c2c873416061a88cd40a59a35b576474 M\tZ/MM\n-:100644 100644 b16d7b25b869f2beb124efa53467d8a1550ad694 a5c544c21cfcb07eb80a4d89a5b7d1570002edfd M\tZ/MN\n+cat >.test-recursive-OA <<EOF\n+:000000 100644 $(test_oid zero) $(test_oid aa_1) A\tAA\n+:000000 100644 $(test_oid zero) $(test_oid an_1) A\tAN\n+:100644 000000 $(test_oid dd_1) $(test_oid zero) D\tDD\n+:000000 100644 $(test_oid zero) $(test_oid dfd1) A\tDF/DF\n+:100644 000000 $(test_oid dm_1) $(test_oid zero) D\tDM\n+:100644 000000 $(test_oid dn_1) $(test_oid zero) D\tDN\n+:000000 100644 $(test_oid zero) $(test_oid ll_2) A\tLL\n+:100644 100644 $(test_oid md_1) $(test_oid md_2) M\tMD\n+:100644 100644 $(test_oid mm_1) $(test_oid mm_2) M\tMM\n+:100644 100644 $(test_oid mn_1) $(test_oid mn_2) M\tMN\n+:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M\tSS\n+:100644 100644 $(test_oid tt_1) $(test_oid tt_2) M\tTT\n+:000000 100644 $(test_oid zero) $(test_oid zaa1) A\tZ/AA\n+:000000 100644 $(test_oid zero) $(test_oid zan1) A\tZ/AN\n+:100644 000000 $(test_oid zdd1) $(test_oid zero) D\tZ/DD\n+:100644 000000 $(test_oid zdm1) $(test_oid zero) D\tZ/DM\n+:100644 000000 $(test_oid zdn1) $(test_oid zero) D\tZ/DN\n+:100644 100644 $(test_oid zmd1) $(test_oid zmd2) M\tZ/MD\n+:100644 100644 $(test_oid zmm1) $(test_oid zmm2) M\tZ/MM\n+:100644 100644 $(test_oid zmn1) $(test_oid zmn2) M\tZ/MN\n EOF\n-cat >.test-plain-OB <<\\EOF\n-:000000 100644 0000000000000000000000000000000000000000 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 A\tAA\n-:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D\tDD\n-:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A\tDF\n-:100644 100644 141c1f1642328e4bc46a7d801a71da392e66791e 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 M\tDM\n-:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A\tLL\n-:100644 000000 03f24c8c4700babccfd28b654e7e8eac402ad6cd 0000000000000000000000000000000000000000 D\tMD\n-:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 19989d4559aae417fedee240ccf2ba315ea4dc2b M\tMM\n-:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A\tNA\n-:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D\tND\n-:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M\tNM\n-:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M\tSS\n-:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M\tTT\n-:040000 040000 7d670fdcdb9929f6c7dac196ff78689cd1c566a1 1ba523955d5160681af65cb776411f574c1e8155 M\tZ\n+cat >.test-plain-OB <<EOF\n+:000000 100644 $(test_oid zero) $(test_oid aa_2) A\tAA\n+:100644 000000 $(test_oid dd_1) $(test_oid zero) D\tDD\n+:000000 100644 $(test_oid zero) $(test_oid df_2) A\tDF\n+:100644 100644 $(test_oid dm_1) $(test_oid dm_2) M\tDM\n+:000000 100644 $(test_oid zero) $(test_oid ll_2) A\tLL\n+:100644 000000 $(test_oid md_1) $(test_oid zero) D\tMD\n+:100644 100644 $(test_oid mm_1) $(test_oid mm_3) M\tMM\n+:000000 100644 $(test_oid zero) $(test_oid na_1) A\tNA\n+:100644 000000 $(test_oid nd_1) $(test_oid zero) D\tND\n+:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M\tNM\n+:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M\tSS\n+:100644 100644 $(test_oid tt_1) $(test_oid tt_3) M\tTT\n+:040000 040000 $(test_oid z__1) $(test_oid z__3) M\tZ\n EOF\n-cat >.test-recursive-OB <<\\EOF\n-:000000 100644 0000000000000000000000000000000000000000 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 A\tAA\n-:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D\tDD\n-:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A\tDF\n-:100644 100644 141c1f1642328e4bc46a7d801a71da392e66791e 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 M\tDM\n-:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A\tLL\n-:100644 000000 03f24c8c4700babccfd28b654e7e8eac402ad6cd 0000000000000000000000000000000000000000 D\tMD\n-:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 19989d4559aae417fedee240ccf2ba315ea4dc2b M\tMM\n-:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A\tNA\n-:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D\tND\n-:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M\tNM\n-:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M\tSS\n-:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M\tTT\n-:000000 100644 0000000000000000000000000000000000000000 6c0b99286d0bce551ac4a7b3dff8b706edff3715 A\tZ/AA\n-:100644 000000 879007efae624d2b1307214b24a956f0a8d686a8 0000000000000000000000000000000000000000 D\tZ/DD\n-:100644 100644 9b541b2275c06e3a7b13f28badf5294e2ae63df4 d77371d15817fcaa57eeec27f770c505ba974ec1 M\tZ/DM\n-:100644 000000 d41fda41b7ec4de46b43cb7ea42a45001ae393d5 0000000000000000000000000000000000000000 D\tZ/MD\n-:100644 100644 4ca22bae2527d3d9e1676498a0fba3b355bd1278 697aad7715a1e7306ca76290a3dd4208fbaeddfa M\tZ/MM\n-:000000 100644 0000000000000000000000000000000000000000 d12979c22fff69c59ca9409e7a8fe3ee25eaee80 A\tZ/NA\n-:100644 000000 a18393c636b98e9bd7296b8b437ea4992b72440c 0000000000000000000000000000000000000000 D\tZ/ND\n-:100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d M\tZ/NM\n+cat >.test-recursive-OB <<EOF\n+:000000 100644 $(test_oid zero) $(test_oid aa_2) A\tAA\n+:100644 000000 $(test_oid dd_1) $(test_oid zero) D\tDD\n+:000000 100644 $(test_oid zero) $(test_oid df_2) A\tDF\n+:100644 100644 $(test_oid dm_1) $(test_oid dm_2) M\tDM\n+:000000 100644 $(test_oid zero) $(test_oid ll_2) A\tLL\n+:100644 000000 $(test_oid md_1) $(test_oid zero) D\tMD\n+:100644 100644 $(test_oid mm_1) $(test_oid mm_3) M\tMM\n+:000000 100644 $(test_oid zero) $(test_oid na_1) A\tNA\n+:100644 000000 $(test_oid nd_1) $(test_oid zero) D\tND\n+:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M\tNM\n+:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M\tSS\n+:100644 100644 $(test_oid tt_1) $(test_oid tt_3) M\tTT\n+:000000 100644 $(test_oid zero) $(test_oid zaa2) A\tZ/AA\n+:100644 000000 $(test_oid zdd1) $(test_oid zero) D\tZ/DD\n+:100644 100644 $(test_oid zdm1) $(test_oid zdm2) M\tZ/DM\n+:100644 000000 $(test_oid zmd1) $(test_oid zero) D\tZ/MD\n+:100644 100644 $(test_oid zmm1) $(test_oid zmm3) M\tZ/MM\n+:000000 100644 $(test_oid zero) $(test_oid zna1) A\tZ/NA\n+:100644 000000 $(test_oid znd1) $(test_oid zero) D\tZ/ND\n+:100644 100644 $(test_oid znm1) $(test_oid znm2) M\tZ/NM\n EOF\n-cat >.test-plain-AB <<\\EOF\n-:100644 100644 ccba72ad3888a3520b39efcf780b9ee64167535d 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 M\tAA\n-:100644 000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 0000000000000000000000000000000000000000 D\tAN\n-:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A\tDF\n-:040000 000000 6d50f65d3bdab91c63444294d38f08aeff328e42 0000000000000000000000000000000000000000 D\tDF\n-:000000 100644 0000000000000000000000000000000000000000 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 A\tDM\n-:000000 100644 0000000000000000000000000000000000000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 A\tDN\n-:100644 000000 103d9f89b50b9aad03054b579be5e7aa665f2d57 0000000000000000000000000000000000000000 D\tMD\n-:100644 100644 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 19989d4559aae417fedee240ccf2ba315ea4dc2b M\tMM\n-:100644 100644 a716d58de4a570e0038f5c307bd8db34daea021f bd084b0c27c7b6cc34f11d6d0509a29be3caf970 M\tMN\n-:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A\tNA\n-:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D\tND\n-:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M\tNM\n-:100644 100644 4c86f9a85fbc5e6804ee2e17a797538fbe785bca c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M\tTT\n-:040000 040000 5e5f22072bb39f6e12cf663a57cb634c76eefb49 1ba523955d5160681af65cb776411f574c1e8155 M\tZ\n+cat >.test-plain-AB <<EOF\n+:100644 100644 $(test_oid aa_1) $(test_oid aa_2) M\tAA\n+:100644 000000 $(test_oid an_1) $(test_oid zero) D\tAN\n+:000000 100644 $(test_oid zero) $(test_oid df_2) A\tDF\n+:040000 000000 $(test_oid df_1) $(test_oid zero) D\tDF\n+:000000 100644 $(test_oid zero) $(test_oid dm_2) A\tDM\n+:000000 100644 $(test_oid zero) $(test_oid dn_1) A\tDN\n+:100644 000000 $(test_oid md_2) $(test_oid zero) D\tMD\n+:100644 100644 $(test_oid mm_2) $(test_oid mm_3) M\tMM\n+:100644 100644 $(test_oid mn_2) $(test_oid mn_1) M\tMN\n+:000000 100644 $(test_oid zero) $(test_oid na_1) A\tNA\n+:100644 000000 $(test_oid nd_1) $(test_oid zero) D\tND\n+:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M\tNM\n+:100644 100644 $(test_oid tt_2) $(test_oid tt_3) M\tTT\n+:040000 040000 $(test_oid z__2) $(test_oid z__3) M\tZ\n EOF\n-cat >.test-recursive-AB <<\\EOF\n-:100644 100644 ccba72ad3888a3520b39efcf780b9ee64167535d 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 M\tAA\n-:100644 000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 0000000000000000000000000000000000000000 D\tAN\n-:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A\tDF\n-:100644 000000 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 0000000000000000000000000000000000000000 D\tDF/DF\n-:000000 100644 0000000000000000000000000000000000000000 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 A\tDM\n-:000000 100644 0000000000000000000000000000000000000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 A\tDN\n-:100644 000000 103d9f89b50b9aad03054b579be5e7aa665f2d57 0000000000000000000000000000000000000000 D\tMD\n-:100644 100644 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 19989d4559aae417fedee240ccf2ba315ea4dc2b M\tMM\n-:100644 100644 a716d58de4a570e0038f5c307bd8db34daea021f bd084b0c27c7b6cc34f11d6d0509a29be3caf970 M\tMN\n-:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A\tNA\n-:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D\tND\n-:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M\tNM\n-:100644 100644 4c86f9a85fbc5e6804ee2e17a797538fbe785bca c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M\tTT\n-:100644 100644 8acb8e9750e3f644bf323fcf3d338849db106c77 6c0b99286d0bce551ac4a7b3dff8b706edff3715 M\tZ/AA\n-:100644 000000 087494262084cefee7ed484d20c8dc0580791272 0000000000000000000000000000000000000000 D\tZ/AN\n-:000000 100644 0000000000000000000000000000000000000000 d77371d15817fcaa57eeec27f770c505ba974ec1 A\tZ/DM\n-:000000 100644 0000000000000000000000000000000000000000 beb5d38c55283d280685ea21a0e50cfcc0ca064a A\tZ/DN\n-:100644 000000 a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 0000000000000000000000000000000000000000 D\tZ/MD\n-:100644 100644 61422ba9c2c873416061a88cd40a59a35b576474 697aad7715a1e7306ca76290a3dd4208fbaeddfa M\tZ/MM\n-:100644 100644 a5c544c21cfcb07eb80a4d89a5b7d1570002edfd b16d7b25b869f2beb124efa53467d8a1550ad694 M\tZ/MN\n-:000000 100644 0000000000000000000000000000000000000000 d12979c22fff69c59ca9409e7a8fe3ee25eaee80 A\tZ/NA\n-:100644 000000 a18393c636b98e9bd7296b8b437ea4992b72440c 0000000000000000000000000000000000000000 D\tZ/ND\n-:100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d M\tZ/NM\n+cat >.test-recursive-AB <<EOF\n+:100644 100644 $(test_oid aa_1) $(test_oid aa_2) M\tAA\n+:100644 000000 $(test_oid an_1) $(test_oid zero) D\tAN\n+:000000 100644 $(test_oid zero) $(test_oid df_2) A\tDF\n+:100644 000000 $(test_oid dfd1) $(test_oid zero) D\tDF/DF\n+:000000 100644 $(test_oid zero) $(test_oid dm_2) A\tDM\n+:000000 100644 $(test_oid zero) $(test_oid dn_1) A\tDN\n+:100644 000000 $(test_oid md_2) $(test_oid zero) D\tMD\n+:100644 100644 $(test_oid mm_2) $(test_oid mm_3) M\tMM\n+:100644 100644 $(test_oid mn_2) $(test_oid mn_1) M\tMN\n+:000000 100644 $(test_oid zero) $(test_oid na_1) A\tNA\n+:100644 000000 $(test_oid nd_1) $(test_oid zero) D\tND\n+:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M\tNM\n+:100644 100644 $(test_oid tt_2) $(test_oid tt_3) M\tTT\n+:100644 100644 $(test_oid zaa1) $(test_oid zaa2) M\tZ/AA\n+:100644 000000 $(test_oid zan1) $(test_oid zero) D\tZ/AN\n+:000000 100644 $(test_oid zero) $(test_oid zdm2) A\tZ/DM\n+:000000 100644 $(test_oid zero) $(test_oid zdn1) A\tZ/DN\n+:100644 000000 $(test_oid zmd2) $(test_oid zero) D\tZ/MD\n+:100644 100644 $(test_oid zmm2) $(test_oid zmm3) M\tZ/MM\n+:100644 100644 $(test_oid zmn2) $(test_oid zmn1) M\tZ/MN\n+:000000 100644 $(test_oid zero) $(test_oid zna1) A\tZ/NA\n+:100644 000000 $(test_oid znd1) $(test_oid zero) D\tZ/ND\n+:100644 100644 $(test_oid znm1) $(test_oid znm2) M\tZ/NM\n EOF\n \n cmp_diff_files_output () {\n"},{"id":"380648","messageId":"20190818191646.868106-15-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH 14/14] t4009: make hash size independent","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:46Z","receivedAt":"2019-08-18T19:21:11Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Instead of hard-coding object IDs, compute them and use those in the\ncomparison.  Note that the comparison code ignores the actual object\nIDs, but does check that they're the right size, so computing them is\nthe easiest way to ensure that they are.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t4009-diff-rename-4.sh | 19 +++++++++++--------\n 1 file changed, 11 insertions(+), 8 deletions(-)\n\ndiff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh\nindex 3641fd84d6..b63bdf031f 100755\n--- a/t/t4009-diff-rename-4.sh\n+++ b/t/t4009-diff-rename-4.sh\n@@ -14,6 +14,7 @@ test_expect_success \\\n     'cat \"$TEST_DIRECTORY\"/diff-lib/COPYING >COPYING &&\n      echo frotz >rezrov &&\n     git update-index --add COPYING rezrov &&\n+    orig=$(git hash-object COPYING) &&\n     tree=$(git write-tree) &&\n     echo $tree'\n \n@@ -22,6 +23,8 @@ test_expect_success \\\n     'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&\n     sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&\n     rm -f COPYING &&\n+    c1=$(git hash-object COPYING.1) &&\n+    c2=$(git hash-object COPYING.2) &&\n     git update-index --add --remove COPYING COPYING.?'\n \n # tree has COPYING and rezrov.  work tree has COPYING.1 and COPYING.2,\n@@ -31,11 +34,11 @@ test_expect_success \\\n \n git diff-index -z -C $tree >current\n \n-cat >expected <<\\EOF\n-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234\n+cat >expected <<EOF\n+:100644 100644 $orig $c1 C1234\n COPYING\n COPYING.1\n-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 R1234\n+:100644 100644 $orig $c2 R1234\n COPYING\n COPYING.2\n EOF\n@@ -57,10 +60,10 @@ test_expect_success \\\n # about rezrov.\n \n git diff-index -z -C $tree >current\n-cat >expected <<\\EOF\n-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 M\n+cat >expected <<EOF\n+:100644 100644 $orig $c2 M\n COPYING\n-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234\n+:100644 100644 $orig $c1 C1234\n COPYING\n COPYING.1\n EOF\n@@ -82,8 +85,8 @@ test_expect_success \\\n      git update-index --add --remove COPYING COPYING.1'\n \n git diff-index -z -C --find-copies-harder $tree >current\n-cat >expected <<\\EOF\n-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234\n+cat >expected <<EOF\n+:100644 100644 $orig $c1 C1234\n COPYING\n COPYING.1\n EOF\n"},{"id":"380649","messageId":"20190818191646.868106-10-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH 09/14] t3600: make hash size independent","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T19:16:41Z","receivedAt":"2019-08-18T19:21:12Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Instead of hard-coding a fixed length invalid object ID in the test,\ncompute one using the lookup tables.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3600-rm.sh | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\nindex 66282a720e..8c8cca5bfb 100755\n--- a/t/t3600-rm.sh\n+++ b/t/t3600-rm.sh\n@@ -240,12 +240,14 @@ test_expect_success 'refresh index before checking if it is up-to-date' '\n '\n \n test_expect_success 'choking \"git rm\" should not let it die with cruft' '\n+\ttest_oid_init &&\n \tgit reset -q --hard &&\n \ttest_when_finished \"rm -f .git/index.lock && git reset -q --hard\" &&\n \ti=0 &&\n+\thash=$(test_oid deadbeef) &&\n \twhile test $i -lt 12000\n \tdo\n-\t\techo \"100644 1234567890123456789012345678901234567890 0\tsome-file-$i\"\n+\t\techo \"100644 $hash 0\tsome-file-$i\"\n \t\ti=$(( $i + 1 ))\n \tdone | git update-index --index-info &&\n \tgit rm -n \"some-file-*\" | : &&\n"},{"id":"380678","messageId":"CAPig+cT6Depk8sJ48zOp6+AObVmy3_eXnXPEsZJYXY=daZqXXQ@mail.gmail.com","threadId":"51667","inReplyTo":"20190818191646.868106-12-sandals@crustytoothpaste.net","subject":"Re: [PATCH 11/14] t3903: abstract away SHA-1-specific constants","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-08-18T20:27:41Z","receivedAt":"2019-08-18T20:27:55Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Aug 18, 2019 at 3:21 PM brian m. carlson\n<sandals@crustytoothpaste.net> wrote:\n> Abstract away the SHA-1-specific constants by sanitizing diff output to\n> remove the index lines, since it's clear from the assertions in question\n> that we are not interested in the specific object IDs.\n>\n> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> ---\n> diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\n> @@ -7,6 +7,18 @@ test_description='Test git stash'\n> +diff_cmp () {\n> +       for i in \"$1\" \"$2\"\n> +       do\n> +               sed -e 's/^index 0000000\\.\\.[0-9a-f]*/index 0000000..1234567/' \\\n> +               -e 's/^index [0-9a-f]*\\.\\.[0-9a-f]*/index 1234567..89abcde/' \\\n> +               -e 's/^index [0-9a-f]*,[0-9a-f]*\\.\\.[0-9a-f]*/index 1234567,7654321..89abcde/' \\\n> +               \"$i\" > \"$i.compare\"\n> +       done &&\n> +       test_cmp \"$1.compare\" \"$2.compare\" &&\n> +       rm -f \"$1.compare\" \"$2.compare\"\n> +}\n\nFor safety, it would probably be a good idea to check the exit status\nof the 'sed' invocation in the for-loop:\n\n    for i in \"$1\" \"$2\"\n    do\n        sed -e ... \"$i\" >\"$i.compare\" || return 1\n    done &&\n    ...\n\n(Note, also, that I dropped the whitespace after the '>' operator.)\n"},{"id":"380679","messageId":"CAPig+cQkcyfWui_4jchtQ_=+_u-+N5uas7zGKdHB1=1mZCh39g@mail.gmail.com","threadId":"51667","inReplyTo":"20190818191646.868106-14-sandals@crustytoothpaste.net","subject":"Re: [PATCH 13/14] t4002: make hash independent","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-08-18T20:29:18Z","receivedAt":"2019-08-18T20:29:31Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Aug 18, 2019 at 3:21 PM brian m. carlson\n<sandals@crustytoothpaste.net> wrote:\n> Refactor out the hard-coded object IDs and use test_oid to provide\n> values for both SHA-1 and SHA-256.\n\ns/Refactor out/Factor out/\n\n> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n"},{"id":"380680","messageId":"20190818203417.GG365197@genre.crustytoothpaste.net","threadId":"51667","inReplyTo":"CAPig+cT6Depk8sJ48zOp6+AObVmy3_eXnXPEsZJYXY=daZqXXQ@mail.gmail.com","subject":"Re: [PATCH 11/14] t3903: abstract away SHA-1-specific constants","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-18T20:34:18Z","receivedAt":"2019-08-18T20:34:26Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2019-08-18 at 20:27:41, Eric Sunshine wrote:\n> For safety, it would probably be a good idea to check the exit status\n> of the 'sed' invocation in the for-loop:\n> \n>     for i in \"$1\" \"$2\"\n>     do\n>         sed -e ... \"$i\" >\"$i.compare\" || return 1\n>     done &&\n>     ...\n\nI'll do that.\n\n> (Note, also, that I dropped the whitespace after the '>' operator.)\n\nAh, good catch.\n-- \nbrian m. carlson: Houston, Texas, US\nOpenPGP: https://keybase.io/bk2204\n"},{"id":"381136","messageId":"20190826014344.16008-1-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190818191646.868106-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 00/14] Hash-independent tests, part 5","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:30Z","receivedAt":"2019-08-26T01:44:03Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"This is the fifth series of test fixes for SHA-256 compatibility.  It\nconsists of 15 patches fixing various tests from t3201 through t4009 and\nhas only test fixes: no test helper changes are included.\n\nChanges from v1:\n* Add sanity-checking to the sed invocation.\n* Remove whitespace between redirection operator and file name.\n* Change \"Refactor out\" to \"Factor out\".\n\nbrian m. carlson (14):\n  t3201: abstract away SHA-1-specific constants\n  t3206: abstract away hash size constants\n  t3301: abstract away SHA-1-specific constants\n  t3305: make hash size independent\n  t3306: abstract away SHA-1-specific constants\n  t3404: abstract away SHA-1-specific constants\n  t3430: avoid hard-coded object IDs\n  t3506: make hash independent\n  t3600: make hash size independent\n  t3800: make hash-size independent\n  t3903: abstract away SHA-1-specific constants\n  t4000: make hash size independent\n  t4002: make hash independent\n  t4009: make hash size independent\n\n t/t3201-branch-contains.sh    |   8 +-\n t/t3206-range-diff.sh         | 227 +++++++++++++++------\n t/t3301-notes.sh              | 140 ++++++++-----\n t/t3305-notes-fanout.sh       |  22 +-\n t/t3306-notes-prune.sh        |  45 ++---\n t/t3404-rebase-interactive.sh |  22 +-\n t/t3430-rebase-merges.sh      |  23 ++-\n t/t3506-cherry-pick-ff.sh     |   8 +-\n t/t3600-rm.sh                 |   4 +-\n t/t3800-mktag.sh              |  49 ++---\n t/t3903-stash.sh              |  32 ++-\n t/t4000-diff-format.sh        |   2 +-\n t/t4002-diff-basic.sh         | 367 ++++++++++++++++++++++++----------\n t/t4009-diff-rename-4.sh      |  19 +-\n 14 files changed, 640 insertions(+), 328 deletions(-)\n\nRange-diff against v1:\n1:  bfc39503dc ! 1:  1a0bda61ca t3903: abstract away SHA-1-specific constants\n    @@ t/t3903-stash.sh: test_description='Test git stash'\n     +\t\tsed -e 's/^index 0000000\\.\\.[0-9a-f]*/index 0000000..1234567/' \\\n     +\t\t-e 's/^index [0-9a-f]*\\.\\.[0-9a-f]*/index 1234567..89abcde/' \\\n     +\t\t-e 's/^index [0-9a-f]*,[0-9a-f]*\\.\\.[0-9a-f]*/index 1234567,7654321..89abcde/' \\\n    -+\t\t\"$i\" > \"$i.compare\"\n    ++\t\t\"$i\" >\"$i.compare\" || return 1\n     +\tdone &&\n     +\ttest_cmp \"$1.compare\" \"$2.compare\" &&\n     +\trm -f \"$1.compare\" \"$2.compare\"\n2:  2da09f10f4 = 2:  64b2fc645b t4000: make hash size independent\n3:  239d6de517 ! 3:  a8f0255ea0 t4002: make hash independent\n    @@ Metadata\n      ## Commit message ##\n         t4002: make hash independent\n     \n    -    Refactor out the hard-coded object IDs and use test_oid to provide\n    -    values for both SHA-1 and SHA-256.\n    +    Factor out the hard-coded object IDs and use test_oid to provide values\n    +    for both SHA-1 and SHA-256.\n     \n         Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n     \n4:  7cb314ec8a = 4:  837d185fa6 t4009: make hash size independent\n"},{"id":"381137","messageId":"20190826014344.16008-2-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190826014344.16008-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 01/14] t3201: abstract away SHA-1-specific constants","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:31Z","receivedAt":"2019-08-26T01:44:03Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Adjust the test so that it computes variables for object IDs instead of\nusing hard-coded hashes.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3201-branch-contains.sh | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh\nindex 0ea4fc4694..40251c9f8f 100755\n--- a/t/t3201-branch-contains.sh\n+++ b/t/t3201-branch-contains.sh\n@@ -192,10 +192,10 @@ test_expect_success 'branch --merged with --verbose' '\n \tEOF\n \ttest_cmp expect actual &&\n \tgit branch --verbose --merged topic >actual &&\n-\tcat >expect <<-\\EOF &&\n-\t  master c77a0a9 second on master\n-\t* topic  2c939f4 [ahead 1] foo\n-\t  zzz    c77a0a9 second on master\n+\tcat >expect <<-EOF &&\n+\t  master $(git rev-parse --short master) second on master\n+\t* topic  $(git rev-parse --short topic ) [ahead 1] foo\n+\t  zzz    $(git rev-parse --short zzz   ) second on master\n \tEOF\n \ttest_i18ncmp expect actual\n '\n"},{"id":"381138","messageId":"20190826014344.16008-5-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190826014344.16008-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 04/14] t3305: make hash size independent","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:34Z","receivedAt":"2019-08-26T01:44:07Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Instead of hard-coding 40-character shell patterns, use grep to\ndetermine if all of the paths have either zero or one levels of fanout,\nas appropriate.\n\nNote that the final test is implicitly dependent on the hash algorithm.\nDepending on the algorithm in use, the fanout may or may not completely\ncompress.  In its current state, this is not a problem, but it could be\nif the hash algorithm changes again.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3305-notes-fanout.sh | 22 ++++++----------------\n 1 file changed, 6 insertions(+), 16 deletions(-)\n\ndiff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh\nindex 54460beec4..831f83d211 100755\n--- a/t/t3305-notes-fanout.sh\n+++ b/t/t3305-notes-fanout.sh\n@@ -35,15 +35,10 @@ test_expect_success 'many notes created with git-notes triggers fanout' '\n \tgit ls-tree -r --name-only refs/notes/commits |\n \twhile read path\n \tdo\n-\t\tcase \"$path\" in\n-\t\t??/??????????????????????????????????????)\n-\t\t\t: true\n-\t\t\t;;\n-\t\t*)\n+\t\techo $path | grep \"^../[0-9a-f]*$\" || {\n \t\t\techo \"Invalid path \\\"$path\\\"\" &&\n-\t\t\treturn 1\n-\t\t\t;;\n-\t\tesac\n+\t\t\treturn 1;\n+\t\t}\n \tdone\n '\n \n@@ -77,15 +72,10 @@ test_expect_success 'deleting most notes triggers fanout consolidation' '\n \tgit ls-tree -r --name-only refs/notes/commits |\n \twhile read path\n \tdo\n-\t\tcase \"$path\" in\n-\t\t????????????????????????????????????????)\n-\t\t\t: true\n-\t\t\t;;\n-\t\t*)\n+\t\techo $path | grep -v \"^../.*\" || {\n \t\t\techo \"Invalid path \\\"$path\\\"\" &&\n-\t\t\treturn 1\n-\t\t\t;;\n-\t\tesac\n+\t\t\treturn 1;\n+\t\t}\n \tdone\n '\n \n"},{"id":"381139","messageId":"20190826014344.16008-4-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190826014344.16008-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 03/14] t3301: abstract away SHA-1-specific constants","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:33Z","receivedAt":"2019-08-26T01:44:08Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Adjust the test so that it computes variables for object IDs instead of\nusing hard-coded hashes.  Move some invocations of test_commit around so\nthat we can compute the object IDs for these commits.\n\nCompute several object IDs in the tests instead of using hard-coded\nvalues so that the test works with any hash algorithm.  Since the actual\nvalues are sorted by the object ID of the object being annotated, sort\nthe expected values accordingly as well.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3301-notes.sh | 140 ++++++++++++++++++++++++++++++-----------------\n 1 file changed, 90 insertions(+), 50 deletions(-)\n\ndiff --git a/t/t3301-notes.sh b/t/t3301-notes.sh\nindex 704bbc6541..d3fa298c6a 100755\n--- a/t/t3301-notes.sh\n+++ b/t/t3301-notes.sh\n@@ -66,8 +66,9 @@ test_expect_success 'show notes entry with %N' '\n '\n \n test_expect_success 'create reflog entry' '\n+\tref=$(git rev-parse --short refs/notes/commits) &&\n \tcat <<-EOF >expect &&\n-\t\ta1d8fa6 refs/notes/commits@{0}: notes: Notes added by '\\''git notes add'\\''\n+\t\t$ref refs/notes/commits@{0}: notes: Notes added by '\\''git notes add'\\''\n \tEOF\n \tgit reflog show refs/notes/commits >actual &&\n \ttest_cmp expect actual\n@@ -134,8 +135,9 @@ test_expect_success 'can overwrite existing note with \"git notes add -f\"' '\n '\n \n test_expect_success 'show notes' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 7a4ca6ee52a974a66cbaa78e33214535dff1d691\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:14:13 2005 -0700\n \n@@ -152,8 +154,9 @@ test_expect_success 'show notes' '\n test_expect_success 'show multi-line notes' '\n \ttest_commit 3rd &&\n \tMSG=\"b3${LF}c3c3c3c3${LF}d3d3d3\" git notes add &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect-multiline <<-EOF &&\n-\t\tcommit d07d62e5208f22eb5695e7eb47667dc8b9860290\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:15:13 2005 -0700\n \n@@ -174,8 +177,9 @@ test_expect_success 'show -F notes' '\n \ttest_commit 4th &&\n \techo \"xyzzy\" >note5 &&\n \tgit notes add -F note5 &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect-F <<-EOF &&\n-\t\tcommit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:16:13 2005 -0700\n \n@@ -198,10 +202,13 @@ test_expect_success 'Re-adding -F notes without -f fails' '\n '\n \n test_expect_success 'git log --pretty=raw does not show notes' '\n+\tcommit=$(git rev-parse HEAD) &&\n+\ttree=$(git rev-parse HEAD^{tree}) &&\n+\tparent=$(git rev-parse HEAD^) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11\n-\t\ttree 05ac65288c4c4b3b709a020ae94b2ece2f2201ae\n-\t\tparent d07d62e5208f22eb5695e7eb47667dc8b9860290\n+\t\tcommit $commit\n+\t\ttree $tree\n+\t\tparent $parent\n \t\tauthor A U Thor <author@example.com> 1112912173 -0700\n \t\tcommitter C O Mitter <committer@example.com> 1112912173 -0700\n \n@@ -291,8 +298,9 @@ test_expect_success 'git log --no-notes resets ref list' '\n test_expect_success 'show -m notes' '\n \ttest_commit 5th &&\n \tgit notes add -m spam -m \"foo${LF}bar${LF}baz\" &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect-m <<-EOF &&\n-\t\tcommit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:17:13 2005 -0700\n \n@@ -313,8 +321,9 @@ test_expect_success 'show -m notes' '\n \n test_expect_success 'remove note with add -f -F /dev/null' '\n \tgit notes add -f -F /dev/null &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect-rm-F <<-EOF &&\n-\t\tcommit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:17:13 2005 -0700\n \n@@ -356,14 +365,16 @@ test_expect_success 'create note with combination of -m and -F' '\n test_expect_success 'remove note with \"git notes remove\"' '\n \tgit notes remove HEAD^ &&\n \tgit notes remove &&\n+\tcommit=$(git rev-parse HEAD) &&\n+\tparent=$(git rev-parse HEAD^) &&\n \tcat >expect-rm-remove <<-EOF &&\n-\t\tcommit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:17:13 2005 -0700\n \n \t\t${indent}5th\n \n-\t\tcommit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11\n+\t\tcommit $parent\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:16:13 2005 -0700\n \n@@ -459,9 +470,11 @@ test_expect_success 'removing with --stdin --ignore-missing' '\n '\n \n test_expect_success 'list notes with \"git notes list\"' '\n-\tcat >expect <<-EOF &&\n-\t\tc9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 7a4ca6ee52a974a66cbaa78e33214535dff1d691\n-\t\tc18dc024e14f08d18d14eea0d747ff692d66d6a3 d07d62e5208f22eb5695e7eb47667dc8b9860290\n+\tcommit_2=$(git rev-parse 2nd) &&\n+\tcommit_3=$(git rev-parse 3rd) &&\n+\tsort -t\" \" -k2 >expect <<-EOF &&\n+\t\t$(git rev-parse refs/notes/commits:$commit_2) $commit_2\n+\t\t$(git rev-parse refs/notes/commits:$commit_3) $commit_3\n \tEOF\n \tgit notes list >actual &&\n \ttest_cmp expect actual\n@@ -474,7 +487,7 @@ test_expect_success 'list notes with \"git notes\"' '\n \n test_expect_success 'list specific note with \"git notes list <object>\"' '\n \tcat >expect <<-EOF &&\n-\t\tc18dc024e14f08d18d14eea0d747ff692d66d6a3\n+\t\t$(git rev-parse refs/notes/commits:$commit_3)\n \tEOF\n \tgit notes list HEAD^^ >actual &&\n \ttest_cmp expect actual\n@@ -498,10 +511,11 @@ test_expect_success 'append to existing note with \"git notes append\"' '\n '\n \n test_expect_success '\"git notes list\" does not expand to \"git notes list HEAD\"' '\n-\tcat >expect_list <<-EOF &&\n-\t\tc9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 7a4ca6ee52a974a66cbaa78e33214535dff1d691\n-\t\t4b6ad22357cc8a1296720574b8d2fbc22fab0671 7f9ad8836c775acb134c0a055fc55fb4cd1ba361\n-\t\tc18dc024e14f08d18d14eea0d747ff692d66d6a3 d07d62e5208f22eb5695e7eb47667dc8b9860290\n+\tcommit_5=$(git rev-parse 5th) &&\n+\tsort -t\" \" -k2 >expect_list <<-EOF &&\n+\t\t$(git rev-parse refs/notes/commits:$commit_2) $commit_2\n+\t\t$(git rev-parse refs/notes/commits:$commit_3) $commit_3\n+\t\t$(git rev-parse refs/notes/commits:$commit_5) $commit_5\n \tEOF\n \tgit notes list >actual &&\n \ttest_cmp expect_list actual\n@@ -531,8 +545,9 @@ test_expect_success 'appending empty string to non-existing note does not create\n test_expect_success 'create other note on a different notes ref (setup)' '\n \ttest_commit 6th &&\n \tGIT_NOTES_REF=\"refs/notes/other\" git notes add -m \"other note\" &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect-not-other <<-EOF &&\n-\t\tcommit 2c125331118caba0ff8238b7f4958ac6e93fe39c\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:18:13 2005 -0700\n \n@@ -569,8 +584,10 @@ test_expect_success 'Do not show note when core.notesRef is overridden' '\n '\n \n test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '\n+\tcommit=$(git rev-parse HEAD) &&\n+\tparent=$(git rev-parse HEAD^) &&\n \tcat >expect-both <<-EOF &&\n-\t\tcommit 2c125331118caba0ff8238b7f4958ac6e93fe39c\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:18:13 2005 -0700\n \n@@ -582,7 +599,7 @@ test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '\n \t\tNotes (other):\n \t\t${indent}other note\n \n-\t\tcommit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361\n+\t\tcommit $parent\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:17:13 2005 -0700\n \n@@ -616,8 +633,9 @@ test_expect_success 'notes.displayRef can be given more than once' '\n '\n \n test_expect_success 'notes.displayRef respects order' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect-both-reversed <<-EOF &&\n-\t\tcommit 2c125331118caba0ff8238b7f4958ac6e93fe39c\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:18:13 2005 -0700\n \n@@ -642,14 +660,16 @@ test_expect_success 'GIT_NOTES_DISPLAY_REF works' '\n '\n \n test_expect_success 'GIT_NOTES_DISPLAY_REF overrides config' '\n+\tcommit=$(git rev-parse HEAD) &&\n+\tparent=$(git rev-parse HEAD^) &&\n \tcat >expect-none <<-EOF &&\n-\t\tcommit 2c125331118caba0ff8238b7f4958ac6e93fe39c\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:18:13 2005 -0700\n \n \t\t${indent}6th\n \n-\t\tcommit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361\n+\t\tcommit $parent\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:17:13 2005 -0700\n \n@@ -666,8 +686,9 @@ test_expect_success '--show-notes=* adds to GIT_NOTES_DISPLAY_REF' '\n '\n \n test_expect_success '--no-standard-notes' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect-commits <<-EOF &&\n-\t\tcommit 2c125331118caba0ff8238b7f4958ac6e93fe39c\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:18:13 2005 -0700\n \n@@ -712,8 +733,10 @@ test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '\n '\n \n test_expect_success 'create note from other note with \"git notes add -C\"' '\n+\ttest_commit 7th &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit fb01e0ca8c33b6cc0c6451dde747f97df567cb5c\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:19:13 2005 -0700\n \n@@ -722,7 +745,6 @@ test_expect_success 'create note from other note with \"git notes add -C\"' '\n \t\tNotes:\n \t\t${indent}order test\n \tEOF\n-\ttest_commit 7th &&\n \tgit notes add -C $(git notes list HEAD^) &&\n \tgit log -1 >actual &&\n \ttest_cmp expect actual &&\n@@ -744,8 +766,9 @@ test_expect_success 'create note from non-blob with \"git notes add -C\" fails' '\n '\n \n test_expect_success 'create note from blob with \"git notes add -C\" reuses blob id' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 9a4c31c7f722b5d517e92c64e932dd751e1413bf\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:20:13 2005 -0700\n \n@@ -762,8 +785,10 @@ test_expect_success 'create note from blob with \"git notes add -C\" reuses blob i\n '\n \n test_expect_success 'create note from other note with \"git notes add -c\"' '\n+\ttest_commit 9th &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 2e0db4bc649e174d667a1cde19e725cf897a5bd2\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:21:13 2005 -0700\n \n@@ -772,7 +797,6 @@ test_expect_success 'create note from other note with \"git notes add -c\"' '\n \t\tNotes:\n \t\t${indent}yet another note\n \tEOF\n-\ttest_commit 9th &&\n \tMSG=\"yet another note\" git notes add -c $(git notes list HEAD^^) &&\n \tgit log -1 >actual &&\n \ttest_cmp expect actual\n@@ -785,8 +809,9 @@ test_expect_success 'create note from non-existing note with \"git notes add -c\"\n '\n \n test_expect_success 'append to note from other note with \"git notes append -C\"' '\n+\tcommit=$(git rev-parse HEAD^) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 2e0db4bc649e174d667a1cde19e725cf897a5bd2\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:21:13 2005 -0700\n \n@@ -803,8 +828,9 @@ test_expect_success 'append to note from other note with \"git notes append -C\"'\n '\n \n test_expect_success 'create note from other note with \"git notes append -c\"' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 7c3b87ab368f81e11b1ea87b2ab99a71ccd25406\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:22:13 2005 -0700\n \n@@ -819,8 +845,9 @@ test_expect_success 'create note from other note with \"git notes append -c\"' '\n '\n \n test_expect_success 'append to note from other note with \"git notes append -c\"' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 7c3b87ab368f81e11b1ea87b2ab99a71ccd25406\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:22:13 2005 -0700\n \n@@ -837,8 +864,10 @@ test_expect_success 'append to note from other note with \"git notes append -c\"'\n '\n \n test_expect_success 'copy note with \"git notes copy\"' '\n+\ttest_commit 11th &&\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit a446fff8777efdc6eb8f4b7c8a5ff699484df0d5\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:23:13 2005 -0700\n \n@@ -849,7 +878,6 @@ test_expect_success 'copy note with \"git notes copy\"' '\n \t\t${indent}\n \t\t${indent}yet another note\n \tEOF\n-\ttest_commit 11th &&\n \tgit notes copy HEAD^ HEAD &&\n \tgit log -1 >actual &&\n \ttest_cmp expect actual &&\n@@ -864,8 +892,9 @@ test_expect_success 'prevent overwrite with \"git notes copy\"' '\n '\n \n test_expect_success 'allow overwrite with \"git notes copy -f\"' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit a446fff8777efdc6eb8f4b7c8a5ff699484df0d5\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:23:13 2005 -0700\n \n@@ -889,8 +918,10 @@ test_expect_success 'cannot copy note from object without notes' '\n '\n \n test_expect_success 'git notes copy --stdin' '\n+\tcommit=$(git rev-parse HEAD) &&\n+\tparent=$(git rev-parse HEAD^) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit e871aa61182b1d95d0a6fb75445d891722863b6b\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:25:13 2005 -0700\n \n@@ -901,7 +932,7 @@ test_expect_success 'git notes copy --stdin' '\n \t\t${indent}\n \t\t${indent}yet another note\n \n-\t\tcommit 65e263ded02ae4e8839bc151095113737579dc12\n+\t\tcommit $parent\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:24:13 2005 -0700\n \n@@ -922,21 +953,23 @@ test_expect_success 'git notes copy --stdin' '\n '\n \n test_expect_success 'git notes copy --for-rewrite (unconfigured)' '\n+\ttest_commit 14th &&\n+\ttest_commit 15th &&\n+\tcommit=$(git rev-parse HEAD) &&\n+\tparent=$(git rev-parse HEAD^) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 4acf42e847e7fffbbf89ee365c20ac7caf40de89\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:27:13 2005 -0700\n \n \t\t${indent}15th\n \n-\t\tcommit 07c85d77059393ed0154b8c96906547a59dfcddd\n+\t\tcommit $parent\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:26:13 2005 -0700\n \n \t\t${indent}14th\n \tEOF\n-\ttest_commit 14th &&\n-\ttest_commit 15th &&\n \t(echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&\n \techo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |\n \tgit notes copy --for-rewrite=foo &&\n@@ -945,8 +978,10 @@ test_expect_success 'git notes copy --for-rewrite (unconfigured)' '\n '\n \n test_expect_success 'git notes copy --for-rewrite (enabled)' '\n+\tcommit=$(git rev-parse HEAD) &&\n+\tparent=$(git rev-parse HEAD^) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 4acf42e847e7fffbbf89ee365c20ac7caf40de89\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:27:13 2005 -0700\n \n@@ -957,7 +992,7 @@ test_expect_success 'git notes copy --for-rewrite (enabled)' '\n \t\t${indent}\n \t\t${indent}yet another note\n \n-\t\tcommit 07c85d77059393ed0154b8c96906547a59dfcddd\n+\t\tcommit $parent\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:26:13 2005 -0700\n \n@@ -986,8 +1021,9 @@ test_expect_success 'git notes copy --for-rewrite (disabled)' '\n '\n \n test_expect_success 'git notes copy --for-rewrite (overwrite)' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 4acf42e847e7fffbbf89ee365c20ac7caf40de89\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:27:13 2005 -0700\n \n@@ -1015,8 +1051,9 @@ test_expect_success 'git notes copy --for-rewrite (ignore)' '\n '\n \n test_expect_success 'git notes copy --for-rewrite (append)' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 4acf42e847e7fffbbf89ee365c20ac7caf40de89\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:27:13 2005 -0700\n \n@@ -1037,8 +1074,9 @@ test_expect_success 'git notes copy --for-rewrite (append)' '\n '\n \n test_expect_success 'git notes copy --for-rewrite (append two to one)' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 4acf42e847e7fffbbf89ee365c20ac7caf40de89\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:27:13 2005 -0700\n \n@@ -1075,8 +1113,9 @@ test_expect_success 'git notes copy --for-rewrite (append empty)' '\n '\n \n test_expect_success 'GIT_NOTES_REWRITE_MODE works' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 4acf42e847e7fffbbf89ee365c20ac7caf40de89\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:27:13 2005 -0700\n \n@@ -1095,8 +1134,9 @@ test_expect_success 'GIT_NOTES_REWRITE_MODE works' '\n '\n \n test_expect_success 'GIT_NOTES_REWRITE_REF works' '\n+\tcommit=$(git rev-parse HEAD) &&\n \tcat >expect <<-EOF &&\n-\t\tcommit 4acf42e847e7fffbbf89ee365c20ac7caf40de89\n+\t\tcommit $commit\n \t\tAuthor: A U Thor <author@example.com>\n \t\tDate:   Thu Apr 7 15:27:13 2005 -0700\n \n"},{"id":"381140","messageId":"20190826014344.16008-3-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190826014344.16008-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 02/14] t3206: abstract away hash size constants","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:32Z","receivedAt":"2019-08-26T01:44:09Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"The various short object IDs in the range-diff output differ between\nhash algorithms.  Use test_oid_cache to look up values for both SHA-1\nand SHA-256.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3206-range-diff.sh | 227 +++++++++++++++++++++++++++++++-----------\n 1 file changed, 167 insertions(+), 60 deletions(-)\n\ndiff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh\nindex ec548654ce..0120f769f1 100755\n--- a/t/t3206-range-diff.sh\n+++ b/t/t3206-range-diff.sh\n@@ -8,17 +8,124 @@ test_description='range-diff tests'\n # harm than good.  We need some real history.\n \n test_expect_success 'setup' '\n-\tgit fast-import < \"$TEST_DIRECTORY\"/t3206/history.export\n+\tgit fast-import < \"$TEST_DIRECTORY\"/t3206/history.export &&\n+\ttest_oid_cache <<-EOF\n+\t# topic\n+\tt1 sha1:4de457d\n+\tt2 sha1:fccce22\n+\tt3 sha1:147e64e\n+\tt4 sha1:a63e992\n+\tt1 sha256:b89f8b9\n+\tt2 sha256:5f12aad\n+\tt3 sha256:ea8b273\n+\tt4 sha256:14b7336\n+\n+\t# unmodified\n+\tu1 sha1:35b9b25\n+\tu2 sha1:de345ab\n+\tu3 sha1:9af6654\n+\tu4 sha1:2901f77\n+\tu1 sha256:e3731be\n+\tu2 sha256:14fadf8\n+\tu3 sha256:736c4bc\n+\tu4 sha256:673e77d\n+\n+\t# reordered\n+\tr1 sha1:aca177a\n+\tr2 sha1:14ad629\n+\tr3 sha1:ee58208\n+\tr4 sha1:307b27a\n+\tr1 sha256:f59d3aa\n+\tr2 sha256:fb261a8\n+\tr3 sha256:cb2649b\n+\tr4 sha256:958577e\n+\n+\t# removed (deleted)\n+\td1 sha1:7657159\n+\td2 sha1:43d84d3\n+\td3 sha1:a740396\n+\td1 sha256:e312513\n+\td2 sha256:eb19258\n+\td3 sha256:1ccb3c1\n+\n+\t# added\n+\ta1 sha1:2716022\n+\ta2 sha1:b62accd\n+\ta3 sha1:df46cfa\n+\ta4 sha1:3e64548\n+\ta5 sha1:12b4063\n+\ta1 sha256:d724f4d\n+\ta2 sha256:1de7762\n+\ta3 sha256:e159431\n+\ta4 sha256:b3e483c\n+\ta5 sha256:90866a7\n+\n+\t# rebased\n+\tb1 sha1:cc9c443\n+\tb2 sha1:c5d9641\n+\tb3 sha1:28cc2b6\n+\tb4 sha1:5628ab7\n+\tb5 sha1:a31b12e\n+\tb1 sha256:a1a8717\n+\tb2 sha256:20a5862\n+\tb3 sha256:587172a\n+\tb4 sha256:2721c5d\n+\tb5 sha256:7b57864\n+\n+\t# changed\n+\tc1 sha1:a4b3333\n+\tc2 sha1:f51d370\n+\tc3 sha1:0559556\n+\tc4 sha1:d966c5c\n+\tc1 sha256:f8c2b9d\n+\tc2 sha256:3fb6318\n+\tc3 sha256:168ab68\n+\tc4 sha256:3526539\n+\n+\t# changed-message\n+\tm1 sha1:f686024\n+\tm2 sha1:4ab067d\n+\tm3 sha1:b9cb956\n+\tm4 sha1:8add5f1\n+\tm1 sha256:31e6281\n+\tm2 sha256:a06bf1b\n+\tm3 sha256:82dc654\n+\tm4 sha256:48470c5\n+\n+\t# renamed\n+\tn1 sha1:f258d75\n+\tn2 sha1:017b62d\n+\tn3 sha1:3ce7af6\n+\tn4 sha1:1e6226b\n+\tn1 sha256:ad52114\n+\tn2 sha256:3b54c8f\n+\tn3 sha256:3b0a644\n+\tn4 sha256:e461653\n+\n+\t# added and removed\n+\ts1 sha1:096b1ba\n+\ts2 sha1:d92e698\n+\ts3 sha1:9a1db4d\n+\ts4 sha1:fea3b5c\n+\ts1 sha256:a7f9134\n+\ts2 sha256:b4c2580\n+\ts3 sha256:1d62aa2\n+\ts4 sha256:48160e8\n+\n+\t# Empty delimiter (included so lines match neatly)\n+\t__ sha1:-------\n+\t__ sha256:-------\n+\tEOF\n '\n \n test_expect_success 'simple A..B A..C (unmodified)' '\n \tgit range-diff --no-color master..topic master..unmodified \\\n \t\t>actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  35b9b25 s/5/A/\n-\t2:  fccce22 = 2:  de345ab s/4/A/\n-\t3:  147e64e = 3:  9af6654 s/11/B/\n-\t4:  a63e992 = 4:  2901f77 s/12/B/\n+\t1:  $(test_oid t1) = 1:  $(test_oid u1) s/5/A/\n+\t2:  $(test_oid t2) = 2:  $(test_oid u2) s/4/A/\n+\t3:  $(test_oid t3) = 3:  $(test_oid u3) s/11/B/\n+\t4:  $(test_oid t4) = 4:  $(test_oid u4) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -38,10 +145,10 @@ test_expect_success 'simple A B C (unmodified)' '\n test_expect_success 'trivial reordering' '\n \tgit range-diff --no-color master topic reordered >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  aca177a s/5/A/\n-\t3:  147e64e = 2:  14ad629 s/11/B/\n-\t4:  a63e992 = 3:  ee58208 s/12/B/\n-\t2:  fccce22 = 4:  307b27a s/4/A/\n+\t1:  $(test_oid t1) = 1:  $(test_oid r1) s/5/A/\n+\t3:  $(test_oid t3) = 2:  $(test_oid r2) s/11/B/\n+\t4:  $(test_oid t4) = 3:  $(test_oid r3) s/12/B/\n+\t2:  $(test_oid t2) = 4:  $(test_oid r4) s/4/A/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -49,10 +156,10 @@ test_expect_success 'trivial reordering' '\n test_expect_success 'removed a commit' '\n \tgit range-diff --no-color master topic removed >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  7657159 s/5/A/\n-\t2:  fccce22 < -:  ------- s/4/A/\n-\t3:  147e64e = 2:  43d84d3 s/11/B/\n-\t4:  a63e992 = 3:  a740396 s/12/B/\n+\t1:  $(test_oid t1) = 1:  $(test_oid d1) s/5/A/\n+\t2:  $(test_oid t2) < -:  $(test_oid __) s/4/A/\n+\t3:  $(test_oid t3) = 2:  $(test_oid d2) s/11/B/\n+\t4:  $(test_oid t4) = 3:  $(test_oid d3) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -60,11 +167,11 @@ test_expect_success 'removed a commit' '\n test_expect_success 'added a commit' '\n \tgit range-diff --no-color master topic added >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  2716022 s/5/A/\n-\t2:  fccce22 = 2:  b62accd s/4/A/\n-\t-:  ------- > 3:  df46cfa s/6/A/\n-\t3:  147e64e = 4:  3e64548 s/11/B/\n-\t4:  a63e992 = 5:  12b4063 s/12/B/\n+\t1:  $(test_oid t1) = 1:  $(test_oid a1) s/5/A/\n+\t2:  $(test_oid t2) = 2:  $(test_oid a2) s/4/A/\n+\t-:  $(test_oid __) > 3:  $(test_oid a3) s/6/A/\n+\t3:  $(test_oid t3) = 4:  $(test_oid a4) s/11/B/\n+\t4:  $(test_oid t4) = 5:  $(test_oid a5) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -72,10 +179,10 @@ test_expect_success 'added a commit' '\n test_expect_success 'new base, A B C' '\n \tgit range-diff --no-color master topic rebased >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  cc9c443 s/5/A/\n-\t2:  fccce22 = 2:  c5d9641 s/4/A/\n-\t3:  147e64e = 3:  28cc2b6 s/11/B/\n-\t4:  a63e992 = 4:  5628ab7 s/12/B/\n+\t1:  $(test_oid t1) = 1:  $(test_oid b1) s/5/A/\n+\t2:  $(test_oid t2) = 2:  $(test_oid b2) s/4/A/\n+\t3:  $(test_oid t3) = 3:  $(test_oid b3) s/11/B/\n+\t4:  $(test_oid t4) = 4:  $(test_oid b4) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -84,11 +191,11 @@ test_expect_success 'new base, B...C' '\n \t# this syntax includes the commits from master!\n \tgit range-diff --no-color topic...rebased >actual &&\n \tcat >expected <<-EOF &&\n-\t-:  ------- > 1:  a31b12e unrelated\n-\t1:  4de457d = 2:  cc9c443 s/5/A/\n-\t2:  fccce22 = 3:  c5d9641 s/4/A/\n-\t3:  147e64e = 4:  28cc2b6 s/11/B/\n-\t4:  a63e992 = 5:  5628ab7 s/12/B/\n+\t-:  $(test_oid __) > 1:  $(test_oid b5) unrelated\n+\t1:  $(test_oid t1) = 2:  $(test_oid b1) s/5/A/\n+\t2:  $(test_oid t2) = 3:  $(test_oid b2) s/4/A/\n+\t3:  $(test_oid t3) = 4:  $(test_oid b3) s/11/B/\n+\t4:  $(test_oid t4) = 5:  $(test_oid b4) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -96,9 +203,9 @@ test_expect_success 'new base, B...C' '\n test_expect_success 'changed commit' '\n \tgit range-diff --no-color topic...changed >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  a4b3333 s/5/A/\n-\t2:  fccce22 = 2:  f51d370 s/4/A/\n-\t3:  147e64e ! 3:  0559556 s/11/B/\n+\t1:  $(test_oid t1) = 1:  $(test_oid c1) s/5/A/\n+\t2:  $(test_oid t2) = 2:  $(test_oid c2) s/4/A/\n+\t3:  $(test_oid t3) ! 3:  $(test_oid c3) s/11/B/\n \t    @@ file: A\n \t      9\n \t      10\n@@ -108,7 +215,7 @@ test_expect_success 'changed commit' '\n \t      12\n \t      13\n \t      14\n-\t4:  a63e992 ! 4:  d966c5c s/12/B/\n+\t4:  $(test_oid t4) ! 4:  $(test_oid c4) s/12/B/\n \t    @@ file\n \t     @@ file: A\n \t      9\n@@ -125,10 +232,10 @@ test_expect_success 'changed commit' '\n test_expect_success 'changed commit with --no-patch diff option' '\n \tgit range-diff --no-color --no-patch topic...changed >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  a4b3333 s/5/A/\n-\t2:  fccce22 = 2:  f51d370 s/4/A/\n-\t3:  147e64e ! 3:  0559556 s/11/B/\n-\t4:  a63e992 ! 4:  d966c5c s/12/B/\n+\t1:  $(test_oid t1) = 1:  $(test_oid c1) s/5/A/\n+\t2:  $(test_oid t2) = 2:  $(test_oid c2) s/4/A/\n+\t3:  $(test_oid t3) ! 3:  $(test_oid c3) s/11/B/\n+\t4:  $(test_oid t4) ! 4:  $(test_oid c4) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -136,16 +243,16 @@ test_expect_success 'changed commit with --no-patch diff option' '\n test_expect_success 'changed commit with --stat diff option' '\n \tgit range-diff --no-color --stat topic...changed >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  a4b3333 s/5/A/\n+\t1:  $(test_oid t1) = 1:  $(test_oid c1) s/5/A/\n \t     a => b | 0\n \t     1 file changed, 0 insertions(+), 0 deletions(-)\n-\t2:  fccce22 = 2:  f51d370 s/4/A/\n+\t2:  $(test_oid t2) = 2:  $(test_oid c2) s/4/A/\n \t     a => b | 0\n \t     1 file changed, 0 insertions(+), 0 deletions(-)\n-\t3:  147e64e ! 3:  0559556 s/11/B/\n+\t3:  $(test_oid t3) ! 3:  $(test_oid c3) s/11/B/\n \t     a => b | 0\n \t     1 file changed, 0 insertions(+), 0 deletions(-)\n-\t4:  a63e992 ! 4:  d966c5c s/12/B/\n+\t4:  $(test_oid t4) ! 4:  $(test_oid c4) s/12/B/\n \t     a => b | 0\n \t     1 file changed, 0 insertions(+), 0 deletions(-)\n \tEOF\n@@ -155,9 +262,9 @@ test_expect_success 'changed commit with --stat diff option' '\n test_expect_success 'changed commit with sm config' '\n \tgit range-diff --no-color --submodule=log topic...changed >actual &&\n \tcat >expected <<-EOF &&\n-\t1:  4de457d = 1:  a4b3333 s/5/A/\n-\t2:  fccce22 = 2:  f51d370 s/4/A/\n-\t3:  147e64e ! 3:  0559556 s/11/B/\n+\t1:  $(test_oid t1) = 1:  $(test_oid c1) s/5/A/\n+\t2:  $(test_oid t2) = 2:  $(test_oid c2) s/4/A/\n+\t3:  $(test_oid t3) ! 3:  $(test_oid c3) s/11/B/\n \t    @@ file: A\n \t      9\n \t      10\n@@ -167,7 +274,7 @@ test_expect_success 'changed commit with sm config' '\n \t      12\n \t      13\n \t      14\n-\t4:  a63e992 ! 4:  d966c5c s/12/B/\n+\t4:  $(test_oid t4) ! 4:  $(test_oid c4) s/12/B/\n \t    @@ file\n \t     @@ file: A\n \t      9\n@@ -184,8 +291,8 @@ test_expect_success 'changed commit with sm config' '\n test_expect_success 'renamed file' '\n \tgit range-diff --no-color --submodule=log topic...renamed-file >actual &&\n \tsed s/Z/\\ /g >expected <<-EOF &&\n-\t1:  4de457d = 1:  f258d75 s/5/A/\n-\t2:  fccce22 ! 2:  017b62d s/4/A/\n+\t1:  $(test_oid t1) = 1:  $(test_oid n1) s/5/A/\n+\t2:  $(test_oid t2) ! 2:  $(test_oid n2) s/4/A/\n \t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n@@ -198,7 +305,7 @@ test_expect_success 'renamed file' '\n \t    Z@@\n \t    Z 1\n \t    Z 2\n-\t3:  147e64e ! 3:  3ce7af6 s/11/B/\n+\t3:  $(test_oid t3) ! 3:  $(test_oid n3) s/11/B/\n \t    @@ Metadata\n \t    Z ## Commit message ##\n \t    Z    s/11/B/\n@@ -210,7 +317,7 @@ test_expect_success 'renamed file' '\n \t    Z 8\n \t    Z 9\n \t    Z 10\n-\t4:  a63e992 ! 4:  1e6226b s/12/B/\n+\t4:  $(test_oid t4) ! 4:  $(test_oid n4) s/12/B/\n \t    @@ Metadata\n \t    Z ## Commit message ##\n \t    Z    s/12/B/\n@@ -229,8 +336,8 @@ test_expect_success 'renamed file' '\n test_expect_success 'file added and later removed' '\n \tgit range-diff --no-color --submodule=log topic...added-removed >actual &&\n \tsed s/Z/\\ /g >expected <<-EOF &&\n-\t1:  4de457d = 1:  096b1ba s/5/A/\n-\t2:  fccce22 ! 2:  d92e698 s/4/A/\n+\t1:  $(test_oid t1) = 1:  $(test_oid s1) s/5/A/\n+\t2:  $(test_oid t2) ! 2:  $(test_oid s2) s/4/A/\n \t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n@@ -246,7 +353,7 @@ test_expect_success 'file added and later removed' '\n \t    Z 7\n \t    +\n \t    + ## new-file (new) ##\n-\t3:  147e64e ! 3:  9a1db4d s/11/B/\n+\t3:  $(test_oid t3) ! 3:  $(test_oid s3) s/11/B/\n \t    @@ Metadata\n \t    ZAuthor: Thomas Rast <trast@inf.ethz.ch>\n \t    Z\n@@ -262,7 +369,7 @@ test_expect_success 'file added and later removed' '\n \t    Z 14\n \t    +\n \t    + ## new-file (deleted) ##\n-\t4:  a63e992 = 4:  fea3b5c s/12/B/\n+\t4:  $(test_oid t4) = 4:  $(test_oid s4) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n@@ -275,8 +382,8 @@ test_expect_success 'no commits on one side' '\n test_expect_success 'changed message' '\n \tgit range-diff --no-color topic...changed-message >actual &&\n \tsed s/Z/\\ /g >expected <<-EOF &&\n-\t1:  4de457d = 1:  f686024 s/5/A/\n-\t2:  fccce22 ! 2:  4ab067d s/4/A/\n+\t1:  $(test_oid t1) = 1:  $(test_oid m1) s/5/A/\n+\t2:  $(test_oid t2) ! 2:  $(test_oid m2) s/4/A/\n \t    @@ Metadata\n \t    Z ## Commit message ##\n \t    Z    s/4/A/\n@@ -286,16 +393,16 @@ test_expect_success 'changed message' '\n \t    Z ## file ##\n \t    Z@@\n \t    Z 1\n-\t3:  147e64e = 3:  b9cb956 s/11/B/\n-\t4:  a63e992 = 4:  8add5f1 s/12/B/\n+\t3:  $(test_oid t3) = 3:  $(test_oid m3) s/11/B/\n+\t4:  $(test_oid t4) = 4:  $(test_oid m4) s/12/B/\n \tEOF\n \ttest_cmp expected actual\n '\n \n test_expect_success 'dual-coloring' '\n-\tsed -e \"s|^:||\" >expect <<-\\EOF &&\n-\t:<YELLOW>1:  a4b3333 = 1:  f686024 s/5/A/<RESET>\n-\t:<RED>2:  f51d370 <RESET><YELLOW>!<RESET><GREEN> 2:  4ab067d<RESET><YELLOW> s/4/A/<RESET>\n+\tsed -e \"s|^:||\" >expect <<-EOF &&\n+\t:<YELLOW>1:  $(test_oid c1) = 1:  $(test_oid m1) s/5/A/<RESET>\n+\t:<RED>2:  $(test_oid c2) <RESET><YELLOW>!<RESET><GREEN> 2:  $(test_oid m2)<RESET><YELLOW> s/4/A/<RESET>\n \t:    <REVERSE><CYAN>@@<RESET> <RESET>Metadata<RESET>\n \t:      ## Commit message ##<RESET>\n \t:         s/4/A/<RESET>\n@@ -305,7 +412,7 @@ test_expect_success 'dual-coloring' '\n \t:      ## file ##<RESET>\n \t:    <CYAN> @@<RESET>\n \t:      1<RESET>\n-\t:<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>\n+\t:<RED>3:  $(test_oid c3) <RESET><YELLOW>!<RESET><GREEN> 3:  $(test_oid m3)<RESET><YELLOW> s/11/B/<RESET>\n \t:    <REVERSE><CYAN>@@<RESET> <RESET>file: A<RESET>\n \t:      9<RESET>\n \t:      10<RESET>\n@@ -315,7 +422,7 @@ test_expect_success 'dual-coloring' '\n \t:      12<RESET>\n \t:      13<RESET>\n \t:      14<RESET>\n-\t:<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>\n+\t:<RED>4:  $(test_oid c4) <RESET><YELLOW>!<RESET><GREEN> 4:  $(test_oid m4)<RESET><YELLOW> s/12/B/<RESET>\n \t:    <REVERSE><CYAN>@@<RESET> <RESET>file<RESET>\n \t:    <CYAN> @@ file: A<RESET>\n \t:      9<RESET>\n"},{"id":"381141","messageId":"20190826014344.16008-8-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190826014344.16008-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 07/14] t3430: avoid hard-coded object IDs","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:37Z","receivedAt":"2019-08-26T01:44:11Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Compute the object IDs used in the todo list instead of hard-coding\nthem.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3430-rebase-merges.sh | 23 +++++++++++++++--------\n 1 file changed, 15 insertions(+), 8 deletions(-)\n\ndiff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh\nindex 7b6c4847ad..c1ea2ee297 100755\n--- a/t/t3430-rebase-merges.sh\n+++ b/t/t3430-rebase-merges.sh\n@@ -37,20 +37,27 @@ test_expect_success 'setup' '\n \ttest_commit A &&\n \tgit checkout -b first &&\n \ttest_commit B &&\n+\tb=$(git rev-parse --short HEAD) &&\n \tgit checkout master &&\n \ttest_commit C &&\n+\tc=$(git rev-parse --short HEAD) &&\n \ttest_commit D &&\n+\td=$(git rev-parse --short HEAD) &&\n \tgit merge --no-commit B &&\n \ttest_tick &&\n \tgit commit -m E &&\n \tgit tag -m E E &&\n+\te=$(git rev-parse --short HEAD) &&\n \tgit checkout -b second C &&\n \ttest_commit F &&\n+\tf=$(git rev-parse --short HEAD) &&\n \ttest_commit G &&\n+\tg=$(git rev-parse --short HEAD) &&\n \tgit checkout master &&\n \tgit merge --no-commit G &&\n \ttest_tick &&\n \tgit commit -m H &&\n+\th=$(git rev-parse --short HEAD) &&\n \tgit tag -m H H &&\n \tgit checkout A &&\n \ttest_commit conflicting-G G.t\n@@ -93,24 +100,24 @@ test_expect_success 'create completely different structure' '\n '\n \n test_expect_success 'generate correct todo list' '\n-\tcat >expect <<-\\EOF &&\n+\tcat >expect <<-EOF &&\n \tlabel onto\n \n \treset onto\n-\tpick d9df450 B\n+\tpick $b B\n \tlabel E\n \n \treset onto\n-\tpick 5dee784 C\n+\tpick $c C\n \tlabel branch-point\n-\tpick ca2c861 F\n-\tpick 088b00a G\n+\tpick $f F\n+\tpick $g G\n \tlabel H\n \n \treset branch-point # C\n-\tpick 12bd07b D\n-\tmerge -C 2051b56 E # E\n-\tmerge -C 233d48a H # H\n+\tpick $d D\n+\tmerge -C $e E # E\n+\tmerge -C $h H # H\n \n \tEOF\n \n"},{"id":"381142","messageId":"20190826014344.16008-6-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190826014344.16008-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 05/14] t3306: abstract away SHA-1-specific constants","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:35Z","receivedAt":"2019-08-26T01:44:12Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Adjust the test so that it computes variables for object IDs instead of\nusing hard-coded hashes.  Convert some single-line heredocs into inline\nuses of echo now that they can be expressed succinctly.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3306-notes-prune.sh | 45 ++++++++++++++++++++----------------------\n 1 file changed, 21 insertions(+), 24 deletions(-)\n\ndiff --git a/t/t3306-notes-prune.sh b/t/t3306-notes-prune.sh\nindex 61748088eb..8f4102ff9e 100755\n--- a/t/t3306-notes-prune.sh\n+++ b/t/t3306-notes-prune.sh\n@@ -11,23 +11,26 @@ test_expect_success 'setup: create a few commits with notes' '\n \ttest_tick &&\n \tgit commit -m 1st &&\n \tgit notes add -m \"Note #1\" &&\n+\tfirst=$(git rev-parse HEAD) &&\n \t: > file2 &&\n \tgit add file2 &&\n \ttest_tick &&\n \tgit commit -m 2nd &&\n \tgit notes add -m \"Note #2\" &&\n+\tsecond=$(git rev-parse HEAD) &&\n \t: > file3 &&\n \tgit add file3 &&\n \ttest_tick &&\n \tgit commit -m 3rd &&\n-\tCOMMIT_FILE=.git/objects/5e/e1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&\n+\tthird=$(git rev-parse HEAD) &&\n+\tCOMMIT_FILE=$(echo $third | sed \"s!^..!.git/objects/&/!\") &&\n \ttest -f $COMMIT_FILE &&\n \ttest-tool chmtime =+0 $COMMIT_FILE &&\n \tgit notes add -m \"Note #3\"\n '\n \n cat > expect <<END_OF_LOG\n-commit 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29\n+commit $third\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:15:13 2005 -0700\n \n@@ -36,7 +39,7 @@ Date:   Thu Apr 7 15:15:13 2005 -0700\n Notes:\n     Note #3\n \n-commit 08341ad9e94faa089d60fd3f523affb25c6da189\n+commit $second\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:14:13 2005 -0700\n \n@@ -45,7 +48,7 @@ Date:   Thu Apr 7 15:14:13 2005 -0700\n Notes:\n     Note #2\n \n-commit ab5f302035f2e7aaf04265f08b42034c23256e1f\n+commit $first\n Author: A U Thor <author@example.com>\n Date:   Thu Apr 7 15:13:13 2005 -0700\n \n@@ -70,16 +73,16 @@ test_expect_success 'remove some commits' '\n \n test_expect_success 'verify that commits are gone' '\n \n-\ttest_must_fail git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&\n-\tgit cat-file -p 08341ad9e94faa089d60fd3f523affb25c6da189 &&\n-\tgit cat-file -p ab5f302035f2e7aaf04265f08b42034c23256e1f\n+\ttest_must_fail git cat-file -p $third &&\n+\tgit cat-file -p $second &&\n+\tgit cat-file -p $first\n '\n \n test_expect_success 'verify that notes are still present' '\n \n-\tgit notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&\n-\tgit notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&\n-\tgit notes show ab5f302035f2e7aaf04265f08b42034c23256e1f\n+\tgit notes show $third &&\n+\tgit notes show $second &&\n+\tgit notes show $first\n '\n \n test_expect_success 'prune -n does not remove notes' '\n@@ -90,13 +93,10 @@ test_expect_success 'prune -n does not remove notes' '\n \ttest_cmp expect actual\n '\n \n-cat > expect <<EOF\n-5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29\n-EOF\n \n test_expect_success 'prune -n lists prunable notes' '\n \n-\n+\techo $third >expect &&\n \tgit notes prune -n > actual &&\n \ttest_cmp expect actual\n '\n@@ -109,9 +109,9 @@ test_expect_success 'prune notes' '\n \n test_expect_success 'verify that notes are gone' '\n \n-\ttest_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&\n-\tgit notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&\n-\tgit notes show ab5f302035f2e7aaf04265f08b42034c23256e1f\n+\ttest_must_fail git notes show $third &&\n+\tgit notes show $second &&\n+\tgit notes show $first\n '\n \n test_expect_success 'remove some commits' '\n@@ -121,21 +121,18 @@ test_expect_success 'remove some commits' '\n \tgit gc --prune=now\n '\n \n-cat > expect <<EOF\n-08341ad9e94faa089d60fd3f523affb25c6da189\n-EOF\n-\n test_expect_success 'prune -v notes' '\n \n+\techo $second >expect &&\n \tgit notes prune -v > actual &&\n \ttest_cmp expect actual\n '\n \n test_expect_success 'verify that notes are gone' '\n \n-\ttest_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&\n-\ttest_must_fail git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&\n-\tgit notes show ab5f302035f2e7aaf04265f08b42034c23256e1f\n+\ttest_must_fail git notes show $third &&\n+\ttest_must_fail git notes show $second &&\n+\tgit notes show $first\n '\n \n test_done\n"},{"id":"381143","messageId":"20190826014344.16008-9-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190826014344.16008-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 08/14] t3506: make hash independent","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:38Z","receivedAt":"2019-08-26T01:44:13Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"This test uses a hard-coded object ID to ensure that the result of\ncherry-pick --ff is correct.  Use test_oid to make this work for both\nSHA-1 and SHA-256.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3506-cherry-pick-ff.sh | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh\nindex 127dd0082f..9d5adbc130 100755\n--- a/t/t3506-cherry-pick-ff.sh\n+++ b/t/t3506-cherry-pick-ff.sh\n@@ -16,7 +16,11 @@ test_expect_success setup '\n \tgit add file1 &&\n \ttest_tick &&\n \tgit commit -m \"second\" &&\n-\tgit tag second\n+\tgit tag second &&\n+\ttest_oid_cache <<-EOF\n+\tcp_ff sha1:1df192cd8bc58a2b275d842cede4d221ad9000d1\n+\tcp_ff sha256:e70d6b7fc064bddb516b8d512c9057094b96ce6ff08e12080acc4fe7f1d60a1d\n+\tEOF\n '\n \n test_expect_success 'cherry-pick using --ff fast forwards' '\n@@ -102,7 +106,7 @@ test_expect_success 'cherry pick a root commit with --ff' '\n \tgit add file2 &&\n \tgit commit --amend -m \"file2\" &&\n \tgit cherry-pick --ff first &&\n-\ttest \"$(git rev-parse --verify HEAD)\" = \"1df192cd8bc58a2b275d842cede4d221ad9000d1\"\n+\ttest \"$(git rev-parse --verify HEAD)\" = \"$(test_oid cp_ff)\"\n '\n \n test_expect_success 'cherry-pick --ff on unborn branch' '\n"},{"id":"381144","messageId":"20190826014344.16008-7-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190826014344.16008-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 06/14] t3404: abstract away SHA-1-specific constants","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:36Z","receivedAt":"2019-08-26T01:44:15Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Adjust the test so that it computes variables for object IDs instead of\nusing hard-coded hashes.  Add a use of $EMPTY_TREE instead of a\nhard-coded value.  Remove a comment about hard-coded hashes which is no\nlonger applicable.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3404-rebase-interactive.sh | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 461dd539ff..87ee69f78e 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -29,9 +29,6 @@ Initial setup:\n \n . \"$TEST_DIRECTORY\"/lib-rebase.sh\n \n-# WARNING: Modifications to the initial repository can change the SHA ID used\n-# in the expect2 file for the 'stop on conflicting pick' test.\n-\n test_expect_success 'setup' '\n \ttest_commit A file1 &&\n \ttest_commit B file1 &&\n@@ -233,25 +230,28 @@ test_expect_success 'exchange two commits' '\n \tset_fake_editor &&\n \tFAKE_LINES=\"2 1\" git rebase -i HEAD~2 &&\n \ttest H = $(git cat-file commit HEAD^ | sed -ne \\$p) &&\n-\ttest G = $(git cat-file commit HEAD | sed -ne \\$p)\n+\ttest G = $(git cat-file commit HEAD | sed -ne \\$p) &&\n+\tblob1=$(git rev-parse --short HEAD^:file1) &&\n+\tblob2=$(git rev-parse --short HEAD:file1) &&\n+\tcommit=$(git rev-parse --short HEAD)\n '\n \n test_expect_success 'stop on conflicting pick' '\n-\tcat >expect <<-\\EOF &&\n+\tcat >expect <<-EOF &&\n \tdiff --git a/file1 b/file1\n-\tindex f70f10e..fd79235 100644\n+\tindex $blob1..$blob2 100644\n \t--- a/file1\n \t+++ b/file1\n \t@@ -1 +1 @@\n \t-A\n \t+G\n \tEOF\n-\tcat >expect2 <<-\\EOF &&\n+\tcat >expect2 <<-EOF &&\n \t<<<<<<< HEAD\n \tD\n \t=======\n \tG\n-\t>>>>>>> 5d18e54... G\n+\t>>>>>>> $commit... G\n \tEOF\n \tgit tag new-branch1 &&\n \tset_fake_editor &&\n@@ -1003,7 +1003,7 @@ test_expect_success 'rebase -i --root temporary sentinel commit' '\n \tgit checkout B &&\n \tset_fake_editor &&\n \ttest_must_fail env FAKE_LINES=\"2\" git rebase -i --root &&\n-\tgit cat-file commit HEAD | grep \"^tree 4b825dc642cb\" &&\n+\tgit cat-file commit HEAD | grep \"^tree $EMPTY_TREE\" &&\n \tgit rebase --abort\n '\n \n@@ -1161,7 +1161,7 @@ test_expect_success 'rebase -i error on commits with \\ in message' '\n \ttest_expect_code 1 grep  \"\temp\" error\n '\n \n-test_expect_success 'short SHA-1 setup' '\n+test_expect_success SHA1 'short SHA-1 setup' '\n \ttest_when_finished \"git checkout master\" &&\n \tgit checkout --orphan collide &&\n \tgit rm -rf . &&\n@@ -1173,7 +1173,7 @@ test_expect_success 'short SHA-1 setup' '\n \t)\n '\n \n-test_expect_success 'short SHA-1 collide' '\n+test_expect_success SHA1 'short SHA-1 collide' '\n \ttest_when_finished \"reset_rebase && git checkout master\" &&\n \tgit checkout collide &&\n \t(\n"},{"id":"381145","messageId":"20190826014344.16008-10-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190826014344.16008-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 09/14] t3600: make hash size independent","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:39Z","receivedAt":"2019-08-26T01:44:17Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Instead of hard-coding a fixed length invalid object ID in the test,\ncompute one using the lookup tables.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3600-rm.sh | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\nindex 66282a720e..8c8cca5bfb 100755\n--- a/t/t3600-rm.sh\n+++ b/t/t3600-rm.sh\n@@ -240,12 +240,14 @@ test_expect_success 'refresh index before checking if it is up-to-date' '\n '\n \n test_expect_success 'choking \"git rm\" should not let it die with cruft' '\n+\ttest_oid_init &&\n \tgit reset -q --hard &&\n \ttest_when_finished \"rm -f .git/index.lock && git reset -q --hard\" &&\n \ti=0 &&\n+\thash=$(test_oid deadbeef) &&\n \twhile test $i -lt 12000\n \tdo\n-\t\techo \"100644 1234567890123456789012345678901234567890 0\tsome-file-$i\"\n+\t\techo \"100644 $hash 0\tsome-file-$i\"\n \t\ti=$(( $i + 1 ))\n \tdone | git update-index --index-info &&\n \tgit rm -n \"some-file-*\" | : &&\n"},{"id":"381146","messageId":"20190826014344.16008-11-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190826014344.16008-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 10/14] t3800: make hash-size independent","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:40Z","receivedAt":"2019-08-26T01:44:19Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Replace references to several hard-coded object IDs with a variable\nreferring to the generated commit.  Avoid matching on exact character\npositions, which will be different depending on the hash in use.  In the\ntest for a valid object ID, use an obviously invalid one from the lookup\ntable.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3800-mktag.sh | 49 ++++++++++++++++++++++++------------------------\n 1 file changed, 25 insertions(+), 24 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 8eb47942e2..64dcc5ec28 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -23,6 +23,7 @@ check_verify_failure () {\n # first create a commit, so we have a valid object/type\n # for the tag.\n test_expect_success 'setup' '\n+\ttest_oid_init &&\n \techo Hello >A &&\n \tgit update-index --add A &&\n \tgit commit -m \"Initial commit\" &&\n@@ -69,28 +70,28 @@ check_verify_failure '\"object\" line SHA1 check' '^error: char7: .*SHA1 hash$'\n #  4. type line label check\n \n cat >tag.sig <<EOF\n-object 779e9b33986b1c2670fff52c5067603117b3e895\n+object $head\n xxxx tag\n tag mytag\n tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"type\" line label check' '^error: char47: .*\"\\\\ntype \"$'\n+check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n \n ############################################################\n #  5. type line eol check\n \n-echo \"object 779e9b33986b1c2670fff52c5067603117b3e895\" >tag.sig\n+echo \"object $head\" >tag.sig\n printf \"type tagsssssssssssssssssssssssssssssss\" >>tag.sig\n \n-check_verify_failure '\"type\" line eol check' '^error: char48: .*\"\\\\n\"$'\n+check_verify_failure '\"type\" line eol check' '^error: char.*: .*\"\\\\n\"$'\n \n ############################################################\n #  6. tag line label check #1\n \n cat >tag.sig <<EOF\n-object 779e9b33986b1c2670fff52c5067603117b3e895\n+object $head\n type tag\n xxx mytag\n tagger . <> 0 +0000\n@@ -98,37 +99,37 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure '\"tag\" line label check #1' \\\n-\t'^error: char57: no \"tag \" found$'\n+\t'^error: char.*: no \"tag \" found$'\n \n ############################################################\n #  7. tag line label check #2\n \n cat >tag.sig <<EOF\n-object 779e9b33986b1c2670fff52c5067603117b3e895\n+object $head\n type taggggggggggggggggggggggggggggggg\n tag\n EOF\n \n check_verify_failure '\"tag\" line label check #2' \\\n-\t'^error: char87: no \"tag \" found$'\n+\t'^error: char.*: no \"tag \" found$'\n \n ############################################################\n #  8. type line type-name length check\n \n cat >tag.sig <<EOF\n-object 779e9b33986b1c2670fff52c5067603117b3e895\n+object $head\n type taggggggggggggggggggggggggggggggg\n tag mytag\n EOF\n \n check_verify_failure '\"type\" line type-name length check' \\\n-\t'^error: char53: type too long$'\n+\t'^error: char.*: type too long$'\n \n ############################################################\n #  9. verify object (SHA1/type) check\n \n cat >tag.sig <<EOF\n-object 779e9b33986b1c2670fff52c5067603117b3e895\n+object $(test_oid deadbeef)\n type tagggg\n tag mytag\n tagger . <> 0 +0000\n@@ -150,7 +151,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify tag-name check' \\\n-\t'^error: char67: could not verify tag name$'\n+\t'^error: char.*: could not verify tag name$'\n \n ############################################################\n # 11. tagger line label check #1\n@@ -164,7 +165,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #1' \\\n-\t'^error: char70: could not find \"tagger \"$'\n+\t'^error: char.*: could not find \"tagger \"$'\n \n ############################################################\n # 12. tagger line label check #2\n@@ -179,7 +180,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #2' \\\n-\t'^error: char70: could not find \"tagger \"$'\n+\t'^error: char.*: could not find \"tagger \"$'\n \n ############################################################\n # 13. disallow missing tag author name\n@@ -194,7 +195,7 @@ This is filler\n EOF\n \n check_verify_failure 'disallow missing tag author name' \\\n-\t'^error: char77: missing tagger name$'\n+\t'^error: char.*: missing tagger name$'\n \n ############################################################\n # 14. disallow missing tag author name\n@@ -209,7 +210,7 @@ tagger T A Gger <\n EOF\n \n check_verify_failure 'disallow malformed tagger' \\\n-\t'^error: char77: malformed tagger field$'\n+\t'^error: char.*: malformed tagger field$'\n \n ############################################################\n # 15. allow empty tag email\n@@ -238,7 +239,7 @@ tagger T A Gger <tag ger@example.com> 0 +0000\n EOF\n \n check_verify_failure 'disallow spaces in tag email' \\\n-\t'^error: char77: malformed tagger field$'\n+\t'^error: char.*: malformed tagger field$'\n \n ############################################################\n # 17. disallow missing tag timestamp\n@@ -252,7 +253,7 @@ tagger T A Gger <tagger@example.com>__\n EOF\n \n check_verify_failure 'disallow missing tag timestamp' \\\n-\t'^error: char107: missing tag timestamp$'\n+\t'^error: char.*: missing tag timestamp$'\n \n ############################################################\n # 18. detect invalid tag timestamp1\n@@ -266,7 +267,7 @@ tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008\n EOF\n \n check_verify_failure 'detect invalid tag timestamp1' \\\n-\t'^error: char107: missing tag timestamp$'\n+\t'^error: char.*: missing tag timestamp$'\n \n ############################################################\n # 19. detect invalid tag timestamp2\n@@ -280,7 +281,7 @@ tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500\n EOF\n \n check_verify_failure 'detect invalid tag timestamp2' \\\n-\t'^error: char111: malformed tag timestamp$'\n+\t'^error: char.*: malformed tag timestamp$'\n \n ############################################################\n # 20. detect invalid tag timezone1\n@@ -294,7 +295,7 @@ tagger T A Gger <tagger@example.com> 1206478233 GMT\n EOF\n \n check_verify_failure 'detect invalid tag timezone1' \\\n-\t'^error: char118: malformed tag timezone$'\n+\t'^error: char.*: malformed tag timezone$'\n \n ############################################################\n # 21. detect invalid tag timezone2\n@@ -308,7 +309,7 @@ tagger T A Gger <tagger@example.com> 1206478233 +  30\n EOF\n \n check_verify_failure 'detect invalid tag timezone2' \\\n-\t'^error: char118: malformed tag timezone$'\n+\t'^error: char.*: malformed tag timezone$'\n \n ############################################################\n # 22. detect invalid tag timezone3\n@@ -322,7 +323,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -1430\n EOF\n \n check_verify_failure 'detect invalid tag timezone3' \\\n-\t'^error: char118: malformed tag timezone$'\n+\t'^error: char.*: malformed tag timezone$'\n \n ############################################################\n # 23. detect invalid header entry\n@@ -337,7 +338,7 @@ this line should not be here\n EOF\n \n check_verify_failure 'detect invalid header entry' \\\n-\t'^error: char124: trailing garbage in tag header$'\n+\t'^error: char.*: trailing garbage in tag header$'\n \n ############################################################\n # 24. create valid tag\n"},{"id":"381147","messageId":"20190826014344.16008-12-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190826014344.16008-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 11/14] t3903: abstract away SHA-1-specific constants","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:41Z","receivedAt":"2019-08-26T01:44:20Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Abstract away the SHA-1-specific constants by sanitizing diff output to\nremove the index lines, since it's clear from the assertions in question\nthat we are not interested in the specific object IDs.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t3903-stash.sh | 32 ++++++++++++++++++++++----------\n 1 file changed, 22 insertions(+), 10 deletions(-)\n\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex b8e337893f..820b350aeb 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -7,6 +7,18 @@ test_description='Test git stash'\n \n . ./test-lib.sh\n \n+diff_cmp () {\n+\tfor i in \"$1\" \"$2\"\n+\tdo\n+\t\tsed -e 's/^index 0000000\\.\\.[0-9a-f]*/index 0000000..1234567/' \\\n+\t\t-e 's/^index [0-9a-f]*\\.\\.[0-9a-f]*/index 1234567..89abcde/' \\\n+\t\t-e 's/^index [0-9a-f]*,[0-9a-f]*\\.\\.[0-9a-f]*/index 1234567,7654321..89abcde/' \\\n+\t\t\"$i\" >\"$i.compare\" || return 1\n+\tdone &&\n+\ttest_cmp \"$1.compare\" \"$2.compare\" &&\n+\trm -f \"$1.compare\" \"$2.compare\"\n+}\n+\n test_expect_success 'stash some dirty working directory' '\n \techo 1 >file &&\n \tgit add file &&\n@@ -36,7 +48,7 @@ EOF\n test_expect_success 'parents of stash' '\n \ttest $(git rev-parse stash^) = $(git rev-parse HEAD) &&\n \tgit diff stash^2..stash >output &&\n-\ttest_cmp expect output\n+\tdiff_cmp expect output\n '\n \n test_expect_success 'applying bogus stash does nothing' '\n@@ -210,13 +222,13 @@ test_expect_success 'stash branch' '\n \ttest refs/heads/stashbranch = $(git symbolic-ref HEAD) &&\n \ttest $(git rev-parse HEAD) = $(git rev-parse master^) &&\n \tgit diff --cached >output &&\n-\ttest_cmp expect output &&\n+\tdiff_cmp expect output &&\n \tgit diff >output &&\n-\ttest_cmp expect1 output &&\n+\tdiff_cmp expect1 output &&\n \tgit add file &&\n \tgit commit -m alternate\\ second &&\n \tgit diff master..stashbranch >output &&\n-\ttest_cmp output expect2 &&\n+\tdiff_cmp output expect2 &&\n \ttest 0 = $(git stash list | wc -l)\n '\n \n@@ -577,7 +589,7 @@ test_expect_success 'stash show -p - stashes on stack, stash-like argument' '\n \t+bar\n \tEOF\n \tgit stash show -p ${STASH_ID} >actual &&\n-\ttest_cmp expected actual\n+\tdiff_cmp expected actual\n '\n \n test_expect_success 'stash show - no stashes on stack, stash-like argument' '\n@@ -609,7 +621,7 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' '\n \t+foo\n \tEOF\n \tgit stash show -p ${STASH_ID} >actual &&\n-\ttest_cmp expected actual\n+\tdiff_cmp expected actual\n '\n \n test_expect_success 'stash show --patience shows diff' '\n@@ -627,7 +639,7 @@ test_expect_success 'stash show --patience shows diff' '\n \t+foo\n \tEOF\n \tgit stash show --patience ${STASH_ID} >actual &&\n-\ttest_cmp expected actual\n+\tdiff_cmp expected actual\n '\n \n test_expect_success 'drop: fail early if specified stash is not a stash ref' '\n@@ -791,7 +803,7 @@ test_expect_success 'stash where working directory contains \"HEAD\" file' '\n \tgit diff-index --cached --quiet HEAD &&\n \ttest \"$(git rev-parse stash^)\" = \"$(git rev-parse HEAD)\" &&\n \tgit diff stash^..stash >output &&\n-\ttest_cmp expect output\n+\tdiff_cmp expect output\n '\n \n test_expect_success 'store called with invalid commit' '\n@@ -847,7 +859,7 @@ test_expect_success 'stash list implies --first-parent -m' '\n \t+working\n \tEOF\n \tgit stash list --format=%gd -p >actual &&\n-\ttest_cmp expect actual\n+\tdiff_cmp expect actual\n '\n \n test_expect_success 'stash list --cc shows combined diff' '\n@@ -864,7 +876,7 @@ test_expect_success 'stash list --cc shows combined diff' '\n \t++working\n \tEOF\n \tgit stash list --format=%gd -p --cc >actual &&\n-\ttest_cmp expect actual\n+\tdiff_cmp expect actual\n '\n \n test_expect_success 'stash is not confused by partial renames' '\n"},{"id":"381148","messageId":"20190826014344.16008-13-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190826014344.16008-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 12/14] t4000: make hash size independent","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:42Z","receivedAt":"2019-08-26T01:44:20Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Use $ZERO_OID instead of hard-coding a fixed size all-zeros object ID.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t4000-diff-format.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh\nindex 8de36b7d12..e5116a76a1 100755\n--- a/t/t4000-diff-format.sh\n+++ b/t/t4000-diff-format.sh\n@@ -78,7 +78,7 @@ test_expect_success 'git diff-files --no-patch --patch shows the patch' '\n \n test_expect_success 'git diff-files --no-patch --patch-with-raw shows the patch and raw data' '\n \tgit diff-files --no-patch --patch-with-raw >actual &&\n-\tgrep -q \"^:100644 100755 .* 0000000000000000000000000000000000000000 M\tpath0\\$\" actual &&\n+\tgrep -q \"^:100644 100755 .* $ZERO_OID M\tpath0\\$\" actual &&\n \ttail -n +4 actual >actual-patch &&\n \tcompare_diff_patch expected actual-patch\n '\n"},{"id":"381149","messageId":"20190826014344.16008-15-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190826014344.16008-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 14/14] t4009: make hash size independent","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:44Z","receivedAt":"2019-08-26T01:44:23Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Instead of hard-coding object IDs, compute them and use those in the\ncomparison.  Note that the comparison code ignores the actual object\nIDs, but does check that they're the right size, so computing them is\nthe easiest way to ensure that they are.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t4009-diff-rename-4.sh | 19 +++++++++++--------\n 1 file changed, 11 insertions(+), 8 deletions(-)\n\ndiff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh\nindex 3641fd84d6..b63bdf031f 100755\n--- a/t/t4009-diff-rename-4.sh\n+++ b/t/t4009-diff-rename-4.sh\n@@ -14,6 +14,7 @@ test_expect_success \\\n     'cat \"$TEST_DIRECTORY\"/diff-lib/COPYING >COPYING &&\n      echo frotz >rezrov &&\n     git update-index --add COPYING rezrov &&\n+    orig=$(git hash-object COPYING) &&\n     tree=$(git write-tree) &&\n     echo $tree'\n \n@@ -22,6 +23,8 @@ test_expect_success \\\n     'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&\n     sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&\n     rm -f COPYING &&\n+    c1=$(git hash-object COPYING.1) &&\n+    c2=$(git hash-object COPYING.2) &&\n     git update-index --add --remove COPYING COPYING.?'\n \n # tree has COPYING and rezrov.  work tree has COPYING.1 and COPYING.2,\n@@ -31,11 +34,11 @@ test_expect_success \\\n \n git diff-index -z -C $tree >current\n \n-cat >expected <<\\EOF\n-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234\n+cat >expected <<EOF\n+:100644 100644 $orig $c1 C1234\n COPYING\n COPYING.1\n-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 R1234\n+:100644 100644 $orig $c2 R1234\n COPYING\n COPYING.2\n EOF\n@@ -57,10 +60,10 @@ test_expect_success \\\n # about rezrov.\n \n git diff-index -z -C $tree >current\n-cat >expected <<\\EOF\n-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 M\n+cat >expected <<EOF\n+:100644 100644 $orig $c2 M\n COPYING\n-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234\n+:100644 100644 $orig $c1 C1234\n COPYING\n COPYING.1\n EOF\n@@ -82,8 +85,8 @@ test_expect_success \\\n      git update-index --add --remove COPYING COPYING.1'\n \n git diff-index -z -C --find-copies-harder $tree >current\n-cat >expected <<\\EOF\n-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234\n+cat >expected <<EOF\n+:100644 100644 $orig $c1 C1234\n COPYING\n COPYING.1\n EOF\n"},{"id":"381150","messageId":"20190826014344.16008-14-sandals@crustytoothpaste.net","threadId":"51667","inReplyTo":"20190826014344.16008-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 13/14] t4002: make hash independent","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-08-26T01:43:43Z","receivedAt":"2019-08-26T01:44:24Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Factor out the hard-coded object IDs and use test_oid to provide values\nfor both SHA-1 and SHA-256.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t4002-diff-basic.sh | 367 +++++++++++++++++++++++++++++-------------\n 1 file changed, 258 insertions(+), 109 deletions(-)\n\ndiff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh\nindex 3a6c21e825..cbcdd10464 100755\n--- a/t/t4002-diff-basic.sh\n+++ b/t/t4002-diff-basic.sh\n@@ -7,123 +7,272 @@ test_description='Test diff raw-output.\n \n '\n . ./test-lib.sh\n+\n . \"$TEST_DIRECTORY\"/lib-read-tree-m-3way.sh\n \n-cat >.test-plain-OA <<\\EOF\n-:000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A\tAA\n-:000000 100644 0000000000000000000000000000000000000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 A\tAN\n-:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D\tDD\n-:000000 040000 0000000000000000000000000000000000000000 6d50f65d3bdab91c63444294d38f08aeff328e42 A\tDF\n-:100644 000000 141c1f1642328e4bc46a7d801a71da392e66791e 0000000000000000000000000000000000000000 D\tDM\n-:100644 000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 0000000000000000000000000000000000000000 D\tDN\n-:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A\tLL\n-:100644 100644 03f24c8c4700babccfd28b654e7e8eac402ad6cd 103d9f89b50b9aad03054b579be5e7aa665f2d57 M\tMD\n-:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 M\tMM\n-:100644 100644 bd084b0c27c7b6cc34f11d6d0509a29be3caf970 a716d58de4a570e0038f5c307bd8db34daea021f M\tMN\n-:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M\tSS\n-:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 4c86f9a85fbc5e6804ee2e17a797538fbe785bca M\tTT\n-:040000 040000 7d670fdcdb9929f6c7dac196ff78689cd1c566a1 5e5f22072bb39f6e12cf663a57cb634c76eefb49 M\tZ\n+test_oid_init\n+\n+test_oid_cache <<\\EOF\n+aa_1 sha1:ccba72ad3888a3520b39efcf780b9ee64167535d\n+aa_1 sha256:9febfbf18197819b2735c45291f138525d2476d59470f98239647544586ba403\n+\n+aa_2 sha1:6aa2b5335b16431a0ef71e5c0a28be69183cf6a2\n+aa_2 sha256:6eaa3437de83f145a4aaa6ba355303075ade547b128ec6a2cd00a81ff7ce7a56\n+\n+an_1 sha1:7e426fb079479fd67f6d81f984e4ec649a44bc25\n+an_1 sha256:8f92a0bec99e399a38e3bd0e1bf19fbf121e0160efb29b857df79d439f1c4536\n+\n+dd_1 sha1:bcc68ef997017466d5c9094bcf7692295f588c9a\n+dd_1 sha256:07e17428b00639b85485d2b01083d219e2f3e3ba8579e9ca44e9cc8dd554d952\n+\n+df_1 sha1:6d50f65d3bdab91c63444294d38f08aeff328e42\n+df_1 sha256:e367cecc27e9bf5451b1c65828cb21938d36a5f8e39c1b03ad6509cc36bb8e9d\n+\n+df_2 sha1:71420ab81e254145d26d6fc0cddee64c1acd4787\n+df_2 sha256:0f0a86d10347ff6921d03a3c954679f3f1d14fa3d5cd82f57b32c09755f3a47d\n+\n+dfd1 sha1:68a6d8b91da11045cf4aa3a5ab9f2a781c701249\n+dfd1 sha256:f3bd3265b02b6978ce86490d8ad026c573639c974b3de1d9faf30d8d5a77d3d5\n+\n+dm_1 sha1:141c1f1642328e4bc46a7d801a71da392e66791e\n+dm_1 sha256:c89f8656e7b94e21ee5fbaf0e2149bbf783c51edbe2ce110349cac13059ee7ed\n+\n+dm_2 sha1:3c4d8de5fbad08572bab8e10eef8dbb264cf0231\n+dm_2 sha256:83a572e37e0c94086294dae2cecc43d9131afd6f6c906e495c78972230b54988\n+\n+dn_1 sha1:35abde1506ddf806572ff4d407bd06885d0f8ee9\n+dn_1 sha256:775d5852582070e620be63327bfa515fab8f71c7ac3e4f0c3cd6267b4377ba28\n+\n+ll_2 sha1:1d41122ebdd7a640f29d3c9cc4f9d70094374762\n+ll_2 sha256:7917b4948a883cfed0a77d3d5a625dc8577d6ddcc3c6c3bbc56c4d4226a2246d\n+\n+md_1 sha1:03f24c8c4700babccfd28b654e7e8eac402ad6cd\n+md_1 sha256:fc9f30369b978595ad685ba11ca9a17de0af16d79cd4b629975f4f1590033902\n+\n+md_2 sha1:103d9f89b50b9aad03054b579be5e7aa665f2d57\n+md_2 sha256:fc78ec75275628762fe520479a6b2398dec295ce7aabcb1d15e5963c7b4e9317\n+\n+mm_1 sha1:b258508afb7ceb449981bd9d63d2d3e971bf8d34\n+mm_1 sha256:a4b7847d228e900e3000285e240c20fd96f9dd41ce1445305f6eada126d4a04a\n+\n+mm_2 sha1:b431b272d829ff3aa4d1a5085f4394ab4d3305b6\n+mm_2 sha256:3f8b83ea36aacf689bcf1a1290a9a8ed341564d32682ea6f76fea9a979186782\n+\n+mm_3 sha1:19989d4559aae417fedee240ccf2ba315ea4dc2b\n+mm_3 sha256:71b3bfc5747ac033fff9ea0ab39ee453a3af2969890e75d6ef547b87544e2681\n+\n+mn_1 sha1:bd084b0c27c7b6cc34f11d6d0509a29be3caf970\n+mn_1 sha256:47a67450583d7a329eb01a7c4ba644945af72c0ed2c7c95eb5a00d6e46d4d483\n+\n+mn_2 sha1:a716d58de4a570e0038f5c307bd8db34daea021f\n+mn_2 sha256:f95104c1ebe27acb84bac25a7be98c71f6b8d3054b21f357a5be0c524ad97e08\n+\n+nm_1 sha1:c8f25781e8f1792e3e40b74225e20553041b5226\n+nm_1 sha256:09baddc7afaa62e62e152c23c9c3ab94bf15a3894031e227e9be7fe68e1f4e49\n+\n+nm_2 sha1:cdb9a8c3da571502ac30225e9c17beccb8387983\n+nm_2 sha256:58b5227956ac2d2a08d0efa513c0ae37430948b16791ea3869a1308dbf05536d\n+\n+na_1 sha1:15885881ea69115351c09b38371f0348a3fb8c67\n+na_1 sha256:18e4fdd1670cd7968ee23d35bfd29e5418d56fb190c840094c1c57ceee0aad8f\n+\n+nd_1 sha1:a4e179e4291e5536a5e1c82e091052772d2c5a93\n+nd_1 sha256:07dac9b01d00956ea0c65bd993d7de4864aeef2ed3cbb1255d9f1d949fcd6df6\n+\n+ss_1 sha1:40c959f984c8b89a2b02520d17f00d717f024397\n+ss_1 sha256:50fc1b5df74d9910db2f9270993484235f15b69b75b01bcfb53e059289d14af9\n+\n+ss_2 sha1:2ac547ae9614a00d1b28275de608131f7a0e259f\n+ss_2 sha256:a90f02e6044f1497d13db587d22ab12f90150a7d1e084afcf96065fab35ae2bc\n+\n+tt_1 sha1:4ac13458899ab908ef3b1128fa378daefc88d356\n+tt_1 sha256:c53113c7dd5060e86b5b251428bd058f6726f66273c6a24bff1c61a04f498dd3\n+\n+tt_2 sha1:4c86f9a85fbc5e6804ee2e17a797538fbe785bca\n+tt_2 sha256:0775f2a296129a7cf2862b46bc0e88c14d593f2773a3e3fb1c5193db6f5a7e77\n+\n+tt_3 sha1:c4e4a12231b9fa79a0053cb6077fcb21bb5b135a\n+tt_3 sha256:47860f93cdd211f96443e0560f21c57ab6c2f4b0ac27ff03651a352e53fe8484\n+\n+z__1 sha1:7d670fdcdb9929f6c7dac196ff78689cd1c566a1\n+z__1 sha256:44d0f37aff5e51cfcfdd1134c93a6419bcca7b9964f792ffcd5f9b4fcba1ee63\n+\n+z__2 sha1:5e5f22072bb39f6e12cf663a57cb634c76eefb49\n+z__2 sha256:d29de162113190fed104eb5f010820cef4e315f89b9326e8497f7219fb737894\n+\n+z__3 sha1:1ba523955d5160681af65cb776411f574c1e8155\n+z__3 sha256:07422d772b07794ab4369a5648e617719f89c2d2212cbeab05d97214b6471636\n+\n+zaa1 sha1:8acb8e9750e3f644bf323fcf3d338849db106c77\n+zaa1 sha256:e79b029282c8abec2d9f3f7faceaf2a1405e02d1f368e66450ae66cf5b68d1f4\n+\n+zaa2 sha1:6c0b99286d0bce551ac4a7b3dff8b706edff3715\n+zaa2 sha256:c82bd78c3e69ea1796e6b1a7a3ba45bb106c50e819296475b862123d3f5cc5a0\n+\n+zan1 sha1:087494262084cefee7ed484d20c8dc0580791272\n+zan1 sha256:4b159eb3804d05599023dd074f771d06d02870f4ab24a7165add8ac3d703b8d3\n+\n+zdd1 sha1:879007efae624d2b1307214b24a956f0a8d686a8\n+zdd1 sha256:eecfdd4d8092dd0363fb6d4548b54c6afc8982c3ed9b34e393f1d6a921d8eaa3\n+\n+zdm1 sha1:9b541b2275c06e3a7b13f28badf5294e2ae63df4\n+zdm1 sha256:ab136e88e19a843c4bf7713d2090d5a2186ba16a6a80dacc12eeddd256a8e556\n+\n+zdm2 sha1:d77371d15817fcaa57eeec27f770c505ba974ec1\n+zdm2 sha256:1c1a5f57363f46a15d95ce8527b3c2c158d88d16853b4acbf81bd20fd2c89a46\n+\n+zdn1 sha1:beb5d38c55283d280685ea21a0e50cfcc0ca064a\n+zdn1 sha256:0f0eca66183617b0aa5ad74b256540329f841470922ca6760263c996d825eb18\n+\n+zmd1 sha1:d41fda41b7ec4de46b43cb7ea42a45001ae393d5\n+zmd1 sha256:1ed32d481852eddf31a0ce12652a0ad14bf5b7a842667b5dbb0b50f35bf1c80a\n+\n+zmd2 sha1:a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9\n+zmd2 sha256:b238da211b404f8917df2d9c6f7030535e904b2186131007a3c292ec6902f933\n+\n+zmm1 sha1:4ca22bae2527d3d9e1676498a0fba3b355bd1278\n+zmm1 sha256:072b1d85b5f34fabc99dfa46008c5418df68302d3e317430006f49b32d244226\n+\n+zmm2 sha1:61422ba9c2c873416061a88cd40a59a35b576474\n+zmm2 sha256:81dd5d2b3c5cda16fef552256aed4e2ea0802a8450a08f308a92142112ff6dda\n+\n+zmm3 sha1:697aad7715a1e7306ca76290a3dd4208fbaeddfa\n+zmm3 sha256:8b10fab49e9be3414aa5e9a93d0e46f9569053440138a7c19a5eb5536d8e95bf\n+\n+zmn1 sha1:b16d7b25b869f2beb124efa53467d8a1550ad694\n+zmn1 sha256:609e4f75d1295e844c826feeba213acb0b6cfc609adfe8ff705b19e3829ae3e9\n+\n+zmn2 sha1:a5c544c21cfcb07eb80a4d89a5b7d1570002edfd\n+zmn2 sha256:d6d03edf2dc1a3b267a8205de5f41a2ff4b03def8c7ae02052b543fb09d589fc\n+\n+zna1 sha1:d12979c22fff69c59ca9409e7a8fe3ee25eaee80\n+zna1 sha256:b37b80e789e8ea32aa323f004628f02013f632124b0282c7fe00a127d3c64c3c\n+\n+znd1 sha1:a18393c636b98e9bd7296b8b437ea4992b72440c\n+znd1 sha256:af92a22eee8c38410a0c9d2b5135a10aeb052cbc7cf675541ed9a67bfcaf7cf9\n+\n+znm1 sha1:3fdbe17fd013303a2e981e1ca1c6cd6e72789087\n+znm1 sha256:f75aeaa0c11e76918e381c105f0752932c6150e941fec565d24fa31098a13dc1\n+\n+znm2 sha1:7e09d6a3a14bd630913e8c75693cea32157b606d\n+znm2 sha256:938d73cfbaa1c902a84fb5b3afd9736aa0590367fb9bd59c6c4d072ce70fcd6d\n+EOF\n+\n+cat >.test-plain-OA <<EOF\n+:000000 100644 $(test_oid zero) $(test_oid aa_1) A\tAA\n+:000000 100644 $(test_oid zero) $(test_oid an_1) A\tAN\n+:100644 000000 $(test_oid dd_1) $(test_oid zero) D\tDD\n+:000000 040000 $(test_oid zero) $(test_oid df_1) A\tDF\n+:100644 000000 $(test_oid dm_1) $(test_oid zero) D\tDM\n+:100644 000000 $(test_oid dn_1) $(test_oid zero) D\tDN\n+:000000 100644 $(test_oid zero) $(test_oid ll_2) A\tLL\n+:100644 100644 $(test_oid md_1) $(test_oid md_2) M\tMD\n+:100644 100644 $(test_oid mm_1) $(test_oid mm_2) M\tMM\n+:100644 100644 $(test_oid mn_1) $(test_oid mn_2) M\tMN\n+:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M\tSS\n+:100644 100644 $(test_oid tt_1) $(test_oid tt_2) M\tTT\n+:040000 040000 $(test_oid z__1) $(test_oid z__2) M\tZ\n EOF\n \n-cat >.test-recursive-OA <<\\EOF\n-:000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A\tAA\n-:000000 100644 0000000000000000000000000000000000000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 A\tAN\n-:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D\tDD\n-:000000 100644 0000000000000000000000000000000000000000 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 A\tDF/DF\n-:100644 000000 141c1f1642328e4bc46a7d801a71da392e66791e 0000000000000000000000000000000000000000 D\tDM\n-:100644 000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 0000000000000000000000000000000000000000 D\tDN\n-:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A\tLL\n-:100644 100644 03f24c8c4700babccfd28b654e7e8eac402ad6cd 103d9f89b50b9aad03054b579be5e7aa665f2d57 M\tMD\n-:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 M\tMM\n-:100644 100644 bd084b0c27c7b6cc34f11d6d0509a29be3caf970 a716d58de4a570e0038f5c307bd8db34daea021f M\tMN\n-:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M\tSS\n-:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 4c86f9a85fbc5e6804ee2e17a797538fbe785bca M\tTT\n-:000000 100644 0000000000000000000000000000000000000000 8acb8e9750e3f644bf323fcf3d338849db106c77 A\tZ/AA\n-:000000 100644 0000000000000000000000000000000000000000 087494262084cefee7ed484d20c8dc0580791272 A\tZ/AN\n-:100644 000000 879007efae624d2b1307214b24a956f0a8d686a8 0000000000000000000000000000000000000000 D\tZ/DD\n-:100644 000000 9b541b2275c06e3a7b13f28badf5294e2ae63df4 0000000000000000000000000000000000000000 D\tZ/DM\n-:100644 000000 beb5d38c55283d280685ea21a0e50cfcc0ca064a 0000000000000000000000000000000000000000 D\tZ/DN\n-:100644 100644 d41fda41b7ec4de46b43cb7ea42a45001ae393d5 a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 M\tZ/MD\n-:100644 100644 4ca22bae2527d3d9e1676498a0fba3b355bd1278 61422ba9c2c873416061a88cd40a59a35b576474 M\tZ/MM\n-:100644 100644 b16d7b25b869f2beb124efa53467d8a1550ad694 a5c544c21cfcb07eb80a4d89a5b7d1570002edfd M\tZ/MN\n+cat >.test-recursive-OA <<EOF\n+:000000 100644 $(test_oid zero) $(test_oid aa_1) A\tAA\n+:000000 100644 $(test_oid zero) $(test_oid an_1) A\tAN\n+:100644 000000 $(test_oid dd_1) $(test_oid zero) D\tDD\n+:000000 100644 $(test_oid zero) $(test_oid dfd1) A\tDF/DF\n+:100644 000000 $(test_oid dm_1) $(test_oid zero) D\tDM\n+:100644 000000 $(test_oid dn_1) $(test_oid zero) D\tDN\n+:000000 100644 $(test_oid zero) $(test_oid ll_2) A\tLL\n+:100644 100644 $(test_oid md_1) $(test_oid md_2) M\tMD\n+:100644 100644 $(test_oid mm_1) $(test_oid mm_2) M\tMM\n+:100644 100644 $(test_oid mn_1) $(test_oid mn_2) M\tMN\n+:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M\tSS\n+:100644 100644 $(test_oid tt_1) $(test_oid tt_2) M\tTT\n+:000000 100644 $(test_oid zero) $(test_oid zaa1) A\tZ/AA\n+:000000 100644 $(test_oid zero) $(test_oid zan1) A\tZ/AN\n+:100644 000000 $(test_oid zdd1) $(test_oid zero) D\tZ/DD\n+:100644 000000 $(test_oid zdm1) $(test_oid zero) D\tZ/DM\n+:100644 000000 $(test_oid zdn1) $(test_oid zero) D\tZ/DN\n+:100644 100644 $(test_oid zmd1) $(test_oid zmd2) M\tZ/MD\n+:100644 100644 $(test_oid zmm1) $(test_oid zmm2) M\tZ/MM\n+:100644 100644 $(test_oid zmn1) $(test_oid zmn2) M\tZ/MN\n EOF\n-cat >.test-plain-OB <<\\EOF\n-:000000 100644 0000000000000000000000000000000000000000 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 A\tAA\n-:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D\tDD\n-:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A\tDF\n-:100644 100644 141c1f1642328e4bc46a7d801a71da392e66791e 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 M\tDM\n-:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A\tLL\n-:100644 000000 03f24c8c4700babccfd28b654e7e8eac402ad6cd 0000000000000000000000000000000000000000 D\tMD\n-:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 19989d4559aae417fedee240ccf2ba315ea4dc2b M\tMM\n-:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A\tNA\n-:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D\tND\n-:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M\tNM\n-:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M\tSS\n-:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M\tTT\n-:040000 040000 7d670fdcdb9929f6c7dac196ff78689cd1c566a1 1ba523955d5160681af65cb776411f574c1e8155 M\tZ\n+cat >.test-plain-OB <<EOF\n+:000000 100644 $(test_oid zero) $(test_oid aa_2) A\tAA\n+:100644 000000 $(test_oid dd_1) $(test_oid zero) D\tDD\n+:000000 100644 $(test_oid zero) $(test_oid df_2) A\tDF\n+:100644 100644 $(test_oid dm_1) $(test_oid dm_2) M\tDM\n+:000000 100644 $(test_oid zero) $(test_oid ll_2) A\tLL\n+:100644 000000 $(test_oid md_1) $(test_oid zero) D\tMD\n+:100644 100644 $(test_oid mm_1) $(test_oid mm_3) M\tMM\n+:000000 100644 $(test_oid zero) $(test_oid na_1) A\tNA\n+:100644 000000 $(test_oid nd_1) $(test_oid zero) D\tND\n+:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M\tNM\n+:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M\tSS\n+:100644 100644 $(test_oid tt_1) $(test_oid tt_3) M\tTT\n+:040000 040000 $(test_oid z__1) $(test_oid z__3) M\tZ\n EOF\n-cat >.test-recursive-OB <<\\EOF\n-:000000 100644 0000000000000000000000000000000000000000 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 A\tAA\n-:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D\tDD\n-:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A\tDF\n-:100644 100644 141c1f1642328e4bc46a7d801a71da392e66791e 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 M\tDM\n-:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A\tLL\n-:100644 000000 03f24c8c4700babccfd28b654e7e8eac402ad6cd 0000000000000000000000000000000000000000 D\tMD\n-:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 19989d4559aae417fedee240ccf2ba315ea4dc2b M\tMM\n-:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A\tNA\n-:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D\tND\n-:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M\tNM\n-:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M\tSS\n-:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M\tTT\n-:000000 100644 0000000000000000000000000000000000000000 6c0b99286d0bce551ac4a7b3dff8b706edff3715 A\tZ/AA\n-:100644 000000 879007efae624d2b1307214b24a956f0a8d686a8 0000000000000000000000000000000000000000 D\tZ/DD\n-:100644 100644 9b541b2275c06e3a7b13f28badf5294e2ae63df4 d77371d15817fcaa57eeec27f770c505ba974ec1 M\tZ/DM\n-:100644 000000 d41fda41b7ec4de46b43cb7ea42a45001ae393d5 0000000000000000000000000000000000000000 D\tZ/MD\n-:100644 100644 4ca22bae2527d3d9e1676498a0fba3b355bd1278 697aad7715a1e7306ca76290a3dd4208fbaeddfa M\tZ/MM\n-:000000 100644 0000000000000000000000000000000000000000 d12979c22fff69c59ca9409e7a8fe3ee25eaee80 A\tZ/NA\n-:100644 000000 a18393c636b98e9bd7296b8b437ea4992b72440c 0000000000000000000000000000000000000000 D\tZ/ND\n-:100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d M\tZ/NM\n+cat >.test-recursive-OB <<EOF\n+:000000 100644 $(test_oid zero) $(test_oid aa_2) A\tAA\n+:100644 000000 $(test_oid dd_1) $(test_oid zero) D\tDD\n+:000000 100644 $(test_oid zero) $(test_oid df_2) A\tDF\n+:100644 100644 $(test_oid dm_1) $(test_oid dm_2) M\tDM\n+:000000 100644 $(test_oid zero) $(test_oid ll_2) A\tLL\n+:100644 000000 $(test_oid md_1) $(test_oid zero) D\tMD\n+:100644 100644 $(test_oid mm_1) $(test_oid mm_3) M\tMM\n+:000000 100644 $(test_oid zero) $(test_oid na_1) A\tNA\n+:100644 000000 $(test_oid nd_1) $(test_oid zero) D\tND\n+:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M\tNM\n+:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M\tSS\n+:100644 100644 $(test_oid tt_1) $(test_oid tt_3) M\tTT\n+:000000 100644 $(test_oid zero) $(test_oid zaa2) A\tZ/AA\n+:100644 000000 $(test_oid zdd1) $(test_oid zero) D\tZ/DD\n+:100644 100644 $(test_oid zdm1) $(test_oid zdm2) M\tZ/DM\n+:100644 000000 $(test_oid zmd1) $(test_oid zero) D\tZ/MD\n+:100644 100644 $(test_oid zmm1) $(test_oid zmm3) M\tZ/MM\n+:000000 100644 $(test_oid zero) $(test_oid zna1) A\tZ/NA\n+:100644 000000 $(test_oid znd1) $(test_oid zero) D\tZ/ND\n+:100644 100644 $(test_oid znm1) $(test_oid znm2) M\tZ/NM\n EOF\n-cat >.test-plain-AB <<\\EOF\n-:100644 100644 ccba72ad3888a3520b39efcf780b9ee64167535d 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 M\tAA\n-:100644 000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 0000000000000000000000000000000000000000 D\tAN\n-:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A\tDF\n-:040000 000000 6d50f65d3bdab91c63444294d38f08aeff328e42 0000000000000000000000000000000000000000 D\tDF\n-:000000 100644 0000000000000000000000000000000000000000 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 A\tDM\n-:000000 100644 0000000000000000000000000000000000000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 A\tDN\n-:100644 000000 103d9f89b50b9aad03054b579be5e7aa665f2d57 0000000000000000000000000000000000000000 D\tMD\n-:100644 100644 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 19989d4559aae417fedee240ccf2ba315ea4dc2b M\tMM\n-:100644 100644 a716d58de4a570e0038f5c307bd8db34daea021f bd084b0c27c7b6cc34f11d6d0509a29be3caf970 M\tMN\n-:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A\tNA\n-:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D\tND\n-:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M\tNM\n-:100644 100644 4c86f9a85fbc5e6804ee2e17a797538fbe785bca c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M\tTT\n-:040000 040000 5e5f22072bb39f6e12cf663a57cb634c76eefb49 1ba523955d5160681af65cb776411f574c1e8155 M\tZ\n+cat >.test-plain-AB <<EOF\n+:100644 100644 $(test_oid aa_1) $(test_oid aa_2) M\tAA\n+:100644 000000 $(test_oid an_1) $(test_oid zero) D\tAN\n+:000000 100644 $(test_oid zero) $(test_oid df_2) A\tDF\n+:040000 000000 $(test_oid df_1) $(test_oid zero) D\tDF\n+:000000 100644 $(test_oid zero) $(test_oid dm_2) A\tDM\n+:000000 100644 $(test_oid zero) $(test_oid dn_1) A\tDN\n+:100644 000000 $(test_oid md_2) $(test_oid zero) D\tMD\n+:100644 100644 $(test_oid mm_2) $(test_oid mm_3) M\tMM\n+:100644 100644 $(test_oid mn_2) $(test_oid mn_1) M\tMN\n+:000000 100644 $(test_oid zero) $(test_oid na_1) A\tNA\n+:100644 000000 $(test_oid nd_1) $(test_oid zero) D\tND\n+:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M\tNM\n+:100644 100644 $(test_oid tt_2) $(test_oid tt_3) M\tTT\n+:040000 040000 $(test_oid z__2) $(test_oid z__3) M\tZ\n EOF\n-cat >.test-recursive-AB <<\\EOF\n-:100644 100644 ccba72ad3888a3520b39efcf780b9ee64167535d 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 M\tAA\n-:100644 000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 0000000000000000000000000000000000000000 D\tAN\n-:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A\tDF\n-:100644 000000 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 0000000000000000000000000000000000000000 D\tDF/DF\n-:000000 100644 0000000000000000000000000000000000000000 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 A\tDM\n-:000000 100644 0000000000000000000000000000000000000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 A\tDN\n-:100644 000000 103d9f89b50b9aad03054b579be5e7aa665f2d57 0000000000000000000000000000000000000000 D\tMD\n-:100644 100644 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 19989d4559aae417fedee240ccf2ba315ea4dc2b M\tMM\n-:100644 100644 a716d58de4a570e0038f5c307bd8db34daea021f bd084b0c27c7b6cc34f11d6d0509a29be3caf970 M\tMN\n-:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A\tNA\n-:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D\tND\n-:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M\tNM\n-:100644 100644 4c86f9a85fbc5e6804ee2e17a797538fbe785bca c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M\tTT\n-:100644 100644 8acb8e9750e3f644bf323fcf3d338849db106c77 6c0b99286d0bce551ac4a7b3dff8b706edff3715 M\tZ/AA\n-:100644 000000 087494262084cefee7ed484d20c8dc0580791272 0000000000000000000000000000000000000000 D\tZ/AN\n-:000000 100644 0000000000000000000000000000000000000000 d77371d15817fcaa57eeec27f770c505ba974ec1 A\tZ/DM\n-:000000 100644 0000000000000000000000000000000000000000 beb5d38c55283d280685ea21a0e50cfcc0ca064a A\tZ/DN\n-:100644 000000 a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 0000000000000000000000000000000000000000 D\tZ/MD\n-:100644 100644 61422ba9c2c873416061a88cd40a59a35b576474 697aad7715a1e7306ca76290a3dd4208fbaeddfa M\tZ/MM\n-:100644 100644 a5c544c21cfcb07eb80a4d89a5b7d1570002edfd b16d7b25b869f2beb124efa53467d8a1550ad694 M\tZ/MN\n-:000000 100644 0000000000000000000000000000000000000000 d12979c22fff69c59ca9409e7a8fe3ee25eaee80 A\tZ/NA\n-:100644 000000 a18393c636b98e9bd7296b8b437ea4992b72440c 0000000000000000000000000000000000000000 D\tZ/ND\n-:100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d M\tZ/NM\n+cat >.test-recursive-AB <<EOF\n+:100644 100644 $(test_oid aa_1) $(test_oid aa_2) M\tAA\n+:100644 000000 $(test_oid an_1) $(test_oid zero) D\tAN\n+:000000 100644 $(test_oid zero) $(test_oid df_2) A\tDF\n+:100644 000000 $(test_oid dfd1) $(test_oid zero) D\tDF/DF\n+:000000 100644 $(test_oid zero) $(test_oid dm_2) A\tDM\n+:000000 100644 $(test_oid zero) $(test_oid dn_1) A\tDN\n+:100644 000000 $(test_oid md_2) $(test_oid zero) D\tMD\n+:100644 100644 $(test_oid mm_2) $(test_oid mm_3) M\tMM\n+:100644 100644 $(test_oid mn_2) $(test_oid mn_1) M\tMN\n+:000000 100644 $(test_oid zero) $(test_oid na_1) A\tNA\n+:100644 000000 $(test_oid nd_1) $(test_oid zero) D\tND\n+:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M\tNM\n+:100644 100644 $(test_oid tt_2) $(test_oid tt_3) M\tTT\n+:100644 100644 $(test_oid zaa1) $(test_oid zaa2) M\tZ/AA\n+:100644 000000 $(test_oid zan1) $(test_oid zero) D\tZ/AN\n+:000000 100644 $(test_oid zero) $(test_oid zdm2) A\tZ/DM\n+:000000 100644 $(test_oid zero) $(test_oid zdn1) A\tZ/DN\n+:100644 000000 $(test_oid zmd2) $(test_oid zero) D\tZ/MD\n+:100644 100644 $(test_oid zmm2) $(test_oid zmm3) M\tZ/MM\n+:100644 100644 $(test_oid zmn2) $(test_oid zmn1) M\tZ/MN\n+:000000 100644 $(test_oid zero) $(test_oid zna1) A\tZ/NA\n+:100644 000000 $(test_oid znd1) $(test_oid zero) D\tZ/ND\n+:100644 100644 $(test_oid znm1) $(test_oid znm2) M\tZ/NM\n EOF\n \n cmp_diff_files_output () {\n"},{"id":"410521","messageId":"20201123120111.13567-1-avarab@gmail.com","threadId":"51667","inReplyTo":"20190826014344.16008-11-sandals@crustytoothpaste.net","subject":"[PATCH] mktag: don't check SHA-1 object length under SHA-256","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-23T12:01:11Z","receivedAt":"2020-11-23T12:01:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the hardcoded minimum tag length size to vary based on whether\nwe're operating in the SHA-1 or SHA-256 mode, and update the \"mktag\"\ndocumentation, tests and code comments to reflect that this command\ncan take either SHA-1 or SHA-256 input.\n\nLet's make the code more self-documenting by verbosely inlining what a\nminimum tag looks like. The fixed-string strlen() will be optimized\naway by any modern compiler. This also future-proofs us for any future\nhash function transition.\n\nThen change the tests amended in acb49d1cc8b (t3800: make hash-size\nindependent, 2019-08-18) even more for SHA-256. Some of the tests were\nfailing for the wrong reasons. E.g. yes <some sha-1 length garbage> is\nan invalid SHA-1, but we should test <some sha256 length garbage> when\nunder SHA-256. For testing an invalid non-hexadecimal object ID let's\nuse ROT13.\n\nFor the \"object line label check\" test the \"139e9b339...\" SHA-1 didn't\nexist, but what's actually being tested there is getting \"xxxxxx\"\ninstead of \"object\", so let's provide a valid SHA there\ninstead. There's no need to make or hardcode a nonexisting object ID.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt |  2 +-\n builtin/mktag.c             | 26 +++++++++++++-------\n t/t3800-mktag.sh            | 47 ++++++++++++++++++++++++++-----------\n 3 files changed, 52 insertions(+), 23 deletions(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex fa6a7561236..bbcc0a086bf 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -23,7 +23,7 @@ Tag Format\n A tag signature file, to be fed to this command's standard input,\n has a very simple fixed format: four lines of\n \n-  object <sha1>\n+  object <sha>\n   type <typename>\n   tag <tagname>\n   tagger <tagger>\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 4982d3a93ef..3fa17243e34 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -5,13 +5,15 @@\n \n /*\n  * A signature file has a very simple fixed format: four lines\n- * of \"object <sha1>\" + \"type <typename>\" + \"tag <tagname>\" +\n+ * of \"object <sha>\" + \"type <typename>\" + \"tag <tagname>\" +\n  * \"tagger <committer>\", followed by a blank line, a free-form tag\n  * message and a signature block that git itself doesn't care about,\n  * but that can be verified with gpg or similar.\n  *\n- * The first four lines are guaranteed to be at least 83 bytes:\n- * \"object <sha1>\\n\" is 48 bytes, \"type tag\\n\" at 9 bytes is the\n+ * The first four lines are guaranteed to be either 83 or 107 bytes;\n+ * depending on whether we're referencing a SHA-1 or SHA-256 tag.\n+ *\n+ * \"object <sha1>\\n\" is 48 or a 72 bytes, \"type tag\\n\" at 9 bytes is the\n  * shortest possible type-line, \"tag .\\n\" at 6 bytes is the shortest\n  * single-character-tag line, and \"tagger . <> 0 +0000\\n\" at 20 bytes is\n  * the shortest possible tagger-line.\n@@ -46,9 +48,17 @@ static int verify_tag(char *buffer, unsigned long size)\n \tstruct object_id oid;\n \tconst char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;\n \tsize_t len;\n-\n-\tif (size < 84)\n-\t\treturn error(\"wanna fool me ? you obviously got the size wrong !\");\n+\tint minimum_size =\n+\t\t/* Minimum possible input, see t/t3800-mktag.sh */\n+\t\tstrlen(\"object \") + the_hash_algo->hexsz + strlen(\"\\n\") +\n+\t\tstrlen(\"type tag\\n\") +\n+\t\tstrlen(\"tag x\\n\") +\n+\t\tstrlen(\"tagger . <> 0 +0000\\n\") +\n+\t\tstrlen(\"\\n\");\n+\n+\tif (size < minimum_size)\n+\t\treturn error(\"got %\"PRIuMAX\" bytes of input, need at least %d bytes\",\n+\t\t\t     size, minimum_size);\n \n \tbuffer[size] = 0;\n \n@@ -58,7 +68,7 @@ static int verify_tag(char *buffer, unsigned long size)\n \t\treturn error(\"char%d: does not start with \\\"object \\\"\", 0);\n \n \tif (parse_oid_hex(object + 7, &oid, &p))\n-\t\treturn error(\"char%d: could not get SHA1 hash\", 7);\n+\t\treturn error(\"char%d: expected object ID, got garbage\", 7);\n \n \t/* Verify type line */\n \ttype_line = p + 1;\n@@ -166,7 +176,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \t}\n \n \t/* Verify it for some basic sanity: it needs to start with\n-\t   \"object <sha1>\\ntype\\ntagger \" */\n+\t   \"object <sha>\\ntype\\ntagger \" */\n \tif (verify_tag(buf.buf, buf.len) < 0)\n \t\tdie(\"invalid tag signature file\");\n \ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex d696aa4e52e..93a19bb8df9 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -26,24 +26,42 @@ test_expect_success 'setup' '\n \techo Hello >A &&\n \tgit update-index --add A &&\n \tgit commit -m \"Initial commit\" &&\n-\thead=$(git rev-parse --verify HEAD)\n+\thead=$(git rev-parse --verify HEAD) &&\n+\n+\tgit tag -m\"some tag\" annotated &&\n+\tannotated=$(git rev-parse --verify annotated)\n '\n \n ############################################################\n #  1. length check\n-\n-cat >tag.sig <<EOF\n-too short for a tag\n-EOF\n-\n-check_verify_failure 'Tag object length check' \\\n-\t'^error: .*size wrong.*$'\n+for objType in t ta tag\n+do\n+\tcat >tag.sig <<-EOF\n+\tobject $annotated\n+\ttype $objType\n+\ttag x\n+\ttagger . <> 0 +0000\n+\n+\tEOF\n+\tlen=$(wc -c tag.sig)\n+\n+\tif test $objType = \"tag\"\n+\tthen\n+\t\ttest_expect_success \"Tag object length check $len passed\" '\n+\t\t\tgit mktag <tag.sig >.git/refs/tags/x 2>message &&\n+\t\t\tgit rev-parse refs/tags/x\n+\t\t'\n+\telse\n+\t\tcheck_verify_failure \"Tag object length check on length $len\" \\\n+\t\t\t'^error: got .* bytes of input, need at least'\n+\tfi\n+done\n \n ############################################################\n #  2. object line label check\n \n cat >tag.sig <<EOF\n-xxxxxx 139e9b33986b1c2670fff52c5067603117b3e895\n+xxxxxx $head\n type tag\n tag mytag\n tagger . <> 0 +0000\n@@ -53,17 +71,18 @@ EOF\n check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n \n ############################################################\n-#  3. object line SHA1 check\n+#  3. object line SHA check\n \n+invalid_sha=$(echo $head | tr A-Za-z N-ZA-Mn-za-m)\n cat >tag.sig <<EOF\n-object zz9e9b33986b1c2670fff52c5067603117b3e895\n+object $invalid_sha\n type tag\n tag mytag\n tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"object\" line SHA1 check' '^error: char7: .*SHA1 hash$'\n+check_verify_failure '\"object\" line object check' '^error: char7: .*expected object ID, got garbage'\n \n ############################################################\n #  4. type line label check\n@@ -125,7 +144,7 @@ check_verify_failure '\"type\" line type-name length check' \\\n \t'^error: char.*: type too long$'\n \n ############################################################\n-#  9. verify object (SHA1/type) check\n+#  9. verify object (SHA/type) check\n \n cat >tag.sig <<EOF\n object $(test_oid deadbeef)\n@@ -135,7 +154,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure 'verify object (SHA1/type) check' \\\n+check_verify_failure 'verify object (SHA/type) check' \\\n \t'^error: char7: could not verify object.*$'\n \n ############################################################\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410546","messageId":"xmqqlferoq1m.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201123120111.13567-1-avarab@gmail.com","subject":"Re: [PATCH] mktag: don't check SHA-1 object length under SHA-256","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-23T19:01:57Z","receivedAt":"2020-11-23T19:02:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> diff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\n> index fa6a7561236..bbcc0a086bf 100644\n> --- a/Documentation/git-mktag.txt\n> +++ b/Documentation/git-mktag.txt\n> @@ -23,7 +23,7 @@ Tag Format\n>  A tag signature file, to be fed to this command's standard input,\n>  has a very simple fixed format: four lines of\n>  \n> -  object <sha1>\n> +  object <sha>\n\nPerhaps <hash>? or <objectname>.\n\n> diff --git a/builtin/mktag.c b/builtin/mktag.c\n> index 4982d3a93ef..3fa17243e34 100644\n> --- a/builtin/mktag.c\n> +++ b/builtin/mktag.c\n> @@ -5,13 +5,15 @@\n>  \n>  /*\n>   * A signature file has a very simple fixed format: four lines\n> - * of \"object <sha1>\" + \"type <typename>\" + \"tag <tagname>\" +\n> + * of \"object <sha>\" + \"type <typename>\" + \"tag <tagname>\" +\n\nDitto.\n\n>   * \"tagger <committer>\", followed by a blank line, a free-form tag\n>   * message and a signature block that git itself doesn't care about,\n>   * but that can be verified with gpg or similar.\n>   *\n> + * The first four lines are guaranteed to be either 83 or 107 bytes;\n> + * depending on whether we're referencing a SHA-1 or SHA-256 tag.\n> + *\n> + * \"object <sha1>\\n\" is 48 or a 72 bytes, \"type tag\\n\" at 9 bytes is the\n\nAt least <sha> to be consistent with the above, or <hash>.\n\n>   * shortest possible type-line, \"tag .\\n\" at 6 bytes is the shortest\n>   * single-character-tag line, and \"tagger . <> 0 +0000\\n\" at 20 bytes is\n>   * the shortest possible tagger-line.\n> @@ -46,9 +48,17 @@ static int verify_tag(char *buffer, unsigned long size)\n>  \tstruct object_id oid;\n>  \tconst char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;\n>  \tsize_t len;\n> -\n> -\tif (size < 84)\n> -\t\treturn error(\"wanna fool me ? you obviously got the size wrong !\");\n> +\tint minimum_size =\n> +\t\t/* Minimum possible input, see t/t3800-mktag.sh */\n> +\t\tstrlen(\"object \") + the_hash_algo->hexsz + strlen(\"\\n\") +\n> +\t\tstrlen(\"type tag\\n\") +\n> +\t\tstrlen(\"tag x\\n\") +\n> +\t\tstrlen(\"tagger . <> 0 +0000\\n\") +\n> +\t\tstrlen(\"\\n\");\n> +\n> +\tif (size < minimum_size)\n> +\t\treturn error(\"got %\"PRIuMAX\" bytes of input, need at least %d bytes\",\n> +\t\t\t     size, minimum_size);\n\nI agree with the patch that this message is not _(\"marked for\ntranslation\"), as it is output from a plumbing.\n\nYou need to cast \"size\" as \"(uintmax_t)size\" here, probably.  \n\n\n> @@ -58,7 +68,7 @@ static int verify_tag(char *buffer, unsigned long size)\n>  \t\treturn error(\"char%d: does not start with \\\"object \\\"\", 0);\n>  \n>  \tif (parse_oid_hex(object + 7, &oid, &p))\n> -\t\treturn error(\"char%d: could not get SHA1 hash\", 7);\n> +\t\treturn error(\"char%d: expected object ID, got garbage\", 7);\n\nHere you say object ID, which is better than <hash> or <sha>.  Let's\nbe consistent (I'd say \"object name\" if I were choosing which to\nuse).\n\n>  \t/* Verify it for some basic sanity: it needs to start with\n> -\t   \"object <sha1>\\ntype\\ntagger \" */\n> +\t   \"object <sha>\\ntype\\ntagger \" */\n\nHere it is <sha>.\n\n> diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\n> index d696aa4e52e..93a19bb8df9 100755\n> --- a/t/t3800-mktag.sh\n> +++ b/t/t3800-mktag.sh\n> @@ -26,24 +26,42 @@ test_expect_success 'setup' '\n>  \techo Hello >A &&\n>  \tgit update-index --add A &&\n>  \tgit commit -m \"Initial commit\" &&\n> -\thead=$(git rev-parse --verify HEAD)\n> +\thead=$(git rev-parse --verify HEAD) &&\n> +\n> +\tgit tag -m\"some tag\" annotated &&\n\nA SP between -m and its argument, like four lines above?\n\n> +\tannotated=$(git rev-parse --verify annotated)\n>  '\n>  \n>  ############################################################\n>  #  1. length check\n> +for objType in t ta tag\n\ncute ;-)\n\n> +do\n> +\tcat >tag.sig <<-EOF\n> +\tobject $annotated\n> +\ttype $objType\n> +\ttag x\n> +\ttagger . <> 0 +0000\n> +\n> +\tEOF\n> +\tlen=$(wc -c tag.sig)\n> +\n> +\tif test $objType = \"tag\"\n> +\tthen\n> +\t\ttest_expect_success \"Tag object length check $len passed\" '\n\nHere $len may see excess leading whitespace depending on what \"wc\n-c\" on the platform does, but the only effect of that is a bit\nuglier test title, so it would be oK.\n\n> +\t\t\tgit mktag <tag.sig >.git/refs/tags/x 2>message &&\n\nDo we use \"message\" in any way?  \n\nLet's not write directly into filesystem; instead do\n\n\t\t\ttagobj=$(git mktag <tag.sig) &&\n\t\t\tgit update-ref refs/tags/x $tagobj &&\n\nor something like that.\n\n> +\t\t\tgit rev-parse refs/tags/x\n> +\t\t'\n> +\telse\n> +\t\tcheck_verify_failure \"Tag object length check on length $len\" \\\n> +\t\t\t'^error: got .* bytes of input, need at least'\n\nOK.  This is not an issue with this patch, but I do not know why we\nwant a subshell in check_verify_failure.  May want to clean it up\nbut not in this patch.\n\n> +\tfi\n> +done\n>  \n>  ############################################################\n>  #  2. object line label check\n>  \n>  cat >tag.sig <<EOF\n> -xxxxxx 139e9b33986b1c2670fff52c5067603117b3e895\n> +xxxxxx $head\n>  type tag\n>  tag mytag\n>  tagger . <> 0 +0000\n> @@ -53,17 +71,18 @@ EOF\n>  check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n>  \n>  ############################################################\n> -#  3. object line SHA1 check\n> +#  3. object line SHA check\n\nYou say \"object\" or \"object ID\" to the tester below; let's use that\nconsistently instead of SHA.\n\n> +invalid_sha=$(echo $head | tr A-Za-z N-ZA-Mn-za-m)\n>  cat >tag.sig <<EOF\n> -object zz9e9b33986b1c2670fff52c5067603117b3e895\n> +object $invalid_sha\n>  type tag\n>  tag mytag\n>  tagger . <> 0 +0000\n>  \n>  EOF\n>  \n> -check_verify_failure '\"object\" line SHA1 check' '^error: char7: .*SHA1 hash$'\n> +check_verify_failure '\"object\" line object check' '^error: char7: .*expected object ID, got garbage'\n>  \n>  ############################################################\n>  #  4. type line label check\n> @@ -125,7 +144,7 @@ check_verify_failure '\"type\" line type-name length check' \\\n>  \t'^error: char.*: type too long$'\n>  \n>  ############################################################\n> -#  9. verify object (SHA1/type) check\n> +#  9. verify object (SHA/type) check\n>  \n>  cat >tag.sig <<EOF\n>  object $(test_oid deadbeef)\n> @@ -135,7 +154,7 @@ tagger . <> 0 +0000\n>  \n>  EOF\n>  \n> -check_verify_failure 'verify object (SHA1/type) check' \\\n> +check_verify_failure 'verify object (SHA/type) check' \\\n>  \t'^error: char7: could not verify object.*$'\n>  \n>  ############################################################\n\nDitto.\n\nThanks.\n"},{"id":"410571","messageId":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","threadId":"51667","inReplyTo":"20201123120111.13567-1-avarab@gmail.com","subject":"Re: [PATCH] mktag: don't check SHA-1 object length under SHA-256","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2020-11-23T21:34:21Z","receivedAt":"2020-11-23T21:34:44Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Nov 23, 2020 at 01:01:11PM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> @@ -46,9 +48,17 @@ static int verify_tag(char *buffer, unsigned long size)\n>  \tstruct object_id oid;\n>  \tconst char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;\n>  \tsize_t len;\n> -\n> -\tif (size < 84)\n> -\t\treturn error(\"wanna fool me ? you obviously got the size wrong !\");\n> +\tint minimum_size =\n> +\t\t/* Minimum possible input, see t/t3800-mktag.sh */\n> +\t\tstrlen(\"object \") + the_hash_algo->hexsz + strlen(\"\\n\") +\n> +\t\tstrlen(\"type tag\\n\") +\n> +\t\tstrlen(\"tag x\\n\") +\n> +\t\tstrlen(\"tagger . <> 0 +0000\\n\") +\n> +\t\tstrlen(\"\\n\");\n\nThis is an obvious improvement, but I have to wonder whether this kind\nof \"minimum size\" up-front check is really buying us much. We do a lot\nof fixed-size memcmps in the rest of the function.\n\nIn the early bits, like:\n\n  if (memcmp(type_line - 1, \"\\ntype \", 6))\n          return error(\"char%d: could not find \\\"\\\\ntype \\\"\", 47);\n\nthat is saving us from going off the edge of a too-small input (though\nwow, that bare \"47\" is really horrific).\n\nBut later, we'll do this:\n\n  if (memcmp(tagger_line, \"tagger \", 7))\n           return error(\"char%\"PRIuMAX\": could not find \\\"tagger \\\"\",\n                   (uintmax_t) (tagger_line - buffer));\n\nWe've already parsed \"tag x\" at this point, and \"x\" can be arbitrarily\nlong. Are we sure we have 7 bytes of the buffer left?\n\nThe buffer always has a trailing NUL in it, so it would never match but\nmemcmp() is allowed to look at the whole thing (e.g., it can start at\nthe end, or do quadword comparisons). And that usually wouldn't matter,\nbut may depending on how the strbuf grows, and whether our heap buffer\nis near the end of the last allocate page. Try this:\n\n  git init\n  git commit --allow-empty -m foo\n  {\n    echo \"object $(git rev-parse HEAD)\"\n    echo \"type commit\"\n    perl -e 'print \"tag \"; print \"a\" x 4026; print \"\\n\"'\n  } | git mktag\n\nwhich ASan will complain about, since the \"tagger\" memcmp reads past the\nend of the buffer (I found 4026 by running that in a loop of 1..4096).\n\nI think this whole thing would be better written to just parse\nleft-to-right within the buffer, using bits like skip_prefix() that rely\non us having a trailing NUL (which we always will, and there should not\nbe one inside the header section).\n\nThis is how fsck_tag() does it. In fact, it seems like we could just\nreuse fsck_tag() here. It wants to pass an oid to report(), but it would\nbe OK to use null_oid here; ultimately it just ends up back in our\ncallback error_func().\n\n-Peff\n"},{"id":"410572","messageId":"X7wrbI/pefHJsfdY@coredump.intra.peff.net","threadId":"51667","inReplyTo":"xmqqlferoq1m.fsf@gitster.c.googlers.com","subject":"Re: [PATCH] mktag: don't check SHA-1 object length under SHA-256","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2020-11-23T21:36:44Z","receivedAt":"2020-11-23T21:36:48Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Nov 23, 2020 at 11:01:57AM -0800, Junio C Hamano wrote:\n\n> > @@ -58,7 +68,7 @@ static int verify_tag(char *buffer, unsigned long size)\n> >  \t\treturn error(\"char%d: does not start with \\\"object \\\"\", 0);\n> >  \n> >  \tif (parse_oid_hex(object + 7, &oid, &p))\n> > -\t\treturn error(\"char%d: could not get SHA1 hash\", 7);\n> > +\t\treturn error(\"char%d: expected object ID, got garbage\", 7);\n> \n> Here you say object ID, which is better than <hash> or <sha>.  Let's\n> be consistent (I'd say \"object name\" if I were choosing which to\n> use).\n\nIt might just be me, but \"object name\" makes me think we'd take any name\n(e.g., a refname that resolves to an object), whereas \"object id\" would\nmean the object's hash specifically. And in this instance we only allow\nthe latter.\n\nI agree very much with your other comments that if we are changing\nthese, we should get away from <sha> completely.\n\n-Peff\n"},{"id":"410579","messageId":"xmqqft4zn2f7.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"X7wrbI/pefHJsfdY@coredump.intra.peff.net","subject":"Re: [PATCH] mktag: don't check SHA-1 object length under SHA-256","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-11-23T22:17:32Z","receivedAt":"2020-11-23T22:17:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> It might just be me, but \"object name\" makes me think we'd take any name\n> (e.g., a refname that resolves to an object), whereas \"object id\" would\n> mean the object's hash specifically. And in this instance we only allow\n> the latter.\n\nYeah, but glossary-content is very much explicit about this.  \"name\"\nis the full hexadecimal hash, \"identifier\" is a synonym for it.  And\n\"id\" does not even appear to be defined.  We used to call \"any name\nthat refers to an object\" an \"extended SHA-1\", but I haven't seen\nthe phrase used for a long time on the list.\n\n> I agree very much with your other comments that if we are changing\n> these, we should get away from <sha> completely.\n\nYup.\n\nI've always found it cumbersome that, when I want to mean a full hex\nrepresentation, I have to say \"40-byte object name\".  It is not even\ntechnically correct these days with SHA-256.\n\nWe'd probably need to update the glossary to make sure we have ways\nto refer to \"a way, any way, to name an object\" and to \"the\nhexadecimal representation of the full hash value that refers to an\nobject\".  Here is my attempt to redefine \"object ID\" to be the\nnarrower one, while losing \"usually a 40-character hex\" from \"object\nname\".  I am not sure if I like the result as a whole, but it\ncertainly is nice to have a word or phrase shorter than \"full hex\nobject name\" to refer to it.\n\nThanks.\n\n Documentation/glossary-content.txt | 15 +++++++++++----\n 1 file changed, 11 insertions(+), 4 deletions(-)\n\ndiff --git c/Documentation/glossary-content.txt w/Documentation/glossary-content.txt\nindex 090c888335..e2ab920911 100644\n--- c/Documentation/glossary-content.txt\n+++ w/Documentation/glossary-content.txt\n@@ -262,13 +262,20 @@ This commit is referred to as a \"merge commit\", or sometimes just a\n \tidentified by its <<def_object_name,object name>>. The objects usually\n \tlive in `$GIT_DIR/objects/`.\n \n+[[def_object_id]]object ID::\n+\tSynonym for <<def_object_identifier,object identifier>>.\n+\n [[def_object_identifier]]object identifier::\n-\tSynonym for <<def_object_name,object name>>.\n+\tAn <<def_object_name, object name>> written as an\n+\tunabbreviated hexadecimal representation of the hash value\n+\tthat uniquely identifies an <<def_object,object>>.\n+\tAlso colloquially called <<def_SHA1,SHA-1>>.\n \n [[def_object_name]]object name::\n-\tThe unique identifier of an <<def_object,object>>.  The\n-\tobject name is usually represented by a 40 character\n-\thexadecimal string.  Also colloquially called <<def_SHA1,SHA-1>>.\n+\tA name that identifies an <<def_object,object>> uniquely,\n+\twhich can be given in various ways, including but not\n+\tlimited to, the object's full <<def_object_identifier,object\n+\tidentifier>>, a <<def_ref,ref>> that refers to the object.\n \n [[def_object_type]]object type::\n \tOne of the identifiers \"<<def_commit_object,commit>>\",\n"},{"id":"410609","messageId":"X7xYFPGthu8BMWpH@coredump.intra.peff.net","threadId":"51667","inReplyTo":"xmqqft4zn2f7.fsf@gitster.c.googlers.com","subject":"Re: [PATCH] mktag: don't check SHA-1 object length under SHA-256","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2020-11-24T00:47:16Z","receivedAt":"2020-11-24T00:47:19Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Nov 23, 2020 at 02:17:32PM -0800, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > It might just be me, but \"object name\" makes me think we'd take any name\n> > (e.g., a refname that resolves to an object), whereas \"object id\" would\n> > mean the object's hash specifically. And in this instance we only allow\n> > the latter.\n> \n> Yeah, but glossary-content is very much explicit about this.  \"name\"\n> is the full hexadecimal hash, \"identifier\" is a synonym for it.  And\n> \"id\" does not even appear to be defined.  We used to call \"any name\n> that refers to an object\" an \"extended SHA-1\", but I haven't seen\n> the phrase used for a long time on the list.\n\nOK, then my \"it might just be me\" is clearly just me. :)\n\nI think the distinction I laid out earlier is nicer, but it may not be\nworth the trouble of trying to _change_ nomenclature at this point.\nLet's see what your glossary changes say...\n\n> I've always found it cumbersome that, when I want to mean a full hex\n> representation, I have to say \"40-byte object name\".  It is not even\n> technically correct these days with SHA-256.\n\nYeah, that is both long and wrong. I'd maybe say \"hex object id\" in some\ncases, which is slightly less cumbersome and extends to sha256. And\ndistinguishes it from a binary object id (though see below).\n\n>  Documentation/glossary-content.txt | 15 +++++++++++----\n>  1 file changed, 11 insertions(+), 4 deletions(-)\n> \n> diff --git c/Documentation/glossary-content.txt w/Documentation/glossary-content.txt\n> index 090c888335..e2ab920911 100644\n> --- c/Documentation/glossary-content.txt\n> +++ w/Documentation/glossary-content.txt\n> @@ -262,13 +262,20 @@ This commit is referred to as a \"merge commit\", or sometimes just a\n>  \tidentified by its <<def_object_name,object name>>. The objects usually\n>  \tlive in `$GIT_DIR/objects/`.\n>  \n> +[[def_object_id]]object ID::\n> +\tSynonym for <<def_object_identifier,object identifier>>.\n> +\n>  [[def_object_identifier]]object identifier::\n> -\tSynonym for <<def_object_name,object name>>.\n> +\tAn <<def_object_name, object name>> written as an\n> +\tunabbreviated hexadecimal representation of the hash value\n> +\tthat uniquely identifies an <<def_object,object>>.\n> +\tAlso colloquially called <<def_SHA1,SHA-1>>.\n\nYou might want to touch on \"binary\" here, too, with something like:\n\n  This may also be used to refer to the binary representation of the\n  hash value (e.g., as found within Git trees). Unless specified, this\n  term typically implies the hexadecimal representation.\n\nBut maybe that is overkill. When we are talking about the command line\ninterface, stdin, etc, I can't think of a place where we'd take the\nbinary (\"hash-object -t tree\", but I don't really count that).\n\n>  [[def_object_name]]object name::\n> -\tThe unique identifier of an <<def_object,object>>.  The\n> -\tobject name is usually represented by a 40 character\n> -\thexadecimal string.  Also colloquially called <<def_SHA1,SHA-1>>.\n> +\tA name that identifies an <<def_object,object>> uniquely,\n> +\twhich can be given in various ways, including but not\n> +\tlimited to, the object's full <<def_object_identifier,object\n> +\tidentifier>>, a <<def_ref,ref>> that refers to the object.\n\nThis all seems like an improvement to me, though the real question is\nhow often the term \"object name\" appears in the _other_ manpages to\nrefer to the more limited case.\n\n-Peff\n"},{"id":"410614","messageId":"20201124010710.GI389879@camp.crustytoothpaste.net","threadId":"51667","inReplyTo":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","subject":"Re: [PATCH] mktag: don't check SHA-1 object length under SHA-256","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2020-11-24T01:07:10Z","receivedAt":"2020-11-24T01:08:21Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2020-11-23 at 21:34:21, Jeff King wrote:\n> This is how fsck_tag() does it. In fact, it seems like we could just\n> reuse fsck_tag() here. It wants to pass an oid to report(), but it would\n> be OK to use null_oid here; ultimately it just ends up back in our\n> callback error_func().\n\nI would very much appreciate getting rid of this custom code.  It was a\npain during the SHA-256 transition, and even with significant effort, it\nappears I missed a few parts.  Also, while I'm for casual language, its\nuse here is going to be a little hard to understand for non-native\nspeakers.\n\nMoreover, when we start mapping objects to write the SHA-1 values into\nthe loose object index (which is a series I'm slowly working on), most\nof this custom code is going to have to disappear or be coalesced\nanyway.  If we can use an existing, more robust function now, so much\nthe better.\n-- \nbrian m. carlson (he/him or they/them)\nHouston, Texas, US\n"},{"id":"410851","messageId":"20201126012854.399-2-avarab@gmail.com","threadId":"51667","inReplyTo":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","subject":"[RFC/PATCH 01/12] mktag: use default strbuf_read() hint","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T01:28:43Z","receivedAt":"2020-11-26T01:29:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the hardcoded hint of 2^12 to 0. The default strbuf hint is\nperfectly fine here, and the only reason we were hardcoding it is\nbecause it survived migration from a pre-strbuf fixed-sized buffer.\n\nSee fd17f5b5f77 (Replace all read_fd use with strbuf_read, and get rid\nof it., 2007-09-10) for that migration.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 4982d3a93e..ff7ac8e0e5 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -161,7 +161,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (argc != 1)\n \t\tusage(\"git mktag\");\n \n-\tif (strbuf_read(&buf, 0, 4096) < 0) {\n+\tif (strbuf_read(&buf, 0, 0) < 0) {\n \t\tdie_errno(\"could not read from stdin\");\n \t}\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410852","messageId":"20201126012854.399-1-avarab@gmail.com","threadId":"51667","inReplyTo":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","subject":"[RFC/PATCH 00/12] make \"mktag\" use fsck_tag()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T01:28:42Z","receivedAt":"2020-11-26T01:29:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This is a replacement for my earlier one-off SHA-1->SHA-256 cleanup\npatch, on the basis of Jeff's comment that we should just be using\nfsck_tag().\n\nI was pretty much ready to send this as a non-RFC, but as seen in\n12/12 fully duplicating the mktag logic in the context of using\nfsck_tag() is a bit tricker than it looks. That breakage was only\nrevealed by a test I added while writing this.\n\nThe result is still nicer than the current code, but even then this\nseries breaks the tests mid-way through (in \"improve verify_object()\ntest coverage\"), but fixes them in 12/12.\n\nI ran out of time to make this more sensible, but wanted to send an\nRFC with the current state, and in case anyone else has any better\nideas for how to deal with 12/12. This could be applied as a non-RFC\nby squashing 12/12 into the \"use fsck instead of custom verify_tag\"\nchange.\n\nÆvar Arnfjörð Bjarmason (12):\n  mktag: use default strbuf_read() hint\n  mktag: reword write_object_file() error\n  mktag: remove redundant braces in one-line body \"if\"\n  mktag tests: don't needlessly use a subshell\n  mktag tests: remove needless SHA-1 hardcoding\n  mktag tests: improve verify_object() test coverage\n  fsck: add new \"extra\" checks for \"mktag\"\n  mktag: use fsck instead of custom verify_tag()\n  mktag: remove now-unused verify_tag() code\n  mktag doc: update to explain why to use this\n  mktag docs: say <hash> not <sha1>\n  mktag: bring back some of the verify_object() logic\n\n Documentation/git-hash-object.txt |   4 +\n Documentation/git-mktag.txt       |  31 ++++-\n builtin/mktag.c                   | 207 ++++++++++--------------------\n fsck.c                            |  32 ++++-\n fsck.h                            |   2 +\n t/t1006-cat-file.sh               |   2 +-\n t/t3800-mktag.sh                  | 103 ++++++++++-----\n 7 files changed, 202 insertions(+), 179 deletions(-)\n\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410854","messageId":"20201126012854.399-3-avarab@gmail.com","threadId":"51667","inReplyTo":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","subject":"[RFC/PATCH 02/12] mktag: reword write_object_file() error","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T01:28:44Z","receivedAt":"2020-11-26T01:29:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the error message emitted when write_object_file() fails to\nmake more sense. At this point we're not writing a \"tag file\" (which\nas an aside we never do, we just write to stdout). We are writing an\nannotated tag object, let's say that instead.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex ff7ac8e0e5..603b55aca0 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -171,7 +171,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \t\tdie(\"invalid tag signature file\");\n \n \tif (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)\n-\t\tdie(\"unable to write tag file\");\n+\t\tdie(\"unable to write annotated tag object\");\n \n \tstrbuf_release(&buf);\n \tprintf(\"%s\\n\", oid_to_hex(&result));\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410853","messageId":"20201126012854.399-4-avarab@gmail.com","threadId":"51667","inReplyTo":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","subject":"[RFC/PATCH 03/12] mktag: remove redundant braces in one-line body \"if\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T01:28:45Z","receivedAt":"2020-11-26T01:29:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This minor stylistic churn is usually something we'd avoid, but if we\ndon't do this then the file after changes in subsequent commits will\nonly have this minor style inconsistency, so let's change this while\nwe're at it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 603b55aca0..dc354828f7 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -161,9 +161,8 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (argc != 1)\n \t\tusage(\"git mktag\");\n \n-\tif (strbuf_read(&buf, 0, 0) < 0) {\n+\tif (strbuf_read(&buf, 0, 0) < 0)\n \t\tdie_errno(\"could not read from stdin\");\n-\t}\n \n \t/* Verify it for some basic sanity: it needs to start with\n \t   \"object <sha1>\\ntype\\ntagger \" */\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410855","messageId":"20201126012854.399-7-avarab@gmail.com","threadId":"51667","inReplyTo":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","subject":"[RFC/PATCH 06/12] mktag tests: improve verify_object() test coverage","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T01:28:48Z","receivedAt":"2020-11-26T01:29:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The verify_object() function in \"mktag.c\" is tasked with ensuring that\nour tag refers to a valid object. The existing test for this might\nfail because it was also testing that \"type taggg\" didn't refer to a\nvalid object type (it should be \"type tag\").\n\nLet's split these tests up, so we're testing all combinations of a\nnon-existing object and in invalid \"type\" line.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 22 ++++++++++++++++++++++\n 1 file changed, 22 insertions(+)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex b5013af2aa..3801d3a285 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -128,6 +128,28 @@ check_verify_failure '\"type\" line type-name length check' \\\n ############################################################\n #  9. verify object (SHA1/type) check\n \n+cat >tag.sig <<EOF\n+object $(test_oid deadbeef)\n+type tag\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+check_verify_failure 'verify object (SHA1/type) check' \\\n+\t'^error: char7: could not verify object.*$'\n+\n+cat >tag.sig <<EOF\n+object $head\n+type tagggg\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+check_verify_failure 'verify object (SHA1/type) check' \\\n+\t'^fatal: invalid object type'\n+\n cat >tag.sig <<EOF\n object $(test_oid deadbeef)\n type tagggg\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410856","messageId":"20201126012854.399-5-avarab@gmail.com","threadId":"51667","inReplyTo":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","subject":"[RFC/PATCH 04/12] mktag tests: don't needlessly use a subshell","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T01:28:46Z","receivedAt":"2020-11-26T01:29:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The use of a subshell dates back to e9b20943b77 (t/t3800: do not use a\ntemporary file to hold expected result., 2008-01-04). It's not needed\nanymore, if it ever was.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex d696aa4e52..0e411e3c45 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -14,7 +14,7 @@ test_description='git mktag: tag object verify test'\n check_verify_failure () {\n \texpect=\"$2\"\n \ttest_expect_success \"$1\" '\n-\t\t( test_must_fail git mktag <tag.sig 2>message ) &&\n+\t\ttest_must_fail git mktag <tag.sig 2>message &&\n \t\tgrep \"$expect\" message\n \t'\n }\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410858","messageId":"20201126012854.399-6-avarab@gmail.com","threadId":"51667","inReplyTo":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","subject":"[RFC/PATCH 05/12] mktag tests: remove needless SHA-1 hardcoding","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T01:28:47Z","receivedAt":"2020-11-26T01:29:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the tests amended in acb49d1cc8b (t3800: make hash-size\nindependent, 2019-08-18) even more to make them independent of either\nSHA-1 or SHA-256.\n\nSome of these tests were failing for the wrong reasons. The first one\nbeing modified here would fail because the line starts with \"xxxxxx\"\ninstead of \"object\", the rest of the line doesn't matter. Let's just\nput a valid hash on the rest of the line anyway to narrow the test\ndown for just the s/object/xxxxxx/ case.\n\nThe second one being modified here would fail under\nGIT_TEST_DEFAULT_HASH=sha256 because <some sha-1 length garbage> is an\ninvalid SHA-256, but we should really be testing <some sha-256 length\ngarbage> when under SHA-256.\n\nThis doesn't really matter since we should be able to trust other\nparts of the code to validate things in the 0-9a-f range, but let's do\nit for good measure.\n\nThere's a later test which tests an invalid SHA which looks like a\nvalid one, to stress the \"We refuse to tag something we can't\nverify[...]\" logic in mktag.c.\n\nBut here we're testing for a SHA-length string which contains\ncharacters outside of the /[0-9a-f]/i set. Let's just do that with a\nROT13 invocation.\n\nWe could get really unlucky and switch to a future hash function that\njust happens to produce all [0-9] output for this particular input,\nbut that's very unlikely.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 7 ++++---\n 1 file changed, 4 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 0e411e3c45..b5013af2aa 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -43,7 +43,7 @@ check_verify_failure 'Tag object length check' \\\n #  2. object line label check\n \n cat >tag.sig <<EOF\n-xxxxxx 139e9b33986b1c2670fff52c5067603117b3e895\n+xxxxxx $head\n type tag\n tag mytag\n tagger . <> 0 +0000\n@@ -53,10 +53,11 @@ EOF\n check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n \n ############################################################\n-#  3. object line SHA1 check\n+#  3. object line SHA check\n \n+invalid_sha=$(echo $head | tr A-Za-z N-ZA-Mn-za-m)\n cat >tag.sig <<EOF\n-object zz9e9b33986b1c2670fff52c5067603117b3e895\n+object $invalid_sha\n type tag\n tag mytag\n tagger . <> 0 +0000\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410862","messageId":"20201126012854.399-9-avarab@gmail.com","threadId":"51667","inReplyTo":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","subject":"[RFC/PATCH 08/12] mktag: use fsck instead of custom verify_tag()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T01:28:50Z","receivedAt":"2020-11-26T01:29:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"TODO: This subtly breaks one check, see the last patch in this series.\n\nChange the validation logic in \"mktag\" to use fsck's fsck_tag()\ninstead of its own custom parser. Curiously the logic for both dates\nback to the same commit[1]. Let's unify them so we're not maintaining\ntwo sets functions to verify that a tag is OK.\n\nMoving to fsck_tag() required teaching it to optionally use some\nvalidations that only the old mktag code could perform. That was done\nin an earlier commit, the \"extraHeaderEntry\" and\n\"extraHeaderBodyNewline\" tests being added here make use of that\nlogic.\n\nThere was other \"mktag\" validation logic that I think makes sense to\njust remove. Namely:\n\n A. fsck only cares that the timezone matches [-+][0-9]{4}. The mktag\n    code disallowed values larger than 1400.\n\n    Yes there's currently no timezone with a greater offset[2], but\n    since we allow any number of non-offical timezones (e.g. +1234)\n    passing this through seems fine. Git also won't break in the\n    future if e.g. French Polynesia decides it needs to outdo the Line\n    Islands when it comes to timezone extravagance.\n\n B. fsck allows missing author names such as \"tagger <email>\", mktag\n    wouldn't, but would allow e.g. \"tagger <email>\" (but not \"tagger\n    <email>\"). Now we allow all of these.\n\n C. Like B, but \"mktag\" disallowed spaces in the <email> part, fsck\n    allows it.\n\nWe didn't only lose obscure validation logic, we also gained some:\n\n D. fsck disallows zero-padded dates, but mktag didn't care. So\n    e.g. the timestamp \"0000000000 +0000\" produces an error now. A\n    test in \"t1006-cat-file.sh\" relied on this, it's been changed to\n    use \"hash-object\" (without fsck) instead.\n\n1. ec4465adb38 (Add \"tag\" objects that can be used to sign other\n   objects., 2005-04-25)\n\n2. https://en.wikipedia.org/wiki/List_of_UTC_time_offsets\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c     | 45 +++++++++++++++++++++++++---\n t/t1006-cat-file.sh |  2 +-\n t/t3800-mktag.sh    | 72 +++++++++++++++++++++++++++------------------\n 3 files changed, 86 insertions(+), 33 deletions(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex dc354828f7..a1ae80702d 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -2,6 +2,7 @@\n #include \"tag.h\"\n #include \"replace-object.h\"\n #include \"object-store.h\"\n+#include \"fsck.h\"\n \n /*\n  * A signature file has a very simple fixed format: four lines\n@@ -47,6 +48,9 @@ static int verify_tag(char *buffer, unsigned long size)\n \tconst char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;\n \tsize_t len;\n \n+\t/* verify_tag() will be removed in the next commit */\n+\treturn 0;\n+\n \tif (size < 84)\n \t\treturn error(\"wanna fool me ? you obviously got the size wrong !\");\n \n@@ -153,10 +157,34 @@ static int verify_tag(char *buffer, unsigned long size)\n \treturn 0;\n }\n \n+static int mktag_fsck_error_func(struct fsck_options *o,\n+\t\t\t\t const struct object_id *oid,\n+\t\t\t\t enum object_type object_type,\n+\t\t\t\t int msg_type, const char *message)\n+{\n+\tswitch (msg_type) {\n+\tcase FSCK_WARN:\n+\tcase FSCK_ERROR:\n+\tcase FSCK_EXTRA:\n+\t\t/*\n+\t\t * We treat both warnings and errors as errors, things\n+\t\t * like missing \"tagger\" lines are \"only\" warnings\n+\t\t * under fsck, we've always considered them an error.\n+\t\t */\n+\t\tfprintf_ln(stderr, \"error: %s\", message);\n+\t\treturn 1;\n+\tdefault:\n+\t\tBUG(\"%d (FSCK_IGNORE?) should never trigger this callback\",\n+\t\t    msg_type);\n+\t}\n+}\n+\n int cmd_mktag(int argc, const char **argv, const char *prefix)\n {\n+\tstruct object obj;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct object_id result;\n+\tstruct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \n \tif (argc != 1)\n \t\tusage(\"git mktag\");\n@@ -164,10 +192,19 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (strbuf_read(&buf, 0, 0) < 0)\n \t\tdie_errno(\"could not read from stdin\");\n \n-\t/* Verify it for some basic sanity: it needs to start with\n-\t   \"object <sha1>\\ntype\\ntagger \" */\n-\tif (verify_tag(buf.buf, buf.len) < 0)\n-\t\tdie(\"invalid tag signature file\");\n+\t/* verify_tag() will be removed in the next commit */\n+\tverify_tag(\"\", 0);\n+\n+\t/*\n+\t * Fake up an object for fsck_object()\n+\t */\n+\tobj.parsed = 1;\n+\tobj.type = OBJ_TAG;\n+\n+\tfsck_options.extra = 1;\n+\tfsck_options.error_func = mktag_fsck_error_func;\n+\tif (fsck_object(&obj, buf.buf, buf.len, &fsck_options))\n+\t\tdie(\"tag on stdin did not pass our strict fsck check\");\n \n \tif (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)\n \t\tdie(\"unable to write annotated tag object\");\ndiff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh\nindex 2f501d2dc9..5d2dc99b74 100755\n--- a/t/t1006-cat-file.sh\n+++ b/t/t1006-cat-file.sh\n@@ -166,7 +166,7 @@ tag_content=\"$tag_header_without_timestamp 0000000000 +0000\n \n $tag_description\"\n \n-tag_sha1=$(echo_without_newline \"$tag_content\" | git mktag)\n+tag_sha1=$(echo_without_newline \"$tag_content\" | git hash-object -t tag --stdin -w)\n tag_size=$(strlen \"$tag_content\")\n \n run_tests 'tag' $tag_sha1 $tag_size \"$tag_content\" \"$tag_content\" 1\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 3801d3a285..bc57ee85c9 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -37,7 +37,7 @@ too short for a tag\n EOF\n \n check_verify_failure 'Tag object length check' \\\n-\t'^error: .*size wrong.*$'\n+\t'^error: missingObject:'\n \n ############################################################\n #  2. object line label check\n@@ -50,7 +50,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n+check_verify_failure '\"object\" line label check' '^error: missingObject:'\n \n ############################################################\n #  3. object line SHA check\n@@ -64,7 +64,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"object\" line SHA1 check' '^error: char7: .*SHA1 hash$'\n+check_verify_failure '\"object\" line check' '^error: badObjectSha1:'\n \n ############################################################\n #  4. type line label check\n@@ -77,7 +77,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n+check_verify_failure '\"type\" line label check' '^error: missingTypeEntry:'\n \n ############################################################\n #  5. type line eol check\n@@ -85,7 +85,7 @@ check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n echo \"object $head\" >tag.sig\n printf \"type tagsssssssssssssssssssssssssssssss\" >>tag.sig\n \n-check_verify_failure '\"type\" line eol check' '^error: char.*: .*\"\\\\n\"$'\n+check_verify_failure '\"type\" line eol check' '^error: unterminatedHeader:'\n \n ############################################################\n #  6. tag line label check #1\n@@ -99,7 +99,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure '\"tag\" line label check #1' \\\n-\t'^error: char.*: no \"tag \" found$'\n+\t'^error: missingTagEntry:'\n \n ############################################################\n #  7. tag line label check #2\n@@ -111,7 +111,7 @@ tag\n EOF\n \n check_verify_failure '\"tag\" line label check #2' \\\n-\t'^error: char.*: no \"tag \" found$'\n+\t'^error: badType:'\n \n ############################################################\n #  8. type line type-name length check\n@@ -123,7 +123,7 @@ tag mytag\n EOF\n \n check_verify_failure '\"type\" line type-name length check' \\\n-\t'^error: char.*: type too long$'\n+\t'^error: badType:'\n \n ############################################################\n #  9. verify object (SHA1/type) check\n@@ -159,7 +159,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (SHA1/type) check' \\\n-\t'^error: char7: could not verify object.*$'\n+\t'^error: badType:'\n \n ############################################################\n # 10. verify tag-name check\n@@ -173,7 +173,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify tag-name check' \\\n-\t'^error: char.*: could not verify tag name$'\n+\t'^error: badTagName:'\n \n ############################################################\n # 11. tagger line label check #1\n@@ -187,7 +187,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #1' \\\n-\t'^error: char.*: could not find \"tagger \"$'\n+\t'^error: missingTaggerEntry:'\n \n ############################################################\n # 12. tagger line label check #2\n@@ -202,10 +202,10 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #2' \\\n-\t'^error: char.*: could not find \"tagger \"$'\n+\t'^error: missingTaggerEntry:'\n \n ############################################################\n-# 13. disallow missing tag author name\n+# 13. allow missing tag author name like fsck\n \n cat >tag.sig <<EOF\n object $head\n@@ -216,8 +216,9 @@ tagger  <> 0 +0000\n This is filler\n EOF\n \n-check_verify_failure 'disallow missing tag author name' \\\n-\t'^error: char.*: missing tagger name$'\n+test_expect_success 'allow missing tag author name' '\n+\tgit mktag <tag.sig\n+'\n \n ############################################################\n # 14. disallow missing tag author name\n@@ -232,7 +233,7 @@ tagger T A Gger <\n EOF\n \n check_verify_failure 'disallow malformed tagger' \\\n-\t'^error: char.*: malformed tagger field$'\n+\t'^error: badEmail:'\n \n ############################################################\n # 15. allow empty tag email\n@@ -250,7 +251,7 @@ test_expect_success \\\n     'git mktag <tag.sig >.git/refs/tags/mytag 2>message'\n \n ############################################################\n-# 16. disallow spaces in tag email\n+# 16. allow spaces in tag email like fsck\n \n cat >tag.sig <<EOF\n object $head\n@@ -260,8 +261,9 @@ tagger T A Gger <tag ger@example.com> 0 +0000\n \n EOF\n \n-check_verify_failure 'disallow spaces in tag email' \\\n-\t'^error: char.*: malformed tagger field$'\n+test_expect_success 'allow spaces in tag email like fsck' '\n+\tgit mktag <tag.sig\n+'\n \n ############################################################\n # 17. disallow missing tag timestamp\n@@ -275,7 +277,7 @@ tagger T A Gger <tagger@example.com>__\n EOF\n \n check_verify_failure 'disallow missing tag timestamp' \\\n-\t'^error: char.*: missing tag timestamp$'\n+\t'^error: badDate:'\n \n ############################################################\n # 18. detect invalid tag timestamp1\n@@ -289,7 +291,7 @@ tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008\n EOF\n \n check_verify_failure 'detect invalid tag timestamp1' \\\n-\t'^error: char.*: missing tag timestamp$'\n+\t'^error: badDate:'\n \n ############################################################\n # 19. detect invalid tag timestamp2\n@@ -303,7 +305,7 @@ tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500\n EOF\n \n check_verify_failure 'detect invalid tag timestamp2' \\\n-\t'^error: char.*: malformed tag timestamp$'\n+\t'^error: badDate:'\n \n ############################################################\n # 20. detect invalid tag timezone1\n@@ -317,7 +319,7 @@ tagger T A Gger <tagger@example.com> 1206478233 GMT\n EOF\n \n check_verify_failure 'detect invalid tag timezone1' \\\n-\t'^error: char.*: malformed tag timezone$'\n+\t'^error: badTimezone:'\n \n ############################################################\n # 21. detect invalid tag timezone2\n@@ -331,10 +333,10 @@ tagger T A Gger <tagger@example.com> 1206478233 +  30\n EOF\n \n check_verify_failure 'detect invalid tag timezone2' \\\n-\t'^error: char.*: malformed tag timezone$'\n+\t'^error: badTimezone:'\n \n ############################################################\n-# 22. detect invalid tag timezone3\n+# 22. allow invalid tag timezone3 (the maximum is -1200/+1400)\n \n cat >tag.sig <<EOF\n object $head\n@@ -344,8 +346,9 @@ tagger T A Gger <tagger@example.com> 1206478233 -1430\n \n EOF\n \n-check_verify_failure 'detect invalid tag timezone3' \\\n-\t'^error: char.*: malformed tag timezone$'\n+test_expect_success 'allow invalid tag timezone' '\n+\tgit mktag <tag.sig\n+'\n \n ############################################################\n # 23. detect invalid header entry\n@@ -360,7 +363,20 @@ this line should not be here\n EOF\n \n check_verify_failure 'detect invalid header entry' \\\n-\t'^error: char.*: trailing garbage in tag header$'\n+\t'^error: extraHeaderEntry:'\n+\n+cat >tag.sig <<EOF\n+object $head\n+type commit\n+tag mytag\n+tagger T A Gger <tagger@example.com> 1206478233 -0500\n+\n+\n+this line should be one line up\n+EOF\n+\n+check_verify_failure 'detect invalid header entry' \\\n+\t'^error: extraHeaderBodyNewline:'\n \n ############################################################\n # 24. create valid tag\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410857","messageId":"20201126012854.399-8-avarab@gmail.com","threadId":"51667","inReplyTo":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","subject":"[RFC/PATCH 07/12] fsck: add new \"extra\" checks for \"mktag\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T01:28:49Z","receivedAt":"2020-11-26T01:29:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add optional \"extra\" checks to fsck, these are needed to eventually\nreplace the custom not-quite-fsck code in mktag.c.\n\nThe mktag checks differ from fsck_tag() in several ways, one of those\nis that fsck doesn't know how to refuse an object with custom headers,\nand isn't strict about header and body newline separation.\n\nTeach it how to optionally report these. I thought the best way to do\nthat given the current structure of the code was to add a new \"extra\"\ncategory in addition to error/warn/info. Under --strict the \"info\"\nbecomes a \"warn\" and \"warn\" becomes \"error\". Existing users of\nfsck's (and others, e.g. index-pack) --strict option rely on this.\n\nBy adding an \"extra\" category and only reporting it based on a flag in\nfsck_options callers can opt-in to these \"extra\" messages, which\nthey'll then need to deal with in their own \"error_func\".\n\nNo tests are being added for this new functionality, they're added in\na subsequent commit where we teach \"mktag\" to use this new validation\nmode.\n\nI'm not changing fsck_commit() to validate commit objects like this\neither, we could do that, but unlike in the tag case that code\nwouldn't be used anywhere. If someone wants to write a \"mkcommit\" they\nwhich behaves like \"mktag\" they can borrow or refactor this logic for\nuse in fsck_commit().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n fsck.c | 32 +++++++++++++++++++++++++++++++-\n fsck.h |  2 ++\n 2 files changed, 33 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex f82e2fe9e3..3c25df2244 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -80,7 +80,10 @@ static struct oidset gitmodules_done = OIDSET_INIT;\n \t/* infos (reported as warnings, but ignored by default) */ \\\n \tFUNC(GITMODULES_PARSE, INFO) \\\n \tFUNC(BAD_TAG_NAME, INFO) \\\n-\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n+\tFUNC(MISSING_TAGGER_ENTRY, INFO) \\\n+\t/* extra (only reported when requested) */ \\\n+\tFUNC(EXTRA_HEADER_ENTRY, EXTRA) \\\n+\tFUNC(EXTRA_HEADER_BODY_NEWLINE, EXTRA)\n \n #define MSG_ID(id, msg_type) FSCK_MSG_##id,\n enum fsck_msg_id {\n@@ -975,6 +978,33 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \telse\n \t\tret = fsck_ident(&buffer, oid, OBJ_TAG, options);\n \n+\tif (options->extra && *buffer) {\n+\t\tif (!starts_with(buffer, \"\\n\")) {\n+\t\t\t/*\n+\t\t\t * The verify_headers() check will allow\n+\t\t\t * e.g. \"[...]tagger <tagger>\\nsome\n+\t\t\t * garbage\\n\\nmessage\" to pass, thinking \"some\n+\t\t\t * garbage\" could be a custom\n+\t\t\t * header. E.g. \"mktag\" doesn't want any\n+\t\t\t * unknown headers.\n+\t\t\t */\n+\t\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_EXTRA_HEADER_ENTRY, \"invalid format - extra header(s) after 'tagger'\");\n+\t\t\tif (ret)\n+\t\t\t\tgoto done;\n+\t\t}\n+\t\tif (starts_with(buffer, \"\\n\\n\")) {\n+\t\t\t/*\n+\t\t\t * Some callers such as \"mktag\" want to\n+\t\t\t * disallow \"[...]tagger\n+\t\t\t * <tagger>\\n\\n\\nmessage\", only allowing a\n+\t\t\t * single newline for separation.\n+\t\t\t */\n+\t\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_EXTRA_HEADER_BODY_NEWLINE, \"invalid format - headers separated body by more than one newline\");\n+\t\t\tif (ret)\n+\t\t\t\tgoto done;\n+\t\t}\n+\t}\n+\n done:\n \tstrbuf_release(&sb);\n \treturn ret;\ndiff --git a/fsck.h b/fsck.h\nindex 69cf715e79..110efc65fd 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -6,6 +6,7 @@\n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n #define FSCK_IGNORE 3\n+#define FSCK_EXTRA 4\n \n struct fsck_options;\n struct object;\n@@ -40,6 +41,7 @@ struct fsck_options {\n \tunsigned strict:1;\n \tint *msg_type;\n \tstruct oidset skiplist;\n+\tunsigned extra:1;\n \tkh_oid_map_t *object_names;\n };\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410859","messageId":"20201126012854.399-12-avarab@gmail.com","threadId":"51667","inReplyTo":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","subject":"[RFC/PATCH 11/12] mktag docs: say <hash> not <sha1>","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T01:28:53Z","receivedAt":"2020-11-26T01:29:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the \"mktag\" documentation to refer to the input hash as just\n\"hash\", not \"sha1\". This command has supported SHA-256 for a while\nnow.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex 11ce8e4bb4..3ba15072df 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -33,7 +33,7 @@ Tag Format\n A tag signature file, to be fed to this command's standard input,\n has a very simple fixed format: four lines of\n \n-  object <sha1>\n+  object <hash>\n   type <typename>\n   tag <tagname>\n   tagger <tagger>\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410861","messageId":"20201126012854.399-10-avarab@gmail.com","threadId":"51667","inReplyTo":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","subject":"[RFC/PATCH 09/12] mktag: remove now-unused verify_tag() code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T01:28:51Z","receivedAt":"2020-11-26T01:29:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The preceding commit removed the use of the verify_tag() function, but\ndidn't remove this code to keep the diff smaller. Let's remove this\nunused code now.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 157 ------------------------------------------------\n 1 file changed, 157 deletions(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex a1ae80702d..e9a0954dcb 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -1,162 +1,8 @@\n #include \"builtin.h\"\n #include \"tag.h\"\n-#include \"replace-object.h\"\n #include \"object-store.h\"\n #include \"fsck.h\"\n \n-/*\n- * A signature file has a very simple fixed format: four lines\n- * of \"object <sha1>\" + \"type <typename>\" + \"tag <tagname>\" +\n- * \"tagger <committer>\", followed by a blank line, a free-form tag\n- * message and a signature block that git itself doesn't care about,\n- * but that can be verified with gpg or similar.\n- *\n- * The first four lines are guaranteed to be at least 83 bytes:\n- * \"object <sha1>\\n\" is 48 bytes, \"type tag\\n\" at 9 bytes is the\n- * shortest possible type-line, \"tag .\\n\" at 6 bytes is the shortest\n- * single-character-tag line, and \"tagger . <> 0 +0000\\n\" at 20 bytes is\n- * the shortest possible tagger-line.\n- */\n-\n-/*\n- * We refuse to tag something we can't verify. Just because.\n- */\n-static int verify_object(const struct object_id *oid, const char *expected_type)\n-{\n-\tint ret = -1;\n-\tenum object_type type;\n-\tunsigned long size;\n-\tvoid *buffer = read_object_file(oid, &type, &size);\n-\tconst struct object_id *repl = lookup_replace_object(the_repository, oid);\n-\n-\tif (buffer) {\n-\t\tif (type == type_from_string(expected_type)) {\n-\t\t\tret = check_object_signature(the_repository, repl,\n-\t\t\t\t\t\t     buffer, size,\n-\t\t\t\t\t\t     expected_type);\n-\t\t}\n-\t\tfree(buffer);\n-\t}\n-\treturn ret;\n-}\n-\n-static int verify_tag(char *buffer, unsigned long size)\n-{\n-\tint typelen;\n-\tchar type[20];\n-\tstruct object_id oid;\n-\tconst char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;\n-\tsize_t len;\n-\n-\t/* verify_tag() will be removed in the next commit */\n-\treturn 0;\n-\n-\tif (size < 84)\n-\t\treturn error(\"wanna fool me ? you obviously got the size wrong !\");\n-\n-\tbuffer[size] = 0;\n-\n-\t/* Verify object line */\n-\tobject = buffer;\n-\tif (memcmp(object, \"object \", 7))\n-\t\treturn error(\"char%d: does not start with \\\"object \\\"\", 0);\n-\n-\tif (parse_oid_hex(object + 7, &oid, &p))\n-\t\treturn error(\"char%d: could not get SHA1 hash\", 7);\n-\n-\t/* Verify type line */\n-\ttype_line = p + 1;\n-\tif (memcmp(type_line - 1, \"\\ntype \", 6))\n-\t\treturn error(\"char%d: could not find \\\"\\\\ntype \\\"\", 47);\n-\n-\t/* Verify tag-line */\n-\ttag_line = strchr(type_line, '\\n');\n-\tif (!tag_line)\n-\t\treturn error(\"char%\"PRIuMAX\": could not find next \\\"\\\\n\\\"\",\n-\t\t\t\t(uintmax_t) (type_line - buffer));\n-\ttag_line++;\n-\tif (memcmp(tag_line, \"tag \", 4) || tag_line[4] == '\\n')\n-\t\treturn error(\"char%\"PRIuMAX\": no \\\"tag \\\" found\",\n-\t\t\t\t(uintmax_t) (tag_line - buffer));\n-\n-\t/* Get the actual type */\n-\ttypelen = tag_line - type_line - strlen(\"type \\n\");\n-\tif (typelen >= sizeof(type))\n-\t\treturn error(\"char%\"PRIuMAX\": type too long\",\n-\t\t\t\t(uintmax_t) (type_line+5 - buffer));\n-\n-\tmemcpy(type, type_line+5, typelen);\n-\ttype[typelen] = 0;\n-\n-\t/* Verify that the object matches */\n-\tif (verify_object(&oid, type))\n-\t\treturn error(\"char%d: could not verify object %s\", 7, oid_to_hex(&oid));\n-\n-\t/* Verify the tag-name: we don't allow control characters or spaces in it */\n-\ttag_line += 4;\n-\tfor (;;) {\n-\t\tunsigned char c = *tag_line++;\n-\t\tif (c == '\\n')\n-\t\t\tbreak;\n-\t\tif (c > ' ')\n-\t\t\tcontinue;\n-\t\treturn error(\"char%\"PRIuMAX\": could not verify tag name\",\n-\t\t\t\t(uintmax_t) (tag_line - buffer));\n-\t}\n-\n-\t/* Verify the tagger line */\n-\ttagger_line = tag_line;\n-\n-\tif (memcmp(tagger_line, \"tagger \", 7))\n-\t\treturn error(\"char%\"PRIuMAX\": could not find \\\"tagger \\\"\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/*\n-\t * Check for correct form for name and email\n-\t * i.e. \" <\" followed by \"> \" on _this_ line\n-\t * No angle brackets within the name or email address fields.\n-\t * No spaces within the email address field.\n-\t */\n-\ttagger_line += 7;\n-\tif (!(lb = strstr(tagger_line, \" <\")) || !(rb = strstr(lb+2, \"> \")) ||\n-\t\tstrpbrk(tagger_line, \"<>\\n\") != lb+1 ||\n-\t\tstrpbrk(lb+2, \"><\\n \") != rb)\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tagger field\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/* Check for author name, at least one character, space is acceptable */\n-\tif (lb == tagger_line)\n-\t\treturn error(\"char%\"PRIuMAX\": missing tagger name\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/* timestamp, 1 or more digits followed by space */\n-\ttagger_line = rb + 2;\n-\tif (!(len = strspn(tagger_line, \"0123456789\")))\n-\t\treturn error(\"char%\"PRIuMAX\": missing tag timestamp\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line += len;\n-\tif (*tagger_line != ' ')\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tag timestamp\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line++;\n-\n-\t/* timezone, 5 digits [+-]hhmm, max. 1400 */\n-\tif (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&\n-\t      strspn(tagger_line+1, \"0123456789\") == 4 &&\n-\t      tagger_line[5] == '\\n' && atoi(tagger_line+1) <= 1400))\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tag timezone\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line += 6;\n-\n-\t/* Verify the blank line separating the header from the body */\n-\tif (*tagger_line != '\\n')\n-\t\treturn error(\"char%\"PRIuMAX\": trailing garbage in tag header\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/* The actual stuff afterwards we don't care about.. */\n-\treturn 0;\n-}\n-\n static int mktag_fsck_error_func(struct fsck_options *o,\n \t\t\t\t const struct object_id *oid,\n \t\t\t\t enum object_type object_type,\n@@ -192,9 +38,6 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (strbuf_read(&buf, 0, 0) < 0)\n \t\tdie_errno(\"could not read from stdin\");\n \n-\t/* verify_tag() will be removed in the next commit */\n-\tverify_tag(\"\", 0);\n-\n \t/*\n \t * Fake up an object for fsck_object()\n \t */\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410863","messageId":"20201126012854.399-13-avarab@gmail.com","threadId":"51667","inReplyTo":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","subject":"[RFC/PATCH 12/12] mktag: bring back some of the verify_object() logic","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T01:28:54Z","receivedAt":"2020-11-26T01:29:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"When working on this series I saw too late that I'd removed the mktag\ncheck for validating the object the tag points to. The fsck_tag() code\ndoesn't do this because it's meant for the context of fsck, where\nwe're validating reachability anyway.\n\nWe'd need to either refactor fsck_tag() so that it can pass us back\nits \"tagged_oid\" and the \"type_from_string_gently()\" value it throws\naway to get rid of the re-parsing of stdin here, or just duplicate the\nlogic as I'm doing here.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c  | 48 ++++++++++++++++++++++++++++++++++++++++++++++++\n t/t3800-mktag.sh |  4 ++--\n 2 files changed, 50 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex e9a0954dcb..f1f1cf04ff 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -1,5 +1,6 @@\n #include \"builtin.h\"\n #include \"tag.h\"\n+#include \"replace-object.h\"\n #include \"object-store.h\"\n #include \"fsck.h\"\n \n@@ -25,6 +26,50 @@ static int mktag_fsck_error_func(struct fsck_options *o,\n \t}\n }\n \n+static int verify_object_in_tag(const char *stdin)\n+{\n+\tstruct object_id oid;\n+\tchar *eol;\n+\tconst char *p;\n+\tint expected_type_id;\n+\tconst char *expected_type;\n+\tint ret = -1;\n+\tenum object_type type;\n+\tunsigned long size;\n+\tvoid *buffer;\n+\tconst struct object_id *repl;\n+\n+\tif (!skip_prefix(stdin, \"object \", &stdin))\n+\t\tgoto bug;\n+\tif (parse_oid_hex(stdin, &oid, &p) || *p != '\\n')\n+\t\tgoto bug;\n+\tstdin = p + 1;\n+\tif (!skip_prefix(stdin, \"type \", &stdin))\n+\t\tgoto bug;\n+\teol = strchr(stdin, '\\n');\n+\texpected_type_id = type_from_string_gently(stdin, eol - stdin, 1);\n+\tif (expected_type_id < 0)\n+\t\tgoto bug;\n+\texpected_type = type_name(expected_type_id);\n+\n+\tbuffer = read_object_file(&oid, &type, &size);\n+\trepl = lookup_replace_object(the_repository, &oid);\n+\n+\tif (buffer) {\n+\t\tif (type == type_from_string(expected_type)) {\n+\t\t\tret = check_object_signature(the_repository, repl,\n+\t\t\t\t\t\t     buffer, size,\n+\t\t\t\t\t\t     expected_type);\n+\t\t}\n+\t\tfree(buffer);\n+\t}\n+\tgoto ok;\n+bug:\n+\tBUG(\"fsck_object() should have ensured a sane tag format already!\");\n+ok:\n+\treturn ret;\n+}\n+\n int cmd_mktag(int argc, const char **argv, const char *prefix)\n {\n \tstruct object obj;\n@@ -49,6 +94,9 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (fsck_object(&obj, buf.buf, buf.len, &fsck_options))\n \t\tdie(\"tag on stdin did not pass our strict fsck check\");\n \n+\tif (verify_object_in_tag(buf.buf))\n+\t\tdie(\"tag on stdin did not refer to a valid object\");\n+\n \tif (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)\n \t\tdie(\"unable to write annotated tag object\");\n \ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex bc57ee85c9..74cd2eb141 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -137,7 +137,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (SHA1/type) check' \\\n-\t'^error: char7: could not verify object.*$'\n+\t'^fatal: tag on stdin did not refer to a valid object'\n \n cat >tag.sig <<EOF\n object $head\n@@ -148,7 +148,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (SHA1/type) check' \\\n-\t'^fatal: invalid object type'\n+\t'^error: badType:'\n \n cat >tag.sig <<EOF\n object $(test_oid deadbeef)\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410860","messageId":"20201126012854.399-11-avarab@gmail.com","threadId":"51667","inReplyTo":"X7wq3ba3QoxjyyrQ@coredump.intra.peff.net","subject":"[RFC/PATCH 10/12] mktag doc: update to explain why to use this","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T01:28:52Z","receivedAt":"2020-11-26T01:29:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the mktag documentation to compare itself to the similar\n\"hash-object -t tag\" command. Before this someone reading the\ndocumentation wouldn't have much of an idea what the difference\nwas.\n\nLet's make it clear that it's to do with slightly different fsck\nvalidation logic, and cross-link the \"mktag\" and \"hash-object\"\ndocumentation to aid discover-ability.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-hash-object.txt |  4 ++++\n Documentation/git-mktag.txt       | 29 +++++++++++++++++++++++++----\n 2 files changed, 29 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-hash-object.txt b/Documentation/git-hash-object.txt\nindex df9e2c58bd..c535661ced 100644\n--- a/Documentation/git-hash-object.txt\n+++ b/Documentation/git-hash-object.txt\n@@ -58,6 +58,10 @@ OPTIONS\n \tstress-testing Git itself or reproducing characteristics of corrupt or\n \tbogus objects encountered in the wild.\n \n+SEE ALSO\n+--------\n+linkgit:git-mktag[1]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex fa6a756123..11ce8e4bb4 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -3,7 +3,7 @@ git-mktag(1)\n \n NAME\n ----\n-git-mktag - Creates a tag object\n+git-mktag - Creates a tag object with extra validation\n \n \n SYNOPSIS\n@@ -13,10 +13,20 @@ SYNOPSIS\n \n DESCRIPTION\n -----------\n-Reads a tag contents on standard input and creates a tag object\n-that can also be used to sign other objects.\n \n-The output is the new tag's <object> identifier.\n+Reads a tag contents on standard input and creates a tag object. The\n+output is the new tag's <object> identifier.\n+\n+This command accepts a subset of what linkgit:git-hash-object[1] would\n+accept with `-t tag --stdin`. I.e. both of these work:\n+\n+    git mktag <my-tag\n+    git hash-object -t tag --stdin <my-tag\n+\n+The difference between the two is that mktag does the equivalent of a\n+linkgit:git-fsck(1) check on its input, and furthermore disallows some\n+thing linkgit:git-hash-object[1] would pass, e.g. extra headers in the\n+object before the message.\n \n Tag Format\n ----------\n@@ -34,6 +44,17 @@ exists, is separated by a blank line from the header.  The\n message part may contain a signature that Git itself doesn't\n care about, but that can be verified with gpg.\n \n+HISTORY\n+-------\n+\n+In versions of Git before v2.30.0 the \"mktag\" command's validation\n+logic was subtly different than that of linkgit:git-fsck[1]. It is now\n+a strict superset of linkgit:git-fsck[1]'s validation logic.\n+\n+SEE ALSO\n+--------\n+linkgit:git-hash-object[1],\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410869","messageId":"X79eCcmLbgtV1jzJ@coredump.intra.peff.net","threadId":"51667","inReplyTo":"20201126012854.399-6-avarab@gmail.com","subject":"Re: [RFC/PATCH 05/12] mktag tests: remove needless SHA-1 hardcoding","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2020-11-26T07:49:29Z","receivedAt":"2020-11-26T07:49:33Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Nov 26, 2020 at 02:28:47AM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> But here we're testing for a SHA-length string which contains\n> characters outside of the /[0-9a-f]/i set. Let's just do that with a\n> ROT13 invocation.\n> \n> We could get really unlucky and switch to a future hash function that\n> just happens to produce all [0-9] output for this particular input,\n> but that's very unlikely.\n\nMaybe s/[0-9a-f]/z/ would be both simpler and more robust?\n\n-Peff\n"},{"id":"410870","messageId":"X79hCWF8jDZBw/NG@coredump.intra.peff.net","threadId":"51667","inReplyTo":"20201126012854.399-8-avarab@gmail.com","subject":"Re: [RFC/PATCH 07/12] fsck: add new \"extra\" checks for \"mktag\"","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2020-11-26T08:02:17Z","receivedAt":"2020-11-26T08:02:35Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Nov 26, 2020 at 02:28:49AM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> Add optional \"extra\" checks to fsck, these are needed to eventually\n> replace the custom not-quite-fsck code in mktag.c.\n> \n> The mktag checks differ from fsck_tag() in several ways, one of those\n> is that fsck doesn't know how to refuse an object with custom headers,\n> and isn't strict about header and body newline separation.\n> \n> Teach it how to optionally report these. I thought the best way to do\n> that given the current structure of the code was to add a new \"extra\"\n> category in addition to error/warn/info.\n\nHmm, this new severity (and the extra options bit) feels a bit\nbackwards. We are already passing the information on what we find to the\nreport() callback. It seems like that is the place that should be\ndeciding what is important and what is not.\n\nUnfortunately the defaults are somewhat backwards here. We'd have to\nteach the fsck callbacks to ignore these harmless entries, rather than\nteaching the mktag caller that they need to be respected.\n\nSo probably the extra bit in options to say \"do these extra tag checks\"\nis the least-bad thing. But then why do we need to put them in their own\nEXTRA section? The only caller that wants them would treat them as\nerrors.\n\nI'm slightly on the fence on whether mktag really needs to enforce the\n\"unknown header\" thing at all. Sure, we don't encourage them, but it's a\nplumbing tool one could use to experiment with new headers. I guess the\ndownside is that a typo'd header would not be caught.\n\n> Under --strict the \"info\"\n> becomes a \"warn\" and \"warn\" becomes \"error\". Existing users of\n> fsck's (and others, e.g. index-pack) --strict option rely on this.\n\nYeah, this is a weirdness I think we should eventually fix (along with\nre-prioritizing some of the existing checks). I'm wary of doing anything\nthat further cements that somewhat broken world-view (keep in mind that\n\"index-pack --strict\" is not \"do fsck more strictly\" but \"do fsck at\nall\").\n\n> I'm not changing fsck_commit() to validate commit objects like this\n> either, we could do that, but unlike in the tag case that code\n> wouldn't be used anywhere. If someone wants to write a \"mkcommit\" they\n> which behaves like \"mktag\" they can borrow or refactor this logic for\n> use in fsck_commit().\n\nSeems reasonable.\n\n-Peff\n"},{"id":"410871","messageId":"X79kgLjz4U9ubIKB@coredump.intra.peff.net","threadId":"51667","inReplyTo":"20201126012854.399-9-avarab@gmail.com","subject":"Re: [RFC/PATCH 08/12] mktag: use fsck instead of custom verify_tag()","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2020-11-26T08:17:04Z","receivedAt":"2020-11-26T08:17:09Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Nov 26, 2020 at 02:28:50AM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> There was other \"mktag\" validation logic that I think makes sense to\n> just remove. Namely:\n> \n>  A. fsck only cares that the timezone matches [-+][0-9]{4}. The mktag\n>     code disallowed values larger than 1400.\n> \n>     Yes there's currently no timezone with a greater offset[2], but\n>     since we allow any number of non-offical timezones (e.g. +1234)\n>     passing this through seems fine. Git also won't break in the\n>     future if e.g. French Polynesia decides it needs to outdo the Line\n>     Islands when it comes to timezone extravagance.\n\nYeah, I think this is a good choice to loosen.\n\n>  B. fsck allows missing author names such as \"tagger <email>\", mktag\n>     wouldn't, but would allow e.g. \"tagger <email>\" (but not \"tagger\n>     <email>\"). Now we allow all of these.\n\nLikewise, though I am confused. Should the second \"tagger <email>\" in\nthat paragraph have something else in it?\n\n>  C. Like B, but \"mktag\" disallowed spaces in the <email> part, fsck\n>     allows it.\n\nPossibly something we'd want to tighten in fsck, but I think keeping\nthem in alignment is a good idea for now.\n\n> We didn't only lose obscure validation logic, we also gained some:\n> \n>  D. fsck disallows zero-padded dates, but mktag didn't care. So\n>     e.g. the timestamp \"0000000000 +0000\" produces an error now. A\n>     test in \"t1006-cat-file.sh\" relied on this, it's been changed to\n>     use \"hash-object\" (without fsck) instead.\n\nSeems reasonable.\n\n> +\t/* verify_tag() will be removed in the next commit */\n> +\tverify_tag(\"\", 0);\n> +\n> +\t/*\n> +\t * Fake up an object for fsck_object()\n> +\t */\n> +\tobj.parsed = 1;\n> +\tobj.type = OBJ_TAG;\n\nI don't love this \"fake object struct on the stack\" thing. I can't think\nof anything that would break outright, but it may be the only place\nwhere that struct isn't coming from the usual pool, and representing the\ncommon part of a larger object. Two definite gotchas, though:\n\n  - if the type is OBJ_TAG, then it may get cast to a \"struct tag\" by\n    other code, which could look past the end of the struct. I think\n    that fsck_object() doesn't do this, but it could (and it definitely\n    used to)\n\n  - you don't initialize the other fields. We'll definitely pass\n    &obj->oid in the fsck code, and even back to our report() callback,\n    even though it's full of garbage. In practice this is OK because our\n    custom report() won't look at them, but it seems awfully fragile.\n\nI recently genericized the type-specific fsck_* functions so that they\njust need an oid, and not an object struct. I think I didn't do\nfsck_object() just because it didn't have any callers where it mattered.\nSo I think it would make sense here to either:\n\n  - make fsck_tag() specifically available outside of fsck.c, so could\n    call it directly\n\n  - convert fsck_object() to take an oid rather than an object struct.\n    It only uses the object itself to check for NULL-ness. Looking at\n    the callers, they all have non-NULL objects already. So I think that\n    check can't be triggered.\n\n>  check_verify_failure 'Tag object length check' \\\n> -\t'^error: .*size wrong.*$'\n> +\t'^error: missingObject:'\n\nWe may want to enhance the \"error:\" here to make it clear we're talking\nabout a format error in the tag input. Maybe:\n\n  error: tag input does not pass fsck: missingObject: ...\n\nor something.\n\n-Peff\n"},{"id":"410872","messageId":"X79lY/bPW8O1itjF@coredump.intra.peff.net","threadId":"51667","inReplyTo":"20201126012854.399-10-avarab@gmail.com","subject":"Re: [RFC/PATCH 09/12] mktag: remove now-unused verify_tag() code","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2020-11-26T08:20:51Z","receivedAt":"2020-11-26T08:21:09Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Nov 26, 2020 at 02:28:51AM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> The preceding commit removed the use of the verify_tag() function, but\n> didn't remove this code to keep the diff smaller. Let's remove this\n> unused code now.\n\nI do appreciate keeping diffs readable, but IMHO this is making things\nmore confusing, because you had to add a new dummy call to verify_tag()\nin the previous commit in order to keep the compiler from complaining\nabout the unused static function (I'm actually surprised that it doesn't\ncomplain about the immediate \"return 0\" in that function, which makes\nthe rest of the function unambiguously unreachable).\n\n-Peff\n"},{"id":"410874","messageId":"X79oKaMARatPS83t@coredump.intra.peff.net","threadId":"51667","inReplyTo":"20201126012854.399-13-avarab@gmail.com","subject":"Re: [RFC/PATCH 12/12] mktag: bring back some of the verify_object() logic","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2020-11-26T08:32:41Z","receivedAt":"2020-11-26T08:32:59Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Nov 26, 2020 at 02:28:54AM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> When working on this series I saw too late that I'd removed the mktag\n> check for validating the object the tag points to. The fsck_tag() code\n> doesn't do this because it's meant for the context of fsck, where\n> we're validating reachability anyway.\n> \n> We'd need to either refactor fsck_tag() so that it can pass us back\n> its \"tagged_oid\" and the \"type_from_string_gently()\" value it throws\n> away to get rid of the re-parsing of stdin here, or just duplicate the\n> logic as I'm doing here.\n\nWe have yet another tag parser (because of course there's more): the one\nin parse_tag_buffer() that we use for reading tags. I think your new\nfunction here could just be:\n\n  enum object_type real_type;\n  struct tag t; /* yuck! another fake object */\n\n  memset(&t, 0, sizeof(t));\n  if (parse_tag_buffer(r, &t, buf, len) < 0)\n\tdie(\"unable to parse\");\n\n  real_type = oid_object_info(r, &t->tagged->oid, NULL);\n  if (real_type < 0)\n\tdie(\"tagged object does not exist\");\n  if (real_type != t->tagged->type)\n\tdie(\"tagged object's type does not match tag type field\");\n\nI almost wonder if we could simply rely on parse_tag_buffer() instead of\nfsck_tag(), but it is not nearly as picky about finding potential\nproblems (its goal is the opposite: to return something usable if it's\nat all possible to do so).\n\nThe \"yuck\" above isn't great. We could use pretend_object_file() for\nthat, though it's a bit heavy-weight (it computes the actual oid!). And\nalso it's a weird one-off that we've talked about getting rid of.\n\n-Peff\n"},{"id":"410879","messageId":"87tutcnva8.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"X79hCWF8jDZBw/NG@coredump.intra.peff.net","subject":"Re: [RFC/PATCH 07/12] fsck: add new \"extra\" checks for \"mktag\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T12:43:11Z","receivedAt":"2020-11-26T12:43:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Nov 26 2020, Jeff King wrote:\n\n> On Thu, Nov 26, 2020 at 02:28:49AM +0100, Ævar Arnfjörð Bjarmason wrote:\n>\n>> Add optional \"extra\" checks to fsck, these are needed to eventually\n>> replace the custom not-quite-fsck code in mktag.c.\n>> \n>> The mktag checks differ from fsck_tag() in several ways, one of those\n>> is that fsck doesn't know how to refuse an object with custom headers,\n>> and isn't strict about header and body newline separation.\n>> \n>> Teach it how to optionally report these. I thought the best way to do\n>> that given the current structure of the code was to add a new \"extra\"\n>> category in addition to error/warn/info.\n>\n> Hmm, this new severity (and the extra options bit) feels a bit\n> backwards. We are already passing the information on what we find to the\n> report() callback. It seems like that is the place that should be\n> deciding what is important and what is not.\n>\n> Unfortunately the defaults are somewhat backwards here. We'd have to\n> teach the fsck callbacks to ignore these harmless entries, rather than\n> teaching the mktag caller that they need to be respected.\n>\n> So probably the extra bit in options to say \"do these extra tag checks\"\n> is the least-bad thing. But then why do we need to put them in their own\n> EXTRA section? The only caller that wants them would treat them as\n> errors.\n\nRight, it'll be hidden behind options->extra, so I could just make them\nERROR. I guess I was thinking it would be confusing to stick stuff in\nthe middle of ERROR that wasn't on by default, e.g. I've sometimes\nskimmed that macro definition and saw \"ah, bad parent sha1 is an error\",\nas in transfer.fsckObjects would reject it.\n\nSo I'm slightly on the fence about keeping it as it is, what do you\nthink?\n\n> I'm slightly on the fence on whether mktag really needs to enforce the\n> \"unknown header\" thing at all. Sure, we don't encourage them, but it's a\n> plumbing tool one could use to experiment with new headers. I guess the\n> downside is that a typo'd header would not be caught.\n\nThe problem is that since verify_headers() in fsck.c wants to allow it,\nthere's no way for it to distinguish a fat-fingerd \"didn't separate the\nbody from the headers\" v.s. actually wanting a custom header in some\ncases.\n\n>> Under --strict the \"info\"\n>> becomes a \"warn\" and \"warn\" becomes \"error\". Existing users of\n>> fsck's (and others, e.g. index-pack) --strict option rely on this.\n>\n> Yeah, this is a weirdness I think we should eventually fix (along with\n> re-prioritizing some of the existing checks). I'm wary of doing anything\n> that further cements that somewhat broken world-view (keep in mind that\n> \"index-pack --strict\" is not \"do fsck more strictly\" but \"do fsck at\n> all\").\n\n*nod*, will note that.\n\n>> I'm not changing fsck_commit() to validate commit objects like this\n>> either, we could do that, but unlike in the tag case that code\n>> wouldn't be used anywhere. If someone wants to write a \"mkcommit\" they\n>> which behaves like \"mktag\" they can borrow or refactor this logic for\n>> use in fsck_commit().\n>\n> Seems reasonable.\n>\n> -Peff\n\n"},{"id":"410880","messageId":"87r1ognv4b.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"X79kgLjz4U9ubIKB@coredump.intra.peff.net","subject":"Re: [RFC/PATCH 08/12] mktag: use fsck instead of custom verify_tag()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T12:46:44Z","receivedAt":"2020-11-26T12:46:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Nov 26 2020, Jeff King wrote:\n\n> On Thu, Nov 26, 2020 at 02:28:50AM +0100, Ævar Arnfjörð Bjarmason wrote:\n>\n>> There was other \"mktag\" validation logic that I think makes sense to\n>> just remove. Namely:\n>> \n>>  A. fsck only cares that the timezone matches [-+][0-9]{4}. The mktag\n>>     code disallowed values larger than 1400.\n>> \n>>     Yes there's currently no timezone with a greater offset[2], but\n>>     since we allow any number of non-offical timezones (e.g. +1234)\n>>     passing this through seems fine. Git also won't break in the\n>>     future if e.g. French Polynesia decides it needs to outdo the Line\n>>     Islands when it comes to timezone extravagance.\n>\n> Yeah, I think this is a good choice to loosen.\n>\n>>  B. fsck allows missing author names such as \"tagger <email>\", mktag\n>>     wouldn't, but would allow e.g. \"tagger <email>\" (but not \"tagger\n>>     <email>\"). Now we allow all of these.\n>\n> Likewise, though I am confused. Should the second \"tagger <email>\" in\n> that paragraph have something else in it?\n\nThere should be 1-2 and 3 spaces there, don't know why that didn't make\nit. Will fix.\n\n>>  C. Like B, but \"mktag\" disallowed spaces in the <email> part, fsck\n>>     allows it.\n>\n> Possibly something we'd want to tighten in fsck, but I think keeping\n> them in alignment is a good idea for now.\n>\n>> We didn't only lose obscure validation logic, we also gained some:\n>> \n>>  D. fsck disallows zero-padded dates, but mktag didn't care. So\n>>     e.g. the timestamp \"0000000000 +0000\" produces an error now. A\n>>     test in \"t1006-cat-file.sh\" relied on this, it's been changed to\n>>     use \"hash-object\" (without fsck) instead.\n>\n> Seems reasonable.\n>\n>> +\t/* verify_tag() will be removed in the next commit */\n>> +\tverify_tag(\"\", 0);\n>> +\n>> +\t/*\n>> +\t * Fake up an object for fsck_object()\n>> +\t */\n>> +\tobj.parsed = 1;\n>> +\tobj.type = OBJ_TAG;\n>\n> I don't love this \"fake object struct on the stack\" thing. I can't think\n> of anything that would break outright, but it may be the only place\n> where that struct isn't coming from the usual pool, and representing the\n> common part of a larger object. Two definite gotchas, though:\n>\n>   - if the type is OBJ_TAG, then it may get cast to a \"struct tag\" by\n>     other code, which could look past the end of the struct. I think\n>     that fsck_object() doesn't do this, but it could (and it definitely\n>     used to)\n>\n>   - you don't initialize the other fields. We'll definitely pass\n>     &obj->oid in the fsck code, and even back to our report() callback,\n>     even though it's full of garbage. In practice this is OK because our\n>     custom report() won't look at them, but it seems awfully fragile.\n>\n> I recently genericized the type-specific fsck_* functions so that they\n> just need an oid, and not an object struct. I think I didn't do\n> fsck_object() just because it didn't have any callers where it mattered.\n> So I think it would make sense here to either:\n>\n>   - make fsck_tag() specifically available outside of fsck.c, so could\n>     call it directly\n>\n>   - convert fsck_object() to take an oid rather than an object struct.\n>     It only uses the object itself to check for NULL-ness. Looking at\n>     the callers, they all have non-NULL objects already. So I think that\n>     check can't be triggered.\n>\n>>  check_verify_failure 'Tag object length check' \\\n>> -\t'^error: .*size wrong.*$'\n>> +\t'^error: missingObject:'\n\nWhen I started out hacking on this I just made fsck_tag() a non-static\nfunction. The faking up obj only got added because I needed to call\nfsck_object().\n\nBetween this and your comment to 12/12 I wonder if the least bad thing\nisn't just to split up fsck_tag() into fsck_tag() and\nfsck_tag_standalone(), so I can call the latter with a \"populate the\nobject and type\" argument, and fsck.c will just NULL those two\nparameters since it doesn't care.\n\nGets rid of the obj faking, and gets rid of the custom parser in\nmktag.c. It'll only need to do the object lookup at that point, and even\nthen since I've added everything else to fsck.c I might as well make\nthat another custom ERROR/EXTRA, i.e. have it learn to do the lookup\nitself.\n\n> We may want to enhance the \"error:\" here to make it clear we're talking\n> about a format error in the tag input. Maybe:\n>\n>   error: tag input does not pass fsck: missingObject: ...\n>\n> or something.\n\n*nod*\n"},{"id":"410904","messageId":"20201126222257.5629-3-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126012854.399-1-avarab@gmail.com","subject":"[PATCH v2 02/10] mktag: use default strbuf_read() hint","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T22:22:49Z","receivedAt":"2020-11-26T22:23:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the hardcoded hint of 2^12 to 0. The default strbuf hint is\nperfectly fine here, and the only reason we were hardcoding it is\nbecause it survived migration from a pre-strbuf fixed-sized buffer.\n\nSee fd17f5b5f77 (Replace all read_fd use with strbuf_read, and get rid\nof it., 2007-09-10) for that migration.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 4982d3a93e..ff7ac8e0e5 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -161,7 +161,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (argc != 1)\n \t\tusage(\"git mktag\");\n \n-\tif (strbuf_read(&buf, 0, 4096) < 0) {\n+\tif (strbuf_read(&buf, 0, 0) < 0) {\n \t\tdie_errno(\"could not read from stdin\");\n \t}\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410905","messageId":"20201126222257.5629-5-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126012854.399-1-avarab@gmail.com","subject":"[PATCH v2 04/10] mktag: remove redundant braces in one-line body \"if\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T22:22:51Z","receivedAt":"2020-11-26T22:23:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This minor stylistic churn is usually something we'd avoid, but if we\ndon't do this then the file after changes in subsequent commits will\nonly have this minor style inconsistency, so let's change this while\nwe're at it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 603b55aca0..dc354828f7 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -161,9 +161,8 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (argc != 1)\n \t\tusage(\"git mktag\");\n \n-\tif (strbuf_read(&buf, 0, 0) < 0) {\n+\tif (strbuf_read(&buf, 0, 0) < 0)\n \t\tdie_errno(\"could not read from stdin\");\n-\t}\n \n \t/* Verify it for some basic sanity: it needs to start with\n \t   \"object <sha1>\\ntype\\ntagger \" */\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410906","messageId":"20201126222257.5629-4-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126012854.399-1-avarab@gmail.com","subject":"[PATCH v2 03/10] mktag: reword write_object_file() error","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T22:22:50Z","receivedAt":"2020-11-26T22:23:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the error message emitted when write_object_file() fails to\nmake more sense. At this point we're not writing a \"tag file\" (which\nas an aside we never do, we just write to stdout). We are writing an\nannotated tag object, let's say that instead.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex ff7ac8e0e5..603b55aca0 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -171,7 +171,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \t\tdie(\"invalid tag signature file\");\n \n \tif (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)\n-\t\tdie(\"unable to write tag file\");\n+\t\tdie(\"unable to write annotated tag object\");\n \n \tstrbuf_release(&buf);\n \tprintf(\"%s\\n\", oid_to_hex(&result));\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410907","messageId":"20201126222257.5629-1-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126012854.399-1-avarab@gmail.com","subject":"[PATCH v2 00/10] make \"mktag\" use fsck_tag()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T22:22:47Z","receivedAt":"2020-11-26T22:23:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now a non-RFC. I went for the approach I suggested in\n<87r1ognv4b.fsf@evledraar.gmail.com> of just having fsck_tag() able to\noptionally tell us about its parsed tag/type, thus avoiding any need\nfor a custom parser in mktag.c. Hopefully I've addressed the rest of\nthe feedback, range-diff below.\n\nÆvar Arnfjörð Bjarmason (10):\n  mktag doc: say <hash> not <sha1>\n  mktag: use default strbuf_read() hint\n  mktag: reword write_object_file() error\n  mktag: remove redundant braces in one-line body \"if\"\n  mktag tests: don't needlessly use a subshell\n  mktag tests: remove needless SHA-1 hardcoding\n  mktag tests: improve verify_object() test coverage\n  fsck: add new \"extra\" checks for \"mktag\"\n  mktag: use fsck instead of custom verify_tag()\n  mktag doc: update to explain why to use this\n\n Documentation/git-hash-object.txt |   4 +\n Documentation/git-mktag.txt       |  31 ++++-\n builtin/mktag.c                   | 202 ++++++++----------------------\n fsck.c                            |  47 ++++++-\n fsck.h                            |  11 ++\n t/t1006-cat-file.sh               |   2 +-\n t/t3800-mktag.sh                  | 117 ++++++++++++-----\n 7 files changed, 222 insertions(+), 192 deletions(-)\n\nRange-diff:\n11:  a7a6524b2a !  1:  f46abb37df mktag docs: say <hash> not <sha1>\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    mktag docs: say <hash> not <sha1>\n    +    mktag doc: say <hash> not <sha1>\n     \n         Change the \"mktag\" documentation to refer to the input hash as just\n         \"hash\", not \"sha1\". This command has supported SHA-256 for a while\n 1:  f6d9482c5b =  2:  1b4d9a5330 mktag: use default strbuf_read() hint\n 2:  49be8dbdd3 =  3:  83f4af6013 mktag: reword write_object_file() error\n 3:  868d424e65 =  4:  bca1484ed9 mktag: remove redundant braces in one-line body \"if\"\n 4:  e57969ce5d =  5:  ac7c4097c9 mktag tests: don't needlessly use a subshell\n 5:  8cf5d95b1e !  6:  5e076659e4 mktag tests: remove needless SHA-1 hardcoding\n    @@ Commit message\n     \n         Some of these tests were failing for the wrong reasons. The first one\n         being modified here would fail because the line starts with \"xxxxxx\"\n    -    instead of \"object\", the rest of the line doesn't matter. Let's just\n    -    put a valid hash on the rest of the line anyway to narrow the test\n    -    down for just the s/object/xxxxxx/ case.\n    +    instead of \"object\", the rest of the line doesn't matter.\n    +\n    +    Let's just put a valid hash on the rest of the line anyway to narrow\n    +    the test down for just the s/object/xxxxxx/ case.\n     \n         The second one being modified here would fail under\n         GIT_TEST_DEFAULT_HASH=sha256 because <some sha-1 length garbage> is an\n    @@ Commit message\n         garbage> when under SHA-256.\n     \n         This doesn't really matter since we should be able to trust other\n    -    parts of the code to validate things in the 0-9a-f range, but let's do\n    -    it for good measure.\n    +    parts of the code to validate things in the 0-9a-f range, but let's\n    +    keep it for good measure.\n     \n         There's a later test which tests an invalid SHA which looks like a\n         valid one, to stress the \"We refuse to tag something we can't\n         verify[...]\" logic in mktag.c.\n     \n         But here we're testing for a SHA-length string which contains\n    -    characters outside of the /[0-9a-f]/i set. Let's just do that with a\n    -    ROT13 invocation.\n    -\n    -    We could get really unlucky and switch to a future hash function that\n    -    just happens to produce all [0-9] output for this particular input,\n    -    but that's very unlikely.\n    +    characters outside of the /[0-9a-f]/i set.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ t/t3800-mktag.sh: EOF\n     -#  3. object line SHA1 check\n     +#  3. object line SHA check\n      \n    -+invalid_sha=$(echo $head | tr A-Za-z N-ZA-Mn-za-m)\n      cat >tag.sig <<EOF\n     -object zz9e9b33986b1c2670fff52c5067603117b3e895\n    -+object $invalid_sha\n    ++object $(echo $head | tr 0-9a-f z)\n      type tag\n      tag mytag\n      tagger . <> 0 +0000\n 6:  2899f9cda5 <  -:  ---------- mktag tests: improve verify_object() test coverage\n -:  ---------- >  7:  a048c3e640 mktag tests: improve verify_object() test coverage\n 7:  70d1d877aa =  8:  dab44d3235 fsck: add new \"extra\" checks for \"mktag\"\n 8:  d37d684135 !  9:  8ff853caee mktag: use fsck instead of custom verify_tag()\n    @@ Metadata\n      ## Commit message ##\n         mktag: use fsck instead of custom verify_tag()\n     \n    -    TODO: This subtly breaks one check, see the last patch in this series.\n    -\n         Change the validation logic in \"mktag\" to use fsck's fsck_tag()\n         instead of its own custom parser. Curiously the logic for both dates\n         back to the same commit[1]. Let's unify them so we're not maintaining\n    @@ Commit message\n             Islands when it comes to timezone extravagance.\n     \n          B. fsck allows missing author names such as \"tagger <email>\", mktag\n    -        wouldn't, but would allow e.g. \"tagger <email>\" (but not \"tagger\n    -        <email>\"). Now we allow all of these.\n    +        wouldn't, but would allow e.g. \"tagger [2 spaces] <email>\" (but\n    +        not \"tagger [1 space] <email>\"). Now we allow all of these.\n     \n          C. Like B, but \"mktag\" disallowed spaces in the <email> part, fsck\n             allows it.\n    @@ builtin/mktag.c\n      #include \"object-store.h\"\n     +#include \"fsck.h\"\n      \n    - /*\n    -  * A signature file has a very simple fixed format: four lines\n    -@@ builtin/mktag.c: static int verify_tag(char *buffer, unsigned long size)\n    - \tconst char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;\n    - \tsize_t len;\n    - \n    -+\t/* verify_tag() will be removed in the next commit */\n    -+\treturn 0;\n    -+\n    - \tif (size < 84)\n    - \t\treturn error(\"wanna fool me ? you obviously got the size wrong !\");\n    - \n    -@@ builtin/mktag.c: static int verify_tag(char *buffer, unsigned long size)\n    - \treturn 0;\n    - }\n    - \n    +-/*\n    +- * A signature file has a very simple fixed format: four lines\n    +- * of \"object <sha1>\" + \"type <typename>\" + \"tag <tagname>\" +\n    +- * \"tagger <committer>\", followed by a blank line, a free-form tag\n    +- * message and a signature block that git itself doesn't care about,\n    +- * but that can be verified with gpg or similar.\n    +- *\n    +- * The first four lines are guaranteed to be at least 83 bytes:\n    +- * \"object <sha1>\\n\" is 48 bytes, \"type tag\\n\" at 9 bytes is the\n    +- * shortest possible type-line, \"tag .\\n\" at 6 bytes is the shortest\n    +- * single-character-tag line, and \"tagger . <> 0 +0000\\n\" at 20 bytes is\n    +- * the shortest possible tagger-line.\n    +- */\n    +-\n    +-/*\n    +- * We refuse to tag something we can't verify. Just because.\n    +- */\n    +-static int verify_object(const struct object_id *oid, const char *expected_type)\n     +static int mktag_fsck_error_func(struct fsck_options *o,\n     +\t\t\t\t const struct object_id *oid,\n     +\t\t\t\t enum object_type object_type,\n     +\t\t\t\t int msg_type, const char *message)\n    -+{\n    + {\n    +-\tint ret = -1;\n    +-\tenum object_type type;\n    +-\tunsigned long size;\n    +-\tvoid *buffer = read_object_file(oid, &type, &size);\n    +-\tconst struct object_id *repl = lookup_replace_object(the_repository, oid);\n    +-\n    +-\tif (buffer) {\n    +-\t\tif (type == type_from_string(expected_type)) {\n    +-\t\t\tret = check_object_signature(the_repository, repl,\n    +-\t\t\t\t\t\t     buffer, size,\n    +-\t\t\t\t\t\t     expected_type);\n    +-\t\t}\n    +-\t\tfree(buffer);\n     +\tswitch (msg_type) {\n     +\tcase FSCK_WARN:\n     +\tcase FSCK_ERROR:\n    @@ builtin/mktag.c: static int verify_tag(char *buffer, unsigned long size)\n     +\t\t * like missing \"tagger\" lines are \"only\" warnings\n     +\t\t * under fsck, we've always considered them an error.\n     +\t\t */\n    -+\t\tfprintf_ln(stderr, \"error: %s\", message);\n    ++\t\tfprintf_ln(stderr, \"error: tag input does not pass fsck: %s\", message);\n     +\t\treturn 1;\n     +\tdefault:\n     +\t\tBUG(\"%d (FSCK_IGNORE?) should never trigger this callback\",\n     +\t\t    msg_type);\n    -+\t}\n    -+}\n    + \t}\n    +-\treturn ret;\n    + }\n    + \n    +-static int verify_tag(char *buffer, unsigned long size)\n    ++static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n    + {\n    +-\tint typelen;\n    +-\tchar type[20];\n    +-\tstruct object_id oid;\n    +-\tconst char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;\n    +-\tsize_t len;\n    +-\n    +-\tif (size < 84)\n    +-\t\treturn error(\"wanna fool me ? you obviously got the size wrong !\");\n    +-\n    +-\tbuffer[size] = 0;\n    +-\n    +-\t/* Verify object line */\n    +-\tobject = buffer;\n    +-\tif (memcmp(object, \"object \", 7))\n    +-\t\treturn error(\"char%d: does not start with \\\"object \\\"\", 0);\n    +-\n    +-\tif (parse_oid_hex(object + 7, &oid, &p))\n    +-\t\treturn error(\"char%d: could not get SHA1 hash\", 7);\n    +-\n    +-\t/* Verify type line */\n    +-\ttype_line = p + 1;\n    +-\tif (memcmp(type_line - 1, \"\\ntype \", 6))\n    +-\t\treturn error(\"char%d: could not find \\\"\\\\ntype \\\"\", 47);\n    +-\n    +-\t/* Verify tag-line */\n    +-\ttag_line = strchr(type_line, '\\n');\n    +-\tif (!tag_line)\n    +-\t\treturn error(\"char%\"PRIuMAX\": could not find next \\\"\\\\n\\\"\",\n    +-\t\t\t\t(uintmax_t) (type_line - buffer));\n    +-\ttag_line++;\n    +-\tif (memcmp(tag_line, \"tag \", 4) || tag_line[4] == '\\n')\n    +-\t\treturn error(\"char%\"PRIuMAX\": no \\\"tag \\\" found\",\n    +-\t\t\t\t(uintmax_t) (tag_line - buffer));\n    +-\n    +-\t/* Get the actual type */\n    +-\ttypelen = tag_line - type_line - strlen(\"type \\n\");\n    +-\tif (typelen >= sizeof(type))\n    +-\t\treturn error(\"char%\"PRIuMAX\": type too long\",\n    +-\t\t\t\t(uintmax_t) (type_line+5 - buffer));\n    +-\n    +-\tmemcpy(type, type_line+5, typelen);\n    +-\ttype[typelen] = 0;\n    +-\n    +-\t/* Verify that the object matches */\n    +-\tif (verify_object(&oid, type))\n    +-\t\treturn error(\"char%d: could not verify object %s\", 7, oid_to_hex(&oid));\n    +-\n    +-\t/* Verify the tag-name: we don't allow control characters or spaces in it */\n    +-\ttag_line += 4;\n    +-\tfor (;;) {\n    +-\t\tunsigned char c = *tag_line++;\n    +-\t\tif (c == '\\n')\n    +-\t\t\tbreak;\n    +-\t\tif (c > ' ')\n    +-\t\t\tcontinue;\n    +-\t\treturn error(\"char%\"PRIuMAX\": could not verify tag name\",\n    +-\t\t\t\t(uintmax_t) (tag_line - buffer));\n    +-\t}\n    +-\n    +-\t/* Verify the tagger line */\n    +-\ttagger_line = tag_line;\n    +-\n    +-\tif (memcmp(tagger_line, \"tagger \", 7))\n    +-\t\treturn error(\"char%\"PRIuMAX\": could not find \\\"tagger \\\"\",\n    +-\t\t\t(uintmax_t) (tagger_line - buffer));\n    +-\n    +-\t/*\n    +-\t * Check for correct form for name and email\n    +-\t * i.e. \" <\" followed by \"> \" on _this_ line\n    +-\t * No angle brackets within the name or email address fields.\n    +-\t * No spaces within the email address field.\n    +-\t */\n    +-\ttagger_line += 7;\n    +-\tif (!(lb = strstr(tagger_line, \" <\")) || !(rb = strstr(lb+2, \"> \")) ||\n    +-\t\tstrpbrk(tagger_line, \"<>\\n\") != lb+1 ||\n    +-\t\tstrpbrk(lb+2, \"><\\n \") != rb)\n    +-\t\treturn error(\"char%\"PRIuMAX\": malformed tagger field\",\n    +-\t\t\t(uintmax_t) (tagger_line - buffer));\n    +-\n    +-\t/* Check for author name, at least one character, space is acceptable */\n    +-\tif (lb == tagger_line)\n    +-\t\treturn error(\"char%\"PRIuMAX\": missing tagger name\",\n    +-\t\t\t(uintmax_t) (tagger_line - buffer));\n    +-\n    +-\t/* timestamp, 1 or more digits followed by space */\n    +-\ttagger_line = rb + 2;\n    +-\tif (!(len = strspn(tagger_line, \"0123456789\")))\n    +-\t\treturn error(\"char%\"PRIuMAX\": missing tag timestamp\",\n    +-\t\t\t(uintmax_t) (tagger_line - buffer));\n    +-\ttagger_line += len;\n    +-\tif (*tagger_line != ' ')\n    +-\t\treturn error(\"char%\"PRIuMAX\": malformed tag timestamp\",\n    +-\t\t\t(uintmax_t) (tagger_line - buffer));\n    +-\ttagger_line++;\n    +-\n    +-\t/* timezone, 5 digits [+-]hhmm, max. 1400 */\n    +-\tif (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&\n    +-\t      strspn(tagger_line+1, \"0123456789\") == 4 &&\n    +-\t      tagger_line[5] == '\\n' && atoi(tagger_line+1) <= 1400))\n    +-\t\treturn error(\"char%\"PRIuMAX\": malformed tag timezone\",\n    +-\t\t\t(uintmax_t) (tagger_line - buffer));\n    +-\ttagger_line += 6;\n    +-\n    +-\t/* Verify the blank line separating the header from the body */\n    +-\tif (*tagger_line != '\\n')\n    +-\t\treturn error(\"char%\"PRIuMAX\": trailing garbage in tag header\",\n    +-\t\t\t(uintmax_t) (tagger_line - buffer));\n    ++\tint ret;\n    ++\tenum object_type type;\n    ++\tunsigned long size;\n    ++\tvoid *buffer;\n    ++\tconst struct object_id *repl;\n    ++\n    ++\tbuffer = read_object_file(tagged_oid, &type, &size);\n    ++\tif (!buffer)\n    ++\t\tdie(\"could not read tagged object '%s'\\n\",\n    ++\t\t    oid_to_hex(tagged_oid));\n    ++\tif (type != *tagged_type)\n    ++\t\tdie(\"object '%s' tagged as '%s', but is a '%s' type\\n\",\n    ++\t\t    oid_to_hex(tagged_oid),\n    ++\t\t    type_name(*tagged_type), type_name(type));\n     +\n    ++\trepl = lookup_replace_object(the_repository, tagged_oid);\n    ++\tret = check_object_signature(the_repository, repl,\n    ++\t\t\t\t     buffer, size, type_name(*tagged_type));\n    ++\tfree(buffer);\n    + \n    +-\t/* The actual stuff afterwards we don't care about.. */\n    +-\treturn 0;\n    ++\treturn ret;\n    + }\n    + \n      int cmd_mktag(int argc, const char **argv, const char *prefix)\n      {\n    -+\tstruct object obj;\n      \tstruct strbuf buf = STRBUF_INIT;\n    - \tstruct object_id result;\n     +\tstruct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n    ++\tstruct object_id tagged_oid;\n    ++\tint tagged_type;\n    + \tstruct object_id result;\n      \n      \tif (argc != 1)\n    - \t\tusage(\"git mktag\");\n     @@ builtin/mktag.c: int cmd_mktag(int argc, const char **argv, const char *prefix)\n      \tif (strbuf_read(&buf, 0, 0) < 0)\n      \t\tdie_errno(\"could not read from stdin\");\n    @@ builtin/mktag.c: int cmd_mktag(int argc, const char **argv, const char *prefix)\n     -\t   \"object <sha1>\\ntype\\ntagger \" */\n     -\tif (verify_tag(buf.buf, buf.len) < 0)\n     -\t\tdie(\"invalid tag signature file\");\n    -+\t/* verify_tag() will be removed in the next commit */\n    -+\tverify_tag(\"\", 0);\n    -+\n    -+\t/*\n    -+\t * Fake up an object for fsck_object()\n    -+\t */\n    -+\tobj.parsed = 1;\n    -+\tobj.type = OBJ_TAG;\n    -+\n     +\tfsck_options.extra = 1;\n     +\tfsck_options.error_func = mktag_fsck_error_func;\n    -+\tif (fsck_object(&obj, buf.buf, buf.len, &fsck_options))\n    ++\tif (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,\n    ++\t\t\t\t&tagged_oid, &tagged_type))\n     +\t\tdie(\"tag on stdin did not pass our strict fsck check\");\n    ++\n    ++\tif (verify_object_in_tag(&tagged_oid, &tagged_type))\n    ++\t\tdie(\"tag on stdin did not refer to a valid object\");\n      \n      \tif (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)\n      \t\tdie(\"unable to write annotated tag object\");\n     \n    + ## fsck.c ##\n    +@@ fsck.c: static int fsck_tag(const struct object_id *oid, const char *buffer,\n    + \t\t    unsigned long size, struct fsck_options *options)\n    + {\n    + \tstruct object_id tagged_oid;\n    ++\tint tagged_type;\n    ++\treturn fsck_tag_standalone(oid, buffer, size, options, &tagged_oid,\n    ++\t\t\t\t   &tagged_type);\n    ++}\n    ++\n    ++int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n    ++\t\t\tunsigned long size, struct fsck_options *options,\n    ++\t\t\tstruct object_id *tagged_oid,\n    ++\t\t\tint *tagged_type)\n    ++{\n    + \tint ret = 0;\n    + \tchar *eol;\n    + \tstruct strbuf sb = STRBUF_INIT;\n    +@@ fsck.c: static int fsck_tag(const struct object_id *oid, const char *buffer,\n    + \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, \"invalid format - expected 'object' line\");\n    + \t\tgoto done;\n    + \t}\n    +-\tif (parse_oid_hex(buffer, &tagged_oid, &p) || *p != '\\n') {\n    ++\tif (parse_oid_hex(buffer, tagged_oid, &p) || *p != '\\n') {\n    + \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n    + \t\tif (ret)\n    + \t\t\tgoto done;\n    +@@ fsck.c: static int fsck_tag(const struct object_id *oid, const char *buffer,\n    + \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE, \"invalid format - unexpected end after 'type' line\");\n    + \t\tgoto done;\n    + \t}\n    +-\tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n    ++\t*tagged_type = type_from_string_gently(buffer, eol - buffer, 1);\n    ++\tif (*tagged_type < 0)\n    + \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_TYPE, \"invalid 'type' value\");\n    + \tif (ret)\n    + \t\tgoto done;\n    +\n    + ## fsck.h ##\n    +@@ fsck.h: int fsck_walk(struct object *obj, void *data, struct fsck_options *options);\n    + int fsck_object(struct object *obj, void *data, unsigned long size,\n    + \tstruct fsck_options *options);\n    + \n    ++/*\n    ++ * fsck a tag, and pass info about it back to the caller. This is\n    ++ * exposed fsck_object() internals for git-mktag(1).\n    ++ */\n    ++int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n    ++\t\t\tunsigned long size, struct fsck_options *options,\n    ++\t\t\tstruct object_id *tagged_oid,\n    ++\t\t\tint *tag_type);\n    ++\n    + /*\n    +  * Some fsck checks are context-dependent, and may end up queued; run this\n    +  * after completing all fsck_object() calls in order to resolve any remaining\n    +\n      ## t/t1006-cat-file.sh ##\n     @@ t/t1006-cat-file.sh: tag_content=\"$tag_header_without_timestamp 0000000000 +0000\n      \n    @@ t/t3800-mktag.sh: too short for a tag\n      \n      check_verify_failure 'Tag object length check' \\\n     -\t'^error: .*size wrong.*$'\n    -+\t'^error: missingObject:'\n    ++\t'^error:.* missingObject:'\n      \n      ############################################################\n      #  2. object line label check\n    @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n      EOF\n      \n     -check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n    -+check_verify_failure '\"object\" line label check' '^error: missingObject:'\n    ++check_verify_failure '\"object\" line label check' '^error:.* missingObject:'\n      \n      ############################################################\n      #  3. object line SHA check\n    @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n      EOF\n      \n     -check_verify_failure '\"object\" line SHA1 check' '^error: char7: .*SHA1 hash$'\n    -+check_verify_failure '\"object\" line check' '^error: badObjectSha1:'\n    ++check_verify_failure '\"object\" line check' '^error:.* badObjectSha1:'\n      \n      ############################################################\n      #  4. type line label check\n    @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n      EOF\n      \n     -check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n    -+check_verify_failure '\"type\" line label check' '^error: missingTypeEntry:'\n    ++check_verify_failure '\"type\" line label check' '^error:.* missingTypeEntry:'\n      \n      ############################################################\n      #  5. type line eol check\n    @@ t/t3800-mktag.sh: check_verify_failure '\"type\" line label check' '^error: char.*\n      printf \"type tagsssssssssssssssssssssssssssssss\" >>tag.sig\n      \n     -check_verify_failure '\"type\" line eol check' '^error: char.*: .*\"\\\\n\"$'\n    -+check_verify_failure '\"type\" line eol check' '^error: unterminatedHeader:'\n    ++check_verify_failure '\"type\" line eol check' '^error:.* unterminatedHeader:'\n      \n      ############################################################\n      #  6. tag line label check #1\n    @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n      \n      check_verify_failure '\"tag\" line label check #1' \\\n     -\t'^error: char.*: no \"tag \" found$'\n    -+\t'^error: missingTagEntry:'\n    ++\t'^error:.* missingTagEntry:'\n      \n      ############################################################\n      #  7. tag line label check #2\n    @@ t/t3800-mktag.sh: tag\n      \n      check_verify_failure '\"tag\" line label check #2' \\\n     -\t'^error: char.*: no \"tag \" found$'\n    -+\t'^error: badType:'\n    ++\t'^error:.* badType:'\n      \n      ############################################################\n      #  8. type line type-name length check\n    @@ t/t3800-mktag.sh: tag mytag\n      \n      check_verify_failure '\"type\" line type-name length check' \\\n     -\t'^error: char.*: type too long$'\n    -+\t'^error: badType:'\n    ++\t'^error:.* badType:'\n      \n      ############################################################\n    - #  9. verify object (SHA1/type) check\n    + #  9. verify object (SHA/type) check\n     @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n      EOF\n      \n    - check_verify_failure 'verify object (SHA1/type) check' \\\n    + check_verify_failure 'verify object (SHA/type) check -- correct type, nonexisting object' \\\n     -\t'^error: char7: could not verify object.*$'\n    -+\t'^error: badType:'\n    ++\t'^fatal: could not read tagged object'\n    + \n    + cat >tag.sig <<EOF\n    + object $head\n    +@@ t/t3800-mktag.sh: tagger . <> 0 +0000\n    + EOF\n    + \n    + check_verify_failure 'verify object (SHA/type) check -- made-up type, nonexisting object' \\\n    +-\t'^fatal: invalid object type'\n    ++\t'^error:.* badType:'\n    + \n    + cat >tag.sig <<EOF\n    + object $(test_oid deadbeef)\n    +@@ t/t3800-mktag.sh: tagger . <> 0 +0000\n    + EOF\n    + \n    + check_verify_failure 'verify object (SHA/type) check -- incorrect type, valid object' \\\n    +-\t'^error: char7: could not verify object.*$'\n    ++\t'^error:.* badType:'\n    + \n    + cat >tag.sig <<EOF\n    + object $head\n    +@@ t/t3800-mktag.sh: tagger . <> 0 +0000\n    + EOF\n    + \n    + check_verify_failure 'verify object (SHA/type) check -- incorrect type, valid object' \\\n    +-\t'^error: char7: could not verify object'\n    ++\t'^fatal: object.*tagged as.*tree.*but is.*commit'\n      \n      ############################################################\n      # 10. verify tag-name check\n    @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n      \n      check_verify_failure 'verify tag-name check' \\\n     -\t'^error: char.*: could not verify tag name$'\n    -+\t'^error: badTagName:'\n    ++\t'^error:.* badTagName:'\n      \n      ############################################################\n      # 11. tagger line label check #1\n    @@ t/t3800-mktag.sh: This is filler\n      \n      check_verify_failure '\"tagger\" line label check #1' \\\n     -\t'^error: char.*: could not find \"tagger \"$'\n    -+\t'^error: missingTaggerEntry:'\n    ++\t'^error:.* missingTaggerEntry:'\n      \n      ############################################################\n      # 12. tagger line label check #2\n    @@ t/t3800-mktag.sh: This is filler\n      \n      check_verify_failure '\"tagger\" line label check #2' \\\n     -\t'^error: char.*: could not find \"tagger \"$'\n    -+\t'^error: missingTaggerEntry:'\n    ++\t'^error:.* missingTaggerEntry:'\n      \n      ############################################################\n     -# 13. disallow missing tag author name\n    @@ t/t3800-mktag.sh: tagger T A Gger <\n      \n      check_verify_failure 'disallow malformed tagger' \\\n     -\t'^error: char.*: malformed tagger field$'\n    -+\t'^error: badEmail:'\n    ++\t'^error:.* badEmail:'\n      \n      ############################################################\n      # 15. allow empty tag email\n    @@ t/t3800-mktag.sh: tagger T A Gger <tagger@example.com>__\n      \n      check_verify_failure 'disallow missing tag timestamp' \\\n     -\t'^error: char.*: missing tag timestamp$'\n    -+\t'^error: badDate:'\n    ++\t'^error:.* badDate:'\n      \n      ############################################################\n      # 18. detect invalid tag timestamp1\n    @@ t/t3800-mktag.sh: tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008\n      \n      check_verify_failure 'detect invalid tag timestamp1' \\\n     -\t'^error: char.*: missing tag timestamp$'\n    -+\t'^error: badDate:'\n    ++\t'^error:.* badDate:'\n      \n      ############################################################\n      # 19. detect invalid tag timestamp2\n    @@ t/t3800-mktag.sh: tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500\n      \n      check_verify_failure 'detect invalid tag timestamp2' \\\n     -\t'^error: char.*: malformed tag timestamp$'\n    -+\t'^error: badDate:'\n    ++\t'^error:.* badDate:'\n      \n      ############################################################\n      # 20. detect invalid tag timezone1\n    @@ t/t3800-mktag.sh: tagger T A Gger <tagger@example.com> 1206478233 GMT\n      \n      check_verify_failure 'detect invalid tag timezone1' \\\n     -\t'^error: char.*: malformed tag timezone$'\n    -+\t'^error: badTimezone:'\n    ++\t'^error:.* badTimezone:'\n      \n      ############################################################\n      # 21. detect invalid tag timezone2\n    @@ t/t3800-mktag.sh: tagger T A Gger <tagger@example.com> 1206478233 +  30\n      \n      check_verify_failure 'detect invalid tag timezone2' \\\n     -\t'^error: char.*: malformed tag timezone$'\n    -+\t'^error: badTimezone:'\n    ++\t'^error:.* badTimezone:'\n      \n      ############################################################\n     -# 22. detect invalid tag timezone3\n    @@ t/t3800-mktag.sh: this line should not be here\n      \n      check_verify_failure 'detect invalid header entry' \\\n     -\t'^error: char.*: trailing garbage in tag header$'\n    -+\t'^error: extraHeaderEntry:'\n    ++\t'^error:.* extraHeaderEntry:'\n     +\n     +cat >tag.sig <<EOF\n     +object $head\n    @@ t/t3800-mktag.sh: this line should not be here\n     +EOF\n     +\n     +check_verify_failure 'detect invalid header entry' \\\n    -+\t'^error: extraHeaderBodyNewline:'\n    ++\t'^error:.* extraHeaderBodyNewline:'\n      \n      ############################################################\n      # 24. create valid tag\n 9:  403820698b <  -:  ---------- mktag: remove now-unused verify_tag() code\n10:  78a4877450 = 10:  e38feefd3f mktag doc: update to explain why to use this\n12:  0e840246a8 <  -:  ---------- mktag: bring back some of the verify_object() logic\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410908","messageId":"20201126222257.5629-6-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126012854.399-1-avarab@gmail.com","subject":"[PATCH v2 05/10] mktag tests: don't needlessly use a subshell","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T22:22:52Z","receivedAt":"2020-11-26T22:23:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The use of a subshell dates back to e9b20943b77 (t/t3800: do not use a\ntemporary file to hold expected result., 2008-01-04). It's not needed\nanymore, if it ever was.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex d696aa4e52..0e411e3c45 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -14,7 +14,7 @@ test_description='git mktag: tag object verify test'\n check_verify_failure () {\n \texpect=\"$2\"\n \ttest_expect_success \"$1\" '\n-\t\t( test_must_fail git mktag <tag.sig 2>message ) &&\n+\t\ttest_must_fail git mktag <tag.sig 2>message &&\n \t\tgrep \"$expect\" message\n \t'\n }\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410909","messageId":"20201126222257.5629-2-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126012854.399-1-avarab@gmail.com","subject":"[PATCH v2 01/10] mktag doc: say <hash> not <sha1>","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T22:22:48Z","receivedAt":"2020-11-26T22:23:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the \"mktag\" documentation to refer to the input hash as just\n\"hash\", not \"sha1\". This command has supported SHA-256 for a while\nnow.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex fa6a756123..a158428eb9 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -23,7 +23,7 @@ Tag Format\n A tag signature file, to be fed to this command's standard input,\n has a very simple fixed format: four lines of\n \n-  object <sha1>\n+  object <hash>\n   type <typename>\n   tag <tagname>\n   tagger <tagger>\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410910","messageId":"20201126222257.5629-8-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126012854.399-1-avarab@gmail.com","subject":"[PATCH v2 07/10] mktag tests: improve verify_object() test coverage","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T22:22:54Z","receivedAt":"2020-11-26T22:23:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The verify_object() function in \"mktag.c\" is tasked with ensuring that\nour tag refers to a valid object.\n\nThe existing test for this might fail because it was also testing that\n\"type taggg\" didn't refer to a valid object type (it should be \"type\ntag\"), or because we referred to a valid object but got the type\nwrong.\n\nLet's split these tests up, so we're testing all combinations of a\nnon-existing object and in invalid/wrong \"type\" lines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 37 +++++++++++++++++++++++++++++++++++--\n 1 file changed, 35 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex e9d7799537..a6a67b6f27 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -125,19 +125,52 @@ check_verify_failure '\"type\" line type-name length check' \\\n \t'^error: char.*: type too long$'\n \n ############################################################\n-#  9. verify object (SHA1/type) check\n+#  9. verify object (SHA/type) check\n \n cat >tag.sig <<EOF\n object $(test_oid deadbeef)\n+type tag\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+check_verify_failure 'verify object (SHA/type) check -- correct type, nonexisting object' \\\n+\t'^error: char7: could not verify object.*$'\n+\n+cat >tag.sig <<EOF\n+object $head\n type tagggg\n tag mytag\n tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure 'verify object (SHA1/type) check' \\\n+check_verify_failure 'verify object (SHA/type) check -- made-up type, nonexisting object' \\\n+\t'^fatal: invalid object type'\n+\n+cat >tag.sig <<EOF\n+object $(test_oid deadbeef)\n+type tagggg\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+check_verify_failure 'verify object (SHA/type) check -- incorrect type, valid object' \\\n \t'^error: char7: could not verify object.*$'\n \n+cat >tag.sig <<EOF\n+object $head\n+type tree\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+check_verify_failure 'verify object (SHA/type) check -- incorrect type, valid object' \\\n+\t'^error: char7: could not verify object'\n+\n ############################################################\n # 10. verify tag-name check\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410911","messageId":"20201126222257.5629-11-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126012854.399-1-avarab@gmail.com","subject":"[PATCH v2 10/10] mktag doc: update to explain why to use this","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T22:22:57Z","receivedAt":"2020-11-26T22:23:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the mktag documentation to compare itself to the similar\n\"hash-object -t tag\" command. Before this someone reading the\ndocumentation wouldn't have much of an idea what the difference\nwas.\n\nLet's make it clear that it's to do with slightly different fsck\nvalidation logic, and cross-link the \"mktag\" and \"hash-object\"\ndocumentation to aid discover-ability.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-hash-object.txt |  4 ++++\n Documentation/git-mktag.txt       | 29 +++++++++++++++++++++++++----\n 2 files changed, 29 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-hash-object.txt b/Documentation/git-hash-object.txt\nindex df9e2c58bd..c535661ced 100644\n--- a/Documentation/git-hash-object.txt\n+++ b/Documentation/git-hash-object.txt\n@@ -58,6 +58,10 @@ OPTIONS\n \tstress-testing Git itself or reproducing characteristics of corrupt or\n \tbogus objects encountered in the wild.\n \n+SEE ALSO\n+--------\n+linkgit:git-mktag[1]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex a158428eb9..3ba15072df 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -3,7 +3,7 @@ git-mktag(1)\n \n NAME\n ----\n-git-mktag - Creates a tag object\n+git-mktag - Creates a tag object with extra validation\n \n \n SYNOPSIS\n@@ -13,10 +13,20 @@ SYNOPSIS\n \n DESCRIPTION\n -----------\n-Reads a tag contents on standard input and creates a tag object\n-that can also be used to sign other objects.\n \n-The output is the new tag's <object> identifier.\n+Reads a tag contents on standard input and creates a tag object. The\n+output is the new tag's <object> identifier.\n+\n+This command accepts a subset of what linkgit:git-hash-object[1] would\n+accept with `-t tag --stdin`. I.e. both of these work:\n+\n+    git mktag <my-tag\n+    git hash-object -t tag --stdin <my-tag\n+\n+The difference between the two is that mktag does the equivalent of a\n+linkgit:git-fsck(1) check on its input, and furthermore disallows some\n+thing linkgit:git-hash-object[1] would pass, e.g. extra headers in the\n+object before the message.\n \n Tag Format\n ----------\n@@ -34,6 +44,17 @@ exists, is separated by a blank line from the header.  The\n message part may contain a signature that Git itself doesn't\n care about, but that can be verified with gpg.\n \n+HISTORY\n+-------\n+\n+In versions of Git before v2.30.0 the \"mktag\" command's validation\n+logic was subtly different than that of linkgit:git-fsck[1]. It is now\n+a strict superset of linkgit:git-fsck[1]'s validation logic.\n+\n+SEE ALSO\n+--------\n+linkgit:git-hash-object[1],\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410912","messageId":"20201126222257.5629-7-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126012854.399-1-avarab@gmail.com","subject":"[PATCH v2 06/10] mktag tests: remove needless SHA-1 hardcoding","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T22:22:53Z","receivedAt":"2020-11-26T22:23:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the tests amended in acb49d1cc8b (t3800: make hash-size\nindependent, 2019-08-18) even more to make them independent of either\nSHA-1 or SHA-256.\n\nSome of these tests were failing for the wrong reasons. The first one\nbeing modified here would fail because the line starts with \"xxxxxx\"\ninstead of \"object\", the rest of the line doesn't matter.\n\nLet's just put a valid hash on the rest of the line anyway to narrow\nthe test down for just the s/object/xxxxxx/ case.\n\nThe second one being modified here would fail under\nGIT_TEST_DEFAULT_HASH=sha256 because <some sha-1 length garbage> is an\ninvalid SHA-256, but we should really be testing <some sha-256 length\ngarbage> when under SHA-256.\n\nThis doesn't really matter since we should be able to trust other\nparts of the code to validate things in the 0-9a-f range, but let's\nkeep it for good measure.\n\nThere's a later test which tests an invalid SHA which looks like a\nvalid one, to stress the \"We refuse to tag something we can't\nverify[...]\" logic in mktag.c.\n\nBut here we're testing for a SHA-length string which contains\ncharacters outside of the /[0-9a-f]/i set.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 0e411e3c45..e9d7799537 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -43,7 +43,7 @@ check_verify_failure 'Tag object length check' \\\n #  2. object line label check\n \n cat >tag.sig <<EOF\n-xxxxxx 139e9b33986b1c2670fff52c5067603117b3e895\n+xxxxxx $head\n type tag\n tag mytag\n tagger . <> 0 +0000\n@@ -53,10 +53,10 @@ EOF\n check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n \n ############################################################\n-#  3. object line SHA1 check\n+#  3. object line SHA check\n \n cat >tag.sig <<EOF\n-object zz9e9b33986b1c2670fff52c5067603117b3e895\n+object $(echo $head | tr 0-9a-f z)\n type tag\n tag mytag\n tagger . <> 0 +0000\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410913","messageId":"20201126222257.5629-10-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126012854.399-1-avarab@gmail.com","subject":"[PATCH v2 09/10] mktag: use fsck instead of custom verify_tag()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T22:22:56Z","receivedAt":"2020-11-26T22:23:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the validation logic in \"mktag\" to use fsck's fsck_tag()\ninstead of its own custom parser. Curiously the logic for both dates\nback to the same commit[1]. Let's unify them so we're not maintaining\ntwo sets functions to verify that a tag is OK.\n\nMoving to fsck_tag() required teaching it to optionally use some\nvalidations that only the old mktag code could perform. That was done\nin an earlier commit, the \"extraHeaderEntry\" and\n\"extraHeaderBodyNewline\" tests being added here make use of that\nlogic.\n\nThere was other \"mktag\" validation logic that I think makes sense to\njust remove. Namely:\n\n A. fsck only cares that the timezone matches [-+][0-9]{4}. The mktag\n    code disallowed values larger than 1400.\n\n    Yes there's currently no timezone with a greater offset[2], but\n    since we allow any number of non-offical timezones (e.g. +1234)\n    passing this through seems fine. Git also won't break in the\n    future if e.g. French Polynesia decides it needs to outdo the Line\n    Islands when it comes to timezone extravagance.\n\n B. fsck allows missing author names such as \"tagger <email>\", mktag\n    wouldn't, but would allow e.g. \"tagger [2 spaces] <email>\" (but\n    not \"tagger [1 space] <email>\"). Now we allow all of these.\n\n C. Like B, but \"mktag\" disallowed spaces in the <email> part, fsck\n    allows it.\n\nWe didn't only lose obscure validation logic, we also gained some:\n\n D. fsck disallows zero-padded dates, but mktag didn't care. So\n    e.g. the timestamp \"0000000000 +0000\" produces an error now. A\n    test in \"t1006-cat-file.sh\" relied on this, it's been changed to\n    use \"hash-object\" (without fsck) instead.\n\n1. ec4465adb38 (Add \"tag\" objects that can be used to sign other\n   objects., 2005-04-25)\n\n2. https://en.wikipedia.org/wiki/List_of_UTC_time_offsets\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c     | 197 ++++++++++++--------------------------------\n fsck.c              |  15 +++-\n fsck.h              |   9 ++\n t/t1006-cat-file.sh |   2 +-\n t/t3800-mktag.sh    |  78 +++++++++++-------\n 5 files changed, 121 insertions(+), 180 deletions(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex dc354828f7..54b0d75ff7 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -2,160 +2,61 @@\n #include \"tag.h\"\n #include \"replace-object.h\"\n #include \"object-store.h\"\n+#include \"fsck.h\"\n \n-/*\n- * A signature file has a very simple fixed format: four lines\n- * of \"object <sha1>\" + \"type <typename>\" + \"tag <tagname>\" +\n- * \"tagger <committer>\", followed by a blank line, a free-form tag\n- * message and a signature block that git itself doesn't care about,\n- * but that can be verified with gpg or similar.\n- *\n- * The first four lines are guaranteed to be at least 83 bytes:\n- * \"object <sha1>\\n\" is 48 bytes, \"type tag\\n\" at 9 bytes is the\n- * shortest possible type-line, \"tag .\\n\" at 6 bytes is the shortest\n- * single-character-tag line, and \"tagger . <> 0 +0000\\n\" at 20 bytes is\n- * the shortest possible tagger-line.\n- */\n-\n-/*\n- * We refuse to tag something we can't verify. Just because.\n- */\n-static int verify_object(const struct object_id *oid, const char *expected_type)\n+static int mktag_fsck_error_func(struct fsck_options *o,\n+\t\t\t\t const struct object_id *oid,\n+\t\t\t\t enum object_type object_type,\n+\t\t\t\t int msg_type, const char *message)\n {\n-\tint ret = -1;\n-\tenum object_type type;\n-\tunsigned long size;\n-\tvoid *buffer = read_object_file(oid, &type, &size);\n-\tconst struct object_id *repl = lookup_replace_object(the_repository, oid);\n-\n-\tif (buffer) {\n-\t\tif (type == type_from_string(expected_type)) {\n-\t\t\tret = check_object_signature(the_repository, repl,\n-\t\t\t\t\t\t     buffer, size,\n-\t\t\t\t\t\t     expected_type);\n-\t\t}\n-\t\tfree(buffer);\n+\tswitch (msg_type) {\n+\tcase FSCK_WARN:\n+\tcase FSCK_ERROR:\n+\tcase FSCK_EXTRA:\n+\t\t/*\n+\t\t * We treat both warnings and errors as errors, things\n+\t\t * like missing \"tagger\" lines are \"only\" warnings\n+\t\t * under fsck, we've always considered them an error.\n+\t\t */\n+\t\tfprintf_ln(stderr, \"error: tag input does not pass fsck: %s\", message);\n+\t\treturn 1;\n+\tdefault:\n+\t\tBUG(\"%d (FSCK_IGNORE?) should never trigger this callback\",\n+\t\t    msg_type);\n \t}\n-\treturn ret;\n }\n \n-static int verify_tag(char *buffer, unsigned long size)\n+static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n {\n-\tint typelen;\n-\tchar type[20];\n-\tstruct object_id oid;\n-\tconst char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;\n-\tsize_t len;\n-\n-\tif (size < 84)\n-\t\treturn error(\"wanna fool me ? you obviously got the size wrong !\");\n-\n-\tbuffer[size] = 0;\n-\n-\t/* Verify object line */\n-\tobject = buffer;\n-\tif (memcmp(object, \"object \", 7))\n-\t\treturn error(\"char%d: does not start with \\\"object \\\"\", 0);\n-\n-\tif (parse_oid_hex(object + 7, &oid, &p))\n-\t\treturn error(\"char%d: could not get SHA1 hash\", 7);\n-\n-\t/* Verify type line */\n-\ttype_line = p + 1;\n-\tif (memcmp(type_line - 1, \"\\ntype \", 6))\n-\t\treturn error(\"char%d: could not find \\\"\\\\ntype \\\"\", 47);\n-\n-\t/* Verify tag-line */\n-\ttag_line = strchr(type_line, '\\n');\n-\tif (!tag_line)\n-\t\treturn error(\"char%\"PRIuMAX\": could not find next \\\"\\\\n\\\"\",\n-\t\t\t\t(uintmax_t) (type_line - buffer));\n-\ttag_line++;\n-\tif (memcmp(tag_line, \"tag \", 4) || tag_line[4] == '\\n')\n-\t\treturn error(\"char%\"PRIuMAX\": no \\\"tag \\\" found\",\n-\t\t\t\t(uintmax_t) (tag_line - buffer));\n-\n-\t/* Get the actual type */\n-\ttypelen = tag_line - type_line - strlen(\"type \\n\");\n-\tif (typelen >= sizeof(type))\n-\t\treturn error(\"char%\"PRIuMAX\": type too long\",\n-\t\t\t\t(uintmax_t) (type_line+5 - buffer));\n-\n-\tmemcpy(type, type_line+5, typelen);\n-\ttype[typelen] = 0;\n-\n-\t/* Verify that the object matches */\n-\tif (verify_object(&oid, type))\n-\t\treturn error(\"char%d: could not verify object %s\", 7, oid_to_hex(&oid));\n-\n-\t/* Verify the tag-name: we don't allow control characters or spaces in it */\n-\ttag_line += 4;\n-\tfor (;;) {\n-\t\tunsigned char c = *tag_line++;\n-\t\tif (c == '\\n')\n-\t\t\tbreak;\n-\t\tif (c > ' ')\n-\t\t\tcontinue;\n-\t\treturn error(\"char%\"PRIuMAX\": could not verify tag name\",\n-\t\t\t\t(uintmax_t) (tag_line - buffer));\n-\t}\n-\n-\t/* Verify the tagger line */\n-\ttagger_line = tag_line;\n-\n-\tif (memcmp(tagger_line, \"tagger \", 7))\n-\t\treturn error(\"char%\"PRIuMAX\": could not find \\\"tagger \\\"\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/*\n-\t * Check for correct form for name and email\n-\t * i.e. \" <\" followed by \"> \" on _this_ line\n-\t * No angle brackets within the name or email address fields.\n-\t * No spaces within the email address field.\n-\t */\n-\ttagger_line += 7;\n-\tif (!(lb = strstr(tagger_line, \" <\")) || !(rb = strstr(lb+2, \"> \")) ||\n-\t\tstrpbrk(tagger_line, \"<>\\n\") != lb+1 ||\n-\t\tstrpbrk(lb+2, \"><\\n \") != rb)\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tagger field\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/* Check for author name, at least one character, space is acceptable */\n-\tif (lb == tagger_line)\n-\t\treturn error(\"char%\"PRIuMAX\": missing tagger name\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/* timestamp, 1 or more digits followed by space */\n-\ttagger_line = rb + 2;\n-\tif (!(len = strspn(tagger_line, \"0123456789\")))\n-\t\treturn error(\"char%\"PRIuMAX\": missing tag timestamp\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line += len;\n-\tif (*tagger_line != ' ')\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tag timestamp\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line++;\n-\n-\t/* timezone, 5 digits [+-]hhmm, max. 1400 */\n-\tif (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&\n-\t      strspn(tagger_line+1, \"0123456789\") == 4 &&\n-\t      tagger_line[5] == '\\n' && atoi(tagger_line+1) <= 1400))\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tag timezone\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line += 6;\n-\n-\t/* Verify the blank line separating the header from the body */\n-\tif (*tagger_line != '\\n')\n-\t\treturn error(\"char%\"PRIuMAX\": trailing garbage in tag header\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n+\tint ret;\n+\tenum object_type type;\n+\tunsigned long size;\n+\tvoid *buffer;\n+\tconst struct object_id *repl;\n+\n+\tbuffer = read_object_file(tagged_oid, &type, &size);\n+\tif (!buffer)\n+\t\tdie(\"could not read tagged object '%s'\\n\",\n+\t\t    oid_to_hex(tagged_oid));\n+\tif (type != *tagged_type)\n+\t\tdie(\"object '%s' tagged as '%s', but is a '%s' type\\n\",\n+\t\t    oid_to_hex(tagged_oid),\n+\t\t    type_name(*tagged_type), type_name(type));\n+\n+\trepl = lookup_replace_object(the_repository, tagged_oid);\n+\tret = check_object_signature(the_repository, repl,\n+\t\t\t\t     buffer, size, type_name(*tagged_type));\n+\tfree(buffer);\n \n-\t/* The actual stuff afterwards we don't care about.. */\n-\treturn 0;\n+\treturn ret;\n }\n \n int cmd_mktag(int argc, const char **argv, const char *prefix)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n+\tstruct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n+\tstruct object_id tagged_oid;\n+\tint tagged_type;\n \tstruct object_id result;\n \n \tif (argc != 1)\n@@ -164,10 +65,14 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (strbuf_read(&buf, 0, 0) < 0)\n \t\tdie_errno(\"could not read from stdin\");\n \n-\t/* Verify it for some basic sanity: it needs to start with\n-\t   \"object <sha1>\\ntype\\ntagger \" */\n-\tif (verify_tag(buf.buf, buf.len) < 0)\n-\t\tdie(\"invalid tag signature file\");\n+\tfsck_options.extra = 1;\n+\tfsck_options.error_func = mktag_fsck_error_func;\n+\tif (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,\n+\t\t\t\t&tagged_oid, &tagged_type))\n+\t\tdie(\"tag on stdin did not pass our strict fsck check\");\n+\n+\tif (verify_object_in_tag(&tagged_oid, &tagged_type))\n+\t\tdie(\"tag on stdin did not refer to a valid object\");\n \n \tif (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)\n \t\tdie(\"unable to write annotated tag object\");\ndiff --git a/fsck.c b/fsck.c\nindex 3c25df2244..97da3a13c6 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -914,6 +914,16 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \t\t    unsigned long size, struct fsck_options *options)\n {\n \tstruct object_id tagged_oid;\n+\tint tagged_type;\n+\treturn fsck_tag_standalone(oid, buffer, size, options, &tagged_oid,\n+\t\t\t\t   &tagged_type);\n+}\n+\n+int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n+\t\t\tunsigned long size, struct fsck_options *options,\n+\t\t\tstruct object_id *tagged_oid,\n+\t\t\tint *tagged_type)\n+{\n \tint ret = 0;\n \tchar *eol;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -927,7 +937,7 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n-\tif (parse_oid_hex(buffer, &tagged_oid, &p) || *p != '\\n') {\n+\tif (parse_oid_hex(buffer, tagged_oid, &p) || *p != '\\n') {\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n \t\tif (ret)\n \t\t\tgoto done;\n@@ -943,7 +953,8 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n-\tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n+\t*tagged_type = type_from_string_gently(buffer, eol - buffer, 1);\n+\tif (*tagged_type < 0)\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_TYPE, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\ndiff --git a/fsck.h b/fsck.h\nindex 110efc65fd..cf43046146 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -64,6 +64,15 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options);\n int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options);\n \n+/*\n+ * fsck a tag, and pass info about it back to the caller. This is\n+ * exposed fsck_object() internals for git-mktag(1).\n+ */\n+int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n+\t\t\tunsigned long size, struct fsck_options *options,\n+\t\t\tstruct object_id *tagged_oid,\n+\t\t\tint *tag_type);\n+\n /*\n  * Some fsck checks are context-dependent, and may end up queued; run this\n  * after completing all fsck_object() calls in order to resolve any remaining\ndiff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh\nindex 2f501d2dc9..5d2dc99b74 100755\n--- a/t/t1006-cat-file.sh\n+++ b/t/t1006-cat-file.sh\n@@ -166,7 +166,7 @@ tag_content=\"$tag_header_without_timestamp 0000000000 +0000\n \n $tag_description\"\n \n-tag_sha1=$(echo_without_newline \"$tag_content\" | git mktag)\n+tag_sha1=$(echo_without_newline \"$tag_content\" | git hash-object -t tag --stdin -w)\n tag_size=$(strlen \"$tag_content\")\n \n run_tests 'tag' $tag_sha1 $tag_size \"$tag_content\" \"$tag_content\" 1\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex a6a67b6f27..352235353f 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -37,7 +37,7 @@ too short for a tag\n EOF\n \n check_verify_failure 'Tag object length check' \\\n-\t'^error: .*size wrong.*$'\n+\t'^error:.* missingObject:'\n \n ############################################################\n #  2. object line label check\n@@ -50,7 +50,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n+check_verify_failure '\"object\" line label check' '^error:.* missingObject:'\n \n ############################################################\n #  3. object line SHA check\n@@ -63,7 +63,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"object\" line SHA1 check' '^error: char7: .*SHA1 hash$'\n+check_verify_failure '\"object\" line check' '^error:.* badObjectSha1:'\n \n ############################################################\n #  4. type line label check\n@@ -76,7 +76,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n+check_verify_failure '\"type\" line label check' '^error:.* missingTypeEntry:'\n \n ############################################################\n #  5. type line eol check\n@@ -84,7 +84,7 @@ check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n echo \"object $head\" >tag.sig\n printf \"type tagsssssssssssssssssssssssssssssss\" >>tag.sig\n \n-check_verify_failure '\"type\" line eol check' '^error: char.*: .*\"\\\\n\"$'\n+check_verify_failure '\"type\" line eol check' '^error:.* unterminatedHeader:'\n \n ############################################################\n #  6. tag line label check #1\n@@ -98,7 +98,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure '\"tag\" line label check #1' \\\n-\t'^error: char.*: no \"tag \" found$'\n+\t'^error:.* missingTagEntry:'\n \n ############################################################\n #  7. tag line label check #2\n@@ -110,7 +110,7 @@ tag\n EOF\n \n check_verify_failure '\"tag\" line label check #2' \\\n-\t'^error: char.*: no \"tag \" found$'\n+\t'^error:.* badType:'\n \n ############################################################\n #  8. type line type-name length check\n@@ -122,7 +122,7 @@ tag mytag\n EOF\n \n check_verify_failure '\"type\" line type-name length check' \\\n-\t'^error: char.*: type too long$'\n+\t'^error:.* badType:'\n \n ############################################################\n #  9. verify object (SHA/type) check\n@@ -136,7 +136,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (SHA/type) check -- correct type, nonexisting object' \\\n-\t'^error: char7: could not verify object.*$'\n+\t'^fatal: could not read tagged object'\n \n cat >tag.sig <<EOF\n object $head\n@@ -147,7 +147,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (SHA/type) check -- made-up type, nonexisting object' \\\n-\t'^fatal: invalid object type'\n+\t'^error:.* badType:'\n \n cat >tag.sig <<EOF\n object $(test_oid deadbeef)\n@@ -158,7 +158,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (SHA/type) check -- incorrect type, valid object' \\\n-\t'^error: char7: could not verify object.*$'\n+\t'^error:.* badType:'\n \n cat >tag.sig <<EOF\n object $head\n@@ -169,7 +169,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (SHA/type) check -- incorrect type, valid object' \\\n-\t'^error: char7: could not verify object'\n+\t'^fatal: object.*tagged as.*tree.*but is.*commit'\n \n ############################################################\n # 10. verify tag-name check\n@@ -183,7 +183,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify tag-name check' \\\n-\t'^error: char.*: could not verify tag name$'\n+\t'^error:.* badTagName:'\n \n ############################################################\n # 11. tagger line label check #1\n@@ -197,7 +197,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #1' \\\n-\t'^error: char.*: could not find \"tagger \"$'\n+\t'^error:.* missingTaggerEntry:'\n \n ############################################################\n # 12. tagger line label check #2\n@@ -212,10 +212,10 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #2' \\\n-\t'^error: char.*: could not find \"tagger \"$'\n+\t'^error:.* missingTaggerEntry:'\n \n ############################################################\n-# 13. disallow missing tag author name\n+# 13. allow missing tag author name like fsck\n \n cat >tag.sig <<EOF\n object $head\n@@ -226,8 +226,9 @@ tagger  <> 0 +0000\n This is filler\n EOF\n \n-check_verify_failure 'disallow missing tag author name' \\\n-\t'^error: char.*: missing tagger name$'\n+test_expect_success 'allow missing tag author name' '\n+\tgit mktag <tag.sig\n+'\n \n ############################################################\n # 14. disallow missing tag author name\n@@ -242,7 +243,7 @@ tagger T A Gger <\n EOF\n \n check_verify_failure 'disallow malformed tagger' \\\n-\t'^error: char.*: malformed tagger field$'\n+\t'^error:.* badEmail:'\n \n ############################################################\n # 15. allow empty tag email\n@@ -260,7 +261,7 @@ test_expect_success \\\n     'git mktag <tag.sig >.git/refs/tags/mytag 2>message'\n \n ############################################################\n-# 16. disallow spaces in tag email\n+# 16. allow spaces in tag email like fsck\n \n cat >tag.sig <<EOF\n object $head\n@@ -270,8 +271,9 @@ tagger T A Gger <tag ger@example.com> 0 +0000\n \n EOF\n \n-check_verify_failure 'disallow spaces in tag email' \\\n-\t'^error: char.*: malformed tagger field$'\n+test_expect_success 'allow spaces in tag email like fsck' '\n+\tgit mktag <tag.sig\n+'\n \n ############################################################\n # 17. disallow missing tag timestamp\n@@ -285,7 +287,7 @@ tagger T A Gger <tagger@example.com>__\n EOF\n \n check_verify_failure 'disallow missing tag timestamp' \\\n-\t'^error: char.*: missing tag timestamp$'\n+\t'^error:.* badDate:'\n \n ############################################################\n # 18. detect invalid tag timestamp1\n@@ -299,7 +301,7 @@ tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008\n EOF\n \n check_verify_failure 'detect invalid tag timestamp1' \\\n-\t'^error: char.*: missing tag timestamp$'\n+\t'^error:.* badDate:'\n \n ############################################################\n # 19. detect invalid tag timestamp2\n@@ -313,7 +315,7 @@ tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500\n EOF\n \n check_verify_failure 'detect invalid tag timestamp2' \\\n-\t'^error: char.*: malformed tag timestamp$'\n+\t'^error:.* badDate:'\n \n ############################################################\n # 20. detect invalid tag timezone1\n@@ -327,7 +329,7 @@ tagger T A Gger <tagger@example.com> 1206478233 GMT\n EOF\n \n check_verify_failure 'detect invalid tag timezone1' \\\n-\t'^error: char.*: malformed tag timezone$'\n+\t'^error:.* badTimezone:'\n \n ############################################################\n # 21. detect invalid tag timezone2\n@@ -341,10 +343,10 @@ tagger T A Gger <tagger@example.com> 1206478233 +  30\n EOF\n \n check_verify_failure 'detect invalid tag timezone2' \\\n-\t'^error: char.*: malformed tag timezone$'\n+\t'^error:.* badTimezone:'\n \n ############################################################\n-# 22. detect invalid tag timezone3\n+# 22. allow invalid tag timezone3 (the maximum is -1200/+1400)\n \n cat >tag.sig <<EOF\n object $head\n@@ -354,8 +356,9 @@ tagger T A Gger <tagger@example.com> 1206478233 -1430\n \n EOF\n \n-check_verify_failure 'detect invalid tag timezone3' \\\n-\t'^error: char.*: malformed tag timezone$'\n+test_expect_success 'allow invalid tag timezone' '\n+\tgit mktag <tag.sig\n+'\n \n ############################################################\n # 23. detect invalid header entry\n@@ -370,7 +373,20 @@ this line should not be here\n EOF\n \n check_verify_failure 'detect invalid header entry' \\\n-\t'^error: char.*: trailing garbage in tag header$'\n+\t'^error:.* extraHeaderEntry:'\n+\n+cat >tag.sig <<EOF\n+object $head\n+type commit\n+tag mytag\n+tagger T A Gger <tagger@example.com> 1206478233 -0500\n+\n+\n+this line should be one line up\n+EOF\n+\n+check_verify_failure 'detect invalid header entry' \\\n+\t'^error:.* extraHeaderBodyNewline:'\n \n ############################################################\n # 24. create valid tag\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"410914","messageId":"20201126222257.5629-9-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126012854.399-1-avarab@gmail.com","subject":"[PATCH v2 08/10] fsck: add new \"extra\" checks for \"mktag\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-11-26T22:22:55Z","receivedAt":"2020-11-26T22:23:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add optional \"extra\" checks to fsck, these are needed to eventually\nreplace the custom not-quite-fsck code in mktag.c.\n\nThe mktag checks differ from fsck_tag() in several ways, one of those\nis that fsck doesn't know how to refuse an object with custom headers,\nand isn't strict about header and body newline separation.\n\nTeach it how to optionally report these. I thought the best way to do\nthat given the current structure of the code was to add a new \"extra\"\ncategory in addition to error/warn/info. Under --strict the \"info\"\nbecomes a \"warn\" and \"warn\" becomes \"error\". Existing users of\nfsck's (and others, e.g. index-pack) --strict option rely on this.\n\nBy adding an \"extra\" category and only reporting it based on a flag in\nfsck_options callers can opt-in to these \"extra\" messages, which\nthey'll then need to deal with in their own \"error_func\".\n\nNo tests are being added for this new functionality, they're added in\na subsequent commit where we teach \"mktag\" to use this new validation\nmode.\n\nI'm not changing fsck_commit() to validate commit objects like this\neither, we could do that, but unlike in the tag case that code\nwouldn't be used anywhere. If someone wants to write a \"mkcommit\" they\nwhich behaves like \"mktag\" they can borrow or refactor this logic for\nuse in fsck_commit().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n fsck.c | 32 +++++++++++++++++++++++++++++++-\n fsck.h |  2 ++\n 2 files changed, 33 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex f82e2fe9e3..3c25df2244 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -80,7 +80,10 @@ static struct oidset gitmodules_done = OIDSET_INIT;\n \t/* infos (reported as warnings, but ignored by default) */ \\\n \tFUNC(GITMODULES_PARSE, INFO) \\\n \tFUNC(BAD_TAG_NAME, INFO) \\\n-\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n+\tFUNC(MISSING_TAGGER_ENTRY, INFO) \\\n+\t/* extra (only reported when requested) */ \\\n+\tFUNC(EXTRA_HEADER_ENTRY, EXTRA) \\\n+\tFUNC(EXTRA_HEADER_BODY_NEWLINE, EXTRA)\n \n #define MSG_ID(id, msg_type) FSCK_MSG_##id,\n enum fsck_msg_id {\n@@ -975,6 +978,33 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \telse\n \t\tret = fsck_ident(&buffer, oid, OBJ_TAG, options);\n \n+\tif (options->extra && *buffer) {\n+\t\tif (!starts_with(buffer, \"\\n\")) {\n+\t\t\t/*\n+\t\t\t * The verify_headers() check will allow\n+\t\t\t * e.g. \"[...]tagger <tagger>\\nsome\n+\t\t\t * garbage\\n\\nmessage\" to pass, thinking \"some\n+\t\t\t * garbage\" could be a custom\n+\t\t\t * header. E.g. \"mktag\" doesn't want any\n+\t\t\t * unknown headers.\n+\t\t\t */\n+\t\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_EXTRA_HEADER_ENTRY, \"invalid format - extra header(s) after 'tagger'\");\n+\t\t\tif (ret)\n+\t\t\t\tgoto done;\n+\t\t}\n+\t\tif (starts_with(buffer, \"\\n\\n\")) {\n+\t\t\t/*\n+\t\t\t * Some callers such as \"mktag\" want to\n+\t\t\t * disallow \"[...]tagger\n+\t\t\t * <tagger>\\n\\n\\nmessage\", only allowing a\n+\t\t\t * single newline for separation.\n+\t\t\t */\n+\t\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_EXTRA_HEADER_BODY_NEWLINE, \"invalid format - headers separated body by more than one newline\");\n+\t\t\tif (ret)\n+\t\t\t\tgoto done;\n+\t\t}\n+\t}\n+\n done:\n \tstrbuf_release(&sb);\n \treturn ret;\ndiff --git a/fsck.h b/fsck.h\nindex 69cf715e79..110efc65fd 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -6,6 +6,7 @@\n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n #define FSCK_IGNORE 3\n+#define FSCK_EXTRA 4\n \n struct fsck_options;\n struct object;\n@@ -40,6 +41,7 @@ struct fsck_options {\n \tunsigned strict:1;\n \tint *msg_type;\n \tstruct oidset skiplist;\n+\tunsigned extra:1;\n \tkh_oid_map_t *object_names;\n };\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"411050","messageId":"87zh2xvny6.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"20201126222257.5629-1-avarab@gmail.com","subject":"Re: [PATCH v2 00/10] make \"mktag\" use fsck_tag()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-01T10:08:01Z","receivedAt":"2020-12-01T10:08:47Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Nov 26 2020, Ævar Arnfjörð Bjarmason wrote:\n\n> Now a non-RFC. I went for the approach I suggested in\n> <87r1ognv4b.fsf@evledraar.gmail.com> of just having fsck_tag() able to\n> optionally tell us about its parsed tag/type, thus avoiding any need\n> for a custom parser in mktag.c. Hopefully I've addressed the rest of\n> the feedback, range-diff below.\n\nPing @ Jeff & brian: you said you wanted this in one shape or another,\nso mind seeing if the v2 looks good to you?:)\n\nJunio didn't pick it up for the \"What's Cooking\" sent out recently,\nhopefully some reviewer ACK/NACK will help move it forward. Thanks!\n"},{"id":"411091","messageId":"xmqqlfehpa7p.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"87zh2xvny6.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 00/10] make \"mktag\" use fsck_tag()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-01T20:01:14Z","receivedAt":"2020-12-01T20:02:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> On Thu, Nov 26 2020, Ævar Arnfjörð Bjarmason wrote:\n>\n>> Now a non-RFC. I went for the approach I suggested in\n>> <87r1ognv4b.fsf@evledraar.gmail.com> of just having fsck_tag() able to\n>> optionally tell us about its parsed tag/type, thus avoiding any need\n>> for a custom parser in mktag.c. Hopefully I've addressed the rest of\n>> the feedback, range-diff below.\n>\n> Ping @ Jeff & brian: you said you wanted this in one shape or another,\n> so mind seeing if the v2 looks good to you?:)\n>\n> Junio didn't pick it up for the \"What's Cooking\" sent out recently,\n> hopefully some reviewer ACK/NACK will help move it forward. Thanks!\n\nTrue.  I don't want to queue too many topics on 'seen', only to end\nup with a pile of patches that haven't been reviewed adequately and\ncannot move forward.\n"},{"id":"411094","messageId":"xmqqa6uxp9fv.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201126222257.5629-2-avarab@gmail.com","subject":"Re: [PATCH v2 01/10] mktag doc: say <hash> not <sha1>","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-01T20:17:56Z","receivedAt":"2020-12-01T20:19:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Change the \"mktag\" documentation to refer to the input hash as just\n> \"hash\", not \"sha1\". This command has supported SHA-256 for a while\n> now.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n\nI think saying <hash> instead of <sha> is a very good idea---it won't\ntie us to the SHA family of hashes, including SHA-256, forever.\n\n>  Documentation/git-mktag.txt | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n>\n> diff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\n> index fa6a756123..a158428eb9 100644\n> --- a/Documentation/git-mktag.txt\n> +++ b/Documentation/git-mktag.txt\n> @@ -23,7 +23,7 @@ Tag Format\n>  A tag signature file, to be fed to this command's standard input,\n>  has a very simple fixed format: four lines of\n>  \n> -  object <sha1>\n> +  object <hash>\n>    type <typename>\n>    tag <tagname>\n>    tagger <tagger>\n"},{"id":"411095","messageId":"xmqq5z5lp9d9.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201126222257.5629-3-avarab@gmail.com","subject":"Re: [PATCH v2 02/10] mktag: use default strbuf_read() hint","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-01T20:19:30Z","receivedAt":"2020-12-01T20:20:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Change the hardcoded hint of 2^12 to 0. The default strbuf hint is\n> perfectly fine here, and the only reason we were hardcoding it is\n> because it survived migration from a pre-strbuf fixed-sized buffer.\n>\n> See fd17f5b5f77 (Replace all read_fd use with strbuf_read, and get rid\n> of it., 2007-09-10) for that migration.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/mktag.c | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n>\n> diff --git a/builtin/mktag.c b/builtin/mktag.c\n> index 4982d3a93e..ff7ac8e0e5 100644\n> --- a/builtin/mktag.c\n> +++ b/builtin/mktag.c\n> @@ -161,7 +161,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n\nIn the lines beyond the pre-context there is a comment that mentions\n<sha1>, but I think it is sensible not to clean it up with this\npatch, as it will go away with the main \"use fsck machinery\" step.\n\n>  \tif (argc != 1)\n>  \t\tusage(\"git mktag\");\n>  \n> -\tif (strbuf_read(&buf, 0, 4096) < 0) {\n> +\tif (strbuf_read(&buf, 0, 0) < 0) {\n>  \t\tdie_errno(\"could not read from stdin\");\n>  \t}\n"},{"id":"411096","messageId":"xmqq1rg9p9as.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201126222257.5629-4-avarab@gmail.com","subject":"Re: [PATCH v2 03/10] mktag: reword write_object_file() error","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-01T20:20:59Z","receivedAt":"2020-12-01T20:21:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Change the error message emitted when write_object_file() fails to\n> make more sense. At this point we're not writing a \"tag file\" (which\n> as an aside we never do, we just write to stdout). We are writing an\n> annotated tag object, let's say that instead.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/mktag.c | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n>\n> diff --git a/builtin/mktag.c b/builtin/mktag.c\n> index ff7ac8e0e5..603b55aca0 100644\n> --- a/builtin/mktag.c\n> +++ b/builtin/mktag.c\n> @@ -171,7 +171,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n>  \t\tdie(\"invalid tag signature file\");\n>  \n>  \tif (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)\n> -\t\tdie(\"unable to write tag file\");\n> +\t\tdie(\"unable to write annotated tag object\");\n\n\"write an annotated tag object\"?\n\nIt is not just this call to die(), but we'd eventually want to _(l10n/i18n)\nthese messages.  Perhaps in a separate step but on all messages fed\nto die/error/warn in this file.\n\n>  \n>  \tstrbuf_release(&buf);\n>  \tprintf(\"%s\\n\", oid_to_hex(&result));\n"},{"id":"411097","messageId":"xmqqv9dlnuk5.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201126222257.5629-7-avarab@gmail.com","subject":"Re: [PATCH v2 06/10] mktag tests: remove needless SHA-1 hardcoding","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-01T20:24:42Z","receivedAt":"2020-12-01T20:25:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Change the tests amended in acb49d1cc8b (t3800: make hash-size\n> independent, 2019-08-18) even more to make them independent of either\n> SHA-1 or SHA-256.\n>\n> Some of these tests were failing for the wrong reasons. The first one\n> being modified here would fail because the line starts with \"xxxxxx\"\n> instead of \"object\", the rest of the line doesn't matter.\n>\n> Let's just put a valid hash on the rest of the line anyway to narrow\n> the test down for just the s/object/xxxxxx/ case.\n>\n> The second one being modified here would fail under\n> GIT_TEST_DEFAULT_HASH=sha256 because <some sha-1 length garbage> is an\n> invalid SHA-256, but we should really be testing <some sha-256 length\n> garbage> when under SHA-256.\n>\n> This doesn't really matter since we should be able to trust other\n> parts of the code to validate things in the 0-9a-f range, but let's\n> keep it for good measure.\n>\n> There's a later test which tests an invalid SHA which looks like a\n> valid one, to stress the \"We refuse to tag something we can't\n> verify[...]\" logic in mktag.c.\n>\n> But here we're testing for a SHA-length string which contains\n> characters outside of the /[0-9a-f]/i set.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t3800-mktag.sh | 6 +++---\n>  1 file changed, 3 insertions(+), 3 deletions(-)\n>\n> diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\n> index 0e411e3c45..e9d7799537 100755\n> --- a/t/t3800-mktag.sh\n> +++ b/t/t3800-mktag.sh\n> @@ -43,7 +43,7 @@ check_verify_failure 'Tag object length check' \\\n>  #  2. object line label check\n>  \n>  cat >tag.sig <<EOF\n> -xxxxxx 139e9b33986b1c2670fff52c5067603117b3e895\n> +xxxxxx $head\n>  type tag\n>  tag mytag\n>  tagger . <> 0 +0000\n\nThese steps to prepare the input and the expected output outside the\ntest_expect_* block look too old-fashioned but they are consistently\nso, so let's let them pass, at least inside this series.\n\n> @@ -53,10 +53,10 @@ EOF\n>  check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n>  \n>  ############################################################\n> -#  3. object line SHA1 check\n> +#  3. object line SHA check\n\nShouldn't we say hash instead of SHA for consistency?\n\n>  \n>  cat >tag.sig <<EOF\n> -object zz9e9b33986b1c2670fff52c5067603117b3e895\n> +object $(echo $head | tr 0-9a-f z)\n>  type tag\n>  tag mytag\n>  tagger . <> 0 +0000\n"},{"id":"411098","messageId":"xmqqo8jdnu63.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201126222257.5629-9-avarab@gmail.com","subject":"Re: [PATCH v2 08/10] fsck: add new \"extra\" checks for \"mktag\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-01T20:33:08Z","receivedAt":"2020-12-01T20:33:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Add optional \"extra\" checks to fsck, these are needed to eventually\n> replace the custom not-quite-fsck code in mktag.c.\n>\n> The mktag checks differ from fsck_tag() in several ways, one of those\n> is that fsck doesn't know how to refuse an object with custom headers,\n> and isn't strict about header and body newline separation.\n\nYou say \"there must be only one blank line between the header and\nthe body\", but viewing from the way we parse header and body, I\nthink that such a rule actually forbids a leading blank line in the\nbody and steps into checking whitespace errors---makes readers\nwonder if we should be also detecting trailing whitespaces on lines,\netc.\n\nIs there actually such a check enforced in the original?  Or is this\na new rule that appeared out of thin air?  We'd have to inspect the\nlines deleted from builtin/mktag.c in the next step, I gues.\n\n> By adding an \"extra\" category and only reporting it based on a flag in\n> fsck_options callers can opt-in to these \"extra\" messages, which\n> they'll then need to deal with in their own \"error_func\".\n\nMakes sense.\n\n"},{"id":"411099","messageId":"xmqqk0u1nthg.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201126222257.5629-10-avarab@gmail.com","subject":"Re: [PATCH v2 09/10] mktag: use fsck instead of custom verify_tag()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-01T20:47:55Z","receivedAt":"2020-12-01T20:48:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Change the validation logic in \"mktag\" to use fsck's fsck_tag()\n> instead of its own custom parser. Curiously the logic for both dates\n> back to the same commit[1]. Let's unify them so we're not maintaining\n> two sets functions to verify that a tag is OK.\n>\n> Moving to fsck_tag() required teaching it to optionally use some\n> validations that only the old mktag code could perform. That was done\n> in an earlier commit, the \"extraHeaderEntry\" and\n> \"extraHeaderBodyNewline\" tests being added here make use of that\n> logic.\n>\n> There was other \"mktag\" validation logic that I think makes sense to\n> just remove. Namely:\n\nI may not agree with the specifics in this list, but I agree that\nthe validation should be consistent between mktag and fsck.\n\n> +\tswitch (msg_type) {\n> +\tcase FSCK_WARN:\n> +\tcase FSCK_ERROR:\n> +\tcase FSCK_EXTRA:\n> +\t\t/*\n> +\t\t * We treat both warnings and errors as errors, things\n> +\t\t * like missing \"tagger\" lines are \"only\" warnings\n> +\t\t * under fsck, we've always considered them an error.\n> +\t\t */\n\nThis is a good comment and design decision.  \n\nAs far as I am concerned, the only reason we warn on a missing\ntagger line without erroring out is because very early versions of\nGit did allow creating such tag objects, and there are annotated\ntags in the wild that lack the tagger line.\n\n> +\t\tfprintf_ln(stderr, \"error: tag input does not pass fsck: %s\", message);\n> +\t\treturn 1;\n> +\tdefault:\n> +\t\tBUG(\"%d (FSCK_IGNORE?) should never trigger this callback\",\n> +\t\t    msg_type);\n>  \t}\n> -\treturn ret;\n>  }\n>  \n> -static int verify_tag(char *buffer, unsigned long size)\n> ...\n> -\t/* timezone, 5 digits [+-]hhmm, max. 1400 */\n> -\tif (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&\n> -\t      strspn(tagger_line+1, \"0123456789\") == 4 &&\n> -\t      tagger_line[5] == '\\n' && atoi(tagger_line+1) <= 1400))\n> -\t\treturn error(\"char%\"PRIuMAX\": malformed tag timezone\",\n> -\t\t\t(uintmax_t) (tagger_line - buffer));\n> -\ttagger_line += 6;\n> -\n> -\t/* Verify the blank line separating the header from the body */\n> -\tif (*tagger_line != '\\n')\n> -\t\treturn error(\"char%\"PRIuMAX\": trailing garbage in tag header\",\n> -\t\t\t(uintmax_t) (tagger_line - buffer));\n>  \n> -\t/* The actual stuff afterwards we don't care about.. */\n> -\treturn 0;\n\nI do not see the \"we do not want more than one blank line after the\nheader\" in the original, which was one thing I was looking for after\nseeing what 08/10 did.\n\n> +\tfsck_options.extra = 1;\n> +\tfsck_options.error_func = mktag_fsck_error_func;\n\nI wonder if we really need the new .extra bit member in the\nfsck_options.  Wouldn't it be sufficient to instead teach\nfsck_error_func() that the extra level is a non-event?\n\n"},{"id":"411100","messageId":"xmqqczztnte6.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"xmqq1rg9p9as.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v2 03/10] mktag: reword write_object_file() error","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-01T20:49:53Z","receivedAt":"2020-12-01T20:50:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Change the error message emitted when write_object_file() fails to\n>> make more sense. At this point we're not writing a \"tag file\" (which\n>> as an aside we never do, we just write to stdout). We are writing an\n>> annotated tag object, let's say that instead.\n>>\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>  builtin/mktag.c | 2 +-\n>>  1 file changed, 1 insertion(+), 1 deletion(-)\n>>\n>> diff --git a/builtin/mktag.c b/builtin/mktag.c\n>> index ff7ac8e0e5..603b55aca0 100644\n>> --- a/builtin/mktag.c\n>> +++ b/builtin/mktag.c\n>> @@ -171,7 +171,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n>>  \t\tdie(\"invalid tag signature file\");\n>>  \n>>  \tif (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)\n>> -\t\tdie(\"unable to write tag file\");\n>> +\t\tdie(\"unable to write annotated tag object\");\n>\n> \"write an annotated tag object\"?\n\nActually, there is no such thing as an \"unannotated tag object\".\nPerhaps _(\"unable to create a tag object\")?\n\n>\n> It is not just this call to die(), but we'd eventually want to _(l10n/i18n)\n> these messages.  Perhaps in a separate step but on all messages fed\n> to die/error/warn in this file.\n>\n>>  \n>>  \tstrbuf_release(&buf);\n>>  \tprintf(\"%s\\n\", oid_to_hex(&result));\n"},{"id":"411103","messageId":"xmqq8sahnsy3.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201126222257.5629-11-avarab@gmail.com","subject":"Re: [PATCH v2 10/10] mktag doc: update to explain why to use this","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-01T20:59:32Z","receivedAt":"2020-12-01T21:00:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> +Reads a tag contents on standard input and creates a tag object. The\n> +output is the new tag's <object> identifier.\n> +\n> +This command accepts a subset of what linkgit:git-hash-object[1] would\n> +accept with `-t tag --stdin`. I.e. both of these work:\n> +\n> +    git mktag <my-tag\n> +    git hash-object -t tag --stdin <my-tag\n\nIt's misleading to say \"both of these work\", I am afraid.  If the\nformer works, the latter would.  That is what \"accepts a subset\"\nmeans, no?\n\n> +The difference between the two is that mktag does the equivalent of a\n> +linkgit:git-fsck(1) check on its input, and furthermore disallows some\n> +thing linkgit:git-hash-object[1] would pass, e.g. extra headers in the\n> +object before the message.\n\nGood description.\n\n> @@ -34,6 +44,17 @@ exists, is separated by a blank line from the header.  The\n>  message part may contain a signature that Git itself doesn't\n>  care about, but that can be verified with gpg.\n>  \n> +HISTORY\n> +-------\n> +\n> +In versions of Git before v2.30.0 the \"mktag\" command's validation\n\nHardcoding v2.30.0 here feels problematic.  If the series cooks in\n'next' while 2.30 gets released, it has to be kicked out of 'next'\nto update this line before it gets allowed in 'next'.\n\n> +logic was subtly different than that of linkgit:git-fsck[1]. It is now\n> +a strict superset of linkgit:git-fsck[1]'s validation logic.\n\nIt may be a better direction to go to drop this section.\n\nAlso, I somehow have a feeling that we'd rather want to loosen the\n\"no other headers allowed\" in the longer run to be consistent with\nwhat \"fsck\" does.\n\nI also wonder if we want to teach \"commit-tree\" to also run fsck\ncheck like this new version of mktag does.  It is outside the scope\nof this \"mktag\" series, of course.\n\nThanks.\n\n> +SEE ALSO\n> +--------\n> +linkgit:git-hash-object[1],\n> +\n>  GIT\n>  ---\n>  Part of the linkgit:git[1] suite\n"},{"id":"411114","messageId":"xmqqv9dlma9y.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"xmqqk0u1nthg.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v2 09/10] mktag: use fsck instead of custom verify_tag()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-01T22:28:09Z","receivedAt":"2020-12-01T22:28:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I do not see the \"we do not want more than one blank line after the\n> header\" in the original, which was one thing I was looking for after\n> seeing what 08/10 did.\n>\n>> +\tfsck_options.extra = 1;\n>> +\tfsck_options.error_func = mktag_fsck_error_func;\n\nHmph, recent \"released\" versions of Git seems to be fine when\n\n--- >8 ------ >8 ------ >8 ------ >8 ------ >8 ---\nobject 72ffeb997eaf999f6938b2a7e0d9a75dcceaa311\ntype commit\ntag tester\ntagger Junio C Hamano <gitster@pobox.com> 1606860947 -0800\n\n\na message after a blank\n--- 8< ------ 8< ------ 8< ------ 8< ------ 8< ---\n\nis fed to their \"git mktag\".  If I am not mistaken, we definitely\nwant to drop the EXTRA_HEADER_BODY_NEWLINE check from 08/10, even\nthough EXTRA_HEADER_ENTRY may be something we want to retain at\nleast for some releases for backward compatibility.\n\n\n\n"},{"id":"411180","messageId":"xmqqwnxzluir.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"xmqqlfehpa7p.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v2 00/10] make \"mktag\" use fsck_tag()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-02T22:20:44Z","receivedAt":"2020-12-02T22:21:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> On Thu, Nov 26 2020, Ævar Arnfjörð Bjarmason wrote:\n>>\n>>> Now a non-RFC. I went for the approach I suggested in\n>>> <87r1ognv4b.fsf@evledraar.gmail.com> of just having fsck_tag() able to\n>>> optionally tell us about its parsed tag/type, thus avoiding any need\n>>> for a custom parser in mktag.c. Hopefully I've addressed the rest of\n>>> the feedback, range-diff below.\n>>\n>> Ping @ Jeff & brian: you said you wanted this in one shape or another,\n>> so mind seeing if the v2 looks good to you?:)\n>>\n>> Junio didn't pick it up for the \"What's Cooking\" sent out recently,\n>> hopefully some reviewer ACK/NACK will help move it forward. Thanks!\n>\n> True.  I don't want to queue too many topics on 'seen', only to end\n> up with a pile of patches that haven't been reviewed adequately and\n> cannot move forward.\n\nSo, now I've seen all of them.\n\nThere were some minor things in the earlier part I commented on, and\nif I am not mistaken, a new check, \"body must not begin with a blank\nline\", should not be added at all, which would affect 08/10.\n\nI am not sure how much longer we want to retain the .extra level of\nchecks that are more strict than those of fsck.  If we decide that\nit is not worth it to forbid new object headers, we may be able to\nlose 08/10 altogether.\n\nOther than the above, the series mostly looked well done.\n\nThanks.\n"},{"id":"411254","messageId":"87h7p2vo8n.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"xmqqwnxzluir.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v2 00/10] make \"mktag\" use fsck_tag()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-03T16:38:32Z","receivedAt":"2020-12-03T16:39:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Dec 02 2020, Junio C Hamano wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>>\n>>> On Thu, Nov 26 2020, Ævar Arnfjörð Bjarmason wrote:\n>>>\n>>>> Now a non-RFC. I went for the approach I suggested in\n>>>> <87r1ognv4b.fsf@evledraar.gmail.com> of just having fsck_tag() able to\n>>>> optionally tell us about its parsed tag/type, thus avoiding any need\n>>>> for a custom parser in mktag.c. Hopefully I've addressed the rest of\n>>>> the feedback, range-diff below.\n>>>\n>>> Ping @ Jeff & brian: you said you wanted this in one shape or another,\n>>> so mind seeing if the v2 looks good to you?:)\n>>>\n>>> Junio didn't pick it up for the \"What's Cooking\" sent out recently,\n>>> hopefully some reviewer ACK/NACK will help move it forward. Thanks!\n>>\n>> True.  I don't want to queue too many topics on 'seen', only to end\n>> up with a pile of patches that haven't been reviewed adequately and\n>> cannot move forward.\n>\n> So, now I've seen all of them.\n>\n> There were some minor things in the earlier part I commented on, and\n> if I am not mistaken, a new check, \"body must not begin with a blank\n> line\", should not be added at all, which would affect 08/10.\n>\n> I am not sure how much longer we want to retain the .extra level of\n> checks that are more strict than those of fsck.  If we decide that\n> it is not worth it to forbid new object headers, we may be able to\n> lose 08/10 altogether.\n>\n> Other than the above, the series mostly looked well done.\n\nHi. Just a quick note to say thanks for all the feedback, and that I\ndon't have a v3 ready now, but will submit one soon-ish.\n\nFWIW the header-body newline thing wasn't something I was trying to\nsneak in, I went wrong in my testing somewhere and thought it was a bug\nunder mktag. Will test for that, remove that check or whatever as\nappropriate. Thanks for the review.\n"},{"id":"411916","messageId":"20201209200140.29425-3-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126222257.5629-1-avarab@gmail.com","subject":"[PATCH v3 02/10] mktag: use default strbuf_read() hint","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-09T20:01:32Z","receivedAt":"2020-12-09T20:02:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the hardcoded hint of 2^12 to 0. The default strbuf hint is\nperfectly fine here, and the only reason we were hardcoding it is\nbecause it survived migration from a pre-strbuf fixed-sized buffer.\n\nSee fd17f5b5f77 (Replace all read_fd use with strbuf_read, and get rid\nof it., 2007-09-10) for that migration.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 4982d3a93ef..ff7ac8e0e5d 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -161,7 +161,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (argc != 1)\n \t\tusage(\"git mktag\");\n \n-\tif (strbuf_read(&buf, 0, 4096) < 0) {\n+\tif (strbuf_read(&buf, 0, 0) < 0) {\n \t\tdie_errno(\"could not read from stdin\");\n \t}\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"411917","messageId":"20201209200140.29425-2-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126222257.5629-1-avarab@gmail.com","subject":"[PATCH v3 01/10] mktag doc: say <hash> not <sha1>","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-09T20:01:31Z","receivedAt":"2020-12-09T20:02:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the \"mktag\" documentation to refer to the input hash as just\n\"hash\", not \"sha1\". This command has supported SHA-256 for a while\nnow.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex fa6a7561236..a158428eb9f 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -23,7 +23,7 @@ Tag Format\n A tag signature file, to be fed to this command's standard input,\n has a very simple fixed format: four lines of\n \n-  object <sha1>\n+  object <hash>\n   type <typename>\n   tag <tagname>\n   tagger <tagger>\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"411918","messageId":"20201209200140.29425-4-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126222257.5629-1-avarab@gmail.com","subject":"[PATCH v3 03/10] mktag: remove redundant braces in one-line body \"if\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-09T20:01:33Z","receivedAt":"2020-12-09T20:03:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This minor stylistic churn is usually something we'd avoid, but if we\ndon't do this then the file after changes in subsequent commits will\nonly have this minor style inconsistency, so let's change this while\nwe're at it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex ff7ac8e0e5d..97ca5f28b1b 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -161,9 +161,8 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (argc != 1)\n \t\tusage(\"git mktag\");\n \n-\tif (strbuf_read(&buf, 0, 0) < 0) {\n+\tif (strbuf_read(&buf, 0, 0) < 0)\n \t\tdie_errno(\"could not read from stdin\");\n-\t}\n \n \t/* Verify it for some basic sanity: it needs to start with\n \t   \"object <sha1>\\ntype\\ntagger \" */\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"411919","messageId":"20201209200140.29425-7-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126222257.5629-1-avarab@gmail.com","subject":"[PATCH v3 06/10] mktag tests: improve verify_object() test coverage","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-09T20:01:36Z","receivedAt":"2020-12-09T20:03:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The verify_object() function in \"mktag.c\" is tasked with ensuring that\nour tag refers to a valid object.\n\nThe existing test for this might fail because it was also testing that\n\"type taggg\" didn't refer to a valid object type (it should be \"type\ntag\"), or because we referred to a valid object but got the type\nwrong.\n\nLet's split these tests up, so we're testing all combinations of a\nnon-existing object and in invalid/wrong \"type\" lines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 37 +++++++++++++++++++++++++++++++++++--\n 1 file changed, 35 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex a22a0954d5e..9ae1b0bb0af 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -125,19 +125,52 @@ check_verify_failure '\"type\" line type-name length check' \\\n \t'^error: char.*: type too long$'\n \n ############################################################\n-#  9. verify object (SHA1/type) check\n+#  9. verify object (hash/type) check\n \n cat >tag.sig <<EOF\n object $(test_oid deadbeef)\n+type tag\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+check_verify_failure 'verify object (hash/type) check -- correct type, nonexisting object' \\\n+\t'^error: char7: could not verify object.*$'\n+\n+cat >tag.sig <<EOF\n+object $head\n type tagggg\n tag mytag\n tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure 'verify object (SHA1/type) check' \\\n+check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n+\t'^fatal: invalid object type'\n+\n+cat >tag.sig <<EOF\n+object $(test_oid deadbeef)\n+type tagggg\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n \t'^error: char7: could not verify object.*$'\n \n+cat >tag.sig <<EOF\n+object $head\n+type tree\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n+\t'^error: char7: could not verify object'\n+\n ############################################################\n # 10. verify tag-name check\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"411920","messageId":"20201209200140.29425-11-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126222257.5629-1-avarab@gmail.com","subject":"[PATCH v3 10/10] mktag: allow turning off fsck.extraHeaderEntry","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-09T20:01:40Z","receivedAt":"2020-12-09T20:03:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In earlier commits mktag learned to use the fsck machinery, at which\npoint we needed to add fsck.extraHeaderEntry so it could be as strict\nabout extra headers as it's been ever since it was implemented.\n\nBut it's not nice to need to switch away from \"mktag\" to \"hash-object\"\n+ manual \"fsck\" just because you'd like to have an extra header. So\nlet's support turning it off by getting \"fsck.*\" variables from the\nconfig.\n\nPedantically speaking it's still not possible to make \"mktag\" behave\njust like \"hash-object -t tag\" does, since we're unconditionally going\nto check the referenced object in verify_object_in_tag(), which is our\nown check, and not one that exists in fsck.c.\n\nBut the spirit of \"this works like fsck\" is preserved, in that if you\ncreated such a tag with \"hash-object\" and did a full \"fsck\" on the\nrepository it would also error out about that invalid object, it just\nwouldn't emit the same message as fsck does.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt |  9 +++++++--\n builtin/mktag.c             | 11 ++++++++++-\n t/t3800-mktag.sh            | 14 ++++++++++++++\n 3 files changed, 31 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex b164ab563bd..e1506dde561 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -30,8 +30,13 @@ tag doesn't pass a linkgit:git-fsck[1] check.\n The \"fsck\" check done mktag is is stricter than what\n linkgit:git-fsck[1] would run by default in that all `fsck.<msg-id>`\n messages are promoted from warnings to errors (so e.g. a missing\n-\"tagger\" line is an error). Extra headers in the object are also an\n-error under mktag, but ignored by linkgit:git-fsck[1].\n+\"tagger\" line is an error).\n+\n+Extra headers in the object are also an error under mktag, but ignored\n+by linkgit:git-fsck[1]. This extra check can be turned off by setting\n+the appropriate `fsck.<msg-id>` varible:\n+\n+    git -c fsck.extraHeaderEntry=ignore mktag <my-tag-with-headers\n \n Tag Format\n ----------\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 5765cde0032..c1398ccf133 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -3,6 +3,14 @@\n #include \"replace-object.h\"\n #include \"object-store.h\"\n #include \"fsck.h\"\n+#include \"config.h\"\n+\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n+\n+static int mktag_config(const char *var, const char *value, void *cb)\n+{\n+\treturn fsck_config_internal(var, value, cb, &fsck_options);\n+}\n \n static int mktag_fsck_error_func(struct fsck_options *o,\n \t\t\t\t const struct object_id *oid,\n@@ -53,7 +61,6 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n int cmd_mktag(int argc, const char **argv, const char *prefix)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n-\tstruct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \tstruct object_id tagged_oid;\n \tint tagged_type;\n \tstruct object_id result;\n@@ -66,6 +73,8 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \n \tfsck_options.error_func = mktag_fsck_error_func;\n \tfsck_set_msg_type(&fsck_options, \"extraheaderentry\", \"warn\");\n+\t/* config might set fsck.extraHeaderEntry=* again */\n+\tgit_config(mktag_config, NULL);\n \tif (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,\n \t\t\t\t&tagged_oid, &tagged_type))\n \t\tdie(\"tag on stdin did not pass our strict fsck check\");\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 19b3c1bca9c..59082e06277 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -375,6 +375,20 @@ EOF\n check_verify_failure 'detect invalid header entry' \\\n \t'^error:.* extraHeaderEntry:'\n \n+test_expect_success 'invalid header entry config & fsck' '\n+\ttest_must_fail git mktag <tag.sig &&\n+\ttest_must_fail git -c fsck.extraHeaderEntry=error mktag <tag.sig &&\n+\ttest_must_fail git -c fsck.extraHeaderEntry=warn mktag <tag.sig &&\n+\tgit -c fsck.extraHeaderEntry=ignore mktag <tag.sig &&\n+\tgit fsck &&\n+\tenv GIT_TEST_GETTEXT_POISON=false \\\n+\t\tgit -c fsck.extraHeaderEntry=warn fsck 2>err &&\n+\tgrep \"warning .*extraHeaderEntry:\" err &&\n+\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n+\t\tgit -c fsck.extraHeaderEntry=error 2>err fsck &&\n+\tgrep \"error .* extraHeaderEntry:\" err\n+'\n+\n cat >tag.sig <<EOF\n object $head\n type commit\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"411921","messageId":"20201209200140.29425-9-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126222257.5629-1-avarab@gmail.com","subject":"[PATCH v3 08/10] mktag doc: update to explain why to use this","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-09T20:01:38Z","receivedAt":"2020-12-09T20:03:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the mktag documentation to compare itself to the similar\n\"hash-object -t tag\" command. Before this someone reading the\ndocumentation wouldn't have much of an idea what the difference\nwas.\n\nLet's make it clear that it's to do with slightly different fsck\nvalidation logic, and cross-link the \"mktag\" and \"hash-object\"\ndocumentation to aid discover-ability.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-hash-object.txt |  4 ++++\n Documentation/git-mktag.txt       | 27 +++++++++++++++++++++++----\n 2 files changed, 27 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-hash-object.txt b/Documentation/git-hash-object.txt\nindex df9e2c58bdb..c535661ced8 100644\n--- a/Documentation/git-hash-object.txt\n+++ b/Documentation/git-hash-object.txt\n@@ -58,6 +58,10 @@ OPTIONS\n \tstress-testing Git itself or reproducing characteristics of corrupt or\n \tbogus objects encountered in the wild.\n \n+SEE ALSO\n+--------\n+linkgit:git-mktag[1]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex a158428eb9f..b164ab563bd 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -3,7 +3,7 @@ git-mktag(1)\n \n NAME\n ----\n-git-mktag - Creates a tag object\n+git-mktag - Creates a tag object with extra validation\n \n \n SYNOPSIS\n@@ -13,10 +13,25 @@ SYNOPSIS\n \n DESCRIPTION\n -----------\n-Reads a tag contents on standard input and creates a tag object\n-that can also be used to sign other objects.\n \n-The output is the new tag's <object> identifier.\n+Reads a tag contents on standard input and creates a tag object. The\n+output is the new tag's <object> identifier.\n+\n+This command is mostly equivalent to linkgit:git-hash-object[1]\n+invoked with `-t tag -w --stdin`. I.e. both of these will create and\n+write a tag found in `my-tag`:\n+\n+    git mktag <my-tag\n+    git hash-object -t tag -w --stdin <my-tag\n+\n+The difference is that mktag will die before writing the tag if the\n+tag doesn't pass a linkgit:git-fsck[1] check.\n+\n+The \"fsck\" check done mktag is is stricter than what\n+linkgit:git-fsck[1] would run by default in that all `fsck.<msg-id>`\n+messages are promoted from warnings to errors (so e.g. a missing\n+\"tagger\" line is an error). Extra headers in the object are also an\n+error under mktag, but ignored by linkgit:git-fsck[1].\n \n Tag Format\n ----------\n@@ -34,6 +49,10 @@ exists, is separated by a blank line from the header.  The\n message part may contain a signature that Git itself doesn't\n care about, but that can be verified with gpg.\n \n+SEE ALSO\n+--------\n+linkgit:git-hash-object[1],\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"411922","messageId":"20201209200140.29425-10-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126222257.5629-1-avarab@gmail.com","subject":"[PATCH v3 09/10] fsck: make fsck_config() re-usable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-09T20:01:39Z","receivedAt":"2020-12-09T20:03:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Move the fsck_config() function from builtin/fsck.c to fsck.[ch]. This\nallows for re-using it in other tools that expose fsck logic and want\nto support its configuration variables.\n\nA logical continuation of this change would be to use a common\nfunction for all of {fetch,receive}.fsck.* and fsck.*. See\n5d477a334a6 (fsck (receive-pack): allow demoting errors to warnings,\n2015-06-22) and my own 1362df0d413 (fetch: implement fetch.fsck.*,\n2018-07-27) for the relevant code.\n\nHowever, those routines want to not parse the fsck.skipList into OIDs,\nbut rather pass them along with the --strict option to another\nprocess. It would be possible to refactor that whole thing so we\nsupport e.g. a \"fetch.\" prefix, then just keep track of the skiplist\nas a filename instead of parsing it, and learn to spew that all out\nfrom our internal structures into something we can append to the\n--strict option.\n\nBut instead I'm planning to re-use this in \"mktag\", which'll just\nre-use these \"fsck.*\" variables as-is.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fsck.c | 20 +-------------------\n fsck.c         | 25 +++++++++++++++++++++++++\n fsck.h         |  7 +++++++\n 3 files changed, 33 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex fbf26cafcfd..821e7798c70 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -73,25 +73,7 @@ static const char *printable_type(const struct object_id *oid,\n \n static int fsck_config(const char *var, const char *value, void *cb)\n {\n-\tif (strcmp(var, \"fsck.skiplist\") == 0) {\n-\t\tconst char *path;\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\n-\t\tif (git_config_pathname(&path, var, value))\n-\t\t\treturn 1;\n-\t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n-\t\tfree((char *)path);\n-\t\tfsck_set_msg_types(&fsck_obj_options, sb.buf);\n-\t\tstrbuf_release(&sb);\n-\t\treturn 0;\n-\t}\n-\n-\tif (skip_prefix(var, \"fsck.\", &var)) {\n-\t\tfsck_set_msg_type(&fsck_obj_options, var, value);\n-\t\treturn 0;\n-\t}\n-\n-\treturn git_default_config(var, value, cb);\n+\treturn fsck_config_internal(var, value, cb, &fsck_obj_options);\n }\n \n static int objerror(struct object *obj, const char *err)\ndiff --git a/fsck.c b/fsck.c\nindex bed5e20e03b..9067a290a2e 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -1310,3 +1310,28 @@ int fsck_finish(struct fsck_options *options)\n \toidset_clear(&gitmodules_done);\n \treturn ret;\n }\n+\n+int fsck_config_internal(const char *var, const char *value, void *cb,\n+\t\t\t struct fsck_options *options)\n+{\n+\tif (strcmp(var, \"fsck.skiplist\") == 0) {\n+\t\tconst char *path;\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tif (git_config_pathname(&path, var, value))\n+\t\t\treturn 1;\n+\t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n+\t\tfree((char *)path);\n+\t\tfsck_set_msg_types(options, sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\treturn 0;\n+\t}\n+\n+\tif (skip_prefix(var, \"fsck.\", &var)) {\n+\t\tfsck_set_msg_type(options, var, value);\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_default_config(var, value, cb);\n+}\n+\ndiff --git a/fsck.h b/fsck.h\nindex 29ee4c45e87..423c467feb7 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -103,4 +103,11 @@ void fsck_put_object_name(struct fsck_options *options,\n const char *fsck_describe_object(struct fsck_options *options,\n \t\t\t\t const struct object_id *oid);\n \n+/*\n+ * git_config() callback for use by fsck-y tools that want to support\n+ * fsck.<msg> fsck.skipList etc.\n+ */\n+int fsck_config_internal(const char *var, const char *value, void *cb,\n+\t\t\t struct fsck_options *options);\n+\n #endif\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"411923","messageId":"20201209200140.29425-8-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126222257.5629-1-avarab@gmail.com","subject":"[PATCH v3 07/10] mktag: use fsck instead of custom verify_tag()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-09T20:01:37Z","receivedAt":"2020-12-09T20:03:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the validation logic in \"mktag\" to use fsck's fsck_tag()\ninstead of its own custom parser. Curiously the logic for both dates\nback to the same commit[1]. Let's unify them so we're not maintaining\ntwo sets functions to verify that a tag is OK.\n\nThe behavior of fsck_tag() and the old \"mktag\" code being removed here\nis different in few aspects.\n\nI think it makes sense to remove some of those checks, namely:\n\n A. fsck only cares that the timezone matches [-+][0-9]{4}. The mktag\n    code disallowed values larger than 1400.\n\n    Yes there's currently no timezone with a greater offset[2], but\n    since we allow any number of non-offical timezones (e.g. +1234)\n    passing this through seems fine. Git also won't break in the\n    future if e.g. French Polynesia decides it needs to outdo the Line\n    Islands when it comes to timezone extravagance.\n\n B. fsck allows missing author names such as \"tagger <email>\", mktag\n    wouldn't, but would allow e.g. \"tagger [2 spaces] <email>\" (but\n    not \"tagger [1 space] <email>\"). Now we allow all of these.\n\n C. Like B, but \"mktag\" disallowed spaces in the <email> part, fsck\n    allows it.\n\nIn some ways fsck_tag() is stricter than \"mktag\" was, namely:\n\n D. fsck disallows zero-padded dates, but mktag didn't care. So\n    e.g. the timestamp \"0000000000 +0000\" produces an error now. A\n    test in \"t1006-cat-file.sh\" relied on this, it's been changed to\n    use \"hash-object\" (without fsck) instead.\n\nThere was one check I deemed worth keeping by porting it over to\nfsck_tag():\n\n E. \"mktag\" did not allow any custom headers, and by extension (as an\n    empty commit is allowed) also forbade an extra stray trailing\n    newline after the headers it knew about.\n\n    Add a new check in the \"ignore\" category to fsck and use it. This\n    somewhat abuses the facility added in efaba7cc77f (fsck:\n    optionally ignore specific fsck issues completely, 2015-06-22).\n\n    This is somewhat of hack, but probably the least invasive change\n    we can make here. The fsck command will shuffle these categories\n    around, e.g. under --strict the \"info\" becomes a \"warn\" and \"warn\"\n    becomes \"error\". Existing users of fsck's (and others,\n    e.g. index-pack) --strict option rely on this.\n\n    So we need to put something into a category that'll be ignored by\n    all existing users of the API. Pretending that\n    fsck.extraHeaderEntry=error (\"ignore\" by default) was set serves\n    to do this for us.\n\n1. ec4465adb38 (Add \"tag\" objects that can be used to sign other\n   objects., 2005-04-25)\n\n2. https://en.wikipedia.org/wiki/List_of_UTC_time_offsets\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c     | 196 +++++++++++---------------------------------\n fsck.c              |  32 +++++++-\n fsck.h              |   9 ++\n t/t1006-cat-file.sh |   2 +-\n t/t3800-mktag.sh    |  79 +++++++++++-------\n 5 files changed, 137 insertions(+), 181 deletions(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 97ca5f28b1b..5765cde0032 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -2,160 +2,60 @@\n #include \"tag.h\"\n #include \"replace-object.h\"\n #include \"object-store.h\"\n+#include \"fsck.h\"\n \n-/*\n- * A signature file has a very simple fixed format: four lines\n- * of \"object <sha1>\" + \"type <typename>\" + \"tag <tagname>\" +\n- * \"tagger <committer>\", followed by a blank line, a free-form tag\n- * message and a signature block that git itself doesn't care about,\n- * but that can be verified with gpg or similar.\n- *\n- * The first four lines are guaranteed to be at least 83 bytes:\n- * \"object <sha1>\\n\" is 48 bytes, \"type tag\\n\" at 9 bytes is the\n- * shortest possible type-line, \"tag .\\n\" at 6 bytes is the shortest\n- * single-character-tag line, and \"tagger . <> 0 +0000\\n\" at 20 bytes is\n- * the shortest possible tagger-line.\n- */\n-\n-/*\n- * We refuse to tag something we can't verify. Just because.\n- */\n-static int verify_object(const struct object_id *oid, const char *expected_type)\n+static int mktag_fsck_error_func(struct fsck_options *o,\n+\t\t\t\t const struct object_id *oid,\n+\t\t\t\t enum object_type object_type,\n+\t\t\t\t int msg_type, const char *message)\n {\n-\tint ret = -1;\n-\tenum object_type type;\n-\tunsigned long size;\n-\tvoid *buffer = read_object_file(oid, &type, &size);\n-\tconst struct object_id *repl = lookup_replace_object(the_repository, oid);\n-\n-\tif (buffer) {\n-\t\tif (type == type_from_string(expected_type)) {\n-\t\t\tret = check_object_signature(the_repository, repl,\n-\t\t\t\t\t\t     buffer, size,\n-\t\t\t\t\t\t     expected_type);\n-\t\t}\n-\t\tfree(buffer);\n+\tswitch (msg_type) {\n+\tcase FSCK_WARN:\n+\tcase FSCK_ERROR:\n+\t\t/*\n+\t\t * We treat both warnings and errors as errors, things\n+\t\t * like missing \"tagger\" lines are \"only\" warnings\n+\t\t * under fsck, we've always considered them an error.\n+\t\t */\n+\t\tfprintf_ln(stderr, \"error: tag input does not pass fsck: %s\", message);\n+\t\treturn 1;\n+\tdefault:\n+\t\tBUG(\"%d (FSCK_IGNORE?) should never trigger this callback\",\n+\t\t    msg_type);\n \t}\n-\treturn ret;\n }\n \n-static int verify_tag(char *buffer, unsigned long size)\n+static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n {\n-\tint typelen;\n-\tchar type[20];\n-\tstruct object_id oid;\n-\tconst char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;\n-\tsize_t len;\n-\n-\tif (size < 84)\n-\t\treturn error(\"wanna fool me ? you obviously got the size wrong !\");\n-\n-\tbuffer[size] = 0;\n-\n-\t/* Verify object line */\n-\tobject = buffer;\n-\tif (memcmp(object, \"object \", 7))\n-\t\treturn error(\"char%d: does not start with \\\"object \\\"\", 0);\n-\n-\tif (parse_oid_hex(object + 7, &oid, &p))\n-\t\treturn error(\"char%d: could not get SHA1 hash\", 7);\n-\n-\t/* Verify type line */\n-\ttype_line = p + 1;\n-\tif (memcmp(type_line - 1, \"\\ntype \", 6))\n-\t\treturn error(\"char%d: could not find \\\"\\\\ntype \\\"\", 47);\n-\n-\t/* Verify tag-line */\n-\ttag_line = strchr(type_line, '\\n');\n-\tif (!tag_line)\n-\t\treturn error(\"char%\"PRIuMAX\": could not find next \\\"\\\\n\\\"\",\n-\t\t\t\t(uintmax_t) (type_line - buffer));\n-\ttag_line++;\n-\tif (memcmp(tag_line, \"tag \", 4) || tag_line[4] == '\\n')\n-\t\treturn error(\"char%\"PRIuMAX\": no \\\"tag \\\" found\",\n-\t\t\t\t(uintmax_t) (tag_line - buffer));\n-\n-\t/* Get the actual type */\n-\ttypelen = tag_line - type_line - strlen(\"type \\n\");\n-\tif (typelen >= sizeof(type))\n-\t\treturn error(\"char%\"PRIuMAX\": type too long\",\n-\t\t\t\t(uintmax_t) (type_line+5 - buffer));\n-\n-\tmemcpy(type, type_line+5, typelen);\n-\ttype[typelen] = 0;\n-\n-\t/* Verify that the object matches */\n-\tif (verify_object(&oid, type))\n-\t\treturn error(\"char%d: could not verify object %s\", 7, oid_to_hex(&oid));\n-\n-\t/* Verify the tag-name: we don't allow control characters or spaces in it */\n-\ttag_line += 4;\n-\tfor (;;) {\n-\t\tunsigned char c = *tag_line++;\n-\t\tif (c == '\\n')\n-\t\t\tbreak;\n-\t\tif (c > ' ')\n-\t\t\tcontinue;\n-\t\treturn error(\"char%\"PRIuMAX\": could not verify tag name\",\n-\t\t\t\t(uintmax_t) (tag_line - buffer));\n-\t}\n-\n-\t/* Verify the tagger line */\n-\ttagger_line = tag_line;\n-\n-\tif (memcmp(tagger_line, \"tagger \", 7))\n-\t\treturn error(\"char%\"PRIuMAX\": could not find \\\"tagger \\\"\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/*\n-\t * Check for correct form for name and email\n-\t * i.e. \" <\" followed by \"> \" on _this_ line\n-\t * No angle brackets within the name or email address fields.\n-\t * No spaces within the email address field.\n-\t */\n-\ttagger_line += 7;\n-\tif (!(lb = strstr(tagger_line, \" <\")) || !(rb = strstr(lb+2, \"> \")) ||\n-\t\tstrpbrk(tagger_line, \"<>\\n\") != lb+1 ||\n-\t\tstrpbrk(lb+2, \"><\\n \") != rb)\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tagger field\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/* Check for author name, at least one character, space is acceptable */\n-\tif (lb == tagger_line)\n-\t\treturn error(\"char%\"PRIuMAX\": missing tagger name\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/* timestamp, 1 or more digits followed by space */\n-\ttagger_line = rb + 2;\n-\tif (!(len = strspn(tagger_line, \"0123456789\")))\n-\t\treturn error(\"char%\"PRIuMAX\": missing tag timestamp\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line += len;\n-\tif (*tagger_line != ' ')\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tag timestamp\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line++;\n-\n-\t/* timezone, 5 digits [+-]hhmm, max. 1400 */\n-\tif (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&\n-\t      strspn(tagger_line+1, \"0123456789\") == 4 &&\n-\t      tagger_line[5] == '\\n' && atoi(tagger_line+1) <= 1400))\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tag timezone\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line += 6;\n-\n-\t/* Verify the blank line separating the header from the body */\n-\tif (*tagger_line != '\\n')\n-\t\treturn error(\"char%\"PRIuMAX\": trailing garbage in tag header\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n+\tint ret;\n+\tenum object_type type;\n+\tunsigned long size;\n+\tvoid *buffer;\n+\tconst struct object_id *repl;\n+\n+\tbuffer = read_object_file(tagged_oid, &type, &size);\n+\tif (!buffer)\n+\t\tdie(\"could not read tagged object '%s'\\n\",\n+\t\t    oid_to_hex(tagged_oid));\n+\tif (type != *tagged_type)\n+\t\tdie(\"object '%s' tagged as '%s', but is a '%s' type\\n\",\n+\t\t    oid_to_hex(tagged_oid),\n+\t\t    type_name(*tagged_type), type_name(type));\n+\n+\trepl = lookup_replace_object(the_repository, tagged_oid);\n+\tret = check_object_signature(the_repository, repl,\n+\t\t\t\t     buffer, size, type_name(*tagged_type));\n+\tfree(buffer);\n \n-\t/* The actual stuff afterwards we don't care about.. */\n-\treturn 0;\n+\treturn ret;\n }\n \n int cmd_mktag(int argc, const char **argv, const char *prefix)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n+\tstruct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n+\tstruct object_id tagged_oid;\n+\tint tagged_type;\n \tstruct object_id result;\n \n \tif (argc != 1)\n@@ -164,10 +64,14 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (strbuf_read(&buf, 0, 0) < 0)\n \t\tdie_errno(\"could not read from stdin\");\n \n-\t/* Verify it for some basic sanity: it needs to start with\n-\t   \"object <sha1>\\ntype\\ntagger \" */\n-\tif (verify_tag(buf.buf, buf.len) < 0)\n-\t\tdie(\"invalid tag signature file\");\n+\tfsck_options.error_func = mktag_fsck_error_func;\n+\tfsck_set_msg_type(&fsck_options, \"extraheaderentry\", \"warn\");\n+\tif (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,\n+\t\t\t\t&tagged_oid, &tagged_type))\n+\t\tdie(\"tag on stdin did not pass our strict fsck check\");\n+\n+\tif (verify_object_in_tag(&tagged_oid, &tagged_type))\n+\t\tdie(\"tag on stdin did not refer to a valid object\");\n \n \tif (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)\n \t\tdie(\"unable to write tag file\");\ndiff --git a/fsck.c b/fsck.c\nindex f82e2fe9e30..bed5e20e03b 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -80,7 +80,9 @@ static struct oidset gitmodules_done = OIDSET_INIT;\n \t/* infos (reported as warnings, but ignored by default) */ \\\n \tFUNC(GITMODULES_PARSE, INFO) \\\n \tFUNC(BAD_TAG_NAME, INFO) \\\n-\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n+\tFUNC(MISSING_TAGGER_ENTRY, INFO) \\\n+\t/* ignored (elevated when requested) */ \\\n+\tFUNC(EXTRA_HEADER_ENTRY, IGNORE)\n \n #define MSG_ID(id, msg_type) FSCK_MSG_##id,\n enum fsck_msg_id {\n@@ -911,6 +913,16 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \t\t    unsigned long size, struct fsck_options *options)\n {\n \tstruct object_id tagged_oid;\n+\tint tagged_type;\n+\treturn fsck_tag_standalone(oid, buffer, size, options, &tagged_oid,\n+\t\t\t\t   &tagged_type);\n+}\n+\n+int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n+\t\t\tunsigned long size, struct fsck_options *options,\n+\t\t\tstruct object_id *tagged_oid,\n+\t\t\tint *tagged_type)\n+{\n \tint ret = 0;\n \tchar *eol;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -924,7 +936,7 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n-\tif (parse_oid_hex(buffer, &tagged_oid, &p) || *p != '\\n') {\n+\tif (parse_oid_hex(buffer, tagged_oid, &p) || *p != '\\n') {\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n \t\tif (ret)\n \t\t\tgoto done;\n@@ -940,7 +952,8 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n-\tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n+\t*tagged_type = type_from_string_gently(buffer, eol - buffer, 1);\n+\tif (*tagged_type < 0)\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_TYPE, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n@@ -975,6 +988,19 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \telse\n \t\tret = fsck_ident(&buffer, oid, OBJ_TAG, options);\n \n+\tif (!starts_with(buffer, \"\\n\")) {\n+\t\t/*\n+\t\t * The verify_headers() check will allow\n+\t\t * e.g. \"[...]tagger <tagger>\\nsome\n+\t\t * garbage\\n\\nmessage\" to pass, thinking \"some\n+\t\t * garbage\" could be a custom header. E.g. \"mktag\"\n+\t\t * doesn't want any unknown headers.\n+\t\t */\n+\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_EXTRA_HEADER_ENTRY, \"invalid format - extra header(s) after 'tagger'\");\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n+\n done:\n \tstrbuf_release(&sb);\n \treturn ret;\ndiff --git a/fsck.h b/fsck.h\nindex 69cf715e798..29ee4c45e87 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -62,6 +62,15 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options);\n int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options);\n \n+/*\n+ * fsck a tag, and pass info about it back to the caller. This is\n+ * exposed fsck_object() internals for git-mktag(1).\n+ */\n+int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n+\t\t\tunsigned long size, struct fsck_options *options,\n+\t\t\tstruct object_id *tagged_oid,\n+\t\t\tint *tag_type);\n+\n /*\n  * Some fsck checks are context-dependent, and may end up queued; run this\n  * after completing all fsck_object() calls in order to resolve any remaining\ndiff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh\nindex 2f501d2dc94..5d2dc99b74a 100755\n--- a/t/t1006-cat-file.sh\n+++ b/t/t1006-cat-file.sh\n@@ -166,7 +166,7 @@ tag_content=\"$tag_header_without_timestamp 0000000000 +0000\n \n $tag_description\"\n \n-tag_sha1=$(echo_without_newline \"$tag_content\" | git mktag)\n+tag_sha1=$(echo_without_newline \"$tag_content\" | git hash-object -t tag --stdin -w)\n tag_size=$(strlen \"$tag_content\")\n \n run_tests 'tag' $tag_sha1 $tag_size \"$tag_content\" \"$tag_content\" 1\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 9ae1b0bb0af..19b3c1bca9c 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -37,7 +37,7 @@ too short for a tag\n EOF\n \n check_verify_failure 'Tag object length check' \\\n-\t'^error: .*size wrong.*$'\n+\t'^error:.* missingObject:'\n \n ############################################################\n #  2. object line label check\n@@ -50,7 +50,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n+check_verify_failure '\"object\" line label check' '^error:.* missingObject:'\n \n ############################################################\n #  3. object line hash check\n@@ -63,7 +63,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"object\" line SHA1 check' '^error: char7: .*SHA1 hash$'\n+check_verify_failure '\"object\" line check' '^error:.* badObjectSha1:'\n \n ############################################################\n #  4. type line label check\n@@ -76,7 +76,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n+check_verify_failure '\"type\" line label check' '^error:.* missingTypeEntry:'\n \n ############################################################\n #  5. type line eol check\n@@ -84,7 +84,7 @@ check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n echo \"object $head\" >tag.sig\n printf \"type tagsssssssssssssssssssssssssssssss\" >>tag.sig\n \n-check_verify_failure '\"type\" line eol check' '^error: char.*: .*\"\\\\n\"$'\n+check_verify_failure '\"type\" line eol check' '^error:.* unterminatedHeader:'\n \n ############################################################\n #  6. tag line label check #1\n@@ -98,7 +98,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure '\"tag\" line label check #1' \\\n-\t'^error: char.*: no \"tag \" found$'\n+\t'^error:.* missingTagEntry:'\n \n ############################################################\n #  7. tag line label check #2\n@@ -110,7 +110,7 @@ tag\n EOF\n \n check_verify_failure '\"tag\" line label check #2' \\\n-\t'^error: char.*: no \"tag \" found$'\n+\t'^error:.* badType:'\n \n ############################################################\n #  8. type line type-name length check\n@@ -122,7 +122,7 @@ tag mytag\n EOF\n \n check_verify_failure '\"type\" line type-name length check' \\\n-\t'^error: char.*: type too long$'\n+\t'^error:.* badType:'\n \n ############################################################\n #  9. verify object (hash/type) check\n@@ -136,7 +136,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (hash/type) check -- correct type, nonexisting object' \\\n-\t'^error: char7: could not verify object.*$'\n+\t'^fatal: could not read tagged object'\n \n cat >tag.sig <<EOF\n object $head\n@@ -147,7 +147,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n-\t'^fatal: invalid object type'\n+\t'^error:.* badType:'\n \n cat >tag.sig <<EOF\n object $(test_oid deadbeef)\n@@ -158,7 +158,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n-\t'^error: char7: could not verify object.*$'\n+\t'^error:.* badType:'\n \n cat >tag.sig <<EOF\n object $head\n@@ -169,7 +169,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n-\t'^error: char7: could not verify object'\n+\t'^fatal: object.*tagged as.*tree.*but is.*commit'\n \n ############################################################\n # 10. verify tag-name check\n@@ -183,7 +183,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify tag-name check' \\\n-\t'^error: char.*: could not verify tag name$'\n+\t'^error:.* badTagName:'\n \n ############################################################\n # 11. tagger line label check #1\n@@ -197,7 +197,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #1' \\\n-\t'^error: char.*: could not find \"tagger \"$'\n+\t'^error:.* missingTaggerEntry:'\n \n ############################################################\n # 12. tagger line label check #2\n@@ -212,10 +212,10 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #2' \\\n-\t'^error: char.*: could not find \"tagger \"$'\n+\t'^error:.* missingTaggerEntry:'\n \n ############################################################\n-# 13. disallow missing tag author name\n+# 13. allow missing tag author name like fsck\n \n cat >tag.sig <<EOF\n object $head\n@@ -226,8 +226,9 @@ tagger  <> 0 +0000\n This is filler\n EOF\n \n-check_verify_failure 'disallow missing tag author name' \\\n-\t'^error: char.*: missing tagger name$'\n+test_expect_success 'allow missing tag author name' '\n+\tgit mktag <tag.sig\n+'\n \n ############################################################\n # 14. disallow missing tag author name\n@@ -242,7 +243,7 @@ tagger T A Gger <\n EOF\n \n check_verify_failure 'disallow malformed tagger' \\\n-\t'^error: char.*: malformed tagger field$'\n+\t'^error:.* badEmail:'\n \n ############################################################\n # 15. allow empty tag email\n@@ -260,7 +261,7 @@ test_expect_success \\\n     'git mktag <tag.sig >.git/refs/tags/mytag 2>message'\n \n ############################################################\n-# 16. disallow spaces in tag email\n+# 16. allow spaces in tag email like fsck\n \n cat >tag.sig <<EOF\n object $head\n@@ -270,8 +271,9 @@ tagger T A Gger <tag ger@example.com> 0 +0000\n \n EOF\n \n-check_verify_failure 'disallow spaces in tag email' \\\n-\t'^error: char.*: malformed tagger field$'\n+test_expect_success 'allow spaces in tag email like fsck' '\n+\tgit mktag <tag.sig\n+'\n \n ############################################################\n # 17. disallow missing tag timestamp\n@@ -285,7 +287,7 @@ tagger T A Gger <tagger@example.com>__\n EOF\n \n check_verify_failure 'disallow missing tag timestamp' \\\n-\t'^error: char.*: missing tag timestamp$'\n+\t'^error:.* badDate:'\n \n ############################################################\n # 18. detect invalid tag timestamp1\n@@ -299,7 +301,7 @@ tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008\n EOF\n \n check_verify_failure 'detect invalid tag timestamp1' \\\n-\t'^error: char.*: missing tag timestamp$'\n+\t'^error:.* badDate:'\n \n ############################################################\n # 19. detect invalid tag timestamp2\n@@ -313,7 +315,7 @@ tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500\n EOF\n \n check_verify_failure 'detect invalid tag timestamp2' \\\n-\t'^error: char.*: malformed tag timestamp$'\n+\t'^error:.* badDate:'\n \n ############################################################\n # 20. detect invalid tag timezone1\n@@ -327,7 +329,7 @@ tagger T A Gger <tagger@example.com> 1206478233 GMT\n EOF\n \n check_verify_failure 'detect invalid tag timezone1' \\\n-\t'^error: char.*: malformed tag timezone$'\n+\t'^error:.* badTimezone:'\n \n ############################################################\n # 21. detect invalid tag timezone2\n@@ -341,10 +343,10 @@ tagger T A Gger <tagger@example.com> 1206478233 +  30\n EOF\n \n check_verify_failure 'detect invalid tag timezone2' \\\n-\t'^error: char.*: malformed tag timezone$'\n+\t'^error:.* badTimezone:'\n \n ############################################################\n-# 22. detect invalid tag timezone3\n+# 22. allow invalid tag timezone3 (the maximum is -1200/+1400)\n \n cat >tag.sig <<EOF\n object $head\n@@ -354,8 +356,9 @@ tagger T A Gger <tagger@example.com> 1206478233 -1430\n \n EOF\n \n-check_verify_failure 'detect invalid tag timezone3' \\\n-\t'^error: char.*: malformed tag timezone$'\n+test_expect_success 'allow invalid tag timezone' '\n+\tgit mktag <tag.sig\n+'\n \n ############################################################\n # 23. detect invalid header entry\n@@ -370,7 +373,21 @@ this line should not be here\n EOF\n \n check_verify_failure 'detect invalid header entry' \\\n-\t'^error: char.*: trailing garbage in tag header$'\n+\t'^error:.* extraHeaderEntry:'\n+\n+cat >tag.sig <<EOF\n+object $head\n+type commit\n+tag mytag\n+tagger T A Gger <tagger@example.com> 1206478233 -0500\n+\n+\n+this line comes after an extra newline\n+EOF\n+\n+test_expect_success \\\n+    'allow extra newlines at start of body' \\\n+    'git mktag <tag.sig >.git/refs/tags/mytag 2>message'\n \n ############################################################\n # 24. create valid tag\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"411924","messageId":"20201209200140.29425-1-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126222257.5629-1-avarab@gmail.com","subject":"[PATCH v3 00/10] make \"mktag\" use fsck_tag()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-09T20:01:30Z","receivedAt":"2020-12-09T20:04:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This version should address all the comments Junio made on v2. Changes:\n\n * The whole \"extra\" fsck option is gone, I just didn't realize I\n   could set the new check to \"ignore\", and then manually promote it.\n\n * Ejected \"mktag: reword write_object_file() error\". It was the same\n   phrasing as \"git tag\" uses, let's just keep it.\n\n * Clarifications in docs/commit messages\n\n * There's 2 extra patches at the end now which take the first steps\n   into making \"git mktag\" more of a normal builtin. It reads fsck.*\n   config variables, so you can turn off that \"no extra headers\" check\n   through the normal fsck.<msg-id>=ignore config.\n\n   It should also be moved to getopts, and we could make it support\n   --no-strict to have the same idea of error/warning as fsck itself,\n   but that's #leftoverbits, along with moving it to i18n.\n\n   It would be nice to have patches 1-8 merged down if they're deemed\n   ready, and if 9-10 aren't deemed wanted just discard them. I think\n   it makes sense though...\n\nÆvar Arnfjörð Bjarmason (10):\n  mktag doc: say <hash> not <sha1>\n  mktag: use default strbuf_read() hint\n  mktag: remove redundant braces in one-line body \"if\"\n  mktag tests: don't needlessly use a subshell\n  mktag tests: remove needless SHA-1 hardcoding\n  mktag tests: improve verify_object() test coverage\n  mktag: use fsck instead of custom verify_tag()\n  mktag doc: update to explain why to use this\n  fsck: make fsck_config() re-usable\n  mktag: allow turning off fsck.extraHeaderEntry\n\n Documentation/git-hash-object.txt |   4 +\n Documentation/git-mktag.txt       |  34 ++++-\n builtin/fsck.c                    |  20 +--\n builtin/mktag.c                   | 204 +++++++++---------------------\n fsck.c                            |  57 ++++++++-\n fsck.h                            |  16 +++\n t/t1006-cat-file.sh               |   2 +-\n t/t3800-mktag.sh                  | 132 ++++++++++++++-----\n 8 files changed, 261 insertions(+), 208 deletions(-)\n\nRange-diff:\n 1:  f46abb37df9 =  1:  aee3f52a478 mktag doc: say <hash> not <sha1>\n 2:  1b4d9a53302 =  2:  6e98557709a mktag: use default strbuf_read() hint\n 3:  83f4af6013e <  -:  ----------- mktag: reword write_object_file() error\n 4:  bca1484ed96 =  3:  8e5fe08f155 mktag: remove redundant braces in one-line body \"if\"\n 5:  ac7c4097c90 =  4:  1f06b9c0cf9 mktag tests: don't needlessly use a subshell\n 6:  5e076659e45 !  5:  5d1cb73ca35 mktag tests: remove needless SHA-1 hardcoding\n    @@ t/t3800-mktag.sh: EOF\n      \n      ############################################################\n     -#  3. object line SHA1 check\n    -+#  3. object line SHA check\n    ++#  3. object line hash check\n      \n      cat >tag.sig <<EOF\n     -object zz9e9b33986b1c2670fff52c5067603117b3e895\n 7:  a048c3e6401 !  6:  cf86f4ca37d mktag tests: improve verify_object() test coverage\n    @@ t/t3800-mktag.sh: check_verify_failure '\"type\" line type-name length check' \\\n      \n      ############################################################\n     -#  9. verify object (SHA1/type) check\n    -+#  9. verify object (SHA/type) check\n    ++#  9. verify object (hash/type) check\n      \n      cat >tag.sig <<EOF\n      object $(test_oid deadbeef)\n    @@ t/t3800-mktag.sh: check_verify_failure '\"type\" line type-name length check' \\\n     +\n     +EOF\n     +\n    -+check_verify_failure 'verify object (SHA/type) check -- correct type, nonexisting object' \\\n    ++check_verify_failure 'verify object (hash/type) check -- correct type, nonexisting object' \\\n     +\t'^error: char7: could not verify object.*$'\n     +\n     +cat >tag.sig <<EOF\n    @@ t/t3800-mktag.sh: check_verify_failure '\"type\" line type-name length check' \\\n      EOF\n      \n     -check_verify_failure 'verify object (SHA1/type) check' \\\n    -+check_verify_failure 'verify object (SHA/type) check -- made-up type, nonexisting object' \\\n    ++check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n     +\t'^fatal: invalid object type'\n     +\n     +cat >tag.sig <<EOF\n    @@ t/t3800-mktag.sh: check_verify_failure '\"type\" line type-name length check' \\\n     +\n     +EOF\n     +\n    -+check_verify_failure 'verify object (SHA/type) check -- incorrect type, valid object' \\\n    ++check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n      \t'^error: char7: could not verify object.*$'\n      \n     +cat >tag.sig <<EOF\n    @@ t/t3800-mktag.sh: check_verify_failure '\"type\" line type-name length check' \\\n     +\n     +EOF\n     +\n    -+check_verify_failure 'verify object (SHA/type) check -- incorrect type, valid object' \\\n    ++check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n     +\t'^error: char7: could not verify object'\n     +\n      ############################################################\n 8:  dab44d32359 <  -:  ----------- fsck: add new \"extra\" checks for \"mktag\"\n 9:  8ff853caeea !  7:  5812ee53c97 mktag: use fsck instead of custom verify_tag()\n    @@ Commit message\n         back to the same commit[1]. Let's unify them so we're not maintaining\n         two sets functions to verify that a tag is OK.\n     \n    -    Moving to fsck_tag() required teaching it to optionally use some\n    -    validations that only the old mktag code could perform. That was done\n    -    in an earlier commit, the \"extraHeaderEntry\" and\n    -    \"extraHeaderBodyNewline\" tests being added here make use of that\n    -    logic.\n    +    The behavior of fsck_tag() and the old \"mktag\" code being removed here\n    +    is different in few aspects.\n     \n    -    There was other \"mktag\" validation logic that I think makes sense to\n    -    just remove. Namely:\n    +    I think it makes sense to remove some of those checks, namely:\n     \n          A. fsck only cares that the timezone matches [-+][0-9]{4}. The mktag\n             code disallowed values larger than 1400.\n    @@ Commit message\n          C. Like B, but \"mktag\" disallowed spaces in the <email> part, fsck\n             allows it.\n     \n    -    We didn't only lose obscure validation logic, we also gained some:\n    +    In some ways fsck_tag() is stricter than \"mktag\" was, namely:\n     \n          D. fsck disallows zero-padded dates, but mktag didn't care. So\n             e.g. the timestamp \"0000000000 +0000\" produces an error now. A\n             test in \"t1006-cat-file.sh\" relied on this, it's been changed to\n             use \"hash-object\" (without fsck) instead.\n     \n    +    There was one check I deemed worth keeping by porting it over to\n    +    fsck_tag():\n    +\n    +     E. \"mktag\" did not allow any custom headers, and by extension (as an\n    +        empty commit is allowed) also forbade an extra stray trailing\n    +        newline after the headers it knew about.\n    +\n    +        Add a new check in the \"ignore\" category to fsck and use it. This\n    +        somewhat abuses the facility added in efaba7cc77f (fsck:\n    +        optionally ignore specific fsck issues completely, 2015-06-22).\n    +\n    +        This is somewhat of hack, but probably the least invasive change\n    +        we can make here. The fsck command will shuffle these categories\n    +        around, e.g. under --strict the \"info\" becomes a \"warn\" and \"warn\"\n    +        becomes \"error\". Existing users of fsck's (and others,\n    +        e.g. index-pack) --strict option rely on this.\n    +\n    +        So we need to put something into a category that'll be ignored by\n    +        all existing users of the API. Pretending that\n    +        fsck.extraHeaderEntry=error (\"ignore\" by default) was set serves\n    +        to do this for us.\n    +\n         1. ec4465adb38 (Add \"tag\" objects that can be used to sign other\n            objects., 2005-04-25)\n     \n    @@ builtin/mktag.c\n     +\tswitch (msg_type) {\n     +\tcase FSCK_WARN:\n     +\tcase FSCK_ERROR:\n    -+\tcase FSCK_EXTRA:\n     +\t\t/*\n     +\t\t * We treat both warnings and errors as errors, things\n     +\t\t * like missing \"tagger\" lines are \"only\" warnings\n    @@ builtin/mktag.c: int cmd_mktag(int argc, const char **argv, const char *prefix)\n     -\t   \"object <sha1>\\ntype\\ntagger \" */\n     -\tif (verify_tag(buf.buf, buf.len) < 0)\n     -\t\tdie(\"invalid tag signature file\");\n    -+\tfsck_options.extra = 1;\n     +\tfsck_options.error_func = mktag_fsck_error_func;\n    ++\tfsck_set_msg_type(&fsck_options, \"extraheaderentry\", \"warn\");\n     +\tif (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,\n     +\t\t\t\t&tagged_oid, &tagged_type))\n     +\t\tdie(\"tag on stdin did not pass our strict fsck check\");\n    @@ builtin/mktag.c: int cmd_mktag(int argc, const char **argv, const char *prefix)\n     +\t\tdie(\"tag on stdin did not refer to a valid object\");\n      \n      \tif (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)\n    - \t\tdie(\"unable to write annotated tag object\");\n    + \t\tdie(\"unable to write tag file\");\n     \n      ## fsck.c ##\n    +@@ fsck.c: static struct oidset gitmodules_done = OIDSET_INIT;\n    + \t/* infos (reported as warnings, but ignored by default) */ \\\n    + \tFUNC(GITMODULES_PARSE, INFO) \\\n    + \tFUNC(BAD_TAG_NAME, INFO) \\\n    +-\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n    ++\tFUNC(MISSING_TAGGER_ENTRY, INFO) \\\n    ++\t/* ignored (elevated when requested) */ \\\n    ++\tFUNC(EXTRA_HEADER_ENTRY, IGNORE)\n    + \n    + #define MSG_ID(id, msg_type) FSCK_MSG_##id,\n    + enum fsck_msg_id {\n     @@ fsck.c: static int fsck_tag(const struct object_id *oid, const char *buffer,\n      \t\t    unsigned long size, struct fsck_options *options)\n      {\n    @@ fsck.c: static int fsck_tag(const struct object_id *oid, const char *buffer,\n      \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_TYPE, \"invalid 'type' value\");\n      \tif (ret)\n      \t\tgoto done;\n    +@@ fsck.c: static int fsck_tag(const struct object_id *oid, const char *buffer,\n    + \telse\n    + \t\tret = fsck_ident(&buffer, oid, OBJ_TAG, options);\n    + \n    ++\tif (!starts_with(buffer, \"\\n\")) {\n    ++\t\t/*\n    ++\t\t * The verify_headers() check will allow\n    ++\t\t * e.g. \"[...]tagger <tagger>\\nsome\n    ++\t\t * garbage\\n\\nmessage\" to pass, thinking \"some\n    ++\t\t * garbage\" could be a custom header. E.g. \"mktag\"\n    ++\t\t * doesn't want any unknown headers.\n    ++\t\t */\n    ++\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_EXTRA_HEADER_ENTRY, \"invalid format - extra header(s) after 'tagger'\");\n    ++\t\tif (ret)\n    ++\t\t\tgoto done;\n    ++\t}\n    ++\n    + done:\n    + \tstrbuf_release(&sb);\n    + \treturn ret;\n     \n      ## fsck.h ##\n     @@ fsck.h: int fsck_walk(struct object *obj, void *data, struct fsck_options *options);\n    @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n     +check_verify_failure '\"object\" line label check' '^error:.* missingObject:'\n      \n      ############################################################\n    - #  3. object line SHA check\n    + #  3. object line hash check\n     @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n      \n      EOF\n    @@ t/t3800-mktag.sh: tag mytag\n     +\t'^error:.* badType:'\n      \n      ############################################################\n    - #  9. verify object (SHA/type) check\n    + #  9. verify object (hash/type) check\n     @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n      EOF\n      \n    - check_verify_failure 'verify object (SHA/type) check -- correct type, nonexisting object' \\\n    + check_verify_failure 'verify object (hash/type) check -- correct type, nonexisting object' \\\n     -\t'^error: char7: could not verify object.*$'\n     +\t'^fatal: could not read tagged object'\n      \n    @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n     @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n      EOF\n      \n    - check_verify_failure 'verify object (SHA/type) check -- made-up type, nonexisting object' \\\n    + check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n     -\t'^fatal: invalid object type'\n     +\t'^error:.* badType:'\n      \n    @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n     @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n      EOF\n      \n    - check_verify_failure 'verify object (SHA/type) check -- incorrect type, valid object' \\\n    + check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n     -\t'^error: char7: could not verify object.*$'\n     +\t'^error:.* badType:'\n      \n    @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n     @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n      EOF\n      \n    - check_verify_failure 'verify object (SHA/type) check -- incorrect type, valid object' \\\n    + check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n     -\t'^error: char7: could not verify object'\n     +\t'^fatal: object.*tagged as.*tree.*but is.*commit'\n      \n    @@ t/t3800-mktag.sh: this line should not be here\n     +tagger T A Gger <tagger@example.com> 1206478233 -0500\n     +\n     +\n    -+this line should be one line up\n    ++this line comes after an extra newline\n     +EOF\n     +\n    -+check_verify_failure 'detect invalid header entry' \\\n    -+\t'^error:.* extraHeaderBodyNewline:'\n    ++test_expect_success \\\n    ++    'allow extra newlines at start of body' \\\n    ++    'git mktag <tag.sig >.git/refs/tags/mytag 2>message'\n      \n      ############################################################\n      # 24. create valid tag\n10:  e38feefd3f8 !  8:  fa04664f7f1 mktag doc: update to explain why to use this\n    @@ Documentation/git-mktag.txt: SYNOPSIS\n     +Reads a tag contents on standard input and creates a tag object. The\n     +output is the new tag's <object> identifier.\n     +\n    -+This command accepts a subset of what linkgit:git-hash-object[1] would\n    -+accept with `-t tag --stdin`. I.e. both of these work:\n    ++This command is mostly equivalent to linkgit:git-hash-object[1]\n    ++invoked with `-t tag -w --stdin`. I.e. both of these will create and\n    ++write a tag found in `my-tag`:\n     +\n     +    git mktag <my-tag\n    -+    git hash-object -t tag --stdin <my-tag\n    ++    git hash-object -t tag -w --stdin <my-tag\n     +\n    -+The difference between the two is that mktag does the equivalent of a\n    -+linkgit:git-fsck(1) check on its input, and furthermore disallows some\n    -+thing linkgit:git-hash-object[1] would pass, e.g. extra headers in the\n    -+object before the message.\n    ++The difference is that mktag will die before writing the tag if the\n    ++tag doesn't pass a linkgit:git-fsck[1] check.\n    ++\n    ++The \"fsck\" check done mktag is is stricter than what\n    ++linkgit:git-fsck[1] would run by default in that all `fsck.<msg-id>`\n    ++messages are promoted from warnings to errors (so e.g. a missing\n    ++\"tagger\" line is an error). Extra headers in the object are also an\n    ++error under mktag, but ignored by linkgit:git-fsck[1].\n      \n      Tag Format\n      ----------\n    @@ Documentation/git-mktag.txt: exists, is separated by a blank line from the heade\n      message part may contain a signature that Git itself doesn't\n      care about, but that can be verified with gpg.\n      \n    -+HISTORY\n    -+-------\n    -+\n    -+In versions of Git before v2.30.0 the \"mktag\" command's validation\n    -+logic was subtly different than that of linkgit:git-fsck[1]. It is now\n    -+a strict superset of linkgit:git-fsck[1]'s validation logic.\n    -+\n     +SEE ALSO\n     +--------\n     +linkgit:git-hash-object[1],\n -:  ----------- >  9:  30eff9170fb fsck: make fsck_config() re-usable\n -:  ----------- > 10:  11139ec2b8d mktag: allow turning off fsck.extraHeaderEntry\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"411925","messageId":"20201209200140.29425-5-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126222257.5629-1-avarab@gmail.com","subject":"[PATCH v3 04/10] mktag tests: don't needlessly use a subshell","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-09T20:01:34Z","receivedAt":"2020-12-09T20:04:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The use of a subshell dates back to e9b20943b77 (t/t3800: do not use a\ntemporary file to hold expected result., 2008-01-04). It's not needed\nanymore, if it ever was.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex d696aa4e52e..0e411e3c45f 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -14,7 +14,7 @@ test_description='git mktag: tag object verify test'\n check_verify_failure () {\n \texpect=\"$2\"\n \ttest_expect_success \"$1\" '\n-\t\t( test_must_fail git mktag <tag.sig 2>message ) &&\n+\t\ttest_must_fail git mktag <tag.sig 2>message &&\n \t\tgrep \"$expect\" message\n \t'\n }\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"411926","messageId":"20201209200140.29425-6-avarab@gmail.com","threadId":"51667","inReplyTo":"20201126222257.5629-1-avarab@gmail.com","subject":"[PATCH v3 05/10] mktag tests: remove needless SHA-1 hardcoding","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-09T20:01:35Z","receivedAt":"2020-12-09T20:04:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the tests amended in acb49d1cc8b (t3800: make hash-size\nindependent, 2019-08-18) even more to make them independent of either\nSHA-1 or SHA-256.\n\nSome of these tests were failing for the wrong reasons. The first one\nbeing modified here would fail because the line starts with \"xxxxxx\"\ninstead of \"object\", the rest of the line doesn't matter.\n\nLet's just put a valid hash on the rest of the line anyway to narrow\nthe test down for just the s/object/xxxxxx/ case.\n\nThe second one being modified here would fail under\nGIT_TEST_DEFAULT_HASH=sha256 because <some sha-1 length garbage> is an\ninvalid SHA-256, but we should really be testing <some sha-256 length\ngarbage> when under SHA-256.\n\nThis doesn't really matter since we should be able to trust other\nparts of the code to validate things in the 0-9a-f range, but let's\nkeep it for good measure.\n\nThere's a later test which tests an invalid SHA which looks like a\nvalid one, to stress the \"We refuse to tag something we can't\nverify[...]\" logic in mktag.c.\n\nBut here we're testing for a SHA-length string which contains\ncharacters outside of the /[0-9a-f]/i set.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 0e411e3c45f..a22a0954d5e 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -43,7 +43,7 @@ check_verify_failure 'Tag object length check' \\\n #  2. object line label check\n \n cat >tag.sig <<EOF\n-xxxxxx 139e9b33986b1c2670fff52c5067603117b3e895\n+xxxxxx $head\n type tag\n tag mytag\n tagger . <> 0 +0000\n@@ -53,10 +53,10 @@ EOF\n check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n \n ############################################################\n-#  3. object line SHA1 check\n+#  3. object line hash check\n \n cat >tag.sig <<EOF\n-object zz9e9b33986b1c2670fff52c5067603117b3e895\n+object $(echo $head | tr 0-9a-f z)\n type tag\n tag mytag\n tagger . <> 0 +0000\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"411941","messageId":"xmqq7dpq1uju.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"Re: [PATCH v3 00/10] make \"mktag\" use fsck_tag()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-09T22:30:45Z","receivedAt":"2020-12-09T22:31:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> This version should address all the comments Junio made on v2. Changes:\n>\n>  * The whole \"extra\" fsck option is gone, I just didn't realize I\n>    could set the new check to \"ignore\", and then manually promote it.\n>\n>  * Ejected \"mktag: reword write_object_file() error\". It was the same\n>    phrasing as \"git tag\" uses, let's just keep it.\n>\n>  * Clarifications in docs/commit messages\n>\n>  * There's 2 extra patches at the end now which take the first steps\n>    into making \"git mktag\" more of a normal builtin. It reads fsck.*\n>    config variables, so you can turn off that \"no extra headers\" check\n>    through the normal fsck.<msg-id>=ignore config.\n>\n>    It should also be moved to getopts, and we could make it support\n>    --no-strict to have the same idea of error/warning as fsck itself,\n>    but that's #leftoverbits, along with moving it to i18n.\n>\n>    It would be nice to have patches 1-8 merged down if they're deemed\n>    ready, and if 9-10 aren't deemed wanted just discard them. I think\n>    it makes sense though...\n\nThanks.  I haven't read the individual patches, but spotted an\nobvious \"is is\" typo in the doc while scanning through the end\nresult of applying all of them.\n\n Documentation/git-mktag.txt | 7 +++----\n 1 file changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git c/Documentation/git-mktag.txt w/Documentation/git-mktag.txt\nindex e1506dde56..2c1afedef6 100644\n--- c/Documentation/git-mktag.txt\n+++ w/Documentation/git-mktag.txt\n@@ -27,10 +27,9 @@ write a tag found in `my-tag`:\n The difference is that mktag will die before writing the tag if the\n tag doesn't pass a linkgit:git-fsck[1] check.\n \n-The \"fsck\" check done mktag is is stricter than what\n-linkgit:git-fsck[1] would run by default in that all `fsck.<msg-id>`\n-messages are promoted from warnings to errors (so e.g. a missing\n-\"tagger\" line is an error).\n+The \"fsck\" check done mktag is stricter than what linkgit:git-fsck[1]\n+would run by default in that all `fsck.<msg-id>` messages are promoted\n+from warnings to errors (so e.g. a missing \"tagger\" line is an error).\n \n Extra headers in the object are also an error under mktag, but ignored\n by linkgit:git-fsck[1]. This extra check can be turned off by setting\n"},{"id":"412877","messageId":"20201223013606.7972-2-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 01/20] mktag doc: say <hash> not <sha1>","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:46Z","receivedAt":"2020-12-23T01:38:26Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the \"mktag\" documentation to refer to the input hash as just\n\"hash\", not \"sha1\". This command has supported SHA-256 for a while\nnow.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex fa6a756123..a158428eb9 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -23,7 +23,7 @@ Tag Format\n A tag signature file, to be fed to this command's standard input,\n has a very simple fixed format: four lines of\n \n-  object <sha1>\n+  object <hash>\n   type <typename>\n   tag <tagname>\n   tagger <tagger>\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412878","messageId":"20201223013606.7972-1-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 00/20] make \"mktag\" use fsck_tag() & more","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:45Z","receivedAt":"2020-12-23T01:38:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"So, when re-rolling this with Junio's small fixup this grew in scope a\nbit, but should paradoxically be easier to deal with even though it's\n2x the size now. Read on:\n\nÆvar Arnfjörð Bjarmason (20):\n  mktag doc: say <hash> not <sha1>\n  mktag doc: grammar fix, when exists -> when it exists\n  mktag doc: update to explain why to use this\n  mktag tests: don't needlessly use a subshell\n  mktag tests: remove needless SHA-1 hardcoding\n  mktag tests: improve verify_object() test coverage\n  mktag tests: don't pipe to stderr needlessly\n  mktag tests: don't create \"mytag\" twice\n  mktag tests: stress test whitespace handling\n  mktag tests: test \"hash-object\" compatibility\n\nI re-arranged this series so the doc/test patches for existing\nbehavior all come first now. There's some new patches there (see\nrange-diff), but all rather easy-to review fixes or tests for existing\nbehavior.\n\n  mktag: use default strbuf_read() hint\n  mktag: remove redundant braces in one-line body \"if\"\n  mktag: use puts(str) instead of printf(\"%s\\n\", str)\n\nTrivial coding style changes, the puts() patch is new.\n\n  mktag: use fsck instead of custom verify_tag()\n\nStill the real meat of the series, unchanged in any meaningful way,\nexcept in (as seen in the range-diff) carrying forward doc/test\nchanges made earlier.\n\n  fsck: make fsck_config() re-usable\n  mktag: allow turning off fsck.extraHeaderEntry\n\nditto unchanged.\n\n  mktag: allow omitting the header/body \\n separator\n\nI discovered a regression in mktag in git since 2008 where it refuses\nto accept input without an empty newline separating the body & message\nin cases where there's no message.\n\nNow we again accept the same input as hash-object, and with the new\n\"hash-object\" test integration earlier in the series we're confident\nthat mktag & hash-object do the same thing in all these cases.\n\n  mktag: convert to parse-options\n  mktag: mark strings for translation\n  mktag: add a --no-strict option\n\nThe #leftoverbits I suggested in v3 of converting to parse-options &\ndoing i18n for mktag, and finally supporting --no-strict so you can\nmake it behave like \"fsck\" does in its default mode.\n\n Documentation/git-hash-object.txt |   4 +\n Documentation/git-mktag.txt       |  42 +++++-\n builtin/fsck.c                    |  20 +--\n builtin/mktag.c                   | 235 +++++++++++-------------------\n fsck.c                            |  59 +++++++-\n fsck.h                            |  16 ++\n parse-options.h                   |   1 +\n t/t1006-cat-file.sh               |   2 +-\n t/t3800-mktag.sh                  | 211 +++++++++++++++++++++------\n 9 files changed, 361 insertions(+), 229 deletions(-)\n\nRange-diff:\n 1:  aee3f52a47 =  1:  a31c305cfc mktag doc: say <hash> not <sha1>\n -:  ---------- >  2:  81cb4cba5c mktag doc: grammar fix, when exists -> when it exists\n 8:  fa04664f7f !  3:  b4bc6f894c mktag doc: update to explain why to use this\n    @@ Commit message\n         documentation wouldn't have much of an idea what the difference\n         was.\n     \n    -    Let's make it clear that it's to do with slightly different fsck\n    -    validation logic, and cross-link the \"mktag\" and \"hash-object\"\n    -    documentation to aid discover-ability.\n    +    Let's allude to our own validation logic, and cross-link the \"mktag\"\n    +    and \"hash-object\" documentation to aid discover-ability. A follow-up\n    +    change to migrate \"mktag\" to use \"fsck\" validation will make the part\n    +    about validation logic clearer.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ Documentation/git-mktag.txt: SYNOPSIS\n     +    git hash-object -t tag -w --stdin <my-tag\n     +\n     +The difference is that mktag will die before writing the tag if the\n    -+tag doesn't pass a linkgit:git-fsck[1] check.\n    -+\n    -+The \"fsck\" check done mktag is is stricter than what\n    -+linkgit:git-fsck[1] would run by default in that all `fsck.<msg-id>`\n    -+messages are promoted from warnings to errors (so e.g. a missing\n    -+\"tagger\" line is an error). Extra headers in the object are also an\n    -+error under mktag, but ignored by linkgit:git-fsck[1].\n    ++tag doesn't pass a sanity check.\n      \n      Tag Format\n      ----------\n 4:  1f06b9c0cf =  4:  acb94e0289 mktag tests: don't needlessly use a subshell\n 5:  5d1cb73ca3 =  5:  4ae76ec5e3 mktag tests: remove needless SHA-1 hardcoding\n 6:  cf86f4ca37 =  6:  9effb4532b mktag tests: improve verify_object() test coverage\n -:  ---------- >  7:  b81d31a917 mktag tests: don't pipe to stderr needlessly\n -:  ---------- >  8:  11f59718b4 mktag tests: don't create \"mytag\" twice\n -:  ---------- >  9:  dd6b012b0c mktag tests: stress test whitespace handling\n -:  ---------- > 10:  56c6b562fd mktag tests: test \"hash-object\" compatibility\n 2:  6e98557709 = 11:  1e2e4ec269 mktag: use default strbuf_read() hint\n 3:  8e5fe08f15 = 12:  be2ab3edab mktag: remove redundant braces in one-line body \"if\"\n -:  ---------- > 13:  d8514df970 mktag: use puts(str) instead of printf(\"%s\\n\", str)\n 7:  5812ee53c9 ! 14:  346d73cc97 mktag: use fsck instead of custom verify_tag()\n    @@ Commit message\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    + ## Documentation/git-mktag.txt ##\n    +@@ Documentation/git-mktag.txt: write a tag found in `my-tag`:\n    +     git hash-object -t tag -w --stdin <my-tag\n    + \n    + The difference is that mktag will die before writing the tag if the\n    +-tag doesn't pass a sanity check.\n    ++tag doesn't pass a linkgit:git-fsck[1] check.\n    ++\n    ++The \"fsck\" check done mktag is stricter than what linkgit:git-fsck[1]\n    ++would run by default in that all `fsck.<msg-id>` messages are promoted\n    ++from warnings to errors (so e.g. a missing \"tagger\" line is an error).\n    ++\n    ++Extra headers in the object are also an error under mktag, but ignored\n    ++by linkgit:git-fsck[1]\n    + \n    + Tag Format\n    + ----------\n    +\n      ## builtin/mktag.c ##\n     @@\n      #include \"tag.h\"\n    @@ builtin/mktag.c\n     +\n     +\tbuffer = read_object_file(tagged_oid, &type, &size);\n     +\tif (!buffer)\n    -+\t\tdie(\"could not read tagged object '%s'\\n\",\n    ++\t\tdie(\"could not read tagged object '%s'\",\n     +\t\t    oid_to_hex(tagged_oid));\n     +\tif (type != *tagged_type)\n    -+\t\tdie(\"object '%s' tagged as '%s', but is a '%s' type\\n\",\n    ++\t\tdie(\"object '%s' tagged as '%s', but is a '%s' type\",\n     +\t\t    oid_to_hex(tagged_oid),\n     +\t\t    type_name(*tagged_type), type_name(type));\n     +\n    @@ t/t3800-mktag.sh: tagger  <> 0 +0000\n      \n     -check_verify_failure 'disallow missing tag author name' \\\n     -\t'^error: char.*: missing tagger name$'\n    -+test_expect_success 'allow missing tag author name' '\n    -+\tgit mktag <tag.sig\n    -+'\n    ++test_expect_mktag_success 'allow missing tag author name'\n      \n      ############################################################\n      # 14. disallow missing tag author name\n    @@ t/t3800-mktag.sh: tagger T A Gger <\n      \n      ############################################################\n      # 15. allow empty tag email\n    -@@ t/t3800-mktag.sh: test_expect_success \\\n    -     'git mktag <tag.sig >.git/refs/tags/mytag 2>message'\n    +@@ t/t3800-mktag.sh: EOF\n    + test_expect_mktag_success 'allow empty tag email'\n      \n      ############################################################\n     -# 16. disallow spaces in tag email\n    @@ t/t3800-mktag.sh: tagger T A Gger <tag ger@example.com> 0 +0000\n      \n     -check_verify_failure 'disallow spaces in tag email' \\\n     -\t'^error: char.*: malformed tagger field$'\n    -+test_expect_success 'allow spaces in tag email like fsck' '\n    -+\tgit mktag <tag.sig\n    -+'\n    ++test_expect_mktag_success 'allow spaces in tag email like fsck'\n      \n      ############################################################\n      # 17. disallow missing tag timestamp\n    @@ t/t3800-mktag.sh: tagger T A Gger <tagger@example.com> 1206478233 -1430\n      \n     -check_verify_failure 'detect invalid tag timezone3' \\\n     -\t'^error: char.*: malformed tag timezone$'\n    -+test_expect_success 'allow invalid tag timezone' '\n    -+\tgit mktag <tag.sig\n    -+'\n    ++test_expect_mktag_success 'allow invalid tag timezone'\n      \n      ############################################################\n      # 23. detect invalid header entry\n    @@ t/t3800-mktag.sh: this line should not be here\n      check_verify_failure 'detect invalid header entry' \\\n     -\t'^error: char.*: trailing garbage in tag header$'\n     +\t'^error:.* extraHeaderEntry:'\n    -+\n    -+cat >tag.sig <<EOF\n    -+object $head\n    -+type commit\n    -+tag mytag\n    -+tagger T A Gger <tagger@example.com> 1206478233 -0500\n    -+\n    -+\n    -+this line comes after an extra newline\n    -+EOF\n    -+\n    -+test_expect_success \\\n    -+    'allow extra newlines at start of body' \\\n    -+    'git mktag <tag.sig >.git/refs/tags/mytag 2>message'\n    + \n    + cat >tag.sig <<EOF\n    + object $head\n    +@@ t/t3800-mktag.sh: tagger T A Gger <tagger@example.com> 1206478233 -0500$space\n    + EOF\n    + \n    + check_verify_failure 'extra whitespace at end of headers' \\\n    +-\t'^error: char.*: malformed tag timezone$'\n    ++\t'^error:.* badTimezone:'\n    + \n    + cat >tag.sig <<EOF\n    + object $head\n    +@@ t/t3800-mktag.sh: tagger T A Gger <tagger@example.com> 1206478233 -0500\n    + EOF\n    + \n    + check_verify_failure 'disallow no header / body newline separator' \\\n    +-\t'^error: char.*: trailing garbage in tag header$'\n    ++\t'^error:.* extraHeaderEntry:'\n      \n      ############################################################\n      # 24. create valid tag\n 9:  30eff9170f = 15:  0e7994d8fc fsck: make fsck_config() re-usable\n10:  11139ec2b8 ! 16:  5e8046022b mktag: allow turning off fsck.extraHeaderEntry\n    @@ Commit message\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Documentation/git-mktag.txt ##\n    -@@ Documentation/git-mktag.txt: tag doesn't pass a linkgit:git-fsck[1] check.\n    - The \"fsck\" check done mktag is is stricter than what\n    - linkgit:git-fsck[1] would run by default in that all `fsck.<msg-id>`\n    - messages are promoted from warnings to errors (so e.g. a missing\n    --\"tagger\" line is an error). Extra headers in the object are also an\n    --error under mktag, but ignored by linkgit:git-fsck[1].\n    -+\"tagger\" line is an error).\n    -+\n    -+Extra headers in the object are also an error under mktag, but ignored\n    +@@ Documentation/git-mktag.txt: would run by default in that all `fsck.<msg-id>` messages are promoted\n    + from warnings to errors (so e.g. a missing \"tagger\" line is an error).\n    + \n    + Extra headers in the object are also an error under mktag, but ignored\n    +-by linkgit:git-fsck[1]\n     +by linkgit:git-fsck[1]. This extra check can be turned off by setting\n     +the appropriate `fsck.<msg-id>` varible:\n     +\n -:  ---------- > 17:  32698e1d00 mktag: allow omitting the header/body \\n separator\n -:  ---------- > 18:  b6a22f2f99 mktag: convert to parse-options\n -:  ---------- > 19:  7fc0b81df7 mktag: mark strings for translation\n -:  ---------- > 20:  6fa443d528 mktag: add a --no-strict option\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412879","messageId":"20201223013606.7972-3-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 02/20] mktag doc: grammar fix, when exists -> when it exists","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:47Z","receivedAt":"2020-12-23T01:38:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend the wording of documentation added in 6cfec03680 (mktag:\nminimally update the description., 2007-06-10). It makes more sense to\nsay \"when it exists\" here, as we're referring to \"the message\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex a158428eb9..1b0667e372 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -29,7 +29,7 @@ has a very simple fixed format: four lines of\n   tagger <tagger>\n \n followed by some 'optional' free-form message (some tags created\n-by older Git may not have `tagger` line).  The message, when\n+by older Git may not have `tagger` line).  The message, when it\n exists, is separated by a blank line from the header.  The\n message part may contain a signature that Git itself doesn't\n care about, but that can be verified with gpg.\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412880","messageId":"20201223013606.7972-4-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 03/20] mktag doc: update to explain why to use this","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:48Z","receivedAt":"2020-12-23T01:38:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the mktag documentation to compare itself to the similar\n\"hash-object -t tag\" command. Before this someone reading the\ndocumentation wouldn't have much of an idea what the difference\nwas.\n\nLet's allude to our own validation logic, and cross-link the \"mktag\"\nand \"hash-object\" documentation to aid discover-ability. A follow-up\nchange to migrate \"mktag\" to use \"fsck\" validation will make the part\nabout validation logic clearer.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-hash-object.txt |  4 ++++\n Documentation/git-mktag.txt       | 21 +++++++++++++++++----\n 2 files changed, 21 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-hash-object.txt b/Documentation/git-hash-object.txt\nindex df9e2c58bd..c535661ced 100644\n--- a/Documentation/git-hash-object.txt\n+++ b/Documentation/git-hash-object.txt\n@@ -58,6 +58,10 @@ OPTIONS\n \tstress-testing Git itself or reproducing characteristics of corrupt or\n \tbogus objects encountered in the wild.\n \n+SEE ALSO\n+--------\n+linkgit:git-mktag[1]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex 1b0667e372..adc63f6d4e 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -3,7 +3,7 @@ git-mktag(1)\n \n NAME\n ----\n-git-mktag - Creates a tag object\n+git-mktag - Creates a tag object with extra validation\n \n \n SYNOPSIS\n@@ -13,10 +13,19 @@ SYNOPSIS\n \n DESCRIPTION\n -----------\n-Reads a tag contents on standard input and creates a tag object\n-that can also be used to sign other objects.\n \n-The output is the new tag's <object> identifier.\n+Reads a tag contents on standard input and creates a tag object. The\n+output is the new tag's <object> identifier.\n+\n+This command is mostly equivalent to linkgit:git-hash-object[1]\n+invoked with `-t tag -w --stdin`. I.e. both of these will create and\n+write a tag found in `my-tag`:\n+\n+    git mktag <my-tag\n+    git hash-object -t tag -w --stdin <my-tag\n+\n+The difference is that mktag will die before writing the tag if the\n+tag doesn't pass a sanity check.\n \n Tag Format\n ----------\n@@ -34,6 +43,10 @@ exists, is separated by a blank line from the header.  The\n message part may contain a signature that Git itself doesn't\n care about, but that can be verified with gpg.\n \n+SEE ALSO\n+--------\n+linkgit:git-hash-object[1],\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412881","messageId":"20201223013606.7972-6-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 05/20] mktag tests: remove needless SHA-1 hardcoding","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:50Z","receivedAt":"2020-12-23T01:38:31Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the tests amended in acb49d1cc8b (t3800: make hash-size\nindependent, 2019-08-18) even more to make them independent of either\nSHA-1 or SHA-256.\n\nSome of these tests were failing for the wrong reasons. The first one\nbeing modified here would fail because the line starts with \"xxxxxx\"\ninstead of \"object\", the rest of the line doesn't matter.\n\nLet's just put a valid hash on the rest of the line anyway to narrow\nthe test down for just the s/object/xxxxxx/ case.\n\nThe second one being modified here would fail under\nGIT_TEST_DEFAULT_HASH=sha256 because <some sha-1 length garbage> is an\ninvalid SHA-256, but we should really be testing <some sha-256 length\ngarbage> when under SHA-256.\n\nThis doesn't really matter since we should be able to trust other\nparts of the code to validate things in the 0-9a-f range, but let's\nkeep it for good measure.\n\nThere's a later test which tests an invalid SHA which looks like a\nvalid one, to stress the \"We refuse to tag something we can't\nverify[...]\" logic in mktag.c.\n\nBut here we're testing for a SHA-length string which contains\ncharacters outside of the /[0-9a-f]/i set.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 0e411e3c45..a22a0954d5 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -43,7 +43,7 @@ check_verify_failure 'Tag object length check' \\\n #  2. object line label check\n \n cat >tag.sig <<EOF\n-xxxxxx 139e9b33986b1c2670fff52c5067603117b3e895\n+xxxxxx $head\n type tag\n tag mytag\n tagger . <> 0 +0000\n@@ -53,10 +53,10 @@ EOF\n check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n \n ############################################################\n-#  3. object line SHA1 check\n+#  3. object line hash check\n \n cat >tag.sig <<EOF\n-object zz9e9b33986b1c2670fff52c5067603117b3e895\n+object $(echo $head | tr 0-9a-f z)\n type tag\n tag mytag\n tagger . <> 0 +0000\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412882","messageId":"20201223013606.7972-5-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 04/20] mktag tests: don't needlessly use a subshell","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:49Z","receivedAt":"2020-12-23T01:38:32Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The use of a subshell dates back to e9b20943b77 (t/t3800: do not use a\ntemporary file to hold expected result., 2008-01-04). It's not needed\nanymore, if it ever was.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex d696aa4e52..0e411e3c45 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -14,7 +14,7 @@ test_description='git mktag: tag object verify test'\n check_verify_failure () {\n \texpect=\"$2\"\n \ttest_expect_success \"$1\" '\n-\t\t( test_must_fail git mktag <tag.sig 2>message ) &&\n+\t\ttest_must_fail git mktag <tag.sig 2>message &&\n \t\tgrep \"$expect\" message\n \t'\n }\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412883","messageId":"20201223013606.7972-17-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 16/20] mktag: allow turning off fsck.extraHeaderEntry","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:36:01Z","receivedAt":"2020-12-23T01:39:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In earlier commits mktag learned to use the fsck machinery, at which\npoint we needed to add fsck.extraHeaderEntry so it could be as strict\nabout extra headers as it's been ever since it was implemented.\n\nBut it's not nice to need to switch away from \"mktag\" to \"hash-object\"\n+ manual \"fsck\" just because you'd like to have an extra header. So\nlet's support turning it off by getting \"fsck.*\" variables from the\nconfig.\n\nPedantically speaking it's still not possible to make \"mktag\" behave\njust like \"hash-object -t tag\" does, since we're unconditionally going\nto check the referenced object in verify_object_in_tag(), which is our\nown check, and not one that exists in fsck.c.\n\nBut the spirit of \"this works like fsck\" is preserved, in that if you\ncreated such a tag with \"hash-object\" and did a full \"fsck\" on the\nrepository it would also error out about that invalid object, it just\nwouldn't emit the same message as fsck does.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt |  5 ++++-\n builtin/mktag.c             | 11 ++++++++++-\n t/t3800-mktag.sh            | 14 ++++++++++++++\n 3 files changed, 28 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex 03cd63d9f6..d04657b219 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -32,7 +32,10 @@ would run by default in that all `fsck.<msg-id>` messages are promoted\n from warnings to errors (so e.g. a missing \"tagger\" line is an error).\n \n Extra headers in the object are also an error under mktag, but ignored\n-by linkgit:git-fsck[1]\n+by linkgit:git-fsck[1]. This extra check can be turned off by setting\n+the appropriate `fsck.<msg-id>` varible:\n+\n+    git -c fsck.extraHeaderEntry=ignore mktag <my-tag-with-headers\n \n Tag Format\n ----------\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 4dd35bc79e..373926d7e0 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -3,6 +3,14 @@\n #include \"replace-object.h\"\n #include \"object-store.h\"\n #include \"fsck.h\"\n+#include \"config.h\"\n+\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n+\n+static int mktag_config(const char *var, const char *value, void *cb)\n+{\n+\treturn fsck_config_internal(var, value, cb, &fsck_options);\n+}\n \n static int mktag_fsck_error_func(struct fsck_options *o,\n \t\t\t\t const struct object_id *oid,\n@@ -53,7 +61,6 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n int cmd_mktag(int argc, const char **argv, const char *prefix)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n-\tstruct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \tstruct object_id tagged_oid;\n \tint tagged_type;\n \tstruct object_id result;\n@@ -66,6 +73,8 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \n \tfsck_options.error_func = mktag_fsck_error_func;\n \tfsck_set_msg_type(&fsck_options, \"extraheaderentry\", \"warn\");\n+\t/* config might set fsck.extraHeaderEntry=* again */\n+\tgit_config(mktag_config, NULL);\n \tif (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,\n \t\t\t\t&tagged_oid, &tagged_type))\n \t\tdie(\"tag on stdin did not pass our strict fsck check\");\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex d20adf0544..1019b42378 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -377,6 +377,20 @@ EOF\n check_verify_failure 'detect invalid header entry' \\\n \t'^error:.* extraHeaderEntry:'\n \n+test_expect_success 'invalid header entry config & fsck' '\n+\ttest_must_fail git mktag <tag.sig &&\n+\ttest_must_fail git -c fsck.extraHeaderEntry=error mktag <tag.sig &&\n+\ttest_must_fail git -c fsck.extraHeaderEntry=warn mktag <tag.sig &&\n+\tgit -c fsck.extraHeaderEntry=ignore mktag <tag.sig &&\n+\tgit fsck &&\n+\tenv GIT_TEST_GETTEXT_POISON=false \\\n+\t\tgit -c fsck.extraHeaderEntry=warn fsck 2>err &&\n+\tgrep \"warning .*extraHeaderEntry:\" err &&\n+\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n+\t\tgit -c fsck.extraHeaderEntry=error 2>err fsck &&\n+\tgrep \"error .* extraHeaderEntry:\" err\n+'\n+\n cat >tag.sig <<EOF\n object $head\n type commit\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412884","messageId":"20201223013606.7972-15-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 14/20] mktag: use fsck instead of custom verify_tag()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:59Z","receivedAt":"2020-12-23T01:39:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the validation logic in \"mktag\" to use fsck's fsck_tag()\ninstead of its own custom parser. Curiously the logic for both dates\nback to the same commit[1]. Let's unify them so we're not maintaining\ntwo sets functions to verify that a tag is OK.\n\nThe behavior of fsck_tag() and the old \"mktag\" code being removed here\nis different in few aspects.\n\nI think it makes sense to remove some of those checks, namely:\n\n A. fsck only cares that the timezone matches [-+][0-9]{4}. The mktag\n    code disallowed values larger than 1400.\n\n    Yes there's currently no timezone with a greater offset[2], but\n    since we allow any number of non-offical timezones (e.g. +1234)\n    passing this through seems fine. Git also won't break in the\n    future if e.g. French Polynesia decides it needs to outdo the Line\n    Islands when it comes to timezone extravagance.\n\n B. fsck allows missing author names such as \"tagger <email>\", mktag\n    wouldn't, but would allow e.g. \"tagger [2 spaces] <email>\" (but\n    not \"tagger [1 space] <email>\"). Now we allow all of these.\n\n C. Like B, but \"mktag\" disallowed spaces in the <email> part, fsck\n    allows it.\n\nIn some ways fsck_tag() is stricter than \"mktag\" was, namely:\n\n D. fsck disallows zero-padded dates, but mktag didn't care. So\n    e.g. the timestamp \"0000000000 +0000\" produces an error now. A\n    test in \"t1006-cat-file.sh\" relied on this, it's been changed to\n    use \"hash-object\" (without fsck) instead.\n\nThere was one check I deemed worth keeping by porting it over to\nfsck_tag():\n\n E. \"mktag\" did not allow any custom headers, and by extension (as an\n    empty commit is allowed) also forbade an extra stray trailing\n    newline after the headers it knew about.\n\n    Add a new check in the \"ignore\" category to fsck and use it. This\n    somewhat abuses the facility added in efaba7cc77f (fsck:\n    optionally ignore specific fsck issues completely, 2015-06-22).\n\n    This is somewhat of hack, but probably the least invasive change\n    we can make here. The fsck command will shuffle these categories\n    around, e.g. under --strict the \"info\" becomes a \"warn\" and \"warn\"\n    becomes \"error\". Existing users of fsck's (and others,\n    e.g. index-pack) --strict option rely on this.\n\n    So we need to put something into a category that'll be ignored by\n    all existing users of the API. Pretending that\n    fsck.extraHeaderEntry=error (\"ignore\" by default) was set serves\n    to do this for us.\n\n1. ec4465adb38 (Add \"tag\" objects that can be used to sign other\n   objects., 2005-04-25)\n\n2. https://en.wikipedia.org/wiki/List_of_UTC_time_offsets\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt |   9 +-\n builtin/mktag.c             | 196 +++++++++---------------------------\n fsck.c                      |  32 +++++-\n fsck.h                      |   9 ++\n t/t1006-cat-file.sh         |   2 +-\n t/t3800-mktag.sh            |  63 ++++++------\n 6 files changed, 127 insertions(+), 184 deletions(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex adc63f6d4e..03cd63d9f6 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -25,7 +25,14 @@ write a tag found in `my-tag`:\n     git hash-object -t tag -w --stdin <my-tag\n \n The difference is that mktag will die before writing the tag if the\n-tag doesn't pass a sanity check.\n+tag doesn't pass a linkgit:git-fsck[1] check.\n+\n+The \"fsck\" check done mktag is stricter than what linkgit:git-fsck[1]\n+would run by default in that all `fsck.<msg-id>` messages are promoted\n+from warnings to errors (so e.g. a missing \"tagger\" line is an error).\n+\n+Extra headers in the object are also an error under mktag, but ignored\n+by linkgit:git-fsck[1]\n \n Tag Format\n ----------\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex d89a3c201d..4dd35bc79e 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -2,160 +2,60 @@\n #include \"tag.h\"\n #include \"replace-object.h\"\n #include \"object-store.h\"\n+#include \"fsck.h\"\n \n-/*\n- * A signature file has a very simple fixed format: four lines\n- * of \"object <sha1>\" + \"type <typename>\" + \"tag <tagname>\" +\n- * \"tagger <committer>\", followed by a blank line, a free-form tag\n- * message and a signature block that git itself doesn't care about,\n- * but that can be verified with gpg or similar.\n- *\n- * The first four lines are guaranteed to be at least 83 bytes:\n- * \"object <sha1>\\n\" is 48 bytes, \"type tag\\n\" at 9 bytes is the\n- * shortest possible type-line, \"tag .\\n\" at 6 bytes is the shortest\n- * single-character-tag line, and \"tagger . <> 0 +0000\\n\" at 20 bytes is\n- * the shortest possible tagger-line.\n- */\n-\n-/*\n- * We refuse to tag something we can't verify. Just because.\n- */\n-static int verify_object(const struct object_id *oid, const char *expected_type)\n+static int mktag_fsck_error_func(struct fsck_options *o,\n+\t\t\t\t const struct object_id *oid,\n+\t\t\t\t enum object_type object_type,\n+\t\t\t\t int msg_type, const char *message)\n {\n-\tint ret = -1;\n-\tenum object_type type;\n-\tunsigned long size;\n-\tvoid *buffer = read_object_file(oid, &type, &size);\n-\tconst struct object_id *repl = lookup_replace_object(the_repository, oid);\n-\n-\tif (buffer) {\n-\t\tif (type == type_from_string(expected_type)) {\n-\t\t\tret = check_object_signature(the_repository, repl,\n-\t\t\t\t\t\t     buffer, size,\n-\t\t\t\t\t\t     expected_type);\n-\t\t}\n-\t\tfree(buffer);\n+\tswitch (msg_type) {\n+\tcase FSCK_WARN:\n+\tcase FSCK_ERROR:\n+\t\t/*\n+\t\t * We treat both warnings and errors as errors, things\n+\t\t * like missing \"tagger\" lines are \"only\" warnings\n+\t\t * under fsck, we've always considered them an error.\n+\t\t */\n+\t\tfprintf_ln(stderr, \"error: tag input does not pass fsck: %s\", message);\n+\t\treturn 1;\n+\tdefault:\n+\t\tBUG(\"%d (FSCK_IGNORE?) should never trigger this callback\",\n+\t\t    msg_type);\n \t}\n-\treturn ret;\n }\n \n-static int verify_tag(char *buffer, unsigned long size)\n+static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n {\n-\tint typelen;\n-\tchar type[20];\n-\tstruct object_id oid;\n-\tconst char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;\n-\tsize_t len;\n-\n-\tif (size < 84)\n-\t\treturn error(\"wanna fool me ? you obviously got the size wrong !\");\n-\n-\tbuffer[size] = 0;\n-\n-\t/* Verify object line */\n-\tobject = buffer;\n-\tif (memcmp(object, \"object \", 7))\n-\t\treturn error(\"char%d: does not start with \\\"object \\\"\", 0);\n-\n-\tif (parse_oid_hex(object + 7, &oid, &p))\n-\t\treturn error(\"char%d: could not get SHA1 hash\", 7);\n-\n-\t/* Verify type line */\n-\ttype_line = p + 1;\n-\tif (memcmp(type_line - 1, \"\\ntype \", 6))\n-\t\treturn error(\"char%d: could not find \\\"\\\\ntype \\\"\", 47);\n-\n-\t/* Verify tag-line */\n-\ttag_line = strchr(type_line, '\\n');\n-\tif (!tag_line)\n-\t\treturn error(\"char%\"PRIuMAX\": could not find next \\\"\\\\n\\\"\",\n-\t\t\t\t(uintmax_t) (type_line - buffer));\n-\ttag_line++;\n-\tif (memcmp(tag_line, \"tag \", 4) || tag_line[4] == '\\n')\n-\t\treturn error(\"char%\"PRIuMAX\": no \\\"tag \\\" found\",\n-\t\t\t\t(uintmax_t) (tag_line - buffer));\n-\n-\t/* Get the actual type */\n-\ttypelen = tag_line - type_line - strlen(\"type \\n\");\n-\tif (typelen >= sizeof(type))\n-\t\treturn error(\"char%\"PRIuMAX\": type too long\",\n-\t\t\t\t(uintmax_t) (type_line+5 - buffer));\n-\n-\tmemcpy(type, type_line+5, typelen);\n-\ttype[typelen] = 0;\n-\n-\t/* Verify that the object matches */\n-\tif (verify_object(&oid, type))\n-\t\treturn error(\"char%d: could not verify object %s\", 7, oid_to_hex(&oid));\n-\n-\t/* Verify the tag-name: we don't allow control characters or spaces in it */\n-\ttag_line += 4;\n-\tfor (;;) {\n-\t\tunsigned char c = *tag_line++;\n-\t\tif (c == '\\n')\n-\t\t\tbreak;\n-\t\tif (c > ' ')\n-\t\t\tcontinue;\n-\t\treturn error(\"char%\"PRIuMAX\": could not verify tag name\",\n-\t\t\t\t(uintmax_t) (tag_line - buffer));\n-\t}\n-\n-\t/* Verify the tagger line */\n-\ttagger_line = tag_line;\n-\n-\tif (memcmp(tagger_line, \"tagger \", 7))\n-\t\treturn error(\"char%\"PRIuMAX\": could not find \\\"tagger \\\"\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/*\n-\t * Check for correct form for name and email\n-\t * i.e. \" <\" followed by \"> \" on _this_ line\n-\t * No angle brackets within the name or email address fields.\n-\t * No spaces within the email address field.\n-\t */\n-\ttagger_line += 7;\n-\tif (!(lb = strstr(tagger_line, \" <\")) || !(rb = strstr(lb+2, \"> \")) ||\n-\t\tstrpbrk(tagger_line, \"<>\\n\") != lb+1 ||\n-\t\tstrpbrk(lb+2, \"><\\n \") != rb)\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tagger field\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/* Check for author name, at least one character, space is acceptable */\n-\tif (lb == tagger_line)\n-\t\treturn error(\"char%\"PRIuMAX\": missing tagger name\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/* timestamp, 1 or more digits followed by space */\n-\ttagger_line = rb + 2;\n-\tif (!(len = strspn(tagger_line, \"0123456789\")))\n-\t\treturn error(\"char%\"PRIuMAX\": missing tag timestamp\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line += len;\n-\tif (*tagger_line != ' ')\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tag timestamp\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line++;\n-\n-\t/* timezone, 5 digits [+-]hhmm, max. 1400 */\n-\tif (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&\n-\t      strspn(tagger_line+1, \"0123456789\") == 4 &&\n-\t      tagger_line[5] == '\\n' && atoi(tagger_line+1) <= 1400))\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tag timezone\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line += 6;\n-\n-\t/* Verify the blank line separating the header from the body */\n-\tif (*tagger_line != '\\n')\n-\t\treturn error(\"char%\"PRIuMAX\": trailing garbage in tag header\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n+\tint ret;\n+\tenum object_type type;\n+\tunsigned long size;\n+\tvoid *buffer;\n+\tconst struct object_id *repl;\n+\n+\tbuffer = read_object_file(tagged_oid, &type, &size);\n+\tif (!buffer)\n+\t\tdie(\"could not read tagged object '%s'\",\n+\t\t    oid_to_hex(tagged_oid));\n+\tif (type != *tagged_type)\n+\t\tdie(\"object '%s' tagged as '%s', but is a '%s' type\",\n+\t\t    oid_to_hex(tagged_oid),\n+\t\t    type_name(*tagged_type), type_name(type));\n+\n+\trepl = lookup_replace_object(the_repository, tagged_oid);\n+\tret = check_object_signature(the_repository, repl,\n+\t\t\t\t     buffer, size, type_name(*tagged_type));\n+\tfree(buffer);\n \n-\t/* The actual stuff afterwards we don't care about.. */\n-\treturn 0;\n+\treturn ret;\n }\n \n int cmd_mktag(int argc, const char **argv, const char *prefix)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n+\tstruct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n+\tstruct object_id tagged_oid;\n+\tint tagged_type;\n \tstruct object_id result;\n \n \tif (argc != 1)\n@@ -164,10 +64,14 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (strbuf_read(&buf, 0, 0) < 0)\n \t\tdie_errno(\"could not read from stdin\");\n \n-\t/* Verify it for some basic sanity: it needs to start with\n-\t   \"object <sha1>\\ntype\\ntagger \" */\n-\tif (verify_tag(buf.buf, buf.len) < 0)\n-\t\tdie(\"invalid tag signature file\");\n+\tfsck_options.error_func = mktag_fsck_error_func;\n+\tfsck_set_msg_type(&fsck_options, \"extraheaderentry\", \"warn\");\n+\tif (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,\n+\t\t\t\t&tagged_oid, &tagged_type))\n+\t\tdie(\"tag on stdin did not pass our strict fsck check\");\n+\n+\tif (verify_object_in_tag(&tagged_oid, &tagged_type))\n+\t\tdie(\"tag on stdin did not refer to a valid object\");\n \n \tif (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)\n \t\tdie(\"unable to write tag file\");\ndiff --git a/fsck.c b/fsck.c\nindex f82e2fe9e3..bed5e20e03 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -80,7 +80,9 @@ static struct oidset gitmodules_done = OIDSET_INIT;\n \t/* infos (reported as warnings, but ignored by default) */ \\\n \tFUNC(GITMODULES_PARSE, INFO) \\\n \tFUNC(BAD_TAG_NAME, INFO) \\\n-\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n+\tFUNC(MISSING_TAGGER_ENTRY, INFO) \\\n+\t/* ignored (elevated when requested) */ \\\n+\tFUNC(EXTRA_HEADER_ENTRY, IGNORE)\n \n #define MSG_ID(id, msg_type) FSCK_MSG_##id,\n enum fsck_msg_id {\n@@ -911,6 +913,16 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \t\t    unsigned long size, struct fsck_options *options)\n {\n \tstruct object_id tagged_oid;\n+\tint tagged_type;\n+\treturn fsck_tag_standalone(oid, buffer, size, options, &tagged_oid,\n+\t\t\t\t   &tagged_type);\n+}\n+\n+int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n+\t\t\tunsigned long size, struct fsck_options *options,\n+\t\t\tstruct object_id *tagged_oid,\n+\t\t\tint *tagged_type)\n+{\n \tint ret = 0;\n \tchar *eol;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -924,7 +936,7 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n-\tif (parse_oid_hex(buffer, &tagged_oid, &p) || *p != '\\n') {\n+\tif (parse_oid_hex(buffer, tagged_oid, &p) || *p != '\\n') {\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n \t\tif (ret)\n \t\t\tgoto done;\n@@ -940,7 +952,8 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n-\tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n+\t*tagged_type = type_from_string_gently(buffer, eol - buffer, 1);\n+\tif (*tagged_type < 0)\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_TYPE, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n@@ -975,6 +988,19 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \telse\n \t\tret = fsck_ident(&buffer, oid, OBJ_TAG, options);\n \n+\tif (!starts_with(buffer, \"\\n\")) {\n+\t\t/*\n+\t\t * The verify_headers() check will allow\n+\t\t * e.g. \"[...]tagger <tagger>\\nsome\n+\t\t * garbage\\n\\nmessage\" to pass, thinking \"some\n+\t\t * garbage\" could be a custom header. E.g. \"mktag\"\n+\t\t * doesn't want any unknown headers.\n+\t\t */\n+\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_EXTRA_HEADER_ENTRY, \"invalid format - extra header(s) after 'tagger'\");\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n+\n done:\n \tstrbuf_release(&sb);\n \treturn ret;\ndiff --git a/fsck.h b/fsck.h\nindex 69cf715e79..29ee4c45e8 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -62,6 +62,15 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options);\n int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options);\n \n+/*\n+ * fsck a tag, and pass info about it back to the caller. This is\n+ * exposed fsck_object() internals for git-mktag(1).\n+ */\n+int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n+\t\t\tunsigned long size, struct fsck_options *options,\n+\t\t\tstruct object_id *tagged_oid,\n+\t\t\tint *tag_type);\n+\n /*\n  * Some fsck checks are context-dependent, and may end up queued; run this\n  * after completing all fsck_object() calls in order to resolve any remaining\ndiff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh\nindex 2f501d2dc9..5d2dc99b74 100755\n--- a/t/t1006-cat-file.sh\n+++ b/t/t1006-cat-file.sh\n@@ -166,7 +166,7 @@ tag_content=\"$tag_header_without_timestamp 0000000000 +0000\n \n $tag_description\"\n \n-tag_sha1=$(echo_without_newline \"$tag_content\" | git mktag)\n+tag_sha1=$(echo_without_newline \"$tag_content\" | git hash-object -t tag --stdin -w)\n tag_size=$(strlen \"$tag_content\")\n \n run_tests 'tag' $tag_sha1 $tag_size \"$tag_content\" \"$tag_content\" 1\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex c6826762d9..d20adf0544 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -47,7 +47,7 @@ too short for a tag\n EOF\n \n check_verify_failure 'Tag object length check' \\\n-\t'^error: .*size wrong.*$'\n+\t'^error:.* missingObject:'\n \n ############################################################\n #  2. object line label check\n@@ -60,7 +60,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n+check_verify_failure '\"object\" line label check' '^error:.* missingObject:'\n \n ############################################################\n #  3. object line hash check\n@@ -73,7 +73,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"object\" line SHA1 check' '^error: char7: .*SHA1 hash$'\n+check_verify_failure '\"object\" line check' '^error:.* badObjectSha1:'\n \n ############################################################\n #  4. type line label check\n@@ -86,7 +86,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n+check_verify_failure '\"type\" line label check' '^error:.* missingTypeEntry:'\n \n ############################################################\n #  5. type line eol check\n@@ -94,7 +94,7 @@ check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n echo \"object $head\" >tag.sig\n printf \"type tagsssssssssssssssssssssssssssssss\" >>tag.sig\n \n-check_verify_failure '\"type\" line eol check' '^error: char.*: .*\"\\\\n\"$'\n+check_verify_failure '\"type\" line eol check' '^error:.* unterminatedHeader:'\n \n ############################################################\n #  6. tag line label check #1\n@@ -108,7 +108,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure '\"tag\" line label check #1' \\\n-\t'^error: char.*: no \"tag \" found$'\n+\t'^error:.* missingTagEntry:'\n \n ############################################################\n #  7. tag line label check #2\n@@ -120,7 +120,7 @@ tag\n EOF\n \n check_verify_failure '\"tag\" line label check #2' \\\n-\t'^error: char.*: no \"tag \" found$'\n+\t'^error:.* badType:'\n \n ############################################################\n #  8. type line type-name length check\n@@ -132,7 +132,7 @@ tag mytag\n EOF\n \n check_verify_failure '\"type\" line type-name length check' \\\n-\t'^error: char.*: type too long$'\n+\t'^error:.* badType:'\n \n ############################################################\n #  9. verify object (hash/type) check\n@@ -146,7 +146,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (hash/type) check -- correct type, nonexisting object' \\\n-\t'^error: char7: could not verify object.*$'\n+\t'^fatal: could not read tagged object'\n \n cat >tag.sig <<EOF\n object $head\n@@ -157,7 +157,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n-\t'^fatal: invalid object type'\n+\t'^error:.* badType:'\n \n cat >tag.sig <<EOF\n object $(test_oid deadbeef)\n@@ -168,7 +168,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n-\t'^error: char7: could not verify object.*$'\n+\t'^error:.* badType:'\n \n cat >tag.sig <<EOF\n object $head\n@@ -179,7 +179,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n-\t'^error: char7: could not verify object'\n+\t'^fatal: object.*tagged as.*tree.*but is.*commit'\n \n ############################################################\n # 10. verify tag-name check\n@@ -193,7 +193,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify tag-name check' \\\n-\t'^error: char.*: could not verify tag name$'\n+\t'^error:.* badTagName:'\n \n ############################################################\n # 11. tagger line label check #1\n@@ -207,7 +207,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #1' \\\n-\t'^error: char.*: could not find \"tagger \"$'\n+\t'^error:.* missingTaggerEntry:'\n \n ############################################################\n # 12. tagger line label check #2\n@@ -222,10 +222,10 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #2' \\\n-\t'^error: char.*: could not find \"tagger \"$'\n+\t'^error:.* missingTaggerEntry:'\n \n ############################################################\n-# 13. disallow missing tag author name\n+# 13. allow missing tag author name like fsck\n \n cat >tag.sig <<EOF\n object $head\n@@ -236,8 +236,7 @@ tagger  <> 0 +0000\n This is filler\n EOF\n \n-check_verify_failure 'disallow missing tag author name' \\\n-\t'^error: char.*: missing tagger name$'\n+test_expect_mktag_success 'allow missing tag author name'\n \n ############################################################\n # 14. disallow missing tag author name\n@@ -252,7 +251,7 @@ tagger T A Gger <\n EOF\n \n check_verify_failure 'disallow malformed tagger' \\\n-\t'^error: char.*: malformed tagger field$'\n+\t'^error:.* badEmail:'\n \n ############################################################\n # 15. allow empty tag email\n@@ -268,7 +267,7 @@ EOF\n test_expect_mktag_success 'allow empty tag email'\n \n ############################################################\n-# 16. disallow spaces in tag email\n+# 16. allow spaces in tag email like fsck\n \n cat >tag.sig <<EOF\n object $head\n@@ -278,8 +277,7 @@ tagger T A Gger <tag ger@example.com> 0 +0000\n \n EOF\n \n-check_verify_failure 'disallow spaces in tag email' \\\n-\t'^error: char.*: malformed tagger field$'\n+test_expect_mktag_success 'allow spaces in tag email like fsck'\n \n ############################################################\n # 17. disallow missing tag timestamp\n@@ -293,7 +291,7 @@ tagger T A Gger <tagger@example.com>__\n EOF\n \n check_verify_failure 'disallow missing tag timestamp' \\\n-\t'^error: char.*: missing tag timestamp$'\n+\t'^error:.* badDate:'\n \n ############################################################\n # 18. detect invalid tag timestamp1\n@@ -307,7 +305,7 @@ tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008\n EOF\n \n check_verify_failure 'detect invalid tag timestamp1' \\\n-\t'^error: char.*: missing tag timestamp$'\n+\t'^error:.* badDate:'\n \n ############################################################\n # 19. detect invalid tag timestamp2\n@@ -321,7 +319,7 @@ tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500\n EOF\n \n check_verify_failure 'detect invalid tag timestamp2' \\\n-\t'^error: char.*: malformed tag timestamp$'\n+\t'^error:.* badDate:'\n \n ############################################################\n # 20. detect invalid tag timezone1\n@@ -335,7 +333,7 @@ tagger T A Gger <tagger@example.com> 1206478233 GMT\n EOF\n \n check_verify_failure 'detect invalid tag timezone1' \\\n-\t'^error: char.*: malformed tag timezone$'\n+\t'^error:.* badTimezone:'\n \n ############################################################\n # 21. detect invalid tag timezone2\n@@ -349,10 +347,10 @@ tagger T A Gger <tagger@example.com> 1206478233 +  30\n EOF\n \n check_verify_failure 'detect invalid tag timezone2' \\\n-\t'^error: char.*: malformed tag timezone$'\n+\t'^error:.* badTimezone:'\n \n ############################################################\n-# 22. detect invalid tag timezone3\n+# 22. allow invalid tag timezone3 (the maximum is -1200/+1400)\n \n cat >tag.sig <<EOF\n object $head\n@@ -362,8 +360,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -1430\n \n EOF\n \n-check_verify_failure 'detect invalid tag timezone3' \\\n-\t'^error: char.*: malformed tag timezone$'\n+test_expect_mktag_success 'allow invalid tag timezone'\n \n ############################################################\n # 23. detect invalid header entry\n@@ -378,7 +375,7 @@ this line should not be here\n EOF\n \n check_verify_failure 'detect invalid header entry' \\\n-\t'^error: char.*: trailing garbage in tag header$'\n+\t'^error:.* extraHeaderEntry:'\n \n cat >tag.sig <<EOF\n object $head\n@@ -412,7 +409,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500$space\n EOF\n \n check_verify_failure 'extra whitespace at end of headers' \\\n-\t'^error: char.*: malformed tag timezone$'\n+\t'^error:.* badTimezone:'\n \n cat >tag.sig <<EOF\n object $head\n@@ -422,7 +419,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500\n EOF\n \n check_verify_failure 'disallow no header / body newline separator' \\\n-\t'^error: char.*: trailing garbage in tag header$'\n+\t'^error:.* extraHeaderEntry:'\n \n ############################################################\n # 24. create valid tag\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412885","messageId":"20201223013606.7972-21-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 20/20] mktag: add a --no-strict option","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:36:05Z","receivedAt":"2020-12-23T01:39:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that mktag has been migrated to use the fsck machinery to check\nits input, it makes sense to teach it to run in the equivalent of \"git\nfsck\"'s default mode, instead of hardcoding \"git fsck --strict\". Let's\ndo that and support the \"--no-strict\" option.\n\nSince this is a new option we don't need to cater to parse-option.c's\ndefault of automatically supporting --strict. So let's use\nPARSE_OPT_NONEG, using a new trivial helper macro.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt |  7 +++++++\n builtin/mktag.c             |  9 +++++++++\n parse-options.h             |  1 +\n t/t3800-mktag.sh            | 35 ++++++++++++++++++++++++-----------\n 4 files changed, 41 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex d04657b219..05e49b6787 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -11,6 +11,13 @@ SYNOPSIS\n [verse]\n 'git mktag'\n \n+OPTIONS\n+-------\n+\n+--no-strict::\n+\tBy default mktag turns on the equivalent of\n+\tlinkgit:git-fsck[1] `--strict` mode. This disables it.\n+\n DESCRIPTION\n -----------\n \ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 9b04b61c2b..a6a4612247 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -10,6 +10,7 @@ static char const * const builtin_mktag_usage[] = {\n \tN_(\"git mktag\"),\n \tNULL\n };\n+static int option_no_strict;\n \n static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \n@@ -25,6 +26,12 @@ static int mktag_fsck_error_func(struct fsck_options *o,\n {\n \tswitch (msg_type) {\n \tcase FSCK_WARN:\n+\t\tif (option_no_strict) {\n+\t\t\tfprintf_ln(stderr, _(\"warning: tag input does not pass fsck: %s\"), message);\n+\t\t\treturn 0;\n+\n+\t\t}\n+\t\t/* fallthrough */\n \tcase FSCK_ERROR:\n \t\t/*\n \t\t * We treat both warnings and errors as errors, things\n@@ -67,6 +74,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n int cmd_mktag(int argc, const char **argv, const char *prefix)\n {\n \tstatic struct option builtin_mktag_options[] = {\n+\t\tOPT_NO_BOOL(0, \"no-strict\", &option_no_strict,\n+\t\t\t    N_(\"don't do strict fsck checks\")),\n \t\tOPT_END(),\n \t};\n \tstruct strbuf buf = STRBUF_INIT;\ndiff --git a/parse-options.h b/parse-options.h\nindex 7030d8f3da..90d6a817d7 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -166,6 +166,7 @@ struct option {\n #define OPT_COUNTUP(s, l, v, h)     OPT_COUNTUP_F(s, l, v, h, 0)\n #define OPT_SET_INT(s, l, v, h, i)  OPT_SET_INT_F(s, l, v, h, i, 0)\n #define OPT_BOOL(s, l, v, h)        OPT_BOOL_F(s, l, v, h, 0)\n+#define OPT_NO_BOOL(s, l, v, h)     OPT_BOOL_F(s, l, v, h, PARSE_OPT_NONEG)\n #define OPT_HIDDEN_BOOL(s, l, v, h) { OPTION_SET_INT, (s), (l), (v), NULL, \\\n \t\t\t\t      (h), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1}\n #define OPT_CMDMODE(s, l, v, h, i)  { OPTION_SET_INT, (s), (l), (v), NULL, \\\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 2e8b718379..b436ae1e44 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -12,12 +12,16 @@ test_description='git mktag: tag object verify test'\n # given in the expect.pat file.\n \n check_verify_failure () {\n-\texpect=\"$2\"\n-\ttest_expect_success \"$1\" '\n+\ttest_expect_success \"$1\" \"\n \t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n \t\t\tgit mktag <tag.sig 2>message &&\n-\t\tgrep \"$expect\" message\n-\t'\n+\t\tgrep '$2' message &&\n+\t\tif test '$3' != '--no-strict'\n+\t\tthen\n+\t\t\ttest_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&\n+\t\t\tgrep '$2' message.no-strict\n+\t\tfi\n+\t\"\n }\n \n test_expect_mktag_success() {\n@@ -49,7 +53,8 @@ test_expect_success 'basic usage' '\n \tEOF\n \tgit mktag <tag.sig &&\n \tgit mktag --end-of-options <tag.sig &&\n-\ttest_expect_code 129 git mktag --unknown-option\n+\ttest_expect_code 129 git mktag --unknown-option &&\n+\ttest_expect_code 129 git mktag --strict\n '\n \n ############################################################\n@@ -60,7 +65,7 @@ too short for a tag\n EOF\n \n check_verify_failure 'Tag object length check' \\\n-\t'^error:.* missingObject:'\n+\t'^error:.* missingObject:' 'strict'\n \n ############################################################\n #  2. object line label check\n@@ -206,7 +211,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify tag-name check' \\\n-\t'^error:.* badTagName:'\n+\t'^error:.* badTagName:' '--no-strict'\n \n ############################################################\n # 11. tagger line label check #1\n@@ -220,7 +225,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #1' \\\n-\t'^error:.* missingTaggerEntry:'\n+\t'^error:.* missingTaggerEntry:' '--no-strict'\n \n ############################################################\n # 12. tagger line label check #2\n@@ -235,7 +240,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #2' \\\n-\t'^error:.* missingTaggerEntry:'\n+\t'^error:.* missingTaggerEntry:' '--no-strict'\n \n ############################################################\n # 13. allow missing tag author name like fsck\n@@ -264,7 +269,7 @@ tagger T A Gger <\n EOF\n \n check_verify_failure 'disallow malformed tagger' \\\n-\t'^error:.* badEmail:'\n+\t'^error:.* badEmail:' '--no-strict'\n \n ############################################################\n # 15. allow empty tag email\n@@ -388,13 +393,21 @@ this line should not be here\n EOF\n \n check_verify_failure 'detect invalid header entry' \\\n-\t'^error:.* extraHeaderEntry:'\n+\t'^error:.* extraHeaderEntry:' '--no-strict'\n \n test_expect_success 'invalid header entry config & fsck' '\n \ttest_must_fail git mktag <tag.sig &&\n+\tgit mktag --no-strict <tag.sig &&\n+\n \ttest_must_fail git -c fsck.extraHeaderEntry=error mktag <tag.sig &&\n+\ttest_must_fail git -c fsck.extraHeaderEntry=error mktag --no-strict <tag.sig &&\n+\n \ttest_must_fail git -c fsck.extraHeaderEntry=warn mktag <tag.sig &&\n+\tgit -c fsck.extraHeaderEntry=warn mktag --no-strict <tag.sig &&\n+\n \tgit -c fsck.extraHeaderEntry=ignore mktag <tag.sig &&\n+\tgit -c fsck.extraHeaderEntry=ignore mktag --no-strict <tag.sig &&\n+\n \tgit fsck &&\n \tenv GIT_TEST_GETTEXT_POISON=false \\\n \t\tgit -c fsck.extraHeaderEntry=warn fsck 2>err &&\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412886","messageId":"20201223013606.7972-19-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 18/20] mktag: convert to parse-options","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:36:03Z","receivedAt":"2020-12-23T01:39:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Convert the \"mktag\" command to use parse-options.h instead of its own\nad-hoc argc handling. This doesn't matter much in practice since it\ndoesn't support any options, but removes another special-case in our\ncodebase, and makes it easier to add options to it in the future.\n\nIt does marginally improve the situation for programs that want to\nexecute git commands in a consistent manner and e.g. always use\n--end-of-options. E.g. \"gitaly\" does that, and has a blacklist of\nbuilt-ins that don't support --end-of-options. This is one less\nspecial case for it and other similar programs to support.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c  | 14 ++++++++++++--\n t/t3800-mktag.sh | 12 ++++++++++++\n 2 files changed, 24 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 373926d7e0..18b8492f4d 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -1,10 +1,16 @@\n #include \"builtin.h\"\n+#include \"parse-options.h\"\n #include \"tag.h\"\n #include \"replace-object.h\"\n #include \"object-store.h\"\n #include \"fsck.h\"\n #include \"config.h\"\n \n+static char const * const builtin_mktag_usage[] = {\n+\tN_(\"git mktag\"),\n+\tNULL\n+};\n+\n static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \n static int mktag_config(const char *var, const char *value, void *cb)\n@@ -60,13 +66,17 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n \n int cmd_mktag(int argc, const char **argv, const char *prefix)\n {\n+\tstatic struct option builtin_mktag_options[] = {\n+\t\tOPT_END(),\n+\t};\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct object_id tagged_oid;\n \tint tagged_type;\n \tstruct object_id result;\n \n-\tif (argc != 1)\n-\t\tusage(\"git mktag\");\n+\targc = parse_options(argc, argv, NULL,\n+\t\t\t     builtin_mktag_options,\n+\t\t\t     builtin_mktag_usage, 0);\n \n \tif (strbuf_read(&buf, 0, 0) < 0)\n \t\tdie_errno(\"could not read from stdin\");\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 9383f7795b..177ab1682d 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -39,6 +39,18 @@ test_expect_success 'setup' '\n \thead=$(git rev-parse --verify HEAD)\n '\n \n+test_expect_success 'basic usage' '\n+\tcat >tag.sig <<-EOF &&\n+\tobject $head\n+\ttype commit\n+\ttag mytag\n+\ttagger T A Gger <tagger@example.com> 1206478233 -0500\n+\tEOF\n+\tgit mktag <tag.sig &&\n+\tgit mktag --end-of-options <tag.sig &&\n+\ttest_expect_code 129 git mktag --unknown-option\n+'\n+\n ############################################################\n #  1. length check\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412887","messageId":"20201223013606.7972-7-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 06/20] mktag tests: improve verify_object() test coverage","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:51Z","receivedAt":"2020-12-23T01:39:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The verify_object() function in \"mktag.c\" is tasked with ensuring that\nour tag refers to a valid object.\n\nThe existing test for this might fail because it was also testing that\n\"type taggg\" didn't refer to a valid object type (it should be \"type\ntag\"), or because we referred to a valid object but got the type\nwrong.\n\nLet's split these tests up, so we're testing all combinations of a\nnon-existing object and in invalid/wrong \"type\" lines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 37 +++++++++++++++++++++++++++++++++++--\n 1 file changed, 35 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex a22a0954d5..9ae1b0bb0a 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -125,19 +125,52 @@ check_verify_failure '\"type\" line type-name length check' \\\n \t'^error: char.*: type too long$'\n \n ############################################################\n-#  9. verify object (SHA1/type) check\n+#  9. verify object (hash/type) check\n \n cat >tag.sig <<EOF\n object $(test_oid deadbeef)\n+type tag\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+check_verify_failure 'verify object (hash/type) check -- correct type, nonexisting object' \\\n+\t'^error: char7: could not verify object.*$'\n+\n+cat >tag.sig <<EOF\n+object $head\n type tagggg\n tag mytag\n tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure 'verify object (SHA1/type) check' \\\n+check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n+\t'^fatal: invalid object type'\n+\n+cat >tag.sig <<EOF\n+object $(test_oid deadbeef)\n+type tagggg\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n \t'^error: char7: could not verify object.*$'\n \n+cat >tag.sig <<EOF\n+object $head\n+type tree\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n+\t'^error: char7: could not verify object'\n+\n ############################################################\n # 10. verify tag-name check\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412888","messageId":"20201223013606.7972-12-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 11/20] mktag: use default strbuf_read() hint","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:56Z","receivedAt":"2020-12-23T01:39:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the hardcoded hint of 2^12 to 0. The default strbuf hint is\nperfectly fine here, and the only reason we were hardcoding it is\nbecause it survived migration from a pre-strbuf fixed-sized buffer.\n\nSee fd17f5b5f77 (Replace all read_fd use with strbuf_read, and get rid\nof it., 2007-09-10) for that migration.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 4982d3a93e..ff7ac8e0e5 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -161,7 +161,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (argc != 1)\n \t\tusage(\"git mktag\");\n \n-\tif (strbuf_read(&buf, 0, 4096) < 0) {\n+\tif (strbuf_read(&buf, 0, 0) < 0) {\n \t\tdie_errno(\"could not read from stdin\");\n \t}\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412889","messageId":"20201223013606.7972-8-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 07/20] mktag tests: don't pipe to stderr needlessly","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:52Z","receivedAt":"2020-12-23T01:39:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove the piping of stderr to \"message\" in the valid tag test. This\npattern seems to have been copy/pasted from the failure case in\n446c6faec6 (New tests and en-passant modifications to mktag.,\n2006-07-29).\n\nNothing is piped to \"message\" here, and in the event of the test\nfailing it only serves to hide the error.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 9ae1b0bb0a..bbd148618e 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -257,7 +257,7 @@ EOF\n \n test_expect_success \\\n     'allow empty tag email' \\\n-    'git mktag <tag.sig >.git/refs/tags/mytag 2>message'\n+    'git mktag <tag.sig >.git/refs/tags/mytag'\n \n ############################################################\n # 16. disallow spaces in tag email\n@@ -385,7 +385,7 @@ EOF\n \n test_expect_success \\\n     'create valid tag' \\\n-    'git mktag <tag.sig >.git/refs/tags/mytag 2>message'\n+    'git mktag <tag.sig >.git/refs/tags/mytag'\n \n ############################################################\n # 25. check mytag\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412890","messageId":"20201223013606.7972-11-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 10/20] mktag tests: test \"hash-object\" compatibility","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:55Z","receivedAt":"2020-12-23T01:39:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change all the successful \"mktag\" tests to test that \"hash-object\"\nproduces the same hash for the input, and that fsck passes for\nboth.\n\nThis tests e.g. that \"mktag\" doesn't trim its input or otherwise munge\nit in a way that \"hash-object\" doesn't.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 26 +++++++++++++++-----------\n 1 file changed, 15 insertions(+), 11 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex f339321be8..c6826762d9 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -19,6 +19,16 @@ check_verify_failure () {\n \t'\n }\n \n+test_expect_mktag_success() {\n+\ttest_expect_success \"$1\" '\n+\t\tgit hash-object -t tag -w --stdin <tag.sig >expected &&\n+\t\tgit fsck --strict &&\n+\t\tgit mktag <tag.sig >hash &&\n+\t\tgit fsck --strict &&\n+\t\ttest_cmp expected hash\n+\t'\n+}\n+\n ###########################################################\n # first create a commit, so we have a valid object/type\n # for the tag.\n@@ -255,9 +265,7 @@ tagger T A Gger <> 0 +0000\n \n EOF\n \n-test_expect_success \\\n-    'allow empty tag email' \\\n-    'git mktag <tag.sig'\n+test_expect_mktag_success 'allow empty tag email'\n \n ############################################################\n # 16. disallow spaces in tag email\n@@ -382,9 +390,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500\n this line comes after an extra newline\n EOF\n \n-test_expect_success 'allow extra newlines at start of body' '\n-\tgit mktag <tag.sig\n-'\n+test_expect_mktag_success 'allow extra newlines at start of body'\n \n cat >tag.sig <<EOF\n object $head\n@@ -394,9 +400,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500\n \n EOF\n \n-test_expect_success 'allow extra newlines at end of headers' '\n-\tgit mktag <tag.sig\n-'\n+test_expect_mktag_success 'allow extra newlines at end of headers'\n \n space=' '\n cat >tag.sig <<EOF\n@@ -431,8 +435,8 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500\n \n EOF\n \n-test_expect_success 'create valid tag' '\n-\tgit mktag <tag.sig >hash &&\n+test_expect_mktag_success 'create valid tag object'\n+test_expect_success 'create valid tag name' '\n \tgit update-ref refs/tags/mytag $(cat hash) $(test_oid zero)\n '\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412891","messageId":"20201223013606.7972-14-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 13/20] mktag: use puts(str) instead of printf(\"%s\\n\", str)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:58Z","receivedAt":"2020-12-23T01:39:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This introduces no functional change, but refactors the print-out of\nthe hash at the end to do the same thing with less code.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 97ca5f28b1..d89a3c201d 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -173,6 +173,6 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \t\tdie(\"unable to write tag file\");\n \n \tstrbuf_release(&buf);\n-\tprintf(\"%s\\n\", oid_to_hex(&result));\n+\tputs(oid_to_hex(&result));\n \treturn 0;\n }\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412892","messageId":"20201223013606.7972-18-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 17/20] mktag: allow omitting the header/body \\n separator","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:36:02Z","receivedAt":"2020-12-23T01:39:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change mktag's acceptance rules to accept an empty body without an\nempty line after the header again. This fixes an ancient unintended\ndregression in \"mktag\".\n\nWhen \"mktag\" was introduced in ec4465adb3 (Add \"tag\" objects that can\nbe used to sign other objects., 2005-04-25) the input checks were much\nlooser. When it was documented it 6cfec03680 (mktag: minimally update\nthe description., 2007-06-10) it was clearly intended for this \\n to\nbe optional:\n\n    The message, when [it] exists, is separated by a blank line from\n    the header.\n\nBut then in e0aaf781f6 (mktag.c: improve verification of tagger field\nand tests, 2008-03-27) this was made an error, seemingly by\naccident. It was just a result of the general header checks, and all\nthe tests after that patch have a trailing empty line (but did not\nbefore).\n\nLet's allow this again, and tweak the test semantics changed in\ne0aaf781f6 to remove the redundant empty line. New tests added in\nprevious commits of mine already added an explicit test for allowing\nthe empty line between header and body.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n fsck.c           | 2 ++\n t/t3800-mktag.sh | 6 +++---\n 2 files changed, 5 insertions(+), 3 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 9067a290a2..29c1eaca4c 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -987,6 +987,8 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n \t}\n \telse\n \t\tret = fsck_ident(&buffer, oid, OBJ_TAG, options);\n+\tif (!*buffer)\n+\t\tgoto done;\n \n \tif (!starts_with(buffer, \"\\n\")) {\n \t\t/*\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 1019b42378..9383f7795b 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -432,8 +432,9 @@ tag mytag\n tagger T A Gger <tagger@example.com> 1206478233 -0500\n EOF\n \n-check_verify_failure 'disallow no header / body newline separator' \\\n-\t'^error:.* extraHeaderEntry:'\n+test_expect_success 'allow no header / body newline separator' '\n+\tgit mktag <tag.sig\n+'\n \n ############################################################\n # 24. create valid tag\n@@ -443,7 +444,6 @@ object $head\n type commit\n tag mytag\n tagger T A Gger <tagger@example.com> 1206478233 -0500\n-\n EOF\n \n test_expect_mktag_success 'create valid tag object'\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412893","messageId":"20201223013606.7972-13-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 12/20] mktag: remove redundant braces in one-line body \"if\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:57Z","receivedAt":"2020-12-23T01:39:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This minor stylistic churn is usually something we'd avoid, but if we\ndon't do this then the file after changes in subsequent commits will\nonly have this minor style inconsistency, so let's change this while\nwe're at it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex ff7ac8e0e5..97ca5f28b1 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -161,9 +161,8 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (argc != 1)\n \t\tusage(\"git mktag\");\n \n-\tif (strbuf_read(&buf, 0, 0) < 0) {\n+\tif (strbuf_read(&buf, 0, 0) < 0)\n \t\tdie_errno(\"could not read from stdin\");\n-\t}\n \n \t/* Verify it for some basic sanity: it needs to start with\n \t   \"object <sha1>\\ntype\\ntagger \" */\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412894","messageId":"20201223013606.7972-16-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 15/20] fsck: make fsck_config() re-usable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:36:00Z","receivedAt":"2020-12-23T01:39:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Move the fsck_config() function from builtin/fsck.c to fsck.[ch]. This\nallows for re-using it in other tools that expose fsck logic and want\nto support its configuration variables.\n\nA logical continuation of this change would be to use a common\nfunction for all of {fetch,receive}.fsck.* and fsck.*. See\n5d477a334a6 (fsck (receive-pack): allow demoting errors to warnings,\n2015-06-22) and my own 1362df0d413 (fetch: implement fetch.fsck.*,\n2018-07-27) for the relevant code.\n\nHowever, those routines want to not parse the fsck.skipList into OIDs,\nbut rather pass them along with the --strict option to another\nprocess. It would be possible to refactor that whole thing so we\nsupport e.g. a \"fetch.\" prefix, then just keep track of the skiplist\nas a filename instead of parsing it, and learn to spew that all out\nfrom our internal structures into something we can append to the\n--strict option.\n\nBut instead I'm planning to re-use this in \"mktag\", which'll just\nre-use these \"fsck.*\" variables as-is.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fsck.c | 20 +-------------------\n fsck.c         | 25 +++++++++++++++++++++++++\n fsck.h         |  7 +++++++\n 3 files changed, 33 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex fbf26cafcf..821e7798c7 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -73,25 +73,7 @@ static const char *printable_type(const struct object_id *oid,\n \n static int fsck_config(const char *var, const char *value, void *cb)\n {\n-\tif (strcmp(var, \"fsck.skiplist\") == 0) {\n-\t\tconst char *path;\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\n-\t\tif (git_config_pathname(&path, var, value))\n-\t\t\treturn 1;\n-\t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n-\t\tfree((char *)path);\n-\t\tfsck_set_msg_types(&fsck_obj_options, sb.buf);\n-\t\tstrbuf_release(&sb);\n-\t\treturn 0;\n-\t}\n-\n-\tif (skip_prefix(var, \"fsck.\", &var)) {\n-\t\tfsck_set_msg_type(&fsck_obj_options, var, value);\n-\t\treturn 0;\n-\t}\n-\n-\treturn git_default_config(var, value, cb);\n+\treturn fsck_config_internal(var, value, cb, &fsck_obj_options);\n }\n \n static int objerror(struct object *obj, const char *err)\ndiff --git a/fsck.c b/fsck.c\nindex bed5e20e03..9067a290a2 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -1310,3 +1310,28 @@ int fsck_finish(struct fsck_options *options)\n \toidset_clear(&gitmodules_done);\n \treturn ret;\n }\n+\n+int fsck_config_internal(const char *var, const char *value, void *cb,\n+\t\t\t struct fsck_options *options)\n+{\n+\tif (strcmp(var, \"fsck.skiplist\") == 0) {\n+\t\tconst char *path;\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tif (git_config_pathname(&path, var, value))\n+\t\t\treturn 1;\n+\t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n+\t\tfree((char *)path);\n+\t\tfsck_set_msg_types(options, sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\treturn 0;\n+\t}\n+\n+\tif (skip_prefix(var, \"fsck.\", &var)) {\n+\t\tfsck_set_msg_type(options, var, value);\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_default_config(var, value, cb);\n+}\n+\ndiff --git a/fsck.h b/fsck.h\nindex 29ee4c45e8..423c467feb 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -103,4 +103,11 @@ void fsck_put_object_name(struct fsck_options *options,\n const char *fsck_describe_object(struct fsck_options *options,\n \t\t\t\t const struct object_id *oid);\n \n+/*\n+ * git_config() callback for use by fsck-y tools that want to support\n+ * fsck.<msg> fsck.skipList etc.\n+ */\n+int fsck_config_internal(const char *var, const char *value, void *cb,\n+\t\t\t struct fsck_options *options);\n+\n #endif\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412895","messageId":"20201223013606.7972-10-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 09/20] mktag tests: stress test whitespace handling","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:54Z","receivedAt":"2020-12-23T01:39:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add tests for various whitespace edge cases around the header/body\nboundary, such as two newlines separating the header and body, a space\nat the end of the \"tagger\" line before the header etc.\n\nAdd a test showing that we insist that an empty body must be preceded\nby an empty line. This test for a long-standing regression which goes\nagainst the command's documented behavior. It'll be addressed in a\nfollow-up change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 48 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 48 insertions(+)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex b6dcdbebe6..f339321be8 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -372,6 +372,54 @@ EOF\n check_verify_failure 'detect invalid header entry' \\\n \t'^error: char.*: trailing garbage in tag header$'\n \n+cat >tag.sig <<EOF\n+object $head\n+type commit\n+tag mytag\n+tagger T A Gger <tagger@example.com> 1206478233 -0500\n+\n+\n+this line comes after an extra newline\n+EOF\n+\n+test_expect_success 'allow extra newlines at start of body' '\n+\tgit mktag <tag.sig\n+'\n+\n+cat >tag.sig <<EOF\n+object $head\n+type commit\n+tag mytag\n+tagger T A Gger <tagger@example.com> 1206478233 -0500\n+\n+EOF\n+\n+test_expect_success 'allow extra newlines at end of headers' '\n+\tgit mktag <tag.sig\n+'\n+\n+space=' '\n+cat >tag.sig <<EOF\n+object $head\n+type commit\n+tag mytag\n+tagger T A Gger <tagger@example.com> 1206478233 -0500$space\n+\n+EOF\n+\n+check_verify_failure 'extra whitespace at end of headers' \\\n+\t'^error: char.*: malformed tag timezone$'\n+\n+cat >tag.sig <<EOF\n+object $head\n+type commit\n+tag mytag\n+tagger T A Gger <tagger@example.com> 1206478233 -0500\n+EOF\n+\n+check_verify_failure 'disallow no header / body newline separator' \\\n+\t'^error: char.*: trailing garbage in tag header$'\n+\n ############################################################\n # 24. create valid tag\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412896","messageId":"20201223013606.7972-9-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 08/20] mktag tests: don't create \"mytag\" twice","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:35:53Z","receivedAt":"2020-12-23T01:39:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change a test added in e0aaf781f6 (mktag.c: improve verification of\ntagger field and tests, 2008-03-27) to not create \"mytag\", which\nshould only be created and verified at the end in an earlier test\nadded in 446c6faec6 (New tests and en-passant modifications to mktag.,\n2006-07-29).\n\nWhile we're at it let's prevent a similar logic error from creeping\ninto the test by asserting that \"mytag\" doesn't exist before we create\nit. Let's do this by moving the test to use \"update-ref\", instead of\nour own homebrew ad-hoc refstore update.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 17 +++++------------\n 1 file changed, 5 insertions(+), 12 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex bbd148618e..b6dcdbebe6 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -257,7 +257,7 @@ EOF\n \n test_expect_success \\\n     'allow empty tag email' \\\n-    'git mktag <tag.sig >.git/refs/tags/mytag'\n+    'git mktag <tag.sig'\n \n ############################################################\n # 16. disallow spaces in tag email\n@@ -383,16 +383,9 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500\n \n EOF\n \n-test_expect_success \\\n-    'create valid tag' \\\n-    'git mktag <tag.sig >.git/refs/tags/mytag'\n-\n-############################################################\n-# 25. check mytag\n-\n-test_expect_success \\\n-    'check mytag' \\\n-    'git tag -l | grep mytag'\n-\n+test_expect_success 'create valid tag' '\n+\tgit mktag <tag.sig >hash &&\n+\tgit update-ref refs/tags/mytag $(cat hash) $(test_oid zero)\n+'\n \n test_done\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412897","messageId":"20201223013606.7972-20-avarab@gmail.com","threadId":"51667","inReplyTo":"20201209200140.29425-1-avarab@gmail.com","subject":"[PATCH v4 19/20] mktag: mark strings for translation","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2020-12-23T01:36:04Z","receivedAt":"2020-12-23T01:39:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Mark the errors mktag might emit for translation. This is a plumbing\ncommand, but the errors it emits are intended to be human-readable.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c  | 16 ++++++++--------\n t/t3800-mktag.sh |  3 ++-\n 2 files changed, 10 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 18b8492f4d..9b04b61c2b 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -31,10 +31,10 @@ static int mktag_fsck_error_func(struct fsck_options *o,\n \t\t * like missing \"tagger\" lines are \"only\" warnings\n \t\t * under fsck, we've always considered them an error.\n \t\t */\n-\t\tfprintf_ln(stderr, \"error: tag input does not pass fsck: %s\", message);\n+\t\tfprintf_ln(stderr, _(\"error: tag input does not pass fsck: %s\"), message);\n \t\treturn 1;\n \tdefault:\n-\t\tBUG(\"%d (FSCK_IGNORE?) should never trigger this callback\",\n+\t\tBUG(_(\"%d (FSCK_IGNORE?) should never trigger this callback\"),\n \t\t    msg_type);\n \t}\n }\n@@ -49,10 +49,10 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n \n \tbuffer = read_object_file(tagged_oid, &type, &size);\n \tif (!buffer)\n-\t\tdie(\"could not read tagged object '%s'\",\n+\t\tdie(_(\"could not read tagged object '%s'\"),\n \t\t    oid_to_hex(tagged_oid));\n \tif (type != *tagged_type)\n-\t\tdie(\"object '%s' tagged as '%s', but is a '%s' type\",\n+\t\tdie(_(\"object '%s' tagged as '%s', but is a '%s' type\"),\n \t\t    oid_to_hex(tagged_oid),\n \t\t    type_name(*tagged_type), type_name(type));\n \n@@ -79,7 +79,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \t\t\t     builtin_mktag_usage, 0);\n \n \tif (strbuf_read(&buf, 0, 0) < 0)\n-\t\tdie_errno(\"could not read from stdin\");\n+\t\tdie_errno(_(\"could not read from stdin\"));\n \n \tfsck_options.error_func = mktag_fsck_error_func;\n \tfsck_set_msg_type(&fsck_options, \"extraheaderentry\", \"warn\");\n@@ -87,13 +87,13 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tgit_config(mktag_config, NULL);\n \tif (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,\n \t\t\t\t&tagged_oid, &tagged_type))\n-\t\tdie(\"tag on stdin did not pass our strict fsck check\");\n+\t\tdie(_(\"tag on stdin did not pass our strict fsck check\"));\n \n \tif (verify_object_in_tag(&tagged_oid, &tagged_type))\n-\t\tdie(\"tag on stdin did not refer to a valid object\");\n+\t\tdie(_(\"tag on stdin did not refer to a valid object\"));\n \n \tif (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)\n-\t\tdie(\"unable to write tag file\");\n+\t\tdie(_(\"unable to write tag file\"));\n \n \tstrbuf_release(&buf);\n \tputs(oid_to_hex(&result));\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 177ab1682d..2e8b718379 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -14,7 +14,8 @@ test_description='git mktag: tag object verify test'\n check_verify_failure () {\n \texpect=\"$2\"\n \ttest_expect_success \"$1\" '\n-\t\ttest_must_fail git mktag <tag.sig 2>message &&\n+\t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n+\t\t\tgit mktag <tag.sig 2>message &&\n \t\tgrep \"$expect\" message\n \t'\n }\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"412898","messageId":"xmqqy2hpe128.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201223013606.7972-4-avarab@gmail.com","subject":"Re: [PATCH v4 03/20] mktag doc: update to explain why to use this","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-23T01:57:51Z","receivedAt":"2020-12-23T01:58:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Change the mktag documentation to compare itself to the similar\n> \"hash-object -t tag\" command. Before this someone reading the\n> documentation wouldn't have much of an idea what the difference\n> was.\n>\n> Let's allude to our own validation logic, and cross-link the \"mktag\"\n> and \"hash-object\" documentation to aid discover-ability. A follow-up\n> change to migrate \"mktag\" to use \"fsck\" validation will make the part\n> about validation logic clearer.\n\nWe do not add links to refer to commands like \"git commit-tree\",\n\"git mktree\" or \"git add\" just because these other commands can\ncreate commit, tree and blob objects like hash-object does, so\nadding a link to mktag in the hash-object documentation feels a\nbit fishy, but the extended description in mktag that highlights\nthe added value of the command over hash-object reads very well.\n\n>  Tag Format\n>  ----------\n> @@ -34,6 +43,10 @@ exists, is separated by a blank line from the header.  The\n>  message part may contain a signature that Git itself doesn't\n>  care about, but that can be verified with gpg.\n>  \n> +SEE ALSO\n> +--------\n> +linkgit:git-hash-object[1],\n\nComma,\n\n> +\n>  GIT\n>  ---\n>  Part of the linkgit:git[1] suite\n"},{"id":"412899","messageId":"xmqqtusde0qf.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201223013606.7972-7-avarab@gmail.com","subject":"Re: [PATCH v4 06/20] mktag tests: improve verify_object() test coverage","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-23T02:04:56Z","receivedAt":"2020-12-23T02:05:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n>  cat >tag.sig <<EOF\n>  object $(test_oid deadbeef)\n> +type tag\n> +tag mytag\n> +tagger . <> 0 +0000\n> +\n> +EOF\n> +\n> +check_verify_failure 'verify object (hash/type) check -- correct type, nonexisting object' \\\n> +\t'^error: char7: could not verify object.*$'\n> +\n\nOK.\n\n\n> +cat >tag.sig <<EOF\n> +object $head\n>  type tagggg\n>  tag mytag\n>  tagger . <> 0 +0000\n>  \n>  EOF\n>  \n> -check_verify_failure 'verify object (SHA1/type) check' \\\n> +check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n> +\t'^fatal: invalid object type'\n\ntagggg is \"made-up type\".  Where did we grab head from?  it is\nhead=$(git rev-parse --verify HEAD) at the very beginning.  Is that\nreally a nonexisting object?\n\n> +cat >tag.sig <<EOF\n> +object $(test_oid deadbeef)\n> +type tagggg\n> +tag mytag\n> +tagger . <> 0 +0000\n> +\n> +EOF\n> +\n> +check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n>  \t'^error: char7: could not verify object.*$'\n\nThis is the \"made-up type, nonexisting object\", and previous one is\n\"made-up type, valid object\", no?\n\n> +cat >tag.sig <<EOF\n> +object $head\n> +type tree\n> +tag mytag\n> +tagger . <> 0 +0000\n> +\n> +EOF\n> +\n> +check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n> +\t'^error: char7: could not verify object'\n\nAnd this is correct.  \"type tree\" is not a made-up type, and $head\nis a valid object but it is not of \"type tree\".\n\n\nSomehow I feel as if this patch is trying to see if reviewers are\npaying attention to ;-)\n\nThanks.\n"},{"id":"412900","messageId":"xmqqpn31e0hp.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201223013606.7972-8-avarab@gmail.com","subject":"Re: [PATCH v4 07/20] mktag tests: don't pipe to stderr needlessly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-23T02:10:10Z","receivedAt":"2020-12-23T02:11:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Remove the piping of stderr to \"message\" in the valid tag test. This\n> pattern seems to have been copy/pasted from the failure case in\n> 446c6faec6 (New tests and en-passant modifications to mktag.,\n> 2006-07-29).\n>\n> Nothing is piped to \"message\" here, and in the event of the test\n> failing it only serves to hide the error.\n\nI think the verb \"to pipe\" specifically means \"redirecting the\noutput to a pipe\" and nothing else.\n\n    mktag tests: don't redirect stderr to a file needlessly\n\n    Remove the redirection of stderr to \"message\" in the valid tag\n    tests.  This pattern ... 2006-07-29).  Nobody examines the\n    contents of the resulting \"message\" file, so the net result is\n    that error messages cannot be seen in \"sh t3800-mktag.sh -v\"\n    output.\n\nor something like that?\n\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t3800-mktag.sh | 4 ++--\n>  1 file changed, 2 insertions(+), 2 deletions(-)\n>\n> diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\n> index 9ae1b0bb0a..bbd148618e 100755\n> --- a/t/t3800-mktag.sh\n> +++ b/t/t3800-mktag.sh\n> @@ -257,7 +257,7 @@ EOF\n>  \n>  test_expect_success \\\n>      'allow empty tag email' \\\n> -    'git mktag <tag.sig >.git/refs/tags/mytag 2>message'\n> +    'git mktag <tag.sig >.git/refs/tags/mytag'\n>  \n>  ############################################################\n>  # 16. disallow spaces in tag email\n> @@ -385,7 +385,7 @@ EOF\n>  \n>  test_expect_success \\\n>      'create valid tag' \\\n> -    'git mktag <tag.sig >.git/refs/tags/mytag 2>message'\n> +    'git mktag <tag.sig >.git/refs/tags/mytag'\n>  \n>  ############################################################\n>  # 25. check mytag\n"},{"id":"412901","messageId":"xmqqh7oddzo1.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201223013606.7972-10-avarab@gmail.com","subject":"Re: [PATCH v4 09/20] mktag tests: stress test whitespace handling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-23T02:27:58Z","receivedAt":"2020-12-23T02:29:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> +cat >tag.sig <<EOF\n> +object $head\n> +type commit\n> +tag mytag\n> +tagger T A Gger <tagger@example.com> 1206478233 -0500\n> +\n> +\n> +this line comes after an extra newline\n> +EOF\n> +\n> +test_expect_success 'allow extra newlines at start of body' '\n> +\tgit mktag <tag.sig\n> +'\n\nOK.\n\n> +cat >tag.sig <<EOF\n> +object $head\n> +type commit\n> +tag mytag\n> +tagger T A Gger <tagger@example.com> 1206478233 -0500\n> +\n> +EOF\n> +\n> +test_expect_success 'allow extra newlines at end of headers' '\n> +\tgit mktag <tag.sig\n> +'\n\nI am not sure what the contrast between \"at start of body\" and \"at\nend of headers\" means.\n\nAhh, OK, we are making sure that we \"allow a blank line after the\nheaders before an empty body\".\n\nMakes sense (but see below).\n\n> +space=' '\n> +cat >tag.sig <<EOF\n> +object $head\n> +type commit\n> +tag mytag\n> +tagger T A Gger <tagger@example.com> 1206478233 -0500$space\n> +\n> +EOF\n> +\n> +check_verify_failure 'extra whitespace at end of headers' \\\n> +\t'^error: char.*: malformed tag timezone$'\n\nOK.  Presumably all the other header lines forbid trailing space\nas a malformed line?\n\n> +cat >tag.sig <<EOF\n> +object $head\n> +type commit\n> +tag mytag\n> +tagger T A Gger <tagger@example.com> 1206478233 -0500\n> +EOF\n> +\n> +check_verify_failure 'disallow no header / body newline separator' \\\n> +\t'^error: char.*: trailing garbage in tag header$'\n\nNow I am confused.  This fails because...?   We earlier saw that we\n\"allow\" a blank line after the header when no body is present, but\nthis says the test fails if a blank line exists after the header\nwhen no body exists.  I do not have any objection to the behaviour\nof the command, but doesn't this mean the earlier test was not\nlabelled correctly?  It may make more sense to move this test next\nto the earlier one, and name them\n\n    \"require a blank line before even an empty body (1)\"\n    \"require a blank line before even an empty body (2)\"\n\nor something?  The earlier one ensures that a payload with such a\nblank line is OK, and this one ensures that a payload without such a\nblank line causes a failure.\n\nThanks.\n\n\n> +\n>  ############################################################\n>  # 24. create valid tag\n"},{"id":"412902","messageId":"xmqqczz1dzm3.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201223013606.7972-11-avarab@gmail.com","subject":"Re: [PATCH v4 10/20] mktag tests: test \"hash-object\" compatibility","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-23T02:29:08Z","receivedAt":"2020-12-23T02:30:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Change all the successful \"mktag\" tests to test that \"hash-object\"\n> produces the same hash for the input, and that fsck passes for\n> both.\n>\n> This tests e.g. that \"mktag\" doesn't trim its input or otherwise munge\n> it in a way that \"hash-object\" doesn't.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t3800-mktag.sh | 26 +++++++++++++++-----------\n>  1 file changed, 15 insertions(+), 11 deletions(-)\n>\n> diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\n> index f339321be8..c6826762d9 100755\n> --- a/t/t3800-mktag.sh\n> +++ b/t/t3800-mktag.sh\n> @@ -19,6 +19,16 @@ check_verify_failure () {\n>  \t'\n>  }\n>  \n> +test_expect_mktag_success() {\n> +\ttest_expect_success \"$1\" '\n> +\t\tgit hash-object -t tag -w --stdin <tag.sig >expected &&\n> +\t\tgit fsck --strict &&\n> +\t\tgit mktag <tag.sig >hash &&\n> +\t\tgit fsck --strict &&\n> +\t\ttest_cmp expected hash\n> +\t'\n> +}\n\n;-)  Nice.\n\nI'll stop here for tonight.  Thanks for working on this.\n"},{"id":"412903","messageId":"xmqqlfdpe04b.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201223013606.7972-9-avarab@gmail.com","subject":"Re: [PATCH v4 08/20] mktag tests: don't create \"mytag\" twice","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-23T02:18:12Z","receivedAt":"2020-12-23T03:07:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Change a test added in e0aaf781f6 (mktag.c: improve verification of\n> tagger field and tests, 2008-03-27) to not create \"mytag\", which\n> should only be created and verified at the end in an earlier test\n> added in 446c6faec6 (New tests and en-passant modifications to mktag.,\n> 2006-07-29).\n\nIf mktag fails to create a tag, presumably .git/refs/tags/mytag file\nwould be left empty.  Wouldn't \"git tag -l\" notice that it is not a\nvalid ref and omit it from its output?  I suspect if you corrupt the\ncontents of tag.sig temporarily while the first of the original\ntests were running in such a way that mktag notices a bogosity, the\nsecond half of the original would notice.\n\nHaving said that, I like the new way much better.\n\nBut do we even need to create the tag with update-ref in the first\nplace?  What does it check?  The fact that \"mktag\" only creates an\nobject and never creates a new ref to point at the newly created\nobject (hence we expect refs/tags/mytag does not yet exist)?\n\nI am not complaining that it runs update-ref there or creates the\nmytag tag.  I am just saying I do not understand what the test wants\nto check by doing so.\n\n> While we're at it let's prevent a similar logic error from creeping\n> into the test by asserting that \"mytag\" doesn't exist before we create\n> it. Let's do this by moving the test to use \"update-ref\", instead of\n> our own homebrew ad-hoc refstore update.\n\nGreat.  I really like this part of the change that future-proofs us.\n\nThanks.\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t3800-mktag.sh | 17 +++++------------\n>  1 file changed, 5 insertions(+), 12 deletions(-)\n>\n> diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\n> index bbd148618e..b6dcdbebe6 100755\n> --- a/t/t3800-mktag.sh\n> +++ b/t/t3800-mktag.sh\n> @@ -257,7 +257,7 @@ EOF\n>  \n>  test_expect_success \\\n>      'allow empty tag email' \\\n> -    'git mktag <tag.sig >.git/refs/tags/mytag'\n> +    'git mktag <tag.sig'\n>  \n>  ############################################################\n>  # 16. disallow spaces in tag email\n> @@ -383,16 +383,9 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500\n>  \n>  EOF\n>  \n> -test_expect_success \\\n> -    'create valid tag' \\\n> -    'git mktag <tag.sig >.git/refs/tags/mytag'\n> -\n> -############################################################\n> -# 25. check mytag\n> -\n> -test_expect_success \\\n> -    'check mytag' \\\n> -    'git tag -l | grep mytag'\n> -\n> +test_expect_success 'create valid tag' '\n> +\tgit mktag <tag.sig >hash &&\n> +\tgit update-ref refs/tags/mytag $(cat hash) $(test_oid zero)\n> +'\n>  \n>  test_done\n"},{"id":"412918","messageId":"xmqqmty4agb4.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201223013606.7972-21-avarab@gmail.com","subject":"Re: [PATCH v4 20/20] mktag: add a --no-strict option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-23T11:54:23Z","receivedAt":"2020-12-23T11:55:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Now that mktag has been migrated to use the fsck machinery to check\n> its input, it makes sense to teach it to run in the equivalent of \"git\n> fsck\"'s default mode, instead of hardcoding \"git fsck --strict\". Let's\n> do that and support the \"--no-strict\" option.\n>\n> Since this is a new option we don't need to cater to parse-option.c's\n> default of automatically supporting --strict. So let's use\n> PARSE_OPT_NONEG, using a new trivial helper macro.\n\nHmph, I would have thought that the internal file-scope static done\nlike\n\n    static int option_strict = 1;\n\nwith OPT_BOOL() would be more natural, than a double negation.\n\nWhat problem does this new OPT_NO_BOOL() thing solve?\n\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  Documentation/git-mktag.txt |  7 +++++++\n>  builtin/mktag.c             |  9 +++++++++\n>  parse-options.h             |  1 +\n>  t/t3800-mktag.sh            | 35 ++++++++++++++++++++++++-----------\n>  4 files changed, 41 insertions(+), 11 deletions(-)\n>\n> diff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\n> index d04657b219..05e49b6787 100644\n> --- a/Documentation/git-mktag.txt\n> +++ b/Documentation/git-mktag.txt\n> @@ -11,6 +11,13 @@ SYNOPSIS\n>  [verse]\n>  'git mktag'\n>  \n> +OPTIONS\n> +-------\n> +\n> +--no-strict::\n> +\tBy default mktag turns on the equivalent of\n> +\tlinkgit:git-fsck[1] `--strict` mode. This disables it.\n> +\n>  DESCRIPTION\n>  -----------\n>  \n> diff --git a/builtin/mktag.c b/builtin/mktag.c\n> index 9b04b61c2b..a6a4612247 100644\n> --- a/builtin/mktag.c\n> +++ b/builtin/mktag.c\n> @@ -10,6 +10,7 @@ static char const * const builtin_mktag_usage[] = {\n>  \tN_(\"git mktag\"),\n>  \tNULL\n>  };\n> +static int option_no_strict;\n>  \n>  static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n>  \n> @@ -25,6 +26,12 @@ static int mktag_fsck_error_func(struct fsck_options *o,\n>  {\n>  \tswitch (msg_type) {\n>  \tcase FSCK_WARN:\n> +\t\tif (option_no_strict) {\n> +\t\t\tfprintf_ln(stderr, _(\"warning: tag input does not pass fsck: %s\"), message);\n> +\t\t\treturn 0;\n> +\n> +\t\t}\n> +\t\t/* fallthrough */\n>  \tcase FSCK_ERROR:\n>  \t\t/*\n>  \t\t * We treat both warnings and errors as errors, things\n> @@ -67,6 +74,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n>  int cmd_mktag(int argc, const char **argv, const char *prefix)\n>  {\n>  \tstatic struct option builtin_mktag_options[] = {\n> +\t\tOPT_NO_BOOL(0, \"no-strict\", &option_no_strict,\n> +\t\t\t    N_(\"don't do strict fsck checks\")),\n>  \t\tOPT_END(),\n>  \t};\n>  \tstruct strbuf buf = STRBUF_INIT;\n> diff --git a/parse-options.h b/parse-options.h\n> index 7030d8f3da..90d6a817d7 100644\n> --- a/parse-options.h\n> +++ b/parse-options.h\n> @@ -166,6 +166,7 @@ struct option {\n>  #define OPT_COUNTUP(s, l, v, h)     OPT_COUNTUP_F(s, l, v, h, 0)\n>  #define OPT_SET_INT(s, l, v, h, i)  OPT_SET_INT_F(s, l, v, h, i, 0)\n>  #define OPT_BOOL(s, l, v, h)        OPT_BOOL_F(s, l, v, h, 0)\n> +#define OPT_NO_BOOL(s, l, v, h)     OPT_BOOL_F(s, l, v, h, PARSE_OPT_NONEG)\n>  #define OPT_HIDDEN_BOOL(s, l, v, h) { OPTION_SET_INT, (s), (l), (v), NULL, \\\n>  \t\t\t\t      (h), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1}\n>  #define OPT_CMDMODE(s, l, v, h, i)  { OPTION_SET_INT, (s), (l), (v), NULL, \\\n> diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\n> index 2e8b718379..b436ae1e44 100755\n> --- a/t/t3800-mktag.sh\n> +++ b/t/t3800-mktag.sh\n> @@ -12,12 +12,16 @@ test_description='git mktag: tag object verify test'\n>  # given in the expect.pat file.\n>  \n>  check_verify_failure () {\n> -\texpect=\"$2\"\n> -\ttest_expect_success \"$1\" '\n> +\ttest_expect_success \"$1\" \"\n>  \t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n>  \t\t\tgit mktag <tag.sig 2>message &&\n> -\t\tgrep \"$expect\" message\n> -\t'\n> +\t\tgrep '$2' message &&\n> +\t\tif test '$3' != '--no-strict'\n> +\t\tthen\n> +\t\t\ttest_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&\n> +\t\t\tgrep '$2' message.no-strict\n> +\t\tfi\n> +\t\"\n>  }\n>  \n>  test_expect_mktag_success() {\n> @@ -49,7 +53,8 @@ test_expect_success 'basic usage' '\n>  \tEOF\n>  \tgit mktag <tag.sig &&\n>  \tgit mktag --end-of-options <tag.sig &&\n> -\ttest_expect_code 129 git mktag --unknown-option\n> +\ttest_expect_code 129 git mktag --unknown-option &&\n> +\ttest_expect_code 129 git mktag --strict\n>  '\n>  \n>  ############################################################\n> @@ -60,7 +65,7 @@ too short for a tag\n>  EOF\n>  \n>  check_verify_failure 'Tag object length check' \\\n> -\t'^error:.* missingObject:'\n> +\t'^error:.* missingObject:' 'strict'\n>  \n>  ############################################################\n>  #  2. object line label check\n> @@ -206,7 +211,7 @@ tagger . <> 0 +0000\n>  EOF\n>  \n>  check_verify_failure 'verify tag-name check' \\\n> -\t'^error:.* badTagName:'\n> +\t'^error:.* badTagName:' '--no-strict'\n>  \n>  ############################################################\n>  # 11. tagger line label check #1\n> @@ -220,7 +225,7 @@ This is filler\n>  EOF\n>  \n>  check_verify_failure '\"tagger\" line label check #1' \\\n> -\t'^error:.* missingTaggerEntry:'\n> +\t'^error:.* missingTaggerEntry:' '--no-strict'\n>  \n>  ############################################################\n>  # 12. tagger line label check #2\n> @@ -235,7 +240,7 @@ This is filler\n>  EOF\n>  \n>  check_verify_failure '\"tagger\" line label check #2' \\\n> -\t'^error:.* missingTaggerEntry:'\n> +\t'^error:.* missingTaggerEntry:' '--no-strict'\n>  \n>  ############################################################\n>  # 13. allow missing tag author name like fsck\n> @@ -264,7 +269,7 @@ tagger T A Gger <\n>  EOF\n>  \n>  check_verify_failure 'disallow malformed tagger' \\\n> -\t'^error:.* badEmail:'\n> +\t'^error:.* badEmail:' '--no-strict'\n>  \n>  ############################################################\n>  # 15. allow empty tag email\n> @@ -388,13 +393,21 @@ this line should not be here\n>  EOF\n>  \n>  check_verify_failure 'detect invalid header entry' \\\n> -\t'^error:.* extraHeaderEntry:'\n> +\t'^error:.* extraHeaderEntry:' '--no-strict'\n>  \n>  test_expect_success 'invalid header entry config & fsck' '\n>  \ttest_must_fail git mktag <tag.sig &&\n> +\tgit mktag --no-strict <tag.sig &&\n> +\n>  \ttest_must_fail git -c fsck.extraHeaderEntry=error mktag <tag.sig &&\n> +\ttest_must_fail git -c fsck.extraHeaderEntry=error mktag --no-strict <tag.sig &&\n> +\n>  \ttest_must_fail git -c fsck.extraHeaderEntry=warn mktag <tag.sig &&\n> +\tgit -c fsck.extraHeaderEntry=warn mktag --no-strict <tag.sig &&\n> +\n>  \tgit -c fsck.extraHeaderEntry=ignore mktag <tag.sig &&\n> +\tgit -c fsck.extraHeaderEntry=ignore mktag --no-strict <tag.sig &&\n> +\n>  \tgit fsck &&\n>  \tenv GIT_TEST_GETTEXT_POISON=false \\\n>  \t\tgit -c fsck.extraHeaderEntry=warn fsck 2>err &&\n"},{"id":"412921","messageId":"xmqqh7ocabog.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201223013606.7972-15-avarab@gmail.com","subject":"Re: [PATCH v4 14/20] mktag: use fsck instead of custom verify_tag()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-23T13:34:23Z","receivedAt":"2020-12-23T13:35:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> +static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n>  {\n> +\tint ret;\n> +\tenum object_type type;\n> +\tunsigned long size;\n> +\tvoid *buffer;\n> +\tconst struct object_id *repl;\n> +\n> +\tbuffer = read_object_file(tagged_oid, &type, &size);\n> +\tif (!buffer)\n> +\t\tdie(\"could not read tagged object '%s'\",\n> +\t\t    oid_to_hex(tagged_oid));\n> +\tif (type != *tagged_type)\n> +\t\tdie(\"object '%s' tagged as '%s', but is a '%s' type\",\n> +\t\t    oid_to_hex(tagged_oid),\n> +\t\t    type_name(*tagged_type), type_name(type));\n> +\n> +\trepl = lookup_replace_object(the_repository, tagged_oid);\n> +\tret = check_object_signature(the_repository, repl,\n> +\t\t\t\t     buffer, size, type_name(*tagged_type));\n> +\tfree(buffer);\n\nThis is not new, but I wonder if the object whose name is repl can\nbe of different type than the object whose name is tagged_oid, and\nwhat happens when such a replacement is defined.\n\n> +\treturn ret;\n>  }\n> ...\n> +/*\n> + * fsck a tag, and pass info about it back to the caller. This is\n> + * exposed fsck_object() internals for git-mktag(1).\n> + */\n> +int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n> +\t\t\tunsigned long size, struct fsck_options *options,\n> +\t\t\tstruct object_id *tagged_oid,\n> +\t\t\tint *tag_type);\n> +\n\nOK.\n\n> diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\n> index c6826762d9..d20adf0544 100755\n> --- a/t/t3800-mktag.sh\n> +++ b/t/t3800-mktag.sh\n> @@ -47,7 +47,7 @@ too short for a tag\n>  EOF\n\nOK.  This seems to show rather nicely what checks got loosened\n(and nothing got tightened it seems).\n\nThanks.  Looking good so far.\n\n>  \n>  check_verify_failure 'Tag object length check' \\\n> -\t'^error: .*size wrong.*$'\n> +\t'^error:.* missingObject:'\n>  \n>  ############################################################\n>  #  2. object line label check\n> @@ -60,7 +60,7 @@ tagger . <> 0 +0000\n>  \n>  EOF\n>  \n> -check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n> +check_verify_failure '\"object\" line label check' '^error:.* missingObject:'\n>  \n>  ############################################################\n>  #  3. object line hash check\n> @@ -73,7 +73,7 @@ tagger . <> 0 +0000\n>  \n>  EOF\n>  \n> -check_verify_failure '\"object\" line SHA1 check' '^error: char7: .*SHA1 hash$'\n> +check_verify_failure '\"object\" line check' '^error:.* badObjectSha1:'\n>  \n>  ############################################################\n>  #  4. type line label check\n> @@ -86,7 +86,7 @@ tagger . <> 0 +0000\n>  \n>  EOF\n>  \n> -check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n> +check_verify_failure '\"type\" line label check' '^error:.* missingTypeEntry:'\n>  \n>  ############################################################\n>  #  5. type line eol check\n> @@ -94,7 +94,7 @@ check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n>  echo \"object $head\" >tag.sig\n>  printf \"type tagsssssssssssssssssssssssssssssss\" >>tag.sig\n>  \n> -check_verify_failure '\"type\" line eol check' '^error: char.*: .*\"\\\\n\"$'\n> +check_verify_failure '\"type\" line eol check' '^error:.* unterminatedHeader:'\n>  \n>  ############################################################\n>  #  6. tag line label check #1\n> @@ -108,7 +108,7 @@ tagger . <> 0 +0000\n>  EOF\n>  \n>  check_verify_failure '\"tag\" line label check #1' \\\n> -\t'^error: char.*: no \"tag \" found$'\n> +\t'^error:.* missingTagEntry:'\n>  \n>  ############################################################\n>  #  7. tag line label check #2\n> @@ -120,7 +120,7 @@ tag\n>  EOF\n>  \n>  check_verify_failure '\"tag\" line label check #2' \\\n> -\t'^error: char.*: no \"tag \" found$'\n> +\t'^error:.* badType:'\n>  \n>  ############################################################\n>  #  8. type line type-name length check\n> @@ -132,7 +132,7 @@ tag mytag\n>  EOF\n>  \n>  check_verify_failure '\"type\" line type-name length check' \\\n> -\t'^error: char.*: type too long$'\n> +\t'^error:.* badType:'\n>  \n>  ############################################################\n>  #  9. verify object (hash/type) check\n> @@ -146,7 +146,7 @@ tagger . <> 0 +0000\n>  EOF\n>  \n>  check_verify_failure 'verify object (hash/type) check -- correct type, nonexisting object' \\\n> -\t'^error: char7: could not verify object.*$'\n> +\t'^fatal: could not read tagged object'\n>  \n>  cat >tag.sig <<EOF\n>  object $head\n> @@ -157,7 +157,7 @@ tagger . <> 0 +0000\n>  EOF\n>  \n>  check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n> -\t'^fatal: invalid object type'\n> +\t'^error:.* badType:'\n>  \n>  cat >tag.sig <<EOF\n>  object $(test_oid deadbeef)\n> @@ -168,7 +168,7 @@ tagger . <> 0 +0000\n>  EOF\n>  \n>  check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n> -\t'^error: char7: could not verify object.*$'\n> +\t'^error:.* badType:'\n>  \n>  cat >tag.sig <<EOF\n>  object $head\n> @@ -179,7 +179,7 @@ tagger . <> 0 +0000\n>  EOF\n>  \n>  check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n> -\t'^error: char7: could not verify object'\n> +\t'^fatal: object.*tagged as.*tree.*but is.*commit'\n>  \n>  ############################################################\n>  # 10. verify tag-name check\n> @@ -193,7 +193,7 @@ tagger . <> 0 +0000\n>  EOF\n>  \n>  check_verify_failure 'verify tag-name check' \\\n> -\t'^error: char.*: could not verify tag name$'\n> +\t'^error:.* badTagName:'\n>  \n>  ############################################################\n>  # 11. tagger line label check #1\n> @@ -207,7 +207,7 @@ This is filler\n>  EOF\n>  \n>  check_verify_failure '\"tagger\" line label check #1' \\\n> -\t'^error: char.*: could not find \"tagger \"$'\n> +\t'^error:.* missingTaggerEntry:'\n>  \n>  ############################################################\n>  # 12. tagger line label check #2\n> @@ -222,10 +222,10 @@ This is filler\n>  EOF\n>  \n>  check_verify_failure '\"tagger\" line label check #2' \\\n> -\t'^error: char.*: could not find \"tagger \"$'\n> +\t'^error:.* missingTaggerEntry:'\n>  \n>  ############################################################\n> -# 13. disallow missing tag author name\n> +# 13. allow missing tag author name like fsck\n>  \n>  cat >tag.sig <<EOF\n>  object $head\n> @@ -236,8 +236,7 @@ tagger  <> 0 +0000\n>  This is filler\n>  EOF\n>  \n> -check_verify_failure 'disallow missing tag author name' \\\n> -\t'^error: char.*: missing tagger name$'\n> +test_expect_mktag_success 'allow missing tag author name'\n>  \n>  ############################################################\n>  # 14. disallow missing tag author name\n> @@ -252,7 +251,7 @@ tagger T A Gger <\n>  EOF\n>  \n>  check_verify_failure 'disallow malformed tagger' \\\n> -\t'^error: char.*: malformed tagger field$'\n> +\t'^error:.* badEmail:'\n>  \n>  ############################################################\n>  # 15. allow empty tag email\n> @@ -268,7 +267,7 @@ EOF\n>  test_expect_mktag_success 'allow empty tag email'\n>  \n>  ############################################################\n> -# 16. disallow spaces in tag email\n> +# 16. allow spaces in tag email like fsck\n>  \n>  cat >tag.sig <<EOF\n>  object $head\n> @@ -278,8 +277,7 @@ tagger T A Gger <tag ger@example.com> 0 +0000\n>  \n>  EOF\n>  \n> -check_verify_failure 'disallow spaces in tag email' \\\n> -\t'^error: char.*: malformed tagger field$'\n> +test_expect_mktag_success 'allow spaces in tag email like fsck'\n>  \n>  ############################################################\n>  # 17. disallow missing tag timestamp\n> @@ -293,7 +291,7 @@ tagger T A Gger <tagger@example.com>__\n>  EOF\n>  \n>  check_verify_failure 'disallow missing tag timestamp' \\\n> -\t'^error: char.*: missing tag timestamp$'\n> +\t'^error:.* badDate:'\n>  \n>  ############################################################\n>  # 18. detect invalid tag timestamp1\n> @@ -307,7 +305,7 @@ tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008\n>  EOF\n>  \n>  check_verify_failure 'detect invalid tag timestamp1' \\\n> -\t'^error: char.*: missing tag timestamp$'\n> +\t'^error:.* badDate:'\n>  \n>  ############################################################\n>  # 19. detect invalid tag timestamp2\n> @@ -321,7 +319,7 @@ tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500\n>  EOF\n>  \n>  check_verify_failure 'detect invalid tag timestamp2' \\\n> -\t'^error: char.*: malformed tag timestamp$'\n> +\t'^error:.* badDate:'\n>  \n>  ############################################################\n>  # 20. detect invalid tag timezone1\n> @@ -335,7 +333,7 @@ tagger T A Gger <tagger@example.com> 1206478233 GMT\n>  EOF\n>  \n>  check_verify_failure 'detect invalid tag timezone1' \\\n> -\t'^error: char.*: malformed tag timezone$'\n> +\t'^error:.* badTimezone:'\n>  \n>  ############################################################\n>  # 21. detect invalid tag timezone2\n> @@ -349,10 +347,10 @@ tagger T A Gger <tagger@example.com> 1206478233 +  30\n>  EOF\n>  \n>  check_verify_failure 'detect invalid tag timezone2' \\\n> -\t'^error: char.*: malformed tag timezone$'\n> +\t'^error:.* badTimezone:'\n>  \n>  ############################################################\n> -# 22. detect invalid tag timezone3\n> +# 22. allow invalid tag timezone3 (the maximum is -1200/+1400)\n>  \n>  cat >tag.sig <<EOF\n>  object $head\n> @@ -362,8 +360,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -1430\n>  \n>  EOF\n>  \n> -check_verify_failure 'detect invalid tag timezone3' \\\n> -\t'^error: char.*: malformed tag timezone$'\n> +test_expect_mktag_success 'allow invalid tag timezone'\n>  \n>  ############################################################\n>  # 23. detect invalid header entry\n> @@ -378,7 +375,7 @@ this line should not be here\n>  EOF\n>  \n>  check_verify_failure 'detect invalid header entry' \\\n> -\t'^error: char.*: trailing garbage in tag header$'\n> +\t'^error:.* extraHeaderEntry:'\n>  \n>  cat >tag.sig <<EOF\n>  object $head\n> @@ -412,7 +409,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500$space\n>  EOF\n>  \n>  check_verify_failure 'extra whitespace at end of headers' \\\n> -\t'^error: char.*: malformed tag timezone$'\n> +\t'^error:.* badTimezone:'\n>  \n>  cat >tag.sig <<EOF\n>  object $head\n> @@ -422,7 +419,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500\n>  EOF\n>  \n>  check_verify_failure 'disallow no header / body newline separator' \\\n> -\t'^error: char.*: trailing garbage in tag header$'\n> +\t'^error:.* extraHeaderEntry:'\n>  \n>  ############################################################\n>  # 24. create valid tag\n"},{"id":"412923","messageId":"xmqq5z4saar4.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"Re: [PATCH v4 00/20] make \"mktag\" use fsck_tag() & more","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-23T13:54:23Z","receivedAt":"2020-12-23T13:55:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> So, when re-rolling this with Junio's small fixup this grew in scope a\n> bit, but should paradoxically be easier to deal with even though it's\n> 2x the size now. Read on:\n\nThe core part didn't change all that much, which made it a bit\neasier to see.  I left comments here and there, but the series\nmostly looked sensible in general.\n\nThanks.\n"},{"id":"412955","messageId":"20201223220922.GJ8396@szeder.dev","threadId":"51667","inReplyTo":"20201223013606.7972-17-avarab@gmail.com","subject":"Re: [PATCH v4 16/20] mktag: allow turning off fsck.extraHeaderEntry","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2020-12-23T22:09:22Z","receivedAt":"2020-12-23T22:10:08Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Wed, Dec 23, 2020 at 02:36:01AM +0100, Ævar Arnfjörð Bjarmason wrote:\n> In earlier commits mktag learned to use the fsck machinery, at which\n> point we needed to add fsck.extraHeaderEntry so it could be as strict\n> about extra headers as it's been ever since it was implemented.\n> \n> But it's not nice to need to switch away from \"mktag\" to \"hash-object\"\n> + manual \"fsck\" just because you'd like to have an extra header. So\n> let's support turning it off by getting \"fsck.*\" variables from the\n> config.\n> \n> Pedantically speaking it's still not possible to make \"mktag\" behave\n> just like \"hash-object -t tag\" does, since we're unconditionally going\n> to check the referenced object in verify_object_in_tag(), which is our\n> own check, and not one that exists in fsck.c.\n> \n> But the spirit of \"this works like fsck\" is preserved, in that if you\n> created such a tag with \"hash-object\" and did a full \"fsck\" on the\n> repository it would also error out about that invalid object, it just\n> wouldn't emit the same message as fsck does.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n\n> diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\n> index d20adf0544..1019b42378 100755\n> --- a/t/t3800-mktag.sh\n> +++ b/t/t3800-mktag.sh\n> @@ -377,6 +377,20 @@ EOF\n>  check_verify_failure 'detect invalid header entry' \\\n>  \t'^error:.* extraHeaderEntry:'\n>  \n> +test_expect_success 'invalid header entry config & fsck' '\n> +\ttest_must_fail git mktag <tag.sig &&\n> +\ttest_must_fail git -c fsck.extraHeaderEntry=error mktag <tag.sig &&\n> +\ttest_must_fail git -c fsck.extraHeaderEntry=warn mktag <tag.sig &&\n> +\tgit -c fsck.extraHeaderEntry=ignore mktag <tag.sig &&\n> +\tgit fsck &&\n> +\tenv GIT_TEST_GETTEXT_POISON=false \\\n\nWhy this 'env GIT_TEST_GETTEXT_POISON=false' here...\n\n> +\t\tgit -c fsck.extraHeaderEntry=warn fsck 2>err &&\n> +\tgrep \"warning .*extraHeaderEntry:\" err &&\n\n... instead of test_i18ngrep here?\n\n> +\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n> +\t\tgit -c fsck.extraHeaderEntry=error 2>err fsck &&\n> +\tgrep \"error .* extraHeaderEntry:\" err\n> +'\n> +\n>  cat >tag.sig <<EOF\n>  object $head\n>  type commit\n> -- \n> 2.29.2.222.g5d2a92d10f8\n> \n"},{"id":"412956","messageId":"20201223222031.GK8396@szeder.dev","threadId":"51667","inReplyTo":"20201223013606.7972-21-avarab@gmail.com","subject":"Re: [PATCH v4 20/20] mktag: add a --no-strict option","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2020-12-23T22:20:31Z","receivedAt":"2020-12-23T22:21:31Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Wed, Dec 23, 2020 at 02:36:05AM +0100, Ævar Arnfjörð Bjarmason wrote:\n> Now that mktag has been migrated to use the fsck machinery to check\n> its input, it makes sense to teach it to run in the equivalent of \"git\n> fsck\"'s default mode, instead of hardcoding \"git fsck --strict\". Let's\n> do that and support the \"--no-strict\" option.\n> \n> Since this is a new option we don't need to cater to parse-option.c's\n> default of automatically supporting --strict. So let's use\n> PARSE_OPT_NONEG, using a new trivial helper macro.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\n> diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\n> index 2e8b718379..b436ae1e44 100755\n> --- a/t/t3800-mktag.sh\n> +++ b/t/t3800-mktag.sh\n> @@ -12,12 +12,16 @@ test_description='git mktag: tag object verify test'\n>  # given in the expect.pat file.\n>  \n>  check_verify_failure () {\n> -\texpect=\"$2\"\n> -\ttest_expect_success \"$1\" '\n> +\ttest_expect_success \"$1\" \"\n>  \t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n>  \t\t\tgit mktag <tag.sig 2>message &&\n\nSo this 'git mktag' is always invoked with\nGIT_TEST_GETTEXT_POISON=false (instead of, for some reason, using\ntest_i18ngrep on the next line)...\n\n> -\t\tgrep \"$expect\" message\n> -\t'\n> +\t\tgrep '$2' message &&\n> +\t\tif test '$3' != '--no-strict'\n> +\t\tthen\n> +\t\t\ttest_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&\n\nWouldn't a GIT_TEST_GETTEXT_POISON=false be necessary for this 'git\nmktag' as well?  Or, alternatively (preferably?), a test_i18ngrep\nbelow.\n\n> +\t\t\tgrep '$2' message.no-strict\n> +\t\tfi\n> +\t\"\n>  }\n>  \n>  test_expect_mktag_success() {\n"},{"id":"412992","messageId":"xmqqmty336np.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"xmqq5z4saar4.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v4 00/20] make \"mktag\" use fsck_tag() & more","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-12-24T09:16:58Z","receivedAt":"2020-12-24T09:17:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> So, when re-rolling this with Junio's small fixup this grew in scope a\n>> bit, but should paradoxically be easier to deal with even though it's\n>> 2x the size now. Read on:\n>\n> The core part didn't change all that much, which made it a bit\n> easier to see.  I left comments here and there, but the series\n> mostly looked sensible in general.\n\nThis failure is likely from the \"mktag\" series:\n\n  https://github.com/git/git/runs/1602776090#step:4:1622 (summary)\n  https://github.com/git/git/runs/1602776090#step:5:158 (the first one of them)\n\nThanks.\n"},{"id":"413483","messageId":"20210105194252.627-1-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 00/23] make \"mktag\" use fsck_tag() & more","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:29Z","receivedAt":"2021-01-05T19:44:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"A late re-roll due to the holidays. Thanks all for the feedback on v4,\nthis should address all of it.\n\nBrief comments on each patch below, and then the range-diff.\n\nÆvar Arnfjörð Bjarmason (23):\n  mktag doc: say <hash> not <sha1>\n  mktag doc: grammar fix, when exists -> when it exists\n\nNo changes.\n\n  mktag doc: update to explain why to use this\n\nGot rid of the cross-linking of mktag and hash-object manpages as\nsuggested by Junio.\n\n  mktag tests: don't needlessly use a subshell\n\nno changes.\n\n  mktag tests: use \"test_commit\" helper\n\nNEW: Another trivial mktag test cleanup, make it use test_commit\ninstead of 3 boilerplate lines in its test. Made a later change\nsmaller.\n\n  mktag tests: remove needless SHA-1 hardcoding\n\nno changes.\n\n  mktag tests: don't redirect stderr to a file needlessly\n\nReworded the commit message, and fixed another copy/pasted mktag test\n(in the git-replace test file) that used the same pattern.\n\n  mktag tests: don't create \"mytag\" twice\n\nThe commit message notes that testing the tag at the end is pointless,\nbut now later we'll do something with it...\n\n  mktag tests: run \"fsck\" after creating \"mytag\"\n\n...i.e. run fsck to (potentially in the future) check the envelope\nv.s. refname.\n\n  mktag tests: stress test whitespace handling\n\nReworded commit message & test descriptions to make more sense, got\nrid of the one $space test, which didn't really add anything.\n\n  mktag tests: test \"hash-object\" compatibility\n\nGets rid of the \"create tag refname\" special case at the end of the\ntests by now doing it for every successful tag creation.\n\n  mktag tests: improve verify_object() test coverage\n\nCommit message update, notes that we don't do \"git replace\" tests.\n\n  mktag tests: test verify_object() with replaced objects\n\nNEW: test mktag with git-replace'd objects.\n\n  mktag: use default strbuf_read() hint\n  mktag: remove redundant braces in one-line body \"if\"\n  mktag: use puts(str) instead of printf(\"%s\\n\", str)\n\nno changes.\n\n  mktag: use fsck instead of custom verify_tag()\n\nonly carried-forward changes from rebasing on the changes above.\n\n  fsck: make fsck_config() re-usable\n  mktag: allow turning off fsck.extraHeaderEntry\n\nno changes.\n\n  mktag: allow omitting the header/body \\n separator\n  mktag: convert to parse-options\n\nditto carried-forward changes.\n\n  mktag: mark strings for translation\n  mktag: add a --no-strict option\n\nUse the more normal OPT_BOOL pattern, and fix a stupid error in\nGIT_TEST_GETTEXT_POISON=true testing that broke the CI.\n\n Documentation/git-mktag.txt |  39 +++++-\n builtin/fsck.c              |  20 +--\n builtin/mktag.c             | 235 +++++++++++++----------------------\n fsck.c                      |  59 ++++++++-\n fsck.h                      |  16 +++\n t/t1006-cat-file.sh         |   2 +-\n t/t3800-mktag.sh            | 236 ++++++++++++++++++++++++++++--------\n t/t6050-replace.sh          |   2 +-\n 8 files changed, 375 insertions(+), 234 deletions(-)\n\nRange-diff:\n 1:  a31c305cfc6 =  1:  74b035fb842 mktag doc: say <hash> not <sha1>\n 2:  81cb4cba5c0 =  2:  202bd33533f mktag doc: grammar fix, when exists -> when it exists\n 3:  b4bc6f894cb !  3:  d1f650b6c4c mktag doc: update to explain why to use this\n    @@ Commit message\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    - ## Documentation/git-hash-object.txt ##\n    -@@ Documentation/git-hash-object.txt: OPTIONS\n    - \tstress-testing Git itself or reproducing characteristics of corrupt or\n    - \tbogus objects encountered in the wild.\n    - \n    -+SEE ALSO\n    -+--------\n    -+linkgit:git-mktag[1]\n    -+\n    - GIT\n    - ---\n    - Part of the linkgit:git[1] suite\n    -\n      ## Documentation/git-mktag.txt ##\n     @@ Documentation/git-mktag.txt: git-mktag(1)\n      \n    @@ Documentation/git-mktag.txt: SYNOPSIS\n      \n      Tag Format\n      ----------\n    -@@ Documentation/git-mktag.txt: exists, is separated by a blank line from the header.  The\n    - message part may contain a signature that Git itself doesn't\n    - care about, but that can be verified with gpg.\n    - \n    -+SEE ALSO\n    -+--------\n    -+linkgit:git-hash-object[1],\n    -+\n    - GIT\n    - ---\n    - Part of the linkgit:git[1] suite\n 4:  acb94e02895 =  4:  64432d22952 mktag tests: don't needlessly use a subshell\n -:  ----------- >  5:  57c7bdde80b mktag tests: use \"test_commit\" helper\n 5:  4ae76ec5e3f =  6:  ba581a99c89 mktag tests: remove needless SHA-1 hardcoding\n 7:  b81d31a917c !  7:  974d1b9c6ec mktag tests: don't pipe to stderr needlessly\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    mktag tests: don't pipe to stderr needlessly\n    +    mktag tests: don't redirect stderr to a file needlessly\n     \n    -    Remove the piping of stderr to \"message\" in the valid tag test. This\n    -    pattern seems to have been copy/pasted from the failure case in\n    -    446c6faec6 (New tests and en-passant modifications to mktag.,\n    +    Remove the redirection of stderr to \"message\" in the valid tag\n    +    test. This pattern seems to have been copy/pasted from the failure\n    +    case in 446c6faec6 (New tests and en-passant modifications to mktag.,\n         2006-07-29).\n     \n    -    Nothing is piped to \"message\" here, and in the event of the test\n    -    failing it only serves to hide the error.\n    +    While I'm at it do the same for the \"replace\" tests. The tag creation\n    +    I'm changing here seems to have been copy/pasted from the \"mktag\"\n    +    tests to those tests in cc400f50112 (mktag: call\n    +    \"check_sha1_signature\" with the replacement sha1, 2009-01-23).\n    +\n    +    Nobody examines the contents of the resulting \"message\" file, so the\n    +    net result is that error messages cannot be seen in \"sh t3800-mktag.sh\n    +    -v\" output.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ t/t3800-mktag.sh: EOF\n      \n      ############################################################\n      # 25. check mytag\n    +\n    + ## t/t6050-replace.sh ##\n    +@@ t/t6050-replace.sh: tagger T A Gger <> 0 +0000\n    + EOF\n    + \n    + test_expect_success 'tag replaced commit' '\n    +-     git mktag <tag.sig >.git/refs/tags/mytag 2>message\n    ++     git mktag <tag.sig >.git/refs/tags/mytag\n    + '\n    + \n    + test_expect_success '\"git fsck\" works' '\n 8:  11f59718b4b !  8:  73d82dbca24 mktag tests: don't create \"mytag\" twice\n    @@ Commit message\n         it. Let's do this by moving the test to use \"update-ref\", instead of\n         our own homebrew ad-hoc refstore update.\n     \n    +    We're not really testing for anything yet by creating the tag at the\n    +    end here. A subsequent commit will change that.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## t/t3800-mktag.sh ##\n -:  ----------- >  9:  0aa8190f404 mktag tests: run \"fsck\" after creating \"mytag\"\n 9:  dd6b012b0c6 ! 10:  078e73c4221 mktag tests: stress test whitespace handling\n    @@ Metadata\n      ## Commit message ##\n         mktag tests: stress test whitespace handling\n     \n    -    Add tests for various whitespace edge cases around the header/body\n    -    boundary, such as two newlines separating the header and body, a space\n    -    at the end of the \"tagger\" line before the header etc.\n    +    Add tests for a couple of whitespace edge cases around the header/body\n    +    boundary.\n     \n    -    Add a test showing that we insist that an empty body must be preceded\n    -    by an empty line. This test for a long-standing regression which goes\n    -    against the command's documented behavior. It'll be addressed in a\n    -    follow-up change.\n    +    I consider the requirement for a blank line before the empty body a\n    +    bug, it's a long-standing regression which goes against the command's\n    +    documented behavior. This bug will be addressed in a follow-up change.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ t/t3800-mktag.sh: EOF\n     +\n     +EOF\n     +\n    -+test_expect_success 'allow extra newlines at end of headers' '\n    ++test_expect_success 'require a blank line before an empty body (1)' '\n     +\tgit mktag <tag.sig\n     +'\n     +\n    -+space=' '\n    -+cat >tag.sig <<EOF\n    -+object $head\n    -+type commit\n    -+tag mytag\n    -+tagger T A Gger <tagger@example.com> 1206478233 -0500$space\n    -+\n    -+EOF\n    -+\n    -+check_verify_failure 'extra whitespace at end of headers' \\\n    -+\t'^error: char.*: malformed tag timezone$'\n    -+\n     +cat >tag.sig <<EOF\n     +object $head\n     +type commit\n    @@ t/t3800-mktag.sh: EOF\n     +tagger T A Gger <tagger@example.com> 1206478233 -0500\n     +EOF\n     +\n    -+check_verify_failure 'disallow no header / body newline separator' \\\n    ++check_verify_failure 'require a blank line before an empty body (2)' \\\n     +\t'^error: char.*: trailing garbage in tag header$'\n     +\n      ############################################################\n10:  56c6b562fd5 ! 11:  83d7ca86b8f mktag tests: test \"hash-object\" compatibility\n    @@ Commit message\n         This tests e.g. that \"mktag\" doesn't trim its input or otherwise munge\n         it in a way that \"hash-object\" doesn't.\n     \n    +    Since we're doing an \"fsck --strict\" here at the end let's incorporate\n    +    the creation of the \"mytag\" name into this test, removing the\n    +    special-case at the end of the file.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## t/t3800-mktag.sh ##\n    @@ t/t3800-mktag.sh: check_verify_failure () {\n     +\ttest_expect_success \"$1\" '\n     +\t\tgit hash-object -t tag -w --stdin <tag.sig >expected &&\n     +\t\tgit fsck --strict &&\n    ++\n     +\t\tgit mktag <tag.sig >hash &&\n    -+\t\tgit fsck --strict &&\n    -+\t\ttest_cmp expected hash\n    ++\t\ttest_cmp expected hash &&\n    ++\t\ttest_when_finished \"git update-ref -d refs/tags/mytag $(cat hash)\" &&\n    ++\t\tgit update-ref refs/tags/mytag $(cat hash) $(test_oid zero) &&\n    ++\t\tgit fsck --strict\t\n     +\t'\n     +}\n     +\n    @@ t/t3800-mktag.sh: tagger T A Gger <tagger@example.com> 1206478233 -0500\n      \n      EOF\n      \n    --test_expect_success 'allow extra newlines at end of headers' '\n    +-test_expect_success 'require a blank line before an empty body (1)' '\n     -\tgit mktag <tag.sig\n     -'\n    -+test_expect_mktag_success 'allow extra newlines at end of headers'\n    ++test_expect_mktag_success 'require a blank line before an empty body (1)'\n      \n    - space=' '\n      cat >tag.sig <<EOF\n    + object $head\n     @@ t/t3800-mktag.sh: tagger T A Gger <tagger@example.com> 1206478233 -0500\n      \n      EOF\n      \n     -test_expect_success 'create valid tag' '\n     -\tgit mktag <tag.sig >hash &&\n    +-\tgit update-ref refs/tags/mytag $(cat hash) $(test_oid zero) &&\n    +-\tgit fsck --strict\n    +-'\n     +test_expect_mktag_success 'create valid tag object'\n    -+test_expect_success 'create valid tag name' '\n    - \tgit update-ref refs/tags/mytag $(cat hash) $(test_oid zero)\n    - '\n      \n    + test_done\n 6:  9effb4532bc ! 12:  6cbf9176c35 mktag tests: improve verify_object() test coverage\n    @@ Commit message\n         Let's split these tests up, so we're testing all combinations of a\n         non-existing object and in invalid/wrong \"type\" lines.\n     \n    +    We need to provide GIT_TEST_GETTEXT_POISON=false here because the\n    +    \"invalid object type\" error is emitted by\n    +    parse_loose_header_extended(), which has that message already marked\n    +    for translation. Another option would be to use test_i18ngrep, but I\n    +    prefer always running the test, not skipping it under gettext poison\n    +    testing.\n    +\n    +    I'm not testing this in combination with \"git replace\". That'll be\n    +    done in a subsequent commit.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## t/t3800-mktag.sh ##\n    +@@ t/t3800-mktag.sh: test_description='git mktag: tag object verify test'\n    + check_verify_failure () {\n    + \texpect=\"$2\"\n    + \ttest_expect_success \"$1\" '\n    +-\t\ttest_must_fail git mktag <tag.sig 2>message &&\n    ++\t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n    ++\t\t\tgit mktag <tag.sig 2>message &&\n    + \t\tgrep \"$expect\" message\n    + \t'\n    + }\n     @@ t/t3800-mktag.sh: check_verify_failure '\"type\" line type-name length check' \\\n      \t'^error: char.*: type too long$'\n      \n    @@ t/t3800-mktag.sh: check_verify_failure '\"type\" line type-name length check' \\\n      EOF\n      \n     -check_verify_failure 'verify object (SHA1/type) check' \\\n    -+check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n    ++check_verify_failure 'verify object (hash/type) check -- made-up type, valid object' \\\n     +\t'^fatal: invalid object type'\n     +\n     +cat >tag.sig <<EOF\n    @@ t/t3800-mktag.sh: check_verify_failure '\"type\" line type-name length check' \\\n     +\n     +EOF\n     +\n    -+check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n    ++check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n      \t'^error: char7: could not verify object.*$'\n      \n     +cat >tag.sig <<EOF\n    @@ t/t3800-mktag.sh: check_verify_failure '\"type\" line type-name length check' \\\n     +\n     +EOF\n     +\n    -+check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n    ++check_verify_failure 'verify object (hash/type) check -- mismatched type, valid object' \\\n     +\t'^error: char7: could not verify object'\n     +\n      ############################################################\n -:  ----------- > 13:  ca1567866b0 mktag tests: test verify_object() with replaced objects\n11:  1e2e4ec2697 = 14:  ac31d8658f3 mktag: use default strbuf_read() hint\n12:  be2ab3edab0 = 15:  e32292af8b4 mktag: remove redundant braces in one-line body \"if\"\n13:  d8514df970c = 16:  405fd2c747d mktag: use puts(str) instead of printf(\"%s\\n\", str)\n14:  346d73cc972 ! 17:  6edaa992a25 mktag: use fsck instead of custom verify_tag()\n    @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n     @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n      EOF\n      \n    - check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n    + check_verify_failure 'verify object (hash/type) check -- made-up type, valid object' \\\n     -\t'^fatal: invalid object type'\n     +\t'^error:.* badType:'\n      \n    @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n     @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n      EOF\n      \n    - check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n    + check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n     -\t'^error: char7: could not verify object.*$'\n     +\t'^error:.* badType:'\n      \n    @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n     @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n      EOF\n      \n    - check_verify_failure 'verify object (hash/type) check -- incorrect type, valid object' \\\n    + check_verify_failure 'verify object (hash/type) check -- mismatched type, valid object' \\\n     -\t'^error: char7: could not verify object'\n     +\t'^fatal: object.*tagged as.*tree.*but is.*commit'\n      \n    + ############################################################\n    + #  9.5. verify object (hash/type) check -- replacement\n    +@@ t/t3800-mktag.sh: tagger . <> 0 +0000\n    + EOF\n    + \n    + check_verify_failure 'verify object (hash/type) check -- mismatched type, valid object' \\\n    +-\t'^error: char7: could not verify object'\n    ++\t'^fatal: object.*tagged as.*tree.*but is.*blob'\n    + \n      ############################################################\n      # 10. verify tag-name check\n     @@ t/t3800-mktag.sh: tagger . <> 0 +0000\n    @@ t/t3800-mktag.sh: this line should not be here\n     -\t'^error: char.*: trailing garbage in tag header$'\n     +\t'^error:.* extraHeaderEntry:'\n      \n    - cat >tag.sig <<EOF\n    - object $head\n    -@@ t/t3800-mktag.sh: tagger T A Gger <tagger@example.com> 1206478233 -0500$space\n    - EOF\n    - \n    - check_verify_failure 'extra whitespace at end of headers' \\\n    --\t'^error: char.*: malformed tag timezone$'\n    -+\t'^error:.* badTimezone:'\n    - \n      cat >tag.sig <<EOF\n      object $head\n     @@ t/t3800-mktag.sh: tagger T A Gger <tagger@example.com> 1206478233 -0500\n      EOF\n      \n    - check_verify_failure 'disallow no header / body newline separator' \\\n    + check_verify_failure 'require a blank line before an empty body (2)' \\\n     -\t'^error: char.*: trailing garbage in tag header$'\n     +\t'^error:.* extraHeaderEntry:'\n      \n15:  0e7994d8fc3 = 18:  4c3fbb7ed58 fsck: make fsck_config() re-usable\n16:  5e8046022b6 = 19:  32fc2264ec1 mktag: allow turning off fsck.extraHeaderEntry\n17:  32698e1d005 ! 20:  32922a42dda mktag: allow omitting the header/body \\n separator\n    @@ fsck.c: int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n      \t\t/*\n     \n      ## t/t3800-mktag.sh ##\n    +@@ t/t3800-mktag.sh: tagger T A Gger <tagger@example.com> 1206478233 -0500\n    + \n    + EOF\n    + \n    +-test_expect_mktag_success 'require a blank line before an empty body (1)'\n    ++test_expect_mktag_success 'allow a blank line before an empty body (1)'\n    + \n    + cat >tag.sig <<EOF\n    + object $head\n     @@ t/t3800-mktag.sh: tag mytag\n      tagger T A Gger <tagger@example.com> 1206478233 -0500\n      EOF\n      \n    --check_verify_failure 'disallow no header / body newline separator' \\\n    +-check_verify_failure 'require a blank line before an empty body (2)' \\\n     -\t'^error:.* extraHeaderEntry:'\n    -+test_expect_success 'allow no header / body newline separator' '\n    -+\tgit mktag <tag.sig\n    -+'\n    ++test_expect_mktag_success 'allow no blank line before an empty body (2)'\n      \n      ############################################################\n      # 24. create valid tag\n18:  b6a22f2f992 ! 21:  3c991dda02b mktag: convert to parse-options\n    @@ builtin/mktag.c: static int verify_object_in_tag(struct object_id *tagged_oid, i\n     \n      ## t/t3800-mktag.sh ##\n     @@ t/t3800-mktag.sh: test_expect_success 'setup' '\n    - \thead=$(git rev-parse --verify HEAD)\n    + \tblob=$(git rev-parse --verify HEAD:B.t)\n      '\n      \n     +test_expect_success 'basic usage' '\n19:  7fc0b81df7d ! 22:  16db5858a08 mktag: mark strings for translation\n    @@ builtin/mktag.c: int cmd_mktag(int argc, const char **argv, const char *prefix)\n      \n      \tstrbuf_release(&buf);\n      \tputs(oid_to_hex(&result));\n    -\n    - ## t/t3800-mktag.sh ##\n    -@@ t/t3800-mktag.sh: test_description='git mktag: tag object verify test'\n    - check_verify_failure () {\n    - \texpect=\"$2\"\n    - \ttest_expect_success \"$1\" '\n    --\t\ttest_must_fail git mktag <tag.sig 2>message &&\n    -+\t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n    -+\t\t\tgit mktag <tag.sig 2>message &&\n    - \t\tgrep \"$expect\" message\n    - \t'\n    - }\n20:  6fa443d528c ! 23:  94d6aca6962 mktag: add a --no-strict option\n    @@ Documentation/git-mktag.txt: SYNOPSIS\n     +OPTIONS\n     +-------\n     +\n    -+--no-strict::\n    ++--strict::\n     +\tBy default mktag turns on the equivalent of\n    -+\tlinkgit:git-fsck[1] `--strict` mode. This disables it.\n    ++\tlinkgit:git-fsck[1] `--strict` mode. Use `--no-strict` to\n    ++\tdisable it.\n     +\n      DESCRIPTION\n      -----------\n    @@ builtin/mktag.c: static char const * const builtin_mktag_usage[] = {\n      \tN_(\"git mktag\"),\n      \tNULL\n      };\n    -+static int option_no_strict;\n    ++static int option_strict = 1;\n      \n      static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n      \n    @@ builtin/mktag.c: static int mktag_fsck_error_func(struct fsck_options *o,\n      {\n      \tswitch (msg_type) {\n      \tcase FSCK_WARN:\n    -+\t\tif (option_no_strict) {\n    ++\t\tif (!option_strict) {\n     +\t\t\tfprintf_ln(stderr, _(\"warning: tag input does not pass fsck: %s\"), message);\n     +\t\t\treturn 0;\n     +\n    @@ builtin/mktag.c: static int verify_object_in_tag(struct object_id *tagged_oid, i\n      int cmd_mktag(int argc, const char **argv, const char *prefix)\n      {\n      \tstatic struct option builtin_mktag_options[] = {\n    -+\t\tOPT_NO_BOOL(0, \"no-strict\", &option_no_strict,\n    -+\t\t\t    N_(\"don't do strict fsck checks\")),\n    ++\t\tOPT_BOOL(0, \"strict\", &option_strict,\n    ++\t\t\t N_(\"enable more strict checking\")),\n      \t\tOPT_END(),\n      \t};\n      \tstruct strbuf buf = STRBUF_INIT;\n     \n    - ## parse-options.h ##\n    -@@ parse-options.h: struct option {\n    - #define OPT_COUNTUP(s, l, v, h)     OPT_COUNTUP_F(s, l, v, h, 0)\n    - #define OPT_SET_INT(s, l, v, h, i)  OPT_SET_INT_F(s, l, v, h, i, 0)\n    - #define OPT_BOOL(s, l, v, h)        OPT_BOOL_F(s, l, v, h, 0)\n    -+#define OPT_NO_BOOL(s, l, v, h)     OPT_BOOL_F(s, l, v, h, PARSE_OPT_NONEG)\n    - #define OPT_HIDDEN_BOOL(s, l, v, h) { OPTION_SET_INT, (s), (l), (v), NULL, \\\n    - \t\t\t\t      (h), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1}\n    - #define OPT_CMDMODE(s, l, v, h, i)  { OPTION_SET_INT, (s), (l), (v), NULL, \\\n    -\n      ## t/t3800-mktag.sh ##\n     @@ t/t3800-mktag.sh: test_description='git mktag: tag object verify test'\n      # given in the expect.pat file.\n    @@ t/t3800-mktag.sh: test_description='git mktag: tag object verify test'\n     +\t\tgrep '$2' message &&\n     +\t\tif test '$3' != '--no-strict'\n     +\t\tthen\n    -+\t\t\ttest_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&\n    ++\t\t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n    ++\t\t\t\tgit mktag --no-strict <tag.sig 2>message.no-strict &&\n     +\t\t\tgrep '$2' message.no-strict\n     +\t\tfi\n     +\t\"\n      }\n      \n      test_expect_mktag_success() {\n    -@@ t/t3800-mktag.sh: test_expect_success 'basic usage' '\n    - \tEOF\n    - \tgit mktag <tag.sig &&\n    - \tgit mktag --end-of-options <tag.sig &&\n    --\ttest_expect_code 129 git mktag --unknown-option\n    -+\ttest_expect_code 129 git mktag --unknown-option &&\n    -+\ttest_expect_code 129 git mktag --strict\n    - '\n    - \n    - ############################################################\n     @@ t/t3800-mktag.sh: too short for a tag\n      EOF\n      \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413484","messageId":"20210105194252.627-2-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 01/23] mktag doc: say <hash> not <sha1>","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:30Z","receivedAt":"2021-01-05T19:44:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the \"mktag\" documentation to refer to the input hash as just\n\"hash\", not \"sha1\". This command has supported SHA-256 for a while\nnow.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex fa6a7561236..a158428eb9f 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -23,7 +23,7 @@ Tag Format\n A tag signature file, to be fed to this command's standard input,\n has a very simple fixed format: four lines of\n \n-  object <sha1>\n+  object <hash>\n   type <typename>\n   tag <tagname>\n   tagger <tagger>\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413485","messageId":"20210105194252.627-4-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 03/23] mktag doc: update to explain why to use this","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:32Z","receivedAt":"2021-01-05T19:44:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the mktag documentation to compare itself to the similar\n\"hash-object -t tag\" command. Before this someone reading the\ndocumentation wouldn't have much of an idea what the difference\nwas.\n\nLet's allude to our own validation logic, and cross-link the \"mktag\"\nand \"hash-object\" documentation to aid discover-ability. A follow-up\nchange to migrate \"mktag\" to use \"fsck\" validation will make the part\nabout validation logic clearer.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt | 17 +++++++++++++----\n 1 file changed, 13 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex 1b0667e372a..20af1915e9e 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -3,7 +3,7 @@ git-mktag(1)\n \n NAME\n ----\n-git-mktag - Creates a tag object\n+git-mktag - Creates a tag object with extra validation\n \n \n SYNOPSIS\n@@ -13,10 +13,19 @@ SYNOPSIS\n \n DESCRIPTION\n -----------\n-Reads a tag contents on standard input and creates a tag object\n-that can also be used to sign other objects.\n \n-The output is the new tag's <object> identifier.\n+Reads a tag contents on standard input and creates a tag object. The\n+output is the new tag's <object> identifier.\n+\n+This command is mostly equivalent to linkgit:git-hash-object[1]\n+invoked with `-t tag -w --stdin`. I.e. both of these will create and\n+write a tag found in `my-tag`:\n+\n+    git mktag <my-tag\n+    git hash-object -t tag -w --stdin <my-tag\n+\n+The difference is that mktag will die before writing the tag if the\n+tag doesn't pass a sanity check.\n \n Tag Format\n ----------\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413486","messageId":"20210105194252.627-5-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 04/23] mktag tests: don't needlessly use a subshell","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:33Z","receivedAt":"2021-01-05T19:44:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The use of a subshell dates back to e9b20943b77 (t/t3800: do not use a\ntemporary file to hold expected result., 2008-01-04). It's not needed\nanymore, if it ever was.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex d696aa4e52e..0e411e3c45f 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -14,7 +14,7 @@ test_description='git mktag: tag object verify test'\n check_verify_failure () {\n \texpect=\"$2\"\n \ttest_expect_success \"$1\" '\n-\t\t( test_must_fail git mktag <tag.sig 2>message ) &&\n+\t\ttest_must_fail git mktag <tag.sig 2>message &&\n \t\tgrep \"$expect\" message\n \t'\n }\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413487","messageId":"20210105194252.627-3-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 02/23] mktag doc: grammar fix, when exists -> when it exists","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:31Z","receivedAt":"2021-01-05T19:44:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend the wording of documentation added in 6cfec03680 (mktag:\nminimally update the description., 2007-06-10). It makes more sense to\nsay \"when it exists\" here, as we're referring to \"the message\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex a158428eb9f..1b0667e372a 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -29,7 +29,7 @@ has a very simple fixed format: four lines of\n   tagger <tagger>\n \n followed by some 'optional' free-form message (some tags created\n-by older Git may not have `tagger` line).  The message, when\n+by older Git may not have `tagger` line).  The message, when it\n exists, is separated by a blank line from the header.  The\n message part may contain a signature that Git itself doesn't\n care about, but that can be verified with gpg.\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413488","messageId":"20210105194252.627-7-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 06/23] mktag tests: remove needless SHA-1 hardcoding","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:35Z","receivedAt":"2021-01-05T19:44:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the tests amended in acb49d1cc8b (t3800: make hash-size\nindependent, 2019-08-18) even more to make them independent of either\nSHA-1 or SHA-256.\n\nSome of these tests were failing for the wrong reasons. The first one\nbeing modified here would fail because the line starts with \"xxxxxx\"\ninstead of \"object\", the rest of the line doesn't matter.\n\nLet's just put a valid hash on the rest of the line anyway to narrow\nthe test down for just the s/object/xxxxxx/ case.\n\nThe second one being modified here would fail under\nGIT_TEST_DEFAULT_HASH=sha256 because <some sha-1 length garbage> is an\ninvalid SHA-256, but we should really be testing <some sha-256 length\ngarbage> when under SHA-256.\n\nThis doesn't really matter since we should be able to trust other\nparts of the code to validate things in the 0-9a-f range, but let's\nkeep it for good measure.\n\nThere's a later test which tests an invalid SHA which looks like a\nvalid one, to stress the \"We refuse to tag something we can't\nverify[...]\" logic in mktag.c.\n\nBut here we're testing for a SHA-length string which contains\ncharacters outside of the /[0-9a-f]/i set.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex dd21a1247a2..e1a2892f58b 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -41,7 +41,7 @@ check_verify_failure 'Tag object length check' \\\n #  2. object line label check\n \n cat >tag.sig <<EOF\n-xxxxxx 139e9b33986b1c2670fff52c5067603117b3e895\n+xxxxxx $head\n type tag\n tag mytag\n tagger . <> 0 +0000\n@@ -51,10 +51,10 @@ EOF\n check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n \n ############################################################\n-#  3. object line SHA1 check\n+#  3. object line hash check\n \n cat >tag.sig <<EOF\n-object zz9e9b33986b1c2670fff52c5067603117b3e895\n+object $(echo $head | tr 0-9a-f z)\n type tag\n tag mytag\n tagger . <> 0 +0000\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413489","messageId":"20210105194252.627-6-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 05/23] mktag tests: use \"test_commit\" helper","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:34Z","receivedAt":"2021-01-05T19:44:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Replace ad-hoc setup of a single commit in the \"mktag\" tests with our\nstandard helper pattern. The old setup dated back to 446c6faec69 (New\ntests and en-passant modifications to mktag., 2006-07-29) before the\nhelper existed.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 4 +---\n 1 file changed, 1 insertion(+), 3 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 0e411e3c45f..dd21a1247a2 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -23,9 +23,7 @@ check_verify_failure () {\n # first create a commit, so we have a valid object/type\n # for the tag.\n test_expect_success 'setup' '\n-\techo Hello >A &&\n-\tgit update-index --add A &&\n-\tgit commit -m \"Initial commit\" &&\n+\ttest_commit A &&\n \thead=$(git rev-parse --verify HEAD)\n '\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413490","messageId":"20210105194252.627-8-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 07/23] mktag tests: don't redirect stderr to a file needlessly","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:36Z","receivedAt":"2021-01-05T19:44:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove the redirection of stderr to \"message\" in the valid tag\ntest. This pattern seems to have been copy/pasted from the failure\ncase in 446c6faec6 (New tests and en-passant modifications to mktag.,\n2006-07-29).\n\nWhile I'm at it do the same for the \"replace\" tests. The tag creation\nI'm changing here seems to have been copy/pasted from the \"mktag\"\ntests to those tests in cc400f50112 (mktag: call\n\"check_sha1_signature\" with the replacement sha1, 2009-01-23).\n\nNobody examines the contents of the resulting \"message\" file, so the\nnet result is that error messages cannot be seen in \"sh t3800-mktag.sh\n-v\" output.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh   | 4 ++--\n t/t6050-replace.sh | 2 +-\n 2 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex e1a2892f58b..c542c3e1a8e 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -222,7 +222,7 @@ EOF\n \n test_expect_success \\\n     'allow empty tag email' \\\n-    'git mktag <tag.sig >.git/refs/tags/mytag 2>message'\n+    'git mktag <tag.sig >.git/refs/tags/mytag'\n \n ############################################################\n # 16. disallow spaces in tag email\n@@ -350,7 +350,7 @@ EOF\n \n test_expect_success \\\n     'create valid tag' \\\n-    'git mktag <tag.sig >.git/refs/tags/mytag 2>message'\n+    'git mktag <tag.sig >.git/refs/tags/mytag'\n \n ############################################################\n # 25. check mytag\ndiff --git a/t/t6050-replace.sh b/t/t6050-replace.sh\nindex c80dc10b8f1..0dbe086118a 100755\n--- a/t/t6050-replace.sh\n+++ b/t/t6050-replace.sh\n@@ -129,7 +129,7 @@ tagger T A Gger <> 0 +0000\n EOF\n \n test_expect_success 'tag replaced commit' '\n-     git mktag <tag.sig >.git/refs/tags/mytag 2>message\n+     git mktag <tag.sig >.git/refs/tags/mytag\n '\n \n test_expect_success '\"git fsck\" works' '\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413491","messageId":"20210105194252.627-9-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 08/23] mktag tests: don't create \"mytag\" twice","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:37Z","receivedAt":"2021-01-05T19:44:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change a test added in e0aaf781f6 (mktag.c: improve verification of\ntagger field and tests, 2008-03-27) to not create \"mytag\", which\nshould only be created and verified at the end in an earlier test\nadded in 446c6faec6 (New tests and en-passant modifications to mktag.,\n2006-07-29).\n\nWhile we're at it let's prevent a similar logic error from creeping\ninto the test by asserting that \"mytag\" doesn't exist before we create\nit. Let's do this by moving the test to use \"update-ref\", instead of\nour own homebrew ad-hoc refstore update.\n\nWe're not really testing for anything yet by creating the tag at the\nend here. A subsequent commit will change that.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 17 +++++------------\n 1 file changed, 5 insertions(+), 12 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex c542c3e1a8e..bb300d567db 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -222,7 +222,7 @@ EOF\n \n test_expect_success \\\n     'allow empty tag email' \\\n-    'git mktag <tag.sig >.git/refs/tags/mytag'\n+    'git mktag <tag.sig'\n \n ############################################################\n # 16. disallow spaces in tag email\n@@ -348,16 +348,9 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500\n \n EOF\n \n-test_expect_success \\\n-    'create valid tag' \\\n-    'git mktag <tag.sig >.git/refs/tags/mytag'\n-\n-############################################################\n-# 25. check mytag\n-\n-test_expect_success \\\n-    'check mytag' \\\n-    'git tag -l | grep mytag'\n-\n+test_expect_success 'create valid tag' '\n+\tgit mktag <tag.sig >hash &&\n+\tgit update-ref refs/tags/mytag $(cat hash) $(test_oid zero)\n+'\n \n test_done\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413492","messageId":"20210105194252.627-12-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 11/23] mktag tests: test \"hash-object\" compatibility","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:40Z","receivedAt":"2021-01-05T19:44:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change all the successful \"mktag\" tests to test that \"hash-object\"\nproduces the same hash for the input, and that fsck passes for\nboth.\n\nThis tests e.g. that \"mktag\" doesn't trim its input or otherwise munge\nit in a way that \"hash-object\" doesn't.\n\nSince we're doing an \"fsck --strict\" here at the end let's incorporate\nthe creation of the \"mytag\" name into this test, removing the\nspecial-case at the end of the file.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 31 +++++++++++++++++--------------\n 1 file changed, 17 insertions(+), 14 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 661b62f0912..47380292223 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -19,6 +19,19 @@ check_verify_failure () {\n \t'\n }\n \n+test_expect_mktag_success() {\n+\ttest_expect_success \"$1\" '\n+\t\tgit hash-object -t tag -w --stdin <tag.sig >expected &&\n+\t\tgit fsck --strict &&\n+\n+\t\tgit mktag <tag.sig >hash &&\n+\t\ttest_cmp expected hash &&\n+\t\ttest_when_finished \"git update-ref -d refs/tags/mytag $(cat hash)\" &&\n+\t\tgit update-ref refs/tags/mytag $(cat hash) $(test_oid zero) &&\n+\t\tgit fsck --strict\t\n+\t'\n+}\n+\n ###########################################################\n # first create a commit, so we have a valid object/type\n # for the tag.\n@@ -220,9 +233,7 @@ tagger T A Gger <> 0 +0000\n \n EOF\n \n-test_expect_success \\\n-    'allow empty tag email' \\\n-    'git mktag <tag.sig'\n+test_expect_mktag_success 'allow empty tag email'\n \n ############################################################\n # 16. disallow spaces in tag email\n@@ -347,9 +358,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500\n this line comes after an extra newline\n EOF\n \n-test_expect_success 'allow extra newlines at start of body' '\n-\tgit mktag <tag.sig\n-'\n+test_expect_mktag_success 'allow extra newlines at start of body'\n \n cat >tag.sig <<EOF\n object $head\n@@ -359,9 +368,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500\n \n EOF\n \n-test_expect_success 'require a blank line before an empty body (1)' '\n-\tgit mktag <tag.sig\n-'\n+test_expect_mktag_success 'require a blank line before an empty body (1)'\n \n cat >tag.sig <<EOF\n object $head\n@@ -384,10 +391,6 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500\n \n EOF\n \n-test_expect_success 'create valid tag' '\n-\tgit mktag <tag.sig >hash &&\n-\tgit update-ref refs/tags/mytag $(cat hash) $(test_oid zero) &&\n-\tgit fsck --strict\n-'\n+test_expect_mktag_success 'create valid tag object'\n \n test_done\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413493","messageId":"20210105194252.627-14-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 13/23] mktag tests: test verify_object() with replaced objects","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:42Z","receivedAt":"2021-01-05T19:44:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add tests to demonstrate what \"mktag\" does in the face of replaced\nobjects.\n\nThere was an existing test for replaced objects fed to \"mktag\" added\nin cc400f50112 (mktag: call \"check_sha1_signature\" with the\nreplacement sha1, 2009-01-23), but that one only tests a\ncommit->commit mapping. Not a mapping to a different type as like\nwe're also testing for here. We could remove the \"mktag\" test in\nt6050-replace.sh now if the created tag wasn't being used by a\nsubsequent \"fsck\" test.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 35 ++++++++++++++++++++++++++++++++++-\n 1 file changed, 34 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex ced540035d4..fbaf648491f 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -38,7 +38,11 @@ test_expect_mktag_success() {\n # for the tag.\n test_expect_success 'setup' '\n \ttest_commit A &&\n-\thead=$(git rev-parse --verify HEAD)\n+\ttest_commit B &&\n+\thead=$(git rev-parse --verify HEAD) &&\n+\thead_parent=$(git rev-parse --verify HEAD~) &&\n+\ttree=$(git rev-parse HEAD^{tree}) &&\n+\tblob=$(git rev-parse --verify HEAD:B.t)\n '\n \n ############################################################\n@@ -180,6 +184,35 @@ tagger . <> 0 +0000\n \n EOF\n \n+check_verify_failure 'verify object (hash/type) check -- mismatched type, valid object' \\\n+\t'^error: char7: could not verify object'\n+\n+############################################################\n+#  9.5. verify object (hash/type) check -- replacement\n+\n+test_expect_success 'setup replacement of commit -> commit and tree -> blob' '\n+\tgit replace $head_parent $head &&\n+\tgit replace -f $tree $blob\n+'\n+\n+cat >tag.sig <<EOF\n+object $head_parent\n+type commit\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+test_expect_mktag_success 'tag to a commit replaced by another commit'\n+\n+cat >tag.sig <<EOF\n+object $tree\n+type tree\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n check_verify_failure 'verify object (hash/type) check -- mismatched type, valid object' \\\n \t'^error: char7: could not verify object'\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413494","messageId":"20210105194252.627-13-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 12/23] mktag tests: improve verify_object() test coverage","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:41Z","receivedAt":"2021-01-05T19:44:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The verify_object() function in \"mktag.c\" is tasked with ensuring that\nour tag refers to a valid object.\n\nThe existing test for this might fail because it was also testing that\n\"type taggg\" didn't refer to a valid object type (it should be \"type\ntag\"), or because we referred to a valid object but got the type\nwrong.\n\nLet's split these tests up, so we're testing all combinations of a\nnon-existing object and in invalid/wrong \"type\" lines.\n\nWe need to provide GIT_TEST_GETTEXT_POISON=false here because the\n\"invalid object type\" error is emitted by\nparse_loose_header_extended(), which has that message already marked\nfor translation. Another option would be to use test_i18ngrep, but I\nprefer always running the test, not skipping it under gettext poison\ntesting.\n\nI'm not testing this in combination with \"git replace\". That'll be\ndone in a subsequent commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 40 +++++++++++++++++++++++++++++++++++++---\n 1 file changed, 37 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 47380292223..ced540035d4 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -14,7 +14,8 @@ test_description='git mktag: tag object verify test'\n check_verify_failure () {\n \texpect=\"$2\"\n \ttest_expect_success \"$1\" '\n-\t\ttest_must_fail git mktag <tag.sig 2>message &&\n+\t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n+\t\t\tgit mktag <tag.sig 2>message &&\n \t\tgrep \"$expect\" message\n \t'\n }\n@@ -136,19 +137,52 @@ check_verify_failure '\"type\" line type-name length check' \\\n \t'^error: char.*: type too long$'\n \n ############################################################\n-#  9. verify object (SHA1/type) check\n+#  9. verify object (hash/type) check\n \n cat >tag.sig <<EOF\n object $(test_oid deadbeef)\n+type tag\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+check_verify_failure 'verify object (hash/type) check -- correct type, nonexisting object' \\\n+\t'^error: char7: could not verify object.*$'\n+\n+cat >tag.sig <<EOF\n+object $head\n type tagggg\n tag mytag\n tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure 'verify object (SHA1/type) check' \\\n+check_verify_failure 'verify object (hash/type) check -- made-up type, valid object' \\\n+\t'^fatal: invalid object type'\n+\n+cat >tag.sig <<EOF\n+object $(test_oid deadbeef)\n+type tagggg\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n \t'^error: char7: could not verify object.*$'\n \n+cat >tag.sig <<EOF\n+object $head\n+type tree\n+tag mytag\n+tagger . <> 0 +0000\n+\n+EOF\n+\n+check_verify_failure 'verify object (hash/type) check -- mismatched type, valid object' \\\n+\t'^error: char7: could not verify object'\n+\n ############################################################\n # 10. verify tag-name check\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413495","messageId":"20210105194252.627-10-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 09/23] mktag tests: run \"fsck\" after creating \"mytag\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:38Z","receivedAt":"2021-01-05T19:44:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the last test in the file to run an \"fsck --strict\" after\ncreating the tag at the end.\n\nWe're just doing this for good measure to check that fsck behaves as\nexpected now that there's finally a reference for our valid tag. Other\ntests going to be checking this elsewhere, but it's nice to cover all\nthe edge cases in this test to make it as self-contained as possible.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex bb300d567db..048000cda9a 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -350,7 +350,8 @@ EOF\n \n test_expect_success 'create valid tag' '\n \tgit mktag <tag.sig >hash &&\n-\tgit update-ref refs/tags/mytag $(cat hash) $(test_oid zero)\n+\tgit update-ref refs/tags/mytag $(cat hash) $(test_oid zero) &&\n+\tgit fsck --strict\n '\n \n test_done\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413496","messageId":"20210105194252.627-11-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 10/23] mktag tests: stress test whitespace handling","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:39Z","receivedAt":"2021-01-05T19:44:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add tests for a couple of whitespace edge cases around the header/body\nboundary.\n\nI consider the requirement for a blank line before the empty body a\nbug, it's a long-standing regression which goes against the command's\ndocumented behavior. This bug will be addressed in a follow-up change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 36 ++++++++++++++++++++++++++++++++++++\n 1 file changed, 36 insertions(+)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 048000cda9a..661b62f0912 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -337,6 +337,42 @@ EOF\n check_verify_failure 'detect invalid header entry' \\\n \t'^error: char.*: trailing garbage in tag header$'\n \n+cat >tag.sig <<EOF\n+object $head\n+type commit\n+tag mytag\n+tagger T A Gger <tagger@example.com> 1206478233 -0500\n+\n+\n+this line comes after an extra newline\n+EOF\n+\n+test_expect_success 'allow extra newlines at start of body' '\n+\tgit mktag <tag.sig\n+'\n+\n+cat >tag.sig <<EOF\n+object $head\n+type commit\n+tag mytag\n+tagger T A Gger <tagger@example.com> 1206478233 -0500\n+\n+EOF\n+\n+test_expect_success 'require a blank line before an empty body (1)' '\n+\tgit mktag <tag.sig\n+'\n+\n+cat >tag.sig <<EOF\n+object $head\n+type commit\n+tag mytag\n+tagger T A Gger <tagger@example.com> 1206478233 -0500\n+EOF\n+\n+check_verify_failure 'require a blank line before an empty body (2)' \\\n+\t'^error: char.*: trailing garbage in tag header$'\n+\n ############################################################\n # 24. create valid tag\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413497","messageId":"20210105194252.627-15-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 14/23] mktag: use default strbuf_read() hint","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:43Z","receivedAt":"2021-01-05T19:44:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the hardcoded hint of 2^12 to 0. The default strbuf hint is\nperfectly fine here, and the only reason we were hardcoding it is\nbecause it survived migration from a pre-strbuf fixed-sized buffer.\n\nSee fd17f5b5f77 (Replace all read_fd use with strbuf_read, and get rid\nof it., 2007-09-10) for that migration.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 4982d3a93ef..ff7ac8e0e5d 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -161,7 +161,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (argc != 1)\n \t\tusage(\"git mktag\");\n \n-\tif (strbuf_read(&buf, 0, 4096) < 0) {\n+\tif (strbuf_read(&buf, 0, 0) < 0) {\n \t\tdie_errno(\"could not read from stdin\");\n \t}\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413498","messageId":"20210105194252.627-16-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 15/23] mktag: remove redundant braces in one-line body \"if\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:44Z","receivedAt":"2021-01-05T19:45:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This minor stylistic churn is usually something we'd avoid, but if we\ndon't do this then the file after changes in subsequent commits will\nonly have this minor style inconsistency, so let's change this while\nwe're at it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex ff7ac8e0e5d..97ca5f28b1b 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -161,9 +161,8 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (argc != 1)\n \t\tusage(\"git mktag\");\n \n-\tif (strbuf_read(&buf, 0, 0) < 0) {\n+\tif (strbuf_read(&buf, 0, 0) < 0)\n \t\tdie_errno(\"could not read from stdin\");\n-\t}\n \n \t/* Verify it for some basic sanity: it needs to start with\n \t   \"object <sha1>\\ntype\\ntagger \" */\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413499","messageId":"20210105194252.627-17-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 16/23] mktag: use puts(str) instead of printf(\"%s\\n\", str)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:45Z","receivedAt":"2021-01-05T19:45:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This introduces no functional change, but refactors the print-out of\nthe hash at the end to do the same thing with less code.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 97ca5f28b1b..d89a3c201de 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -173,6 +173,6 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \t\tdie(\"unable to write tag file\");\n \n \tstrbuf_release(&buf);\n-\tprintf(\"%s\\n\", oid_to_hex(&result));\n+\tputs(oid_to_hex(&result));\n \treturn 0;\n }\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413500","messageId":"20210105194252.627-22-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 21/23] mktag: convert to parse-options","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:50Z","receivedAt":"2021-01-05T19:45:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Convert the \"mktag\" command to use parse-options.h instead of its own\nad-hoc argc handling. This doesn't matter much in practice since it\ndoesn't support any options, but removes another special-case in our\ncodebase, and makes it easier to add options to it in the future.\n\nIt does marginally improve the situation for programs that want to\nexecute git commands in a consistent manner and e.g. always use\n--end-of-options. E.g. \"gitaly\" does that, and has a blacklist of\nbuilt-ins that don't support --end-of-options. This is one less\nspecial case for it and other similar programs to support.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c  | 14 ++++++++++++--\n t/t3800-mktag.sh | 12 ++++++++++++\n 2 files changed, 24 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 373926d7e0c..18b8492f4d4 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -1,10 +1,16 @@\n #include \"builtin.h\"\n+#include \"parse-options.h\"\n #include \"tag.h\"\n #include \"replace-object.h\"\n #include \"object-store.h\"\n #include \"fsck.h\"\n #include \"config.h\"\n \n+static char const * const builtin_mktag_usage[] = {\n+\tN_(\"git mktag\"),\n+\tNULL\n+};\n+\n static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \n static int mktag_config(const char *var, const char *value, void *cb)\n@@ -60,13 +66,17 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n \n int cmd_mktag(int argc, const char **argv, const char *prefix)\n {\n+\tstatic struct option builtin_mktag_options[] = {\n+\t\tOPT_END(),\n+\t};\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct object_id tagged_oid;\n \tint tagged_type;\n \tstruct object_id result;\n \n-\tif (argc != 1)\n-\t\tusage(\"git mktag\");\n+\targc = parse_options(argc, argv, NULL,\n+\t\t\t     builtin_mktag_options,\n+\t\t\t     builtin_mktag_usage, 0);\n \n \tif (strbuf_read(&buf, 0, 0) < 0)\n \t\tdie_errno(\"could not read from stdin\");\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 601b064e97a..0ddd0f01cc7 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -45,6 +45,18 @@ test_expect_success 'setup' '\n \tblob=$(git rev-parse --verify HEAD:B.t)\n '\n \n+test_expect_success 'basic usage' '\n+\tcat >tag.sig <<-EOF &&\n+\tobject $head\n+\ttype commit\n+\ttag mytag\n+\ttagger T A Gger <tagger@example.com> 1206478233 -0500\n+\tEOF\n+\tgit mktag <tag.sig &&\n+\tgit mktag --end-of-options <tag.sig &&\n+\ttest_expect_code 129 git mktag --unknown-option\n+'\n+\n ############################################################\n #  1. length check\n \n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413501","messageId":"20210105194252.627-20-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 19/23] mktag: allow turning off fsck.extraHeaderEntry","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:48Z","receivedAt":"2021-01-05T19:45:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In earlier commits mktag learned to use the fsck machinery, at which\npoint we needed to add fsck.extraHeaderEntry so it could be as strict\nabout extra headers as it's been ever since it was implemented.\n\nBut it's not nice to need to switch away from \"mktag\" to \"hash-object\"\n+ manual \"fsck\" just because you'd like to have an extra header. So\nlet's support turning it off by getting \"fsck.*\" variables from the\nconfig.\n\nPedantically speaking it's still not possible to make \"mktag\" behave\njust like \"hash-object -t tag\" does, since we're unconditionally going\nto check the referenced object in verify_object_in_tag(), which is our\nown check, and not one that exists in fsck.c.\n\nBut the spirit of \"this works like fsck\" is preserved, in that if you\ncreated such a tag with \"hash-object\" and did a full \"fsck\" on the\nrepository it would also error out about that invalid object, it just\nwouldn't emit the same message as fsck does.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt |  5 ++++-\n builtin/mktag.c             | 11 ++++++++++-\n t/t3800-mktag.sh            | 14 ++++++++++++++\n 3 files changed, 28 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex fa070e21a0b..79813ff8df0 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -32,7 +32,10 @@ would run by default in that all `fsck.<msg-id>` messages are promoted\n from warnings to errors (so e.g. a missing \"tagger\" line is an error).\n \n Extra headers in the object are also an error under mktag, but ignored\n-by linkgit:git-fsck[1]\n+by linkgit:git-fsck[1]. This extra check can be turned off by setting\n+the appropriate `fsck.<msg-id>` varible:\n+\n+    git -c fsck.extraHeaderEntry=ignore mktag <my-tag-with-headers\n \n Tag Format\n ----------\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 4dd35bc79e2..373926d7e0c 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -3,6 +3,14 @@\n #include \"replace-object.h\"\n #include \"object-store.h\"\n #include \"fsck.h\"\n+#include \"config.h\"\n+\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n+\n+static int mktag_config(const char *var, const char *value, void *cb)\n+{\n+\treturn fsck_config_internal(var, value, cb, &fsck_options);\n+}\n \n static int mktag_fsck_error_func(struct fsck_options *o,\n \t\t\t\t const struct object_id *oid,\n@@ -53,7 +61,6 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n int cmd_mktag(int argc, const char **argv, const char *prefix)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n-\tstruct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \tstruct object_id tagged_oid;\n \tint tagged_type;\n \tstruct object_id result;\n@@ -66,6 +73,8 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \n \tfsck_options.error_func = mktag_fsck_error_func;\n \tfsck_set_msg_type(&fsck_options, \"extraheaderentry\", \"warn\");\n+\t/* config might set fsck.extraHeaderEntry=* again */\n+\tgit_config(mktag_config, NULL);\n \tif (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,\n \t\t\t\t&tagged_oid, &tagged_type))\n \t\tdie(\"tag on stdin did not pass our strict fsck check\");\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 0e2a9a883ba..dccf4503235 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -412,6 +412,20 @@ EOF\n check_verify_failure 'detect invalid header entry' \\\n \t'^error:.* extraHeaderEntry:'\n \n+test_expect_success 'invalid header entry config & fsck' '\n+\ttest_must_fail git mktag <tag.sig &&\n+\ttest_must_fail git -c fsck.extraHeaderEntry=error mktag <tag.sig &&\n+\ttest_must_fail git -c fsck.extraHeaderEntry=warn mktag <tag.sig &&\n+\tgit -c fsck.extraHeaderEntry=ignore mktag <tag.sig &&\n+\tgit fsck &&\n+\tenv GIT_TEST_GETTEXT_POISON=false \\\n+\t\tgit -c fsck.extraHeaderEntry=warn fsck 2>err &&\n+\tgrep \"warning .*extraHeaderEntry:\" err &&\n+\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n+\t\tgit -c fsck.extraHeaderEntry=error 2>err fsck &&\n+\tgrep \"error .* extraHeaderEntry:\" err\n+'\n+\n cat >tag.sig <<EOF\n object $head\n type commit\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413502","messageId":"20210105194252.627-24-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 23/23] mktag: add a --no-strict option","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:52Z","receivedAt":"2021-01-05T19:45:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that mktag has been migrated to use the fsck machinery to check\nits input, it makes sense to teach it to run in the equivalent of \"git\nfsck\"'s default mode, instead of hardcoding \"git fsck --strict\". Let's\ndo that and support the \"--no-strict\" option.\n\nSince this is a new option we don't need to cater to parse-option.c's\ndefault of automatically supporting --strict. So let's use\nPARSE_OPT_NONEG, using a new trivial helper macro.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt |  8 ++++++++\n builtin/mktag.c             |  9 +++++++++\n t/t3800-mktag.sh            | 33 +++++++++++++++++++++++----------\n 3 files changed, 40 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex 79813ff8df0..17a2603a600 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -11,6 +11,14 @@ SYNOPSIS\n [verse]\n 'git mktag'\n \n+OPTIONS\n+-------\n+\n+--strict::\n+\tBy default mktag turns on the equivalent of\n+\tlinkgit:git-fsck[1] `--strict` mode. Use `--no-strict` to\n+\tdisable it.\n+\n DESCRIPTION\n -----------\n \ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 9b04b61c2bb..41a399a69e4 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -10,6 +10,7 @@ static char const * const builtin_mktag_usage[] = {\n \tN_(\"git mktag\"),\n \tNULL\n };\n+static int option_strict = 1;\n \n static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \n@@ -25,6 +26,12 @@ static int mktag_fsck_error_func(struct fsck_options *o,\n {\n \tswitch (msg_type) {\n \tcase FSCK_WARN:\n+\t\tif (!option_strict) {\n+\t\t\tfprintf_ln(stderr, _(\"warning: tag input does not pass fsck: %s\"), message);\n+\t\t\treturn 0;\n+\n+\t\t}\n+\t\t/* fallthrough */\n \tcase FSCK_ERROR:\n \t\t/*\n \t\t * We treat both warnings and errors as errors, things\n@@ -67,6 +74,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n int cmd_mktag(int argc, const char **argv, const char *prefix)\n {\n \tstatic struct option builtin_mktag_options[] = {\n+\t\tOPT_BOOL(0, \"strict\", &option_strict,\n+\t\t\t N_(\"enable more strict checking\")),\n \t\tOPT_END(),\n \t};\n \tstruct strbuf buf = STRBUF_INIT;\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 0ddd0f01cc7..4d76ccbbc19 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -12,12 +12,17 @@ test_description='git mktag: tag object verify test'\n # given in the expect.pat file.\n \n check_verify_failure () {\n-\texpect=\"$2\"\n-\ttest_expect_success \"$1\" '\n+\ttest_expect_success \"$1\" \"\n \t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n \t\t\tgit mktag <tag.sig 2>message &&\n-\t\tgrep \"$expect\" message\n-\t'\n+\t\tgrep '$2' message &&\n+\t\tif test '$3' != '--no-strict'\n+\t\tthen\n+\t\t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n+\t\t\t\tgit mktag --no-strict <tag.sig 2>message.no-strict &&\n+\t\t\tgrep '$2' message.no-strict\n+\t\tfi\n+\t\"\n }\n \n test_expect_mktag_success() {\n@@ -65,7 +70,7 @@ too short for a tag\n EOF\n \n check_verify_failure 'Tag object length check' \\\n-\t'^error:.* missingObject:'\n+\t'^error:.* missingObject:' 'strict'\n \n ############################################################\n #  2. object line label check\n@@ -240,7 +245,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify tag-name check' \\\n-\t'^error:.* badTagName:'\n+\t'^error:.* badTagName:' '--no-strict'\n \n ############################################################\n # 11. tagger line label check #1\n@@ -254,7 +259,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #1' \\\n-\t'^error:.* missingTaggerEntry:'\n+\t'^error:.* missingTaggerEntry:' '--no-strict'\n \n ############################################################\n # 12. tagger line label check #2\n@@ -269,7 +274,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #2' \\\n-\t'^error:.* missingTaggerEntry:'\n+\t'^error:.* missingTaggerEntry:' '--no-strict'\n \n ############################################################\n # 13. allow missing tag author name like fsck\n@@ -298,7 +303,7 @@ tagger T A Gger <\n EOF\n \n check_verify_failure 'disallow malformed tagger' \\\n-\t'^error:.* badEmail:'\n+\t'^error:.* badEmail:' '--no-strict'\n \n ############################################################\n # 15. allow empty tag email\n@@ -422,13 +427,21 @@ this line should not be here\n EOF\n \n check_verify_failure 'detect invalid header entry' \\\n-\t'^error:.* extraHeaderEntry:'\n+\t'^error:.* extraHeaderEntry:' '--no-strict'\n \n test_expect_success 'invalid header entry config & fsck' '\n \ttest_must_fail git mktag <tag.sig &&\n+\tgit mktag --no-strict <tag.sig &&\n+\n \ttest_must_fail git -c fsck.extraHeaderEntry=error mktag <tag.sig &&\n+\ttest_must_fail git -c fsck.extraHeaderEntry=error mktag --no-strict <tag.sig &&\n+\n \ttest_must_fail git -c fsck.extraHeaderEntry=warn mktag <tag.sig &&\n+\tgit -c fsck.extraHeaderEntry=warn mktag --no-strict <tag.sig &&\n+\n \tgit -c fsck.extraHeaderEntry=ignore mktag <tag.sig &&\n+\tgit -c fsck.extraHeaderEntry=ignore mktag --no-strict <tag.sig &&\n+\n \tgit fsck &&\n \tenv GIT_TEST_GETTEXT_POISON=false \\\n \t\tgit -c fsck.extraHeaderEntry=warn fsck 2>err &&\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413503","messageId":"20210105194252.627-23-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 22/23] mktag: mark strings for translation","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:51Z","receivedAt":"2021-01-05T19:45:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Mark the errors mktag might emit for translation. This is a plumbing\ncommand, but the errors it emits are intended to be human-readable.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/mktag.c | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 18b8492f4d4..9b04b61c2bb 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -31,10 +31,10 @@ static int mktag_fsck_error_func(struct fsck_options *o,\n \t\t * like missing \"tagger\" lines are \"only\" warnings\n \t\t * under fsck, we've always considered them an error.\n \t\t */\n-\t\tfprintf_ln(stderr, \"error: tag input does not pass fsck: %s\", message);\n+\t\tfprintf_ln(stderr, _(\"error: tag input does not pass fsck: %s\"), message);\n \t\treturn 1;\n \tdefault:\n-\t\tBUG(\"%d (FSCK_IGNORE?) should never trigger this callback\",\n+\t\tBUG(_(\"%d (FSCK_IGNORE?) should never trigger this callback\"),\n \t\t    msg_type);\n \t}\n }\n@@ -49,10 +49,10 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n \n \tbuffer = read_object_file(tagged_oid, &type, &size);\n \tif (!buffer)\n-\t\tdie(\"could not read tagged object '%s'\",\n+\t\tdie(_(\"could not read tagged object '%s'\"),\n \t\t    oid_to_hex(tagged_oid));\n \tif (type != *tagged_type)\n-\t\tdie(\"object '%s' tagged as '%s', but is a '%s' type\",\n+\t\tdie(_(\"object '%s' tagged as '%s', but is a '%s' type\"),\n \t\t    oid_to_hex(tagged_oid),\n \t\t    type_name(*tagged_type), type_name(type));\n \n@@ -79,7 +79,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \t\t\t     builtin_mktag_usage, 0);\n \n \tif (strbuf_read(&buf, 0, 0) < 0)\n-\t\tdie_errno(\"could not read from stdin\");\n+\t\tdie_errno(_(\"could not read from stdin\"));\n \n \tfsck_options.error_func = mktag_fsck_error_func;\n \tfsck_set_msg_type(&fsck_options, \"extraheaderentry\", \"warn\");\n@@ -87,13 +87,13 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tgit_config(mktag_config, NULL);\n \tif (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,\n \t\t\t\t&tagged_oid, &tagged_type))\n-\t\tdie(\"tag on stdin did not pass our strict fsck check\");\n+\t\tdie(_(\"tag on stdin did not pass our strict fsck check\"));\n \n \tif (verify_object_in_tag(&tagged_oid, &tagged_type))\n-\t\tdie(\"tag on stdin did not refer to a valid object\");\n+\t\tdie(_(\"tag on stdin did not refer to a valid object\"));\n \n \tif (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)\n-\t\tdie(\"unable to write tag file\");\n+\t\tdie(_(\"unable to write tag file\"));\n \n \tstrbuf_release(&buf);\n \tputs(oid_to_hex(&result));\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413504","messageId":"20210105194252.627-21-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 20/23] mktag: allow omitting the header/body \\n separator","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:49Z","receivedAt":"2021-01-05T19:45:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change mktag's acceptance rules to accept an empty body without an\nempty line after the header again. This fixes an ancient unintended\ndregression in \"mktag\".\n\nWhen \"mktag\" was introduced in ec4465adb3 (Add \"tag\" objects that can\nbe used to sign other objects., 2005-04-25) the input checks were much\nlooser. When it was documented it 6cfec03680 (mktag: minimally update\nthe description., 2007-06-10) it was clearly intended for this \\n to\nbe optional:\n\n    The message, when [it] exists, is separated by a blank line from\n    the header.\n\nBut then in e0aaf781f6 (mktag.c: improve verification of tagger field\nand tests, 2008-03-27) this was made an error, seemingly by\naccident. It was just a result of the general header checks, and all\nthe tests after that patch have a trailing empty line (but did not\nbefore).\n\nLet's allow this again, and tweak the test semantics changed in\ne0aaf781f6 to remove the redundant empty line. New tests added in\nprevious commits of mine already added an explicit test for allowing\nthe empty line between header and body.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n fsck.c           | 2 ++\n t/t3800-mktag.sh | 6 ++----\n 2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 9067a290a2e..29c1eaca4cf 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -987,6 +987,8 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n \t}\n \telse\n \t\tret = fsck_ident(&buffer, oid, OBJ_TAG, options);\n+\tif (!*buffer)\n+\t\tgoto done;\n \n \tif (!starts_with(buffer, \"\\n\")) {\n \t\t/*\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex dccf4503235..601b064e97a 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -446,7 +446,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500\n \n EOF\n \n-test_expect_mktag_success 'require a blank line before an empty body (1)'\n+test_expect_mktag_success 'allow a blank line before an empty body (1)'\n \n cat >tag.sig <<EOF\n object $head\n@@ -455,8 +455,7 @@ tag mytag\n tagger T A Gger <tagger@example.com> 1206478233 -0500\n EOF\n \n-check_verify_failure 'require a blank line before an empty body (2)' \\\n-\t'^error:.* extraHeaderEntry:'\n+test_expect_mktag_success 'allow no blank line before an empty body (2)'\n \n ############################################################\n # 24. create valid tag\n@@ -466,7 +465,6 @@ object $head\n type commit\n tag mytag\n tagger T A Gger <tagger@example.com> 1206478233 -0500\n-\n EOF\n \n test_expect_mktag_success 'create valid tag object'\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413505","messageId":"20210105194252.627-18-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 17/23] mktag: use fsck instead of custom verify_tag()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:46Z","receivedAt":"2021-01-05T19:45:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the validation logic in \"mktag\" to use fsck's fsck_tag()\ninstead of its own custom parser. Curiously the logic for both dates\nback to the same commit[1]. Let's unify them so we're not maintaining\ntwo sets functions to verify that a tag is OK.\n\nThe behavior of fsck_tag() and the old \"mktag\" code being removed here\nis different in few aspects.\n\nI think it makes sense to remove some of those checks, namely:\n\n A. fsck only cares that the timezone matches [-+][0-9]{4}. The mktag\n    code disallowed values larger than 1400.\n\n    Yes there's currently no timezone with a greater offset[2], but\n    since we allow any number of non-offical timezones (e.g. +1234)\n    passing this through seems fine. Git also won't break in the\n    future if e.g. French Polynesia decides it needs to outdo the Line\n    Islands when it comes to timezone extravagance.\n\n B. fsck allows missing author names such as \"tagger <email>\", mktag\n    wouldn't, but would allow e.g. \"tagger [2 spaces] <email>\" (but\n    not \"tagger [1 space] <email>\"). Now we allow all of these.\n\n C. Like B, but \"mktag\" disallowed spaces in the <email> part, fsck\n    allows it.\n\nIn some ways fsck_tag() is stricter than \"mktag\" was, namely:\n\n D. fsck disallows zero-padded dates, but mktag didn't care. So\n    e.g. the timestamp \"0000000000 +0000\" produces an error now. A\n    test in \"t1006-cat-file.sh\" relied on this, it's been changed to\n    use \"hash-object\" (without fsck) instead.\n\nThere was one check I deemed worth keeping by porting it over to\nfsck_tag():\n\n E. \"mktag\" did not allow any custom headers, and by extension (as an\n    empty commit is allowed) also forbade an extra stray trailing\n    newline after the headers it knew about.\n\n    Add a new check in the \"ignore\" category to fsck and use it. This\n    somewhat abuses the facility added in efaba7cc77f (fsck:\n    optionally ignore specific fsck issues completely, 2015-06-22).\n\n    This is somewhat of hack, but probably the least invasive change\n    we can make here. The fsck command will shuffle these categories\n    around, e.g. under --strict the \"info\" becomes a \"warn\" and \"warn\"\n    becomes \"error\". Existing users of fsck's (and others,\n    e.g. index-pack) --strict option rely on this.\n\n    So we need to put something into a category that'll be ignored by\n    all existing users of the API. Pretending that\n    fsck.extraHeaderEntry=error (\"ignore\" by default) was set serves\n    to do this for us.\n\n1. ec4465adb38 (Add \"tag\" objects that can be used to sign other\n   objects., 2005-04-25)\n\n2. https://en.wikipedia.org/wiki/List_of_UTC_time_offsets\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-mktag.txt |   9 +-\n builtin/mktag.c             | 196 +++++++++---------------------------\n fsck.c                      |  32 +++++-\n fsck.h                      |   9 ++\n t/t1006-cat-file.sh         |   2 +-\n t/t3800-mktag.sh            |  63 ++++++------\n 6 files changed, 127 insertions(+), 184 deletions(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex 20af1915e9e..fa070e21a0b 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -25,7 +25,14 @@ write a tag found in `my-tag`:\n     git hash-object -t tag -w --stdin <my-tag\n \n The difference is that mktag will die before writing the tag if the\n-tag doesn't pass a sanity check.\n+tag doesn't pass a linkgit:git-fsck[1] check.\n+\n+The \"fsck\" check done mktag is stricter than what linkgit:git-fsck[1]\n+would run by default in that all `fsck.<msg-id>` messages are promoted\n+from warnings to errors (so e.g. a missing \"tagger\" line is an error).\n+\n+Extra headers in the object are also an error under mktag, but ignored\n+by linkgit:git-fsck[1]\n \n Tag Format\n ----------\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex d89a3c201de..4dd35bc79e2 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -2,160 +2,60 @@\n #include \"tag.h\"\n #include \"replace-object.h\"\n #include \"object-store.h\"\n+#include \"fsck.h\"\n \n-/*\n- * A signature file has a very simple fixed format: four lines\n- * of \"object <sha1>\" + \"type <typename>\" + \"tag <tagname>\" +\n- * \"tagger <committer>\", followed by a blank line, a free-form tag\n- * message and a signature block that git itself doesn't care about,\n- * but that can be verified with gpg or similar.\n- *\n- * The first four lines are guaranteed to be at least 83 bytes:\n- * \"object <sha1>\\n\" is 48 bytes, \"type tag\\n\" at 9 bytes is the\n- * shortest possible type-line, \"tag .\\n\" at 6 bytes is the shortest\n- * single-character-tag line, and \"tagger . <> 0 +0000\\n\" at 20 bytes is\n- * the shortest possible tagger-line.\n- */\n-\n-/*\n- * We refuse to tag something we can't verify. Just because.\n- */\n-static int verify_object(const struct object_id *oid, const char *expected_type)\n+static int mktag_fsck_error_func(struct fsck_options *o,\n+\t\t\t\t const struct object_id *oid,\n+\t\t\t\t enum object_type object_type,\n+\t\t\t\t int msg_type, const char *message)\n {\n-\tint ret = -1;\n-\tenum object_type type;\n-\tunsigned long size;\n-\tvoid *buffer = read_object_file(oid, &type, &size);\n-\tconst struct object_id *repl = lookup_replace_object(the_repository, oid);\n-\n-\tif (buffer) {\n-\t\tif (type == type_from_string(expected_type)) {\n-\t\t\tret = check_object_signature(the_repository, repl,\n-\t\t\t\t\t\t     buffer, size,\n-\t\t\t\t\t\t     expected_type);\n-\t\t}\n-\t\tfree(buffer);\n+\tswitch (msg_type) {\n+\tcase FSCK_WARN:\n+\tcase FSCK_ERROR:\n+\t\t/*\n+\t\t * We treat both warnings and errors as errors, things\n+\t\t * like missing \"tagger\" lines are \"only\" warnings\n+\t\t * under fsck, we've always considered them an error.\n+\t\t */\n+\t\tfprintf_ln(stderr, \"error: tag input does not pass fsck: %s\", message);\n+\t\treturn 1;\n+\tdefault:\n+\t\tBUG(\"%d (FSCK_IGNORE?) should never trigger this callback\",\n+\t\t    msg_type);\n \t}\n-\treturn ret;\n }\n \n-static int verify_tag(char *buffer, unsigned long size)\n+static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n {\n-\tint typelen;\n-\tchar type[20];\n-\tstruct object_id oid;\n-\tconst char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;\n-\tsize_t len;\n-\n-\tif (size < 84)\n-\t\treturn error(\"wanna fool me ? you obviously got the size wrong !\");\n-\n-\tbuffer[size] = 0;\n-\n-\t/* Verify object line */\n-\tobject = buffer;\n-\tif (memcmp(object, \"object \", 7))\n-\t\treturn error(\"char%d: does not start with \\\"object \\\"\", 0);\n-\n-\tif (parse_oid_hex(object + 7, &oid, &p))\n-\t\treturn error(\"char%d: could not get SHA1 hash\", 7);\n-\n-\t/* Verify type line */\n-\ttype_line = p + 1;\n-\tif (memcmp(type_line - 1, \"\\ntype \", 6))\n-\t\treturn error(\"char%d: could not find \\\"\\\\ntype \\\"\", 47);\n-\n-\t/* Verify tag-line */\n-\ttag_line = strchr(type_line, '\\n');\n-\tif (!tag_line)\n-\t\treturn error(\"char%\"PRIuMAX\": could not find next \\\"\\\\n\\\"\",\n-\t\t\t\t(uintmax_t) (type_line - buffer));\n-\ttag_line++;\n-\tif (memcmp(tag_line, \"tag \", 4) || tag_line[4] == '\\n')\n-\t\treturn error(\"char%\"PRIuMAX\": no \\\"tag \\\" found\",\n-\t\t\t\t(uintmax_t) (tag_line - buffer));\n-\n-\t/* Get the actual type */\n-\ttypelen = tag_line - type_line - strlen(\"type \\n\");\n-\tif (typelen >= sizeof(type))\n-\t\treturn error(\"char%\"PRIuMAX\": type too long\",\n-\t\t\t\t(uintmax_t) (type_line+5 - buffer));\n-\n-\tmemcpy(type, type_line+5, typelen);\n-\ttype[typelen] = 0;\n-\n-\t/* Verify that the object matches */\n-\tif (verify_object(&oid, type))\n-\t\treturn error(\"char%d: could not verify object %s\", 7, oid_to_hex(&oid));\n-\n-\t/* Verify the tag-name: we don't allow control characters or spaces in it */\n-\ttag_line += 4;\n-\tfor (;;) {\n-\t\tunsigned char c = *tag_line++;\n-\t\tif (c == '\\n')\n-\t\t\tbreak;\n-\t\tif (c > ' ')\n-\t\t\tcontinue;\n-\t\treturn error(\"char%\"PRIuMAX\": could not verify tag name\",\n-\t\t\t\t(uintmax_t) (tag_line - buffer));\n-\t}\n-\n-\t/* Verify the tagger line */\n-\ttagger_line = tag_line;\n-\n-\tif (memcmp(tagger_line, \"tagger \", 7))\n-\t\treturn error(\"char%\"PRIuMAX\": could not find \\\"tagger \\\"\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/*\n-\t * Check for correct form for name and email\n-\t * i.e. \" <\" followed by \"> \" on _this_ line\n-\t * No angle brackets within the name or email address fields.\n-\t * No spaces within the email address field.\n-\t */\n-\ttagger_line += 7;\n-\tif (!(lb = strstr(tagger_line, \" <\")) || !(rb = strstr(lb+2, \"> \")) ||\n-\t\tstrpbrk(tagger_line, \"<>\\n\") != lb+1 ||\n-\t\tstrpbrk(lb+2, \"><\\n \") != rb)\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tagger field\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/* Check for author name, at least one character, space is acceptable */\n-\tif (lb == tagger_line)\n-\t\treturn error(\"char%\"PRIuMAX\": missing tagger name\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\n-\t/* timestamp, 1 or more digits followed by space */\n-\ttagger_line = rb + 2;\n-\tif (!(len = strspn(tagger_line, \"0123456789\")))\n-\t\treturn error(\"char%\"PRIuMAX\": missing tag timestamp\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line += len;\n-\tif (*tagger_line != ' ')\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tag timestamp\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line++;\n-\n-\t/* timezone, 5 digits [+-]hhmm, max. 1400 */\n-\tif (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&\n-\t      strspn(tagger_line+1, \"0123456789\") == 4 &&\n-\t      tagger_line[5] == '\\n' && atoi(tagger_line+1) <= 1400))\n-\t\treturn error(\"char%\"PRIuMAX\": malformed tag timezone\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n-\ttagger_line += 6;\n-\n-\t/* Verify the blank line separating the header from the body */\n-\tif (*tagger_line != '\\n')\n-\t\treturn error(\"char%\"PRIuMAX\": trailing garbage in tag header\",\n-\t\t\t(uintmax_t) (tagger_line - buffer));\n+\tint ret;\n+\tenum object_type type;\n+\tunsigned long size;\n+\tvoid *buffer;\n+\tconst struct object_id *repl;\n+\n+\tbuffer = read_object_file(tagged_oid, &type, &size);\n+\tif (!buffer)\n+\t\tdie(\"could not read tagged object '%s'\",\n+\t\t    oid_to_hex(tagged_oid));\n+\tif (type != *tagged_type)\n+\t\tdie(\"object '%s' tagged as '%s', but is a '%s' type\",\n+\t\t    oid_to_hex(tagged_oid),\n+\t\t    type_name(*tagged_type), type_name(type));\n+\n+\trepl = lookup_replace_object(the_repository, tagged_oid);\n+\tret = check_object_signature(the_repository, repl,\n+\t\t\t\t     buffer, size, type_name(*tagged_type));\n+\tfree(buffer);\n \n-\t/* The actual stuff afterwards we don't care about.. */\n-\treturn 0;\n+\treturn ret;\n }\n \n int cmd_mktag(int argc, const char **argv, const char *prefix)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n+\tstruct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n+\tstruct object_id tagged_oid;\n+\tint tagged_type;\n \tstruct object_id result;\n \n \tif (argc != 1)\n@@ -164,10 +64,14 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)\n \tif (strbuf_read(&buf, 0, 0) < 0)\n \t\tdie_errno(\"could not read from stdin\");\n \n-\t/* Verify it for some basic sanity: it needs to start with\n-\t   \"object <sha1>\\ntype\\ntagger \" */\n-\tif (verify_tag(buf.buf, buf.len) < 0)\n-\t\tdie(\"invalid tag signature file\");\n+\tfsck_options.error_func = mktag_fsck_error_func;\n+\tfsck_set_msg_type(&fsck_options, \"extraheaderentry\", \"warn\");\n+\tif (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,\n+\t\t\t\t&tagged_oid, &tagged_type))\n+\t\tdie(\"tag on stdin did not pass our strict fsck check\");\n+\n+\tif (verify_object_in_tag(&tagged_oid, &tagged_type))\n+\t\tdie(\"tag on stdin did not refer to a valid object\");\n \n \tif (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)\n \t\tdie(\"unable to write tag file\");\ndiff --git a/fsck.c b/fsck.c\nindex f82e2fe9e30..bed5e20e03b 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -80,7 +80,9 @@ static struct oidset gitmodules_done = OIDSET_INIT;\n \t/* infos (reported as warnings, but ignored by default) */ \\\n \tFUNC(GITMODULES_PARSE, INFO) \\\n \tFUNC(BAD_TAG_NAME, INFO) \\\n-\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n+\tFUNC(MISSING_TAGGER_ENTRY, INFO) \\\n+\t/* ignored (elevated when requested) */ \\\n+\tFUNC(EXTRA_HEADER_ENTRY, IGNORE)\n \n #define MSG_ID(id, msg_type) FSCK_MSG_##id,\n enum fsck_msg_id {\n@@ -911,6 +913,16 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \t\t    unsigned long size, struct fsck_options *options)\n {\n \tstruct object_id tagged_oid;\n+\tint tagged_type;\n+\treturn fsck_tag_standalone(oid, buffer, size, options, &tagged_oid,\n+\t\t\t\t   &tagged_type);\n+}\n+\n+int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n+\t\t\tunsigned long size, struct fsck_options *options,\n+\t\t\tstruct object_id *tagged_oid,\n+\t\t\tint *tagged_type)\n+{\n \tint ret = 0;\n \tchar *eol;\n \tstruct strbuf sb = STRBUF_INIT;\n@@ -924,7 +936,7 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n-\tif (parse_oid_hex(buffer, &tagged_oid, &p) || *p != '\\n') {\n+\tif (parse_oid_hex(buffer, tagged_oid, &p) || *p != '\\n') {\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n \t\tif (ret)\n \t\t\tgoto done;\n@@ -940,7 +952,8 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n-\tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n+\t*tagged_type = type_from_string_gently(buffer, eol - buffer, 1);\n+\tif (*tagged_type < 0)\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_TYPE, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n@@ -975,6 +988,19 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,\n \telse\n \t\tret = fsck_ident(&buffer, oid, OBJ_TAG, options);\n \n+\tif (!starts_with(buffer, \"\\n\")) {\n+\t\t/*\n+\t\t * The verify_headers() check will allow\n+\t\t * e.g. \"[...]tagger <tagger>\\nsome\n+\t\t * garbage\\n\\nmessage\" to pass, thinking \"some\n+\t\t * garbage\" could be a custom header. E.g. \"mktag\"\n+\t\t * doesn't want any unknown headers.\n+\t\t */\n+\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_EXTRA_HEADER_ENTRY, \"invalid format - extra header(s) after 'tagger'\");\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n+\n done:\n \tstrbuf_release(&sb);\n \treturn ret;\ndiff --git a/fsck.h b/fsck.h\nindex 69cf715e798..29ee4c45e87 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -62,6 +62,15 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options);\n int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options);\n \n+/*\n+ * fsck a tag, and pass info about it back to the caller. This is\n+ * exposed fsck_object() internals for git-mktag(1).\n+ */\n+int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n+\t\t\tunsigned long size, struct fsck_options *options,\n+\t\t\tstruct object_id *tagged_oid,\n+\t\t\tint *tag_type);\n+\n /*\n  * Some fsck checks are context-dependent, and may end up queued; run this\n  * after completing all fsck_object() calls in order to resolve any remaining\ndiff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh\nindex 2f501d2dc94..5d2dc99b74a 100755\n--- a/t/t1006-cat-file.sh\n+++ b/t/t1006-cat-file.sh\n@@ -166,7 +166,7 @@ tag_content=\"$tag_header_without_timestamp 0000000000 +0000\n \n $tag_description\"\n \n-tag_sha1=$(echo_without_newline \"$tag_content\" | git mktag)\n+tag_sha1=$(echo_without_newline \"$tag_content\" | git hash-object -t tag --stdin -w)\n tag_size=$(strlen \"$tag_content\")\n \n run_tests 'tag' $tag_sha1 $tag_size \"$tag_content\" \"$tag_content\" 1\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex fbaf648491f..0e2a9a883ba 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -53,7 +53,7 @@ too short for a tag\n EOF\n \n check_verify_failure 'Tag object length check' \\\n-\t'^error: .*size wrong.*$'\n+\t'^error:.* missingObject:'\n \n ############################################################\n #  2. object line label check\n@@ -66,7 +66,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"object\" line label check' '^error: char0: .*\"object \"$'\n+check_verify_failure '\"object\" line label check' '^error:.* missingObject:'\n \n ############################################################\n #  3. object line hash check\n@@ -79,7 +79,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"object\" line SHA1 check' '^error: char7: .*SHA1 hash$'\n+check_verify_failure '\"object\" line check' '^error:.* badObjectSha1:'\n \n ############################################################\n #  4. type line label check\n@@ -92,7 +92,7 @@ tagger . <> 0 +0000\n \n EOF\n \n-check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n+check_verify_failure '\"type\" line label check' '^error:.* missingTypeEntry:'\n \n ############################################################\n #  5. type line eol check\n@@ -100,7 +100,7 @@ check_verify_failure '\"type\" line label check' '^error: char.*: .*\"\\\\ntype \"$'\n echo \"object $head\" >tag.sig\n printf \"type tagsssssssssssssssssssssssssssssss\" >>tag.sig\n \n-check_verify_failure '\"type\" line eol check' '^error: char.*: .*\"\\\\n\"$'\n+check_verify_failure '\"type\" line eol check' '^error:.* unterminatedHeader:'\n \n ############################################################\n #  6. tag line label check #1\n@@ -114,7 +114,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure '\"tag\" line label check #1' \\\n-\t'^error: char.*: no \"tag \" found$'\n+\t'^error:.* missingTagEntry:'\n \n ############################################################\n #  7. tag line label check #2\n@@ -126,7 +126,7 @@ tag\n EOF\n \n check_verify_failure '\"tag\" line label check #2' \\\n-\t'^error: char.*: no \"tag \" found$'\n+\t'^error:.* badType:'\n \n ############################################################\n #  8. type line type-name length check\n@@ -138,7 +138,7 @@ tag mytag\n EOF\n \n check_verify_failure '\"type\" line type-name length check' \\\n-\t'^error: char.*: type too long$'\n+\t'^error:.* badType:'\n \n ############################################################\n #  9. verify object (hash/type) check\n@@ -152,7 +152,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (hash/type) check -- correct type, nonexisting object' \\\n-\t'^error: char7: could not verify object.*$'\n+\t'^fatal: could not read tagged object'\n \n cat >tag.sig <<EOF\n object $head\n@@ -163,7 +163,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (hash/type) check -- made-up type, valid object' \\\n-\t'^fatal: invalid object type'\n+\t'^error:.* badType:'\n \n cat >tag.sig <<EOF\n object $(test_oid deadbeef)\n@@ -174,7 +174,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n-\t'^error: char7: could not verify object.*$'\n+\t'^error:.* badType:'\n \n cat >tag.sig <<EOF\n object $head\n@@ -185,7 +185,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (hash/type) check -- mismatched type, valid object' \\\n-\t'^error: char7: could not verify object'\n+\t'^fatal: object.*tagged as.*tree.*but is.*commit'\n \n ############################################################\n #  9.5. verify object (hash/type) check -- replacement\n@@ -214,7 +214,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify object (hash/type) check -- mismatched type, valid object' \\\n-\t'^error: char7: could not verify object'\n+\t'^fatal: object.*tagged as.*tree.*but is.*blob'\n \n ############################################################\n # 10. verify tag-name check\n@@ -228,7 +228,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify tag-name check' \\\n-\t'^error: char.*: could not verify tag name$'\n+\t'^error:.* badTagName:'\n \n ############################################################\n # 11. tagger line label check #1\n@@ -242,7 +242,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #1' \\\n-\t'^error: char.*: could not find \"tagger \"$'\n+\t'^error:.* missingTaggerEntry:'\n \n ############################################################\n # 12. tagger line label check #2\n@@ -257,10 +257,10 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #2' \\\n-\t'^error: char.*: could not find \"tagger \"$'\n+\t'^error:.* missingTaggerEntry:'\n \n ############################################################\n-# 13. disallow missing tag author name\n+# 13. allow missing tag author name like fsck\n \n cat >tag.sig <<EOF\n object $head\n@@ -271,8 +271,7 @@ tagger  <> 0 +0000\n This is filler\n EOF\n \n-check_verify_failure 'disallow missing tag author name' \\\n-\t'^error: char.*: missing tagger name$'\n+test_expect_mktag_success 'allow missing tag author name'\n \n ############################################################\n # 14. disallow missing tag author name\n@@ -287,7 +286,7 @@ tagger T A Gger <\n EOF\n \n check_verify_failure 'disallow malformed tagger' \\\n-\t'^error: char.*: malformed tagger field$'\n+\t'^error:.* badEmail:'\n \n ############################################################\n # 15. allow empty tag email\n@@ -303,7 +302,7 @@ EOF\n test_expect_mktag_success 'allow empty tag email'\n \n ############################################################\n-# 16. disallow spaces in tag email\n+# 16. allow spaces in tag email like fsck\n \n cat >tag.sig <<EOF\n object $head\n@@ -313,8 +312,7 @@ tagger T A Gger <tag ger@example.com> 0 +0000\n \n EOF\n \n-check_verify_failure 'disallow spaces in tag email' \\\n-\t'^error: char.*: malformed tagger field$'\n+test_expect_mktag_success 'allow spaces in tag email like fsck'\n \n ############################################################\n # 17. disallow missing tag timestamp\n@@ -328,7 +326,7 @@ tagger T A Gger <tagger@example.com>__\n EOF\n \n check_verify_failure 'disallow missing tag timestamp' \\\n-\t'^error: char.*: missing tag timestamp$'\n+\t'^error:.* badDate:'\n \n ############################################################\n # 18. detect invalid tag timestamp1\n@@ -342,7 +340,7 @@ tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008\n EOF\n \n check_verify_failure 'detect invalid tag timestamp1' \\\n-\t'^error: char.*: missing tag timestamp$'\n+\t'^error:.* badDate:'\n \n ############################################################\n # 19. detect invalid tag timestamp2\n@@ -356,7 +354,7 @@ tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500\n EOF\n \n check_verify_failure 'detect invalid tag timestamp2' \\\n-\t'^error: char.*: malformed tag timestamp$'\n+\t'^error:.* badDate:'\n \n ############################################################\n # 20. detect invalid tag timezone1\n@@ -370,7 +368,7 @@ tagger T A Gger <tagger@example.com> 1206478233 GMT\n EOF\n \n check_verify_failure 'detect invalid tag timezone1' \\\n-\t'^error: char.*: malformed tag timezone$'\n+\t'^error:.* badTimezone:'\n \n ############################################################\n # 21. detect invalid tag timezone2\n@@ -384,10 +382,10 @@ tagger T A Gger <tagger@example.com> 1206478233 +  30\n EOF\n \n check_verify_failure 'detect invalid tag timezone2' \\\n-\t'^error: char.*: malformed tag timezone$'\n+\t'^error:.* badTimezone:'\n \n ############################################################\n-# 22. detect invalid tag timezone3\n+# 22. allow invalid tag timezone3 (the maximum is -1200/+1400)\n \n cat >tag.sig <<EOF\n object $head\n@@ -397,8 +395,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -1430\n \n EOF\n \n-check_verify_failure 'detect invalid tag timezone3' \\\n-\t'^error: char.*: malformed tag timezone$'\n+test_expect_mktag_success 'allow invalid tag timezone'\n \n ############################################################\n # 23. detect invalid header entry\n@@ -413,7 +410,7 @@ this line should not be here\n EOF\n \n check_verify_failure 'detect invalid header entry' \\\n-\t'^error: char.*: trailing garbage in tag header$'\n+\t'^error:.* extraHeaderEntry:'\n \n cat >tag.sig <<EOF\n object $head\n@@ -445,7 +442,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500\n EOF\n \n check_verify_failure 'require a blank line before an empty body (2)' \\\n-\t'^error: char.*: trailing garbage in tag header$'\n+\t'^error:.* extraHeaderEntry:'\n \n ############################################################\n # 24. create valid tag\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413506","messageId":"20210105194252.627-19-avarab@gmail.com","threadId":"51667","inReplyTo":"20201223013606.7972-1-avarab@gmail.com","subject":"[PATCH v5 18/23] fsck: make fsck_config() re-usable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-05T19:42:47Z","receivedAt":"2021-01-05T19:45:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Move the fsck_config() function from builtin/fsck.c to fsck.[ch]. This\nallows for re-using it in other tools that expose fsck logic and want\nto support its configuration variables.\n\nA logical continuation of this change would be to use a common\nfunction for all of {fetch,receive}.fsck.* and fsck.*. See\n5d477a334a6 (fsck (receive-pack): allow demoting errors to warnings,\n2015-06-22) and my own 1362df0d413 (fetch: implement fetch.fsck.*,\n2018-07-27) for the relevant code.\n\nHowever, those routines want to not parse the fsck.skipList into OIDs,\nbut rather pass them along with the --strict option to another\nprocess. It would be possible to refactor that whole thing so we\nsupport e.g. a \"fetch.\" prefix, then just keep track of the skiplist\nas a filename instead of parsing it, and learn to spew that all out\nfrom our internal structures into something we can append to the\n--strict option.\n\nBut instead I'm planning to re-use this in \"mktag\", which'll just\nre-use these \"fsck.*\" variables as-is.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fsck.c | 20 +-------------------\n fsck.c         | 25 +++++++++++++++++++++++++\n fsck.h         |  7 +++++++\n 3 files changed, 33 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex fbf26cafcfd..821e7798c70 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -73,25 +73,7 @@ static const char *printable_type(const struct object_id *oid,\n \n static int fsck_config(const char *var, const char *value, void *cb)\n {\n-\tif (strcmp(var, \"fsck.skiplist\") == 0) {\n-\t\tconst char *path;\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\n-\t\tif (git_config_pathname(&path, var, value))\n-\t\t\treturn 1;\n-\t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n-\t\tfree((char *)path);\n-\t\tfsck_set_msg_types(&fsck_obj_options, sb.buf);\n-\t\tstrbuf_release(&sb);\n-\t\treturn 0;\n-\t}\n-\n-\tif (skip_prefix(var, \"fsck.\", &var)) {\n-\t\tfsck_set_msg_type(&fsck_obj_options, var, value);\n-\t\treturn 0;\n-\t}\n-\n-\treturn git_default_config(var, value, cb);\n+\treturn fsck_config_internal(var, value, cb, &fsck_obj_options);\n }\n \n static int objerror(struct object *obj, const char *err)\ndiff --git a/fsck.c b/fsck.c\nindex bed5e20e03b..9067a290a2e 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -1310,3 +1310,28 @@ int fsck_finish(struct fsck_options *options)\n \toidset_clear(&gitmodules_done);\n \treturn ret;\n }\n+\n+int fsck_config_internal(const char *var, const char *value, void *cb,\n+\t\t\t struct fsck_options *options)\n+{\n+\tif (strcmp(var, \"fsck.skiplist\") == 0) {\n+\t\tconst char *path;\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tif (git_config_pathname(&path, var, value))\n+\t\t\treturn 1;\n+\t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n+\t\tfree((char *)path);\n+\t\tfsck_set_msg_types(options, sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\treturn 0;\n+\t}\n+\n+\tif (skip_prefix(var, \"fsck.\", &var)) {\n+\t\tfsck_set_msg_type(options, var, value);\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_default_config(var, value, cb);\n+}\n+\ndiff --git a/fsck.h b/fsck.h\nindex 29ee4c45e87..423c467feb7 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -103,4 +103,11 @@ void fsck_put_object_name(struct fsck_options *options,\n const char *fsck_describe_object(struct fsck_options *options,\n \t\t\t\t const struct object_id *oid);\n \n+/*\n+ * git_config() callback for use by fsck-y tools that want to support\n+ * fsck.<msg> fsck.skipList etc.\n+ */\n+int fsck_config_internal(const char *var, const char *value, void *cb,\n+\t\t\t struct fsck_options *options);\n+\n #endif\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413520","messageId":"xmqq4kjuygw7.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20210105194252.627-24-avarab@gmail.com","subject":"Re: [PATCH v5 23/23] mktag: add a --no-strict option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-01-06T01:48:40Z","receivedAt":"2021-01-06T01:49:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Now that mktag has been migrated to use the fsck machinery to check\n> its input, it makes sense to teach it to run in the equivalent of \"git\n> fsck\"'s default mode, instead of hardcoding \"git fsck --strict\". Let's\n> do that and support the \"--no-strict\" option.\n>\n> Since this is a new option we don't need to cater to parse-option.c's\n> default of automatically supporting --strict. So let's use\n> PARSE_OPT_NONEG, using a new trivial helper macro.\n\nI do not understand the last paragraph.\n\n    git mktag --no-strict --strict --no-strict\n\nshould follow the usual \"the last one wins\" rule, so I would say\nthat supporting \"--strict\", whether it is done automatically as a\nnatural consequence of use of parse-options or not, would be a good\nthing (also I so not see PARSE_OPT_NONEG in this patch).  Perhaps\nthe log message (and the title) needs updating?\n\n    mktag: add a --[no-]strict option\n\n    Now that mktag has been migrated to use the fsck machinery to\n    check its input, it makes sense to teach it to run in the\n    equivalent of \"git fsck\"'s default mode.  For cases where mktag\n    is used to (re)create a tag object using data from an existing\n    and malformed tag object, the validation may optionally have to\n    be loosened.  Teach the command to take the \"--[no-]strict\"\n    option to do so.\n\nor something?\n"},{"id":"413567","messageId":"20210106114727.8405-1-avarab@gmail.com","threadId":"51667","inReplyTo":"xmqq4kjuygw7.fsf@gitster.c.googlers.com","subject":"[PATCH v6 23/23] mktag: add a --[no-]strict option","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-06T11:47:27Z","receivedAt":"2021-01-06T11:48:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that mktag has been migrated to use the fsck machinery to check\nits input, it makes sense to teach it to run in the equivalent of \"git\nfsck\"'s default mode.\n\nFor cases where mktag is used to (re)create a tag object using data\nfrom an existing and malformed tag object, the validation may\noptionally have to be loosened. Teach the command to take the\n\"--[no-]strict\" option to do so.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n\nOn Wed, Jan 06 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Now that mktag has been migrated to use the fsck machinery to check\n>> its input, it makes sense to teach it to run in the equivalent of \"git\n>> fsck\"'s default mode, instead of hardcoding \"git fsck --strict\". Let's\n>> do that and support the \"--no-strict\" option.\n>>\n>> Since this is a new option we don't need to cater to parse-option.c's\n>> default of automatically supporting --strict. So let's use\n>> PARSE_OPT_NONEG, using a new trivial helper macro.\n>\n> I do not understand the last paragraph.\n\nI forgot to update the commit message when changing this from\nv4->v5. Here's a v6 of just this patch (to save on list traffic) with\nan updated commit message. Thanks!\n\n Documentation/git-mktag.txt |  8 ++++++++\n builtin/mktag.c             |  9 +++++++++\n t/t3800-mktag.sh            | 33 +++++++++++++++++++++++----------\n 3 files changed, 40 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\nindex 79813ff8df0..17a2603a600 100644\n--- a/Documentation/git-mktag.txt\n+++ b/Documentation/git-mktag.txt\n@@ -11,6 +11,14 @@ SYNOPSIS\n [verse]\n 'git mktag'\n \n+OPTIONS\n+-------\n+\n+--strict::\n+\tBy default mktag turns on the equivalent of\n+\tlinkgit:git-fsck[1] `--strict` mode. Use `--no-strict` to\n+\tdisable it.\n+\n DESCRIPTION\n -----------\n \ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 9b04b61c2bb..41a399a69e4 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -10,6 +10,7 @@ static char const * const builtin_mktag_usage[] = {\n \tN_(\"git mktag\"),\n \tNULL\n };\n+static int option_strict = 1;\n \n static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \n@@ -25,6 +26,12 @@ static int mktag_fsck_error_func(struct fsck_options *o,\n {\n \tswitch (msg_type) {\n \tcase FSCK_WARN:\n+\t\tif (!option_strict) {\n+\t\t\tfprintf_ln(stderr, _(\"warning: tag input does not pass fsck: %s\"), message);\n+\t\t\treturn 0;\n+\n+\t\t}\n+\t\t/* fallthrough */\n \tcase FSCK_ERROR:\n \t\t/*\n \t\t * We treat both warnings and errors as errors, things\n@@ -67,6 +74,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n int cmd_mktag(int argc, const char **argv, const char *prefix)\n {\n \tstatic struct option builtin_mktag_options[] = {\n+\t\tOPT_BOOL(0, \"strict\", &option_strict,\n+\t\t\t N_(\"enable more strict checking\")),\n \t\tOPT_END(),\n \t};\n \tstruct strbuf buf = STRBUF_INIT;\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 0ddd0f01cc7..4d76ccbbc19 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -12,12 +12,17 @@ test_description='git mktag: tag object verify test'\n # given in the expect.pat file.\n \n check_verify_failure () {\n-\texpect=\"$2\"\n-\ttest_expect_success \"$1\" '\n+\ttest_expect_success \"$1\" \"\n \t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n \t\t\tgit mktag <tag.sig 2>message &&\n-\t\tgrep \"$expect\" message\n-\t'\n+\t\tgrep '$2' message &&\n+\t\tif test '$3' != '--no-strict'\n+\t\tthen\n+\t\t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n+\t\t\t\tgit mktag --no-strict <tag.sig 2>message.no-strict &&\n+\t\t\tgrep '$2' message.no-strict\n+\t\tfi\n+\t\"\n }\n \n test_expect_mktag_success() {\n@@ -65,7 +70,7 @@ too short for a tag\n EOF\n \n check_verify_failure 'Tag object length check' \\\n-\t'^error:.* missingObject:'\n+\t'^error:.* missingObject:' 'strict'\n \n ############################################################\n #  2. object line label check\n@@ -240,7 +245,7 @@ tagger . <> 0 +0000\n EOF\n \n check_verify_failure 'verify tag-name check' \\\n-\t'^error:.* badTagName:'\n+\t'^error:.* badTagName:' '--no-strict'\n \n ############################################################\n # 11. tagger line label check #1\n@@ -254,7 +259,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #1' \\\n-\t'^error:.* missingTaggerEntry:'\n+\t'^error:.* missingTaggerEntry:' '--no-strict'\n \n ############################################################\n # 12. tagger line label check #2\n@@ -269,7 +274,7 @@ This is filler\n EOF\n \n check_verify_failure '\"tagger\" line label check #2' \\\n-\t'^error:.* missingTaggerEntry:'\n+\t'^error:.* missingTaggerEntry:' '--no-strict'\n \n ############################################################\n # 13. allow missing tag author name like fsck\n@@ -298,7 +303,7 @@ tagger T A Gger <\n EOF\n \n check_verify_failure 'disallow malformed tagger' \\\n-\t'^error:.* badEmail:'\n+\t'^error:.* badEmail:' '--no-strict'\n \n ############################################################\n # 15. allow empty tag email\n@@ -422,13 +427,21 @@ this line should not be here\n EOF\n \n check_verify_failure 'detect invalid header entry' \\\n-\t'^error:.* extraHeaderEntry:'\n+\t'^error:.* extraHeaderEntry:' '--no-strict'\n \n test_expect_success 'invalid header entry config & fsck' '\n \ttest_must_fail git mktag <tag.sig &&\n+\tgit mktag --no-strict <tag.sig &&\n+\n \ttest_must_fail git -c fsck.extraHeaderEntry=error mktag <tag.sig &&\n+\ttest_must_fail git -c fsck.extraHeaderEntry=error mktag --no-strict <tag.sig &&\n+\n \ttest_must_fail git -c fsck.extraHeaderEntry=warn mktag <tag.sig &&\n+\tgit -c fsck.extraHeaderEntry=warn mktag --no-strict <tag.sig &&\n+\n \tgit -c fsck.extraHeaderEntry=ignore mktag <tag.sig &&\n+\tgit -c fsck.extraHeaderEntry=ignore mktag --no-strict <tag.sig &&\n+\n \tgit fsck &&\n \tenv GIT_TEST_GETTEXT_POISON=false \\\n \t\tgit -c fsck.extraHeaderEntry=warn fsck 2>err &&\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"413614","messageId":"xmqqmtxlpuyg.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20210106114727.8405-1-avarab@gmail.com","subject":"Re: [PATCH v6 23/23] mktag: add a --[no-]strict option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-01-06T22:21:59Z","receivedAt":"2021-01-06T22:23:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Now that mktag has been migrated to use the fsck machinery to check\n> its input, it makes sense to teach it to run in the equivalent of \"git\n> fsck\"'s default mode.\n>\n> For cases where mktag is used to (re)create a tag object using data\n> from an existing and malformed tag object, the validation may\n> optionally have to be loosened. Teach the command to take the\n> \"--[no-]strict\" option to do so.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>\n> On Wed, Jan 06 2021, Junio C Hamano wrote:\n>\n>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>>\n>>> Now that mktag has been migrated to use the fsck machinery to check\n>>> its input, it makes sense to teach it to run in the equivalent of \"git\n>>> fsck\"'s default mode, instead of hardcoding \"git fsck --strict\". Let's\n>>> do that and support the \"--no-strict\" option.\n>>>\n>>> Since this is a new option we don't need to cater to parse-option.c's\n>>> default of automatically supporting --strict. So let's use\n>>> PARSE_OPT_NONEG, using a new trivial helper macro.\n>>\n>> I do not understand the last paragraph.\n>\n> I forgot to update the commit message when changing this from\n> v4->v5. Here's a v6 of just this patch (to save on list traffic) with\n> an updated commit message. Thanks!\n\nOkay.  I actually was expecting to hear the reason why the approach\ntaken in v4 to use PARSE_OPT_NONEG is a good one, but I take the\nchange of approach in v5 as the sign that you now agree with me that\na plain vanilla bool that happens to default to true is easier to\nmanage.\n\nWill replace.  Thanks.\n\n>\n>  Documentation/git-mktag.txt |  8 ++++++++\n>  builtin/mktag.c             |  9 +++++++++\n>  t/t3800-mktag.sh            | 33 +++++++++++++++++++++++----------\n>  3 files changed, 40 insertions(+), 10 deletions(-)\n>\n> diff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\n> index 79813ff8df0..17a2603a600 100644\n> --- a/Documentation/git-mktag.txt\n> +++ b/Documentation/git-mktag.txt\n> @@ -11,6 +11,14 @@ SYNOPSIS\n>  [verse]\n>  'git mktag'\n>  \n> +OPTIONS\n> +-------\n> +\n> +--strict::\n> +\tBy default mktag turns on the equivalent of\n> +\tlinkgit:git-fsck[1] `--strict` mode. Use `--no-strict` to\n> +\tdisable it.\n> +\n>  DESCRIPTION\n>  -----------\n>  \n> diff --git a/builtin/mktag.c b/builtin/mktag.c\n> index 9b04b61c2bb..41a399a69e4 100644\n> --- a/builtin/mktag.c\n> +++ b/builtin/mktag.c\n> @@ -10,6 +10,7 @@ static char const * const builtin_mktag_usage[] = {\n>  \tN_(\"git mktag\"),\n>  \tNULL\n>  };\n> +static int option_strict = 1;\n>  \n>  static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n>  \n> @@ -25,6 +26,12 @@ static int mktag_fsck_error_func(struct fsck_options *o,\n>  {\n>  \tswitch (msg_type) {\n>  \tcase FSCK_WARN:\n> +\t\tif (!option_strict) {\n> +\t\t\tfprintf_ln(stderr, _(\"warning: tag input does not pass fsck: %s\"), message);\n> +\t\t\treturn 0;\n> +\n> +\t\t}\n> +\t\t/* fallthrough */\n>  \tcase FSCK_ERROR:\n>  \t\t/*\n>  \t\t * We treat both warnings and errors as errors, things\n> @@ -67,6 +74,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n>  int cmd_mktag(int argc, const char **argv, const char *prefix)\n>  {\n>  \tstatic struct option builtin_mktag_options[] = {\n> +\t\tOPT_BOOL(0, \"strict\", &option_strict,\n> +\t\t\t N_(\"enable more strict checking\")),\n>  \t\tOPT_END(),\n>  \t};\n>  \tstruct strbuf buf = STRBUF_INIT;\n> diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\n> index 0ddd0f01cc7..4d76ccbbc19 100755\n> --- a/t/t3800-mktag.sh\n> +++ b/t/t3800-mktag.sh\n> @@ -12,12 +12,17 @@ test_description='git mktag: tag object verify test'\n>  # given in the expect.pat file.\n>  \n>  check_verify_failure () {\n> -\texpect=\"$2\"\n> -\ttest_expect_success \"$1\" '\n> +\ttest_expect_success \"$1\" \"\n>  \t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n>  \t\t\tgit mktag <tag.sig 2>message &&\n> -\t\tgrep \"$expect\" message\n> -\t'\n> +\t\tgrep '$2' message &&\n> +\t\tif test '$3' != '--no-strict'\n> +\t\tthen\n> +\t\t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n> +\t\t\t\tgit mktag --no-strict <tag.sig 2>message.no-strict &&\n> +\t\t\tgrep '$2' message.no-strict\n> +\t\tfi\n> +\t\"\n>  }\n>  \n>  test_expect_mktag_success() {\n> @@ -65,7 +70,7 @@ too short for a tag\n>  EOF\n>  \n>  check_verify_failure 'Tag object length check' \\\n> -\t'^error:.* missingObject:'\n> +\t'^error:.* missingObject:' 'strict'\n>  \n>  ############################################################\n>  #  2. object line label check\n> @@ -240,7 +245,7 @@ tagger . <> 0 +0000\n>  EOF\n>  \n>  check_verify_failure 'verify tag-name check' \\\n> -\t'^error:.* badTagName:'\n> +\t'^error:.* badTagName:' '--no-strict'\n>  \n>  ############################################################\n>  # 11. tagger line label check #1\n> @@ -254,7 +259,7 @@ This is filler\n>  EOF\n>  \n>  check_verify_failure '\"tagger\" line label check #1' \\\n> -\t'^error:.* missingTaggerEntry:'\n> +\t'^error:.* missingTaggerEntry:' '--no-strict'\n>  \n>  ############################################################\n>  # 12. tagger line label check #2\n> @@ -269,7 +274,7 @@ This is filler\n>  EOF\n>  \n>  check_verify_failure '\"tagger\" line label check #2' \\\n> -\t'^error:.* missingTaggerEntry:'\n> +\t'^error:.* missingTaggerEntry:' '--no-strict'\n>  \n>  ############################################################\n>  # 13. allow missing tag author name like fsck\n> @@ -298,7 +303,7 @@ tagger T A Gger <\n>  EOF\n>  \n>  check_verify_failure 'disallow malformed tagger' \\\n> -\t'^error:.* badEmail:'\n> +\t'^error:.* badEmail:' '--no-strict'\n>  \n>  ############################################################\n>  # 15. allow empty tag email\n> @@ -422,13 +427,21 @@ this line should not be here\n>  EOF\n>  \n>  check_verify_failure 'detect invalid header entry' \\\n> -\t'^error:.* extraHeaderEntry:'\n> +\t'^error:.* extraHeaderEntry:' '--no-strict'\n>  \n>  test_expect_success 'invalid header entry config & fsck' '\n>  \ttest_must_fail git mktag <tag.sig &&\n> +\tgit mktag --no-strict <tag.sig &&\n> +\n>  \ttest_must_fail git -c fsck.extraHeaderEntry=error mktag <tag.sig &&\n> +\ttest_must_fail git -c fsck.extraHeaderEntry=error mktag --no-strict <tag.sig &&\n> +\n>  \ttest_must_fail git -c fsck.extraHeaderEntry=warn mktag <tag.sig &&\n> +\tgit -c fsck.extraHeaderEntry=warn mktag --no-strict <tag.sig &&\n> +\n>  \tgit -c fsck.extraHeaderEntry=ignore mktag <tag.sig &&\n> +\tgit -c fsck.extraHeaderEntry=ignore mktag --no-strict <tag.sig &&\n> +\n>  \tgit fsck &&\n>  \tenv GIT_TEST_GETTEXT_POISON=false \\\n>  \t\tgit -c fsck.extraHeaderEntry=warn fsck 2>err &&\n"},{"id":"413660","messageId":"87zh1lqjg9.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"xmqqmtxlpuyg.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v6 23/23] mktag: add a --[no-]strict option","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-07T07:45:10Z","receivedAt":"2021-01-07T07:45:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Jan 06 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Now that mktag has been migrated to use the fsck machinery to check\n>> its input, it makes sense to teach it to run in the equivalent of \"git\n>> fsck\"'s default mode.\n>>\n>> For cases where mktag is used to (re)create a tag object using data\n>> from an existing and malformed tag object, the validation may\n>> optionally have to be loosened. Teach the command to take the\n>> \"--[no-]strict\" option to do so.\n>>\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>\n>> On Wed, Jan 06 2021, Junio C Hamano wrote:\n>>\n>>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>>>\n>>>> Now that mktag has been migrated to use the fsck machinery to check\n>>>> its input, it makes sense to teach it to run in the equivalent of \"git\n>>>> fsck\"'s default mode, instead of hardcoding \"git fsck --strict\". Let's\n>>>> do that and support the \"--no-strict\" option.\n>>>>\n>>>> Since this is a new option we don't need to cater to parse-option.c's\n>>>> default of automatically supporting --strict. So let's use\n>>>> PARSE_OPT_NONEG, using a new trivial helper macro.\n>>>\n>>> I do not understand the last paragraph.\n>>\n>> I forgot to update the commit message when changing this from\n>> v4->v5. Here's a v6 of just this patch (to save on list traffic) with\n>> an updated commit message. Thanks!\n>\n> Okay.  I actually was expecting to hear the reason why the approach\n> taken in v4 to use PARSE_OPT_NONEG is a good one, but I take the\n> change of approach in v5 as the sign that you now agree with me that\n> a plain vanilla bool that happens to default to true is easier to\n> manage.\n\nThere was no good reason. I couldn't find whether &no_var with \"no-OPT\"\noptions & PARSE_OPT_NONEG was the preferred pattern or &var with a\ndefault of 1 with \"OPT\". There's a few examples of the former in the\ncodebase, but I'm happy to go with the latter.\n\n>>\n>>  Documentation/git-mktag.txt |  8 ++++++++\n>>  builtin/mktag.c             |  9 +++++++++\n>>  t/t3800-mktag.sh            | 33 +++++++++++++++++++++++----------\n>>  3 files changed, 40 insertions(+), 10 deletions(-)\n>>\n>> diff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt\n>> index 79813ff8df0..17a2603a600 100644\n>> --- a/Documentation/git-mktag.txt\n>> +++ b/Documentation/git-mktag.txt\n>> @@ -11,6 +11,14 @@ SYNOPSIS\n>>  [verse]\n>>  'git mktag'\n>>  \n>> +OPTIONS\n>> +-------\n>> +\n>> +--strict::\n>> +\tBy default mktag turns on the equivalent of\n>> +\tlinkgit:git-fsck[1] `--strict` mode. Use `--no-strict` to\n>> +\tdisable it.\n>> +\n>>  DESCRIPTION\n>>  -----------\n>>  \n>> diff --git a/builtin/mktag.c b/builtin/mktag.c\n>> index 9b04b61c2bb..41a399a69e4 100644\n>> --- a/builtin/mktag.c\n>> +++ b/builtin/mktag.c\n>> @@ -10,6 +10,7 @@ static char const * const builtin_mktag_usage[] = {\n>>  \tN_(\"git mktag\"),\n>>  \tNULL\n>>  };\n>> +static int option_strict = 1;\n>>  \n>>  static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n>>  \n>> @@ -25,6 +26,12 @@ static int mktag_fsck_error_func(struct fsck_options *o,\n>>  {\n>>  \tswitch (msg_type) {\n>>  \tcase FSCK_WARN:\n>> +\t\tif (!option_strict) {\n>> +\t\t\tfprintf_ln(stderr, _(\"warning: tag input does not pass fsck: %s\"), message);\n>> +\t\t\treturn 0;\n>> +\n>> +\t\t}\n>> +\t\t/* fallthrough */\n>>  \tcase FSCK_ERROR:\n>>  \t\t/*\n>>  \t\t * We treat both warnings and errors as errors, things\n>> @@ -67,6 +74,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)\n>>  int cmd_mktag(int argc, const char **argv, const char *prefix)\n>>  {\n>>  \tstatic struct option builtin_mktag_options[] = {\n>> +\t\tOPT_BOOL(0, \"strict\", &option_strict,\n>> +\t\t\t N_(\"enable more strict checking\")),\n>>  \t\tOPT_END(),\n>>  \t};\n>>  \tstruct strbuf buf = STRBUF_INIT;\n>> diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\n>> index 0ddd0f01cc7..4d76ccbbc19 100755\n>> --- a/t/t3800-mktag.sh\n>> +++ b/t/t3800-mktag.sh\n>> @@ -12,12 +12,17 @@ test_description='git mktag: tag object verify test'\n>>  # given in the expect.pat file.\n>>  \n>>  check_verify_failure () {\n>> -\texpect=\"$2\"\n>> -\ttest_expect_success \"$1\" '\n>> +\ttest_expect_success \"$1\" \"\n>>  \t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n>>  \t\t\tgit mktag <tag.sig 2>message &&\n>> -\t\tgrep \"$expect\" message\n>> -\t'\n>> +\t\tgrep '$2' message &&\n>> +\t\tif test '$3' != '--no-strict'\n>> +\t\tthen\n>> +\t\t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n>> +\t\t\t\tgit mktag --no-strict <tag.sig 2>message.no-strict &&\n>> +\t\t\tgrep '$2' message.no-strict\n>> +\t\tfi\n>> +\t\"\n>>  }\n>>  \n>>  test_expect_mktag_success() {\n>> @@ -65,7 +70,7 @@ too short for a tag\n>>  EOF\n>>  \n>>  check_verify_failure 'Tag object length check' \\\n>> -\t'^error:.* missingObject:'\n>> +\t'^error:.* missingObject:' 'strict'\n>>  \n>>  ############################################################\n>>  #  2. object line label check\n>> @@ -240,7 +245,7 @@ tagger . <> 0 +0000\n>>  EOF\n>>  \n>>  check_verify_failure 'verify tag-name check' \\\n>> -\t'^error:.* badTagName:'\n>> +\t'^error:.* badTagName:' '--no-strict'\n>>  \n>>  ############################################################\n>>  # 11. tagger line label check #1\n>> @@ -254,7 +259,7 @@ This is filler\n>>  EOF\n>>  \n>>  check_verify_failure '\"tagger\" line label check #1' \\\n>> -\t'^error:.* missingTaggerEntry:'\n>> +\t'^error:.* missingTaggerEntry:' '--no-strict'\n>>  \n>>  ############################################################\n>>  # 12. tagger line label check #2\n>> @@ -269,7 +274,7 @@ This is filler\n>>  EOF\n>>  \n>>  check_verify_failure '\"tagger\" line label check #2' \\\n>> -\t'^error:.* missingTaggerEntry:'\n>> +\t'^error:.* missingTaggerEntry:' '--no-strict'\n>>  \n>>  ############################################################\n>>  # 13. allow missing tag author name like fsck\n>> @@ -298,7 +303,7 @@ tagger T A Gger <\n>>  EOF\n>>  \n>>  check_verify_failure 'disallow malformed tagger' \\\n>> -\t'^error:.* badEmail:'\n>> +\t'^error:.* badEmail:' '--no-strict'\n>>  \n>>  ############################################################\n>>  # 15. allow empty tag email\n>> @@ -422,13 +427,21 @@ this line should not be here\n>>  EOF\n>>  \n>>  check_verify_failure 'detect invalid header entry' \\\n>> -\t'^error:.* extraHeaderEntry:'\n>> +\t'^error:.* extraHeaderEntry:' '--no-strict'\n>>  \n>>  test_expect_success 'invalid header entry config & fsck' '\n>>  \ttest_must_fail git mktag <tag.sig &&\n>> +\tgit mktag --no-strict <tag.sig &&\n>> +\n>>  \ttest_must_fail git -c fsck.extraHeaderEntry=error mktag <tag.sig &&\n>> +\ttest_must_fail git -c fsck.extraHeaderEntry=error mktag --no-strict <tag.sig &&\n>> +\n>>  \ttest_must_fail git -c fsck.extraHeaderEntry=warn mktag <tag.sig &&\n>> +\tgit -c fsck.extraHeaderEntry=warn mktag --no-strict <tag.sig &&\n>> +\n>>  \tgit -c fsck.extraHeaderEntry=ignore mktag <tag.sig &&\n>> +\tgit -c fsck.extraHeaderEntry=ignore mktag --no-strict <tag.sig &&\n>> +\n>>  \tgit fsck &&\n>>  \tenv GIT_TEST_GETTEXT_POISON=false \\\n>>  \t\tgit -c fsck.extraHeaderEntry=warn fsck 2>err &&\n\n"},{"id":"413967","messageId":"20210110132155.GT8396@szeder.dev","threadId":"51667","inReplyTo":"20210105194252.627-13-avarab@gmail.com","subject":"Re: [PATCH v5 12/23] mktag tests: improve verify_object() test coverage","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2021-01-10T13:21:55Z","receivedAt":"2021-01-10T13:22:53Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Tue, Jan 05, 2021 at 08:42:41PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> The verify_object() function in \"mktag.c\" is tasked with ensuring that\n> our tag refers to a valid object.\n> \n> The existing test for this might fail because it was also testing that\n> \"type taggg\" didn't refer to a valid object type (it should be \"type\n> tag\"), or because we referred to a valid object but got the type\n> wrong.\n> \n> Let's split these tests up, so we're testing all combinations of a\n> non-existing object and in invalid/wrong \"type\" lines.\n> \n> We need to provide GIT_TEST_GETTEXT_POISON=false here because the\n> \"invalid object type\" error is emitted by\n> parse_loose_header_extended(), which has that message already marked\n> for translation. Another option would be to use test_i18ngrep, but I\n> prefer always running the test, not skipping it under gettext poison\n> testing.\n\nThis is fairly unconvincing.  Why do you prefer it that way?  What is\nso special in these tests of 'git mktags' that could possibly warrant\nthis special treatment WRT gettext poisoning, as opposed to the other\n~1500 invocations of test_i18n{grep,cmp}?\n\n> I'm not testing this in combination with \"git replace\". That'll be\n> done in a subsequent commit.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t3800-mktag.sh | 40 +++++++++++++++++++++++++++++++++++++---\n>  1 file changed, 37 insertions(+), 3 deletions(-)\n> \n> diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\n> index 47380292223..ced540035d4 100755\n> --- a/t/t3800-mktag.sh\n> +++ b/t/t3800-mktag.sh\n> @@ -14,7 +14,8 @@ test_description='git mktag: tag object verify test'\n>  check_verify_failure () {\n>  \texpect=\"$2\"\n>  \ttest_expect_success \"$1\" '\n> -\t\ttest_must_fail git mktag <tag.sig 2>message &&\n> +\t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n> +\t\t\tgit mktag <tag.sig 2>message &&\n>  \t\tgrep \"$expect\" message\n>  \t'\n>  }\n> @@ -136,19 +137,52 @@ check_verify_failure '\"type\" line type-name length check' \\\n>  \t'^error: char.*: type too long$'\n>  \n>  ############################################################\n> -#  9. verify object (SHA1/type) check\n> +#  9. verify object (hash/type) check\n>  \n>  cat >tag.sig <<EOF\n>  object $(test_oid deadbeef)\n> +type tag\n> +tag mytag\n> +tagger . <> 0 +0000\n> +\n> +EOF\n> +\n> +check_verify_failure 'verify object (hash/type) check -- correct type, nonexisting object' \\\n> +\t'^error: char7: could not verify object.*$'\n> +\n> +cat >tag.sig <<EOF\n> +object $head\n>  type tagggg\n>  tag mytag\n>  tagger . <> 0 +0000\n>  \n>  EOF\n>  \n> -check_verify_failure 'verify object (SHA1/type) check' \\\n> +check_verify_failure 'verify object (hash/type) check -- made-up type, valid object' \\\n> +\t'^fatal: invalid object type'\n> +\n> +cat >tag.sig <<EOF\n> +object $(test_oid deadbeef)\n> +type tagggg\n> +tag mytag\n> +tagger . <> 0 +0000\n> +\n> +EOF\n> +\n> +check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \\\n>  \t'^error: char7: could not verify object.*$'\n>  \n> +cat >tag.sig <<EOF\n> +object $head\n> +type tree\n> +tag mytag\n> +tagger . <> 0 +0000\n> +\n> +EOF\n> +\n> +check_verify_failure 'verify object (hash/type) check -- mismatched type, valid object' \\\n> +\t'^error: char7: could not verify object'\n> +\n>  ############################################################\n>  # 10. verify tag-name check\n>  \n> -- \n> 2.29.2.222.g5d2a92d10f8\n> \n"},{"id":"413979","messageId":"87y2h062jd.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"20210110132155.GT8396@szeder.dev","subject":"Should you use test_i18ngrep or GIT_TEST_GETTEXT_POISON=false?","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-10T18:59:50Z","receivedAt":"2021-01-10T19:00:35Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Jan 10 2021, SZEDER Gábor wrote:\n\n> On Tue, Jan 05, 2021 at 08:42:41PM +0100, Ævar Arnfjörð Bjarmason wrote:\n>> The verify_object() function in \"mktag.c\" is tasked with ensuring that\n>> our tag refers to a valid object.\n>> \n>> The existing test for this might fail because it was also testing that\n>> \"type taggg\" didn't refer to a valid object type (it should be \"type\n>> tag\"), or because we referred to a valid object but got the type\n>> wrong.\n>> \n>> Let's split these tests up, so we're testing all combinations of a\n>> non-existing object and in invalid/wrong \"type\" lines.\n>> \n>> We need to provide GIT_TEST_GETTEXT_POISON=false here because the\n>> \"invalid object type\" error is emitted by\n>> parse_loose_header_extended(), which has that message already marked\n>> for translation. Another option would be to use test_i18ngrep, but I\n>> prefer always running the test, not skipping it under gettext poison\n>> testing.\n>\n> This is fairly unconvincing.  Why do you prefer it that way?  What is\n> so special in these tests of 'git mktags' that could possibly warrant\n> this special treatment WRT gettext poisoning, as opposed to the other\n> ~1500 invocations of test_i18n{grep,cmp}?\n\nYou prodded me about this and Johannes also did off-list. So given that\nthis is already in \"next\" I think it's best to let usage in this mktag\nseries land as-is.\n\nI promise I'll follow-up with something to (at least start to) fix this\ngenerally one way or the other on \"master\" after that. It's a trivial\npart of this particular series.\n\nI.e. either document & migrate away from test_i18ngrep (mostly, in some\ncases it's still convenient), or git rid of this and the few existing\nGIT_TEST_GETTEXT_POISON=false uses.\n\nAnyway, as both the initial author of this poison facility & of its\ncurrent runtime incarnation in git.git, some context on it:\n\nThe need for test_i18ngrep was always a wart. When I submitted the\ninitial i18n patches a big selling point at the time to a (rightly so)\nskeptical audience on the Git ML was that it could almost entirely be\nturned off at compile-time, and that we had something in place to make\nsure we didn't introduce plumbing bugs by overtranslating.\n\nFor a while the mass-i18n patches were about as noisy on-list as the\nsha256 conversion patches were in more recenty memory.\n\nSo if you look through the history you'll see that first we skipped\nwhole tests with the C_LOCALE_OUTPUT prereq, and then test_i18ngrep was\na more granular way to do that:\n\n    git some-command >output && \n    test_i18ngrep \"c locale string\" output\n\nBut since 6cdccfce1e0 (i18n: make GETTEXT_POISON a runtime option,\n2018-11-08) this hasn't been needed. But yes, we have a bit under 10\nyears of test_i18ngrep usage in the test suite now.\n\nBut that stuff is the wart, and purely a historical artifact of this\nhaving once been compile-time only. We don't do this sort of thing with\nany other GIT_TEST_* option, e.g. we have tests like:\n\n    t/t5512-ls-remote.sh:   GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref --heads . >actual &&\n    t/t5512-ls-remote.sh-   test_cmp expect actual &&\n\nNot:\n\n    t/t5512-ls-remote.sh:   git ls-remote --symref --heads . >actual &&\n    t/t5512-ls-remote.sh-   test_protocolv0cmp expect actual &&\n\nBecause that doesn't make sense. If we know we're testing protocol v0\nhere why wouldn't we just pass that as a parameter in the test? Same for\nGIT_TEST_GETTEXT_POISON=false.\n\nBut yes. looking at e.g. po/README now I dropped the ball on following\nthrough with some documentation etc. about this at the time.\n\nI have also wondered if a better approach at this point isn't to just\nremove the thing entirely. We're just doing incremental i18n-ization at\nthis point, so the odds that it's catching accidental plumbing\ntranslations is rather low compared to 2011-era git.git.\n"},{"id":"413984","messageId":"xmqqczyca6vq.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"87y2h062jd.fsf@evledraar.gmail.com","subject":"Re: Should you use test_i18ngrep or GIT_TEST_GETTEXT_POISON=false?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-01-10T20:12:25Z","receivedAt":"2021-01-10T20:13:32Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> You prodded me about this and Johannes also did off-list. So given that\n> this is already in \"next\" I think it's best to let usage in this mktag\n> series land as-is.\n\nFor an issue like this \"test_i18ngrep or not?\" that would end up\ngiving a useful hint to future developers, a follow-up commit with\nan explanation why test_i18ngrep is preferred (or not) would be a\ngreat way to go forward (if the \"fix\" is needed in the first place,\nthat is).  It is easy to add a patch to fix things up while a series\nis cooking in 'next', without having to remember doing so after the\nseries hits 'master'.\n\n>     git some-command >output && \n>     test_i18ngrep \"c locale string\" output\n>\n> But since 6cdccfce1e0 (i18n: make GETTEXT_POISON a runtime option,\n> 2018-11-08) this hasn't been needed.\n\nI did not immediately see why that commit changes any equation, but\nI am inclined to say that your argument makes sort-of sense, if not\n100%, to me.  POISON test is not about testing features that ought\nto work (that is what non-POISON tests are for).  The primary\nobjective of POISON test is to ensure that we didn't over-localize;\nif all the output from some-command is expected to be fully\nlocalized, between the above and\n\n\tGIT_TEST_GETTEXT_POISON=no git some-command >output &&\n\tgrep \"c locale string\" output\n\nthere wouldn't be much difference.\n\nBut if the output contains strings, some of which are expected to be\nlocalized (e.g. human facing messages) and some are not (e.g.\nprotocol dump), and the test output is inspected for both types of\nstrings, it would not be an equivalent test.\n\nHaving said that, it may be OK that a mixed-output-command is tested\nonly in C locale without localization.  Our tests do *not* make sure\nthat the strings that ought to be localized are indeed localized\nanyway, so as long as the inspection of the output string does not\ncheck for string that are *not* to be localized, we won't break the\nprimary objective of having POISON tests.\n\nIn any case, if you want to push forward in that direction to use\nmore GIT_TEST_* settings in the test to replace i18ncmp/grep, please\nmake sure you propose something easier to read than \"GETTEXT_POISON\"\nfor the environment variable name.  It is overly long and makes the\ntests unreadable by pushing the part of the command that are more\nimportant off of the right edge of the screen.\n\nThanks.\n"},{"id":"414022","messageId":"87pn2b6eyb.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"xmqqczyca6vq.fsf@gitster.c.googlers.com","subject":"Re: Should you use test_i18ngrep or GIT_TEST_GETTEXT_POISON=false?","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-11T08:43:56Z","receivedAt":"2021-01-11T08:45:03Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Jan 10 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> You prodded me about this and Johannes also did off-list. So given that\n>> this is already in \"next\" I think it's best to let usage in this mktag\n>> series land as-is.\n>\n> For an issue like this \"test_i18ngrep or not?\" that would end up\n> giving a useful hint to future developers, a follow-up commit with\n> an explanation why test_i18ngrep is preferred (or not) would be a\n> great way to go forward (if the \"fix\" is needed in the first place,\n> that is).  It is easy to add a patch to fix things up while a series\n> is cooking in 'next', without having to remember doing so after the\n> series hits 'master'.\n>\n>>     git some-command >output && \n>>     test_i18ngrep \"c locale string\" output\n>>\n>> But since 6cdccfce1e0 (i18n: make GETTEXT_POISON a runtime option,\n>> 2018-11-08) this hasn't been needed.\n>\n> I did not immediately see why that commit changes any equation, but\n> I am inclined to say that your argument makes sort-of sense, if not\n> 100%, to me.  POISON test is not about testing features that ought\n> to work (that is what non-POISON tests are for).  The primary\n> objective of POISON test is to ensure that we didn't over-localize;\n> if all the output from some-command is expected to be fully\n> localized, between the above and\n>\n> \tGIT_TEST_GETTEXT_POISON=no git some-command >output &&\n> \tgrep \"c locale string\" output\n>\n> there wouldn't be much difference.\n>\n> But if the output contains strings, some of which are expected to be\n> localized (e.g. human facing messages) and some are not (e.g.\n> protocol dump), and the test output is inspected for both types of\n> strings, it would not be an equivalent test.\n\n*nod* we have a few tests like that, but most of it is \"run git & do one\n grep\".\n\n> Having said that, it may be OK that a mixed-output-command is tested\n> only in C locale without localization.  Our tests do *not* make sure\n> that the strings that ought to be localized are indeed localized\n> anyway, so as long as the inspection of the output string does not\n> check for string that are *not* to be localized, we won't break the\n> primary objective of having POISON tests.\n\nWhat do you think about just removing it? I.e. make setting it a noop?\n\n> In any case, if you want to push forward in that direction to use\n> more GIT_TEST_* settings in the test to replace i18ncmp/grep, please\n> make sure you propose something easier to read than \"GETTEXT_POISON\"\n> for the environment variable name.  It is overly long and makes the\n> tests unreadable by pushing the part of the command that are more\n> important off of the right edge of the screen.\n\nYeah, that sucks a bit. The common case though is that the \"git\"\ninvocation itself isn't reindented by more than a \"\\t\". E.g. in my patch\nupthread:\n\n\n-\t\ttest_must_fail git mktag <tag.sig 2>message &&\n+\t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n+\t\t\tgit mktag <tag.sig 2>message &&\n\nI wondered if I could support:\n\n    git --lang=C mktag [...]\n\nBut the order of argv parsing & gettext setup makes that a bit\ninconvenient. The argv parsing itself emits i18n messages, it could be\ndone in the C code.\n\nAlso a pain to pass it over to the Perl + sh side without changing\nsemantics (simplest would be to setenv(LC_ALL, \"C\"), but then anything\ndownstream of us changes too.\n\nHrm, but I see I never added the \"poison\" support to the Perl side, just\nthe SH code.\n\nNow I seem to recall that the last time I looked at this I punted on it\nbecause I thought I'd just wait until the Shell built-ins needing i18n\ndisappeared, which makes any changes to it easier.\n\nTo me it makes sense just to use it. GETTEXT_POISON isn't *that* long of\na name, and any shortening-ing of it seems not worth it to e.g. break\nshell history / needing to alter existing C settings etc. again.\n\nIt's a typical length for for the GIT_TEST_* variables, so if we think\ntheir use makes tests too verbose it makes sense to re-visit that as a\nmore general topic.\n\nBut I also think it makes more sense to just get rid of it\nentirely...\n"},{"id":"414044","messageId":"20210111131451.7084-1-avarab@gmail.com","threadId":"51667","inReplyTo":"87pn2b6eyb.fsf@evledraar.gmail.com","subject":"[PATCH 0/2] Makefile & gettext.c: remove warning & long comment","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-11T13:14:49Z","receivedAt":"2021-01-11T13:15:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"We've got a huge comment in gettext.c that nobody cares about anymore,\nremove it, and an old warning in the Makefile while I'm at it.\n\nThis doesn't conflict with any other patches I'm about to submit, I\nwanted to submit it separately because these are small isolated\nnon-contentious changes.\n\nÆvar Arnfjörð Bjarmason (2):\n  Makefile: remove a warning about old GETTEXT_POISON flag\n  gettext.c: remove/reword a mostly-useless comment\n\n Makefile  |  3 --\n gettext.c | 94 +++++++++----------------------------------------------\n 2 files changed, 15 insertions(+), 82 deletions(-)\n\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"414045","messageId":"20210111131451.7084-3-avarab@gmail.com","threadId":"51667","inReplyTo":"87pn2b6eyb.fsf@evledraar.gmail.com","subject":"[PATCH 2/2] gettext.c: remove/reword a mostly-useless comment","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-11T13:14:51Z","receivedAt":"2021-01-11T13:15:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Mostly remove the comment I added 5e9637c6297 (i18n: add\ninfrastructure for translating Git with gettext, 2011-11-18). Since\nthen we had a fix in 9c0495d23e6 (gettext.c: detect the vsnprintf bug\nat runtime, 2013-12-01) so we're not running with the \"set back to C\nlocale\" hack on any modern system.\n\nSo having more than 1/4 of the file taken up by a digression about a\nglibc bug that mostly doesn't happen to anyone anymore is just a\nneedless distraction. Shorten the comment to make a brief mention of\nthe bug, and where to find more info by looking at the git history for\nthis now-removed comment.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n gettext.c | 94 +++++++++----------------------------------------------\n 1 file changed, 15 insertions(+), 79 deletions(-)\n\ndiff --git a/gettext.c b/gettext.c\nindex 35d2c1218db..1b564216d03 100644\n--- a/gettext.c\n+++ b/gettext.c\n@@ -87,88 +87,24 @@ static int test_vsnprintf(const char *fmt, ...)\n \n static void init_gettext_charset(const char *domain)\n {\n-\t/*\n-\t   This trick arranges for messages to be emitted in the user's\n-\t   requested encoding, but avoids setting LC_CTYPE from the\n-\t   environment for the whole program.\n-\n-\t   This primarily done to avoid a bug in vsnprintf in the GNU C\n-\t   Library [1]. which triggered a \"your vsnprintf is broken\" error\n-\t   on Git's own repository when inspecting v0.99.6~1 under a UTF-8\n-\t   locale.\n-\n-\t   That commit contains a ISO-8859-1 encoded author name, which\n-\t   the locale aware vsnprintf(3) won't interpolate in the format\n-\t   argument, due to mismatch between the data encoding and the\n-\t   locale.\n-\n-\t   Even if it wasn't for that bug we wouldn't want to use LC_CTYPE at\n-\t   this point, because it'd require auditing all the code that uses C\n-\t   functions whose semantics are modified by LC_CTYPE.\n-\n-\t   But only setting LC_MESSAGES as we do creates a problem, since\n-\t   we declare the encoding of our PO files[2] the gettext\n-\t   implementation will try to recode it to the user's locale, but\n-\t   without LC_CTYPE it'll emit something like this on 'git init'\n-\t   under the Icelandic locale:\n-\n-\t       Bj? til t?ma Git lind ? /hlagh/.git/\n-\n-\t   Gettext knows about the encoding of our PO file, but we haven't\n-\t   told it about the user's encoding, so all the non-US-ASCII\n-\t   characters get encoded to question marks.\n-\n-\t   But we're in luck! We can set LC_CTYPE from the environment\n-\t   only while we call nl_langinfo and\n-\t   bind_textdomain_codeset. That suffices to tell gettext what\n-\t   encoding it should emit in, so it'll now say:\n-\n-\t       Bjó til tóma Git lind í /hlagh/.git/\n-\n-\t   And the equivalent ISO-8859-1 string will be emitted under a\n-\t   ISO-8859-1 locale.\n-\n-\t   With this change way we get the advantages of setting LC_CTYPE\n-\t   (talk to the user in his language/encoding), without the major\n-\t   drawbacks (changed semantics for C functions we rely on).\n-\n-\t   However foreign functions using other message catalogs that\n-\t   aren't using our neat trick will still have a problem, e.g. if\n-\t   we have to call perror(3):\n-\n-\t   #include <stdio.h>\n-\t   #include <locale.h>\n-\t   #include <errno.h>\n-\n-\t   int main(void)\n-\t   {\n-\t\t   setlocale(LC_MESSAGES, \"\");\n-\t\t   setlocale(LC_CTYPE, \"C\");\n-\t\t   errno = ENODEV;\n-\t\t   perror(\"test\");\n-\t\t   return 0;\n-\t   }\n-\n-\t   Running that will give you a message with question marks:\n-\n-\t   $ LANGUAGE= LANG=de_DE.utf8 ./test\n-\t   test: Kein passendes Ger?t gefunden\n-\n-\t   The vsnprintf bug has been fixed since glibc 2.17.\n-\n-\t   Then we could simply set LC_CTYPE from the environment, which would\n-\t   make things like the external perror(3) messages work.\n-\n-\t   See t/t0203-gettext-setlocale-sanity.sh's \"gettext.c\" tests for\n-\t   regression tests.\n-\n-\t   1. http://sourceware.org/bugzilla/show_bug.cgi?id=6530\n-\t   2. E.g. \"Content-Type: text/plain; charset=UTF-8\\n\" in po/is.po\n-\t*/\n \tsetlocale(LC_CTYPE, \"\");\n \tcharset = locale_charset();\n \tbind_textdomain_codeset(domain, charset);\n-\t/* the string is taken from v0.99.6~1 */\n+\n+\t/*\n+\t * Work around an old bug fixed in glibc 2.17 (released on\n+\t * 2012-12-24), at the cost of potentially making translated\n+\t * messages from external functions like perror() emitted in\n+\t * the wrong encoding.\n+\t *\n+\t * The bug affected e.g. git.git's own 7eb93c89651 ([PATCH]\n+\t * Simplify git script, 2005-09-07), which is the origin of\n+\t * the \"David_K\\345gedal\" test string.\n+\t *\n+\t * See a much longer comment added to this file in 5e9637c6297\n+\t * (i18n: add infrastructure for translating Git with gettext,\n+\t * 2011-11-18) for more details.\n+\t */\n \tif (test_vsnprintf(\"%.*s\", 13, \"David_K\\345gedal\") < 0)\n \t\tsetlocale(LC_CTYPE, \"C\");\n }\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"414046","messageId":"20210111131451.7084-2-avarab@gmail.com","threadId":"51667","inReplyTo":"87pn2b6eyb.fsf@evledraar.gmail.com","subject":"[PATCH 1/2] Makefile: remove a warning about old GETTEXT_POISON flag","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-11T13:14:50Z","receivedAt":"2021-01-11T13:31:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove a migratory warning I added in 6cdccfce1e0 (i18n: make\nGETTEXT_POISON a runtime option, 2018-11-08) to give anyone using that\noption in their builds a heads-up about the change from compile-time\nto runtime introduced in that commit.\n\nIt's been more than 2 years since then, anyone who ran into this is\nlikely to have made a change as a result, so removing this is long\noverdue.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Makefile | 3 ---\n 1 file changed, 3 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 7b64106930a..5a9a8a00a06 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1554,9 +1554,6 @@ endif\n ifdef NO_SYMLINK_HEAD\n \tBASIC_CFLAGS += -DNO_SYMLINK_HEAD\n endif\n-ifdef GETTEXT_POISON\n-$(warning The GETTEXT_POISON option has been removed in favor of runtime GIT_TEST_GETTEXT_POISON. See t/README!)\n-endif\n ifdef NO_GETTEXT\n \tBASIC_CFLAGS += -DNO_GETTEXT\n \tUSE_GETTEXT_SCHEME ?= fallthrough\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"414048","messageId":"20210111144740.6092-3-avarab@gmail.com","threadId":"51667","inReplyTo":"87pn2b6eyb.fsf@evledraar.gmail.com","subject":"[PATCH 2/6] tests: remove support for GIT_TEST_GETTEXT_POISON","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-11T14:47:36Z","receivedAt":"2021-01-11T14:48:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This removes the ability to inject \"poison\" gettext() messages via the\nGIT_TEST_GETTEXT_POISON special test setup.\n\nI initially added this as a compile-time option in bb946bba761 (i18n:\nadd GETTEXT_POISON to simulate unfriendly translator, 2011-02-22), and\nmost recently modified to be toggleable at runtime in\n6cdccfce1e0 (i18n: make GETTEXT_POISON a runtime option, 2018-11-08)..\n\nThe reason for its removal is that the trade-off of maintaining it\nv.s. what it's getting us has long since flipped. When gettext was\nintegrated in 5e9637c6297 (i18n: add infrastructure for translating\nGit with gettext, 2011-11-18) there was understandable concern on the\nGit ML that in marking messages for translation en-masse we'd\ninadvertently mark plumbing messages. The GETTEXT_POISON facility was\na way to smoke those out via our test suite.\n\nNowadays however we're done (or almost entirely done) with any marking\nof messages for translation. New messages are usually marked by their\nauthors, who'll know whether it makes sense to translate them or\nnot. If not any errors in marking the messages are much more likely to\nbe spotted in review than in the the initial deluge of i18n patches in\nthe 2011-2012 era.\n\nSo let's just remove this. This leaves the test suite in a state where\nwe still have a lot of test_i18n, C_LOCALE_OUTPUT\netc. uses. Subsequent commits will remove those too.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/MyFirstContribution.txt |  2 +-\n config.c                              |  9 -------\n gettext.c                             | 10 -------\n gettext.h                             |  7 +----\n git-sh-i18n.sh                        | 22 +--------------\n po/README                             | 34 ++++++++++++-----------\n t/README                              |  6 -----\n t/lib-gettext.sh                      |  2 +-\n t/t0017-env-helper.sh                 |  8 +++---\n t/t0205-gettext-poison.sh             | 39 ---------------------------\n t/test-lib-functions.sh               | 23 +++++-----------\n t/test-lib.sh                         | 23 +++-------------\n 12 files changed, 38 insertions(+), 147 deletions(-)\n delete mode 100755 t/t0205-gettext-poison.sh\n\ndiff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt\nindex 7c9a037cc2..af0a9da62e 100644\n--- a/Documentation/MyFirstContribution.txt\n+++ b/Documentation/MyFirstContribution.txt\n@@ -664,7 +664,7 @@ mention the right animal somewhere:\n ----\n test_expect_success 'runs correctly with no args and good output' '\n \tgit psuh >actual &&\n-\ttest_i18ngrep Pony actual\n+\tgrep Pony actual\n '\n ----\n \ndiff --git a/config.c b/config.c\nindex 1137bd73af..a8dec9a247 100644\n--- a/config.c\n+++ b/config.c\n@@ -996,15 +996,6 @@ static void die_bad_number(const char *name, const char *value)\n \tif (!value)\n \t\tvalue = \"\";\n \n-\tif (!strcmp(name, \"GIT_TEST_GETTEXT_POISON\"))\n-\t\t/*\n-\t\t * We explicitly *don't* use _() here since it would\n-\t\t * cause an infinite loop with _() needing to call\n-\t\t * use_gettext_poison(). This is why marked up\n-\t\t * translations with N_() above.\n-\t\t */\n-\t\tdie(bad_numeric, value, name, error_type);\n-\n \tif (!(cf && cf->name))\n \t\tdie(_(bad_numeric), value, name, _(error_type));\n \ndiff --git a/gettext.c b/gettext.c\nindex 35d2c1218d..308c4b3129 100644\n--- a/gettext.c\n+++ b/gettext.c\n@@ -65,14 +65,6 @@ const char *get_preferred_languages(void)\n \treturn NULL;\n }\n \n-int use_gettext_poison(void)\n-{\n-\tstatic int poison_requested = -1;\n-\tif (poison_requested == -1)\n-\t\tpoison_requested = git_env_bool(\"GIT_TEST_GETTEXT_POISON\", 0);\n-\treturn poison_requested;\n-}\n-\n #ifndef NO_GETTEXT\n static int test_vsnprintf(const char *fmt, ...)\n {\n@@ -181,8 +173,6 @@ void git_setup_gettext(void)\n \tif (!podir)\n \t\tpodir = p = system_path(GIT_LOCALE_PATH);\n \n-\tuse_gettext_poison(); /* getenv() reentrancy paranoia */\n-\n \tif (!is_directory(podir)) {\n \t\tfree(p);\n \t\treturn;\ndiff --git a/gettext.h b/gettext.h\nindex bee52eb113..c8b34fd612 100644\n--- a/gettext.h\n+++ b/gettext.h\n@@ -28,15 +28,12 @@\n \n #define FORMAT_PRESERVING(n) __attribute__((format_arg(n)))\n \n-int use_gettext_poison(void);\n-\n #ifndef NO_GETTEXT\n void git_setup_gettext(void);\n int gettext_width(const char *s);\n #else\n static inline void git_setup_gettext(void)\n {\n-\tuse_gettext_poison(); /* getenv() reentrancy paranoia */\n }\n static inline int gettext_width(const char *s)\n {\n@@ -48,14 +45,12 @@ static inline FORMAT_PRESERVING(1) const char *_(const char *msgid)\n {\n \tif (!*msgid)\n \t\treturn \"\";\n-\treturn use_gettext_poison() ? \"# GETTEXT POISON #\" : gettext(msgid);\n+\treturn gettext(msgid);\n }\n \n static inline FORMAT_PRESERVING(1) FORMAT_PRESERVING(2)\n const char *Q_(const char *msgid, const char *plu, unsigned long n)\n {\n-\tif (use_gettext_poison())\n-\t\treturn \"# GETTEXT POISON #\";\n \treturn ngettext(msgid, plu, n);\n }\n \ndiff --git a/git-sh-i18n.sh b/git-sh-i18n.sh\nindex 8eef60b43f..e3d9f4836d 100644\n--- a/git-sh-i18n.sh\n+++ b/git-sh-i18n.sh\n@@ -17,12 +17,7 @@ export TEXTDOMAINDIR\n \n # First decide what scheme to use...\n GIT_INTERNAL_GETTEXT_SH_SCHEME=fallthrough\n-if test -n \"$GIT_TEST_GETTEXT_POISON\" &&\n-\t    git env--helper --type=bool --default=0 --exit-code \\\n-\t\tGIT_TEST_GETTEXT_POISON\n-then\n-\tGIT_INTERNAL_GETTEXT_SH_SCHEME=poison\n-elif test -n \"@@USE_GETTEXT_SCHEME@@\"\n+if test -n \"@@USE_GETTEXT_SCHEME@@\"\n then\n \tGIT_INTERNAL_GETTEXT_SH_SCHEME=\"@@USE_GETTEXT_SCHEME@@\"\n elif test -n \"$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS\"\n@@ -63,21 +58,6 @@ gettext_without_eval_gettext)\n \t\t)\n \t}\n \t;;\n-poison)\n-\t# Emit garbage so that tests that incorrectly rely on translatable\n-\t# strings will fail.\n-\tgettext () {\n-\t\tprintf \"%s\" \"# GETTEXT POISON #\"\n-\t}\n-\n-\teval_gettext () {\n-\t\tprintf \"%s\" \"# GETTEXT POISON #\"\n-\t}\n-\n-\teval_ngettext () {\n-\t\tprintf \"%s\" \"# GETTEXT POISON #\"\n-\t}\n-\t;;\n *)\n \tgettext () {\n \t\tprintf \"%s\" \"$1\"\ndiff --git a/po/README b/po/README\nindex 07595d369b..b9b6c9cb24 100644\n--- a/po/README\n+++ b/po/README\n@@ -284,23 +284,27 @@ Perl:\n Testing marked strings\n ----------------------\n \n-Even if you've correctly marked porcelain strings for translation\n-something in the test suite might still depend on the US English\n-version of the strings, e.g. to grep some error message or other\n-output.\n+Git's tests are run under LANG=C LC_ALL=C. So the tests do not need be\n+changed to account for translations as they're added.\n \n-To smoke out issues like these, Git tested with a translation mode that\n-emits gibberish on every call to gettext. To use it run the test suite\n-with it, e.g.:\n+Testing whether marked strings are used by plumbing\n+---------------------------------------------------\n \n-    cd t && GIT_TEST_GETTEXT_POISON=true prove -j 9 ./t[0-9]*.sh\n+It may be useful to use the test suite to smoke out any strings that\n+shouldn't be translated, because they are used by plumbing or\n+otherwise part of machine-readable output.\n \n-If tests break with it you should inspect them manually and see if\n-what you're translating is sane, i.e. that you're not translating\n+This is most useful in cases where e.g. library function might return\n+a string like _(\"tree\"), and the developer marking strings for\n+translation wishes to check that they're not inadvertently translating\n plumbing output.\n \n-If not you should replace calls to grep with test_i18ngrep, or\n-test_cmp calls with test_i18ncmp. If that's not enough you can skip\n-the whole test by making it depend on the C_LOCALE_OUTPUT\n-prerequisite. See existing test files with this prerequisite for\n-examples.\n+There used to be a GIT_TEST_GETTEXT_POISON=true test mode to replace\n+translated strings with gibberish to facilitate this. Maintaining it\n+and annotating tests appropriately was considered too high of a burden\n+for its usefulness.\n+\n+It's still possible to do a one-off test of where a string might be\n+used in the tests by simply replacing the \"some hardcoded string\" with\n+something like \"POISON ME\" locally and running the tests, before\n+finally converting it to _(\"some hardcoded string\").\ndiff --git a/t/README b/t/README\nindex c730a70770..9a9dded033 100644\n--- a/t/README\n+++ b/t/README\n@@ -358,12 +358,6 @@ whether this mode is active, and e.g. skip some tests that are hard to\n refactor to deal with it. The \"SYMLINKS\" prerequisite is currently\n excluded as so much relies on it, but this might change in the future.\n \n-GIT_TEST_GETTEXT_POISON=<boolean> turns all strings marked for\n-translation into gibberish if true. Used for spotting those tests that\n-need to be marked with a C_LOCALE_OUTPUT prerequisite when adding more\n-strings for translation. See \"Testing marked strings\" in po/README for\n-details.\n-\n GIT_TEST_SPLIT_INDEX=<boolean> forces split-index mode on the whole\n test suite. Accept any boolean values that are accepted by git-config.\n \ndiff --git a/t/lib-gettext.sh b/t/lib-gettext.sh\nindex 2139b427ca..cc6bb2cdea 100644\n--- a/t/lib-gettext.sh\n+++ b/t/lib-gettext.sh\n@@ -17,7 +17,7 @@ else\n \t. \"$GIT_BUILD_DIR\"/git-sh-i18n\n fi\n \n-if test_have_prereq GETTEXT && test_have_prereq C_LOCALE_OUTPUT\n+if test_have_prereq GETTEXT\n then\n \t# is_IS.UTF-8 on Solaris and FreeBSD, is_IS.utf8 on Debian\n \tis_IS_locale=$(locale -a 2>/dev/null |\ndiff --git a/t/t0017-env-helper.sh b/t/t0017-env-helper.sh\nindex c1ecf6aeac..4a159f99e4 100755\n--- a/t/t0017-env-helper.sh\n+++ b/t/t0017-env-helper.sh\n@@ -86,14 +86,14 @@ test_expect_success 'env--helper reads config thanks to trace2' '\n \tgit config -f home/cycle include.path .gitconfig &&\n \n \ttest_must_fail \\\n-\t\tenv HOME=\"$(pwd)/home\" GIT_TEST_GETTEXT_POISON=false \\\n+\t\tenv HOME=\"$(pwd)/home\" \\\n \t\tgit config -l 2>err &&\n \tgrep \"exceeded maximum include depth\" err &&\n \n \ttest_must_fail \\\n-\t\tenv HOME=\"$(pwd)/home\" GIT_TEST_GETTEXT_POISON=true \\\n-\t\tgit -C cycle env--helper --type=bool --default=0 --exit-code GIT_TEST_GETTEXT_POISON 2>err &&\n-\tgrep \"# GETTEXT POISON #\" err\n+\t\tenv HOME=\"$(pwd)/home\" GIT_TEST_ENV_HELPER=true \\\n+\t\tgit -C cycle env--helper --type=bool --default=0 --exit-code GIT_TEST_ENV_HELPER 2>err &&\n+\tgrep \"exceeded maximum include depth\" err\n '\n \n test_done\ndiff --git a/t/t0205-gettext-poison.sh b/t/t0205-gettext-poison.sh\ndeleted file mode 100755\nindex f9fa16ad83..0000000000\n--- a/t/t0205-gettext-poison.sh\n+++ /dev/null\n@@ -1,39 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (c) 2010 Ævar Arnfjörð Bjarmason\n-#\n-\n-test_description='Gettext Shell poison'\n-\n-GIT_TEST_GETTEXT_POISON=true\n-export GIT_TEST_GETTEXT_POISON\n-. ./lib-gettext.sh\n-\n-test_expect_success 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME\" is poison' '\n-    test \"$GIT_INTERNAL_GETTEXT_SH_SCHEME\" = \"poison\"\n-'\n-\n-test_expect_success 'gettext: our gettext() fallback has poison semantics' '\n-    printf \"# GETTEXT POISON #\" >expect &&\n-    gettext \"test\" >actual &&\n-    test_cmp expect actual &&\n-    printf \"# GETTEXT POISON #\" >expect &&\n-    gettext \"test more words\" >actual &&\n-    test_cmp expect actual\n-'\n-\n-test_expect_success 'eval_gettext: our eval_gettext() fallback has poison semantics' '\n-    printf \"# GETTEXT POISON #\" >expect &&\n-    eval_gettext \"test\" >actual &&\n-    test_cmp expect actual &&\n-    printf \"# GETTEXT POISON #\" >expect &&\n-    eval_gettext \"test more words\" >actual &&\n-    test_cmp expect actual\n-'\n-\n-test_expect_success \"gettext: invalid GIT_TEST_GETTEXT_POISON value doesn't infinitely loop\" \"\n-\ttest_must_fail env GIT_TEST_GETTEXT_POISON=xyz git version 2>error &&\n-\tgrep \\\"fatal: bad numeric config value 'xyz' for 'GIT_TEST_GETTEXT_POISON': invalid unit\\\" error\n-\"\n-\n-test_done\ndiff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\nindex 999982fe4a..c7b1d857c4 100644\n--- a/t/test-lib-functions.sh\n+++ b/t/test-lib-functions.sh\n@@ -988,19 +988,16 @@ test_cmp_bin () {\n \tcmp \"$@\"\n }\n \n-# Use this instead of test_cmp to compare files that contain expected and\n-# actual output from git commands that can be translated.  When running\n-# under GIT_TEST_GETTEXT_POISON this pretends that the command produced expected\n-# results.\n+# Wrapper for test_cmp which used to be used for\n+# GIT_TEST_GETTEXT_POISON=false. Only here as a shim for other\n+# in-flight changes. Should not be used and will be removed soon.\n test_i18ncmp () {\n-\t! test_have_prereq C_LOCALE_OUTPUT || test_cmp \"$@\"\n+\ttest_cmp \"$@\"\n }\n \n-# Use this instead of \"grep expected-string actual\" to see if the\n-# output from a git command that can be translated either contains an\n-# expected string, or does not contain an unwanted one.  When running\n-# under GIT_TEST_GETTEXT_POISON this pretends that the command produced expected\n-# results.\n+# Wrapper for grep which used to be used for\n+# GIT_TEST_GETTEXT_POISON=false. Only here as a shim for other\n+# in-flight changes. Should not be used and will be removed soon.\n test_i18ngrep () {\n \teval \"last_arg=\\${$#}\"\n \n@@ -1013,12 +1010,6 @@ test_i18ngrep () {\n \t\tBUG \"too few parameters to test_i18ngrep\"\n \tfi\n \n-\tif test_have_prereq !C_LOCALE_OUTPUT\n-\tthen\n-\t\t# pretend success\n-\t\treturn 0\n-\tfi\n-\n \tif test \"x!\" = \"x$1\"\n \tthen\n \t\tshift\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 9fa7c1d0f6..c1ff5db2c1 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -404,15 +404,6 @@ TZ=UTC\n export LANG LC_ALL PAGER TZ\n EDITOR=:\n \n-# GIT_TEST_GETTEXT_POISON should not influence git commands executed\n-# during initialization of test-lib and the test repo. Back it up,\n-# unset and then restore after initialization is finished.\n-if test -n \"$GIT_TEST_GETTEXT_POISON\"\n-then\n-\tGIT_TEST_GETTEXT_POISON_ORIG=$GIT_TEST_GETTEXT_POISON\n-\tunset GIT_TEST_GETTEXT_POISON\n-fi\n-\n # A call to \"unset\" with no arguments causes at least Solaris 10\n # /usr/xpg4/bin/sh and /bin/ksh to bail out.  So keep the unsets\n # deriving from the command substitution clustered with the other\n@@ -1529,16 +1520,10 @@ test -n \"$USE_LIBPCRE1\" && test_set_prereq LIBPCRE1\n test -n \"$USE_LIBPCRE2\" && test_set_prereq LIBPCRE2\n test -z \"$NO_GETTEXT\" && test_set_prereq GETTEXT\n \n-if test -n \"$GIT_TEST_GETTEXT_POISON_ORIG\"\n-then\n-\tGIT_TEST_GETTEXT_POISON=$GIT_TEST_GETTEXT_POISON_ORIG\n-\texport GIT_TEST_GETTEXT_POISON\n-\tunset GIT_TEST_GETTEXT_POISON_ORIG\n-fi\n-\n-test_lazy_prereq C_LOCALE_OUTPUT '\n-\t! test_bool_env GIT_TEST_GETTEXT_POISON false\n-'\n+# Used to be used for GIT_TEST_GETTEXT_POISON=false. Only here as a\n+# shim for other in-flight changes. Should not be used and will be\n+# removed soon.\n+test_set_prereq C_LOCALE_OUTPUT\n \n if test -z \"$GIT_TEST_CHECK_CACHE_TREE\"\n then\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"414049","messageId":"20210111144740.6092-1-avarab@gmail.com","threadId":"51667","inReplyTo":"87pn2b6eyb.fsf@evledraar.gmail.com","subject":"[PATCH 0/6] Remove GIT_TEST_GETTEXT_POISON facility","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-11T14:47:34Z","receivedAt":"2021-01-11T14:48:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As argued upthread and in 2/6 this test setup has outrun its\nusefulness.\n\nThis series begins to remove it in a way that's very careful about not\nstepping on the toes of any other in-flight patches. In particular\n\"master\" isn't anywhere in the diff context here to avoid conflicts\nwith the series to do that mass-replacement in t/. \n\nThis series merges and tests cleanly in combination with the current\n\"seen\" branch.\n\nAt the end we're left with a bunch of tests still using\ntest_i18n{cmp,grep}, but I think that's preferable to having in-flight\nconflicts. It'll be trivial to search-replace those at some point in\nthe future where we don't have another search-replacement series\namending t/ in-flight.\n\nÆvar Arnfjörð Bjarmason (6):\n  ci: remove GETTEXT_POISON jobs\n  tests: remove support for GIT_TEST_GETTEXT_POISON\n  tests: remove misc use of test_i18n{cmp,grep}\n  tests: (almost) remove use of \"test_i18ngrep !\"\n  tests: (almost) remove C_LOCALE_OUTPUT prerequisites\n  tests: remove uses of GIT_TEST_GETTEXT_POISON=false\n\n .github/workflows/main.yml                    |  2 +-\n .travis.yml                                   |  2 +-\n Documentation/MyFirstContribution.txt         |  2 +-\n ci/install-dependencies.sh                    |  2 +-\n ci/lib.sh                                     |  3 +-\n config.c                                      |  9 -----\n .../t/t9363-mw-to-git-export-import.sh        |  2 +-\n gettext.c                                     | 10 -----\n gettext.h                                     |  7 +---\n git-sh-i18n.sh                                | 22 +----------\n po/README                                     | 34 +++++++++-------\n t/README                                      |  6 ---\n t/lib-credential.sh                           |  2 +-\n t/lib-gettext.sh                              |  2 +-\n t/lib-httpd.sh                                |  2 +-\n t/lib-log-graph.sh                            |  4 +-\n t/lib-submodule-update.sh                     |  2 +-\n t/t0000-basic.sh                              |  2 +-\n t/t0017-env-helper.sh                         |  8 ++--\n t/t0020-crlf.sh                               |  6 +--\n t/t0041-usage.sh                              | 12 +++---\n t/t0201-gettext-fallbacks.sh                  |  2 +-\n t/t0205-gettext-poison.sh                     | 39 -------------------\n t/t1060-object-corruption.sh                  |  2 +-\n t/t1091-sparse-checkout-builtin.sh            | 10 ++---\n t/t1305-config-include.sh                     |  4 +-\n t/t1430-bad-ref-name.sh                       |  2 +-\n t/t1450-fsck.sh                               |  4 +-\n t/t1506-rev-parse-diagnosis.sh                |  2 +-\n t/t1512-rev-parse-disambiguation.sh           | 14 +++----\n t/t2019-checkout-ambiguous-ref.sh             |  4 +-\n t/t2020-checkout-detach.sh                    |  4 +-\n t/t2024-checkout-dwim.sh                      |  2 +-\n t/t3404-rebase-interactive.sh                 | 14 +++----\n t/t3415-rebase-autosquash.sh                  | 10 ++---\n t/t3418-rebase-continue.sh                    |  2 +-\n t/t3507-cherry-pick-conflict.sh               |  2 +-\n t/t3600-rm.sh                                 |  2 +-\n t/t3701-add-interactive.sh                    |  2 +-\n t/t4001-diff-rename.sh                        |  2 +-\n t/t4012-diff-binary.sh                        |  4 +-\n t/t4014-format-patch.sh                       |  2 +-\n t/t4018-diff-funcname.sh                      |  8 ++--\n t/t4153-am-resume-override-opts.sh            |  2 +-\n t/t4205-log-pretty-formats.sh                 |  2 +-\n t/t5300-pack-object.sh                        |  4 +-\n t/t5324-split-commit-graph.sh                 |  2 +-\n t/t5411/common-functions.sh                   |  5 +--\n t/t5411/test-0026-push-options.sh             |  3 +-\n t/t5411/test-0027-push-options--porcelain.sh  |  3 +-\n t/t5505-remote.sh                             |  8 ++--\n t/t5510-fetch.sh                              |  6 +--\n t/t5534-push-signed.sh                        |  2 +-\n t/t5541-http-push-smart.sh                    |  4 +-\n t/t5580-unc-paths.sh                          |  2 +-\n t/t5601-clone.sh                              |  2 +-\n t/t5606-clone-options.sh                      |  2 +-\n t/t6001-rev-list-graft.sh                     |  2 +-\n t/t6050-replace.sh                            |  2 +-\n t/t6423-merge-rename-directories.sh           | 14 +++----\n t/t6433-merge-toplevel.sh                     |  2 +-\n t/t6437-submodule-merge.sh                    |  2 +-\n t/t6500-gc.sh                                 |  2 +-\n t/t7201-co.sh                                 |  2 +-\n t/t7300-clean.sh                              |  6 +--\n t/t7400-submodule-basic.sh                    | 16 ++++----\n t/t7414-submodule-mistakes.sh                 |  6 +--\n t/t7415-submodule-names.sh                    |  2 +-\n t/t7416-submodule-dash-url.sh                 |  2 +-\n t/t7502-commit-porcelain.sh                   | 10 ++---\n t/t7508-status.sh                             |  8 ++--\n t/t7518-ident-corner-cases.sh                 |  2 +-\n t/t7519-status-fsmonitor.sh                   |  4 +-\n t/t7520-ignored-hook-warning.sh               |  6 +--\n t/t7601-merge-pull-config.sh                  | 34 ++++++++--------\n t/t7810-grep.sh                               |  4 +-\n t/t7816-grep-binary-pattern.sh                |  2 +-\n t/t9003-help-autocorrect.sh                   |  7 +---\n t/t9800-git-p4-basic.sh                       |  2 +-\n t/t9807-git-p4-submit.sh                      |  2 +-\n t/t9902-completion.sh                         |  1 -\n t/test-lib-functions.sh                       | 23 ++++-------\n t/test-lib.sh                                 | 23 ++---------\n 83 files changed, 195 insertions(+), 316 deletions(-)\n delete mode 100755 t/t0205-gettext-poison.sh\n\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"414050","messageId":"20210111144740.6092-2-avarab@gmail.com","threadId":"51667","inReplyTo":"87pn2b6eyb.fsf@evledraar.gmail.com","subject":"[PATCH 1/6] ci: remove GETTEXT_POISON jobs","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-11T14:47:35Z","receivedAt":"2021-01-11T14:48:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"A subsequent commit will remove GETTEXT_POISON entirely, let's start\nby removing the CI jobs that enable the option.\n\nWe cannot just remove the job because the CI is implicitly depending\non the \"poison\" job being a sort of \"default\" job. Let's instead add a\n\"default\" job.\n\nThis means we can remove the initial \"make test\" from the \"linux-gcc\"\njob (it does another one after setting a bunch of GIT_TEST_*\nvariables).\n\nI'm not doing that because it would conflict with the in-flight\n334afbc76fb (tests: mark tests relying on the current default for\n`init.defaultBranch`, 2020-11-18) (currently on the \"seen\" branch, so\nthe SHA-1 will almost definitely change). It's going to use that \"make\ntest\" again for different reasons, so let's preserve it for now.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n .github/workflows/main.yml | 2 +-\n .travis.yml                | 2 +-\n ci/install-dependencies.sh | 2 +-\n ci/lib.sh                  | 3 +--\n 4 files changed, 4 insertions(+), 5 deletions(-)\n\ndiff --git a/.github/workflows/main.yml b/.github/workflows/main.yml\nindex aef6643648..8b52df200f 100644\n--- a/.github/workflows/main.yml\n+++ b/.github/workflows/main.yml\n@@ -286,7 +286,7 @@ jobs:\n           - jobname: osx-gcc\n             cc: gcc\n             pool: macos-latest\n-          - jobname: GETTEXT_POISON\n+          - jobname: linux-gcc-default\n             cc: gcc\n             pool: ubuntu-latest\n     env:\ndiff --git a/.travis.yml b/.travis.yml\nindex 05f3e3f8d7..908330a0a3 100644\n--- a/.travis.yml\n+++ b/.travis.yml\n@@ -16,7 +16,7 @@ compiler:\n \n matrix:\n   include:\n-    - env: jobname=GETTEXT_POISON\n+    - env: jobname=linux-gcc-default\n       os: linux\n       compiler:\n       addons:\ndiff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh\nindex 0229a77f7d..79c0633a18 100755\n--- a/ci/install-dependencies.sh\n+++ b/ci/install-dependencies.sh\n@@ -72,7 +72,7 @@ Documentation)\n \ttest -n \"$ALREADY_HAVE_ASCIIDOCTOR\" ||\n \tsudo gem install --version 1.5.8 asciidoctor\n \t;;\n-linux-gcc-4.8|GETTEXT_POISON)\n+linux-gcc-default|linux-gcc-4.8)\n \tsudo apt-get -q update\n \tsudo apt-get -q -y install $UBUNTU_COMMON_PKGS\n \t;;\ndiff --git a/ci/lib.sh b/ci/lib.sh\nindex 38c0eac351..d848c036c5 100755\n--- a/ci/lib.sh\n+++ b/ci/lib.sh\n@@ -220,8 +220,7 @@ osx-clang|osx-gcc)\n \t# Travis CI OS X\n \texport GIT_SKIP_TESTS=\"t9810 t9816\"\n \t;;\n-GETTEXT_POISON)\n-\texport GIT_TEST_GETTEXT_POISON=true\n+linux-gcc-default)\n \t;;\n Linux32)\n \tCC=gcc\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"414051","messageId":"20210111144740.6092-4-avarab@gmail.com","threadId":"51667","inReplyTo":"87pn2b6eyb.fsf@evledraar.gmail.com","subject":"[PATCH 3/6] tests: remove misc use of test_i18n{cmp,grep}","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-11T14:47:37Z","receivedAt":"2021-01-11T14:49:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove miscellaneous use of test_i18n{cmp,grep} from contrib/*,\nlib-*.sh and t/*/*.sh etc. noted in an earlier commit we're removing\nthe GIT_TEST_GETTEXT_POISON=true facility.\n\nThis leaves the only users of these functions in t/t[0-9]*.sh tests,\nwhich makes it easier to do a follow-up for-loop search-replacement of\nthose occurrences at a later date.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh | 2 +-\n t/lib-credential.sh                                  | 2 +-\n t/lib-httpd.sh                                       | 2 +-\n t/lib-log-graph.sh                                   | 4 ++--\n t/lib-submodule-update.sh                            | 2 +-\n t/t5411/test-0026-push-options.sh                    | 3 +--\n t/t5411/test-0027-push-options--porcelain.sh         | 3 +--\n 7 files changed, 8 insertions(+), 10 deletions(-)\n\ndiff --git a/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh b/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh\nindex 6187ec67fa..3278334b9b 100755\n--- a/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh\n+++ b/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh\n@@ -161,7 +161,7 @@ test_expect_success 'git push properly warns about insufficient permissions' '\n \t\tgit add foo.forbidden &&\n \t\tgit commit -m \"add a file\" &&\n \t\tgit push 2>actual &&\n-\t\ttest_i18ngrep \"foo.forbidden is not a permitted file\" actual\n+\t\tgrep \"foo.forbidden is not a permitted file\" actual\n \t)\n '\n \ndiff --git a/t/lib-credential.sh b/t/lib-credential.sh\nindex dea2cbef51..5ea8bc9f1d 100644\n--- a/t/lib-credential.sh\n+++ b/t/lib-credential.sh\n@@ -20,7 +20,7 @@ check() {\n \t\tfalse\n \tfi &&\n \ttest_cmp expect-stdout stdout &&\n-\ttest_i18ncmp expect-stderr stderr\n+\ttest_cmp expect-stderr stderr\n }\n \n read_chunk() {\ndiff --git a/t/lib-httpd.sh b/t/lib-httpd.sh\nindex d2edfa4c50..a3ae0616e6 100644\n--- a/t/lib-httpd.sh\n+++ b/t/lib-httpd.sh\n@@ -219,7 +219,7 @@ test_http_push_nonff () {\n \t'\n \n \ttest_expect_success 'non-fast-forward push shows help message' '\n-\t\ttest_i18ngrep \"Updates were rejected because\" output\n+\t\tgrep \"Updates were rejected because\" output\n \t'\n \n \ttest_expect_${EXPECT_CAS_RESULT} 'force with lease aka cas' '\ndiff --git a/t/lib-log-graph.sh b/t/lib-log-graph.sh\nindex 1184cceef2..bf952ef920 100644\n--- a/t/lib-log-graph.sh\n+++ b/t/lib-log-graph.sh\n@@ -12,13 +12,13 @@ sanitize_log_output () {\n lib_test_cmp_graph () {\n \tgit log --graph \"$@\" >output &&\n \tsed 's/ *$//' >output.sanitized <output &&\n-\ttest_i18ncmp expect output.sanitized\n+\ttest_cmp expect output.sanitized\n }\n \n lib_test_cmp_short_graph () {\n \tgit log --graph --pretty=short \"$@\" >output &&\n \tsanitize_log_output >output.sanitized <output &&\n-\ttest_i18ncmp expect output.sanitized\n+\ttest_cmp expect output.sanitized\n }\n \n lib_test_cmp_colored_graph () {\ndiff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh\nindex bd3fa3c6da..e04b5c4ba3 100644\n--- a/t/lib-submodule-update.sh\n+++ b/t/lib-submodule-update.sh\n@@ -830,7 +830,7 @@ test_submodule_recursing_with_args_common () {\n \t\t\tcd submodule_update &&\n \t\t\tgit branch -t invalid_sub1 origin/invalid_sub1 &&\n \t\t\ttest_must_fail $command invalid_sub1 2>err &&\n-\t\t\ttest_i18ngrep sub1 err &&\n+\t\t\tgrep sub1 err &&\n \t\t\ttest_superproject_content origin/add_sub1 &&\n \t\t\ttest_submodule_content sub1 origin/add_sub1\n \t\t)\ndiff --git a/t/t5411/test-0026-push-options.sh b/t/t5411/test-0026-push-options.sh\nindex e88edb16a4..a03f9ac5c4 100644\n--- a/t/t5411/test-0026-push-options.sh\n+++ b/t/t5411/test-0026-push-options.sh\n@@ -18,8 +18,7 @@ test_expect_success \"proc-receive: not support push options ($PROTOCOL)\" '\n \t\tHEAD:refs/for/main/topic \\\n \t\t>out 2>&1 &&\n \tmake_user_friendly_and_stable_output <out >actual &&\n-\ttest_i18ngrep \"fatal: the receiving end does not support push options\" \\\n-\t\tactual &&\n+\tgrep \"fatal: the receiving end does not support push options\" actual &&\n \tgit -C \"$upstream\" show-ref >out &&\n \tmake_user_friendly_and_stable_output <out >actual &&\n \tcat >expect <<-EOF &&\ndiff --git a/t/t5411/test-0027-push-options--porcelain.sh b/t/t5411/test-0027-push-options--porcelain.sh\nindex 3a6561b5ea..e734247d69 100644\n--- a/t/t5411/test-0027-push-options--porcelain.sh\n+++ b/t/t5411/test-0027-push-options--porcelain.sh\n@@ -19,8 +19,7 @@ test_expect_success \"proc-receive: not support push options ($PROTOCOL/porcelain\n \t\tHEAD:refs/for/main/topic \\\n \t\t>out 2>&1 &&\n \tmake_user_friendly_and_stable_output <out >actual &&\n-\ttest_i18ngrep \"fatal: the receiving end does not support push options\" \\\n-\t\tactual &&\n+\tgrep \"fatal: the receiving end does not support push options\" actual &&\n \tgit -C \"$upstream\" show-ref >out &&\n \tmake_user_friendly_and_stable_output <out >actual &&\n \tcat >expect <<-EOF &&\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"414052","messageId":"20210111144740.6092-7-avarab@gmail.com","threadId":"51667","inReplyTo":"87pn2b6eyb.fsf@evledraar.gmail.com","subject":"[PATCH 6/6] tests: remove uses of GIT_TEST_GETTEXT_POISON=false","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-11T14:47:40Z","receivedAt":"2021-01-11T14:49:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As noted in previous commits we are removing the use of\nGIT_TEST_GETTEXT_POISON=false. These tests all relied on the facility\nbeing off, it always is off after an earlier change, but we hadn't\nremoved the redundant assignments to \"false\" in the tests.\n\nI'm preserving the deletion of \"error\" lines in 38b9197a76a (t5411:\nadd basic test cases for proc-receive hook, 2020-08-27), it turns out\nthat's useful even without GIT_TEST_GETTEXT_POISON=true in\nplay. Update a comment added in that commit to note that.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t1305-config-include.sh   | 4 +---\n t/t5411/common-functions.sh | 5 ++---\n t/t7201-co.sh               | 2 +-\n t/t9902-completion.sh       | 1 -\n 4 files changed, 4 insertions(+), 8 deletions(-)\n\ndiff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh\nindex f1e1b289f9..f08cfcdfae 100755\n--- a/t/t1305-config-include.sh\n+++ b/t/t1305-config-include.sh\n@@ -352,9 +352,7 @@ test_expect_success 'include cycles are detected' '\n \tgit init --bare cycle &&\n \tgit -C cycle config include.path cycle &&\n \tgit config -f cycle/cycle include.path config &&\n-\ttest_must_fail \\\n-\t\tenv GIT_TEST_GETTEXT_POISON=false \\\n-\t\tgit -C cycle config --get-all test.value 2>stderr &&\n+\ttest_must_fail git -C cycle config --get-all test.value 2>stderr &&\n \tgrep \"exceeded maximum include depth\" stderr\n '\n \ndiff --git a/t/t5411/common-functions.sh b/t/t5411/common-functions.sh\nindex 344d13f61a..13107fcdb6 100644\n--- a/t/t5411/common-functions.sh\n+++ b/t/t5411/common-functions.sh\n@@ -36,9 +36,8 @@ create_commits_in () {\n # without having to worry about future changes of the commit ID and spaces\n # of the output.  Single quotes are replaced with double quotes, because\n # it is boring to prepare unquoted single quotes in expect text.  We also\n-# remove some locale error messages, which break test if we turn on\n-# `GIT_TEST_GETTEXT_POISON=true` in order to test unintentional translations\n-# on plumbing commands.\n+# remove some locale error messages. The emitted human-readable errors are\n+# redundant to the more machine-readable output the tests already assert.\n make_user_friendly_and_stable_output () {\n \tsed \\\n \t\t-e \"s/  *\\$//\" \\\ndiff --git a/t/t7201-co.sh b/t/t7201-co.sh\nindex b36a93056f..d10076efd7 100755\n--- a/t/t7201-co.sh\n+++ b/t/t7201-co.sh\n@@ -245,7 +245,7 @@ test_expect_success 'checkout to detach HEAD' '\n \trev=$(git rev-parse --short renamer^) &&\n \tgit checkout -f renamer &&\n \tgit clean -f &&\n-\tGIT_TEST_GETTEXT_POISON=false git checkout renamer^ 2>messages &&\n+\tgit checkout renamer^ 2>messages &&\n \tgrep \"HEAD is now at $rev\" messages &&\n \ttest_line_count -gt 1 messages &&\n \tH=$(git rev-parse --verify HEAD) &&\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex a1c4f1f6d4..e5adee27d4 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -2363,7 +2363,6 @@ test_expect_success 'sourcing the completion script clears cached commands' '\n '\n \n test_expect_success 'sourcing the completion script clears cached merge strategies' '\n-\tGIT_TEST_GETTEXT_POISON=false &&\n \t__git_compute_merge_strategies &&\n \tverbose test -n \"$__git_merge_strategies\" &&\n \t. \"$GIT_BUILD_DIR/contrib/completion/git-completion.bash\" &&\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"414053","messageId":"20210111144740.6092-5-avarab@gmail.com","threadId":"51667","inReplyTo":"87pn2b6eyb.fsf@evledraar.gmail.com","subject":"[PATCH 4/6] tests: (almost) remove use of \"test_i18ngrep !\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-11T14:47:38Z","receivedAt":"2021-01-11T14:49:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As noted in previous commits we are removing uses of\ntest_i18ngrep. Let's start with the more rare negated form. This\nleaves us with \"test_i18ngrep\" as a mere wrapper for \"grep\" at the\nend.\n\nDone with:\n\n    cd t &&\n    perl -pi -e 's/test_i18ngrep ! /! grep /g' *.sh\n\nI excluded those things that would conflict with other in-flight\nchanges on the \"seen\" branch, as well as not removing support from\nthis invocation from \"test_i18ngrep\" for those & any other in-flight\nand soon-to-be-submitted changes. We can do a later final cleanup and\nremoval of the unused code in some later change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t0041-usage.sh                    | 12 +++++-----\n t/t1060-object-corruption.sh        |  2 +-\n t/t1091-sparse-checkout-builtin.sh  | 10 ++++-----\n t/t1450-fsck.sh                     |  4 ++--\n t/t1506-rev-parse-diagnosis.sh      |  2 +-\n t/t2019-checkout-ambiguous-ref.sh   |  4 ++--\n t/t2020-checkout-detach.sh          |  4 ++--\n t/t2024-checkout-dwim.sh            |  2 +-\n t/t3418-rebase-continue.sh          |  2 +-\n t/t3507-cherry-pick-conflict.sh     |  2 +-\n t/t3600-rm.sh                       |  2 +-\n t/t4001-diff-rename.sh              |  2 +-\n t/t4014-format-patch.sh             |  2 +-\n t/t4018-diff-funcname.sh            |  8 +++----\n t/t4153-am-resume-override-opts.sh  |  2 +-\n t/t5324-split-commit-graph.sh       |  2 +-\n t/t5505-remote.sh                   |  2 +-\n t/t5510-fetch.sh                    |  2 +-\n t/t5534-push-signed.sh              |  2 +-\n t/t5541-http-push-smart.sh          |  4 ++--\n t/t5580-unc-paths.sh                |  2 +-\n t/t5606-clone-options.sh            |  2 +-\n t/t6001-rev-list-graft.sh           |  2 +-\n t/t6050-replace.sh                  |  2 +-\n t/t6423-merge-rename-directories.sh | 12 +++++-----\n t/t6433-merge-toplevel.sh           |  2 +-\n t/t6437-submodule-merge.sh          |  2 +-\n t/t6500-gc.sh                       |  2 +-\n t/t7400-submodule-basic.sh          | 16 +++++++-------\n t/t7414-submodule-mistakes.sh       |  6 ++---\n t/t7415-submodule-names.sh          |  2 +-\n t/t7416-submodule-dash-url.sh       |  2 +-\n t/t7502-commit-porcelain.sh         | 10 ++++-----\n t/t7508-status.sh                   |  8 +++----\n t/t7518-ident-corner-cases.sh       |  2 +-\n t/t7519-status-fsmonitor.sh         |  4 ++--\n t/t7520-ignored-hook-warning.sh     |  6 ++---\n t/t7601-merge-pull-config.sh        | 34 ++++++++++++++---------------\n t/t7810-grep.sh                     |  2 +-\n t/t7816-grep-binary-pattern.sh      |  2 +-\n t/t9800-git-p4-basic.sh             |  2 +-\n t/t9807-git-p4-submit.sh            |  2 +-\n 42 files changed, 98 insertions(+), 98 deletions(-)\n\ndiff --git a/t/t0041-usage.sh b/t/t0041-usage.sh\nindex 5b927b76fe..7d84671941 100755\n--- a/t/t0041-usage.sh\n+++ b/t/t0041-usage.sh\n@@ -18,7 +18,7 @@ test_expect_success 'tag --contains <inexistent_tag>' '\n \ttest_must_fail git tag --contains \"notag\" >actual 2>actual.err &&\n \ttest_line_count = 0 actual &&\n \ttest_i18ngrep \"error\" actual.err &&\n-\ttest_i18ngrep ! \"usage\" actual.err\n+\t! grep \"usage\" actual.err\n '\n \n test_expect_success 'tag --no-contains <existent_tag>' '\n@@ -31,7 +31,7 @@ test_expect_success 'tag --no-contains <inexistent_tag>' '\n \ttest_must_fail git tag --no-contains \"notag\" >actual 2>actual.err &&\n \ttest_line_count = 0 actual &&\n \ttest_i18ngrep \"error\" actual.err &&\n-\ttest_i18ngrep ! \"usage\" actual.err\n+\t! grep \"usage\" actual.err\n '\n \n test_expect_success 'tag usage error' '\n@@ -50,7 +50,7 @@ test_expect_success 'branch --contains <inexistent_commit>' '\n \ttest_must_fail git branch --no-contains \"nocommit\" >actual 2>actual.err &&\n \ttest_line_count = 0 actual &&\n \ttest_i18ngrep \"error\" actual.err &&\n-\ttest_i18ngrep ! \"usage\" actual.err\n+\t! grep \"usage\" actual.err\n '\n \n test_expect_success 'branch --no-contains <existent_commit>' '\n@@ -63,7 +63,7 @@ test_expect_success 'branch --no-contains <inexistent_commit>' '\n \ttest_must_fail git branch --no-contains \"nocommit\" >actual 2>actual.err &&\n \ttest_line_count = 0 actual &&\n \ttest_i18ngrep \"error\" actual.err &&\n-\ttest_i18ngrep ! \"usage\" actual.err\n+\t! grep \"usage\" actual.err\n '\n \n test_expect_success 'branch usage error' '\n@@ -82,7 +82,7 @@ test_expect_success 'for-each-ref --contains <inexistent_object>' '\n \ttest_must_fail git for-each-ref --no-contains \"noobject\" >actual 2>actual.err &&\n \ttest_line_count = 0 actual &&\n \ttest_i18ngrep \"error\" actual.err &&\n-\ttest_i18ngrep ! \"usage\" actual.err\n+\t! grep \"usage\" actual.err\n '\n \n test_expect_success 'for-each-ref --no-contains <existent_object>' '\n@@ -95,7 +95,7 @@ test_expect_success 'for-each-ref --no-contains <inexistent_object>' '\n \ttest_must_fail git for-each-ref --no-contains \"noobject\" >actual 2>actual.err &&\n \ttest_line_count = 0 actual &&\n \ttest_i18ngrep \"error\" actual.err &&\n-\ttest_i18ngrep ! \"usage\" actual.err\n+\t! grep \"usage\" actual.err\n '\n \n test_expect_success 'for-each-ref usage error' '\ndiff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh\nindex bc89371f53..18dd448c7a 100755\n--- a/t/t1060-object-corruption.sh\n+++ b/t/t1060-object-corruption.sh\n@@ -123,7 +123,7 @@ test_expect_success 'fetch into corrupted repo with index-pack' '\n \t\tcd bit-error-cp &&\n \t\ttest_must_fail git -c transfer.unpackLimit=1 \\\n \t\t\tfetch ../no-bit-error 2>stderr &&\n-\t\ttest_i18ngrep ! -i collision stderr\n+\t\t! grep -i collision stderr\n \t)\n '\n \ndiff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh\nindex 84acfc48b6..e0b3c7e5e4 100755\n--- a/t/t1091-sparse-checkout-builtin.sh\n+++ b/t/t1091-sparse-checkout-builtin.sh\n@@ -180,7 +180,7 @@ test_expect_success 'cone mode: match patterns' '\n \tgit -C repo config --worktree core.sparseCheckoutCone true &&\n \trm -rf repo/a repo/folder1 repo/folder2 &&\n \tgit -C repo read-tree -mu HEAD 2>err &&\n-\ttest_i18ngrep ! \"disabling cone patterns\" err &&\n+\t! grep \"disabling cone patterns\" err &&\n \tgit -C repo reset --hard &&\n \tcheck_files repo a folder1 folder2\n '\n@@ -323,8 +323,8 @@ test_expect_success 'revert to old sparse-checkout on empty update' '\n \t\tgit add file &&\n \t\tgit commit -m \"test\" &&\n \t\tgit sparse-checkout set nothing 2>err &&\n-\t\ttest_i18ngrep ! \"Sparse checkout leaves no entry on working directory\" err &&\n-\t\ttest_i18ngrep ! \".git/index.lock\" err &&\n+\t\t! grep \"Sparse checkout leaves no entry on working directory\" err &&\n+\t\t! grep \".git/index.lock\" err &&\n \t\tgit sparse-checkout set file\n \t)\n '\n@@ -340,7 +340,7 @@ test_expect_success '.gitignore should not warn about cone mode' '\n \tgit -C repo config --worktree core.sparseCheckoutCone true &&\n \techo \"**/bin/*\" >repo/.gitignore &&\n \tgit -C repo reset --hard 2>err &&\n-\ttest_i18ngrep ! \"disabling cone patterns\" err\n+\t! grep \"disabling cone patterns\" err\n '\n \n test_expect_success 'sparse-checkout (init|set|disable) warns with dirty status' '\n@@ -417,7 +417,7 @@ test_expect_success 'sparse-checkout reapply' '\n \n \tgit -C tweak checkout HEAD deep/deeper2/a &&\n \tgit -C tweak sparse-checkout reapply 2>err &&\n-\ttest_i18ngrep ! \"warning.*The following paths are not up to date\" err &&\n+\t! grep \"warning.*The following paths are not up to date\" err &&\n \ttest_path_is_missing tweak/deep/deeper2/a &&\n \ttest_i18ngrep \"warning.*The following paths are unmerged\" err &&\n \ttest_path_is_file tweak/folder1/a &&\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex b17f5c21fb..884b33569c 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -301,7 +301,7 @@ test_expect_success 'unparseable tree object' '\n \ttest_must_fail git fsck 2>out &&\n \ttest_i18ngrep \"error: empty filename in tree entry\" out &&\n \ttest_i18ngrep \"$tree_sha1\" out &&\n-\ttest_i18ngrep ! \"fatal: empty filename in tree entry\" out\n+\t! grep \"fatal: empty filename in tree entry\" out\n '\n \n test_expect_success 'tree entry with type mismatch' '\n@@ -319,7 +319,7 @@ test_expect_success 'tree entry with type mismatch' '\n \tgit update-ref refs/heads/type_mismatch $commit &&\n \ttest_must_fail git fsck >out 2>&1 &&\n \ttest_i18ngrep \"is a blob, not a tree\" out &&\n-\ttest_i18ngrep ! \"dangling blob\" out\n+\t! grep \"dangling blob\" out\n '\n \n test_expect_success 'tag pointing to nonexistent' '\ndiff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh\nindex e2ae15a2cf..80006b3707 100755\n--- a/t/t1506-rev-parse-diagnosis.sh\n+++ b/t/t1506-rev-parse-diagnosis.sh\n@@ -167,7 +167,7 @@ test_expect_success 'relative path when cwd is outside worktree' '\n \n test_expect_success '<commit>:file correctly diagnosed after a pathname' '\n \ttest_must_fail git rev-parse file.txt HEAD:file.txt 1>actual 2>error &&\n-\ttest_i18ngrep ! \"exists on disk\" error &&\n+\t! grep \"exists on disk\" error &&\n \ttest_i18ngrep \"no such path in the working tree\" error &&\n \tcat >expect <<-\\EOF &&\n \tfile.txt\ndiff --git a/t/t2019-checkout-ambiguous-ref.sh b/t/t2019-checkout-ambiguous-ref.sh\nindex b99d5192a9..2a46257c76 100755\n--- a/t/t2019-checkout-ambiguous-ref.sh\n+++ b/t/t2019-checkout-ambiguous-ref.sh\n@@ -31,7 +31,7 @@ test_expect_success 'checkout chooses branch over tag' '\n \n test_expect_success 'checkout reports switch to branch' '\n \ttest_i18ngrep \"Switched to branch\" stderr &&\n-\ttest_i18ngrep ! \"^HEAD is now at\" stderr\n+\t! grep \"^HEAD is now at\" stderr\n '\n \n test_expect_success 'checkout vague ref succeeds' '\n@@ -53,7 +53,7 @@ test_expect_success VAGUENESS_SUCCESS 'checkout chooses branch over tag' '\n \n test_expect_success VAGUENESS_SUCCESS 'checkout reports switch to branch' '\n \ttest_i18ngrep \"Switched to branch\" stderr &&\n-\ttest_i18ngrep ! \"^HEAD is now at\" stderr\n+\t! grep \"^HEAD is now at\" stderr\n '\n \n test_done\ndiff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh\nindex b748db9946..552dd9f505 100755\n--- a/t/t2020-checkout-detach.sh\n+++ b/t/t2020-checkout-detach.sh\n@@ -14,10 +14,10 @@ check_not_detached () {\n PREV_HEAD_DESC='Previous HEAD position was'\n check_orphan_warning() {\n \ttest_i18ngrep \"you are leaving $2 behind\" \"$1\" &&\n-\ttest_i18ngrep ! \"$PREV_HEAD_DESC\" \"$1\"\n+\t! grep \"$PREV_HEAD_DESC\" \"$1\"\n }\n check_no_orphan_warning() {\n-\ttest_i18ngrep ! \"you are leaving .* commit.*behind\" \"$1\" &&\n+\t! grep \"you are leaving .* commit.*behind\" \"$1\" &&\n \ttest_i18ngrep \"$PREV_HEAD_DESC\" \"$1\"\n }\n \ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex a4f8d3a67e..6e5042c606 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -118,7 +118,7 @@ test_expect_success PERL 'checkout -p with multiple remotes does not print advic\n \ttest_might_fail git branch -D foo &&\n \n \tgit checkout -p foo 2>stderr &&\n-\ttest_i18ngrep ! \"^hint: \" stderr &&\n+\t! grep \"^hint: \" stderr &&\n \tstatus_uno_is_clean\n '\n \ndiff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh\nindex 7a2da972fd..958653aebf 100755\n--- a/t/t3418-rebase-continue.sh\n+++ b/t/t3418-rebase-continue.sh\n@@ -178,7 +178,7 @@ test_expect_success '--skip after failed fixup cleans commit message' '\n \n \t(test_set_editor \"$PWD/copy-editor.sh\" && git rebase --skip) &&\n \tgit show HEAD >out &&\n-\ttest_i18ngrep ! \"# This is a combination\" out &&\n+\t! grep \"# This is a combination\" out &&\n \n \t: Final squash failed, but there was still a squash &&\n \ttest_i18ngrep \"# This is a combination of 2 commits\" .git/copy.txt &&\ndiff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\nindex a21adcf0e4..2cc7338b75 100755\n--- a/t/t3507-cherry-pick-conflict.sh\n+++ b/t/t3507-cherry-pick-conflict.sh\n@@ -555,7 +555,7 @@ test_expect_success 'cherry-pick preserves sparse-checkout' '\n \techo /unrelated >.git/info/sparse-checkout &&\n \tgit read-tree --reset -u HEAD &&\n \ttest_must_fail git cherry-pick -Xours picked>actual &&\n-\ttest_i18ngrep ! \"Changes not staged for commit:\" actual\n+\t! grep \"Changes not staged for commit:\" actual\n '\n \n test_expect_success 'cherry-pick --continue remembers --keep-redundant-commits' '\ndiff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\nindex efec8d13b6..aa407095e9 100755\n--- a/t/t3600-rm.sh\n+++ b/t/t3600-rm.sh\n@@ -264,7 +264,7 @@ test_expect_success 'Resolving by removal is not a warning-worthy event' '\n \t\techo \"100644 $blob $stage\tblob\"\n \tdone | git update-index --index-info &&\n \tgit rm blob >msg 2>&1 &&\n-\ttest_i18ngrep ! \"needs merge\" msg &&\n+\t! grep \"needs merge\" msg &&\n \ttest_must_fail git ls-files -s --error-unmatch blob\n '\n \ndiff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh\nindex c16486a9d4..9fa37836c0 100755\n--- a/t/t4001-diff-rename.sh\n+++ b/t/t4001-diff-rename.sh\n@@ -145,7 +145,7 @@ test_expect_success 'test diff.renames=true for git status' '\n \n test_expect_success 'test diff.renames=false for git status' '\n \tgit -c diff.renames=false status >out &&\n-\ttest_i18ngrep ! \"renamed: .*path1 -> subdir/path1\" out &&\n+\t! grep \"renamed: .*path1 -> subdir/path1\" out &&\n \ttest_i18ngrep \"new file: .*subdir/path1\" out &&\n \ttest_i18ngrep \"deleted: .*[^/]path1\" out\n '\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex c5e5e0da3f..dce16a4741 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -2242,7 +2242,7 @@ test_expect_success 'interdiff: cover-letter' '\n \tEOF\n \tgit format-patch --cover-letter --interdiff=boop~2 -1 boop &&\n \ttest_i18ngrep \"^Interdiff:$\" 0000-cover-letter.patch &&\n-\ttest_i18ngrep ! \"^Interdiff:$\" 0001-fleep.patch &&\n+\t! grep \"^Interdiff:$\" 0001-fleep.patch &&\n \tsed \"1,/^@@ /d; /^-- $/q\" 0000-cover-letter.patch >actual &&\n \ttest_cmp expect actual\n '\ndiff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh\nindex 9675bc17db..b75b6358d1 100755\n--- a/t/t4018-diff-funcname.sh\n+++ b/t/t4018-diff-funcname.sh\n@@ -60,15 +60,15 @@ do\n \t\techo \"*.java diff=$p\" >.gitattributes &&\n \t\ttest_expect_code 1 git diff --no-index \\\n \t\t\tA.java B.java 2>msg &&\n-\t\ttest_i18ngrep ! fatal msg &&\n-\t\ttest_i18ngrep ! error msg\n+\t\t! grep fatal msg &&\n+\t\t! grep error msg\n \t'\n \ttest_expect_success \"builtin $p wordRegex pattern compiles\" '\n \t\techo \"*.java diff=$p\" >.gitattributes &&\n \t\ttest_expect_code 1 git diff --no-index --word-diff \\\n \t\t\tA.java B.java 2>msg &&\n-\t\ttest_i18ngrep ! fatal msg &&\n-\t\ttest_i18ngrep ! error msg\n+\t\t! grep fatal msg &&\n+\t\t! grep error msg\n \t'\n done\n \ndiff --git a/t/t4153-am-resume-override-opts.sh b/t/t4153-am-resume-override-opts.sh\nindex 8ea22d1bcb..74d4e07632 100755\n--- a/t/t4153-am-resume-override-opts.sh\n+++ b/t/t4153-am-resume-override-opts.sh\n@@ -53,7 +53,7 @@ test_expect_success '--no-quiet overrides --quiet' '\n \t# Applying side1 will be quiet.\n \ttest_must_fail git am --quiet side[123].eml >out &&\n \ttest_path_is_dir .git/rebase-apply &&\n-\ttest_i18ngrep ! \"^Applying: \" out &&\n+\t! grep \"^Applying: \" out &&\n \techo side1 >file &&\n \tgit add file &&\n \ndiff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh\nindex 4d3842b83b..5dc03be6e6 100755\n--- a/t/t5324-split-commit-graph.sh\n+++ b/t/t5324-split-commit-graph.sh\n@@ -342,7 +342,7 @@ test_expect_success 'add octopus merge' '\n \tgit commit-graph write --reachable --split &&\n \tgit commit-graph verify --progress 2>err &&\n \ttest_line_count = 3 err &&\n-\ttest_i18ngrep ! warning err &&\n+\t! grep warning err &&\n \ttest_line_count = 3 $graphdir/commit-graph-chain\n '\n \ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex ecadf02d64..8db6c1feaf 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -1341,7 +1341,7 @@ test_expect_success 'unqualified <dst> refspec DWIM and advice' '\n \t\t\ttest_must_fail git -c advice.pushUnqualifiedRefName=false \\\n \t\t\t\tpush origin $oid:dst 2>err &&\n \t\t\ttest_i18ngrep \"error: The destination you\" err &&\n-\t\t\ttest_i18ngrep ! \"hint: Did you mean\" err ||\n+\t\t\t! grep \"hint: Did you mean\" err ||\n \t\t\texit_with=false\n \t\tdone &&\n \t\t$exit_with\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 2013051a64..bfb282744e 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -993,7 +993,7 @@ test_expect_success '--no-show-forced-updates' '\n \t(\n \t\tcd no-forced-update-clone &&\n \t\tgit fetch --no-show-forced-updates origin 2>output &&\n-\t\ttest_i18ngrep ! \"(forced update)\" output\n+\t\t! grep \"(forced update)\" output\n \t)\n '\n \ndiff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh\nindex af0385fb89..a9a4df21b2 100755\n--- a/t/t5534-push-signed.sh\n+++ b/t/t5534-push-signed.sh\n@@ -284,7 +284,7 @@ test_expect_success GPG 'failed atomic push does not execute GPG' '\n \t\t\t--signed --atomic --porcelain \\\n \t\t\tdst noop ff noff >out 2>err &&\n \n-\ttest_i18ngrep ! \"gpg failed to sign\" err &&\n+\t! grep \"gpg failed to sign\" err &&\n \tcat >expect <<-EOF &&\n \tTo dst\n \t=\trefs/heads/noop:refs/heads/noop\t[up to date]\ndiff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh\nindex 187454f5dd..6969744d93 100755\n--- a/t/t5541-http-push-smart.sh\n+++ b/t/t5541-http-push-smart.sh\n@@ -321,7 +321,7 @@ test_expect_success TTY 'push --no-progress silences progress but not status' '\n \ttest_commit no-progress &&\n \ttest_terminal git push --no-progress >output 2>&1 &&\n \ttest_i18ngrep \"^To http\" output &&\n-\ttest_i18ngrep ! \"^Writing objects\" output\n+\t! grep \"^Writing objects\" output\n '\n \n test_expect_success 'push --progress shows progress to non-tty' '\n@@ -503,7 +503,7 @@ test_expect_success 'colorize errors/hints' '\n \ttest_i18ngrep \"<RED>.*rejected.*<RESET>\" decoded &&\n \ttest_i18ngrep \"<RED>error: failed to push some refs\" decoded &&\n \ttest_i18ngrep \"<YELLOW>hint: \" decoded &&\n-\ttest_i18ngrep ! \"^hint: \" decoded\n+\t! grep \"^hint: \" decoded\n '\n \n test_done\ndiff --git a/t/t5580-unc-paths.sh b/t/t5580-unc-paths.sh\nindex cf768b3a27..c143989456 100755\n--- a/t/t5580-unc-paths.sh\n+++ b/t/t5580-unc-paths.sh\n@@ -71,7 +71,7 @@ test_expect_success push '\n test_expect_success MINGW 'remote nick cannot contain backslashes' '\n \tBACKSLASHED=\"$(winpwd | tr / \\\\\\\\)\" &&\n \tgit ls-remote \"$BACKSLASHED\" 2>err &&\n-\ttest_i18ngrep ! \"unable to access\" err\n+\t! grep \"unable to access\" err\n '\n \n test_expect_success 'unc alternates' '\ndiff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\nindex 7f082fb23b..2eb221adb3 100755\n--- a/t/t5606-clone-options.sh\n+++ b/t/t5606-clone-options.sh\n@@ -89,7 +89,7 @@ test_expect_success 'redirected clone does not show progress' '\n \n \tgit clone \"file://$(pwd)/parent\" clone-redirected >out 2>err &&\n \t! grep % err &&\n-\ttest_i18ngrep ! \"Checking connectivity\" err\n+\t! grep \"Checking connectivity\" err\n \n '\n \ndiff --git a/t/t6001-rev-list-graft.sh b/t/t6001-rev-list-graft.sh\nindex 7504ba4751..5af149f58a 100755\n--- a/t/t6001-rev-list-graft.sh\n+++ b/t/t6001-rev-list-graft.sh\n@@ -116,7 +116,7 @@ test_expect_success 'show advice that grafts are deprecated' '\n \ttest_i18ngrep \"git replace\" err &&\n \ttest_config advice.graftFileDeprecated false &&\n \tgit show HEAD 2>err &&\n-\ttest_i18ngrep ! \"git replace\" err\n+\t! grep \"git replace\" err\n '\n \n test_done\ndiff --git a/t/t6050-replace.sh b/t/t6050-replace.sh\nindex c80dc10b8f..a6641e8874 100755\n--- a/t/t6050-replace.sh\n+++ b/t/t6050-replace.sh\n@@ -489,7 +489,7 @@ test_expect_success '--convert-graft-file' '\n \tgit status 2>stderr &&\n \ttest_i18ngrep \"hint:.*grafts is deprecated\" stderr &&\n \tgit replace --convert-graft-file 2>stderr &&\n-\ttest_i18ngrep ! \"hint:.*grafts is deprecated\" stderr &&\n+\t! grep \"hint:.*grafts is deprecated\" stderr &&\n \ttest_path_is_missing .git/info/grafts &&\n \n \t: verify that the history is now \"grafted\" &&\ndiff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh\nindex 4ab133f489..089f220160 100755\n--- a/t/t6423-merge-rename-directories.sh\n+++ b/t/t6423-merge-rename-directories.sh\n@@ -591,7 +591,7 @@ test_expect_success '2b: Directory split into two on one side, with equal number\n \t\tgit rev-parse >expect \\\n \t\t\t O:z/b  O:z/c  B:x/d &&\n \t\ttest_cmp expect actual &&\n-\t\ttest_i18ngrep ! \"CONFLICT.*directory rename split\" out\n+\t\t! grep \"CONFLICT.*directory rename split\" out\n \t)\n '\n \n@@ -727,7 +727,7 @@ test_expect_success '3b: Avoid implicit rename if involved as source on current\n \n \t\ttest_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&\n \t\ttest_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&\n-\t\ttest_i18ngrep ! CONFLICT.*rename/rename.*y/d out &&\n+\t\t! grep CONFLICT.*rename/rename.*y/d out &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 5 out &&\n@@ -3606,7 +3606,7 @@ test_expect_merge_algorithm failure success '10e: Does git complain about untrac\n \t\techo random >z/c &&\n \n \t\tgit -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&\n-\t\ttest_i18ngrep ! \"following untracked working tree files would be overwritten by merge\" err &&\n+\t\t! grep \"following untracked working tree files would be overwritten by merge\" err &&\n \n \t\tgit ls-files -s >out &&\n \t\ttest_line_count = 3 out &&\n@@ -5356,9 +5356,9 @@ test_expect_success '13e: directory rename detection in recursive case' '\n \n \t\tgit -c merge.directoryRenames=conflict merge -s recursive C^0 >out 2>err &&\n \n-\t\ttest_i18ngrep ! CONFLICT out &&\n-\t\ttest_i18ngrep ! BUG: err &&\n-\t\ttest_i18ngrep ! core.dumped err &&\n+\t\t! grep CONFLICT out &&\n+\t\t! grep BUG: err &&\n+\t\t! grep core.dumped err &&\n \t\ttest_must_be_empty err &&\n \n \t\tgit ls-files >paths &&\ndiff --git a/t/t6433-merge-toplevel.sh b/t/t6433-merge-toplevel.sh\nindex e29c284b9b..8418050d2d 100755\n--- a/t/t6433-merge-toplevel.sh\n+++ b/t/t6433-merge-toplevel.sh\n@@ -148,7 +148,7 @@ test_expect_success 'refuse two-project merge by default, quit before --autostas\n \techo change >>one.t &&\n \tgit diff >expect &&\n \ttest_must_fail git merge --autostash five 2>err &&\n-\ttest_i18ngrep ! \"stash\" err &&\n+\t! grep \"stash\" err &&\n \tgit diff >actual &&\n \ttest_cmp expect actual\n '\ndiff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh\nindex 3ead2b726f..e42a96cad4 100755\n--- a/t/t6437-submodule-merge.sh\n+++ b/t/t6437-submodule-merge.sh\n@@ -446,7 +446,7 @@ test_expect_failure !FAIL_PREREQS 'directory/submodule conflict; should not trea\n \t\t# We do not want files within the submodule to prevent the\n \t\t# merge from starting; we should not be writing to such paths\n \t\t# anyway.\n-\t\ttest_i18ngrep ! \"refusing to lose untracked file at\" err\n+\t\t! grep \"refusing to lose untracked file at\" err\n \t)\n '\n \ndiff --git a/t/t6500-gc.sh b/t/t6500-gc.sh\nindex 4a3b8f48ac..8670da6bf9 100755\n--- a/t/t6500-gc.sh\n+++ b/t/t6500-gc.sh\n@@ -111,7 +111,7 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre\n \ttest_commit \"$(test_oid obj4)\" &&\n \n \tgit gc --auto 2>err &&\n-\ttest_i18ngrep ! \"^warning:\" err &&\n+\t! grep \"^warning:\" err &&\n \tls .git/objects/pack/ | sort >post_packs &&\n \tcomm -1 -3 existing_packs post_packs >new &&\n \tcomm -2 -3 existing_packs post_packs >del &&\ndiff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh\nindex fec7e0299d..113b25892b 100755\n--- a/t/t7400-submodule-basic.sh\n+++ b/t/t7400-submodule-basic.sh\n@@ -158,7 +158,7 @@ test_expect_success 'redirected submodule add does not show progress' '\n \tgit -C addtest submodule add \"file://$submodurl/parent\" submod-redirected \\\n \t\t2>err &&\n \t! grep % err &&\n-\ttest_i18ngrep ! \"Checking connectivity\" err\n+\t! grep \"Checking connectivity\" err\n '\n \n test_expect_success 'redirected submodule add --progress does show progress' '\n@@ -1101,7 +1101,7 @@ test_expect_success 'submodule deinit deinits a submodule when its work tree is\n \tgit submodule deinit init example2 >actual &&\n \ttest -z \"$(git config --get-regexp \"submodule\\.example\\.\")\" &&\n \ttest -z \"$(git config --get-regexp \"submodule\\.example2\\.\")\" &&\n-\ttest_i18ngrep ! \"Cleared directory .init\" actual &&\n+\t! grep \"Cleared directory .init\" actual &&\n \ttest_i18ngrep \"Cleared directory .example2\" actual &&\n \trmdir init\n '\n@@ -1151,19 +1151,19 @@ test_expect_success 'submodule deinit is silent when used on an uninitialized su\n \ttest_i18ngrep \"Submodule .example. (.*) unregistered for path .init\" actual &&\n \ttest_i18ngrep \"Cleared directory .init\" actual &&\n \tgit submodule deinit init >actual &&\n-\ttest_i18ngrep ! \"Submodule .example. (.*) unregistered for path .init\" actual &&\n+\t! grep \"Submodule .example. (.*) unregistered for path .init\" actual &&\n \ttest_i18ngrep \"Cleared directory .init\" actual &&\n \tgit submodule deinit . >actual &&\n-\ttest_i18ngrep ! \"Submodule .example. (.*) unregistered for path .init\" actual &&\n+\t! grep \"Submodule .example. (.*) unregistered for path .init\" actual &&\n \ttest_i18ngrep \"Submodule .example2. (.*) unregistered for path .example2\" actual &&\n \ttest_i18ngrep \"Cleared directory .init\" actual &&\n \tgit submodule deinit . >actual &&\n-\ttest_i18ngrep ! \"Submodule .example. (.*) unregistered for path .init\" actual &&\n-\ttest_i18ngrep ! \"Submodule .example2. (.*) unregistered for path .example2\" actual &&\n+\t! grep \"Submodule .example. (.*) unregistered for path .init\" actual &&\n+\t! grep \"Submodule .example2. (.*) unregistered for path .example2\" actual &&\n \ttest_i18ngrep \"Cleared directory .init\" actual &&\n \tgit submodule deinit --all >actual &&\n-\ttest_i18ngrep ! \"Submodule .example. (.*) unregistered for path .init\" actual &&\n-\ttest_i18ngrep ! \"Submodule .example2. (.*) unregistered for path .example2\" actual &&\n+\t! grep \"Submodule .example. (.*) unregistered for path .init\" actual &&\n+\t! grep \"Submodule .example2. (.*) unregistered for path .example2\" actual &&\n \ttest_i18ngrep \"Cleared directory .init\" actual &&\n \trmdir init example2\n '\ndiff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh\nindex f2e7df59cf..2bae75228e 100755\n--- a/t/t7414-submodule-mistakes.sh\n+++ b/t/t7414-submodule-mistakes.sh\n@@ -17,7 +17,7 @@ test_expect_success 'git-add on embedded repository warns' '\n test_expect_success '--no-warn-embedded-repo suppresses warning' '\n \ttest_when_finished \"git rm --cached -f embed\" &&\n \tgit add --no-warn-embedded-repo embed 2>stderr &&\n-\ttest_i18ngrep ! warning stderr\n+\t! grep warning stderr\n '\n \n test_expect_success 'no warning when updating entry' '\n@@ -25,13 +25,13 @@ test_expect_success 'no warning when updating entry' '\n \tgit add embed &&\n \tgit -C embed commit --allow-empty -m two &&\n \tgit add embed 2>stderr &&\n-\ttest_i18ngrep ! warning stderr\n+\t! grep warning stderr\n '\n \n test_expect_success 'submodule add does not warn' '\n \ttest_when_finished \"git rm -rf submodule .gitmodules\" &&\n \tgit submodule add ./embed submodule 2>stderr &&\n-\ttest_i18ngrep ! warning stderr\n+\t! grep warning stderr\n '\n \n test_done\ndiff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh\nindex f70368bc2e..5228c88b9f 100755\n--- a/t/t7415-submodule-names.sh\n+++ b/t/t7415-submodule-names.sh\n@@ -187,7 +187,7 @@ test_expect_success 'fsck detects corrupt .gitmodules' '\n \n \t\tgit fsck 2>output &&\n \t\ttest_i18ngrep gitmodulesParse output &&\n-\t\ttest_i18ngrep ! \"bad config\" output\n+\t\t! grep \"bad config\" output\n \t)\n '\n \ndiff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh\nindex eec96e0ba9..80b36914c4 100755\n--- a/t/t7416-submodule-dash-url.sh\n+++ b/t/t7416-submodule-dash-url.sh\n@@ -57,7 +57,7 @@ test_expect_success 'trailing backslash is handled correctly' '\n \tmv .new .gitmodules &&\n \tgit commit -am \"Add testmodule\" &&\n \ttest_must_fail git clone --verbose --recurse-submodules . dolly 2>err &&\n-\ttest_i18ngrep ! \"unknown option\" err\n+\t! grep \"unknown option\" err\n '\n \n test_expect_success 'fsck rejects missing URL scheme' '\ndiff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh\nindex 14c92e4c25..6639a849c9 100755\n--- a/t/t7502-commit-porcelain.sh\n+++ b/t/t7502-commit-porcelain.sh\n@@ -526,7 +526,7 @@ try_commit () {\n \tGIT_EDITOR=.git/FAKE_EDITOR git commit -a $* $use_template &&\n \tcase \"$use_template\" in\n \t'')\n-\t\ttest_i18ngrep ! \"^## Custom template\" .git/COMMIT_EDITMSG ;;\n+\t\t! grep \"^## Custom template\" .git/COMMIT_EDITMSG ;;\n \t*)\n \t\ttest_i18ngrep \"^## Custom template\" .git/COMMIT_EDITMSG ;;\n \tesac\n@@ -546,7 +546,7 @@ try_commit_status_combo () {\n \n \ttest_expect_success 'commit --no-status' '\n \t\ttry_commit --no-status &&\n-\t\ttest_i18ngrep ! \"^# Changes to be committed:\" .git/COMMIT_EDITMSG\n+\t\t! grep \"^# Changes to be committed:\" .git/COMMIT_EDITMSG\n \t'\n \n \ttest_expect_success 'commit with commit.status = yes' '\n@@ -558,7 +558,7 @@ try_commit_status_combo () {\n \ttest_expect_success 'commit with commit.status = no' '\n \t\ttest_config commit.status no &&\n \t\ttry_commit \"\" &&\n-\t\ttest_i18ngrep ! \"^# Changes to be committed:\" .git/COMMIT_EDITMSG\n+\t\t! grep \"^# Changes to be committed:\" .git/COMMIT_EDITMSG\n \t'\n \n \ttest_expect_success 'commit --status with commit.status = yes' '\n@@ -570,7 +570,7 @@ try_commit_status_combo () {\n \ttest_expect_success 'commit --no-status with commit.status = yes' '\n \t\ttest_config commit.status yes &&\n \t\ttry_commit --no-status &&\n-\t\ttest_i18ngrep ! \"^# Changes to be committed:\" .git/COMMIT_EDITMSG\n+\t\t! grep \"^# Changes to be committed:\" .git/COMMIT_EDITMSG\n \t'\n \n \ttest_expect_success 'commit --status with commit.status = no' '\n@@ -582,7 +582,7 @@ try_commit_status_combo () {\n \ttest_expect_success 'commit --no-status with commit.status = no' '\n \t\ttest_config commit.status no &&\n \t\ttry_commit --no-status &&\n-\t\ttest_i18ngrep ! \"^# Changes to be committed:\" .git/COMMIT_EDITMSG\n+\t\t! grep \"^# Changes to be committed:\" .git/COMMIT_EDITMSG\n \t'\n \n }\ndiff --git a/t/t7508-status.sh b/t/t7508-status.sh\nindex 963fed6821..091c4dadea 100755\n--- a/t/t7508-status.sh\n+++ b/t/t7508-status.sh\n@@ -1547,7 +1547,7 @@ test_expect_success 'git commit will commit a staged but ignored submodule' '\n \texport GIT_EDITOR &&\n \tgit commit -uno &&\n \tgit status -s --ignore-submodules=dirty >output &&\n-\ttest_i18ngrep ! \"^M. sm\" output\n+\t! grep \"^M. sm\" output\n '\n \n test_expect_success 'git commit --dry-run will show a staged but ignored submodule' '\n@@ -1579,7 +1579,7 @@ EOF\n test_expect_success 'git commit -m will commit a staged but ignored submodule' '\n \tgit commit -uno -m message &&\n \tgit status -s --ignore-submodules=dirty >output &&\n-\ttest_i18ngrep ! \"^M. sm\" output &&\n+\t! grep \"^M. sm\" output &&\n \tgit config --remove-section submodule.subname &&\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n@@ -1626,7 +1626,7 @@ test_expect_success '\"No commits yet\" should not be noted in status output' '\n \tgit checkout --orphan empty-branch-2 &&\n \ttest_commit test-commit-1 &&\n \tgit status >output &&\n-\ttest_i18ngrep ! \"No commits yet\" output\n+\t! grep \"No commits yet\" output\n '\n \n test_expect_success '\"Initial commit\" should be noted in commit template' '\n@@ -1643,7 +1643,7 @@ test_expect_success '\"Initial commit\" should not be noted in commit template' '\n \ttouch to_be_committed_2 &&\n \tgit add to_be_committed_2 &&\n \tgit commit --dry-run >output &&\n-\ttest_i18ngrep ! \"Initial commit\" output\n+\t! grep \"Initial commit\" output\n '\n \n test_expect_success '--no-optional-locks prevents index update' '\ndiff --git a/t/t7518-ident-corner-cases.sh b/t/t7518-ident-corner-cases.sh\nindex 905957bd0a..1310b4381e 100755\n--- a/t/t7518-ident-corner-cases.sh\n+++ b/t/t7518-ident-corner-cases.sh\n@@ -13,7 +13,7 @@ test_expect_success 'empty name and missing email' '\n \t\tsane_unset GIT_AUTHOR_EMAIL &&\n \t\tGIT_AUTHOR_NAME= &&\n \t\ttest_must_fail git commit --allow-empty -m foo 2>err &&\n-\t\ttest_i18ngrep ! \"(null)\" err\n+\t\t! grep \"(null)\" err\n \t)\n '\n \ndiff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh\nindex fbfdcca000..f2c1e0b741 100755\n--- a/t/t7519-status-fsmonitor.sh\n+++ b/t/t7519-status-fsmonitor.sh\n@@ -290,10 +290,10 @@ do\n \t\t\trm -f marker &&\n \t\t\tgit status >actual &&\n \t\t\ttest_path_is_file marker &&\n-\t\t\ttest_i18ngrep ! \"Changes not staged for commit:\" actual &&\n+\t\t\t! grep \"Changes not staged for commit:\" actual &&\n \t\t\tif test $uc_val = true\n \t\t\tthen\n-\t\t\t\ttest_i18ngrep ! \"Untracked files:\" actual\n+\t\t\t\t! grep \"Untracked files:\" actual\n \t\t\tfi &&\n \t\t\tif test $uc_val = false\n \t\t\tthen\ndiff --git a/t/t7520-ignored-hook-warning.sh b/t/t7520-ignored-hook-warning.sh\nindex 634fb7f23a..06682b20d0 100755\n--- a/t/t7520-ignored-hook-warning.sh\n+++ b/t/t7520-ignored-hook-warning.sh\n@@ -15,7 +15,7 @@ test_expect_success setup '\n \n test_expect_success 'no warning if hook is not ignored' '\n \tgit commit --allow-empty -m \"more\" 2>message &&\n-\ttest_i18ngrep ! -e \"hook was ignored\" message\n+\t! grep -e \"hook was ignored\" message\n '\n \n test_expect_success POSIXPERM 'warning if hook is ignored' '\n@@ -28,14 +28,14 @@ test_expect_success POSIXPERM 'no warning if advice.ignoredHook set to false' '\n \ttest_config advice.ignoredHook false &&\n \tchmod -x \"$hook\" &&\n \tgit commit --allow-empty -m \"even more\" 2>message &&\n-\ttest_i18ngrep ! -e \"hook was ignored\" message\n+\t! grep -e \"hook was ignored\" message\n '\n \n test_expect_success 'no warning if unset advice.ignoredHook and hook removed' '\n \trm -f \"$hook\" &&\n \ttest_unconfig advice.ignoredHook &&\n \tgit commit --allow-empty -m \"even more\" 2>message &&\n-\ttest_i18ngrep ! -e \"hook was ignored\" message\n+\t! grep -e \"hook was ignored\" message\n '\n \n test_done\ndiff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh\nindex 52e8ccc933..10b392028e 100755\n--- a/t/t7601-merge-pull-config.sh\n+++ b/t/t7601-merge-pull-config.sh\n@@ -30,58 +30,58 @@ test_expect_success 'setup' '\n test_expect_success 'pull.rebase not set' '\n \tgit reset --hard c0 &&\n \tgit pull . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and pull.ff=true' '\n \tgit reset --hard c0 &&\n \ttest_config pull.ff true &&\n \tgit pull . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and pull.ff=false' '\n \tgit reset --hard c0 &&\n \ttest_config pull.ff false &&\n \tgit pull . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and pull.ff=only' '\n \tgit reset --hard c0 &&\n \ttest_config pull.ff only &&\n \tgit pull . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and --rebase given' '\n \tgit reset --hard c0 &&\n \tgit pull --rebase . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and --no-rebase given' '\n \tgit reset --hard c0 &&\n \tgit pull --no-rebase . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and --ff given' '\n \tgit reset --hard c0 &&\n \tgit pull --ff . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and --no-ff given' '\n \tgit reset --hard c0 &&\n \tgit pull --no-ff . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and --ff-only given' '\n \tgit reset --hard c0 &&\n \tgit pull --ff-only . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set (not-fast-forward)' '\n@@ -96,51 +96,51 @@ test_expect_success 'pull.rebase not set and pull.ff=true (not-fast-forward)' '\n \tgit reset --hard c2 &&\n \ttest_config pull.ff true &&\n \tgit pull . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and pull.ff=false (not-fast-forward)' '\n \tgit reset --hard c2 &&\n \ttest_config pull.ff false &&\n \tgit pull . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and pull.ff=only (not-fast-forward)' '\n \tgit reset --hard c2 &&\n \ttest_config pull.ff only &&\n \ttest_must_fail git pull . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and --rebase given (not-fast-forward)' '\n \tgit reset --hard c2 &&\n \tgit pull --rebase . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and --no-rebase given (not-fast-forward)' '\n \tgit reset --hard c2 &&\n \tgit pull --no-rebase . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and --ff given (not-fast-forward)' '\n \tgit reset --hard c2 &&\n \tgit pull --ff . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and --no-ff given (not-fast-forward)' '\n \tgit reset --hard c2 &&\n \tgit pull --no-ff . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'pull.rebase not set and --ff-only given (not-fast-forward)' '\n \tgit reset --hard c2 &&\n \ttest_must_fail git pull --ff-only . c1 2>err &&\n-\ttest_i18ngrep ! \"Pulling without specifying how to reconcile\" err\n+\t! grep \"Pulling without specifying how to reconcile\" err\n '\n \n test_expect_success 'merge c1 with c2' '\ndiff --git a/t/t7810-grep.sh b/t/t7810-grep.sh\nindex 991d5bd9c0..a7e17b1a5e 100755\n--- a/t/t7810-grep.sh\n+++ b/t/t7810-grep.sh\n@@ -1268,7 +1268,7 @@ test_expect_success 'grep --no-index prefers paths to revs' '\n \n test_expect_success 'grep --no-index does not \"diagnose\" revs' '\n \ttest_must_fail git grep --no-index o :1:hello.c 2>err &&\n-\ttest_i18ngrep ! -i \"did you mean\" err\n+\t! grep -i \"did you mean\" err\n '\n \n cat >expected <<EOF\ndiff --git a/t/t7816-grep-binary-pattern.sh b/t/t7816-grep-binary-pattern.sh\nindex 60bab291e4..25cbc16446 100755\n--- a/t/t7816-grep-binary-pattern.sh\n+++ b/t/t7816-grep-binary-pattern.sh\n@@ -25,7 +25,7 @@ nul_match_internal () {\n \t\t\t>stderr &&\n \t\t\tprintf '$pattern' | q_to_nul >f &&\n \t\t\ttest_must_fail env LC_ALL=\\\"$lc_all\\\" git grep $extra_flags -f f $flags a 2>stderr &&\n-\t\t\ttest_i18ngrep ! 'This is only supported with -P under PCRE v2' stderr\n+\t\t\t! grep 'This is only supported with -P under PCRE v2' stderr\n \t\t\"\n \telif test \"$matches\" = P\n \tthen\ndiff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh\nindex c98c1dfc23..3f656bbbaa 100755\n--- a/t/t9800-git-p4-basic.sh\n+++ b/t/t9800-git-p4-basic.sh\n@@ -202,7 +202,7 @@ test_expect_success 'exit when p4 fails to produce marshaled output' '\n \t\texport PATH &&\n \t\ttest_expect_code 1 git p4 clone --dest=\"$git\" //depot >errs 2>&1\n \t) &&\n-\ttest_i18ngrep ! Traceback errs\n+\t! grep Traceback errs\n '\n \n # Hide a file from p4d, make sure we catch its complaint.  This won't fail in\ndiff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh\nindex eaaae414a1..22c8106b13 100755\n--- a/t/t9807-git-p4-submit.sh\n+++ b/t/t9807-git-p4-submit.sh\n@@ -460,7 +460,7 @@ test_expect_success 'submit --prepare-p4-only' '\n \t\tgit p4 submit --prepare-p4-only >out &&\n \t\ttest_i18ngrep \"prepared for submission\" out &&\n \t\ttest_i18ngrep \"must be deleted\" out &&\n-\t\ttest_i18ngrep ! \"everything below this line is just the diff\" out\n+\t\t! grep \"everything below this line is just the diff\" out\n \t) &&\n \t(\n \t\tcd \"$cli\" &&\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"414054","messageId":"20210111144740.6092-6-avarab@gmail.com","threadId":"51667","inReplyTo":"87pn2b6eyb.fsf@evledraar.gmail.com","subject":"[PATCH 5/6] tests: (almost) remove C_LOCALE_OUTPUT prerequisites","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-11T14:47:39Z","receivedAt":"2021-01-11T14:49:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Almost entirely Remove the C_LOCALE_OUTPUT prerequisites and its uses\nas a part of getting rid of the GIT_TEST_GETTEXT_POISON facility.\n\nAs with test_i18ngrep we're not removing the stub C_LOCALE_OUTPUT\nprerequisite yet for the benefit of other in-flight code.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t0000-basic.sh                    |  2 +-\n t/t0020-crlf.sh                     |  6 ++----\n t/t0201-gettext-fallbacks.sh        |  2 +-\n t/t1430-bad-ref-name.sh             |  2 +-\n t/t1512-rev-parse-disambiguation.sh | 14 +++++++-------\n t/t3404-rebase-interactive.sh       | 14 +++++++-------\n t/t3415-rebase-autosquash.sh        | 10 +++++-----\n t/t3701-add-interactive.sh          |  2 +-\n t/t4012-diff-binary.sh              |  4 ++--\n t/t4205-log-pretty-formats.sh       |  2 +-\n t/t5300-pack-object.sh              |  4 ++--\n t/t5505-remote.sh                   |  6 +++---\n t/t5510-fetch.sh                    |  4 ++--\n t/t5601-clone.sh                    |  2 +-\n t/t6423-merge-rename-directories.sh |  2 +-\n t/t7300-clean.sh                    |  6 +++---\n t/t7810-grep.sh                     |  2 +-\n t/t9003-help-autocorrect.sh         |  7 ++-----\n 18 files changed, 43 insertions(+), 48 deletions(-)\n\ndiff --git a/t/t0000-basic.sh b/t/t0000-basic.sh\nindex f4ba2e8c85..45a88315ad 100755\n--- a/t/t0000-basic.sh\n+++ b/t/t0000-basic.sh\n@@ -271,7 +271,7 @@ test_expect_success 'pretend we have a mix of all possible results' \"\n \tEOF\n \"\n \n-test_expect_success C_LOCALE_OUTPUT 'test --verbose' '\n+test_expect_success 'test --verbose' '\n \trun_sub_test_lib_test_err \\\n \t\tt1234-verbose \"test verbose\" --verbose <<-\\EOF &&\n \ttest_expect_success \"passing test\" true\ndiff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh\nindex b63ba62e5d..7081485e21 100755\n--- a/t/t0020-crlf.sh\n+++ b/t/t0020-crlf.sh\n@@ -84,10 +84,8 @@ test_expect_success 'safecrlf: print warning only once' '\n \tgit commit -m \"nowarn\" &&\n \tfor w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >doublewarn &&\n \tgit add doublewarn 2>err &&\n-\tif test_have_prereq C_LOCALE_OUTPUT\n-\tthen\n-\t\ttest $(grep \"CRLF will be replaced by LF\" err | wc -l) = 1\n-\tfi\n+\tgrep \"CRLF will be replaced by LF\" err >err.lines &&\n+\ttest_line_count = 1 err.lines\n '\n \n \ndiff --git a/t/t0201-gettext-fallbacks.sh b/t/t0201-gettext-fallbacks.sh\nindex 90da1c7ddc..a7b3e4a2c7 100755\n--- a/t/t0201-gettext-fallbacks.sh\n+++ b/t/t0201-gettext-fallbacks.sh\n@@ -18,7 +18,7 @@ test_expect_success 'sanity: $GIT_INTERNAL_GETTEXT_TEST_FALLBACKS is set' '\n     test -n \"$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS\"\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME\" is fallthrough' '\n+test_expect_success 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME\" is fallthrough' '\n     echo fallthrough >expect &&\n     echo $GIT_INTERNAL_GETTEXT_SH_SCHEME >actual &&\n     test_cmp expect actual\ndiff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh\nindex c7878a60ed..138fc9b1c3 100755\n--- a/t/t1430-bad-ref-name.sh\n+++ b/t/t1430-bad-ref-name.sh\n@@ -122,7 +122,7 @@ test_expect_success 'push cannot create a badly named ref' '\n \t! grep -e \"broken\\.\\.\\.ref\" output\n '\n \n-test_expect_failure C_LOCALE_OUTPUT 'push --mirror can delete badly named ref' '\n+test_expect_failure 'push --mirror can delete badly named ref' '\n \ttop=$(pwd) &&\n \tgit init src &&\n \tgit init dest &&\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex 18fa6cf40d..3a4b337ae0 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -311,39 +311,39 @@ test_expect_success 'ambiguous short sha1 ref' '\n \tgrep \"refname.*${REF}.*ambiguous\" err\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (raw)' '\n+test_expect_success 'ambiguity errors are not repeated (raw)' '\n \ttest_must_fail git rev-parse 00000 2>stderr &&\n \tgrep \"is ambiguous\" stderr >errors &&\n \ttest_line_count = 1 errors\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (treeish)' '\n+test_expect_success 'ambiguity errors are not repeated (treeish)' '\n \ttest_must_fail git rev-parse 00000:foo 2>stderr &&\n \tgrep \"is ambiguous\" stderr >errors &&\n \ttest_line_count = 1 errors\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (peel)' '\n+test_expect_success 'ambiguity errors are not repeated (peel)' '\n \ttest_must_fail git rev-parse 00000^{commit} 2>stderr &&\n \tgrep \"is ambiguous\" stderr >errors &&\n \ttest_line_count = 1 errors\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'ambiguity hints' '\n+test_expect_success 'ambiguity hints' '\n \ttest_must_fail git rev-parse 000000000 2>stderr &&\n \tgrep ^hint: stderr >hints &&\n \t# 16 candidates, plus one intro line\n \ttest_line_count = 17 hints\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'ambiguity hints respect type' '\n+test_expect_success 'ambiguity hints respect type' '\n \ttest_must_fail git rev-parse 000000000^{commit} 2>stderr &&\n \tgrep ^hint: stderr >hints &&\n \t# 5 commits, 1 tag (which is a committish), plus intro line\n \ttest_line_count = 7 hints\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'failed type-selector still shows hint' '\n+test_expect_success 'failed type-selector still shows hint' '\n \t# these two blobs share the same prefix \"ee3d\", but neither\n \t# will pass for a commit\n \techo 851 | git hash-object --stdin -w &&\n@@ -367,7 +367,7 @@ test_expect_success 'core.disambiguate does not override context' '\n \t\tgit -c core.disambiguate=committish rev-parse $sha1^{tree}\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'ambiguous commits are printed by type first, then hash order' '\n+test_expect_success 'ambiguous commits are printed by type first, then hash order' '\n \ttest_must_fail git rev-parse 0000 2>stderr &&\n \tgrep ^hint: stderr >hints &&\n \tgrep 0000 hints >objects &&\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex b06fc36159..c53ae6b5c7 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -447,7 +447,7 @@ test_expect_success 'verbose flag is heeded, even after --continue' '\n \tgrep \"^ file1 | 2 +-$\" output\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'multi-squash only fires up editor once' '\n+test_expect_success 'multi-squash only fires up editor once' '\n \tbase=$(git rev-parse HEAD~4) &&\n \t(\n \t\tset_fake_editor &&\n@@ -460,7 +460,7 @@ test_expect_success C_LOCALE_OUTPUT 'multi-squash only fires up editor once' '\n \ttest 1 = $(git show | grep ONCE | wc -l)\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'multi-fixup does not fire up editor' '\n+test_expect_success 'multi-fixup does not fire up editor' '\n \tgit checkout -b multi-fixup E &&\n \tbase=$(git rev-parse HEAD~4) &&\n \t(\n@@ -511,7 +511,7 @@ test_expect_success 'commit message retained after conflict' '\n \tgit branch -D conflict-squash\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messages' '\n+test_expect_success 'squash and fixup generate correct log messages' '\n \tcat >expect-squash-fixup <<-\\EOF &&\n \tB\n \n@@ -538,7 +538,7 @@ test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messa\n \tgit branch -D squash-fixup\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'squash ignores comments' '\n+test_expect_success 'squash ignores comments' '\n \tgit checkout -b skip-comments E &&\n \tbase=$(git rev-parse HEAD~4) &&\n \t(\n@@ -554,7 +554,7 @@ test_expect_success C_LOCALE_OUTPUT 'squash ignores comments' '\n \tgit branch -D skip-comments\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'squash ignores blank lines' '\n+test_expect_success 'squash ignores blank lines' '\n \tgit checkout -b skip-blank-lines E &&\n \tbase=$(git rev-parse HEAD~4) &&\n \t(\n@@ -992,7 +992,7 @@ test_expect_success 'rebase -ix with several instances of --exec' '\n \ttest_cmp expected actual\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'rebase -ix with --autosquash' '\n+test_expect_success 'rebase -ix with --autosquash' '\n \tgit reset --hard execute &&\n \tgit checkout -b autosquash &&\n \techo second >second.txt &&\n@@ -1133,7 +1133,7 @@ test_expect_success 'rebase -i --root reword root when root has untracked file c\n \ttest \"$(git rev-list --count HEAD)\" = 2\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-interactive rebase' '\n+test_expect_success 'rebase --edit-todo does not work on non-interactive rebase' '\n \tgit checkout reword-original-root-branch &&\n \tgit reset --hard &&\n \tgit checkout conflict-branch &&\ndiff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh\nindex 7bab6000dc..e678f26c01 100755\n--- a/t/t3415-rebase-autosquash.sh\n+++ b/t/t3415-rebase-autosquash.sh\n@@ -303,23 +303,23 @@ test_auto_fixup_fixup () {\n \tfi\n }\n \n-test_expect_success C_LOCALE_OUTPUT 'fixup! fixup!' '\n+test_expect_success 'fixup! fixup!' '\n \ttest_auto_fixup_fixup fixup fixup\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'fixup! squash!' '\n+test_expect_success 'fixup! squash!' '\n \ttest_auto_fixup_fixup fixup squash\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'squash! squash!' '\n+test_expect_success 'squash! squash!' '\n \ttest_auto_fixup_fixup squash squash\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'squash! fixup!' '\n+test_expect_success 'squash! fixup!' '\n \ttest_auto_fixup_fixup squash fixup\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'autosquash with custom inst format' '\n+test_expect_success 'autosquash with custom inst format' '\n \tgit reset --hard base &&\n \tgit config --add rebase.instructionFormat \"[%an @ %ar] %s\"  &&\n \techo 2 >file1 &&\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex cc3f434a97..6cdde711e0 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -367,7 +367,7 @@ test_expect_success 'setup expected' '\n '\n \n # Test splitting the first patch, then adding both\n-test_expect_success C_LOCALE_OUTPUT 'add first line works' '\n+test_expect_success 'add first line works' '\n \tgit commit -am \"clear local changes\" &&\n \tgit apply patch &&\n \tprintf \"%s\\n\" s y y | git add -p file 2>error |\ndiff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh\nindex 6579c81216..bd59328e4b 100755\n--- a/t/t4012-diff-binary.sh\n+++ b/t/t4012-diff-binary.sh\n@@ -63,7 +63,7 @@ test_expect_success 'apply --numstat understands diff --binary format' '\n \n # apply needs to be able to skip the binary material correctly\n # in order to report the line number of a corrupt patch.\n-test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '\n+test_expect_success 'apply detecting corrupt patch correctly' '\n \tgit diff >output &&\n \tsed -e \"s/-CIT/xCIT/\" <output >broken &&\n \ttest_must_fail git apply --stat --summary broken 2>detected &&\n@@ -73,7 +73,7 @@ test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '\n \ttest \"$detected\" = xCIT\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '\n+test_expect_success 'apply detecting corrupt patch correctly' '\n \tgit diff --binary | sed -e \"s/-CIT/xCIT/\" >broken &&\n \ttest_must_fail git apply --stat --summary broken 2>detected &&\n \tdetected=$(cat detected) &&\ndiff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh\nindex 749bc1431a..5ec0f47b56 100755\n--- a/t/t4205-log-pretty-formats.sh\n+++ b/t/t4205-log-pretty-formats.sh\n@@ -126,7 +126,7 @@ test_expect_success 'NUL separation with --stat' '\n \ttest_i18ncmp expected actual\n '\n \n-test_expect_failure C_LOCALE_OUTPUT 'NUL termination with --stat' '\n+test_expect_failure 'NUL termination with --stat' '\n \tstat0_part=$(git diff --stat HEAD^ HEAD) &&\n \tstat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&\n \tprintf \"add bar\\n$stat0_part\\n\\0$(commit_msg)\\n$stat1_part\\n0\" >expected &&\ndiff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh\nindex 392201cabd..d586fdc7a9 100755\n--- a/t/t5300-pack-object.sh\n+++ b/t/t5300-pack-object.sh\n@@ -427,7 +427,7 @@ test_expect_success 'index-pack --strict <pack> works in non-repo' '\n \ttest_path_is_file foo.idx\n '\n \n-test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'index-pack --threads=N or pack.threads=N warns when no pthreads' '\n+test_expect_success !PTHREADS 'index-pack --threads=N or pack.threads=N warns when no pthreads' '\n \ttest_must_fail git index-pack --threads=2 2>err &&\n \tgrep ^warning: err >warnings &&\n \ttest_line_count = 1 warnings &&\n@@ -445,7 +445,7 @@ test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'index-pack --threads=N or pack.th\n \tgrep -F \"no threads support, ignoring pack.threads\" err\n '\n \n-test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'pack-objects --threads=N or pack.threads=N warns when no pthreads' '\n+test_expect_success !PTHREADS 'pack-objects --threads=N or pack.threads=N warns when no pthreads' '\n \tgit pack-objects --threads=2 --stdout --all </dev/null >/dev/null 2>err &&\n \tgrep ^warning: err >warnings &&\n \ttest_line_count = 1 warnings &&\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex 8db6c1feaf..3d5360d7cd 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -56,7 +56,7 @@ test_expect_success 'add remote whose URL agrees with url.<...>.insteadOf' '\n \tgit remote add myremote git@host.com:team/repo.git\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' '\n+test_expect_success 'remote information for the origin' '\n \t(\n \t\tcd test &&\n \t\ttokens_match origin \"$(git remote)\" &&\n@@ -78,7 +78,7 @@ test_expect_success 'add another remote' '\n \t)\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'check remote-tracking' '\n+test_expect_success 'check remote-tracking' '\n \t(\n \t\tcd test &&\n \t\tcheck_remote_track origin main side &&\n@@ -104,7 +104,7 @@ test_expect_success 'remove remote' '\n \t)\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'remove remote' '\n+test_expect_success 'remove remote' '\n \t(\n \t\tcd test &&\n \t\ttokens_match origin \"$(git remote)\" &&\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex bfb282744e..b7233fc8ce 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -944,7 +944,7 @@ test_expect_success 'fetching with auto-gc does not lock up' '\n \t)\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'fetch aligned output' '\n+test_expect_success 'fetch aligned output' '\n \tgit clone . full-output &&\n \ttest_commit looooooooooooong-tag &&\n \t(\n@@ -959,7 +959,7 @@ test_expect_success C_LOCALE_OUTPUT 'fetch aligned output' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'fetch compact output' '\n+test_expect_success 'fetch compact output' '\n \tgit clone . compact &&\n \ttest_commit extraaa &&\n \t(\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex 7df3c5373a..d2b02fd25e 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -37,7 +37,7 @@ test_expect_success 'clone with excess parameters (2)' '\n \n '\n \n-test_expect_success C_LOCALE_OUTPUT 'output from clone' '\n+test_expect_success 'output from clone' '\n \trm -fr dst &&\n \tgit clone -n \"file://$(pwd)/src\" dst >output 2>&1 &&\n \ttest $(grep Clon output | wc -l) = 1\ndiff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh\nindex 089f220160..2ca70f6274 100755\n--- a/t/t6423-merge-rename-directories.sh\n+++ b/t/t6423-merge-rename-directories.sh\n@@ -2905,7 +2905,7 @@ test_setup_9e () {\n \t)\n }\n \n-test_expect_success C_LOCALE_OUTPUT '9e: N-to-1 whammo' '\n+test_expect_success '9e: N-to-1 whammo' '\n \ttest_setup_9e &&\n \t(\n \t\tcd 9e &&\ndiff --git a/t/t7300-clean.sh b/t/t7300-clean.sh\nindex cb5e34d94c..a74816ca8b 100755\n--- a/t/t7300-clean.sh\n+++ b/t/t7300-clean.sh\n@@ -110,7 +110,7 @@ test_expect_success 'git clean with prefix' '\n \n '\n \n-test_expect_success C_LOCALE_OUTPUT 'git clean with relative prefix' '\n+test_expect_success 'git clean with relative prefix' '\n \n \tmkdir -p build docs &&\n \ttouch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&\n@@ -123,7 +123,7 @@ test_expect_success C_LOCALE_OUTPUT 'git clean with relative prefix' '\n \tverbose test \"$would_clean\" = ../src/part3.c\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'git clean with absolute path' '\n+test_expect_success 'git clean with absolute path' '\n \n \tmkdir -p build docs &&\n \ttouch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&\n@@ -407,7 +407,7 @@ test_expect_success 'clean.requireForce and -f' '\n \n '\n \n-test_expect_success C_LOCALE_OUTPUT 'core.excludesfile' '\n+test_expect_success 'core.excludesfile' '\n \n \techo excludes >excludes &&\n \techo included >included &&\ndiff --git a/t/t7810-grep.sh b/t/t7810-grep.sh\nindex a7e17b1a5e..0c4a0e3865 100755\n--- a/t/t7810-grep.sh\n+++ b/t/t7810-grep.sh\n@@ -978,7 +978,7 @@ do\n \t\"\n done\n \n-test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'grep --threads=N or pack.threads=N warns when no pthreads' '\n+test_expect_success !PTHREADS 'grep --threads=N or pack.threads=N warns when no pthreads' '\n \tgit grep --threads=2 Hello hello_world 2>err &&\n \tgrep ^warning: err >warnings &&\n \ttest_line_count = 1 warnings &&\ndiff --git a/t/t9003-help-autocorrect.sh b/t/t9003-help-autocorrect.sh\nindex 03cd5c5423..f00deaf381 100755\n--- a/t/t9003-help-autocorrect.sh\n+++ b/t/t9003-help-autocorrect.sh\n@@ -56,11 +56,8 @@ test_expect_success 'autocorrect can be declined altogether' '\n \tgit config help.autocorrect never &&\n \n \ttest_must_fail git lfg 2>actual &&\n-\tif test_have_prereq C_LOCALE_OUTPUT\n-\tthen\n-\t\tgrep \"is not a git command\" actual &&\n-\t\ttest_line_count = 1 actual\n-\tfi\n+\tgrep \"is not a git command\" actual &&\n+\ttest_line_count = 1 actual\n '\n \n test_done\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"414077","messageId":"xmqqim836v6m.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"87pn2b6eyb.fsf@evledraar.gmail.com","subject":"Re: Should you use test_i18ngrep or GIT_TEST_GETTEXT_POISON=false?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-01-11T21:05:37Z","receivedAt":"2021-01-11T21:06:41Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> What do you think about just removing it? I.e. make setting it a noop?\n\nI have been seeing occasional CI job failures from new tests that\nforget to use test_i18ngrep.  I actually think marking such a grep\nas \"this is looking for a string that is meant for humans\" a good\nway to document the interface and expected end-user interaction,\nso I am not sure about just removing it.\n\nSo after all, test_i18ngrep may make more sense than setting\nGIT_TEST_GETTEXT_POISON to false.  I dunno.\n"},{"id":"414078","messageId":"xmqqeeir6v5p.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20210111131451.7084-1-avarab@gmail.com","subject":"Re: [PATCH 0/2] Makefile & gettext.c: remove warning & long comment","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-01-11T21:06:10Z","receivedAt":"2021-01-11T21:06:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> We've got a huge comment in gettext.c that nobody cares about anymore,\n> remove it, and an old warning in the Makefile while I'm at it.\n>\n> This doesn't conflict with any other patches I'm about to submit, I\n> wanted to submit it separately because these are small isolated\n> non-contentious changes.\n\nBoth look sensible.\n\nThanks, will queue.\n\n> Ævar Arnfjörð Bjarmason (2):\n>   Makefile: remove a warning about old GETTEXT_POISON flag\n>   gettext.c: remove/reword a mostly-useless comment\n>\n>  Makefile  |  3 --\n>  gettext.c | 94 +++++++++----------------------------------------------\n>  2 files changed, 15 insertions(+), 82 deletions(-)\n"},{"id":"414079","messageId":"xmqqa6tf6sxj.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20210111144740.6092-3-avarab@gmail.com","subject":"Re: [PATCH 2/6] tests: remove support for GIT_TEST_GETTEXT_POISON","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-01-11T21:54:16Z","receivedAt":"2021-01-11T21:55:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> diff --git a/po/README b/po/README\n> index 07595d369b..b9b6c9cb24 100644\n> --- a/po/README\n> +++ b/po/README\n> @@ -284,23 +284,27 @@ Perl:\n>  Testing marked strings\n>  ----------------------\n>  \n> +Git's tests are run under LANG=C LC_ALL=C. So the tests do not need be\n> +changed to account for translations as they're added.\n\nAfter we told readers this ...\n\n> +Testing whether marked strings are used by plumbing\n> ...\n> +It's still possible to do a one-off test of where a string might be\n> +used in the tests by simply replacing the \"some hardcoded string\" with\n> +something like \"POISON ME\" locally and running the tests, before\n> +finally converting it to _(\"some hardcoded string\").\n\n... I do not see how this manual check can still be effective.\n\nPeople rely on our tests running under C locale, and will add tests\nto grep for error messages meant for humans in C locale.  What does\nit help to notice that a particular string you are contemplating to\ntranslate _is_ being looked for in an existing test?  The author of\nsuch a test knew it was meant for humans, but trusted our promise to\nrun tests in the C locale and used grep for it, in which case it is\na fair game to be marked with _().\n\nI think we should honestly admit that after this change it is no\nlonger possible for our tests to help catching strings that are\nmistakenly marked for translation when they are meant for machine\nconsumption, and justify why it is still OK to apply this change.\n\n> diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\n> index 999982fe4a..c7b1d857c4 100644\n> --- a/t/test-lib-functions.sh\n> +++ b/t/test-lib-functions.sh\n\nThe changes to this file all look very attractive ;-)  Not having to\nworry about poison test certainly makes it easier to write tests.\n\nI just do not know if the price we are paying for the easier life is\ntoo high.\n\nThanks.\n"},{"id":"414116","messageId":"20210112085051.GV8396@szeder.dev","threadId":"51667","inReplyTo":"20210111144740.6092-2-avarab@gmail.com","subject":"Re: [PATCH 1/6] ci: remove GETTEXT_POISON jobs","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2021-01-12T08:50:51Z","receivedAt":"2021-01-12T08:51:36Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Mon, Jan 11, 2021 at 03:47:35PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> A subsequent commit will remove GETTEXT_POISON entirely, let's start\n> by removing the CI jobs that enable the option.\n> \n> We cannot just remove the job because the CI is implicitly depending\n> on the \"poison\" job being a sort of \"default\" job.\n\nI don't understand what you mean here with a \"default job\" that the CI\nis implicitly depending on.  There is certainly no such default job on\nTravis CI, and I don't think there is one on the GitHub thing.\n\n> Let's instead add a\n> \"default\" job.\n> \n> This means we can remove the initial \"make test\" from the \"linux-gcc\"\n> job (it does another one after setting a bunch of GIT_TEST_*\n> variables).\n> \n> I'm not doing that because it would conflict with the in-flight\n> 334afbc76fb (tests: mark tests relying on the current default for\n> `init.defaultBranch`, 2020-11-18) (currently on the \"seen\" branch, so\n> the SHA-1 will almost definitely change). It's going to use that \"make\n> test\" again for different reasons, so let's preserve it for now.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  .github/workflows/main.yml | 2 +-\n>  .travis.yml                | 2 +-\n>  ci/install-dependencies.sh | 2 +-\n>  ci/lib.sh                  | 3 +--\n>  4 files changed, 4 insertions(+), 5 deletions(-)\n> \n> diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml\n> index aef6643648..8b52df200f 100644\n> --- a/.github/workflows/main.yml\n> +++ b/.github/workflows/main.yml\n> @@ -286,7 +286,7 @@ jobs:\n>            - jobname: osx-gcc\n>              cc: gcc\n>              pool: macos-latest\n> -          - jobname: GETTEXT_POISON\n> +          - jobname: linux-gcc-default\n>              cc: gcc\n>              pool: ubuntu-latest\n>      env:\n> diff --git a/.travis.yml b/.travis.yml\n> index 05f3e3f8d7..908330a0a3 100644\n> --- a/.travis.yml\n> +++ b/.travis.yml\n> @@ -16,7 +16,7 @@ compiler:\n>  \n>  matrix:\n>    include:\n> -    - env: jobname=GETTEXT_POISON\n> +    - env: jobname=linux-gcc-default\n>        os: linux\n>        compiler:\n>        addons:\n> diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh\n> index 0229a77f7d..79c0633a18 100755\n> --- a/ci/install-dependencies.sh\n> +++ b/ci/install-dependencies.sh\n> @@ -72,7 +72,7 @@ Documentation)\n>  \ttest -n \"$ALREADY_HAVE_ASCIIDOCTOR\" ||\n>  \tsudo gem install --version 1.5.8 asciidoctor\n>  \t;;\n> -linux-gcc-4.8|GETTEXT_POISON)\n> +linux-gcc-default|linux-gcc-4.8)\n>  \tsudo apt-get -q update\n>  \tsudo apt-get -q -y install $UBUNTU_COMMON_PKGS\n>  \t;;\n> diff --git a/ci/lib.sh b/ci/lib.sh\n> index 38c0eac351..d848c036c5 100755\n> --- a/ci/lib.sh\n> +++ b/ci/lib.sh\n> @@ -220,8 +220,7 @@ osx-clang|osx-gcc)\n>  \t# Travis CI OS X\n>  \texport GIT_SKIP_TESTS=\"t9810 t9816\"\n>  \t;;\n> -GETTEXT_POISON)\n> -\texport GIT_TEST_GETTEXT_POISON=true\n> +linux-gcc-default)\n>  \t;;\n>  Linux32)\n>  \tCC=gcc\n> -- \n> 2.29.2.222.g5d2a92d10f8\n> \n"},{"id":"414130","messageId":"X/2Gd9J/zWqK2wLn@coredump.intra.peff.net","threadId":"51667","inReplyTo":"xmqqim836v6m.fsf@gitster.c.googlers.com","subject":"Re: Should you use test_i18ngrep or GIT_TEST_GETTEXT_POISON=false?","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-01-12T11:22:31Z","receivedAt":"2021-01-12T11:23:14Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Jan 11, 2021 at 01:05:37PM -0800, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n> \n> > What do you think about just removing it? I.e. make setting it a noop?\n> \n> I have been seeing occasional CI job failures from new tests that\n> forget to use test_i18ngrep.  I actually think marking such a grep\n> as \"this is looking for a string that is meant for humans\" a good\n> way to document the interface and expected end-user interaction,\n> so I am not sure about just removing it.\n> \n> So after all, test_i18ngrep may make more sense than setting\n> GIT_TEST_GETTEXT_POISON to false.  I dunno.\n\nI agree that test_i18ngrep is preferable to setting\nGIT_TEST_GETTEXT_POISON. Since it's tied to the comparison itself, I\nthink it's easier to see which messages we're expecting as\nhuman-readable. It's also shorter and more readable IMHO (you'd already\nbe calling grep or test_cmp, so it is only a little longer).\n\nI am on the fence on whether the presence of something like\ntest_i18ngrep in the test suite is really serving as a useful indication\nof what was meant to be translated and what wasn't. It's such an\nincomplete coverage of the total set of messages we generate that I\nwouldn't trust it. I'd be more likely to look at the source to see if a\nmessage is actually translated, or just assume we follow some basic\nrules (in general, things to stderr are translatable; I think the\nplumbing outputs from unpack_trees() were really the exception).\n\nSo I'm open to the idea that the whole poison mechanism has just\noutlived its usefulness.\n\n-Peff\n"},{"id":"414799","messageId":"20210120152132.GC8396@szeder.dev","threadId":"51667","inReplyTo":"87y2h062jd.fsf@evledraar.gmail.com","subject":"Re: Should you use test_i18ngrep or GIT_TEST_GETTEXT_POISON=false?","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2021-01-20T15:21:32Z","receivedAt":"2021-01-20T15:25:02Z","isPatch":false,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Sun, Jan 10, 2021 at 07:59:50PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Sun, Jan 10 2021, SZEDER Gábor wrote:\n> \n> > On Tue, Jan 05, 2021 at 08:42:41PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> >> We need to provide GIT_TEST_GETTEXT_POISON=false here because the\n> >> \"invalid object type\" error is emitted by\n> >> parse_loose_header_extended(), which has that message already marked\n> >> for translation. Another option would be to use test_i18ngrep, but I\n> >> prefer always running the test, not skipping it under gettext poison\n> >> testing.\n> >\n> > This is fairly unconvincing.  Why do you prefer it that way?  What is\n> > so special in these tests of 'git mktags' that could possibly warrant\n> > this special treatment WRT gettext poisoning, as opposed to the other\n> > ~1500 invocations of test_i18n{grep,cmp}?\n> \n> You prodded me about this and Johannes also did off-list. So given that\n> this is already in \"next\" I think it's best to let usage in this mktag\n> series land as-is.\n> \n> I promise I'll follow-up with something to (at least start to) fix this\n> generally one way or the other on \"master\" after that. It's a trivial\n> part of this particular series.\n> \n> I.e. either document & migrate away from test_i18ngrep (mostly, in some\n> cases it's still convenient), or git rid of this and the few existing\n> GIT_TEST_GETTEXT_POISON=false uses.\n> \n> Anyway, as both the initial author of this poison facility & of its\n> current runtime incarnation in git.git, some context on it:\n> \n> The need for test_i18ngrep was always a wart. When I submitted the\n> initial i18n patches a big selling point at the time to a (rightly so)\n> skeptical audience on the Git ML was that it could almost entirely be\n> turned off at compile-time, and that we had something in place to make\n> sure we didn't introduce plumbing bugs by overtranslating.\n> \n> For a while the mass-i18n patches were about as noisy on-list as the\n> sha256 conversion patches were in more recenty memory.\n> \n> So if you look through the history you'll see that first we skipped\n> whole tests with the C_LOCALE_OUTPUT prereq, and then test_i18ngrep was\n> a more granular way to do that:\n> \n>     git some-command >output && \n>     test_i18ngrep \"c locale string\" output\n> \n> But since 6cdccfce1e0 (i18n: make GETTEXT_POISON a runtime option,\n> 2018-11-08) this hasn't been needed. But yes, we have a bit under 10\n> years of test_i18ngrep usage in the test suite now.\n> \n> But that stuff is the wart, and purely a historical artifact of this\n> having once been compile-time only. We don't do this sort of thing with\n> any other GIT_TEST_* option, e.g. we have tests like:\n> \n>     t/t5512-ls-remote.sh:   GIT_TEST_PROTOCOL_VERSION=0 git ls-remote --symref --heads . >actual &&\n>     t/t5512-ls-remote.sh-   test_cmp expect actual &&\n> \n> Not:\n> \n>     t/t5512-ls-remote.sh:   git ls-remote --symref --heads . >actual &&\n>     t/t5512-ls-remote.sh-   test_protocolv0cmp expect actual &&\n> \n> Because that doesn't make sense. If we know we're testing protocol v0\n> here why wouldn't we just pass that as a parameter in the test? Same for\n> GIT_TEST_GETTEXT_POISON=false.\n\nI think it's the comparison of GIT_TEST_GETTEXT_POISON and\nGIT_TEST_PROTOCOL_VERSION that doesn't make sense in the first place.\n\nFor the sake of users who might be stuck on Git version not yet\nunderstanding protocol v2 or have to connect to a v0-only remote, we\nmust make sure that both v0 and v2 protocols work, so a simple\n'make test' must exercise both.  Since protocol v2 is the default\nnowadays, we need a way to tell some tests to use v0 instead, and\nsetting GIT_TEST_PROTOCOL_VERSION=0 ended up our standard way to do\nthat.  Besides, the uses of GIT_TEST_PROTOCOL_VERSION is quite\ncontained, as it is set in only 34 times in 13 test scripts.\n\nGETTEXT_POISON is purely a developer aid (of questionable usefulness\nas you explained above), no end user would ever see it, and yet the\ntest_i18n* functions are used ~1500 times in 250+ test scripts.  I\nsure dont't want to see 1500+ uses of 'GIT_TEST_GETTEXT_POISON=false\ngit cmd...'.  Besides, what would be the purpose of\n'GIT_TEST_GETTEXT_POSION=true make test' if it would eventually be\noverriden all over the place?\n\n> I have also wondered if a better approach at this point isn't to just\n> remove the thing entirely. We're just doing incremental i18n-ization at\n> this point, so the odds that it's catching accidental plumbing\n> translations is rather low compared to 2011-era git.git.\n\nI wouldn't mind seeing it go...  well, I think I would welcome it,\nactually :)  I can only recall cases where a failure of the\nGETTEXT_POISON CI job was resolved by another replacement of 'grep'\nwith 'test_i18ngrep', but I don't seem to remember a case where we\ndecided that \"Nah, this message should not have been translated in the\nfirst place\".\n\nI would, however, really miss the error reporting of 'test_i18ngrep',\nthough, i.e. that it shows the contents of the file when it doesn't\nfind a match.\n\n"},{"id":"414800","messageId":"20210120153307.GD8396@szeder.dev","threadId":"51667","inReplyTo":"20210111144740.6092-1-avarab@gmail.com","subject":"Re: [PATCH 0/6] Remove GIT_TEST_GETTEXT_POISON facility","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2021-01-20T15:33:07Z","receivedAt":"2021-01-20T15:34:57Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Mon, Jan 11, 2021 at 03:47:34PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> As argued upthread and in 2/6 this test setup has outrun its\n> usefulness.\n> \n> This series begins to remove it in a way that's very careful about not\n> stepping on the toes of any other in-flight patches. In particular\n> \"master\" isn't anywhere in the diff context here to avoid conflicts\n> with the series to do that mass-replacement in t/. \n> \n> This series merges and tests cleanly in combination with the current\n> \"seen\" branch.\n> \n> At the end we're left with a bunch of tests still using\n> test_i18n{cmp,grep}, but I think that's preferable to having in-flight\n> conflicts. It'll be trivial to search-replace those at some point in\n> the future where we don't have another search-replacement series\n> amending t/ in-flight.\n> \n> Ævar Arnfjörð Bjarmason (6):\n>   ci: remove GETTEXT_POISON jobs\n>   tests: remove support for GIT_TEST_GETTEXT_POISON\n>   tests: remove misc use of test_i18n{cmp,grep}\n>   tests: (almost) remove use of \"test_i18ngrep !\"\n>   tests: (almost) remove C_LOCALE_OUTPUT prerequisites\n>   tests: remove uses of GIT_TEST_GETTEXT_POISON=false\n\nAt the end of this patch series there are still a couple of remnants\nleft:\n\n  - t3406: has a comment about gettext poisoning.\n\n  - Makefile still has a warning about for the GETTEXT_POISON build\n    knob referring to GIT_TEST_GETTEXT_POISON and t/README.\n\nI think both can be simply removed.\n"},{"id":"414830","messageId":"87a6t38p71.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"20210112085051.GV8396@szeder.dev","subject":"Re: [PATCH 1/6] ci: remove GETTEXT_POISON jobs","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-20T17:59:14Z","receivedAt":"2021-01-20T18:01:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Jan 12 2021, SZEDER Gábor wrote:\n\n> On Mon, Jan 11, 2021 at 03:47:35PM +0100, Ævar Arnfjörð Bjarmason wrote:\n>> A subsequent commit will remove GETTEXT_POISON entirely, let's start\n>> by removing the CI jobs that enable the option.\n>> \n>> We cannot just remove the job because the CI is implicitly depending\n>> on the \"poison\" job being a sort of \"default\" job.\n>\n> I don't understand what you mean here with a \"default job\" that the CI\n> is implicitly depending on.  There is certainly no such default job on\n> Travis CI, and I don't think there is one on the GitHub thing.\n\nI'll reword this. I meant that the poison job was using the default\ncompiler etc., whereas the other ones were setting custom values.\n\nI vaguely remember some list traffic about this in the past, i.e. the\nreliance on the poison job not just for that, but also as \"the default\nOS image\" setup.\n\n"},{"id":"414832","messageId":"877do78ojc.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"20210120153307.GD8396@szeder.dev","subject":"Re: [PATCH 0/6] Remove GIT_TEST_GETTEXT_POISON facility","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-20T18:13:27Z","receivedAt":"2021-01-20T18:15:32Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Jan 20 2021, SZEDER Gábor wrote:\n\n> On Mon, Jan 11, 2021 at 03:47:34PM +0100, Ævar Arnfjörð Bjarmason wrote:\n>> As argued upthread and in 2/6 this test setup has outrun its\n>> usefulness.\n>> \n>> This series begins to remove it in a way that's very careful about not\n>> stepping on the toes of any other in-flight patches. In particular\n>> \"master\" isn't anywhere in the diff context here to avoid conflicts\n>> with the series to do that mass-replacement in t/. \n>> \n>> This series merges and tests cleanly in combination with the current\n>> \"seen\" branch.\n>> \n>> At the end we're left with a bunch of tests still using\n>> test_i18n{cmp,grep}, but I think that's preferable to having in-flight\n>> conflicts. It'll be trivial to search-replace those at some point in\n>> the future where we don't have another search-replacement series\n>> amending t/ in-flight.\n>> \n>> Ævar Arnfjörð Bjarmason (6):\n>>   ci: remove GETTEXT_POISON jobs\n>>   tests: remove support for GIT_TEST_GETTEXT_POISON\n>>   tests: remove misc use of test_i18n{cmp,grep}\n>>   tests: (almost) remove use of \"test_i18ngrep !\"\n>>   tests: (almost) remove C_LOCALE_OUTPUT prerequisites\n>>   tests: remove uses of GIT_TEST_GETTEXT_POISON=false\n>\n> At the end of this patch series there are still a couple of remnants\n> left:\n>\n>   - t3406: has a comment about gettext poisoning.\n>\n>   - Makefile still has a warning about for the GETTEXT_POISON build\n>     knob referring to GIT_TEST_GETTEXT_POISON and t/README.\n>\n> I think both can be simply removed.\n\nThe Makefile comment was in the 20210111131451.7084-1-avarab@gmail.com\nsibling series, but I'll fix the other one in this one. Thanks!\n"},{"id":"414834","messageId":"20210120182759.31102-4-avarab@gmail.com","threadId":"51667","inReplyTo":"20210111144740.6092-1-avarab@gmail.com","subject":"[PATCH v2 3/3] tests: remove uses of GIT_TEST_GETTEXT_POISON=false","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-20T18:27:59Z","receivedAt":"2021-01-20T18:40:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As noted in previous commits we are removing the use of\nGIT_TEST_GETTEXT_POISON=false. These tests all relied on the facility\nbeing off, it always is off after an earlier change, but we hadn't\nremoved the redundant assignments to \"false\" in the tests.\n\nI'm preserving the deletion of \"error\" lines in 38b9197a76a (t5411:\nadd basic test cases for proc-receive hook, 2020-08-27), it turns out\nthat's useful even without GIT_TEST_GETTEXT_POISON=true in\nplay. Update a comment added in that commit to note that.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t1305-config-include.sh   | 4 +---\n t/t5411/common-functions.sh | 5 ++---\n t/t7201-co.sh               | 2 +-\n t/t9902-completion.sh       | 1 -\n 4 files changed, 4 insertions(+), 8 deletions(-)\n\ndiff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh\nindex f1e1b289f9..f08cfcdfae 100755\n--- a/t/t1305-config-include.sh\n+++ b/t/t1305-config-include.sh\n@@ -352,9 +352,7 @@ test_expect_success 'include cycles are detected' '\n \tgit init --bare cycle &&\n \tgit -C cycle config include.path cycle &&\n \tgit config -f cycle/cycle include.path config &&\n-\ttest_must_fail \\\n-\t\tenv GIT_TEST_GETTEXT_POISON=false \\\n-\t\tgit -C cycle config --get-all test.value 2>stderr &&\n+\ttest_must_fail git -C cycle config --get-all test.value 2>stderr &&\n \tgrep \"exceeded maximum include depth\" stderr\n '\n \ndiff --git a/t/t5411/common-functions.sh b/t/t5411/common-functions.sh\nindex 344d13f61a..13107fcdb6 100644\n--- a/t/t5411/common-functions.sh\n+++ b/t/t5411/common-functions.sh\n@@ -36,9 +36,8 @@ create_commits_in () {\n # without having to worry about future changes of the commit ID and spaces\n # of the output.  Single quotes are replaced with double quotes, because\n # it is boring to prepare unquoted single quotes in expect text.  We also\n-# remove some locale error messages, which break test if we turn on\n-# `GIT_TEST_GETTEXT_POISON=true` in order to test unintentional translations\n-# on plumbing commands.\n+# remove some locale error messages. The emitted human-readable errors are\n+# redundant to the more machine-readable output the tests already assert.\n make_user_friendly_and_stable_output () {\n \tsed \\\n \t\t-e \"s/  *\\$//\" \\\ndiff --git a/t/t7201-co.sh b/t/t7201-co.sh\nindex b36a93056f..d10076efd7 100755\n--- a/t/t7201-co.sh\n+++ b/t/t7201-co.sh\n@@ -245,7 +245,7 @@ test_expect_success 'checkout to detach HEAD' '\n \trev=$(git rev-parse --short renamer^) &&\n \tgit checkout -f renamer &&\n \tgit clean -f &&\n-\tGIT_TEST_GETTEXT_POISON=false git checkout renamer^ 2>messages &&\n+\tgit checkout renamer^ 2>messages &&\n \tgrep \"HEAD is now at $rev\" messages &&\n \ttest_line_count -gt 1 messages &&\n \tH=$(git rev-parse --verify HEAD) &&\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex c4a7758409..ea669cfaf5 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -2363,7 +2363,6 @@ test_expect_success 'sourcing the completion script clears cached commands' '\n '\n \n test_expect_success 'sourcing the completion script clears cached merge strategies' '\n-\tGIT_TEST_GETTEXT_POISON=false &&\n \t__git_compute_merge_strategies &&\n \tverbose test -n \"$__git_merge_strategies\" &&\n \t. \"$GIT_BUILD_DIR/contrib/completion/git-completion.bash\" &&\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"414835","messageId":"20210120182759.31102-3-avarab@gmail.com","threadId":"51667","inReplyTo":"20210111144740.6092-1-avarab@gmail.com","subject":"[PATCH v2 2/3] tests: remove support for GIT_TEST_GETTEXT_POISON","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-20T18:27:58Z","receivedAt":"2021-01-20T18:40:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This removes the ability to inject \"poison\" gettext() messages via the\nGIT_TEST_GETTEXT_POISON special test setup.\n\nI initially added this as a compile-time option in bb946bba761 (i18n:\nadd GETTEXT_POISON to simulate unfriendly translator, 2011-02-22), and\nmost recently modified to be toggleable at runtime in\n6cdccfce1e0 (i18n: make GETTEXT_POISON a runtime option, 2018-11-08)..\n\nThe reason for its removal is that the trade-off of maintaining it\nv.s. what it's getting us has long since flipped. When gettext was\nintegrated in 5e9637c6297 (i18n: add infrastructure for translating\nGit with gettext, 2011-11-18) there was understandable concern on the\nGit ML that in marking messages for translation en-masse we'd\ninadvertently mark plumbing messages. The GETTEXT_POISON facility was\na way to smoke those out via our test suite.\n\nNowadays however we're done (or almost entirely done) with any marking\nof messages for translation. New messages are usually marked by their\nauthors, who'll know whether it makes sense to translate them or\nnot. If not any errors in marking the messages are much more likely to\nbe spotted in review than in the the initial deluge of i18n patches in\nthe 2011-2012 era.\n\nSo let's just remove this. This leaves the test suite in a state where\nwe still have a lot of test_i18n, C_LOCALE_OUTPUT\netc. uses. Subsequent commits will remove those too.\n\nThe change to t/lib-rebase.sh is a selective revert of the relevant\npart of f2d17068fd (i18n: rebase-interactive: mark comments of squash\nfor translation, 2016-06-17), and the comment in\nt/t3406-rebase-message.sh is from c7108bf9ed (i18n: rebase: mark\nmessages for translation, 2012-07-25).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/MyFirstContribution.txt |  2 +-\n config.c                              |  9 -------\n gettext.c                             | 10 -------\n gettext.h                             |  7 +----\n git-sh-i18n.sh                        | 22 +--------------\n po/README                             | 22 ++-------------\n t/README                              |  6 -----\n t/lib-gettext.sh                      |  2 +-\n t/lib-rebase.sh                       |  1 -\n t/t0017-env-helper.sh                 |  8 +++---\n t/t0205-gettext-poison.sh             | 39 ---------------------------\n t/t3406-rebase-message.sh             |  7 -----\n t/test-lib-functions.sh               | 23 +++++-----------\n t/test-lib.sh                         | 23 +++-------------\n 14 files changed, 21 insertions(+), 160 deletions(-)\n delete mode 100755 t/t0205-gettext-poison.sh\n\ndiff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt\nindex 7c9a037cc2..af0a9da62e 100644\n--- a/Documentation/MyFirstContribution.txt\n+++ b/Documentation/MyFirstContribution.txt\n@@ -664,7 +664,7 @@ mention the right animal somewhere:\n ----\n test_expect_success 'runs correctly with no args and good output' '\n \tgit psuh >actual &&\n-\ttest_i18ngrep Pony actual\n+\tgrep Pony actual\n '\n ----\n \ndiff --git a/config.c b/config.c\nindex 4c0cf3a1c1..3d34c0fd76 100644\n--- a/config.c\n+++ b/config.c\n@@ -996,15 +996,6 @@ static void die_bad_number(const char *name, const char *value)\n \tif (!value)\n \t\tvalue = \"\";\n \n-\tif (!strcmp(name, \"GIT_TEST_GETTEXT_POISON\"))\n-\t\t/*\n-\t\t * We explicitly *don't* use _() here since it would\n-\t\t * cause an infinite loop with _() needing to call\n-\t\t * use_gettext_poison(). This is why marked up\n-\t\t * translations with N_() above.\n-\t\t */\n-\t\tdie(bad_numeric, value, name, error_type);\n-\n \tif (!(cf && cf->name))\n \t\tdie(_(bad_numeric), value, name, _(error_type));\n \ndiff --git a/gettext.c b/gettext.c\nindex 1b564216d0..af2413b47e 100644\n--- a/gettext.c\n+++ b/gettext.c\n@@ -65,14 +65,6 @@ const char *get_preferred_languages(void)\n \treturn NULL;\n }\n \n-int use_gettext_poison(void)\n-{\n-\tstatic int poison_requested = -1;\n-\tif (poison_requested == -1)\n-\t\tpoison_requested = git_env_bool(\"GIT_TEST_GETTEXT_POISON\", 0);\n-\treturn poison_requested;\n-}\n-\n #ifndef NO_GETTEXT\n static int test_vsnprintf(const char *fmt, ...)\n {\n@@ -117,8 +109,6 @@ void git_setup_gettext(void)\n \tif (!podir)\n \t\tpodir = p = system_path(GIT_LOCALE_PATH);\n \n-\tuse_gettext_poison(); /* getenv() reentrancy paranoia */\n-\n \tif (!is_directory(podir)) {\n \t\tfree(p);\n \t\treturn;\ndiff --git a/gettext.h b/gettext.h\nindex bee52eb113..c8b34fd612 100644\n--- a/gettext.h\n+++ b/gettext.h\n@@ -28,15 +28,12 @@\n \n #define FORMAT_PRESERVING(n) __attribute__((format_arg(n)))\n \n-int use_gettext_poison(void);\n-\n #ifndef NO_GETTEXT\n void git_setup_gettext(void);\n int gettext_width(const char *s);\n #else\n static inline void git_setup_gettext(void)\n {\n-\tuse_gettext_poison(); /* getenv() reentrancy paranoia */\n }\n static inline int gettext_width(const char *s)\n {\n@@ -48,14 +45,12 @@ static inline FORMAT_PRESERVING(1) const char *_(const char *msgid)\n {\n \tif (!*msgid)\n \t\treturn \"\";\n-\treturn use_gettext_poison() ? \"# GETTEXT POISON #\" : gettext(msgid);\n+\treturn gettext(msgid);\n }\n \n static inline FORMAT_PRESERVING(1) FORMAT_PRESERVING(2)\n const char *Q_(const char *msgid, const char *plu, unsigned long n)\n {\n-\tif (use_gettext_poison())\n-\t\treturn \"# GETTEXT POISON #\";\n \treturn ngettext(msgid, plu, n);\n }\n \ndiff --git a/git-sh-i18n.sh b/git-sh-i18n.sh\nindex 8eef60b43f..e3d9f4836d 100644\n--- a/git-sh-i18n.sh\n+++ b/git-sh-i18n.sh\n@@ -17,12 +17,7 @@ export TEXTDOMAINDIR\n \n # First decide what scheme to use...\n GIT_INTERNAL_GETTEXT_SH_SCHEME=fallthrough\n-if test -n \"$GIT_TEST_GETTEXT_POISON\" &&\n-\t    git env--helper --type=bool --default=0 --exit-code \\\n-\t\tGIT_TEST_GETTEXT_POISON\n-then\n-\tGIT_INTERNAL_GETTEXT_SH_SCHEME=poison\n-elif test -n \"@@USE_GETTEXT_SCHEME@@\"\n+if test -n \"@@USE_GETTEXT_SCHEME@@\"\n then\n \tGIT_INTERNAL_GETTEXT_SH_SCHEME=\"@@USE_GETTEXT_SCHEME@@\"\n elif test -n \"$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS\"\n@@ -63,21 +58,6 @@ gettext_without_eval_gettext)\n \t\t)\n \t}\n \t;;\n-poison)\n-\t# Emit garbage so that tests that incorrectly rely on translatable\n-\t# strings will fail.\n-\tgettext () {\n-\t\tprintf \"%s\" \"# GETTEXT POISON #\"\n-\t}\n-\n-\teval_gettext () {\n-\t\tprintf \"%s\" \"# GETTEXT POISON #\"\n-\t}\n-\n-\teval_ngettext () {\n-\t\tprintf \"%s\" \"# GETTEXT POISON #\"\n-\t}\n-\t;;\n *)\n \tgettext () {\n \t\tprintf \"%s\" \"$1\"\ndiff --git a/po/README b/po/README\nindex 07595d369b..efd5baaf1d 100644\n--- a/po/README\n+++ b/po/README\n@@ -284,23 +284,5 @@ Perl:\n Testing marked strings\n ----------------------\n \n-Even if you've correctly marked porcelain strings for translation\n-something in the test suite might still depend on the US English\n-version of the strings, e.g. to grep some error message or other\n-output.\n-\n-To smoke out issues like these, Git tested with a translation mode that\n-emits gibberish on every call to gettext. To use it run the test suite\n-with it, e.g.:\n-\n-    cd t && GIT_TEST_GETTEXT_POISON=true prove -j 9 ./t[0-9]*.sh\n-\n-If tests break with it you should inspect them manually and see if\n-what you're translating is sane, i.e. that you're not translating\n-plumbing output.\n-\n-If not you should replace calls to grep with test_i18ngrep, or\n-test_cmp calls with test_i18ncmp. If that's not enough you can skip\n-the whole test by making it depend on the C_LOCALE_OUTPUT\n-prerequisite. See existing test files with this prerequisite for\n-examples.\n+Git's tests are run under LANG=C LC_ALL=C. So the tests do not need be\n+changed to account for translations as they're added.\ndiff --git a/t/README b/t/README\nindex c730a70770..9a9dded033 100644\n--- a/t/README\n+++ b/t/README\n@@ -358,12 +358,6 @@ whether this mode is active, and e.g. skip some tests that are hard to\n refactor to deal with it. The \"SYMLINKS\" prerequisite is currently\n excluded as so much relies on it, but this might change in the future.\n \n-GIT_TEST_GETTEXT_POISON=<boolean> turns all strings marked for\n-translation into gibberish if true. Used for spotting those tests that\n-need to be marked with a C_LOCALE_OUTPUT prerequisite when adding more\n-strings for translation. See \"Testing marked strings\" in po/README for\n-details.\n-\n GIT_TEST_SPLIT_INDEX=<boolean> forces split-index mode on the whole\n test suite. Accept any boolean values that are accepted by git-config.\n \ndiff --git a/t/lib-gettext.sh b/t/lib-gettext.sh\nindex 2139b427ca..cc6bb2cdea 100644\n--- a/t/lib-gettext.sh\n+++ b/t/lib-gettext.sh\n@@ -17,7 +17,7 @@ else\n \t. \"$GIT_BUILD_DIR\"/git-sh-i18n\n fi\n \n-if test_have_prereq GETTEXT && test_have_prereq C_LOCALE_OUTPUT\n+if test_have_prereq GETTEXT\n then\n \t# is_IS.UTF-8 on Solaris and FreeBSD, is_IS.utf8 on Debian\n \tis_IS_locale=$(locale -a 2>/dev/null |\ndiff --git a/t/lib-rebase.sh b/t/lib-rebase.sh\nindex b72c051f47..172d7459ff 100644\n--- a/t/lib-rebase.sh\n+++ b/t/lib-rebase.sh\n@@ -29,7 +29,6 @@ set_fake_editor () {\n \t*/COMMIT_EDITMSG)\n \t\ttest -z \"$EXPECT_HEADER_COUNT\" ||\n \t\t\ttest \"$EXPECT_HEADER_COUNT\" = \"$(sed -n '1s/^# This is a combination of \\(.*\\) commits\\./\\1/p' < \"$1\")\" ||\n-\t\t\ttest \"# # GETTEXT POISON #\" = \"$(sed -n '1p' < \"$1\")\" ||\n \t\t\texit\n \t\ttest -z \"$FAKE_COMMIT_MESSAGE\" || echo \"$FAKE_COMMIT_MESSAGE\" > \"$1\"\n \t\ttest -z \"$FAKE_COMMIT_AMEND\" || echo \"$FAKE_COMMIT_AMEND\" >> \"$1\"\ndiff --git a/t/t0017-env-helper.sh b/t/t0017-env-helper.sh\nindex c1ecf6aeac..4a159f99e4 100755\n--- a/t/t0017-env-helper.sh\n+++ b/t/t0017-env-helper.sh\n@@ -86,14 +86,14 @@ test_expect_success 'env--helper reads config thanks to trace2' '\n \tgit config -f home/cycle include.path .gitconfig &&\n \n \ttest_must_fail \\\n-\t\tenv HOME=\"$(pwd)/home\" GIT_TEST_GETTEXT_POISON=false \\\n+\t\tenv HOME=\"$(pwd)/home\" \\\n \t\tgit config -l 2>err &&\n \tgrep \"exceeded maximum include depth\" err &&\n \n \ttest_must_fail \\\n-\t\tenv HOME=\"$(pwd)/home\" GIT_TEST_GETTEXT_POISON=true \\\n-\t\tgit -C cycle env--helper --type=bool --default=0 --exit-code GIT_TEST_GETTEXT_POISON 2>err &&\n-\tgrep \"# GETTEXT POISON #\" err\n+\t\tenv HOME=\"$(pwd)/home\" GIT_TEST_ENV_HELPER=true \\\n+\t\tgit -C cycle env--helper --type=bool --default=0 --exit-code GIT_TEST_ENV_HELPER 2>err &&\n+\tgrep \"exceeded maximum include depth\" err\n '\n \n test_done\ndiff --git a/t/t0205-gettext-poison.sh b/t/t0205-gettext-poison.sh\ndeleted file mode 100755\nindex f9fa16ad83..0000000000\n--- a/t/t0205-gettext-poison.sh\n+++ /dev/null\n@@ -1,39 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (c) 2010 Ævar Arnfjörð Bjarmason\n-#\n-\n-test_description='Gettext Shell poison'\n-\n-GIT_TEST_GETTEXT_POISON=true\n-export GIT_TEST_GETTEXT_POISON\n-. ./lib-gettext.sh\n-\n-test_expect_success 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME\" is poison' '\n-    test \"$GIT_INTERNAL_GETTEXT_SH_SCHEME\" = \"poison\"\n-'\n-\n-test_expect_success 'gettext: our gettext() fallback has poison semantics' '\n-    printf \"# GETTEXT POISON #\" >expect &&\n-    gettext \"test\" >actual &&\n-    test_cmp expect actual &&\n-    printf \"# GETTEXT POISON #\" >expect &&\n-    gettext \"test more words\" >actual &&\n-    test_cmp expect actual\n-'\n-\n-test_expect_success 'eval_gettext: our eval_gettext() fallback has poison semantics' '\n-    printf \"# GETTEXT POISON #\" >expect &&\n-    eval_gettext \"test\" >actual &&\n-    test_cmp expect actual &&\n-    printf \"# GETTEXT POISON #\" >expect &&\n-    eval_gettext \"test more words\" >actual &&\n-    test_cmp expect actual\n-'\n-\n-test_expect_success \"gettext: invalid GIT_TEST_GETTEXT_POISON value doesn't infinitely loop\" \"\n-\ttest_must_fail env GIT_TEST_GETTEXT_POISON=xyz git version 2>error &&\n-\tgrep \\\"fatal: bad numeric config value 'xyz' for 'GIT_TEST_GETTEXT_POISON': invalid unit\\\" error\n-\"\n-\n-test_done\ndiff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh\nindex 4afc528165..6340e9bc72 100755\n--- a/t/t3406-rebase-message.sh\n+++ b/t/t3406-rebase-message.sh\n@@ -64,13 +64,6 @@ test_expect_success 'rebase -n overrides config rebase.stat config' '\n \t! grep \"^ fileX |  *1 +$\" diffstat.txt\n '\n \n-# Output to stderr:\n-#\n-#     \"Does not point to a valid commit: invalid-ref\"\n-#\n-# NEEDSWORK: This \"grep\" is fine in real non-C locales, but\n-# GIT_TEST_GETTEXT_POISON poisons the refname along with the enclosing\n-# error message.\n test_expect_success 'rebase --onto outputs the invalid ref' '\n \ttest_must_fail git rebase --onto invalid-ref HEAD HEAD 2>err &&\n \ttest_i18ngrep \"invalid-ref\" err\ndiff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\nindex 2f08ce7cba..5befa8bd03 100644\n--- a/t/test-lib-functions.sh\n+++ b/t/test-lib-functions.sh\n@@ -993,19 +993,16 @@ test_cmp_bin () {\n \tcmp \"$@\"\n }\n \n-# Use this instead of test_cmp to compare files that contain expected and\n-# actual output from git commands that can be translated.  When running\n-# under GIT_TEST_GETTEXT_POISON this pretends that the command produced expected\n-# results.\n+# Wrapper for test_cmp which used to be used for\n+# GIT_TEST_GETTEXT_POISON=false. Only here as a shim for other\n+# in-flight changes. Should not be used and will be removed soon.\n test_i18ncmp () {\n-\t! test_have_prereq C_LOCALE_OUTPUT || test_cmp \"$@\"\n+\ttest_cmp \"$@\"\n }\n \n-# Use this instead of \"grep expected-string actual\" to see if the\n-# output from a git command that can be translated either contains an\n-# expected string, or does not contain an unwanted one.  When running\n-# under GIT_TEST_GETTEXT_POISON this pretends that the command produced expected\n-# results.\n+# Wrapper for grep which used to be used for\n+# GIT_TEST_GETTEXT_POISON=false. Only here as a shim for other\n+# in-flight changes. Should not be used and will be removed soon.\n test_i18ngrep () {\n \teval \"last_arg=\\${$#}\"\n \n@@ -1018,12 +1015,6 @@ test_i18ngrep () {\n \t\tBUG \"too few parameters to test_i18ngrep\"\n \tfi\n \n-\tif test_have_prereq !C_LOCALE_OUTPUT\n-\tthen\n-\t\t# pretend success\n-\t\treturn 0\n-\tfi\n-\n \tif test \"x!\" = \"x$1\"\n \tthen\n \t\tshift\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 03c1c0836f..a5d8c207dd 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -404,15 +404,6 @@ TZ=UTC\n export LANG LC_ALL PAGER TZ\n EDITOR=:\n \n-# GIT_TEST_GETTEXT_POISON should not influence git commands executed\n-# during initialization of test-lib and the test repo. Back it up,\n-# unset and then restore after initialization is finished.\n-if test -n \"$GIT_TEST_GETTEXT_POISON\"\n-then\n-\tGIT_TEST_GETTEXT_POISON_ORIG=$GIT_TEST_GETTEXT_POISON\n-\tunset GIT_TEST_GETTEXT_POISON\n-fi\n-\n # A call to \"unset\" with no arguments causes at least Solaris 10\n # /usr/xpg4/bin/sh and /bin/ksh to bail out.  So keep the unsets\n # deriving from the command substitution clustered with the other\n@@ -1529,16 +1520,10 @@ test -n \"$USE_LIBPCRE1\" && test_set_prereq LIBPCRE1\n test -n \"$USE_LIBPCRE2\" && test_set_prereq LIBPCRE2\n test -z \"$NO_GETTEXT\" && test_set_prereq GETTEXT\n \n-if test -n \"$GIT_TEST_GETTEXT_POISON_ORIG\"\n-then\n-\tGIT_TEST_GETTEXT_POISON=$GIT_TEST_GETTEXT_POISON_ORIG\n-\texport GIT_TEST_GETTEXT_POISON\n-\tunset GIT_TEST_GETTEXT_POISON_ORIG\n-fi\n-\n-test_lazy_prereq C_LOCALE_OUTPUT '\n-\t! test_bool_env GIT_TEST_GETTEXT_POISON false\n-'\n+# Used to be used for GIT_TEST_GETTEXT_POISON=false. Only here as a\n+# shim for other in-flight changes. Should not be used and will be\n+# removed soon.\n+test_set_prereq C_LOCALE_OUTPUT\n \n if test -z \"$GIT_TEST_CHECK_CACHE_TREE\"\n then\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"414836","messageId":"20210120182759.31102-1-avarab@gmail.com","threadId":"51667","inReplyTo":"20210111144740.6092-1-avarab@gmail.com","subject":"[PATCH v2 0/3] Remove GIT_TEST_GETTEXT_POISON facility","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-20T18:27:56Z","receivedAt":"2021-01-20T18:41:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"A slimmed-down and much easier to read version of v1 which addresses\nthe feedback on v1.\n\nI'm not removing any test_i18ngrep etc. uses here in this series,\nbecause SZEDER indicated in <20210120152132.GC8396@szeder.dev> that he\nlikes the test_i18ngrep error reporting. This leaves the door open to\nhim submitting some related series to add a\ns/test_i18ngrep/test_grep/g helper or whatever.\n\nAs before I'll leave whether we *should* do this to list consensus. My\nreading of it is that Junio/Jeff/SZEDER are leaning more towards this\napproach, and Johannes more towards the rot13 alternate.\n\nI certainly don't want to stomp on Johannes's toes with implementing\nthe rot13 alternative, and if this series were accepted that facilty\nwould need to start by mostly reverting this, so if the consensus is\nleaning towards that approach this series should be dropped. Johannes\ndoesn't have time to work on that right now, but there's no rush here\neither way.\n\nBut if it's leaning more towards \"meh, let's just nuke it\" we can\napply this.\n\nÆvar Arnfjörð Bjarmason (3):\n  ci: remove GETTEXT_POISON jobs\n  tests: remove support for GIT_TEST_GETTEXT_POISON\n  tests: remove uses of GIT_TEST_GETTEXT_POISON=false\n\n .github/workflows/main.yml            |  2 +-\n .travis.yml                           |  2 +-\n Documentation/MyFirstContribution.txt |  2 +-\n ci/install-dependencies.sh            |  2 +-\n ci/lib.sh                             |  3 +--\n config.c                              |  9 -------\n gettext.c                             | 10 -------\n gettext.h                             |  7 +----\n git-sh-i18n.sh                        | 22 +--------------\n po/README                             | 22 ++-------------\n t/README                              |  6 -----\n t/lib-gettext.sh                      |  2 +-\n t/lib-rebase.sh                       |  1 -\n t/t0017-env-helper.sh                 |  8 +++---\n t/t0205-gettext-poison.sh             | 39 ---------------------------\n t/t1305-config-include.sh             |  4 +--\n t/t3406-rebase-message.sh             |  7 -----\n t/t5411/common-functions.sh           |  5 ++--\n t/t7201-co.sh                         |  2 +-\n t/t9902-completion.sh                 |  1 -\n t/test-lib-functions.sh               | 23 +++++-----------\n t/test-lib.sh                         | 23 +++-------------\n 22 files changed, 29 insertions(+), 173 deletions(-)\n delete mode 100755 t/t0205-gettext-poison.sh\n\nRange-diff:\n1:  00c5326694 ! 1:  164296c7d6 ci: remove GETTEXT_POISON jobs\n    @@ Commit message\n         by removing the CI jobs that enable the option.\n     \n         We cannot just remove the job because the CI is implicitly depending\n    -    on the \"poison\" job being a sort of \"default\" job. Let's instead add a\n    -    \"default\" job.\n    +    on the \"poison\" job being a sort of \"default\" job in the sense that\n    +    it's the job that was otherwise run with the default compiler, no\n    +    other GIT_TEST_* options etc. So let's keep it under the name\n    +    \"linux-gcc-default\".\n     \n         This means we can remove the initial \"make test\" from the \"linux-gcc\"\n         job (it does another one after setting a bunch of GIT_TEST_*\n2:  ac0f3a78d7 ! 2:  5c6299cde8 tests: remove support for GIT_TEST_GETTEXT_POISON\n    @@ Commit message\n         we still have a lot of test_i18n, C_LOCALE_OUTPUT\n         etc. uses. Subsequent commits will remove those too.\n     \n    +    The change to t/lib-rebase.sh is a selective revert of the relevant\n    +    part of f2d17068fd (i18n: rebase-interactive: mark comments of squash\n    +    for translation, 2016-06-17), and the comment in\n    +    t/t3406-rebase-message.sh is from c7108bf9ed (i18n: rebase: mark\n    +    messages for translation, 2012-07-25).\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Documentation/MyFirstContribution.txt ##\n    @@ po/README: Perl:\n     -something in the test suite might still depend on the US English\n     -version of the strings, e.g. to grep some error message or other\n     -output.\n    -+Git's tests are run under LANG=C LC_ALL=C. So the tests do not need be\n    -+changed to account for translations as they're added.\n    - \n    +-\n     -To smoke out issues like these, Git tested with a translation mode that\n     -emits gibberish on every call to gettext. To use it run the test suite\n     -with it, e.g.:\n    -+Testing whether marked strings are used by plumbing\n    -+---------------------------------------------------\n    - \n    +-\n     -    cd t && GIT_TEST_GETTEXT_POISON=true prove -j 9 ./t[0-9]*.sh\n    -+It may be useful to use the test suite to smoke out any strings that\n    -+shouldn't be translated, because they are used by plumbing or\n    -+otherwise part of machine-readable output.\n    - \n    +-\n     -If tests break with it you should inspect them manually and see if\n     -what you're translating is sane, i.e. that you're not translating\n    -+This is most useful in cases where e.g. library function might return\n    -+a string like _(\"tree\"), and the developer marking strings for\n    -+translation wishes to check that they're not inadvertently translating\n    - plumbing output.\n    - \n    +-plumbing output.\n    +-\n     -If not you should replace calls to grep with test_i18ngrep, or\n     -test_cmp calls with test_i18ncmp. If that's not enough you can skip\n     -the whole test by making it depend on the C_LOCALE_OUTPUT\n     -prerequisite. See existing test files with this prerequisite for\n     -examples.\n    -+There used to be a GIT_TEST_GETTEXT_POISON=true test mode to replace\n    -+translated strings with gibberish to facilitate this. Maintaining it\n    -+and annotating tests appropriately was considered too high of a burden\n    -+for its usefulness.\n    -+\n    -+It's still possible to do a one-off test of where a string might be\n    -+used in the tests by simply replacing the \"some hardcoded string\" with\n    -+something like \"POISON ME\" locally and running the tests, before\n    -+finally converting it to _(\"some hardcoded string\").\n    ++Git's tests are run under LANG=C LC_ALL=C. So the tests do not need be\n    ++changed to account for translations as they're added.\n     \n      ## t/README ##\n     @@ t/README: whether this mode is active, and e.g. skip some tests that are hard to\n    @@ t/lib-gettext.sh: else\n      \t# is_IS.UTF-8 on Solaris and FreeBSD, is_IS.utf8 on Debian\n      \tis_IS_locale=$(locale -a 2>/dev/null |\n     \n    + ## t/lib-rebase.sh ##\n    +@@ t/lib-rebase.sh: set_fake_editor () {\n    + \t*/COMMIT_EDITMSG)\n    + \t\ttest -z \"$EXPECT_HEADER_COUNT\" ||\n    + \t\t\ttest \"$EXPECT_HEADER_COUNT\" = \"$(sed -n '1s/^# This is a combination of \\(.*\\) commits\\./\\1/p' < \"$1\")\" ||\n    +-\t\t\ttest \"# # GETTEXT POISON #\" = \"$(sed -n '1p' < \"$1\")\" ||\n    + \t\t\texit\n    + \t\ttest -z \"$FAKE_COMMIT_MESSAGE\" || echo \"$FAKE_COMMIT_MESSAGE\" > \"$1\"\n    + \t\ttest -z \"$FAKE_COMMIT_AMEND\" || echo \"$FAKE_COMMIT_AMEND\" >> \"$1\"\n    +\n      ## t/t0017-env-helper.sh ##\n     @@ t/t0017-env-helper.sh: test_expect_success 'env--helper reads config thanks to trace2' '\n      \tgit config -f home/cycle include.path .gitconfig &&\n    @@ t/t0205-gettext-poison.sh (deleted)\n     -\n     -test_done\n     \n    + ## t/t3406-rebase-message.sh ##\n    +@@ t/t3406-rebase-message.sh: test_expect_success 'rebase -n overrides config rebase.stat config' '\n    + \t! grep \"^ fileX |  *1 +$\" diffstat.txt\n    + '\n    + \n    +-# Output to stderr:\n    +-#\n    +-#     \"Does not point to a valid commit: invalid-ref\"\n    +-#\n    +-# NEEDSWORK: This \"grep\" is fine in real non-C locales, but\n    +-# GIT_TEST_GETTEXT_POISON poisons the refname along with the enclosing\n    +-# error message.\n    + test_expect_success 'rebase --onto outputs the invalid ref' '\n    + \ttest_must_fail git rebase --onto invalid-ref HEAD HEAD 2>err &&\n    + \ttest_i18ngrep \"invalid-ref\" err\n    +\n      ## t/test-lib-functions.sh ##\n     @@ t/test-lib-functions.sh: test_cmp_bin () {\n      \tcmp \"$@\"\n3:  8bdf8d9513 < -:  ---------- tests: remove misc use of test_i18n{cmp,grep}\n4:  ead017acc8 < -:  ---------- tests: (almost) remove use of \"test_i18ngrep !\"\n5:  b1113122d0 < -:  ---------- tests: (almost) remove C_LOCALE_OUTPUT prerequisites\n6:  9226c3a2df = 3:  7f982be41d tests: remove uses of GIT_TEST_GETTEXT_POISON=false\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"414840","messageId":"20210120191406.GE8396@szeder.dev","threadId":"51667","inReplyTo":"87a6t38p71.fsf@evledraar.gmail.com","subject":"Re: [PATCH 1/6] ci: remove GETTEXT_POISON jobs","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2021-01-20T19:14:06Z","receivedAt":"2021-01-20T19:15:25Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Wed, Jan 20, 2021 at 06:59:14PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Tue, Jan 12 2021, SZEDER Gábor wrote:\n> \n> > On Mon, Jan 11, 2021 at 03:47:35PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> >> A subsequent commit will remove GETTEXT_POISON entirely, let's start\n> >> by removing the CI jobs that enable the option.\n> >> \n> >> We cannot just remove the job because the CI is implicitly depending\n> >> on the \"poison\" job being a sort of \"default\" job.\n> >\n> > I don't understand what you mean here with a \"default job\" that the CI\n> > is implicitly depending on.  There is certainly no such default job on\n> > Travis CI, and I don't think there is one on the GitHub thing.\n> \n> I'll reword this. I meant that the poison job was using the default\n> compiler etc., whereas the other ones were setting custom values.\n> \n> I vaguely remember some list traffic about this in the past, i.e. the\n> reliance on the poison job not just for that, but also as \"the default\n> OS image\" setup.\n\nOh, I didn't mean that the commit message should be clarified.  I\nmeant that the GETTEXT_POISON job can simply be deleted.  Sorry for\nnot being clear enough.\n\n"},{"id":"414852","messageId":"20210120182759.31102-2-avarab@gmail.com","threadId":"51667","inReplyTo":"20210111144740.6092-1-avarab@gmail.com","subject":"[PATCH v2 1/3] ci: remove GETTEXT_POISON jobs","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-20T18:27:57Z","receivedAt":"2021-01-20T19:58:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"A subsequent commit will remove GETTEXT_POISON entirely, let's start\nby removing the CI jobs that enable the option.\n\nWe cannot just remove the job because the CI is implicitly depending\non the \"poison\" job being a sort of \"default\" job in the sense that\nit's the job that was otherwise run with the default compiler, no\nother GIT_TEST_* options etc. So let's keep it under the name\n\"linux-gcc-default\".\n\nThis means we can remove the initial \"make test\" from the \"linux-gcc\"\njob (it does another one after setting a bunch of GIT_TEST_*\nvariables).\n\nI'm not doing that because it would conflict with the in-flight\n334afbc76fb (tests: mark tests relying on the current default for\n`init.defaultBranch`, 2020-11-18) (currently on the \"seen\" branch, so\nthe SHA-1 will almost definitely change). It's going to use that \"make\ntest\" again for different reasons, so let's preserve it for now.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n .github/workflows/main.yml | 2 +-\n .travis.yml                | 2 +-\n ci/install-dependencies.sh | 2 +-\n ci/lib.sh                  | 3 +--\n 4 files changed, 4 insertions(+), 5 deletions(-)\n\ndiff --git a/.github/workflows/main.yml b/.github/workflows/main.yml\nindex aef6643648..8b52df200f 100644\n--- a/.github/workflows/main.yml\n+++ b/.github/workflows/main.yml\n@@ -286,7 +286,7 @@ jobs:\n           - jobname: osx-gcc\n             cc: gcc\n             pool: macos-latest\n-          - jobname: GETTEXT_POISON\n+          - jobname: linux-gcc-default\n             cc: gcc\n             pool: ubuntu-latest\n     env:\ndiff --git a/.travis.yml b/.travis.yml\nindex 05f3e3f8d7..908330a0a3 100644\n--- a/.travis.yml\n+++ b/.travis.yml\n@@ -16,7 +16,7 @@ compiler:\n \n matrix:\n   include:\n-    - env: jobname=GETTEXT_POISON\n+    - env: jobname=linux-gcc-default\n       os: linux\n       compiler:\n       addons:\ndiff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh\nindex 0b1184e04a..67852d0d37 100755\n--- a/ci/install-dependencies.sh\n+++ b/ci/install-dependencies.sh\n@@ -72,7 +72,7 @@ Documentation)\n \ttest -n \"$ALREADY_HAVE_ASCIIDOCTOR\" ||\n \tsudo gem install --version 1.5.8 asciidoctor\n \t;;\n-linux-gcc-4.8|GETTEXT_POISON)\n+linux-gcc-default|linux-gcc-4.8)\n \tsudo apt-get -q update\n \tsudo apt-get -q -y install $UBUNTU_COMMON_PKGS\n \t;;\ndiff --git a/ci/lib.sh b/ci/lib.sh\nindex 38c0eac351..d848c036c5 100755\n--- a/ci/lib.sh\n+++ b/ci/lib.sh\n@@ -220,8 +220,7 @@ osx-clang|osx-gcc)\n \t# Travis CI OS X\n \texport GIT_SKIP_TESTS=\"t9810 t9816\"\n \t;;\n-GETTEXT_POISON)\n-\texport GIT_TEST_GETTEXT_POISON=true\n+linux-gcc-default)\n \t;;\n Linux32)\n \tCC=gcc\n-- \n2.29.2.222.g5d2a92d10f8\n\n"},{"id":"415396","messageId":"87pn1rp5nk.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"20210120191406.GE8396@szeder.dev","subject":"Re: [PATCH 1/6] ci: remove GETTEXT_POISON jobs","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-01-27T00:47:27Z","receivedAt":"2021-01-27T07:11:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Jan 20 2021, SZEDER Gábor wrote:\n\n> On Wed, Jan 20, 2021 at 06:59:14PM +0100, Ævar Arnfjörð Bjarmason wrote:\n>> \n>> On Tue, Jan 12 2021, SZEDER Gábor wrote:\n>> \n>> > On Mon, Jan 11, 2021 at 03:47:35PM +0100, Ævar Arnfjörð Bjarmason wrote:\n>> >> A subsequent commit will remove GETTEXT_POISON entirely, let's start\n>> >> by removing the CI jobs that enable the option.\n>> >> \n>> >> We cannot just remove the job because the CI is implicitly depending\n>> >> on the \"poison\" job being a sort of \"default\" job.\n>> >\n>> > I don't understand what you mean here with a \"default job\" that the CI\n>> > is implicitly depending on.  There is certainly no such default job on\n>> > Travis CI, and I don't think there is one on the GitHub thing.\n>> \n>> I'll reword this. I meant that the poison job was using the default\n>> compiler etc., whereas the other ones were setting custom values.\n>> \n>> I vaguely remember some list traffic about this in the past, i.e. the\n>> reliance on the poison job not just for that, but also as \"the default\n>> OS image\" setup.\n>\n> Oh, I didn't mean that the commit message should be clarified.  I\n> meant that the GETTEXT_POISON job can simply be deleted.  Sorry for\n> not being clear enough.\n\nI still don't get it, and my patch still seems fine as it is to me. But\nI'm probably missing something.\n\nI'm looking at these, both pointing at e6362826a04 (The fourth batch,\n2021-01-25):\n\n    # Currently says \"Please be aware travis-ci.org will be shutting\n    # down in several weeks\"\n    https://www.travis-ci.org/github/git/git/builds/756176098\n\n    https://github.com/git/git/actions/runs/510676142\n\nFor the GitHub one, there's only two things that are linux && gcc. The\nlinux-gcc job, and GETTEXT_POISON (renamed to linux-gcc-default in this\nseries).\n\nIn ci/install-dependencies.sh we install GCC 8 for linux-gcc, but also\nperforce, apache, git-lfs etc.\n\nThen in ci/lib.sh we set CC=gcc-8 on linux-gcc, but retain the default\non GETTEXT_POISON. We also set GIT_TEST_HTTPD=true.\n\nSo hence \"linux-gcc-default\", we're running as close to a default set of\npackages we can get and build/run git at all. I.e. just \"apt install\n$UBUNTU_COMMON_PKGS\".\n\nThen in ci/run-build-and-tests.sh the \"linux-gcc\" job is also our \"let's\nturn on every GIT_TEST_* option you've heard of\" job. Whereas on the\nGETTEXT_POISON job we just ran \"make test\", well, with\nGIT_TEST_GETTEXT_POISON=true before this series, but that was the only\nnon-default, now we've got no non-defaults.\n\nI can see why we'd want this on top:\n    \n    diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh\n    index 50e0b90073f..3071099eaca 100755\n    --- a/ci/run-build-and-tests.sh\n    +++ b/ci/run-build-and-tests.sh\n    @@ -32,11 +32,6 @@ linux-clang)\n            export GIT_TEST_DEFAULT_HASH=sha256\n            make test\n            ;;\n    -linux-gcc-4.8)\n    -       # Don't run the tests; we only care about whether Git can be\n    -       # built with GCC 4.8, as it errors out on some undesired (C99)\n    -       # constructs that newer compilers seem to quietly accept.\n    -       ;;\n     *)\n            make test\n            ;;\n\nBecause there we're skipping tests on linux-gcc-4.8, but on travis it's\nthe only Ubuntu Trusty image (default is Xenial), so yes we build with\ngcc 4.8, but we're also missing out in seeing if our tests are working\non a slightly older Ubuntu.\n\nAlso on GitHub's page the GETTEXT_POISON job finishes in ~10m but all\nthe other ones in ~30m, I see some run \"make test\" twice, as an aside is\nthere some limit on jobs but not overall CPU? Otherwise those would\nfinish faster if that was split up.\n\nBut \"make test\" twice doesn't account for a ~3x increase, so it's got to\nbe the extra tests we're running, i.e. svn tests, httpd etc.\n\nIt seems to me to provide value in itself to run some shorter version of\nthe CI for people using it as a poor man's edit/push/ci/edit loop.\n"},{"id":"415831","messageId":"20210201220437.GB2091@szeder.dev","threadId":"51667","inReplyTo":"87pn1rp5nk.fsf@evledraar.gmail.com","subject":"Re: [PATCH 1/6] ci: remove GETTEXT_POISON jobs","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2021-02-01T22:04:37Z","receivedAt":"2021-02-01T22:08:58Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Wed, Jan 27, 2021 at 01:47:27AM +0100, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Wed, Jan 20 2021, SZEDER Gábor wrote:\n> \n> > On Wed, Jan 20, 2021 at 06:59:14PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> >> \n> >> On Tue, Jan 12 2021, SZEDER Gábor wrote:\n> >> \n> >> > On Mon, Jan 11, 2021 at 03:47:35PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> >> >> A subsequent commit will remove GETTEXT_POISON entirely, let's start\n> >> >> by removing the CI jobs that enable the option.\n> >> >> \n> >> >> We cannot just remove the job because the CI is implicitly depending\n> >> >> on the \"poison\" job being a sort of \"default\" job.\n> >> >\n> >> > I don't understand what you mean here with a \"default job\" that the CI\n> >> > is implicitly depending on.  There is certainly no such default job on\n> >> > Travis CI, and I don't think there is one on the GitHub thing.\n> >> \n> >> I'll reword this. I meant that the poison job was using the default\n> >> compiler etc., whereas the other ones were setting custom values.\n> >> \n> >> I vaguely remember some list traffic about this in the past, i.e. the\n> >> reliance on the poison job not just for that, but also as \"the default\n> >> OS image\" setup.\n> >\n> > Oh, I didn't mean that the commit message should be clarified.  I\n> > meant that the GETTEXT_POISON job can simply be deleted.  Sorry for\n> > not being clear enough.\n> \n> I still don't get it, and my patch still seems fine as it is to me. But\n> I'm probably missing something.\n> \n> I'm looking at these, both pointing at e6362826a04 (The fourth batch,\n> 2021-01-25):\n> \n>     # Currently says \"Please be aware travis-ci.org will be shutting\n>     # down in several weeks\"\n\n... and continues with:\n\n  \"with all accounts migrating to travis-ci.com.  Please stay tuned\n   here for more information.\"\n\nFWIW, I recently migrated manually, and apart from the different TLD\nhaven't noticed anything different (though somehow the jobs in my .org\nbuilds sometimes took a while to start lately, but on .com they all\nstarted real fast so far, as they should).\n\n>     https://www.travis-ci.org/github/git/git/builds/756176098\n> \n>     https://github.com/git/git/actions/runs/510676142\n> \n> For the GitHub one, there's only two things that are linux && gcc. The\n> linux-gcc job, and GETTEXT_POISON (renamed to linux-gcc-default in this\n> series).\n> \n> In ci/install-dependencies.sh we install GCC 8 for linux-gcc, but also\n> perforce, apache, git-lfs etc.\n> \n> Then in ci/lib.sh we set CC=gcc-8 on linux-gcc, but retain the default\n> on GETTEXT_POISON. We also set GIT_TEST_HTTPD=true.\n> \n> So hence \"linux-gcc-default\", we're running as close to a default set of\n> packages we can get and build/run git at all. I.e. just \"apt install\n> $UBUNTU_COMMON_PKGS\".\n\nWe started installing newer compilers, because we wanted to benefit\nfrom their better error reporting and improved warnings.  The reason\nwe did that in existing CI jobs without adding a \"default\" job was\nthat we don't care enough about the default compiler and configuration\nto justify its additional runtime, because we run the test suite\nenough times already.\n\nConsidering that the number of CI jobs has grown since then, I would\nrather see the build complete faster than a default job running the\ntest suite for the N+1th time.\n\n> Then in ci/run-build-and-tests.sh the \"linux-gcc\" job is also our \"let's\n> turn on every GIT_TEST_* option you've heard of\" job. Whereas on the\n> GETTEXT_POISON job we just ran \"make test\", well, with\n> GIT_TEST_GETTEXT_POISON=true before this series, but that was the only\n> non-default, now we've got no non-defaults.\n> \n> I can see why we'd want this on top:\n>     \n>     diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh\n>     index 50e0b90073f..3071099eaca 100755\n>     --- a/ci/run-build-and-tests.sh\n>     +++ b/ci/run-build-and-tests.sh\n>     @@ -32,11 +32,6 @@ linux-clang)\n>             export GIT_TEST_DEFAULT_HASH=sha256\n>             make test\n>             ;;\n>     -linux-gcc-4.8)\n>     -       # Don't run the tests; we only care about whether Git can be\n>     -       # built with GCC 4.8, as it errors out on some undesired (C99)\n>     -       # constructs that newer compilers seem to quietly accept.\n>     -       ;;\n>      *)\n>             make test\n>             ;;\n> \n> Because there we're skipping tests on linux-gcc-4.8, but on travis it's\n> the only Ubuntu Trusty image (default is Xenial), so yes we build with\n> gcc 4.8, but we're also missing out in seeing if our tests are working\n> on a slightly older Ubuntu.\n\nThe above comment you propose to remove explains why you should not\nremove it...  And, again, running the test suite doesn't seem to worth\nthe extra runtime.\n\n\n> Also on GitHub's page the GETTEXT_POISON job finishes in ~10m but all\n> the other ones in ~30m, I see some run \"make test\" twice, as an aside is\n> there some limit on jobs but not overall CPU? Otherwise those would\n> finish faster if that was split up.\n> \n> But \"make test\" twice doesn't account for a ~3x increase, so it's got to\n> be the extra tests we're running, i.e. svn tests, httpd etc.\n> \n> It seems to me to provide value in itself to run some shorter version of\n> the CI for people using it as a poor man's edit/push/ci/edit loop.\n"},{"id":"416039","messageId":"87ft2de4cz.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"20210201220437.GB2091@szeder.dev","subject":"Re: [PATCH 1/6] ci: remove GETTEXT_POISON jobs","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-03T12:13:32Z","receivedAt":"2021-02-03T12:14:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Feb 01 2021, SZEDER Gábor wrote:\n\n> On Wed, Jan 27, 2021 at 01:47:27AM +0100, Ævar Arnfjörð Bjarmason wrote:\n>> \n>> On Wed, Jan 20 2021, SZEDER Gábor wrote:\n>> \n>> > On Wed, Jan 20, 2021 at 06:59:14PM +0100, Ævar Arnfjörð Bjarmason wrote:\n>> >> \n>> >> On Tue, Jan 12 2021, SZEDER Gábor wrote:\n>> >> \n>> >> > On Mon, Jan 11, 2021 at 03:47:35PM +0100, Ævar Arnfjörð Bjarmason wrote:\n>> >> >> A subsequent commit will remove GETTEXT_POISON entirely, let's start\n>> >> >> by removing the CI jobs that enable the option.\n>> >> >> \n>> >> >> We cannot just remove the job because the CI is implicitly depending\n>> >> >> on the \"poison\" job being a sort of \"default\" job.\n>> >> >\n>> >> > I don't understand what you mean here with a \"default job\" that the CI\n>> >> > is implicitly depending on.  There is certainly no such default job on\n>> >> > Travis CI, and I don't think there is one on the GitHub thing.\n>> >> \n>> >> I'll reword this. I meant that the poison job was using the default\n>> >> compiler etc., whereas the other ones were setting custom values.\n>> >> \n>> >> I vaguely remember some list traffic about this in the past, i.e. the\n>> >> reliance on the poison job not just for that, but also as \"the default\n>> >> OS image\" setup.\n>> >\n>> > Oh, I didn't mean that the commit message should be clarified.  I\n>> > meant that the GETTEXT_POISON job can simply be deleted.  Sorry for\n>> > not being clear enough.\n>> \n>> I still don't get it, and my patch still seems fine as it is to me. But\n>> I'm probably missing something.\n>> \n>> I'm looking at these, both pointing at e6362826a04 (The fourth batch,\n>> 2021-01-25):\n>> \n>>     # Currently says \"Please be aware travis-ci.org will be shutting\n>>     # down in several weeks\"\n>\n> ... and continues with:\n>\n>   \"with all accounts migrating to travis-ci.com.  Please stay tuned\n>    here for more information.\"\n>\n> FWIW, I recently migrated manually, and apart from the different TLD\n> haven't noticed anything different (though somehow the jobs in my .org\n> builds sometimes took a while to start lately, but on .com they all\n> started real fast so far, as they should).\n>\n>>     https://www.travis-ci.org/github/git/git/builds/756176098\n>> \n>>     https://github.com/git/git/actions/runs/510676142\n>> \n>> For the GitHub one, there's only two things that are linux && gcc. The\n>> linux-gcc job, and GETTEXT_POISON (renamed to linux-gcc-default in this\n>> series).\n>> \n>> In ci/install-dependencies.sh we install GCC 8 for linux-gcc, but also\n>> perforce, apache, git-lfs etc.\n>> \n>> Then in ci/lib.sh we set CC=gcc-8 on linux-gcc, but retain the default\n>> on GETTEXT_POISON. We also set GIT_TEST_HTTPD=true.\n>> \n>> So hence \"linux-gcc-default\", we're running as close to a default set of\n>> packages we can get and build/run git at all. I.e. just \"apt install\n>> $UBUNTU_COMMON_PKGS\".\n>\n> We started installing newer compilers, because we wanted to benefit\n> from their better error reporting and improved warnings.  The reason\n> we did that in existing CI jobs without adding a \"default\" job was\n> that we don't care enough about the default compiler and configuration\n> to justify its additional runtime, because we run the test suite\n> enough times already.\n\nSo in summary, you'd like it removed because the combination of\nGETTEXT_POISON + old compiler is no longer worth it, but while we were\nrunning with GETTEXT_POISON anyway we might as well run it on the old\ncompiler?\n\n> Considering that the number of CI jobs has grown since then, I would\n> rather see the build complete faster than a default job running the\n> test suite for the N+1th time.\n\n[...]\n\n>> Then in ci/run-build-and-tests.sh the \"linux-gcc\" job is also our \"let's\n>> turn on every GIT_TEST_* option you've heard of\" job. Whereas on the\n>> GETTEXT_POISON job we just ran \"make test\", well, with\n>> GIT_TEST_GETTEXT_POISON=true before this series, but that was the only\n>> non-default, now we've got no non-defaults.\n>> \n>> I can see why we'd want this on top:\n>>     \n>>     diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh\n>>     index 50e0b90073f..3071099eaca 100755\n>>     --- a/ci/run-build-and-tests.sh\n>>     +++ b/ci/run-build-and-tests.sh\n>>     @@ -32,11 +32,6 @@ linux-clang)\n>>             export GIT_TEST_DEFAULT_HASH=sha256\n>>             make test\n>>             ;;\n>>     -linux-gcc-4.8)\n>>     -       # Don't run the tests; we only care about whether Git can be\n>>     -       # built with GCC 4.8, as it errors out on some undesired (C99)\n>>     -       # constructs that newer compilers seem to quietly accept.\n>>     -       ;;\n>>      *)\n>>             make test\n>>             ;;\n>> \n>> Because there we're skipping tests on linux-gcc-4.8, but on travis it's\n>> the only Ubuntu Trusty image (default is Xenial), so yes we build with\n>> gcc 4.8, but we're also missing out in seeing if our tests are working\n>> on a slightly older Ubuntu.\n>\n> The above comment you propose to remove explains why you should not\n> remove it...  And, again, running the test suite doesn't seem to worth\n> the extra runtime.\n\nHow is it extra runtime in any meaningful sense?\n\nYes, we spend more CPU time, but as far as I can tell (and I'm looking\nat an ongoing run on GitHub now) these jobs are run concurrently. Some\nof that was covered in the quoted paragraph below that you didn't reply\nto.\n\nI.e. if I push a commit I don't care if 10 minutes of CPU time are being\nwasted by a machine sitting in some rack somewhere, the important part\nis that I may be saving 20 minutes of my time, since the faster 10\nminute test (running concurrently with the ~30m tests) might fail\nearlier.\n\nOr are there some job limits involved here that I'm missing?\n\nAs I was writing this the CI job for GETTEXT_POISON just finished in\n10m16s showing OK=green, and the rest are still churning.\n\n>> Also on GitHub's page the GETTEXT_POISON job finishes in ~10m but all\n>> the other ones in ~30m, I see some run \"make test\" twice, as an aside is\n>> there some limit on jobs but not overall CPU? Otherwise those would\n>> finish faster if that was split up.\n>> \n>> But \"make test\" twice doesn't account for a ~3x increase, so it's got to\n>> be the extra tests we're running, i.e. svn tests, httpd etc.\n>> \n>> It seems to me to provide value in itself to run some shorter version of\n>> the CI for people using it as a poor man's edit/push/ci/edit loop.\n\n"},{"id":"416095","messageId":"20210203212419.GF2091@szeder.dev","threadId":"51667","inReplyTo":"87ft2de4cz.fsf@evledraar.gmail.com","subject":"Re: [PATCH 1/6] ci: remove GETTEXT_POISON jobs","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2021-02-03T21:24:19Z","receivedAt":"2021-02-03T21:25:26Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Wed, Feb 03, 2021 at 01:13:32PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Mon, Feb 01 2021, SZEDER Gábor wrote:\n> \n> > On Wed, Jan 27, 2021 at 01:47:27AM +0100, Ævar Arnfjörð Bjarmason wrote:\n> >> \n> >> On Wed, Jan 20 2021, SZEDER Gábor wrote:\n> >> \n> >> > On Wed, Jan 20, 2021 at 06:59:14PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> >> >> \n> >> >> On Tue, Jan 12 2021, SZEDER Gábor wrote:\n> >> >> \n> >> >> > On Mon, Jan 11, 2021 at 03:47:35PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> >> >> >> A subsequent commit will remove GETTEXT_POISON entirely, let's start\n> >> >> >> by removing the CI jobs that enable the option.\n> >> >> >> \n> >> >> >> We cannot just remove the job because the CI is implicitly depending\n> >> >> >> on the \"poison\" job being a sort of \"default\" job.\n> >> >> >\n> >> >> > I don't understand what you mean here with a \"default job\" that the CI\n> >> >> > is implicitly depending on.  There is certainly no such default job on\n> >> >> > Travis CI, and I don't think there is one on the GitHub thing.\n> >> >> \n> >> >> I'll reword this. I meant that the poison job was using the default\n> >> >> compiler etc., whereas the other ones were setting custom values.\n> >> >> \n> >> >> I vaguely remember some list traffic about this in the past, i.e. the\n> >> >> reliance on the poison job not just for that, but also as \"the default\n> >> >> OS image\" setup.\n> >> >\n> >> > Oh, I didn't mean that the commit message should be clarified.  I\n> >> > meant that the GETTEXT_POISON job can simply be deleted.  Sorry for\n> >> > not being clear enough.\n> >> \n> >> I still don't get it, and my patch still seems fine as it is to me. But\n> >> I'm probably missing something.\n> >> \n> >> I'm looking at these, both pointing at e6362826a04 (The fourth batch,\n> >> 2021-01-25):\n> >> \n> >>     # Currently says \"Please be aware travis-ci.org will be shutting\n> >>     # down in several weeks\"\n> >\n> > ... and continues with:\n> >\n> >   \"with all accounts migrating to travis-ci.com.  Please stay tuned\n> >    here for more information.\"\n> >\n> > FWIW, I recently migrated manually, and apart from the different TLD\n> > haven't noticed anything different (though somehow the jobs in my .org\n> > builds sometimes took a while to start lately, but on .com they all\n> > started real fast so far, as they should).\n> >\n> >>     https://www.travis-ci.org/github/git/git/builds/756176098\n> >> \n> >>     https://github.com/git/git/actions/runs/510676142\n> >> \n> >> For the GitHub one, there's only two things that are linux && gcc. The\n> >> linux-gcc job, and GETTEXT_POISON (renamed to linux-gcc-default in this\n> >> series).\n> >> \n> >> In ci/install-dependencies.sh we install GCC 8 for linux-gcc, but also\n> >> perforce, apache, git-lfs etc.\n> >> \n> >> Then in ci/lib.sh we set CC=gcc-8 on linux-gcc, but retain the default\n> >> on GETTEXT_POISON. We also set GIT_TEST_HTTPD=true.\n> >> \n> >> So hence \"linux-gcc-default\", we're running as close to a default set of\n> >> packages we can get and build/run git at all. I.e. just \"apt install\n> >> $UBUNTU_COMMON_PKGS\".\n> >\n> > We started installing newer compilers, because we wanted to benefit\n> > from their better error reporting and improved warnings.  The reason\n> > we did that in existing CI jobs without adding a \"default\" job was\n> > that we don't care enough about the default compiler and configuration\n> > to justify its additional runtime, because we run the test suite\n> > enough times already.\n> \n> So in summary, you'd like it removed because the combination of\n> GETTEXT_POISON + old compiler is no longer worth it, but while we were\n> running with GETTEXT_POISON anyway we might as well run it on the old\n> compiler?\n\nYes, though I suspect that \"no one gave a damn about the compiler used\nin the GETTEXT_POISON job\" might be closer to the truth.  Or even \"no\none gave a damn about the GETTEXT_POISON job\"...\n\nAnd apart from the occasional translation-related failure (i.e. 'grep\n\"<translated message>\" actual') and one flaky test that happened to\nfail in the GETTEXT_POISON job I can't recall a single case where\nbuilding and testing with the default compiler discovered an issue\nthat the other jobs didn't.\n\n> > Considering that the number of CI jobs has grown since then, I would\n> > rather see the build complete faster than a default job running the\n> > test suite for the N+1th time.\n> \n> [...]\n> \n> >> Then in ci/run-build-and-tests.sh the \"linux-gcc\" job is also our \"let's\n> >> turn on every GIT_TEST_* option you've heard of\" job. Whereas on the\n> >> GETTEXT_POISON job we just ran \"make test\", well, with\n> >> GIT_TEST_GETTEXT_POISON=true before this series, but that was the only\n> >> non-default, now we've got no non-defaults.\n> >> \n> >> I can see why we'd want this on top:\n> >>     \n> >>     diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh\n> >>     index 50e0b90073f..3071099eaca 100755\n> >>     --- a/ci/run-build-and-tests.sh\n> >>     +++ b/ci/run-build-and-tests.sh\n> >>     @@ -32,11 +32,6 @@ linux-clang)\n> >>             export GIT_TEST_DEFAULT_HASH=sha256\n> >>             make test\n> >>             ;;\n> >>     -linux-gcc-4.8)\n> >>     -       # Don't run the tests; we only care about whether Git can be\n> >>     -       # built with GCC 4.8, as it errors out on some undesired (C99)\n> >>     -       # constructs that newer compilers seem to quietly accept.\n> >>     -       ;;\n> >>      *)\n> >>             make test\n> >>             ;;\n> >> \n> >> Because there we're skipping tests on linux-gcc-4.8, but on travis it's\n> >> the only Ubuntu Trusty image (default is Xenial), so yes we build with\n> >> gcc 4.8, but we're also missing out in seeing if our tests are working\n> >> on a slightly older Ubuntu.\n> >\n> > The above comment you propose to remove explains why you should not\n> > remove it...  And, again, running the test suite doesn't seem to worth\n> > the extra runtime.\n> \n> How is it extra runtime in any meaningful sense?\n> \n> Yes, we spend more CPU time, but as far as I can tell (and I'm looking\n> at an ongoing run on GitHub now)\n\n(Well, I'm looking at eight ongoing builds now...)\n\n> these jobs are run concurrently. Some\n> of that was covered in the quoted paragraph below that you didn't reply\n> to.\n> \n> I.e. if I push a commit I don't care if 10 minutes of CPU time are being\n> wasted by a machine sitting in some rack somewhere, the important part\n> is that I may be saving 20 minutes of my time, since the faster 10\n> minute test (running concurrently with the ~30m tests) might fail\n> earlier.\n> \n> Or are there some job limits involved here that I'm missing?\n\nTravis CI usually runs 3 Linux and 2 OSX jobs in parallel, and I doubt\nthat GitHub would be foolish enough to run all my jobs parallel if I\nwere to use it.\n\n> As I was writing this the CI job for GETTEXT_POISON just finished in\n> 10m16s showing OK=green, and the rest are still churning.\n\nIndeed, but you should also consider that:\n\n  - At 10m16s that job is notably slower than even my tiny old laptop,\n    even though it runs less tests.  If you really want to save time,\n    then run 'make test' locally.\n\n  - Without the GETTEXT_POISON job the whole build would be finished\n    ~10 minutes earlier.  With six builds that's an hour.\n\n\n> >> Also on GitHub's page the GETTEXT_POISON job finishes in ~10m but all\n> >> the other ones in ~30m, I see some run \"make test\" twice, as an aside is\n> >> there some limit on jobs but not overall CPU? Otherwise those would\n> >> finish faster if that was split up.\n> >> \n> >> But \"make test\" twice doesn't account for a ~3x increase, so it's got to\n> >> be the extra tests we're running, i.e. svn tests, httpd etc.\n> >> \n> >> It seems to me to provide value in itself to run some shorter version of\n> >> the CI for people using it as a poor man's edit/push/ci/edit loop.\n> \n"},{"id":"416689","messageId":"20210211015353.28680-2-avarab@gmail.com","threadId":"51667","inReplyTo":"20210120182759.31102-1-avarab@gmail.com","subject":"[PATCH 1/4] tests: remove last uses of GIT_TEST_GETTEXT_POISON=false","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-11T01:53:50Z","receivedAt":"2021-02-11T01:59:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Follow-up my 73c01d25fe2 (tests: remove uses of\nGIT_TEST_GETTEXT_POISON=false, 2021-01-20) by removing the last uses\nof GIT_TEST_GETTEXT_POISON=*.\n\nThese assignments were part of branch that was in-flight at the time\nof the gettext poison removal. See 466f94ec45e (Merge branch\n'ab/detox-gettext-tests', 2021-02-10) and c7d6d419b0d (Merge branch\n'ab/mktag', 2021-01-25) for the merging of the two branches.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 12 ++++--------\n 1 file changed, 4 insertions(+), 8 deletions(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 86bfeb271ec..60a666da595 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -13,13 +13,11 @@ test_description='git mktag: tag object verify test'\n \n check_verify_failure () {\n \ttest_expect_success \"$1\" \"\n-\t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n-\t\t\tgit mktag <tag.sig 2>message &&\n+\t\ttest_must_fail git mktag <tag.sig 2>message &&\n \t\tgrep '$2' message &&\n \t\tif test '$3' != '--no-strict'\n \t\tthen\n-\t\t\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n-\t\t\t\tgit mktag --no-strict <tag.sig 2>message.no-strict &&\n+\t\t\ttest_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&xb\n \t\t\tgrep '$2' message.no-strict\n \t\tfi\n \t\"\n@@ -443,11 +441,9 @@ test_expect_success 'invalid header entry config & fsck' '\n \tgit -c fsck.extraHeaderEntry=ignore mktag --no-strict <tag.sig &&\n \n \tgit fsck &&\n-\tenv GIT_TEST_GETTEXT_POISON=false \\\n-\t\tgit -c fsck.extraHeaderEntry=warn fsck 2>err &&\n+\tgit -c fsck.extraHeaderEntry=warn fsck 2>err &&\n \tgrep \"warning .*extraHeaderEntry:\" err &&\n-\ttest_must_fail env GIT_TEST_GETTEXT_POISON=false \\\n-\t\tgit -c fsck.extraHeaderEntry=error 2>err fsck &&\n+\ttest_must_fail git -c fsck.extraHeaderEntry=error 2>err fsck &&\n \tgrep \"error .* extraHeaderEntry:\" err\n '\n \n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416690","messageId":"20210211015353.28680-4-avarab@gmail.com","threadId":"51667","inReplyTo":"20210120182759.31102-1-avarab@gmail.com","subject":"[PATCH 3/4] tests: remove last uses of C_LOCALE_OUTPUT","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-11T01:53:52Z","receivedAt":"2021-02-11T02:00:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove the last uses of the C_LOCALE_OUTPUT prerequisite as well as\nthe prerequisite itself. This is a follow-up to d162b25f956 (tests:\nremove support for GIT_TEST_GETTEXT_POISON, 2021-01-20), as well as\nthe preceding commit where we removed the simpler uses of\nC_LOCALE_OUTPUT.\n\nHere I'm slightly refactoring a test added in 21e5ad50fc5 (safecrlf:\nAdd mechanism to warn about irreversible crlf conversions,\n2008-02-06), as well as getting rid of another \"test_have_prereq\nC_LOCALE_OUTPUT\" use.\n\nI'm not leaving the prerequisite itself in place for in-flight changes\nas there currently are none that introduce new tests that rely on it,\nand because C_LOCALE_OUTPUT is currently a noop on the master branch\nwe likely won't have any new submissions that use it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t0020-crlf.sh             | 6 ++----\n t/t9003-help-autocorrect.sh | 7 ++-----\n t/test-lib.sh               | 5 -----\n 3 files changed, 4 insertions(+), 14 deletions(-)\n\ndiff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh\nindex 375cf943985..f25ae8b5e1f 100755\n--- a/t/t0020-crlf.sh\n+++ b/t/t0020-crlf.sh\n@@ -87,10 +87,8 @@ test_expect_success 'safecrlf: print warning only once' '\n \tgit commit -m \"nowarn\" &&\n \tfor w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >doublewarn &&\n \tgit add doublewarn 2>err &&\n-\tif test_have_prereq C_LOCALE_OUTPUT\n-\tthen\n-\t\ttest $(grep \"CRLF will be replaced by LF\" err | wc -l) = 1\n-\tfi\n+\tgrep \"CRLF will be replaced by LF\" err >err.warnings &&\n+\ttest_line_count = 1 err.warnings\n '\n \n \ndiff --git a/t/t9003-help-autocorrect.sh b/t/t9003-help-autocorrect.sh\nindex 03cd5c54236..f00deaf3815 100755\n--- a/t/t9003-help-autocorrect.sh\n+++ b/t/t9003-help-autocorrect.sh\n@@ -56,11 +56,8 @@ test_expect_success 'autocorrect can be declined altogether' '\n \tgit config help.autocorrect never &&\n \n \ttest_must_fail git lfg 2>actual &&\n-\tif test_have_prereq C_LOCALE_OUTPUT\n-\tthen\n-\t\tgrep \"is not a git command\" actual &&\n-\t\ttest_line_count = 1 actual\n-\tfi\n+\tgrep \"is not a git command\" actual &&\n+\ttest_line_count = 1 actual\n '\n \n test_done\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 431adba0fb3..6d188a8075d 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -1519,11 +1519,6 @@ test -n \"$USE_LIBPCRE2\" && test_set_prereq PCRE\n test -n \"$USE_LIBPCRE2\" && test_set_prereq LIBPCRE2\n test -z \"$NO_GETTEXT\" && test_set_prereq GETTEXT\n \n-# Used to be used for GIT_TEST_GETTEXT_POISON=false. Only here as a\n-# shim for other in-flight changes. Should not be used and will be\n-# removed soon.\n-test_set_prereq C_LOCALE_OUTPUT\n-\n if test -z \"$GIT_TEST_CHECK_CACHE_TREE\"\n then\n \tGIT_TEST_CHECK_CACHE_TREE=true\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416691","messageId":"20210211015353.28680-1-avarab@gmail.com","threadId":"51667","inReplyTo":"20210120182759.31102-1-avarab@gmail.com","subject":"[PATCH 0/4] More GETTEXT_POISON removal","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-11T01:53:49Z","receivedAt":"2021-02-11T02:00:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that the removal of GETTEXT_POISON is in master follow-up [1] with\nsome more removal of now-dead code that assumed it still existed.\n\nThis leaves no uses of GIT_TEST_GETTEXT_POISON=false or\nC_LOCALE_OUTPUT at the end, in 4/4 I'm removing most of test_i18ncmp,\nbut stopping short of conflicting with other in-flight topics, so once\nthose land we'll need another cleanup patch to clean up the\nstragglers.\n\nAs with [1] I'm not touching test_i18ngrep since SZEDER expressed some\ninterest in retainng it as a generally useful grep with more verbose\ndebugging.\n\n1. https://lore.kernel.org/git/20210120182759.31102-1-avarab@gmail.com/\n\nÆvar Arnfjörð Bjarmason (4):\n  tests: remove last uses of GIT_TEST_GETTEXT_POISON=false\n  tests: remove most uses of C_LOCALE_OUTPUT\n  tests: remove last uses of C_LOCALE_OUTPUT\n  tests: remove most uses of test_i18ncmp\n\n t/lib-credential.sh                       |   2 +-\n t/lib-log-graph.sh                        |   4 +-\n t/t0000-basic.sh                          |   2 +-\n t/t0006-date.sh                           |   4 +-\n t/t0008-ignores.sh                        |   2 +-\n t/t0012-help.sh                           |   2 +-\n t/t0018-advice.sh                         |   4 +-\n t/t0020-crlf.sh                           |   6 +-\n t/t0027-auto-crlf.sh                      |   2 +-\n t/t0040-parse-options.sh                  |   8 +-\n t/t0201-gettext-fallbacks.sh              |  16 ++--\n t/t0300-credentials.sh                    |   8 +-\n t/t0500-progress-display.sh               |  22 ++---\n t/t1011-read-tree-sparse-checkout.sh      |   2 +-\n t/t1308-config-set.sh                     |   4 +-\n t/t1400-update-ref.sh                     |   6 +-\n t/t1404-update-ref-errors.sh              |   2 +-\n t/t1430-bad-ref-name.sh                   |   2 +-\n t/t1450-fsck.sh                           |   6 +-\n t/t1502-rev-parse-parseopt.sh             |  10 +-\n t/t1506-rev-parse-diagnosis.sh            |   2 +-\n t/t1507-rev-parse-upstream.sh             |  10 +-\n t/t1509-root-work-tree.sh                 |   4 +-\n t/t1512-rev-parse-disambiguation.sh       |  14 +--\n t/t1600-index.sh                          |   6 +-\n t/t2018-checkout-branch.sh                |   2 +-\n t/t2020-checkout-detach.sh                |  20 ++--\n t/t2200-add-update.sh                     |   2 +-\n t/t2401-worktree-prune.sh                 |   4 +-\n t/t2402-worktree-list.sh                  |   2 +-\n t/t3005-ls-files-relative.sh              |   4 +-\n t/t3200-branch.sh                         |  26 ++---\n t/t3201-branch-contains.sh                |   2 +-\n t/t3203-branch-output.sh                  |  28 +++---\n t/t3300-funny-names.sh                    |   2 +-\n t/t3400-rebase.sh                         |   2 +-\n t/t3404-rebase-interactive.sh             |  38 ++++----\n t/t3415-rebase-autosquash.sh              |  10 +-\n t/t3420-rebase-autostash.sh               |   4 +-\n t/t3504-cherry-pick-rerere.sh             |   4 +-\n t/t3507-cherry-pick-conflict.sh           |  12 +--\n t/t3508-cherry-pick-many-commits.sh       |   4 +-\n t/t3510-cherry-pick-sequence.sh           |   8 +-\n t/t3600-rm.sh                             |  16 ++--\n t/t3700-add.sh                            |   8 +-\n t/t3701-add-interactive.sh                |   4 +-\n t/t3800-mktag.sh                          |  12 +--\n t/t3903-stash.sh                          |   8 +-\n t/t3905-stash-include-untracked.sh        |   2 +-\n t/t4006-diff-mode.sh                      |   8 +-\n t/t4012-diff-binary.sh                    |  10 +-\n t/t4013-diff-various.sh                   |   2 +-\n t/t4014-format-patch.sh                   |   6 +-\n t/t4016-diff-quote.sh                     |   2 +-\n t/t4030-diff-textconv.sh                  |   2 +-\n t/t4045-diff-relative.sh                  |   2 +-\n t/t4049-diff-stat-count.sh                |   6 +-\n t/t4100-apply-stat.sh                     |   4 +-\n t/t4150-am.sh                             |   2 +-\n t/t4153-am-resume-override-opts.sh        |   2 +-\n t/t4205-log-pretty-formats.sh             |   4 +-\n t/t4254-am-corrupt.sh                     |   2 +-\n t/t5150-request-pull.sh                   |   4 +-\n t/t5300-pack-object.sh                    |   4 +-\n t/t5316-pack-delta-depth.sh               |   4 +-\n t/t5500-fetch-pack.sh                     |   6 +-\n t/t5505-remote.sh                         |  30 +++---\n t/t5510-fetch.sh                          |   6 +-\n t/t5512-ls-remote.sh                      |   2 +-\n t/t5526-fetch-submodules.sh               |  38 ++++----\n t/t5541-http-push-smart.sh                |   2 +-\n t/t5601-clone.sh                          |   2 +-\n t/t6020-bundle-misc.sh                    |  28 +++---\n t/t6030-bisect-porcelain.sh               |   4 +-\n t/t6040-tracking-info.sh                  |  22 ++---\n t/t6120-describe.sh                       |   2 +-\n t/t6134-pathspec-in-submodule.sh          |   2 +-\n t/t6301-for-each-ref-errors.sh            |  10 +-\n t/t6423-merge-rename-directories.sh       |   2 +-\n t/t6436-merge-overwrite.sh                |   4 +-\n t/t6439-merge-co-error-msgs.sh            |  12 +--\n t/t7001-mv.sh                             |   2 +-\n t/t7004-tag.sh                            |   8 +-\n t/t7012-skip-worktree-writing.sh          |   4 +-\n t/t7060-wtstatus.sh                       |  14 +--\n t/t7063-status-untracked-cache.sh         |   2 +-\n t/t7102-reset.sh                          |   6 +-\n t/t7300-clean.sh                          |   6 +-\n t/t7400-submodule-basic.sh                |   4 +-\n t/t7401-submodule-summary.sh              |  12 +--\n t/t7406-submodule-update.sh               |  16 ++--\n t/t7407-submodule-foreach.sh              |  10 +-\n t/t7500-commit-template-squash-signoff.sh |   4 +-\n t/t7502-commit-porcelain.sh               |   6 +-\n t/t7505-prepare-commit-msg-hook.sh        |   4 +-\n t/t7508-status.sh                         | 110 +++++++++++-----------\n t/t7512-status-help.sh                    |  74 +++++++--------\n t/t7519-status-fsmonitor.sh               |   4 +-\n t/t7521-ignored-mode.sh                   |  22 ++---\n t/t7600-merge.sh                          |   4 +-\n t/t7602-merge-octopus-many.sh             |   6 +-\n t/t7810-grep.sh                           |   2 +-\n t/t9003-help-autocorrect.sh               |   7 +-\n t/test-lib.sh                             |   5 -\n 104 files changed, 464 insertions(+), 478 deletions(-)\n\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416692","messageId":"20210211015353.28680-3-avarab@gmail.com","threadId":"51667","inReplyTo":"20210120182759.31102-1-avarab@gmail.com","subject":"[PATCH 2/4] tests: remove most uses of C_LOCALE_OUTPUT","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-11T01:53:51Z","receivedAt":"2021-02-11T02:00:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As a follow-up to d162b25f956 (tests: remove support for\nGIT_TEST_GETTEXT_POISON, 2021-01-20) remove those uses of the now\nalways true C_LOCALE_OUTPUT prerequisite from those tests which\ndeclare it as an argument to test_expect_{success,failure}.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t0000-basic.sh                    |  2 +-\n t/t0201-gettext-fallbacks.sh        |  2 +-\n t/t1430-bad-ref-name.sh             |  2 +-\n t/t1512-rev-parse-disambiguation.sh | 14 +++++++-------\n t/t3404-rebase-interactive.sh       | 14 +++++++-------\n t/t3415-rebase-autosquash.sh        | 10 +++++-----\n t/t3701-add-interactive.sh          |  2 +-\n t/t4012-diff-binary.sh              |  4 ++--\n t/t4205-log-pretty-formats.sh       |  2 +-\n t/t5300-pack-object.sh              |  4 ++--\n t/t5505-remote.sh                   |  6 +++---\n t/t5510-fetch.sh                    |  4 ++--\n t/t5601-clone.sh                    |  2 +-\n t/t6423-merge-rename-directories.sh |  2 +-\n t/t7300-clean.sh                    |  6 +++---\n t/t7505-prepare-commit-msg-hook.sh  |  4 ++--\n t/t7810-grep.sh                     |  2 +-\n 17 files changed, 41 insertions(+), 41 deletions(-)\n\ndiff --git a/t/t0000-basic.sh b/t/t0000-basic.sh\nindex a6e570d674a..705d62cc27a 100755\n--- a/t/t0000-basic.sh\n+++ b/t/t0000-basic.sh\n@@ -271,7 +271,7 @@ test_expect_success 'pretend we have a mix of all possible results' '\n \tEOF\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'test --verbose' '\n+test_expect_success 'test --verbose' '\n \trun_sub_test_lib_test_err \\\n \t\tt1234-verbose \"test verbose\" --verbose <<-\\EOF &&\n \ttest_expect_success \"passing test\" true\ndiff --git a/t/t0201-gettext-fallbacks.sh b/t/t0201-gettext-fallbacks.sh\nindex 90da1c7ddc4..a7b3e4a2c73 100755\n--- a/t/t0201-gettext-fallbacks.sh\n+++ b/t/t0201-gettext-fallbacks.sh\n@@ -18,7 +18,7 @@ test_expect_success 'sanity: $GIT_INTERNAL_GETTEXT_TEST_FALLBACKS is set' '\n     test -n \"$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS\"\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME\" is fallthrough' '\n+test_expect_success 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME\" is fallthrough' '\n     echo fallthrough >expect &&\n     echo $GIT_INTERNAL_GETTEXT_SH_SCHEME >actual &&\n     test_cmp expect actual\ndiff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh\nindex 354902e514f..b1839e08771 100755\n--- a/t/t1430-bad-ref-name.sh\n+++ b/t/t1430-bad-ref-name.sh\n@@ -125,7 +125,7 @@ test_expect_success 'push cannot create a badly named ref' '\n \t! grep -e \"broken\\.\\.\\.ref\" output\n '\n \n-test_expect_failure C_LOCALE_OUTPUT 'push --mirror can delete badly named ref' '\n+test_expect_failure 'push --mirror can delete badly named ref' '\n \ttop=$(pwd) &&\n \tgit init src &&\n \tgit init dest &&\ndiff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh\nindex 242abbfa0bc..7891a6becf3 100755\n--- a/t/t1512-rev-parse-disambiguation.sh\n+++ b/t/t1512-rev-parse-disambiguation.sh\n@@ -314,39 +314,39 @@ test_expect_success 'ambiguous short sha1 ref' '\n \tgrep \"refname.*${REF}.*ambiguous\" err\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (raw)' '\n+test_expect_success 'ambiguity errors are not repeated (raw)' '\n \ttest_must_fail git rev-parse 00000 2>stderr &&\n \tgrep \"is ambiguous\" stderr >errors &&\n \ttest_line_count = 1 errors\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (treeish)' '\n+test_expect_success 'ambiguity errors are not repeated (treeish)' '\n \ttest_must_fail git rev-parse 00000:foo 2>stderr &&\n \tgrep \"is ambiguous\" stderr >errors &&\n \ttest_line_count = 1 errors\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (peel)' '\n+test_expect_success 'ambiguity errors are not repeated (peel)' '\n \ttest_must_fail git rev-parse 00000^{commit} 2>stderr &&\n \tgrep \"is ambiguous\" stderr >errors &&\n \ttest_line_count = 1 errors\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'ambiguity hints' '\n+test_expect_success 'ambiguity hints' '\n \ttest_must_fail git rev-parse 000000000 2>stderr &&\n \tgrep ^hint: stderr >hints &&\n \t# 16 candidates, plus one intro line\n \ttest_line_count = 17 hints\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'ambiguity hints respect type' '\n+test_expect_success 'ambiguity hints respect type' '\n \ttest_must_fail git rev-parse 000000000^{commit} 2>stderr &&\n \tgrep ^hint: stderr >hints &&\n \t# 5 commits, 1 tag (which is a committish), plus intro line\n \ttest_line_count = 7 hints\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'failed type-selector still shows hint' '\n+test_expect_success 'failed type-selector still shows hint' '\n \t# these two blobs share the same prefix \"ee3d\", but neither\n \t# will pass for a commit\n \techo 851 | git hash-object --stdin -w &&\n@@ -370,7 +370,7 @@ test_expect_success 'core.disambiguate does not override context' '\n \t\tgit -c core.disambiguate=committish rev-parse $sha1^{tree}\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'ambiguous commits are printed by type first, then hash order' '\n+test_expect_success 'ambiguous commits are printed by type first, then hash order' '\n \ttest_must_fail git rev-parse 0000 2>stderr &&\n \tgrep ^hint: stderr >hints &&\n \tgrep 0000 hints >objects &&\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 1e738df81d5..dc45552b4d6 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -450,7 +450,7 @@ test_expect_success 'verbose flag is heeded, even after --continue' '\n \tgrep \"^ file1 | 2 +-$\" output\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'multi-squash only fires up editor once' '\n+test_expect_success 'multi-squash only fires up editor once' '\n \tbase=$(git rev-parse HEAD~4) &&\n \t(\n \t\tset_fake_editor &&\n@@ -463,7 +463,7 @@ test_expect_success C_LOCALE_OUTPUT 'multi-squash only fires up editor once' '\n \ttest 1 = $(git show | grep ONCE | wc -l)\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'multi-fixup does not fire up editor' '\n+test_expect_success 'multi-fixup does not fire up editor' '\n \tgit checkout -b multi-fixup E &&\n \tbase=$(git rev-parse HEAD~4) &&\n \t(\n@@ -514,7 +514,7 @@ test_expect_success 'commit message retained after conflict' '\n \tgit branch -D conflict-squash\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messages' '\n+test_expect_success 'squash and fixup generate correct log messages' '\n \tcat >expect-squash-fixup <<-\\EOF &&\n \tB\n \n@@ -541,7 +541,7 @@ test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messa\n \tgit branch -D squash-fixup\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'squash ignores comments' '\n+test_expect_success 'squash ignores comments' '\n \tgit checkout -b skip-comments E &&\n \tbase=$(git rev-parse HEAD~4) &&\n \t(\n@@ -557,7 +557,7 @@ test_expect_success C_LOCALE_OUTPUT 'squash ignores comments' '\n \tgit branch -D skip-comments\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'squash ignores blank lines' '\n+test_expect_success 'squash ignores blank lines' '\n \tgit checkout -b skip-blank-lines E &&\n \tbase=$(git rev-parse HEAD~4) &&\n \t(\n@@ -995,7 +995,7 @@ test_expect_success 'rebase -ix with several instances of --exec' '\n \ttest_cmp expected actual\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'rebase -ix with --autosquash' '\n+test_expect_success 'rebase -ix with --autosquash' '\n \tgit reset --hard execute &&\n \tgit checkout -b autosquash &&\n \techo second >second.txt &&\n@@ -1136,7 +1136,7 @@ test_expect_success 'rebase -i --root reword root when root has untracked file c\n \ttest \"$(git rev-list --count HEAD)\" = 2\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-interactive rebase' '\n+test_expect_success 'rebase --edit-todo does not work on non-interactive rebase' '\n \tgit checkout reword-original-root-branch &&\n \tgit reset --hard &&\n \tgit checkout conflict-branch &&\ndiff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh\nindex 36f169d7f15..908016c2f88 100755\n--- a/t/t3415-rebase-autosquash.sh\n+++ b/t/t3415-rebase-autosquash.sh\n@@ -306,23 +306,23 @@ test_auto_fixup_fixup () {\n \tfi\n }\n \n-test_expect_success C_LOCALE_OUTPUT 'fixup! fixup!' '\n+test_expect_success 'fixup! fixup!' '\n \ttest_auto_fixup_fixup fixup fixup\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'fixup! squash!' '\n+test_expect_success 'fixup! squash!' '\n \ttest_auto_fixup_fixup fixup squash\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'squash! squash!' '\n+test_expect_success 'squash! squash!' '\n \ttest_auto_fixup_fixup squash squash\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'squash! fixup!' '\n+test_expect_success 'squash! fixup!' '\n \ttest_auto_fixup_fixup squash fixup\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'autosquash with custom inst format' '\n+test_expect_success 'autosquash with custom inst format' '\n \tgit reset --hard base &&\n \tgit config --add rebase.instructionFormat \"[%an @ %ar] %s\"  &&\n \techo 2 >file1 &&\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex b2f90997dbc..ed00c40370b 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -370,7 +370,7 @@ test_expect_success 'setup expected' '\n '\n \n # Test splitting the first patch, then adding both\n-test_expect_success C_LOCALE_OUTPUT 'add first line works' '\n+test_expect_success 'add first line works' '\n \tgit commit -am \"clear local changes\" &&\n \tgit apply patch &&\n \tprintf \"%s\\n\" s y y | git add -p file 2>error |\ndiff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh\nindex 6579c81216a..bd59328e4b2 100755\n--- a/t/t4012-diff-binary.sh\n+++ b/t/t4012-diff-binary.sh\n@@ -63,7 +63,7 @@ test_expect_success 'apply --numstat understands diff --binary format' '\n \n # apply needs to be able to skip the binary material correctly\n # in order to report the line number of a corrupt patch.\n-test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '\n+test_expect_success 'apply detecting corrupt patch correctly' '\n \tgit diff >output &&\n \tsed -e \"s/-CIT/xCIT/\" <output >broken &&\n \ttest_must_fail git apply --stat --summary broken 2>detected &&\n@@ -73,7 +73,7 @@ test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '\n \ttest \"$detected\" = xCIT\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '\n+test_expect_success 'apply detecting corrupt patch correctly' '\n \tgit diff --binary | sed -e \"s/-CIT/xCIT/\" >broken &&\n \ttest_must_fail git apply --stat --summary broken 2>detected &&\n \tdetected=$(cat detected) &&\ndiff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh\nindex 749bc1431ac..5ec0f47b567 100755\n--- a/t/t4205-log-pretty-formats.sh\n+++ b/t/t4205-log-pretty-formats.sh\n@@ -126,7 +126,7 @@ test_expect_success 'NUL separation with --stat' '\n \ttest_i18ncmp expected actual\n '\n \n-test_expect_failure C_LOCALE_OUTPUT 'NUL termination with --stat' '\n+test_expect_failure 'NUL termination with --stat' '\n \tstat0_part=$(git diff --stat HEAD^ HEAD) &&\n \tstat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&\n \tprintf \"add bar\\n$stat0_part\\n\\0$(commit_msg)\\n$stat1_part\\n0\" >expected &&\ndiff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh\nindex 392201cabdf..d586fdc7a95 100755\n--- a/t/t5300-pack-object.sh\n+++ b/t/t5300-pack-object.sh\n@@ -427,7 +427,7 @@ test_expect_success 'index-pack --strict <pack> works in non-repo' '\n \ttest_path_is_file foo.idx\n '\n \n-test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'index-pack --threads=N or pack.threads=N warns when no pthreads' '\n+test_expect_success !PTHREADS 'index-pack --threads=N or pack.threads=N warns when no pthreads' '\n \ttest_must_fail git index-pack --threads=2 2>err &&\n \tgrep ^warning: err >warnings &&\n \ttest_line_count = 1 warnings &&\n@@ -445,7 +445,7 @@ test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'index-pack --threads=N or pack.th\n \tgrep -F \"no threads support, ignoring pack.threads\" err\n '\n \n-test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'pack-objects --threads=N or pack.threads=N warns when no pthreads' '\n+test_expect_success !PTHREADS 'pack-objects --threads=N or pack.threads=N warns when no pthreads' '\n \tgit pack-objects --threads=2 --stdout --all </dev/null >/dev/null 2>err &&\n \tgrep ^warning: err >warnings &&\n \ttest_line_count = 1 warnings &&\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex 045398b94e6..d3a3bb0a42b 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -59,7 +59,7 @@ test_expect_success 'add remote whose URL agrees with url.<...>.insteadOf' '\n \tgit remote add myremote git@host.com:team/repo.git\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' '\n+test_expect_success 'remote information for the origin' '\n \t(\n \t\tcd test &&\n \t\ttokens_match origin \"$(git remote)\" &&\n@@ -81,7 +81,7 @@ test_expect_success 'add another remote' '\n \t)\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'check remote-tracking' '\n+test_expect_success 'check remote-tracking' '\n \t(\n \t\tcd test &&\n \t\tcheck_remote_track origin main side &&\n@@ -107,7 +107,7 @@ test_expect_success 'remove remote' '\n \t)\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'remove remote' '\n+test_expect_success 'remove remote' '\n \t(\n \t\tcd test &&\n \t\ttokens_match origin \"$(git remote)\" &&\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 42f55030047..a37df6d35a7 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -1097,7 +1097,7 @@ test_expect_success 'fetching with auto-gc does not lock up' '\n \t)\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'fetch aligned output' '\n+test_expect_success 'fetch aligned output' '\n \tgit clone . full-output &&\n \ttest_commit looooooooooooong-tag &&\n \t(\n@@ -1112,7 +1112,7 @@ test_expect_success C_LOCALE_OUTPUT 'fetch aligned output' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'fetch compact output' '\n+test_expect_success 'fetch compact output' '\n \tgit clone . compact &&\n \ttest_commit extraaa &&\n \t(\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex 664c9138662..e7e6c089554 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -40,7 +40,7 @@ test_expect_success 'clone with excess parameters (2)' '\n \n '\n \n-test_expect_success C_LOCALE_OUTPUT 'output from clone' '\n+test_expect_success 'output from clone' '\n \trm -fr dst &&\n \tgit clone -n \"file://$(pwd)/src\" dst >output 2>&1 &&\n \ttest $(grep Clon output | wc -l) = 1\ndiff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh\nindex 4ab133f489c..5d3b711fe68 100755\n--- a/t/t6423-merge-rename-directories.sh\n+++ b/t/t6423-merge-rename-directories.sh\n@@ -2905,7 +2905,7 @@ test_setup_9e () {\n \t)\n }\n \n-test_expect_success C_LOCALE_OUTPUT '9e: N-to-1 whammo' '\n+test_expect_success '9e: N-to-1 whammo' '\n \ttest_setup_9e &&\n \t(\n \t\tcd 9e &&\ndiff --git a/t/t7300-clean.sh b/t/t7300-clean.sh\nindex cb5e34d94c3..a74816ca8b4 100755\n--- a/t/t7300-clean.sh\n+++ b/t/t7300-clean.sh\n@@ -110,7 +110,7 @@ test_expect_success 'git clean with prefix' '\n \n '\n \n-test_expect_success C_LOCALE_OUTPUT 'git clean with relative prefix' '\n+test_expect_success 'git clean with relative prefix' '\n \n \tmkdir -p build docs &&\n \ttouch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&\n@@ -123,7 +123,7 @@ test_expect_success C_LOCALE_OUTPUT 'git clean with relative prefix' '\n \tverbose test \"$would_clean\" = ../src/part3.c\n '\n \n-test_expect_success C_LOCALE_OUTPUT 'git clean with absolute path' '\n+test_expect_success 'git clean with absolute path' '\n \n \tmkdir -p build docs &&\n \ttouch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&\n@@ -407,7 +407,7 @@ test_expect_success 'clean.requireForce and -f' '\n \n '\n \n-test_expect_success C_LOCALE_OUTPUT 'core.excludesfile' '\n+test_expect_success 'core.excludesfile' '\n \n \techo excludes >excludes &&\n \techo included >included &&\ndiff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh\nindex 321b4bc0fc6..7a8194ce720 100755\n--- a/t/t7505-prepare-commit-msg-hook.sh\n+++ b/t/t7505-prepare-commit-msg-hook.sh\n@@ -218,7 +218,7 @@ test_expect_success 'with hook and editor (merge)' '\n test_rebase () {\n \texpect=$1 &&\n \tmode=$2 &&\n-\ttest_expect_$expect C_LOCALE_OUTPUT \"with hook (rebase ${mode:--i})\" '\n+\ttest_expect_$expect \"with hook (rebase ${mode:--i})\" '\n \t\ttest_when_finished \"\\\n \t\t\tgit rebase --abort\n \t\t\tgit checkout -f main\n@@ -307,7 +307,7 @@ test_expect_success 'with failing hook (merge)' '\n \n '\n \n-test_expect_success C_LOCALE_OUTPUT 'with failing hook (cherry-pick)' '\n+test_expect_success 'with failing hook (cherry-pick)' '\n \ttest_when_finished \"git checkout -f main\" &&\n \tgit checkout -B other b &&\n \ttest_must_fail git cherry-pick rebase-1 2>actual &&\ndiff --git a/t/t7810-grep.sh b/t/t7810-grep.sh\nindex 8f7591c9ccd..edfaa9a6d1c 100755\n--- a/t/t7810-grep.sh\n+++ b/t/t7810-grep.sh\n@@ -969,7 +969,7 @@ do\n \t\"\n done\n \n-test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'grep --threads=N or pack.threads=N warns when no pthreads' '\n+test_expect_success !PTHREADS 'grep --threads=N or pack.threads=N warns when no pthreads' '\n \tgit grep --threads=2 Hello hello_world 2>err &&\n \tgrep ^warning: err >warnings &&\n \ttest_line_count = 1 warnings &&\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416693","messageId":"20210211015353.28680-5-avarab@gmail.com","threadId":"51667","inReplyTo":"20210120182759.31102-1-avarab@gmail.com","subject":"[PATCH 4/4] tests: remove most uses of test_i18ncmp","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-11T01:53:53Z","receivedAt":"2021-02-11T02:00:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As a follow-up to d162b25f956 (tests: remove support for\nGIT_TEST_GETTEXT_POISON, 2021-01-20) remove most uses of test_i18ncmp\nvia a simple s/test_i18ncmp/test_cmp/g search-replacement.\n\nI'm leaving t6300-for-each-ref.sh out due to a conflict with in-flight\nchanges between \"master\" and \"seen\", as well as the prerequisite\nitself due to other changes between \"master\" and \"next/seen\" which add\nnew test_i18ncmp uses.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/lib-credential.sh                       |   2 +-\n t/lib-log-graph.sh                        |   4 +-\n t/t0006-date.sh                           |   4 +-\n t/t0008-ignores.sh                        |   2 +-\n t/t0012-help.sh                           |   2 +-\n t/t0018-advice.sh                         |   4 +-\n t/t0027-auto-crlf.sh                      |   2 +-\n t/t0040-parse-options.sh                  |   8 +-\n t/t0201-gettext-fallbacks.sh              |  14 +--\n t/t0300-credentials.sh                    |   8 +-\n t/t0500-progress-display.sh               |  22 ++---\n t/t1011-read-tree-sparse-checkout.sh      |   2 +-\n t/t1308-config-set.sh                     |   4 +-\n t/t1400-update-ref.sh                     |   6 +-\n t/t1404-update-ref-errors.sh              |   2 +-\n t/t1450-fsck.sh                           |   6 +-\n t/t1502-rev-parse-parseopt.sh             |  10 +-\n t/t1506-rev-parse-diagnosis.sh            |   2 +-\n t/t1507-rev-parse-upstream.sh             |  10 +-\n t/t1509-root-work-tree.sh                 |   4 +-\n t/t1600-index.sh                          |   6 +-\n t/t2018-checkout-branch.sh                |   2 +-\n t/t2020-checkout-detach.sh                |  20 ++--\n t/t2200-add-update.sh                     |   2 +-\n t/t2401-worktree-prune.sh                 |   4 +-\n t/t2402-worktree-list.sh                  |   2 +-\n t/t3005-ls-files-relative.sh              |   4 +-\n t/t3200-branch.sh                         |  26 ++---\n t/t3201-branch-contains.sh                |   2 +-\n t/t3203-branch-output.sh                  |  28 +++---\n t/t3300-funny-names.sh                    |   2 +-\n t/t3400-rebase.sh                         |   2 +-\n t/t3404-rebase-interactive.sh             |  24 ++---\n t/t3420-rebase-autostash.sh               |   4 +-\n t/t3504-cherry-pick-rerere.sh             |   4 +-\n t/t3507-cherry-pick-conflict.sh           |  12 +--\n t/t3508-cherry-pick-many-commits.sh       |   4 +-\n t/t3510-cherry-pick-sequence.sh           |   8 +-\n t/t3600-rm.sh                             |  16 ++--\n t/t3700-add.sh                            |   8 +-\n t/t3701-add-interactive.sh                |   2 +-\n t/t3903-stash.sh                          |   8 +-\n t/t3905-stash-include-untracked.sh        |   2 +-\n t/t4006-diff-mode.sh                      |   8 +-\n t/t4012-diff-binary.sh                    |   6 +-\n t/t4013-diff-various.sh                   |   2 +-\n t/t4014-format-patch.sh                   |   6 +-\n t/t4016-diff-quote.sh                     |   2 +-\n t/t4030-diff-textconv.sh                  |   2 +-\n t/t4045-diff-relative.sh                  |   2 +-\n t/t4049-diff-stat-count.sh                |   6 +-\n t/t4100-apply-stat.sh                     |   4 +-\n t/t4150-am.sh                             |   2 +-\n t/t4153-am-resume-override-opts.sh        |   2 +-\n t/t4205-log-pretty-formats.sh             |   2 +-\n t/t4254-am-corrupt.sh                     |   2 +-\n t/t5150-request-pull.sh                   |   4 +-\n t/t5316-pack-delta-depth.sh               |   4 +-\n t/t5500-fetch-pack.sh                     |   6 +-\n t/t5505-remote.sh                         |  24 ++---\n t/t5510-fetch.sh                          |   2 +-\n t/t5512-ls-remote.sh                      |   2 +-\n t/t5526-fetch-submodules.sh               |  38 ++++----\n t/t5541-http-push-smart.sh                |   2 +-\n t/t6020-bundle-misc.sh                    |  28 +++---\n t/t6030-bisect-porcelain.sh               |   4 +-\n t/t6040-tracking-info.sh                  |  22 ++---\n t/t6120-describe.sh                       |   2 +-\n t/t6134-pathspec-in-submodule.sh          |   2 +-\n t/t6301-for-each-ref-errors.sh            |  10 +-\n t/t6436-merge-overwrite.sh                |   4 +-\n t/t6439-merge-co-error-msgs.sh            |  12 +--\n t/t7001-mv.sh                             |   2 +-\n t/t7004-tag.sh                            |   8 +-\n t/t7012-skip-worktree-writing.sh          |   4 +-\n t/t7060-wtstatus.sh                       |  14 +--\n t/t7063-status-untracked-cache.sh         |   2 +-\n t/t7102-reset.sh                          |   6 +-\n t/t7400-submodule-basic.sh                |   4 +-\n t/t7401-submodule-summary.sh              |  12 +--\n t/t7406-submodule-update.sh               |  16 ++--\n t/t7407-submodule-foreach.sh              |  10 +-\n t/t7500-commit-template-squash-signoff.sh |   4 +-\n t/t7502-commit-porcelain.sh               |   6 +-\n t/t7508-status.sh                         | 110 +++++++++++-----------\n t/t7512-status-help.sh                    |  74 +++++++--------\n t/t7519-status-fsmonitor.sh               |   4 +-\n t/t7521-ignored-mode.sh                   |  22 ++---\n t/t7600-merge.sh                          |   4 +-\n t/t7602-merge-octopus-many.sh             |   6 +-\n 90 files changed, 415 insertions(+), 415 deletions(-)\n\ndiff --git a/t/lib-credential.sh b/t/lib-credential.sh\nindex dea2cbef517..5ea8bc9f1dc 100644\n--- a/t/lib-credential.sh\n+++ b/t/lib-credential.sh\n@@ -20,7 +20,7 @@ check() {\n \t\tfalse\n \tfi &&\n \ttest_cmp expect-stdout stdout &&\n-\ttest_i18ncmp expect-stderr stderr\n+\ttest_cmp expect-stderr stderr\n }\n \n read_chunk() {\ndiff --git a/t/lib-log-graph.sh b/t/lib-log-graph.sh\nindex 1184cceef26..bf952ef9204 100644\n--- a/t/lib-log-graph.sh\n+++ b/t/lib-log-graph.sh\n@@ -12,13 +12,13 @@ sanitize_log_output () {\n lib_test_cmp_graph () {\n \tgit log --graph \"$@\" >output &&\n \tsed 's/ *$//' >output.sanitized <output &&\n-\ttest_i18ncmp expect output.sanitized\n+\ttest_cmp expect output.sanitized\n }\n \n lib_test_cmp_short_graph () {\n \tgit log --graph --pretty=short \"$@\" >output &&\n \tsanitize_log_output >output.sanitized <output &&\n-\ttest_i18ncmp expect output.sanitized\n+\ttest_cmp expect output.sanitized\n }\n \n lib_test_cmp_colored_graph () {\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 75ee9a96b80..6b757d71692 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -11,7 +11,7 @@ check_relative() {\n \techo \"$t -> $2\" >expect\n \ttest_expect_${3:-success} \"relative date ($2)\" \"\n \ttest-tool date relative $t >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n \t\"\n }\n \n@@ -139,7 +139,7 @@ check_date_format_human() {\n \techo \"$t -> $2\" >expect\n \ttest_expect_success \"human date $t\" '\n \t\ttest-tool date human $t >actual &&\n-\t\ttest_i18ncmp expect actual\n+\t\ttest_cmp expect actual\n '\n }\n \ndiff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh\nindex 370a389e5c5..f7abde62f63 100755\n--- a/t/t0008-ignores.sh\n+++ b/t/t0008-ignores.sh\n@@ -34,7 +34,7 @@ expect_from_stdin () {\n test_stderr () {\n \texpected=\"$1\"\n \texpect_in stderr \"$1\" &&\n-\ttest_i18ncmp \"$HOME/expected-stderr\" \"$HOME/stderr\"\n+\ttest_cmp \"$HOME/expected-stderr\" \"$HOME/stderr\"\n }\n \n broken_c_unquote () {\ndiff --git a/t/t0012-help.sh b/t/t0012-help.sh\nindex e8ef7300ecb..5679e29c624 100755\n--- a/t/t0012-help.sh\n+++ b/t/t0012-help.sh\n@@ -55,7 +55,7 @@ test_expect_success \"--help does not work for guides\" \"\n \t\tgit: 'revisions' is not a git command. See 'git --help'.\n \tEOF\n \ttest_must_fail git revisions --help 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n \"\n \n test_expect_success 'git help' '\ndiff --git a/t/t0018-advice.sh b/t/t0018-advice.sh\nindex e03554d2f34..39e5e4b34f8 100755\n--- a/t/t0018-advice.sh\n+++ b/t/t0018-advice.sh\n@@ -10,7 +10,7 @@ test_expect_success 'advice should be printed when config variable is unset' '\n \thint: Disable this message with \"git config advice.nestedTag false\"\n \tEOF\n \ttest-tool advise \"This is a piece of advice\" 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'advice should be printed when config variable is set to true' '\n@@ -20,7 +20,7 @@ test_expect_success 'advice should be printed when config variable is set to tru\n \tEOF\n \ttest_config advice.nestedTag true &&\n \ttest-tool advise \"This is a piece of advice\" 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'advice should not be printed when config variable is set to false' '\ndiff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh\nindex 51f74a3ddf3..d24d5acfbc7 100755\n--- a/t/t0027-auto-crlf.sh\n+++ b/t/t0027-auto-crlf.sh\n@@ -83,7 +83,7 @@ check_warning () {\n \t*) echo >&2 \"Illegal 1\": \"$1\" ; return false ;;\n \tesac\n \tgrep \"will be replaced by\" \"$2\" | sed -e \"s/\\(.*\\) in [^ ]*$/\\1/\" | uniq  >\"$2\".actual\n-\ttest_i18ncmp \"$2\".expect \"$2\".actual\n+\ttest_cmp \"$2\".expect \"$2\".actual\n }\n \n commit_check_warn () {\ndiff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh\nindex 14cafc138b7..ad4746d899a 100755\n--- a/t/t0040-parse-options.sh\n+++ b/t/t0040-parse-options.sh\n@@ -61,7 +61,7 @@ EOF\n test_expect_success 'test help' '\n \ttest_must_fail test-tool parse-options -h >output 2>output.err &&\n \ttest_must_be_empty output.err &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n mv expect expect.err\n@@ -84,7 +84,7 @@ check_unknown_i18n() {\n \tcat expect.err >>expect &&\n \ttest_must_fail test-tool parse-options $* >output 2>output.err &&\n \ttest_must_be_empty output &&\n-\ttest_i18ncmp expect output.err\n+\ttest_cmp expect output.err\n }\n \n test_expect_success 'OPT_BOOL() #1' 'check boolean: 1 --yes'\n@@ -250,7 +250,7 @@ EOF\n test_expect_success 'detect possible typos' '\n \ttest_must_fail test-tool parse-options -boolean >output 2>output.err &&\n \ttest_must_be_empty output &&\n-\ttest_i18ncmp typo.err output.err\n+\ttest_cmp typo.err output.err\n '\n \n cat >typo.err <<\\EOF\n@@ -260,7 +260,7 @@ EOF\n test_expect_success 'detect possible typos' '\n \ttest_must_fail test-tool parse-options -ambiguous >output 2>output.err &&\n \ttest_must_be_empty output &&\n-\ttest_i18ncmp typo.err output.err\n+\ttest_cmp typo.err output.err\n '\n \n test_expect_success 'keep some options as arguments' '\ndiff --git a/t/t0201-gettext-fallbacks.sh b/t/t0201-gettext-fallbacks.sh\nindex a7b3e4a2c73..6c74df0dc67 100755\n--- a/t/t0201-gettext-fallbacks.sh\n+++ b/t/t0201-gettext-fallbacks.sh\n@@ -27,25 +27,25 @@ test_expect_success 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME\" is fallthrough' '\n test_expect_success 'gettext: our gettext() fallback has pass-through semantics' '\n     printf \"test\" >expect &&\n     gettext \"test\" >actual &&\n-    test_i18ncmp expect actual &&\n+    test_cmp expect actual &&\n     printf \"test more words\" >expect &&\n     gettext \"test more words\" >actual &&\n-    test_i18ncmp expect actual\n+    test_cmp expect actual\n '\n \n test_expect_success 'eval_gettext: our eval_gettext() fallback has pass-through semantics' '\n     printf \"test\" >expect &&\n     eval_gettext \"test\" >actual &&\n-    test_i18ncmp expect actual &&\n+    test_cmp expect actual &&\n     printf \"test more words\" >expect &&\n     eval_gettext \"test more words\" >actual &&\n-    test_i18ncmp expect actual\n+    test_cmp expect actual\n '\n \n test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables' '\n     printf \"test YesPlease\" >expect &&\n     GIT_INTERNAL_GETTEXT_TEST_FALLBACKS=YesPlease eval_gettext \"test \\$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS\" >actual &&\n-    test_i18ncmp expect actual\n+    test_cmp expect actual\n '\n \n test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces' '\n@@ -53,7 +53,7 @@ test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate v\n     export cmdline &&\n     printf \"When you have resolved this problem, run git am --resolved.\" >expect &&\n     eval_gettext \"When you have resolved this problem, run \\$cmdline --resolved.\" >actual &&\n-    test_i18ncmp expect actual\n+    test_cmp expect actual\n '\n \n test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces and quotes' '\n@@ -61,7 +61,7 @@ test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate v\n     export cmdline &&\n     printf \"When you have resolved this problem, run \\\"git am --resolved\\\".\" >expect &&\n     eval_gettext \"When you have resolved this problem, run \\\"\\$cmdline --resolved\\\".\" >actual &&\n-    test_i18ncmp expect actual\n+    test_cmp expect actual\n '\n \n test_done\ndiff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh\nindex a18f8a473bf..3485c0534e6 100755\n--- a/t/t0300-credentials.sh\n+++ b/t/t0300-credentials.sh\n@@ -578,7 +578,7 @@ test_expect_success 'helpers can abort the process' '\n \tquit: host=example.com\n \tfatal: credential helper '\\''quit'\\'' told us to quit\n \tEOF\n-\ttest_i18ncmp expect stderr\n+\ttest_cmp expect stderr\n '\n \n test_expect_success 'empty helper spec resets helper list' '\n@@ -606,7 +606,7 @@ test_expect_success 'url parser rejects embedded newlines' '\n \twarning: url contains a newline in its path component: https://one.example.com?%0ahost=two.example.com/\n \tfatal: credential url cannot be parsed: https://one.example.com?%0ahost=two.example.com/\n \tEOF\n-\ttest_i18ncmp expect stderr\n+\ttest_cmp expect stderr\n '\n \n test_expect_success 'host-less URLs are parsed as empty host' '\n@@ -633,7 +633,7 @@ test_expect_success 'credential system refuses to work with missing host' '\n \tcat >expect <<-\\EOF &&\n \tfatal: refusing to work with credential missing host field\n \tEOF\n-\ttest_i18ncmp expect stderr\n+\ttest_cmp expect stderr\n '\n \n test_expect_success 'credential system refuses to work with missing protocol' '\n@@ -643,7 +643,7 @@ test_expect_success 'credential system refuses to work with missing protocol' '\n \tcat >expect <<-\\EOF &&\n \tfatal: refusing to work with credential missing protocol field\n \tEOF\n-\ttest_i18ncmp expect stderr\n+\ttest_cmp expect stderr\n '\n \n # usage: check_host_and_path <url> <expected-host> <expected-path>\ndiff --git a/t/t0500-progress-display.sh b/t/t0500-progress-display.sh\nindex 84cce345e7d..22058b503ac 100755\n--- a/t/t0500-progress-display.sh\n+++ b/t/t0500-progress-display.sh\n@@ -29,7 +29,7 @@ test_expect_success 'simple progress display' '\n \ttest-tool progress \"Working hard\" <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n-\ttest_i18ncmp expect out\n+\ttest_cmp expect out\n '\n \n test_expect_success 'progress display with total' '\n@@ -48,7 +48,7 @@ test_expect_success 'progress display with total' '\n \ttest-tool progress --total=3 \"Working hard\" <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n-\ttest_i18ncmp expect out\n+\ttest_cmp expect out\n '\n \n test_expect_success 'progress display breaks long lines #1' '\n@@ -72,7 +72,7 @@ EOF\n \t\t<in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n-\ttest_i18ncmp expect out\n+\ttest_cmp expect out\n '\n \n test_expect_success 'progress display breaks long lines #2' '\n@@ -100,7 +100,7 @@ EOF\n \t\t<in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n-\ttest_i18ncmp expect out\n+\ttest_cmp expect out\n '\n \n test_expect_success 'progress display breaks long lines #3 - even the first is too long' '\n@@ -126,7 +126,7 @@ EOF\n \t\t<in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n-\ttest_i18ncmp expect out\n+\ttest_cmp expect out\n '\n \n test_expect_success 'progress display breaks long lines #4 - title line matches terminal width' '\n@@ -150,7 +150,7 @@ EOF\n \t\t<in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n-\ttest_i18ncmp expect out\n+\ttest_cmp expect out\n '\n \n # Progress counter goes backwards, this should not happen in practice.\n@@ -172,7 +172,7 @@ test_expect_success 'progress shortens - crazy caller' '\n \ttest-tool progress --total=1000 \"Working hard\" <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n-\ttest_i18ncmp expect out\n+\ttest_cmp expect out\n '\n \n test_expect_success 'progress display with throughput' '\n@@ -201,7 +201,7 @@ test_expect_success 'progress display with throughput' '\n \ttest-tool progress \"Working hard\" <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n-\ttest_i18ncmp expect out\n+\ttest_cmp expect out\n '\n \n test_expect_success 'progress display with throughput and total' '\n@@ -226,7 +226,7 @@ test_expect_success 'progress display with throughput and total' '\n \ttest-tool progress --total=40 \"Working hard\" <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n-\ttest_i18ncmp expect out\n+\ttest_cmp expect out\n '\n \n test_expect_success 'cover up after throughput shortens' '\n@@ -255,7 +255,7 @@ test_expect_success 'cover up after throughput shortens' '\n \ttest-tool progress \"Working hard\" <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n-\ttest_i18ncmp expect out\n+\ttest_cmp expect out\n '\n \n test_expect_success 'cover up after throughput shortens a lot' '\n@@ -280,7 +280,7 @@ test_expect_success 'cover up after throughput shortens a lot' '\n \ttest-tool progress \"Working hard\" <in 2>stderr &&\n \n \tshow_cr <stderr >out &&\n-\ttest_i18ncmp expect out\n+\ttest_cmp expect out\n '\n \n test_expect_success 'progress generates traces' '\ndiff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh\nindex dfe9794a740..24092c09a95 100755\n--- a/t/t1011-read-tree-sparse-checkout.sh\n+++ b/t/t1011-read-tree-sparse-checkout.sh\n@@ -253,7 +253,7 @@ warning: The following paths were already present and thus not updated despite s\n \n After fixing the above paths, you may want to run `git sparse-checkout reapply`.\n EOF\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'checkout without --ignore-skip-worktree-bits' '\ndiff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh\nindex 3a527e3a843..88b119a0a35 100755\n--- a/t/t1308-config-set.sh\n+++ b/t/t1308-config-set.sh\n@@ -208,14 +208,14 @@ test_expect_success 'proper error on error in default config files' '\n \techo \"[\" >>.git/config &&\n \techo \"fatal: bad config line 34 in file .git/config\" >expect &&\n \ttest_expect_code 128 test-tool config get_value foo.bar 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'proper error on error in custom config files' '\n \techo \"[\" >>syntax-error &&\n \techo \"fatal: bad config line 1 in file syntax-error\" >expect &&\n \ttest_expect_code 128 test-tool config configset_get_value foo.bar syntax-error 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'check line errors for malformed values' '\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex 359d8731c8f..e31f65f381c 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -374,7 +374,7 @@ test_expect_success 'Query \"main@{May 25 2005}\" (before history)' '\n \techo \"$C\" >expect &&\n \ttest_cmp expect o &&\n \techo \"warning: log for '\\''main'\\'' only goes back to $ed\" >expect &&\n-\ttest_i18ncmp expect e\n+\ttest_cmp expect e\n '\n test_expect_success 'Query main@{2005-05-25} (before history)' '\n \ttest_when_finished \"rm -f o e\" &&\n@@ -382,7 +382,7 @@ test_expect_success 'Query main@{2005-05-25} (before history)' '\n \techo \"$C\" >expect &&\n \ttest_cmp expect o &&\n \techo \"warning: log for '\\''main'\\'' only goes back to $ed\" >expect &&\n-\ttest_i18ncmp expect e\n+\ttest_cmp expect e\n '\n test_expect_success 'Query \"main@{May 26 2005 23:31:59}\" (1 second before history)' '\n \ttest_when_finished \"rm -f o e\" &&\n@@ -390,7 +390,7 @@ test_expect_success 'Query \"main@{May 26 2005 23:31:59}\" (1 second before histor\n \techo \"$C\" >expect &&\n \ttest_cmp expect o &&\n \techo \"warning: log for '\\''main'\\'' only goes back to $ed\" >expect &&\n-\ttest_i18ncmp expect e\n+\ttest_cmp expect e\n '\n test_expect_success 'Query \"main@{May 26 2005 23:32:00}\" (exactly history start)' '\n \ttest_when_finished \"rm -f o e\" &&\ndiff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh\nindex 2d142e5535e..8b51c4efc13 100755\n--- a/t/t1404-update-ref-errors.sh\n+++ b/t/t1404-update-ref-errors.sh\n@@ -101,7 +101,7 @@ df_test() {\n \t\tprintf \"%s\\n\" \"delete $delname\" \"create $addname $D\"\n \tfi >commands &&\n \ttest_must_fail git update-ref --stdin <commands 2>output.err &&\n-\ttest_i18ncmp expected-err output.err &&\n+\ttest_cmp expected-err output.err &&\n \tprintf \"%s\\n\" \"$C $delref\" >expected-refs &&\n \tgit for-each-ref --format=\"%(objectname) %(refname)\" $prefix/r >actual-refs &&\n \ttest_cmp expected-refs actual-refs\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex a30fc5f74a9..f3241e0e7e2 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -380,7 +380,7 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n \twarning in tag $tag: badTagName: invalid '\\''tag'\\'' name: wrong name format\n \twarning in tag $tag: missingTaggerEntry: invalid format - expected '\\''tagger'\\'' line\n \tEOF\n-\ttest_i18ncmp expect out\n+\ttest_cmp expect out\n '\n \n test_expect_success 'tag with bad tagger' '\n@@ -806,7 +806,7 @@ test_expect_success 'fsck notices dangling objects' '\n \t\tgit fsck >actual &&\n \t\t# the output order is non-deterministic, as it comes from a hash\n \t\tsort <actual >actual.sorted &&\n-\t\ttest_i18ncmp expect actual.sorted\n+\t\ttest_cmp expect actual.sorted\n \t)\n '\n \n@@ -816,7 +816,7 @@ test_expect_success 'fsck --connectivity-only notices dangling objects' '\n \t\tgit fsck --connectivity-only >actual &&\n \t\t# the output order is non-deterministic, as it comes from a hash\n \t\tsort <actual >actual.sorted &&\n-\t\ttest_i18ncmp expect actual.sorted\n+\t\ttest_cmp expect actual.sorted\n \t)\n '\n \ndiff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh\nindex a859abedf58..b29563fc997 100755\n--- a/t/t1502-rev-parse-parseopt.sh\n+++ b/t/t1502-rev-parse-parseopt.sh\n@@ -95,7 +95,7 @@ test_expect_success 'test --parseopt help output' '\n |EOF\n END_EXPECT\n \ttest_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'test --parseopt help output no switches' '\n@@ -108,7 +108,7 @@ test_expect_success 'test --parseopt help output no switches' '\n |EOF\n END_EXPECT\n \ttest_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_no_switches &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'test --parseopt help output hidden switches' '\n@@ -121,7 +121,7 @@ test_expect_success 'test --parseopt help output hidden switches' '\n |EOF\n END_EXPECT\n \ttest_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_only_hidden_switches &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'test --parseopt help-all output hidden switches' '\n@@ -136,7 +136,7 @@ test_expect_success 'test --parseopt help-all output hidden switches' '\n |EOF\n END_EXPECT\n \ttest_expect_code 129 git rev-parse --parseopt -- --help-all > output < optionspec_only_hidden_switches &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'test --parseopt invalid switch help output' '\n@@ -176,7 +176,7 @@ test_expect_success 'test --parseopt invalid switch help output' '\n |\n END_EXPECT\n \ttest_expect_code 129 git rev-parse --parseopt -- --does-not-exist 1>/dev/null 2>output < optionspec &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'setup expect.1' \"\ndiff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh\nindex f6e6f23f7e6..65a154a8a20 100755\n--- a/t/t1506-rev-parse-diagnosis.sh\n+++ b/t/t1506-rev-parse-diagnosis.sh\n@@ -15,7 +15,7 @@ test_did_you_mean ()\n \tfatal: path '$2$3' $4, but not ${5:-$SQ$3$SQ}\n \thint: Did you mean '$1:$2$3'${2:+ aka $SQ$1:./$3$SQ}?\n \tEOF\n-\ttest_i18ncmp expected error\n+\ttest_cmp expected error\n }\n \n HASH_file=\ndiff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh\nindex 73b4f34c6ee..c34714ffe3f 100755\n--- a/t/t1507-rev-parse-upstream.sh\n+++ b/t/t1507-rev-parse-upstream.sh\n@@ -172,7 +172,7 @@ test_expect_success 'branch@{u} error message when no upstream' '\n \tfatal: no upstream configured for branch ${SQ}non-tracking${SQ}\n \tEOF\n \terror_message non-tracking@{u} &&\n-\ttest_i18ncmp expect error\n+\ttest_cmp expect error\n '\n \n test_expect_success '@{u} error message when no upstream' '\n@@ -180,7 +180,7 @@ test_expect_success '@{u} error message when no upstream' '\n \tfatal: no upstream configured for branch ${SQ}main${SQ}\n \tEOF\n \ttest_must_fail git rev-parse --verify @{u} 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'branch@{u} error message with misspelt branch' '\n@@ -188,7 +188,7 @@ test_expect_success 'branch@{u} error message with misspelt branch' '\n \tfatal: no such branch: ${SQ}no-such-branch${SQ}\n \tEOF\n \terror_message no-such-branch@{u} &&\n-\ttest_i18ncmp expect error\n+\ttest_cmp expect error\n '\n \n test_expect_success '@{u} error message when not on a branch' '\n@@ -197,7 +197,7 @@ test_expect_success '@{u} error message when not on a branch' '\n \tEOF\n \tgit checkout HEAD^0 &&\n \ttest_must_fail git rev-parse --verify @{u} 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'branch@{u} error message if upstream branch not fetched' '\n@@ -205,7 +205,7 @@ test_expect_success 'branch@{u} error message if upstream branch not fetched' '\n \tfatal: upstream branch ${SQ}refs/heads/side${SQ} not stored as a remote-tracking branch\n \tEOF\n \terror_message bad-upstream@{u} &&\n-\ttest_i18ncmp expect error\n+\ttest_cmp expect error\n '\n \n test_expect_success 'pull works when tracking a local branch' '\ndiff --git a/t/t1509-root-work-tree.sh b/t/t1509-root-work-tree.sh\nindex fd2f7abf1c1..553a3f601ba 100755\n--- a/t/t1509-root-work-tree.sh\n+++ b/t/t1509-root-work-tree.sh\n@@ -221,7 +221,7 @@ test_expect_success 'setup' '\n \trm -rf /.git &&\n \techo \"Initialized empty Git repository in /.git/\" > expected &&\n \tgit init > result &&\n-\ttest_i18ncmp expected result\n+\ttest_cmp expected result\n '\n \n test_vars 'auto gitdir, root' \".git\" \"/\" \"\"\n@@ -246,7 +246,7 @@ test_expect_success 'setup' '\n \tcd / &&\n \techo \"Initialized empty Git repository in /\" > expected &&\n \tgit init --bare > result &&\n-\ttest_i18ncmp expected result\n+\ttest_cmp expected result\n '\n \n test_vars 'auto gitdir, root' \".\" \"\" \"\"\ndiff --git a/t/t1600-index.sh b/t/t1600-index.sh\nindex b7c31aa86a1..c9b9e718b89 100755\n--- a/t/t1600-index.sh\n+++ b/t/t1600-index.sh\n@@ -18,7 +18,7 @@ test_expect_success 'bogus GIT_INDEX_VERSION issues warning' '\n \t\t\twarning: GIT_INDEX_VERSION set, but the value is invalid.\n \t\t\tUsing version Z\n \t\tEOF\n-\t\ttest_i18ncmp expect.err actual.err\n+\t\ttest_cmp expect.err actual.err\n \t)\n '\n \n@@ -32,7 +32,7 @@ test_expect_success 'out of bounds GIT_INDEX_VERSION issues warning' '\n \t\t\twarning: GIT_INDEX_VERSION set, but the value is invalid.\n \t\t\tUsing version Z\n \t\tEOF\n-\t\ttest_i18ncmp expect.err actual.err\n+\t\ttest_cmp expect.err actual.err\n \t)\n '\n \n@@ -55,7 +55,7 @@ test_expect_success 'out of bounds index.version issues warning' '\n \t\t\twarning: index.version set, but the value is invalid.\n \t\t\tUsing version Z\n \t\tEOF\n-\t\ttest_i18ncmp expect.err actual.err\n+\t\ttest_cmp expect.err actual.err\n \t)\n '\n \ndiff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh\nindex 5f761bc616e..93be1c0eae5 100755\n--- a/t/t2018-checkout-branch.sh\n+++ b/t/t2018-checkout-branch.sh\n@@ -150,7 +150,7 @@ test_expect_success 'checkout -b to @{-1} fails with the right branch name' '\n \tgit checkout branch2 &&\n \techo  >expect \"fatal: A branch named '\\''branch1'\\'' already exists.\" &&\n \ttest_must_fail git checkout -b @{-1} 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'checkout -B to an existing branch resets branch to HEAD' '\ndiff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh\nindex b432b6427b6..bc46713a43e 100755\n--- a/t/t2020-checkout-detach.sh\n+++ b/t/t2020-checkout-detach.sh\n@@ -163,7 +163,7 @@ test_expect_success 'tracking count is accurate after orphan check' '\n \tgit config branch.child.merge refs/heads/main &&\n \tgit checkout child^ &&\n \tgit checkout child >stdout &&\n-\ttest_i18ncmp expect stdout\n+\ttest_cmp expect stdout\n '\n \n test_expect_success 'no advice given for explicit detached head state' '\n@@ -237,15 +237,15 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as\n \tsane_unset GIT_PRINT_SHA1_ELLIPSIS &&\n \tgit -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&\n \tcheck_detached &&\n-\ttest_i18ncmp 1st_detach actual &&\n+\ttest_cmp 1st_detach actual &&\n \n \tGIT_PRINT_SHA1_ELLIPSIS=\"no\" git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&\n \tcheck_detached &&\n-\ttest_i18ncmp 2nd_detach actual &&\n+\ttest_cmp 2nd_detach actual &&\n \n \tGIT_PRINT_SHA1_ELLIPSIS= git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&\n \tcheck_detached &&\n-\ttest_i18ncmp 3rd_detach actual &&\n+\ttest_cmp 3rd_detach actual &&\n \n \tsane_unset GIT_PRINT_SHA1_ELLIPSIS &&\n \n@@ -256,17 +256,17 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as\n \t# Make no mention of the env var at all\n \tgit -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&\n \tcheck_detached &&\n-\ttest_i18ncmp 1st_detach actual &&\n+\ttest_cmp 1st_detach actual &&\n \n \tGIT_PRINT_SHA1_ELLIPSIS='nope' &&\n \tgit -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&\n \tcheck_detached &&\n-\ttest_i18ncmp 2nd_detach actual &&\n+\ttest_cmp 2nd_detach actual &&\n \n \tGIT_PRINT_SHA1_ELLIPSIS=nein &&\n \tgit -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&\n \tcheck_detached &&\n-\ttest_i18ncmp 3rd_detach actual &&\n+\ttest_cmp 3rd_detach actual &&\n \n \ttrue\n \"\n@@ -319,15 +319,15 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked\n \n \tGIT_PRINT_SHA1_ELLIPSIS=yes git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&\n \tcheck_detached &&\n-\ttest_i18ncmp 1st_detach actual &&\n+\ttest_cmp 1st_detach actual &&\n \n \tGIT_PRINT_SHA1_ELLIPSIS=Yes git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&\n \tcheck_detached &&\n-\ttest_i18ncmp 2nd_detach actual &&\n+\ttest_cmp 2nd_detach actual &&\n \n \tGIT_PRINT_SHA1_ELLIPSIS=YES git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&\n \tcheck_detached &&\n-\ttest_i18ncmp 3rd_detach actual &&\n+\ttest_cmp 3rd_detach actual &&\n \n \ttrue\n \"\ndiff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh\nindex 7cb7a703827..45ca35d60ac 100755\n--- a/t/t2200-add-update.sh\n+++ b/t/t2200-add-update.sh\n@@ -135,7 +135,7 @@ test_expect_success 'add -n -u should not add but just report' '\n \tafter=$(git ls-files -s check top) &&\n \n \ttest \"$before\" = \"$after\" &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n \n '\n \ndiff --git a/t/t2401-worktree-prune.sh b/t/t2401-worktree-prune.sh\nindex aff877590df..a615d3b4838 100755\n--- a/t/t2401-worktree-prune.sh\n+++ b/t/t2401-worktree-prune.sh\n@@ -23,7 +23,7 @@ test_expect_success 'prune files inside $GIT_DIR/worktrees' '\n \tcat >expect <<EOF &&\n Removing worktrees/abc: not a valid directory\n EOF\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \t! test -f .git/worktrees/abc &&\n \t! test -d .git/worktrees\n '\n@@ -35,7 +35,7 @@ test_expect_success 'prune directories without gitdir' '\n Removing worktrees/def: gitdir file does not exist\n EOF\n \tgit worktree prune --verbose >actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \t! test -d .git/worktrees/def &&\n \t! test -d .git/worktrees\n '\ndiff --git a/t/t2402-worktree-list.sh b/t/t2402-worktree-list.sh\nindex 42d35d9ae82..fedcefe8de3 100755\n--- a/t/t2402-worktree-list.sh\n+++ b/t/t2402-worktree-list.sh\n@@ -167,7 +167,7 @@ test_expect_success '\"list\" all worktrees --verbose with prunable' '\n \trm -rf prunable &&\n \tgit worktree list --verbose >out &&\n \tsed -n \"s/  */ /g;/\\/prunable  *[0-9a-f].*$/,/prunable: .*$/p\" <out >actual &&\n-\ttest_i18ncmp actual expect\n+\ttest_cmp actual expect\n '\n \n test_expect_success 'bare repo setup' '\ndiff --git a/t/t3005-ls-files-relative.sh b/t/t3005-ls-files-relative.sh\nindex 2ec69a8a266..727e9ae1a44 100755\n--- a/t/t3005-ls-files-relative.sh\n+++ b/t/t3005-ls-files-relative.sh\n@@ -46,7 +46,7 @@ test_expect_success 'ls-files -c' '\n \t\tls ../x* >expect.out &&\n \t\ttest_must_fail git ls-files -c --error-unmatch ../[xy]* >actual.out 2>actual.err &&\n \t\ttest_cmp expect.out actual.out &&\n-\t\ttest_i18ncmp expect.err actual.err\n+\t\ttest_cmp expect.err actual.err\n \t)\n '\n \n@@ -61,7 +61,7 @@ test_expect_success 'ls-files -o' '\n \t\tls ../y* >expect.out &&\n \t\ttest_must_fail git ls-files -o --error-unmatch ../[xy]* >actual.out 2>actual.err &&\n \t\ttest_cmp expect.out actual.out &&\n-\t\ttest_i18ncmp expect.err actual.err\n+\t\ttest_cmp expect.err actual.err\n \t)\n '\n \ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 00761e40801..cc4b10236e2 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -695,7 +695,7 @@ test_expect_success 'deleting a symref' '\n \tgit branch -d symref >actual &&\n \ttest_path_is_file .git/refs/heads/target &&\n \ttest_path_is_missing .git/refs/heads/symref &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'deleting a dangling symref' '\n@@ -704,7 +704,7 @@ test_expect_success 'deleting a dangling symref' '\n \techo \"Deleted branch dangling-symref (was nowhere).\" >expect &&\n \tgit branch -d dangling-symref >actual &&\n \ttest_path_is_missing .git/refs/heads/dangling-symref &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'deleting a self-referential symref' '\n@@ -713,7 +713,7 @@ test_expect_success 'deleting a self-referential symref' '\n \techo \"Deleted branch self-reference (was refs/heads/self-reference).\" >expect &&\n \tgit branch -d self-reference >actual &&\n \ttest_path_is_missing .git/refs/heads/self-reference &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'renaming a symref is not allowed' '\n@@ -808,7 +808,7 @@ test_expect_success 'test deleting branch without config' '\n \tsha1=$(git rev-parse my7 | cut -c 1-7) &&\n \techo \"Deleted branch my7 (was $sha1).\" >expect &&\n \tgit branch -d my7 >actual 2>&1 &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'deleting currently checked out branch fails' '\n@@ -843,7 +843,7 @@ test_expect_success 'branch from tag w/--track causes failure' '\n test_expect_success '--set-upstream-to fails on multiple branches' '\n \techo \"fatal: too many arguments to set new upstream\" >expect &&\n \ttest_must_fail git branch --set-upstream-to main a b c 2>err &&\n-\ttest_i18ncmp expect err\n+\ttest_cmp expect err\n '\n \n test_expect_success '--set-upstream-to fails on detached HEAD' '\n@@ -851,13 +851,13 @@ test_expect_success '--set-upstream-to fails on detached HEAD' '\n \ttest_when_finished git checkout - &&\n \techo \"fatal: could not set upstream of HEAD to main when it does not point to any branch.\" >expect &&\n \ttest_must_fail git branch --set-upstream-to main 2>err &&\n-\ttest_i18ncmp expect err\n+\ttest_cmp expect err\n '\n \n test_expect_success '--set-upstream-to fails on a missing dst branch' '\n \techo \"fatal: branch '\"'\"'does-not-exist'\"'\"' does not exist\" >expect &&\n \ttest_must_fail git branch --set-upstream-to main does-not-exist 2>err &&\n-\ttest_i18ncmp expect err\n+\ttest_cmp expect err\n '\n \n test_expect_success '--set-upstream-to fails on a missing src branch' '\n@@ -868,7 +868,7 @@ test_expect_success '--set-upstream-to fails on a missing src branch' '\n test_expect_success '--set-upstream-to fails on a non-ref' '\n \techo \"fatal: Cannot setup tracking information; starting point '\"'\"'HEAD^{}'\"'\"' is not a branch.\" >expect &&\n \ttest_must_fail git branch --set-upstream-to HEAD^{} 2>err &&\n-\ttest_i18ncmp expect err\n+\ttest_cmp expect err\n '\n \n test_expect_success '--set-upstream-to fails on locked config' '\n@@ -899,7 +899,7 @@ test_expect_success 'use --set-upstream-to modify a particular branch' '\n test_expect_success '--unset-upstream should fail if given a non-existent branch' '\n \techo \"fatal: Branch '\"'\"'i-dont-exist'\"'\"' has no upstream information\" >expect &&\n \ttest_must_fail git branch --unset-upstream i-dont-exist 2>err &&\n-\ttest_i18ncmp expect err\n+\ttest_cmp expect err\n '\n \n test_expect_success '--unset-upstream should fail if config is locked' '\n@@ -921,13 +921,13 @@ test_expect_success 'test --unset-upstream on HEAD' '\n \t# fail for a branch without upstream set\n \techo \"fatal: Branch '\"'\"'main'\"'\"' has no upstream information\" >expect &&\n \ttest_must_fail git branch --unset-upstream 2>err &&\n-\ttest_i18ncmp expect err\n+\ttest_cmp expect err\n '\n \n test_expect_success '--unset-upstream should fail on multiple branches' '\n \techo \"fatal: too many arguments to unset upstream\" >expect &&\n \ttest_must_fail git branch --unset-upstream a b c 2>err &&\n-\ttest_i18ncmp expect err\n+\ttest_cmp expect err\n '\n \n test_expect_success '--unset-upstream should fail on detached HEAD' '\n@@ -935,7 +935,7 @@ test_expect_success '--unset-upstream should fail on detached HEAD' '\n \ttest_when_finished git checkout - &&\n \techo \"fatal: could not unset upstream of HEAD when it does not point to any branch.\" >expect &&\n \ttest_must_fail git branch --unset-upstream 2>err &&\n-\ttest_i18ncmp expect err\n+\ttest_cmp expect err\n '\n \n test_expect_success 'test --unset-upstream on a particular branch' '\n@@ -957,7 +957,7 @@ test_expect_success '--set-upstream-to notices an error to set branch as own ups\n \tEOF\n \ttest_expect_code 1 git config branch.my13.remote &&\n \ttest_expect_code 1 git config branch.my13.merge &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n # Keep this test last, as it changes the current branch\ndiff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh\nindex 578b5f48255..349a810cee1 100755\n--- a/t/t3201-branch-contains.sh\n+++ b/t/t3201-branch-contains.sh\n@@ -264,7 +264,7 @@ test_expect_success 'branch --merged with --verbose' '\n \t* topic $(git rev-parse --short topic ) [ahead 1] foo\n \t  zzz   $(git rev-parse --short zzz   ) second on main\n \tEOF\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_done\ndiff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh\nindex b6fcd017afe..5325b9f67a0 100755\n--- a/t/t3203-branch-output.sh\n+++ b/t/t3203-branch-output.sh\n@@ -160,7 +160,7 @@ test_expect_success 'git branch shows detached HEAD properly' '\n EOF\n \tgit checkout HEAD^0 &&\n \tgit branch >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'git branch shows detached HEAD properly after checkout --detach' '\n@@ -173,7 +173,7 @@ test_expect_success 'git branch shows detached HEAD properly after checkout --de\n EOF\n \tgit checkout --detach &&\n \tgit branch >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'git branch shows detached HEAD properly after moving' '\n@@ -185,7 +185,7 @@ test_expect_success 'git branch shows detached HEAD properly after moving' '\n EOF\n \tgit reset --hard HEAD^1 &&\n \tgit branch >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'git branch shows detached HEAD properly from tag' '\n@@ -198,7 +198,7 @@ EOF\n \tgit tag fromtag main &&\n \tgit checkout fromtag &&\n \tgit branch >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'git branch shows detached HEAD properly after moving from tag' '\n@@ -210,7 +210,7 @@ test_expect_success 'git branch shows detached HEAD properly after moving from t\n EOF\n \tgit reset --hard HEAD^1 &&\n \tgit branch >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'git branch `--sort=[-]objectsize` option' '\n@@ -221,7 +221,7 @@ test_expect_success 'git branch `--sort=[-]objectsize` option' '\n \t  main\n \tEOF\n \tgit branch --sort=objectsize >actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \tcat >expect <<-\\EOF &&\n \t* (HEAD detached from fromtag)\n@@ -230,7 +230,7 @@ test_expect_success 'git branch `--sort=[-]objectsize` option' '\n \t  branch-two\n \tEOF\n \tgit branch --sort=-objectsize >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'git branch `--sort=[-]type` option' '\n@@ -241,7 +241,7 @@ test_expect_success 'git branch `--sort=[-]type` option' '\n \t  main\n \tEOF\n \tgit branch --sort=type >actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \tcat >expect <<-\\EOF &&\n \t* (HEAD detached from fromtag)\n@@ -250,7 +250,7 @@ test_expect_success 'git branch `--sort=[-]type` option' '\n \t  main\n \tEOF\n \tgit branch --sort=-type >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'git branch `--sort=[-]version:refname` option' '\n@@ -261,7 +261,7 @@ test_expect_success 'git branch `--sort=[-]version:refname` option' '\n \t  main\n \tEOF\n \tgit branch --sort=version:refname >actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \tcat >expect <<-\\EOF &&\n \t* (HEAD detached from fromtag)\n@@ -270,7 +270,7 @@ test_expect_success 'git branch `--sort=[-]version:refname` option' '\n \t  branch-one\n \tEOF\n \tgit branch --sort=-version:refname >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'git branch --points-at option' '\n@@ -337,7 +337,7 @@ test_expect_success 'git branch --format option' '\n \tRefname is refs/heads/ref-to-remote\n \tEOF\n \tgit branch --format=\"Refname is %(refname)\" >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'worktree colors correct' '\n@@ -355,7 +355,7 @@ test_expect_success 'worktree colors correct' '\n \trm -r worktree_dir &&\n \tgit worktree prune &&\n \ttest_decode_color <actual.raw >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success \"set up color tests\" '\n@@ -398,7 +398,7 @@ test_expect_success 'verbose output lists worktree path' '\n \tgit branch -vv >actual &&\n \trm -r worktree_dir &&\n \tgit worktree prune &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_done\ndiff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh\nindex 04de03cad0a..f5bf16abcd8 100755\n--- a/t/t3300-funny-names.sh\n+++ b/t/t3300-funny-names.sh\n@@ -181,7 +181,7 @@ test_expect_success 'diffstat for rename quotes funny filename' '\n \tgit diff-index -M -p $t0 >diff &&\n \tgit apply --stat <diff >diffstat &&\n \tsed -e \"s/|.*//\" -e \"s/ *\\$//\" <diffstat >current &&\n-\ttest_i18ncmp expected current\n+\ttest_cmp expected current\n '\n \n test_expect_success 'numstat for rename quotes funny filename' '\ndiff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh\nindex 5e88a10f8d7..587b408063a 100755\n--- a/t/t3400-rebase.sh\n+++ b/t/t3400-rebase.sh\n@@ -113,7 +113,7 @@ test_expect_success 'rebase off of the previous branch using \"-\"' '\n \ttest_cmp expect.forkpoint actual.forkpoint &&\n \t# the next one is dubious---we may want to say \"-\",\n \t# instead of @{-1}, in the message\n-\ttest_i18ncmp expect.messages actual.messages\n+\ttest_cmp expect.messages actual.messages\n '\n \n test_expect_success 'rebase a single mode change' '\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex dc45552b4d6..b0431782230 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -86,7 +86,7 @@ test_expect_success 'rebase -i with empty todo list' '\n \t\t\tgit rebase -i HEAD^ >output 2>&1\n \t) &&\n \ttail -n 1 output >actual &&  # Ignore output about changing todo list\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'rebase -i with the exec command' '\n@@ -158,9 +158,9 @@ test_expect_success 'rebase -x with empty command fails' '\n \ttest_when_finished \"git rebase --abort ||:\" &&\n \ttest_must_fail env git rebase -x \"\" @ 2>actual &&\n \ttest_write_lines \"error: empty exec command\" >expected &&\n-\ttest_i18ncmp expected actual &&\n+\ttest_cmp expected actual &&\n \ttest_must_fail env git rebase -x \" \" @ 2>actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'rebase -x with newline in command fails' '\n@@ -168,7 +168,7 @@ test_expect_success 'rebase -x with newline in command fails' '\n \ttest_must_fail env git rebase -x \"a${LF}b\" @ 2>actual &&\n \ttest_write_lines \"error: exec commands cannot contain newlines\" \\\n \t\t\t >expected &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'rebase -i with exec of inexistent command' '\n@@ -1465,7 +1465,7 @@ test_expect_success 'rebase -i respects rebase.missingCommitsCheck = warn' '\n \t\tFAKE_LINES=\"1 2 3 4\" git rebase -i --root 2>actual.2\n \t) &&\n \thead -n4 actual.2 >actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \ttest D = $(git cat-file commit HEAD | sed -ne \\$p)\n '\n \n@@ -1489,7 +1489,7 @@ test_expect_success 'rebase -i respects rebase.missingCommitsCheck = error' '\n \t\tset_fake_editor &&\n \t\ttest_must_fail env FAKE_LINES=\"1 2 4\" \\\n \t\t\tgit rebase -i --root 2>actual &&\n-\t\ttest_i18ncmp expect actual &&\n+\t\ttest_cmp expect actual &&\n \t\tcp .git/rebase-merge/git-rebase-todo.backup \\\n \t\t\t.git/rebase-merge/git-rebase-todo &&\n \t\tFAKE_LINES=\"1 2 drop 3 4 drop 5\" git rebase --edit-todo\n@@ -1535,11 +1535,11 @@ test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = wa\n \t\tcp .git/rebase-merge/git-rebase-todo.backup orig &&\n \t\tFAKE_LINES=\"2 3 4\" git rebase --edit-todo 2>actual.2 &&\n \t\thead -n6 actual.2 >actual &&\n-\t\ttest_i18ncmp expect actual &&\n+\t\ttest_cmp expect actual &&\n \t\tcp orig .git/rebase-merge/git-rebase-todo &&\n \t\tFAKE_LINES=\"1 2 3 4\" git rebase --edit-todo 2>actual.2 &&\n \t\thead -n4 actual.2 >actual &&\n-\t\ttest_i18ncmp expect.3 actual &&\n+\t\ttest_cmp expect.3 actual &&\n \t\tgit rebase --continue 2>actual\n \t) &&\n \ttest D = $(git cat-file commit HEAD | sed -ne \\$p) &&\n@@ -1575,16 +1575,16 @@ test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = er\n \t\tcp .git/rebase-merge/git-rebase-todo.backup orig &&\n \t\ttest_must_fail env FAKE_LINES=\"2 3 4\" \\\n \t\t\tgit rebase --edit-todo 2>actual &&\n-\t\ttest_i18ncmp expect actual &&\n+\t\ttest_cmp expect actual &&\n \t\ttest_must_fail git rebase --continue 2>actual &&\n-\t\ttest_i18ncmp expect.2 actual &&\n+\t\ttest_cmp expect.2 actual &&\n \t\ttest_must_fail git rebase --edit-todo &&\n \t\tcp orig .git/rebase-merge/git-rebase-todo &&\n \t\ttest_must_fail env FAKE_LINES=\"1 2 3 4\" \\\n \t\t\tgit rebase --edit-todo 2>actual &&\n-\t\ttest_i18ncmp expect.3 actual &&\n+\t\ttest_cmp expect.3 actual &&\n \t\ttest_must_fail git rebase --continue 2>actual &&\n-\t\ttest_i18ncmp expect.3 actual &&\n+\t\ttest_cmp expect.3 actual &&\n \t\tcp orig .git/rebase-merge/git-rebase-todo &&\n \t\tFAKE_LINES=\"1 2 3 4 drop 5\" git rebase --edit-todo &&\n \t\tgit rebase --continue 2>actual\ndiff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh\nindex 4caa014c71c..43fcb68f27e 100755\n--- a/t/t3420-rebase-autostash.sh\n+++ b/t/t3420-rebase-autostash.sh\n@@ -110,7 +110,7 @@ testrebase () {\n \t\tfi &&\n \t\tcreate_expected_success_$suffix &&\n \t\tsed \"$remove_progress_re\" <actual >actual2 &&\n-\t\ttest_i18ncmp expected actual2\n+\t\ttest_cmp expected actual2\n \t'\n \n \ttest_expect_success \"rebase$type: dirty index, non-conflicting rebase\" '\n@@ -231,7 +231,7 @@ testrebase () {\n \t\tfi &&\n \t\tcreate_expected_failure_$suffix &&\n \t\tsed \"$remove_progress_re\" <actual >actual2 &&\n-\t\ttest_i18ncmp expected actual2\n+\t\ttest_cmp expected actual2\n \t'\n }\n \ndiff --git a/t/t3504-cherry-pick-rerere.sh b/t/t3504-cherry-pick-rerere.sh\nindex 9198535874c..4581ae98b87 100755\n--- a/t/t3504-cherry-pick-rerere.sh\n+++ b/t/t3504-cherry-pick-rerere.sh\n@@ -74,10 +74,10 @@ test_expect_success 'cherry-pick --continue rejects --rerere-autoupdate' '\n \tgit diff-files --quiet &&\n \ttest_must_fail git cherry-pick --continue --rerere-autoupdate >actual 2>&1 &&\n \techo \"fatal: cherry-pick: --rerere-autoupdate cannot be used with --continue\" >expect &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \ttest_must_fail git cherry-pick --continue --no-rerere-autoupdate >actual 2>&1 &&\n \techo \"fatal: cherry-pick: --no-rerere-autoupdate cannot be used with --continue\" >expect &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \tgit cherry-pick --abort\n '\n \ndiff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\nindex 5f4564c8306..014001b8f32 100755\n--- a/t/t3507-cherry-pick-conflict.sh\n+++ b/t/t3507-cherry-pick-conflict.sh\n@@ -59,7 +59,7 @@ test_expect_success 'advice from failed cherry-pick' \"\n \tEOF\n \ttest_must_fail git cherry-pick picked 2>actual &&\n \n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n \"\n \n test_expect_success 'advice from failed cherry-pick --no-commit' \"\n@@ -73,7 +73,7 @@ test_expect_success 'advice from failed cherry-pick --no-commit' \"\n \tEOF\n \ttest_must_fail git cherry-pick --no-commit picked 2>actual &&\n \n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n \"\n \n test_expect_success 'failed cherry-pick sets CHERRY_PICK_HEAD' '\n@@ -256,7 +256,7 @@ test_expect_success \\\n \n \ttest_must_fail git cherry-pick picked &&\n \n-\ttest_i18ncmp expected .git/MERGE_MSG\n+\ttest_cmp expected .git/MERGE_MSG\n '\n \n test_expect_success \\\n@@ -276,7 +276,7 @@ test_expect_success \\\n \n \ttest_must_fail git cherry-pick --cleanup=scissors picked &&\n \n-\ttest_i18ncmp expected .git/MERGE_MSG\n+\ttest_cmp expected .git/MERGE_MSG\n '\n \n test_expect_success 'failed cherry-pick describes conflict in work tree' '\n@@ -465,7 +465,7 @@ test_expect_success \\\n \ttest_must_fail git revert picked &&\n \n \tsed \"s/$OID_REGEX/OBJID/\" .git/MERGE_MSG >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success \\\n@@ -488,7 +488,7 @@ test_expect_success \\\n \ttest_must_fail git revert --cleanup=scissors picked &&\n \n \tsed \"s/$OID_REGEX/OBJID/\" .git/MERGE_MSG >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'failed cherry-pick does not forget -s' '\ndiff --git a/t/t3508-cherry-pick-many-commits.sh b/t/t3508-cherry-pick-many-commits.sh\nindex ec7a2c9fcfa..e8375d1c970 100755\n--- a/t/t3508-cherry-pick-many-commits.sh\n+++ b/t/t3508-cherry-pick-many-commits.sh\n@@ -86,7 +86,7 @@ test_expect_success 'output to keep user entertained during multi-pick' '\n \tgit cherry-pick first..fourth >actual &&\n \tsed -e \"s/$_x05[0-9a-f][0-9a-f]/OBJID/\" <actual >actual.fuzzy &&\n \ttest_line_count -ge 3 actual.fuzzy &&\n-\ttest_i18ncmp expected actual.fuzzy\n+\ttest_cmp expected actual.fuzzy\n '\n \n test_expect_success 'cherry-pick --strategy resolve first..fourth works' '\n@@ -123,7 +123,7 @@ test_expect_success 'output during multi-pick indicates merge strategy' '\n \ttest_tick &&\n \tgit cherry-pick --strategy resolve first..fourth >actual &&\n \tsed -e \"s/$_x05[0-9a-f][0-9a-f]/OBJID/\" <actual >actual.fuzzy &&\n-\ttest_i18ncmp expected actual.fuzzy\n+\ttest_cmp expected actual.fuzzy\n '\n \n test_expect_success 'cherry-pick --ff first..fourth works' '\ndiff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh\nindex 5b94fdaa671..b76cb6de91d 100755\n--- a/t/t3510-cherry-pick-sequence.sh\n+++ b/t/t3510-cherry-pick-sequence.sh\n@@ -170,7 +170,7 @@ test_expect_success 'check advice when we move HEAD by committing' '\n \tgit commit -a &&\n \ttest_path_is_missing .git/CHERRY_PICK_HEAD &&\n \ttest_must_fail git cherry-pick --skip 2>advice &&\n-\ttest_i18ncmp expect advice\n+\ttest_cmp expect advice\n '\n \n test_expect_success 'selectively advise --skip while launching another sequence' '\n@@ -182,7 +182,7 @@ test_expect_success 'selectively advise --skip while launching another sequence'\n \tEOF\n \ttest_must_fail git cherry-pick picked..yetanotherpick &&\n \ttest_must_fail git cherry-pick picked..yetanotherpick 2>advice &&\n-\ttest_i18ncmp expect advice &&\n+\ttest_cmp expect advice &&\n \tcat >expect <<-EOF &&\n \terror: cherry-pick is already in progress\n \thint: try \"git cherry-pick (--continue | --abort | --quit)\"\n@@ -190,7 +190,7 @@ test_expect_success 'selectively advise --skip while launching another sequence'\n \tEOF\n \tgit reset --merge &&\n \ttest_must_fail git cherry-pick picked..yetanotherpick 2>advice &&\n-\ttest_i18ncmp expect advice\n+\ttest_cmp expect advice\n '\n \n test_expect_success 'allow skipping commit but not abort for a new history' '\n@@ -204,7 +204,7 @@ test_expect_success 'allow skipping commit but not abort for a new history' '\n \ttest_must_fail git cherry-pick anotherpick &&\n \ttest_must_fail git cherry-pick --abort 2>advice &&\n \tgit cherry-pick --skip &&\n-\ttest_i18ncmp expect advice\n+\ttest_cmp expect advice\n '\n \n test_expect_success 'allow skipping stopped cherry-pick because of untracked file modifications' '\ndiff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\nindex dff12286699..857b145cc25 100755\n--- a/t/t3600-rm.sh\n+++ b/t/t3600-rm.sh\n@@ -442,7 +442,7 @@ test_expect_success 'rm issues a warning when section is not found in .gitmodule\n \tgit add .gitmodules &&\n \techo \"warning: Could not find section in .gitmodules where path=submod\" >expect.err &&\n \tgit rm submod >actual 2>actual.err &&\n-\ttest_i18ncmp expect.err actual.err &&\n+\ttest_cmp expect.err actual.err &&\n \ttest_path_is_missing submod &&\n \ttest_path_is_missing submod/.git &&\n \tgit status -s -uno >actual &&\n@@ -812,7 +812,7 @@ test_expect_success 'rm files with different staged content' '\n \techo content1 >foo.txt &&\n \techo content1 >bar.txt &&\n \ttest_must_fail git rm foo.txt bar.txt 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'rm files with different staged content without hints' '\n@@ -825,7 +825,7 @@ test_expect_success 'rm files with different staged content without hints' '\n \techo content2 >foo.txt &&\n \techo content2 >bar.txt &&\n \ttest_must_fail git -c advice.rmhints=false rm foo.txt bar.txt 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'rm file with local modification' '\n@@ -837,7 +837,7 @@ test_expect_success 'rm file with local modification' '\n \tgit commit -m \"testing rm 3\" &&\n \techo content3 >foo.txt &&\n \ttest_must_fail git rm foo.txt 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'rm file with local modification without hints' '\n@@ -847,7 +847,7 @@ test_expect_success 'rm file with local modification without hints' '\n \tEOF\n \techo content4 >bar.txt &&\n \ttest_must_fail git -c advice.rmhints=false rm bar.txt 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'rm file with changes in the index' '\n@@ -860,7 +860,7 @@ test_expect_success 'rm file with changes in the index' '\n \techo content5 >foo.txt &&\n \tgit add foo.txt &&\n \ttest_must_fail git rm foo.txt 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'rm file with changes in the index without hints' '\n@@ -869,7 +869,7 @@ test_expect_success 'rm file with changes in the index without hints' '\n \t    foo.txt\n \tEOF\n \ttest_must_fail git -c advice.rmhints=false rm foo.txt 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'rm files with two different errors' '\n@@ -888,7 +888,7 @@ test_expect_success 'rm files with two different errors' '\n \techo content6 >bar1.txt &&\n \tgit add bar1.txt &&\n \ttest_must_fail git rm bar1.txt foo1.txt 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'rm empty string should fail' '\ndiff --git a/t/t3700-add.sh b/t/t3700-add.sh\nindex b7d4ba608cb..dda84004f96 100755\n--- a/t/t3700-add.sh\n+++ b/t/t3700-add.sh\n@@ -304,7 +304,7 @@ test_expect_success 'error on a repository with no commits' '\n \terror: '\"'empty/'\"' does not have a commit checked out\n \tfatal: adding files failed\n \tEOF\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'git add --dry-run of existing changed file' \"\n@@ -320,7 +320,7 @@ test_expect_success 'git add --dry-run of non-existing file' \"\n \n test_expect_success 'git add --dry-run of an existing file output' \"\n \techo \\\"fatal: pathspec 'ignored-file' did not match any files\\\" >expect &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n \"\n \n cat >expect.err <<\\EOF\n@@ -339,8 +339,8 @@ test_expect_success 'git add --dry-run --ignore-missing of non-existing file' '\n '\n \n test_expect_success 'git add --dry-run --ignore-missing of non-existing file output' '\n-\ttest_i18ncmp expect.out actual.out &&\n-\ttest_i18ncmp expect.err actual.err\n+\ttest_cmp expect.out actual.out &&\n+\ttest_cmp expect.err actual.err\n '\n \n test_expect_success 'git add empty string should fail' '\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex ed00c40370b..207714655f2 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -974,7 +974,7 @@ test_expect_success 'show help from add--helper' '\n \tEOF\n \ttest_write_lines h | force_color git add -i >actual.colored &&\n \ttest_decode_color <actual.colored >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_done\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex 84b039e5739..5f282ecf617 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -564,7 +564,7 @@ test_expect_success 'stash show format defaults to --stat' '\n \t 1 file changed, 1 insertion(+)\n \tEOF\n \tgit stash show ${STASH_ID} >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'stash show - stashes on stack, stash-like argument' '\n@@ -792,7 +792,7 @@ test_expect_success 'apply: show same status as git status (relative to ./)' '\n \t\tgit stash apply\n \t) |\n \tsed -e 1d >actual && # drop \"Saved...\"\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n cat >expect <<EOF\n@@ -1114,7 +1114,7 @@ test_expect_success 'stash push -p with pathspec shows no changes only once' '\n \tgit stash push -p foo >actual &&\n \techo \"No local changes to save\" >expect &&\n \tgit reset --hard HEAD~ &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'push <pathspec>: show no changes when there are none' '\n@@ -1124,7 +1124,7 @@ test_expect_success 'push <pathspec>: show no changes when there are none' '\n \tgit stash push foo >actual &&\n \techo \"No local changes to save\" >expect &&\n \tgit reset --hard HEAD~ &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'push: <pathspec> not in the repository errors out' '\ndiff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh\nindex f075c7f1f31..3d8b6b491f0 100755\n--- a/t/t3905-stash-include-untracked.sh\n+++ b/t/t3905-stash-include-untracked.sh\n@@ -280,7 +280,7 @@ test_expect_success 'stash -u -- <ignored> leaves ignored file alone' '\n test_expect_success 'stash -u -- <non-existent> shows no changes when there are none' '\n \tgit stash push -u -- non-existent >actual &&\n \techo \"No local changes to save\" >expect &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'stash -u with globs' '\ndiff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh\nindex 03489aff14e..275ce5fa15b 100755\n--- a/t/t4006-diff-mode.sh\n+++ b/t/t4006-diff-mode.sh\n@@ -39,13 +39,13 @@ test_expect_success '--stat output after text chmod' '\n \t 1 file changed, 0 insertions(+), 0 deletions(-)\n \tEOF\n \tgit diff HEAD --stat >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success '--shortstat output after text chmod' '\n \ttail -n 1 <expect >expect.short &&\n \tgit diff HEAD --shortstat >actual &&\n-\ttest_i18ncmp expect.short actual\n+\ttest_cmp expect.short actual\n '\n \n test_expect_success '--stat output after binary chmod' '\n@@ -56,13 +56,13 @@ test_expect_success '--stat output after binary chmod' '\n \t 2 files changed, 0 insertions(+), 0 deletions(-)\n \tEOF\n \tgit diff HEAD --stat >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success '--shortstat output after binary chmod' '\n \ttail -n 1 <expect >expect.short &&\n \tgit diff HEAD --shortstat >actual &&\n-\ttest_i18ncmp expect.short actual\n+\ttest_cmp expect.short actual\n '\n \n test_done\ndiff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh\nindex bd59328e4b2..33ff588ebca 100755\n--- a/t/t4012-diff-binary.sh\n+++ b/t/t4012-diff-binary.sh\n@@ -34,19 +34,19 @@ EOF\n test_expect_success 'apply --stat output for binary file change' '\n \tgit diff >diff &&\n \tgit apply --stat --summary <diff >current &&\n-\ttest_i18ncmp expected current\n+\ttest_cmp expected current\n '\n \n test_expect_success 'diff --shortstat output for binary file change' '\n \ttail -n 1 expected >expect &&\n \tgit diff --shortstat >current &&\n-\ttest_i18ncmp expect current\n+\ttest_cmp expect current\n '\n \n test_expect_success 'diff --shortstat output for binary file change only' '\n \techo \" 1 file changed, 0 insertions(+), 0 deletions(-)\" >expected &&\n \tgit diff --shortstat -- b >current &&\n-\ttest_i18ncmp expected current\n+\ttest_cmp expected current\n '\n \n test_expect_success 'apply --numstat notices binary file change' '\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex ce6aa3914fe..47d4df08f9e 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -222,7 +222,7 @@ do\n \t\t\tprocess_diffs \"$expect\" >expect &&\n \t\t\tcase $cmd in\n \t\t\t*format-patch* | *-stat*)\n-\t\t\t\ttest_i18ncmp expect actual;;\n+\t\t\t\ttest_cmp expect actual;;\n \t\t\t*)\n \t\t\t\ttest_cmp expect actual;;\n \t\t\tesac &&\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex 66630c8413d..cdd3154e702 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -888,11 +888,11 @@ echo \"fatal: --check does not make sense\" >expect.check\n \n test_expect_success 'options no longer allowed for format-patch' '\n \ttest_must_fail git format-patch --name-only 2>output &&\n-\ttest_i18ncmp expect.name-only output &&\n+\ttest_cmp expect.name-only output &&\n \ttest_must_fail git format-patch --name-status 2>output &&\n-\ttest_i18ncmp expect.name-status output &&\n+\ttest_cmp expect.name-status output &&\n \ttest_must_fail git format-patch --check 2>output &&\n-\ttest_i18ncmp expect.check output\n+\ttest_cmp expect.check output\n '\n \n test_expect_success 'format-patch --numstat should produce a patch' '\ndiff --git a/t/t4016-diff-quote.sh b/t/t4016-diff-quote.sh\nindex 9c48e5c2c99..876271d6826 100755\n--- a/t/t4016-diff-quote.sh\n+++ b/t/t4016-diff-quote.sh\n@@ -82,7 +82,7 @@ test_expect_success 'git diff --stat -M HEAD' '\n \t 7 files changed, 0 insertions(+), 0 deletions(-)\n \tEOF\n \tgit diff --stat -M HEAD >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_done\ndiff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh\nindex 4cb9f0e523d..c906320b60d 100755\n--- a/t/t4030-diff-textconv.sh\n+++ b/t/t4030-diff-textconv.sh\n@@ -139,7 +139,7 @@ EOF\n test_expect_success 'diffstat does not run textconv' '\n \techo file diff=fail >.gitattributes &&\n \tgit diff --stat HEAD^ HEAD >actual &&\n-\ttest_i18ncmp expect.stat actual &&\n+\ttest_cmp expect.stat actual &&\n \n \thead -n1 <expect.stat >expect.line1 &&\n \thead -n1 <actual >actual.line1 &&\ndiff --git a/t/t4045-diff-relative.sh b/t/t4045-diff-relative.sh\nindex 7be1de736d8..61ba5f707fb 100755\n--- a/t/t4045-diff-relative.sh\n+++ b/t/t4045-diff-relative.sh\n@@ -61,7 +61,7 @@ check_stat () {\n \tEOF\n \ttest_expect_success \"--stat $*\" \"\n \t\tgit -C '$dir' diff --stat $* HEAD^ >actual &&\n-\t\ttest_i18ncmp expected actual\n+\t\ttest_cmp expected actual\n \t\"\n }\n \ndiff --git a/t/t4049-diff-stat-count.sh b/t/t4049-diff-stat-count.sh\nindex a34121740a4..53061b104ec 100755\n--- a/t/t4049-diff-stat-count.sh\n+++ b/t/t4049-diff-stat-count.sh\n@@ -25,7 +25,7 @@ test_expect_success 'mode-only change show as a 0-line change' '\n \t 4 files changed, 2 insertions(+)\n \tEOF\n \tgit diff --stat --stat-count=2 HEAD >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'binary changes do not count in lines' '\n@@ -40,7 +40,7 @@ test_expect_success 'binary changes do not count in lines' '\n \t 3 files changed, 2 insertions(+)\n \tEOF\n \tgit diff --stat --stat-count=2 >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'exclude unmerged entries from total file count' '\n@@ -62,7 +62,7 @@ test_expect_success 'exclude unmerged entries from total file count' '\n \t 3 files changed, 3 insertions(+)\n \tEOF\n \tgit diff --stat --stat-count=2 >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_done\ndiff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh\nindex 744b8e51bea..9b433de8363 100755\n--- a/t/t4100-apply-stat.sh\n+++ b/t/t4100-apply-stat.sh\n@@ -17,13 +17,13 @@ do\n \ttest_expect_success \"$title\" '\n \t\tgit apply --stat --summary \\\n \t\t\t<\"$TEST_DIRECTORY/t4100/t-apply-$num.patch\" >current &&\n-\t\ttest_i18ncmp \"$TEST_DIRECTORY\"/t4100/t-apply-$num.expect current\n+\t\ttest_cmp \"$TEST_DIRECTORY\"/t4100/t-apply-$num.expect current\n \t'\n \n \ttest_expect_success \"$title with recount\" '\n \t\tsed -e \"$UNC\" <\"$TEST_DIRECTORY/t4100/t-apply-$num.patch\" |\n \t\tgit apply --recount --stat --summary >current &&\n-\t\ttest_i18ncmp \"$TEST_DIRECTORY\"/t4100/t-apply-$num.expect current\n+\t\ttest_cmp \"$TEST_DIRECTORY\"/t4100/t-apply-$num.expect current\n \t'\n done <<\\EOF\n rename\ndiff --git a/t/t4150-am.sh b/t/t4150-am.sh\nindex 99987515dc2..2aaaa0d7ded 100755\n--- a/t/t4150-am.sh\n+++ b/t/t4150-am.sh\n@@ -906,7 +906,7 @@ test_expect_success 'am empty-file does not infloop' '\n \ttest_tick &&\n \ttest_must_fail git am empty-file 2>actual &&\n \techo Patch format detection failed. >expected &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'am --message-id really adds the message id' '\ndiff --git a/t/t4153-am-resume-override-opts.sh b/t/t4153-am-resume-override-opts.sh\nindex 8ea22d1bcbb..b7c3861407d 100755\n--- a/t/t4153-am-resume-override-opts.sh\n+++ b/t/t4153-am-resume-override-opts.sh\n@@ -61,7 +61,7 @@ test_expect_success '--no-quiet overrides --quiet' '\n \t# Applying side2 will be quiet.\n \tgit am --no-quiet --continue >out &&\n \techo \"Applying: side1\" >expected &&\n-\ttest_i18ncmp expected out\n+\ttest_cmp expected out\n '\n \n test_expect_success '--signoff overrides --no-signoff' '\ndiff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh\nindex 5ec0f47b567..85432b80ff5 100755\n--- a/t/t4205-log-pretty-formats.sh\n+++ b/t/t4205-log-pretty-formats.sh\n@@ -123,7 +123,7 @@ test_expect_success 'NUL separation with --stat' '\n \tstat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&\n \tprintf \"add bar\\n$stat0_part\\n\\0$(commit_msg)\\n$stat1_part\\n\" >expected &&\n \tgit log -z --stat --pretty=\"format:%s\" >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_failure 'NUL termination with --stat' '\ndiff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh\nindex daf01c309d0..54be7da1611 100755\n--- a/t/t4254-am-corrupt.sh\n+++ b/t/t4254-am-corrupt.sh\n@@ -60,7 +60,7 @@ test_expect_success 'try to apply corrupted patch' '\n \ttest_must_fail git -c advice.amWorkDir=false am bad-patch.diff 2>actual &&\n \techo \"error: git diff header lacks filename information (line 4)\" >expected &&\n \ttest_path_is_file f &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success \"NUL in commit message's body\" '\ndiff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh\nindex db4e15fd596..cb67bac1c47 100755\n--- a/t/t5150-request-pull.sh\n+++ b/t/t5150-request-pull.sh\n@@ -223,14 +223,14 @@ test_expect_success 'pull request format' '\n \t\tgit request-pull initial \"$downstream_url\" tags/full >../request\n \t) &&\n \t<request sed -nf fuzz.sed >request.fuzzy &&\n-\ttest_i18ncmp expect request.fuzzy &&\n+\ttest_cmp expect request.fuzzy &&\n \n \t(\n \t\tcd local &&\n \t\tgit request-pull initial \"$downstream_url\" tags/full:refs/tags/full\n \t) >request &&\n \tsed -nf fuzz.sed <request >request.fuzzy &&\n-\ttest_i18ncmp expect request.fuzzy &&\n+\ttest_cmp expect request.fuzzy &&\n \n \t(\n \t\tcd local &&\ndiff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh\nindex 0f06c40eb13..a8c1bc0f66b 100755\n--- a/t/t5316-pack-delta-depth.sh\n+++ b/t/t5316-pack-delta-depth.sh\n@@ -84,14 +84,14 @@ test_expect_success 'packing produces a long delta' '\n \tpack=$(git pack-objects --all --window=0 </dev/null pack) &&\n \techo 9 >expect &&\n \tmax_chain pack-$pack.pack >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success '--depth limits depth' '\n \tpack=$(git pack-objects --all --depth=5 </dev/null pack) &&\n \techo 5 >expect &&\n \tmax_chain pack-$pack.pack >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_done\ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex 1f4cc873a83..8a5d3492c71 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -547,7 +547,7 @@ test_expect_success 'test lonely missing ref' '\n \t\tcd client &&\n \t\ttest_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy 2>../error-m\n \t) &&\n-\ttest_i18ncmp expect-error error-m\n+\ttest_cmp expect-error error-m\n '\n \n test_expect_success 'test missing ref after existing' '\n@@ -555,7 +555,7 @@ test_expect_success 'test missing ref after existing' '\n \t\tcd client &&\n \t\ttest_must_fail git fetch-pack --no-progress .. refs/heads/A refs/heads/xyzzy 2>../error-em\n \t) &&\n-\ttest_i18ncmp expect-error error-em\n+\ttest_cmp expect-error error-em\n '\n \n test_expect_success 'test missing ref before existing' '\n@@ -563,7 +563,7 @@ test_expect_success 'test missing ref before existing' '\n \t\tcd client &&\n \t\ttest_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy refs/heads/A 2>../error-me\n \t) &&\n-\ttest_i18ncmp expect-error error-me\n+\ttest_cmp expect-error error-me\n '\n \n test_expect_success 'test --all, --depth, and explicit head' '\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex d3a3bb0a42b..8c462f20ae6 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -140,8 +140,8 @@ test_expect_success 'remove remote protects local branches' '\n \t\tgit remote rm oops 2>actual2 &&\n \t\tgit branch -d foobranch &&\n \t\tgit tag -d footag &&\n-\t\ttest_i18ncmp expect1 actual1 &&\n-\t\ttest_i18ncmp expect2 actual2\n+\t\ttest_cmp expect1 actual1 &&\n+\t\ttest_cmp expect2 actual2\n \t)\n '\n \n@@ -150,7 +150,7 @@ test_expect_success 'remove errors out early when deleting non-existent branch'\n \t\tcd test &&\n \t\techo \"error: No such remote: '\\''foo'\\''\" >expect &&\n \t\ttest_expect_code 2 git remote rm foo 2>actual &&\n-\t\ttest_i18ncmp expect actual\n+\t\ttest_cmp expect actual\n \t)\n '\n \n@@ -178,7 +178,7 @@ test_expect_success 'rename errors out early when deleting non-existent branch'\n \t\tcd test &&\n \t\techo \"error: No such remote: '\\''foo'\\''\" >expect &&\n \t\ttest_expect_code 2 git remote rename foo bar 2>actual &&\n-\t\ttest_i18ncmp expect actual\n+\t\ttest_cmp expect actual\n \t)\n '\n \n@@ -186,14 +186,14 @@ test_expect_success 'rename errors out early when when new name is invalid' '\n \ttest_config remote.foo.vcs bar &&\n \techo \"fatal: '\\''invalid...name'\\'' is not a valid remote name\" >expect &&\n \ttest_must_fail git remote rename foo invalid...name 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'add existing foreign_vcs remote' '\n \ttest_config remote.foo.vcs bar &&\n \techo \"error: remote foo already exists.\" >expect &&\n \ttest_expect_code 3 git remote add foo bar 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'add existing foreign_vcs remote' '\n@@ -201,13 +201,13 @@ test_expect_success 'add existing foreign_vcs remote' '\n \ttest_config remote.bar.vcs bar &&\n \techo \"error: remote bar already exists.\" >expect &&\n \ttest_expect_code 3 git remote rename foo bar 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'add invalid foreign_vcs remote' '\n \techo \"fatal: '\\''invalid...name'\\'' is not a valid remote name\" >expect &&\n \ttest_must_fail git remote add invalid...name bar 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n cat >test/expect <<EOF\n@@ -267,7 +267,7 @@ test_expect_success 'show' '\n \t\tgit config --add remote.two.push refs/heads/main:refs/heads/another &&\n \t\tgit remote show origin two >output &&\n \t\tgit branch -d rebase octopus &&\n-\t\ttest_i18ncmp expect output\n+\t\ttest_cmp expect output\n \t)\n '\n \n@@ -294,7 +294,7 @@ test_expect_success 'show -n' '\n \t\tcd test &&\n \t\tgit remote show -n origin >output &&\n \t\tmv ../one.unreachable ../one &&\n-\t\ttest_i18ncmp expect output\n+\t\ttest_cmp expect output\n \t)\n '\n \n@@ -337,7 +337,7 @@ test_expect_success 'set-head --auto has no problem w/multiple HEADs' '\n \t\tgit fetch two \"refs/heads/*:refs/remotes/two/*\" &&\n \t\tgit remote set-head --auto two >output 2>&1 &&\n \t\techo \"two/HEAD set to main\" >expect &&\n-\t\ttest_i18ncmp expect output\n+\t\ttest_cmp expect output\n \t)\n '\n \n@@ -369,7 +369,7 @@ test_expect_success 'prune --dry-run' '\n \t\tgit remote prune --dry-run origin >output &&\n \t\tgit rev-parse refs/remotes/origin/side2 &&\n \t\ttest_must_fail git rev-parse refs/remotes/origin/side &&\n-\t\ttest_i18ncmp expect output\n+\t\ttest_cmp expect output\n \t)\n '\n \ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex a37df6d35a7..ad1d1d5fd6b 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -1051,7 +1051,7 @@ test_expect_success 'fetch --prune prints the remotes url' '\n \t\tgit fetch --prune origin 2>&1 | head -n1 >../actual\n \t) &&\n \techo \"From ${D}/.\" >expect &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'branchname D/F conflict resolved by --prune' '\ndiff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh\nindex 4a568a2398e..f53f58895a1 100755\n--- a/t/t5512-ls-remote.sh\n+++ b/t/t5512-ls-remote.sh\n@@ -150,7 +150,7 @@ test_expect_success 'confuses pattern as remote when no remote specified' '\n \t# We could just as easily have used \"main\"; the \"*\" emphasizes its\n \t# role as a pattern.\n \ttest_must_fail git ls-remote \"$does_not_exist\" >actual 2>&1 &&\n-\ttest_i18ncmp exp actual\n+\ttest_cmp exp actual\n '\n \n test_expect_success 'die with non-2 for wrong repository even with --exit-code' '\ndiff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh\nindex 11513ec15e1..ed11569d8d7 100755\n--- a/t/t5526-fetch-submodules.sh\n+++ b/t/t5526-fetch-submodules.sh\n@@ -74,7 +74,7 @@ test_expect_success \"fetch --recurse-submodules recurses into submodules\" '\n \t\tgit fetch --recurse-submodules >../actual.out 2>../actual.err\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err actual.err\n+\ttest_cmp expect.err actual.err\n '\n \n test_expect_success \"submodule.recurse option triggers recursive fetch\" '\n@@ -84,7 +84,7 @@ test_expect_success \"submodule.recurse option triggers recursive fetch\" '\n \t\tgit -c submodule.recurse fetch >../actual.out 2>../actual.err\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err actual.err\n+\ttest_cmp expect.err actual.err\n '\n \n test_expect_success \"fetch --recurse-submodules -j2 has the same output behaviour\" '\n@@ -94,7 +94,7 @@ test_expect_success \"fetch --recurse-submodules -j2 has the same output behaviou\n \t\tGIT_TRACE=\"$TRASH_DIRECTORY/trace.out\" git fetch --recurse-submodules -j2 2>../actual.err\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err actual.err &&\n+\ttest_cmp expect.err actual.err &&\n \tgrep \"2 tasks\" trace.out\n '\n \n@@ -124,7 +124,7 @@ test_expect_success \"using fetchRecurseSubmodules=true in .gitmodules recurses i\n \t\tgit fetch >../actual.out 2>../actual.err\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err actual.err\n+\ttest_cmp expect.err actual.err\n '\n \n test_expect_success \"--no-recurse-submodules overrides .gitmodules config\" '\n@@ -155,7 +155,7 @@ test_expect_success \"--recurse-submodules overrides fetchRecurseSubmodules setti\n \t\tgit config --unset submodule.submodule.fetchRecurseSubmodules\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err actual.err\n+\ttest_cmp expect.err actual.err\n '\n \n test_expect_success \"--quiet propagates to submodules\" '\n@@ -183,7 +183,7 @@ test_expect_success \"--dry-run propagates to submodules\" '\n \t\tgit fetch --recurse-submodules --dry-run >../actual.out 2>../actual.err\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err actual.err\n+\ttest_cmp expect.err actual.err\n '\n \n test_expect_success \"Without --dry-run propagates to submodules\" '\n@@ -192,7 +192,7 @@ test_expect_success \"Without --dry-run propagates to submodules\" '\n \t\tgit fetch --recurse-submodules >../actual.out 2>../actual.err\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err actual.err\n+\ttest_cmp expect.err actual.err\n '\n \n test_expect_success \"recurseSubmodules=true propagates into submodules\" '\n@@ -203,7 +203,7 @@ test_expect_success \"recurseSubmodules=true propagates into submodules\" '\n \t\tgit fetch >../actual.out 2>../actual.err\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err actual.err\n+\ttest_cmp expect.err actual.err\n '\n \n test_expect_success \"--recurse-submodules overrides config in submodule\" '\n@@ -217,7 +217,7 @@ test_expect_success \"--recurse-submodules overrides config in submodule\" '\n \t\tgit fetch --recurse-submodules >../actual.out 2>../actual.err\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err actual.err\n+\ttest_cmp expect.err actual.err\n '\n \n test_expect_success \"--no-recurse-submodules overrides config setting\" '\n@@ -257,7 +257,7 @@ test_expect_success \"Recursion stops when no new submodule commits are fetched\"\n \t\tcd downstream &&\n \t\tgit fetch >../actual.out 2>../actual.err\n \t) &&\n-\ttest_i18ncmp expect.err.sub actual.err &&\n+\ttest_cmp expect.err.sub actual.err &&\n \ttest_must_be_empty actual.out\n '\n \n@@ -275,7 +275,7 @@ test_expect_success \"Recursion doesn't happen when new superproject commits don'\n \t\tgit fetch >../actual.out 2>../actual.err\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err.file actual.err\n+\ttest_cmp expect.err.file actual.err\n '\n \n test_expect_success \"Recursion picks up config in submodule\" '\n@@ -303,7 +303,7 @@ test_expect_success \"Recursion picks up config in submodule\" '\n \t\t\tgit config --unset fetch.recurseSubmodules\n \t\t)\n \t) &&\n-\ttest_i18ncmp expect.err.sub actual.err &&\n+\ttest_cmp expect.err.sub actual.err &&\n \ttest_must_be_empty actual.out\n '\n \n@@ -336,7 +336,7 @@ test_expect_success \"Recursion picks up all submodules when necessary\" '\n \t\tcd downstream &&\n \t\tgit fetch >../actual.out 2>../actual.err\n \t) &&\n-\ttest_i18ncmp expect.err.2 actual.err &&\n+\ttest_cmp expect.err.2 actual.err &&\n \ttest_must_be_empty actual.out\n '\n \n@@ -392,7 +392,7 @@ test_expect_success \"'--recurse-submodules=on-demand' recurses as deep as necess\n \t\t)\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err actual.err\n+\ttest_cmp expect.err actual.err\n '\n \n test_expect_success \"'--recurse-submodules=on-demand' stops when no new submodule commits are found in the superproject (and ignores config)\" '\n@@ -409,7 +409,7 @@ test_expect_success \"'--recurse-submodules=on-demand' stops when no new submodul\n \t\tgit fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err.file actual.err\n+\ttest_cmp expect.err.file actual.err\n '\n \n test_expect_success \"'fetch.recurseSubmodules=on-demand' overrides global config\" '\n@@ -437,7 +437,7 @@ test_expect_success \"'fetch.recurseSubmodules=on-demand' overrides global config\n \t\tgit config --unset fetch.recurseSubmodules\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err.2 actual.err\n+\ttest_cmp expect.err.2 actual.err\n '\n \n test_expect_success \"'submodule.<sub>.fetchRecurseSubmodules=on-demand' overrides fetch.recurseSubmodules\" '\n@@ -465,7 +465,7 @@ test_expect_success \"'submodule.<sub>.fetchRecurseSubmodules=on-demand' override\n \t\tgit config --unset submodule.submodule.fetchRecurseSubmodules\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err.2 actual.err\n+\ttest_cmp expect.err.2 actual.err\n '\n \n test_expect_success \"don't fetch submodule when newly recorded commits are already present\" '\n@@ -484,7 +484,7 @@ test_expect_success \"don't fetch submodule when newly recorded commits are alrea\n \t\tgit fetch >../actual.out 2>../actual.err\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err actual.err &&\n+\ttest_cmp expect.err actual.err &&\n \t(\n \t\tcd submodule &&\n \t\tgit checkout -q sub\n@@ -520,7 +520,7 @@ test_expect_success \"'fetch.recurseSubmodules=on-demand' works also without .git\n \t\tgit reset --hard\n \t) &&\n \ttest_must_be_empty actual.out &&\n-\ttest_i18ncmp expect.err.2 actual.err &&\n+\ttest_cmp expect.err.2 actual.err &&\n \tgit checkout HEAD^ -- .gitmodules &&\n \tgit add .gitmodules &&\n \tgit commit -m \"new submodule restored .gitmodules\"\ndiff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh\nindex bc5ccf233f0..c024fa28183 100755\n--- a/t/t5541-http-push-smart.sh\n+++ b/t/t5541-http-push-smart.sh\n@@ -118,7 +118,7 @@ test_expect_success 'rejected update prints status' '\n \tgit commit -m dev2 &&\n \ttest_must_fail git push origin dev2 2>act &&\n \tsed -e \"/^remote: /s/ *$//\" <act >cmp &&\n-\ttest_i18ncmp exp cmp\n+\ttest_cmp exp cmp\n '\n rm -f \"$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update\"\n \ndiff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh\nindex 6249420a806..90b2be9b2ed 100755\n--- a/t/t6020-bundle-misc.sh\n+++ b/t/t6020-bundle-misc.sh\n@@ -175,7 +175,7 @@ test_expect_success 'create bundle from special rev: main^!' '\n \tcat >expect <<-\\EOF &&\n \t<COMMIT-P> refs/heads/main\n \tEOF\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \tgit bundle verify special-rev.bdl |\n \t\tmake_user_friendly_and_stable_output >actual &&\n@@ -185,7 +185,7 @@ test_expect_success 'create bundle from special rev: main^!' '\n \tThe bundle requires this ref:\n \t<COMMIT-O>\n \tEOF\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \ttest_bundle_object_count special-rev.bdl 3\n '\n@@ -207,7 +207,7 @@ test_expect_success 'create bundle with --max-count option' '\n \tThe bundle requires this ref:\n \t<COMMIT-O>\n \tEOF\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \ttest_bundle_object_count max-count.bdl 4\n '\n@@ -236,7 +236,7 @@ test_expect_success 'create bundle with --since option' '\n \t<COMMIT-M>\n \t<COMMIT-K>\n \tEOF\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \ttest_bundle_object_count --thin since.bdl 13\n '\n@@ -262,11 +262,11 @@ test_expect_success 'create bundle 1 - no prerequisites' '\n \t# verify bundle, which has no prerequisites\n \tgit bundle verify 1.bdl |\n \t\tmake_user_friendly_and_stable_output >actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \tgit bundle verify stdin-1.bdl |\n \t\tmake_user_friendly_and_stable_output >actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \ttest_bundle_object_count       1.bdl 24 &&\n \ttest_bundle_object_count stdin-1.bdl 24\n@@ -304,11 +304,11 @@ test_expect_success 'create bundle 2 - has prerequisites' '\n \n \tgit bundle verify 2.bdl |\n \t\tmake_user_friendly_and_stable_output >actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \tgit bundle verify stdin-2.bdl |\n \t\tmake_user_friendly_and_stable_output >actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \ttest_bundle_object_count       2.bdl 16 &&\n \ttest_bundle_object_count stdin-2.bdl 16\n@@ -326,11 +326,11 @@ test_expect_success 'fail to verify bundle without prerequisites' '\n \n \ttest_must_fail git -C test1.git bundle verify ../2.bdl 2>&1 |\n \t\tmake_user_friendly_and_stable_output >actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \ttest_must_fail git -C test1.git bundle verify ../stdin-2.bdl 2>&1 |\n \t\tmake_user_friendly_and_stable_output >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'create bundle 3 - two refs, same object' '\n@@ -363,11 +363,11 @@ test_expect_success 'create bundle 3 - two refs, same object' '\n \n \tgit bundle verify 3.bdl |\n \t\tmake_user_friendly_and_stable_output >actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \tgit bundle verify stdin-3.bdl |\n \t\tmake_user_friendly_and_stable_output >actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \ttest_bundle_object_count       3.bdl 4 &&\n \ttest_bundle_object_count stdin-3.bdl 4\n@@ -404,11 +404,11 @@ test_expect_success 'create bundle 4 - with tags' '\n \n \tgit bundle verify 4.bdl |\n \t\tmake_user_friendly_and_stable_output >actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \tgit bundle verify stdin-4.bdl |\n \t\tmake_user_friendly_and_stable_output >actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \n \ttest_bundle_object_count       4.bdl 3 &&\n \ttest_bundle_object_count stdin-4.bdl 3\ndiff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh\nindex 7bcde054d73..ef7bdcedf23 100755\n--- a/t/t6030-bisect-porcelain.sh\n+++ b/t/t6030-bisect-porcelain.sh\n@@ -829,7 +829,7 @@ test_expect_success 'bisect terms needs 0 or 1 argument' '\n \ttest_must_fail git bisect terms 1 2 &&\n \ttest_must_fail git bisect terms 2>actual &&\n \techo \"error: no terms defined\" >expected &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'bisect terms shows good/bad after start' '\n@@ -903,7 +903,7 @@ test_expect_success 'bisect start --term-* does store terms' '\n \tYour current terms are two for the old state\n \tand one for the new state.\n \tEOF\n-\ttest_i18ncmp expected actual &&\n+\ttest_cmp expected actual &&\n \tgit bisect terms --term-bad >actual &&\n \techo one >expected &&\n \ttest_cmp expected actual &&\ndiff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh\nindex a160b2bf99c..a313849406d 100755\n--- a/t/t6040-tracking-info.sh\n+++ b/t/t6040-tracking-info.sh\n@@ -57,7 +57,7 @@ test_expect_success 'branch -v' '\n \t\tgit branch -v\n \t) |\n \tsed -n -e \"$t6040_script\" >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n cat >expect <<\\EOF\n@@ -75,7 +75,7 @@ test_expect_success 'branch -vv' '\n \t\tgit branch -vv\n \t) |\n \tsed -n -e \"$t6040_script\" >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'checkout (diverged from upstream)' '\n@@ -146,7 +146,7 @@ test_expect_success 'status -s -b (diverged from upstream)' '\n \t\tgit checkout b1 >/dev/null &&\n \t\tgit status -s -b | head -1\n \t) >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n cat >expect <<\\EOF\n@@ -159,7 +159,7 @@ test_expect_success 'status -s -b --no-ahead-behind (diverged from upstream)' '\n \t\tgit checkout b1 >/dev/null &&\n \t\tgit status -s -b --no-ahead-behind | head -1\n \t) >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n cat >expect <<\\EOF\n@@ -172,7 +172,7 @@ test_expect_success 'status.aheadbehind=false status -s -b (diverged from upstre\n \t\tgit checkout b1 >/dev/null &&\n \t\tgit -c status.aheadbehind=false status -s -b | head -1\n \t) >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n cat >expect <<\\EOF\n@@ -187,7 +187,7 @@ test_expect_success 'status --long --branch' '\n \t\tgit checkout b1 >/dev/null &&\n \t\tgit status --long -b | head -3\n \t) >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'status --long --branch' '\n@@ -196,7 +196,7 @@ test_expect_success 'status --long --branch' '\n \t\tgit checkout b1 >/dev/null &&\n \t\tgit -c status.aheadbehind=true status --long -b | head -3\n \t) >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n cat >expect <<\\EOF\n@@ -210,7 +210,7 @@ test_expect_success 'status --long --branch --no-ahead-behind' '\n \t\tgit checkout b1 >/dev/null &&\n \t\tgit status --long -b --no-ahead-behind | head -2\n \t) >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'status.aheadbehind=false status --long --branch' '\n@@ -219,7 +219,7 @@ test_expect_success 'status.aheadbehind=false status --long --branch' '\n \t\tgit checkout b1 >/dev/null &&\n \t\tgit -c status.aheadbehind=false status --long -b | head -2\n \t) >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n cat >expect <<\\EOF\n@@ -232,7 +232,7 @@ test_expect_success 'status -s -b (upstream is gone)' '\n \t\tgit checkout b5 >/dev/null &&\n \t\tgit status -s -b | head -1\n \t) >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n cat >expect <<\\EOF\n@@ -245,7 +245,7 @@ test_expect_success 'status -s -b (up-to-date with upstream)' '\n \t\tgit checkout b6 >/dev/null &&\n \t\tgit status -s -b | head -1\n \t) >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'fail to track lightweight tags' '\ndiff --git a/t/t6120-describe.sh b/t/t6120-describe.sh\nindex 546796f8473..e89b6747bee 100755\n--- a/t/t6120-describe.sh\n+++ b/t/t6120-describe.sh\n@@ -136,7 +136,7 @@ warning: tag 'Q' is externally known as 'A'\n EOF\n check_describe A-* HEAD\n test_expect_success 'warning was displayed for Q' '\n-\ttest_i18ncmp err.expect err.actual\n+\ttest_cmp err.expect err.actual\n '\n test_expect_success 'misnamed annotated tag forces long output' '\n \tdescription=$(git describe --no-long Q^0) &&\ndiff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh\nindex c6706684098..0f1cb49cedc 100755\n--- a/t/t6134-pathspec-in-submodule.sh\n+++ b/t/t6134-pathspec-in-submodule.sh\n@@ -21,7 +21,7 @@ EOF\n test_expect_success 'error message for path inside submodule' '\n \techo a >sub/a &&\n \ttest_must_fail git add sub/a 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'error message for path inside submodule from within submodule' '\ndiff --git a/t/t6301-for-each-ref-errors.sh b/t/t6301-for-each-ref-errors.sh\nindex 809854fc0ce..40edf9dab53 100755\n--- a/t/t6301-for-each-ref-errors.sh\n+++ b/t/t6301-for-each-ref-errors.sh\n@@ -20,8 +20,8 @@ test_expect_success 'Broken refs are reported correctly' '\n \ttest_when_finished \"rm -f .git/$r\" &&\n \techo \"warning: ignoring broken ref $r\" >broken-err &&\n \tgit for-each-ref >out 2>err &&\n-\ttest_i18ncmp full-list out &&\n-\ttest_i18ncmp broken-err err\n+\ttest_cmp full-list out &&\n+\ttest_cmp broken-err err\n '\n \n test_expect_success 'NULL_SHA1 refs are reported correctly' '\n@@ -31,10 +31,10 @@ test_expect_success 'NULL_SHA1 refs are reported correctly' '\n \techo \"warning: ignoring broken ref $r\" >zeros-err &&\n \tgit for-each-ref >out 2>err &&\n \ttest_cmp full-list out &&\n-\ttest_i18ncmp zeros-err err &&\n+\ttest_cmp zeros-err err &&\n \tgit for-each-ref --format=\"%(objectname) %(refname)\" >brief-out 2>brief-err &&\n \ttest_cmp brief-list brief-out &&\n-\ttest_i18ncmp zeros-err brief-err\n+\ttest_cmp zeros-err brief-err\n '\n \n test_expect_success 'Missing objects are reported correctly' '\n@@ -43,7 +43,7 @@ test_expect_success 'Missing objects are reported correctly' '\n \ttest_when_finished \"rm -f .git/$r\" &&\n \techo \"fatal: missing object $MISSING for $r\" >missing-err &&\n \ttest_must_fail git for-each-ref 2>err &&\n-\ttest_i18ncmp missing-err err &&\n+\ttest_cmp missing-err err &&\n \t(\n \t\tcat brief-list &&\n \t\techo \"$MISSING $r\"\ndiff --git a/t/t6436-merge-overwrite.sh b/t/t6436-merge-overwrite.sh\nindex 362ae37a122..84b4aacf496 100755\n--- a/t/t6436-merge-overwrite.sh\n+++ b/t/t6436-merge-overwrite.sh\n@@ -139,7 +139,7 @@ test_expect_success 'will not overwrite untracked file in leading path' '\n \tcp important sub &&\n \tcp important sub2 &&\n \ttest_must_fail git merge sub 2>out &&\n-\ttest_i18ncmp out expect &&\n+\ttest_cmp out expect &&\n \ttest_path_is_missing .git/MERGE_HEAD &&\n \ttest_cmp important sub &&\n \ttest_cmp important sub2 &&\n@@ -174,7 +174,7 @@ test_expect_success 'will not overwrite untracked file on unborn branch' '\n \tgit checkout --orphan new &&\n \tcp important c0.c &&\n \ttest_must_fail git merge c0 2>out &&\n-\ttest_i18ncmp out expect\n+\ttest_cmp out expect\n '\n \n test_expect_success 'will not overwrite untracked file on unborn branch .git/MERGE_HEAD sanity etc.' '\ndiff --git a/t/t6439-merge-co-error-msgs.sh b/t/t6439-merge-co-error-msgs.sh\nindex e176475ed5b..5bfb027099a 100755\n--- a/t/t6439-merge-co-error-msgs.sh\n+++ b/t/t6439-merge-co-error-msgs.sh\n@@ -40,14 +40,14 @@ EOF\n \n test_expect_success 'untracked files overwritten by merge (fast and non-fast forward)' '\n \ttest_must_fail git merge branch 2>out &&\n-\ttest_i18ncmp out expect &&\n+\ttest_cmp out expect &&\n \tgit commit --allow-empty -m empty &&\n \t(\n \t\tGIT_MERGE_VERBOSITY=0 &&\n \t\texport GIT_MERGE_VERBOSITY &&\n \t\ttest_must_fail git merge branch 2>out2\n \t) &&\n-\ttest_i18ncmp out2 expect &&\n+\ttest_cmp out2 expect &&\n \tgit reset --hard HEAD^\n '\n \n@@ -68,7 +68,7 @@ test_expect_success 'untracked files or local changes ovewritten by merge' '\n \tgit add three &&\n \tgit add four &&\n \ttest_must_fail git merge branch 2>out &&\n-\ttest_i18ncmp out expect\n+\ttest_cmp out expect\n '\n \n cat >expect <<\\EOF\n@@ -90,7 +90,7 @@ test_expect_success 'cannot switch branches because of local changes' '\n \techo uno >rep/one &&\n \techo dos >rep/two &&\n \ttest_must_fail git checkout branch 2>out &&\n-\ttest_i18ncmp out expect\n+\ttest_cmp out expect\n '\n \n cat >expect <<\\EOF\n@@ -104,7 +104,7 @@ EOF\n test_expect_success 'not uptodate file porcelain checkout error' '\n \tgit add rep/one rep/two &&\n \ttest_must_fail git checkout branch 2>out &&\n-\ttest_i18ncmp out expect\n+\ttest_cmp out expect\n '\n \n cat >expect <<\\EOF\n@@ -135,7 +135,7 @@ test_expect_success 'not_uptodate_dir porcelain checkout error' '\n \t>rep/untracked-file &&\n \t>rep2/untracked-file &&\n \ttest_must_fail git checkout branch 2>out &&\n-\ttest_i18ncmp out ../expect\n+\ttest_cmp out ../expect\n '\n \n test_done\ndiff --git a/t/t7001-mv.sh b/t/t7001-mv.sh\nindex 63d5f41a124..080a6530831 100755\n--- a/t/t7001-mv.sh\n+++ b/t/t7001-mv.sh\n@@ -446,7 +446,7 @@ test_expect_success 'mv issues a warning when section is not found in .gitmodule\n \techo \"warning: Could not find section in .gitmodules where path=sub\" >expect.err &&\n \tmkdir mod &&\n \tgit mv sub mod/sub 2>actual.err &&\n-\ttest_i18ncmp expect.err actual.err &&\n+\ttest_cmp expect.err actual.err &&\n \t! test -e sub &&\n \t[ \"$entry\" = \"$(git ls-files --stage mod/sub | cut -f 1)\" ] &&\n \t(\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex 943a7d5c1db..bbd2b7b6f35 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -91,7 +91,7 @@ test_expect_success 'creating a tag with --create-reflog should create reflog' '\n \tgit tag --create-reflog tag_with_reflog &&\n \tgit reflog exists refs/tags/tag_with_reflog &&\n \tsed -e \"s/^.*\t//\" .git/logs/refs/tags/tag_with_reflog >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'annotated tag with --create-reflog has correct message' '\n@@ -102,7 +102,7 @@ test_expect_success 'annotated tag with --create-reflog has correct message' '\n \tgit tag -m \"annotated tag\" --create-reflog tag_with_reflog &&\n \tgit reflog exists refs/tags/tag_with_reflog &&\n \tsed -e \"s/^.*\t//\" .git/logs/refs/tags/tag_with_reflog >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success '--create-reflog does not create reflog on failure' '\n@@ -1409,7 +1409,7 @@ test_expect_success 'message in editor has initial comment: first line' '\n \t# check the first line --- should be empty\n \techo >first.expect &&\n \tsed -e 1q <actual >first.actual &&\n-\ttest_i18ncmp first.expect first.actual\n+\ttest_cmp first.expect first.actual\n '\n \n test_expect_success \\\n@@ -1732,7 +1732,7 @@ test_expect_success 'recursive tagging should give advice' '\n \thint: Disable this message with \"git config advice.nestedTag false\"\n \tEOF\n \tgit tag -m nested nested annotated-v4.0 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'multiple --points-at are OR-ed together' '\ndiff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh\nindex e5c6a038fbf..f2a8e765114 100755\n--- a/t/t7012-skip-worktree-writing.sh\n+++ b/t/t7012-skip-worktree-writing.sh\n@@ -125,13 +125,13 @@ EOF\n test_expect_success 'git-clean, absent case' '\n \tsetup_absent &&\n \tgit clean -n > result &&\n-\ttest_i18ncmp expected result\n+\ttest_cmp expected result\n '\n \n test_expect_success 'git-clean, dirty case' '\n \tsetup_dirty &&\n \tgit clean -n > result &&\n-\ttest_i18ncmp expected result\n+\ttest_cmp expected result\n '\n \n test_expect_success '--ignore-skip-worktree-entries leaves worktree alone' '\ndiff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh\nindex 72fb418b890..0f4344c55e6 100755\n--- a/t/t7060-wtstatus.sh\n+++ b/t/t7060-wtstatus.sh\n@@ -56,9 +56,9 @@ EOF\n \t\tgit commit -m delete &&\n \t\ttest_must_fail git merge main &&\n \t\ttest_must_fail git commit --dry-run >../actual &&\n-\t\ttest_i18ncmp ../expect ../actual &&\n+\t\ttest_cmp ../expect ../actual &&\n \t\tgit status >../actual &&\n-\t\ttest_i18ncmp ../expect ../actual\n+\t\ttest_cmp ../expect ../actual\n \t)\n '\n \n@@ -151,7 +151,7 @@ Unmerged paths:\n no changes added to commit (use \"git add\" and/or \"git commit -a\")\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -185,7 +185,7 @@ Unmerged paths:\n no changes added to commit (use \"git add\" and/or \"git commit -a\")\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -210,7 +210,7 @@ Unmerged paths:\n Untracked files not listed (use -u option to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual &&\n+\ttest_cmp expected actual &&\n \tgit reset --hard &&\n \tgit checkout main\n '\n@@ -227,7 +227,7 @@ test_expect_success 'status --branch with detached HEAD' '\n \t?? expected\n \t?? mdconflict/\n \tEOF\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n ## Duplicate the above test and verify --porcelain=v1 arg parsing.\n@@ -243,7 +243,7 @@ test_expect_success 'status --porcelain=v1 --branch with detached HEAD' '\n \t?? expected\n \t?? mdconflict/\n \tEOF\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n ## Verify parser error on invalid --porcelain argument.\ndiff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh\nindex f01bf277279..accefde72fb 100755\n--- a/t/t7063-status-untracked-cache.sh\n+++ b/t/t7063-status-untracked-cache.sh\n@@ -731,7 +731,7 @@ test_expect_success 'test ident field is working' '\n \tcp -R done dthree dtwo four three ../other_worktree &&\n \tGIT_WORK_TREE=../other_worktree git status 2>../err &&\n \techo \"warning: untracked cache is disabled on this system or location\" >../expect &&\n-\ttest_i18ncmp ../expect ../err\n+\ttest_cmp ../expect ../err\n '\n \n test_expect_success 'untracked cache survives a checkout' '\ndiff --git a/t/t7102-reset.sh b/t/t7102-reset.sh\nindex 7d8fb188ee5..601b2bf97f0 100755\n--- a/t/t7102-reset.sh\n+++ b/t/t7102-reset.sh\n@@ -75,14 +75,14 @@ test_expect_success 'reset --hard message' '\n \thex=$(git log -1 --format=\"%h\") &&\n \tgit reset --hard >.actual &&\n \techo HEAD is now at $hex $(commit_msg) >.expected &&\n-\ttest_i18ncmp .expected .actual\n+\ttest_cmp .expected .actual\n '\n \n test_expect_success 'reset --hard message (ISO8859-1 logoutputencoding)' '\n \thex=$(git log -1 --format=\"%h\") &&\n \tgit -c \"i18n.logOutputEncoding=$test_encoding\" reset --hard >.actual &&\n \techo HEAD is now at $hex $(commit_msg $test_encoding) >.expected &&\n-\ttest_i18ncmp .expected .actual\n+\ttest_cmp .expected .actual\n '\n \n test_expect_success 'giving a non existing revision should fail' '\n@@ -469,7 +469,7 @@ test_expect_success '--mixed refreshes the index' '\n \tEOF\n \techo 123 >>file2 &&\n \tgit reset --mixed HEAD >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'resetting specific path that is unmerged' '\ndiff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh\nindex d44f6962933..a924fdb7a6c 100755\n--- a/t/t7400-submodule-basic.sh\n+++ b/t/t7400-submodule-basic.sh\n@@ -55,7 +55,7 @@ test_expect_success 'add aborts on repository with no commits' '\n \tEOF\n \tgit init repo-no-commits &&\n \ttest_must_fail git submodule add ../a ./repo-no-commits 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'status should ignore inner git repo when not added' '\n@@ -185,7 +185,7 @@ test_expect_success 'submodule add to .gitignored path fails' '\n \t\tgit add --force .gitignore &&\n \t\tgit commit -m\"Ignore everything\" &&\n \t\t! git submodule add \"$submodurl\" submod >actual 2>&1 &&\n-\t\ttest_i18ncmp expect actual\n+\t\ttest_cmp expect actual\n \t)\n '\n \ndiff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh\nindex 76088147089..9c3cc4cf404 100755\n--- a/t/t7401-submodule-summary.sh\n+++ b/t/t7401-submodule-summary.sh\n@@ -190,7 +190,7 @@ test_expect_success 'typechanged submodule(submodule->blob), --cached' \"\n \t  < Add foo5\n \n \tEOF\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n \"\n \n test_expect_success 'typechanged submodule(submodule->blob), --files' \"\n@@ -200,7 +200,7 @@ test_expect_success 'typechanged submodule(submodule->blob), --files' \"\n \t  > Add foo5\n \n \tEOF\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n \"\n \n rm -rf sm1 &&\n@@ -211,7 +211,7 @@ test_expect_success 'typechanged submodule(submodule->blob)' \"\n \t* sm1 $head4(submodule)->$head5(blob):\n \n \tEOF\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n \"\n \n rm -f sm1 &&\n@@ -224,7 +224,7 @@ test_expect_success 'nonexistent commit' \"\n \t  Warn: sm1 doesn't contain commit $head4_full\n \n \tEOF\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n \"\n \n commit_file\n@@ -235,7 +235,7 @@ test_expect_success 'typechanged submodule(blob->submodule)' \"\n \t  > Add foo7\n \n \tEOF\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n \"\n \n commit_file sm1 &&\n@@ -292,7 +292,7 @@ test_expect_success 'given commit' \"\n \n test_expect_success '--for-status' \"\n \tgit submodule summary --for-status HEAD^ >actual &&\n-\ttest_i18ncmp - actual <<-EOF\n+\ttest_cmp - actual <<-EOF\n \t* sm1 $head6...0000000:\n \n \t* sm2 0000000...$head7 (2):\ndiff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh\nindex b9c1624fba9..ff3ba5422e9 100755\n--- a/t/t7406-submodule-update.sh\n+++ b/t/t7406-submodule-update.sh\n@@ -155,9 +155,9 @@ test_expect_success 'submodule update --init --recursive from subdirectory' '\n \t cd tmp &&\n \t git submodule update --init --recursive ../super >../../actual 2>../../actual2\n \t) &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \tsort actual2 >actual2.sorted &&\n-\ttest_i18ncmp expect2 actual2.sorted\n+\ttest_cmp expect2 actual2.sorted\n '\n \n cat <<EOF >expect2\n@@ -174,7 +174,7 @@ test_expect_success 'submodule update --init from and of subdirectory' '\n \t  git submodule update --init sub 2>../../actual2\n \t )\n \t) &&\n-\ttest_i18ncmp expect2 actual2\n+\ttest_cmp expect2 actual2\n '\n \n test_expect_success 'submodule update does not fetch already present commits' '\n@@ -192,7 +192,7 @@ test_expect_success 'submodule update does not fetch already present commits' '\n \t(cd super &&\n \t  git submodule update > ../actual 2> ../actual.err\n \t) &&\n-\ttest_i18ncmp expected actual &&\n+\ttest_cmp expected actual &&\n \ttest_must_be_empty actual.err\n '\n \n@@ -461,7 +461,7 @@ test_expect_success 'submodule update - command in .git/config catches failure'\n \t(cd super &&\n \t test_must_fail git submodule update submodule 2>../actual\n \t) &&\n-\ttest_i18ncmp actual expect\n+\ttest_cmp actual expect\n '\n \n cat << EOF >expect\n@@ -479,7 +479,7 @@ test_expect_success 'submodule update - command in .git/config catches failure -\n \t mkdir tmp && cd tmp &&\n \t test_must_fail git submodule update ../submodule 2>../../actual\n \t) &&\n-\ttest_i18ncmp actual expect\n+\ttest_cmp actual expect\n '\n \n test_expect_success 'submodule update - command run for initial population of submodule' '\n@@ -488,7 +488,7 @@ test_expect_success 'submodule update - command run for initial population of su\n \tEOF\n \trm -rf super/submodule &&\n \ttest_must_fail git -C super submodule update 2>actual &&\n-\ttest_i18ncmp expect actual &&\n+\ttest_cmp expect actual &&\n \tgit -C super submodule update --checkout\n '\n \n@@ -509,7 +509,7 @@ test_expect_success 'recursive submodule update - command in .git/config catches\n \t mkdir -p tmp && cd tmp &&\n \t test_must_fail git submodule update --recursive ../super 2>../../actual\n \t) &&\n-\ttest_i18ncmp actual expect\n+\ttest_cmp actual expect\n '\n \n test_expect_success 'submodule init does not copy command into .git/config' '\ndiff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh\nindex 79981b51eb8..e2f110b7863 100755\n--- a/t/t7407-submodule-foreach.sh\n+++ b/t/t7407-submodule-foreach.sh\n@@ -80,7 +80,7 @@ test_expect_success 'test basic \"submodule foreach\" usage' '\n \t\tgit config foo.bar zar &&\n \t\tgit submodule foreach \"git config --file \\\"\\$toplevel/.git/config\\\" foo.bar\"\n \t) &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n cat >expect <<EOF\n@@ -96,7 +96,7 @@ test_expect_success 'test \"submodule foreach\" from subdirectory' '\n \t\tcd clone/sub &&\n \t\tgit submodule foreach \"echo \\$toplevel-\\$name-\\$sm_path-\\$displaypath-\\$sha1\" >../../actual\n \t) &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'setup nested submodules' '\n@@ -177,7 +177,7 @@ test_expect_success 'test messages from \"foreach --recursive\"' '\n \t\tcd clone2 &&\n \t\tgit submodule foreach --recursive \"true\" > ../actual\n \t) &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n cat > expect <<EOF\n@@ -197,7 +197,7 @@ test_expect_success 'test messages from \"foreach --recursive\" from subdirectory'\n \t\tcd untracked &&\n \t\tgit submodule foreach --recursive >../../actual\n \t) &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n sub1sha1=$(cd clone2/sub1 && git rev-parse HEAD)\n sub2sha1=$(cd clone2/sub2 && git rev-parse HEAD)\n@@ -229,7 +229,7 @@ test_expect_success 'test \"submodule foreach --recursive\" from subdirectory' '\n \t\tcd clone2/untracked &&\n \t\tgit submodule foreach --recursive \"echo toplevel: \\$toplevel name: \\$name path: \\$sm_path displaypath: \\$displaypath hash: \\$sha1\" >../../actual\n \t) &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n cat > expect <<EOF\ndiff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh\nindex 6d19ece05dd..e41ac18e7e0 100755\n--- a/t/t7500-commit-template-squash-signoff.sh\n+++ b/t/t7500-commit-template-squash-signoff.sh\n@@ -15,7 +15,7 @@ commit_msg_is () {\n \n \tprintf \"%s\" \"$(git log --pretty=format:%s%b -1)\" >\"$actual\" &&\n \tprintf \"%s\" \"$1\" >\"$expect\" &&\n-\ttest_i18ncmp \"$expect\" \"$actual\"\n+\ttest_cmp \"$expect\" \"$actual\"\n }\n \n # A sanity check to see if commit is working at all.\n@@ -356,7 +356,7 @@ test_expect_success 'new line found before status message in commit template' '\n \ttouch commit-template-check &&\n \tgit add commit-template-check &&\n \tGIT_EDITOR=\"cat >editor-input\" git commit --untracked-files=no --allow-empty-message &&\n-\ttest_i18ncmp expected-template editor-input\n+\ttest_cmp expected-template editor-input\n '\n \n test_expect_success 'setup empty commit with unstaged rename and copy' '\ndiff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh\nindex e5332adc9a1..6396897cc81 100755\n--- a/t/t7502-commit-porcelain.sh\n+++ b/t/t7502-commit-porcelain.sh\n@@ -13,7 +13,7 @@ commit_msg_is () {\n \n \tprintf \"%s\" \"$(git log --pretty=format:%s%b -1)\" >$actual &&\n \tprintf \"%s\" \"$1\" >$expect &&\n-\ttest_i18ncmp $expect $actual\n+\ttest_cmp $expect $actual\n }\n \n # Arguments: [<prefix] [<commit message>] [<commit options>]\n@@ -35,7 +35,7 @@ check_summary_oneline() {\n \tSUMMARY_POSTFIX=\"$(git log -1 --pretty='format:%h')\"\n \techo \"[$SUMMARY_PREFIX $SUMMARY_POSTFIX] $2\" >exp &&\n \n-\ttest_i18ncmp exp act\n+\ttest_cmp exp act\n }\n \n test_expect_success 'output summary format' '\n@@ -300,7 +300,7 @@ echo \"sample\n # with '#' will be ignored, and an empty message aborts the commit.\" >expect\n \n test_expect_success 'cleanup commit messages (strip option,-F,-e): output' '\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'cleanup commit message (fail on invalid cleanup mode option)' '\ndiff --git a/t/t7508-status.sh b/t/t7508-status.sh\nindex d01aacb66b5..2b72451ba3e 100755\n--- a/t/t7508-status.sh\n+++ b/t/t7508-status.sh\n@@ -109,13 +109,13 @@ test_expect_success 'status --column' '\n #\n EOF\n \tCOLUMNS=50 git -c status.displayCommentPrefix=true status --column=\"column dense\" >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'status --column status.displayCommentPrefix=false' '\n \tstrip_comments expect &&\n \tCOLUMNS=49 git -c status.displayCommentPrefix=false status --column=\"column dense\" >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n cat >expect <<\\EOF\n@@ -144,19 +144,19 @@ EOF\n \n test_expect_success 'status with status.displayCommentPrefix=true' '\n \tgit -c status.displayCommentPrefix=true status >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'status with status.displayCommentPrefix=false' '\n \tstrip_comments expect &&\n \tgit -c status.displayCommentPrefix=false status >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'status -v' '\n \t(cat expect && git diff --cached) >expect-with-v &&\n \tgit status -v >output &&\n-\ttest_i18ncmp expect-with-v output\n+\ttest_cmp expect-with-v output\n '\n \n test_expect_success 'status -v -v' '\n@@ -167,7 +167,7 @@ test_expect_success 'status -v -v' '\n \t echo \"Changes not staged for commit:\" &&\n \t git -c diff.mnemonicprefix=true diff) >expect-with-v &&\n \tgit status -v -v >output &&\n-\ttest_i18ncmp expect-with-v output\n+\ttest_cmp expect-with-v output\n '\n \n test_expect_success 'setup fake editor' '\n@@ -214,7 +214,7 @@ EOF\n test_expect_success 'status (advice.statusHints false)' '\n \ttest_config advice.statusHints false &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n \n '\n \n@@ -296,7 +296,7 @@ Ignored files:\n \n EOF\n \tgit status --ignored >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'status with gitignore (nothing untracked)' '\n@@ -358,7 +358,7 @@ Ignored files:\n \n EOF\n \tgit status --ignored >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n cat >.gitignore <<\\EOF\n@@ -380,7 +380,7 @@ EOF\n test_expect_success 'status -s -b' '\n \n \tgit status -s -b >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n \n '\n \n@@ -390,7 +390,7 @@ test_expect_success 'status -s -z -b' '\n \tgit status -s -z -b >output &&\n \tnul_to_q <output >output.q &&\n \tmv output.q output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'setup dir3' '\n@@ -418,13 +418,13 @@ Changes not staged for commit:\n Untracked files not listed (use -u option to show untracked files)\n EOF\n \tgit status -uno >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'status (status.showUntrackedFiles no)' '\n \ttest_config status.showuntrackedfiles no &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'status -uno (advice.statusHints false)' '\n@@ -443,7 +443,7 @@ Untracked files not listed\n EOF\n \ttest_config advice.statusHints false &&\n \tgit status -uno >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n cat >expect << EOF\n@@ -487,13 +487,13 @@ Untracked files:\n \n EOF\n \tgit status -unormal >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'status (status.showUntrackedFiles normal)' '\n \ttest_config status.showuntrackedfiles normal &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n cat >expect <<EOF\n@@ -543,13 +543,13 @@ Untracked files:\n \n EOF\n \tgit status -uall >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'status (status.showUntrackedFiles all)' '\n \ttest_config status.showuntrackedfiles all &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'teardown dir3' '\n@@ -601,7 +601,7 @@ Untracked files:\n \n EOF\n \t(cd dir1 && git status) >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n cat >expect <<\\EOF\n@@ -670,13 +670,13 @@ Untracked files:\n EOF\n \ttest_config color.ui auto &&\n \ttest_terminal git status | test_decode_color >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success TTY 'status with color.status' '\n \ttest_config color.status auto &&\n \ttest_terminal git status | test_decode_color >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n cat >expect <<\\EOF\n@@ -718,7 +718,7 @@ EOF\n test_expect_success TTY 'status -s -b with color.status' '\n \n \ttest_terminal git status -s -b | test_decode_color >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n \n '\n \n@@ -793,7 +793,7 @@ Untracked files:\n EOF\n \ttest_config status.relativePaths false &&\n \t(cd dir1 && git status) >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n \n '\n \n@@ -860,7 +860,7 @@ Untracked files:\n \n EOF\n \tgit commit --dry-run dir1/modified >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n cat >expect <<EOF\n@@ -921,13 +921,13 @@ Untracked files:\n \n EOF\n \tgit status >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n # we expect the same as the previous test\n test_expect_success 'status --untracked-files=all does not show submodule' '\n \tgit status --untracked-files=all >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n cat >expect <<EOF\n@@ -984,13 +984,13 @@ Untracked files:\n EOF\n \tgit config status.submodulesummary 10 &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'status submodule summary with status.displayCommentPrefix=false' '\n \tstrip_comments expect &&\n \tgit -c status.displayCommentPrefix=false status >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'commit with submodule summary ignores status.displayCommentPrefix' '\n@@ -1035,9 +1035,9 @@ EOF\n \tgit commit -m \"commit submodule\" &&\n \tgit config status.submodulesummary 10 &&\n \ttest_must_fail git commit --dry-run >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n cat >expect <<EOF\n@@ -1091,7 +1091,7 @@ Untracked files:\n EOF\n \tgit config status.submodulesummary 10 &&\n \tgit commit --dry-run --amend >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success POSIXPERM,SANITY 'status succeeds in a read-only repository' '\n@@ -1143,17 +1143,17 @@ Untracked files:\n EOF\n \techo modified  sm/untracked &&\n \tgit status --ignore-submodules=untracked >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success '.gitmodules ignore=untracked suppresses submodules with untracked content' '\n \ttest_config diff.ignoreSubmodules dirty &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config --add -f .gitmodules submodule.subname.ignore untracked &&\n \tgit config --add -f .gitmodules submodule.subname.path sm &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n \n@@ -1163,14 +1163,14 @@ test_expect_success '.git/config ignore=untracked suppresses submodules with unt\n \tgit config --add submodule.subname.ignore untracked &&\n \tgit config --add submodule.subname.path sm &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config --remove-section submodule.subname &&\n \tgit config --remove-section -f .gitmodules submodule.subname\n '\n \n test_expect_success '--ignore-submodules=dirty suppresses submodules with untracked content' '\n \tgit status --ignore-submodules=dirty >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success '.gitmodules ignore=dirty suppresses submodules with untracked content' '\n@@ -1180,7 +1180,7 @@ test_expect_success '.gitmodules ignore=dirty suppresses submodules with untrack\n \tgit config --add -f .gitmodules submodule.subname.ignore dirty &&\n \tgit config --add -f .gitmodules submodule.subname.path sm &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n \n@@ -1190,7 +1190,7 @@ test_expect_success '.git/config ignore=dirty suppresses submodules with untrack\n \tgit config --add submodule.subname.ignore dirty &&\n \tgit config --add submodule.subname.path sm &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config --remove-section submodule.subname &&\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n@@ -1198,14 +1198,14 @@ test_expect_success '.git/config ignore=dirty suppresses submodules with untrack\n test_expect_success '--ignore-submodules=dirty suppresses submodules with modified content' '\n \techo modified >sm/foo &&\n \tgit status --ignore-submodules=dirty >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success '.gitmodules ignore=dirty suppresses submodules with modified content' '\n \tgit config --add -f .gitmodules submodule.subname.ignore dirty &&\n \tgit config --add -f .gitmodules submodule.subname.path sm &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n \n@@ -1215,7 +1215,7 @@ test_expect_success '.git/config ignore=dirty suppresses submodules with modifie\n \tgit config --add submodule.subname.ignore dirty &&\n \tgit config --add submodule.subname.path sm &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config --remove-section submodule.subname &&\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n@@ -1253,14 +1253,14 @@ Untracked files:\n \n EOF\n \tgit status --ignore-submodules=untracked > output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success \".gitmodules ignore=untracked doesn't suppress submodules with modified content\" '\n \tgit config --add -f .gitmodules submodule.subname.ignore untracked &&\n \tgit config --add -f .gitmodules submodule.subname.path sm &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n \n@@ -1270,7 +1270,7 @@ test_expect_success \".git/config ignore=untracked doesn't suppress submodules wi\n \tgit config --add submodule.subname.ignore untracked &&\n \tgit config --add submodule.subname.path sm &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config --remove-section submodule.subname &&\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n@@ -1314,14 +1314,14 @@ Untracked files:\n \n EOF\n \tgit status --ignore-submodules=untracked > output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success \".gitmodules ignore=untracked doesn't suppress submodule summary\" '\n \tgit config --add -f .gitmodules submodule.subname.ignore untracked &&\n \tgit config --add -f .gitmodules submodule.subname.path sm &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n \n@@ -1331,20 +1331,20 @@ test_expect_success \".git/config ignore=untracked doesn't suppress submodule sum\n \tgit config --add submodule.subname.ignore untracked &&\n \tgit config --add submodule.subname.path sm &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config --remove-section submodule.subname &&\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n \n test_expect_success \"--ignore-submodules=dirty doesn't suppress submodule summary\" '\n \tgit status --ignore-submodules=dirty > output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n test_expect_success \".gitmodules ignore=dirty doesn't suppress submodule summary\" '\n \tgit config --add -f .gitmodules submodule.subname.ignore dirty &&\n \tgit config --add -f .gitmodules submodule.subname.path sm &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n \n@@ -1354,7 +1354,7 @@ test_expect_success \".git/config ignore=dirty doesn't suppress submodule summary\n \tgit config --add submodule.subname.ignore dirty &&\n \tgit config --add submodule.subname.path sm &&\n \tgit status >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config --remove-section submodule.subname &&\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n@@ -1398,7 +1398,7 @@ EOF\n test_expect_success \"status (core.commentchar with submodule summary)\" '\n \ttest_config core.commentchar \";\" &&\n \tgit -c status.displayCommentPrefix=true status >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success \"status (core.commentchar with two chars with submodule summary)\" '\n@@ -1429,7 +1429,7 @@ Untracked files:\n no changes added to commit (use \"git add\" and/or \"git commit -a\")\n EOF\n \tgit status --ignore-submodules=all > output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success '.gitmodules ignore=all suppresses unstaged submodule summary' '\n@@ -1460,7 +1460,7 @@ EOF\n \tgit config --add -f .gitmodules submodule.subname.ignore all &&\n \tgit config --add -f .gitmodules submodule.subname.path sm &&\n \tgit status > output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n \n@@ -1470,7 +1470,7 @@ test_expect_success '.git/config ignore=all suppresses unstaged submodule summar\n \tgit config --add submodule.subname.ignore all &&\n \tgit config --add submodule.subname.path sm &&\n \tgit status > output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit config --remove-section submodule.subname &&\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n@@ -1571,7 +1571,7 @@ Changes not staged for commit:\n Untracked files not listed (use -u option to show untracked files)\n EOF\n \tgit commit -uno --dry-run >output &&\n-\ttest_i18ncmp expect output &&\n+\ttest_cmp expect output &&\n \tgit status -s --ignore-submodules=dirty >output &&\n \ttest_i18ngrep \"^M. sm\" output\n '\ndiff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh\nindex 9f5e3ce7931..7f2956d77ad 100755\n--- a/t/t7512-status-help.sh\n+++ b/t/t7512-status-help.sh\n@@ -41,7 +41,7 @@ Unmerged paths:\n no changes added to commit (use \"git add\" and/or \"git commit -a\")\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -61,7 +61,7 @@ Changes to be committed:\n Untracked files not listed (use -u option to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -93,7 +93,7 @@ Unmerged paths:\n no changes added to commit (use \"git add\" and/or \"git commit -a\")\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -116,7 +116,7 @@ Changes to be committed:\n Untracked files not listed (use -u option to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -154,7 +154,7 @@ Unmerged paths:\n no changes added to commit (use \"git add\" and/or \"git commit -a\")\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -180,7 +180,7 @@ Changes to be committed:\n Untracked files not listed (use -u option to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -210,7 +210,7 @@ You are currently editing a commit while rebasing branch '\\''rebase_i_edit'\\'' o\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -249,7 +249,7 @@ Changes not staged for commit:\n no changes added to commit (use \"git add\" and/or \"git commit -a\")\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -282,7 +282,7 @@ You are currently editing a commit while rebasing branch '\\''amend_last'\\'' on '\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -321,7 +321,7 @@ You are currently editing a commit while rebasing branch '\\''several_edits'\\'' o\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -356,7 +356,7 @@ Changes not staged for commit:\n no changes added to commit (use \"git add\" and/or \"git commit -a\")\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -387,7 +387,7 @@ You are currently editing a commit while rebasing branch '\\''several_edits'\\'' o\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -418,7 +418,7 @@ You are currently editing a commit while rebasing branch '\\''several_edits'\\'' o\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -454,7 +454,7 @@ Changes not staged for commit:\n no changes added to commit (use \"git add\" and/or \"git commit -a\")\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -486,7 +486,7 @@ You are currently editing a commit while rebasing branch '\\''several_edits'\\'' o\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -519,7 +519,7 @@ You are currently editing a commit while rebasing branch '\\''several_edits'\\'' o\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -557,7 +557,7 @@ Changes not staged for commit:\n no changes added to commit (use \"git add\" and/or \"git commit -a\")\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -591,7 +591,7 @@ You are currently editing a commit while rebasing branch '\\''several_edits'\\'' o\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -619,7 +619,7 @@ You are in the middle of an am session.\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -641,7 +641,7 @@ You are in the middle of an am session.\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -664,7 +664,7 @@ The current patch is empty.\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -687,7 +687,7 @@ You are currently bisecting, started from branch '\\''bisect'\\''.\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -712,7 +712,7 @@ Unmerged paths:\n no changes added to commit\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -746,7 +746,7 @@ Unmerged paths:\n no changes added to commit (use \"git add\" and/or \"git commit -a\")\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n \n@@ -770,7 +770,7 @@ Changes to be committed:\n Untracked files not listed (use -u option to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'status when cherry-picking after committing conflict resolution' '\n@@ -789,7 +789,7 @@ Cherry-pick currently in progress.\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'status shows cherry-pick with invalid oid' '\n@@ -798,7 +798,7 @@ test_expect_success 'status shows cherry-pick with invalid oid' '\n \tgit status --untracked-files=no >actual 2>err &&\n \tgit cherry-pick --quit &&\n \ttest_must_be_empty err &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'status does not show error if .git/sequencer is a file' '\n@@ -816,7 +816,7 @@ HEAD detached at atag\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual &&\n+\ttest_cmp expected actual &&\n \n \tgit reset --hard HEAD^ &&\n \tcat >expected <<\\EOF &&\n@@ -824,7 +824,7 @@ HEAD detached from atag\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'status while reverting commit (conflicts)' '\n@@ -852,7 +852,7 @@ Unmerged paths:\n no changes added to commit (use \"git add\" and/or \"git commit -a\")\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'status while reverting commit (conflicts resolved)' '\n@@ -872,7 +872,7 @@ Changes to be committed:\n Untracked files not listed (use -u option to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'status after reverting commit' '\n@@ -882,7 +882,7 @@ On branch main\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'status while reverting after committing conflict resolution' '\n@@ -901,7 +901,7 @@ Revert currently in progress.\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'prepare for different number of commits rebased' '\n@@ -931,7 +931,7 @@ You are currently editing a commit while rebasing branch '\\''several_commits'\\''\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'status: two commands done with some white lines in done file' '\n@@ -959,7 +959,7 @@ You are currently editing a commit while rebasing branch '\\''several_commits'\\''\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'status: two remaining commands with some white lines in todo file' '\n@@ -988,7 +988,7 @@ You are currently editing a commit while rebasing branch '\\''several_commits'\\''\n nothing to commit (use -u to show untracked files)\n EOF\n \tgit status --untracked-files=no >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'status: handle not-yet-started rebase -i gracefully' '\n@@ -1007,7 +1007,7 @@ You are currently editing a commit while rebasing branch '\\''several_commits'\\''\n \n nothing to commit (use -u to show untracked files)\n EOF\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_done\ndiff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh\nindex fbfdcca0007..45d025f9601 100755\n--- a/t/t7519-status-fsmonitor.sh\n+++ b/t/t7519-status-fsmonitor.sh\n@@ -236,7 +236,7 @@ test_expect_success 'refresh_index() invalidates fsmonitor cache' '\n \tgit reset HEAD~1 &&\n \tgit status >actual &&\n \tgit -c core.fsmonitor= status >expect &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n # test fsmonitor with and without preloadIndex\n@@ -273,7 +273,7 @@ do\n \t\t\tgit add dir2/new &&\n \t\t\tgit status >actual &&\n \t\t\tgit -c core.fsmonitor= status >expect &&\n-\t\t\ttest_i18ncmp expect actual\n+\t\t\ttest_cmp expect actual\n \t\t'\n \n \t\t# Make sure it's actually skipping the check for modified and untracked\ndiff --git a/t/t7521-ignored-mode.sh b/t/t7521-ignored-mode.sh\nindex 91790943c3d..a88b02b06ed 100755\n--- a/t/t7521-ignored-mode.sh\n+++ b/t/t7521-ignored-mode.sh\n@@ -30,7 +30,7 @@ test_expect_success 'Verify behavior of status on directories with ignored files\n \t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n \n \tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'Verify status behavior on directory with tracked & ignored files' '\n@@ -55,7 +55,7 @@ test_expect_success 'Verify status behavior on directory with tracked & ignored\n \tgit commit -m \"commit tracked files\" &&\n \n \tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'Verify status behavior on directory with untracked and ignored files' '\n@@ -80,7 +80,7 @@ test_expect_success 'Verify status behavior on directory with untracked and igno\n \t\tdir/untracked_ignored/ignored_1.ign dir/untracked_ignored/ignored_2.ign &&\n \n \tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'Verify status matching ignored files on ignored directory' '\n@@ -96,7 +96,7 @@ test_expect_success 'Verify status matching ignored files on ignored directory'\n \t\tignored_dir/ignored_1.ign ignored_dir/ignored_2.ign &&\n \n \tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'Verify status behavior on ignored directory containing tracked file' '\n@@ -117,7 +117,7 @@ test_expect_success 'Verify status behavior on ignored directory containing trac\n \tgit add -f ignored_dir/tracked &&\n \tgit commit -m \"Force add file in ignored directory\" &&\n \tgit status --porcelain=v2 --ignored=matching --untracked-files=all >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n@@ -136,7 +136,7 @@ test_expect_success 'Verify matching ignored files with --untracked-files=normal\n \t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n \t\tuntracked_dir/untracked &&\n \tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'Verify matching ignored files with --untracked-files=normal' '\n@@ -155,7 +155,7 @@ test_expect_success 'Verify matching ignored files with --untracked-files=normal\n \t\tignored_files/ignored_1.ign ignored_files/ignored_2.ign \\\n \t\tuntracked_dir/untracked &&\n \tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'Verify status behavior on ignored directory containing tracked file' '\n@@ -176,7 +176,7 @@ test_expect_success 'Verify status behavior on ignored directory containing trac\n \tgit add -f ignored_dir/tracked &&\n \tgit commit -m \"Force add file in ignored directory\" &&\n \tgit status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'Verify behavior of status with --ignored=no' '\n@@ -191,7 +191,7 @@ test_expect_success 'Verify behavior of status with --ignored=no' '\n \t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n \n \tgit status --porcelain=v2 --ignored=no --untracked-files=all >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=all' '\n@@ -210,7 +210,7 @@ test_expect_success 'Verify behavior of status with --ignored=traditional and --\n \t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n \n \tgit status --porcelain=v2 --ignored=traditional --untracked-files=all >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=normal' '\n@@ -227,7 +227,7 @@ test_expect_success 'Verify behavior of status with --ignored=traditional and --\n \t\tdir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&\n \n \tgit status --porcelain=v2 --ignored=traditional --untracked-files=normal >output &&\n-\ttest_i18ncmp expect output\n+\ttest_cmp expect output\n '\n \n test_done\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex b2c1d861dcb..1cbc9715a81 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -272,7 +272,7 @@ test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '\n \tEOF\n \tgit cat-file commit HEAD >raw &&\n \tsed -e \"1,/^$/d\" raw >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '\n@@ -296,7 +296,7 @@ test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '\n \tEOF\n \tgit cat-file commit HEAD >raw &&\n \tsed -e \"1,/^$/d\" raw >actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_debug 'git log --graph --decorate --oneline --all'\ndiff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh\nindex 13859ec8595..a9c816b47f2 100755\n--- a/t/t7602-merge-octopus-many.sh\n+++ b/t/t7602-merge-octopus-many.sh\n@@ -66,7 +66,7 @@ EOF\n test_expect_success 'merge output uses pretty names' '\n \tgit reset --hard c1 &&\n \tgit merge c2 c3 c4 >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n cat >expected <<\\EOF\n@@ -84,7 +84,7 @@ test_expect_success 'merge reduces irrelevant remote heads' '\n \t\trm expected.tmp\n \tfi &&\n \tGIT_MERGE_VERBOSITY=0 git merge c4 c5 >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n cat >expected <<\\EOF\n@@ -101,7 +101,7 @@ EOF\n test_expect_success 'merge fast-forward output uses pretty names' '\n \tgit reset --hard c0 &&\n \tgit merge c1 c2 >actual &&\n-\ttest_i18ncmp expected actual\n+\ttest_cmp expected actual\n '\n \n test_done\n-- \n2.30.0.284.gd98b1dd5eaa7\n\n"},{"id":"416755","messageId":"xmqqa6say0h8.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20210211015353.28680-5-avarab@gmail.com","subject":"Re: [PATCH 4/4] tests: remove most uses of test_i18ncmp","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-11T17:32:03Z","receivedAt":"2021-02-11T19:23:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> As a follow-up to d162b25f956 (tests: remove support for\n> GIT_TEST_GETTEXT_POISON, 2021-01-20) remove most uses of test_i18ncmp\n> via a simple s/test_i18ncmp/test_cmp/g search-replacement.\n\nI am a bit hesitant to take this step, primarily because the\ndistinction between test_cmp and test_i18ncmp is serving as a sign\nfor tests to identify if they are dealing with translatable messages\nor plumbing/machine-readable messages.\n\nIf a totally different approach to test i18n messging part\n(e.g. Dscho's rot13) appears any time soon, it would be helpful to\nleave these distinction in, rather than ripping them out, with two\ncaveats.\n\n - If such a change does happen soon enough, then the codebase this\n   huge patch touches would not be all that different by the time\n   when it happens, so reverting this patch should not be too\n   involved, which means my hesitation is not all that warranted.\n\n - If such a change does not happen for some time, then it would\n   become impractical to revert this patch when it does happen due\n   to changes in the codebase, if we apply it with the intention of\n   \"we can easily revert if we need to\".  But even if we drop this\n   step, until such a change appears, we surely will gain more calls\n   to test_cmp (and possibly test_i18ncmp) in the codebase, and at\n   that point, we can trust the distinction between test_cmp and\n   test_i18ncmp even less, which means it does not help all that\n   much if we tried to keep test_i18ncmp.\n\nSo, perhaps we should bite the bullet and just take this step.\n\n\n"},{"id":"416839","messageId":"8735y0ca4i.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"xmqqa6say0h8.fsf@gitster.c.googlers.com","subject":"Re: [PATCH 4/4] tests: remove most uses of test_i18ncmp","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-13T14:31:25Z","receivedAt":"2021-02-13T14:32:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Feb 11 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> As a follow-up to d162b25f956 (tests: remove support for\n>> GIT_TEST_GETTEXT_POISON, 2021-01-20) remove most uses of test_i18ncmp\n>> via a simple s/test_i18ncmp/test_cmp/g search-replacement.\n>\n> I am a bit hesitant to take this step, primarily because the\n> distinction between test_cmp and test_i18ncmp is serving as a sign\n> for tests to identify if they are dealing with translatable messages\n> or plumbing/machine-readable messages.\n>\n> If a totally different approach to test i18n messging part\n> (e.g. Dscho's rot13) appears any time soon, it would be helpful to\n> leave these distinction in, rather than ripping them out, with two\n> caveats.\n>\n>  - If such a change does happen soon enough, then the codebase this\n>    huge patch touches would not be all that different by the time\n>    when it happens, so reverting this patch should not be too\n>    involved, which means my hesitation is not all that warranted.\n>\n>  - If such a change does not happen for some time, then it would\n>    become impractical to revert this patch when it does happen due\n>    to changes in the codebase, if we apply it with the intention of\n>    \"we can easily revert if we need to\".  But even if we drop this\n>    step, until such a change appears, we surely will gain more calls\n>    to test_cmp (and possibly test_i18ncmp) in the codebase, and at\n>    that point, we can trust the distinction between test_cmp and\n>    test_i18ncmp even less, which means it does not help all that\n>    much if we tried to keep test_i18ncmp.\n>\n> So, perhaps we should bite the bullet and just take this step.\n\nI think it either makes sense to take this series as a whole, or not\ntake any of it and also revert the already merged 73c01d25fe (tests:\nremove uses of GIT_TEST_GETTEXT_POISON=false, 2021-01-20).\n\nI.e. if we're going to not remove test_i18ncmp uses then we should leave\nC_LOCALE_OUTPUT & GIT_TEST_GETTEXT_POISON=false alone as well (or\nconvert them to the test_i18n* forms).\n\nAs noted before I'm not invested in the outcome here, I just submitted\nthis to not leave the obvious post-cleanup undone.\n\nMy reading of the list discussion/consensus[1] (such as it was) was that\neven if we had this rot13 mode ready we wouldn't think it's worth the\ntime investment overall, hence the follow-up patches.\n\nAlso, if we have it down the line we're probably better off with these\npatches. The parts that'll revert cleanly can likely be resurrected\nas-is, but conflicts would be a good thing, as that code needs to be\nlooked at anyway (it will be re-arranged tests that never used\nGETTEXT_POISON in their current form).\n\nWe also won't have cargo-culting in the meantime, with new code using\ntest_i18ncmp or whatever just because the test above them did.\n\n1. https://lore.kernel.org/git/20210120182759.31102-1-avarab@gmail.com/\n"},{"id":"417420","messageId":"YDAsvpIzgsIhh+5X@google.com","threadId":"51667","inReplyTo":"xmqqa6say0h8.fsf@gitster.c.googlers.com","subject":"Re: [PATCH 4/4] tests: remove most uses of test_i18ncmp","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2021-02-19T21:25:18Z","receivedAt":"2021-02-19T21:26:21Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Junio C Hamano wrote:\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n>> As a follow-up to d162b25f956 (tests: remove support for\n>> GIT_TEST_GETTEXT_POISON, 2021-01-20) remove most uses of test_i18ncmp\n>> via a simple s/test_i18ncmp/test_cmp/g search-replacement.\n>\n> I am a bit hesitant to take this step, primarily because the\n> distinction between test_cmp and test_i18ncmp is serving as a sign\n> for tests to identify if they are dealing with translatable messages\n> or plumbing/machine-readable messages.\n\nI agree: in particular, I would value the ability to run tests with,\nsay, LANG=fr_CH.UTF-8 and catch localization-specific issues that way.\nRemoving the test_i18ncmp helper removes that ability.\n\nThat said:\n\n[...]\n> So, perhaps we should bite the bullet and just take this step.\n\nIf we don't have a convenient way to keep \"testing the testsuite\"\n(i.e., some kind of CI run that uses a locale other than C), then I\nagree that explicitly removing the test_i18ncmp / test_cmp distinction\nis better than letting it bitrot.\n\nThanks,\nJonathan\n"},{"id":"418416","messageId":"20210307132905.14212-1-avarab@gmail.com","threadId":"51667","inReplyTo":"20210211015353.28680-2-avarab@gmail.com","subject":"[PATCH] mktag tests: fix broken \"&&\" chain","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-07T13:29:05Z","receivedAt":"2021-03-07T13:30:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove a stray \"xb\" I inadvertently introduced in 780aa0a21e0 (tests:\nremove last uses of GIT_TEST_GETTEXT_POISON=false, 2021-02-11). This\nwould have been a failed attempt to type \"C-x C-b\" that snuck into the\ncode.\n\nThe chainlint check did not catch this one, but I don't know where to\nstart patching the wall-of-sed that is chainlint.sed to fix that.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3800-mktag.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 60a666da595..6275c98523f 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -17,7 +17,7 @@ check_verify_failure () {\n \t\tgrep '$2' message &&\n \t\tif test '$3' != '--no-strict'\n \t\tthen\n-\t\t\ttest_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&xb\n+\t\t\ttest_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&\n \t\t\tgrep '$2' message.no-strict\n \t\tfi\n \t\"\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418417","messageId":"20210307163655.GD3590451@szeder.dev","threadId":"51667","inReplyTo":"20210307132905.14212-1-avarab@gmail.com","subject":"Re: [PATCH] mktag tests: fix broken \"&&\" chain","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2021-03-07T16:36:55Z","receivedAt":"2021-03-07T16:38:03Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Sun, Mar 07, 2021 at 02:29:05PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> Remove a stray \"xb\" I inadvertently introduced in 780aa0a21e0 (tests:\n> remove last uses of GIT_TEST_GETTEXT_POISON=false, 2021-02-11). This\n> would have been a failed attempt to type \"C-x C-b\" that snuck into the\n> code.\n> \n> The chainlint check did not catch this one, but I don't know where to\n> start patching the wall-of-sed that is chainlint.sed to fix that.\n\nChain-lint only checks 'test_expect_{success,failure}' blocks, but\nthis && chain is in a helper function.\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t3800-mktag.sh | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n> \n> diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\n> index 60a666da595..6275c98523f 100755\n> --- a/t/t3800-mktag.sh\n> +++ b/t/t3800-mktag.sh\n> @@ -17,7 +17,7 @@ check_verify_failure () {\n>  \t\tgrep '$2' message &&\n>  \t\tif test '$3' != '--no-strict'\n>  \t\tthen\n> -\t\t\ttest_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&xb\n> +\t\t\ttest_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&\n>  \t\t\tgrep '$2' message.no-strict\n>  \t\tfi\n>  \t\"\n> -- \n> 2.31.0.rc0.126.g04f22c5b82\n> \n"},{"id":"418423","messageId":"xmqqy2ey7kzx.fsf@gitster.c.googlers.com","threadId":"51667","inReplyTo":"20210307132905.14212-1-avarab@gmail.com","subject":"Re: [PATCH] mktag tests: fix broken \"&&\" chain","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-07T20:43:30Z","receivedAt":"2021-03-07T20:44:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Remove a stray \"xb\" I inadvertently introduced in 780aa0a21e0 (tests:\n> remove last uses of GIT_TEST_GETTEXT_POISON=false, 2021-02-11). This\n> would have been a failed attempt to type \"C-x C-b\" that snuck into the\n> code.\n>\n> The chainlint check did not catch this one, but I don't know where to\n> start patching the wall-of-sed that is chainlint.sed to fix that.\n\nI do not think the chainlint check is designed to deal with helper\nfunctions, but I wonder why nobody noticed a runtime failure.  Is\nthis an unused/dead codepath?\n\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t3800-mktag.sh | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n>\n> diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\n> index 60a666da595..6275c98523f 100755\n> --- a/t/t3800-mktag.sh\n> +++ b/t/t3800-mktag.sh\n> @@ -17,7 +17,7 @@ check_verify_failure () {\n>  \t\tgrep '$2' message &&\n>  \t\tif test '$3' != '--no-strict'\n>  \t\tthen\n> -\t\t\ttest_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&xb\n> +\t\t\ttest_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&\n>  \t\t\tgrep '$2' message.no-strict\n>  \t\tfi\n>  \t\"\n"},{"id":"418424","messageId":"CAPig+cRnAFaHWGFqNes9SVXDeW=zgV4ONvY9=RTKcyJ+CnZO8Q@mail.gmail.com","threadId":"51667","inReplyTo":"xmqqy2ey7kzx.fsf@gitster.c.googlers.com","subject":"Re: [PATCH] mktag tests: fix broken \"&&\" chain","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-03-07T20:52:24Z","receivedAt":"2021-03-07T20:53:40Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Mar 7, 2021 at 3:43 PM Junio C Hamano <gitster@pobox.com> wrote:\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n> > -                     test_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&xb\n> > +                     test_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&\n> >                       grep '$2' message.no-strict\n>\n> I do not think the chainlint check is designed to deal with helper\n> functions, but I wonder why nobody noticed a runtime failure.  Is\n> this an unused/dead codepath?\n\nThe `git mktag` invocation would still have produced the expected\nstderr output regardless of what happens later (by \"later\", I mean the\nfailing invocation of the non-existent `xb` command), which means that\nthe subsequent `grep` will find what it's looking for, making the\nfunction succeed as expected.\n\nSomeone looking at the verbose output might have had a chance of\nspotting the \"xp: not found\" error message, but there likely wasn't\nany reason to scan the verbose output since the test succeeded.\n"},{"id":"420096","messageId":"patch-1.1-0b43e43b949-20210324T021049Z-avarab@gmail.com","threadId":"51667","inReplyTo":"20210307132905.14212-1-avarab@gmail.com","subject":"[PATCH v2] mktag tests: fix broken \"&&\" chain","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-24T02:11:52Z","receivedAt":"2021-03-24T02:13:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove a stray \"xb\" I inadvertently introduced in 780aa0a21e0 (tests:\nremove last uses of GIT_TEST_GETTEXT_POISON=false, 2021-02-11).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n\nA v2 of a stray patch of mine that wasn't picked up during the release window.\n\nRange-diff:\n1:  bd8235ead3c ! 1:  0b43e43b949 mktag tests: fix broken \"&&\" chain\n    @@ Commit message\n         mktag tests: fix broken \"&&\" chain\n     \n         Remove a stray \"xb\" I inadvertently introduced in 780aa0a21e0 (tests:\n    -    remove last uses of GIT_TEST_GETTEXT_POISON=false, 2021-02-11). This\n    -    would have been a failed attempt to type \"C-x C-b\" that snuck into the\n    -    code.\n    -\n    -    The chainlint check did not catch this one, but I don't know where to\n    -    start patching the wall-of-sed that is chainlint.sed to fix that.\n    +    remove last uses of GIT_TEST_GETTEXT_POISON=false, 2021-02-11).\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n\n t/t3800-mktag.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh\nindex 60a666da595..6275c98523f 100755\n--- a/t/t3800-mktag.sh\n+++ b/t/t3800-mktag.sh\n@@ -17,7 +17,7 @@ check_verify_failure () {\n \t\tgrep '$2' message &&\n \t\tif test '$3' != '--no-strict'\n \t\tthen\n-\t\t\ttest_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&xb\n+\t\t\ttest_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&\n \t\t\tgrep '$2' message.no-strict\n \t\tfi\n \t\"\n-- \n2.31.0.354.gc8cbd507b5a\n\n"},{"id":"420139","messageId":"xmqqo8f8xr2p.fsf@gitster.g","threadId":"51667","inReplyTo":"patch-1.1-0b43e43b949-20210324T021049Z-avarab@gmail.com","subject":"Re: [PATCH v2] mktag tests: fix broken \"&&\" chain","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-24T19:57:34Z","receivedAt":"2021-03-24T19:58:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Remove a stray \"xb\" I inadvertently introduced in 780aa0a21e0 (tests:\n> remove last uses of GIT_TEST_GETTEXT_POISON=false, 2021-02-11).\n\nThanks.\n"},{"id":"420166","messageId":"patch-1.1-ea968affa8c-20210324T233254Z-avarab@gmail.com","threadId":"51667","inReplyTo":"20210111144740.6092-3-avarab@gmail.com","subject":"[PATCH] config.c: remove last remnant of GIT_TEST_GETTEXT_POISON","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-24T23:36:09Z","receivedAt":"2021-03-24T23:37:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove a use of GIT_TEST_GETTEXT_POISON added in f276e2a4694 (config:\nimprove error message for boolean config, 2021-02-11).\n\nThis was simultaneously in-flight with my d162b25f956 (tests: remove\nsupport for GIT_TEST_GETTEXT_POISON, 2021-01-20) which removed the\nrest of the GIT_TEST_GETTEXT_POISON code.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n\nAt this point I'm going to call it a win if I'm not submitting a patch\nin 2022 to remove some more stray GETTEXT_POISON ... :)\n\n config.c          | 16 +---------------\n t/t1300-config.sh |  2 +-\n 2 files changed, 2 insertions(+), 16 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex 6428393a414..870d9534def 100644\n--- a/config.c\n+++ b/config.c\n@@ -1180,20 +1180,6 @@ static void die_bad_number(const char *name, const char *value)\n \t}\n }\n \n-NORETURN\n-static void die_bad_bool(const char *name, const char *value)\n-{\n-\tif (!strcmp(name, \"GIT_TEST_GETTEXT_POISON\"))\n-\t\t/*\n-\t\t * We explicitly *don't* use _() here since it would\n-\t\t * cause an infinite loop with _() needing to call\n-\t\t * use_gettext_poison().\n-\t\t */\n-\t\tdie(\"bad boolean config value '%s' for '%s'\", value, name);\n-\telse\n-\t\tdie(_(\"bad boolean config value '%s' for '%s'\"), value, name);\n-}\n-\n int git_config_int(const char *name, const char *value)\n {\n \tint ret;\n@@ -1268,7 +1254,7 @@ int git_config_bool(const char *name, const char *value)\n {\n \tint v = git_parse_maybe_bool(value);\n \tif (v < 0)\n-\t\tdie_bad_bool(name, value);\n+\t\tdie(_(\"bad boolean config value '%s' for '%s'\"), value, name);\n \treturn v;\n }\n \ndiff --git a/t/t1300-config.sh b/t/t1300-config.sh\nindex e0dd5d65ced..2280c2504ac 100755\n--- a/t/t1300-config.sh\n+++ b/t/t1300-config.sh\n@@ -679,7 +679,7 @@ test_expect_success 'invalid unit boolean' '\n \tgit config commit.gpgsign \"1true\" &&\n \ttest_cmp_config 1true commit.gpgsign &&\n \ttest_must_fail git config --bool --get commit.gpgsign 2>actual &&\n-\ttest_i18ngrep \"bad boolean config value .1true. for .commit.gpgsign.\" actual\n+\tgrep \"bad boolean config value .1true. for .commit.gpgsign.\" actual\n '\n \n test_expect_success 'line number is reported correctly' '\n-- \n2.31.0.354.gc8cbd507b5a\n\n"},{"id":"420168","messageId":"YFvbE1K2tZ5ych7z@coredump.intra.peff.net","threadId":"51667","inReplyTo":"patch-1.1-ea968affa8c-20210324T233254Z-avarab@gmail.com","subject":"Re: [PATCH] config.c: remove last remnant of GIT_TEST_GETTEXT_POISON","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-03-25T00:36:35Z","receivedAt":"2021-03-25T00:37:32Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Mar 25, 2021 at 12:36:09AM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> Remove a use of GIT_TEST_GETTEXT_POISON added in f276e2a4694 (config:\n> improve error message for boolean config, 2021-02-11).\n> \n> This was simultaneously in-flight with my d162b25f956 (tests: remove\n> support for GIT_TEST_GETTEXT_POISON, 2021-01-20) which removed the\n> rest of the GIT_TEST_GETTEXT_POISON code.\n\nYay. :)\n\n> diff --git a/config.c b/config.c\n> index 6428393a414..870d9534def 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -1180,20 +1180,6 @@ static void die_bad_number(const char *name, const char *value)\n>  \t}\n>  }\n>  \n> -NORETURN\n> -static void die_bad_bool(const char *name, const char *value)\n> -{\n> -\tif (!strcmp(name, \"GIT_TEST_GETTEXT_POISON\"))\n> -\t\t/*\n> -\t\t * We explicitly *don't* use _() here since it would\n> -\t\t * cause an infinite loop with _() needing to call\n> -\t\t * use_gettext_poison().\n> -\t\t */\n> -\t\tdie(\"bad boolean config value '%s' for '%s'\", value, name);\n> -\telse\n> -\t\tdie(_(\"bad boolean config value '%s' for '%s'\"), value, name);\n> -}\n> -\n>  int git_config_int(const char *name, const char *value)\n>  {\n>  \tint ret;\n> @@ -1268,7 +1254,7 @@ int git_config_bool(const char *name, const char *value)\n>  {\n>  \tint v = git_parse_maybe_bool(value);\n>  \tif (v < 0)\n> -\t\tdie_bad_bool(name, value);\n> +\t\tdie(_(\"bad boolean config value '%s' for '%s'\"), value, name);\n>  \treturn v;\n>  }\n\nThis code change looks good, but...\n\n> diff --git a/t/t1300-config.sh b/t/t1300-config.sh\n> index e0dd5d65ced..2280c2504ac 100755\n> --- a/t/t1300-config.sh\n> +++ b/t/t1300-config.sh\n> @@ -679,7 +679,7 @@ test_expect_success 'invalid unit boolean' '\n>  \tgit config commit.gpgsign \"1true\" &&\n>  \ttest_cmp_config 1true commit.gpgsign &&\n>  \ttest_must_fail git config --bool --get commit.gpgsign 2>actual &&\n> -\ttest_i18ngrep \"bad boolean config value .1true. for .commit.gpgsign.\" actual\n> +\tgrep \"bad boolean config value .1true. for .commit.gpgsign.\" actual\n>  '\n\nwhy are we losing test_i18ngrep here? The message is still marked for\ntranslation. I know we've discussed dropping all of the test_i18n\nhelpers, but that seems unrelated to the rest of the patch.\n\n-Peff\n"},{"id":"420169","messageId":"87ft0kox24.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"YFvbE1K2tZ5ych7z@coredump.intra.peff.net","subject":"Re: [PATCH] config.c: remove last remnant of GIT_TEST_GETTEXT_POISON","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-25T01:13:07Z","receivedAt":"2021-03-25T01:14:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Mar 25 2021, Jeff King wrote:\n\n> On Thu, Mar 25, 2021 at 12:36:09AM +0100, Ævar Arnfjörð Bjarmason wrote:\n>\n>> Remove a use of GIT_TEST_GETTEXT_POISON added in f276e2a4694 (config:\n>> improve error message for boolean config, 2021-02-11).\n>> \n>> This was simultaneously in-flight with my d162b25f956 (tests: remove\n>> support for GIT_TEST_GETTEXT_POISON, 2021-01-20) which removed the\n>> rest of the GIT_TEST_GETTEXT_POISON code.\n>\n> Yay. :)\n>\n>> diff --git a/config.c b/config.c\n>> index 6428393a414..870d9534def 100644\n>> --- a/config.c\n>> +++ b/config.c\n>> @@ -1180,20 +1180,6 @@ static void die_bad_number(const char *name, const char *value)\n>>  \t}\n>>  }\n>>  \n>> -NORETURN\n>> -static void die_bad_bool(const char *name, const char *value)\n>> -{\n>> -\tif (!strcmp(name, \"GIT_TEST_GETTEXT_POISON\"))\n>> -\t\t/*\n>> -\t\t * We explicitly *don't* use _() here since it would\n>> -\t\t * cause an infinite loop with _() needing to call\n>> -\t\t * use_gettext_poison().\n>> -\t\t */\n>> -\t\tdie(\"bad boolean config value '%s' for '%s'\", value, name);\n>> -\telse\n>> -\t\tdie(_(\"bad boolean config value '%s' for '%s'\"), value, name);\n>> -}\n>> -\n>>  int git_config_int(const char *name, const char *value)\n>>  {\n>>  \tint ret;\n>> @@ -1268,7 +1254,7 @@ int git_config_bool(const char *name, const char *value)\n>>  {\n>>  \tint v = git_parse_maybe_bool(value);\n>>  \tif (v < 0)\n>> -\t\tdie_bad_bool(name, value);\n>> +\t\tdie(_(\"bad boolean config value '%s' for '%s'\"), value, name);\n>>  \treturn v;\n>>  }\n>\n> This code change looks good, but...\n>\n>> diff --git a/t/t1300-config.sh b/t/t1300-config.sh\n>> index e0dd5d65ced..2280c2504ac 100755\n>> --- a/t/t1300-config.sh\n>> +++ b/t/t1300-config.sh\n>> @@ -679,7 +679,7 @@ test_expect_success 'invalid unit boolean' '\n>>  \tgit config commit.gpgsign \"1true\" &&\n>>  \ttest_cmp_config 1true commit.gpgsign &&\n>>  \ttest_must_fail git config --bool --get commit.gpgsign 2>actual &&\n>> -\ttest_i18ngrep \"bad boolean config value .1true. for .commit.gpgsign.\" actual\n>> +\tgrep \"bad boolean config value .1true. for .commit.gpgsign.\" actual\n>>  '\n>\n> why are we losing test_i18ngrep here? The message is still marked for\n> translation. I know we've discussed dropping all of the test_i18n\n> helpers, but that seems unrelated to the rest of the patch.\n\nFor new tests we're suggesting not to use it, so while I'm holding off\non some general s/test_i18ngrep/grep/ refactoring, it seemed natural to\nadjust the test added by the commit whose code I'm modifying.\n\nIt also shows reviewers that there is such a test, so e.g. I didn't\ninvert the name/value parameters or something in refactoring this.\n"},{"id":"420203","messageId":"xmqqv99fuhmm.fsf@gitster.g","threadId":"51667","inReplyTo":"87ft0kox24.fsf@evledraar.gmail.com","subject":"Re: [PATCH] config.c: remove last remnant of GIT_TEST_GETTEXT_POISON","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-25T20:02:09Z","receivedAt":"2021-03-25T20:02:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n>>> diff --git a/t/t1300-config.sh b/t/t1300-config.sh\n>>> index e0dd5d65ced..2280c2504ac 100755\n>>> --- a/t/t1300-config.sh\n>>> +++ b/t/t1300-config.sh\n>>> @@ -679,7 +679,7 @@ test_expect_success 'invalid unit boolean' '\n>>>  \tgit config commit.gpgsign \"1true\" &&\n>>>  \ttest_cmp_config 1true commit.gpgsign &&\n>>>  \ttest_must_fail git config --bool --get commit.gpgsign 2>actual &&\n>>> -\ttest_i18ngrep \"bad boolean config value .1true. for .commit.gpgsign.\" actual\n>>> +\tgrep \"bad boolean config value .1true. for .commit.gpgsign.\" actual\n>>>  '\n>>\n>> why are we losing test_i18ngrep here? The message is still marked for\n>> translation. I know we've discussed dropping all of the test_i18n\n>> helpers, but that seems unrelated to the rest of the patch.\n>\n> For new tests we're suggesting not to use it, so while I'm holding off\n> on some general s/test_i18ngrep/grep/ refactoring, it seemed natural to\n> adjust the test added by the commit whose code I'm modifying.\n\nIt would have been understandable if the proposed log message said\n\n    Remove a use of GIT_TEST_GETTEXT_POISON added in f276e2a4694 (config:\n    improve error message for boolean config, 2021-02-11), together with\n    a new test added.\n\nand removed the test.\n\nBut the test still has value for the remaining codebase, just like\nall the other tests that happen to use test_i18n{grep,cmp}.  In a\nsense, the original mixed two separate things into one commit\n(i.e. use of TEST_GETTEXT_POISON to decide if the message given to\ndie() is localized, and a test to see how \"git config --bool --get\"\nbehaves when a malformed boolean value is given), and that may have\nbeen justifiable back in the world where GETTEXT_POISON was a thing,\nmaking these two things closely interrelated.  Since we left that\nworld behind, I think we should treat them as two separate things.\n\nIn short, I do not think \"The C code we are removing was added in\nthe same commit\" is a good excuse for this \"while at it\" change.\n\nPutting it another way, imagine back then there was the t1300 test\nand there was no die_bad_bool().  The test may have been expecting\n\"bad numeric\" or \"invalid unit\", with grep (with a known bug that\nthe test would not pass under GETTEXT_POISON).\n\nIn such an alternative past, the change you are reverting may have\nbeen only to config.c to make the die(_()) work correctly with\nGETTEXT_POISON, and turned grep to test_i18ngrep.  And your \"we are\nreverting the whole commit\" may have made more sense.\n\nBut we are not living in such an alternative world.\n\nHaving said all that, I do not particularly care when, in which\nexact commit, a use of test_i18n* in t/ among 1100+ of them lost\nits i18n-ness (it just felt a bit out of place in this particular\ncommit, that's all).\n\nThanks.\n"},{"id":"421208","messageId":"patch-1.1-2b2ac8471f-20210408T132357Z-avarab@gmail.com","threadId":"51667","inReplyTo":"patch-1.1-ea968affa8c-20210324T233254Z-avarab@gmail.com","subject":"[PATCH v2] config.c: remove last remnant of GIT_TEST_GETTEXT_POISON","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-08T13:25:55Z","receivedAt":"2021-04-08T13:26:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove a use of GIT_TEST_GETTEXT_POISON added in f276e2a4694 (config:\nimprove error message for boolean config, 2021-02-11).\n\nThis was simultaneously in-flight with my d162b25f956 (tests: remove\nsupport for GIT_TEST_GETTEXT_POISON, 2021-01-20) which removed the\nrest of the GIT_TEST_GETTEXT_POISON code.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n\nThis trivial cleanup now without the objectionable and needless change\nto the tests.\n\nRange-diff:\n1:  ea968affa8 ! 1:  2b2ac8471f config.c: remove last remnant of GIT_TEST_GETTEXT_POISON\n    @@ config.c: int git_config_bool(const char *name, const char *value)\n      \treturn v;\n      }\n      \n    -\n    - ## t/t1300-config.sh ##\n    -@@ t/t1300-config.sh: test_expect_success 'invalid unit boolean' '\n    - \tgit config commit.gpgsign \"1true\" &&\n    - \ttest_cmp_config 1true commit.gpgsign &&\n    - \ttest_must_fail git config --bool --get commit.gpgsign 2>actual &&\n    --\ttest_i18ngrep \"bad boolean config value .1true. for .commit.gpgsign.\" actual\n    -+\tgrep \"bad boolean config value .1true. for .commit.gpgsign.\" actual\n    - '\n    - \n    - test_expect_success 'line number is reported correctly' '\n\n config.c | 16 +---------------\n 1 file changed, 1 insertion(+), 15 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex 6428393a41..870d9534de 100644\n--- a/config.c\n+++ b/config.c\n@@ -1180,20 +1180,6 @@ static void die_bad_number(const char *name, const char *value)\n \t}\n }\n \n-NORETURN\n-static void die_bad_bool(const char *name, const char *value)\n-{\n-\tif (!strcmp(name, \"GIT_TEST_GETTEXT_POISON\"))\n-\t\t/*\n-\t\t * We explicitly *don't* use _() here since it would\n-\t\t * cause an infinite loop with _() needing to call\n-\t\t * use_gettext_poison().\n-\t\t */\n-\t\tdie(\"bad boolean config value '%s' for '%s'\", value, name);\n-\telse\n-\t\tdie(_(\"bad boolean config value '%s' for '%s'\"), value, name);\n-}\n-\n int git_config_int(const char *name, const char *value)\n {\n \tint ret;\n@@ -1268,7 +1254,7 @@ int git_config_bool(const char *name, const char *value)\n {\n \tint v = git_parse_maybe_bool(value);\n \tif (v < 0)\n-\t\tdie_bad_bool(name, value);\n+\t\tdie(_(\"bad boolean config value '%s' for '%s'\"), value, name);\n \treturn v;\n }\n \n-- \n2.31.1.527.g9b8f7de2547\n\n"},{"id":"421250","messageId":"xmqqr1jku0d3.fsf@gitster.g","threadId":"51667","inReplyTo":"patch-1.1-2b2ac8471f-20210408T132357Z-avarab@gmail.com","subject":"Re: [PATCH v2] config.c: remove last remnant of GIT_TEST_GETTEXT_POISON","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-08T17:55:52Z","receivedAt":"2021-04-08T17:55:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Remove a use of GIT_TEST_GETTEXT_POISON added in f276e2a4694 (config:\n> improve error message for boolean config, 2021-02-11).\n\nThanks.  Will queue.\n\n>\n> This was simultaneously in-flight with my d162b25f956 (tests: remove\n> support for GIT_TEST_GETTEXT_POISON, 2021-01-20) which removed the\n> rest of the GIT_TEST_GETTEXT_POISON code.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>\n> This trivial cleanup now without the objectionable and needless change\n> to the tests.\n>\n> Range-diff:\n> 1:  ea968affa8 ! 1:  2b2ac8471f config.c: remove last remnant of GIT_TEST_GETTEXT_POISON\n>     @@ config.c: int git_config_bool(const char *name, const char *value)\n>       \treturn v;\n>       }\n>       \n>     -\n>     - ## t/t1300-config.sh ##\n>     -@@ t/t1300-config.sh: test_expect_success 'invalid unit boolean' '\n>     - \tgit config commit.gpgsign \"1true\" &&\n>     - \ttest_cmp_config 1true commit.gpgsign &&\n>     - \ttest_must_fail git config --bool --get commit.gpgsign 2>actual &&\n>     --\ttest_i18ngrep \"bad boolean config value .1true. for .commit.gpgsign.\" actual\n>     -+\tgrep \"bad boolean config value .1true. for .commit.gpgsign.\" actual\n>     - '\n>     - \n>     - test_expect_success 'line number is reported correctly' '\n>\n>  config.c | 16 +---------------\n>  1 file changed, 1 insertion(+), 15 deletions(-)\n>\n> diff --git a/config.c b/config.c\n> index 6428393a41..870d9534de 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -1180,20 +1180,6 @@ static void die_bad_number(const char *name, const char *value)\n>  \t}\n>  }\n>  \n> -NORETURN\n> -static void die_bad_bool(const char *name, const char *value)\n> -{\n> -\tif (!strcmp(name, \"GIT_TEST_GETTEXT_POISON\"))\n> -\t\t/*\n> -\t\t * We explicitly *don't* use _() here since it would\n> -\t\t * cause an infinite loop with _() needing to call\n> -\t\t * use_gettext_poison().\n> -\t\t */\n> -\t\tdie(\"bad boolean config value '%s' for '%s'\", value, name);\n> -\telse\n> -\t\tdie(_(\"bad boolean config value '%s' for '%s'\"), value, name);\n> -}\n> -\n>  int git_config_int(const char *name, const char *value)\n>  {\n>  \tint ret;\n> @@ -1268,7 +1254,7 @@ int git_config_bool(const char *name, const char *value)\n>  {\n>  \tint v = git_parse_maybe_bool(value);\n>  \tif (v < 0)\n> -\t\tdie_bad_bool(name, value);\n> +\t\tdie(_(\"bad boolean config value '%s' for '%s'\"), value, name);\n>  \treturn v;\n>  }\n"},{"id":"421884","messageId":"patch-1.1-f0542cd902c-20210413T121930Z-avarab@gmail.com","threadId":"51667","inReplyTo":"20210211015353.28680-5-avarab@gmail.com","subject":"[PATCH] tests: remove all uses of test_i18cmp","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-13T12:19:51Z","receivedAt":"2021-04-13T12:20:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Finish the removal I started in 1108cea7f8e (tests: remove most uses\nof test_i18ncmp, 2021-02-11). At that time the function wasn't removed\ndue to disruption with in-flight changes, remove the occurrences that\nhave landed since then.\n\nAs of writing this there are no test_i18ncmp uses between \"master\" and\n\"seen\", so let's also remove the function to finally put it to rest.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3437-rebase-fixup-options.sh | 6 +++---\n t/t6300-for-each-ref.sh         | 6 +++---\n t/test-lib-functions.sh         | 7 -------\n 3 files changed, 6 insertions(+), 13 deletions(-)\n\ndiff --git a/t/t3437-rebase-fixup-options.sh b/t/t3437-rebase-fixup-options.sh\nindex d0bdc7ed02d..c023fefd681 100755\n--- a/t/t3437-rebase-fixup-options.sh\n+++ b/t/t3437-rebase-fixup-options.sh\n@@ -157,7 +157,7 @@ test_expect_success 'sequence of fixup, fixup -C & squash --signoff works' '\n \t\tgit -c commit.status=false rebase -ik --signoff A &&\n \tgit diff-tree --exit-code --patch HEAD B3 -- &&\n \ttest_cmp_rev HEAD^ A &&\n-\ttest_i18ncmp \"$TEST_DIRECTORY/t3437/expected-squash-message\" \\\n+\ttest_cmp \"$TEST_DIRECTORY/t3437/expected-squash-message\" \\\n \t\tactual-squash-message\n '\n \n@@ -191,7 +191,7 @@ test_expect_success 'sequence squash, fixup & fixup -c gives combined message' '\n \tFAKE_LINES=\"1 squash 2 fixup 3 fixup_-c 4\" \\\n \t\tFAKE_MESSAGE_COPY=actual-combined-message \\\n \t\tgit -c commit.status=false rebase -i A &&\n-\ttest_i18ncmp \"$TEST_DIRECTORY/t3437/expected-combined-message\" \\\n+\ttest_cmp \"$TEST_DIRECTORY/t3437/expected-combined-message\" \\\n \t\tactual-combined-message &&\n \ttest_cmp_rev HEAD^ A\n '\n@@ -204,7 +204,7 @@ test_expect_success 'fixup -C works upon --autosquash with amend!' '\n \t\t\t\t\t\t--signoff A &&\n \tgit diff-tree --exit-code --patch HEAD B3 -- &&\n \ttest_cmp_rev HEAD^ A &&\n-\ttest_i18ncmp \"$TEST_DIRECTORY/t3437/expected-squash-message\" \\\n+\ttest_cmp \"$TEST_DIRECTORY/t3437/expected-squash-message\" \\\n \t\tactual-squash-message\n '\n \ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex cac7f443d00..7cfa934913b 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -945,9 +945,9 @@ test_failing_trailer_option () {\n \ttest_expect_success \"$title\" '\n \t\t# error message cannot be checked under i18n\n \t\ttest_must_fail git for-each-ref --format=\"%($option)\" refs/heads/main 2>actual &&\n-\t\ttest_i18ncmp expect actual &&\n+\t\ttest_cmp expect actual &&\n \t\ttest_must_fail git for-each-ref --format=\"%(contents:$option)\" refs/heads/main 2>actual &&\n-\t\ttest_i18ncmp expect actual\n+\t\ttest_cmp expect actual\n \t'\n }\n \n@@ -966,7 +966,7 @@ test_expect_success 'if arguments, %(contents:trailers) shows error if colon is\n \tfatal: unrecognized %(contents) argument: trailersonly\n \tEOF\n \ttest_must_fail git for-each-ref --format=\"%(contents:trailersonly)\" 2>actual &&\n-\ttest_i18ncmp expect actual\n+\ttest_cmp expect actual\n '\n \n test_expect_success 'basic atom: head contents:trailers' '\ndiff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\nindex 6348e8d7339..b823c140271 100644\n--- a/t/test-lib-functions.sh\n+++ b/t/test-lib-functions.sh\n@@ -1025,13 +1025,6 @@ test_cmp_bin () {\n \tcmp \"$@\"\n }\n \n-# Wrapper for test_cmp which used to be used for\n-# GIT_TEST_GETTEXT_POISON=false. Only here as a shim for other\n-# in-flight changes. Should not be used and will be removed soon.\n-test_i18ncmp () {\n-\ttest_cmp \"$@\"\n-}\n-\n # Wrapper for grep which used to be used for\n # GIT_TEST_GETTEXT_POISON=false. Only here as a shim for other\n # in-flight changes. Should not be used and will be removed soon.\n-- \n2.31.1.646.g06d606cb9c7\n\n"},{"id":"421885","messageId":"cover-0.2-00000000000-20210413T122645Z-avarab@gmail.com","threadId":"51667","inReplyTo":"patch-1.1-f0542cd902c-20210413T121930Z-avarab@gmail.com","subject":"[PATCH 0/2] diff tests: un-flaky and post-gettext-poison cleanup","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-13T12:28:32Z","receivedAt":"2021-04-13T12:30:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In reply to [1] because they're related post-GETTEXT_POISON cleanup,\nbut both apply on top of master.\n\nI had a flaky t4013 test with some local WIP code that I hadn't\ninvestigated, as it turns out re-running the test was hiding the\nfailure. 2/2 fixes that, 1/2 is a minor post-GETTEXT_POISON cleanup.\n\n1. https://lore.kernel.org/git/patch-1.1-f0542cd902c-20210413T121930Z-avarab@gmail.com \n\nÆvar Arnfjörð Bjarmason (2):\n  diff tests: remove redundant case statement\n  diff tests: rewrite flakyness-causing test \"aid\"\n\n t/t4013-diff-various.sh | 31 +++++++++++++++----------------\n 1 file changed, 15 insertions(+), 16 deletions(-)\n\n-- \n2.31.1.646.g06d606cb9c7\n\n"},{"id":"421886","messageId":"patch-2.2-c2cb52b6605-20210413T122645Z-avarab@gmail.com","threadId":"51667","inReplyTo":"cover-0.2-00000000000-20210413T122645Z-avarab@gmail.com","subject":"[PATCH 2/2] diff tests: rewrite flakyness-causing test \"aid\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-13T12:28:34Z","receivedAt":"2021-04-13T12:30:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"If a new test is added to this \"while read magic cmd\" test facility\nadded in 3c2f75b590c (t4013: add tests for diff/log family output\noptions., 2006-06-26) but no test file is added it'll fail the first\ntime, but then succeed on subsequent runs as a new file has been added\nin t4013.\n\nLet's accomplish the same aim in way that doesn't cause subsequent\ntest runs to succeed. If we can't find the file we'll BUG out, and\nsuggest to the developer that they copy our \"expect.new\" file over,\nunlike the previous \"expect\" file this won't be picked up on\nsubsequent runs.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4013-diff-various.sh | 25 +++++++++++++++----------\n 1 file changed, 15 insertions(+), 10 deletions(-)\n\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex 67f6411aff9..228ff100c61 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -200,10 +200,12 @@ do\n \tesac\n \ttest=$(echo \"$label\" | sed -e 's|[/ ][/ ]*|_|g')\n \tpfx=$(printf \"%04d\" $test_count)\n-\texpect=\"$TEST_DIRECTORY/t4013/diff.$test\"\n+\texpect_relative=\"t4013/diff.$test\"\n+\texpect=\"$TEST_DIRECTORY/$expect_relative\"\n \tactual=\"$pfx-diff.$test\"\n \n \ttest_expect_$status \"git $cmd # magic is ${magic:-(not used)}\" '\n+\t\ttest_when_finished \"rm $actual\" &&\n \t\t{\n \t\t\techo \"$ git $cmd\"\n \t\t\tcase \"$magic\" in\n@@ -216,16 +218,19 @@ do\n \t\t\t    -e \"s/^\\\\(.*mixed; boundary=\\\"-*\\\\)$V\\\\(-*\\\\)\\\"\\$/\\\\1g-i-t--v-e-r-s-i-o-n\\2\\\"/\"\n \t\t\techo \"\\$\"\n \t\t} >\"$actual\" &&\n-\t\tif test -f \"$expect\"\n+\n+\t\tif ! test -f \"$expect\"\n \t\tthen\n-\t\t\tprocess_diffs \"$actual\" >actual &&\n-\t\t\tprocess_diffs \"$expect\" >expect &&\n-\t\t\ttest_cmp expect actual\n-\t\telse\n-\t\t\t# this is to help developing new tests.\n-\t\t\tcp \"$actual\" \"$expect\"\n-\t\t\tfalse\n-\t\tfi\n+\t\t\texpect_new=\"$expect.new\" &&\n+\t\t\tcp \"$actual\" \"$expect_new\" &&\n+\t\t\tBUG \"Have no \\\"$expect_relative\\\", new test? The output is in \\\"$expect_new\\\", maybe use that?\"\n+\t\tfi &&\n+\n+\t\ttest_when_finished \"rm actual\" &&\n+\t\tprocess_diffs \"$actual\" >actual &&\n+\t\ttest_when_finished \"rm expect\" &&\n+\t\tprocess_diffs \"$expect\" >expect &&\n+\t\ttest_cmp expect actual\n \t'\n done <<\\EOF\n diff-tree initial\n-- \n2.31.1.646.g06d606cb9c7\n\n"},{"id":"421887","messageId":"patch-1.2-35e77153831-20210413T122645Z-avarab@gmail.com","threadId":"51667","inReplyTo":"cover-0.2-00000000000-20210413T122645Z-avarab@gmail.com","subject":"[PATCH 1/2] diff tests: remove redundant case statement","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-13T12:28:33Z","receivedAt":"2021-04-13T12:30:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove a case statement whose two arms executed the same code. This\nwas added for i18n testing in 6dd88832e77 (diffstat summary line\nvaries by locale: miscellany, 2012-03-13), and was changed from\ntest_i18ncmp in my 1108cea7f8e (tests: remove most uses of\ntest_i18ncmp, 2021-02-11).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4013-diff-various.sh | 8 +-------\n 1 file changed, 1 insertion(+), 7 deletions(-)\n\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex 6cca8b84a6b..67f6411aff9 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -220,13 +220,7 @@ do\n \t\tthen\n \t\t\tprocess_diffs \"$actual\" >actual &&\n \t\t\tprocess_diffs \"$expect\" >expect &&\n-\t\t\tcase $cmd in\n-\t\t\t*format-patch* | *-stat*)\n-\t\t\t\ttest_cmp expect actual;;\n-\t\t\t*)\n-\t\t\t\ttest_cmp expect actual;;\n-\t\t\tesac &&\n-\t\t\trm -f \"$actual\" actual expect\n+\t\t\ttest_cmp expect actual\n \t\telse\n \t\t\t# this is to help developing new tests.\n \t\t\tcp \"$actual\" \"$expect\"\n-- \n2.31.1.646.g06d606cb9c7\n\n"},{"id":"421904","messageId":"CAHd-oW5nwzg4iiDheMPLe_M19SXO9hPhGeJK8dWfZ-yTKkPVmA@mail.gmail.com","threadId":"51667","inReplyTo":"patch-1.2-35e77153831-20210413T122645Z-avarab@gmail.com","subject":"Re: [PATCH 1/2] diff tests: remove redundant case statement","fromName":"Matheus Tavares Bernardino","fromEmail":"matheus.bernardino@usp.br","sentAt":"2021-04-13T14:18:42Z","receivedAt":"2021-04-13T14:19:11Z","isPatch":true,"sender":{"key":"matheus.tavb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701583?v=4"},"body":"On Tue, Apr 13, 2021 at 9:31 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\n> index 6cca8b84a6b..67f6411aff9 100755\n> --- a/t/t4013-diff-various.sh\n> +++ b/t/t4013-diff-various.sh\n> @@ -220,13 +220,7 @@ do\n>                 then\n>                         process_diffs \"$actual\" >actual &&\n>                         process_diffs \"$expect\" >expect &&\n> -                       case $cmd in\n> -                       *format-patch* | *-stat*)\n> -                               test_cmp expect actual;;\n> -                       *)\n> -                               test_cmp expect actual;;\n> -                       esac &&\n> -                       rm -f \"$actual\" actual expect\n\nWas this line removed by accident? It doesn't seem related to the\ndescribed change. Shouldn't this line be left after...\n\n> +                       test_cmp expect actual\n\n...this one?\n\n>                 else\n>                         # this is to help developing new tests.\n>                         cp \"$actual\" \"$expect\"\n> --\n> 2.31.1.646.g06d606cb9c7\n>\n"},{"id":"421906","messageId":"87im4q2rho.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"CAHd-oW5nwzg4iiDheMPLe_M19SXO9hPhGeJK8dWfZ-yTKkPVmA@mail.gmail.com","subject":"Re: [PATCH 1/2] diff tests: remove redundant case statement","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-13T14:23:15Z","receivedAt":"2021-04-13T14:23:22Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Apr 13 2021, Matheus Tavares Bernardino wrote:\n\n> On Tue, Apr 13, 2021 at 9:31 AM Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>>\n>> diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\n>> index 6cca8b84a6b..67f6411aff9 100755\n>> --- a/t/t4013-diff-various.sh\n>> +++ b/t/t4013-diff-various.sh\n>> @@ -220,13 +220,7 @@ do\n>>                 then\n>>                         process_diffs \"$actual\" >actual &&\n>>                         process_diffs \"$expect\" >expect &&\n>> -                       case $cmd in\n>> -                       *format-patch* | *-stat*)\n>> -                               test_cmp expect actual;;\n>> -                       *)\n>> -                               test_cmp expect actual;;\n>> -                       esac &&\n>> -                       rm -f \"$actual\" actual expect\n>\n> Was this line removed by accident? It doesn't seem related to the\n> described change. Shouldn't this line be left after...\n>\n>> +                       test_cmp expect actual\n>\n> ...this one?\n\nYes, it should be in 2/2 where this gets turned into a\ntest_when_finished \"rm [...]\", sorry.\n"},{"id":"421907","messageId":"CAHd-oW4G+7z3UM3qjhPp=2oqOPE4a49fweew0n+gheGtQEy5VA@mail.gmail.com","threadId":"51667","inReplyTo":"patch-2.2-c2cb52b6605-20210413T122645Z-avarab@gmail.com","subject":"Re: [PATCH 2/2] diff tests: rewrite flakyness-causing test \"aid\"","fromName":"Matheus Tavares Bernardino","fromEmail":"matheus.bernardino@usp.br","sentAt":"2021-04-13T14:44:50Z","receivedAt":"2021-04-13T14:45:09Z","isPatch":true,"sender":{"key":"matheus.tavb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701583?v=4"},"body":"On Tue, Apr 13, 2021 at 9:31 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> If a new test is added to this \"while read magic cmd\" test facility\n> added in 3c2f75b590c (t4013: add tests for diff/log family output\n> options., 2006-06-26) but no test file is added it'll fail the first\n> time, but then succeed on subsequent runs as a new file has been added\n> in t4013.\n>\n> Let's accomplish the same aim in way that doesn't cause subsequent\n\ns/in way/in a way/ ?\n\n> test runs to succeed. If we can't find the file we'll BUG out, and\n> suggest to the developer that they copy our \"expect.new\" file over,\n> unlike the previous \"expect\" file this won't be picked up on\n> subsequent runs.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t4013-diff-various.sh | 25 +++++++++++++++----------\n>  1 file changed, 15 insertions(+), 10 deletions(-)\n>\n> diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\n> index 67f6411aff9..228ff100c61 100755\n> --- a/t/t4013-diff-various.sh\n> +++ b/t/t4013-diff-various.sh\n> @@ -200,10 +200,12 @@ do\n>         esac\n>         test=$(echo \"$label\" | sed -e 's|[/ ][/ ]*|_|g')\n>         pfx=$(printf \"%04d\" $test_count)\n> -       expect=\"$TEST_DIRECTORY/t4013/diff.$test\"\n> +       expect_relative=\"t4013/diff.$test\"\n> +       expect=\"$TEST_DIRECTORY/$expect_relative\"\n>         actual=\"$pfx-diff.$test\"\n>\n>         test_expect_$status \"git $cmd # magic is ${magic:-(not used)}\" '\n> +               test_when_finished \"rm $actual\" &&\n\nNit: before these two patches, \"$actual\" was only removed when the\ntest succeeded. So, in case of failure, the failed output files would\nstill be there for debugging. It might be interesting to keep this\nbehavior and only remove \"$actual\" at the end of the test.\n\n>                 {\n>                         echo \"$ git $cmd\"\n>                         case \"$magic\" in\n> @@ -216,16 +218,19 @@ do\n>                             -e \"s/^\\\\(.*mixed; boundary=\\\"-*\\\\)$V\\\\(-*\\\\)\\\"\\$/\\\\1g-i-t--v-e-r-s-i-o-n\\2\\\"/\"\n>                         echo \"\\$\"\n>                 } >\"$actual\" &&\n> -               if test -f \"$expect\"\n> +\n> +               if ! test -f \"$expect\"\n>                 then\n> -                       process_diffs \"$actual\" >actual &&\n> -                       process_diffs \"$expect\" >expect &&\n> -                       test_cmp expect actual\n> -               else\n> -                       # this is to help developing new tests.\n> -                       cp \"$actual\" \"$expect\"\n> -                       false\n> -               fi\n> +                       expect_new=\"$expect.new\" &&\n> +                       cp \"$actual\" \"$expect_new\" &&\n> +                       BUG \"Have no \\\"$expect_relative\\\", new test? The output is in \\\"$expect_new\\\", maybe use that?\"\n> +               fi &&\n> +\n> +               test_when_finished \"rm actual\" &&\n> +               process_diffs \"$actual\" >actual &&\n> +               test_when_finished \"rm expect\" &&\n> +               process_diffs \"$expect\" >expect &&\n> +               test_cmp expect actual\n>         '\n>  done <<\\EOF\n>  diff-tree initial\n\nThe rest LGTM, thanks.\n"},{"id":"421917","messageId":"87fszu2elw.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"CAHd-oW4G+7z3UM3qjhPp=2oqOPE4a49fweew0n+gheGtQEy5VA@mail.gmail.com","subject":"Re: [PATCH 2/2] diff tests: rewrite flakyness-causing test \"aid\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-13T19:01:31Z","receivedAt":"2021-04-13T19:01:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Apr 13 2021, Matheus Tavares Bernardino wrote:\n\n> On Tue, Apr 13, 2021 at 9:31 AM Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>>\n>> If a new test is added to this \"while read magic cmd\" test facility\n>> added in 3c2f75b590c (t4013: add tests for diff/log family output\n>> options., 2006-06-26) but no test file is added it'll fail the first\n>> time, but then succeed on subsequent runs as a new file has been added\n>> in t4013.\n>>\n>> Let's accomplish the same aim in way that doesn't cause subsequent\n>\n> s/in way/in a way/ ?\n\n*nod*\n\n>> test runs to succeed. If we can't find the file we'll BUG out, and\n>> suggest to the developer that they copy our \"expect.new\" file over,\n>> unlike the previous \"expect\" file this won't be picked up on\n>> subsequent runs.\n>>\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>  t/t4013-diff-various.sh | 25 +++++++++++++++----------\n>>  1 file changed, 15 insertions(+), 10 deletions(-)\n>>\n>> diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\n>> index 67f6411aff9..228ff100c61 100755\n>> --- a/t/t4013-diff-various.sh\n>> +++ b/t/t4013-diff-various.sh\n>> @@ -200,10 +200,12 @@ do\n>>         esac\n>>         test=$(echo \"$label\" | sed -e 's|[/ ][/ ]*|_|g')\n>>         pfx=$(printf \"%04d\" $test_count)\n>> -       expect=\"$TEST_DIRECTORY/t4013/diff.$test\"\n>> +       expect_relative=\"t4013/diff.$test\"\n>> +       expect=\"$TEST_DIRECTORY/$expect_relative\"\n>>         actual=\"$pfx-diff.$test\"\n>>\n>>         test_expect_$status \"git $cmd # magic is ${magic:-(not used)}\" '\n>> +               test_when_finished \"rm $actual\" &&\n>\n> Nit: before these two patches, \"$actual\" was only removed when the\n> test succeeded. So, in case of failure, the failed output files would\n> still be there for debugging. It might be interesting to keep this\n> behavior and only remove \"$actual\" at the end of the test.\n\nEither I'm missing something or you are, that's how test_when_finished\nworks.\n\nIt's skipped under e.g. \"--immediate --debug\". See b586744a864 (test:\nskip clean-up when running under --immediate mode, 2011-06-27)\n\nMaybe there's some edge case where we'd like to keep the files that it's\nnot covering, but then we should patch it to do the right thing, not use\nmanual \"rm\" at the end instead.\n\n>>                 {\n>>                         echo \"$ git $cmd\"\n>>                         case \"$magic\" in\n>> @@ -216,16 +218,19 @@ do\n>>                             -e \"s/^\\\\(.*mixed; boundary=\\\"-*\\\\)$V\\\\(-*\\\\)\\\"\\$/\\\\1g-i-t--v-e-r-s-i-o-n\\2\\\"/\"\n>>                         echo \"\\$\"\n>>                 } >\"$actual\" &&\n>> -               if test -f \"$expect\"\n>> +\n>> +               if ! test -f \"$expect\"\n>>                 then\n>> -                       process_diffs \"$actual\" >actual &&\n>> -                       process_diffs \"$expect\" >expect &&\n>> -                       test_cmp expect actual\n>> -               else\n>> -                       # this is to help developing new tests.\n>> -                       cp \"$actual\" \"$expect\"\n>> -                       false\n>> -               fi\n>> +                       expect_new=\"$expect.new\" &&\n>> +                       cp \"$actual\" \"$expect_new\" &&\n>> +                       BUG \"Have no \\\"$expect_relative\\\", new test? The output is in \\\"$expect_new\\\", maybe use that?\"\n>> +               fi &&\n>> +\n>> +               test_when_finished \"rm actual\" &&\n>> +               process_diffs \"$actual\" >actual &&\n>> +               test_when_finished \"rm expect\" &&\n>> +               process_diffs \"$expect\" >expect &&\n>> +               test_cmp expect actual\n>>         '\n>>  done <<\\EOF\n>>  diff-tree initial\n>\n> The rest LGTM, thanks.\n\nThanks a lot for the review!\n"},{"id":"421923","messageId":"CAHd-oW6D7Tmp2TmrmGvXDt-NnT_wxhfr_bn_P_7=ot=m_2eyyA@mail.gmail.com","threadId":"51667","inReplyTo":"87fszu2elw.fsf@evledraar.gmail.com","subject":"Re: [PATCH 2/2] diff tests: rewrite flakyness-causing test \"aid\"","fromName":"Matheus Tavares Bernardino","fromEmail":"matheus.bernardino@usp.br","sentAt":"2021-04-13T19:55:23Z","receivedAt":"2021-04-13T19:55:37Z","isPatch":true,"sender":{"key":"matheus.tavb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/12701583?v=4"},"body":"On Tue, Apr 13, 2021 at 4:01 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n>\n> On Tue, Apr 13 2021, Matheus Tavares Bernardino wrote:\n>\n> > On Tue, Apr 13, 2021 at 9:31 AM Ævar Arnfjörð Bjarmason\n> > <avarab@gmail.com> wrote:\n> >>  t/t4013-diff-various.sh | 25 +++++++++++++++----------\n> >>  1 file changed, 15 insertions(+), 10 deletions(-)\n> >>\n> >> diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\n> >> index 67f6411aff9..228ff100c61 100755\n> >> --- a/t/t4013-diff-various.sh\n> >> +++ b/t/t4013-diff-various.sh\n> >> @@ -200,10 +200,12 @@ do\n> >>         esac\n> >>         test=$(echo \"$label\" | sed -e 's|[/ ][/ ]*|_|g')\n> >>         pfx=$(printf \"%04d\" $test_count)\n> >> -       expect=\"$TEST_DIRECTORY/t4013/diff.$test\"\n> >> +       expect_relative=\"t4013/diff.$test\"\n> >> +       expect=\"$TEST_DIRECTORY/$expect_relative\"\n> >>         actual=\"$pfx-diff.$test\"\n> >>\n> >>         test_expect_$status \"git $cmd # magic is ${magic:-(not used)}\" '\n> >> +               test_when_finished \"rm $actual\" &&\n> >\n> > Nit: before these two patches, \"$actual\" was only removed when the\n> > test succeeded. So, in case of failure, the failed output files would\n> > still be there for debugging. It might be interesting to keep this\n> > behavior and only remove \"$actual\" at the end of the test.\n>\n> Either I'm missing something or you are, that's how test_when_finished\n> works.\n>\n> It's skipped under e.g. \"--immediate --debug\". See b586744a864 (test:\n> skip clean-up when running under --immediate mode, 2011-06-27)\n\nI was mostly thinking about the `artifacts` zip we get from our CI\nwhen a test fails. I find the final trash dir quite useful for some\npost-mortem analysis, especially to debug WIP tests that only fail\noccasionally or test failures on OSes I don't have quick access to.\n"},{"id":"421936","messageId":"xmqqpmyxkgjb.fsf@gitster.g","threadId":"51667","inReplyTo":"cover-0.2-00000000000-20210413T122645Z-avarab@gmail.com","subject":"Re: [PATCH 0/2] diff tests: un-flaky and post-gettext-poison cleanup","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-13T21:42:32Z","receivedAt":"2021-04-13T21:42:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> In reply to [1] because they're related post-GETTEXT_POISON cleanup,\n> but both apply on top of master.\n>\n> I had a flaky t4013 test with some local WIP code that I hadn't\n> investigated, as it turns out re-running the test was hiding the\n> failure. 2/2 fixes that, 1/2 is a minor post-GETTEXT_POISON cleanup.\n\nI feel somewhat offended to see t4013's \"auto preparation of the\nexpected output\" feature (which of course I invented) as \"flakey\" ;-)\nbut I agree that was a misfeature.  Thanks for taking a look.\n"},{"id":"421937","messageId":"xmqqlf9lkfye.fsf@gitster.g","threadId":"51667","inReplyTo":"CAHd-oW6D7Tmp2TmrmGvXDt-NnT_wxhfr_bn_P_7=ot=m_2eyyA@mail.gmail.com","subject":"Re: [PATCH 2/2] diff tests: rewrite flakyness-causing test \"aid\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-13T21:55:05Z","receivedAt":"2021-04-13T21:55:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matheus Tavares Bernardino <matheus.bernardino@usp.br> writes:\n\n> On Tue, Apr 13, 2021 at 4:01 PM Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>>\n>> > Nit: before these two patches, \"$actual\" was only removed when the\n>> > test succeeded. So, in case of failure, the failed output files would\n>> > still be there for debugging. It might be interesting to keep this\n>> > behavior and only remove \"$actual\" at the end of the test.\n>>\n>> Either I'm missing something or you are, that's how test_when_finished\n>> works.\n>>\n>> It's skipped under e.g. \"--immediate --debug\". See b586744a864 (test:\n>> skip clean-up when running under --immediate mode, 2011-06-27)\n>\n> I was mostly thinking about the `artifacts` zip we get from our CI\n> when a test fails. I find the final trash dir quite useful for some\n> post-mortem analysis, especially to debug WIP tests that only fail\n> occasionally or test failures on OSes I don't have quick access to.\n\nYeah, without \"--immediate\" we do run the clean-up tasks registered\nwith test_when_finished, so it may be a problem.\n\n"},{"id":"421951","messageId":"874kg92xn0.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"CAHd-oW6D7Tmp2TmrmGvXDt-NnT_wxhfr_bn_P_7=ot=m_2eyyA@mail.gmail.com","subject":"Re: [PATCH 2/2] diff tests: rewrite flakyness-causing test \"aid\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-14T06:22:43Z","receivedAt":"2021-04-14T06:22:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Apr 13 2021, Matheus Tavares Bernardino wrote:\n\n> On Tue, Apr 13, 2021 at 4:01 PM Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>>\n>>\n>> On Tue, Apr 13 2021, Matheus Tavares Bernardino wrote:\n>>\n>> > On Tue, Apr 13, 2021 at 9:31 AM Ævar Arnfjörð Bjarmason\n>> > <avarab@gmail.com> wrote:\n>> >>  t/t4013-diff-various.sh | 25 +++++++++++++++----------\n>> >>  1 file changed, 15 insertions(+), 10 deletions(-)\n>> >>\n>> >> diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\n>> >> index 67f6411aff9..228ff100c61 100755\n>> >> --- a/t/t4013-diff-various.sh\n>> >> +++ b/t/t4013-diff-various.sh\n>> >> @@ -200,10 +200,12 @@ do\n>> >>         esac\n>> >>         test=$(echo \"$label\" | sed -e 's|[/ ][/ ]*|_|g')\n>> >>         pfx=$(printf \"%04d\" $test_count)\n>> >> -       expect=\"$TEST_DIRECTORY/t4013/diff.$test\"\n>> >> +       expect_relative=\"t4013/diff.$test\"\n>> >> +       expect=\"$TEST_DIRECTORY/$expect_relative\"\n>> >>         actual=\"$pfx-diff.$test\"\n>> >>\n>> >>         test_expect_$status \"git $cmd # magic is ${magic:-(not used)}\" '\n>> >> +               test_when_finished \"rm $actual\" &&\n>> >\n>> > Nit: before these two patches, \"$actual\" was only removed when the\n>> > test succeeded. So, in case of failure, the failed output files would\n>> > still be there for debugging. It might be interesting to keep this\n>> > behavior and only remove \"$actual\" at the end of the test.\n>>\n>> Either I'm missing something or you are, that's how test_when_finished\n>> works.\n>>\n>> It's skipped under e.g. \"--immediate --debug\". See b586744a864 (test:\n>> skip clean-up when running under --immediate mode, 2011-06-27)\n>\n> I was mostly thinking about the `artifacts` zip we get from our CI\n> when a test fails. I find the final trash dir quite useful for some\n> post-mortem analysis, especially to debug WIP tests that only fail\n> occasionally or test failures on OSes I don't have quick access to.\n\nAh, yes that's a problem we should solve, but I think we should not put\noff migration to test_when_finished because of that.\n\nThe whole reason we use it is to clean up the work area for the next\ntest.\n\nThus if we do:\n\n    git something >expected &&\n    test_cmp expected actual &&\n    rm expected actual\n\nAnd \"git something\" segfaults it's only dumb luck that subsequent tests\ndon't fail in non-obvious ways due to the files being left behind.\n\nIf we could rely on this in general we could make everything under\ntest_when_finished a noop, but I think you'll find that'll fail a lot of\nthings in the test suite if we do that.\n\nBut the problem you cite is legitimate, but we should solve it with\nsomething like:\n\n 1. If we fail tests 58 and 67 out of 100 we should/could run these\n    again at the end under some more exhaustive debugging mode where\n    we'd save away all the intermediate files.\n\n 2. Or, and test_when_finished helps here, we could set $PATH when it\n    runs to a directory with a custom \"rm\", which would save away the\n    temporary files, expecting that we'll tar them up if any of the\n    tests whose files we saved failed (we'd delete the other ones).\n\n 3. Similarly, wrap \"rm\" in some \"cat and rm\" for common cases like\n    actual/expected files, and improve the verbose output to some\n    \"verbose if fails\".\n\n"},{"id":"421952","messageId":"xmqqim4pz83e.fsf@gitster.g","threadId":"51667","inReplyTo":"874kg92xn0.fsf@evledraar.gmail.com","subject":"Re: [PATCH 2/2] diff tests: rewrite flakyness-causing test \"aid\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-14T06:35:49Z","receivedAt":"2021-04-14T06:35:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> On Tue, Apr 13 2021, Matheus Tavares Bernardino wrote:\n> ...\n>>> >>         actual=\"$pfx-diff.$test\"\n>>> >>\n>>> >>         test_expect_$status \"git $cmd # magic is ${magic:-(not used)}\" '\n>>> >> +               test_when_finished \"rm $actual\" &&\n>>> >\n>>> > Nit: before these two patches, \"$actual\" was only removed when the\n>>> > test succeeded. So, in case of failure, the failed output files would\n>>> > still be there for debugging. It might be interesting to keep this\n>>> > behavior and only remove \"$actual\" at the end of the test.\n> ...\n> Ah, yes that's a problem we should solve, but I think we should not put\n> off migration to test_when_finished because of that.\n>\n> The whole reason we use it is to clean up the work area for the next\n> test.\n>\n> Thus if we do:\n>\n>     git something >expected &&\n>     test_cmp expected actual &&\n>     rm expected actual\n\nIsn't it a poor example to use to argue for your particular change,\nwhere $actual in the original is designed to be unique among tests,\nin order to ensure that $actual files left after test pieces fail\nwould not interfere with the tests that come later?  IOW, there is\nnot a reason to remove $actual until the end of the test sequence,\nis there?\n"},{"id":"421955","messageId":"871rbd2u47.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"xmqqim4pz83e.fsf@gitster.g","subject":"Re: [PATCH 2/2] diff tests: rewrite flakyness-causing test \"aid\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-14T07:38:48Z","receivedAt":"2021-04-14T07:38:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Apr 14 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> On Tue, Apr 13 2021, Matheus Tavares Bernardino wrote:\n>> ...\n>>>> >>         actual=\"$pfx-diff.$test\"\n>>>> >>\n>>>> >>         test_expect_$status \"git $cmd # magic is ${magic:-(not used)}\" '\n>>>> >> +               test_when_finished \"rm $actual\" &&\n>>>> >\n>>>> > Nit: before these two patches, \"$actual\" was only removed when the\n>>>> > test succeeded. So, in case of failure, the failed output files would\n>>>> > still be there for debugging. It might be interesting to keep this\n>>>> > behavior and only remove \"$actual\" at the end of the test.\n>> ...\n>> Ah, yes that's a problem we should solve, but I think we should not put\n>> off migration to test_when_finished because of that.\n>>\n>> The whole reason we use it is to clean up the work area for the next\n>> test.\n>>\n>> Thus if we do:\n>>\n>>     git something >expected &&\n>>     test_cmp expected actual &&\n>>     rm expected actual\n>\n> Isn't it a poor example to use to argue for your particular change,\n> where $actual in the original is designed to be unique among tests,\n> in order to ensure that $actual files left after test pieces fail\n> would not interfere with the tests that come later?  IOW, there is\n> not a reason to remove $actual until the end of the test sequence,\n> is there?\n\nNot really, because you needed to read the rest of the test file to come\nto that conclusion.\n\nThe point of using a helper that guarantees cleanup such as\ntest_when_finished or test_config over manual \"git config\" or \"git rm\"\nisn't that we can prove that we need it because a later test needs the\ncleanup, but that anyone can add new tests or functionality without\nhaving to worry about cleaning up the existing trash directory.\n\nSo yes, it's not needed here, but that's only because we know the rest\nof the tests don't have e.g. a test that does a:\n\n    for file in t4013/*\n\nAnyway, that's only half of the example I cited for why we should use\ntest_when_finished in general, the other half was noting that because\nwe've semantically annotated cleanup tasks we can smartly handle those\nfor debugging, e.g. stash the \"removed\" files away, and if the test\nfails present them to the user.\n\n"},{"id":"421956","messageId":"xmqqblahz4hx.fsf@gitster.g","threadId":"51667","inReplyTo":"871rbd2u47.fsf@evledraar.gmail.com","subject":"Re: [PATCH 2/2] diff tests: rewrite flakyness-causing test \"aid\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-14T07:53:30Z","receivedAt":"2021-04-14T07:54:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n>>> Thus if we do:\n>>>\n>>>     git something >expected &&\n>>>     test_cmp expected actual &&\n>>>     rm expected actual\n>>\n>> Isn't it a poor example to use to argue for your particular change,\n>> where $actual in the original is designed to be unique among tests,\n>> in order to ensure that $actual files left after test pieces fail\n>> would not interfere with the tests that come later?  IOW, there is\n>> not a reason to remove $actual until the end of the test sequence,\n>> is there?\n>\n> Not really, because you needed to read the rest of the test file to come\n> to that conclusion.\n>\n> The point of using a helper that guarantees cleanup such as\n> test_when_finished or test_config over manual \"git config\" or \"git rm\"\n> isn't that we can prove that we need it because a later test needs the\n> cleanup, but that anyone can add new tests or functionality without\n> having to worry about cleaning up the existing trash directory.\n>\n> So yes, it's not needed here, but that's only because we know the rest\n> of the tests don't have e.g. a test that does a:\n\nIn this particular case, $actual files are designed to be left\nbehind for failed test pieces, so that the tester can come back and\ninspect them.  I probably should have said it a bit more strongly\nthan \"there is not a reason to remove\".  You SHOULD NOT remove and\nthat is why we had \"check and then remove only upon success\" there,\ninstead of test_when_finished.  We want them left for and only for\nfailing test pieces.\n\nPlease do not advocate for and encourage newbies who would be\nreading the discussion from sidelines to use test_when_finished out\nof dogmatic principle without thinking.  Even though there are valid\ncases where test_when_finished is the perfect fit, in this\nparticular case, use of it is a clear regression.\n\nThanks.\n"},{"id":"421960","messageId":"87y2dl18mg.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"xmqqblahz4hx.fsf@gitster.g","subject":"Re: [PATCH 2/2] diff tests: rewrite flakyness-causing test \"aid\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-14T10:08:23Z","receivedAt":"2021-04-14T10:09:14Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Apr 14 2021, Junio C Hamano wrote:\n\n[\"tl;dr\" below]:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>>>> Thus if we do:\n>>>>\n>>>>     git something >expected &&\n>>>>     test_cmp expected actual &&\n>>>>     rm expected actual\n>>>\n>>> Isn't it a poor example to use to argue for your particular change,\n>>> where $actual in the original is designed to be unique among tests,\n>>> in order to ensure that $actual files left after test pieces fail\n>>> would not interfere with the tests that come later?  IOW, there is\n>>> not a reason to remove $actual until the end of the test sequence,\n>>> is there?\n>>\n>> Not really, because you needed to read the rest of the test file to come\n>> to that conclusion.\n>>\n>> The point of using a helper that guarantees cleanup such as\n>> test_when_finished or test_config over manual \"git config\" or \"git rm\"\n>> isn't that we can prove that we need it because a later test needs the\n>> cleanup, but that anyone can add new tests or functionality without\n>> having to worry about cleaning up the existing trash directory.\n>>\n>> So yes, it's not needed here, but that's only because we know the rest\n>> of the tests don't have e.g. a test that does a:\n>\n> In this particular case, $actual files are designed to be left\n> behind for failed test pieces, so that the tester can come back and\n> inspect them.  I probably should have said it a bit more strongly\n> than \"there is not a reason to remove\".  You SHOULD NOT remove and\n> that is why we had \"check and then remove only upon success\" there,\n> instead of test_when_finished.  We want them left for and only for\n> failing test pieces.\n\nYes, it's clear that it's designed to do that. I'm not disagreeing on\nthe intent of your commit in 2006 to set it up this way.\n\nWhat I am saying is that it's incompatible to have:\n\n 1. Failing tests\n 2. Not removing scratch files that would otherwise be removed\n\nAnd:\n\n 3. Knowing that the rest of the tests pass in the case of #1 without\n    reading them all.\n\nHence the suggestion that we should use test_when_finished without\nexception for such patterns.\n\nBecause consistently using the helper would allow us to smartly get #1\nand #2 without #3, i.e. some \"copy them for later analysis\" as suggested\nupthread in:\nhttp://lore.kernel.org/git/874kg92xn0.fsf@evledraar.gmail.com\n\n> Please do not advocate for and encourage newbies who would be\n> reading the discussion from sidelines to use test_when_finished out\n> of dogmatic principle without thinking.\n\nIs Matheus the newbie here? I think he's contributed enough to form his\nown opinion.\n\nI think it's clear to anyone else reading from the sidelines that we're\nhaving some informed disagreement about a finer point of patterns in the\ntest suite.\n\nI doubt either one of us is likely to have much of an impact on newbies\nreading from the sidelines.\n\nIn any case, I don't see how you're able to read this thread and come to\nthe conclusion that I'm proposing the use of test_when_finished out of\n\"[some] dogmatic principle\".\n\nI'm not. I'm proposing to use it because I think it makes sense, and I\nthink the reasons you've noted for avoiding it in this case have more\ndownsides than upsites, as noted in the #1-3 examples above.\n\n> Even though there are valid cases where test_when_finished is the\n> perfect fit, in this particular case, use of it is a clear regression.\n\nWell, I disagree with you.\n\nI think even if the patch I was proposing was a mere conversion to\ntest_when_finished as would be typical for most newly written tests,\ni.e. a diff like this:\n\t\n\tdiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\n\tindex 6cca8b84a6b..d0031aa0f7b 100755\n\t--- a/t/t4013-diff-various.sh\n\t+++ b/t/t4013-diff-various.sh\n\t@@ -204,6 +204,7 @@ do\n\t \tactual=\"$pfx-diff.$test\"\n\t \n\t \ttest_expect_$status \"git $cmd # magic is ${magic:-(not used)}\" '\n\t+\t\ttest_when_finished \"rm \\\"$actual\\\"\" &&\n\t \t\t{\n\t \t\t\techo \"$ git $cmd\"\n\t \t\t\tcase \"$magic\" in\n\t@@ -216,22 +217,11 @@ do\n\t \t\t\t    -e \"s/^\\\\(.*mixed; boundary=\\\"-*\\\\)$V\\\\(-*\\\\)\\\"\\$/\\\\1g-i-t--v-e-r-s-i-o-n\\2\\\"/\"\n\t \t\t\techo \"\\$\"\n\t \t\t} >\"$actual\" &&\n\t-\t\tif test -f \"$expect\"\n\t-\t\tthen\n\t-\t\t\tprocess_diffs \"$actual\" >actual &&\n\t-\t\t\tprocess_diffs \"$expect\" >expect &&\n\t-\t\t\tcase $cmd in\n\t-\t\t\t*format-patch* | *-stat*)\n\t-\t\t\t\ttest_cmp expect actual;;\n\t-\t\t\t*)\n\t-\t\t\t\ttest_cmp expect actual;;\n\t-\t\t\tesac &&\n\t-\t\t\trm -f \"$actual\" actual expect\n\t-\t\telse\n\t-\t\t\t# this is to help developing new tests.\n\t-\t\t\tcp \"$actual\" \"$expect\"\n\t-\t\t\tfalse\n\t-\t\tfi\n\t+\t\ttest_when_finished \"rm actual\" &&\n\t+\t\tprocess_diffs \"$actual\" >actual &&\n\t+\t\ttest_when_finished \"rm expect\" &&\n\t+\t\tprocess_diffs \"$expect\" >expect &&\n\t+\t\ttest_cmp expect actual\n\t \t'\n\t done <<\\EOF\n\t diff-tree initial\n\nIt's a clear improvement, because, on current master, emulating the case\nwhen you add a new test-case:\n\n    $ rm t4013/diff.log_-GF_-p_--pickaxe-all_master\n\nNow run it once:\n\n    $ ./t4013-diff-various.sh\n    [...]\n    # failed 1 among 199 test(s)\n\nAnd then to debug it:\n\n    $ ./t4013-diff-various.sh -vixd\n    [...]\n    # passed all 199 test(s)\n\nSo because we copied the file around the only get the failure the first\ntime around.\n\nYou might rightly say that we could have narrowly just fixed that\nparticular bug and kept the old \"copy and return false\", so something\nlike:\n\t\n\tdiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\n\tindex 6cca8b84a6b..6f0f3c7f53c 100755\n\t--- a/t/t4013-diff-various.sh\n\t+++ b/t/t4013-diff-various.sh\n\t@@ -229,7 +229,7 @@ do\n\t \t\t\trm -f \"$actual\" actual expect\n\t \t\telse\n\t \t\t\t# this is to help developing new tests.\n\t-\t\t\tcp \"$actual\" \"$expect\"\n\t+\t\t\tcp \"$actual\" \"$expect.MAYBE-USE-THIS\"\n\t \t\t\tfalse\n\t \t\tfi\n\t \t'\n\nBut as noted above that gets us into needing to worry about cascading\nfailure. I.e. any new test added later in the file needs to think about\n\"what if the t4013 directory is in this state?\".\n\nWe also document what we'll do and how we'll do it in t/README in this\ncase:\n\t\n\t--immediate::\n\t\tThis causes the test to immediately exit upon the first\n\t\tfailed test. Cleanup commands requested with\n\t\ttest_when_finished are not executed if the test failed,\n\t\tin order to keep the state for inspection by the tester\n\t\tto diagnose the bug.\n\nSo it's much more obvious and consistent to have a failure that ends on\nthe test_cmp and being able to see the test_when_finished for the\nintermediate files in the trace output.\n\nThe hypothetical newbie might have debugged such a pattern already, but\nnot having it end in a special-case used only for this test:\n\n    [...]\n    + test -f /home/avar/g/git/t/t4013/diff.log_-GF_-p_--pickaxe-all_master\n    + cp 0096-diff.log_-GF_-p_--pickaxe-all_master /home/avar/g/git/t/t4013/diff.log_-GF_-p_--pickaxe-all_master.MAYBE-USE-THIS\n    + false\n\nIn any case, neither one of those is the patch I'm suggesting upthread.\n\nI am suggesting not just to use test_when_finished, but that we should\nuse BUG here. Since not having the file itself is a bug in git's test\nsuite, not a mere failure of the test case. The test will never pass if\nthe file doesn't exist.\n\nSo even if I agreed with you that the more narrow migration to\ntest_when_finished would be a regression, I don't see how you think\n*this* patch could be a regression.\n\nYou seem to just be focusing on the test_when_finished part of it, but\nthat's not all it's doing. Since we also use BUG the output is now:\n\n    $ ./t4013-diff-various.sh \n    [...]\n    error: bug in the test script: Have no \"t4013/diff.log_-GF_-p_--pickaxe-all_master\", new test? [...]\n\nI.e. whether we used \"rm\" or \"cp\" or \"test_when_finished 'rm [...]'\"\nearlier has become irrelevant to achieve the states aims in 3c2f75b590c\n(t4013: add tests for diff/log family output options., 2006-06-26).\n\nIt doesn't matter how we're removing/copying etc. the file anymore,\nthat's no longer how we're helping the user debug the test.\n\ntl;dr:\n\nIn any case, I think it's best to just drop this series. I wrote the\nabove more as a synopsis of what I belive we should be doing in general,\nI do have some patches queued up to address the general debug-ability of\nour test output (e.g. for CI).\n\nFor that end-goal having e.g. test_when_finished (mostly) consistently\nused would help a lot, but not using it in any one test would be\nfine. This is just one such case, and something I thought wouldn't be a\ncontroversial patch.\n\nI think at this point me trying to gleaming entrails of what you want &\nyou responding back is going to take both of us much more time than\nreplacing this series with a patch you'd be happy with, and this wasn't\nsomething I cared about more than a one-off.\n"},{"id":"422004","messageId":"xmqqlf9krmkx.fsf@gitster.g","threadId":"51667","inReplyTo":"87y2dl18mg.fsf@evledraar.gmail.com","subject":"Re: [PATCH 2/2] diff tests: rewrite flakyness-causing test \"aid\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-15T08:14:38Z","receivedAt":"2021-04-15T08:14:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> What I am saying is that it's incompatible to have:\n>\n>  1. Failing tests\n>  2. Not removing scratch files that would otherwise be removed\n>\n> And:\n>\n>  3. Knowing that the rest of the tests pass in the case of #1 without\n>     reading them all.\n>\n> Hence the suggestion that we should use test_when_finished without\n> exception for such patterns.\n\nI disagree with the above; t4013's \"read magic cmd\" part is designed\nto be independent from each other; I do not think you need to read\nall of the parts enclosed in << ... EOF to understand that.\n\nIn short,\n\n * \"test_when_finished 'rm ...'\" is a good tool to ensure something\n   gets removed no matter what else happens in the same test.  Since\n   it does not run the clean-up under \"-i\", it can even be used on\n   files that would be useful in diagnosing failures.  But under\n   \"-d\", it does clean-up to avoid affecting the following test.\n\n * $actual was made unique so that even under \"-d\", a failing test\n   would not negatively affect the subsequent ones.  Removing it for\n   failure cases is actively wrong, so use of test_when_finished,\n   which may be an expedient way to remove artifact that would\n   negatively affect later test pieces, does not apply --- existing\n   code is doing better than test_when_finished can offer, and\n   replacing it with test_when_finished is a regression.\n\n * And the most important part: the unnecessary removal of $actual\n   was not even part of the \"flakyness-causing\" bug you started this\n   topic to fix anyway.  We should just remove the regression caused\n   by unnecessary use of test_when_finished and focus on the \"don't\n   place the actual output from a brand new test under the file used\n   for the expectation the next time---instead place it under\n   temporary file and call for attention\" part, which was the real\n   improvement.\n\n>> Please do not advocate for and encourage newbies who would be\n>> reading the discussion from sidelines to use test_when_finished out\n>> of dogmatic principle without thinking.\n>\n> Is Matheus the newbie here? I think he's contributed enough to form his\n> own opinion.\n\nNo, those \"reading the discussion from sidelines\" are not on To: or\nCc:, but are eager to learn by reading what is available to git@\nsubscribers (including the lore archive).  I do not want those of us\nwhose name appear often in the list archive to be sending a wrong\nsignal to them.\n\n> In any case, I think it's best to just drop this series.\n\nThat is a sad and wrong conclusion.  We should just realize what we\nreally wanted to fix in the first place and keep the improvement;\notherwise all the review time was wasted.\n\nAnd next time, I'd suggest you not to spend too many bytes on \"while\nat it\".  Clear and obvious clean-up while the tree and the project\nis otherwise quiescent is very much no-brainer welcome, but\notherwise...\n\nThanks.\n"},{"id":"422030","messageId":"8735vrvg39.fsf@evledraar.gmail.com","threadId":"51667","inReplyTo":"xmqqlf9krmkx.fsf@gitster.g","subject":"Re: [PATCH 2/2] diff tests: rewrite flakyness-causing test \"aid\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-15T13:21:14Z","receivedAt":"2021-04-15T13:21:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Apr 15 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> What I am saying is that it's incompatible to have:\n>>\n>>  1. Failing tests\n>>  2. Not removing scratch files that would otherwise be removed\n>>\n>> And:\n>>\n>>  3. Knowing that the rest of the tests pass in the case of #1 without\n>>     reading them all.\n>>\n>> Hence the suggestion that we should use test_when_finished without\n>> exception for such patterns.\n>\n> I disagree with the above; t4013's \"read magic cmd\" part is designed\n> to be independent from each other; I do not think you need to read\n> all of the parts enclosed in << ... EOF to understand that.\n>\n> In short,\n>\n>  * \"test_when_finished 'rm ...'\" is a good tool to ensure something\n>    gets removed no matter what else happens in the same test.  Since\n>    it does not run the clean-up under \"-i\", it can even be used on\n>    files that would be useful in diagnosing failures.  But under\n>    \"-d\", it does clean-up to avoid affecting the following test.\n>\n>  * $actual was made unique so that even under \"-d\", a failing test\n>    would not negatively affect the subsequent ones. [...]\n\nI don't mind disagreeing with you, but I do feel like we're just talking\npast each other, either that or we've got a different understanding of\n\"negatively affect the subsequent ones\".\n\nI'm saying that yes I agree that *right now* it doesn't negatively\naffect any of the subsequent ones, but that you can only know that if\nyou need all the tests in the file past the one you're modifying.\n\nI.e. imagine if we added, obviously contrived, but a stand-in for \"we\nexpect consistent state\":\n\t\n\tdiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\n\tindex 6cca8b84a6b..a326968e8f3 100755\n\t--- a/t/t4013-diff-various.sh\n\t+++ b/t/t4013-diff-various.sh\n\t@@ -526,4 +526,9 @@ test_expect_success 'diff -I<regex>: detect malformed regex' '\n\t \ttest_i18ngrep \"invalid regex given to -I: \" error\n\t '\n\t \n\t+test_expect_success 'stuff' '\n\t+\tfind * -type f >actual &&\n\t+\ttest_line_count = 9 actual\n\t+'\n\t+\n\t test_done\n\nNow run it, and you'll pass them all, now break one of the earlier tests\nwith:\n\n    >t4013/diff.log_-GF_-p_--pickaxe-all_master\n\nYou'll fail two tests (the expected one, and the new one), instead of\njust one, which is purely an artifact of the earlier test's cleanup /\nstate being different as a result of whether it passes or not.\n\nWith my patch you'll fail just the one earlier test, because we do the\nsame cleanup no matter whether we failed midway through it or not.\n\nSo yes, while this small series amounts to re-arranging some deck chairs\non the Titanic I do think not being able to rely on consistent state is\na big anti-pattern in the test suite in general.\n\nIt's a big part of the reason for why you can't reliably use --run=N or\nGIT_SKIP_TESTS (especially in cases of partial failures) to selectively\nskip things without what amounts to bespoke analysis of each test you'd\nlike to run a subset of.\n\nNow, of course test_when_finished won't get us there by itself, we'd\nalso need consistently use some \"test_expect_setup\" to clearly delineate\n\"setup\" and other \"this must run\" state changes from \"just consumes\nexisting state and resets\". But consistently writing new/modified tests\nas \"just consumes existing state and resets\" will slowly get us there.\n\n>    [...] Removing it for\n>    failure cases is actively wrong, so use of test_when_finished,\n>    which may be an expedient way to remove artifact that would\n>    negatively affect later test pieces, does not apply --- existing\n>    code is doing better than test_when_finished can offer, and\n>    replacing it with test_when_finished is a regression.\n\nAside from whether we agree or disagree on whether test_when_finished\nshould be consistently used to clean up state I genuinely still don't\nsee how the sum total of replacing the current \"rm && cp\" with\n\"test_when_finished && BUG\" leaves us worse off.\n\nAre you thinking of the case where someone wants to add N new lines to\nthe \"while read magic cmd\" loop?\n\nThat's the only case I can think of that would arguably be worse, but I\nthink it's far outweighed by the more obvious failure mode, and a \"git\nblame\" on the lines it consumes shows that additions are very occasional\n(and not in large batches).\n\nOther than that, under e.g. -vixd having the test suite stop on the\nspecific line that failed (the test_cmp, not a \"false\" after some \"cp\")\nis how most other tests works, and I think makes things much more\nreadable and understandable at a glance.\n\n>  * And the most important part: the unnecessary removal of $actual\n>    was not even part of the \"flakyness-causing\" bug you started this\n>    topic to fix anyway.  We should just remove the regression caused\n>    by unnecessary use of test_when_finished and focus on the \"don't\n>    place the actual output from a brand new test under the file used\n>    for the expectation the next time---instead place it under\n>    temporary file and call for attention\" part, which was the real\n>    improvement.\n\nI see your point and agree in theory that the \"we write into the\nt/t4013/ directory\" and \"we inconsistently clear up the trash directory\non OK/NOK\" are in principle different issues.\n\nBut in this case we always removed or copied \"$actual\" away from its\noriginal name before, we do the same now (just more reliably, and in a\nway that doesn't cause flakyness in the \"cp\" case).\n\nSo I don't really see how to split up those two arguably different\nchanges in a way that would make sense.\n\nThe only case where \"$actual\" was left before was (I think) a clear of\nleaking state unnecessarily outside of --immediate.\n\n>>> Please do not advocate for and encourage newbies who would be\n>>> reading the discussion from sidelines to use test_when_finished out\n>>> of dogmatic principle without thinking.\n>>\n>> Is Matheus the newbie here? I think he's contributed enough to form his\n>> own opinion.\n>\n> No, those \"reading the discussion from sidelines\" are not on To: or\n> Cc:, but are eager to learn by reading what is available to git@\n> subscribers (including the lore archive).  I do not want those of us\n> whose name appear often in the list archive to be sending a wrong\n> signal to them.\n\nI'll endeavor to make it clear that my opinion is just my opinion\netc. in the future, I thought it was clear from the context :)\n\n>> In any case, I think it's best to just drop this series.\n>\n> That is a sad and wrong conclusion.  We should just realize what we\n> really wanted to fix in the first place and keep the improvement;\n> otherwise all the review time was wasted.\n\nTo be clear that's not some \"I can't have the change I want so I'm\ntaking my toys and going home\" tantrum. I don't mind making changes in\nresponse to reviews etc.\n\nAs noted above I'm genuinely still unable to see how I'd selectively\nuntangle the change I made in a way that both wouldn't either fix bugs\nin the test, or introduce new bugs while I was at it, and that I'd be\ncomfortable putting my name behind.\n\nHence the suggestion that since we're already we past the point of just\nhaving come up with two independent approaches to this in terms of time\ninvestment, that doing so would be a better thing overall.\n\nI do think (and would be willing to work on) that t/README should have\nsome summary of best practices for new/updated tests that we should be\nstriving for. Looking at our \"test_when_finished\" there's at least tow\nindependent bugs[1] in it, so ther's some room for improvement.\n\n1. It creates a ref with echo, but removes it with update-ref -d, which\n   e.g. couldn't delete a corrupt ref (if rev-parse is buggy); and more\n   importantly if rev-parse dies midway through we're left with a\n   corrupt repo, the \"test_when_finished ... update-ref -d\" should come\n   *first*.\n\n"},{"id":"422063","messageId":"20210415212600.GN2947267@szeder.dev","threadId":"51667","inReplyTo":"CAHd-oW6D7Tmp2TmrmGvXDt-NnT_wxhfr_bn_P_7=ot=m_2eyyA@mail.gmail.com","subject":"Re: [PATCH 2/2] diff tests: rewrite flakyness-causing test \"aid\"","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2021-04-15T21:26:00Z","receivedAt":"2021-04-15T21:26:05Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Tue, Apr 13, 2021 at 04:55:23PM -0300, Matheus Tavares Bernardino wrote:\n> On Tue, Apr 13, 2021 at 4:01 PM Ævar Arnfjörð Bjarmason\n> > >> diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\n> > >> index 67f6411aff9..228ff100c61 100755\n> > >> --- a/t/t4013-diff-various.sh\n> > >> +++ b/t/t4013-diff-various.sh\n> > >> @@ -200,10 +200,12 @@ do\n> > >>         esac\n> > >>         test=$(echo \"$label\" | sed -e 's|[/ ][/ ]*|_|g')\n> > >>         pfx=$(printf \"%04d\" $test_count)\n> > >> -       expect=\"$TEST_DIRECTORY/t4013/diff.$test\"\n> > >> +       expect_relative=\"t4013/diff.$test\"\n> > >> +       expect=\"$TEST_DIRECTORY/$expect_relative\"\n> > >>         actual=\"$pfx-diff.$test\"\n> > >>\n> > >>         test_expect_$status \"git $cmd # magic is ${magic:-(not used)}\" '\n> > >> +               test_when_finished \"rm $actual\" &&\n> > >\n> > > Nit: before these two patches, \"$actual\" was only removed when the\n> > > test succeeded. So, in case of failure, the failed output files would\n> > > still be there for debugging. It might be interesting to keep this\n> > > behavior and only remove \"$actual\" at the end of the test.\n> >\n> > Either I'm missing something or you are, that's how test_when_finished\n> > works.\n> >\n> > It's skipped under e.g. \"--immediate --debug\". See b586744a864 (test:\n> > skip clean-up when running under --immediate mode, 2011-06-27)\n> \n> I was mostly thinking about the `artifacts` zip we get from our CI\n> when a test fails. I find the final trash dir quite useful for some\n> post-mortem analysis, especially to debug WIP tests that only fail\n> occasionally or test failures on OSes I don't have quick access to.\n\nOn Travis CI we run tests with '--immediate' for exactly this reason;\nI don't know why it's done differently on other CI systems, and,\nunfortunately, 'git log --grep=immediate ci/' didn't turn up any\ninsights.\n\n"},{"id":"422109","messageId":"xmqqsg3qm65k.fsf@gitster.g","threadId":"51667","inReplyTo":"8735vrvg39.fsf@evledraar.gmail.com","subject":"Re: [PATCH 2/2] diff tests: rewrite flakyness-causing test \"aid\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-16T18:32:55Z","receivedAt":"2021-04-16T18:33:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Are you thinking of the case where someone wants to add N new lines to\n> the \"while read magic cmd\" loop?\n\nNo.  I think stopping with BUG is fine.  Adding a new test is a\nserious event and I'd prefer to make it stop to give it undistracted\nattention by the developer, instead of letting many to be handled at\nonce.\n\nOf course, we _could_ instead remember the fact that we deposted\nnewly proposed expectation pattern that the user must audit, still\nkeep going and list the new files at the end, but I do not think it\nis necessary.  Your newly added BUG is perfectly fine. \n\n>>> In any case, I think it's best to just drop this series.\n>>\n>> That is a sad and wrong conclusion.  We should just realize what we\n>> really wanted to fix in the first place and keep the improvement;\n>> otherwise all the review time was wasted.\n>\n> To be clear that's not some \"I can't have the change I want so I'm\n> taking my toys and going home\" tantrum. I don't mind making changes in\n> response to reviews etc.\n>\n> As noted above I'm genuinely still unable to see how I'd selectively\n> untangle the change I made in a way that both wouldn't either fix bugs\n> in the test, or introduce new bugs while I was at it, and that I'd be\n> comfortable putting my name behind.\n\nI fail to see why the \"rewrite flakyness-causing test aid\" that you\nset out to perform should need more than a two-liner change.  There\nis no reason to touch '&& rm \"$actual\" actual expect' at the end.\nIt is the success case where the flakyness-causing \"aid\" does not\neven kick in.\n\n t/t4013-diff-various.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git c/t/t4013-diff-various.sh w/t/t4013-diff-various.sh\nindex 6cca8b84a6..da6da13920 100755\n--- c/t/t4013-diff-various.sh\n+++ w/t/t4013-diff-various.sh\n@@ -229,8 +229,8 @@ do\n \t\t\trm -f \"$actual\" actual expect\n \t\telse\n \t\t\t# this is to help developing new tests.\n-\t\t\tcp \"$actual\" \"$expect\"\n-\t\t\tfalse\n+\t\t\tmv \"$actual\" \"$expect.new\"\n+\t\t\tBUG \"No $expect; check $expect.new and use it perhaps?\"\n \t\tfi\n \t'\n done <<\\EOF\nn\n"}]}