{"thread":{"id":"57383","subject":"[PATCH v11 01/13] ls-tree: remove commented-out code","startedAt":"2022-02-08T13:14:55Z","lastAt":"2022-06-09T20:02:31Z","messageCount":107,"participants":["Teng Long","Ævar Arnfjörð Bjarmason","Junio C Hamano","Josh Steadmon","Johannes Schindelin"],"isPatch":true,"patchVersion":11,"patchTotal":13},"messages":[{"id":"447972","messageId":"2fcff7e0d40255e0be9496647787352dd0aeb784.1644319434.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1644319434.git.dyroneteng@gmail.com","subject":"[PATCH v11 01/13] ls-tree: remove commented-out code","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-02-08T12:14:26Z","receivedAt":"2022-02-08T13:14:55Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nRemove code added in f35a6d3bce7 (Teach core object handling functions\nabout gitlinks, 2007-04-09), later patched in 7d0b18a4da1 (Add output\nflushing before fork(), 2008-08-04), and then finally ending up in its\ncurrent form in d3bee161fef (tree.c: allow read_tree_recursive() to\ntraverse gitlink entries, 2009-01-25). All while being commented-out!\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 9 ---------\n 1 file changed, 9 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3a442631c7..5f7c84950c 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -69,15 +69,6 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tconst char *type = blob_type;\n \n \tif (S_ISGITLINK(mode)) {\n-\t\t/*\n-\t\t * Maybe we want to have some recursive version here?\n-\t\t *\n-\t\t * Something similar to this incomplete example:\n-\t\t *\n-\t\tif (show_subprojects(base, baselen, pathname))\n-\t\t\tretval = READ_TREE_RECURSIVE;\n-\t\t *\n-\t\t */\n \t\ttype = commit_type;\n \t} else if (S_ISDIR(mode)) {\n \t\tif (show_recursive(base->buf, base->len, pathname)) {\n-- \n2.34.1.403.gb35f2687cf.dirty\n\n"},{"id":"447974","messageId":"cover.1644319434.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1641978175.git.dyroneteng@gmail.com","subject":"[PATCH v11 00/13] ls-tree: \"--object-only\" and \"--format\" opts","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-02-08T12:14:25Z","receivedAt":"2022-02-08T13:15:15Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"Main diffs from v10:\n\n1. Remove commit b04188c822 (ls-tree: optimize naming and handling of \"return\" in\n   show_tree()) and split to 2 new commits:\n   \t\t99e6d47108 (ls-tree: rename \"retval\" to \"recurse\" in \"show_tree()\")\n\t\t3816a65fe6 (simplify nesting if/else logic in \"show_tree()\")\n\n   And also use existed `object_type()` instead of a new `get_type()` which bring by v10.\n\n   These changes are based on Ævar Arnfjörð Bjarmason's suggestion on [1]. \n\n2. Better order of \"--format\" and \"--object-only\" features\n\n   The v11 set `--format` before than `--object-only`, this make the patch progression\n   much easier to read and let `--object-only` simplier to implement as a case of \"--format\".\n\n   The change is based on Ævar Arnfjörð Bjarmason's suggestion on [2].\n\n3. Replace to correct author names in partial commits.\n\n   The change is based on Ævar Arnfjörð Bjarmason's suggestion on [3].\n\n4. Changes on commit of \"--format\" feature.\n\n   * Documentation bugfix:\n\n     \t\t  1) Add back the \"\\n\" before \"DESCRIPTION\".\n\t\t  2) Use a simpler usage case instead of a fragile JSON example.\n\t\t  3) Other problems that be mentioned in [4].\n   \n   * Use a correspondingly uniform naming frame of the \"fieldnames\" instead of the old ones.\n\n     \t          1) mode   ->  objectmode\n\t\t  2) type   ->  objecttype\n\t\t  3) object ->  objectname\n\t\t  4) size   ->  objectsize\n\t\t  5) file   ->  path\n\n   These changes are based on Ævar Arnfjörð Bjarmason's suggestion on [4].\n\n5. Add more tests such as let \"--format\" combined with `-t`, `--full-tree` and `--full-name`. \n\n6. Add a new commit to introduce \"fast_path()\".\n\n[1] https://public-inbox.org/git/220114.86ee5attm4.gmgdl@evledraar.gmail.com/\n[2] https://public-inbox.org/git/CADMgQSSNQFHhf3=K+PiaoonBnheoDcoKpWy9-zjSu90d9rDY2w@mail.gmail.com/\n[3] https://public-inbox.org/git/CADMgQSQaE4EtiNXyGKebPyPS_0YTQ=HN+dU89_jD6BgQ1C470A@mail.gmail.com/\n[4] https://public-inbox.org/git/20220118125939.99956-1-dyroneteng@gmail.com/\n\n\nJohannes Schindelin (1):\n  cocci: allow padding with `strbuf_addf()`\n\nTeng Long (6):\n  ls-tree: rename \"retval\" to \"recurse\" in \"show_tree()\"\n  ls-tree: simplify nesting if/else logic in \"show_tree()\"\n  ls-tree: fix \"--name-only\" and \"--long\" combined use bug\n  ls-tree: slightly refactor `show_tree()`\n  ls-tree: introduce function \"fast_path()\"\n  ls-tree.c: support --object-only option for \"git-ls-tree\"\n\nÆvar Arnfjörð Bjarmason (6):\n  ls-tree: remove commented-out code\n  ls-tree: add missing braces to \"else\" arms\n  ls-tree: use \"enum object_type\", not {blob,tree,commit}_type\n  ls-tree: use \"size_t\", not \"int\" for \"struct strbuf\"'s \"len\"\n  ls-tree: introduce struct \"show_tree_data\"\n  ls-tree.c: introduce \"--format\" option\n\n Documentation/git-ls-tree.txt   |  64 ++++++-\n builtin/ls-tree.c               | 322 +++++++++++++++++++++++++-------\n contrib/coccinelle/strbuf.cocci |   6 +-\n t/t3104-ls-tree-format.sh       |  93 +++++++++\n t/t3105-ls-tree-oid.sh          |  51 +++++\n 5 files changed, 465 insertions(+), 71 deletions(-)\n create mode 100755 t/t3104-ls-tree-format.sh\n create mode 100755 t/t3105-ls-tree-oid.sh\n\nRange-diff against v10:\n 1:  b04188c822 <  -:  ---------- ls-tree: optimize naming and handling of \"return\" in show_tree()\n -:  ---------- >  1:  2fcff7e0d4 ls-tree: remove commented-out code\n -:  ---------- >  2:  6fd1dd9383 ls-tree: add missing braces to \"else\" arms\n -:  ---------- >  3:  208654b5e2 ls-tree: use \"enum object_type\", not {blob,tree,commit}_type\n -:  ---------- >  4:  2637464fd8 ls-tree: use \"size_t\", not \"int\" for \"struct strbuf\"'s \"len\"\n -:  ---------- >  5:  99e6d47108 ls-tree: rename \"retval\" to \"recurse\" in \"show_tree()\"\n -:  ---------- >  6:  3816a65fe6 ls-tree: simplify nesting if/else logic in \"show_tree()\"\n -:  ---------- >  7:  b22c2dc49e ls-tree: fix \"--name-only\" and \"--long\" combined use bug\n 2:  bcfbc935b8 !  8:  41e8ed5047 ls-tree.c: support --object-only option for \"git-ls-tree\"\n    @@ Metadata\n     Author: Teng Long <dyroneteng@gmail.com>\n     \n      ## Commit message ##\n    -    ls-tree.c: support --object-only option for \"git-ls-tree\"\n    +    ls-treeMain diffs from v10:\n\n1. Remove commit b04188c822 (ls-tree: optimize naming and handling of \"return\" in\n   show_tree()) and split to 2 new commits:\n                99e6d47108 (ls-tree: rename \"retval\" to \"recurse\" in \"show_tree()\")\n                3816a65fe6 (simplify nesting if/else logic in \"show_tree()\")\n\n   And also use existed `object_type()` instead of a new `get_type()` which bring by v10.\n\n   These changes are based on Ævar Arnfjörð Bjarmason's suggestion on [1].\n\n2. Better order of \"--format\" and \"--object-only\" features\n\n   The v11 set `--format` before than `--object-only`, this make the patch progression\n   much easier to read and let `--object-only` simplier to implement as a case of \"--format\".\n\n   The change is based on Ævar Arnfjörð Bjarmason's suggestion on [2].\n\n3. Fix the author'signs of som commits.\n\n   The change is based on Ævar Arnfjörð Bjarmason's suggestion on [3].\n\n4. Changes on commit of \"--format\" feature\n\n   * Documentation bugfix:\n\n                  1) Add back the \"\\n\" before \"DESCRIPTION\".\n                  2) Use a simplier usage case instead of a fragile JSON example.\n                  3) Other problems that be mentioned in [4].\n\n   * Use a correspondingly uniform naming frame of the \"fieldnames\" instead of the old ones.\n\n                  1) mode   ->  objectmode\n                  2) type   ->  objecttype\n                  3) object ->  objectname\n                  4) size   ->  objectsize\n                  5) file   ->  path\n\n   These changes are based on Ævar Arnfjörð Bjarmason's suggestion on [4].\n\n5. Add more tests such like let \"--format\" combined with `-t`, `--full-tree` and `--full-name`.\n\n6. Add a new commit to introduce \"fast_path()\".\n\n[1] https://public-inbox.org/git/220114.86ee5attm4.gmgdl@evledraar.gmail.com/\n[2] https://public-inbox.org/git/CADMgQSSNQFHhf3=K+PiaoonBnheoDcoKpWy9-zjSu90d9rDY2w@mail.gmail.com/\n[3] https://public-inbox.org/git/CADMgQSQaE4EtiNXyGKebPyPS_0YTQ=HN+dU89_jD6BgQ1C470A@mail.gmail.com/\n[4] https://public-inbox.org/git/20220118125939.99956-1-dyroneteng@gmail.com/: slightly refactor `show_tree()`\n     \n    -    We usually pipe the output from `git ls-trees` to tools like\n    -    `sed` or `cut` when we only want to extract some fields.\n    +    This is a non-functional change, we use a new int \"shown_fields\" to mark\n    +    which columns to output, and `parse_shown_fields()` to calculate the\n    +    value of \"shown_fields\".\n     \n    -    When we want only the pathname component, we can pass\n    -    `--name-only` option to omit such a pipeline, but there are no\n    -    options for extracting other fields.\n    -\n    -    Teach the \"--object-only\" option to the command to only show the\n    -    object name. This option cannot be used together with\n    -    \"--name-only\" or \"--long\" , they are mutually exclusive (actually\n    -    \"--name-only\" and \"--long\" can be combined together before, this\n    -    commit by the way fix this bug).\n    -\n    -    A simple refactoring was done to the \"show_tree\" function, intead by\n    -    using bitwise operations to recognize the format for printing to\n    -    stdout. The reason for doing this is that we don't want to increase\n    -    the readability difficulty with the addition of \"-object-only\",\n    -    making this part of the logic easier to read and expand.\n    -\n    -    In terms of performance, there is no loss comparing to the\n    -    \"master\" (2ae0a9cb8298185a94e5998086f380a355dd8907), here are the\n    -    results of the performance tests in my environment based on linux\n    -    repository:\n    -\n    -        $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r HEAD\"\n    -        Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD\n    -        Time (mean ± σ):     105.8 ms ±   2.7 ms    [User: 85.7 ms, System: 20.0 ms]\n    -        Range (min … max):   101.5 ms … 111.3 ms    28 runs\n    -\n    -        $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD\"\n    -        Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD\n    -        Time (mean ± σ):     105.0 ms ±   3.0 ms    [User: 83.7 ms, System: 21.2 ms]\n    -        Range (min … max):    99.3 ms … 109.5 ms    27 runs\n    -\n    -        $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r -l HEAD\"\n    -        Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD\n    -        Time (mean ± σ):     337.4 ms ±  10.9 ms    [User: 308.3 ms, System: 29.0 ms]\n    -        Range (min … max):   323.0 ms … 355.0 ms    10 runs\n    -\n    -        $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD\"\n    -        Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD\n    -        Time (mean ± σ):     337.6 ms ±   6.2 ms    [User: 309.4 ms, System: 28.1 ms]\n    -        Range (min … max):   330.4 ms … 349.9 ms    10 runs\n    +    This has the advantage of making the show_tree logic simpler and more\n    +    readable, as well as making it easier to extend new options (for example,\n    +    if we want to add a \"--object-only\" option, we just need to add a similar\n    +    \"if (shown_fields == FIELD_OBJECT_NAME)\" short-circuit logic in\n    +    \"show_tree()\").\n     \n         Signed-off-by: Teng Long <dyroneteng@gmail.com>\n     \n    - ## Documentation/git-ls-tree.txt ##\n    -@@ Documentation/git-ls-tree.txt: SYNOPSIS\n    - --------\n    - [verse]\n    - 'git ls-tree' [-d] [-r] [-t] [-l] [-z]\n    --\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]]\n    -+\t    [--name-only] [--name-status] [--object-only] [--full-name] [--full-tree] [--abbrev[=<n>]]\n    - \t    <tree-ish> [<path>...]\n    - \n    - DESCRIPTION\n    -@@ Documentation/git-ls-tree.txt: OPTIONS\n    - --name-only::\n    - --name-status::\n    - \tList only filenames (instead of the \"long\" output), one per line.\n    -+\tCannot be combined with `--object-only`.\n    -+\n    -+--object-only::\n    -+\tList only names of the objects, one per line. Cannot be combined\n    -+\twith `--name-only` or `--name-status`.\n    - \n    - --abbrev[=<n>]::\n    - \tInstead of showing the full 40-byte hexadecimal object\n    -\n      ## builtin/ls-tree.c ##\n     @@\n      \n    @@ builtin/ls-tree.c\n     +#define LS_SHOW_TREES (1 << 2)\n     +#define LS_NAME_ONLY (1 << 3)\n     +#define LS_SHOW_SIZE (1 << 4)\n    -+#define LS_OBJECT_ONLY (1 << 5)\n      static int abbrev;\n      static int ls_options;\n      static struct pathspec pathspec;\n      static int chomp_prefix;\n      static const char *ls_tree_prefix;\n     +static unsigned int shown_fields;\n    -+#define FIELD_FILE_NAME 1\n    ++#define FIELD_PATH_NAME 1\n     +#define FIELD_SIZE (1 << 1)\n     +#define FIELD_OBJECT_NAME (1 << 2)\n     +#define FIELD_TYPE (1 << 3)\n    @@ builtin/ls-tree.c\n      \tNULL\n      };\n      \n    --static int show_recursive(const char *base, size_t baselen, const char *pathname)\n     +enum {\n     +\tMODE_UNSPECIFIED = 0,\n     +\tMODE_NAME_ONLY,\n    -+\tMODE_OBJECT_ONLY,\n     +\tMODE_LONG,\n     +};\n     +\n    @@ builtin/ls-tree.c\n     +static int parse_shown_fields(void)\n     +{\n     +\tif (cmdmode == MODE_NAME_ONLY) {\n    -+\t\tshown_fields = FIELD_FILE_NAME;\n    -+\t\treturn 0;\n    -+\t}\n    -+\tif (cmdmode == MODE_OBJECT_ONLY) {\n    -+\t\tshown_fields = FIELD_OBJECT_NAME;\n    ++\t\tshown_fields = FIELD_PATH_NAME;\n     +\t\treturn 0;\n     +\t}\n    ++\n     +\tif (!ls_options || (ls_options & LS_RECURSIVE)\n     +\t    || (ls_options & LS_SHOW_TREES)\n     +\t    || (ls_options & LS_TREE_ONLY))\n    @@ builtin/ls-tree.c\n     +\treturn 1;\n     +}\n     +\n    -+static int show_recursive(const char *base, size_t baselen,\n    -+\t\t\t  const char *pathname)\n    + static int show_recursive(const char *base, size_t baselen, const char *pathname)\n      {\n      \tint i;\n    - \n    -@@ builtin/ls-tree.c: static enum object_type get_type(unsigned int mode)\n    - \t        : OBJ_BLOB);\n    +@@ builtin/ls-tree.c: static int show_recursive(const char *base, size_t baselen, const char *pathname\n    + \treturn 0;\n      }\n      \n     +static int show_default(const struct object_id *oid, enum object_type type,\n    @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strb\n     -\t\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n     -\t\t\t       find_unique_abbrev(oid, abbrev));\n     -\t\t}\n    -+\tif (shown_fields == FIELD_OBJECT_NAME) {\n    -+\t\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n    ++\tif (shown_fields == FIELD_PATH_NAME) {\n    ++\t\tbaselen = base->len;\n    ++\t\tstrbuf_addstr(base, pathname);\n    ++\t\twrite_name_quoted_relative(base->buf,\n    ++\t\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n    ++\t\t\t\t\t   stdout, line_termination);\n    ++\t\tstrbuf_setlen(base, baselen);\n     +\t\treturn recurse;\n      \t}\n     -\tbaselen = base->len;\n    @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strb\n     -\t\t\t\t   stdout, line_termination);\n     -\tstrbuf_setlen(base, baselen);\n     +\n    -+\tif (shown_fields == FIELD_FILE_NAME) {\n    -+\t\tbaselen = base->len;\n    -+\t\tstrbuf_addstr(base, pathname);\n    -+\t\twrite_name_quoted_relative(base->buf,\n    -+\t\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n    -+\t\t\t\t\t   stdout, line_termination);\n    -+\t\tstrbuf_setlen(base, baselen);\n    -+\t\treturn recurse;\n    -+\t}\n    -+\n     +\tif (shown_fields >= FIELD_DEFAULT)\n     +\t\tshow_default(oid, type, pathname, mode, base);\n     +\n    @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref\n      \t\t\tLS_SHOW_TREES),\n      \t\tOPT_SET_INT('z', NULL, &line_termination,\n      \t\t\t    N_(\"terminate entries with NUL byte\"), 0),\n    --\t\tOPT_BIT('l', \"long\", &ls_options, N_(\"include object size\"),\n    --\t\t\tLS_SHOW_SIZE),\n    --\t\tOPT_BIT(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n    --\t\t\tLS_NAME_ONLY),\n    --\t\tOPT_BIT(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n    --\t\t\tLS_NAME_ONLY),\n    +-\t\tOPT_CMDMODE('l', \"long\", &ls_options, N_(\"include object size\"),\n    +-\t\t\t    LS_SHOW_SIZE),\n    +-\t\tOPT_CMDMODE(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n    +-\t\t\t    LS_NAME_ONLY),\n    +-\t\tOPT_CMDMODE(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n    +-\t\t\t    LS_NAME_ONLY),\n     +\t\tOPT_CMDMODE('l', \"long\", &cmdmode, N_(\"include object size\"),\n     +\t\t\t    MODE_LONG),\n     +\t\tOPT_CMDMODE(0, \"name-only\", &cmdmode, N_(\"list only filenames\"),\n     +\t\t\t    MODE_NAME_ONLY),\n     +\t\tOPT_CMDMODE(0, \"name-status\", &cmdmode, N_(\"list only filenames\"),\n     +\t\t\t    MODE_NAME_ONLY),\n    -+\t\tOPT_CMDMODE(0, \"object-only\", &cmdmode, N_(\"list only objects\"),\n    -+\t\t\t    MODE_OBJECT_ONLY),\n      \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n      \t\t\t    N_(\"use full path names\"), 0),\n      \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n    @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref\n      \t/*\n      \t * show_recursive() rolls its own matching code and is\n      \t * generally ignorant of 'struct pathspec'. The magic mask\n    -\n    - ## t/t3104-ls-tree-oid.sh (new) ##\n    -@@\n    -+#!/bin/sh\n    -+\n    -+test_description='git ls-tree objects handling.'\n    -+\n    -+. ./test-lib.sh\n    -+\n    -+test_expect_success 'setup' '\n    -+\ttest_commit A &&\n    -+\ttest_commit B &&\n    -+\tmkdir -p C &&\n    -+\ttest_commit C/D.txt &&\n    -+\tfind *.txt path* \\( -type f -o -type l \\) -print |\n    -+\txargs git update-index --add &&\n    -+\ttree=$(git write-tree) &&\n    -+\techo $tree\n    -+'\n    -+\n    -+test_expect_success 'usage: --object-only' '\n    -+\tgit ls-tree --object-only $tree >current &&\n    -+\tgit ls-tree $tree >result &&\n    -+\tcut -f1 result | cut -d \" \" -f3 >expected &&\n    -+\ttest_cmp current expected\n    -+'\n    -+\n    -+test_expect_success 'usage: --object-only with -r' '\n    -+\tgit ls-tree --object-only -r $tree >current &&\n    -+\tgit ls-tree -r $tree >result &&\n    -+\tcut -f1 result | cut -d \" \" -f3 >expected &&\n    -+\ttest_cmp current expected\n    -+'\n    -+\n    -+test_expect_success 'usage: --object-only with --abbrev' '\n    -+\tgit ls-tree --object-only --abbrev=6 $tree >current &&\n    -+\tgit ls-tree --abbrev=6 $tree >result &&\n    -+\tcut -f1 result | cut -d \" \" -f3 >expected &&\n    -+\ttest_cmp current expected\n    -+'\n    -+\n    -+test_expect_success 'usage: incompatible options: --name-only with --object-only' '\n    -+\ttest_expect_code 129 git ls-tree --object-only --name-only $tree\n    -+'\n    -+\n    -+test_expect_success 'usage: incompatible options: --name-status with --object-only' '\n    -+\ttest_expect_code 129 git ls-tree --object-only --name-status $tree\n    -+'\n    -+\n    -+test_expect_success 'usage: incompatible options: --long with --object-only' '\n    -+\ttest_expect_code 129 git ls-tree --object-only --long $tree\n    -+'\n    -+\n    -+test_done\n 3:  3ddffa1027 !  9:  46e10a5392 ls-tree.c: introduce struct \"show_tree_data\"\n    @@\n      ## Metadata ##\n    -Author: Teng Long <dyroneteng@gmail.com>\n    +Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    ls-tree.c: introduce struct \"show_tree_data\"\n    +    ls-tree: introduce struct \"show_tree_data\"\n     \n         \"show_tree_data\" is a struct that packages the necessary fields for\n         \"show_tree()\". This commit is a pre-prepared commit for supporting\n         \"--format\" option and it does not affect any existing functionality.\n     \n    +    Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n         Signed-off-by: Teng Long <dyroneteng@gmail.com>\n     \n      ## builtin/ls-tree.c ##\n    @@ builtin/ls-tree.c: static unsigned int shown_fields;\n      static const  char * const ls_tree_usage[] = {\n      \tN_(\"git ls-tree [<options>] <tree-ish> [<path>...]\"),\n      \tNULL\n    -@@ builtin/ls-tree.c: static enum object_type get_type(unsigned int mode)\n    - \t        : OBJ_BLOB);\n    +@@ builtin/ls-tree.c: static int show_recursive(const char *base, size_t baselen, const char *pathname\n    + \treturn 0;\n      }\n      \n     -static int show_default(const struct object_id *oid, enum object_type type,\n    @@ builtin/ls-tree.c: static int show_default(const struct object_id *oid, enum obj\n      }\n      \n     @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strbuf *base,\n    + \tint recurse = 0;\n      \tsize_t baselen;\n    - \tenum object_type type = get_type(mode);\n    - \n    + \tenum object_type type = object_type(mode);\n     +\tstruct show_tree_data data = {\n     +\t\t.mode = mode,\n     +\t\t.type = type,\n    @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strb\n     +\t\t.pathname = pathname,\n     +\t\t.base = base,\n     +\t};\n    -+\n    + \n      \tif (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))\n      \t\trecurse = READ_TREE_RECURSIVE;\n    - \tif (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES))\n     @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strbuf *base,\n      \t}\n      \n 4:  4b58a707c2 ! 10:  c04320b801 cocci: allow padding with `strbuf_addf()`\n    @@\n      ## Metadata ##\n    -Author: Teng Long <dyroneteng@gmail.com>\n    +Author: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n     \n      ## Commit message ##\n         cocci: allow padding with `strbuf_addf()`\n 5:  db058bf670 ! 11:  5936004f13 ls-tree.c: introduce \"--format\" option\n    @@\n      ## Metadata ##\n    -Author: Teng Long <dyroneteng@gmail.com>\n    +Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n         ls-tree.c: introduce \"--format\" option\n    @@ Commit message\n         Links:\n                 [1] https://public-inbox.org/git/RFC-patch-6.7-eac299f06ff-20211217T131635Z-avarab@gmail.com/\n     \n    +    Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n         Signed-off-by: Teng Long <dyroneteng@gmail.com>\n     \n      ## Documentation/git-ls-tree.txt ##\n    @@ Documentation/git-ls-tree.txt: SYNOPSIS\n      --------\n      [verse]\n      'git ls-tree' [-d] [-r] [-t] [-l] [-z]\n    --\t    [--name-only] [--name-status] [--object-only] [--full-name] [--full-tree] [--abbrev[=<n>]]\n    --\t    <tree-ish> [<path>...]\n    --\n    -+\t    [--name-only] [--name-status] [--object-only]\n    -+\t    [--full-name] [--full-tree] [--abbrev[=<n>]]\n    -+\t    [--format=<format>] <tree-ish> [<path>...]\n    +-\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]]\n    ++\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n    + \t    <tree-ish> [<path>...]\n    + \n      DESCRIPTION\n    - -----------\n    - Lists the contents of a given tree object, like what \"/bin/ls -a\" does\n     @@ Documentation/git-ls-tree.txt: OPTIONS\n      \tDo not limit the listing to the current working directory.\n      \tImplies --full-name.\n    @@ Documentation/git-ls-tree.txt: OPTIONS\n      \n      Output Format\n      -------------\n    +-        <mode> SP <type> SP <object> TAB <file>\n    ++\n    ++The output format of `ls-tree` is determined by either the `--format`\n    ++option, or other format-altering options such as `--name-only` etc.\n    ++(see `--format` above).\n     +\n    -+Default format:\n    ++The use of certain `--format` directives is equivalent to using those\n    ++options, but invoking the full formatting machinery can be slower than\n    ++using an appropriate formatting option.\n     +\n    -         <mode> SP <type> SP <object> TAB <file>\n    ++In cases where the `--format` would exactly map to an existing option\n    ++`ls-tree` will use the appropriate faster path. Thus the default format\n    ++is equivalent to:\n    ++\n    ++        %(objectmode) %(objecttype) %(objectname)%x09%(path)\n      \n      This output format is compatible with what `--index-info --stdin` of\n    + 'git update-index' expects.\n    + \n    + When the `-l` option is used, format changes to\n    + \n    +-        <mode> SP <type> SP <object> SP <object size> TAB <file>\n    ++        %(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\n    + \n    +-Object size identified by <object> is given in bytes, and right-justified\n    ++Object size identified by <objectname> is given in bytes, and right-justified\n    + with minimum width of 7 characters.  Object size is given only for blobs\n    + (file) entries; for other entries `-` character is used in place of size.\n    + \n     @@ Documentation/git-ls-tree.txt: quoted as explained for the configuration variable `core.quotePath`\n      (see linkgit:git-config[1]).  Using `-z` the filename is output\n      verbatim and the line is terminated by a NUL byte.\n    @@ Documentation/git-ls-tree.txt: quoted as explained for the configuration variabl\n     +\n     +It is possible to print in a custom format by using the `--format` option,\n     +which is able to interpolate different fields using a `%(fieldname)` notation.\n    -+For example, if you want to only print the <object> and <file> fields with a\n    -+JSON style, executing with a specific \"--format\" like\n    -+\n    -+        git ls-tree --format='{\"object\":\"%(object)\", \"file\":\"%(file)\"}' <tree-ish>\n    ++For example, if you only care about the \"objectname\" and \"path\" fields, you\n    ++can execute with a specific \"--format\" like\n     +\n    -+The output format changes to:\n    -+\n    -+        {\"object\":\"<object>\", \"file\":\"<file>\"}\n    ++        git ls-tree --format='%(objectname) %(path)' <tree-ish>\n     +\n     +FIELD NAMES\n     +-----------\n    @@ Documentation/git-ls-tree.txt: quoted as explained for the configuration variabl\n     +into the resulting output. For each outputing line, the following\n     +names can be used:\n     +\n    -+mode::\n    ++objectmode::\n     +\tThe mode of the object.\n    -+type::\n    ++objecttype::\n     +\tThe type of the object (`blob` or `tree`).\n    -+object::\n    ++objectname::\n     +\tThe name of the object.\n    -+size[:padded]::\n    ++objectsize[:padded]::\n     +\tThe size of the object (\"-\" if it's a tree).\n     +\tIt also supports a padded format of size with \"%(size:padded)\".\n    -+file::\n    -+\tThe filename of the object.\n    ++path::\n    ++\tThe pathname of the object.\n     +\n      GIT\n      ---\n      Part of the linkgit:git[1] suite\n     \n      ## builtin/ls-tree.c ##\n    +@@ builtin/ls-tree.c: static unsigned int shown_fields;\n    + #define FIELD_MODE (1 << 4)\n    + #define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n    + #define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n    +-\n    ++static const char *format;\n    ++static const char *default_format = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\";\n    ++static const char *long_format = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\";\n    ++static const char *name_only_format = \"%(path)\";\n    + struct show_tree_data {\n    + \tunsigned mode;\n    + \tenum object_type type;\n     @@ builtin/ls-tree.c: enum {\n      \n      static int cmdmode = MODE_UNSPECIFIED;\n      \n    -+static const char *format;\n    -+static const char *default_format = \"%(mode) %(type) %(object)%x09%(file)\";\n    -+static const char *long_format = \"%(mode) %(type) %(object) %(size:padded)%x09%(file)\";\n    -+static const char *name_only_format = \"%(file)\";\n    -+static const char *object_only_format = \"%(object)\";\n    -+\n    - static int parse_shown_fields(void)\n    - {\n    - \tif (cmdmode == MODE_NAME_ONLY) {\n    -@@ builtin/ls-tree.c: static int parse_shown_fields(void)\n    - \treturn 1;\n    - }\n    - \n     +static void expand_objectsize(struct strbuf *line, const struct object_id *oid,\n     +\t\t\t      const enum object_type type, unsigned int padded)\n     +{\n    @@ builtin/ls-tree.c: static int parse_shown_fields(void)\n     +\t\t\tdie(_(\"could not get object info about '%s'\"),\n     +\t\t\t    oid_to_hex(oid));\n     +\t\tif (padded)\n    -+\t\t\tstrbuf_addf(line, \"%7\" PRIuMAX, (uintmax_t)size);\n    ++\t\t\tstrbuf_addf(line, \"%7\"PRIuMAX, (uintmax_t)size);\n     +\t\telse\n    -+\t\t\tstrbuf_addf(line, \"%\" PRIuMAX, (uintmax_t)size);\n    ++\t\t\tstrbuf_addf(line, \"%\"PRIuMAX, (uintmax_t)size);\n     +\t} else if (padded) {\n     +\t\tstrbuf_addf(line, \"%7s\", \"-\");\n     +\t} else {\n    @@ builtin/ls-tree.c: static int parse_shown_fields(void)\n     +\t}\n     +}\n     +\n    -+static size_t expand_show_tree(struct strbuf *line, const char *start,\n    ++static size_t expand_show_tree(struct strbuf *sb, const char *start,\n     +\t\t\t       void *context)\n     +{\n     +\tstruct show_tree_data *data = context;\n     +\tconst char *end;\n     +\tconst char *p;\n     +\tunsigned int errlen;\n    -+\tsize_t len = strbuf_expand_literal_cb(line, start, NULL);\n    ++\tsize_t len = strbuf_expand_literal_cb(sb, start, NULL);\n     +\n     +\tif (len)\n     +\t\treturn len;\n    @@ builtin/ls-tree.c: static int parse_shown_fields(void)\n     +\t\tdie(_(\"bad ls-tree format: element '%s' does not end in ')'\"), start);\n     +\n     +\tlen = end - start + 1;\n    -+\tif (skip_prefix(start, \"(mode)\", &p)) {\n    -+\t\tstrbuf_addf(line, \"%06o\", data->mode);\n    -+\t} else if (skip_prefix(start, \"(type)\", &p)) {\n    -+\t\tstrbuf_addstr(line, type_name(data->type));\n    -+\t} else if (skip_prefix(start, \"(size:padded)\", &p)) {\n    -+\t\texpand_objectsize(line, data->oid, data->type, 1);\n    -+\t} else if (skip_prefix(start, \"(size)\", &p)) {\n    -+\t\texpand_objectsize(line, data->oid, data->type, 0);\n    -+\t} else if (skip_prefix(start, \"(object)\", &p)) {\n    -+\t\tstrbuf_add_unique_abbrev(line, data->oid, abbrev);\n    -+\t} else if (skip_prefix(start, \"(file)\", &p)) {\n    ++\tif (skip_prefix(start, \"(objectmode)\", &p)) {\n    ++\t\tstrbuf_addf(sb, \"%06o\", data->mode);\n    ++\t} else if (skip_prefix(start, \"(objecttype)\", &p)) {\n    ++\t\tstrbuf_addstr(sb, type_name(data->type));\n    ++\t} else if (skip_prefix(start, \"(objectsize:padded)\", &p)) {\n    ++\t\texpand_objectsize(sb, data->oid, data->type, 1);\n    ++\t} else if (skip_prefix(start, \"(objectsize)\", &p)) {\n    ++\t\texpand_objectsize(sb, data->oid, data->type, 0);\n    ++\t} else if (skip_prefix(start, \"(objectname)\", &p)) {\n    ++\t\tstrbuf_add_unique_abbrev(sb, data->oid, abbrev);\n    ++\t} else if (skip_prefix(start, \"(path)\", &p)) {\n     +\t\tconst char *name = data->base->buf;\n     +\t\tconst char *prefix = chomp_prefix ? ls_tree_prefix : NULL;\n     +\t\tstruct strbuf quoted = STRBUF_INIT;\n    -+\t\tstruct strbuf sb = STRBUF_INIT;\n    ++\t\tstruct strbuf sbuf = STRBUF_INIT;\n     +\t\tstrbuf_addstr(data->base, data->pathname);\n    -+\t\tname = relative_path(data->base->buf, prefix, &sb);\n    ++\t\tname = relative_path(data->base->buf, prefix, &sbuf);\n     +\t\tquote_c_style(name, &quoted, NULL, 0);\n    -+\t\tstrbuf_addbuf(line, &quoted);\n    -+\t\tstrbuf_release(&sb);\n    ++\t\tstrbuf_addbuf(sb, &quoted);\n    ++\t\tstrbuf_release(&sbuf);\n     +\t\tstrbuf_release(&quoted);\n     +\t} else {\n     +\t\terrlen = (unsigned long)len;\n    @@ builtin/ls-tree.c: static int parse_shown_fields(void)\n     +\treturn len;\n     +}\n     +\n    - static int show_recursive(const char *base, size_t baselen,\n    - \t\t\t  const char *pathname)\n    + static int parse_shown_fields(void)\n      {\n    -@@ builtin/ls-tree.c: static enum object_type get_type(unsigned int mode)\n    - \t        : OBJ_BLOB);\n    + \tif (cmdmode == MODE_NAME_ONLY) {\n    +@@ builtin/ls-tree.c: static int show_recursive(const char *base, size_t baselen, const char *pathname\n    + \treturn 0;\n      }\n      \n     +static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,\n    @@ builtin/ls-tree.c: static enum object_type get_type(unsigned int mode)\n     +{\n     +\tsize_t baselen;\n     +\tint recurse = 0;\n    -+\tstruct strbuf line = STRBUF_INIT;\n    -+\tenum object_type type = get_type(mode);\n    ++\tstruct strbuf sb = STRBUF_INIT;\n    ++\tenum object_type type = object_type(mode);\n     +\n     +\tstruct show_tree_data data = {\n     +\t\t.mode = mode,\n    @@ builtin/ls-tree.c: static enum object_type get_type(unsigned int mode)\n     +\t\treturn 0;\n     +\n     +\tbaselen = base->len;\n    -+\tstrbuf_expand(&line, format, expand_show_tree, &data);\n    -+\tstrbuf_addch(&line, line_termination);\n    -+\tfwrite(line.buf, line.len, 1, stdout);\n    -+\tstrbuf_release(&line);\n    ++\tstrbuf_expand(&sb, format, expand_show_tree, &data);\n    ++\tstrbuf_addch(&sb, line_termination);\n    ++\tfwrite(sb.buf, sb.len, 1, stdout);\n    ++\tstrbuf_release(&sb);\n     +\tstrbuf_setlen(base, baselen);\n     +\treturn recurse;\n     +}\n    @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref\n      \t\t\t N_(\"list entire tree; not just current directory \"\n      \t\t\t    \"(implies --full-name)\")),\n     +\t\tOPT_STRING_F(0, \"format\", &format, N_(\"format\"),\n    -+\t\t\t     N_(\"format to use for the output\"),\n    -+\t\t\t     PARSE_OPT_NONEG),\n    ++\t\t\t\t\t N_(\"format to use for the output\"),\n    ++\t\t\t\t\t PARSE_OPT_NONEG),\n      \t\tOPT__ABBREV(&abbrev),\n      \t\tOPT_END()\n      \t};\n    @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref\n      \t\tdie(\"not a tree object\");\n     -\treturn !!read_tree(the_repository, tree,\n     -\t\t\t   &pathspec, show_tree, NULL);\n    -+\n     +\t/*\n     +\t * The generic show_tree_fmt() is slower than show_tree(), so\n     +\t * take the fast path if possible.\n     +\t */\n    -+\tif (format &&\n    -+\t    (!strcmp(format, default_format) ||\n    -+\t     !strcmp(format, long_format) ||\n    -+\t     !strcmp(format, name_only_format) ||\n    -+\t     !strcmp(format, object_only_format)))\n    ++\tif (format && (!strcmp(format, default_format))) {\n    ++\t\tfn = show_tree;\n    ++\t} else if (format && (!strcmp(format, long_format))) {\n    ++\t\tshown_fields = shown_fields | FIELD_SIZE;\n     +\t\tfn = show_tree;\n    -+\telse if (format)\n    ++\t} else if (format && (!strcmp(format, name_only_format))) {\n    ++\t\tshown_fields = FIELD_PATH_NAME;\n    ++\t\tfn = show_tree;\n    ++\t} else if (format)\n     +\t\tfn = show_tree_fmt;\n     +\n     +\treturn !!read_tree(the_repository, tree, &pathspec, fn, NULL);\n      }\n     \n    - ## t/t3105-ls-tree-format.sh (new) ##\n    + ## t/t3104-ls-tree-format.sh (new) ##\n     @@\n     +#!/bin/sh\n     +\n    @@ t/t3105-ls-tree-format.sh (new)\n     +test_expect_success 'ls-tree --format usage' '\n     +\ttest_expect_code 129 git ls-tree --format=fmt -l HEAD &&\n     +\ttest_expect_code 129 git ls-tree --format=fmt --name-only HEAD &&\n    -+\ttest_expect_code 129 git ls-tree --format=fmt --name-status HEAD &&\n    -+\ttest_expect_code 129 git ls-tree --format=fmt --object-only HEAD\n    ++\ttest_expect_code 129 git ls-tree --format=fmt --name-status HEAD\n     +'\n     +\n     +test_expect_success 'setup' '\n    @@ t/t3105-ls-tree-format.sh (new)\n     +test_ls_tree_format () {\n     +\tformat=$1 &&\n     +\topts=$2 &&\n    ++\tfmtopts=$3 &&\n     +\tshift 2 &&\n     +\tgit ls-tree $opts -r HEAD >expect.raw &&\n     +\tsed \"s/^/> /\" >expect <expect.raw &&\n    -+\tgit ls-tree --format=\"> $format\" -r HEAD >actual &&\n    ++\tgit ls-tree --format=\"> $format\" -r $fmtopts HEAD >actual &&\n     +\ttest_cmp expect actual\n     +}\n     +\n     +test_expect_success 'ls-tree --format=<default-like>' '\n     +\ttest_ls_tree_format \\\n    -+\t\t\"%(mode) %(type) %(object)%x09%(file)\" \\\n    ++\t\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n     +\t\t\"\"\n     +'\n     +\n     +test_expect_success 'ls-tree --format=<long-like>' '\n     +\ttest_ls_tree_format \\\n    -+\t\t\"%(mode) %(type) %(object) %(size:padded)%x09%(file)\" \\\n    ++\t\t\"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\" \\\n     +\t\t\"--long\"\n     +'\n     +\n     +test_expect_success 'ls-tree --format=<name-only-like>' '\n     +\ttest_ls_tree_format \\\n    -+\t\t\"%(file)\" \\\n    ++\t\t\"%(path)\" \\\n     +\t\t\"--name-only\"\n     +'\n     +\n    -+test_expect_success 'ls-tree --format=<object-only-like>' '\n    ++test_expect_success 'ls-tree combine --format=<default-like> and -t' '\n    ++\ttest_ls_tree_format \\\n    ++\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n    ++\t\"-t\" \\\n    ++\t\"-t\"\n    ++'\n    ++\n    ++test_expect_success 'ls-tree combine --format=<default-like> and --full-name' '\n     +\ttest_ls_tree_format \\\n    -+\t\t\"%(object)\" \\\n    -+\t\t\"--object-only\"\n    ++\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n    ++\t\"--full-name\" \\\n    ++\t\"--full-name\"\n     +'\n     +\n    ++test_expect_success 'ls-tree combine --format=<default-like> and --full-tree' '\n    ++\ttest_ls_tree_format \\\n    ++\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n    ++\t\"--full-tree\" \\\n    ++\t\"--full-tree\"\n    ++'\n    ++\n    ++test_expect_success 'ls-tree hit fast-path with --format=<default-like>' '\n    ++\tgit ls-tree -r HEAD >expect &&\n    ++\tgit ls-tree --format=\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" -r HEAD >actual &&\n    ++\ttest_cmp expect actual\n    ++'\n    ++\n    ++test_expect_success 'ls-tree hit fast-path with --format=<name-only-like>' '\n    ++\tgit ls-tree -r --name-only HEAD >expect &&\n    ++\tgit ls-tree --format=\"%(path)\" -r HEAD >actual &&\n    ++\ttest_cmp expect actual\n    ++'\n     +test_done\n -:  ---------- > 12:  6d26497749 ls-tree: introduce function \"fast_path()\"\n -:  ---------- > 13:  e6d98f2560 ls-tree.c: support --object-only option for \"git-ls-tree\"\n-- \n2.34.1.403.gb35f2687cf.dirty\n\n"},{"id":"447975","messageId":"208654b5e2aebbe988b5a8f478869253a0166e9b.1644319434.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1644319434.git.dyroneteng@gmail.com","subject":"[PATCH v11 03/13] ls-tree: use \"enum object_type\", not {blob,tree,commit}_type","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-02-08T12:14:28Z","receivedAt":"2022-02-08T13:15:15Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nChange the ls-tree.c code to use type_name() on the enum instead of\nusing the string constants. This doesn't matter either way for\nperformance, but makes this a bit easier to read as we'll no longer\nneed a strcmp() here.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 0a28f32ccb..3f0225b097 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -66,17 +66,17 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n {\n \tint retval = 0;\n \tint baselen;\n-\tconst char *type = blob_type;\n+\tenum object_type type = OBJ_BLOB;\n \n \tif (S_ISGITLINK(mode)) {\n-\t\ttype = commit_type;\n+\t\ttype = OBJ_COMMIT;\n \t} else if (S_ISDIR(mode)) {\n \t\tif (show_recursive(base->buf, base->len, pathname)) {\n \t\t\tretval = READ_TREE_RECURSIVE;\n \t\t\tif (!(ls_options & LS_SHOW_TREES))\n \t\t\t\treturn retval;\n \t\t}\n-\t\ttype = tree_type;\n+\t\ttype = OBJ_TREE;\n \t}\n \telse if (ls_options & LS_TREE_ONLY)\n \t\treturn 0;\n@@ -84,7 +84,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tif (!(ls_options & LS_NAME_ONLY)) {\n \t\tif (ls_options & LS_SHOW_SIZE) {\n \t\t\tchar size_text[24];\n-\t\t\tif (!strcmp(type, blob_type)) {\n+\t\t\tif (type == OBJ_BLOB) {\n \t\t\t\tunsigned long size;\n \t\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n \t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n@@ -95,11 +95,11 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\t} else {\n \t\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n \t\t\t}\n-\t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type,\n+\t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n \t\t\t       find_unique_abbrev(oid, abbrev),\n \t\t\t       size_text);\n \t\t} else {\n-\t\t\tprintf(\"%06o %s %s\\t\", mode, type,\n+\t\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n \t\t\t       find_unique_abbrev(oid, abbrev));\n \t\t}\n \t}\n-- \n2.34.1.403.gb35f2687cf.dirty\n\n"},{"id":"447977","messageId":"6d264977491b3645292bba5f91479ab1623e8b80.1644319434.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1644319434.git.dyroneteng@gmail.com","subject":"[PATCH v11 12/13] ls-tree: introduce function \"fast_path()\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-02-08T12:14:37Z","receivedAt":"2022-02-08T13:15:15Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"The generic \"show_tree_fmt()\" is slower than \"show_tree()\", so\nwe want to take the fast path if possible.\n\nwhen \"--format=<format>\" is passed, \"fast_path()\" will determine\nwhether to use \"show_tree()\" or insist on using \"show_tree_fmt()\"\nby a try of finding out if the built-int format is hit.\n\nThis commit take out the related codes from \"cmd_ls_tree()\" and\npackage them into a new funtion \"fast_path()\".\n\nExplain it a little bit further, whether fast_path is hit or not,\nthe final correctness should not break. Abstracting a separate method\nhelps improve the readability of \"cmd_ls_tree()\" and the cohesiveness\nand extensibility of fast path logic.\n\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 23 ++++++++++++++---------\n 1 file changed, 14 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 1c71e5d543..ba96bcf602 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -58,6 +58,19 @@ enum {\n \n static int cmdmode = MODE_UNSPECIFIED;\n \n+static int fast_path(void){\n+\tif (!strcmp(format, default_format)) {\n+\t\treturn 1;\n+\t} else if (!strcmp(format, long_format)) {\n+\t\tshown_fields = shown_fields | FIELD_SIZE;\n+\t\treturn 1;\n+\t} else if (!strcmp(format, name_only_format)) {\n+\t\tshown_fields = FIELD_PATH_NAME;\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n static void expand_objectsize(struct strbuf *line, const struct object_id *oid,\n \t\t\t      const enum object_type type, unsigned int padded)\n {\n@@ -350,15 +363,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t * The generic show_tree_fmt() is slower than show_tree(), so\n \t * take the fast path if possible.\n \t */\n-\tif (format && (!strcmp(format, default_format))) {\n-\t\tfn = show_tree;\n-\t} else if (format && (!strcmp(format, long_format))) {\n-\t\tshown_fields = shown_fields | FIELD_SIZE;\n-\t\tfn = show_tree;\n-\t} else if (format && (!strcmp(format, name_only_format))) {\n-\t\tshown_fields = FIELD_PATH_NAME;\n-\t\tfn = show_tree;\n-\t} else if (format)\n+\tif (format && !fast_path())\n \t\tfn = show_tree_fmt;\n \n \treturn !!read_tree(the_repository, tree, &pathspec, fn, NULL);\n-- \n2.34.1.403.gb35f2687cf.dirty\n\n"},{"id":"447978","messageId":"e6d98f2560281e46e4ef2121692a54f796919b59.1644319434.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1644319434.git.dyroneteng@gmail.com","subject":"[PATCH v11 13/13] ls-tree.c: support --object-only option for \"git-ls-tree\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-02-08T12:14:38Z","receivedAt":"2022-02-08T13:15:15Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"We usually pipe the output from `git ls-trees` to tools like\n`sed` or `cut` when we only want to extract some fields.\n\nWhen we want only the pathname component, we can pass\n`--name-only` option to omit such a pipeline, but there are no\noptions for extracting other fields.\n\nTeach the \"--object-only\" option to the command to only show the\nobject name. This option cannot be used together with\n\"--name-only\" or \"--long\" , they are mutually exclusive (actually\n\"--name-only\" and \"--long\" can be combined together before, this\ncommit by the way fix this bug).\n\nIn terms of performance, there is no loss comparing to the\n\"master\" (2ae0a9c), here are the\nresults of the performance tests in my environment based on linux\nrepository:\n\n    $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r HEAD\"\n    Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD\n    Time (mean ± σ):     105.8 ms ±   2.7 ms    [User: 85.7 ms, System: 20.0 ms]\n    Range (min … max):   101.5 ms … 111.3 ms    28 runs\n\n    $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD\"\n    Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD\n    Time (mean ± σ):     105.0 ms ±   3.0 ms    [User: 83.7 ms, System: 21.2 ms]\n    Range (min … max):    99.3 ms … 109.5 ms    27 runs\n\n    $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r -l HEAD\"\n    Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD\n    Time (mean ± σ):     337.4 ms ±  10.9 ms    [User: 308.3 ms, System: 29.0 ms]\n    Range (min … max):   323.0 ms … 355.0 ms    10 runs\n\n    $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD\"\n    Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD\n    Time (mean ± σ):     337.6 ms ±   6.2 ms    [User: 309.4 ms, System: 28.1 ms]\n    Range (min … max):   330.4 ms … 349.9 ms    10 runs\n\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n Documentation/git-ls-tree.txt |  7 ++++-\n builtin/ls-tree.c             | 16 ++++++++++-\n t/t3104-ls-tree-format.sh     | 12 +++++++++\n t/t3105-ls-tree-oid.sh        | 51 +++++++++++++++++++++++++++++++++++\n 4 files changed, 84 insertions(+), 2 deletions(-)\n create mode 100755 t/t3105-ls-tree-oid.sh\n\ndiff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt\nindex db29a9efb5..21045dd163 100644\n--- a/Documentation/git-ls-tree.txt\n+++ b/Documentation/git-ls-tree.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git ls-tree' [-d] [-r] [-t] [-l] [-z]\n-\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n+\t    [--name-only] [--name-status] [--object-only] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n \t    <tree-ish> [<path>...]\n \n DESCRIPTION\n@@ -59,6 +59,11 @@ OPTIONS\n --name-only::\n --name-status::\n \tList only filenames (instead of the \"long\" output), one per line.\n+\tCannot be combined with `--object-only`.\n+\n+--object-only::\n+\tList only names of the objects, one per line. Cannot be combined\n+\twith `--name-only` or `--name-status`.\n \n --abbrev[=<n>]::\n \tInstead of showing the full 40-byte hexadecimal object\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex ba96bcf602..9819a24186 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -20,6 +20,7 @@ static int line_termination = '\\n';\n #define LS_SHOW_TREES (1 << 2)\n #define LS_NAME_ONLY (1 << 3)\n #define LS_SHOW_SIZE (1 << 4)\n+#define LS_OBJECT_ONLY (1 << 5)\n static int abbrev;\n static int ls_options;\n static struct pathspec pathspec;\n@@ -37,6 +38,7 @@ static const char *format;\n static const char *default_format = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\";\n static const char *long_format = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\";\n static const char *name_only_format = \"%(path)\";\n+static const char *object_only_format = \"%(objectname)\";\n struct show_tree_data {\n \tunsigned mode;\n \tenum object_type type;\n@@ -53,6 +55,7 @@ static const  char * const ls_tree_usage[] = {\n enum {\n \tMODE_UNSPECIFIED = 0,\n \tMODE_NAME_ONLY,\n+\tMODE_OBJECT_ONLY,\n \tMODE_LONG,\n };\n \n@@ -67,6 +70,8 @@ static int fast_path(void){\n \t} else if (!strcmp(format, name_only_format)) {\n \t\tshown_fields = FIELD_PATH_NAME;\n \t\treturn 1;\n+\t} else if (!strcmp(format, object_only_format)) {\n+\t\tshown_fields = FIELD_OBJECT_NAME;\n \t}\n \treturn 0;\n }\n@@ -143,7 +148,10 @@ static int parse_shown_fields(void)\n \t\tshown_fields = FIELD_PATH_NAME;\n \t\treturn 0;\n \t}\n-\n+\tif (cmdmode == MODE_OBJECT_ONLY) {\n+\t\tshown_fields = FIELD_OBJECT_NAME;\n+\t\treturn 0;\n+\t}\n \tif (!ls_options || (ls_options & LS_RECURSIVE)\n \t    || (ls_options & LS_SHOW_TREES)\n \t    || (ls_options & LS_TREE_ONLY))\n@@ -267,6 +275,10 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tif (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))\n \t\treturn 0;\n \n+\tif (shown_fields == FIELD_OBJECT_NAME) {\n+\t\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n+\t\treturn recurse;\n+\t}\n \tif (shown_fields == FIELD_PATH_NAME) {\n \t\tbaselen = base->len;\n \t\tstrbuf_addstr(base, pathname);\n@@ -304,6 +316,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\t\t    MODE_NAME_ONLY),\n \t\tOPT_CMDMODE(0, \"name-status\", &cmdmode, N_(\"list only filenames\"),\n \t\t\t    MODE_NAME_ONLY),\n+\t\tOPT_CMDMODE(0, \"object-only\", &cmdmode, N_(\"list only objects\"),\n+\t\t\t    MODE_OBJECT_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n \t\t\t    N_(\"use full path names\"), 0),\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\ndiff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh\nindex e08c83dc47..c0ffc8e1c3 100755\n--- a/t/t3104-ls-tree-format.sh\n+++ b/t/t3104-ls-tree-format.sh\n@@ -46,6 +46,12 @@ test_expect_success 'ls-tree --format=<name-only-like>' '\n \t\t\"--name-only\"\n '\n \n+test_expect_success 'ls-tree --format=<object-only-like>' '\n+\ttest_ls_tree_format \\\n+\t\t\"%(objectname)\" \\\n+\t\t\"--object-only\"\n+'\n+\n test_expect_success 'ls-tree combine --format=<default-like> and -t' '\n \ttest_ls_tree_format \\\n \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n@@ -78,4 +84,10 @@ test_expect_success 'ls-tree hit fast-path with --format=<name-only-like>' '\n \tgit ls-tree --format=\"%(path)\" -r HEAD >actual &&\n \ttest_cmp expect actual\n '\n+\n+test_expect_success 'ls-tree hit fast-path with --format=<object-only-like>' '\n+\tgit ls-tree -r --object-only HEAD >expect &&\n+\tgit ls-tree --format=\"%(objectname)\" -r HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n test_done\ndiff --git a/t/t3105-ls-tree-oid.sh b/t/t3105-ls-tree-oid.sh\nnew file mode 100755\nindex 0000000000..992bb26bfa\n--- /dev/null\n+++ b/t/t3105-ls-tree-oid.sh\n@@ -0,0 +1,51 @@\n+#!/bin/sh\n+\n+test_description='git ls-tree objects handling.'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+test_commit A &&\n+test_commit B &&\n+mkdir -p C &&\n+test_commit C/D.txt &&\n+find *.txt path* \\( -type f -o -type l \\) -print |\n+xargs git update-index --add &&\n+tree=$(git write-tree) &&\n+echo $tree\n+'\n+\n+test_expect_success 'usage: --object-only' '\n+git ls-tree --object-only $tree >current &&\n+git ls-tree $tree >result &&\n+cut -f1 result | cut -d \" \" -f3 >expected &&\n+test_cmp current expected\n+'\n+\n+test_expect_success 'usage: --object-only with -r' '\n+git ls-tree --object-only -r $tree >current &&\n+git ls-tree -r $tree >result &&\n+cut -f1 result | cut -d \" \" -f3 >expected &&\n+test_cmp current expected\n+'\n+\n+test_expect_success 'usage: --object-only with --abbrev' '\n+git ls-tree --object-only --abbrev=6 $tree >current &&\n+git ls-tree --abbrev=6 $tree >result &&\n+cut -f1 result | cut -d \" \" -f3 >expected &&\n+test_cmp current expected\n+'\n+\n+test_expect_success 'usage: incompatible options: --name-only with --object-only' '\n+test_expect_code 129 git ls-tree --object-only --name-only $tree\n+'\n+\n+test_expect_success 'usage: incompatible options: --name-status with --object-only' '\n+test_expect_code 129 git ls-tree --object-only --name-status $tree\n+'\n+\n+test_expect_success 'usage: incompatible options: --long with --object-only' '\n+test_expect_code 129 git ls-tree --object-only --long $tree\n+'\n+\n+test_done\n-- \n2.34.1.403.gb35f2687cf.dirty\n\n"},{"id":"447976","messageId":"2637464fd8f6b8bae6c681f20c10c9fc76dc6d6c.1644319434.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1644319434.git.dyroneteng@gmail.com","subject":"[PATCH v11 04/13] ls-tree: use \"size_t\", not \"int\" for \"struct strbuf\"'s \"len\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-02-08T12:14:29Z","receivedAt":"2022-02-08T13:15:16Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nThe \"struct strbuf\"'s \"len\" member is a \"size_t\", not an \"int\", so\nlet's change our corresponding types accordingly. This also changes\nthe \"len\" and \"speclen\" variables, which are likewise used to store\nthe return value of strlen(), which returns \"size_t\", not \"int\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3f0225b097..eecc7482d5 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -31,7 +31,7 @@ static const  char * const ls_tree_usage[] = {\n \tNULL\n };\n \n-static int show_recursive(const char *base, int baselen, const char *pathname)\n+static int show_recursive(const char *base, size_t baselen, const char *pathname)\n {\n \tint i;\n \n@@ -43,7 +43,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n \n \tfor (i = 0; i < pathspec.nr; i++) {\n \t\tconst char *spec = pathspec.items[i].match;\n-\t\tint len, speclen;\n+\t\tsize_t len, speclen;\n \n \t\tif (strncmp(base, spec, baselen))\n \t\t\tcontinue;\n@@ -65,7 +65,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\tconst char *pathname, unsigned mode, void *context)\n {\n \tint retval = 0;\n-\tint baselen;\n+\tsize_t baselen;\n \tenum object_type type = OBJ_BLOB;\n \n \tif (S_ISGITLINK(mode)) {\n-- \n2.34.1.403.gb35f2687cf.dirty\n\n"},{"id":"447979","messageId":"6fd1dd938303192d5b0a4cdc2914b2ff72cf5a3d.1644319434.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1644319434.git.dyroneteng@gmail.com","subject":"[PATCH v11 02/13] ls-tree: add missing braces to \"else\" arms","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-02-08T12:14:27Z","receivedAt":"2022-02-08T13:15:16Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nAdd missing {} to the \"else\" arms in show_tree() per the\nCodingGuidelines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 6 ++++--\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 5f7c84950c..0a28f32ccb 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -92,14 +92,16 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\t\telse\n \t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n \t\t\t\t\t\t  \"%\"PRIuMAX, (uintmax_t)size);\n-\t\t\t} else\n+\t\t\t} else {\n \t\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n+\t\t\t}\n \t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type,\n \t\t\t       find_unique_abbrev(oid, abbrev),\n \t\t\t       size_text);\n-\t\t} else\n+\t\t} else {\n \t\t\tprintf(\"%06o %s %s\\t\", mode, type,\n \t\t\t       find_unique_abbrev(oid, abbrev));\n+\t\t}\n \t}\n \tbaselen = base->len;\n \tstrbuf_addstr(base, pathname);\n-- \n2.34.1.403.gb35f2687cf.dirty\n\n"},{"id":"447980","messageId":"3816a65fe62b1039c396eb38d27bf2c22e8d84de.1644319434.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1644319434.git.dyroneteng@gmail.com","subject":"[PATCH v11 06/13] ls-tree: simplify nesting if/else logic in \"show_tree()\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-02-08T12:14:31Z","receivedAt":"2022-02-08T13:15:16Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"This commit use \"object_type()\" to get the type, then remove\nsome of the nested if to let the codes here become more cleaner.\n\nSigned-off-by: Teng Long <dyronetengb@gmail.com>\n---\n builtin/ls-tree.c | 18 ++++++------------\n 1 file changed, 6 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex ef8c414f61..9c57a36c8c 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -66,19 +66,13 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n {\n \tint recurse = 0;\n \tsize_t baselen;\n-\tenum object_type type = OBJ_BLOB;\n+\tenum object_type type = object_type(mode);\n \n-\tif (S_ISGITLINK(mode)) {\n-\t\ttype = OBJ_COMMIT;\n-\t} else if (S_ISDIR(mode)) {\n-\t\tif (show_recursive(base->buf, base->len, pathname)) {\n-\t\t\trecurse = READ_TREE_RECURSIVE;\n-\t\t\tif (!(ls_options & LS_SHOW_TREES))\n-\t\t\t\treturn recurse;\n-\t\t}\n-\t\ttype = OBJ_TREE;\n-\t}\n-\telse if (ls_options & LS_TREE_ONLY)\n+\tif (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))\n+\t\trecurse = READ_TREE_RECURSIVE;\n+\tif (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES))\n+\t\treturn recurse;\n+\tif (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))\n \t\treturn 0;\n \n \tif (!(ls_options & LS_NAME_ONLY)) {\n-- \n2.34.1.403.gb35f2687cf.dirty\n\n"},{"id":"447981","messageId":"99e6d47108308b0bbaf406b439170de04e375c56.1644319434.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1644319434.git.dyroneteng@gmail.com","subject":"[PATCH v11 05/13] ls-tree: rename \"retval\" to \"recurse\" in \"show_tree()\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-02-08T12:14:30Z","receivedAt":"2022-02-08T13:15:16Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"The variable which \"show_tree()\" return is named \"retval\", a name that's\na little hard to understand. The commit rename \"retval\" to \"recurse\"\nwhich is a more meaningful name than before in the context. We do not\nneed to take a look at \"read_tree_at()\" in \"tree.c\" to make sure what\ndoes \"retval\" mean.\n\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex eecc7482d5..ef8c414f61 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -64,7 +64,7 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\tconst char *pathname, unsigned mode, void *context)\n {\n-\tint retval = 0;\n+\tint recurse = 0;\n \tsize_t baselen;\n \tenum object_type type = OBJ_BLOB;\n \n@@ -72,9 +72,9 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\ttype = OBJ_COMMIT;\n \t} else if (S_ISDIR(mode)) {\n \t\tif (show_recursive(base->buf, base->len, pathname)) {\n-\t\t\tretval = READ_TREE_RECURSIVE;\n+\t\t\trecurse = READ_TREE_RECURSIVE;\n \t\t\tif (!(ls_options & LS_SHOW_TREES))\n-\t\t\t\treturn retval;\n+\t\t\t\treturn recurse;\n \t\t}\n \t\ttype = OBJ_TREE;\n \t}\n@@ -109,7 +109,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n \t\t\t\t   stdout, line_termination);\n \tstrbuf_setlen(base, baselen);\n-\treturn retval;\n+\treturn recurse;\n }\n \n int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n-- \n2.34.1.403.gb35f2687cf.dirty\n\n"},{"id":"447982","messageId":"b22c2dc49ea3aa0ed7d0df367eddc84e931e9e21.1644319434.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1644319434.git.dyroneteng@gmail.com","subject":"[PATCH v11 07/13] ls-tree: fix \"--name-only\" and \"--long\" combined use bug","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-02-08T12:14:32Z","receivedAt":"2022-02-08T13:15:16Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"If we execute \"git ls-tree\" with combined \"--name-only\" and \"--long\"\n, only the pathname will be printed, the size is omitted (the original\ndiscoverer was Peff in [1]).\n\nThis commit fix this issue by using `OPT_CMDMODE()` instead to make both\nof them mutually exclusive.\n\n[1] https://public-inbox.org/git/YZK0MKCYAJmG+pSU@coredump.intra.peff.net/\n\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 9c57a36c8c..32147e75e6 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -120,12 +120,12 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\t\tLS_SHOW_TREES),\n \t\tOPT_SET_INT('z', NULL, &line_termination,\n \t\t\t    N_(\"terminate entries with NUL byte\"), 0),\n-\t\tOPT_BIT('l', \"long\", &ls_options, N_(\"include object size\"),\n-\t\t\tLS_SHOW_SIZE),\n-\t\tOPT_BIT(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n-\t\t\tLS_NAME_ONLY),\n-\t\tOPT_BIT(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n-\t\t\tLS_NAME_ONLY),\n+\t\tOPT_CMDMODE('l', \"long\", &ls_options, N_(\"include object size\"),\n+\t\t\t    LS_SHOW_SIZE),\n+\t\tOPT_CMDMODE(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n+\t\t\t    LS_NAME_ONLY),\n+\t\tOPT_CMDMODE(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n+\t\t\t    LS_NAME_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n \t\t\t    N_(\"use full path names\"), 0),\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n-- \n2.34.1.403.gb35f2687cf.dirty\n\n"},{"id":"447983","messageId":"41e8ed50476a5afd6009db1a69c7a39b04e038b0.1644319434.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1644319434.git.dyroneteng@gmail.com","subject":"[PATCH v11 08/13] ls-tree: slightly refactor `show_tree()`","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-02-08T12:14:33Z","receivedAt":"2022-02-08T13:15:16Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"This is a non-functional change, we use a new int \"shown_fields\" to mark\nwhich columns to output, and `parse_shown_fields()` to calculate the\nvalue of \"shown_fields\".\n\nThis has the advantage of making the show_tree logic simpler and more\nreadable, as well as making it easier to extend new options (for example,\nif we want to add a \"--object-only\" option, we just need to add a similar\n\"if (shown_fields == FIELD_OBJECT_NAME)\" short-circuit logic in\n\"show_tree()\").\n\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 126 ++++++++++++++++++++++++++++++++--------------\n 1 file changed, 89 insertions(+), 37 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 32147e75e6..8baab7c83e 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -16,21 +16,53 @@\n \n static int line_termination = '\\n';\n #define LS_RECURSIVE 1\n-#define LS_TREE_ONLY 2\n-#define LS_SHOW_TREES 4\n-#define LS_NAME_ONLY 8\n-#define LS_SHOW_SIZE 16\n+#define LS_TREE_ONLY (1 << 1)\n+#define LS_SHOW_TREES (1 << 2)\n+#define LS_NAME_ONLY (1 << 3)\n+#define LS_SHOW_SIZE (1 << 4)\n static int abbrev;\n static int ls_options;\n static struct pathspec pathspec;\n static int chomp_prefix;\n static const char *ls_tree_prefix;\n+static unsigned int shown_fields;\n+#define FIELD_PATH_NAME 1\n+#define FIELD_SIZE (1 << 1)\n+#define FIELD_OBJECT_NAME (1 << 2)\n+#define FIELD_TYPE (1 << 3)\n+#define FIELD_MODE (1 << 4)\n+#define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n+#define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n \n static const  char * const ls_tree_usage[] = {\n \tN_(\"git ls-tree [<options>] <tree-ish> [<path>...]\"),\n \tNULL\n };\n \n+enum {\n+\tMODE_UNSPECIFIED = 0,\n+\tMODE_NAME_ONLY,\n+\tMODE_LONG,\n+};\n+\n+static int cmdmode = MODE_UNSPECIFIED;\n+\n+static int parse_shown_fields(void)\n+{\n+\tif (cmdmode == MODE_NAME_ONLY) {\n+\t\tshown_fields = FIELD_PATH_NAME;\n+\t\treturn 0;\n+\t}\n+\n+\tif (!ls_options || (ls_options & LS_RECURSIVE)\n+\t    || (ls_options & LS_SHOW_TREES)\n+\t    || (ls_options & LS_TREE_ONLY))\n+\t\tshown_fields = FIELD_DEFAULT;\n+\tif (cmdmode == MODE_LONG)\n+\t\tshown_fields = FIELD_LONG_DEFAULT;\n+\treturn 1;\n+}\n+\n static int show_recursive(const char *base, size_t baselen, const char *pathname)\n {\n \tint i;\n@@ -61,6 +93,39 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n \treturn 0;\n }\n \n+static int show_default(const struct object_id *oid, enum object_type type,\n+\t\t\tconst char *pathname, unsigned mode,\n+\t\t\tstruct strbuf *base)\n+{\n+\tsize_t baselen = base->len;\n+\n+\tif (shown_fields & FIELD_SIZE) {\n+\t\tchar size_text[24];\n+\t\tif (type == OBJ_BLOB) {\n+\t\t\tunsigned long size;\n+\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n+\t\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n+\t\t\telse\n+\t\t\t\txsnprintf(size_text, sizeof(size_text),\n+\t\t\t\t\t  \"%\" PRIuMAX, (uintmax_t)size);\n+\t\t} else {\n+\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n+\t\t}\n+\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n+\t\tfind_unique_abbrev(oid, abbrev), size_text);\n+\t} else {\n+\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n+\t\tfind_unique_abbrev(oid, abbrev));\n+\t}\n+\tbaselen = base->len;\n+\tstrbuf_addstr(base, pathname);\n+\twrite_name_quoted_relative(base->buf,\n+\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n+\t\t\t\t   line_termination);\n+\tstrbuf_setlen(base, baselen);\n+\treturn 1;\n+}\n+\n static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\tconst char *pathname, unsigned mode, void *context)\n {\n@@ -75,34 +140,19 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tif (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))\n \t\treturn 0;\n \n-\tif (!(ls_options & LS_NAME_ONLY)) {\n-\t\tif (ls_options & LS_SHOW_SIZE) {\n-\t\t\tchar size_text[24];\n-\t\t\tif (type == OBJ_BLOB) {\n-\t\t\t\tunsigned long size;\n-\t\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n-\t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n-\t\t\t\t\t\t  \"BAD\");\n-\t\t\t\telse\n-\t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n-\t\t\t\t\t\t  \"%\"PRIuMAX, (uintmax_t)size);\n-\t\t\t} else {\n-\t\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n-\t\t\t}\n-\t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n-\t\t\t       find_unique_abbrev(oid, abbrev),\n-\t\t\t       size_text);\n-\t\t} else {\n-\t\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n-\t\t\t       find_unique_abbrev(oid, abbrev));\n-\t\t}\n+\tif (shown_fields == FIELD_PATH_NAME) {\n+\t\tbaselen = base->len;\n+\t\tstrbuf_addstr(base, pathname);\n+\t\twrite_name_quoted_relative(base->buf,\n+\t\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n+\t\t\t\t\t   stdout, line_termination);\n+\t\tstrbuf_setlen(base, baselen);\n+\t\treturn recurse;\n \t}\n-\tbaselen = base->len;\n-\tstrbuf_addstr(base, pathname);\n-\twrite_name_quoted_relative(base->buf,\n-\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n-\t\t\t\t   stdout, line_termination);\n-\tstrbuf_setlen(base, baselen);\n+\n+\tif (shown_fields >= FIELD_DEFAULT)\n+\t\tshow_default(oid, type, pathname, mode, base);\n+\n \treturn recurse;\n }\n \n@@ -120,12 +170,12 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\t\tLS_SHOW_TREES),\n \t\tOPT_SET_INT('z', NULL, &line_termination,\n \t\t\t    N_(\"terminate entries with NUL byte\"), 0),\n-\t\tOPT_CMDMODE('l', \"long\", &ls_options, N_(\"include object size\"),\n-\t\t\t    LS_SHOW_SIZE),\n-\t\tOPT_CMDMODE(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n-\t\t\t    LS_NAME_ONLY),\n-\t\tOPT_CMDMODE(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n-\t\t\t    LS_NAME_ONLY),\n+\t\tOPT_CMDMODE('l', \"long\", &cmdmode, N_(\"include object size\"),\n+\t\t\t    MODE_LONG),\n+\t\tOPT_CMDMODE(0, \"name-only\", &cmdmode, N_(\"list only filenames\"),\n+\t\t\t    MODE_NAME_ONLY),\n+\t\tOPT_CMDMODE(0, \"name-status\", &cmdmode, N_(\"list only filenames\"),\n+\t\t\t    MODE_NAME_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n \t\t\t    N_(\"use full path names\"), 0),\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n@@ -156,6 +206,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tif (get_oid(argv[0], &oid))\n \t\tdie(\"Not a valid object name %s\", argv[0]);\n \n+\tparse_shown_fields();\n+\n \t/*\n \t * show_recursive() rolls its own matching code and is\n \t * generally ignorant of 'struct pathspec'. The magic mask\n-- \n2.34.1.403.gb35f2687cf.dirty\n\n"},{"id":"447984","messageId":"c04320b8018000f1f789b069a49236de798e9a92.1644319434.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1644319434.git.dyroneteng@gmail.com","subject":"[PATCH v11 10/13] cocci: allow padding with `strbuf_addf()`","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-02-08T12:14:35Z","receivedAt":"2022-02-08T13:15:17Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nA convenient way to pad strings is to use something like\n`strbuf_addf(&buf, \"%20s\", \"Hello, world!\")`.\n\nHowever, the Coccinelle rule that forbids a format `\"%s\"` with a\nconstant string argument cast too wide a net, and also forbade such\npadding.\n\nThe original rule was introduced by commit:\n\n    28c23cd4c39 (strbuf.cocci: suggest strbuf_addbuf() to add one strbuf to an other, 2019-01-25)\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n contrib/coccinelle/strbuf.cocci | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/coccinelle/strbuf.cocci b/contrib/coccinelle/strbuf.cocci\nindex d9ada69b43..0970d98ad7 100644\n--- a/contrib/coccinelle/strbuf.cocci\n+++ b/contrib/coccinelle/strbuf.cocci\n@@ -15,7 +15,7 @@ constant fmt !~ \"%\";\n @@\n expression E;\n struct strbuf SB;\n-format F =~ \"s\";\n+format F =~ \"^s$\";\n @@\n - strbuf_addf(E, \"%@F@\", SB.buf);\n + strbuf_addbuf(E, &SB);\n@@ -23,7 +23,7 @@ format F =~ \"s\";\n @@\n expression E;\n struct strbuf *SBP;\n-format F =~ \"s\";\n+format F =~ \"^s$\";\n @@\n - strbuf_addf(E, \"%@F@\", SBP->buf);\n + strbuf_addbuf(E, SBP);\n@@ -44,7 +44,7 @@ struct strbuf *SBP;\n \n @@\n expression E1, E2;\n-format F =~ \"s\";\n+format F =~ \"^s$\";\n @@\n - strbuf_addf(E1, \"%@F@\", E2);\n + strbuf_addstr(E1, E2);\n-- \n2.34.1.403.gb35f2687cf.dirty\n\n"},{"id":"447985","messageId":"46e10a5392ace1b1caef1eb8f7dfc4b60779ff55.1644319434.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1644319434.git.dyroneteng@gmail.com","subject":"[PATCH v11 09/13] ls-tree: introduce struct \"show_tree_data\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-02-08T12:14:34Z","receivedAt":"2022-02-08T13:15:17Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\n\"show_tree_data\" is a struct that packages the necessary fields for\n\"show_tree()\". This commit is a pre-prepared commit for supporting\n\"--format\" option and it does not affect any existing functionality.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 43 ++++++++++++++++++++++++++++---------------\n 1 file changed, 28 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 8baab7c83e..293b8f9dfb 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -34,6 +34,14 @@ static unsigned int shown_fields;\n #define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n #define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n \n+struct show_tree_data {\n+\tunsigned mode;\n+\tenum object_type type;\n+\tconst struct object_id *oid;\n+\tconst char *pathname;\n+\tstruct strbuf *base;\n+};\n+\n static const  char * const ls_tree_usage[] = {\n \tN_(\"git ls-tree [<options>] <tree-ish> [<path>...]\"),\n \tNULL\n@@ -93,17 +101,15 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n \treturn 0;\n }\n \n-static int show_default(const struct object_id *oid, enum object_type type,\n-\t\t\tconst char *pathname, unsigned mode,\n-\t\t\tstruct strbuf *base)\n+static int show_default(struct show_tree_data *data)\n {\n-\tsize_t baselen = base->len;\n+\tsize_t baselen = data->base->len;\n \n \tif (shown_fields & FIELD_SIZE) {\n \t\tchar size_text[24];\n-\t\tif (type == OBJ_BLOB) {\n+\t\tif (data->type == OBJ_BLOB) {\n \t\t\tunsigned long size;\n-\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n+\t\t\tif (oid_object_info(the_repository, data->oid, &size) == OBJ_BAD)\n \t\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n \t\t\telse\n \t\t\t\txsnprintf(size_text, sizeof(size_text),\n@@ -111,18 +117,18 @@ static int show_default(const struct object_id *oid, enum object_type type,\n \t\t} else {\n \t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n \t\t}\n-\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n-\t\tfind_unique_abbrev(oid, abbrev), size_text);\n+\t\tprintf(\"%06o %s %s %7s\\t\", data->mode, type_name(data->type),\n+\t\tfind_unique_abbrev(data->oid, abbrev), size_text);\n \t} else {\n-\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n-\t\tfind_unique_abbrev(oid, abbrev));\n+\t\tprintf(\"%06o %s %s\\t\", data->mode, type_name(data->type),\n+\t\tfind_unique_abbrev(data->oid, abbrev));\n \t}\n-\tbaselen = base->len;\n-\tstrbuf_addstr(base, pathname);\n-\twrite_name_quoted_relative(base->buf,\n+\tbaselen = data->base->len;\n+\tstrbuf_addstr(data->base, data->pathname);\n+\twrite_name_quoted_relative(data->base->buf,\n \t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n \t\t\t\t   line_termination);\n-\tstrbuf_setlen(base, baselen);\n+\tstrbuf_setlen(data->base, baselen);\n \treturn 1;\n }\n \n@@ -132,6 +138,13 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tint recurse = 0;\n \tsize_t baselen;\n \tenum object_type type = object_type(mode);\n+\tstruct show_tree_data data = {\n+\t\t.mode = mode,\n+\t\t.type = type,\n+\t\t.oid = oid,\n+\t\t.pathname = pathname,\n+\t\t.base = base,\n+\t};\n \n \tif (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))\n \t\trecurse = READ_TREE_RECURSIVE;\n@@ -151,7 +164,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t}\n \n \tif (shown_fields >= FIELD_DEFAULT)\n-\t\tshow_default(oid, type, pathname, mode, base);\n+\t\tshow_default(&data);\n \n \treturn recurse;\n }\n-- \n2.34.1.403.gb35f2687cf.dirty\n\n"},{"id":"447986","messageId":"5936004f1386388740ded0208ebeb8c5d0c88ef2.1644319434.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1644319434.git.dyroneteng@gmail.com","subject":"[PATCH v11 11/13] ls-tree.c: introduce \"--format\" option","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-02-08T12:14:36Z","receivedAt":"2022-02-08T13:15:17Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nAdd a --format option to ls-tree. It has an existing default output,\nand then --long and --name-only options to emit the default output\nalong with the objectsize and, or to only emit object paths.\n\nRather than add --type-only, --object-only etc. we can just support a\n--format using a strbuf_expand() similar to \"for-each-ref\n--format\". We might still add such options in the future for\nconvenience.\n\nThe --format implementation is slower than the existing code, but this\nchange does not cause any performance regressions. We'll leave the\nexisting show_tree() unchanged, and only run show_tree_fmt() in if\na --format different than the hardcoded built-in ones corresponding to\nthe existing modes is provided.\n\nI.e. something like the \"--long\" output would be much slower with\nthis, mainly due to how we need to allocate various things to do with\nquote.c instead of spewing the output directly to stdout.\n\nThe new option of '--format' comes from Ævar Arnfjörð Bjarmasonn's\nidea and suggestion, this commit makes modifications in terms of the\noriginal discussion on community [1].\n\nHere is the statistics about performance tests:\n\n1. Default format (hitten the builtin formats):\n\n    \"git ls-tree <tree-ish>\" vs \"--format='%(mode) %(type) %(object)%x09%(file)'\"\n\n    $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r HEAD\"\n    Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD\n    Time (mean ± σ):     105.2 ms ±   3.3 ms    [User: 84.3 ms, System: 20.8 ms]\n    Range (min … max):    99.2 ms … 113.2 ms    28 runs\n\n    $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object)%x09%(file)'  HEAD\"\n    Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object)%x09%(file)'  HEAD\n    Time (mean ± σ):     106.4 ms ±   2.7 ms    [User: 86.1 ms, System: 20.2 ms]\n    Range (min … max):   100.2 ms … 110.5 ms    29 runs\n\n2. Default format includes object size (hitten the builtin formats):\n\n    \"git ls-tree -l <tree-ish>\" vs \"--format='%(mode) %(type) %(object) %(size:padded)%x09%(file)'\"\n\n    $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r -l HEAD\"\n    Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD\n    Time (mean ± σ):     335.1 ms ±   6.5 ms    [User: 304.6 ms, System: 30.4 ms]\n    Range (min … max):   327.5 ms … 348.4 ms    10 runs\n\n    $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object) %(size:padded)%x09%(file)'  HEAD\"\n    Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object) %(size:padded)%x09%(file)'  HEAD\n    Time (mean ± σ):     337.2 ms ±   8.2 ms    [User: 309.2 ms, System: 27.9 ms]\n    Range (min … max):   328.8 ms … 349.4 ms    10 runs\n\nLinks:\n\t[1] https://public-inbox.org/git/RFC-patch-6.7-eac299f06ff-20211217T131635Z-avarab@gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n Documentation/git-ls-tree.txt |  59 ++++++++++++++--\n builtin/ls-tree.c             | 129 +++++++++++++++++++++++++++++++++-\n t/t3104-ls-tree-format.sh     |  81 +++++++++++++++++++++\n 3 files changed, 262 insertions(+), 7 deletions(-)\n create mode 100755 t/t3104-ls-tree-format.sh\n\ndiff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt\nindex db02d6d79a..db29a9efb5 100644\n--- a/Documentation/git-ls-tree.txt\n+++ b/Documentation/git-ls-tree.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git ls-tree' [-d] [-r] [-t] [-l] [-z]\n-\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]]\n+\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n \t    <tree-ish> [<path>...]\n \n DESCRIPTION\n@@ -74,6 +74,16 @@ OPTIONS\n \tDo not limit the listing to the current working directory.\n \tImplies --full-name.\n \n+--format=<format>::\n+\tA string that interpolates `%(fieldname)` from the result\n+\tbeing shown. It also interpolates `%%` to `%`, and\n+\t`%xx` where `xx` are hex digits interpolates to character\n+\twith hex code `xx`; for example `%00` interpolates to\n+\t`\\0` (NUL), `%09` to `\\t` (TAB) and `%0a` to `\\n` (LF).\n+\tWhen specified, `--format` cannot be combined with other\n+\tformat-altering options, including `--long`, `--name-only`\n+\tand `--object-only`.\n+\n [<path>...]::\n \tWhen paths are given, show them (note that this isn't really raw\n \tpathnames, but rather a list of patterns to match).  Otherwise\n@@ -82,16 +92,29 @@ OPTIONS\n \n Output Format\n -------------\n-        <mode> SP <type> SP <object> TAB <file>\n+\n+The output format of `ls-tree` is determined by either the `--format`\n+option, or other format-altering options such as `--name-only` etc.\n+(see `--format` above).\n+\n+The use of certain `--format` directives is equivalent to using those\n+options, but invoking the full formatting machinery can be slower than\n+using an appropriate formatting option.\n+\n+In cases where the `--format` would exactly map to an existing option\n+`ls-tree` will use the appropriate faster path. Thus the default format\n+is equivalent to:\n+\n+        %(objectmode) %(objecttype) %(objectname)%x09%(path)\n \n This output format is compatible with what `--index-info --stdin` of\n 'git update-index' expects.\n \n When the `-l` option is used, format changes to\n \n-        <mode> SP <type> SP <object> SP <object size> TAB <file>\n+        %(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\n \n-Object size identified by <object> is given in bytes, and right-justified\n+Object size identified by <objectname> is given in bytes, and right-justified\n with minimum width of 7 characters.  Object size is given only for blobs\n (file) entries; for other entries `-` character is used in place of size.\n \n@@ -100,6 +123,34 @@ quoted as explained for the configuration variable `core.quotePath`\n (see linkgit:git-config[1]).  Using `-z` the filename is output\n verbatim and the line is terminated by a NUL byte.\n \n+Customized format:\n+\n+It is possible to print in a custom format by using the `--format` option,\n+which is able to interpolate different fields using a `%(fieldname)` notation.\n+For example, if you only care about the \"objectname\" and \"path\" fields, you\n+can execute with a specific \"--format\" like\n+\n+        git ls-tree --format='%(objectname) %(path)' <tree-ish>\n+\n+FIELD NAMES\n+-----------\n+\n+Various values from structured fields can be used to interpolate\n+into the resulting output. For each outputing line, the following\n+names can be used:\n+\n+objectmode::\n+\tThe mode of the object.\n+objecttype::\n+\tThe type of the object (`blob` or `tree`).\n+objectname::\n+\tThe name of the object.\n+objectsize[:padded]::\n+\tThe size of the object (\"-\" if it's a tree).\n+\tIt also supports a padded format of size with \"%(size:padded)\".\n+path::\n+\tThe pathname of the object.\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 293b8f9dfb..1c71e5d543 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -33,7 +33,10 @@ static unsigned int shown_fields;\n #define FIELD_MODE (1 << 4)\n #define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n #define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n-\n+static const char *format;\n+static const char *default_format = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\";\n+static const char *long_format = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\";\n+static const char *name_only_format = \"%(path)\";\n struct show_tree_data {\n \tunsigned mode;\n \tenum object_type type;\n@@ -55,6 +58,72 @@ enum {\n \n static int cmdmode = MODE_UNSPECIFIED;\n \n+static void expand_objectsize(struct strbuf *line, const struct object_id *oid,\n+\t\t\t      const enum object_type type, unsigned int padded)\n+{\n+\tif (type == OBJ_BLOB) {\n+\t\tunsigned long size;\n+\t\tif (oid_object_info(the_repository, oid, &size) < 0)\n+\t\t\tdie(_(\"could not get object info about '%s'\"),\n+\t\t\t    oid_to_hex(oid));\n+\t\tif (padded)\n+\t\t\tstrbuf_addf(line, \"%7\"PRIuMAX, (uintmax_t)size);\n+\t\telse\n+\t\t\tstrbuf_addf(line, \"%\"PRIuMAX, (uintmax_t)size);\n+\t} else if (padded) {\n+\t\tstrbuf_addf(line, \"%7s\", \"-\");\n+\t} else {\n+\t\tstrbuf_addstr(line, \"-\");\n+\t}\n+}\n+\n+static size_t expand_show_tree(struct strbuf *sb, const char *start,\n+\t\t\t       void *context)\n+{\n+\tstruct show_tree_data *data = context;\n+\tconst char *end;\n+\tconst char *p;\n+\tunsigned int errlen;\n+\tsize_t len = strbuf_expand_literal_cb(sb, start, NULL);\n+\n+\tif (len)\n+\t\treturn len;\n+\tif (*start != '(')\n+\t\tdie(_(\"bad ls-tree format: as '%s'\"), start);\n+\n+\tend = strchr(start + 1, ')');\n+\tif (!end)\n+\t\tdie(_(\"bad ls-tree format: element '%s' does not end in ')'\"), start);\n+\n+\tlen = end - start + 1;\n+\tif (skip_prefix(start, \"(objectmode)\", &p)) {\n+\t\tstrbuf_addf(sb, \"%06o\", data->mode);\n+\t} else if (skip_prefix(start, \"(objecttype)\", &p)) {\n+\t\tstrbuf_addstr(sb, type_name(data->type));\n+\t} else if (skip_prefix(start, \"(objectsize:padded)\", &p)) {\n+\t\texpand_objectsize(sb, data->oid, data->type, 1);\n+\t} else if (skip_prefix(start, \"(objectsize)\", &p)) {\n+\t\texpand_objectsize(sb, data->oid, data->type, 0);\n+\t} else if (skip_prefix(start, \"(objectname)\", &p)) {\n+\t\tstrbuf_add_unique_abbrev(sb, data->oid, abbrev);\n+\t} else if (skip_prefix(start, \"(path)\", &p)) {\n+\t\tconst char *name = data->base->buf;\n+\t\tconst char *prefix = chomp_prefix ? ls_tree_prefix : NULL;\n+\t\tstruct strbuf quoted = STRBUF_INIT;\n+\t\tstruct strbuf sbuf = STRBUF_INIT;\n+\t\tstrbuf_addstr(data->base, data->pathname);\n+\t\tname = relative_path(data->base->buf, prefix, &sbuf);\n+\t\tquote_c_style(name, &quoted, NULL, 0);\n+\t\tstrbuf_addbuf(sb, &quoted);\n+\t\tstrbuf_release(&sbuf);\n+\t\tstrbuf_release(&quoted);\n+\t} else {\n+\t\terrlen = (unsigned long)len;\n+\t\tdie(_(\"bad ls-tree format: %%%.*s\"), errlen, start);\n+\t}\n+\treturn len;\n+}\n+\n static int parse_shown_fields(void)\n {\n \tif (cmdmode == MODE_NAME_ONLY) {\n@@ -101,6 +170,38 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n \treturn 0;\n }\n \n+static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,\n+\t\t\t const char *pathname, unsigned mode, void *context)\n+{\n+\tsize_t baselen;\n+\tint recurse = 0;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tenum object_type type = object_type(mode);\n+\n+\tstruct show_tree_data data = {\n+\t\t.mode = mode,\n+\t\t.type = type,\n+\t\t.oid = oid,\n+\t\t.pathname = pathname,\n+\t\t.base = base,\n+\t};\n+\n+\tif (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))\n+\t\trecurse = READ_TREE_RECURSIVE;\n+\tif (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES))\n+\t\treturn recurse;\n+\tif (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))\n+\t\treturn 0;\n+\n+\tbaselen = base->len;\n+\tstrbuf_expand(&sb, format, expand_show_tree, &data);\n+\tstrbuf_addch(&sb, line_termination);\n+\tfwrite(sb.buf, sb.len, 1, stdout);\n+\tstrbuf_release(&sb);\n+\tstrbuf_setlen(base, baselen);\n+\treturn recurse;\n+}\n+\n static int show_default(struct show_tree_data *data)\n {\n \tsize_t baselen = data->base->len;\n@@ -174,6 +275,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tstruct object_id oid;\n \tstruct tree *tree;\n \tint i, full_tree = 0;\n+\tread_tree_fn_t fn = show_tree;\n \tconst struct option ls_tree_options[] = {\n \t\tOPT_BIT('d', NULL, &ls_options, N_(\"only show trees\"),\n \t\t\tLS_TREE_ONLY),\n@@ -194,6 +296,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n \t\t\t N_(\"list entire tree; not just current directory \"\n \t\t\t    \"(implies --full-name)\")),\n+\t\tOPT_STRING_F(0, \"format\", &format, N_(\"format\"),\n+\t\t\t\t\t N_(\"format to use for the output\"),\n+\t\t\t\t\t PARSE_OPT_NONEG),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_END()\n \t};\n@@ -214,6 +319,10 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t    ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))\n \t\tls_options |= LS_SHOW_TREES;\n \n+\tif (format && cmdmode)\n+\t\tusage_msg_opt(\n+\t\t\t_(\"--format can't be combined with other format-altering options\"),\n+\t\t\tls_tree_usage, ls_tree_options);\n \tif (argc < 1)\n \t\tusage_with_options(ls_tree_usage, ls_tree_options);\n \tif (get_oid(argv[0], &oid))\n@@ -237,6 +346,20 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \ttree = parse_tree_indirect(&oid);\n \tif (!tree)\n \t\tdie(\"not a tree object\");\n-\treturn !!read_tree(the_repository, tree,\n-\t\t\t   &pathspec, show_tree, NULL);\n+\t/*\n+\t * The generic show_tree_fmt() is slower than show_tree(), so\n+\t * take the fast path if possible.\n+\t */\n+\tif (format && (!strcmp(format, default_format))) {\n+\t\tfn = show_tree;\n+\t} else if (format && (!strcmp(format, long_format))) {\n+\t\tshown_fields = shown_fields | FIELD_SIZE;\n+\t\tfn = show_tree;\n+\t} else if (format && (!strcmp(format, name_only_format))) {\n+\t\tshown_fields = FIELD_PATH_NAME;\n+\t\tfn = show_tree;\n+\t} else if (format)\n+\t\tfn = show_tree_fmt;\n+\n+\treturn !!read_tree(the_repository, tree, &pathspec, fn, NULL);\n }\ndiff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh\nnew file mode 100755\nindex 0000000000..e08c83dc47\n--- /dev/null\n+++ b/t/t3104-ls-tree-format.sh\n@@ -0,0 +1,81 @@\n+#!/bin/sh\n+\n+test_description='ls-tree --format'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n+. ./test-lib.sh\n+\n+test_expect_success 'ls-tree --format usage' '\n+\ttest_expect_code 129 git ls-tree --format=fmt -l HEAD &&\n+\ttest_expect_code 129 git ls-tree --format=fmt --name-only HEAD &&\n+\ttest_expect_code 129 git ls-tree --format=fmt --name-status HEAD\n+'\n+\n+test_expect_success 'setup' '\n+\tmkdir dir &&\n+\ttest_commit dir/sub-file &&\n+\ttest_commit top-file\n+'\n+\n+test_ls_tree_format () {\n+\tformat=$1 &&\n+\topts=$2 &&\n+\tfmtopts=$3 &&\n+\tshift 2 &&\n+\tgit ls-tree $opts -r HEAD >expect.raw &&\n+\tsed \"s/^/> /\" >expect <expect.raw &&\n+\tgit ls-tree --format=\"> $format\" -r $fmtopts HEAD >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'ls-tree --format=<default-like>' '\n+\ttest_ls_tree_format \\\n+\t\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\t\"\"\n+'\n+\n+test_expect_success 'ls-tree --format=<long-like>' '\n+\ttest_ls_tree_format \\\n+\t\t\"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\" \\\n+\t\t\"--long\"\n+'\n+\n+test_expect_success 'ls-tree --format=<name-only-like>' '\n+\ttest_ls_tree_format \\\n+\t\t\"%(path)\" \\\n+\t\t\"--name-only\"\n+'\n+\n+test_expect_success 'ls-tree combine --format=<default-like> and -t' '\n+\ttest_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"-t\" \\\n+\t\"-t\"\n+'\n+\n+test_expect_success 'ls-tree combine --format=<default-like> and --full-name' '\n+\ttest_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"--full-name\" \\\n+\t\"--full-name\"\n+'\n+\n+test_expect_success 'ls-tree combine --format=<default-like> and --full-tree' '\n+\ttest_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"--full-tree\" \\\n+\t\"--full-tree\"\n+'\n+\n+test_expect_success 'ls-tree hit fast-path with --format=<default-like>' '\n+\tgit ls-tree -r HEAD >expect &&\n+\tgit ls-tree --format=\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" -r HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'ls-tree hit fast-path with --format=<name-only-like>' '\n+\tgit ls-tree -r --name-only HEAD >expect &&\n+\tgit ls-tree --format=\"%(path)\" -r HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+test_done\n-- \n2.34.1.403.gb35f2687cf.dirty\n\n"},{"id":"448836","messageId":"220219.86zgmne82n.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"e6d98f2560281e46e4ef2121692a54f796919b59.1644319434.git.dyroneteng@gmail.com","subject":"Re: [PATCH v11 13/13] ls-tree.c: support --object-only option for \"git-ls-tree\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-19T05:24:36Z","receivedAt":"2022-02-19T05:31:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Feb 08 2022, Teng Long wrote:\n\n> We usually pipe the output from `git ls-trees` to tools like\n> `sed` or `cut` when we only want to extract some fields.\n>\n> When we want only the pathname component, we can pass\n> `--name-only` option to omit such a pipeline, but there are no\n> options for extracting other fields.\n>\n> Teach the \"--object-only\" option to the command to only show the\n> object name. This option cannot be used together with\n> \"--name-only\" or \"--long\" , they are mutually exclusive (actually\n> \"--name-only\" and \"--long\" can be combined together before, this\n> commit by the way fix this bug).\n>\n> In terms of performance, there is no loss comparing to the\n> \"master\" (2ae0a9c), here are the\n> results of the performance tests in my environment based on linux\n> repository:\n\nI think given the re-arrangement in this v11 it would make sense to\nchange the commit messageto say:\n\n * This is an alias for --format=%(objectname)\n * Per benchmark XYZ it's faster\n\nI.e. this:\n\n>     $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r HEAD\"\n>     Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD\n>     Time (mean ± σ):     105.8 ms ±   2.7 ms    [User: 85.7 ms, System: 20.0 ms]\n>     Range (min … max):   101.5 ms … 111.3 ms    28 runs\n>\n>     $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD\"\n>     Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD\n>     Time (mean ± σ):     105.0 ms ±   3.0 ms    [User: 83.7 ms, System: 21.2 ms]\n>     Range (min … max):    99.3 ms … 109.5 ms    27 runs\n>\n>     $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r -l HEAD\"\n>     Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD\n>     Time (mean ± σ):     337.4 ms ±  10.9 ms    [User: 308.3 ms, System: 29.0 ms]\n>     Range (min … max):   323.0 ms … 355.0 ms    10 runs\n>\n>     $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD\"\n>     Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD\n>     Time (mean ± σ):     337.6 ms ±   6.2 ms    [User: 309.4 ms, System: 28.1 ms]\n>     Range (min … max):   330.4 ms … 349.9 ms    10 runs\n\nIs surely more relevant if compared to master & that --format.\n\n> Signed-off-by: Teng Long <dyroneteng@gmail.com>\n> ---\n>  Documentation/git-ls-tree.txt |  7 ++++-\n>  builtin/ls-tree.c             | 16 ++++++++++-\n>  t/t3104-ls-tree-format.sh     | 12 +++++++++\n>  t/t3105-ls-tree-oid.sh        | 51 +++++++++++++++++++++++++++++++++++\n>  4 files changed, 84 insertions(+), 2 deletions(-)\n>  create mode 100755 t/t3105-ls-tree-oid.sh\n>\n> diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt\n> index db29a9efb5..21045dd163 100644\n> --- a/Documentation/git-ls-tree.txt\n> +++ b/Documentation/git-ls-tree.txt\n> @@ -10,7 +10,7 @@ SYNOPSIS\n>  --------\n>  [verse]\n>  'git ls-tree' [-d] [-r] [-t] [-l] [-z]\n> -\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n> +\t    [--name-only] [--name-status] [--object-only] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n>  \t    <tree-ish> [<path>...]\n>  \n>  DESCRIPTION\n> @@ -59,6 +59,11 @@ OPTIONS\n>  --name-only::\n>  --name-status::\n>  \tList only filenames (instead of the \"long\" output), one per line.\n> +\tCannot be combined with `--object-only`.\n> +\n> +--object-only::\n> +\tList only names of the objects, one per line. Cannot be combined\n> +\twith `--name-only` or `--name-status`.\n\nHrm, I regret that in my version of v11 11/13 I didn't add to all of these something like:\n    \n    This is equivalent to specifying `--format=...`, but for both this\n    option and that exact format the command takes a hand-optimized\n    codepath instead of going through the generic formatting mechanism.\n\nOr whatever, and perhaps have everything after \", but for[...]\" part of\nthe generic FORMAT section (no need to say it for every option).\n    \n>  \n>  --abbrev[=<n>]::\n>  \tInstead of showing the full 40-byte hexadecimal object\n> diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\n> index ba96bcf602..9819a24186 100644\n> --- a/builtin/ls-tree.c\n> +++ b/builtin/ls-tree.c\n> @@ -20,6 +20,7 @@ static int line_termination = '\\n';\n>  #define LS_SHOW_TREES (1 << 2)\n>  #define LS_NAME_ONLY (1 << 3)\n>  #define LS_SHOW_SIZE (1 << 4)\n> +#define LS_OBJECT_ONLY (1 << 5)\n>  static int abbrev;\n>  static int ls_options;\n>  static struct pathspec pathspec;\n> @@ -37,6 +38,7 @@ static const char *format;\n>  static const char *default_format = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\";\n>  static const char *long_format = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\";\n>  static const char *name_only_format = \"%(path)\";\n> +static const char *object_only_format = \"%(objectname)\";\n>  struct show_tree_data {\n>  \tunsigned mode;\n>  \tenum object_type type;\n> @@ -53,6 +55,7 @@ static const  char * const ls_tree_usage[] = {\n>  enum {\n>  \tMODE_UNSPECIFIED = 0,\n>  \tMODE_NAME_ONLY,\n> +\tMODE_OBJECT_ONLY,\n>  \tMODE_LONG,\n>  };\n>  \n> @@ -67,6 +70,8 @@ static int fast_path(void){\n>  \t} else if (!strcmp(format, name_only_format)) {\n>  \t\tshown_fields = FIELD_PATH_NAME;\n>  \t\treturn 1;\n> +\t} else if (!strcmp(format, object_only_format)) {\n> +\t\tshown_fields = FIELD_OBJECT_NAME;\n>  \t}\n>  \treturn 0;\n>  }\n> @@ -143,7 +148,10 @@ static int parse_shown_fields(void)\n>  \t\tshown_fields = FIELD_PATH_NAME;\n>  \t\treturn 0;\n>  \t}\n> -\n> +\tif (cmdmode == MODE_OBJECT_ONLY) {\n> +\t\tshown_fields = FIELD_OBJECT_NAME;\n> +\t\treturn 0;\n> +\t}\n>  \tif (!ls_options || (ls_options & LS_RECURSIVE)\n>  \t    || (ls_options & LS_SHOW_TREES)\n>  \t    || (ls_options & LS_TREE_ONLY))\n> @@ -267,6 +275,10 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n>  \tif (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))\n>  \t\treturn 0;\n>  \n> +\tif (shown_fields == FIELD_OBJECT_NAME) {\n> +\t\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n> +\t\treturn recurse;\n> +\t}\n>  \tif (shown_fields == FIELD_PATH_NAME) {\n>  \t\tbaselen = base->len;\n>  \t\tstrbuf_addstr(base, pathname);\n> @@ -304,6 +316,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n>  \t\t\t    MODE_NAME_ONLY),\n>  \t\tOPT_CMDMODE(0, \"name-status\", &cmdmode, N_(\"list only filenames\"),\n>  \t\t\t    MODE_NAME_ONLY),\n> +\t\tOPT_CMDMODE(0, \"object-only\", &cmdmode, N_(\"list only objects\"),\n> +\t\t\t    MODE_OBJECT_ONLY),\n>  \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n>  \t\t\t    N_(\"use full path names\"), 0),\n>  \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n> diff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh\n> index e08c83dc47..c0ffc8e1c3 100755\n> --- a/t/t3104-ls-tree-format.sh\n> +++ b/t/t3104-ls-tree-format.sh\n> @@ -46,6 +46,12 @@ test_expect_success 'ls-tree --format=<name-only-like>' '\n>  \t\t\"--name-only\"\n>  '\n\nThis looks much better/less complex than in earlier rounds.\n\n> +test_expect_success 'ls-tree --format=<object-only-like>' '\n> +\ttest_ls_tree_format \\\n> +\t\t\"%(objectname)\" \\\n> +\t\t\"--object-only\"\n> +'\n> +\n>  test_expect_success 'ls-tree combine --format=<default-like> and -t' '\n>  \ttest_ls_tree_format \\\n>  \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n> @@ -78,4 +84,10 @@ test_expect_success 'ls-tree hit fast-path with --format=<name-only-like>' '\n>  \tgit ls-tree --format=\"%(path)\" -r HEAD >actual &&\n>  \ttest_cmp expect actual\n>  '\n> +\n> +test_expect_success 'ls-tree hit fast-path with --format=<object-only-like>' '\n> +\tgit ls-tree -r --object-only HEAD >expect &&\n> +\tgit ls-tree --format=\"%(objectname)\" -r HEAD >actual &&\n> +\ttest_cmp expect actual\n> +'\n>  test_done\n\nSo, you and I came up with independent tests for these two.\n\nI wonder if this can be re-arranged so that we can share the tests, and\nperhaps test all for both the --format and --object-onnly in some\nfor-loop, or maybe it's not worth it.\n\n> diff --git a/t/t3105-ls-tree-oid.sh b/t/t3105-ls-tree-oid.sh\n> new file mode 100755\n> index 0000000000..992bb26bfa\n> --- /dev/null\n> +++ b/t/t3105-ls-tree-oid.sh\n> @@ -0,0 +1,51 @@\n> +#!/bin/sh\n> +\n> +test_description='git ls-tree objects handling.'\n> +\n> +. ./test-lib.sh\n> +\n> +test_expect_success 'setup' '\n> +test_commit A &&\n> +test_commit B &&\n> +mkdir -p C &&\n> +test_commit C/D.txt &&\n> +find *.txt path* \\( -type f -o -type l \\) -print |\n> +xargs git update-index --add &&\n> +tree=$(git write-tree) &&\n> +echo $tree\n> +'\n> +\n> +test_expect_success 'usage: --object-only' '\n> +git ls-tree --object-only $tree >current &&\n> +git ls-tree $tree >result &&\n> +cut -f1 result | cut -d \" \" -f3 >expected &&\n> +test_cmp current expected\n> +'\n> +\n> +test_expect_success 'usage: --object-only with -r' '\n> +git ls-tree --object-only -r $tree >current &&\n> +git ls-tree -r $tree >result &&\n> +cut -f1 result | cut -d \" \" -f3 >expected &&\n> +test_cmp current expected\n> +'\n> +\n> +test_expect_success 'usage: --object-only with --abbrev' '\n> +git ls-tree --object-only --abbrev=6 $tree >current &&\n> +git ls-tree --abbrev=6 $tree >result &&\n> +cut -f1 result | cut -d \" \" -f3 >expected &&\n> +test_cmp current expected\n> +'\n> +\n> +test_expect_success 'usage: incompatible options: --name-only with --object-only' '\n> +test_expect_code 129 git ls-tree --object-only --name-only $tree\n> +'\n> +\n> +test_expect_success 'usage: incompatible options: --name-status with --object-only' '\n> +test_expect_code 129 git ls-tree --object-only --name-status $tree\n> +'\n> +\n> +test_expect_success 'usage: incompatible options: --long with --object-only' '\n> +test_expect_code 129 git ls-tree --object-only --long $tree\n> +'\n> +\n> +test_done\n\nThis whole test block seems to have lost its indentation since the v10.\n"},{"id":"448837","messageId":"220219.86v8xbe7kb.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"6d264977491b3645292bba5f91479ab1623e8b80.1644319434.git.dyroneteng@gmail.com","subject":"Re: [PATCH v11 12/13] ls-tree: introduce function \"fast_path()\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-19T05:32:21Z","receivedAt":"2022-02-19T05:47:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Feb 08 2022, Teng Long wrote:\n\n> The generic \"show_tree_fmt()\" is slower than \"show_tree()\", so\n> we want to take the fast path if possible.\n>\n> when \"--format=<format>\" is passed, \"fast_path()\" will determine\n> whether to use \"show_tree()\" or insist on using \"show_tree_fmt()\"\n> by a try of finding out if the built-int format is hit.\n>\n> This commit take out the related codes from \"cmd_ls_tree()\" and\n> package them into a new funtion \"fast_path()\".\n>\n> Explain it a little bit further, whether fast_path is hit or not,\n> the final correctness should not break. Abstracting a separate method\n> helps improve the readability of \"cmd_ls_tree()\" and the cohesiveness\n> and extensibility of fast path logic.\n\nThis whole commit message sounds a bit like \"we're introducing this fast\npath\", but really it got added in 11/13, and this is just a refactoring\nto split that into a function to slightly reduce the size of\ncmd_ls_tree() itself.\n\nWhich I really don't mind, but it would be better if the commit message\nsaid so, e.g.:\n\n    In a preceding commit a fast path selection was added to cmd_ls_tree(),\n    split it into a utility function because ...\n\nBut I got stuck on \"...\" because I couldn't find a reason :)\n\nI.e. in 13/13 this isn't used at all, except by adding a new brace arm\nto it, but then it could still live in cmd_ls_tree().\n\nPersonally I think the pre-image is a bit easier to read, but then again\nI wrote that so I'm biased. I don't mind changing this, but structurally\nfor the series it seems better to squash it in if you'd want to keep it.\n\n> Signed-off-by: Teng Long <dyroneteng@gmail.com>\n> ---\n>  builtin/ls-tree.c | 23 ++++++++++++++---------\n>  1 file changed, 14 insertions(+), 9 deletions(-)\n>\n> diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\n> index 1c71e5d543..ba96bcf602 100644\n> --- a/builtin/ls-tree.c\n> +++ b/builtin/ls-tree.c\n> @@ -58,6 +58,19 @@ enum {\n>  \n>  static int cmdmode = MODE_UNSPECIFIED;\n>  \n> +static int fast_path(void){\n> +\tif (!strcmp(format, default_format)) {\n> +\t\treturn 1;\n> +\t} else if (!strcmp(format, long_format)) {\n> +\t\tshown_fields = shown_fields | FIELD_SIZE;\n> +\t\treturn 1;\n> +\t} else if (!strcmp(format, name_only_format)) {\n> +\t\tshown_fields = FIELD_PATH_NAME;\n> +\t\treturn 1;\n> +\t}\n> +\treturn 0;\n> +}\n\nJust in terms of arranging things if you add a static function and it's\nonly used in one other function, here in cmd_ls_tree(), it's more\nreadable to add it immediately before that function.\n\n>  static void expand_objectsize(struct strbuf *line, const struct object_id *oid,\n>  \t\t\t      const enum object_type type, unsigned int padded)\n>  {\n> @@ -350,15 +363,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n>  \t * The generic show_tree_fmt() is slower than show_tree(), so\n>  \t * take the fast path if possible.\n>  \t */\n> -\tif (format && (!strcmp(format, default_format))) {\n> -\t\tfn = show_tree;\n> -\t} else if (format && (!strcmp(format, long_format))) {\n> -\t\tshown_fields = shown_fields | FIELD_SIZE;\n> -\t\tfn = show_tree;\n> -\t} else if (format && (!strcmp(format, name_only_format))) {\n> -\t\tshown_fields = FIELD_PATH_NAME;\n> -\t\tfn = show_tree;\n> -\t} else if (format)\n> +\tif (format && !fast_path())\n>  \t\tfn = show_tree_fmt;\n>  \n>  \treturn !!read_tree(the_repository, tree, &pathspec, fn, NULL);\n\nAlso in terms of structure wouldn't it be better to end up with this:\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 9819a241869..47f7e2136b0 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -355,8 +355,6 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tif (get_oid(argv[0], &oid))\n \t\tdie(\"Not a valid object name %s\", argv[0]);\n \n-\tparse_shown_fields();\n-\n \t/*\n \t * show_recursive() rolls its own matching code and is\n \t * generally ignorant of 'struct pathspec'. The magic mask\n@@ -373,6 +371,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \ttree = parse_tree_indirect(&oid);\n \tif (!tree)\n \t\tdie(\"not a tree object\");\n+\n+\tparse_shown_fields();\n \t/*\n \t * The generic show_tree_fmt() is slower than show_tree(), so\n \t * take the fast path if possible.\n\nI.e. have the whole \"shown_fields\" decisions be near one another.\n"},{"id":"448838","messageId":"220219.86r17ze72s.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"5936004f1386388740ded0208ebeb8c5d0c88ef2.1644319434.git.dyroneteng@gmail.com","subject":"Re: [PATCH v11 11/13] ls-tree.c: introduce \"--format\" option","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-19T05:44:29Z","receivedAt":"2022-02-19T05:52:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Feb 08 2022, Teng Long wrote:\n\n> +\tif (*start != '(')\n> +\t\tdie(_(\"bad ls-tree format: as '%s'\"), start);\n\nMy typo surely, but I think I menat \"as of\" not just \"as\" there:\n\n    $ ./git ls-tree --format=\"%[blah)\" -r HEAD\n    fatal: bad ls-tree format: as of '[blah)'\n\n> +\n> +\tend = strchr(start + 1, ')');\n> +\tif (!end)\n> +\t\tdie(_(\"bad ls-tree format: element '%s' does not end in ')'\"), start);\n\nOr actually:\n\n    $ ./git ls-tree --format=\"%(blah]\" -r HEAD\n    fatal: bad ls-tree format: element '(blah]' does not end in ')'\n\nWe could rather say for the first one:\n\n    $ ./git ls-tree --format=\"%[blah)\" -r HEAD\n    fatal: bad ls-tree format: element '[blah)' does not start with '('\n\n> [...]\n> +\t\terrlen = (unsigned long)len;\n> +\t\tdie(_(\"bad ls-tree format: %%%.*s\"), errlen, start);\n\nI wondered why that %% is there (and I probably wrote this in the first\nplace, I didn't check:). But it makes sense, because strbuf_expand()\nskips past the % for us, and we'd like to say e.g. %(foobar) here, not\n(foobar) or whatever.\n\n> new file mode 100755\n> index 0000000000..e08c83dc47\n> --- /dev/null\n> +++ b/t/t3104-ls-tree-format.sh\n> @@ -0,0 +1,81 @@\n> +#!/bin/sh\n> +\n> +test_description='ls-tree --format'\n> +\n> +TEST_PASSES_SANITIZE_LEAK=true\n\nI notice now after commenting on your 13/13 that you should add\nTEST_PASSES_SANITIZE_LEAK=true to it (assuming it doesn't leak, which I\ndon't think it does, but test with SANITIZE=leak first!)\n\n> +test_ls_tree_format () {\n> +\tformat=$1 &&\n> +\topts=$2 &&\n> +\tfmtopts=$3 &&\n> +\tshift 2 &&\n> +\tgit ls-tree $opts -r HEAD >expect.raw &&\n> +\tsed \"s/^/> /\" >expect <expect.raw &&\n> +\tgit ls-tree --format=\"> $format\" -r $fmtopts HEAD >actual &&\n> +\ttest_cmp expect actual\n> +}\n\nI also forgot I wrote this, but also per my comment on 13/13 you can\njust add your tests added in 13/13 to this file, then we'll assert that\n-r etc. work the same for both.\n"},{"id":"448839","messageId":"220219.86mtine6jb.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"41e8ed50476a5afd6009db1a69c7a39b04e038b0.1644319434.git.dyroneteng@gmail.com","subject":"Re: [PATCH v11 08/13] ls-tree: slightly refactor `show_tree()`","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-19T05:56:47Z","receivedAt":"2022-02-19T06:04:30Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Feb 08 2022, Teng Long wrote:\n\n> This is a non-functional change, we use a new int \"shown_fields\" to mark\n> which columns to output, and `parse_shown_fields()` to calculate the\n> value of \"shown_fields\".\n>\n> This has the advantage of making the show_tree logic simpler and more\n> readable, as well as making it easier to extend new options (for example,\n> if we want to add a \"--object-only\" option, we just need to add a similar\n> \"if (shown_fields == FIELD_OBJECT_NAME)\" short-circuit logic in\n> \"show_tree()\").\n\nI think this and the 09/13 really don't make sense in combination.\n\nNow, I clearly prefer to put options for the command into its own little\nstruct to pass around, I think it makes for easier reading than the\nglobals you end up with.\n\nBut tastes differ, and some built-ins use one, and some the other\npattern.\n\nBut this is really the worst of both worlds, let's just pick one or the\nother, not effectively some some ptions in that struct in 09/13, and\nsome in globals here...\n\n> +static unsigned int shown_fields;\n> +#define FIELD_PATH_NAME 1\n> +#define FIELD_SIZE (1 << 1)\n> +#define FIELD_OBJECT_NAME (1 << 2)\n> +#define FIELD_TYPE (1 << 3)\n> +#define FIELD_MODE (1 << 4)\n> +#define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n\nWhy do we need some FIELD_DEFAULT here as opposed to just having it by\nan enum field with a valu of 0?\n\n> +enum {\n> +\tMODE_UNSPECIFIED = 0,\n> +\tMODE_NAME_ONLY,\n> +\tMODE_LONG,\n> +};\n> +\n> +static int cmdmode = MODE_UNSPECIFIED;\n\nlet's name this enum type and use it, see e.g. builtin/help.c's \"static\nenum help_action\" for an example.\n\n> +\n> +static int parse_shown_fields(void)\n> +{\n> +\tif (cmdmode == MODE_NAME_ONLY) {\n> +\t\tshown_fields = FIELD_PATH_NAME;\n> +\t\treturn 0;\n> +\t}\n> +\n> +\tif (!ls_options || (ls_options & LS_RECURSIVE)\n> +\t    || (ls_options & LS_SHOW_TREES)\n> +\t    || (ls_options & LS_TREE_ONLY))\n> +\t\tshown_fields = FIELD_DEFAULT;\n> +\tif (cmdmode == MODE_LONG)\n> +\t\tshown_fields = FIELD_LONG_DEFAULT;\n> +\treturn 1;\n> +}\n\nI still don't really get why we can't just use the one MODE_*\nhere. E.g. doesn't MODE_LONG map to FIELD_LONG_DEFAULT, MODE_NAME_ONLY\nto FIELD_PATH_NAME etc?\n\nIs this all so we can do \"shown_fields & FIELD_SIZE\" in show_default()\nas opposed to e.g. checking \"default format or long format?\" ?\n"},{"id":"448840","messageId":"220219.86iltbe6i2.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"b22c2dc49ea3aa0ed7d0df367eddc84e931e9e21.1644319434.git.dyroneteng@gmail.com","subject":"Re: [PATCH v11 07/13] ls-tree: fix \"--name-only\" and \"--long\" combined use bug","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-19T06:04:39Z","receivedAt":"2022-02-19T06:05:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Feb 08 2022, Teng Long wrote:\n\n> If we execute \"git ls-tree\" with combined \"--name-only\" and \"--long\"\n> , only the pathname will be printed, the size is omitted (the original\n> discoverer was Peff in [1]).\n>\n> This commit fix this issue by using `OPT_CMDMODE()` instead to make both\n> of them mutually exclusive.\n>\n> [1] https://public-inbox.org/git/YZK0MKCYAJmG+pSU@coredump.intra.peff.net/\n>\n> Signed-off-by: Teng Long <dyroneteng@gmail.com>\n> ---\n>  builtin/ls-tree.c | 12 ++++++------\n>  1 file changed, 6 insertions(+), 6 deletions(-)\n>\n> diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\n> index 9c57a36c8c..32147e75e6 100644\n> --- a/builtin/ls-tree.c\n> +++ b/builtin/ls-tree.c\n> @@ -120,12 +120,12 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n>  \t\t\tLS_SHOW_TREES),\n>  \t\tOPT_SET_INT('z', NULL, &line_termination,\n>  \t\t\t    N_(\"terminate entries with NUL byte\"), 0),\n> -\t\tOPT_BIT('l', \"long\", &ls_options, N_(\"include object size\"),\n> -\t\t\tLS_SHOW_SIZE),\n> -\t\tOPT_BIT(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n> -\t\t\tLS_NAME_ONLY),\n> -\t\tOPT_BIT(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n> -\t\t\tLS_NAME_ONLY),\n> +\t\tOPT_CMDMODE('l', \"long\", &ls_options, N_(\"include object size\"),\n> +\t\t\t    LS_SHOW_SIZE),\n> +\t\tOPT_CMDMODE(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n> +\t\t\t    LS_NAME_ONLY),\n> +\t\tOPT_CMDMODE(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n> +\t\t\t    LS_NAME_ONLY),\n>  \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n>  \t\t\t    N_(\"use full path names\"), 0),\n>  \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n\nThis seems like a sensible fix, but let's add a test for it. See:\n\n    git grep 'test_expect_code 129'\n\nFor examples.\n"},{"id":"448841","messageId":"220219.86ee3ze5kz.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"3816a65fe62b1039c396eb38d27bf2c22e8d84de.1644319434.git.dyroneteng@gmail.com","subject":"Re: [PATCH v11 06/13] ls-tree: simplify nesting if/else logic in \"show_tree()\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-19T06:06:51Z","receivedAt":"2022-02-19T06:25:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Feb 08 2022, Teng Long wrote:\n\n> This commit use \"object_type()\" to get the type, then remove\n> some of the nested if to let the codes here become more cleaner.\n>\n> Signed-off-by: Teng Long <dyronetengb@gmail.com>\n> ---\n>  builtin/ls-tree.c | 18 ++++++------------\n>  1 file changed, 6 insertions(+), 12 deletions(-)\n>\n> diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\n> index ef8c414f61..9c57a36c8c 100644\n> --- a/builtin/ls-tree.c\n> +++ b/builtin/ls-tree.c\n> @@ -66,19 +66,13 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n>  {\n>  \tint recurse = 0;\n>  \tsize_t baselen;\n> -\tenum object_type type = OBJ_BLOB;\n> +\tenum object_type type = object_type(mode);\n>  \n> -\tif (S_ISGITLINK(mode)) {\n> -\t\ttype = OBJ_COMMIT;\n> -\t} else if (S_ISDIR(mode)) {\n> -\t\tif (show_recursive(base->buf, base->len, pathname)) {\n> -\t\t\trecurse = READ_TREE_RECURSIVE;\n> -\t\t\tif (!(ls_options & LS_SHOW_TREES))\n> -\t\t\t\treturn recurse;\n> -\t\t}\n> -\t\ttype = OBJ_TREE;\n> -\t}\n> -\telse if (ls_options & LS_TREE_ONLY)\n> +\tif (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))\n> +\t\trecurse = READ_TREE_RECURSIVE;\n> +\tif (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES))\n> +\t\treturn recurse;\n> +\tif (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))\n>  \t\treturn 0;\n>  \n>  \tif (!(ls_options & LS_NAME_ONLY)) {\n\nI think the use of object_type() here is good, but in any case I think\ndoing a minimal change first for the \"type\" and then this proposed\nrefactoring would be easier to look at, and to independently decide on\nthe two.\n\nI find this much easier to read, both as a diff and as end-state:\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex ef8c414f61a..0af09e94a03 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -66,20 +66,18 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n {\n \tint recurse = 0;\n \tsize_t baselen;\n-\tenum object_type type = OBJ_BLOB;\n+\tenum object_type type = object_type(mode);\n \n-\tif (S_ISGITLINK(mode)) {\n-\t\ttype = OBJ_COMMIT;\n-\t} else if (S_ISDIR(mode)) {\n+\tif (type == OBJ_BLOB) {\n+\t\tif (ls_options & LS_TREE_ONLY)\n+\t\t\treturn 0;\n+\t} else if (type == OBJ_TREE) { \n \t\tif (show_recursive(base->buf, base->len, pathname)) {\n \t\t\trecurse = READ_TREE_RECURSIVE;\n \t\t\tif (!(ls_options & LS_SHOW_TREES))\n \t\t\t\treturn recurse;\n \t\t}\n-\t\ttype = OBJ_TREE;\n \t}\n-\telse if (ls_options & LS_TREE_ONLY)\n-\t\treturn 0;\n \n \tif (!(ls_options & LS_NAME_ONLY)) {\n \t\tif (ls_options & LS_SHOW_SIZE) {\n\nUnrolling this from a logical if/else if to an if/if/if I think also\ndoesn't make sense. At the cost of a slightly larger diff (could be done\non top) we get rid of the show_recursive() branch too:\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex ef8c414f61a..d4be71bad24 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -66,20 +66,17 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n {\n \tint recurse = 0;\n \tsize_t baselen;\n-\tenum object_type type = OBJ_BLOB;\n+\tenum object_type type = object_type(mode);\n \n-\tif (S_ISGITLINK(mode)) {\n-\t\ttype = OBJ_COMMIT;\n-\t} else if (S_ISDIR(mode)) {\n-\t\tif (show_recursive(base->buf, base->len, pathname)) {\n-\t\t\trecurse = READ_TREE_RECURSIVE;\n-\t\t\tif (!(ls_options & LS_SHOW_TREES))\n-\t\t\t\treturn recurse;\n-\t\t}\n-\t\ttype = OBJ_TREE;\n+\tif (type == OBJ_BLOB) {\n+\t\tif (ls_options & LS_TREE_ONLY)\n+\t\t\treturn 0;\n+\t} else if (type == OBJ_TREE &&\n+\t\t   show_recursive(base->buf, base->len, pathname)) {\n+\t\trecurse = READ_TREE_RECURSIVE;\n+\t\tif (!(ls_options & LS_SHOW_TREES))\n+\t\t\treturn recurse;\n \t}\n-\telse if (ls_options & LS_TREE_ONLY)\n-\t\treturn 0;\n \n \tif (!(ls_options & LS_NAME_ONLY)) {\n \t\tif (ls_options & LS_SHOW_SIZE) {\n\nWhich, I think is also nicer to read, we're not checking \"is it a\ntree?\", setting \"recursive\", and then using that \"recursive\" as a\nboolean for no reason. Let's just continue on that \"else if\" chain we're\nalready in instead...\n"},{"id":"449777","messageId":"220228.864k4j3qlh.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"CADMgQSRYKB1ybxZWxQQ3uVM71fmdbzHqcK-WUPNKm2HMxw2C2g@mail.gmail.com","subject":"Re: [PATCH v11 08/13] ls-tree: slightly refactor `show_tree()`","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-28T16:18:10Z","receivedAt":"2022-02-28T16:26:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Feb 28 2022, Teng Long wrote:\n\n[Since this had HTML parts it didn't make it to the git ML, quoting it\nhere in full & replying on-list].\n\n> On Sat, Feb 19, 2022 at 2:04 PM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n>  > I think this and the 09/13 really don't make sense in combination.\n>  > \n>  > Now, I clearly prefer to put options for the command into its own little\n>  > struct to pass around, I think it makes for easier reading than the\n>  > globals you end up with.\n>  > \n>  > But tastes differ, and some built-ins use one, and some the other\n>  > pattern.\n>  > \n>  > But this is really the worst of both worlds, let's just pick one or the\n>  > other, not effectively some some ptions in that struct in 09/13, and\n>  > some in globals here...\n>\n> I'm not 100 percent sure about it, but I agree with we can just pick one or\n> the other.\n>\n> So, how about: \n>      1. add \"unsigned shown_fields\"  in \"struct show_tree_data\"\n>      2.  move global \"show_fields\" to \"struct show_tree_data\"\n>      3. move \"parse_show_fields()\" to \"show_tree()\"\n>\n> diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\n> index 293b8f9dfb..92add01ecc 100644\n> --- a/builtin/ls-tree.c\n> +++ b/builtin/ls-tree.c\n> @@ -25,7 +25,6 @@ static int ls_options;\n>  static struct pathspec pathspec;\n>  static int chomp_prefix;\n>  static const char *ls_tree_prefix;\n> -static unsigned int shown_fields;\n>  #define FIELD_PATH_NAME 1\n>  #define FIELD_SIZE (1 << 1)\n>  #define FIELD_OBJECT_NAME (1 << 2)\n> @@ -40,6 +39,7 @@ struct show_tree_data {\n>         const struct object_id *oid;\n>         const char *pathname;\n>         struct strbuf *base;\n> +       unsigned int shown_fields;\n>  };\n>  \n>  static const  char * const ls_tree_usage[] = {\n> @@ -55,19 +55,19 @@ enum {\n>  \n>  static int cmdmode = MODE_UNSPECIFIED;\n>  \n> -static int parse_shown_fields(void)\n> +static int parse_shown_fields(unsigned int *shown_fields)\n>  {\n>         if (cmdmode == MODE_NAME_ONLY) {\n> -               shown_fields = FIELD_PATH_NAME;\n> +               *shown_fields = FIELD_PATH_NAME;\n>                 return 0;\n>         }\n>  \n>         if (!ls_options || (ls_options & LS_RECURSIVE)\n>             || (ls_options & LS_SHOW_TREES)\n>             || (ls_options & LS_TREE_ONLY))\n> -               shown_fields = FIELD_DEFAULT;\n> +               *shown_fields = FIELD_DEFAULT;\n>         if (cmdmode == MODE_LONG)\n> -               shown_fields = FIELD_LONG_DEFAULT;\n> +               *shown_fields = FIELD_LONG_DEFAULT;\n>         return 1;\n>  }\n>  \n> @@ -105,7 +105,7 @@ static int show_default(struct show_tree_data *data)\n>  {\n>         size_t baselen = data->base->len;\n>  \n> -       if (shown_fields & FIELD_SIZE) {\n> +       if (data->shown_fields & FIELD_SIZE) {\n>                 char size_text[24];\n>                 if (data->type == OBJ_BLOB) {\n>                         unsigned long size;\n> @@ -137,15 +137,19 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n>  {\n>         int recurse = 0;\n>         size_t baselen;\n> +       unsigned int shown_fields = 0;\n>         enum object_type type = object_type(mode);\n> -       struct show_tree_data data = {\n> +               struct show_tree_data data = {\n>                 .mode = mode,\n>                 .type = type,\n>                 .oid = oid,\n>                 .pathname = pathname,\n>                 .base = base,\n> +               .shown_fields = shown_fields,\n>         };\n>  \n> +       parse_shown_fields(&shown_fields);\n> +\n>         if (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))\n>                 recurse = READ_TREE_RECURSIVE;\n>         if (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES))\n> @@ -219,8 +223,6 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n>         if (get_oid(argv[0], &oid))\n>                 die(\"Not a valid object name %s\", argv[0]);\n>  \n> -       parse_shown_fields();\n> -\n>         /*\n>          * show_recursive() rolls its own matching code and is\n>          * generally ignorant of 'struct pathspec'. The magic mask\n>\n\nYes, this looks good, i.e. these are (I think, I didn't look in much\ndetail) now all store one place instead of two.\n\n>  > >+#define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n>\n>  >Why do we need some FIELD_DEFAULT here as opposed to just having it by\n>  >an enum field with a valu of 0?\n>\n>  \n> You mean to use \"cmdmode\" or set \"FIELD_DEFAULT\" to 0, if the former  I think is a\n> similar situation to your last replied paragraph so I will reply to that. Or if the \n> latter, we want them as a bitmask not only a flag,  so  I think \"0\" here means no fields\n> will be shown and \"29\" is the default bitmask value (also some context metioned in\n> https://public-inbox.org/git/xmqqmtlu7bb0.fsf@gitster.g/).\n\nI meant (elaborated on below) that it seemed like this was conflating\n\"default mode\" with \"wants to emit size\" in the state machine.\n\nI.e. I didn't try rewriting it (or re-visited it now), but it seemed at\nthe time with some minor changes it could be eliminated, but maybe\nnot...\n\n>  > let's name this enum type and use it, see e.g. builtin/help.c's \"static\n>  > enum help_action\" for an example.\n>\n> Totally agree with that. \n> I want to name it \"mutx_option\", I'm not sure whether we need a better one. \n\nSure, I don't think the name matters much since it's now, so whatever\nyou think is OK.\n\nI just wanted to point out that some existing code makes the same\npattern simpler by relying on the 0-init and having fields starting at\n0.\n\n>  > I still don't really get why we can't just use the one MODE_*\n>  > here. E.g. doesn't MODE_LONG map to FIELD_LONG_DEFAULT, MODE_NAME_ONLY\n>  > to FIELD_PATH_NAME etc?\n>\n>  \n> For current, the answer is YES I think.\n>\n>  > Is this all so we can do \"shown_fields & FIELD_SIZE\" in show_default()\n>  > as opposed to e.g. checking \"default format or long format?\" ?\n>\n> Actually, in v3 patch I use \"cmdmode\" to determine the output fields and the next patches Junio\n> suggest using bitmasks for the work (https://public-inbox.org/git/xmqqmtlu7bb0.fsf@gitster.g/).\n>\n> I didn't understand it at first,  and I thought it might make sense. The cmd_mode is used here to indicate that options are mutually exclusive, which may have the same effect in certain\n> cases, such as some short-circuited options (-- name-only).\n>\n> However, cmd_mode is not a complete representation of what fields we want to output, because some options may not be mutually exclusive but can also be used to change the output (for\n> example, we want to add -format or others). If there is a bitmask associated with the output field, we can quickly and explicitly know what to output in the show_tree() phase, without\n> thinking about the relationship to cmd_mode or doing a translation of meaning， and at the same time  this will make it easier to adapt to change I think.\n\n*nod*\n\nI'm not quite sure I get what you're aiming for, but I'll look at it\nagain in any future re-roll & see if I can submit a patch-on-top next\ntime if I have any comments (if at all worth it). Thanks!\n"},{"id":"450372","messageId":"cover.1646390152.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1644319434.git.dyroneteng@gmail.com","subject":"[PATCH v12 00/12] ls-tree: \"--object-only\" and \"--format\" opts","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-04T10:42:26Z","receivedAt":"2022-03-04T10:42:59Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"Sorry for the late reply.\n\nMain diff from V11:\n\n1. [v12][06/12] simplify nesting if/else logic in \"show_tree()\"\n\n  * Unrolling this from a logical if/if/if/ to if/else\n\n  It's from Ævar Arnfjörð Bjarmason's suggestion at:\n\n  https://public-inbox.org/git/220219.86ee3ze5kz.gmgdl@evledraar.gmail.com/#t\n\n  By the way, Ævar also think to doing a minimal change first for use\n  \"object_type\", the reason I didn't is because after the unroll, the related\n  codes here is almost the same. So, I think this situation here is ok now.\n\n\n2. [v12][07/12] ls-tree: fix \"--name-only\" and \"--long\" combined use bug\n\n  * Add 2 tests for the bugfix\n\n  It's from Ævar Arnfjörð Bjarmason's suggestion at:\n\n  https://public-inbox.org/git/220219.86iltbe6i2.gmgdl@evledraar.gmail.com/\n\n3. [v12][08/12] ls-tree: slightly refactor `show_tree()\n\n   * Name the cmdmode enum\n\n   It's from Ævar Arnfjörð Bjarmason's suggestion at:\n\n   https://public-inbox.org/git/220219.86mtine6jb.gmgdl@evledraar.gmail.com/\n\n4. [v12][09/12] ls-tree: introduce struct \"show_tree_data\"\n\n   * Move global \"show_fields into struct \"show_data\", avoid to use both global\n     value and local value to save the related data for showing.\n\n   It's from Ævar Arnfjörð Bjarmason's suggestion at:\n\n   https://public-inbox.org/git/220219.86mtine6jb.gmgdl@evledraar.gmail.com/       \n\n5. [v12][11/12] ls-tree.c: introduce \"--format\" option\n\n   * Replace \"die(_(\"bad ls-tree format: as '%s'\"), start);\" with\n     \"die(_(\"bad ls-tree format: element '%s' does not start with '('\"), start)\"\n\n   It's from Ævar Arnfjörð Bjarmason's suggestion at:\n\n   https://public-inbox.org/git/220219.86r17ze72s.gmgdl@evledraar.gmail.com/\n\n6. ls-tree: introduce function \"fast_path()\"\n\n   * This commit is removed now.\n\n7. [v12][12/12] ls-tree.c: support --object-only option for \"git-ls-tree\"\n\n   * Optimize the commit message\n\n   * Add some extra contents under \"--object-only:\" in \"git-ls-tree.txt\"\n\n   * Tidy the tests. I remove t3105-ls-tree-oid.sh, move the format-checking\n     tests to t3104-ls-tree-format.sh and move the option-compatiblilty-checking\n     test to t3103-ls-tree-misc.sh.\n\nThanks.\n\nJohannes Schindelin (1):\n  cocci: allow padding with `strbuf_addf()`\n\nTeng Long (5):\n  ls-tree: rename \"retval\" to \"recurse\" in \"show_tree()\"\n  ls-tree: simplify nesting if/else logic in \"show_tree()\"\n  ls-tree: fix \"--name-only\" and \"--long\" combined use bug\n  ls-tree: slightly refactor `show_tree()`\n  ls-tree: support --object-only option for \"git-ls-tree\"\n\nÆvar Arnfjörð Bjarmason (6):\n  ls-tree: remove commented-out code\n  ls-tree: add missing braces to \"else\" arms\n  ls-tree: use \"enum object_type\", not {blob,tree,commit}_type\n  ls-tree: use \"size_t\", not \"int\" for \"struct strbuf\"'s \"len\"\n  ls-tree: introduce struct \"show_tree_data\"\n  ls-tree: introduce \"--format\" option\n\n Documentation/git-ls-tree.txt   |  68 ++++++-\n builtin/ls-tree.c               | 324 +++++++++++++++++++++++++-------\n contrib/coccinelle/strbuf.cocci |   6 +-\n t/t3103-ls-tree-misc.sh         |  12 ++\n t/t3104-ls-tree-format.sh       | 100 ++++++++++\n 5 files changed, 439 insertions(+), 71 deletions(-)\n create mode 100755 t/t3104-ls-tree-format.sh\n\nRange-diff against v11:\n -:  ---------- >  1:  2fcff7e0d4 ls-tree: remove commented-out code\n -:  ---------- >  2:  6fd1dd9383 ls-tree: add missing braces to \"else\" arms\n -:  ---------- >  3:  208654b5e2 ls-tree: use \"enum object_type\", not {blob,tree,commit}_type\n -:  ---------- >  4:  2637464fd8 ls-tree: use \"size_t\", not \"int\" for \"struct strbuf\"'s \"len\"\n -:  ---------- >  5:  99e6d47108 ls-tree: rename \"retval\" to \"recurse\" in \"show_tree()\"\n 1:  3816a65fe6 !  6:  a8d9b78dea ls-tree: simplify nesting if/else logic in \"show_tree()\"\n    @@ Metadata\n      ## Commit message ##\n         ls-tree: simplify nesting if/else logic in \"show_tree()\"\n     \n    -    This commit use \"object_type()\" to get the type, then remove\n    -    some of the nested if to let the codes here become more cleaner.\n    +    Use the object_type() function to determine the object type from the\n    +    \"mode\" passed to us by read_tree(), instead of doing so with the S_*()\n    +    macros.\n     \n         Signed-off-by: Teng Long <dyronetengb@gmail.com>\n     \n    @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strb\n     -\t\t\t\treturn recurse;\n     -\t\t}\n     -\t\ttype = OBJ_TREE;\n    --\t}\n    --\telse if (ls_options & LS_TREE_ONLY)\n    -+\tif (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))\n    ++\tif (type == OBJ_BLOB) {\n    ++\t\tif (ls_options & LS_TREE_ONLY)\n    ++\t\t\treturn 0;\n    ++\t} else if (type == OBJ_TREE &&\n    ++\t\t   show_recursive(base->buf, base->len, pathname)) {\n     +\t\trecurse = READ_TREE_RECURSIVE;\n    -+\tif (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES))\n    -+\t\treturn recurse;\n    -+\tif (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))\n    - \t\treturn 0;\n    ++\t\tif (!(ls_options & LS_SHOW_TREES))\n    ++\t\t\treturn recurse;\n    + \t}\n    +-\telse if (ls_options & LS_TREE_ONLY)\n    +-\t\treturn 0;\n      \n      \tif (!(ls_options & LS_NAME_ONLY)) {\n    + \t\tif (ls_options & LS_SHOW_SIZE) {\n 2:  b22c2dc49e !  7:  25a07e048f ls-tree: fix \"--name-only\" and \"--long\" combined use bug\n    @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref\n      \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n      \t\t\t    N_(\"use full path names\"), 0),\n      \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n    +\n    + ## t/t3103-ls-tree-misc.sh ##\n    +@@ t/t3103-ls-tree-misc.sh: test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '\n    + \ttest_must_fail git ls-tree -r HEAD\n    + '\n    + \n    ++for opts in \\\n    ++\t\"--name-only --long\" \\\n    ++\t\"--name-status --long\"\n    ++do\n    ++\ttest_expect_success \"usage: incompatible options: $opts\" '\n    ++\t\ttest_expect_code 129 git ls-tree $opts $tree\n    ++    '\n    ++done\n    ++\n    + test_done\n 3:  41e8ed5047 !  8:  f449146b4d ls-tree: slightly refactor `show_tree()`\n    @@ builtin/ls-tree.c\n      \tNULL\n      };\n      \n    -+enum {\n    ++static enum mutx_option {\n     +\tMODE_UNSPECIFIED = 0,\n     +\tMODE_NAME_ONLY,\n     +\tMODE_LONG,\n    -+};\n    -+\n    -+static int cmdmode = MODE_UNSPECIFIED;\n    ++} cmdmode;\n     +\n     +static int parse_shown_fields(void)\n     +{\n    @@ builtin/ls-tree.c: static int show_recursive(const char *base, size_t baselen, c\n      \t\tconst char *pathname, unsigned mode, void *context)\n      {\n     @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strbuf *base,\n    - \tif (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))\n    - \t\treturn 0;\n    + \t\t\treturn recurse;\n    + \t}\n      \n     -\tif (!(ls_options & LS_NAME_ONLY)) {\n     -\t\tif (ls_options & LS_SHOW_SIZE) {\n 4:  46e10a5392 !  9:  80311adc7c ls-tree: introduce struct \"show_tree_data\"\n    @@ Commit message\n         \"show_tree()\". This commit is a pre-prepared commit for supporting\n         \"--format\" option and it does not affect any existing functionality.\n     \n    +    At the same time, we also move global \"shown_fields\" into \"show_tree_data\"\n    +    , the purpose of this is to avoid use both global variable and pass-around\n    +    struct.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n         Signed-off-by: Teng Long <dyroneteng@gmail.com>\n     \n      ## builtin/ls-tree.c ##\n    +@@ builtin/ls-tree.c: static int ls_options;\n    + static struct pathspec pathspec;\n    + static int chomp_prefix;\n    + static const char *ls_tree_prefix;\n    +-static unsigned int shown_fields;\n    + #define FIELD_PATH_NAME 1\n    + #define FIELD_SIZE (1 << 1)\n    + #define FIELD_OBJECT_NAME (1 << 2)\n     @@ builtin/ls-tree.c: static unsigned int shown_fields;\n      #define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n      #define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n    @@ builtin/ls-tree.c: static unsigned int shown_fields;\n     +\tconst struct object_id *oid;\n     +\tconst char *pathname;\n     +\tstruct strbuf *base;\n    ++\tunsigned int shown_fields;\n     +};\n     +\n      static const  char * const ls_tree_usage[] = {\n      \tN_(\"git ls-tree [<options>] <tree-ish> [<path>...]\"),\n      \tNULL\n    +@@ builtin/ls-tree.c: static enum mutx_option {\n    + \tMODE_LONG,\n    + } cmdmode;\n    + \n    +-static int parse_shown_fields(void)\n    ++static int parse_shown_fields(unsigned int *shown_fields)\n    + {\n    + \tif (cmdmode == MODE_NAME_ONLY) {\n    +-\t\tshown_fields = FIELD_PATH_NAME;\n    ++\t\t*shown_fields = FIELD_PATH_NAME;\n    + \t\treturn 0;\n    + \t}\n    + \n    + \tif (!ls_options || (ls_options & LS_RECURSIVE)\n    + \t    || (ls_options & LS_SHOW_TREES)\n    + \t    || (ls_options & LS_TREE_ONLY))\n    +-\t\tshown_fields = FIELD_DEFAULT;\n    ++\t\t*shown_fields = FIELD_DEFAULT;\n    + \tif (cmdmode == MODE_LONG)\n    +-\t\tshown_fields = FIELD_LONG_DEFAULT;\n    ++\t\t*shown_fields = FIELD_LONG_DEFAULT;\n    + \treturn 1;\n    + }\n    + \n     @@ builtin/ls-tree.c: static int show_recursive(const char *base, size_t baselen, const char *pathname\n      \treturn 0;\n      }\n    @@ builtin/ls-tree.c: static int show_recursive(const char *base, size_t baselen, c\n     -\tsize_t baselen = base->len;\n     +\tsize_t baselen = data->base->len;\n      \n    - \tif (shown_fields & FIELD_SIZE) {\n    +-\tif (shown_fields & FIELD_SIZE) {\n    ++\tif (data->shown_fields & FIELD_SIZE) {\n      \t\tchar size_text[24];\n     -\t\tif (type == OBJ_BLOB) {\n     +\t\tif (data->type == OBJ_BLOB) {\n    @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strb\n      \tint recurse = 0;\n      \tsize_t baselen;\n      \tenum object_type type = object_type(mode);\n    ++\tunsigned int shown_fields = *(unsigned int *)context;\n     +\tstruct show_tree_data data = {\n     +\t\t.mode = mode,\n     +\t\t.type = type,\n     +\t\t.oid = oid,\n     +\t\t.pathname = pathname,\n     +\t\t.base = base,\n    ++\t\t.shown_fields = shown_fields,\n     +\t};\n      \n    - \tif (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))\n    - \t\trecurse = READ_TREE_RECURSIVE;\n    + \tif (type == OBJ_BLOB) {\n    + \t\tif (ls_options & LS_TREE_ONLY)\n     @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strbuf *base,\n    + \t\treturn recurse;\n      \t}\n      \n    - \tif (shown_fields >= FIELD_DEFAULT)\n    +-\tif (shown_fields >= FIELD_DEFAULT)\n     -\t\tshow_default(oid, type, pathname, mode, base);\n    ++\tif (shown_fields>= FIELD_DEFAULT)\n     +\t\tshow_default(&data);\n      \n      \treturn recurse;\n      }\n    +@@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    + \tstruct object_id oid;\n    + \tstruct tree *tree;\n    + \tint i, full_tree = 0;\n    ++\tunsigned int shown_fields = 0;\n    + \tconst struct option ls_tree_options[] = {\n    + \t\tOPT_BIT('d', NULL, &ls_options, N_(\"only show trees\"),\n    + \t\t\tLS_TREE_ONLY),\n    +@@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    + \tif (get_oid(argv[0], &oid))\n    + \t\tdie(\"Not a valid object name %s\", argv[0]);\n    + \n    +-\tparse_shown_fields();\n    +-\n    + \t/*\n    + \t * show_recursive() rolls its own matching code and is\n    + \t * generally ignorant of 'struct pathspec'. The magic mask\n    +@@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    + \ttree = parse_tree_indirect(&oid);\n    + \tif (!tree)\n    + \t\tdie(\"not a tree object\");\n    +-\treturn !!read_tree(the_repository, tree,\n    +-\t\t\t   &pathspec, show_tree, NULL);\n    ++\n    ++\tparse_shown_fields(&shown_fields);\n    ++\n    ++\treturn !!read_tree(the_repository, tree, &pathspec, show_tree,\n    ++\t\t\t   &shown_fields);\n    + }\n 5:  c04320b801 = 10:  459080f549 cocci: allow padding with `strbuf_addf()`\n 6:  5936004f13 ! 11:  cb717d08be ls-tree.c: introduce \"--format\" option\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    ls-tree.c: introduce \"--format\" option\n    +    ls-tree: introduce \"--format\" option\n     \n         Add a --format option to ls-tree. It has an existing default output,\n         and then --long and --name-only options to emit the default output\n    @@ Documentation/git-ls-tree.txt: quoted as explained for the configuration variabl\n      Part of the linkgit:git[1] suite\n     \n      ## builtin/ls-tree.c ##\n    -@@ builtin/ls-tree.c: static unsigned int shown_fields;\n    +@@ builtin/ls-tree.c: static const char *ls_tree_prefix;\n      #define FIELD_MODE (1 << 4)\n      #define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n      #define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n    @@ builtin/ls-tree.c: static unsigned int shown_fields;\n      struct show_tree_data {\n      \tunsigned mode;\n      \tenum object_type type;\n    -@@ builtin/ls-tree.c: enum {\n    - \n    - static int cmdmode = MODE_UNSPECIFIED;\n    +@@ builtin/ls-tree.c: static enum mutx_option {\n    + \tMODE_LONG,\n    + } cmdmode;\n      \n     +static void expand_objectsize(struct strbuf *line, const struct object_id *oid,\n     +\t\t\t      const enum object_type type, unsigned int padded)\n    @@ builtin/ls-tree.c: enum {\n     +\tif (len)\n     +\t\treturn len;\n     +\tif (*start != '(')\n    -+\t\tdie(_(\"bad ls-tree format: as '%s'\"), start);\n    ++\t\tdie(_(\"bad ls-tree format: element '%s' does not start with '('\"), start);\n     +\n     +\tend = strchr(start + 1, ')');\n     +\tif (!end)\n    @@ builtin/ls-tree.c: enum {\n     +\treturn len;\n     +}\n     +\n    - static int parse_shown_fields(void)\n    + static int parse_shown_fields(unsigned int *shown_fields)\n      {\n      \tif (cmdmode == MODE_NAME_ONLY) {\n     @@ builtin/ls-tree.c: static int show_recursive(const char *base, size_t baselen, const char *pathname\n    @@ builtin/ls-tree.c: static int show_recursive(const char *base, size_t baselen, c\n      {\n      \tsize_t baselen = data->base->len;\n     @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    - \tstruct object_id oid;\n      \tstruct tree *tree;\n      \tint i, full_tree = 0;\n    + \tunsigned int shown_fields = 0;\n     +\tread_tree_fn_t fn = show_tree;\n      \tconst struct option ls_tree_options[] = {\n      \t\tOPT_BIT('d', NULL, &ls_options, N_(\"only show trees\"),\n    @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref\n      \t\tusage_with_options(ls_tree_usage, ls_tree_options);\n      \tif (get_oid(argv[0], &oid))\n     @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    - \ttree = parse_tree_indirect(&oid);\n    - \tif (!tree)\n    - \t\tdie(\"not a tree object\");\n    --\treturn !!read_tree(the_repository, tree,\n    --\t\t\t   &pathspec, show_tree, NULL);\n    + \n    + \tparse_shown_fields(&shown_fields);\n    + \n    +-\treturn !!read_tree(the_repository, tree, &pathspec, show_tree,\n    +-\t\t\t   &shown_fields);\n     +\t/*\n     +\t * The generic show_tree_fmt() is slower than show_tree(), so\n     +\t * take the fast path if possible.\n    @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref\n     +\t} else if (format)\n     +\t\tfn = show_tree_fmt;\n     +\n    -+\treturn !!read_tree(the_repository, tree, &pathspec, fn, NULL);\n    ++\treturn !!read_tree(the_repository, tree, &pathspec, fn, &shown_fields);\n      }\n     \n      ## t/t3104-ls-tree-format.sh (new) ##\n 7:  6d26497749 <  -:  ---------- ls-tree: introduce function \"fast_path()\"\n 8:  e6d98f2560 ! 12:  55b1c4379d ls-tree.c: support --object-only option for \"git-ls-tree\"\n    @@ Metadata\n     Author: Teng Long <dyroneteng@gmail.com>\n     \n      ## Commit message ##\n    -    ls-tree.c: support --object-only option for \"git-ls-tree\"\n    +    ls-tree: support --object-only option for \"git-ls-tree\"\n     \n    -    We usually pipe the output from `git ls-trees` to tools like\n    -    `sed` or `cut` when we only want to extract some fields.\n    +    '--object-only' is an alias for '--format=%(objectname)'. It cannot\n    +    be used together other format-altering options like '--name-only',\n    +    '--long' or '--format', they are mutually exclusive.\n     \n    -    When we want only the pathname component, we can pass\n    -    `--name-only` option to omit such a pipeline, but there are no\n    -    options for extracting other fields.\n    +    The \"--name-only\" option outputs <filepath> only. Likewise, <objectName>\n    +    is another high frequency used field, so implement '--object-only' option\n    +    will bring intuitive and clear semantics for this scenario. Using\n    +    '--format=%(objectname)' we can achieve a similar effect, but the former\n    +    is with a lower learning cost(without knowing the format requirement\n    +    of '--format' option).\n     \n    -    Teach the \"--object-only\" option to the command to only show the\n    -    object name. This option cannot be used together with\n    -    \"--name-only\" or \"--long\" , they are mutually exclusive (actually\n    -    \"--name-only\" and \"--long\" can be combined together before, this\n    -    commit by the way fix this bug).\n    +    Even so, if a user is prefer to use \"--format=%(objectname)\", this is entirely\n    +    welcome because they are not only equivalent in function, but also have almost\n    +    identical performance. The reason is this commit also add the specific of\n    +    \"--format=%(objectname)\" to the current fast-pathes (builtin formats) to\n    +    avoid running unnecessary parsing mechanisms.\n     \n    -    In terms of performance, there is no loss comparing to the\n    -    \"master\" (2ae0a9c), here are the\n    -    results of the performance tests in my environment based on linux\n    -    repository:\n    +    The following performance benchmarks are based on torvalds/linux.git:\n     \n    -        $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r HEAD\"\n    -        Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD\n    -        Time (mean ± σ):     105.8 ms ±   2.7 ms    [User: 85.7 ms, System: 20.0 ms]\n    -        Range (min … max):   101.5 ms … 111.3 ms    28 runs\n    +      When hit the fast-path:\n     \n    -        $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD\"\n    -        Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r HEAD\n    -        Time (mean ± σ):     105.0 ms ±   3.0 ms    [User: 83.7 ms, System: 21.2 ms]\n    -        Range (min … max):    99.3 ms … 109.5 ms    27 runs\n    +          Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --object-only HEAD\n    +            Time (mean ± σ):      83.6 ms ±   2.0 ms    [User: 59.4 ms, System: 24.1 ms]\n    +            Range (min … max):    80.4 ms …  87.2 ms    35 runs\n     \n    -        $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r -l HEAD\"\n    -        Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD\n    -        Time (mean ± σ):     337.4 ms ±  10.9 ms    [User: 308.3 ms, System: 29.0 ms]\n    -        Range (min … max):   323.0 ms … 355.0 ms    10 runs\n    +          Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(objectname)' HEAD\n    +            Time (mean ± σ):      84.1 ms ±   1.8 ms    [User: 61.7 ms, System: 22.3 ms]\n    +            Range (min … max):    80.9 ms …  87.5 ms    35 runs\n     \n    -        $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD\"\n    -        Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r -l HEAD\n    -        Time (mean ± σ):     337.6 ms ±   6.2 ms    [User: 309.4 ms, System: 28.1 ms]\n    -        Range (min … max):   330.4 ms … 349.9 ms    10 runs\n    +      But for a customized format, it will be slower:\n    +\n    +           Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='oid: %(objectname)' HEAD\n    +             Time (mean ± σ):      96.5 ms ±   2.5 ms    [User: 72.9 ms, System: 23.5 ms]\n    +             Range (min … max):    93.1 ms … 104.1 ms    31 runs\n     \n         Signed-off-by: Teng Long <dyroneteng@gmail.com>\n     \n    @@ Documentation/git-ls-tree.txt: OPTIONS\n     +--object-only::\n     +\tList only names of the objects, one per line. Cannot be combined\n     +\twith `--name-only` or `--name-status`.\n    ++\tThis is equivalent to specifying `--format='%(objectname)'`, but\n    ++\tfor both this option and that exact format the command takes a\n    ++\thand-optimized codepath instead of going through the generic\n    ++\tformatting mechanism.\n      \n      --abbrev[=<n>]::\n      \tInstead of showing the full 40-byte hexadecimal object\n    @@ builtin/ls-tree.c: static const char *format;\n      \tunsigned mode;\n      \tenum object_type type;\n     @@ builtin/ls-tree.c: static const  char * const ls_tree_usage[] = {\n    - enum {\n    + static enum mutx_option {\n      \tMODE_UNSPECIFIED = 0,\n      \tMODE_NAME_ONLY,\n     +\tMODE_OBJECT_ONLY,\n      \tMODE_LONG,\n    - };\n    + } cmdmode;\n      \n    -@@ builtin/ls-tree.c: static int fast_path(void){\n    - \t} else if (!strcmp(format, name_only_format)) {\n    - \t\tshown_fields = FIELD_PATH_NAME;\n    - \t\treturn 1;\n    -+\t} else if (!strcmp(format, object_only_format)) {\n    -+\t\tshown_fields = FIELD_OBJECT_NAME;\n    - \t}\n    - \treturn 0;\n    - }\n    -@@ builtin/ls-tree.c: static int parse_shown_fields(void)\n    - \t\tshown_fields = FIELD_PATH_NAME;\n    +@@ builtin/ls-tree.c: static int parse_shown_fields(unsigned int *shown_fields)\n    + \t\t*shown_fields = FIELD_PATH_NAME;\n      \t\treturn 0;\n      \t}\n     -\n     +\tif (cmdmode == MODE_OBJECT_ONLY) {\n    -+\t\tshown_fields = FIELD_OBJECT_NAME;\n    ++\t\t*shown_fields = FIELD_OBJECT_NAME;\n     +\t\treturn 0;\n     +\t}\n      \tif (!ls_options || (ls_options & LS_RECURSIVE)\n      \t    || (ls_options & LS_SHOW_TREES)\n      \t    || (ls_options & LS_TREE_ONLY))\n     @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strbuf *base,\n    - \tif (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))\n    - \t\treturn 0;\n    + \t\t\treturn recurse;\n    + \t}\n      \n     +\tif (shown_fields == FIELD_OBJECT_NAME) {\n     +\t\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n     +\t\treturn recurse;\n     +\t}\n    ++\n      \tif (shown_fields == FIELD_PATH_NAME) {\n      \t\tbaselen = base->len;\n      \t\tstrbuf_addstr(base, pathname);\n    @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref\n      \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n      \t\t\t    N_(\"use full path names\"), 0),\n      \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n    +@@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    + \t} else if (format && (!strcmp(format, name_only_format))) {\n    + \t\tshown_fields = FIELD_PATH_NAME;\n    + \t\tfn = show_tree;\n    ++\t} else if (format && (!strcmp(format, object_only_format))) {\n    ++\t\tshown_fields = FIELD_OBJECT_NAME;\n    ++\t\tfn = show_tree;\n    + \t} else if (format)\n    + \t\tfn = show_tree_fmt;\n    + \n    +\n    + ## t/t3103-ls-tree-misc.sh ##\n    +@@ t/t3103-ls-tree-misc.sh: test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '\n    + \n    + for opts in \\\n    + \t\"--name-only --long\" \\\n    +-\t\"--name-status --long\"\n    ++\t\"--name-status --long\" \\\n    ++\t\"--name-only --object-only\" \\\n    ++\t\"--name-status --object-only\" \\\n    ++\t\"--object-only --long\" \\\n    ++\t\"--object-only --format\"\n    + do\n    + \ttest_expect_success \"usage: incompatible options: $opts\" '\n    + \t\ttest_expect_code 129 git ls-tree $opts $tree\n    +     '\n    + done\n    +-\n    + test_done\n     \n      ## t/t3104-ls-tree-format.sh ##\n     @@ t/t3104-ls-tree-format.sh: test_expect_success 'ls-tree --format=<name-only-like>' '\n    @@ t/t3104-ls-tree-format.sh: test_expect_success 'ls-tree --format=<name-only-like\n     +\t\t\"%(objectname)\" \\\n     +\t\t\"--object-only\"\n     +'\n    ++\n    ++test_expect_success 'ls-tree --format=<object-only-like> --abbrev' '\n    ++\ttest_ls_tree_format \\\n    ++\t\t\"%(objectname)\" \\\n    ++\t\t\"--object-only --abbrev\" \\\n    ++\t\t\"--abbrev\"\n    ++'\n     +\n      test_expect_success 'ls-tree combine --format=<default-like> and -t' '\n      \ttest_ls_tree_format \\\n    @@ t/t3104-ls-tree-format.sh: test_expect_success 'ls-tree hit fast-path with --for\n     +\ttest_cmp expect actual\n     +'\n      test_done\n    -\n    - ## t/t3105-ls-tree-oid.sh (new) ##\n    -@@\n    -+#!/bin/sh\n    -+\n    -+test_description='git ls-tree objects handling.'\n    -+\n    -+. ./test-lib.sh\n    -+\n    -+test_expect_success 'setup' '\n    -+test_commit A &&\n    -+test_commit B &&\n    -+mkdir -p C &&\n    -+test_commit C/D.txt &&\n    -+find *.txt path* \\( -type f -o -type l \\) -print |\n    -+xargs git update-index --add &&\n    -+tree=$(git write-tree) &&\n    -+echo $tree\n    -+'\n    -+\n    -+test_expect_success 'usage: --object-only' '\n    -+git ls-tree --object-only $tree >current &&\n    -+git ls-tree $tree >result &&\n    -+cut -f1 result | cut -d \" \" -f3 >expected &&\n    -+test_cmp current expected\n    -+'\n    -+\n    -+test_expect_success 'usage: --object-only with -r' '\n    -+git ls-tree --object-only -r $tree >current &&\n    -+git ls-tree -r $tree >result &&\n    -+cut -f1 result | cut -d \" \" -f3 >expected &&\n    -+test_cmp current expected\n    -+'\n    -+\n    -+test_expect_success 'usage: --object-only with --abbrev' '\n    -+git ls-tree --object-only --abbrev=6 $tree >current &&\n    -+git ls-tree --abbrev=6 $tree >result &&\n    -+cut -f1 result | cut -d \" \" -f3 >expected &&\n    -+test_cmp current expected\n    -+'\n    -+\n    -+test_expect_success 'usage: incompatible options: --name-only with --object-only' '\n    -+test_expect_code 129 git ls-tree --object-only --name-only $tree\n    -+'\n    -+\n    -+test_expect_success 'usage: incompatible options: --name-status with --object-only' '\n    -+test_expect_code 129 git ls-tree --object-only --name-status $tree\n    -+'\n    -+\n    -+test_expect_success 'usage: incompatible options: --long with --object-only' '\n    -+test_expect_code 129 git ls-tree --object-only --long $tree\n    -+'\n    -+\n    -+test_done\n-- \n2.34.1.402.gef0f5bd184\n\n"},{"id":"450373","messageId":"2fcff7e0d40255e0be9496647787352dd0aeb784.1646390152.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1646390152.git.dyroneteng@gmail.com","subject":"[PATCH v12 01/12] ls-tree: remove commented-out code","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-04T10:42:27Z","receivedAt":"2022-03-04T10:43:09Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nRemove code added in f35a6d3bce7 (Teach core object handling functions\nabout gitlinks, 2007-04-09), later patched in 7d0b18a4da1 (Add output\nflushing before fork(), 2008-08-04), and then finally ending up in its\ncurrent form in d3bee161fef (tree.c: allow read_tree_recursive() to\ntraverse gitlink entries, 2009-01-25). All while being commented-out!\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 9 ---------\n 1 file changed, 9 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3a442631c7..5f7c84950c 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -69,15 +69,6 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tconst char *type = blob_type;\n \n \tif (S_ISGITLINK(mode)) {\n-\t\t/*\n-\t\t * Maybe we want to have some recursive version here?\n-\t\t *\n-\t\t * Something similar to this incomplete example:\n-\t\t *\n-\t\tif (show_subprojects(base, baselen, pathname))\n-\t\t\tretval = READ_TREE_RECURSIVE;\n-\t\t *\n-\t\t */\n \t\ttype = commit_type;\n \t} else if (S_ISDIR(mode)) {\n \t\tif (show_recursive(base->buf, base->len, pathname)) {\n-- \n2.34.1.402.gef0f5bd184\n\n"},{"id":"450374","messageId":"6fd1dd938303192d5b0a4cdc2914b2ff72cf5a3d.1646390152.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1646390152.git.dyroneteng@gmail.com","subject":"[PATCH v12 02/12] ls-tree: add missing braces to \"else\" arms","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-04T10:42:28Z","receivedAt":"2022-03-04T10:43:10Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nAdd missing {} to the \"else\" arms in show_tree() per the\nCodingGuidelines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 6 ++++--\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 5f7c84950c..0a28f32ccb 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -92,14 +92,16 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\t\telse\n \t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n \t\t\t\t\t\t  \"%\"PRIuMAX, (uintmax_t)size);\n-\t\t\t} else\n+\t\t\t} else {\n \t\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n+\t\t\t}\n \t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type,\n \t\t\t       find_unique_abbrev(oid, abbrev),\n \t\t\t       size_text);\n-\t\t} else\n+\t\t} else {\n \t\t\tprintf(\"%06o %s %s\\t\", mode, type,\n \t\t\t       find_unique_abbrev(oid, abbrev));\n+\t\t}\n \t}\n \tbaselen = base->len;\n \tstrbuf_addstr(base, pathname);\n-- \n2.34.1.402.gef0f5bd184\n\n"},{"id":"450375","messageId":"208654b5e2aebbe988b5a8f478869253a0166e9b.1646390152.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1646390152.git.dyroneteng@gmail.com","subject":"[PATCH v12 03/12] ls-tree: use \"enum object_type\", not {blob,tree,commit}_type","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-04T10:42:29Z","receivedAt":"2022-03-04T10:43:12Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nChange the ls-tree.c code to use type_name() on the enum instead of\nusing the string constants. This doesn't matter either way for\nperformance, but makes this a bit easier to read as we'll no longer\nneed a strcmp() here.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 0a28f32ccb..3f0225b097 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -66,17 +66,17 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n {\n \tint retval = 0;\n \tint baselen;\n-\tconst char *type = blob_type;\n+\tenum object_type type = OBJ_BLOB;\n \n \tif (S_ISGITLINK(mode)) {\n-\t\ttype = commit_type;\n+\t\ttype = OBJ_COMMIT;\n \t} else if (S_ISDIR(mode)) {\n \t\tif (show_recursive(base->buf, base->len, pathname)) {\n \t\t\tretval = READ_TREE_RECURSIVE;\n \t\t\tif (!(ls_options & LS_SHOW_TREES))\n \t\t\t\treturn retval;\n \t\t}\n-\t\ttype = tree_type;\n+\t\ttype = OBJ_TREE;\n \t}\n \telse if (ls_options & LS_TREE_ONLY)\n \t\treturn 0;\n@@ -84,7 +84,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tif (!(ls_options & LS_NAME_ONLY)) {\n \t\tif (ls_options & LS_SHOW_SIZE) {\n \t\t\tchar size_text[24];\n-\t\t\tif (!strcmp(type, blob_type)) {\n+\t\t\tif (type == OBJ_BLOB) {\n \t\t\t\tunsigned long size;\n \t\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n \t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n@@ -95,11 +95,11 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\t} else {\n \t\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n \t\t\t}\n-\t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type,\n+\t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n \t\t\t       find_unique_abbrev(oid, abbrev),\n \t\t\t       size_text);\n \t\t} else {\n-\t\t\tprintf(\"%06o %s %s\\t\", mode, type,\n+\t\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n \t\t\t       find_unique_abbrev(oid, abbrev));\n \t\t}\n \t}\n-- \n2.34.1.402.gef0f5bd184\n\n"},{"id":"450376","messageId":"2637464fd8f6b8bae6c681f20c10c9fc76dc6d6c.1646390152.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1646390152.git.dyroneteng@gmail.com","subject":"[PATCH v12 04/12] ls-tree: use \"size_t\", not \"int\" for \"struct strbuf\"'s \"len\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-04T10:42:30Z","receivedAt":"2022-03-04T10:43:17Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nThe \"struct strbuf\"'s \"len\" member is a \"size_t\", not an \"int\", so\nlet's change our corresponding types accordingly. This also changes\nthe \"len\" and \"speclen\" variables, which are likewise used to store\nthe return value of strlen(), which returns \"size_t\", not \"int\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3f0225b097..eecc7482d5 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -31,7 +31,7 @@ static const  char * const ls_tree_usage[] = {\n \tNULL\n };\n \n-static int show_recursive(const char *base, int baselen, const char *pathname)\n+static int show_recursive(const char *base, size_t baselen, const char *pathname)\n {\n \tint i;\n \n@@ -43,7 +43,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n \n \tfor (i = 0; i < pathspec.nr; i++) {\n \t\tconst char *spec = pathspec.items[i].match;\n-\t\tint len, speclen;\n+\t\tsize_t len, speclen;\n \n \t\tif (strncmp(base, spec, baselen))\n \t\t\tcontinue;\n@@ -65,7 +65,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\tconst char *pathname, unsigned mode, void *context)\n {\n \tint retval = 0;\n-\tint baselen;\n+\tsize_t baselen;\n \tenum object_type type = OBJ_BLOB;\n \n \tif (S_ISGITLINK(mode)) {\n-- \n2.34.1.402.gef0f5bd184\n\n"},{"id":"450377","messageId":"99e6d47108308b0bbaf406b439170de04e375c56.1646390152.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1646390152.git.dyroneteng@gmail.com","subject":"[PATCH v12 05/12] ls-tree: rename \"retval\" to \"recurse\" in \"show_tree()\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-04T10:42:31Z","receivedAt":"2022-03-04T10:43:26Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"The variable which \"show_tree()\" return is named \"retval\", a name that's\na little hard to understand. The commit rename \"retval\" to \"recurse\"\nwhich is a more meaningful name than before in the context. We do not\nneed to take a look at \"read_tree_at()\" in \"tree.c\" to make sure what\ndoes \"retval\" mean.\n\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex eecc7482d5..ef8c414f61 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -64,7 +64,7 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\tconst char *pathname, unsigned mode, void *context)\n {\n-\tint retval = 0;\n+\tint recurse = 0;\n \tsize_t baselen;\n \tenum object_type type = OBJ_BLOB;\n \n@@ -72,9 +72,9 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\ttype = OBJ_COMMIT;\n \t} else if (S_ISDIR(mode)) {\n \t\tif (show_recursive(base->buf, base->len, pathname)) {\n-\t\t\tretval = READ_TREE_RECURSIVE;\n+\t\t\trecurse = READ_TREE_RECURSIVE;\n \t\t\tif (!(ls_options & LS_SHOW_TREES))\n-\t\t\t\treturn retval;\n+\t\t\t\treturn recurse;\n \t\t}\n \t\ttype = OBJ_TREE;\n \t}\n@@ -109,7 +109,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n \t\t\t\t   stdout, line_termination);\n \tstrbuf_setlen(base, baselen);\n-\treturn retval;\n+\treturn recurse;\n }\n \n int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n-- \n2.34.1.402.gef0f5bd184\n\n"},{"id":"450378","messageId":"a8d9b78dea10af317b59ed2749b0732926bf1192.1646390152.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1646390152.git.dyroneteng@gmail.com","subject":"[PATCH v12 06/12] ls-tree: simplify nesting if/else logic in \"show_tree()\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-04T10:42:32Z","receivedAt":"2022-03-04T10:43:28Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"Use the object_type() function to determine the object type from the\n\"mode\" passed to us by read_tree(), instead of doing so with the S_*()\nmacros.\n\nSigned-off-by: Teng Long <dyronetengb@gmail.com>\n---\n builtin/ls-tree.c | 21 +++++++++------------\n 1 file changed, 9 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex ef8c414f61..d4be71bad2 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -66,20 +66,17 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n {\n \tint recurse = 0;\n \tsize_t baselen;\n-\tenum object_type type = OBJ_BLOB;\n+\tenum object_type type = object_type(mode);\n \n-\tif (S_ISGITLINK(mode)) {\n-\t\ttype = OBJ_COMMIT;\n-\t} else if (S_ISDIR(mode)) {\n-\t\tif (show_recursive(base->buf, base->len, pathname)) {\n-\t\t\trecurse = READ_TREE_RECURSIVE;\n-\t\t\tif (!(ls_options & LS_SHOW_TREES))\n-\t\t\t\treturn recurse;\n-\t\t}\n-\t\ttype = OBJ_TREE;\n+\tif (type == OBJ_BLOB) {\n+\t\tif (ls_options & LS_TREE_ONLY)\n+\t\t\treturn 0;\n+\t} else if (type == OBJ_TREE &&\n+\t\t   show_recursive(base->buf, base->len, pathname)) {\n+\t\trecurse = READ_TREE_RECURSIVE;\n+\t\tif (!(ls_options & LS_SHOW_TREES))\n+\t\t\treturn recurse;\n \t}\n-\telse if (ls_options & LS_TREE_ONLY)\n-\t\treturn 0;\n \n \tif (!(ls_options & LS_NAME_ONLY)) {\n \t\tif (ls_options & LS_SHOW_SIZE) {\n-- \n2.34.1.402.gef0f5bd184\n\n"},{"id":"450379","messageId":"25a07e048f25e0efdd2f8690f2e6967352a20e9b.1646390152.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1646390152.git.dyroneteng@gmail.com","subject":"[PATCH v12 07/12] ls-tree: fix \"--name-only\" and \"--long\" combined use bug","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-04T10:42:33Z","receivedAt":"2022-03-04T10:43:32Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"If we execute \"git ls-tree\" with combined \"--name-only\" and \"--long\"\n, only the pathname will be printed, the size is omitted (the original\ndiscoverer was Peff in [1]).\n\nThis commit fix this issue by using `OPT_CMDMODE()` instead to make both\nof them mutually exclusive.\n\n[1] https://public-inbox.org/git/YZK0MKCYAJmG+pSU@coredump.intra.peff.net/\n\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c       | 12 ++++++------\n t/t3103-ls-tree-misc.sh |  9 +++++++++\n 2 files changed, 15 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex d4be71bad2..7be4c13228 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -123,12 +123,12 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\t\tLS_SHOW_TREES),\n \t\tOPT_SET_INT('z', NULL, &line_termination,\n \t\t\t    N_(\"terminate entries with NUL byte\"), 0),\n-\t\tOPT_BIT('l', \"long\", &ls_options, N_(\"include object size\"),\n-\t\t\tLS_SHOW_SIZE),\n-\t\tOPT_BIT(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n-\t\t\tLS_NAME_ONLY),\n-\t\tOPT_BIT(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n-\t\t\tLS_NAME_ONLY),\n+\t\tOPT_CMDMODE('l', \"long\", &ls_options, N_(\"include object size\"),\n+\t\t\t    LS_SHOW_SIZE),\n+\t\tOPT_CMDMODE(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n+\t\t\t    LS_NAME_ONLY),\n+\t\tOPT_CMDMODE(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n+\t\t\t    LS_NAME_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n \t\t\t    N_(\"use full path names\"), 0),\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\ndiff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh\nindex d18ba1bd84..d9d7fa932f 100755\n--- a/t/t3103-ls-tree-misc.sh\n+++ b/t/t3103-ls-tree-misc.sh\n@@ -23,4 +23,13 @@ test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '\n \ttest_must_fail git ls-tree -r HEAD\n '\n \n+for opts in \\\n+\t\"--name-only --long\" \\\n+\t\"--name-status --long\"\n+do\n+\ttest_expect_success \"usage: incompatible options: $opts\" '\n+\t\ttest_expect_code 129 git ls-tree $opts $tree\n+    '\n+done\n+\n test_done\n-- \n2.34.1.402.gef0f5bd184\n\n"},{"id":"450380","messageId":"f449146b4d3cbe0b75fc4a4d4638be821db533be.1646390152.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1646390152.git.dyroneteng@gmail.com","subject":"[PATCH v12 08/12] ls-tree: slightly refactor `show_tree()`","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-04T10:42:34Z","receivedAt":"2022-03-04T10:43:39Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"This is a non-functional change, we use a new int \"shown_fields\" to mark\nwhich columns to output, and `parse_shown_fields()` to calculate the\nvalue of \"shown_fields\".\n\nThis has the advantage of making the show_tree logic simpler and more\nreadable, as well as making it easier to extend new options (for example,\nif we want to add a \"--object-only\" option, we just need to add a similar\n\"if (shown_fields == FIELD_OBJECT_NAME)\" short-circuit logic in\n\"show_tree()\").\n\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 124 ++++++++++++++++++++++++++++++++--------------\n 1 file changed, 87 insertions(+), 37 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 7be4c13228..c70e31ac3d 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -16,21 +16,51 @@\n \n static int line_termination = '\\n';\n #define LS_RECURSIVE 1\n-#define LS_TREE_ONLY 2\n-#define LS_SHOW_TREES 4\n-#define LS_NAME_ONLY 8\n-#define LS_SHOW_SIZE 16\n+#define LS_TREE_ONLY (1 << 1)\n+#define LS_SHOW_TREES (1 << 2)\n+#define LS_NAME_ONLY (1 << 3)\n+#define LS_SHOW_SIZE (1 << 4)\n static int abbrev;\n static int ls_options;\n static struct pathspec pathspec;\n static int chomp_prefix;\n static const char *ls_tree_prefix;\n+static unsigned int shown_fields;\n+#define FIELD_PATH_NAME 1\n+#define FIELD_SIZE (1 << 1)\n+#define FIELD_OBJECT_NAME (1 << 2)\n+#define FIELD_TYPE (1 << 3)\n+#define FIELD_MODE (1 << 4)\n+#define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n+#define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n \n static const  char * const ls_tree_usage[] = {\n \tN_(\"git ls-tree [<options>] <tree-ish> [<path>...]\"),\n \tNULL\n };\n \n+static enum mutx_option {\n+\tMODE_UNSPECIFIED = 0,\n+\tMODE_NAME_ONLY,\n+\tMODE_LONG,\n+} cmdmode;\n+\n+static int parse_shown_fields(void)\n+{\n+\tif (cmdmode == MODE_NAME_ONLY) {\n+\t\tshown_fields = FIELD_PATH_NAME;\n+\t\treturn 0;\n+\t}\n+\n+\tif (!ls_options || (ls_options & LS_RECURSIVE)\n+\t    || (ls_options & LS_SHOW_TREES)\n+\t    || (ls_options & LS_TREE_ONLY))\n+\t\tshown_fields = FIELD_DEFAULT;\n+\tif (cmdmode == MODE_LONG)\n+\t\tshown_fields = FIELD_LONG_DEFAULT;\n+\treturn 1;\n+}\n+\n static int show_recursive(const char *base, size_t baselen, const char *pathname)\n {\n \tint i;\n@@ -61,6 +91,39 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n \treturn 0;\n }\n \n+static int show_default(const struct object_id *oid, enum object_type type,\n+\t\t\tconst char *pathname, unsigned mode,\n+\t\t\tstruct strbuf *base)\n+{\n+\tsize_t baselen = base->len;\n+\n+\tif (shown_fields & FIELD_SIZE) {\n+\t\tchar size_text[24];\n+\t\tif (type == OBJ_BLOB) {\n+\t\t\tunsigned long size;\n+\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n+\t\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n+\t\t\telse\n+\t\t\t\txsnprintf(size_text, sizeof(size_text),\n+\t\t\t\t\t  \"%\" PRIuMAX, (uintmax_t)size);\n+\t\t} else {\n+\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n+\t\t}\n+\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n+\t\tfind_unique_abbrev(oid, abbrev), size_text);\n+\t} else {\n+\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n+\t\tfind_unique_abbrev(oid, abbrev));\n+\t}\n+\tbaselen = base->len;\n+\tstrbuf_addstr(base, pathname);\n+\twrite_name_quoted_relative(base->buf,\n+\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n+\t\t\t\t   line_termination);\n+\tstrbuf_setlen(base, baselen);\n+\treturn 1;\n+}\n+\n static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\tconst char *pathname, unsigned mode, void *context)\n {\n@@ -78,34 +141,19 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\treturn recurse;\n \t}\n \n-\tif (!(ls_options & LS_NAME_ONLY)) {\n-\t\tif (ls_options & LS_SHOW_SIZE) {\n-\t\t\tchar size_text[24];\n-\t\t\tif (type == OBJ_BLOB) {\n-\t\t\t\tunsigned long size;\n-\t\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n-\t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n-\t\t\t\t\t\t  \"BAD\");\n-\t\t\t\telse\n-\t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n-\t\t\t\t\t\t  \"%\"PRIuMAX, (uintmax_t)size);\n-\t\t\t} else {\n-\t\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n-\t\t\t}\n-\t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n-\t\t\t       find_unique_abbrev(oid, abbrev),\n-\t\t\t       size_text);\n-\t\t} else {\n-\t\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n-\t\t\t       find_unique_abbrev(oid, abbrev));\n-\t\t}\n+\tif (shown_fields == FIELD_PATH_NAME) {\n+\t\tbaselen = base->len;\n+\t\tstrbuf_addstr(base, pathname);\n+\t\twrite_name_quoted_relative(base->buf,\n+\t\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n+\t\t\t\t\t   stdout, line_termination);\n+\t\tstrbuf_setlen(base, baselen);\n+\t\treturn recurse;\n \t}\n-\tbaselen = base->len;\n-\tstrbuf_addstr(base, pathname);\n-\twrite_name_quoted_relative(base->buf,\n-\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n-\t\t\t\t   stdout, line_termination);\n-\tstrbuf_setlen(base, baselen);\n+\n+\tif (shown_fields >= FIELD_DEFAULT)\n+\t\tshow_default(oid, type, pathname, mode, base);\n+\n \treturn recurse;\n }\n \n@@ -123,12 +171,12 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\t\tLS_SHOW_TREES),\n \t\tOPT_SET_INT('z', NULL, &line_termination,\n \t\t\t    N_(\"terminate entries with NUL byte\"), 0),\n-\t\tOPT_CMDMODE('l', \"long\", &ls_options, N_(\"include object size\"),\n-\t\t\t    LS_SHOW_SIZE),\n-\t\tOPT_CMDMODE(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n-\t\t\t    LS_NAME_ONLY),\n-\t\tOPT_CMDMODE(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n-\t\t\t    LS_NAME_ONLY),\n+\t\tOPT_CMDMODE('l', \"long\", &cmdmode, N_(\"include object size\"),\n+\t\t\t    MODE_LONG),\n+\t\tOPT_CMDMODE(0, \"name-only\", &cmdmode, N_(\"list only filenames\"),\n+\t\t\t    MODE_NAME_ONLY),\n+\t\tOPT_CMDMODE(0, \"name-status\", &cmdmode, N_(\"list only filenames\"),\n+\t\t\t    MODE_NAME_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n \t\t\t    N_(\"use full path names\"), 0),\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n@@ -159,6 +207,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tif (get_oid(argv[0], &oid))\n \t\tdie(\"Not a valid object name %s\", argv[0]);\n \n+\tparse_shown_fields();\n+\n \t/*\n \t * show_recursive() rolls its own matching code and is\n \t * generally ignorant of 'struct pathspec'. The magic mask\n-- \n2.34.1.402.gef0f5bd184\n\n"},{"id":"450381","messageId":"80311adc7c3630cadac275dca70b6168516e6d21.1646390152.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1646390152.git.dyroneteng@gmail.com","subject":"[PATCH v12 09/12] ls-tree: introduce struct \"show_tree_data\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-04T10:42:35Z","receivedAt":"2022-03-04T10:43:42Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\n\"show_tree_data\" is a struct that packages the necessary fields for\n\"show_tree()\". This commit is a pre-prepared commit for supporting\n\"--format\" option and it does not affect any existing functionality.\n\nAt the same time, we also move global \"shown_fields\" into \"show_tree_data\"\n, the purpose of this is to avoid use both global variable and pass-around\nstruct.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 69 +++++++++++++++++++++++++++++------------------\n 1 file changed, 43 insertions(+), 26 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex c70e31ac3d..04aefc6106 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -25,7 +25,6 @@ static int ls_options;\n static struct pathspec pathspec;\n static int chomp_prefix;\n static const char *ls_tree_prefix;\n-static unsigned int shown_fields;\n #define FIELD_PATH_NAME 1\n #define FIELD_SIZE (1 << 1)\n #define FIELD_OBJECT_NAME (1 << 2)\n@@ -34,6 +33,15 @@ static unsigned int shown_fields;\n #define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n #define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n \n+struct show_tree_data {\n+\tunsigned mode;\n+\tenum object_type type;\n+\tconst struct object_id *oid;\n+\tconst char *pathname;\n+\tstruct strbuf *base;\n+\tunsigned int shown_fields;\n+};\n+\n static const  char * const ls_tree_usage[] = {\n \tN_(\"git ls-tree [<options>] <tree-ish> [<path>...]\"),\n \tNULL\n@@ -45,19 +53,19 @@ static enum mutx_option {\n \tMODE_LONG,\n } cmdmode;\n \n-static int parse_shown_fields(void)\n+static int parse_shown_fields(unsigned int *shown_fields)\n {\n \tif (cmdmode == MODE_NAME_ONLY) {\n-\t\tshown_fields = FIELD_PATH_NAME;\n+\t\t*shown_fields = FIELD_PATH_NAME;\n \t\treturn 0;\n \t}\n \n \tif (!ls_options || (ls_options & LS_RECURSIVE)\n \t    || (ls_options & LS_SHOW_TREES)\n \t    || (ls_options & LS_TREE_ONLY))\n-\t\tshown_fields = FIELD_DEFAULT;\n+\t\t*shown_fields = FIELD_DEFAULT;\n \tif (cmdmode == MODE_LONG)\n-\t\tshown_fields = FIELD_LONG_DEFAULT;\n+\t\t*shown_fields = FIELD_LONG_DEFAULT;\n \treturn 1;\n }\n \n@@ -91,17 +99,15 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n \treturn 0;\n }\n \n-static int show_default(const struct object_id *oid, enum object_type type,\n-\t\t\tconst char *pathname, unsigned mode,\n-\t\t\tstruct strbuf *base)\n+static int show_default(struct show_tree_data *data)\n {\n-\tsize_t baselen = base->len;\n+\tsize_t baselen = data->base->len;\n \n-\tif (shown_fields & FIELD_SIZE) {\n+\tif (data->shown_fields & FIELD_SIZE) {\n \t\tchar size_text[24];\n-\t\tif (type == OBJ_BLOB) {\n+\t\tif (data->type == OBJ_BLOB) {\n \t\t\tunsigned long size;\n-\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n+\t\t\tif (oid_object_info(the_repository, data->oid, &size) == OBJ_BAD)\n \t\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n \t\t\telse\n \t\t\t\txsnprintf(size_text, sizeof(size_text),\n@@ -109,18 +115,18 @@ static int show_default(const struct object_id *oid, enum object_type type,\n \t\t} else {\n \t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n \t\t}\n-\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n-\t\tfind_unique_abbrev(oid, abbrev), size_text);\n+\t\tprintf(\"%06o %s %s %7s\\t\", data->mode, type_name(data->type),\n+\t\tfind_unique_abbrev(data->oid, abbrev), size_text);\n \t} else {\n-\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n-\t\tfind_unique_abbrev(oid, abbrev));\n+\t\tprintf(\"%06o %s %s\\t\", data->mode, type_name(data->type),\n+\t\tfind_unique_abbrev(data->oid, abbrev));\n \t}\n-\tbaselen = base->len;\n-\tstrbuf_addstr(base, pathname);\n-\twrite_name_quoted_relative(base->buf,\n+\tbaselen = data->base->len;\n+\tstrbuf_addstr(data->base, data->pathname);\n+\twrite_name_quoted_relative(data->base->buf,\n \t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n \t\t\t\t   line_termination);\n-\tstrbuf_setlen(base, baselen);\n+\tstrbuf_setlen(data->base, baselen);\n \treturn 1;\n }\n \n@@ -130,6 +136,15 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tint recurse = 0;\n \tsize_t baselen;\n \tenum object_type type = object_type(mode);\n+\tunsigned int shown_fields = *(unsigned int *)context;\n+\tstruct show_tree_data data = {\n+\t\t.mode = mode,\n+\t\t.type = type,\n+\t\t.oid = oid,\n+\t\t.pathname = pathname,\n+\t\t.base = base,\n+\t\t.shown_fields = shown_fields,\n+\t};\n \n \tif (type == OBJ_BLOB) {\n \t\tif (ls_options & LS_TREE_ONLY)\n@@ -151,8 +166,8 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\treturn recurse;\n \t}\n \n-\tif (shown_fields >= FIELD_DEFAULT)\n-\t\tshow_default(oid, type, pathname, mode, base);\n+\tif (shown_fields>= FIELD_DEFAULT)\n+\t\tshow_default(&data);\n \n \treturn recurse;\n }\n@@ -162,6 +177,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tstruct object_id oid;\n \tstruct tree *tree;\n \tint i, full_tree = 0;\n+\tunsigned int shown_fields = 0;\n \tconst struct option ls_tree_options[] = {\n \t\tOPT_BIT('d', NULL, &ls_options, N_(\"only show trees\"),\n \t\t\tLS_TREE_ONLY),\n@@ -207,8 +223,6 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tif (get_oid(argv[0], &oid))\n \t\tdie(\"Not a valid object name %s\", argv[0]);\n \n-\tparse_shown_fields();\n-\n \t/*\n \t * show_recursive() rolls its own matching code and is\n \t * generally ignorant of 'struct pathspec'. The magic mask\n@@ -225,6 +239,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \ttree = parse_tree_indirect(&oid);\n \tif (!tree)\n \t\tdie(\"not a tree object\");\n-\treturn !!read_tree(the_repository, tree,\n-\t\t\t   &pathspec, show_tree, NULL);\n+\n+\tparse_shown_fields(&shown_fields);\n+\n+\treturn !!read_tree(the_repository, tree, &pathspec, show_tree,\n+\t\t\t   &shown_fields);\n }\n-- \n2.34.1.402.gef0f5bd184\n\n"},{"id":"450382","messageId":"459080f549e040d5e2396533abff76276b60c274.1646390152.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1646390152.git.dyroneteng@gmail.com","subject":"[PATCH v12 10/12] cocci: allow padding with `strbuf_addf()`","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-04T10:42:36Z","receivedAt":"2022-03-04T10:43:53Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nA convenient way to pad strings is to use something like\n`strbuf_addf(&buf, \"%20s\", \"Hello, world!\")`.\n\nHowever, the Coccinelle rule that forbids a format `\"%s\"` with a\nconstant string argument cast too wide a net, and also forbade such\npadding.\n\nThe original rule was introduced by commit:\n\n    28c23cd4c39 (strbuf.cocci: suggest strbuf_addbuf() to add one strbuf to an other, 2019-01-25)\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n contrib/coccinelle/strbuf.cocci | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/coccinelle/strbuf.cocci b/contrib/coccinelle/strbuf.cocci\nindex d9ada69b43..0970d98ad7 100644\n--- a/contrib/coccinelle/strbuf.cocci\n+++ b/contrib/coccinelle/strbuf.cocci\n@@ -15,7 +15,7 @@ constant fmt !~ \"%\";\n @@\n expression E;\n struct strbuf SB;\n-format F =~ \"s\";\n+format F =~ \"^s$\";\n @@\n - strbuf_addf(E, \"%@F@\", SB.buf);\n + strbuf_addbuf(E, &SB);\n@@ -23,7 +23,7 @@ format F =~ \"s\";\n @@\n expression E;\n struct strbuf *SBP;\n-format F =~ \"s\";\n+format F =~ \"^s$\";\n @@\n - strbuf_addf(E, \"%@F@\", SBP->buf);\n + strbuf_addbuf(E, SBP);\n@@ -44,7 +44,7 @@ struct strbuf *SBP;\n \n @@\n expression E1, E2;\n-format F =~ \"s\";\n+format F =~ \"^s$\";\n @@\n - strbuf_addf(E1, \"%@F@\", E2);\n + strbuf_addstr(E1, E2);\n-- \n2.34.1.402.gef0f5bd184\n\n"},{"id":"450383","messageId":"cb717d08be87e3239117c6c667cb32caabaad33d.1646390152.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1646390152.git.dyroneteng@gmail.com","subject":"[PATCH v12 11/12] ls-tree: introduce \"--format\" option","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-04T10:42:37Z","receivedAt":"2022-03-04T10:43:57Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nAdd a --format option to ls-tree. It has an existing default output,\nand then --long and --name-only options to emit the default output\nalong with the objectsize and, or to only emit object paths.\n\nRather than add --type-only, --object-only etc. we can just support a\n--format using a strbuf_expand() similar to \"for-each-ref\n--format\". We might still add such options in the future for\nconvenience.\n\nThe --format implementation is slower than the existing code, but this\nchange does not cause any performance regressions. We'll leave the\nexisting show_tree() unchanged, and only run show_tree_fmt() in if\na --format different than the hardcoded built-in ones corresponding to\nthe existing modes is provided.\n\nI.e. something like the \"--long\" output would be much slower with\nthis, mainly due to how we need to allocate various things to do with\nquote.c instead of spewing the output directly to stdout.\n\nThe new option of '--format' comes from Ævar Arnfjörð Bjarmasonn's\nidea and suggestion, this commit makes modifications in terms of the\noriginal discussion on community [1].\n\nHere is the statistics about performance tests:\n\n1. Default format (hitten the builtin formats):\n\n    \"git ls-tree <tree-ish>\" vs \"--format='%(mode) %(type) %(object)%x09%(file)'\"\n\n    $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r HEAD\"\n    Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD\n    Time (mean ± σ):     105.2 ms ±   3.3 ms    [User: 84.3 ms, System: 20.8 ms]\n    Range (min … max):    99.2 ms … 113.2 ms    28 runs\n\n    $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object)%x09%(file)'  HEAD\"\n    Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object)%x09%(file)'  HEAD\n    Time (mean ± σ):     106.4 ms ±   2.7 ms    [User: 86.1 ms, System: 20.2 ms]\n    Range (min … max):   100.2 ms … 110.5 ms    29 runs\n\n2. Default format includes object size (hitten the builtin formats):\n\n    \"git ls-tree -l <tree-ish>\" vs \"--format='%(mode) %(type) %(object) %(size:padded)%x09%(file)'\"\n\n    $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r -l HEAD\"\n    Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD\n    Time (mean ± σ):     335.1 ms ±   6.5 ms    [User: 304.6 ms, System: 30.4 ms]\n    Range (min … max):   327.5 ms … 348.4 ms    10 runs\n\n    $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object) %(size:padded)%x09%(file)'  HEAD\"\n    Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object) %(size:padded)%x09%(file)'  HEAD\n    Time (mean ± σ):     337.2 ms ±   8.2 ms    [User: 309.2 ms, System: 27.9 ms]\n    Range (min … max):   328.8 ms … 349.4 ms    10 runs\n\nLinks:\n\t[1] https://public-inbox.org/git/RFC-patch-6.7-eac299f06ff-20211217T131635Z-avarab@gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n Documentation/git-ls-tree.txt |  59 ++++++++++++++--\n builtin/ls-tree.c             | 129 +++++++++++++++++++++++++++++++++-\n t/t3104-ls-tree-format.sh     |  81 +++++++++++++++++++++\n 3 files changed, 262 insertions(+), 7 deletions(-)\n create mode 100755 t/t3104-ls-tree-format.sh\n\ndiff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt\nindex db02d6d79a..db29a9efb5 100644\n--- a/Documentation/git-ls-tree.txt\n+++ b/Documentation/git-ls-tree.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git ls-tree' [-d] [-r] [-t] [-l] [-z]\n-\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]]\n+\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n \t    <tree-ish> [<path>...]\n \n DESCRIPTION\n@@ -74,6 +74,16 @@ OPTIONS\n \tDo not limit the listing to the current working directory.\n \tImplies --full-name.\n \n+--format=<format>::\n+\tA string that interpolates `%(fieldname)` from the result\n+\tbeing shown. It also interpolates `%%` to `%`, and\n+\t`%xx` where `xx` are hex digits interpolates to character\n+\twith hex code `xx`; for example `%00` interpolates to\n+\t`\\0` (NUL), `%09` to `\\t` (TAB) and `%0a` to `\\n` (LF).\n+\tWhen specified, `--format` cannot be combined with other\n+\tformat-altering options, including `--long`, `--name-only`\n+\tand `--object-only`.\n+\n [<path>...]::\n \tWhen paths are given, show them (note that this isn't really raw\n \tpathnames, but rather a list of patterns to match).  Otherwise\n@@ -82,16 +92,29 @@ OPTIONS\n \n Output Format\n -------------\n-        <mode> SP <type> SP <object> TAB <file>\n+\n+The output format of `ls-tree` is determined by either the `--format`\n+option, or other format-altering options such as `--name-only` etc.\n+(see `--format` above).\n+\n+The use of certain `--format` directives is equivalent to using those\n+options, but invoking the full formatting machinery can be slower than\n+using an appropriate formatting option.\n+\n+In cases where the `--format` would exactly map to an existing option\n+`ls-tree` will use the appropriate faster path. Thus the default format\n+is equivalent to:\n+\n+        %(objectmode) %(objecttype) %(objectname)%x09%(path)\n \n This output format is compatible with what `--index-info --stdin` of\n 'git update-index' expects.\n \n When the `-l` option is used, format changes to\n \n-        <mode> SP <type> SP <object> SP <object size> TAB <file>\n+        %(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\n \n-Object size identified by <object> is given in bytes, and right-justified\n+Object size identified by <objectname> is given in bytes, and right-justified\n with minimum width of 7 characters.  Object size is given only for blobs\n (file) entries; for other entries `-` character is used in place of size.\n \n@@ -100,6 +123,34 @@ quoted as explained for the configuration variable `core.quotePath`\n (see linkgit:git-config[1]).  Using `-z` the filename is output\n verbatim and the line is terminated by a NUL byte.\n \n+Customized format:\n+\n+It is possible to print in a custom format by using the `--format` option,\n+which is able to interpolate different fields using a `%(fieldname)` notation.\n+For example, if you only care about the \"objectname\" and \"path\" fields, you\n+can execute with a specific \"--format\" like\n+\n+        git ls-tree --format='%(objectname) %(path)' <tree-ish>\n+\n+FIELD NAMES\n+-----------\n+\n+Various values from structured fields can be used to interpolate\n+into the resulting output. For each outputing line, the following\n+names can be used:\n+\n+objectmode::\n+\tThe mode of the object.\n+objecttype::\n+\tThe type of the object (`blob` or `tree`).\n+objectname::\n+\tThe name of the object.\n+objectsize[:padded]::\n+\tThe size of the object (\"-\" if it's a tree).\n+\tIt also supports a padded format of size with \"%(size:padded)\".\n+path::\n+\tThe pathname of the object.\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 04aefc6106..a46fa73bb3 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -32,7 +32,10 @@ static const char *ls_tree_prefix;\n #define FIELD_MODE (1 << 4)\n #define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n #define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n-\n+static const char *format;\n+static const char *default_format = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\";\n+static const char *long_format = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\";\n+static const char *name_only_format = \"%(path)\";\n struct show_tree_data {\n \tunsigned mode;\n \tenum object_type type;\n@@ -53,6 +56,72 @@ static enum mutx_option {\n \tMODE_LONG,\n } cmdmode;\n \n+static void expand_objectsize(struct strbuf *line, const struct object_id *oid,\n+\t\t\t      const enum object_type type, unsigned int padded)\n+{\n+\tif (type == OBJ_BLOB) {\n+\t\tunsigned long size;\n+\t\tif (oid_object_info(the_repository, oid, &size) < 0)\n+\t\t\tdie(_(\"could not get object info about '%s'\"),\n+\t\t\t    oid_to_hex(oid));\n+\t\tif (padded)\n+\t\t\tstrbuf_addf(line, \"%7\"PRIuMAX, (uintmax_t)size);\n+\t\telse\n+\t\t\tstrbuf_addf(line, \"%\"PRIuMAX, (uintmax_t)size);\n+\t} else if (padded) {\n+\t\tstrbuf_addf(line, \"%7s\", \"-\");\n+\t} else {\n+\t\tstrbuf_addstr(line, \"-\");\n+\t}\n+}\n+\n+static size_t expand_show_tree(struct strbuf *sb, const char *start,\n+\t\t\t       void *context)\n+{\n+\tstruct show_tree_data *data = context;\n+\tconst char *end;\n+\tconst char *p;\n+\tunsigned int errlen;\n+\tsize_t len = strbuf_expand_literal_cb(sb, start, NULL);\n+\n+\tif (len)\n+\t\treturn len;\n+\tif (*start != '(')\n+\t\tdie(_(\"bad ls-tree format: element '%s' does not start with '('\"), start);\n+\n+\tend = strchr(start + 1, ')');\n+\tif (!end)\n+\t\tdie(_(\"bad ls-tree format: element '%s' does not end in ')'\"), start);\n+\n+\tlen = end - start + 1;\n+\tif (skip_prefix(start, \"(objectmode)\", &p)) {\n+\t\tstrbuf_addf(sb, \"%06o\", data->mode);\n+\t} else if (skip_prefix(start, \"(objecttype)\", &p)) {\n+\t\tstrbuf_addstr(sb, type_name(data->type));\n+\t} else if (skip_prefix(start, \"(objectsize:padded)\", &p)) {\n+\t\texpand_objectsize(sb, data->oid, data->type, 1);\n+\t} else if (skip_prefix(start, \"(objectsize)\", &p)) {\n+\t\texpand_objectsize(sb, data->oid, data->type, 0);\n+\t} else if (skip_prefix(start, \"(objectname)\", &p)) {\n+\t\tstrbuf_add_unique_abbrev(sb, data->oid, abbrev);\n+\t} else if (skip_prefix(start, \"(path)\", &p)) {\n+\t\tconst char *name = data->base->buf;\n+\t\tconst char *prefix = chomp_prefix ? ls_tree_prefix : NULL;\n+\t\tstruct strbuf quoted = STRBUF_INIT;\n+\t\tstruct strbuf sbuf = STRBUF_INIT;\n+\t\tstrbuf_addstr(data->base, data->pathname);\n+\t\tname = relative_path(data->base->buf, prefix, &sbuf);\n+\t\tquote_c_style(name, &quoted, NULL, 0);\n+\t\tstrbuf_addbuf(sb, &quoted);\n+\t\tstrbuf_release(&sbuf);\n+\t\tstrbuf_release(&quoted);\n+\t} else {\n+\t\terrlen = (unsigned long)len;\n+\t\tdie(_(\"bad ls-tree format: %%%.*s\"), errlen, start);\n+\t}\n+\treturn len;\n+}\n+\n static int parse_shown_fields(unsigned int *shown_fields)\n {\n \tif (cmdmode == MODE_NAME_ONLY) {\n@@ -99,6 +168,38 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n \treturn 0;\n }\n \n+static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,\n+\t\t\t const char *pathname, unsigned mode, void *context)\n+{\n+\tsize_t baselen;\n+\tint recurse = 0;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tenum object_type type = object_type(mode);\n+\n+\tstruct show_tree_data data = {\n+\t\t.mode = mode,\n+\t\t.type = type,\n+\t\t.oid = oid,\n+\t\t.pathname = pathname,\n+\t\t.base = base,\n+\t};\n+\n+\tif (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))\n+\t\trecurse = READ_TREE_RECURSIVE;\n+\tif (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES))\n+\t\treturn recurse;\n+\tif (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))\n+\t\treturn 0;\n+\n+\tbaselen = base->len;\n+\tstrbuf_expand(&sb, format, expand_show_tree, &data);\n+\tstrbuf_addch(&sb, line_termination);\n+\tfwrite(sb.buf, sb.len, 1, stdout);\n+\tstrbuf_release(&sb);\n+\tstrbuf_setlen(base, baselen);\n+\treturn recurse;\n+}\n+\n static int show_default(struct show_tree_data *data)\n {\n \tsize_t baselen = data->base->len;\n@@ -178,6 +279,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tstruct tree *tree;\n \tint i, full_tree = 0;\n \tunsigned int shown_fields = 0;\n+\tread_tree_fn_t fn = show_tree;\n \tconst struct option ls_tree_options[] = {\n \t\tOPT_BIT('d', NULL, &ls_options, N_(\"only show trees\"),\n \t\t\tLS_TREE_ONLY),\n@@ -198,6 +300,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n \t\t\t N_(\"list entire tree; not just current directory \"\n \t\t\t    \"(implies --full-name)\")),\n+\t\tOPT_STRING_F(0, \"format\", &format, N_(\"format\"),\n+\t\t\t\t\t N_(\"format to use for the output\"),\n+\t\t\t\t\t PARSE_OPT_NONEG),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_END()\n \t};\n@@ -218,6 +323,10 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t    ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))\n \t\tls_options |= LS_SHOW_TREES;\n \n+\tif (format && cmdmode)\n+\t\tusage_msg_opt(\n+\t\t\t_(\"--format can't be combined with other format-altering options\"),\n+\t\t\tls_tree_usage, ls_tree_options);\n \tif (argc < 1)\n \t\tusage_with_options(ls_tree_usage, ls_tree_options);\n \tif (get_oid(argv[0], &oid))\n@@ -242,6 +351,20 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \n \tparse_shown_fields(&shown_fields);\n \n-\treturn !!read_tree(the_repository, tree, &pathspec, show_tree,\n-\t\t\t   &shown_fields);\n+\t/*\n+\t * The generic show_tree_fmt() is slower than show_tree(), so\n+\t * take the fast path if possible.\n+\t */\n+\tif (format && (!strcmp(format, default_format))) {\n+\t\tfn = show_tree;\n+\t} else if (format && (!strcmp(format, long_format))) {\n+\t\tshown_fields = shown_fields | FIELD_SIZE;\n+\t\tfn = show_tree;\n+\t} else if (format && (!strcmp(format, name_only_format))) {\n+\t\tshown_fields = FIELD_PATH_NAME;\n+\t\tfn = show_tree;\n+\t} else if (format)\n+\t\tfn = show_tree_fmt;\n+\n+\treturn !!read_tree(the_repository, tree, &pathspec, fn, &shown_fields);\n }\ndiff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh\nnew file mode 100755\nindex 0000000000..e08c83dc47\n--- /dev/null\n+++ b/t/t3104-ls-tree-format.sh\n@@ -0,0 +1,81 @@\n+#!/bin/sh\n+\n+test_description='ls-tree --format'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n+. ./test-lib.sh\n+\n+test_expect_success 'ls-tree --format usage' '\n+\ttest_expect_code 129 git ls-tree --format=fmt -l HEAD &&\n+\ttest_expect_code 129 git ls-tree --format=fmt --name-only HEAD &&\n+\ttest_expect_code 129 git ls-tree --format=fmt --name-status HEAD\n+'\n+\n+test_expect_success 'setup' '\n+\tmkdir dir &&\n+\ttest_commit dir/sub-file &&\n+\ttest_commit top-file\n+'\n+\n+test_ls_tree_format () {\n+\tformat=$1 &&\n+\topts=$2 &&\n+\tfmtopts=$3 &&\n+\tshift 2 &&\n+\tgit ls-tree $opts -r HEAD >expect.raw &&\n+\tsed \"s/^/> /\" >expect <expect.raw &&\n+\tgit ls-tree --format=\"> $format\" -r $fmtopts HEAD >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'ls-tree --format=<default-like>' '\n+\ttest_ls_tree_format \\\n+\t\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\t\"\"\n+'\n+\n+test_expect_success 'ls-tree --format=<long-like>' '\n+\ttest_ls_tree_format \\\n+\t\t\"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\" \\\n+\t\t\"--long\"\n+'\n+\n+test_expect_success 'ls-tree --format=<name-only-like>' '\n+\ttest_ls_tree_format \\\n+\t\t\"%(path)\" \\\n+\t\t\"--name-only\"\n+'\n+\n+test_expect_success 'ls-tree combine --format=<default-like> and -t' '\n+\ttest_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"-t\" \\\n+\t\"-t\"\n+'\n+\n+test_expect_success 'ls-tree combine --format=<default-like> and --full-name' '\n+\ttest_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"--full-name\" \\\n+\t\"--full-name\"\n+'\n+\n+test_expect_success 'ls-tree combine --format=<default-like> and --full-tree' '\n+\ttest_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"--full-tree\" \\\n+\t\"--full-tree\"\n+'\n+\n+test_expect_success 'ls-tree hit fast-path with --format=<default-like>' '\n+\tgit ls-tree -r HEAD >expect &&\n+\tgit ls-tree --format=\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" -r HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'ls-tree hit fast-path with --format=<name-only-like>' '\n+\tgit ls-tree -r --name-only HEAD >expect &&\n+\tgit ls-tree --format=\"%(path)\" -r HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+test_done\n-- \n2.34.1.402.gef0f5bd184\n\n"},{"id":"450384","messageId":"55b1c4379d51c8254ac71a7b897d81bddd863143.1646390152.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1646390152.git.dyroneteng@gmail.com","subject":"[PATCH v12 12/12] ls-tree: support --object-only option for \"git-ls-tree\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-04T10:42:38Z","receivedAt":"2022-03-04T10:43:59Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"'--object-only' is an alias for '--format=%(objectname)'. It cannot\nbe used together other format-altering options like '--name-only',\n'--long' or '--format', they are mutually exclusive.\n\nThe \"--name-only\" option outputs <filepath> only. Likewise, <objectName>\nis another high frequency used field, so implement '--object-only' option\nwill bring intuitive and clear semantics for this scenario. Using\n'--format=%(objectname)' we can achieve a similar effect, but the former\nis with a lower learning cost(without knowing the format requirement\nof '--format' option).\n\nEven so, if a user is prefer to use \"--format=%(objectname)\", this is entirely\nwelcome because they are not only equivalent in function, but also have almost\nidentical performance. The reason is this commit also add the specific of\n\"--format=%(objectname)\" to the current fast-pathes (builtin formats) to\navoid running unnecessary parsing mechanisms.\n\nThe following performance benchmarks are based on torvalds/linux.git:\n\n  When hit the fast-path:\n\n      Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --object-only HEAD\n        Time (mean ± σ):      83.6 ms ±   2.0 ms    [User: 59.4 ms, System: 24.1 ms]\n        Range (min … max):    80.4 ms …  87.2 ms    35 runs\n\n      Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(objectname)' HEAD\n        Time (mean ± σ):      84.1 ms ±   1.8 ms    [User: 61.7 ms, System: 22.3 ms]\n        Range (min … max):    80.9 ms …  87.5 ms    35 runs\n\n  But for a customized format, it will be slower:\n\n       Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='oid: %(objectname)' HEAD\n         Time (mean ± σ):      96.5 ms ±   2.5 ms    [User: 72.9 ms, System: 23.5 ms]\n  \t Range (min … max):    93.1 ms … 104.1 ms    31 runs\n\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n Documentation/git-ls-tree.txt | 11 ++++++++++-\n builtin/ls-tree.c             | 18 +++++++++++++++++-\n t/t3103-ls-tree-misc.sh       |  7 +++++--\n t/t3104-ls-tree-format.sh     | 19 +++++++++++++++++++\n 4 files changed, 51 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt\nindex db29a9efb5..1aea998e72 100644\n--- a/Documentation/git-ls-tree.txt\n+++ b/Documentation/git-ls-tree.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git ls-tree' [-d] [-r] [-t] [-l] [-z]\n-\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n+\t    [--name-only] [--name-status] [--object-only] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n \t    <tree-ish> [<path>...]\n \n DESCRIPTION\n@@ -59,6 +59,15 @@ OPTIONS\n --name-only::\n --name-status::\n \tList only filenames (instead of the \"long\" output), one per line.\n+\tCannot be combined with `--object-only`.\n+\n+--object-only::\n+\tList only names of the objects, one per line. Cannot be combined\n+\twith `--name-only` or `--name-status`.\n+\tThis is equivalent to specifying `--format='%(objectname)'`, but\n+\tfor both this option and that exact format the command takes a\n+\thand-optimized codepath instead of going through the generic\n+\tformatting mechanism.\n \n --abbrev[=<n>]::\n \tInstead of showing the full 40-byte hexadecimal object\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex a46fa73bb3..7da21d9963 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -20,6 +20,7 @@ static int line_termination = '\\n';\n #define LS_SHOW_TREES (1 << 2)\n #define LS_NAME_ONLY (1 << 3)\n #define LS_SHOW_SIZE (1 << 4)\n+#define LS_OBJECT_ONLY (1 << 5)\n static int abbrev;\n static int ls_options;\n static struct pathspec pathspec;\n@@ -36,6 +37,7 @@ static const char *format;\n static const char *default_format = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\";\n static const char *long_format = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\";\n static const char *name_only_format = \"%(path)\";\n+static const char *object_only_format = \"%(objectname)\";\n struct show_tree_data {\n \tunsigned mode;\n \tenum object_type type;\n@@ -53,6 +55,7 @@ static const  char * const ls_tree_usage[] = {\n static enum mutx_option {\n \tMODE_UNSPECIFIED = 0,\n \tMODE_NAME_ONLY,\n+\tMODE_OBJECT_ONLY,\n \tMODE_LONG,\n } cmdmode;\n \n@@ -128,7 +131,10 @@ static int parse_shown_fields(unsigned int *shown_fields)\n \t\t*shown_fields = FIELD_PATH_NAME;\n \t\treturn 0;\n \t}\n-\n+\tif (cmdmode == MODE_OBJECT_ONLY) {\n+\t\t*shown_fields = FIELD_OBJECT_NAME;\n+\t\treturn 0;\n+\t}\n \tif (!ls_options || (ls_options & LS_RECURSIVE)\n \t    || (ls_options & LS_SHOW_TREES)\n \t    || (ls_options & LS_TREE_ONLY))\n@@ -257,6 +263,11 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\treturn recurse;\n \t}\n \n+\tif (shown_fields == FIELD_OBJECT_NAME) {\n+\t\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n+\t\treturn recurse;\n+\t}\n+\n \tif (shown_fields == FIELD_PATH_NAME) {\n \t\tbaselen = base->len;\n \t\tstrbuf_addstr(base, pathname);\n@@ -295,6 +306,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\t\t    MODE_NAME_ONLY),\n \t\tOPT_CMDMODE(0, \"name-status\", &cmdmode, N_(\"list only filenames\"),\n \t\t\t    MODE_NAME_ONLY),\n+\t\tOPT_CMDMODE(0, \"object-only\", &cmdmode, N_(\"list only objects\"),\n+\t\t\t    MODE_OBJECT_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n \t\t\t    N_(\"use full path names\"), 0),\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n@@ -363,6 +376,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t} else if (format && (!strcmp(format, name_only_format))) {\n \t\tshown_fields = FIELD_PATH_NAME;\n \t\tfn = show_tree;\n+\t} else if (format && (!strcmp(format, object_only_format))) {\n+\t\tshown_fields = FIELD_OBJECT_NAME;\n+\t\tfn = show_tree;\n \t} else if (format)\n \t\tfn = show_tree_fmt;\n \ndiff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh\nindex d9d7fa932f..d979c0df5d 100755\n--- a/t/t3103-ls-tree-misc.sh\n+++ b/t/t3103-ls-tree-misc.sh\n@@ -25,11 +25,14 @@ test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '\n \n for opts in \\\n \t\"--name-only --long\" \\\n-\t\"--name-status --long\"\n+\t\"--name-status --long\" \\\n+\t\"--name-only --object-only\" \\\n+\t\"--name-status --object-only\" \\\n+\t\"--object-only --long\" \\\n+\t\"--object-only --format\"\n do\n \ttest_expect_success \"usage: incompatible options: $opts\" '\n \t\ttest_expect_code 129 git ls-tree $opts $tree\n     '\n done\n-\n test_done\ndiff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh\nindex e08c83dc47..b86941c202 100755\n--- a/t/t3104-ls-tree-format.sh\n+++ b/t/t3104-ls-tree-format.sh\n@@ -46,6 +46,19 @@ test_expect_success 'ls-tree --format=<name-only-like>' '\n \t\t\"--name-only\"\n '\n \n+test_expect_success 'ls-tree --format=<object-only-like>' '\n+\ttest_ls_tree_format \\\n+\t\t\"%(objectname)\" \\\n+\t\t\"--object-only\"\n+'\n+\n+test_expect_success 'ls-tree --format=<object-only-like> --abbrev' '\n+\ttest_ls_tree_format \\\n+\t\t\"%(objectname)\" \\\n+\t\t\"--object-only --abbrev\" \\\n+\t\t\"--abbrev\"\n+'\n+\n test_expect_success 'ls-tree combine --format=<default-like> and -t' '\n \ttest_ls_tree_format \\\n \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n@@ -78,4 +91,10 @@ test_expect_success 'ls-tree hit fast-path with --format=<name-only-like>' '\n \tgit ls-tree --format=\"%(path)\" -r HEAD >actual &&\n \ttest_cmp expect actual\n '\n+\n+test_expect_success 'ls-tree hit fast-path with --format=<object-only-like>' '\n+\tgit ls-tree -r --object-only HEAD >expect &&\n+\tgit ls-tree --format=\"%(objectname)\" -r HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n test_done\n-- \n2.34.1.402.gef0f5bd184\n\n"},{"id":"451007","messageId":"cover-0.7-00000000000-20220310T134811Z-avarab@gmail.com","threadId":"57383","inReplyTo":"cover.1646390152.git.dyroneteng@gmail.com","subject":"[RFC/REVIEW 0/7] fixups/suggestions/musings for tl/ls-tree-oid-only","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-10T13:56:56Z","receivedAt":"2022-03-10T13:57:12Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Re\nhttps://lore.kernel.org/git/20220308080551.18538-1-dyroneteng@gmail.com/\nsorry about the delay in reviewing this. I did look it over & come up\nwith the below almost a week ago, but forgot/didn't have time to turn\nit into inline comments.\n\nI don't think all of these need to be squashed or fixed up into the\nproposed series, but are just various small issues/questions I came up\nwith while reviewing it. Brief notes below:\n\nÆvar Arnfjörð Bjarmason (7):\n  ls-tree tests: add tests for --name-status\n\nOptional, but maybe a good idea to add to the series. I noticed the\nzero --name-status test coverage when testing it.\n\n  ls-tree tests: exhaustively test fast & slow path for --format\n\nProbably a good idea to add to it. I.e. the tests for the --format\ndrifted from the RFC version I had so that we weren't testing the\noptimized v.s. non-optimized path.\n\n  ls-tree: remove dead labels\n\nRemoves dead code in the proposed series (from an earlier iteration?)\n\n  ls-tree: remove unused \"MODE_UNSPECIFIED\"\n\nThe MODE_UNSPECIFIED is also dead code in this series, but...\n\n  ls-tree: detect and error on --name-only --name-status\n\nWe should die on --name-status --name-only (two option synonyms), but\ndon't on \"master\", so. This does make some subsequent code here\nslightly simpler though...\n\n  ls-tree: remove FIELD_*, just use MODE_*\n\nThe main outstanding thing for me when trying to understand the code\nin this series is why we ended up with FIELD_* defines *and* the\nMODE_*. This moves to just using MODE_*. I think it makes things\neasier to understand. I.e. it's one less thing to pass around, and\nwhat was in parse_shown_fields() is just embedded in show_tree() now\nwithout the indirection.\n\nThere's also a change there to add an array of format <-> fast path fn\nmappings, instead of the current if/else if chain with harcdoded\nstrcmp().\n\n  ls-tree: split up \"fast path\" callbacks\n\nMaybe a good idea, maybe not. Splits up all the \"fast path\" callbacks\ninto individual functions, instead of a monolithic show_default().\n\nI think the resulting code is easier to read, since it's clear what\ndata we need to set up for what, but maybe we're off into the weeds\nhere...\n\n builtin/ls-tree.c          | 279 ++++++++++++++++++++++---------------\n t/t3101-ls-tree-dirname.sh |  55 ++++----\n t/t3103-ls-tree-misc.sh    |  15 +-\n t/t3104-ls-tree-format.sh  |  89 +++++-------\n 4 files changed, 235 insertions(+), 203 deletions(-)\n\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"451008","messageId":"patch-1.7-953a20f5844-20220310T134811Z-avarab@gmail.com","threadId":"57383","inReplyTo":"cover-0.7-00000000000-20220310T134811Z-avarab@gmail.com","subject":"[RFC/REVIEW 1/7] ls-tree tests: add tests for --name-status","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-10T13:56:57Z","receivedAt":"2022-03-10T13:57:12Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The --name-status synonym for --name-only added in\nc639a5548a5 (ls-tree: --name-only, 2005-12-01) had no tests, let's\nmake sure it works the same way as its sibling.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3101-ls-tree-dirname.sh | 55 ++++++++++++++++++++------------------\n 1 file changed, 29 insertions(+), 26 deletions(-)\n\ndiff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh\nindex 05fde642259..217006d1bfb 100755\n--- a/t/t3101-ls-tree-dirname.sh\n+++ b/t/t3101-ls-tree-dirname.sh\n@@ -201,31 +201,34 @@ EOF\n \ttest_cmp expected check\n '\n \n-test_expect_success 'ls-tree --name-only' '\n-\tgit ls-tree --name-only $tree >current &&\n-\tcat >expected <<\\EOF &&\n-1.txt\n-2.txt\n-path0\n-path1\n-path2\n-path3\n-EOF\n-\ttest_output\n-'\n-\n-test_expect_success 'ls-tree --name-only -r' '\n-\tgit ls-tree --name-only -r $tree >current &&\n-\tcat >expected <<\\EOF &&\n-1.txt\n-2.txt\n-path0/a/b/c/1.txt\n-path1/b/c/1.txt\n-path2/1.txt\n-path3/1.txt\n-path3/2.txt\n-EOF\n-\ttest_output\n-'\n+for opt in --name-only --name-status\n+do\n+\ttest_expect_success \"ls-tree $opt\" '\n+\t\tgit ls-tree $opt $tree >current &&\n+\t\tcat >expected <<-\\EOF &&\n+\t\t1.txt\n+\t\t2.txt\n+\t\tpath0\n+\t\tpath1\n+\t\tpath2\n+\t\tpath3\n+\t\tEOF\n+\t\ttest_output\n+\t'\n+\n+\ttest_expect_success \"ls-tree $opt -r\" '\n+\t\tgit ls-tree $opt -r $tree >current &&\n+\t\tcat >expected <<-\\EOF &&\n+\t\t1.txt\n+\t\t2.txt\n+\t\tpath0/a/b/c/1.txt\n+\t\tpath1/b/c/1.txt\n+\t\tpath2/1.txt\n+\t\tpath3/1.txt\n+\t\tpath3/2.txt\n+\t\tEOF\n+\t\ttest_output\n+\t'\n+done\n \n test_done\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"451009","messageId":"patch-2.7-782be37daa6-20220310T134811Z-avarab@gmail.com","threadId":"57383","inReplyTo":"cover-0.7-00000000000-20220310T134811Z-avarab@gmail.com","subject":"[RFC/REVIEW 2/7] ls-tree tests: exhaustively test fast & slow path for --format","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-10T13:56:58Z","receivedAt":"2022-03-10T13:57:18Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change these tests adapted from my initial version in [1] to\nexhaustively test both the fast and slow paths of --format.\n\nIn [1] there was a \"GIT_TEST_LS_TREE_FORMAT_BACKEND\" variable to\nensure that we had test coverage for passing tests that would\notherwise use show_tree() through show_tree_fmt(), and thus that the\nformatting mechanism could handle all the same cases as the\nnon-formatting options.\n\nSomewhere in subsequent re-rolls of that we seem to have drifted away\nfrom what the goal of these tests should be. We're trying to ensure\ncorrectness of show_tree_fmt(). We can't tell if we \"hit [the]\nfast-path\" here, and instead of having an explicit test for that, we\ncan just add it to something our \"test_ls_tree_format\" tests for.\n\n1. https://lore.kernel.org/git/RFC-patch-6.7-eac299f06ff-20211217T131635Z-avarab@gmail.com/\n2. https://lore.kernel.org/git/cb717d08be87e3239117c6c667cb32caabaad33d.1646390152.git.dyroneteng@gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3104-ls-tree-format.sh | 89 +++++++++++++++------------------------\n 1 file changed, 33 insertions(+), 56 deletions(-)\n\ndiff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh\nindex b86941c2024..12105115eaf 100755\n--- a/t/t3104-ls-tree-format.sh\n+++ b/t/t3104-ls-tree-format.sh\n@@ -22,79 +22,56 @@ test_ls_tree_format () {\n \topts=$2 &&\n \tfmtopts=$3 &&\n \tshift 2 &&\n-\tgit ls-tree $opts -r HEAD >expect.raw &&\n-\tsed \"s/^/> /\" >expect <expect.raw &&\n-\tgit ls-tree --format=\"> $format\" -r $fmtopts HEAD >actual &&\n-\ttest_cmp expect actual\n+\n+\ttest_expect_success \"ls-tree '--format=<$format>' is like options '$opts $fmtopts'\" '\n+\t\tgit ls-tree $opts -r HEAD >expect &&\n+\t\tgit ls-tree --format=\"$format\" -r $fmtopts HEAD >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+\n+\ttest_expect_success \"ls-tree '--format=<$format>' on optimized v.s. non-optimized path\" '\n+\t\tgit ls-tree --format=\"$format\" -r $fmtopts HEAD >expect &&\n+\t\tgit ls-tree --format=\"> $format\" -r $fmtopts HEAD >actual.raw &&\n+\t\tsed \"s/^> //\" >actual <actual.raw &&\n+\t\ttest_cmp expect actual\n+\t'\n }\n \n-test_expect_success 'ls-tree --format=<default-like>' '\n-\ttest_ls_tree_format \\\n-\t\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n-\t\t\"\"\n-'\n+test_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"\"\n \n-test_expect_success 'ls-tree --format=<long-like>' '\n-\ttest_ls_tree_format \\\n-\t\t\"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\" \\\n-\t\t\"--long\"\n-'\n+test_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\" \\\n+\t\"--long\"\n \n-test_expect_success 'ls-tree --format=<name-only-like>' '\n-\ttest_ls_tree_format \\\n-\t\t\"%(path)\" \\\n-\t\t\"--name-only\"\n-'\n+test_ls_tree_format \\\n+\t\"%(path)\" \\\n+\t\"--name-only\"\n \n-test_expect_success 'ls-tree --format=<object-only-like>' '\n-\ttest_ls_tree_format \\\n-\t\t\"%(objectname)\" \\\n-\t\t\"--object-only\"\n-'\n+test_ls_tree_format \\\n+\t\"%(objectname)\" \\\n+\t\"--object-only\"\n+\n+test_ls_tree_format \\\n+\t\"%(objectname)\" \\\n+\t\"--object-only --abbrev\" \\\n+\t\"--abbrev\"\n \n-test_expect_success 'ls-tree --format=<object-only-like> --abbrev' '\n-\ttest_ls_tree_format \\\n-\t\t\"%(objectname)\" \\\n-\t\t\"--object-only --abbrev\" \\\n-\t\t\"--abbrev\"\n-'\n \n-test_expect_success 'ls-tree combine --format=<default-like> and -t' '\n-\ttest_ls_tree_format \\\n+test_ls_tree_format \\\n \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n \t\"-t\" \\\n \t\"-t\"\n-'\n \n-test_expect_success 'ls-tree combine --format=<default-like> and --full-name' '\n-\ttest_ls_tree_format \\\n+test_ls_tree_format \\\n \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n \t\"--full-name\" \\\n \t\"--full-name\"\n-'\n \n-test_expect_success 'ls-tree combine --format=<default-like> and --full-tree' '\n-\ttest_ls_tree_format \\\n+test_ls_tree_format \\\n \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n \t\"--full-tree\" \\\n \t\"--full-tree\"\n-'\n-\n-test_expect_success 'ls-tree hit fast-path with --format=<default-like>' '\n-\tgit ls-tree -r HEAD >expect &&\n-\tgit ls-tree --format=\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" -r HEAD >actual &&\n-\ttest_cmp expect actual\n-'\n-\n-test_expect_success 'ls-tree hit fast-path with --format=<name-only-like>' '\n-\tgit ls-tree -r --name-only HEAD >expect &&\n-\tgit ls-tree --format=\"%(path)\" -r HEAD >actual &&\n-\ttest_cmp expect actual\n-'\n \n-test_expect_success 'ls-tree hit fast-path with --format=<object-only-like>' '\n-\tgit ls-tree -r --object-only HEAD >expect &&\n-\tgit ls-tree --format=\"%(objectname)\" -r HEAD >actual &&\n-\ttest_cmp expect actual\n-'\n test_done\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"451010","messageId":"patch-5.7-2d2f0fcec62-20220310T134811Z-avarab@gmail.com","threadId":"57383","inReplyTo":"cover-0.7-00000000000-20220310T134811Z-avarab@gmail.com","subject":"[RFC/REVIEW 5/7] ls-tree: detect and error on --name-only --name-status","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-10T13:57:01Z","receivedAt":"2022-03-10T13:57:20Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The --name-only and --name-status options are synonyms, but let's\ndetect and error if both are provided.\n\nIn addition let's add explicit --format tests for the combination of\nthese various options.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c       | 11 ++++++++++-\n t/t3103-ls-tree-misc.sh | 15 +++++++++------\n 2 files changed, 19 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 95816709d38..528b4e5b5f6 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -52,6 +52,7 @@ static const  char * const ls_tree_usage[] = {\n static enum ls_tree_cmdmode {\n \tMODE_LONG = 1,\n \tMODE_NAME_ONLY,\n+\tMODE_NAME_STATUS,\n \tMODE_OBJECT_ONLY,\n } cmdmode;\n \n@@ -301,7 +302,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\tOPT_CMDMODE(0, \"name-only\", &cmdmode, N_(\"list only filenames\"),\n \t\t\t    MODE_NAME_ONLY),\n \t\tOPT_CMDMODE(0, \"name-status\", &cmdmode, N_(\"list only filenames\"),\n-\t\t\t    MODE_NAME_ONLY),\n+\t\t\t    MODE_NAME_STATUS),\n \t\tOPT_CMDMODE(0, \"object-only\", &cmdmode, N_(\"list only objects\"),\n \t\t\t    MODE_OBJECT_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n@@ -327,6 +328,14 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\tls_tree_prefix = prefix = NULL;\n \t\tchomp_prefix = 0;\n \t}\n+\t/*\n+\t * We wanted to detect conflicts between --name-only and\n+\t * --name-status, but once we're done with that subsequent\n+\t * code should only need to check the primary name.\n+\t */\n+\tif (cmdmode == MODE_NAME_STATUS)\n+\t\tcmdmode = MODE_NAME_ONLY;\n+\n \t/* -d -r should imply -t, but -d by itself should not have to. */\n \tif ( (LS_TREE_ONLY|LS_RECURSIVE) ==\n \t    ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))\ndiff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh\nindex ce39158338a..81c63439623 100755\n--- a/t/t3103-ls-tree-misc.sh\n+++ b/t/t3103-ls-tree-misc.sh\n@@ -24,15 +24,18 @@ test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '\n '\n \n for opts in \\\n-\t\"--name-only --long\" \\\n-\t\"--name-status --long\" \\\n-\t\"--name-only --object-only\" \\\n+\t\"--long --name-only\" \\\n+\t\"--name-only --name-status\" \\\n \t\"--name-status --object-only\" \\\n-\t\"--object-only --long\" \\\n-\t\" --object-only --format\"\n+\t\"--object-only --long\"\n do\n \ttest_expect_success \"usage: incompatible options: $opts\" '\n \t\ttest_expect_code 129 git ls-tree $opts $tree\n-    '\n+\t'\n+\n+\tone_opt=$(echo \"$opts\" | cut -d' '  -f1)\n+\ttest_expect_success \"usage: incompatible options: $one_opt and --format\" '\n+\t\ttest_expect_code 129 git ls-tree $one_opt --format=fmt $tree\n+\t'\n done\n test_done\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"451011","messageId":"patch-4.7-05959f0fe4c-20220310T134811Z-avarab@gmail.com","threadId":"57383","inReplyTo":"cover-0.7-00000000000-20220310T134811Z-avarab@gmail.com","subject":"[RFC/REVIEW 4/7] ls-tree: remove unused \"MODE_UNSPECIFIED\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-10T13:57:00Z","receivedAt":"2022-03-10T13:57:21Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove the \"MODE_UNSPECIFIED\" added in a preceding commit. We don't\nneed it, instead let's start the \"command mode\" enum at 1, as\ne.g. builtin/help.c does for its similar pattern.\n\nIn addition let's change this from \"mutx_option\" to a more obvious\n\"ls-tree_cmdmode\", even if the type name isn't used anywhere else. The\nprevious name sounded as though it had something to do with\na (thread?) mutex.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 5 ++---\n 1 file changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex f04733a9be1..95816709d38 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -49,11 +49,10 @@ static const  char * const ls_tree_usage[] = {\n \tNULL\n };\n \n-static enum mutx_option {\n-\tMODE_UNSPECIFIED = 0,\n+static enum ls_tree_cmdmode {\n+\tMODE_LONG = 1,\n \tMODE_NAME_ONLY,\n \tMODE_OBJECT_ONLY,\n-\tMODE_LONG,\n } cmdmode;\n \n static void expand_objectsize(struct strbuf *line, const struct object_id *oid,\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"451012","messageId":"patch-3.7-cc2989f72ba-20220310T134811Z-avarab@gmail.com","threadId":"57383","inReplyTo":"cover-0.7-00000000000-20220310T134811Z-avarab@gmail.com","subject":"[RFC/REVIEW 3/7] ls-tree: remove dead labels","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-10T13:56:59Z","receivedAt":"2022-03-10T13:57:23Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"These have not been used since a preceding \"ls-tree: slightly refactor\n`show_tree()`\" commit, and should be squashed into that.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 3 ---\n 1 file changed, 3 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 23ec2f0fe76..f04733a9be1 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -18,9 +18,6 @@ static int line_termination = '\\n';\n #define LS_RECURSIVE 1\n #define LS_TREE_ONLY (1 << 1)\n #define LS_SHOW_TREES (1 << 2)\n-#define LS_NAME_ONLY (1 << 3)\n-#define LS_SHOW_SIZE (1 << 4)\n-#define LS_OBJECT_ONLY (1 << 5)\n static int abbrev;\n static int ls_options;\n static struct pathspec pathspec;\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"451013","messageId":"patch-6.7-619a77c1b08-20220310T134811Z-avarab@gmail.com","threadId":"57383","inReplyTo":"cover-0.7-00000000000-20220310T134811Z-avarab@gmail.com","subject":"[RFC/REVIEW 6/7] ls-tree: remove FIELD_*, just use MODE_*","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-10T13:57:02Z","receivedAt":"2022-03-10T13:57:24Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"When we're picking where we should go in the optimized \"show_tree\"\npath there's no reason for why we need to convert our \"cmdmode\" of\ne.g. MODE_LONG into a FIELD_LONG_DEFAULT. Instead we can simply do\nthose checks in the show_tree() function itself.\n\nLet's also make this code more future-proof by unrolling the hardcoded\nstrmp() if/else if chain into something that checks a new \"static\nstruct\" providing a bidirectional mapping between optimized formats\nand the ls_tree_cmdmode.\n\nA preceding commit removed \"MODE_UNSPECIFIED\", now there is a use for\nsuch a thing, as we'll need to add this to our mapping so we'll choose\nthe \"show_tree\" fast-path here.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 98 ++++++++++++++++++++++-------------------------\n 1 file changed, 46 insertions(+), 52 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 528b4e5b5f6..820baddf9a9 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -23,25 +23,13 @@ static int ls_options;\n static struct pathspec pathspec;\n static int chomp_prefix;\n static const char *ls_tree_prefix;\n-#define FIELD_PATH_NAME 1\n-#define FIELD_SIZE (1 << 1)\n-#define FIELD_OBJECT_NAME (1 << 2)\n-#define FIELD_TYPE (1 << 3)\n-#define FIELD_MODE (1 << 4)\n-#define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n-#define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n static const char *format;\n-static const char *default_format = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\";\n-static const char *long_format = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\";\n-static const char *name_only_format = \"%(path)\";\n-static const char *object_only_format = \"%(objectname)\";\n struct show_tree_data {\n \tunsigned mode;\n \tenum object_type type;\n \tconst struct object_id *oid;\n \tconst char *pathname;\n \tstruct strbuf *base;\n-\tunsigned int shown_fields;\n };\n \n static const  char * const ls_tree_usage[] = {\n@@ -50,7 +38,8 @@ static const  char * const ls_tree_usage[] = {\n };\n \n static enum ls_tree_cmdmode {\n-\tMODE_LONG = 1,\n+\tMODE_DEFAULT = 0,\n+\tMODE_LONG,\n \tMODE_NAME_ONLY,\n \tMODE_NAME_STATUS,\n \tMODE_OBJECT_ONLY,\n@@ -122,25 +111,6 @@ static size_t expand_show_tree(struct strbuf *sb, const char *start,\n \treturn len;\n }\n \n-static int parse_shown_fields(unsigned int *shown_fields)\n-{\n-\tif (cmdmode == MODE_NAME_ONLY) {\n-\t\t*shown_fields = FIELD_PATH_NAME;\n-\t\treturn 0;\n-\t}\n-\tif (cmdmode == MODE_OBJECT_ONLY) {\n-\t\t*shown_fields = FIELD_OBJECT_NAME;\n-\t\treturn 0;\n-\t}\n-\tif (!ls_options || (ls_options & LS_RECURSIVE)\n-\t    || (ls_options & LS_SHOW_TREES)\n-\t    || (ls_options & LS_TREE_ONLY))\n-\t\t*shown_fields = FIELD_DEFAULT;\n-\tif (cmdmode == MODE_LONG)\n-\t\t*shown_fields = FIELD_LONG_DEFAULT;\n-\treturn 1;\n-}\n-\n static int show_recursive(const char *base, size_t baselen, const char *pathname)\n {\n \tint i;\n@@ -207,7 +177,7 @@ static int show_default(struct show_tree_data *data)\n {\n \tsize_t baselen = data->base->len;\n \n-\tif (data->shown_fields & FIELD_SIZE) {\n+\tif (cmdmode == MODE_LONG) {\n \t\tchar size_text[24];\n \t\tif (data->type == OBJ_BLOB) {\n \t\t\tunsigned long size;\n@@ -240,14 +210,12 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tint recurse = 0;\n \tsize_t baselen;\n \tenum object_type type = object_type(mode);\n-\tunsigned int shown_fields = *(unsigned int *)context;\n \tstruct show_tree_data data = {\n \t\t.mode = mode,\n \t\t.type = type,\n \t\t.oid = oid,\n \t\t.pathname = pathname,\n \t\t.base = base,\n-\t\t.shown_fields = shown_fields,\n \t};\n \n \tif (type == OBJ_BLOB) {\n@@ -260,12 +228,12 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\treturn recurse;\n \t}\n \n-\tif (shown_fields == FIELD_OBJECT_NAME) {\n+\tif (cmdmode == MODE_OBJECT_ONLY) {\n \t\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n \t\treturn recurse;\n \t}\n \n-\tif (shown_fields == FIELD_PATH_NAME) {\n+\tif (cmdmode == MODE_NAME_ONLY) {\n \t\tbaselen = base->len;\n \t\tstrbuf_addstr(base, pathname);\n \t\twrite_name_quoted_relative(base->buf,\n@@ -275,12 +243,40 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\treturn recurse;\n \t}\n \n-\tif (shown_fields>= FIELD_DEFAULT)\n+\tif (cmdmode == MODE_LONG ||\n+\t    (!ls_options || (ls_options & LS_RECURSIVE)\n+\t     || (ls_options & LS_SHOW_TREES)\n+\t     || (ls_options & LS_TREE_ONLY)))\n \t\tshow_default(&data);\n \n \treturn recurse;\n }\n \n+struct ls_tree_cmdmode_to_fmt {\n+\tenum ls_tree_cmdmode mode;\n+\tconst char *const fmt;\n+};\n+\n+static struct ls_tree_cmdmode_to_fmt ls_tree_cmdmode_format[] = {\n+\t{\n+\t\t.mode = MODE_DEFAULT,\n+\t\t.fmt = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\",\n+\t},\n+\t{\n+\t\t.mode = MODE_LONG,\n+\t\t.fmt = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\",\n+\t},\n+\t{\n+\t\t.mode = MODE_NAME_ONLY, /* And MODE_NAME_STATUS */\n+\t\t.fmt = \"%(path)\",\n+\t},\n+\t{\n+\t\t.mode = MODE_OBJECT_ONLY,\n+\t\t.fmt = \"%(objectname)\",\n+\t},\n+\t{ 0 },\n+};\n+\n int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n {\n \tstruct object_id oid;\n@@ -367,25 +363,23 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tif (!tree)\n \t\tdie(\"not a tree object\");\n \n-\tparse_shown_fields(&shown_fields);\n-\n \t/*\n \t * The generic show_tree_fmt() is slower than show_tree(), so\n \t * take the fast path if possible.\n \t */\n-\tif (format && (!strcmp(format, default_format))) {\n-\t\tfn = show_tree;\n-\t} else if (format && (!strcmp(format, long_format))) {\n-\t\tshown_fields = shown_fields | FIELD_SIZE;\n-\t\tfn = show_tree;\n-\t} else if (format && (!strcmp(format, name_only_format))) {\n-\t\tshown_fields = FIELD_PATH_NAME;\n-\t\tfn = show_tree;\n-\t} else if (format && (!strcmp(format, object_only_format))) {\n-\t\tshown_fields = FIELD_OBJECT_NAME;\n-\t\tfn = show_tree;\n-\t} else if (format)\n+\tif (format) {\n+\t\tstruct ls_tree_cmdmode_to_fmt *m2f;\n+\n \t\tfn = show_tree_fmt;\n+\t\tfor (m2f = ls_tree_cmdmode_format; m2f->fmt; m2f++) {\n+\t\t\tif (strcmp(format, m2f->fmt))\n+\t\t\t\tcontinue;\n+\n+\t\t\tcmdmode = m2f->mode;\n+\t\t\tfn = show_tree;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n \n \treturn !!read_tree(the_repository, tree, &pathspec, fn, &shown_fields);\n }\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"451014","messageId":"patch-7.7-9ce70bae078-20220310T134811Z-avarab@gmail.com","threadId":"57383","inReplyTo":"cover-0.7-00000000000-20220310T134811Z-avarab@gmail.com","subject":"[RFC/REVIEW 7/7] ls-tree: split up \"fast path\" callbacks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-10T13:57:03Z","receivedAt":"2022-03-10T13:57:25Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the various if/else in the callbacks for the \"fast path\" a lot\neasier to read by just using common functions for the parts that are\ncommon, and have per-format callbacks for those parts that are\ndifferent.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 198 +++++++++++++++++++++++++++++-----------------\n 1 file changed, 124 insertions(+), 74 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 820baddf9a9..ee69b8c2702 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -173,108 +173,157 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,\n \treturn recurse;\n }\n \n-static int show_default(struct show_tree_data *data)\n+static int show_tree_common(struct show_tree_data *data, int *recurse,\n+\t\t\t    const struct object_id *oid, struct strbuf *base,\n+\t\t\t    const char *pathname, unsigned mode)\n {\n-\tsize_t baselen = data->base->len;\n-\n-\tif (cmdmode == MODE_LONG) {\n-\t\tchar size_text[24];\n-\t\tif (data->type == OBJ_BLOB) {\n-\t\t\tunsigned long size;\n-\t\t\tif (oid_object_info(the_repository, data->oid, &size) == OBJ_BAD)\n-\t\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n-\t\t\telse\n-\t\t\t\txsnprintf(size_text, sizeof(size_text),\n-\t\t\t\t\t  \"%\" PRIuMAX, (uintmax_t)size);\n-\t\t} else {\n-\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n-\t\t}\n-\t\tprintf(\"%06o %s %s %7s\\t\", data->mode, type_name(data->type),\n-\t\tfind_unique_abbrev(data->oid, abbrev), size_text);\n-\t} else {\n-\t\tprintf(\"%06o %s %s\\t\", data->mode, type_name(data->type),\n-\t\tfind_unique_abbrev(data->oid, abbrev));\n-\t}\n-\tbaselen = data->base->len;\n-\tstrbuf_addstr(data->base, data->pathname);\n-\twrite_name_quoted_relative(data->base->buf,\n-\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n-\t\t\t\t   line_termination);\n-\tstrbuf_setlen(data->base, baselen);\n-\treturn 1;\n-}\n-\n-static int show_tree(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, void *context)\n-{\n-\tint recurse = 0;\n-\tsize_t baselen;\n \tenum object_type type = object_type(mode);\n-\tstruct show_tree_data data = {\n-\t\t.mode = mode,\n-\t\t.type = type,\n-\t\t.oid = oid,\n-\t\t.pathname = pathname,\n-\t\t.base = base,\n-\t};\n+\tint ret = -1;\n+\n+\t*recurse = 0;\n+\tdata->mode = mode;\n+\tdata->type = type;\n+\tdata->oid = oid;\n+\tdata->pathname = pathname;\n+\tdata->base = base;\n \n \tif (type == OBJ_BLOB) {\n \t\tif (ls_options & LS_TREE_ONLY)\n-\t\t\treturn 0;\n+\t\t\tret = 0;\n \t} else if (type == OBJ_TREE &&\n \t\t   show_recursive(base->buf, base->len, pathname)) {\n-\t\trecurse = READ_TREE_RECURSIVE;\n+\t\t*recurse = READ_TREE_RECURSIVE;\n \t\tif (!(ls_options & LS_SHOW_TREES))\n-\t\t\treturn recurse;\n+\t\t\tret = *recurse;\n \t}\n \n-\tif (cmdmode == MODE_OBJECT_ONLY) {\n-\t\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n-\t\treturn recurse;\n-\t}\n+\treturn ret;\n+}\n \n-\tif (cmdmode == MODE_NAME_ONLY) {\n-\t\tbaselen = base->len;\n-\t\tstrbuf_addstr(base, pathname);\n-\t\twrite_name_quoted_relative(base->buf,\n-\t\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n-\t\t\t\t\t   stdout, line_termination);\n-\t\tstrbuf_setlen(base, baselen);\n-\t\treturn recurse;\n+static void show_tree_common_default_long(struct strbuf *base,\n+\t\t\t\t\t  const char *pathname,\n+\t\t\t\t\t  const size_t baselen)\n+{\n+\tstrbuf_addstr(base, pathname);\n+\twrite_name_quoted_relative(base->buf,\n+\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n+\t\t\t\t   line_termination);\n+\tstrbuf_setlen(base, baselen);\n+}\n+\n+static int show_tree_default(const struct object_id *oid, struct strbuf *base,\n+\t\t\t     const char *pathname, unsigned mode,\n+\t\t\t     void *context)\n+{\n+\tint early;\n+\tint recurse;\n+\tstruct show_tree_data data = { 0 };\n+\n+\tearly = show_tree_common(&data, &recurse, oid, base, pathname, mode);\n+\tif (early >= 0)\n+\t\treturn early;\n+\n+\tprintf(\"%06o %s %s\\t\", data.mode, type_name(data.type),\n+\t       find_unique_abbrev(data.oid, abbrev));\n+\tshow_tree_common_default_long(base, pathname, data.base->len);\n+\treturn recurse;\n+}\n+\n+static int show_tree_long(const struct object_id *oid, struct strbuf *base,\n+\t\t\t  const char *pathname, unsigned mode, void *context)\n+{\n+\tint early;\n+\tint recurse;\n+\tstruct show_tree_data data = { 0 };\n+\tchar size_text[24];\n+\n+\tearly = show_tree_common(&data, &recurse, oid, base, pathname, mode);\n+\tif (early >= 0)\n+\t\treturn early;\n+\n+\tif (data.type == OBJ_BLOB) {\n+\t\tunsigned long size;\n+\t\tif (oid_object_info(the_repository, data.oid, &size) == OBJ_BAD)\n+\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n+\t\telse\n+\t\t\txsnprintf(size_text, sizeof(size_text),\n+\t\t\t\t  \"%\" PRIuMAX, (uintmax_t)size);\n+\t} else {\n+\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n \t}\n \n-\tif (cmdmode == MODE_LONG ||\n-\t    (!ls_options || (ls_options & LS_RECURSIVE)\n-\t     || (ls_options & LS_SHOW_TREES)\n-\t     || (ls_options & LS_TREE_ONLY)))\n-\t\tshow_default(&data);\n+\tprintf(\"%06o %s %s %7s\\t\", data.mode, type_name(data.type),\n+\t       find_unique_abbrev(data.oid, abbrev), size_text);\n+\tshow_tree_common_default_long(base, pathname, data.base->len);\n+\treturn 1;\n+}\n \n+static int show_tree_name_only(const struct object_id *oid, struct strbuf *base,\n+\t\t\t       const char *pathname, unsigned mode, void *context)\n+{\n+\tint early;\n+\tint recurse;\n+\tconst size_t baselen = base->len;\n+\tstruct show_tree_data data = { 0 };\n+\n+\tearly = show_tree_common(&data, &recurse, oid, base, pathname, mode);\n+\tif (early >= 0)\n+\t\treturn early;\n+\n+\tstrbuf_addstr(base, pathname);\n+\twrite_name_quoted_relative(base->buf,\n+\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n+\t\t\t\t   stdout, line_termination);\n+\tstrbuf_setlen(base, baselen);\n+\treturn recurse;\n+}\n+\n+static int show_tree_object(const struct object_id *oid, struct strbuf *base,\n+\t\t\t    const char *pathname, unsigned mode, void *context)\n+{\n+\tint early;\n+\tint recurse;\n+\tstruct show_tree_data data = { 0 };\n+\n+\tearly = show_tree_common(&data, &recurse, oid, base, pathname, mode);\n+\tif (early >= 0)\n+\t\treturn early;\n+\n+\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n \treturn recurse;\n }\n \n struct ls_tree_cmdmode_to_fmt {\n \tenum ls_tree_cmdmode mode;\n \tconst char *const fmt;\n+\tread_tree_fn_t fn;\n };\n \n static struct ls_tree_cmdmode_to_fmt ls_tree_cmdmode_format[] = {\n \t{\n \t\t.mode = MODE_DEFAULT,\n \t\t.fmt = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\",\n+\t\t.fn = show_tree_default,\n \t},\n \t{\n \t\t.mode = MODE_LONG,\n \t\t.fmt = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\",\n+\t\t.fn = show_tree_long,\n \t},\n \t{\n \t\t.mode = MODE_NAME_ONLY, /* And MODE_NAME_STATUS */\n \t\t.fmt = \"%(path)\",\n+\t\t.fn = show_tree_name_only,\n \t},\n \t{\n \t\t.mode = MODE_OBJECT_ONLY,\n \t\t.fmt = \"%(objectname)\",\n+\t\t.fn = show_tree_object\n+\t},\n+\t{\n+\t\t/* fallback */\n+\t\t.fn = show_tree_default,\n \t},\n-\t{ 0 },\n };\n \n int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n@@ -283,7 +332,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tstruct tree *tree;\n \tint i, full_tree = 0;\n \tunsigned int shown_fields = 0;\n-\tread_tree_fn_t fn = show_tree;\n+\tread_tree_fn_t fn = NULL;\n \tconst struct option ls_tree_options[] = {\n \t\tOPT_BIT('d', NULL, &ls_options, N_(\"only show trees\"),\n \t\t\tLS_TREE_ONLY),\n@@ -312,6 +361,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_END()\n \t};\n+\tstruct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;\n \n \tgit_config(git_default_config, NULL);\n \tls_tree_prefix = prefix;\n@@ -367,18 +417,18 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t * The generic show_tree_fmt() is slower than show_tree(), so\n \t * take the fast path if possible.\n \t */\n-\tif (format) {\n-\t\tstruct ls_tree_cmdmode_to_fmt *m2f;\n-\n-\t\tfn = show_tree_fmt;\n-\t\tfor (m2f = ls_tree_cmdmode_format; m2f->fmt; m2f++) {\n-\t\t\tif (strcmp(format, m2f->fmt))\n-\t\t\t\tcontinue;\n-\n+\twhile (m2f++) {\n+\t\tif (!m2f->fmt) {\n+\t\t\tfn = format ? show_tree_fmt : show_tree_default;\n+\t\t} else if (format && !strcmp(format, m2f->fmt)) {\n \t\t\tcmdmode = m2f->mode;\n-\t\t\tfn = show_tree;\n-\t\t\tbreak;\n+\t\t\tfn = m2f->fn;\n+\t\t} else if (!format && cmdmode == m2f->mode) {\n+\t\t\tfn = m2f->fn;\n+\t\t} else {\n+\t\t\tcontinue;\n \t\t}\n+\t\tbreak;\n \t}\n \n \treturn !!read_tree(the_repository, tree, &pathspec, fn, &shown_fields);\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"451538","messageId":"20220317095129.86790-1-dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover-0.7-00000000000-20220310T134811Z-avarab@gmail.com","subject":"Re: [RFC/REVIEW 0/7] fixups/suggestions/musings for tl/ls-tree-oid-only","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-17T09:51:29Z","receivedAt":"2022-03-17T09:51:57Z","isPatch":false,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"Ævar Arnfjörð Bjarmason wrote on Thu, 10 Mar 2022 14:56:56 +0100\n\n> I don't think all of these need to be squashed or fixed up into the\n> proposed series, but are just various small issues/questions I came \n> with while reviewing it. Brief notes \n>\n> Ævar Arnfjörð Bjarmason (7):\n> ...\n\n\nI looked these commits in \"RFC/REVIEW\" and I think each one is a good\nimprovement. So next, to make the squashes or keep it alone  I think\nmaybe it's like: \n\n\n(1) ls-tree tests: add tests for --name-status\n\n    They are new test codes that better than old ones. \n\n    Action: Keep it individually.\n\n(2) ls-tree tests: exhaustively test fast & slow path for --format\n\n    They are better test codes for the correctness formatting mechanism.\n\n    Action: Squash into commit 'ls-tree: introduce \"--format\" option'\n\n(3) ls-tree: remove dead labels\n\n    They remove the dead labels, and you mentioned it should be squash into\n    the commit which brought them in.\n    \n    Action: Squash into commit 'ls-tree: slightly refactor `show_tree()`'\n\n(4) ls-tree: remove unused \"MODE_UNSPECIFIED\"\n\n    As the subject describes, remove unused \"MODE_UNSPECIFIED\" and make\n    \"mutx_option\" to a better name \"mutx_option\". It's the prepared commit\n    for 'ls-tree: remove FIELD_*, just use MODE_*''\n\n    Action: Squash into commit 'ls-tree: slightly refactor `show_tree()`'\n\n(5) ls-tree: detect and error on --name-only --name-status\n\n    Optimized the incompatible detecting tests codes in \"t/t3103-ls-tree-misc.sh\"\n    and add a new 'MODE_NAME_STATUS'.\n\n    Action: Keep it individually.\n\n(6) ls-tree: remove FIELD_*, just use MODE_*\n\n    Using MODE directlly and make a format-mode mappings for fast-path detection.\n\n    Action: Keep it individually.\n\n(7) ls-tree: split up \"fast path\" callbacks\n\n    Expand \"ls_tree_cmdmode_format\" structure for each formats with the specific \n    show function and split up the current show functions name.\n\n    Action: Keep it individually.\n\n\nThis is the way I try to continue this work, please let me know if I understand you wrong.\n\nI look forward to your reply and I will decide the next step based on the reply. If we have\na consistent understanding of the next actions, I will try to make a pull request to your\nGit fork first. If there is no problem, I will continue to send patches to the mailing list.\n\nThanks.\n"},{"id":"451539","messageId":"220317.86zglodhcq.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"20220317095129.86790-1-dyroneteng@gmail.com","subject":"Re: [RFC/REVIEW 0/7] fixups/suggestions/musings for tl/ls-tree-oid-only","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-17T10:04:10Z","receivedAt":"2022-03-17T10:05:33Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Mar 17 2022, Teng Long wrote:\n\n> Ævar Arnfjörð Bjarmason wrote on Thu, 10 Mar 2022 14:56:56 +0100\n>\n>> I don't think all of these need to be squashed or fixed up into the\n>> proposed series, but are just various small issues/questions I came \n>> with while reviewing it. Brief notes \n>>\n>> Ævar Arnfjörð Bjarmason (7):\n>> ...\n>\n>\n> I looked these commits in \"RFC/REVIEW\" and I think each one is a good\n> improvement. So next, to make the squashes or keep it alone  I think\n> maybe it's like: \n>\n>\n> (1) ls-tree tests: add tests for --name-status\n>\n>     They are new test codes that better than old ones. \n>\n>     Action: Keep it individually.\n>\n> (2) ls-tree tests: exhaustively test fast & slow path for --format\n>\n>     They are better test codes for the correctness formatting mechanism.\n>\n>     Action: Squash into commit 'ls-tree: introduce \"--format\" option'\n>\n> (3) ls-tree: remove dead labels\n>\n>     They remove the dead labels, and you mentioned it should be squash into\n>     the commit which brought them in.\n>     \n>     Action: Squash into commit 'ls-tree: slightly refactor `show_tree()`'\n>\n> (4) ls-tree: remove unused \"MODE_UNSPECIFIED\"\n>\n>     As the subject describes, remove unused \"MODE_UNSPECIFIED\" and make\n>     \"mutx_option\" to a better name \"mutx_option\". It's the prepared commit\n>     for 'ls-tree: remove FIELD_*, just use MODE_*''\n>\n>     Action: Squash into commit 'ls-tree: slightly refactor `show_tree()`'\n>\n> (5) ls-tree: detect and error on --name-only --name-status\n>\n>     Optimized the incompatible detecting tests codes in \"t/t3103-ls-tree-misc.sh\"\n>     and add a new 'MODE_NAME_STATUS'.\n>\n>     Action: Keep it individually.\n>\n> (6) ls-tree: remove FIELD_*, just use MODE_*\n>\n>     Using MODE directlly and make a format-mode mappings for fast-path detection.\n>\n>     Action: Keep it individually.\n>\n> (7) ls-tree: split up \"fast path\" callbacks\n>\n>     Expand \"ls_tree_cmdmode_format\" structure for each formats with the specific \n>     show function and split up the current show functions name.\n>\n>     Action: Keep it individually.\n\nAll sounds good, or rather. I really meant those as \"hey maybe it's\nuseful, you decide what to do with it\". So I'm happy with whatever you\npicked here :)\n\n> This is the way I try to continue this work, please let me know if I understand you wrong.\n>\n> I look forward to your reply and I will decide the next step based on the reply. If we have\n> a consistent understanding of the next actions, I will try to make a pull request to your\n> Git fork first. If there is no problem, I will continue to send patches to the mailing list.\n\nHi, thanks for looking at it. I think it's better to just post a re-roll\non the list for discussion, with whatever changed you think are\nappropriate. Thanks!\n"},{"id":"451700","messageId":"cover.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1646390152.git.dyroneteng@gmail.com","subject":"[PATCH v13 00/16] ls-tree: \"--object-only\" and \"--format\" opts","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:14Z","receivedAt":"2022-03-21T07:33:42Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"Diff from v12:\n\nCurrent patch is based by the review patch of Ævar Arnfjörð Bjarmason in [1].\n\nIn terms of the review commits, I replied Ævar Arnfjörð Bjarmason [2] and listed the\nactions, therefore this v13 patch was implemented by these actions.  \n\nLinks:\n\n1. https://public-inbox.org/git/cover-0.7-00000000000-20220310T134811Z-avarab@gmail.com/\n2. https://public-inbox.org/git/20220317095129.86790-1-dyroneteng@gmail.com/\n\nThanks.\n\nJohannes Schindelin (1):\n  cocci: allow padding with `strbuf_addf()`\n\nTeng Long (5):\n  ls-tree: rename \"retval\" to \"recurse\" in \"show_tree()\"\n  ls-tree: simplify nesting if/else logic in \"show_tree()\"\n  ls-tree: fix \"--name-only\" and \"--long\" combined use bug\n  ls-tree: slightly refactor `show_tree()`\n  ls-tree: support --object-only option for \"git-ls-tree\"\n\nÆvar Arnfjörð Bjarmason (10):\n  ls-tree: remove commented-out code\n  ls-tree: add missing braces to \"else\" arms\n  ls-tree: use \"enum object_type\", not {blob,tree,commit}_type\n  ls-tree: use \"size_t\", not \"int\" for \"struct strbuf\"'s \"len\"\n  ls-tree: introduce struct \"show_tree_data\"\n  ls-tree: introduce \"--format\" option\n  ls-tree tests: add tests for --name-status\n  ls-tree: detect and error on --name-only --name-status\n  ls-tree: remove FIELD_*, just use MODE_*\n  ls-tree: split up \"fast path\" callbacks\n\n Documentation/git-ls-tree.txt   |  68 +++++-\n builtin/ls-tree.c               | 367 ++++++++++++++++++++++++++------\n contrib/coccinelle/strbuf.cocci |   6 +-\n t/t3101-ls-tree-dirname.sh      |  55 ++---\n t/t3103-ls-tree-misc.sh         |  15 ++\n t/t3104-ls-tree-format.sh       |  76 +++++++\n 6 files changed, 493 insertions(+), 94 deletions(-)\n create mode 100755 t/t3104-ls-tree-format.sh\n\nRange-diff against v12:\n -:  ---------- >  1:  2fcff7e0d4 ls-tree: remove commented-out code\n -:  ---------- >  2:  6fd1dd9383 ls-tree: add missing braces to \"else\" arms\n -:  ---------- >  3:  208654b5e2 ls-tree: use \"enum object_type\", not {blob,tree,commit}_type\n -:  ---------- >  4:  2637464fd8 ls-tree: use \"size_t\", not \"int\" for \"struct strbuf\"'s \"len\"\n -:  ---------- >  5:  99e6d47108 ls-tree: rename \"retval\" to \"recurse\" in \"show_tree()\"\n -:  ---------- >  6:  a8d9b78dea ls-tree: simplify nesting if/else logic in \"show_tree()\"\n -:  ---------- >  7:  25a07e048f ls-tree: fix \"--name-only\" and \"--long\" combined use bug\n 1:  f449146b4d !  8:  55f1e10d7e ls-tree: slightly refactor `show_tree()`\n    @@ builtin/ls-tree.c\n     -#define LS_SHOW_SIZE 16\n     +#define LS_TREE_ONLY (1 << 1)\n     +#define LS_SHOW_TREES (1 << 2)\n    -+#define LS_NAME_ONLY (1 << 3)\n    -+#define LS_SHOW_SIZE (1 << 4)\n      static int abbrev;\n      static int ls_options;\n      static struct pathspec pathspec;\n    @@ builtin/ls-tree.c\n      \tNULL\n      };\n      \n    -+static enum mutx_option {\n    -+\tMODE_UNSPECIFIED = 0,\n    ++static enum ls_tree_cmdmode {\n    ++\tMODE_LONG = 1,\n     +\tMODE_NAME_ONLY,\n    -+\tMODE_LONG,\n     +} cmdmode;\n     +\n     +static int parse_shown_fields(void)\n 2:  80311adc7c !  9:  85d0b0da1d ls-tree: introduce struct \"show_tree_data\"\n    @@ builtin/ls-tree.c: static unsigned int shown_fields;\n      static const  char * const ls_tree_usage[] = {\n      \tN_(\"git ls-tree [<options>] <tree-ish> [<path>...]\"),\n      \tNULL\n    -@@ builtin/ls-tree.c: static enum mutx_option {\n    - \tMODE_LONG,\n    +@@ builtin/ls-tree.c: static enum ls_tree_cmdmode {\n    + \tMODE_NAME_ONLY,\n      } cmdmode;\n      \n     -static int parse_shown_fields(void)\n 3:  459080f549 = 10:  eddcf903ad cocci: allow padding with `strbuf_addf()`\n 4:  cb717d08be ! 11:  7451242daa ls-tree: introduce \"--format\" option\n    @@ Commit message\n         idea and suggestion, this commit makes modifications in terms of the\n         original discussion on community [1].\n     \n    +    In [1] there was a \"GIT_TEST_LS_TREE_FORMAT_BACKEND\" variable to\n    +    ensure that we had test coverage for passing tests that would\n    +    otherwise use show_tree() through show_tree_fmt(), and thus that the\n    +    formatting mechanism could handle all the same cases as the\n    +    non-formatting options.\n    +\n    +    Somewhere in subsequent re-rolls of that we seem to have drifted away\n    +    from what the goal of these tests should be. We're trying to ensure\n    +    correctness of show_tree_fmt(). We can't tell if we \"hit [the]\n    +    fast-path\" here, and instead of having an explicit test for that, we\n    +    can just add it to something our \"test_ls_tree_format\" tests for.\n    +\n         Here is the statistics about performance tests:\n     \n         1. Default format (hitten the builtin formats):\n    @@ Commit message\n     \n         Links:\n                 [1] https://public-inbox.org/git/RFC-patch-6.7-eac299f06ff-20211217T131635Z-avarab@gmail.com/\n    +            [2] https://lore.kernel.org/git/cb717d08be87e3239117c6c667cb32caabaad33d.1646390152.git.dyroneteng@gmail.com/\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n         Signed-off-by: Teng Long <dyroneteng@gmail.com>\n    @@ builtin/ls-tree.c: static const char *ls_tree_prefix;\n      struct show_tree_data {\n      \tunsigned mode;\n      \tenum object_type type;\n    -@@ builtin/ls-tree.c: static enum mutx_option {\n    - \tMODE_LONG,\n    +@@ builtin/ls-tree.c: static enum ls_tree_cmdmode {\n    + \tMODE_NAME_ONLY,\n      } cmdmode;\n      \n     +static void expand_objectsize(struct strbuf *line, const struct object_id *oid,\n    @@ t/t3104-ls-tree-format.sh (new)\n     +\topts=$2 &&\n     +\tfmtopts=$3 &&\n     +\tshift 2 &&\n    -+\tgit ls-tree $opts -r HEAD >expect.raw &&\n    -+\tsed \"s/^/> /\" >expect <expect.raw &&\n    -+\tgit ls-tree --format=\"> $format\" -r $fmtopts HEAD >actual &&\n    -+\ttest_cmp expect actual\n    ++\n    ++\ttest_expect_success \"ls-tree '--format=<$format>' is like options '$opts $fmtopts'\" '\n    ++\t\tgit ls-tree $opts -r HEAD >expect &&\n    ++\t\tgit ls-tree --format=\"$format\" -r $fmtopts HEAD >actual &&\n    ++\t\ttest_cmp expect actual\n    ++\t'\n    ++\n    ++\ttest_expect_success \"ls-tree '--format=<$format>' on optimized v.s. non-optimized path\" '\n    ++\t\tgit ls-tree --format=\"$format\" -r $fmtopts HEAD >expect &&\n    ++\t\tgit ls-tree --format=\"> $format\" -r $fmtopts HEAD >actual.raw &&\n    ++\t\tsed \"s/^> //\" >actual <actual.raw &&\n    ++\t\ttest_cmp expect actual\n    ++\t'\n     +}\n     +\n    -+test_expect_success 'ls-tree --format=<default-like>' '\n    -+\ttest_ls_tree_format \\\n    -+\t\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n    -+\t\t\"\"\n    -+'\n    ++test_ls_tree_format \\\n    ++\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n    ++\t\"\"\n     +\n    -+test_expect_success 'ls-tree --format=<long-like>' '\n    -+\ttest_ls_tree_format \\\n    -+\t\t\"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\" \\\n    -+\t\t\"--long\"\n    -+'\n    ++test_ls_tree_format \\\n    ++\t\"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\" \\\n    ++\t\"--long\"\n     +\n    -+test_expect_success 'ls-tree --format=<name-only-like>' '\n    -+\ttest_ls_tree_format \\\n    -+\t\t\"%(path)\" \\\n    -+\t\t\"--name-only\"\n    -+'\n    ++test_ls_tree_format \\\n    ++\t\"%(path)\" \\\n    ++\t\"--name-only\"\n     +\n    -+test_expect_success 'ls-tree combine --format=<default-like> and -t' '\n    -+\ttest_ls_tree_format \\\n    ++test_ls_tree_format \\\n     +\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n     +\t\"-t\" \\\n     +\t\"-t\"\n    -+'\n     +\n    -+test_expect_success 'ls-tree combine --format=<default-like> and --full-name' '\n    -+\ttest_ls_tree_format \\\n    ++test_ls_tree_format \\\n     +\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n     +\t\"--full-name\" \\\n     +\t\"--full-name\"\n    -+'\n     +\n    -+test_expect_success 'ls-tree combine --format=<default-like> and --full-tree' '\n    -+\ttest_ls_tree_format \\\n    ++test_ls_tree_format \\\n     +\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n     +\t\"--full-tree\" \\\n     +\t\"--full-tree\"\n    -+'\n     +\n    -+test_expect_success 'ls-tree hit fast-path with --format=<default-like>' '\n    -+\tgit ls-tree -r HEAD >expect &&\n    -+\tgit ls-tree --format=\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" -r HEAD >actual &&\n    -+\ttest_cmp expect actual\n    -+'\n    -+\n    -+test_expect_success 'ls-tree hit fast-path with --format=<name-only-like>' '\n    -+\tgit ls-tree -r --name-only HEAD >expect &&\n    -+\tgit ls-tree --format=\"%(path)\" -r HEAD >actual &&\n    -+\ttest_cmp expect actual\n    -+'\n     +test_done\n 5:  55b1c4379d ! 12:  2bcd08ebd0 ls-tree: support --object-only option for \"git-ls-tree\"\n    @@ Documentation/git-ls-tree.txt: OPTIONS\n      \tInstead of showing the full 40-byte hexadecimal object\n     \n      ## builtin/ls-tree.c ##\n    -@@ builtin/ls-tree.c: static int line_termination = '\\n';\n    - #define LS_SHOW_TREES (1 << 2)\n    - #define LS_NAME_ONLY (1 << 3)\n    - #define LS_SHOW_SIZE (1 << 4)\n    -+#define LS_OBJECT_ONLY (1 << 5)\n    - static int abbrev;\n    - static int ls_options;\n    - static struct pathspec pathspec;\n     @@ builtin/ls-tree.c: static const char *format;\n      static const char *default_format = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\";\n      static const char *long_format = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\";\n    @@ builtin/ls-tree.c: static const char *format;\n      \tunsigned mode;\n      \tenum object_type type;\n     @@ builtin/ls-tree.c: static const  char * const ls_tree_usage[] = {\n    - static enum mutx_option {\n    - \tMODE_UNSPECIFIED = 0,\n    + static enum ls_tree_cmdmode {\n    + \tMODE_LONG = 1,\n      \tMODE_NAME_ONLY,\n     +\tMODE_OBJECT_ONLY,\n    - \tMODE_LONG,\n      } cmdmode;\n      \n    + static void expand_objectsize(struct strbuf *line, const struct object_id *oid,\n     @@ builtin/ls-tree.c: static int parse_shown_fields(unsigned int *shown_fields)\n      \t\t*shown_fields = FIELD_PATH_NAME;\n      \t\treturn 0;\n    @@ t/t3103-ls-tree-misc.sh: test_expect_success 'ls-tree fails with non-zero exit c\n      test_done\n     \n      ## t/t3104-ls-tree-format.sh ##\n    -@@ t/t3104-ls-tree-format.sh: test_expect_success 'ls-tree --format=<name-only-like>' '\n    - \t\t\"--name-only\"\n    - '\n    +@@ t/t3104-ls-tree-format.sh: test_ls_tree_format \\\n    + \t\"%(path)\" \\\n    + \t\"--name-only\"\n      \n    -+test_expect_success 'ls-tree --format=<object-only-like>' '\n    -+\ttest_ls_tree_format \\\n    -+\t\t\"%(objectname)\" \\\n    -+\t\t\"--object-only\"\n    -+'\n    ++test_ls_tree_format \\\n    ++\t\"%(objectname)\" \\\n    ++\t\"--object-only\"\n     +\n    -+test_expect_success 'ls-tree --format=<object-only-like> --abbrev' '\n    -+\ttest_ls_tree_format \\\n    -+\t\t\"%(objectname)\" \\\n    -+\t\t\"--object-only --abbrev\" \\\n    -+\t\t\"--abbrev\"\n    -+'\n    ++test_ls_tree_format \\\n    ++\t\"%(objectname)\" \\\n    ++\t\"--object-only --abbrev\" \\\n    ++\t\"--abbrev\"\n     +\n    - test_expect_success 'ls-tree combine --format=<default-like> and -t' '\n    - \ttest_ls_tree_format \\\n    + test_ls_tree_format \\\n      \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n    -@@ t/t3104-ls-tree-format.sh: test_expect_success 'ls-tree hit fast-path with --format=<name-only-like>' '\n    - \tgit ls-tree --format=\"%(path)\" -r HEAD >actual &&\n    - \ttest_cmp expect actual\n    - '\n    -+\n    -+test_expect_success 'ls-tree hit fast-path with --format=<object-only-like>' '\n    -+\tgit ls-tree -r --object-only HEAD >expect &&\n    -+\tgit ls-tree --format=\"%(objectname)\" -r HEAD >actual &&\n    -+\ttest_cmp expect actual\n    -+'\n    - test_done\n    + \t\"-t\" \\\n -:  ---------- > 13:  a5c73de057 ls-tree tests: add tests for --name-status\n -:  ---------- > 14:  89402a8518 ls-tree: detect and error on --name-only --name-status\n -:  ---------- > 15:  b8afca193a ls-tree: remove FIELD_*, just use MODE_*\n -:  ---------- > 16:  010e3c0ece ls-tree: split up \"fast path\" callbacks\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451701","messageId":"2fcff7e0d40255e0be9496647787352dd0aeb784.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 01/16] ls-tree: remove commented-out code","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:15Z","receivedAt":"2022-03-21T07:33:48Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nRemove code added in f35a6d3bce7 (Teach core object handling functions\nabout gitlinks, 2007-04-09), later patched in 7d0b18a4da1 (Add output\nflushing before fork(), 2008-08-04), and then finally ending up in its\ncurrent form in d3bee161fef (tree.c: allow read_tree_recursive() to\ntraverse gitlink entries, 2009-01-25). All while being commented-out!\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 9 ---------\n 1 file changed, 9 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3a442631c7..5f7c84950c 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -69,15 +69,6 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tconst char *type = blob_type;\n \n \tif (S_ISGITLINK(mode)) {\n-\t\t/*\n-\t\t * Maybe we want to have some recursive version here?\n-\t\t *\n-\t\t * Something similar to this incomplete example:\n-\t\t *\n-\t\tif (show_subprojects(base, baselen, pathname))\n-\t\t\tretval = READ_TREE_RECURSIVE;\n-\t\t *\n-\t\t */\n \t\ttype = commit_type;\n \t} else if (S_ISDIR(mode)) {\n \t\tif (show_recursive(base->buf, base->len, pathname)) {\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451702","messageId":"6fd1dd938303192d5b0a4cdc2914b2ff72cf5a3d.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 02/16] ls-tree: add missing braces to \"else\" arms","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:16Z","receivedAt":"2022-03-21T07:33:49Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nAdd missing {} to the \"else\" arms in show_tree() per the\nCodingGuidelines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 6 ++++--\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 5f7c84950c..0a28f32ccb 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -92,14 +92,16 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\t\telse\n \t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n \t\t\t\t\t\t  \"%\"PRIuMAX, (uintmax_t)size);\n-\t\t\t} else\n+\t\t\t} else {\n \t\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n+\t\t\t}\n \t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type,\n \t\t\t       find_unique_abbrev(oid, abbrev),\n \t\t\t       size_text);\n-\t\t} else\n+\t\t} else {\n \t\t\tprintf(\"%06o %s %s\\t\", mode, type,\n \t\t\t       find_unique_abbrev(oid, abbrev));\n+\t\t}\n \t}\n \tbaselen = base->len;\n \tstrbuf_addstr(base, pathname);\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451703","messageId":"208654b5e2aebbe988b5a8f478869253a0166e9b.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 03/16] ls-tree: use \"enum object_type\", not {blob,tree,commit}_type","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:17Z","receivedAt":"2022-03-21T07:33:50Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nChange the ls-tree.c code to use type_name() on the enum instead of\nusing the string constants. This doesn't matter either way for\nperformance, but makes this a bit easier to read as we'll no longer\nneed a strcmp() here.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 0a28f32ccb..3f0225b097 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -66,17 +66,17 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n {\n \tint retval = 0;\n \tint baselen;\n-\tconst char *type = blob_type;\n+\tenum object_type type = OBJ_BLOB;\n \n \tif (S_ISGITLINK(mode)) {\n-\t\ttype = commit_type;\n+\t\ttype = OBJ_COMMIT;\n \t} else if (S_ISDIR(mode)) {\n \t\tif (show_recursive(base->buf, base->len, pathname)) {\n \t\t\tretval = READ_TREE_RECURSIVE;\n \t\t\tif (!(ls_options & LS_SHOW_TREES))\n \t\t\t\treturn retval;\n \t\t}\n-\t\ttype = tree_type;\n+\t\ttype = OBJ_TREE;\n \t}\n \telse if (ls_options & LS_TREE_ONLY)\n \t\treturn 0;\n@@ -84,7 +84,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tif (!(ls_options & LS_NAME_ONLY)) {\n \t\tif (ls_options & LS_SHOW_SIZE) {\n \t\t\tchar size_text[24];\n-\t\t\tif (!strcmp(type, blob_type)) {\n+\t\t\tif (type == OBJ_BLOB) {\n \t\t\t\tunsigned long size;\n \t\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n \t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n@@ -95,11 +95,11 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\t} else {\n \t\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n \t\t\t}\n-\t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type,\n+\t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n \t\t\t       find_unique_abbrev(oid, abbrev),\n \t\t\t       size_text);\n \t\t} else {\n-\t\t\tprintf(\"%06o %s %s\\t\", mode, type,\n+\t\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n \t\t\t       find_unique_abbrev(oid, abbrev));\n \t\t}\n \t}\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451704","messageId":"2637464fd8f6b8bae6c681f20c10c9fc76dc6d6c.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 04/16] ls-tree: use \"size_t\", not \"int\" for \"struct strbuf\"'s \"len\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:18Z","receivedAt":"2022-03-21T07:33:56Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nThe \"struct strbuf\"'s \"len\" member is a \"size_t\", not an \"int\", so\nlet's change our corresponding types accordingly. This also changes\nthe \"len\" and \"speclen\" variables, which are likewise used to store\nthe return value of strlen(), which returns \"size_t\", not \"int\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3f0225b097..eecc7482d5 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -31,7 +31,7 @@ static const  char * const ls_tree_usage[] = {\n \tNULL\n };\n \n-static int show_recursive(const char *base, int baselen, const char *pathname)\n+static int show_recursive(const char *base, size_t baselen, const char *pathname)\n {\n \tint i;\n \n@@ -43,7 +43,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n \n \tfor (i = 0; i < pathspec.nr; i++) {\n \t\tconst char *spec = pathspec.items[i].match;\n-\t\tint len, speclen;\n+\t\tsize_t len, speclen;\n \n \t\tif (strncmp(base, spec, baselen))\n \t\t\tcontinue;\n@@ -65,7 +65,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\tconst char *pathname, unsigned mode, void *context)\n {\n \tint retval = 0;\n-\tint baselen;\n+\tsize_t baselen;\n \tenum object_type type = OBJ_BLOB;\n \n \tif (S_ISGITLINK(mode)) {\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451705","messageId":"99e6d47108308b0bbaf406b439170de04e375c56.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 05/16] ls-tree: rename \"retval\" to \"recurse\" in \"show_tree()\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:19Z","receivedAt":"2022-03-21T07:33:58Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"The variable which \"show_tree()\" return is named \"retval\", a name that's\na little hard to understand. The commit rename \"retval\" to \"recurse\"\nwhich is a more meaningful name than before in the context. We do not\nneed to take a look at \"read_tree_at()\" in \"tree.c\" to make sure what\ndoes \"retval\" mean.\n\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex eecc7482d5..ef8c414f61 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -64,7 +64,7 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\tconst char *pathname, unsigned mode, void *context)\n {\n-\tint retval = 0;\n+\tint recurse = 0;\n \tsize_t baselen;\n \tenum object_type type = OBJ_BLOB;\n \n@@ -72,9 +72,9 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\ttype = OBJ_COMMIT;\n \t} else if (S_ISDIR(mode)) {\n \t\tif (show_recursive(base->buf, base->len, pathname)) {\n-\t\t\tretval = READ_TREE_RECURSIVE;\n+\t\t\trecurse = READ_TREE_RECURSIVE;\n \t\t\tif (!(ls_options & LS_SHOW_TREES))\n-\t\t\t\treturn retval;\n+\t\t\t\treturn recurse;\n \t\t}\n \t\ttype = OBJ_TREE;\n \t}\n@@ -109,7 +109,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n \t\t\t\t   stdout, line_termination);\n \tstrbuf_setlen(base, baselen);\n-\treturn retval;\n+\treturn recurse;\n }\n \n int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451706","messageId":"a8d9b78dea10af317b59ed2749b0732926bf1192.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 06/16] ls-tree: simplify nesting if/else logic in \"show_tree()\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:20Z","receivedAt":"2022-03-21T07:34:00Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"Use the object_type() function to determine the object type from the\n\"mode\" passed to us by read_tree(), instead of doing so with the S_*()\nmacros.\n\nSigned-off-by: Teng Long <dyronetengb@gmail.com>\n---\n builtin/ls-tree.c | 21 +++++++++------------\n 1 file changed, 9 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex ef8c414f61..d4be71bad2 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -66,20 +66,17 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n {\n \tint recurse = 0;\n \tsize_t baselen;\n-\tenum object_type type = OBJ_BLOB;\n+\tenum object_type type = object_type(mode);\n \n-\tif (S_ISGITLINK(mode)) {\n-\t\ttype = OBJ_COMMIT;\n-\t} else if (S_ISDIR(mode)) {\n-\t\tif (show_recursive(base->buf, base->len, pathname)) {\n-\t\t\trecurse = READ_TREE_RECURSIVE;\n-\t\t\tif (!(ls_options & LS_SHOW_TREES))\n-\t\t\t\treturn recurse;\n-\t\t}\n-\t\ttype = OBJ_TREE;\n+\tif (type == OBJ_BLOB) {\n+\t\tif (ls_options & LS_TREE_ONLY)\n+\t\t\treturn 0;\n+\t} else if (type == OBJ_TREE &&\n+\t\t   show_recursive(base->buf, base->len, pathname)) {\n+\t\trecurse = READ_TREE_RECURSIVE;\n+\t\tif (!(ls_options & LS_SHOW_TREES))\n+\t\t\treturn recurse;\n \t}\n-\telse if (ls_options & LS_TREE_ONLY)\n-\t\treturn 0;\n \n \tif (!(ls_options & LS_NAME_ONLY)) {\n \t\tif (ls_options & LS_SHOW_SIZE) {\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451707","messageId":"25a07e048f25e0efdd2f8690f2e6967352a20e9b.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 07/16] ls-tree: fix \"--name-only\" and \"--long\" combined use bug","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:21Z","receivedAt":"2022-03-21T07:34:04Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"If we execute \"git ls-tree\" with combined \"--name-only\" and \"--long\"\n, only the pathname will be printed, the size is omitted (the original\ndiscoverer was Peff in [1]).\n\nThis commit fix this issue by using `OPT_CMDMODE()` instead to make both\nof them mutually exclusive.\n\n[1] https://public-inbox.org/git/YZK0MKCYAJmG+pSU@coredump.intra.peff.net/\n\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c       | 12 ++++++------\n t/t3103-ls-tree-misc.sh |  9 +++++++++\n 2 files changed, 15 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex d4be71bad2..7be4c13228 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -123,12 +123,12 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\t\tLS_SHOW_TREES),\n \t\tOPT_SET_INT('z', NULL, &line_termination,\n \t\t\t    N_(\"terminate entries with NUL byte\"), 0),\n-\t\tOPT_BIT('l', \"long\", &ls_options, N_(\"include object size\"),\n-\t\t\tLS_SHOW_SIZE),\n-\t\tOPT_BIT(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n-\t\t\tLS_NAME_ONLY),\n-\t\tOPT_BIT(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n-\t\t\tLS_NAME_ONLY),\n+\t\tOPT_CMDMODE('l', \"long\", &ls_options, N_(\"include object size\"),\n+\t\t\t    LS_SHOW_SIZE),\n+\t\tOPT_CMDMODE(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n+\t\t\t    LS_NAME_ONLY),\n+\t\tOPT_CMDMODE(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n+\t\t\t    LS_NAME_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n \t\t\t    N_(\"use full path names\"), 0),\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\ndiff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh\nindex d18ba1bd84..d9d7fa932f 100755\n--- a/t/t3103-ls-tree-misc.sh\n+++ b/t/t3103-ls-tree-misc.sh\n@@ -23,4 +23,13 @@ test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '\n \ttest_must_fail git ls-tree -r HEAD\n '\n \n+for opts in \\\n+\t\"--name-only --long\" \\\n+\t\"--name-status --long\"\n+do\n+\ttest_expect_success \"usage: incompatible options: $opts\" '\n+\t\ttest_expect_code 129 git ls-tree $opts $tree\n+    '\n+done\n+\n test_done\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451708","messageId":"55f1e10d7e38d85f15565845c8d29ed733da9f45.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 08/16] ls-tree: slightly refactor `show_tree()`","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:22Z","receivedAt":"2022-03-21T07:34:15Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"This is a non-functional change, we use a new int \"shown_fields\" to mark\nwhich columns to output, and `parse_shown_fields()` to calculate the\nvalue of \"shown_fields\".\n\nThis has the advantage of making the show_tree logic simpler and more\nreadable, as well as making it easier to extend new options (for example,\nif we want to add a \"--object-only\" option, we just need to add a similar\n\"if (shown_fields == FIELD_OBJECT_NAME)\" short-circuit logic in\n\"show_tree()\").\n\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 121 ++++++++++++++++++++++++++++++++--------------\n 1 file changed, 84 insertions(+), 37 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 7be4c13228..4364416e1c 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -16,21 +16,48 @@\n \n static int line_termination = '\\n';\n #define LS_RECURSIVE 1\n-#define LS_TREE_ONLY 2\n-#define LS_SHOW_TREES 4\n-#define LS_NAME_ONLY 8\n-#define LS_SHOW_SIZE 16\n+#define LS_TREE_ONLY (1 << 1)\n+#define LS_SHOW_TREES (1 << 2)\n static int abbrev;\n static int ls_options;\n static struct pathspec pathspec;\n static int chomp_prefix;\n static const char *ls_tree_prefix;\n+static unsigned int shown_fields;\n+#define FIELD_PATH_NAME 1\n+#define FIELD_SIZE (1 << 1)\n+#define FIELD_OBJECT_NAME (1 << 2)\n+#define FIELD_TYPE (1 << 3)\n+#define FIELD_MODE (1 << 4)\n+#define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n+#define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n \n static const  char * const ls_tree_usage[] = {\n \tN_(\"git ls-tree [<options>] <tree-ish> [<path>...]\"),\n \tNULL\n };\n \n+static enum ls_tree_cmdmode {\n+\tMODE_LONG = 1,\n+\tMODE_NAME_ONLY,\n+} cmdmode;\n+\n+static int parse_shown_fields(void)\n+{\n+\tif (cmdmode == MODE_NAME_ONLY) {\n+\t\tshown_fields = FIELD_PATH_NAME;\n+\t\treturn 0;\n+\t}\n+\n+\tif (!ls_options || (ls_options & LS_RECURSIVE)\n+\t    || (ls_options & LS_SHOW_TREES)\n+\t    || (ls_options & LS_TREE_ONLY))\n+\t\tshown_fields = FIELD_DEFAULT;\n+\tif (cmdmode == MODE_LONG)\n+\t\tshown_fields = FIELD_LONG_DEFAULT;\n+\treturn 1;\n+}\n+\n static int show_recursive(const char *base, size_t baselen, const char *pathname)\n {\n \tint i;\n@@ -61,6 +88,39 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n \treturn 0;\n }\n \n+static int show_default(const struct object_id *oid, enum object_type type,\n+\t\t\tconst char *pathname, unsigned mode,\n+\t\t\tstruct strbuf *base)\n+{\n+\tsize_t baselen = base->len;\n+\n+\tif (shown_fields & FIELD_SIZE) {\n+\t\tchar size_text[24];\n+\t\tif (type == OBJ_BLOB) {\n+\t\t\tunsigned long size;\n+\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n+\t\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n+\t\t\telse\n+\t\t\t\txsnprintf(size_text, sizeof(size_text),\n+\t\t\t\t\t  \"%\" PRIuMAX, (uintmax_t)size);\n+\t\t} else {\n+\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n+\t\t}\n+\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n+\t\tfind_unique_abbrev(oid, abbrev), size_text);\n+\t} else {\n+\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n+\t\tfind_unique_abbrev(oid, abbrev));\n+\t}\n+\tbaselen = base->len;\n+\tstrbuf_addstr(base, pathname);\n+\twrite_name_quoted_relative(base->buf,\n+\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n+\t\t\t\t   line_termination);\n+\tstrbuf_setlen(base, baselen);\n+\treturn 1;\n+}\n+\n static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\tconst char *pathname, unsigned mode, void *context)\n {\n@@ -78,34 +138,19 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\treturn recurse;\n \t}\n \n-\tif (!(ls_options & LS_NAME_ONLY)) {\n-\t\tif (ls_options & LS_SHOW_SIZE) {\n-\t\t\tchar size_text[24];\n-\t\t\tif (type == OBJ_BLOB) {\n-\t\t\t\tunsigned long size;\n-\t\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n-\t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n-\t\t\t\t\t\t  \"BAD\");\n-\t\t\t\telse\n-\t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n-\t\t\t\t\t\t  \"%\"PRIuMAX, (uintmax_t)size);\n-\t\t\t} else {\n-\t\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n-\t\t\t}\n-\t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n-\t\t\t       find_unique_abbrev(oid, abbrev),\n-\t\t\t       size_text);\n-\t\t} else {\n-\t\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n-\t\t\t       find_unique_abbrev(oid, abbrev));\n-\t\t}\n+\tif (shown_fields == FIELD_PATH_NAME) {\n+\t\tbaselen = base->len;\n+\t\tstrbuf_addstr(base, pathname);\n+\t\twrite_name_quoted_relative(base->buf,\n+\t\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n+\t\t\t\t\t   stdout, line_termination);\n+\t\tstrbuf_setlen(base, baselen);\n+\t\treturn recurse;\n \t}\n-\tbaselen = base->len;\n-\tstrbuf_addstr(base, pathname);\n-\twrite_name_quoted_relative(base->buf,\n-\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n-\t\t\t\t   stdout, line_termination);\n-\tstrbuf_setlen(base, baselen);\n+\n+\tif (shown_fields >= FIELD_DEFAULT)\n+\t\tshow_default(oid, type, pathname, mode, base);\n+\n \treturn recurse;\n }\n \n@@ -123,12 +168,12 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\t\tLS_SHOW_TREES),\n \t\tOPT_SET_INT('z', NULL, &line_termination,\n \t\t\t    N_(\"terminate entries with NUL byte\"), 0),\n-\t\tOPT_CMDMODE('l', \"long\", &ls_options, N_(\"include object size\"),\n-\t\t\t    LS_SHOW_SIZE),\n-\t\tOPT_CMDMODE(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n-\t\t\t    LS_NAME_ONLY),\n-\t\tOPT_CMDMODE(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n-\t\t\t    LS_NAME_ONLY),\n+\t\tOPT_CMDMODE('l', \"long\", &cmdmode, N_(\"include object size\"),\n+\t\t\t    MODE_LONG),\n+\t\tOPT_CMDMODE(0, \"name-only\", &cmdmode, N_(\"list only filenames\"),\n+\t\t\t    MODE_NAME_ONLY),\n+\t\tOPT_CMDMODE(0, \"name-status\", &cmdmode, N_(\"list only filenames\"),\n+\t\t\t    MODE_NAME_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n \t\t\t    N_(\"use full path names\"), 0),\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n@@ -159,6 +204,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tif (get_oid(argv[0], &oid))\n \t\tdie(\"Not a valid object name %s\", argv[0]);\n \n+\tparse_shown_fields();\n+\n \t/*\n \t * show_recursive() rolls its own matching code and is\n \t * generally ignorant of 'struct pathspec'. The magic mask\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451709","messageId":"85d0b0da1d3b6e8e099a7b78acca57e6a0019a1f.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 09/16] ls-tree: introduce struct \"show_tree_data\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:23Z","receivedAt":"2022-03-21T07:34:16Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\n\"show_tree_data\" is a struct that packages the necessary fields for\n\"show_tree()\". This commit is a pre-prepared commit for supporting\n\"--format\" option and it does not affect any existing functionality.\n\nAt the same time, we also move global \"shown_fields\" into \"show_tree_data\"\n, the purpose of this is to avoid use both global variable and pass-around\nstruct.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 69 +++++++++++++++++++++++++++++------------------\n 1 file changed, 43 insertions(+), 26 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 4364416e1c..0491847b43 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -23,7 +23,6 @@ static int ls_options;\n static struct pathspec pathspec;\n static int chomp_prefix;\n static const char *ls_tree_prefix;\n-static unsigned int shown_fields;\n #define FIELD_PATH_NAME 1\n #define FIELD_SIZE (1 << 1)\n #define FIELD_OBJECT_NAME (1 << 2)\n@@ -32,6 +31,15 @@ static unsigned int shown_fields;\n #define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n #define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n \n+struct show_tree_data {\n+\tunsigned mode;\n+\tenum object_type type;\n+\tconst struct object_id *oid;\n+\tconst char *pathname;\n+\tstruct strbuf *base;\n+\tunsigned int shown_fields;\n+};\n+\n static const  char * const ls_tree_usage[] = {\n \tN_(\"git ls-tree [<options>] <tree-ish> [<path>...]\"),\n \tNULL\n@@ -42,19 +50,19 @@ static enum ls_tree_cmdmode {\n \tMODE_NAME_ONLY,\n } cmdmode;\n \n-static int parse_shown_fields(void)\n+static int parse_shown_fields(unsigned int *shown_fields)\n {\n \tif (cmdmode == MODE_NAME_ONLY) {\n-\t\tshown_fields = FIELD_PATH_NAME;\n+\t\t*shown_fields = FIELD_PATH_NAME;\n \t\treturn 0;\n \t}\n \n \tif (!ls_options || (ls_options & LS_RECURSIVE)\n \t    || (ls_options & LS_SHOW_TREES)\n \t    || (ls_options & LS_TREE_ONLY))\n-\t\tshown_fields = FIELD_DEFAULT;\n+\t\t*shown_fields = FIELD_DEFAULT;\n \tif (cmdmode == MODE_LONG)\n-\t\tshown_fields = FIELD_LONG_DEFAULT;\n+\t\t*shown_fields = FIELD_LONG_DEFAULT;\n \treturn 1;\n }\n \n@@ -88,17 +96,15 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n \treturn 0;\n }\n \n-static int show_default(const struct object_id *oid, enum object_type type,\n-\t\t\tconst char *pathname, unsigned mode,\n-\t\t\tstruct strbuf *base)\n+static int show_default(struct show_tree_data *data)\n {\n-\tsize_t baselen = base->len;\n+\tsize_t baselen = data->base->len;\n \n-\tif (shown_fields & FIELD_SIZE) {\n+\tif (data->shown_fields & FIELD_SIZE) {\n \t\tchar size_text[24];\n-\t\tif (type == OBJ_BLOB) {\n+\t\tif (data->type == OBJ_BLOB) {\n \t\t\tunsigned long size;\n-\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n+\t\t\tif (oid_object_info(the_repository, data->oid, &size) == OBJ_BAD)\n \t\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n \t\t\telse\n \t\t\t\txsnprintf(size_text, sizeof(size_text),\n@@ -106,18 +112,18 @@ static int show_default(const struct object_id *oid, enum object_type type,\n \t\t} else {\n \t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n \t\t}\n-\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n-\t\tfind_unique_abbrev(oid, abbrev), size_text);\n+\t\tprintf(\"%06o %s %s %7s\\t\", data->mode, type_name(data->type),\n+\t\tfind_unique_abbrev(data->oid, abbrev), size_text);\n \t} else {\n-\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n-\t\tfind_unique_abbrev(oid, abbrev));\n+\t\tprintf(\"%06o %s %s\\t\", data->mode, type_name(data->type),\n+\t\tfind_unique_abbrev(data->oid, abbrev));\n \t}\n-\tbaselen = base->len;\n-\tstrbuf_addstr(base, pathname);\n-\twrite_name_quoted_relative(base->buf,\n+\tbaselen = data->base->len;\n+\tstrbuf_addstr(data->base, data->pathname);\n+\twrite_name_quoted_relative(data->base->buf,\n \t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n \t\t\t\t   line_termination);\n-\tstrbuf_setlen(base, baselen);\n+\tstrbuf_setlen(data->base, baselen);\n \treturn 1;\n }\n \n@@ -127,6 +133,15 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tint recurse = 0;\n \tsize_t baselen;\n \tenum object_type type = object_type(mode);\n+\tunsigned int shown_fields = *(unsigned int *)context;\n+\tstruct show_tree_data data = {\n+\t\t.mode = mode,\n+\t\t.type = type,\n+\t\t.oid = oid,\n+\t\t.pathname = pathname,\n+\t\t.base = base,\n+\t\t.shown_fields = shown_fields,\n+\t};\n \n \tif (type == OBJ_BLOB) {\n \t\tif (ls_options & LS_TREE_ONLY)\n@@ -148,8 +163,8 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\treturn recurse;\n \t}\n \n-\tif (shown_fields >= FIELD_DEFAULT)\n-\t\tshow_default(oid, type, pathname, mode, base);\n+\tif (shown_fields>= FIELD_DEFAULT)\n+\t\tshow_default(&data);\n \n \treturn recurse;\n }\n@@ -159,6 +174,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tstruct object_id oid;\n \tstruct tree *tree;\n \tint i, full_tree = 0;\n+\tunsigned int shown_fields = 0;\n \tconst struct option ls_tree_options[] = {\n \t\tOPT_BIT('d', NULL, &ls_options, N_(\"only show trees\"),\n \t\t\tLS_TREE_ONLY),\n@@ -204,8 +220,6 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tif (get_oid(argv[0], &oid))\n \t\tdie(\"Not a valid object name %s\", argv[0]);\n \n-\tparse_shown_fields();\n-\n \t/*\n \t * show_recursive() rolls its own matching code and is\n \t * generally ignorant of 'struct pathspec'. The magic mask\n@@ -222,6 +236,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \ttree = parse_tree_indirect(&oid);\n \tif (!tree)\n \t\tdie(\"not a tree object\");\n-\treturn !!read_tree(the_repository, tree,\n-\t\t\t   &pathspec, show_tree, NULL);\n+\n+\tparse_shown_fields(&shown_fields);\n+\n+\treturn !!read_tree(the_repository, tree, &pathspec, show_tree,\n+\t\t\t   &shown_fields);\n }\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451710","messageId":"eddcf903ada6c598cce1080fefb87d2115b80455.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 10/16] cocci: allow padding with `strbuf_addf()`","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:24Z","receivedAt":"2022-03-21T07:34:19Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nA convenient way to pad strings is to use something like\n`strbuf_addf(&buf, \"%20s\", \"Hello, world!\")`.\n\nHowever, the Coccinelle rule that forbids a format `\"%s\"` with a\nconstant string argument cast too wide a net, and also forbade such\npadding.\n\nThe original rule was introduced by commit:\n\n    28c23cd4c39 (strbuf.cocci: suggest strbuf_addbuf() to add one strbuf to an other, 2019-01-25)\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n contrib/coccinelle/strbuf.cocci | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/coccinelle/strbuf.cocci b/contrib/coccinelle/strbuf.cocci\nindex d9ada69b43..0970d98ad7 100644\n--- a/contrib/coccinelle/strbuf.cocci\n+++ b/contrib/coccinelle/strbuf.cocci\n@@ -15,7 +15,7 @@ constant fmt !~ \"%\";\n @@\n expression E;\n struct strbuf SB;\n-format F =~ \"s\";\n+format F =~ \"^s$\";\n @@\n - strbuf_addf(E, \"%@F@\", SB.buf);\n + strbuf_addbuf(E, &SB);\n@@ -23,7 +23,7 @@ format F =~ \"s\";\n @@\n expression E;\n struct strbuf *SBP;\n-format F =~ \"s\";\n+format F =~ \"^s$\";\n @@\n - strbuf_addf(E, \"%@F@\", SBP->buf);\n + strbuf_addbuf(E, SBP);\n@@ -44,7 +44,7 @@ struct strbuf *SBP;\n \n @@\n expression E1, E2;\n-format F =~ \"s\";\n+format F =~ \"^s$\";\n @@\n - strbuf_addf(E1, \"%@F@\", E2);\n + strbuf_addstr(E1, E2);\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451711","messageId":"7451242daae018c629a62cf2fdb5631b556ad3b8.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 11/16] ls-tree: introduce \"--format\" option","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:25Z","receivedAt":"2022-03-21T07:34:21Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nAdd a --format option to ls-tree. It has an existing default output,\nand then --long and --name-only options to emit the default output\nalong with the objectsize and, or to only emit object paths.\n\nRather than add --type-only, --object-only etc. we can just support a\n--format using a strbuf_expand() similar to \"for-each-ref\n--format\". We might still add such options in the future for\nconvenience.\n\nThe --format implementation is slower than the existing code, but this\nchange does not cause any performance regressions. We'll leave the\nexisting show_tree() unchanged, and only run show_tree_fmt() in if\na --format different than the hardcoded built-in ones corresponding to\nthe existing modes is provided.\n\nI.e. something like the \"--long\" output would be much slower with\nthis, mainly due to how we need to allocate various things to do with\nquote.c instead of spewing the output directly to stdout.\n\nThe new option of '--format' comes from Ævar Arnfjörð Bjarmasonn's\nidea and suggestion, this commit makes modifications in terms of the\noriginal discussion on community [1].\n\nIn [1] there was a \"GIT_TEST_LS_TREE_FORMAT_BACKEND\" variable to\nensure that we had test coverage for passing tests that would\notherwise use show_tree() through show_tree_fmt(), and thus that the\nformatting mechanism could handle all the same cases as the\nnon-formatting options.\n\nSomewhere in subsequent re-rolls of that we seem to have drifted away\nfrom what the goal of these tests should be. We're trying to ensure\ncorrectness of show_tree_fmt(). We can't tell if we \"hit [the]\nfast-path\" here, and instead of having an explicit test for that, we\ncan just add it to something our \"test_ls_tree_format\" tests for.\n\nHere is the statistics about performance tests:\n\n1. Default format (hitten the builtin formats):\n\n    \"git ls-tree <tree-ish>\" vs \"--format='%(mode) %(type) %(object)%x09%(file)'\"\n\n    $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r HEAD\"\n    Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD\n    Time (mean ± σ):     105.2 ms ±   3.3 ms    [User: 84.3 ms, System: 20.8 ms]\n    Range (min … max):    99.2 ms … 113.2 ms    28 runs\n\n    $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object)%x09%(file)'  HEAD\"\n    Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object)%x09%(file)'  HEAD\n    Time (mean ± σ):     106.4 ms ±   2.7 ms    [User: 86.1 ms, System: 20.2 ms]\n    Range (min … max):   100.2 ms … 110.5 ms    29 runs\n\n2. Default format includes object size (hitten the builtin formats):\n\n    \"git ls-tree -l <tree-ish>\" vs \"--format='%(mode) %(type) %(object) %(size:padded)%x09%(file)'\"\n\n    $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r -l HEAD\"\n    Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD\n    Time (mean ± σ):     335.1 ms ±   6.5 ms    [User: 304.6 ms, System: 30.4 ms]\n    Range (min … max):   327.5 ms … 348.4 ms    10 runs\n\n    $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object) %(size:padded)%x09%(file)'  HEAD\"\n    Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object) %(size:padded)%x09%(file)'  HEAD\n    Time (mean ± σ):     337.2 ms ±   8.2 ms    [User: 309.2 ms, System: 27.9 ms]\n    Range (min … max):   328.8 ms … 349.4 ms    10 runs\n\nLinks:\n\t[1] https://public-inbox.org/git/RFC-patch-6.7-eac299f06ff-20211217T131635Z-avarab@gmail.com/\n\t[2] https://lore.kernel.org/git/cb717d08be87e3239117c6c667cb32caabaad33d.1646390152.git.dyroneteng@gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n Documentation/git-ls-tree.txt |  59 ++++++++++++++--\n builtin/ls-tree.c             | 129 +++++++++++++++++++++++++++++++++-\n t/t3104-ls-tree-format.sh     |  67 ++++++++++++++++++\n 3 files changed, 248 insertions(+), 7 deletions(-)\n create mode 100755 t/t3104-ls-tree-format.sh\n\ndiff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt\nindex db02d6d79a..db29a9efb5 100644\n--- a/Documentation/git-ls-tree.txt\n+++ b/Documentation/git-ls-tree.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git ls-tree' [-d] [-r] [-t] [-l] [-z]\n-\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]]\n+\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n \t    <tree-ish> [<path>...]\n \n DESCRIPTION\n@@ -74,6 +74,16 @@ OPTIONS\n \tDo not limit the listing to the current working directory.\n \tImplies --full-name.\n \n+--format=<format>::\n+\tA string that interpolates `%(fieldname)` from the result\n+\tbeing shown. It also interpolates `%%` to `%`, and\n+\t`%xx` where `xx` are hex digits interpolates to character\n+\twith hex code `xx`; for example `%00` interpolates to\n+\t`\\0` (NUL), `%09` to `\\t` (TAB) and `%0a` to `\\n` (LF).\n+\tWhen specified, `--format` cannot be combined with other\n+\tformat-altering options, including `--long`, `--name-only`\n+\tand `--object-only`.\n+\n [<path>...]::\n \tWhen paths are given, show them (note that this isn't really raw\n \tpathnames, but rather a list of patterns to match).  Otherwise\n@@ -82,16 +92,29 @@ OPTIONS\n \n Output Format\n -------------\n-        <mode> SP <type> SP <object> TAB <file>\n+\n+The output format of `ls-tree` is determined by either the `--format`\n+option, or other format-altering options such as `--name-only` etc.\n+(see `--format` above).\n+\n+The use of certain `--format` directives is equivalent to using those\n+options, but invoking the full formatting machinery can be slower than\n+using an appropriate formatting option.\n+\n+In cases where the `--format` would exactly map to an existing option\n+`ls-tree` will use the appropriate faster path. Thus the default format\n+is equivalent to:\n+\n+        %(objectmode) %(objecttype) %(objectname)%x09%(path)\n \n This output format is compatible with what `--index-info --stdin` of\n 'git update-index' expects.\n \n When the `-l` option is used, format changes to\n \n-        <mode> SP <type> SP <object> SP <object size> TAB <file>\n+        %(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\n \n-Object size identified by <object> is given in bytes, and right-justified\n+Object size identified by <objectname> is given in bytes, and right-justified\n with minimum width of 7 characters.  Object size is given only for blobs\n (file) entries; for other entries `-` character is used in place of size.\n \n@@ -100,6 +123,34 @@ quoted as explained for the configuration variable `core.quotePath`\n (see linkgit:git-config[1]).  Using `-z` the filename is output\n verbatim and the line is terminated by a NUL byte.\n \n+Customized format:\n+\n+It is possible to print in a custom format by using the `--format` option,\n+which is able to interpolate different fields using a `%(fieldname)` notation.\n+For example, if you only care about the \"objectname\" and \"path\" fields, you\n+can execute with a specific \"--format\" like\n+\n+        git ls-tree --format='%(objectname) %(path)' <tree-ish>\n+\n+FIELD NAMES\n+-----------\n+\n+Various values from structured fields can be used to interpolate\n+into the resulting output. For each outputing line, the following\n+names can be used:\n+\n+objectmode::\n+\tThe mode of the object.\n+objecttype::\n+\tThe type of the object (`blob` or `tree`).\n+objectname::\n+\tThe name of the object.\n+objectsize[:padded]::\n+\tThe size of the object (\"-\" if it's a tree).\n+\tIt also supports a padded format of size with \"%(size:padded)\".\n+path::\n+\tThe pathname of the object.\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 0491847b43..182705e894 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -30,7 +30,10 @@ static const char *ls_tree_prefix;\n #define FIELD_MODE (1 << 4)\n #define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n #define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n-\n+static const char *format;\n+static const char *default_format = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\";\n+static const char *long_format = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\";\n+static const char *name_only_format = \"%(path)\";\n struct show_tree_data {\n \tunsigned mode;\n \tenum object_type type;\n@@ -50,6 +53,72 @@ static enum ls_tree_cmdmode {\n \tMODE_NAME_ONLY,\n } cmdmode;\n \n+static void expand_objectsize(struct strbuf *line, const struct object_id *oid,\n+\t\t\t      const enum object_type type, unsigned int padded)\n+{\n+\tif (type == OBJ_BLOB) {\n+\t\tunsigned long size;\n+\t\tif (oid_object_info(the_repository, oid, &size) < 0)\n+\t\t\tdie(_(\"could not get object info about '%s'\"),\n+\t\t\t    oid_to_hex(oid));\n+\t\tif (padded)\n+\t\t\tstrbuf_addf(line, \"%7\"PRIuMAX, (uintmax_t)size);\n+\t\telse\n+\t\t\tstrbuf_addf(line, \"%\"PRIuMAX, (uintmax_t)size);\n+\t} else if (padded) {\n+\t\tstrbuf_addf(line, \"%7s\", \"-\");\n+\t} else {\n+\t\tstrbuf_addstr(line, \"-\");\n+\t}\n+}\n+\n+static size_t expand_show_tree(struct strbuf *sb, const char *start,\n+\t\t\t       void *context)\n+{\n+\tstruct show_tree_data *data = context;\n+\tconst char *end;\n+\tconst char *p;\n+\tunsigned int errlen;\n+\tsize_t len = strbuf_expand_literal_cb(sb, start, NULL);\n+\n+\tif (len)\n+\t\treturn len;\n+\tif (*start != '(')\n+\t\tdie(_(\"bad ls-tree format: element '%s' does not start with '('\"), start);\n+\n+\tend = strchr(start + 1, ')');\n+\tif (!end)\n+\t\tdie(_(\"bad ls-tree format: element '%s' does not end in ')'\"), start);\n+\n+\tlen = end - start + 1;\n+\tif (skip_prefix(start, \"(objectmode)\", &p)) {\n+\t\tstrbuf_addf(sb, \"%06o\", data->mode);\n+\t} else if (skip_prefix(start, \"(objecttype)\", &p)) {\n+\t\tstrbuf_addstr(sb, type_name(data->type));\n+\t} else if (skip_prefix(start, \"(objectsize:padded)\", &p)) {\n+\t\texpand_objectsize(sb, data->oid, data->type, 1);\n+\t} else if (skip_prefix(start, \"(objectsize)\", &p)) {\n+\t\texpand_objectsize(sb, data->oid, data->type, 0);\n+\t} else if (skip_prefix(start, \"(objectname)\", &p)) {\n+\t\tstrbuf_add_unique_abbrev(sb, data->oid, abbrev);\n+\t} else if (skip_prefix(start, \"(path)\", &p)) {\n+\t\tconst char *name = data->base->buf;\n+\t\tconst char *prefix = chomp_prefix ? ls_tree_prefix : NULL;\n+\t\tstruct strbuf quoted = STRBUF_INIT;\n+\t\tstruct strbuf sbuf = STRBUF_INIT;\n+\t\tstrbuf_addstr(data->base, data->pathname);\n+\t\tname = relative_path(data->base->buf, prefix, &sbuf);\n+\t\tquote_c_style(name, &quoted, NULL, 0);\n+\t\tstrbuf_addbuf(sb, &quoted);\n+\t\tstrbuf_release(&sbuf);\n+\t\tstrbuf_release(&quoted);\n+\t} else {\n+\t\terrlen = (unsigned long)len;\n+\t\tdie(_(\"bad ls-tree format: %%%.*s\"), errlen, start);\n+\t}\n+\treturn len;\n+}\n+\n static int parse_shown_fields(unsigned int *shown_fields)\n {\n \tif (cmdmode == MODE_NAME_ONLY) {\n@@ -96,6 +165,38 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n \treturn 0;\n }\n \n+static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,\n+\t\t\t const char *pathname, unsigned mode, void *context)\n+{\n+\tsize_t baselen;\n+\tint recurse = 0;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tenum object_type type = object_type(mode);\n+\n+\tstruct show_tree_data data = {\n+\t\t.mode = mode,\n+\t\t.type = type,\n+\t\t.oid = oid,\n+\t\t.pathname = pathname,\n+\t\t.base = base,\n+\t};\n+\n+\tif (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))\n+\t\trecurse = READ_TREE_RECURSIVE;\n+\tif (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES))\n+\t\treturn recurse;\n+\tif (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))\n+\t\treturn 0;\n+\n+\tbaselen = base->len;\n+\tstrbuf_expand(&sb, format, expand_show_tree, &data);\n+\tstrbuf_addch(&sb, line_termination);\n+\tfwrite(sb.buf, sb.len, 1, stdout);\n+\tstrbuf_release(&sb);\n+\tstrbuf_setlen(base, baselen);\n+\treturn recurse;\n+}\n+\n static int show_default(struct show_tree_data *data)\n {\n \tsize_t baselen = data->base->len;\n@@ -175,6 +276,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tstruct tree *tree;\n \tint i, full_tree = 0;\n \tunsigned int shown_fields = 0;\n+\tread_tree_fn_t fn = show_tree;\n \tconst struct option ls_tree_options[] = {\n \t\tOPT_BIT('d', NULL, &ls_options, N_(\"only show trees\"),\n \t\t\tLS_TREE_ONLY),\n@@ -195,6 +297,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n \t\t\t N_(\"list entire tree; not just current directory \"\n \t\t\t    \"(implies --full-name)\")),\n+\t\tOPT_STRING_F(0, \"format\", &format, N_(\"format\"),\n+\t\t\t\t\t N_(\"format to use for the output\"),\n+\t\t\t\t\t PARSE_OPT_NONEG),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_END()\n \t};\n@@ -215,6 +320,10 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t    ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))\n \t\tls_options |= LS_SHOW_TREES;\n \n+\tif (format && cmdmode)\n+\t\tusage_msg_opt(\n+\t\t\t_(\"--format can't be combined with other format-altering options\"),\n+\t\t\tls_tree_usage, ls_tree_options);\n \tif (argc < 1)\n \t\tusage_with_options(ls_tree_usage, ls_tree_options);\n \tif (get_oid(argv[0], &oid))\n@@ -239,6 +348,20 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \n \tparse_shown_fields(&shown_fields);\n \n-\treturn !!read_tree(the_repository, tree, &pathspec, show_tree,\n-\t\t\t   &shown_fields);\n+\t/*\n+\t * The generic show_tree_fmt() is slower than show_tree(), so\n+\t * take the fast path if possible.\n+\t */\n+\tif (format && (!strcmp(format, default_format))) {\n+\t\tfn = show_tree;\n+\t} else if (format && (!strcmp(format, long_format))) {\n+\t\tshown_fields = shown_fields | FIELD_SIZE;\n+\t\tfn = show_tree;\n+\t} else if (format && (!strcmp(format, name_only_format))) {\n+\t\tshown_fields = FIELD_PATH_NAME;\n+\t\tfn = show_tree;\n+\t} else if (format)\n+\t\tfn = show_tree_fmt;\n+\n+\treturn !!read_tree(the_repository, tree, &pathspec, fn, &shown_fields);\n }\ndiff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh\nnew file mode 100755\nindex 0000000000..7f1eb699d3\n--- /dev/null\n+++ b/t/t3104-ls-tree-format.sh\n@@ -0,0 +1,67 @@\n+#!/bin/sh\n+\n+test_description='ls-tree --format'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n+. ./test-lib.sh\n+\n+test_expect_success 'ls-tree --format usage' '\n+\ttest_expect_code 129 git ls-tree --format=fmt -l HEAD &&\n+\ttest_expect_code 129 git ls-tree --format=fmt --name-only HEAD &&\n+\ttest_expect_code 129 git ls-tree --format=fmt --name-status HEAD\n+'\n+\n+test_expect_success 'setup' '\n+\tmkdir dir &&\n+\ttest_commit dir/sub-file &&\n+\ttest_commit top-file\n+'\n+\n+test_ls_tree_format () {\n+\tformat=$1 &&\n+\topts=$2 &&\n+\tfmtopts=$3 &&\n+\tshift 2 &&\n+\n+\ttest_expect_success \"ls-tree '--format=<$format>' is like options '$opts $fmtopts'\" '\n+\t\tgit ls-tree $opts -r HEAD >expect &&\n+\t\tgit ls-tree --format=\"$format\" -r $fmtopts HEAD >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+\n+\ttest_expect_success \"ls-tree '--format=<$format>' on optimized v.s. non-optimized path\" '\n+\t\tgit ls-tree --format=\"$format\" -r $fmtopts HEAD >expect &&\n+\t\tgit ls-tree --format=\"> $format\" -r $fmtopts HEAD >actual.raw &&\n+\t\tsed \"s/^> //\" >actual <actual.raw &&\n+\t\ttest_cmp expect actual\n+\t'\n+}\n+\n+test_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"\"\n+\n+test_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\" \\\n+\t\"--long\"\n+\n+test_ls_tree_format \\\n+\t\"%(path)\" \\\n+\t\"--name-only\"\n+\n+test_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"-t\" \\\n+\t\"-t\"\n+\n+test_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"--full-name\" \\\n+\t\"--full-name\"\n+\n+test_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"--full-tree\" \\\n+\t\"--full-tree\"\n+\n+test_done\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451712","messageId":"2bcd08ebd0a0126f50be677884a02603b9a96a2d.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 12/16] ls-tree: support --object-only option for \"git-ls-tree\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:26Z","receivedAt":"2022-03-21T07:34:23Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"'--object-only' is an alias for '--format=%(objectname)'. It cannot\nbe used together other format-altering options like '--name-only',\n'--long' or '--format', they are mutually exclusive.\n\nThe \"--name-only\" option outputs <filepath> only. Likewise, <objectName>\nis another high frequency used field, so implement '--object-only' option\nwill bring intuitive and clear semantics for this scenario. Using\n'--format=%(objectname)' we can achieve a similar effect, but the former\nis with a lower learning cost(without knowing the format requirement\nof '--format' option).\n\nEven so, if a user is prefer to use \"--format=%(objectname)\", this is entirely\nwelcome because they are not only equivalent in function, but also have almost\nidentical performance. The reason is this commit also add the specific of\n\"--format=%(objectname)\" to the current fast-pathes (builtin formats) to\navoid running unnecessary parsing mechanisms.\n\nThe following performance benchmarks are based on torvalds/linux.git:\n\n  When hit the fast-path:\n\n      Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --object-only HEAD\n        Time (mean ± σ):      83.6 ms ±   2.0 ms    [User: 59.4 ms, System: 24.1 ms]\n        Range (min … max):    80.4 ms …  87.2 ms    35 runs\n\n      Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(objectname)' HEAD\n        Time (mean ± σ):      84.1 ms ±   1.8 ms    [User: 61.7 ms, System: 22.3 ms]\n        Range (min … max):    80.9 ms …  87.5 ms    35 runs\n\n  But for a customized format, it will be slower:\n\n       Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='oid: %(objectname)' HEAD\n         Time (mean ± σ):      96.5 ms ±   2.5 ms    [User: 72.9 ms, System: 23.5 ms]\n  \t Range (min … max):    93.1 ms … 104.1 ms    31 runs\n\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n Documentation/git-ls-tree.txt | 11 ++++++++++-\n builtin/ls-tree.c             | 17 ++++++++++++++++-\n t/t3103-ls-tree-misc.sh       |  7 +++++--\n t/t3104-ls-tree-format.sh     |  9 +++++++++\n 4 files changed, 40 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt\nindex db29a9efb5..1aea998e72 100644\n--- a/Documentation/git-ls-tree.txt\n+++ b/Documentation/git-ls-tree.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git ls-tree' [-d] [-r] [-t] [-l] [-z]\n-\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n+\t    [--name-only] [--name-status] [--object-only] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n \t    <tree-ish> [<path>...]\n \n DESCRIPTION\n@@ -59,6 +59,15 @@ OPTIONS\n --name-only::\n --name-status::\n \tList only filenames (instead of the \"long\" output), one per line.\n+\tCannot be combined with `--object-only`.\n+\n+--object-only::\n+\tList only names of the objects, one per line. Cannot be combined\n+\twith `--name-only` or `--name-status`.\n+\tThis is equivalent to specifying `--format='%(objectname)'`, but\n+\tfor both this option and that exact format the command takes a\n+\thand-optimized codepath instead of going through the generic\n+\tformatting mechanism.\n \n --abbrev[=<n>]::\n \tInstead of showing the full 40-byte hexadecimal object\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 182705e894..1aa65cc87f 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -34,6 +34,7 @@ static const char *format;\n static const char *default_format = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\";\n static const char *long_format = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\";\n static const char *name_only_format = \"%(path)\";\n+static const char *object_only_format = \"%(objectname)\";\n struct show_tree_data {\n \tunsigned mode;\n \tenum object_type type;\n@@ -51,6 +52,7 @@ static const  char * const ls_tree_usage[] = {\n static enum ls_tree_cmdmode {\n \tMODE_LONG = 1,\n \tMODE_NAME_ONLY,\n+\tMODE_OBJECT_ONLY,\n } cmdmode;\n \n static void expand_objectsize(struct strbuf *line, const struct object_id *oid,\n@@ -125,7 +127,10 @@ static int parse_shown_fields(unsigned int *shown_fields)\n \t\t*shown_fields = FIELD_PATH_NAME;\n \t\treturn 0;\n \t}\n-\n+\tif (cmdmode == MODE_OBJECT_ONLY) {\n+\t\t*shown_fields = FIELD_OBJECT_NAME;\n+\t\treturn 0;\n+\t}\n \tif (!ls_options || (ls_options & LS_RECURSIVE)\n \t    || (ls_options & LS_SHOW_TREES)\n \t    || (ls_options & LS_TREE_ONLY))\n@@ -254,6 +259,11 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\treturn recurse;\n \t}\n \n+\tif (shown_fields == FIELD_OBJECT_NAME) {\n+\t\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n+\t\treturn recurse;\n+\t}\n+\n \tif (shown_fields == FIELD_PATH_NAME) {\n \t\tbaselen = base->len;\n \t\tstrbuf_addstr(base, pathname);\n@@ -292,6 +302,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\t\t    MODE_NAME_ONLY),\n \t\tOPT_CMDMODE(0, \"name-status\", &cmdmode, N_(\"list only filenames\"),\n \t\t\t    MODE_NAME_ONLY),\n+\t\tOPT_CMDMODE(0, \"object-only\", &cmdmode, N_(\"list only objects\"),\n+\t\t\t    MODE_OBJECT_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n \t\t\t    N_(\"use full path names\"), 0),\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n@@ -360,6 +372,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t} else if (format && (!strcmp(format, name_only_format))) {\n \t\tshown_fields = FIELD_PATH_NAME;\n \t\tfn = show_tree;\n+\t} else if (format && (!strcmp(format, object_only_format))) {\n+\t\tshown_fields = FIELD_OBJECT_NAME;\n+\t\tfn = show_tree;\n \t} else if (format)\n \t\tfn = show_tree_fmt;\n \ndiff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh\nindex d9d7fa932f..d979c0df5d 100755\n--- a/t/t3103-ls-tree-misc.sh\n+++ b/t/t3103-ls-tree-misc.sh\n@@ -25,11 +25,14 @@ test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '\n \n for opts in \\\n \t\"--name-only --long\" \\\n-\t\"--name-status --long\"\n+\t\"--name-status --long\" \\\n+\t\"--name-only --object-only\" \\\n+\t\"--name-status --object-only\" \\\n+\t\"--object-only --long\" \\\n+\t\"--object-only --format\"\n do\n \ttest_expect_success \"usage: incompatible options: $opts\" '\n \t\ttest_expect_code 129 git ls-tree $opts $tree\n     '\n done\n-\n test_done\ndiff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh\nindex 7f1eb699d3..0769a933d6 100755\n--- a/t/t3104-ls-tree-format.sh\n+++ b/t/t3104-ls-tree-format.sh\n@@ -49,6 +49,15 @@ test_ls_tree_format \\\n \t\"%(path)\" \\\n \t\"--name-only\"\n \n+test_ls_tree_format \\\n+\t\"%(objectname)\" \\\n+\t\"--object-only\"\n+\n+test_ls_tree_format \\\n+\t\"%(objectname)\" \\\n+\t\"--object-only --abbrev\" \\\n+\t\"--abbrev\"\n+\n test_ls_tree_format \\\n \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n \t\"-t\" \\\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451713","messageId":"a5c73de057255cf1cfcf4695db2ae8870108da91.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 13/16] ls-tree tests: add tests for --name-status","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:27Z","receivedAt":"2022-03-21T07:34:24Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nThe --name-status synonym for --name-only added in\nc639a5548a5 (ls-tree: --name-only, 2005-12-01) had no tests, let's\nmake sure it works the same way as its sibling.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n t/t3101-ls-tree-dirname.sh | 55 ++++++++++++++++++++------------------\n 1 file changed, 29 insertions(+), 26 deletions(-)\n\ndiff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh\nindex 05fde64225..217006d1bf 100755\n--- a/t/t3101-ls-tree-dirname.sh\n+++ b/t/t3101-ls-tree-dirname.sh\n@@ -201,31 +201,34 @@ EOF\n \ttest_cmp expected check\n '\n \n-test_expect_success 'ls-tree --name-only' '\n-\tgit ls-tree --name-only $tree >current &&\n-\tcat >expected <<\\EOF &&\n-1.txt\n-2.txt\n-path0\n-path1\n-path2\n-path3\n-EOF\n-\ttest_output\n-'\n-\n-test_expect_success 'ls-tree --name-only -r' '\n-\tgit ls-tree --name-only -r $tree >current &&\n-\tcat >expected <<\\EOF &&\n-1.txt\n-2.txt\n-path0/a/b/c/1.txt\n-path1/b/c/1.txt\n-path2/1.txt\n-path3/1.txt\n-path3/2.txt\n-EOF\n-\ttest_output\n-'\n+for opt in --name-only --name-status\n+do\n+\ttest_expect_success \"ls-tree $opt\" '\n+\t\tgit ls-tree $opt $tree >current &&\n+\t\tcat >expected <<-\\EOF &&\n+\t\t1.txt\n+\t\t2.txt\n+\t\tpath0\n+\t\tpath1\n+\t\tpath2\n+\t\tpath3\n+\t\tEOF\n+\t\ttest_output\n+\t'\n+\n+\ttest_expect_success \"ls-tree $opt -r\" '\n+\t\tgit ls-tree $opt -r $tree >current &&\n+\t\tcat >expected <<-\\EOF &&\n+\t\t1.txt\n+\t\t2.txt\n+\t\tpath0/a/b/c/1.txt\n+\t\tpath1/b/c/1.txt\n+\t\tpath2/1.txt\n+\t\tpath3/1.txt\n+\t\tpath3/2.txt\n+\t\tEOF\n+\t\ttest_output\n+\t'\n+done\n \n test_done\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451714","messageId":"89402a851806aabfcde6ebcbcb2bc16826d7ed4e.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 14/16] ls-tree: detect and error on --name-only --name-status","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:28Z","receivedAt":"2022-03-21T07:34:28Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nThe --name-only and --name-status options are synonyms, but let's\ndetect and error if both are provided.\n\nIn addition let's add explicit --format tests for the combination of\nthese various options.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c       | 11 ++++++++++-\n t/t3103-ls-tree-misc.sh | 15 +++++++++------\n 2 files changed, 19 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 1aa65cc87f..a271941540 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -52,6 +52,7 @@ static const  char * const ls_tree_usage[] = {\n static enum ls_tree_cmdmode {\n \tMODE_LONG = 1,\n \tMODE_NAME_ONLY,\n+\tMODE_NAME_STATUS,\n \tMODE_OBJECT_ONLY,\n } cmdmode;\n \n@@ -301,7 +302,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\tOPT_CMDMODE(0, \"name-only\", &cmdmode, N_(\"list only filenames\"),\n \t\t\t    MODE_NAME_ONLY),\n \t\tOPT_CMDMODE(0, \"name-status\", &cmdmode, N_(\"list only filenames\"),\n-\t\t\t    MODE_NAME_ONLY),\n+\t\t\t    MODE_NAME_STATUS),\n \t\tOPT_CMDMODE(0, \"object-only\", &cmdmode, N_(\"list only objects\"),\n \t\t\t    MODE_OBJECT_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n@@ -327,6 +328,14 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\tls_tree_prefix = prefix = NULL;\n \t\tchomp_prefix = 0;\n \t}\n+\t/*\n+\t * We wanted to detect conflicts between --name-only and\n+\t * --name-status, but once we're done with that subsequent\n+\t * code should only need to check the primary name.\n+\t */\n+\tif (cmdmode == MODE_NAME_STATUS)\n+\t\tcmdmode = MODE_NAME_ONLY;\n+\n \t/* -d -r should imply -t, but -d by itself should not have to. */\n \tif ( (LS_TREE_ONLY|LS_RECURSIVE) ==\n \t    ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))\ndiff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh\nindex d979c0df5d..7454ebbb00 100755\n--- a/t/t3103-ls-tree-misc.sh\n+++ b/t/t3103-ls-tree-misc.sh\n@@ -24,15 +24,18 @@ test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '\n '\n \n for opts in \\\n-\t\"--name-only --long\" \\\n-\t\"--name-status --long\" \\\n-\t\"--name-only --object-only\" \\\n+\t\"--long --name-only\" \\\n+\t\"--name-only --name-status\" \\\n \t\"--name-status --object-only\" \\\n-\t\"--object-only --long\" \\\n-\t\"--object-only --format\"\n+\t\"--object-only --long\" \n do\n \ttest_expect_success \"usage: incompatible options: $opts\" '\n \t\ttest_expect_code 129 git ls-tree $opts $tree\n-    '\n+\t'\n+\n+\tone_opt=$(echo \"$opts\" | cut -d' '  -f1)\n+\ttest_expect_success \"usage: incompatible options: $one_opt and --format\" '\n+\t\ttest_expect_code 129 git ls-tree $one_opt --format=fmt $tree\n+\t'\n done\n test_done\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451715","messageId":"b8afca193ad7edd64612595742929f245cc340f3.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 15/16] ls-tree: remove FIELD_*, just use MODE_*","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:29Z","receivedAt":"2022-03-21T07:34:29Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nWhen we're picking where we should go in the optimized \"show_tree\"\npath there's no reason for why we need to convert our \"cmdmode\" of\ne.g. MODE_LONG into a FIELD_LONG_DEFAULT. Instead we can simply do\nthose checks in the show_tree() function itself.\n\nLet's also make this code more future-proof by unrolling the hardcoded\nstrmp() if/else if chain into something that checks a new \"static\nstruct\" providing a bidirectional mapping between optimized formats\nand the ls_tree_cmdmode.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 98 ++++++++++++++++++++++-------------------------\n 1 file changed, 46 insertions(+), 52 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex a271941540..3e756b5eee 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -23,25 +23,13 @@ static int ls_options;\n static struct pathspec pathspec;\n static int chomp_prefix;\n static const char *ls_tree_prefix;\n-#define FIELD_PATH_NAME 1\n-#define FIELD_SIZE (1 << 1)\n-#define FIELD_OBJECT_NAME (1 << 2)\n-#define FIELD_TYPE (1 << 3)\n-#define FIELD_MODE (1 << 4)\n-#define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n-#define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n static const char *format;\n-static const char *default_format = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\";\n-static const char *long_format = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\";\n-static const char *name_only_format = \"%(path)\";\n-static const char *object_only_format = \"%(objectname)\";\n struct show_tree_data {\n \tunsigned mode;\n \tenum object_type type;\n \tconst struct object_id *oid;\n \tconst char *pathname;\n \tstruct strbuf *base;\n-\tunsigned int shown_fields;\n };\n \n static const  char * const ls_tree_usage[] = {\n@@ -50,7 +38,8 @@ static const  char * const ls_tree_usage[] = {\n };\n \n static enum ls_tree_cmdmode {\n-\tMODE_LONG = 1,\n+\tMODE_DEFAULT = 0,\n+\tMODE_LONG,\n \tMODE_NAME_ONLY,\n \tMODE_NAME_STATUS,\n \tMODE_OBJECT_ONLY,\n@@ -122,25 +111,6 @@ static size_t expand_show_tree(struct strbuf *sb, const char *start,\n \treturn len;\n }\n \n-static int parse_shown_fields(unsigned int *shown_fields)\n-{\n-\tif (cmdmode == MODE_NAME_ONLY) {\n-\t\t*shown_fields = FIELD_PATH_NAME;\n-\t\treturn 0;\n-\t}\n-\tif (cmdmode == MODE_OBJECT_ONLY) {\n-\t\t*shown_fields = FIELD_OBJECT_NAME;\n-\t\treturn 0;\n-\t}\n-\tif (!ls_options || (ls_options & LS_RECURSIVE)\n-\t    || (ls_options & LS_SHOW_TREES)\n-\t    || (ls_options & LS_TREE_ONLY))\n-\t\t*shown_fields = FIELD_DEFAULT;\n-\tif (cmdmode == MODE_LONG)\n-\t\t*shown_fields = FIELD_LONG_DEFAULT;\n-\treturn 1;\n-}\n-\n static int show_recursive(const char *base, size_t baselen, const char *pathname)\n {\n \tint i;\n@@ -207,7 +177,7 @@ static int show_default(struct show_tree_data *data)\n {\n \tsize_t baselen = data->base->len;\n \n-\tif (data->shown_fields & FIELD_SIZE) {\n+\tif (cmdmode == MODE_LONG) {\n \t\tchar size_text[24];\n \t\tif (data->type == OBJ_BLOB) {\n \t\t\tunsigned long size;\n@@ -240,14 +210,12 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tint recurse = 0;\n \tsize_t baselen;\n \tenum object_type type = object_type(mode);\n-\tunsigned int shown_fields = *(unsigned int *)context;\n \tstruct show_tree_data data = {\n \t\t.mode = mode,\n \t\t.type = type,\n \t\t.oid = oid,\n \t\t.pathname = pathname,\n \t\t.base = base,\n-\t\t.shown_fields = shown_fields,\n \t};\n \n \tif (type == OBJ_BLOB) {\n@@ -260,12 +228,12 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\treturn recurse;\n \t}\n \n-\tif (shown_fields == FIELD_OBJECT_NAME) {\n+\tif (cmdmode == MODE_OBJECT_ONLY) {\n \t\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n \t\treturn recurse;\n \t}\n \n-\tif (shown_fields == FIELD_PATH_NAME) {\n+\tif (cmdmode == MODE_NAME_ONLY) {\n \t\tbaselen = base->len;\n \t\tstrbuf_addstr(base, pathname);\n \t\twrite_name_quoted_relative(base->buf,\n@@ -275,12 +243,40 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\treturn recurse;\n \t}\n \n-\tif (shown_fields>= FIELD_DEFAULT)\n+\tif (cmdmode == MODE_LONG ||\n+\t    (!ls_options || (ls_options & LS_RECURSIVE)\n+\t     || (ls_options & LS_SHOW_TREES)\n+\t     || (ls_options & LS_TREE_ONLY)))\n \t\tshow_default(&data);\n \n \treturn recurse;\n }\n \n+struct ls_tree_cmdmode_to_fmt {\n+\tenum ls_tree_cmdmode mode;\n+\tconst char *const fmt;\n+};\n+\n+static struct ls_tree_cmdmode_to_fmt ls_tree_cmdmode_format[] = {\n+\t{\n+\t\t.mode = MODE_DEFAULT,\n+\t\t.fmt = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\",\n+\t},\n+\t{\n+\t\t.mode = MODE_LONG,\n+\t\t.fmt = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\",\n+\t},\n+\t{\n+\t\t.mode = MODE_NAME_ONLY, /* And MODE_NAME_STATUS */\n+\t\t.fmt = \"%(path)\",\n+\t},\n+\t{\n+\t\t.mode = MODE_OBJECT_ONLY,\n+\t\t.fmt = \"%(objectname)\",\n+\t},\n+\t{ 0 },\n+};\n+\n int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n {\n \tstruct object_id oid;\n@@ -367,25 +363,23 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tif (!tree)\n \t\tdie(\"not a tree object\");\n \n-\tparse_shown_fields(&shown_fields);\n-\n \t/*\n \t * The generic show_tree_fmt() is slower than show_tree(), so\n \t * take the fast path if possible.\n \t */\n-\tif (format && (!strcmp(format, default_format))) {\n-\t\tfn = show_tree;\n-\t} else if (format && (!strcmp(format, long_format))) {\n-\t\tshown_fields = shown_fields | FIELD_SIZE;\n-\t\tfn = show_tree;\n-\t} else if (format && (!strcmp(format, name_only_format))) {\n-\t\tshown_fields = FIELD_PATH_NAME;\n-\t\tfn = show_tree;\n-\t} else if (format && (!strcmp(format, object_only_format))) {\n-\t\tshown_fields = FIELD_OBJECT_NAME;\n-\t\tfn = show_tree;\n-\t} else if (format)\n+\tif (format) {\n+\t\tstruct ls_tree_cmdmode_to_fmt *m2f;\n+\n \t\tfn = show_tree_fmt;\n+\t\tfor (m2f = ls_tree_cmdmode_format; m2f->fmt; m2f++) {\n+\t\t\tif (strcmp(format, m2f->fmt))\n+\t\t\t\tcontinue;\n+\n+\t\t\tcmdmode = m2f->mode;\n+\t\t\tfn = show_tree;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n \n \treturn !!read_tree(the_repository, tree, &pathspec, fn, &shown_fields);\n }\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451716","messageId":"010e3c0eceac0a936a447a6df7ba8c9abb7c77b2.1647846935.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v13 16/16] ls-tree: split up \"fast path\" callbacks","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-21T07:33:30Z","receivedAt":"2022-03-21T07:34:47Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nMake the various if/else in the callbacks for the \"fast path\" a lot\neasier to read by just using common functions for the parts that are\ncommon, and have per-format callbacks for those parts that are\ndifferent.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 198 +++++++++++++++++++++++++++++-----------------\n 1 file changed, 124 insertions(+), 74 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3e756b5eee..52ae71efaa 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -173,108 +173,157 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,\n \treturn recurse;\n }\n \n-static int show_default(struct show_tree_data *data)\n+static int show_tree_common(struct show_tree_data *data, int *recurse,\n+\t\t\t    const struct object_id *oid, struct strbuf *base,\n+\t\t\t    const char *pathname, unsigned mode)\n {\n-\tsize_t baselen = data->base->len;\n-\n-\tif (cmdmode == MODE_LONG) {\n-\t\tchar size_text[24];\n-\t\tif (data->type == OBJ_BLOB) {\n-\t\t\tunsigned long size;\n-\t\t\tif (oid_object_info(the_repository, data->oid, &size) == OBJ_BAD)\n-\t\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n-\t\t\telse\n-\t\t\t\txsnprintf(size_text, sizeof(size_text),\n-\t\t\t\t\t  \"%\" PRIuMAX, (uintmax_t)size);\n-\t\t} else {\n-\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n-\t\t}\n-\t\tprintf(\"%06o %s %s %7s\\t\", data->mode, type_name(data->type),\n-\t\tfind_unique_abbrev(data->oid, abbrev), size_text);\n-\t} else {\n-\t\tprintf(\"%06o %s %s\\t\", data->mode, type_name(data->type),\n-\t\tfind_unique_abbrev(data->oid, abbrev));\n-\t}\n-\tbaselen = data->base->len;\n-\tstrbuf_addstr(data->base, data->pathname);\n-\twrite_name_quoted_relative(data->base->buf,\n-\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n-\t\t\t\t   line_termination);\n-\tstrbuf_setlen(data->base, baselen);\n-\treturn 1;\n-}\n-\n-static int show_tree(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, void *context)\n-{\n-\tint recurse = 0;\n-\tsize_t baselen;\n \tenum object_type type = object_type(mode);\n-\tstruct show_tree_data data = {\n-\t\t.mode = mode,\n-\t\t.type = type,\n-\t\t.oid = oid,\n-\t\t.pathname = pathname,\n-\t\t.base = base,\n-\t};\n+\tint ret = -1;\n+\n+\t*recurse = 0;\n+\tdata->mode = mode;\n+\tdata->type = type;\n+\tdata->oid = oid;\n+\tdata->pathname = pathname;\n+\tdata->base = base;\n \n \tif (type == OBJ_BLOB) {\n \t\tif (ls_options & LS_TREE_ONLY)\n-\t\t\treturn 0;\n+\t\t\tret = 0;\n \t} else if (type == OBJ_TREE &&\n \t\t   show_recursive(base->buf, base->len, pathname)) {\n-\t\trecurse = READ_TREE_RECURSIVE;\n+\t\t*recurse = READ_TREE_RECURSIVE;\n \t\tif (!(ls_options & LS_SHOW_TREES))\n-\t\t\treturn recurse;\n+\t\t\tret = *recurse;\n \t}\n \n-\tif (cmdmode == MODE_OBJECT_ONLY) {\n-\t\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n-\t\treturn recurse;\n-\t}\n+\treturn ret;\n+}\n \n-\tif (cmdmode == MODE_NAME_ONLY) {\n-\t\tbaselen = base->len;\n-\t\tstrbuf_addstr(base, pathname);\n-\t\twrite_name_quoted_relative(base->buf,\n-\t\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n-\t\t\t\t\t   stdout, line_termination);\n-\t\tstrbuf_setlen(base, baselen);\n-\t\treturn recurse;\n+static void show_tree_common_default_long(struct strbuf *base,\n+\t\t\t\t\t  const char *pathname,\n+\t\t\t\t\t  const size_t baselen)\n+{\n+\tstrbuf_addstr(base, pathname);\n+\twrite_name_quoted_relative(base->buf,\n+\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n+\t\t\t\t   line_termination);\n+\tstrbuf_setlen(base, baselen);\n+}\n+\n+static int show_tree_default(const struct object_id *oid, struct strbuf *base,\n+\t\t\t     const char *pathname, unsigned mode,\n+\t\t\t     void *context)\n+{\n+\tint early;\n+\tint recurse;\n+\tstruct show_tree_data data = { 0 };\n+\n+\tearly = show_tree_common(&data, &recurse, oid, base, pathname, mode);\n+\tif (early >= 0)\n+\t\treturn early;\n+\n+\tprintf(\"%06o %s %s\\t\", data.mode, type_name(data.type),\n+\t       find_unique_abbrev(data.oid, abbrev));\n+\tshow_tree_common_default_long(base, pathname, data.base->len);\n+\treturn recurse;\n+}\n+\n+static int show_tree_long(const struct object_id *oid, struct strbuf *base,\n+\t\t\t  const char *pathname, unsigned mode, void *context)\n+{\n+\tint early;\n+\tint recurse;\n+\tstruct show_tree_data data = { 0 };\n+\tchar size_text[24];\n+\n+\tearly = show_tree_common(&data, &recurse, oid, base, pathname, mode);\n+\tif (early >= 0)\n+\t\treturn early;\n+\n+\tif (data.type == OBJ_BLOB) {\n+\t\tunsigned long size;\n+\t\tif (oid_object_info(the_repository, data.oid, &size) == OBJ_BAD)\n+\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n+\t\telse\n+\t\t\txsnprintf(size_text, sizeof(size_text),\n+\t\t\t\t  \"%\" PRIuMAX, (uintmax_t)size);\n+\t} else {\n+\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n \t}\n \n-\tif (cmdmode == MODE_LONG ||\n-\t    (!ls_options || (ls_options & LS_RECURSIVE)\n-\t     || (ls_options & LS_SHOW_TREES)\n-\t     || (ls_options & LS_TREE_ONLY)))\n-\t\tshow_default(&data);\n+\tprintf(\"%06o %s %s %7s\\t\", data.mode, type_name(data.type),\n+\t       find_unique_abbrev(data.oid, abbrev), size_text);\n+\tshow_tree_common_default_long(base, pathname, data.base->len);\n+\treturn 1;\n+}\n \n+static int show_tree_name_only(const struct object_id *oid, struct strbuf *base,\n+\t\t\t       const char *pathname, unsigned mode, void *context)\n+{\n+\tint early;\n+\tint recurse;\n+\tconst size_t baselen = base->len;\n+\tstruct show_tree_data data = { 0 };\n+\n+\tearly = show_tree_common(&data, &recurse, oid, base, pathname, mode);\n+\tif (early >= 0)\n+\t\treturn early;\n+\n+\tstrbuf_addstr(base, pathname);\n+\twrite_name_quoted_relative(base->buf,\n+\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n+\t\t\t\t   stdout, line_termination);\n+\tstrbuf_setlen(base, baselen);\n+\treturn recurse;\n+}\n+\n+static int show_tree_object(const struct object_id *oid, struct strbuf *base,\n+\t\t\t    const char *pathname, unsigned mode, void *context)\n+{\n+\tint early;\n+\tint recurse;\n+\tstruct show_tree_data data = { 0 };\n+\n+\tearly = show_tree_common(&data, &recurse, oid, base, pathname, mode);\n+\tif (early >= 0)\n+\t\treturn early;\n+\n+\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n \treturn recurse;\n }\n \n struct ls_tree_cmdmode_to_fmt {\n \tenum ls_tree_cmdmode mode;\n \tconst char *const fmt;\n+\tread_tree_fn_t fn;\n };\n \n static struct ls_tree_cmdmode_to_fmt ls_tree_cmdmode_format[] = {\n \t{\n \t\t.mode = MODE_DEFAULT,\n \t\t.fmt = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\",\n+\t\t.fn = show_tree_default,\n \t},\n \t{\n \t\t.mode = MODE_LONG,\n \t\t.fmt = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\",\n+\t\t.fn = show_tree_long,\n \t},\n \t{\n \t\t.mode = MODE_NAME_ONLY, /* And MODE_NAME_STATUS */\n \t\t.fmt = \"%(path)\",\n+\t\t.fn = show_tree_name_only,\n \t},\n \t{\n \t\t.mode = MODE_OBJECT_ONLY,\n \t\t.fmt = \"%(objectname)\",\n+\t\t.fn = show_tree_object\n+\t},\n+\t{\n+\t\t/* fallback */\n+\t\t.fn = show_tree_default,\n \t},\n-\t{ 0 },\n };\n \n int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n@@ -283,7 +332,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tstruct tree *tree;\n \tint i, full_tree = 0;\n \tunsigned int shown_fields = 0;\n-\tread_tree_fn_t fn = show_tree;\n+\tread_tree_fn_t fn = NULL;\n \tconst struct option ls_tree_options[] = {\n \t\tOPT_BIT('d', NULL, &ls_options, N_(\"only show trees\"),\n \t\t\tLS_TREE_ONLY),\n@@ -312,6 +361,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_END()\n \t};\n+\tstruct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;\n \n \tgit_config(git_default_config, NULL);\n \tls_tree_prefix = prefix;\n@@ -367,18 +417,18 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t * The generic show_tree_fmt() is slower than show_tree(), so\n \t * take the fast path if possible.\n \t */\n-\tif (format) {\n-\t\tstruct ls_tree_cmdmode_to_fmt *m2f;\n-\n-\t\tfn = show_tree_fmt;\n-\t\tfor (m2f = ls_tree_cmdmode_format; m2f->fmt; m2f++) {\n-\t\t\tif (strcmp(format, m2f->fmt))\n-\t\t\t\tcontinue;\n-\n+\twhile (m2f++) {\n+\t\tif (!m2f->fmt) {\n+\t\t\tfn = format ? show_tree_fmt : show_tree_default;\n+\t\t} else if (format && !strcmp(format, m2f->fmt)) {\n \t\t\tcmdmode = m2f->mode;\n-\t\t\tfn = show_tree;\n-\t\t\tbreak;\n+\t\t\tfn = m2f->fn;\n+\t\t} else if (!format && cmdmode == m2f->mode) {\n+\t\t\tfn = m2f->fn;\n+\t\t} else {\n+\t\t\tcontinue;\n \t\t}\n+\t\tbreak;\n \t}\n \n \treturn !!read_tree(the_repository, tree, &pathspec, fn, &shown_fields);\n-- \n2.34.1.406.g2e0e55130e\n\n"},{"id":"451717","messageId":"220321.86a6djd5m4.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"b8afca193ad7edd64612595742929f245cc340f3.1647846935.git.dyroneteng@gmail.com","subject":"Re: [PATCH v13 15/16] ls-tree: remove FIELD_*, just use MODE_*","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-21T09:15:35Z","receivedAt":"2022-03-21T09:20:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Mar 21 2022, Teng Long wrote:\n\n> From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n> When we're picking where we should go in the optimized \"show_tree\"\n> path there's no reason for why we need to convert our \"cmdmode\" of\n> e.g. MODE_LONG into a FIELD_LONG_DEFAULT. Instead we can simply do\n> those checks in the show_tree() function itself.\n>\n> Let's also make this code more future-proof by unrolling the hardcoded\n> strmp() if/else if chain into something that checks a new \"static\n> struct\" providing a bidirectional mapping between optimized formats\n> and the ls_tree_cmdmode.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> Signed-off-by: Teng Long <dyroneteng@gmail.com>\n\nFor this & any other changes I suggested which amend code introduced\nearlier in the series: Let's just fix that up into the respective\ncommits, if you agree this is a good direction that is.\n\nI.e. if it's good to do Y let's not first do X and then change it to Y,\nwe can just start with Y.\n\nMaybe that means none of these fix-up commitsa are left at the end, and\nthat's OK.\n\n> ---\n>  builtin/ls-tree.c | 98 ++++++++++++++++++++++-------------------------\n>  1 file changed, 46 insertions(+), 52 deletions(-)\n>\n> diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\n> index a271941540..3e756b5eee 100644\n> --- a/builtin/ls-tree.c\n> +++ b/builtin/ls-tree.c\n> @@ -23,25 +23,13 @@ static int ls_options;\n>  static struct pathspec pathspec;\n>  static int chomp_prefix;\n>  static const char *ls_tree_prefix;\n> -#define FIELD_PATH_NAME 1\n> -#define FIELD_SIZE (1 << 1)\n> -#define FIELD_OBJECT_NAME (1 << 2)\n> -#define FIELD_TYPE (1 << 3)\n> -#define FIELD_MODE (1 << 4)\n> -#define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n> -#define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n\nI.e. this whole thing.\n\n>  static const char *format;\n> -static const char *default_format = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\";\n> -static const char *long_format = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\";\n> -static const char *name_only_format = \"%(path)\";\n> -static const char *object_only_format = \"%(objectname)\";\n\nAnd turning this into a loop can go with the initial --format change.\n\n> [...]\n\nAll the changes below seemed related...\n"},{"id":"451718","messageId":"220321.865yo7d5ki.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"010e3c0eceac0a936a447a6df7ba8c9abb7c77b2.1647846935.git.dyroneteng@gmail.com","subject":"Re: [PATCH v13 16/16] ls-tree: split up \"fast path\" callbacks","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-21T09:20:34Z","receivedAt":"2022-03-21T09:21:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Mar 21 2022, Teng Long wrote:\n\n> From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n> Make the various if/else in the callbacks for the \"fast path\" a lot\n> easier to read by just using common functions for the parts that are\n> common, and have per-format callbacks for those parts that are\n> different.\n\nFWIW I didn't do any exhaustive benchmarks of this, but I checked a few\nthings against origin/master on linux.git and all the reported\n\"hyperfine\" timings were the same/within the +/- interval.\n"},{"id":"451719","messageId":"220321.861qyvd5j1.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"a5c73de057255cf1cfcf4695db2ae8870108da91.1647846935.git.dyroneteng@gmail.com","subject":"Re: [PATCH v13 13/16] ls-tree tests: add tests for --name-status","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-21T09:21:21Z","receivedAt":"2022-03-21T09:22:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Mar 21 2022, Teng Long wrote:\n\n> From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n> The --name-status synonym for --name-only added in\n> c639a5548a5 (ls-tree: --name-only, 2005-12-01) had no tests, let's\n> make sure it works the same way as its sibling.\n\nLet's move this to the front of the series, or rather have any test-only\nchanges go first. Then it's clear with:\n\n    git rebase -i -x 'make test' @{u}\n\nWhat does and doesn't introduce behavior changes, i.e. this is just\ntesting existing functionality.\n"},{"id":"451720","messageId":"220321.86wngnbqwx.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"7451242daae018c629a62cf2fdb5631b556ad3b8.1647846935.git.dyroneteng@gmail.com","subject":"Re: [PATCH v13 11/16] ls-tree: introduce \"--format\" option","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-21T09:22:44Z","receivedAt":"2022-03-21T09:23:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Mar 21 2022, Teng Long wrote:\n\n> From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> [...]\n> +\t} else if (format && (!strcmp(format, name_only_format))) {\n> +\t\tshown_fields = FIELD_PATH_NAME;\n> +\t\tfn = show_tree;\n> +\t} else if (format)\n> +\t\tfn = show_tree_fmt;\n\nMissing {} braces for this \"else if\" arm.\n"},{"id":"451749","messageId":"xmqqh77rxgyq.fsf@gitster.g","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"Re: [PATCH v13 00/16] ls-tree: \"--object-only\" and \"--format\" opts","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-21T19:07:09Z","receivedAt":"2022-03-21T19:07:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Teng Long <dyroneteng@gmail.com> writes:\n\n> Diff from v12:\n>\n> Current patch is based by the review patch of Ævar Arnfjörð Bjarmason in [1].\n>\n> In terms of the review commits, I replied Ævar Arnfjörð Bjarmason [2] and listed the\n> actions, therefore this v13 patch was implemented by these actions.  \n>\n> Links:\n>\n> 1. https://public-inbox.org/git/cover-0.7-00000000000-20220310T134811Z-avarab@gmail.com/\n> 2. https://public-inbox.org/git/20220317095129.86790-1-dyroneteng@gmail.com/\n>\n> Thanks.\n\nThanks for sticking to this topic.  Will queue.\n"},{"id":"451954","messageId":"cover.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1647846935.git.dyroneteng@gmail.com","subject":"[PATCH v14 00/15] ls-tree: \"--object-only\" and \"--format\" opts","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:00Z","receivedAt":"2022-03-23T09:13:30Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"Main diff from v13:\n\n1. fix missing {} braces in \"else if\" arm.\n\n   https://public-inbox.org/git/220321.86wngnbqwx.gmgdl@evledraar.gmail.com/\n\n2. move \"ls-tree tests: add tests for --name-status\" in front of the series.\n\n   https://public-inbox.org/git/220321.861qyvd5j1.gmgdl@evledraar.gmail.com/\n\n3. squash \"ls-tree: remove FIELD_*, just use MODE_*\" into previous commit \"ls-tree: slightly refactor `show_tree()`\".\n\n   https://public-inbox.org/git/220321.86a6djd5m4.gmgdl@evledraar.gmail.com/\n\n4. make a fix on the performance regression when hit default format fast-path.\n\n   https://public-inbox.org/git/220321.865yo7d5ki.gmgdl@evledraar.gmail.com/#t\n\nThese changes are based on Ævar Arnfjörð Bjarmason's advices and I really appreciate it.\n\nThanks.\n\nJohannes Schindelin (1):\n  cocci: allow padding with `strbuf_addf()`\n\nTeng Long (5):\n  ls-tree: rename \"retval\" to \"recurse\" in \"show_tree()\"\n  ls-tree: simplify nesting if/else logic in \"show_tree()\"\n  ls-tree: fix \"--name-only\" and \"--long\" combined use bug\n  ls-tree: slightly refactor `show_tree()`\n  ls-tree: support --object-only option for \"git-ls-tree\"\n\nÆvar Arnfjörð Bjarmason (9):\n  ls-tree tests: add tests for --name-status\n  ls-tree: remove commented-out code\n  ls-tree: add missing braces to \"else\" arms\n  ls-tree: use \"enum object_type\", not {blob,tree,commit}_type\n  ls-tree: use \"size_t\", not \"int\" for \"struct strbuf\"'s \"len\"\n  ls-tree: introduce struct \"show_tree_data\"\n  ls-tree: introduce \"--format\" option\n  ls-tree: detect and error on --name-only --name-status\n  ls-tree: split up \"fast path\" callbacks\n\n Documentation/git-ls-tree.txt   |  68 +++++-\n builtin/ls-tree.c               | 366 ++++++++++++++++++++++++++------\n contrib/coccinelle/strbuf.cocci |   6 +-\n t/t3101-ls-tree-dirname.sh      |  55 ++---\n t/t3103-ls-tree-misc.sh         |  15 ++\n t/t3104-ls-tree-format.sh       |  76 +++++++\n 6 files changed, 492 insertions(+), 94 deletions(-)\n create mode 100755 t/t3104-ls-tree-format.sh\n\nRange-diff against v13:\n13:  a5c73de057 =  1:  ef1cf83d5e ls-tree tests: add tests for --name-status\n 1:  2fcff7e0d4 =  2:  02779bfd5d ls-tree: remove commented-out code\n 2:  6fd1dd9383 =  3:  93484b102d ls-tree: add missing braces to \"else\" arms\n 3:  208654b5e2 =  4:  2b746624ec ls-tree: use \"enum object_type\", not {blob,tree,commit}_type\n 4:  2637464fd8 =  5:  ca90461e03 ls-tree: use \"size_t\", not \"int\" for \"struct strbuf\"'s \"len\"\n 5:  99e6d47108 =  6:  f51d4fab0a ls-tree: rename \"retval\" to \"recurse\" in \"show_tree()\"\n 6:  a8d9b78dea !  7:  bff68d994f ls-tree: simplify nesting if/else logic in \"show_tree()\"\n    @@ Commit message\n         \"mode\" passed to us by read_tree(), instead of doing so with the S_*()\n         macros.\n     \n    +    Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n         Signed-off-by: Teng Long <dyronetengb@gmail.com>\n     \n      ## builtin/ls-tree.c ##\n 7:  25a07e048f !  8:  1d1808bdf0 ls-tree: fix \"--name-only\" and \"--long\" combined use bug\n    @@ Commit message\n     \n         [1] https://public-inbox.org/git/YZK0MKCYAJmG+pSU@coredump.intra.peff.net/\n     \n    +    Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n         Signed-off-by: Teng Long <dyroneteng@gmail.com>\n     \n      ## builtin/ls-tree.c ##\n 8:  55f1e10d7e !  9:  f4fc377dfa ls-tree: slightly refactor `show_tree()`\n    @@ Metadata\n      ## Commit message ##\n         ls-tree: slightly refactor `show_tree()`\n     \n    -    This is a non-functional change, we use a new int \"shown_fields\" to mark\n    -    which columns to output, and `parse_shown_fields()` to calculate the\n    -    value of \"shown_fields\".\n    +    This is a non-functional change, we introduce an enum \"ls_tree_cmdmode\"\n    +    then use it to mark which columns to output.\n     \n         This has the advantage of making the show_tree logic simpler and more\n         readable, as well as making it easier to extend new options (for example,\n         if we want to add a \"--object-only\" option, we just need to add a similar\n    -    \"if (shown_fields == FIELD_OBJECT_NAME)\" short-circuit logic in\n    -    \"show_tree()\").\n    +    \"short-circuit logic in \"show_tree()\").\n     \n    +    Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n         Signed-off-by: Teng Long <dyroneteng@gmail.com>\n     \n      ## builtin/ls-tree.c ##\n    @@ builtin/ls-tree.c\n      static int abbrev;\n      static int ls_options;\n      static struct pathspec pathspec;\n    - static int chomp_prefix;\n    - static const char *ls_tree_prefix;\n    -+static unsigned int shown_fields;\n    -+#define FIELD_PATH_NAME 1\n    -+#define FIELD_SIZE (1 << 1)\n    -+#define FIELD_OBJECT_NAME (1 << 2)\n    -+#define FIELD_TYPE (1 << 3)\n    -+#define FIELD_MODE (1 << 4)\n    -+#define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n    -+#define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n    - \n    - static const  char * const ls_tree_usage[] = {\n    - \tN_(\"git ls-tree [<options>] <tree-ish> [<path>...]\"),\n    +@@ builtin/ls-tree.c: static const  char * const ls_tree_usage[] = {\n      \tNULL\n      };\n      \n    @@ builtin/ls-tree.c\n     +\tMODE_LONG = 1,\n     +\tMODE_NAME_ONLY,\n     +} cmdmode;\n    -+\n    -+static int parse_shown_fields(void)\n    -+{\n    -+\tif (cmdmode == MODE_NAME_ONLY) {\n    -+\t\tshown_fields = FIELD_PATH_NAME;\n    -+\t\treturn 0;\n    -+\t}\n    -+\n    -+\tif (!ls_options || (ls_options & LS_RECURSIVE)\n    -+\t    || (ls_options & LS_SHOW_TREES)\n    -+\t    || (ls_options & LS_TREE_ONLY))\n    -+\t\tshown_fields = FIELD_DEFAULT;\n    -+\tif (cmdmode == MODE_LONG)\n    -+\t\tshown_fields = FIELD_LONG_DEFAULT;\n    -+\treturn 1;\n    -+}\n     +\n      static int show_recursive(const char *base, size_t baselen, const char *pathname)\n      {\n    @@ builtin/ls-tree.c: static int show_recursive(const char *base, size_t baselen, c\n     +{\n     +\tsize_t baselen = base->len;\n     +\n    -+\tif (shown_fields & FIELD_SIZE) {\n    ++\tif (cmdmode == MODE_LONG) {\n     +\t\tchar size_text[24];\n     +\t\tif (type == OBJ_BLOB) {\n     +\t\t\tunsigned long size;\n    @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strb\n     -\t\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n     -\t\t\t       find_unique_abbrev(oid, abbrev));\n     -\t\t}\n    -+\tif (shown_fields == FIELD_PATH_NAME) {\n    ++\tif (cmdmode == MODE_NAME_ONLY) {\n     +\t\tbaselen = base->len;\n     +\t\tstrbuf_addstr(base, pathname);\n     +\t\twrite_name_quoted_relative(base->buf,\n    @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strb\n     -\t\t\t\t   stdout, line_termination);\n     -\tstrbuf_setlen(base, baselen);\n     +\n    -+\tif (shown_fields >= FIELD_DEFAULT)\n    -+\t\tshow_default(oid, type, pathname, mode, base);\n    ++\tif (cmdmode == MODE_LONG ||\n    ++\t\t(!ls_options || (ls_options & LS_RECURSIVE)\n    ++\t\t || (ls_options & LS_SHOW_TREES)\n    ++\t\t || (ls_options & LS_TREE_ONLY)))\n    ++\t\t\t show_default(oid, type, pathname, mode, base);\n     +\n      \treturn recurse;\n      }\n    @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref\n      \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n      \t\t\t    N_(\"use full path names\"), 0),\n      \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n    -@@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    - \tif (get_oid(argv[0], &oid))\n    - \t\tdie(\"Not a valid object name %s\", argv[0]);\n    - \n    -+\tparse_shown_fields();\n    -+\n    - \t/*\n    - \t * show_recursive() rolls its own matching code and is\n    - \t * generally ignorant of 'struct pathspec'. The magic mask\n 9:  85d0b0da1d ! 10:  5131c0dcff ls-tree: introduce struct \"show_tree_data\"\n    @@ Commit message\n         \"show_tree()\". This commit is a pre-prepared commit for supporting\n         \"--format\" option and it does not affect any existing functionality.\n     \n    -    At the same time, we also move global \"shown_fields\" into \"show_tree_data\"\n    -    , the purpose of this is to avoid use both global variable and pass-around\n    -    struct.\n    -\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n         Signed-off-by: Teng Long <dyroneteng@gmail.com>\n     \n    @@ builtin/ls-tree.c: static int ls_options;\n      static struct pathspec pathspec;\n      static int chomp_prefix;\n      static const char *ls_tree_prefix;\n    --static unsigned int shown_fields;\n    - #define FIELD_PATH_NAME 1\n    - #define FIELD_SIZE (1 << 1)\n    - #define FIELD_OBJECT_NAME (1 << 2)\n    -@@ builtin/ls-tree.c: static unsigned int shown_fields;\n    - #define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n    - #define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n    - \n     +struct show_tree_data {\n     +\tunsigned mode;\n     +\tenum object_type type;\n     +\tconst struct object_id *oid;\n     +\tconst char *pathname;\n     +\tstruct strbuf *base;\n    -+\tunsigned int shown_fields;\n     +};\n    -+\n    + \n      static const  char * const ls_tree_usage[] = {\n      \tN_(\"git ls-tree [<options>] <tree-ish> [<path>...]\"),\n    - \tNULL\n    -@@ builtin/ls-tree.c: static enum ls_tree_cmdmode {\n    - \tMODE_NAME_ONLY,\n    - } cmdmode;\n    - \n    --static int parse_shown_fields(void)\n    -+static int parse_shown_fields(unsigned int *shown_fields)\n    - {\n    - \tif (cmdmode == MODE_NAME_ONLY) {\n    --\t\tshown_fields = FIELD_PATH_NAME;\n    -+\t\t*shown_fields = FIELD_PATH_NAME;\n    - \t\treturn 0;\n    - \t}\n    - \n    - \tif (!ls_options || (ls_options & LS_RECURSIVE)\n    - \t    || (ls_options & LS_SHOW_TREES)\n    - \t    || (ls_options & LS_TREE_ONLY))\n    --\t\tshown_fields = FIELD_DEFAULT;\n    -+\t\t*shown_fields = FIELD_DEFAULT;\n    - \tif (cmdmode == MODE_LONG)\n    --\t\tshown_fields = FIELD_LONG_DEFAULT;\n    -+\t\t*shown_fields = FIELD_LONG_DEFAULT;\n    - \treturn 1;\n    - }\n    - \n     @@ builtin/ls-tree.c: static int show_recursive(const char *base, size_t baselen, const char *pathname\n      \treturn 0;\n      }\n    @@ builtin/ls-tree.c: static int show_recursive(const char *base, size_t baselen, c\n     -\tsize_t baselen = base->len;\n     +\tsize_t baselen = data->base->len;\n      \n    --\tif (shown_fields & FIELD_SIZE) {\n    -+\tif (data->shown_fields & FIELD_SIZE) {\n    + \tif (cmdmode == MODE_LONG) {\n      \t\tchar size_text[24];\n     -\t\tif (type == OBJ_BLOB) {\n     +\t\tif (data->type == OBJ_BLOB) {\n    @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strb\n      \tint recurse = 0;\n      \tsize_t baselen;\n      \tenum object_type type = object_type(mode);\n    -+\tunsigned int shown_fields = *(unsigned int *)context;\n     +\tstruct show_tree_data data = {\n     +\t\t.mode = mode,\n     +\t\t.type = type,\n     +\t\t.oid = oid,\n     +\t\t.pathname = pathname,\n     +\t\t.base = base,\n    -+\t\t.shown_fields = shown_fields,\n     +\t};\n      \n      \tif (type == OBJ_BLOB) {\n      \t\tif (ls_options & LS_TREE_ONLY)\n     @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strbuf *base,\n    - \t\treturn recurse;\n    - \t}\n    - \n    --\tif (shown_fields >= FIELD_DEFAULT)\n    --\t\tshow_default(oid, type, pathname, mode, base);\n    -+\tif (shown_fields>= FIELD_DEFAULT)\n    -+\t\tshow_default(&data);\n    + \t\t(!ls_options || (ls_options & LS_RECURSIVE)\n    + \t\t || (ls_options & LS_SHOW_TREES)\n    + \t\t || (ls_options & LS_TREE_ONLY)))\n    +-\t\t\t show_default(oid, type, pathname, mode, base);\n    ++\t\t\t show_default(&data);\n      \n      \treturn recurse;\n      }\n    -@@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    - \tstruct object_id oid;\n    - \tstruct tree *tree;\n    - \tint i, full_tree = 0;\n    -+\tunsigned int shown_fields = 0;\n    - \tconst struct option ls_tree_options[] = {\n    - \t\tOPT_BIT('d', NULL, &ls_options, N_(\"only show trees\"),\n    - \t\t\tLS_TREE_ONLY),\n    -@@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    - \tif (get_oid(argv[0], &oid))\n    - \t\tdie(\"Not a valid object name %s\", argv[0]);\n    - \n    --\tparse_shown_fields();\n    --\n    - \t/*\n    - \t * show_recursive() rolls its own matching code and is\n    - \t * generally ignorant of 'struct pathspec'. The magic mask\n    -@@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    - \ttree = parse_tree_indirect(&oid);\n    - \tif (!tree)\n    - \t\tdie(\"not a tree object\");\n    --\treturn !!read_tree(the_repository, tree,\n    --\t\t\t   &pathspec, show_tree, NULL);\n    -+\n    -+\tparse_shown_fields(&shown_fields);\n    -+\n    -+\treturn !!read_tree(the_repository, tree, &pathspec, show_tree,\n    -+\t\t\t   &shown_fields);\n    - }\n10:  eddcf903ad = 11:  e4769b64c9 cocci: allow padding with `strbuf_addf()`\n11:  7451242daa ! 12:  8b21b204a5 ls-tree: introduce \"--format\" option\n    @@ Documentation/git-ls-tree.txt: quoted as explained for the configuration variabl\n      Part of the linkgit:git[1] suite\n     \n      ## builtin/ls-tree.c ##\n    -@@ builtin/ls-tree.c: static const char *ls_tree_prefix;\n    - #define FIELD_MODE (1 << 4)\n    - #define FIELD_DEFAULT 29 /* 11101 size is not shown to output by default */\n    - #define FIELD_LONG_DEFAULT  (FIELD_DEFAULT | FIELD_SIZE)\n    --\n    +@@ builtin/ls-tree.c: static int ls_options;\n    + static struct pathspec pathspec;\n    + static int chomp_prefix;\n    + static const char *ls_tree_prefix;\n     +static const char *format;\n    -+static const char *default_format = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\";\n    -+static const char *long_format = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\";\n    -+static const char *name_only_format = \"%(path)\";\n    ++\n      struct show_tree_data {\n      \tunsigned mode;\n      \tenum object_type type;\n    -@@ builtin/ls-tree.c: static enum ls_tree_cmdmode {\n    +@@ builtin/ls-tree.c: static const  char * const ls_tree_usage[] = {\n    + };\n    + \n    + static enum ls_tree_cmdmode {\n    +-\tMODE_LONG = 1,\n    ++\tMODE_DEFAULT = 0,\n    ++\tMODE_LONG,\n      \tMODE_NAME_ONLY,\n      } cmdmode;\n      \n    @@ builtin/ls-tree.c: static enum ls_tree_cmdmode {\n     +\treturn len;\n     +}\n     +\n    - static int parse_shown_fields(unsigned int *shown_fields)\n    + static int show_recursive(const char *base, size_t baselen, const char *pathname)\n      {\n    - \tif (cmdmode == MODE_NAME_ONLY) {\n    + \tint i;\n     @@ builtin/ls-tree.c: static int show_recursive(const char *base, size_t baselen, const char *pathname\n      \treturn 0;\n      }\n    @@ builtin/ls-tree.c: static int show_recursive(const char *base, size_t baselen, c\n      static int show_default(struct show_tree_data *data)\n      {\n      \tsize_t baselen = data->base->len;\n    -@@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    +@@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strbuf *base,\n    + \treturn recurse;\n    + }\n    + \n    ++struct ls_tree_cmdmode_to_fmt {\n    ++\tenum ls_tree_cmdmode mode;\n    ++\tconst char *const fmt;\n    ++};\n    ++\n    ++static struct ls_tree_cmdmode_to_fmt ls_tree_cmdmode_format[] = {\n    ++\t{\n    ++\t\t.mode = MODE_DEFAULT,\n    ++\t\t.fmt = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\",\n    ++\t},\n    ++\t{\n    ++\t\t.mode = MODE_LONG,\n    ++\t\t.fmt = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\",\n    ++\t},\n    ++\t{\n    ++\t\t.mode = MODE_NAME_ONLY, /* And MODE_NAME_STATUS */\n    ++\t\t.fmt = \"%(path)\",\n    ++\t},\n    ++\t{ 0 },\n    ++};\n    ++\n    + int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    + {\n    + \tstruct object_id oid;\n      \tstruct tree *tree;\n      \tint i, full_tree = 0;\n    - \tunsigned int shown_fields = 0;\n     +\tread_tree_fn_t fn = show_tree;\n      \tconst struct option ls_tree_options[] = {\n      \t\tOPT_BIT('d', NULL, &ls_options, N_(\"only show trees\"),\n    @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref\n      \t\tusage_with_options(ls_tree_usage, ls_tree_options);\n      \tif (get_oid(argv[0], &oid))\n     @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    - \n    - \tparse_shown_fields(&shown_fields);\n    - \n    --\treturn !!read_tree(the_repository, tree, &pathspec, show_tree,\n    --\t\t\t   &shown_fields);\n    + \ttree = parse_tree_indirect(&oid);\n    + \tif (!tree)\n    + \t\tdie(\"not a tree object\");\n    +-\treturn !!read_tree(the_repository, tree,\n    +-\t\t\t   &pathspec, show_tree, NULL);\n     +\t/*\n     +\t * The generic show_tree_fmt() is slower than show_tree(), so\n     +\t * take the fast path if possible.\n     +\t */\n    -+\tif (format && (!strcmp(format, default_format))) {\n    -+\t\tfn = show_tree;\n    -+\t} else if (format && (!strcmp(format, long_format))) {\n    -+\t\tshown_fields = shown_fields | FIELD_SIZE;\n    -+\t\tfn = show_tree;\n    -+\t} else if (format && (!strcmp(format, name_only_format))) {\n    -+\t\tshown_fields = FIELD_PATH_NAME;\n    -+\t\tfn = show_tree;\n    -+\t} else if (format)\n    ++\tif (format) {\n    ++\t\tstruct ls_tree_cmdmode_to_fmt *m2f;\n    ++\n     +\t\tfn = show_tree_fmt;\n    ++\t\tfor (m2f = ls_tree_cmdmode_format; m2f->fmt; m2f++) {\n    ++\t\t\tif (strcmp(format, m2f->fmt))\n    ++\t\t\t\tcontinue;\n    ++\n    ++\t\t\tcmdmode = m2f->mode;\n    ++\t\t\tfn = show_tree;\n    ++\t\t\tbreak;\n    ++\t\t}\n    ++\t}\n     +\n    -+\treturn !!read_tree(the_repository, tree, &pathspec, fn, &shown_fields);\n    ++\treturn !!read_tree(the_repository, tree, &pathspec, fn, NULL);\n      }\n     \n      ## t/t3104-ls-tree-format.sh (new) ##\n12:  2bcd08ebd0 ! 13:  27fd1d9e2c ls-tree: support --object-only option for \"git-ls-tree\"\n    @@ Commit message\n                  Time (mean ± σ):      96.5 ms ±   2.5 ms    [User: 72.9 ms, System: 23.5 ms]\n                  Range (min … max):    93.1 ms … 104.1 ms    31 runs\n     \n    +    Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n         Signed-off-by: Teng Long <dyroneteng@gmail.com>\n     \n      ## Documentation/git-ls-tree.txt ##\n    @@ Documentation/git-ls-tree.txt: OPTIONS\n      \tInstead of showing the full 40-byte hexadecimal object\n     \n      ## builtin/ls-tree.c ##\n    -@@ builtin/ls-tree.c: static const char *format;\n    - static const char *default_format = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\";\n    - static const char *long_format = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\";\n    - static const char *name_only_format = \"%(path)\";\n    -+static const char *object_only_format = \"%(objectname)\";\n    +@@ builtin/ls-tree.c: static struct pathspec pathspec;\n    + static int chomp_prefix;\n    + static const char *ls_tree_prefix;\n    + static const char *format;\n    +-\n      struct show_tree_data {\n      \tunsigned mode;\n      \tenum object_type type;\n    -@@ builtin/ls-tree.c: static const  char * const ls_tree_usage[] = {\n    - static enum ls_tree_cmdmode {\n    - \tMODE_LONG = 1,\n    +@@ builtin/ls-tree.c: static enum ls_tree_cmdmode {\n    + \tMODE_DEFAULT = 0,\n    + \tMODE_LONG,\n      \tMODE_NAME_ONLY,\n     +\tMODE_OBJECT_ONLY,\n      } cmdmode;\n      \n      static void expand_objectsize(struct strbuf *line, const struct object_id *oid,\n    -@@ builtin/ls-tree.c: static int parse_shown_fields(unsigned int *shown_fields)\n    - \t\t*shown_fields = FIELD_PATH_NAME;\n    - \t\treturn 0;\n    - \t}\n    --\n    -+\tif (cmdmode == MODE_OBJECT_ONLY) {\n    -+\t\t*shown_fields = FIELD_OBJECT_NAME;\n    -+\t\treturn 0;\n    -+\t}\n    - \tif (!ls_options || (ls_options & LS_RECURSIVE)\n    - \t    || (ls_options & LS_SHOW_TREES)\n    - \t    || (ls_options & LS_TREE_ONLY))\n     @@ builtin/ls-tree.c: static int show_tree(const struct object_id *oid, struct strbuf *base,\n      \t\t\treturn recurse;\n      \t}\n      \n    -+\tif (shown_fields == FIELD_OBJECT_NAME) {\n    ++\tif (cmdmode == MODE_OBJECT_ONLY) {\n     +\t\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n     +\t\treturn recurse;\n     +\t}\n     +\n    - \tif (shown_fields == FIELD_PATH_NAME) {\n    + \tif (cmdmode == MODE_NAME_ONLY) {\n      \t\tbaselen = base->len;\n      \t\tstrbuf_addstr(base, pathname);\n    +@@ builtin/ls-tree.c: static struct ls_tree_cmdmode_to_fmt ls_tree_cmdmode_format[] = {\n    + \t\t.mode = MODE_NAME_ONLY, /* And MODE_NAME_STATUS */\n    + \t\t.fmt = \"%(path)\",\n    + \t},\n    ++\t{\n    ++\t\t.mode = MODE_OBJECT_ONLY,\n    ++\t\t.fmt = \"%(objectname)\",\n    ++\t},\n    + \t{ 0 },\n    + };\n    + \n     @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n      \t\t\t    MODE_NAME_ONLY),\n      \t\tOPT_CMDMODE(0, \"name-status\", &cmdmode, N_(\"list only filenames\"),\n    @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref\n      \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n      \t\t\t    N_(\"use full path names\"), 0),\n      \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n    -@@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    - \t} else if (format && (!strcmp(format, name_only_format))) {\n    - \t\tshown_fields = FIELD_PATH_NAME;\n    - \t\tfn = show_tree;\n    -+\t} else if (format && (!strcmp(format, object_only_format))) {\n    -+\t\tshown_fields = FIELD_OBJECT_NAME;\n    -+\t\tfn = show_tree;\n    - \t} else if (format)\n    - \t\tfn = show_tree_fmt;\n    - \n     \n      ## t/t3103-ls-tree-misc.sh ##\n     @@ t/t3103-ls-tree-misc.sh: test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '\n14:  89402a8518 ! 14:  1d5d9470ab ls-tree: detect and error on --name-only --name-status\n    @@ Commit message\n         Signed-off-by: Teng Long <dyroneteng@gmail.com>\n     \n      ## builtin/ls-tree.c ##\n    -@@ builtin/ls-tree.c: static const  char * const ls_tree_usage[] = {\n    - static enum ls_tree_cmdmode {\n    - \tMODE_LONG = 1,\n    +@@ builtin/ls-tree.c: static enum ls_tree_cmdmode {\n    + \tMODE_DEFAULT = 0,\n    + \tMODE_LONG,\n      \tMODE_NAME_ONLY,\n     +\tMODE_NAME_STATUS,\n      \tMODE_OBJECT_ONLY,\n15:  b8afca193a <  -:  ---------- ls-tree: remove FIELD_*, just use MODE_*\n16:  010e3c0ece ! 15:  55a96e933b ls-tree: split up \"fast path\" callbacks\n    @@ builtin/ls-tree.c: static int show_tree_fmt(const struct object_id *oid, struct\n      \t}\n      \n     -\tif (cmdmode == MODE_LONG ||\n    --\t    (!ls_options || (ls_options & LS_RECURSIVE)\n    --\t     || (ls_options & LS_SHOW_TREES)\n    --\t     || (ls_options & LS_TREE_ONLY)))\n    --\t\tshow_default(&data);\n    +-\t\t(!ls_options || (ls_options & LS_RECURSIVE)\n    +-\t\t || (ls_options & LS_SHOW_TREES)\n    +-\t\t || (ls_options & LS_TREE_ONLY)))\n    +-\t\t\t show_default(&data);\n     +\tprintf(\"%06o %s %s %7s\\t\", data.mode, type_name(data.type),\n     +\t       find_unique_abbrev(data.oid, abbrev), size_text);\n     +\tshow_tree_common_default_long(base, pathname, data.base->len);\n    @@ builtin/ls-tree.c: static int show_tree_fmt(const struct object_id *oid, struct\n      \n      int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n     @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n    + \tstruct object_id oid;\n      \tstruct tree *tree;\n      \tint i, full_tree = 0;\n    - \tunsigned int shown_fields = 0;\n     -\tread_tree_fn_t fn = show_tree;\n     +\tread_tree_fn_t fn = NULL;\n      \tconst struct option ls_tree_options[] = {\n    @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref\n     -\t\t\tif (strcmp(format, m2f->fmt))\n     -\t\t\t\tcontinue;\n     -\n    -+\twhile (m2f++) {\n    ++\twhile (m2f) {\n     +\t\tif (!m2f->fmt) {\n     +\t\t\tfn = format ? show_tree_fmt : show_tree_default;\n     +\t\t} else if (format && !strcmp(format, m2f->fmt)) {\n    @@ builtin/ls-tree.c: int cmd_ls_tree(int argc, const char **argv, const char *pref\n     +\t\t} else if (!format && cmdmode == m2f->mode) {\n     +\t\t\tfn = m2f->fn;\n     +\t\t} else {\n    ++\t\t\tm2f++;\n     +\t\t\tcontinue;\n      \t\t}\n     +\t\tbreak;\n      \t}\n      \n    - \treturn !!read_tree(the_repository, tree, &pathspec, fn, &shown_fields);\n    + \treturn !!read_tree(the_repository, tree, &pathspec, fn, NULL);\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451955","messageId":"ef1cf83d5ea62cd4ce4dbba403b2eae654f7bc5b.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 01/15] ls-tree tests: add tests for --name-status","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:01Z","receivedAt":"2022-03-23T09:13:37Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nThe --name-status synonym for --name-only added in\nc639a5548a5 (ls-tree: --name-only, 2005-12-01) had no tests, let's\nmake sure it works the same way as its sibling.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n t/t3101-ls-tree-dirname.sh | 55 ++++++++++++++++++++------------------\n 1 file changed, 29 insertions(+), 26 deletions(-)\n\ndiff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh\nindex 05fde64225..217006d1bf 100755\n--- a/t/t3101-ls-tree-dirname.sh\n+++ b/t/t3101-ls-tree-dirname.sh\n@@ -201,31 +201,34 @@ EOF\n \ttest_cmp expected check\n '\n \n-test_expect_success 'ls-tree --name-only' '\n-\tgit ls-tree --name-only $tree >current &&\n-\tcat >expected <<\\EOF &&\n-1.txt\n-2.txt\n-path0\n-path1\n-path2\n-path3\n-EOF\n-\ttest_output\n-'\n-\n-test_expect_success 'ls-tree --name-only -r' '\n-\tgit ls-tree --name-only -r $tree >current &&\n-\tcat >expected <<\\EOF &&\n-1.txt\n-2.txt\n-path0/a/b/c/1.txt\n-path1/b/c/1.txt\n-path2/1.txt\n-path3/1.txt\n-path3/2.txt\n-EOF\n-\ttest_output\n-'\n+for opt in --name-only --name-status\n+do\n+\ttest_expect_success \"ls-tree $opt\" '\n+\t\tgit ls-tree $opt $tree >current &&\n+\t\tcat >expected <<-\\EOF &&\n+\t\t1.txt\n+\t\t2.txt\n+\t\tpath0\n+\t\tpath1\n+\t\tpath2\n+\t\tpath3\n+\t\tEOF\n+\t\ttest_output\n+\t'\n+\n+\ttest_expect_success \"ls-tree $opt -r\" '\n+\t\tgit ls-tree $opt -r $tree >current &&\n+\t\tcat >expected <<-\\EOF &&\n+\t\t1.txt\n+\t\t2.txt\n+\t\tpath0/a/b/c/1.txt\n+\t\tpath1/b/c/1.txt\n+\t\tpath2/1.txt\n+\t\tpath3/1.txt\n+\t\tpath3/2.txt\n+\t\tEOF\n+\t\ttest_output\n+\t'\n+done\n \n test_done\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451956","messageId":"02779bfd5d0e9aa216b48ff5f738c75d2882fbd3.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 02/15] ls-tree: remove commented-out code","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:02Z","receivedAt":"2022-03-23T09:13:39Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nRemove code added in f35a6d3bce7 (Teach core object handling functions\nabout gitlinks, 2007-04-09), later patched in 7d0b18a4da1 (Add output\nflushing before fork(), 2008-08-04), and then finally ending up in its\ncurrent form in d3bee161fef (tree.c: allow read_tree_recursive() to\ntraverse gitlink entries, 2009-01-25). All while being commented-out!\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 9 ---------\n 1 file changed, 9 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3a442631c7..5f7c84950c 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -69,15 +69,6 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tconst char *type = blob_type;\n \n \tif (S_ISGITLINK(mode)) {\n-\t\t/*\n-\t\t * Maybe we want to have some recursive version here?\n-\t\t *\n-\t\t * Something similar to this incomplete example:\n-\t\t *\n-\t\tif (show_subprojects(base, baselen, pathname))\n-\t\t\tretval = READ_TREE_RECURSIVE;\n-\t\t *\n-\t\t */\n \t\ttype = commit_type;\n \t} else if (S_ISDIR(mode)) {\n \t\tif (show_recursive(base->buf, base->len, pathname)) {\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451957","messageId":"93484b102d5ac22408bc76f7feecc26406b9e0a4.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 03/15] ls-tree: add missing braces to \"else\" arms","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:03Z","receivedAt":"2022-03-23T09:13:40Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nAdd missing {} to the \"else\" arms in show_tree() per the\nCodingGuidelines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 6 ++++--\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 5f7c84950c..0a28f32ccb 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -92,14 +92,16 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\t\telse\n \t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n \t\t\t\t\t\t  \"%\"PRIuMAX, (uintmax_t)size);\n-\t\t\t} else\n+\t\t\t} else {\n \t\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n+\t\t\t}\n \t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type,\n \t\t\t       find_unique_abbrev(oid, abbrev),\n \t\t\t       size_text);\n-\t\t} else\n+\t\t} else {\n \t\t\tprintf(\"%06o %s %s\\t\", mode, type,\n \t\t\t       find_unique_abbrev(oid, abbrev));\n+\t\t}\n \t}\n \tbaselen = base->len;\n \tstrbuf_addstr(base, pathname);\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451958","messageId":"2b746624ece8525000c6abe3f600696ee8bd0685.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 04/15] ls-tree: use \"enum object_type\", not {blob,tree,commit}_type","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:04Z","receivedAt":"2022-03-23T09:13:42Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nChange the ls-tree.c code to use type_name() on the enum instead of\nusing the string constants. This doesn't matter either way for\nperformance, but makes this a bit easier to read as we'll no longer\nneed a strcmp() here.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 0a28f32ccb..3f0225b097 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -66,17 +66,17 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n {\n \tint retval = 0;\n \tint baselen;\n-\tconst char *type = blob_type;\n+\tenum object_type type = OBJ_BLOB;\n \n \tif (S_ISGITLINK(mode)) {\n-\t\ttype = commit_type;\n+\t\ttype = OBJ_COMMIT;\n \t} else if (S_ISDIR(mode)) {\n \t\tif (show_recursive(base->buf, base->len, pathname)) {\n \t\t\tretval = READ_TREE_RECURSIVE;\n \t\t\tif (!(ls_options & LS_SHOW_TREES))\n \t\t\t\treturn retval;\n \t\t}\n-\t\ttype = tree_type;\n+\t\ttype = OBJ_TREE;\n \t}\n \telse if (ls_options & LS_TREE_ONLY)\n \t\treturn 0;\n@@ -84,7 +84,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tif (!(ls_options & LS_NAME_ONLY)) {\n \t\tif (ls_options & LS_SHOW_SIZE) {\n \t\t\tchar size_text[24];\n-\t\t\tif (!strcmp(type, blob_type)) {\n+\t\t\tif (type == OBJ_BLOB) {\n \t\t\t\tunsigned long size;\n \t\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n \t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n@@ -95,11 +95,11 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\t} else {\n \t\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n \t\t\t}\n-\t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type,\n+\t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n \t\t\t       find_unique_abbrev(oid, abbrev),\n \t\t\t       size_text);\n \t\t} else {\n-\t\t\tprintf(\"%06o %s %s\\t\", mode, type,\n+\t\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n \t\t\t       find_unique_abbrev(oid, abbrev));\n \t\t}\n \t}\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451959","messageId":"ca90461e031421763a600d509929a2c6f9960a03.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 05/15] ls-tree: use \"size_t\", not \"int\" for \"struct strbuf\"'s \"len\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:05Z","receivedAt":"2022-03-23T09:13:44Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nThe \"struct strbuf\"'s \"len\" member is a \"size_t\", not an \"int\", so\nlet's change our corresponding types accordingly. This also changes\nthe \"len\" and \"speclen\" variables, which are likewise used to store\nthe return value of strlen(), which returns \"size_t\", not \"int\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-tree.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3f0225b097..eecc7482d5 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -31,7 +31,7 @@ static const  char * const ls_tree_usage[] = {\n \tNULL\n };\n \n-static int show_recursive(const char *base, int baselen, const char *pathname)\n+static int show_recursive(const char *base, size_t baselen, const char *pathname)\n {\n \tint i;\n \n@@ -43,7 +43,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n \n \tfor (i = 0; i < pathspec.nr; i++) {\n \t\tconst char *spec = pathspec.items[i].match;\n-\t\tint len, speclen;\n+\t\tsize_t len, speclen;\n \n \t\tif (strncmp(base, spec, baselen))\n \t\t\tcontinue;\n@@ -65,7 +65,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\tconst char *pathname, unsigned mode, void *context)\n {\n \tint retval = 0;\n-\tint baselen;\n+\tsize_t baselen;\n \tenum object_type type = OBJ_BLOB;\n \n \tif (S_ISGITLINK(mode)) {\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451960","messageId":"f51d4fab0ac057ceb2974107244c74b2a0054870.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 06/15] ls-tree: rename \"retval\" to \"recurse\" in \"show_tree()\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:06Z","receivedAt":"2022-03-23T09:13:46Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"The variable which \"show_tree()\" return is named \"retval\", a name that's\na little hard to understand. The commit rename \"retval\" to \"recurse\"\nwhich is a more meaningful name than before in the context. We do not\nneed to take a look at \"read_tree_at()\" in \"tree.c\" to make sure what\ndoes \"retval\" mean.\n\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex eecc7482d5..ef8c414f61 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -64,7 +64,7 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\tconst char *pathname, unsigned mode, void *context)\n {\n-\tint retval = 0;\n+\tint recurse = 0;\n \tsize_t baselen;\n \tenum object_type type = OBJ_BLOB;\n \n@@ -72,9 +72,9 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\ttype = OBJ_COMMIT;\n \t} else if (S_ISDIR(mode)) {\n \t\tif (show_recursive(base->buf, base->len, pathname)) {\n-\t\t\tretval = READ_TREE_RECURSIVE;\n+\t\t\trecurse = READ_TREE_RECURSIVE;\n \t\t\tif (!(ls_options & LS_SHOW_TREES))\n-\t\t\t\treturn retval;\n+\t\t\t\treturn recurse;\n \t\t}\n \t\ttype = OBJ_TREE;\n \t}\n@@ -109,7 +109,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n \t\t\t\t   stdout, line_termination);\n \tstrbuf_setlen(base, baselen);\n-\treturn retval;\n+\treturn recurse;\n }\n \n int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451961","messageId":"bff68d994f70021eea92df8f72fd3fa2328f48fb.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 07/15] ls-tree: simplify nesting if/else logic in \"show_tree()\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:07Z","receivedAt":"2022-03-23T09:13:58Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"Use the object_type() function to determine the object type from the\n\"mode\" passed to us by read_tree(), instead of doing so with the S_*()\nmacros.\n\nHelped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyronetengb@gmail.com>\n---\n builtin/ls-tree.c | 21 +++++++++------------\n 1 file changed, 9 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex ef8c414f61..d4be71bad2 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -66,20 +66,17 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n {\n \tint recurse = 0;\n \tsize_t baselen;\n-\tenum object_type type = OBJ_BLOB;\n+\tenum object_type type = object_type(mode);\n \n-\tif (S_ISGITLINK(mode)) {\n-\t\ttype = OBJ_COMMIT;\n-\t} else if (S_ISDIR(mode)) {\n-\t\tif (show_recursive(base->buf, base->len, pathname)) {\n-\t\t\trecurse = READ_TREE_RECURSIVE;\n-\t\t\tif (!(ls_options & LS_SHOW_TREES))\n-\t\t\t\treturn recurse;\n-\t\t}\n-\t\ttype = OBJ_TREE;\n+\tif (type == OBJ_BLOB) {\n+\t\tif (ls_options & LS_TREE_ONLY)\n+\t\t\treturn 0;\n+\t} else if (type == OBJ_TREE &&\n+\t\t   show_recursive(base->buf, base->len, pathname)) {\n+\t\trecurse = READ_TREE_RECURSIVE;\n+\t\tif (!(ls_options & LS_SHOW_TREES))\n+\t\t\treturn recurse;\n \t}\n-\telse if (ls_options & LS_TREE_ONLY)\n-\t\treturn 0;\n \n \tif (!(ls_options & LS_NAME_ONLY)) {\n \t\tif (ls_options & LS_SHOW_SIZE) {\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451962","messageId":"1d1808bdf0a07b500d9242dcfdbe4831f612c67f.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 08/15] ls-tree: fix \"--name-only\" and \"--long\" combined use bug","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:08Z","receivedAt":"2022-03-23T09:14:00Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"If we execute \"git ls-tree\" with combined \"--name-only\" and \"--long\"\n, only the pathname will be printed, the size is omitted (the original\ndiscoverer was Peff in [1]).\n\nThis commit fix this issue by using `OPT_CMDMODE()` instead to make both\nof them mutually exclusive.\n\n[1] https://public-inbox.org/git/YZK0MKCYAJmG+pSU@coredump.intra.peff.net/\n\nHelped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c       | 12 ++++++------\n t/t3103-ls-tree-misc.sh |  9 +++++++++\n 2 files changed, 15 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex d4be71bad2..7be4c13228 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -123,12 +123,12 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\t\tLS_SHOW_TREES),\n \t\tOPT_SET_INT('z', NULL, &line_termination,\n \t\t\t    N_(\"terminate entries with NUL byte\"), 0),\n-\t\tOPT_BIT('l', \"long\", &ls_options, N_(\"include object size\"),\n-\t\t\tLS_SHOW_SIZE),\n-\t\tOPT_BIT(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n-\t\t\tLS_NAME_ONLY),\n-\t\tOPT_BIT(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n-\t\t\tLS_NAME_ONLY),\n+\t\tOPT_CMDMODE('l', \"long\", &ls_options, N_(\"include object size\"),\n+\t\t\t    LS_SHOW_SIZE),\n+\t\tOPT_CMDMODE(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n+\t\t\t    LS_NAME_ONLY),\n+\t\tOPT_CMDMODE(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n+\t\t\t    LS_NAME_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n \t\t\t    N_(\"use full path names\"), 0),\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\ndiff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh\nindex d18ba1bd84..d9d7fa932f 100755\n--- a/t/t3103-ls-tree-misc.sh\n+++ b/t/t3103-ls-tree-misc.sh\n@@ -23,4 +23,13 @@ test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '\n \ttest_must_fail git ls-tree -r HEAD\n '\n \n+for opts in \\\n+\t\"--name-only --long\" \\\n+\t\"--name-status --long\"\n+do\n+\ttest_expect_success \"usage: incompatible options: $opts\" '\n+\t\ttest_expect_code 129 git ls-tree $opts $tree\n+    '\n+done\n+\n test_done\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451963","messageId":"f4fc377dfa8b5c979740d8916a3ed8f8089957bb.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 09/15] ls-tree: slightly refactor `show_tree()`","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:09Z","receivedAt":"2022-03-23T09:14:02Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"This is a non-functional change, we introduce an enum \"ls_tree_cmdmode\"\nthen use it to mark which columns to output.\n\nThis has the advantage of making the show_tree logic simpler and more\nreadable, as well as making it easier to extend new options (for example,\nif we want to add a \"--object-only\" option, we just need to add a similar\n\"short-circuit logic in \"show_tree()\").\n\nHelped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 98 +++++++++++++++++++++++++++++------------------\n 1 file changed, 61 insertions(+), 37 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 7be4c13228..173a534e92 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -16,10 +16,8 @@\n \n static int line_termination = '\\n';\n #define LS_RECURSIVE 1\n-#define LS_TREE_ONLY 2\n-#define LS_SHOW_TREES 4\n-#define LS_NAME_ONLY 8\n-#define LS_SHOW_SIZE 16\n+#define LS_TREE_ONLY (1 << 1)\n+#define LS_SHOW_TREES (1 << 2)\n static int abbrev;\n static int ls_options;\n static struct pathspec pathspec;\n@@ -31,6 +29,11 @@ static const  char * const ls_tree_usage[] = {\n \tNULL\n };\n \n+static enum ls_tree_cmdmode {\n+\tMODE_LONG = 1,\n+\tMODE_NAME_ONLY,\n+} cmdmode;\n+\n static int show_recursive(const char *base, size_t baselen, const char *pathname)\n {\n \tint i;\n@@ -61,6 +64,39 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n \treturn 0;\n }\n \n+static int show_default(const struct object_id *oid, enum object_type type,\n+\t\t\tconst char *pathname, unsigned mode,\n+\t\t\tstruct strbuf *base)\n+{\n+\tsize_t baselen = base->len;\n+\n+\tif (cmdmode == MODE_LONG) {\n+\t\tchar size_text[24];\n+\t\tif (type == OBJ_BLOB) {\n+\t\t\tunsigned long size;\n+\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n+\t\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n+\t\t\telse\n+\t\t\t\txsnprintf(size_text, sizeof(size_text),\n+\t\t\t\t\t  \"%\" PRIuMAX, (uintmax_t)size);\n+\t\t} else {\n+\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n+\t\t}\n+\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n+\t\tfind_unique_abbrev(oid, abbrev), size_text);\n+\t} else {\n+\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n+\t\tfind_unique_abbrev(oid, abbrev));\n+\t}\n+\tbaselen = base->len;\n+\tstrbuf_addstr(base, pathname);\n+\twrite_name_quoted_relative(base->buf,\n+\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n+\t\t\t\t   line_termination);\n+\tstrbuf_setlen(base, baselen);\n+\treturn 1;\n+}\n+\n static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\tconst char *pathname, unsigned mode, void *context)\n {\n@@ -78,34 +114,22 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\treturn recurse;\n \t}\n \n-\tif (!(ls_options & LS_NAME_ONLY)) {\n-\t\tif (ls_options & LS_SHOW_SIZE) {\n-\t\t\tchar size_text[24];\n-\t\t\tif (type == OBJ_BLOB) {\n-\t\t\t\tunsigned long size;\n-\t\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n-\t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n-\t\t\t\t\t\t  \"BAD\");\n-\t\t\t\telse\n-\t\t\t\t\txsnprintf(size_text, sizeof(size_text),\n-\t\t\t\t\t\t  \"%\"PRIuMAX, (uintmax_t)size);\n-\t\t\t} else {\n-\t\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n-\t\t\t}\n-\t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n-\t\t\t       find_unique_abbrev(oid, abbrev),\n-\t\t\t       size_text);\n-\t\t} else {\n-\t\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n-\t\t\t       find_unique_abbrev(oid, abbrev));\n-\t\t}\n+\tif (cmdmode == MODE_NAME_ONLY) {\n+\t\tbaselen = base->len;\n+\t\tstrbuf_addstr(base, pathname);\n+\t\twrite_name_quoted_relative(base->buf,\n+\t\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n+\t\t\t\t\t   stdout, line_termination);\n+\t\tstrbuf_setlen(base, baselen);\n+\t\treturn recurse;\n \t}\n-\tbaselen = base->len;\n-\tstrbuf_addstr(base, pathname);\n-\twrite_name_quoted_relative(base->buf,\n-\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n-\t\t\t\t   stdout, line_termination);\n-\tstrbuf_setlen(base, baselen);\n+\n+\tif (cmdmode == MODE_LONG ||\n+\t\t(!ls_options || (ls_options & LS_RECURSIVE)\n+\t\t || (ls_options & LS_SHOW_TREES)\n+\t\t || (ls_options & LS_TREE_ONLY)))\n+\t\t\t show_default(oid, type, pathname, mode, base);\n+\n \treturn recurse;\n }\n \n@@ -123,12 +147,12 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\t\tLS_SHOW_TREES),\n \t\tOPT_SET_INT('z', NULL, &line_termination,\n \t\t\t    N_(\"terminate entries with NUL byte\"), 0),\n-\t\tOPT_CMDMODE('l', \"long\", &ls_options, N_(\"include object size\"),\n-\t\t\t    LS_SHOW_SIZE),\n-\t\tOPT_CMDMODE(0, \"name-only\", &ls_options, N_(\"list only filenames\"),\n-\t\t\t    LS_NAME_ONLY),\n-\t\tOPT_CMDMODE(0, \"name-status\", &ls_options, N_(\"list only filenames\"),\n-\t\t\t    LS_NAME_ONLY),\n+\t\tOPT_CMDMODE('l', \"long\", &cmdmode, N_(\"include object size\"),\n+\t\t\t    MODE_LONG),\n+\t\tOPT_CMDMODE(0, \"name-only\", &cmdmode, N_(\"list only filenames\"),\n+\t\t\t    MODE_NAME_ONLY),\n+\t\tOPT_CMDMODE(0, \"name-status\", &cmdmode, N_(\"list only filenames\"),\n+\t\t\t    MODE_NAME_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n \t\t\t    N_(\"use full path names\"), 0),\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451964","messageId":"5131c0dcff7407220d43e1ea507047053f65be39.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 10/15] ls-tree: introduce struct \"show_tree_data\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:10Z","receivedAt":"2022-03-23T09:14:02Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\n\"show_tree_data\" is a struct that packages the necessary fields for\n\"show_tree()\". This commit is a pre-prepared commit for supporting\n\"--format\" option and it does not affect any existing functionality.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 42 +++++++++++++++++++++++++++---------------\n 1 file changed, 27 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 173a534e92..edc8bf53a6 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -23,6 +23,13 @@ static int ls_options;\n static struct pathspec pathspec;\n static int chomp_prefix;\n static const char *ls_tree_prefix;\n+struct show_tree_data {\n+\tunsigned mode;\n+\tenum object_type type;\n+\tconst struct object_id *oid;\n+\tconst char *pathname;\n+\tstruct strbuf *base;\n+};\n \n static const  char * const ls_tree_usage[] = {\n \tN_(\"git ls-tree [<options>] <tree-ish> [<path>...]\"),\n@@ -64,17 +71,15 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n \treturn 0;\n }\n \n-static int show_default(const struct object_id *oid, enum object_type type,\n-\t\t\tconst char *pathname, unsigned mode,\n-\t\t\tstruct strbuf *base)\n+static int show_default(struct show_tree_data *data)\n {\n-\tsize_t baselen = base->len;\n+\tsize_t baselen = data->base->len;\n \n \tif (cmdmode == MODE_LONG) {\n \t\tchar size_text[24];\n-\t\tif (type == OBJ_BLOB) {\n+\t\tif (data->type == OBJ_BLOB) {\n \t\t\tunsigned long size;\n-\t\t\tif (oid_object_info(the_repository, oid, &size) == OBJ_BAD)\n+\t\t\tif (oid_object_info(the_repository, data->oid, &size) == OBJ_BAD)\n \t\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n \t\t\telse\n \t\t\t\txsnprintf(size_text, sizeof(size_text),\n@@ -82,18 +87,18 @@ static int show_default(const struct object_id *oid, enum object_type type,\n \t\t} else {\n \t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n \t\t}\n-\t\tprintf(\"%06o %s %s %7s\\t\", mode, type_name(type),\n-\t\tfind_unique_abbrev(oid, abbrev), size_text);\n+\t\tprintf(\"%06o %s %s %7s\\t\", data->mode, type_name(data->type),\n+\t\tfind_unique_abbrev(data->oid, abbrev), size_text);\n \t} else {\n-\t\tprintf(\"%06o %s %s\\t\", mode, type_name(type),\n-\t\tfind_unique_abbrev(oid, abbrev));\n+\t\tprintf(\"%06o %s %s\\t\", data->mode, type_name(data->type),\n+\t\tfind_unique_abbrev(data->oid, abbrev));\n \t}\n-\tbaselen = base->len;\n-\tstrbuf_addstr(base, pathname);\n-\twrite_name_quoted_relative(base->buf,\n+\tbaselen = data->base->len;\n+\tstrbuf_addstr(data->base, data->pathname);\n+\twrite_name_quoted_relative(data->base->buf,\n \t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n \t\t\t\t   line_termination);\n-\tstrbuf_setlen(base, baselen);\n+\tstrbuf_setlen(data->base, baselen);\n \treturn 1;\n }\n \n@@ -103,6 +108,13 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \tint recurse = 0;\n \tsize_t baselen;\n \tenum object_type type = object_type(mode);\n+\tstruct show_tree_data data = {\n+\t\t.mode = mode,\n+\t\t.type = type,\n+\t\t.oid = oid,\n+\t\t.pathname = pathname,\n+\t\t.base = base,\n+\t};\n \n \tif (type == OBJ_BLOB) {\n \t\tif (ls_options & LS_TREE_ONLY)\n@@ -128,7 +140,7 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t(!ls_options || (ls_options & LS_RECURSIVE)\n \t\t || (ls_options & LS_SHOW_TREES)\n \t\t || (ls_options & LS_TREE_ONLY)))\n-\t\t\t show_default(oid, type, pathname, mode, base);\n+\t\t\t show_default(&data);\n \n \treturn recurse;\n }\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451965","messageId":"e4769b64c9400089aa7c1e50464aee1c869c900d.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 11/15] cocci: allow padding with `strbuf_addf()`","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:11Z","receivedAt":"2022-03-23T09:14:04Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nA convenient way to pad strings is to use something like\n`strbuf_addf(&buf, \"%20s\", \"Hello, world!\")`.\n\nHowever, the Coccinelle rule that forbids a format `\"%s\"` with a\nconstant string argument cast too wide a net, and also forbade such\npadding.\n\nThe original rule was introduced by commit:\n\n    28c23cd4c39 (strbuf.cocci: suggest strbuf_addbuf() to add one strbuf to an other, 2019-01-25)\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n contrib/coccinelle/strbuf.cocci | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/coccinelle/strbuf.cocci b/contrib/coccinelle/strbuf.cocci\nindex d9ada69b43..0970d98ad7 100644\n--- a/contrib/coccinelle/strbuf.cocci\n+++ b/contrib/coccinelle/strbuf.cocci\n@@ -15,7 +15,7 @@ constant fmt !~ \"%\";\n @@\n expression E;\n struct strbuf SB;\n-format F =~ \"s\";\n+format F =~ \"^s$\";\n @@\n - strbuf_addf(E, \"%@F@\", SB.buf);\n + strbuf_addbuf(E, &SB);\n@@ -23,7 +23,7 @@ format F =~ \"s\";\n @@\n expression E;\n struct strbuf *SBP;\n-format F =~ \"s\";\n+format F =~ \"^s$\";\n @@\n - strbuf_addf(E, \"%@F@\", SBP->buf);\n + strbuf_addbuf(E, SBP);\n@@ -44,7 +44,7 @@ struct strbuf *SBP;\n \n @@\n expression E1, E2;\n-format F =~ \"s\";\n+format F =~ \"^s$\";\n @@\n - strbuf_addf(E1, \"%@F@\", E2);\n + strbuf_addstr(E1, E2);\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451966","messageId":"8b21b204a5c4be137f47ecb3a07373d36d171019.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 12/15] ls-tree: introduce \"--format\" option","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:12Z","receivedAt":"2022-03-23T09:14:06Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nAdd a --format option to ls-tree. It has an existing default output,\nand then --long and --name-only options to emit the default output\nalong with the objectsize and, or to only emit object paths.\n\nRather than add --type-only, --object-only etc. we can just support a\n--format using a strbuf_expand() similar to \"for-each-ref\n--format\". We might still add such options in the future for\nconvenience.\n\nThe --format implementation is slower than the existing code, but this\nchange does not cause any performance regressions. We'll leave the\nexisting show_tree() unchanged, and only run show_tree_fmt() in if\na --format different than the hardcoded built-in ones corresponding to\nthe existing modes is provided.\n\nI.e. something like the \"--long\" output would be much slower with\nthis, mainly due to how we need to allocate various things to do with\nquote.c instead of spewing the output directly to stdout.\n\nThe new option of '--format' comes from Ævar Arnfjörð Bjarmasonn's\nidea and suggestion, this commit makes modifications in terms of the\noriginal discussion on community [1].\n\nIn [1] there was a \"GIT_TEST_LS_TREE_FORMAT_BACKEND\" variable to\nensure that we had test coverage for passing tests that would\notherwise use show_tree() through show_tree_fmt(), and thus that the\nformatting mechanism could handle all the same cases as the\nnon-formatting options.\n\nSomewhere in subsequent re-rolls of that we seem to have drifted away\nfrom what the goal of these tests should be. We're trying to ensure\ncorrectness of show_tree_fmt(). We can't tell if we \"hit [the]\nfast-path\" here, and instead of having an explicit test for that, we\ncan just add it to something our \"test_ls_tree_format\" tests for.\n\nHere is the statistics about performance tests:\n\n1. Default format (hitten the builtin formats):\n\n    \"git ls-tree <tree-ish>\" vs \"--format='%(mode) %(type) %(object)%x09%(file)'\"\n\n    $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r HEAD\"\n    Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD\n    Time (mean ± σ):     105.2 ms ±   3.3 ms    [User: 84.3 ms, System: 20.8 ms]\n    Range (min … max):    99.2 ms … 113.2 ms    28 runs\n\n    $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object)%x09%(file)'  HEAD\"\n    Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object)%x09%(file)'  HEAD\n    Time (mean ± σ):     106.4 ms ±   2.7 ms    [User: 86.1 ms, System: 20.2 ms]\n    Range (min … max):   100.2 ms … 110.5 ms    29 runs\n\n2. Default format includes object size (hitten the builtin formats):\n\n    \"git ls-tree -l <tree-ish>\" vs \"--format='%(mode) %(type) %(object) %(size:padded)%x09%(file)'\"\n\n    $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r -l HEAD\"\n    Benchmark 1: /opt/git/master/bin/git ls-tree -r -l HEAD\n    Time (mean ± σ):     335.1 ms ±   6.5 ms    [User: 304.6 ms, System: 30.4 ms]\n    Range (min … max):   327.5 ms … 348.4 ms    10 runs\n\n    $hyperfine --warmup=10 \"/opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object) %(size:padded)%x09%(file)'  HEAD\"\n    Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(mode) %(type) %(object) %(size:padded)%x09%(file)'  HEAD\n    Time (mean ± σ):     337.2 ms ±   8.2 ms    [User: 309.2 ms, System: 27.9 ms]\n    Range (min … max):   328.8 ms … 349.4 ms    10 runs\n\nLinks:\n\t[1] https://public-inbox.org/git/RFC-patch-6.7-eac299f06ff-20211217T131635Z-avarab@gmail.com/\n\t[2] https://lore.kernel.org/git/cb717d08be87e3239117c6c667cb32caabaad33d.1646390152.git.dyroneteng@gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n Documentation/git-ls-tree.txt |  59 ++++++++++++-\n builtin/ls-tree.c             | 153 +++++++++++++++++++++++++++++++++-\n t/t3104-ls-tree-format.sh     |  67 +++++++++++++++\n 3 files changed, 272 insertions(+), 7 deletions(-)\n create mode 100755 t/t3104-ls-tree-format.sh\n\ndiff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt\nindex db02d6d79a..db29a9efb5 100644\n--- a/Documentation/git-ls-tree.txt\n+++ b/Documentation/git-ls-tree.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git ls-tree' [-d] [-r] [-t] [-l] [-z]\n-\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]]\n+\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n \t    <tree-ish> [<path>...]\n \n DESCRIPTION\n@@ -74,6 +74,16 @@ OPTIONS\n \tDo not limit the listing to the current working directory.\n \tImplies --full-name.\n \n+--format=<format>::\n+\tA string that interpolates `%(fieldname)` from the result\n+\tbeing shown. It also interpolates `%%` to `%`, and\n+\t`%xx` where `xx` are hex digits interpolates to character\n+\twith hex code `xx`; for example `%00` interpolates to\n+\t`\\0` (NUL), `%09` to `\\t` (TAB) and `%0a` to `\\n` (LF).\n+\tWhen specified, `--format` cannot be combined with other\n+\tformat-altering options, including `--long`, `--name-only`\n+\tand `--object-only`.\n+\n [<path>...]::\n \tWhen paths are given, show them (note that this isn't really raw\n \tpathnames, but rather a list of patterns to match).  Otherwise\n@@ -82,16 +92,29 @@ OPTIONS\n \n Output Format\n -------------\n-        <mode> SP <type> SP <object> TAB <file>\n+\n+The output format of `ls-tree` is determined by either the `--format`\n+option, or other format-altering options such as `--name-only` etc.\n+(see `--format` above).\n+\n+The use of certain `--format` directives is equivalent to using those\n+options, but invoking the full formatting machinery can be slower than\n+using an appropriate formatting option.\n+\n+In cases where the `--format` would exactly map to an existing option\n+`ls-tree` will use the appropriate faster path. Thus the default format\n+is equivalent to:\n+\n+        %(objectmode) %(objecttype) %(objectname)%x09%(path)\n \n This output format is compatible with what `--index-info --stdin` of\n 'git update-index' expects.\n \n When the `-l` option is used, format changes to\n \n-        <mode> SP <type> SP <object> SP <object size> TAB <file>\n+        %(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\n \n-Object size identified by <object> is given in bytes, and right-justified\n+Object size identified by <objectname> is given in bytes, and right-justified\n with minimum width of 7 characters.  Object size is given only for blobs\n (file) entries; for other entries `-` character is used in place of size.\n \n@@ -100,6 +123,34 @@ quoted as explained for the configuration variable `core.quotePath`\n (see linkgit:git-config[1]).  Using `-z` the filename is output\n verbatim and the line is terminated by a NUL byte.\n \n+Customized format:\n+\n+It is possible to print in a custom format by using the `--format` option,\n+which is able to interpolate different fields using a `%(fieldname)` notation.\n+For example, if you only care about the \"objectname\" and \"path\" fields, you\n+can execute with a specific \"--format\" like\n+\n+        git ls-tree --format='%(objectname) %(path)' <tree-ish>\n+\n+FIELD NAMES\n+-----------\n+\n+Various values from structured fields can be used to interpolate\n+into the resulting output. For each outputing line, the following\n+names can be used:\n+\n+objectmode::\n+\tThe mode of the object.\n+objecttype::\n+\tThe type of the object (`blob` or `tree`).\n+objectname::\n+\tThe name of the object.\n+objectsize[:padded]::\n+\tThe size of the object (\"-\" if it's a tree).\n+\tIt also supports a padded format of size with \"%(size:padded)\".\n+path::\n+\tThe pathname of the object.\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex edc8bf53a6..8a1bf2fa4d 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -23,6 +23,8 @@ static int ls_options;\n static struct pathspec pathspec;\n static int chomp_prefix;\n static const char *ls_tree_prefix;\n+static const char *format;\n+\n struct show_tree_data {\n \tunsigned mode;\n \tenum object_type type;\n@@ -37,10 +39,77 @@ static const  char * const ls_tree_usage[] = {\n };\n \n static enum ls_tree_cmdmode {\n-\tMODE_LONG = 1,\n+\tMODE_DEFAULT = 0,\n+\tMODE_LONG,\n \tMODE_NAME_ONLY,\n } cmdmode;\n \n+static void expand_objectsize(struct strbuf *line, const struct object_id *oid,\n+\t\t\t      const enum object_type type, unsigned int padded)\n+{\n+\tif (type == OBJ_BLOB) {\n+\t\tunsigned long size;\n+\t\tif (oid_object_info(the_repository, oid, &size) < 0)\n+\t\t\tdie(_(\"could not get object info about '%s'\"),\n+\t\t\t    oid_to_hex(oid));\n+\t\tif (padded)\n+\t\t\tstrbuf_addf(line, \"%7\"PRIuMAX, (uintmax_t)size);\n+\t\telse\n+\t\t\tstrbuf_addf(line, \"%\"PRIuMAX, (uintmax_t)size);\n+\t} else if (padded) {\n+\t\tstrbuf_addf(line, \"%7s\", \"-\");\n+\t} else {\n+\t\tstrbuf_addstr(line, \"-\");\n+\t}\n+}\n+\n+static size_t expand_show_tree(struct strbuf *sb, const char *start,\n+\t\t\t       void *context)\n+{\n+\tstruct show_tree_data *data = context;\n+\tconst char *end;\n+\tconst char *p;\n+\tunsigned int errlen;\n+\tsize_t len = strbuf_expand_literal_cb(sb, start, NULL);\n+\n+\tif (len)\n+\t\treturn len;\n+\tif (*start != '(')\n+\t\tdie(_(\"bad ls-tree format: element '%s' does not start with '('\"), start);\n+\n+\tend = strchr(start + 1, ')');\n+\tif (!end)\n+\t\tdie(_(\"bad ls-tree format: element '%s' does not end in ')'\"), start);\n+\n+\tlen = end - start + 1;\n+\tif (skip_prefix(start, \"(objectmode)\", &p)) {\n+\t\tstrbuf_addf(sb, \"%06o\", data->mode);\n+\t} else if (skip_prefix(start, \"(objecttype)\", &p)) {\n+\t\tstrbuf_addstr(sb, type_name(data->type));\n+\t} else if (skip_prefix(start, \"(objectsize:padded)\", &p)) {\n+\t\texpand_objectsize(sb, data->oid, data->type, 1);\n+\t} else if (skip_prefix(start, \"(objectsize)\", &p)) {\n+\t\texpand_objectsize(sb, data->oid, data->type, 0);\n+\t} else if (skip_prefix(start, \"(objectname)\", &p)) {\n+\t\tstrbuf_add_unique_abbrev(sb, data->oid, abbrev);\n+\t} else if (skip_prefix(start, \"(path)\", &p)) {\n+\t\tconst char *name = data->base->buf;\n+\t\tconst char *prefix = chomp_prefix ? ls_tree_prefix : NULL;\n+\t\tstruct strbuf quoted = STRBUF_INIT;\n+\t\tstruct strbuf sbuf = STRBUF_INIT;\n+\t\tstrbuf_addstr(data->base, data->pathname);\n+\t\tname = relative_path(data->base->buf, prefix, &sbuf);\n+\t\tquote_c_style(name, &quoted, NULL, 0);\n+\t\tstrbuf_addbuf(sb, &quoted);\n+\t\tstrbuf_release(&sbuf);\n+\t\tstrbuf_release(&quoted);\n+\t} else {\n+\t\terrlen = (unsigned long)len;\n+\t\tdie(_(\"bad ls-tree format: %%%.*s\"), errlen, start);\n+\t}\n+\treturn len;\n+}\n+\n static int show_recursive(const char *base, size_t baselen, const char *pathname)\n {\n \tint i;\n@@ -71,6 +140,38 @@ static int show_recursive(const char *base, size_t baselen, const char *pathname\n \treturn 0;\n }\n \n+static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,\n+\t\t\t const char *pathname, unsigned mode, void *context)\n+{\n+\tsize_t baselen;\n+\tint recurse = 0;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tenum object_type type = object_type(mode);\n+\n+\tstruct show_tree_data data = {\n+\t\t.mode = mode,\n+\t\t.type = type,\n+\t\t.oid = oid,\n+\t\t.pathname = pathname,\n+\t\t.base = base,\n+\t};\n+\n+\tif (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))\n+\t\trecurse = READ_TREE_RECURSIVE;\n+\tif (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES))\n+\t\treturn recurse;\n+\tif (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))\n+\t\treturn 0;\n+\n+\tbaselen = base->len;\n+\tstrbuf_expand(&sb, format, expand_show_tree, &data);\n+\tstrbuf_addch(&sb, line_termination);\n+\tfwrite(sb.buf, sb.len, 1, stdout);\n+\tstrbuf_release(&sb);\n+\tstrbuf_setlen(base, baselen);\n+\treturn recurse;\n+}\n+\n static int show_default(struct show_tree_data *data)\n {\n \tsize_t baselen = data->base->len;\n@@ -145,11 +246,33 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \treturn recurse;\n }\n \n+struct ls_tree_cmdmode_to_fmt {\n+\tenum ls_tree_cmdmode mode;\n+\tconst char *const fmt;\n+};\n+\n+static struct ls_tree_cmdmode_to_fmt ls_tree_cmdmode_format[] = {\n+\t{\n+\t\t.mode = MODE_DEFAULT,\n+\t\t.fmt = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\",\n+\t},\n+\t{\n+\t\t.mode = MODE_LONG,\n+\t\t.fmt = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\",\n+\t},\n+\t{\n+\t\t.mode = MODE_NAME_ONLY, /* And MODE_NAME_STATUS */\n+\t\t.fmt = \"%(path)\",\n+\t},\n+\t{ 0 },\n+};\n+\n int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n {\n \tstruct object_id oid;\n \tstruct tree *tree;\n \tint i, full_tree = 0;\n+\tread_tree_fn_t fn = show_tree;\n \tconst struct option ls_tree_options[] = {\n \t\tOPT_BIT('d', NULL, &ls_options, N_(\"only show trees\"),\n \t\t\tLS_TREE_ONLY),\n@@ -170,6 +293,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\n \t\t\t N_(\"list entire tree; not just current directory \"\n \t\t\t    \"(implies --full-name)\")),\n+\t\tOPT_STRING_F(0, \"format\", &format, N_(\"format\"),\n+\t\t\t\t\t N_(\"format to use for the output\"),\n+\t\t\t\t\t PARSE_OPT_NONEG),\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_END()\n \t};\n@@ -190,6 +316,10 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t    ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))\n \t\tls_options |= LS_SHOW_TREES;\n \n+\tif (format && cmdmode)\n+\t\tusage_msg_opt(\n+\t\t\t_(\"--format can't be combined with other format-altering options\"),\n+\t\t\tls_tree_usage, ls_tree_options);\n \tif (argc < 1)\n \t\tusage_with_options(ls_tree_usage, ls_tree_options);\n \tif (get_oid(argv[0], &oid))\n@@ -211,6 +341,23 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \ttree = parse_tree_indirect(&oid);\n \tif (!tree)\n \t\tdie(\"not a tree object\");\n-\treturn !!read_tree(the_repository, tree,\n-\t\t\t   &pathspec, show_tree, NULL);\n+\t/*\n+\t * The generic show_tree_fmt() is slower than show_tree(), so\n+\t * take the fast path if possible.\n+\t */\n+\tif (format) {\n+\t\tstruct ls_tree_cmdmode_to_fmt *m2f;\n+\n+\t\tfn = show_tree_fmt;\n+\t\tfor (m2f = ls_tree_cmdmode_format; m2f->fmt; m2f++) {\n+\t\t\tif (strcmp(format, m2f->fmt))\n+\t\t\t\tcontinue;\n+\n+\t\t\tcmdmode = m2f->mode;\n+\t\t\tfn = show_tree;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\treturn !!read_tree(the_repository, tree, &pathspec, fn, NULL);\n }\ndiff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh\nnew file mode 100755\nindex 0000000000..7f1eb699d3\n--- /dev/null\n+++ b/t/t3104-ls-tree-format.sh\n@@ -0,0 +1,67 @@\n+#!/bin/sh\n+\n+test_description='ls-tree --format'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n+. ./test-lib.sh\n+\n+test_expect_success 'ls-tree --format usage' '\n+\ttest_expect_code 129 git ls-tree --format=fmt -l HEAD &&\n+\ttest_expect_code 129 git ls-tree --format=fmt --name-only HEAD &&\n+\ttest_expect_code 129 git ls-tree --format=fmt --name-status HEAD\n+'\n+\n+test_expect_success 'setup' '\n+\tmkdir dir &&\n+\ttest_commit dir/sub-file &&\n+\ttest_commit top-file\n+'\n+\n+test_ls_tree_format () {\n+\tformat=$1 &&\n+\topts=$2 &&\n+\tfmtopts=$3 &&\n+\tshift 2 &&\n+\n+\ttest_expect_success \"ls-tree '--format=<$format>' is like options '$opts $fmtopts'\" '\n+\t\tgit ls-tree $opts -r HEAD >expect &&\n+\t\tgit ls-tree --format=\"$format\" -r $fmtopts HEAD >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+\n+\ttest_expect_success \"ls-tree '--format=<$format>' on optimized v.s. non-optimized path\" '\n+\t\tgit ls-tree --format=\"$format\" -r $fmtopts HEAD >expect &&\n+\t\tgit ls-tree --format=\"> $format\" -r $fmtopts HEAD >actual.raw &&\n+\t\tsed \"s/^> //\" >actual <actual.raw &&\n+\t\ttest_cmp expect actual\n+\t'\n+}\n+\n+test_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"\"\n+\n+test_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\" \\\n+\t\"--long\"\n+\n+test_ls_tree_format \\\n+\t\"%(path)\" \\\n+\t\"--name-only\"\n+\n+test_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"-t\" \\\n+\t\"-t\"\n+\n+test_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"--full-name\" \\\n+\t\"--full-name\"\n+\n+test_ls_tree_format \\\n+\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n+\t\"--full-tree\" \\\n+\t\"--full-tree\"\n+\n+test_done\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451967","messageId":"27fd1d9e2c2fad8159b4a38709929e0b66d5f93f.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 13/15] ls-tree: support --object-only option for \"git-ls-tree\"","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:13Z","receivedAt":"2022-03-23T09:14:09Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"'--object-only' is an alias for '--format=%(objectname)'. It cannot\nbe used together other format-altering options like '--name-only',\n'--long' or '--format', they are mutually exclusive.\n\nThe \"--name-only\" option outputs <filepath> only. Likewise, <objectName>\nis another high frequency used field, so implement '--object-only' option\nwill bring intuitive and clear semantics for this scenario. Using\n'--format=%(objectname)' we can achieve a similar effect, but the former\nis with a lower learning cost(without knowing the format requirement\nof '--format' option).\n\nEven so, if a user is prefer to use \"--format=%(objectname)\", this is entirely\nwelcome because they are not only equivalent in function, but also have almost\nidentical performance. The reason is this commit also add the specific of\n\"--format=%(objectname)\" to the current fast-pathes (builtin formats) to\navoid running unnecessary parsing mechanisms.\n\nThe following performance benchmarks are based on torvalds/linux.git:\n\n  When hit the fast-path:\n\n      Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --object-only HEAD\n        Time (mean ± σ):      83.6 ms ±   2.0 ms    [User: 59.4 ms, System: 24.1 ms]\n        Range (min … max):    80.4 ms …  87.2 ms    35 runs\n\n      Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='%(objectname)' HEAD\n        Time (mean ± σ):      84.1 ms ±   1.8 ms    [User: 61.7 ms, System: 22.3 ms]\n        Range (min … max):    80.9 ms …  87.5 ms    35 runs\n\n  But for a customized format, it will be slower:\n\n       Benchmark 1: /opt/git/ls-tree-oid-only/bin/git ls-tree -r --format='oid: %(objectname)' HEAD\n         Time (mean ± σ):      96.5 ms ±   2.5 ms    [User: 72.9 ms, System: 23.5 ms]\n  \t Range (min … max):    93.1 ms … 104.1 ms    31 runs\n\nHelped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n Documentation/git-ls-tree.txt | 11 ++++++++++-\n builtin/ls-tree.c             | 13 ++++++++++++-\n t/t3103-ls-tree-misc.sh       |  7 +++++--\n t/t3104-ls-tree-format.sh     |  9 +++++++++\n 4 files changed, 36 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt\nindex db29a9efb5..1aea998e72 100644\n--- a/Documentation/git-ls-tree.txt\n+++ b/Documentation/git-ls-tree.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git ls-tree' [-d] [-r] [-t] [-l] [-z]\n-\t    [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n+\t    [--name-only] [--name-status] [--object-only] [--full-name] [--full-tree] [--abbrev[=<n>]] [--format=<format>]\n \t    <tree-ish> [<path>...]\n \n DESCRIPTION\n@@ -59,6 +59,15 @@ OPTIONS\n --name-only::\n --name-status::\n \tList only filenames (instead of the \"long\" output), one per line.\n+\tCannot be combined with `--object-only`.\n+\n+--object-only::\n+\tList only names of the objects, one per line. Cannot be combined\n+\twith `--name-only` or `--name-status`.\n+\tThis is equivalent to specifying `--format='%(objectname)'`, but\n+\tfor both this option and that exact format the command takes a\n+\thand-optimized codepath instead of going through the generic\n+\tformatting mechanism.\n \n --abbrev[=<n>]::\n \tInstead of showing the full 40-byte hexadecimal object\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 8a1bf2fa4d..3474f8c3d6 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -24,7 +24,6 @@ static struct pathspec pathspec;\n static int chomp_prefix;\n static const char *ls_tree_prefix;\n static const char *format;\n-\n struct show_tree_data {\n \tunsigned mode;\n \tenum object_type type;\n@@ -42,6 +41,7 @@ static enum ls_tree_cmdmode {\n \tMODE_DEFAULT = 0,\n \tMODE_LONG,\n \tMODE_NAME_ONLY,\n+\tMODE_OBJECT_ONLY,\n } cmdmode;\n \n static void expand_objectsize(struct strbuf *line, const struct object_id *oid,\n@@ -227,6 +227,11 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t\treturn recurse;\n \t}\n \n+\tif (cmdmode == MODE_OBJECT_ONLY) {\n+\t\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n+\t\treturn recurse;\n+\t}\n+\n \tif (cmdmode == MODE_NAME_ONLY) {\n \t\tbaselen = base->len;\n \t\tstrbuf_addstr(base, pathname);\n@@ -264,6 +269,10 @@ static struct ls_tree_cmdmode_to_fmt ls_tree_cmdmode_format[] = {\n \t\t.mode = MODE_NAME_ONLY, /* And MODE_NAME_STATUS */\n \t\t.fmt = \"%(path)\",\n \t},\n+\t{\n+\t\t.mode = MODE_OBJECT_ONLY,\n+\t\t.fmt = \"%(objectname)\",\n+\t},\n \t{ 0 },\n };\n \n@@ -288,6 +297,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\t\t    MODE_NAME_ONLY),\n \t\tOPT_CMDMODE(0, \"name-status\", &cmdmode, N_(\"list only filenames\"),\n \t\t\t    MODE_NAME_ONLY),\n+\t\tOPT_CMDMODE(0, \"object-only\", &cmdmode, N_(\"list only objects\"),\n+\t\t\t    MODE_OBJECT_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n \t\t\t    N_(\"use full path names\"), 0),\n \t\tOPT_BOOL(0, \"full-tree\", &full_tree,\ndiff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh\nindex d9d7fa932f..d979c0df5d 100755\n--- a/t/t3103-ls-tree-misc.sh\n+++ b/t/t3103-ls-tree-misc.sh\n@@ -25,11 +25,14 @@ test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '\n \n for opts in \\\n \t\"--name-only --long\" \\\n-\t\"--name-status --long\"\n+\t\"--name-status --long\" \\\n+\t\"--name-only --object-only\" \\\n+\t\"--name-status --object-only\" \\\n+\t\"--object-only --long\" \\\n+\t\"--object-only --format\"\n do\n \ttest_expect_success \"usage: incompatible options: $opts\" '\n \t\ttest_expect_code 129 git ls-tree $opts $tree\n     '\n done\n-\n test_done\ndiff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh\nindex 7f1eb699d3..0769a933d6 100755\n--- a/t/t3104-ls-tree-format.sh\n+++ b/t/t3104-ls-tree-format.sh\n@@ -49,6 +49,15 @@ test_ls_tree_format \\\n \t\"%(path)\" \\\n \t\"--name-only\"\n \n+test_ls_tree_format \\\n+\t\"%(objectname)\" \\\n+\t\"--object-only\"\n+\n+test_ls_tree_format \\\n+\t\"%(objectname)\" \\\n+\t\"--object-only --abbrev\" \\\n+\t\"--abbrev\"\n+\n test_ls_tree_format \\\n \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n \t\"-t\" \\\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451968","messageId":"1d5d9470aba9564ef75abd550bc508cc173855f7.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 14/15] ls-tree: detect and error on --name-only --name-status","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:14Z","receivedAt":"2022-03-23T09:14:29Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nThe --name-only and --name-status options are synonyms, but let's\ndetect and error if both are provided.\n\nIn addition let's add explicit --format tests for the combination of\nthese various options.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c       | 11 ++++++++++-\n t/t3103-ls-tree-misc.sh | 15 +++++++++------\n 2 files changed, 19 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3474f8c3d6..6550f27dfe 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -41,6 +41,7 @@ static enum ls_tree_cmdmode {\n \tMODE_DEFAULT = 0,\n \tMODE_LONG,\n \tMODE_NAME_ONLY,\n+\tMODE_NAME_STATUS,\n \tMODE_OBJECT_ONLY,\n } cmdmode;\n \n@@ -296,7 +297,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\tOPT_CMDMODE(0, \"name-only\", &cmdmode, N_(\"list only filenames\"),\n \t\t\t    MODE_NAME_ONLY),\n \t\tOPT_CMDMODE(0, \"name-status\", &cmdmode, N_(\"list only filenames\"),\n-\t\t\t    MODE_NAME_ONLY),\n+\t\t\t    MODE_NAME_STATUS),\n \t\tOPT_CMDMODE(0, \"object-only\", &cmdmode, N_(\"list only objects\"),\n \t\t\t    MODE_OBJECT_ONLY),\n \t\tOPT_SET_INT(0, \"full-name\", &chomp_prefix,\n@@ -322,6 +323,14 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\tls_tree_prefix = prefix = NULL;\n \t\tchomp_prefix = 0;\n \t}\n+\t/*\n+\t * We wanted to detect conflicts between --name-only and\n+\t * --name-status, but once we're done with that subsequent\n+\t * code should only need to check the primary name.\n+\t */\n+\tif (cmdmode == MODE_NAME_STATUS)\n+\t\tcmdmode = MODE_NAME_ONLY;\n+\n \t/* -d -r should imply -t, but -d by itself should not have to. */\n \tif ( (LS_TREE_ONLY|LS_RECURSIVE) ==\n \t    ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))\ndiff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh\nindex d979c0df5d..7454ebbb00 100755\n--- a/t/t3103-ls-tree-misc.sh\n+++ b/t/t3103-ls-tree-misc.sh\n@@ -24,15 +24,18 @@ test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '\n '\n \n for opts in \\\n-\t\"--name-only --long\" \\\n-\t\"--name-status --long\" \\\n-\t\"--name-only --object-only\" \\\n+\t\"--long --name-only\" \\\n+\t\"--name-only --name-status\" \\\n \t\"--name-status --object-only\" \\\n-\t\"--object-only --long\" \\\n-\t\"--object-only --format\"\n+\t\"--object-only --long\" \n do\n \ttest_expect_success \"usage: incompatible options: $opts\" '\n \t\ttest_expect_code 129 git ls-tree $opts $tree\n-    '\n+\t'\n+\n+\tone_opt=$(echo \"$opts\" | cut -d' '  -f1)\n+\ttest_expect_success \"usage: incompatible options: $one_opt and --format\" '\n+\t\ttest_expect_code 129 git ls-tree $one_opt --format=fmt $tree\n+\t'\n done\n test_done\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451969","messageId":"55a96e933b0949bbf3f33153b8e370af6c6299bb.1648026472.git.dyroneteng@gmail.com","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"[PATCH v14 15/15] ls-tree: split up \"fast path\" callbacks","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:13:15Z","receivedAt":"2022-03-23T09:14:38Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nMake the various if/else in the callbacks for the \"fast path\" a lot\neasier to read by just using common functions for the parts that are\ncommon, and have per-format callbacks for those parts that are\ndifferent.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Teng Long <dyroneteng@gmail.com>\n---\n builtin/ls-tree.c | 199 +++++++++++++++++++++++++++++-----------------\n 1 file changed, 125 insertions(+), 74 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 6550f27dfe..44a91cf9d0 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -173,108 +173,157 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,\n \treturn recurse;\n }\n \n-static int show_default(struct show_tree_data *data)\n+static int show_tree_common(struct show_tree_data *data, int *recurse,\n+\t\t\t    const struct object_id *oid, struct strbuf *base,\n+\t\t\t    const char *pathname, unsigned mode)\n {\n-\tsize_t baselen = data->base->len;\n-\n-\tif (cmdmode == MODE_LONG) {\n-\t\tchar size_text[24];\n-\t\tif (data->type == OBJ_BLOB) {\n-\t\t\tunsigned long size;\n-\t\t\tif (oid_object_info(the_repository, data->oid, &size) == OBJ_BAD)\n-\t\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n-\t\t\telse\n-\t\t\t\txsnprintf(size_text, sizeof(size_text),\n-\t\t\t\t\t  \"%\" PRIuMAX, (uintmax_t)size);\n-\t\t} else {\n-\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n-\t\t}\n-\t\tprintf(\"%06o %s %s %7s\\t\", data->mode, type_name(data->type),\n-\t\tfind_unique_abbrev(data->oid, abbrev), size_text);\n-\t} else {\n-\t\tprintf(\"%06o %s %s\\t\", data->mode, type_name(data->type),\n-\t\tfind_unique_abbrev(data->oid, abbrev));\n-\t}\n-\tbaselen = data->base->len;\n-\tstrbuf_addstr(data->base, data->pathname);\n-\twrite_name_quoted_relative(data->base->buf,\n-\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n-\t\t\t\t   line_termination);\n-\tstrbuf_setlen(data->base, baselen);\n-\treturn 1;\n-}\n-\n-static int show_tree(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, void *context)\n-{\n-\tint recurse = 0;\n-\tsize_t baselen;\n \tenum object_type type = object_type(mode);\n-\tstruct show_tree_data data = {\n-\t\t.mode = mode,\n-\t\t.type = type,\n-\t\t.oid = oid,\n-\t\t.pathname = pathname,\n-\t\t.base = base,\n-\t};\n+\tint ret = -1;\n+\n+\t*recurse = 0;\n+\tdata->mode = mode;\n+\tdata->type = type;\n+\tdata->oid = oid;\n+\tdata->pathname = pathname;\n+\tdata->base = base;\n \n \tif (type == OBJ_BLOB) {\n \t\tif (ls_options & LS_TREE_ONLY)\n-\t\t\treturn 0;\n+\t\t\tret = 0;\n \t} else if (type == OBJ_TREE &&\n \t\t   show_recursive(base->buf, base->len, pathname)) {\n-\t\trecurse = READ_TREE_RECURSIVE;\n+\t\t*recurse = READ_TREE_RECURSIVE;\n \t\tif (!(ls_options & LS_SHOW_TREES))\n-\t\t\treturn recurse;\n+\t\t\tret = *recurse;\n \t}\n \n-\tif (cmdmode == MODE_OBJECT_ONLY) {\n-\t\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n-\t\treturn recurse;\n-\t}\n+\treturn ret;\n+}\n \n-\tif (cmdmode == MODE_NAME_ONLY) {\n-\t\tbaselen = base->len;\n-\t\tstrbuf_addstr(base, pathname);\n-\t\twrite_name_quoted_relative(base->buf,\n-\t\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n-\t\t\t\t\t   stdout, line_termination);\n-\t\tstrbuf_setlen(base, baselen);\n-\t\treturn recurse;\n+static void show_tree_common_default_long(struct strbuf *base,\n+\t\t\t\t\t  const char *pathname,\n+\t\t\t\t\t  const size_t baselen)\n+{\n+\tstrbuf_addstr(base, pathname);\n+\twrite_name_quoted_relative(base->buf,\n+\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n+\t\t\t\t   line_termination);\n+\tstrbuf_setlen(base, baselen);\n+}\n+\n+static int show_tree_default(const struct object_id *oid, struct strbuf *base,\n+\t\t\t     const char *pathname, unsigned mode,\n+\t\t\t     void *context)\n+{\n+\tint early;\n+\tint recurse;\n+\tstruct show_tree_data data = { 0 };\n+\n+\tearly = show_tree_common(&data, &recurse, oid, base, pathname, mode);\n+\tif (early >= 0)\n+\t\treturn early;\n+\n+\tprintf(\"%06o %s %s\\t\", data.mode, type_name(data.type),\n+\t       find_unique_abbrev(data.oid, abbrev));\n+\tshow_tree_common_default_long(base, pathname, data.base->len);\n+\treturn recurse;\n+}\n+\n+static int show_tree_long(const struct object_id *oid, struct strbuf *base,\n+\t\t\t  const char *pathname, unsigned mode, void *context)\n+{\n+\tint early;\n+\tint recurse;\n+\tstruct show_tree_data data = { 0 };\n+\tchar size_text[24];\n+\n+\tearly = show_tree_common(&data, &recurse, oid, base, pathname, mode);\n+\tif (early >= 0)\n+\t\treturn early;\n+\n+\tif (data.type == OBJ_BLOB) {\n+\t\tunsigned long size;\n+\t\tif (oid_object_info(the_repository, data.oid, &size) == OBJ_BAD)\n+\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n+\t\telse\n+\t\t\txsnprintf(size_text, sizeof(size_text),\n+\t\t\t\t  \"%\" PRIuMAX, (uintmax_t)size);\n+\t} else {\n+\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n \t}\n \n-\tif (cmdmode == MODE_LONG ||\n-\t\t(!ls_options || (ls_options & LS_RECURSIVE)\n-\t\t || (ls_options & LS_SHOW_TREES)\n-\t\t || (ls_options & LS_TREE_ONLY)))\n-\t\t\t show_default(&data);\n+\tprintf(\"%06o %s %s %7s\\t\", data.mode, type_name(data.type),\n+\t       find_unique_abbrev(data.oid, abbrev), size_text);\n+\tshow_tree_common_default_long(base, pathname, data.base->len);\n+\treturn 1;\n+}\n \n+static int show_tree_name_only(const struct object_id *oid, struct strbuf *base,\n+\t\t\t       const char *pathname, unsigned mode, void *context)\n+{\n+\tint early;\n+\tint recurse;\n+\tconst size_t baselen = base->len;\n+\tstruct show_tree_data data = { 0 };\n+\n+\tearly = show_tree_common(&data, &recurse, oid, base, pathname, mode);\n+\tif (early >= 0)\n+\t\treturn early;\n+\n+\tstrbuf_addstr(base, pathname);\n+\twrite_name_quoted_relative(base->buf,\n+\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n+\t\t\t\t   stdout, line_termination);\n+\tstrbuf_setlen(base, baselen);\n+\treturn recurse;\n+}\n+\n+static int show_tree_object(const struct object_id *oid, struct strbuf *base,\n+\t\t\t    const char *pathname, unsigned mode, void *context)\n+{\n+\tint early;\n+\tint recurse;\n+\tstruct show_tree_data data = { 0 };\n+\n+\tearly = show_tree_common(&data, &recurse, oid, base, pathname, mode);\n+\tif (early >= 0)\n+\t\treturn early;\n+\n+\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n \treturn recurse;\n }\n \n struct ls_tree_cmdmode_to_fmt {\n \tenum ls_tree_cmdmode mode;\n \tconst char *const fmt;\n+\tread_tree_fn_t fn;\n };\n \n static struct ls_tree_cmdmode_to_fmt ls_tree_cmdmode_format[] = {\n \t{\n \t\t.mode = MODE_DEFAULT,\n \t\t.fmt = \"%(objectmode) %(objecttype) %(objectname)%x09%(path)\",\n+\t\t.fn = show_tree_default,\n \t},\n \t{\n \t\t.mode = MODE_LONG,\n \t\t.fmt = \"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\",\n+\t\t.fn = show_tree_long,\n \t},\n \t{\n \t\t.mode = MODE_NAME_ONLY, /* And MODE_NAME_STATUS */\n \t\t.fmt = \"%(path)\",\n+\t\t.fn = show_tree_name_only,\n \t},\n \t{\n \t\t.mode = MODE_OBJECT_ONLY,\n \t\t.fmt = \"%(objectname)\",\n+\t\t.fn = show_tree_object\n+\t},\n+\t{\n+\t\t/* fallback */\n+\t\t.fn = show_tree_default,\n \t},\n-\t{ 0 },\n };\n \n int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n@@ -282,7 +331,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tstruct object_id oid;\n \tstruct tree *tree;\n \tint i, full_tree = 0;\n-\tread_tree_fn_t fn = show_tree;\n+\tread_tree_fn_t fn = NULL;\n \tconst struct option ls_tree_options[] = {\n \t\tOPT_BIT('d', NULL, &ls_options, N_(\"only show trees\"),\n \t\t\tLS_TREE_ONLY),\n@@ -311,6 +360,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\tOPT__ABBREV(&abbrev),\n \t\tOPT_END()\n \t};\n+\tstruct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;\n \n \tgit_config(git_default_config, NULL);\n \tls_tree_prefix = prefix;\n@@ -365,18 +415,19 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t * The generic show_tree_fmt() is slower than show_tree(), so\n \t * take the fast path if possible.\n \t */\n-\tif (format) {\n-\t\tstruct ls_tree_cmdmode_to_fmt *m2f;\n-\n-\t\tfn = show_tree_fmt;\n-\t\tfor (m2f = ls_tree_cmdmode_format; m2f->fmt; m2f++) {\n-\t\t\tif (strcmp(format, m2f->fmt))\n-\t\t\t\tcontinue;\n-\n+\twhile (m2f) {\n+\t\tif (!m2f->fmt) {\n+\t\t\tfn = format ? show_tree_fmt : show_tree_default;\n+\t\t} else if (format && !strcmp(format, m2f->fmt)) {\n \t\t\tcmdmode = m2f->mode;\n-\t\t\tfn = show_tree;\n-\t\t\tbreak;\n+\t\t\tfn = m2f->fn;\n+\t\t} else if (!format && cmdmode == m2f->mode) {\n+\t\t\tfn = m2f->fn;\n+\t\t} else {\n+\t\t\tm2f++;\n+\t\t\tcontinue;\n \t\t}\n+\t\tbreak;\n \t}\n \n \treturn !!read_tree(the_repository, tree, &pathspec, fn, NULL);\n-- \n2.34.1.404.g92931a4a2c.dirty\n\n"},{"id":"451970","messageId":"20220323095854.7787-1-dyroneteng@gmail.com","threadId":"57383","inReplyTo":"220321.865yo7d5ki.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v13 16/16] ls-tree: split up \"fast path\" callbacks","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-23T09:58:54Z","receivedAt":"2022-03-23T09:59:02Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"On Mon, 21 Mar 2022 10:20:34 +0100, Ævar Arnfjörð Bjarmason wrote:\n \n> FWIW I didn't do any exhaustive benchmarks of this, but I checked a few\n> things against origin/master on linux.git and all the reported\n> \"hyperfine\" timings were the same/within the +/- interval.\n\nI sended a reply on gmail but seems not updated on public-inbox,\nso I sended again use git send-mail this time. If the duplicated\nmessage appears, sorry for that.\n\nI tested the performance between fast-paths and non-fast-paths again on linux.git.\nAnd I found the default format appeared a performance regression which maybe was\nbrought by this commit: \n\n    $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r HEAD\"\n    Benchmark 1: /opt/git/master/bin/git ls-tree -r HEAD\n      Time (mean ± σ):     111.3 ms ±   2.3 ms    [User: 86.2 ms, System: 25.0 ms]\n      Range (min … max):   107.8 ms … 115.1 ms    25 runs\n\n\n    $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r --format='%(objectmode) %(objecttype) %(objectname)%x09%(path)' HEAD\"\n    Benchmark 1: /opt/git/master/bin/git ls-tree -r --format='%(objectmode) %(objecttype) %(objectname)%x09%(path)' HEAD\n      Time (mean ± σ):     159.1 ms ±   4.7 ms    [User: 131.8 ms, System: 27.3 ms]\n      Range (min … max):   152.3 ms … 170.4 ms    19 runs\n\nFurther, the other fast-paths except \"default format\" seem like what we expect, then I made this change:\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex c9eb7f7243..7d784f97c7 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -406,15 +406,17 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n         * The generic show_tree_fmt() is slower than show_tree(), so\n         * take the fast path if possible.\n         */\n-       while (m2f++) {\n+       while (m2f) {\n                if (!m2f->fmt) {\n                        fn = format ? show_tree_fmt : show_tree_default;\n                } else if (format && !strcmp(format, m2f->fmt)) {\n+                       fprintf(stderr, \"[dyrone] format: %s\\n\", format);\n                        cmdmode = m2f->mode;\n                        fn = m2f->fn;\n                } else if (!format && cmdmode == m2f->mode) {\n                        fn = m2f->fn;\n                } else {\n+                       m2f++;\n                        continue;\n                }\n                break;\n\n----------end diff---------------\n\nAfter the scenario has been tested agin, the result seems OK:\n\n    $hyperfine --warmup=10 \"/opt/git/master/bin/git ls-tree -r --format='%(objectmode) %(objecttype) %(objectname)%x09%(path)' HEAD\"\n    Benchmark 1: /opt/git/master/bin/git ls-tree -r --format='%(objectmode) %(objecttype) %(objectname)%x09%(path)' HEAD\n      Time (mean ± σ):     112.2 ms ±   2.5 ms    [User: 86.3 ms, System: 25.9 ms]\n      Range (min … max):   108.8 ms … 117.4 ms    25 runs\n\n\nThanks.\n\n"},{"id":"452021","messageId":"xmqqee2ssaut.fsf@gitster.g","threadId":"57383","inReplyTo":"cover.1648026472.git.dyroneteng@gmail.com","subject":"Re: [PATCH v14 00/15] ls-tree: \"--object-only\" and \"--format\" opts","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-23T19:54:50Z","receivedAt":"2022-03-23T19:55:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Teng Long <dyroneteng@gmail.com> writes:\n\n>  8:  55f1e10d7e !  9:  f4fc377dfa ls-tree: slightly refactor `show_tree()`\n>     @@ Metadata\n>       ## Commit message ##\n>          ls-tree: slightly refactor `show_tree()`\n>      \n>     -    This is a non-functional change, we use a new int \"shown_fields\" to mark\n>     -    which columns to output, and `parse_shown_fields()` to calculate the\n>     -    value of \"shown_fields\".\n>     +    This is a non-functional change, we introduce an enum \"ls_tree_cmdmode\"\n>     +    then use it to mark which columns to output.\n\nI think readers will read \"non-functional change\" as \"we changed it\nand made it not function anymore\", but you meant something else ;-)\n\nI'll let it pass, though, for the sake of expediency.  The series\nsomehow ended up to be unnecessarily longer and more complicated,\nbecoming closer to a complete rewrite of the implementation,\ncompared to the earlier rounds which were more like incremental\nenhancement.\n\nThanks, will queue.\n\n>          This has the advantage of making the show_tree logic simpler and more\n>          readable, as well as making it easier to extend new options (for example,\n>          if we want to add a \"--object-only\" option, we just need to add a similar\n>     +    \"short-circuit logic in \"show_tree()\").\n\n"},{"id":"452081","messageId":"20220324030010.124703-1-dyroneteng@gmail.com","threadId":"57383","inReplyTo":"xmqqee2ssaut.fsf@gitster.g","subject":"Re: [PATCH v14 00/15] ls-tree: \"--object-only\" and \"--format\" opts","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-03-24T03:00:10Z","receivedAt":"2022-03-24T03:00:19Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"> I think readers will read \"non-functional change\" as \"we changed it\n> and made it not function anymore\", but you meant something else ;-)\n\nI tried to translate by Google and the translation is OK, but indeed\nit will bring some little murmur on \"function\", I will try to avoid\nthis like ambiguity words for more improved description.\n\nThanks.\n"},{"id":"453050","messageId":"YktP0LltNKq7JZZs@google.com","threadId":"57383","inReplyTo":"55a96e933b0949bbf3f33153b8e370af6c6299bb.1648026472.git.dyroneteng@gmail.com","subject":"Re: [PATCH v14 15/15] ls-tree: split up \"fast path\" callbacks","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2022-04-04T20:06:40Z","receivedAt":"2022-04-04T21:23:13Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2022.03.23 17:13, Teng Long wrote:\n> From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> \n> Make the various if/else in the callbacks for the \"fast path\" a lot\n> easier to read by just using common functions for the parts that are\n> common, and have per-format callbacks for those parts that are\n> different.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> Signed-off-by: Teng Long <dyroneteng@gmail.com>\n> ---\n>  builtin/ls-tree.c | 199 +++++++++++++++++++++++++++++-----------------\n>  1 file changed, 125 insertions(+), 74 deletions(-)\n> \n> diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\n> index 6550f27dfe..44a91cf9d0 100644\n> --- a/builtin/ls-tree.c\n> +++ b/builtin/ls-tree.c\n> @@ -173,108 +173,157 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,\n>  \treturn recurse;\n>  }\n>  \n> -static int show_default(struct show_tree_data *data)\n> +static int show_tree_common(struct show_tree_data *data, int *recurse,\n> +\t\t\t    const struct object_id *oid, struct strbuf *base,\n> +\t\t\t    const char *pathname, unsigned mode)\n>  {\n> -\tsize_t baselen = data->base->len;\n> -\n> -\tif (cmdmode == MODE_LONG) {\n> -\t\tchar size_text[24];\n> -\t\tif (data->type == OBJ_BLOB) {\n> -\t\t\tunsigned long size;\n> -\t\t\tif (oid_object_info(the_repository, data->oid, &size) == OBJ_BAD)\n> -\t\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n> -\t\t\telse\n> -\t\t\t\txsnprintf(size_text, sizeof(size_text),\n> -\t\t\t\t\t  \"%\" PRIuMAX, (uintmax_t)size);\n> -\t\t} else {\n> -\t\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n> -\t\t}\n> -\t\tprintf(\"%06o %s %s %7s\\t\", data->mode, type_name(data->type),\n> -\t\tfind_unique_abbrev(data->oid, abbrev), size_text);\n> -\t} else {\n> -\t\tprintf(\"%06o %s %s\\t\", data->mode, type_name(data->type),\n> -\t\tfind_unique_abbrev(data->oid, abbrev));\n> -\t}\n> -\tbaselen = data->base->len;\n> -\tstrbuf_addstr(data->base, data->pathname);\n> -\twrite_name_quoted_relative(data->base->buf,\n> -\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n> -\t\t\t\t   line_termination);\n> -\tstrbuf_setlen(data->base, baselen);\n> -\treturn 1;\n> -}\n> -\n> -static int show_tree(const struct object_id *oid, struct strbuf *base,\n> -\t\tconst char *pathname, unsigned mode, void *context)\n> -{\n> -\tint recurse = 0;\n> -\tsize_t baselen;\n>  \tenum object_type type = object_type(mode);\n> -\tstruct show_tree_data data = {\n> -\t\t.mode = mode,\n> -\t\t.type = type,\n> -\t\t.oid = oid,\n> -\t\t.pathname = pathname,\n> -\t\t.base = base,\n> -\t};\n> +\tint ret = -1;\n> +\n> +\t*recurse = 0;\n> +\tdata->mode = mode;\n> +\tdata->type = type;\n> +\tdata->oid = oid;\n> +\tdata->pathname = pathname;\n> +\tdata->base = base;\n>  \n>  \tif (type == OBJ_BLOB) {\n>  \t\tif (ls_options & LS_TREE_ONLY)\n> -\t\t\treturn 0;\n> +\t\t\tret = 0;\n>  \t} else if (type == OBJ_TREE &&\n>  \t\t   show_recursive(base->buf, base->len, pathname)) {\n> -\t\trecurse = READ_TREE_RECURSIVE;\n> +\t\t*recurse = READ_TREE_RECURSIVE;\n>  \t\tif (!(ls_options & LS_SHOW_TREES))\n> -\t\t\treturn recurse;\n> +\t\t\tret = *recurse;\n>  \t}\n>  \n> -\tif (cmdmode == MODE_OBJECT_ONLY) {\n> -\t\tprintf(\"%s%c\", find_unique_abbrev(oid, abbrev), line_termination);\n> -\t\treturn recurse;\n> -\t}\n> +\treturn ret;\n> +}\n>  \n> -\tif (cmdmode == MODE_NAME_ONLY) {\n> -\t\tbaselen = base->len;\n> -\t\tstrbuf_addstr(base, pathname);\n> -\t\twrite_name_quoted_relative(base->buf,\n> -\t\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL,\n> -\t\t\t\t\t   stdout, line_termination);\n> -\t\tstrbuf_setlen(base, baselen);\n> -\t\treturn recurse;\n> +static void show_tree_common_default_long(struct strbuf *base,\n> +\t\t\t\t\t  const char *pathname,\n> +\t\t\t\t\t  const size_t baselen)\n> +{\n> +\tstrbuf_addstr(base, pathname);\n> +\twrite_name_quoted_relative(base->buf,\n> +\t\t\t\t   chomp_prefix ? ls_tree_prefix : NULL, stdout,\n> +\t\t\t\t   line_termination);\n> +\tstrbuf_setlen(base, baselen);\n> +}\n> +\n> +static int show_tree_default(const struct object_id *oid, struct strbuf *base,\n> +\t\t\t     const char *pathname, unsigned mode,\n> +\t\t\t     void *context)\n> +{\n> +\tint early;\n> +\tint recurse;\n> +\tstruct show_tree_data data = { 0 };\n> +\n> +\tearly = show_tree_common(&data, &recurse, oid, base, pathname, mode);\n> +\tif (early >= 0)\n> +\t\treturn early;\n> +\n> +\tprintf(\"%06o %s %s\\t\", data.mode, type_name(data.type),\n> +\t       find_unique_abbrev(data.oid, abbrev));\n> +\tshow_tree_common_default_long(base, pathname, data.base->len);\n> +\treturn recurse;\n> +}\n> +\n> +static int show_tree_long(const struct object_id *oid, struct strbuf *base,\n> +\t\t\t  const char *pathname, unsigned mode, void *context)\n> +{\n> +\tint early;\n> +\tint recurse;\n> +\tstruct show_tree_data data = { 0 };\n> +\tchar size_text[24];\n> +\n> +\tearly = show_tree_common(&data, &recurse, oid, base, pathname, mode);\n> +\tif (early >= 0)\n> +\t\treturn early;\n> +\n> +\tif (data.type == OBJ_BLOB) {\n> +\t\tunsigned long size;\n> +\t\tif (oid_object_info(the_repository, data.oid, &size) == OBJ_BAD)\n> +\t\t\txsnprintf(size_text, sizeof(size_text), \"BAD\");\n> +\t\telse\n> +\t\t\txsnprintf(size_text, sizeof(size_text),\n> +\t\t\t\t  \"%\" PRIuMAX, (uintmax_t)size);\n> +\t} else {\n> +\t\txsnprintf(size_text, sizeof(size_text), \"-\");\n>  \t}\n>  \n> -\tif (cmdmode == MODE_LONG ||\n> -\t\t(!ls_options || (ls_options & LS_RECURSIVE)\n> -\t\t || (ls_options & LS_SHOW_TREES)\n> -\t\t || (ls_options & LS_TREE_ONLY)))\n> -\t\t\t show_default(&data);\n> +\tprintf(\"%06o %s %s %7s\\t\", data.mode, type_name(data.type),\n> +\t       find_unique_abbrev(data.oid, abbrev), size_text);\n> +\tshow_tree_common_default_long(base, pathname, data.base->len);\n> +\treturn 1;\n> +}\n\nThis commit makes `git ls-tree -l ...` list files recursively,\nregardless of whether `-r` was provided or not. I believe it's due to\nshow_tree_long() returning 1 instead of the value of recurse. Is there a\nreason why we unconditionally return 1 here?\n"},{"id":"453066","messageId":"9ce4dadf140204e934f7025bb91385c376118940.1649111831.git.steadmon@google.com","threadId":"57383","inReplyTo":"YktP0LltNKq7JZZs@google.com","subject":"[RFC PATCH] ls-tree: `-l` should not imply recursive listing","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2022-04-04T22:42:24Z","receivedAt":"2022-04-04T23:08:21Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"In 9c4d58ff2c (ls-tree: split up \"fast path\" callbacks, 2022-03-23), a\nrefactoring of the various read_tree_at() callbacks caused us to\nunconditionally recurse into directories if `-l` (long format) was\npassed on the command line, regardless of whether or not we also pass\nthe `-r` (recursive) flag.\n\nFix this by making show_tree_long() return the value of `recurse`,\nrather than always returning 1. This value is interpreted by\nread_tree_at() to be a signal on whether or not to recurse.\n\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\nI believe this is the correct fix for the change in `git ls-tree -l`\noutput. I would also like to add tests in a future fix, but I do not\nhave time to add them today.\n\n builtin/ls-tree.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 5dac9ee5b9..e279be8bb6 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -255,7 +255,7 @@ static int show_tree_long(const struct object_id *oid, struct strbuf *base,\n \tprintf(\"%06o %s %s %7s\\t\", data.mode, type_name(data.type),\n \t       find_unique_abbrev(data.oid, abbrev), size_text);\n \tshow_tree_common_default_long(base, pathname, data.base->len);\n-\treturn 1;\n+\treturn recurse;\n }\n \n static int show_tree_name_only(const struct object_id *oid, struct strbuf *base,\n\nbase-commit: faa21c10d44184f616d391c158dcbb13b9c72ef3\n-- \n2.35.1.1094.g7c7d902a7c-goog\n\n"},{"id":"453074","messageId":"patch-v2-1.1-ed83b3b74ab-20220404T234507Z-avarab@gmail.com","threadId":"57383","inReplyTo":"9ce4dadf140204e934f7025bb91385c376118940.1649111831.git.steadmon@google.com","subject":"[PATCH v2] ls-tree: fix --long implying -r regression in 9c4d58ff2c3","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-04T23:45:30Z","receivedAt":"2022-04-05T02:19:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a regression introduced in 9c4d58ff2c3 (ls-tree: split up \"fast\npath\" callbacks, 2022-03-23), and improve the tests added in the\n1041d58b4d9 (Merge branch 'tl/ls-tree-oid-only', 2022-04-04) topic it\nwas merged as part of to test the full expected output of various\n\"ls-tree\" options with and without -r.\n\nLet's fix it, and also add tests not only for that blindspot, but also\nany other potential blindspots. To that end test the \"modes\" of -d, -r\nand -t (as well as \"no mode\") against all of the format options.\n\nThese tests all pass with that topic reverted (except those that would\nfail because they're testing the new --object-only feature introduced\nin that topic), which should give us confidence that there were no\nfurther regressions in this area.\n\nReported-By: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n\nOn Mon, Apr 04 2022, Josh Steadmon wrote:\n\n> I believe this is the correct fix for the change in `git ls-tree -l`\n> output. I would also like to add tests in a future fix, but I do not\n> have time to add them today.\n\nIndeed. I guess that makes this a proposed v2,\n\nI refreshed my E-Mail when I was just about to submit this and spotted\nthat you'd sent your fix in, but I came up with this (in retrospect a\npretty obvious think-o) fix independently, sorry about the bug.\n\nThe tests took me a bit longer though...\n\nHaing written them I guess we could do them post-release, since the\nimportant thing is to validate the changes. As noted in the commit\nmessage we're now testing all combinations of the \"mode\" and \"format\"\noptions.\n\n builtin/ls-tree.c         |   2 +-\n t/t3104-ls-tree-format.sh | 126 +++++++++++++++++++++++++++++++++++---\n 2 files changed, 119 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 5dac9ee5b9d..e279be8bb63 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -255,7 +255,7 @@ static int show_tree_long(const struct object_id *oid, struct strbuf *base,\n \tprintf(\"%06o %s %s %7s\\t\", data.mode, type_name(data.type),\n \t       find_unique_abbrev(data.oid, abbrev), size_text);\n \tshow_tree_common_default_long(base, pathname, data.base->len);\n-\treturn 1;\n+\treturn recurse;\n }\n \n static int show_tree_name_only(const struct object_id *oid, struct strbuf *base,\ndiff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh\nindex 0769a933d69..520b5a95c08 100755\n--- a/t/t3104-ls-tree-format.sh\n+++ b/t/t3104-ls-tree-format.sh\n@@ -23,6 +23,19 @@ test_ls_tree_format () {\n \tfmtopts=$3 &&\n \tshift 2 &&\n \n+\tcat >expect &&\n+\tcat <&6 >expect.-d &&\n+\tcat <&7 >expect.-r &&\n+\tcat <&8 >expect.-t &&\n+\n+\tfor opt in '' '-d' '-r' '-t'\n+\tdo\n+\t\ttest_expect_success \"'ls-tree $opts${opt:+ $opt}' output\" '\n+\t\t\tgit ls-tree ${opt:+$opt }$opts $opt HEAD >actual &&\n+\t\t\ttest_cmp expect${opt:+.$opt} actual\n+\t\t'\n+\tdone\n+\n \ttest_expect_success \"ls-tree '--format=<$format>' is like options '$opts $fmtopts'\" '\n \t\tgit ls-tree $opts -r HEAD >expect &&\n \t\tgit ls-tree --format=\"$format\" -r $fmtopts HEAD >actual &&\n@@ -39,38 +52,135 @@ test_ls_tree_format () {\n \n test_ls_tree_format \\\n \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n-\t\"\"\n+\t\"\" \\\n+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n+\tOUT\n+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n+\tOUT_D\n+\t100644 blob $(git rev-parse HEAD:dir/sub-file.t)\tdir/sub-file.t\n+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n+\tOUT_R\n+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n+\tOUT_T\n \n test_ls_tree_format \\\n \t\"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\" \\\n-\t\"--long\"\n+\t\"--long\" \\\n+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n+\t040000 tree $(git rev-parse HEAD:dir)       -\tdir\n+\t100644 blob $(git rev-parse HEAD:top-file.t)       9\ttop-file.t\n+\tOUT\n+\t040000 tree $(git rev-parse HEAD:dir)       -\tdir\n+\tOUT_D\n+\t100644 blob $(git rev-parse HEAD:dir/sub-file.t)      13\tdir/sub-file.t\n+\t100644 blob $(git rev-parse HEAD:top-file.t)       9\ttop-file.t\n+\tOUT_R\n+\t040000 tree $(git rev-parse HEAD:dir)       -\tdir\n+\t100644 blob $(git rev-parse HEAD:top-file.t)       9\ttop-file.t\n+\tOUT_T\n \n test_ls_tree_format \\\n \t\"%(path)\" \\\n-\t\"--name-only\"\n+\t\"--name-only\" \\\n+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n+\tdir\n+\ttop-file.t\n+\tOUT\n+\tdir\n+\tOUT_D\n+\tdir/sub-file.t\n+\ttop-file.t\n+\tOUT_R\n+\tdir\n+\ttop-file.t\n+\tOUT_T\n \n test_ls_tree_format \\\n \t\"%(objectname)\" \\\n-\t\"--object-only\"\n+\t\"--object-only\" \\\n+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n+\t$(git rev-parse HEAD:dir)\n+\t$(git rev-parse HEAD:top-file.t)\n+\tOUT\n+\t$(git rev-parse HEAD:dir)\n+\tOUT_D\n+\t$(git rev-parse HEAD:dir/sub-file.t)\n+\t$(git rev-parse HEAD:top-file.t)\n+\tOUT_R\n+\t$(git rev-parse HEAD:dir)\n+\t$(git rev-parse HEAD:top-file.t)\n+\tOUT_T\n \n test_ls_tree_format \\\n \t\"%(objectname)\" \\\n \t\"--object-only --abbrev\" \\\n-\t\"--abbrev\"\n+\t\"--abbrev\" \\\n+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n+\t$(git rev-parse HEAD:dir | test_copy_bytes 7)\n+\t$(git rev-parse HEAD:top-file.t| test_copy_bytes 7)\n+\tOUT\n+\t$(git rev-parse HEAD:dir | test_copy_bytes 7)\n+\tOUT_D\n+\t$(git rev-parse HEAD:dir/sub-file.t | test_copy_bytes 7)\n+\t$(git rev-parse HEAD:top-file.t | test_copy_bytes 7)\n+\tOUT_R\n+\t$(git rev-parse HEAD:dir | test_copy_bytes 7)\n+\t$(git rev-parse HEAD:top-file.t | test_copy_bytes 7)\n+\tOUT_T\n \n test_ls_tree_format \\\n \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n \t\"-t\" \\\n-\t\"-t\"\n+\t\"-t\" \\\n+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n+\tOUT\n+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n+\tOUT_D\n+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n+\t100644 blob $(git rev-parse HEAD:dir/sub-file.t)\tdir/sub-file.t\n+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n+\tOUT_R\n+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n+\tOUT_T\n \n test_ls_tree_format \\\n \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n \t\"--full-name\" \\\n-\t\"--full-name\"\n+\t\"--full-name\" \\\n+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n+\tOUT\n+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n+\tOUT_D\n+\t100644 blob $(git rev-parse HEAD:dir/sub-file.t)\tdir/sub-file.t\n+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n+\tOUT_R\n+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n+\tOUT_T\n \n test_ls_tree_format \\\n \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n \t\"--full-tree\" \\\n-\t\"--full-tree\"\n+\t\"--full-tree\" \\\n+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n+\tOUT\n+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n+\tOUT_D\n+\t100644 blob $(git rev-parse HEAD:dir/sub-file.t)\tdir/sub-file.t\n+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n+\tOUT_R\n+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n+\tOUT_T\n \n test_done\n-- \n2.35.1.1604.g35dc6517170\n\n"},{"id":"453194","messageId":"xmqq8rsi1ap4.fsf@gitster.g","threadId":"57383","inReplyTo":"9ce4dadf140204e934f7025bb91385c376118940.1649111831.git.steadmon@google.com","subject":"Re: [RFC PATCH] ls-tree: `-l` should not imply recursive listing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-06T15:41:11Z","receivedAt":"2022-04-06T17:32:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> In 9c4d58ff2c (ls-tree: split up \"fast path\" callbacks, 2022-03-23), a\n> refactoring of the various read_tree_at() callbacks caused us to\n> unconditionally recurse into directories if `-l` (long format) was\n> passed on the command line, regardless of whether or not we also pass\n> the `-r` (recursive) flag.\n>\n> Fix this by making show_tree_long() return the value of `recurse`,\n> rather than always returning 1. This value is interpreted by\n> read_tree_at() to be a signal on whether or not to recurse.\n>\n> Signed-off-by: Josh Steadmon <steadmon@google.com>\n> ---\n> I believe this is the correct fix for the change in `git ls-tree -l`\n> output. I would also like to add tests in a future fix, but I do not\n> have time to add them today.\n\nIt's rather an obvious bug and it is sad that existing tests did not\nfind (and no, I do not think lower-level unit testing would have\nhelped us in any way, either).\n\nWill queue.\n\nThanks for a quick fix.\n\n\n>  builtin/ls-tree.c | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n>\n> diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\n> index 5dac9ee5b9..e279be8bb6 100644\n> --- a/builtin/ls-tree.c\n> +++ b/builtin/ls-tree.c\n> @@ -255,7 +255,7 @@ static int show_tree_long(const struct object_id *oid, struct strbuf *base,\n>  \tprintf(\"%06o %s %s %7s\\t\", data.mode, type_name(data.type),\n>  \t       find_unique_abbrev(data.oid, abbrev), size_text);\n>  \tshow_tree_common_default_long(base, pathname, data.base->len);\n> -\treturn 1;\n> +\treturn recurse;\n>  }\n>  \n>  static int show_tree_name_only(const struct object_id *oid, struct strbuf *base,\n>\n> base-commit: faa21c10d44184f616d391c158dcbb13b9c72ef3\n"},{"id":"453211","messageId":"xmqqwng2xfi8.fsf@gitster.g","threadId":"57383","inReplyTo":"patch-v2-1.1-ed83b3b74ab-20220404T234507Z-avarab@gmail.com","subject":"Re: [PATCH v2] ls-tree: fix --long implying -r regression in 9c4d58ff2c3","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-06T17:56:15Z","receivedAt":"2022-04-06T20:02: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> Haing written them I guess we could do them post-release, since the\n> important thing is to validate the changes. As noted in the commit\n> message we're now testing all combinations of the \"mode\" and \"format\"\n> options.\n>\n>  builtin/ls-tree.c         |   2 +-\n>  t/t3104-ls-tree-format.sh | 126 +++++++++++++++++++++++++++++++++++---\n>  2 files changed, 119 insertions(+), 9 deletions(-)\n>\n> diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\n> index 5dac9ee5b9d..e279be8bb63 100644\n> --- a/builtin/ls-tree.c\n> +++ b/builtin/ls-tree.c\n> @@ -255,7 +255,7 @@ static int show_tree_long(const struct object_id *oid, struct strbuf *base,\n>  \tprintf(\"%06o %s %s %7s\\t\", data.mode, type_name(data.type),\n>  \t       find_unique_abbrev(data.oid, abbrev), size_text);\n>  \tshow_tree_common_default_long(base, pathname, data.base->len);\n> -\treturn 1;\n> +\treturn recurse;\n>  }\n\nOK, two people by happenstance wrote the same fix is reassuring ;-)\n\n> +\tcat >expect &&\n> +\tcat <&6 >expect.-d &&\n> +\tcat <&7 >expect.-r &&\n> +\tcat <&8 >expect.-t &&\n\nLet's not go too cute like this.  This forces the caller to remember\nwhich among 6, 7, and 8 corresponds to which option.  It is too ugly\nto live.\n\n>  test_ls_tree_format \\\n>  \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n> -\t\"\"\n> +\t\"\" \\\n> +\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n> +\t040000 tree $(git rev-parse HEAD:dir)\tdir\n> +\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n> +\tOUT\n> +\t040000 tree $(git rev-parse HEAD:dir)\tdir\n> +\tOUT_D\n> +\t100644 blob $(git rev-parse HEAD:dir/sub-file.t)\tdir/sub-file.t\n> +\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n> +\tOUT_R\n> +\t040000 tree $(git rev-parse HEAD:dir)\tdir\n> +\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n> +\tOUT_T\n\nI do not actually mind if it were more like this:\n\n\ttest_ls_tree_format \\\n\t\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" <<-EXPECT\n\tgit ls-tree HEAD\n\t040000 tree $(git rev-parse HEAD:dir)\tdir\n\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n\tgit ls-tree -d HEAD\n\t040000 tree $(git rev-parse HEAD:dir)\tdir\n\tgit ls-tree -r HEAD\n\t100644 blob $(git rev-parse HEAD:dir/sub-file.t)\tdir/sub-file.t\n\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n\tgit ls-tree -t HEAD\n\t040000 tree $(git rev-parse HEAD:dir)\tdir\n\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n\tEXPECT\n\nthat is, we only read from the standard input, each section begins\nwith \"git ls-tree\" command invocation that gets tested, followed by\nits expected output, and ends at either the end of the input or the\nbeginning of the next section.\n"},{"id":"453229","messageId":"220406.8635iqdjzl.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"xmqqwng2xfi8.fsf@gitster.g","subject":"Re: [PATCH v2] ls-tree: fix --long implying -r regression in 9c4d58ff2c3","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-06T20:36:20Z","receivedAt":"2022-04-06T21:30:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Apr 06 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Haing written them I guess we could do them post-release, since the\n>> important thing is to validate the changes. As noted in the commit\n>> message we're now testing all combinations of the \"mode\" and \"format\"\n>> options.\n>>\n>>  builtin/ls-tree.c         |   2 +-\n>>  t/t3104-ls-tree-format.sh | 126 +++++++++++++++++++++++++++++++++++---\n>>  2 files changed, 119 insertions(+), 9 deletions(-)\n>>\n>> diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\n>> index 5dac9ee5b9d..e279be8bb63 100644\n>> --- a/builtin/ls-tree.c\n>> +++ b/builtin/ls-tree.c\n>> @@ -255,7 +255,7 @@ static int show_tree_long(const struct object_id *oid, struct strbuf *base,\n>>  \tprintf(\"%06o %s %s %7s\\t\", data.mode, type_name(data.type),\n>>  \t       find_unique_abbrev(data.oid, abbrev), size_text);\n>>  \tshow_tree_common_default_long(base, pathname, data.base->len);\n>> -\treturn 1;\n>> +\treturn recurse;\n>>  }\n>\n> OK, two people by happenstance wrote the same fix is reassuring ;-)\n\n*nod*\n\n>> +\tcat >expect &&\n>> +\tcat <&6 >expect.-d &&\n>> +\tcat <&7 >expect.-r &&\n>> +\tcat <&8 >expect.-t &&\n>\n> Let's not go too cute like this.  This forces the caller to remember\n> which among 6, 7, and 8 corresponds to which option.  It is too ugly\n> to live.\n\nI think it's rather elegant actually, but to be fair it would, per:\n\n\tgit grep '<&[1-9]| [1-9]<<-'\n\nBe the user with the most FD's using this sort of pattern.\n\n>>  test_ls_tree_format \\\n>>  \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n>> -\t\"\"\n>> +\t\"\" \\\n>> +\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n>> +\t040000 tree $(git rev-parse HEAD:dir)\tdir\n>> +\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n>> +\tOUT\n>> +\t040000 tree $(git rev-parse HEAD:dir)\tdir\n>> +\tOUT_D\n>> +\t100644 blob $(git rev-parse HEAD:dir/sub-file.t)\tdir/sub-file.t\n>> +\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n>> +\tOUT_R\n>> +\t040000 tree $(git rev-parse HEAD:dir)\tdir\n>> +\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n>> +\tOUT_T\n>\n> I do not actually mind if it were more like this:\n>\n> \ttest_ls_tree_format \\\n> \t\t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" <<-EXPECT\n> \tgit ls-tree HEAD\n> \t040000 tree $(git rev-parse HEAD:dir)\tdir\n> \t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n> \tgit ls-tree -d HEAD\n> \t040000 tree $(git rev-parse HEAD:dir)\tdir\n> \tgit ls-tree -r HEAD\n> \t100644 blob $(git rev-parse HEAD:dir/sub-file.t)\tdir/sub-file.t\n> \t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n> \tgit ls-tree -t HEAD\n> \t040000 tree $(git rev-parse HEAD:dir)\tdir\n> \t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n> \tEXPECT\n>\n> that is, we only read from the standard input, each section begins\n> with \"git ls-tree\" command invocation that gets tested, followed by\n> its expected output, and ends at either the end of the input or the\n> beginning of the next section.\n\nI don't see how needing to implementa while-loop parser for a custom\nformat just to get to the end-goal of having those things split up for\nus is more elegant, when the shell can do that by splitting those up by\nstream.\n\nBut if you insist on this being rewritten we could just unroll the loop,\nand have each test_ls_tree_format specify the full option and one\nstandard input, but doing it in the function was a bit less verbosity.\n\nAnyway, I see you replied on Josh's that you'd queue it, so it's unclear\nto me if you'd like to pick this up with the version with the tests at\nall, so I won't hack up a \"v3\" until that's clear...\n"},{"id":"453233","messageId":"xmqq4k35x4m6.fsf@gitster.g","threadId":"57383","inReplyTo":"220406.8635iqdjzl.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v2] ls-tree: fix --long implying -r regression in 9c4d58ff2c3","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-06T21:51:29Z","receivedAt":"2022-04-06T21:53: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>>> +\tcat >expect &&\n>>> +\tcat <&6 >expect.-d &&\n>>> +\tcat <&7 >expect.-r &&\n>>> +\tcat <&8 >expect.-t &&\n>>\n>> Let's not go too cute like this.  This forces the caller to remember\n>> which among 6, 7, and 8 corresponds to which option.  It is too ugly\n>> to live.\n>\n> I think it's rather elegant actually, but to be fair it would, per:\n>\n> \tgit grep '<&[1-9]| [1-9]<<-'\n>\n> Be the user with the most FD's using this sort of pattern.\n\nPlease give a clear explanation why \"-d\" has to be 6, \"-r\" 7 and\n\"-t\" 8, that can be used by developers as a memory aid to help them\nwrite new tests using the helper.\n\nOr justify why the developers have to memorize such a meaningless\ncorrespondence, if there is no any good reason.\n\nAlternatively, you can stop abusing the word \"elegant\".  It is not a\nsynonym to \"what I wrote\" ;-).\n"},{"id":"453246","messageId":"220407.86y20hcpy5.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"xmqq4k35x4m6.fsf@gitster.g","subject":"Re: [PATCH v2] ls-tree: fix --long implying -r regression in 9c4d58ff2c3","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-07T07:14:40Z","receivedAt":"2022-04-07T07:28:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Apr 06 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>>>> +\tcat >expect &&\n>>>> +\tcat <&6 >expect.-d &&\n>>>> +\tcat <&7 >expect.-r &&\n>>>> +\tcat <&8 >expect.-t &&\n>>>\n>>> Let's not go too cute like this.  This forces the caller to remember\n>>> which among 6, 7, and 8 corresponds to which option.  It is too ugly\n>>> to live.\n>>\n>> I think it's rather elegant actually, but to be fair it would, per:\n>>\n>> \tgit grep '<&[1-9]| [1-9]<<-'\n>>\n>> Be the user with the most FD's using this sort of pattern.\n>\n> Please give a clear explanation why \"-d\" has to be 6, \"-r\" 7 and\n> \"-t\" 8, that can be used by developers as a memory aid to help them\n> write new tests using the helper.\n\nIt's documented when test-lib.sh does the redirection, since Fabian\nStelzer's a6714088e0c (test-lib: make BAIL_OUT() work in tests and\nprereq, 2021-12-01).\n\nI guess a bit of archane knowledge not documented there is that FD #5 is\nused for the test-lib.sh itself, e.g. BUG(), so if you want that to work\nproperly you can't touch it.\n\nBut everything as of #6 is generally fair game, secondary helpers like\n\"test_pause\" won't work properly, but as far as the test framork is\nconcerned 6-9 is good.\n\nSo I don't really have a better explanation the same on as why pick \"int\ni\" rather than \"int x\", it's just the prevailing pattern.\n\n> Or justify why the developers have to memorize such a meaningless\n> correspondence, if there is no any good reason.\n\nExplained above, I think.\n\n> Alternatively, you can stop abusing the word \"elegant\".  It is not a\n> synonym to \"what I wrote\" ;-).\n\nThe \"elegant\" part is getting away with passing structured data into a\nshell function, which it's generally resistant to.\n\nAs much as I'd like to take credit from it I just picked it up from code\nIlya wrote in 0445e6f0a12 (test-lib: '--run' to run only specific tests,\n2014-04-30). I didn't know about it before then.\n\nIn any case, you merged down Josh's version without these tests for rc1,\nso I'm assuming that's a \"no\" to the upthread \"if you'd like to pick\nthis up with the version with the tests at all\" in the context of the RC\nperiod.\n\nSo I think we can table this for now, or would you like a version of\nthis without (a)use of these file descriptors to pass in arguments?\n"},{"id":"453249","messageId":"20220407092937.66197-1-dyroneteng@gmail.com","threadId":"57383","inReplyTo":"patch-v2-1.1-ed83b3b74ab-20220404T234507Z-avarab@gmail.com","subject":"Re: [PATCH v2] ls-tree: fix --long implying -r regression in 9c4d58ff2c3","fromName":"Teng Long","fromEmail":"dyroneteng@gmail.com","sentAt":"2022-04-07T09:29:37Z","receivedAt":"2022-04-07T09:29:47Z","isPatch":true,"sender":{"key":"dyroneteng@gmail.com","avatar":"https://avatars.githubusercontent.com/u/7803958?v=4"},"body":"\nOn Tue,  5 Apr 2022 01:45:30 +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> Indeed. I guess that makes this a proposed v2,\n> \n> I refreshed my E-Mail when I was just about to submit this and spotted\n> that you'd sent your fix in, but I came up with this (in retrospect a\n> pretty obvious think-o) fix independently, sorry about the bug.\n> \n> The tests took me a bit longer though...\n> \n> Haing written them I guess we could do them post-release, since the\n> important thing is to validate the changes. As noted in the commit\n> message we're now testing all combinations of the \"mode\" and \"format\"\n> options.\n\n\nThanks for the quickly fix.\n\n> +    \t       git ls-tree ${opt:+$opt }$opts $opt HEAD >actual &&\n\nI think maybe the \"$opt\" here should be removed, because \"${opt:+$opt }\"\nwill not append it if \"$opt\" is null or unset, or will append \"$opt \",\nlike:\n\ndiff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh\nindex 520b5a95c0..9d3ad0295e 100755\n--- a/t/t3104-ls-tree-format.sh\n+++ b/t/t3104-ls-tree-format.sh\n@@ -31,7 +31,7 @@ test_ls_tree_format () {\n        for opt in '' '-d' '-r' '-t'\n        do\n                test_expect_success \"'ls-tree $opts${opt:+ $opt}' output\" '\n-                       git ls-tree ${opt:+$opt }$opts $opt HEAD >actual &&\n+                       git ls-tree ${opt:+$opt }$opts HEAD >actual &&\n                        test_cmp expect${opt:+.$opt} actual\n                '\n        done\n--\n\nBut I'm not sure it's intentional or inadvertently design or maybe I misunderstood\nit here.\n\nThanks.\n"},{"id":"453285","messageId":"xmqqee28spni.fsf@gitster.g","threadId":"57383","inReplyTo":"220407.86y20hcpy5.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v2] ls-tree: fix --long implying -r regression in 9c4d58ff2c3","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-07T18:40:33Z","receivedAt":"2022-04-07T18:40:43Z","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>>>> Let's not go too cute like this.  This forces the caller to remember\n>>>> which among 6, 7, and 8 corresponds to which option.  It is too ugly\n>>>> to live.\n>>>\n>>> I think it's rather elegant actually, but to be fair it would, per:\n>>>\n>>> \tgit grep '<&[1-9]| [1-9]<<-'\n>>>\n>>> Be the user with the most FD's using this sort of pattern.\n>>\n>> Please give a clear explanation why \"-d\" has to be 6, \"-r\" 7 and\n>> \"-t\" 8, that can be used by developers as a memory aid to help them\n>> write new tests using the helper.\n>\n> It's documented when test-lib.sh does the redirection, since Fabian\n> Stelzer's a6714088e0c (test-lib: make BAIL_OUT() work in tests and\n> prereq, 2021-12-01).\n\nSorry, but that is not what I asked.  I know what we use lower file\ndescriptors for, and I didn't ask why we start from 6 (as opposed to\n3).\n\nThe updated helper forces our developers to know that the expected\nresult for \"-d\" has to go to #6 (not #7 or #8), and \"-r\" to #7 (not\n#6 or #8), etc., in order to write new tests using it, and in order\nto spot a mistake while reviewing such new tests.\n\nIt is an invitation to unmaintainable mess.  Don't encourage it.\n"},{"id":"456404","messageId":"patch-1.1-0fdfec624eb-20220531T171908Z-avarab@gmail.com","threadId":"57383","inReplyTo":"xmqqee28spni.fsf@gitster.g","subject":"[PATCH] ls-tree: test for the regression in 9c4d58ff2c3","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-05-31T17:21:54Z","receivedAt":"2022-05-31T17:22:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for the regression introduced in my 9c4d58ff2c3 (ls-tree:\nsplit up \"fast path\" callbacks, 2022-03-23) and fixed in\n350296cc789 (ls-tree: `-l` should not imply recursive listing,\n2022-04-04), and test for the test of ls-tree option/mode combinations\nto make sure we don't have other blind spots.\n\nThe setup for these tests can be shared with those added in the\n1041d58b4d9 (Merge branch 'tl/ls-tree-oid-only', 2022-04-04) topic, so\nlet's create a new t/lib-t3100.sh to help them share data.\n\nThe existing tests in \"t3104-ls-tree-format.sh\" didn't deal with a\nsubmodule, which they'll now encounter with as the\nsetup_basic_ls_tree_data() sets one up.\n\nThis extensive testing should give us confidence that there were no\nfurther regressions in this area. The lack of testing was noted back\nin [1], but unfortunately we didn't cover that blind-spot before\n9c4d58ff2c3.\n\n1. https://lore.kernel.org/git/211115.86o86lqe3c.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n\nOn Thu, Apr 07 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>>>>> Let's not go too cute like this.  This forces the caller to remember\n>>>>> which among 6, 7, and 8 corresponds to which option.  It is too ugly\n>>>>> to live.\n>>>>\n>>>> I think it's rather elegant actually, but to be fair it would, per:\n>>>>\n>>>>    git grep '<&[1-9]| [1-9]<<-'\n>>>>\n>>>> Be the user with the most FD's using this sort of pattern.\n>>>\n>>> Please give a clear explanation why \"-d\" has to be 6, \"-r\" 7 and\n>>> \"-t\" 8, that can be used by developers as a memory aid to help them\n>>> write new tests using the helper.\n>>\n>> It's documented when test-lib.sh does the redirection, since Fabian\n>> Stelzer's a6714088e0c (test-lib: make BAIL_OUT() work in tests and\n>> prereq, 2021-12-01).\n>\n> Sorry, but that is not what I asked.  I know what we use lower file\n> descriptors for, and I didn't ask why we start from 6 (as opposed to\n> 3).\n>\n> The updated helper forces our developers to know that the expected\n> result for \"-d\" has to go to #6 (not #7 or #8), and \"-r\" to #7 (not\n> #6 or #8), etc., in order to write new tests using it, and in order\n> to spot a mistake while reviewing such new tests.\n>\n> It is an invitation to unmaintainable mess.  Don't encourage it.\n\nI came up with this at the time, but didn't submit it since we were in\nthe rc period.\n\nBut since we're past that I think it makes sense to have a test for\nthe regression we fixed, the below is a lot less magical, but larger\nby line count. I think this alternate test implementation should\naddress the concerns you had.\n\nRange-diff:\n1:  ed83b3b74ab ! 1:  0fdfec624eb ls-tree: fix --long implying -r regression in 9c4d58ff2c3\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    ls-tree: fix --long implying -r regression in 9c4d58ff2c3\n    +    ls-tree: test for the regression in 9c4d58ff2c3\n     \n    -    Fix a regression introduced in 9c4d58ff2c3 (ls-tree: split up \"fast\n    -    path\" callbacks, 2022-03-23), and improve the tests added in the\n    -    1041d58b4d9 (Merge branch 'tl/ls-tree-oid-only', 2022-04-04) topic it\n    -    was merged as part of to test the full expected output of various\n    -    \"ls-tree\" options with and without -r.\n    +    Add a test for the regression introduced in my 9c4d58ff2c3 (ls-tree:\n    +    split up \"fast path\" callbacks, 2022-03-23) and fixed in\n    +    350296cc789 (ls-tree: `-l` should not imply recursive listing,\n    +    2022-04-04), and test for the test of ls-tree option/mode combinations\n    +    to make sure we don't have other blind spots.\n     \n    -    Let's fix it, and also add tests not only for that blindspot, but also\n    -    any other potential blindspots. To that end test the \"modes\" of -d, -r\n    -    and -t (as well as \"no mode\") against all of the format options.\n    +    The setup for these tests can be shared with those added in the\n    +    1041d58b4d9 (Merge branch 'tl/ls-tree-oid-only', 2022-04-04) topic, so\n    +    let's create a new t/lib-t3100.sh to help them share data.\n     \n    -    These tests all pass with that topic reverted (except those that would\n    -    fail because they're testing the new --object-only feature introduced\n    -    in that topic), which should give us confidence that there were no\n    -    further regressions in this area.\n    +    The existing tests in \"t3104-ls-tree-format.sh\" didn't deal with a\n    +    submodule, which they'll now encounter with as the\n    +    setup_basic_ls_tree_data() sets one up.\n    +\n    +    This extensive testing should give us confidence that there were no\n    +    further regressions in this area. The lack of testing was noted back\n    +    in [1], but unfortunately we didn't cover that blind-spot before\n    +    9c4d58ff2c3.\n    +\n    +    1. https://lore.kernel.org/git/211115.86o86lqe3c.gmgdl@evledraar.gmail.com/\n     \n    -    Reported-By: Josh Steadmon <steadmon@google.com>\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    - ## builtin/ls-tree.c ##\n    -@@ builtin/ls-tree.c: static int show_tree_long(const struct object_id *oid, struct strbuf *base,\n    - \tprintf(\"%06o %s %s %7s\\t\", data.mode, type_name(data.type),\n    - \t       find_unique_abbrev(data.oid, abbrev), size_text);\n    - \tshow_tree_common_default_long(base, pathname, data.base->len);\n    --\treturn 1;\n    -+\treturn recurse;\n    - }\n    - \n    - static int show_tree_name_only(const struct object_id *oid, struct strbuf *base,\n    + ## t/lib-t3100.sh (new) ##\n    +@@\n    ++#!/bin/sh\n    ++\n    ++setup_basic_ls_tree_data () {\n    ++\tmkdir dir &&\n    ++\ttest_commit dir/sub-file &&\n    ++\ttest_commit top-file &&\n    ++\tgit clone . submodule &&\n    ++\tgit submodule add ./submodule &&\n    ++\tgit commit -m\"add submodule\"\n    ++}\n     \n      ## t/t3104-ls-tree-format.sh ##\n    -@@ t/t3104-ls-tree-format.sh: test_ls_tree_format () {\n    - \tfmtopts=$3 &&\n    - \tshift 2 &&\n    +@@ t/t3104-ls-tree-format.sh: test_description='ls-tree --format'\n    + \n    + TEST_PASSES_SANITIZE_LEAK=true\n    + . ./test-lib.sh\n    ++. \"$TEST_DIRECTORY\"/lib-t3100.sh\n    + \n    + test_expect_success 'ls-tree --format usage' '\n    + \ttest_expect_code 129 git ls-tree --format=fmt -l HEAD &&\n    +@@ t/t3104-ls-tree-format.sh: test_expect_success 'ls-tree --format usage' '\n    + '\n    + \n    + test_expect_success 'setup' '\n    +-\tmkdir dir &&\n    +-\ttest_commit dir/sub-file &&\n    +-\ttest_commit top-file\n    ++\tsetup_basic_ls_tree_data\n    + '\n      \n    + test_ls_tree_format () {\n    +\n    + ## t/t3105-ls-tree-output.sh (new) ##\n    +@@\n    ++#!/bin/sh\n    ++\n    ++test_description='ls-tree output'\n    ++\n    ++TEST_PASSES_SANITIZE_LEAK=true\n    ++. ./test-lib.sh\n    ++. \"$TEST_DIRECTORY\"/lib-t3100.sh\n    ++\n    ++test_expect_success 'ls-tree --format usage' '\n    ++\ttest_expect_code 129 git ls-tree --format=fmt -l HEAD &&\n    ++\ttest_expect_code 129 git ls-tree --format=fmt --name-only HEAD &&\n    ++\ttest_expect_code 129 git ls-tree --format=fmt --name-status HEAD\n    ++'\n    ++\n    ++test_expect_success 'setup' '\n    ++\tsetup_basic_ls_tree_data\n    ++'\n    ++\n    ++test_ls_tree_format_mode_output () {\n    ++\tlocal opts=$1 &&\n    ++\tshift &&\n     +\tcat >expect &&\n    -+\tcat <&6 >expect.-d &&\n    -+\tcat <&7 >expect.-r &&\n    -+\tcat <&8 >expect.-t &&\n     +\n    -+\tfor opt in '' '-d' '-r' '-t'\n    ++\twhile test $# -gt 0\n     +\tdo\n    -+\t\ttest_expect_success \"'ls-tree $opts${opt:+ $opt}' output\" '\n    -+\t\t\tgit ls-tree ${opt:+$opt }$opts $opt HEAD >actual &&\n    -+\t\t\ttest_cmp expect${opt:+.$opt} actual\n    ++\t\tlocal mode=\"$1\" &&\n    ++\t\tshift &&\n    ++\n    ++\t\ttest_expect_success \"'ls-tree $opts${mode:+ $mode}' output\" '\n    ++\t\t\tgit ls-tree ${mode:+$mode }$opts HEAD >actual &&\n    ++\t\t\ttest_cmp expect actual\n     +\t\t'\n    ++\n    ++\t\tcase \"$opts\" in\n    ++\t\t--full-tree)\n    ++\t\t\ttest_expect_success \"'ls-tree $opts${mode:+ $mode}' output (via subdir, fails)\" '\n    ++\t\t\t\ttest_must_fail git -C dir ls-tree --full-name ${mode:+$mode }$opts HEAD -- ../\n    ++\t\t\t'\n    ++\t\t\t;;\n    ++\t\t*)\n    ++\t\t\ttest_expect_success \"'ls-tree $opts${mode:+ $mode}' output (via subdir)\" '\n    ++\t\t\t\tgit -C dir ls-tree --full-name ${mode:+$mode }$opts HEAD -- ../ >actual &&\n    ++\t\t\t\ttest_cmp expect actual\n    ++\t\t\t'\n    ++\t\t\t;;\n    ++\t\tesac\n     +\tdone\n    ++}\n     +\n    - \ttest_expect_success \"ls-tree '--format=<$format>' is like options '$opts $fmtopts'\" '\n    - \t\tgit ls-tree $opts -r HEAD >expect &&\n    - \t\tgit ls-tree --format=\"$format\" -r $fmtopts HEAD >actual &&\n    -@@ t/t3104-ls-tree-format.sh: test_ls_tree_format () {\n    - \n    - test_ls_tree_format \\\n    - \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n    --\t\"\"\n    -+\t\"\" \\\n    -+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n    -+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n    -+\tOUT\n    -+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n    -+\tOUT_D\n    -+\t100644 blob $(git rev-parse HEAD:dir/sub-file.t)\tdir/sub-file.t\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n    -+\tOUT_R\n    -+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n    -+\tOUT_T\n    - \n    - test_ls_tree_format \\\n    - \t\"%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)\" \\\n    --\t\"--long\"\n    -+\t\"--long\" \\\n    -+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n    -+\t040000 tree $(git rev-parse HEAD:dir)       -\tdir\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)       9\ttop-file.t\n    -+\tOUT\n    -+\t040000 tree $(git rev-parse HEAD:dir)       -\tdir\n    -+\tOUT_D\n    -+\t100644 blob $(git rev-parse HEAD:dir/sub-file.t)      13\tdir/sub-file.t\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)       9\ttop-file.t\n    -+\tOUT_R\n    -+\t040000 tree $(git rev-parse HEAD:dir)       -\tdir\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)       9\ttop-file.t\n    -+\tOUT_T\n    - \n    - test_ls_tree_format \\\n    - \t\"%(path)\" \\\n    --\t\"--name-only\"\n    -+\t\"--name-only\" \\\n    -+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n    ++# test exact output of option (none, --long, ...) and mode (none and\n    ++# -d, -r -t) and combinations\n    ++test_expect_success 'setup: HEAD_* variables' '\n    ++\tHEAD_gitmodules=$(git rev-parse HEAD:.gitmodules) &&\n    ++\tHEAD_dir=$(git rev-parse HEAD:dir) &&\n    ++\tHEAD_top_file=$(git rev-parse HEAD:top-file.t) &&\n    ++\tHEAD_submodule=$(git rev-parse HEAD:submodule) &&\n    ++\tHEAD_dir_sub_file=$(git rev-parse HEAD:dir/sub-file.t)\n    ++'\n    ++## opt =\n    ++test_ls_tree_format_mode_output \"\" \"\" \"-t\" <<-EOF\n    ++\t100644 blob $HEAD_gitmodules\t.gitmodules\n    ++\t040000 tree $HEAD_dir\tdir\n    ++\t160000 commit $HEAD_submodule\tsubmodule\n    ++\t100644 blob $HEAD_top_file\ttop-file.t\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"\" \"-d\" <<-EOF\n    ++\t040000 tree $HEAD_dir\tdir\n    ++\t160000 commit $HEAD_submodule\tsubmodule\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"\" \"-r\" <<-EOF\n    ++\t100644 blob $HEAD_gitmodules\t.gitmodules\n    ++\t100644 blob $HEAD_dir_sub_file\tdir/sub-file.t\n    ++\t160000 commit $HEAD_submodule\tsubmodule\n    ++\t100644 blob $HEAD_top_file\ttop-file.t\n    ++\tEOF\n    ++## opt = --long\n    ++test_ls_tree_format_mode_output \"--long\" \"\" \"-t\" <<-EOF\n    ++\t100644 blob $HEAD_gitmodules      61\t.gitmodules\n    ++\t040000 tree $HEAD_dir       -\tdir\n    ++\t160000 commit $HEAD_submodule       -\tsubmodule\n    ++\t100644 blob $HEAD_top_file       9\ttop-file.t\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"--long\" \"-d\" <<-EOF\n    ++\t040000 tree $HEAD_dir       -\tdir\n    ++\t160000 commit $HEAD_submodule       -\tsubmodule\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"--long\" \"-r\" <<-EOF\n    ++\t100644 blob $HEAD_gitmodules      61\t.gitmodules\n    ++\t100644 blob $HEAD_dir_sub_file      13\tdir/sub-file.t\n    ++\t160000 commit $HEAD_submodule       -\tsubmodule\n    ++\t100644 blob $HEAD_top_file       9\ttop-file.t\n    ++\tEOF\n    ++## opt = --name-only\n    ++test_ls_tree_format_mode_output \"--name-only\" \"\" \"-t\" <<-EOF\n    ++\t.gitmodules\n     +\tdir\n    ++\tsubmodule\n     +\ttop-file.t\n    -+\tOUT\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"--name-only\" \"-d\" <<-EOF\n     +\tdir\n    -+\tOUT_D\n    ++\tsubmodule\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"--name-only\" \"-r\" <<-EOF\n    ++\t.gitmodules\n     +\tdir/sub-file.t\n    ++\tsubmodule\n     +\ttop-file.t\n    -+\tOUT_R\n    -+\tdir\n    -+\ttop-file.t\n    -+\tOUT_T\n    - \n    - test_ls_tree_format \\\n    - \t\"%(objectname)\" \\\n    --\t\"--object-only\"\n    -+\t\"--object-only\" \\\n    -+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n    -+\t$(git rev-parse HEAD:dir)\n    -+\t$(git rev-parse HEAD:top-file.t)\n    -+\tOUT\n    -+\t$(git rev-parse HEAD:dir)\n    -+\tOUT_D\n    -+\t$(git rev-parse HEAD:dir/sub-file.t)\n    -+\t$(git rev-parse HEAD:top-file.t)\n    -+\tOUT_R\n    -+\t$(git rev-parse HEAD:dir)\n    -+\t$(git rev-parse HEAD:top-file.t)\n    -+\tOUT_T\n    - \n    - test_ls_tree_format \\\n    - \t\"%(objectname)\" \\\n    - \t\"--object-only --abbrev\" \\\n    --\t\"--abbrev\"\n    -+\t\"--abbrev\" \\\n    -+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n    -+\t$(git rev-parse HEAD:dir | test_copy_bytes 7)\n    -+\t$(git rev-parse HEAD:top-file.t| test_copy_bytes 7)\n    -+\tOUT\n    -+\t$(git rev-parse HEAD:dir | test_copy_bytes 7)\n    -+\tOUT_D\n    -+\t$(git rev-parse HEAD:dir/sub-file.t | test_copy_bytes 7)\n    -+\t$(git rev-parse HEAD:top-file.t | test_copy_bytes 7)\n    -+\tOUT_R\n    -+\t$(git rev-parse HEAD:dir | test_copy_bytes 7)\n    -+\t$(git rev-parse HEAD:top-file.t | test_copy_bytes 7)\n    -+\tOUT_T\n    - \n    - test_ls_tree_format \\\n    - \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n    - \t\"-t\" \\\n    --\t\"-t\"\n    -+\t\"-t\" \\\n    -+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n    -+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n    -+\tOUT\n    -+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n    -+\tOUT_D\n    -+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n    -+\t100644 blob $(git rev-parse HEAD:dir/sub-file.t)\tdir/sub-file.t\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n    -+\tOUT_R\n    -+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n    -+\tOUT_T\n    - \n    - test_ls_tree_format \\\n    - \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n    - \t\"--full-name\" \\\n    --\t\"--full-name\"\n    -+\t\"--full-name\" \\\n    -+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n    -+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n    -+\tOUT\n    -+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n    -+\tOUT_D\n    -+\t100644 blob $(git rev-parse HEAD:dir/sub-file.t)\tdir/sub-file.t\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n    -+\tOUT_R\n    -+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n    -+\tOUT_T\n    - \n    - test_ls_tree_format \\\n    - \t\"%(objectmode) %(objecttype) %(objectname)%x09%(path)\" \\\n    - \t\"--full-tree\" \\\n    --\t\"--full-tree\"\n    -+\t\"--full-tree\" \\\n    -+\t<<-OUT 6<<-OUT_D 7<<-OUT_R 8<<-OUT_T\n    -+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n    -+\tOUT\n    -+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n    -+\tOUT_D\n    -+\t100644 blob $(git rev-parse HEAD:dir/sub-file.t)\tdir/sub-file.t\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n    -+\tOUT_R\n    -+\t040000 tree $(git rev-parse HEAD:dir)\tdir\n    -+\t100644 blob $(git rev-parse HEAD:top-file.t)\ttop-file.t\n    -+\tOUT_T\n    - \n    - test_done\n    ++\tEOF\n    ++## opt = --object-only\n    ++test_ls_tree_format_mode_output \"--object-only\" \"\" \"-t\" <<-EOF\n    ++\t$HEAD_gitmodules\n    ++\t$HEAD_dir\n    ++\t$HEAD_submodule\n    ++\t$HEAD_top_file\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"--object-only\" \"-d\" <<-EOF\n    ++\t$HEAD_dir\n    ++\t$HEAD_submodule\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"--object-only\" \"-r\" <<-EOF\n    ++\t$HEAD_gitmodules\n    ++\t$HEAD_dir_sub_file\n    ++\t$HEAD_submodule\n    ++\t$HEAD_top_file\n    ++\tEOF\n    ++## opt = --object-only --abbrev\n    ++test_expect_success 'setup: HEAD_short_* variables' '\n    ++\tHEAD_short_gitmodules=$(git rev-parse --short HEAD:.gitmodules) &&\n    ++\tHEAD_short_dir=$(git rev-parse --short HEAD:dir) &&\n    ++\tHEAD_short_top_file=$(git rev-parse --short HEAD:top-file.t) &&\n    ++\tHEAD_short_submodule=$(git rev-parse --short HEAD:submodule) &&\n    ++\tHEAD_short_dir_sub_file=$(git rev-parse --short HEAD:dir/sub-file.t)\n    ++'\n    ++test_ls_tree_format_mode_output \"--object-only --abbrev\" \"\" \"-t\" <<-EOF\n    ++\t$HEAD_short_gitmodules\n    ++\t$HEAD_short_dir\n    ++\t$HEAD_short_submodule\n    ++\t$HEAD_short_top_file\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"--object-only --abbrev\" \"-d\" <<-EOF\n    ++\t$HEAD_short_dir\n    ++\t$HEAD_short_submodule\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"--object-only --abbrev\" \"-r\" <<-EOF\n    ++\t$HEAD_short_gitmodules\n    ++\t$HEAD_short_dir_sub_file\n    ++\t$HEAD_short_submodule\n    ++\t$HEAD_short_top_file\n    ++\tEOF\n    ++## opt = --full-name\n    ++test_ls_tree_format_mode_output \"--full-name\" \"\" <<-EOF\n    ++\t100644 blob $HEAD_gitmodules\t.gitmodules\n    ++\t040000 tree $HEAD_dir\tdir\n    ++\t160000 commit $HEAD_submodule\tsubmodule\n    ++\t100644 blob $HEAD_top_file\ttop-file.t\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"--full-name\" \"-d\" <<-EOF\n    ++\t040000 tree $HEAD_dir\tdir\n    ++\t160000 commit $HEAD_submodule\tsubmodule\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"--full-name\" \"-r\" <<-EOF\n    ++\t100644 blob $HEAD_gitmodules\t.gitmodules\n    ++\t100644 blob $HEAD_dir_sub_file\tdir/sub-file.t\n    ++\t160000 commit $HEAD_submodule\tsubmodule\n    ++\t100644 blob $HEAD_top_file\ttop-file.t\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"--full-name\" \"-t\" <<-EOF\n    ++\t100644 blob $HEAD_gitmodules\t.gitmodules\n    ++\t040000 tree $HEAD_dir\tdir\n    ++\t160000 commit $HEAD_submodule\tsubmodule\n    ++\t100644 blob $HEAD_top_file\ttop-file.t\n    ++\tEOF\n    ++## opt = --full-tree\n    ++test_ls_tree_format_mode_output \"--full-tree\" \"\" \"-t\" <<-EOF\n    ++\t100644 blob $HEAD_gitmodules\t.gitmodules\n    ++\t040000 tree $HEAD_dir\tdir\n    ++\t160000 commit $HEAD_submodule\tsubmodule\n    ++\t100644 blob $HEAD_top_file\ttop-file.t\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"--full-tree\" \"-d\" <<-EOF\n    ++\t040000 tree $HEAD_dir\tdir\n    ++\t160000 commit $HEAD_submodule\tsubmodule\n    ++\tEOF\n    ++test_ls_tree_format_mode_output \"--full-tree\" \"-r\" <<-EOF\n    ++\t100644 blob $HEAD_gitmodules\t.gitmodules\n    ++\t100644 blob $HEAD_dir_sub_file\tdir/sub-file.t\n    ++\t160000 commit $HEAD_submodule\tsubmodule\n    ++\t100644 blob $HEAD_top_file\ttop-file.t\n    ++\tEOF\n    ++\n    ++test_done\n\n t/lib-t3100.sh            |  10 ++\n t/t3104-ls-tree-format.sh |   5 +-\n t/t3105-ls-tree-output.sh | 192 ++++++++++++++++++++++++++++++++++++++\n 3 files changed, 204 insertions(+), 3 deletions(-)\n create mode 100644 t/lib-t3100.sh\n create mode 100755 t/t3105-ls-tree-output.sh\n\ndiff --git a/t/lib-t3100.sh b/t/lib-t3100.sh\nnew file mode 100644\nindex 00000000000..eabb5fd8034\n--- /dev/null\n+++ b/t/lib-t3100.sh\n@@ -0,0 +1,10 @@\n+#!/bin/sh\n+\n+setup_basic_ls_tree_data () {\n+\tmkdir dir &&\n+\ttest_commit dir/sub-file &&\n+\ttest_commit top-file &&\n+\tgit clone . submodule &&\n+\tgit submodule add ./submodule &&\n+\tgit commit -m\"add submodule\"\n+}\ndiff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh\nindex 0769a933d69..383896667b6 100755\n--- a/t/t3104-ls-tree-format.sh\n+++ b/t/t3104-ls-tree-format.sh\n@@ -4,6 +4,7 @@ test_description='ls-tree --format'\n \n TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-t3100.sh\n \n test_expect_success 'ls-tree --format usage' '\n \ttest_expect_code 129 git ls-tree --format=fmt -l HEAD &&\n@@ -12,9 +13,7 @@ test_expect_success 'ls-tree --format usage' '\n '\n \n test_expect_success 'setup' '\n-\tmkdir dir &&\n-\ttest_commit dir/sub-file &&\n-\ttest_commit top-file\n+\tsetup_basic_ls_tree_data\n '\n \n test_ls_tree_format () {\ndiff --git a/t/t3105-ls-tree-output.sh b/t/t3105-ls-tree-output.sh\nnew file mode 100755\nindex 00000000000..29511d9331b\n--- /dev/null\n+++ b/t/t3105-ls-tree-output.sh\n@@ -0,0 +1,192 @@\n+#!/bin/sh\n+\n+test_description='ls-tree output'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n+. ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-t3100.sh\n+\n+test_expect_success 'ls-tree --format usage' '\n+\ttest_expect_code 129 git ls-tree --format=fmt -l HEAD &&\n+\ttest_expect_code 129 git ls-tree --format=fmt --name-only HEAD &&\n+\ttest_expect_code 129 git ls-tree --format=fmt --name-status HEAD\n+'\n+\n+test_expect_success 'setup' '\n+\tsetup_basic_ls_tree_data\n+'\n+\n+test_ls_tree_format_mode_output () {\n+\tlocal opts=$1 &&\n+\tshift &&\n+\tcat >expect &&\n+\n+\twhile test $# -gt 0\n+\tdo\n+\t\tlocal mode=\"$1\" &&\n+\t\tshift &&\n+\n+\t\ttest_expect_success \"'ls-tree $opts${mode:+ $mode}' output\" '\n+\t\t\tgit ls-tree ${mode:+$mode }$opts HEAD >actual &&\n+\t\t\ttest_cmp expect actual\n+\t\t'\n+\n+\t\tcase \"$opts\" in\n+\t\t--full-tree)\n+\t\t\ttest_expect_success \"'ls-tree $opts${mode:+ $mode}' output (via subdir, fails)\" '\n+\t\t\t\ttest_must_fail git -C dir ls-tree --full-name ${mode:+$mode }$opts HEAD -- ../\n+\t\t\t'\n+\t\t\t;;\n+\t\t*)\n+\t\t\ttest_expect_success \"'ls-tree $opts${mode:+ $mode}' output (via subdir)\" '\n+\t\t\t\tgit -C dir ls-tree --full-name ${mode:+$mode }$opts HEAD -- ../ >actual &&\n+\t\t\t\ttest_cmp expect actual\n+\t\t\t'\n+\t\t\t;;\n+\t\tesac\n+\tdone\n+}\n+\n+# test exact output of option (none, --long, ...) and mode (none and\n+# -d, -r -t) and combinations\n+test_expect_success 'setup: HEAD_* variables' '\n+\tHEAD_gitmodules=$(git rev-parse HEAD:.gitmodules) &&\n+\tHEAD_dir=$(git rev-parse HEAD:dir) &&\n+\tHEAD_top_file=$(git rev-parse HEAD:top-file.t) &&\n+\tHEAD_submodule=$(git rev-parse HEAD:submodule) &&\n+\tHEAD_dir_sub_file=$(git rev-parse HEAD:dir/sub-file.t)\n+'\n+## opt =\n+test_ls_tree_format_mode_output \"\" \"\" \"-t\" <<-EOF\n+\t100644 blob $HEAD_gitmodules\t.gitmodules\n+\t040000 tree $HEAD_dir\tdir\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\t100644 blob $HEAD_top_file\ttop-file.t\n+\tEOF\n+test_ls_tree_format_mode_output \"\" \"-d\" <<-EOF\n+\t040000 tree $HEAD_dir\tdir\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\tEOF\n+test_ls_tree_format_mode_output \"\" \"-r\" <<-EOF\n+\t100644 blob $HEAD_gitmodules\t.gitmodules\n+\t100644 blob $HEAD_dir_sub_file\tdir/sub-file.t\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\t100644 blob $HEAD_top_file\ttop-file.t\n+\tEOF\n+## opt = --long\n+test_ls_tree_format_mode_output \"--long\" \"\" \"-t\" <<-EOF\n+\t100644 blob $HEAD_gitmodules      61\t.gitmodules\n+\t040000 tree $HEAD_dir       -\tdir\n+\t160000 commit $HEAD_submodule       -\tsubmodule\n+\t100644 blob $HEAD_top_file       9\ttop-file.t\n+\tEOF\n+test_ls_tree_format_mode_output \"--long\" \"-d\" <<-EOF\n+\t040000 tree $HEAD_dir       -\tdir\n+\t160000 commit $HEAD_submodule       -\tsubmodule\n+\tEOF\n+test_ls_tree_format_mode_output \"--long\" \"-r\" <<-EOF\n+\t100644 blob $HEAD_gitmodules      61\t.gitmodules\n+\t100644 blob $HEAD_dir_sub_file      13\tdir/sub-file.t\n+\t160000 commit $HEAD_submodule       -\tsubmodule\n+\t100644 blob $HEAD_top_file       9\ttop-file.t\n+\tEOF\n+## opt = --name-only\n+test_ls_tree_format_mode_output \"--name-only\" \"\" \"-t\" <<-EOF\n+\t.gitmodules\n+\tdir\n+\tsubmodule\n+\ttop-file.t\n+\tEOF\n+test_ls_tree_format_mode_output \"--name-only\" \"-d\" <<-EOF\n+\tdir\n+\tsubmodule\n+\tEOF\n+test_ls_tree_format_mode_output \"--name-only\" \"-r\" <<-EOF\n+\t.gitmodules\n+\tdir/sub-file.t\n+\tsubmodule\n+\ttop-file.t\n+\tEOF\n+## opt = --object-only\n+test_ls_tree_format_mode_output \"--object-only\" \"\" \"-t\" <<-EOF\n+\t$HEAD_gitmodules\n+\t$HEAD_dir\n+\t$HEAD_submodule\n+\t$HEAD_top_file\n+\tEOF\n+test_ls_tree_format_mode_output \"--object-only\" \"-d\" <<-EOF\n+\t$HEAD_dir\n+\t$HEAD_submodule\n+\tEOF\n+test_ls_tree_format_mode_output \"--object-only\" \"-r\" <<-EOF\n+\t$HEAD_gitmodules\n+\t$HEAD_dir_sub_file\n+\t$HEAD_submodule\n+\t$HEAD_top_file\n+\tEOF\n+## opt = --object-only --abbrev\n+test_expect_success 'setup: HEAD_short_* variables' '\n+\tHEAD_short_gitmodules=$(git rev-parse --short HEAD:.gitmodules) &&\n+\tHEAD_short_dir=$(git rev-parse --short HEAD:dir) &&\n+\tHEAD_short_top_file=$(git rev-parse --short HEAD:top-file.t) &&\n+\tHEAD_short_submodule=$(git rev-parse --short HEAD:submodule) &&\n+\tHEAD_short_dir_sub_file=$(git rev-parse --short HEAD:dir/sub-file.t)\n+'\n+test_ls_tree_format_mode_output \"--object-only --abbrev\" \"\" \"-t\" <<-EOF\n+\t$HEAD_short_gitmodules\n+\t$HEAD_short_dir\n+\t$HEAD_short_submodule\n+\t$HEAD_short_top_file\n+\tEOF\n+test_ls_tree_format_mode_output \"--object-only --abbrev\" \"-d\" <<-EOF\n+\t$HEAD_short_dir\n+\t$HEAD_short_submodule\n+\tEOF\n+test_ls_tree_format_mode_output \"--object-only --abbrev\" \"-r\" <<-EOF\n+\t$HEAD_short_gitmodules\n+\t$HEAD_short_dir_sub_file\n+\t$HEAD_short_submodule\n+\t$HEAD_short_top_file\n+\tEOF\n+## opt = --full-name\n+test_ls_tree_format_mode_output \"--full-name\" \"\" <<-EOF\n+\t100644 blob $HEAD_gitmodules\t.gitmodules\n+\t040000 tree $HEAD_dir\tdir\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\t100644 blob $HEAD_top_file\ttop-file.t\n+\tEOF\n+test_ls_tree_format_mode_output \"--full-name\" \"-d\" <<-EOF\n+\t040000 tree $HEAD_dir\tdir\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\tEOF\n+test_ls_tree_format_mode_output \"--full-name\" \"-r\" <<-EOF\n+\t100644 blob $HEAD_gitmodules\t.gitmodules\n+\t100644 blob $HEAD_dir_sub_file\tdir/sub-file.t\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\t100644 blob $HEAD_top_file\ttop-file.t\n+\tEOF\n+test_ls_tree_format_mode_output \"--full-name\" \"-t\" <<-EOF\n+\t100644 blob $HEAD_gitmodules\t.gitmodules\n+\t040000 tree $HEAD_dir\tdir\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\t100644 blob $HEAD_top_file\ttop-file.t\n+\tEOF\n+## opt = --full-tree\n+test_ls_tree_format_mode_output \"--full-tree\" \"\" \"-t\" <<-EOF\n+\t100644 blob $HEAD_gitmodules\t.gitmodules\n+\t040000 tree $HEAD_dir\tdir\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\t100644 blob $HEAD_top_file\ttop-file.t\n+\tEOF\n+test_ls_tree_format_mode_output \"--full-tree\" \"-d\" <<-EOF\n+\t040000 tree $HEAD_dir\tdir\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\tEOF\n+test_ls_tree_format_mode_output \"--full-tree\" \"-r\" <<-EOF\n+\t100644 blob $HEAD_gitmodules\t.gitmodules\n+\t100644 blob $HEAD_dir_sub_file\tdir/sub-file.t\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\t100644 blob $HEAD_top_file\ttop-file.t\n+\tEOF\n+\n+test_done\n-- \n2.36.1.1100.g16130010d07\n\n"},{"id":"456525","messageId":"nycvar.QRO.7.76.6.2206021703110.349@tvgsbejvaqbjf.bet","threadId":"57383","inReplyTo":"patch-1.1-0fdfec624eb-20220531T171908Z-avarab@gmail.com","subject":"Re: [PATCH] ls-tree: test for the regression in 9c4d58ff2c3","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2022-06-02T15:18:13Z","receivedAt":"2022-06-02T15:19:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Tue, 31 May 2022, Ævar Arnfjörð Bjarmason wrote:\n\n> Add a test for the regression introduced in my 9c4d58ff2c3 (ls-tree:\n> split up \"fast path\" callbacks, 2022-03-23) and fixed in\n> 350296cc789 (ls-tree: `-l` should not imply recursive listing,\n> 2022-04-04), and test for the test of ls-tree option/mode combinations\n> to make sure we don't have other blind spots.\n\nThat's adding a lot of new test cases, which is not free of cost.\n\n> Range-diff:\n> [... 433 lines, essentially a rewrite...]\n\nPasting 443 lines of range-diff also is not free of cost.\n\n> diff --git a/t/t3105-ls-tree-output.sh b/t/t3105-ls-tree-output.sh\n> new file mode 100755\n> index 00000000000..29511d9331b\n> --- /dev/null\n> +++ b/t/t3105-ls-tree-output.sh\n> @@ -0,0 +1,192 @@\n> +#!/bin/sh\n> +\n> +test_description='ls-tree output'\n> +\n> [...]\n> +'\n> +\n> +test_ls_tree_format_mode_output () {\n> +\tlocal opts=$1 &&\n\nThis line does not quote `$1`, but...\n\n> +\tshift &&\n> +\tcat >expect &&\n> +\n> +\twhile test $# -gt 0\n> +\tdo\n> +\t\tlocal mode=\"$1\" &&\n> +\t\tshift &&\n> +\n> +\t\ttest_expect_success \"'ls-tree $opts${mode:+ $mode}' output\" '\n> +\t\t\tgit ls-tree ${mode:+$mode }$opts HEAD >actual &&\n> +\t\t\ttest_cmp expect actual\n> +\t\t'\n> +\n> +\t\tcase \"$opts\" in\n> +\t\t--full-tree)\n> +\t\t\ttest_expect_success \"'ls-tree $opts${mode:+ $mode}' output (via subdir, fails)\" '\n> +\t\t\t\ttest_must_fail git -C dir ls-tree --full-name ${mode:+$mode }$opts HEAD -- ../\n> +\t\t\t'\n> +\t\t\t;;\n> +\t\t*)\n> +\t\t\ttest_expect_success \"'ls-tree $opts${mode:+ $mode}' output (via subdir)\" '\n> +\t\t\t\tgit -C dir ls-tree --full-name ${mode:+$mode }$opts HEAD -- ../ >actual &&\n> +\t\t\t\ttest_cmp expect actual\n> +\t\t\t'\n> +\t\t\t;;\n> +\t\tesac\n> +\tdone\n> +}\n> +\n> [...]\n> +## opt = --object-only --abbrev\n> +test_expect_success 'setup: HEAD_short_* variables' '\n> +\tHEAD_short_gitmodules=$(git rev-parse --short HEAD:.gitmodules) &&\n> +\tHEAD_short_dir=$(git rev-parse --short HEAD:dir) &&\n> +\tHEAD_short_top_file=$(git rev-parse --short HEAD:top-file.t) &&\n> +\tHEAD_short_submodule=$(git rev-parse --short HEAD:submodule) &&\n> +\tHEAD_short_dir_sub_file=$(git rev-parse --short HEAD:dir/sub-file.t)\n> +'\n> +test_ls_tree_format_mode_output \"--object-only --abbrev\" \"\" \"-t\" <<-EOF\n\n... this line passes a first argument that contains spaces. Hence the\ntests fail in CI:\nhttps://github.com/git/git/runs/6703333447?check_suite_focus=true\n\nFurther, since this failure is outside of any `test_expect_success` or\n`test_expect_failure`, the error message about this is not even included\nin the weblogs (but of course it is included in the full logs that are\nincluded in the build artifacts). For the record, here is the error\nmessage:\n\n\t[...]\n\n\tok 35 - 'ls-tree --object-only -r' output (via subdir)\n\t+ git rev-parse --short HEAD:.gitmodules\n\t+ HEAD_short_gitmodules=6da7993\n\t+ git rev-parse --short HEAD:dir\n\t+ HEAD_short_dir=aba57e0\n\t+ git rev-parse --short HEAD:top-file.t\n\t+ HEAD_short_top_file=02dad95\n\t+ git rev-parse --short HEAD:submodule\n\t+ HEAD_short_submodule=61a63ac\n\t+ git rev-parse --short HEAD:dir/sub-file.t\n\t+ HEAD_short_dir_sub_file=a150abd\n\n\tok 36 - setup: HEAD_short_* variables\n\tt3105-ls-tree-output.sh: 20: local: --abbrev: bad variable name\n\tFATAL: Unexpected exit with code 2\n\nNote: I am only pointing out why the CI/PR run fails, I have not formed\nany opinion on the actual code other than noticing a lot of what might be\nrepetitive lines and suspecting that adding this many new test cases\nincreases the runtime of the test suite and thereby adds to developer\nfriction and the benefit (namely, to catch future regressions) might not\njustify that. But again, I have not fully formed an opinion about this\npatch.\n\nCiao,\nJohannes\n"},{"id":"456535","messageId":"xmqqo7zbnehy.fsf@gitster.g","threadId":"57383","inReplyTo":"nycvar.QRO.7.76.6.2206021703110.349@tvgsbejvaqbjf.bet","subject":"Re: [PATCH] ls-tree: test for the regression in 9c4d58ff2c3","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-06-02T17:48:09Z","receivedAt":"2022-06-02T17:48:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> +test_ls_tree_format_mode_output () {\n>> +\tlocal opts=$1 &&\n>\n> This line does not quote `$1`, but...\n>\n> ... this line passes a first argument that contains spaces. Hence the\n> tests fail in CI:\n> https://github.com/git/git/runs/6703333447?check_suite_focus=true\n\nWe had a portability discussion not so in distant past on \"local\".\n\n  https://lore.kernel.org/git/xmqqsftc3nd6.fsf@gitster.g/\n\nThe conclusion from the thread IIRC is\n\n * Very safe\n\n\tlocal var; var=$1\n\n * Probably OK\n\n\tlocal var=\"$1\"\n\n * Not portable\n\n\tlocal var=$1\n\nThanks.\n"},{"id":"456557","messageId":"220603.86fskmxd43.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"nycvar.QRO.7.76.6.2206021703110.349@tvgsbejvaqbjf.bet","subject":"js/ci-github-workflow-markup output regression (was: [PATCH] ls-tree: test for the regression in 9c4d58ff2c3)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-06-03T09:54:31Z","receivedAt":"2022-06-03T10:20:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Jun 02 2022, Johannes Schindelin wrote:\n\n> On Tue, 31 May 2022, Ævar Arnfjörð Bjarmason wrote:\n> [...]\n>> +test_ls_tree_format_mode_output () {\n>> +\tlocal opts=$1 &&\n>\n> This line does not quote `$1`, but...\n\nWell spotted, thanks, I don't know how I missed that, will fix it &\nre-roll, but per changed $subject...\n\n> [...]\n> ... this line passes a first argument that contains spaces. Hence the\n> tests fail in CI:\n> https://github.com/git/git/runs/6703333447?check_suite_focus=true\n>\n> Further, since this failure is outside of any `test_expect_success` or\n> `test_expect_failure`, the error message about this is not even included\n> in the weblogs (but of course it is included in the full logs that are\n> included in the build artifacts). For the record, here is the error\n> message:\n\n...this part of it though seems like a pretty bad regression in your\nmerged-to-next js/ci-github-workflow-markup topic, which just happens to\nbe unearthed by this CI failure.\n\nOn top of master this patch will get this CI failure:\nhttps://github.com/avar/git/runs/6675920732?check_suite_focus=true#step:5:598;\nEnding in (\"[...]\" edit is mine):\n\t\n\t ok 35 - 'ls-tree --object-only -r' output (via subdir)\n\t+ git rev-parse --short HEAD:.gitmodules\n\t[...]\n\t+ HEAD_short_dir_sub_file=a150abd\n\t\n\tok 36 - setup: HEAD_short_* variables\n\tt3105-ls-tree-output.sh: 20: local: --abbrev: bad variable name\n\tFATAL: Unexpected exit with code 2\n\nSo here we see that we got to test 36, and then got this error. All of\nwhich is after the step we focused on to begin with would have shown:\n\t\n\t Test Summary Report\n\t-------------------\n\tt3105-ls-tree-output.sh                          (Wstat: 256 Tests: 36 Failed: 0)\n\t  Non-zero exit status: 1\n\t  Parse errors: No plan found in TAP output\n\nI.e. telling us we had a TAP parse error, so something like this was\ngoing on.\n\nBut now we'll instead get (\"=>\" edit is mine, it didn't copy/paste):\n\n\t=> Run tests\n\t=== Failed test: t3105-ls-tree-output ===\n\tThe full logs are in the artifacts attached to this run.\n\tError: Process completed with exit code 1.\n\nWhere expanding the \"=>\" in \"Run tests\" will get us the \"Test summary\nreport\" from before, but now we have no \"ci/print-test-failures.sh\" step\nto emit the output we got from the test, and we just get a cryptic error\nabout t3105 having failed *somewhere*.\n\nAs noted in the breadcrumb trail leading from \"[2]\" in [1] this is a\nfundamental limitation of the approach you picked for\n--github-workflow-markup. You're tasking the test-lib.sh to emit\nwell-formed output, but as shown here we can die prematurely on \"eval\"\nfailures.\n\nBut this does look easy to \"solve\" with a quicker fix, just bringing\nback the \"ci/print-test-failures.sh\" step so you can at least expand it,\nand not have to go to the \"summary\" and download the *.zip of the log\nitself. As that shows we still have the raw log there, it just didn't\nmake it to the new GitHub Markdown formatting mechanism.\n\n1. https://lore.kernel.org/git/220324.8635j7nyvw.gmgdl@evledraar.gmail.com/\n"},{"id":"456558","messageId":"patch-v2-1.1-f2beb02dd29-20220603T102148Z-avarab@gmail.com","threadId":"57383","inReplyTo":"patch-1.1-0fdfec624eb-20220531T171908Z-avarab@gmail.com","subject":"[PATCH v2] ls-tree: test for the regression in 9c4d58ff2c3","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-06-03T10:23:10Z","receivedAt":"2022-06-03T10:23:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for the regression introduced in my 9c4d58ff2c3 (ls-tree:\nsplit up \"fast path\" callbacks, 2022-03-23) and fixed in\n350296cc789 (ls-tree: `-l` should not imply recursive listing,\n2022-04-04), and test for the test of ls-tree option/mode combinations\nto make sure we don't have other blind spots.\n\nThe setup for these tests can be shared with those added in the\n1041d58b4d9 (Merge branch 'tl/ls-tree-oid-only', 2022-04-04) topic, so\nlet's create a new t/lib-t3100.sh to help them share data.\n\nThe existing tests in \"t3104-ls-tree-format.sh\" didn't deal with a\nsubmodule, which they'll now encounter with as the\nsetup_basic_ls_tree_data() sets one up.\n\nThis extensive testing should give us confidence that there were no\nfurther regressions in this area. The lack of testing was noted back\nin [1], but unfortunately we didn't cover that blind-spot before\n9c4d58ff2c3.\n\n1. https://lore.kernel.org/git/211115.86o86lqe3c.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n\nFix a quoting issue in v1 that made CI fail, now passes:\nhttps://github.com/avar/git/tree/avar/ls-tree-rewrite-regression-test-2\n\nI usually check the CI before submitting, but I don't know how that\nslipped through in this case, sorry, and thanks for the quick\nreview/diagnosis.\n\nRange-diff against v1:\n1:  0fdfec624eb ! 1:  f2beb02dd29 ls-tree: test for the regression in 9c4d58ff2c3\n    @@ t/t3105-ls-tree-output.sh (new)\n     +'\n     +\n     +test_ls_tree_format_mode_output () {\n    -+\tlocal opts=$1 &&\n    ++\tlocal opts=\"$1\" &&\n     +\tshift &&\n     +\tcat >expect &&\n     +\n\n t/lib-t3100.sh            |  10 ++\n t/t3104-ls-tree-format.sh |   5 +-\n t/t3105-ls-tree-output.sh | 192 ++++++++++++++++++++++++++++++++++++++\n 3 files changed, 204 insertions(+), 3 deletions(-)\n create mode 100644 t/lib-t3100.sh\n create mode 100755 t/t3105-ls-tree-output.sh\n\ndiff --git a/t/lib-t3100.sh b/t/lib-t3100.sh\nnew file mode 100644\nindex 00000000000..eabb5fd8034\n--- /dev/null\n+++ b/t/lib-t3100.sh\n@@ -0,0 +1,10 @@\n+#!/bin/sh\n+\n+setup_basic_ls_tree_data () {\n+\tmkdir dir &&\n+\ttest_commit dir/sub-file &&\n+\ttest_commit top-file &&\n+\tgit clone . submodule &&\n+\tgit submodule add ./submodule &&\n+\tgit commit -m\"add submodule\"\n+}\ndiff --git a/t/t3104-ls-tree-format.sh b/t/t3104-ls-tree-format.sh\nindex 0769a933d69..383896667b6 100755\n--- a/t/t3104-ls-tree-format.sh\n+++ b/t/t3104-ls-tree-format.sh\n@@ -4,6 +4,7 @@ test_description='ls-tree --format'\n \n TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-t3100.sh\n \n test_expect_success 'ls-tree --format usage' '\n \ttest_expect_code 129 git ls-tree --format=fmt -l HEAD &&\n@@ -12,9 +13,7 @@ test_expect_success 'ls-tree --format usage' '\n '\n \n test_expect_success 'setup' '\n-\tmkdir dir &&\n-\ttest_commit dir/sub-file &&\n-\ttest_commit top-file\n+\tsetup_basic_ls_tree_data\n '\n \n test_ls_tree_format () {\ndiff --git a/t/t3105-ls-tree-output.sh b/t/t3105-ls-tree-output.sh\nnew file mode 100755\nindex 00000000000..ce2391e28be\n--- /dev/null\n+++ b/t/t3105-ls-tree-output.sh\n@@ -0,0 +1,192 @@\n+#!/bin/sh\n+\n+test_description='ls-tree output'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n+. ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-t3100.sh\n+\n+test_expect_success 'ls-tree --format usage' '\n+\ttest_expect_code 129 git ls-tree --format=fmt -l HEAD &&\n+\ttest_expect_code 129 git ls-tree --format=fmt --name-only HEAD &&\n+\ttest_expect_code 129 git ls-tree --format=fmt --name-status HEAD\n+'\n+\n+test_expect_success 'setup' '\n+\tsetup_basic_ls_tree_data\n+'\n+\n+test_ls_tree_format_mode_output () {\n+\tlocal opts=\"$1\" &&\n+\tshift &&\n+\tcat >expect &&\n+\n+\twhile test $# -gt 0\n+\tdo\n+\t\tlocal mode=\"$1\" &&\n+\t\tshift &&\n+\n+\t\ttest_expect_success \"'ls-tree $opts${mode:+ $mode}' output\" '\n+\t\t\tgit ls-tree ${mode:+$mode }$opts HEAD >actual &&\n+\t\t\ttest_cmp expect actual\n+\t\t'\n+\n+\t\tcase \"$opts\" in\n+\t\t--full-tree)\n+\t\t\ttest_expect_success \"'ls-tree $opts${mode:+ $mode}' output (via subdir, fails)\" '\n+\t\t\t\ttest_must_fail git -C dir ls-tree --full-name ${mode:+$mode }$opts HEAD -- ../\n+\t\t\t'\n+\t\t\t;;\n+\t\t*)\n+\t\t\ttest_expect_success \"'ls-tree $opts${mode:+ $mode}' output (via subdir)\" '\n+\t\t\t\tgit -C dir ls-tree --full-name ${mode:+$mode }$opts HEAD -- ../ >actual &&\n+\t\t\t\ttest_cmp expect actual\n+\t\t\t'\n+\t\t\t;;\n+\t\tesac\n+\tdone\n+}\n+\n+# test exact output of option (none, --long, ...) and mode (none and\n+# -d, -r -t) and combinations\n+test_expect_success 'setup: HEAD_* variables' '\n+\tHEAD_gitmodules=$(git rev-parse HEAD:.gitmodules) &&\n+\tHEAD_dir=$(git rev-parse HEAD:dir) &&\n+\tHEAD_top_file=$(git rev-parse HEAD:top-file.t) &&\n+\tHEAD_submodule=$(git rev-parse HEAD:submodule) &&\n+\tHEAD_dir_sub_file=$(git rev-parse HEAD:dir/sub-file.t)\n+'\n+## opt =\n+test_ls_tree_format_mode_output \"\" \"\" \"-t\" <<-EOF\n+\t100644 blob $HEAD_gitmodules\t.gitmodules\n+\t040000 tree $HEAD_dir\tdir\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\t100644 blob $HEAD_top_file\ttop-file.t\n+\tEOF\n+test_ls_tree_format_mode_output \"\" \"-d\" <<-EOF\n+\t040000 tree $HEAD_dir\tdir\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\tEOF\n+test_ls_tree_format_mode_output \"\" \"-r\" <<-EOF\n+\t100644 blob $HEAD_gitmodules\t.gitmodules\n+\t100644 blob $HEAD_dir_sub_file\tdir/sub-file.t\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\t100644 blob $HEAD_top_file\ttop-file.t\n+\tEOF\n+## opt = --long\n+test_ls_tree_format_mode_output \"--long\" \"\" \"-t\" <<-EOF\n+\t100644 blob $HEAD_gitmodules      61\t.gitmodules\n+\t040000 tree $HEAD_dir       -\tdir\n+\t160000 commit $HEAD_submodule       -\tsubmodule\n+\t100644 blob $HEAD_top_file       9\ttop-file.t\n+\tEOF\n+test_ls_tree_format_mode_output \"--long\" \"-d\" <<-EOF\n+\t040000 tree $HEAD_dir       -\tdir\n+\t160000 commit $HEAD_submodule       -\tsubmodule\n+\tEOF\n+test_ls_tree_format_mode_output \"--long\" \"-r\" <<-EOF\n+\t100644 blob $HEAD_gitmodules      61\t.gitmodules\n+\t100644 blob $HEAD_dir_sub_file      13\tdir/sub-file.t\n+\t160000 commit $HEAD_submodule       -\tsubmodule\n+\t100644 blob $HEAD_top_file       9\ttop-file.t\n+\tEOF\n+## opt = --name-only\n+test_ls_tree_format_mode_output \"--name-only\" \"\" \"-t\" <<-EOF\n+\t.gitmodules\n+\tdir\n+\tsubmodule\n+\ttop-file.t\n+\tEOF\n+test_ls_tree_format_mode_output \"--name-only\" \"-d\" <<-EOF\n+\tdir\n+\tsubmodule\n+\tEOF\n+test_ls_tree_format_mode_output \"--name-only\" \"-r\" <<-EOF\n+\t.gitmodules\n+\tdir/sub-file.t\n+\tsubmodule\n+\ttop-file.t\n+\tEOF\n+## opt = --object-only\n+test_ls_tree_format_mode_output \"--object-only\" \"\" \"-t\" <<-EOF\n+\t$HEAD_gitmodules\n+\t$HEAD_dir\n+\t$HEAD_submodule\n+\t$HEAD_top_file\n+\tEOF\n+test_ls_tree_format_mode_output \"--object-only\" \"-d\" <<-EOF\n+\t$HEAD_dir\n+\t$HEAD_submodule\n+\tEOF\n+test_ls_tree_format_mode_output \"--object-only\" \"-r\" <<-EOF\n+\t$HEAD_gitmodules\n+\t$HEAD_dir_sub_file\n+\t$HEAD_submodule\n+\t$HEAD_top_file\n+\tEOF\n+## opt = --object-only --abbrev\n+test_expect_success 'setup: HEAD_short_* variables' '\n+\tHEAD_short_gitmodules=$(git rev-parse --short HEAD:.gitmodules) &&\n+\tHEAD_short_dir=$(git rev-parse --short HEAD:dir) &&\n+\tHEAD_short_top_file=$(git rev-parse --short HEAD:top-file.t) &&\n+\tHEAD_short_submodule=$(git rev-parse --short HEAD:submodule) &&\n+\tHEAD_short_dir_sub_file=$(git rev-parse --short HEAD:dir/sub-file.t)\n+'\n+test_ls_tree_format_mode_output \"--object-only --abbrev\" \"\" \"-t\" <<-EOF\n+\t$HEAD_short_gitmodules\n+\t$HEAD_short_dir\n+\t$HEAD_short_submodule\n+\t$HEAD_short_top_file\n+\tEOF\n+test_ls_tree_format_mode_output \"--object-only --abbrev\" \"-d\" <<-EOF\n+\t$HEAD_short_dir\n+\t$HEAD_short_submodule\n+\tEOF\n+test_ls_tree_format_mode_output \"--object-only --abbrev\" \"-r\" <<-EOF\n+\t$HEAD_short_gitmodules\n+\t$HEAD_short_dir_sub_file\n+\t$HEAD_short_submodule\n+\t$HEAD_short_top_file\n+\tEOF\n+## opt = --full-name\n+test_ls_tree_format_mode_output \"--full-name\" \"\" <<-EOF\n+\t100644 blob $HEAD_gitmodules\t.gitmodules\n+\t040000 tree $HEAD_dir\tdir\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\t100644 blob $HEAD_top_file\ttop-file.t\n+\tEOF\n+test_ls_tree_format_mode_output \"--full-name\" \"-d\" <<-EOF\n+\t040000 tree $HEAD_dir\tdir\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\tEOF\n+test_ls_tree_format_mode_output \"--full-name\" \"-r\" <<-EOF\n+\t100644 blob $HEAD_gitmodules\t.gitmodules\n+\t100644 blob $HEAD_dir_sub_file\tdir/sub-file.t\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\t100644 blob $HEAD_top_file\ttop-file.t\n+\tEOF\n+test_ls_tree_format_mode_output \"--full-name\" \"-t\" <<-EOF\n+\t100644 blob $HEAD_gitmodules\t.gitmodules\n+\t040000 tree $HEAD_dir\tdir\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\t100644 blob $HEAD_top_file\ttop-file.t\n+\tEOF\n+## opt = --full-tree\n+test_ls_tree_format_mode_output \"--full-tree\" \"\" \"-t\" <<-EOF\n+\t100644 blob $HEAD_gitmodules\t.gitmodules\n+\t040000 tree $HEAD_dir\tdir\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\t100644 blob $HEAD_top_file\ttop-file.t\n+\tEOF\n+test_ls_tree_format_mode_output \"--full-tree\" \"-d\" <<-EOF\n+\t040000 tree $HEAD_dir\tdir\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\tEOF\n+test_ls_tree_format_mode_output \"--full-tree\" \"-r\" <<-EOF\n+\t100644 blob $HEAD_gitmodules\t.gitmodules\n+\t100644 blob $HEAD_dir_sub_file\tdir/sub-file.t\n+\t160000 commit $HEAD_submodule\tsubmodule\n+\t100644 blob $HEAD_top_file\ttop-file.t\n+\tEOF\n+\n+test_done\n-- \n2.36.1.1119.g5a713b43bd4\n\n"},{"id":"456628","messageId":"xmqqpmjpeedq.fsf@gitster.g","threadId":"57383","inReplyTo":"220603.86fskmxd43.gmgdl@evledraar.gmail.com","subject":"Re: js/ci-github-workflow-markup output regression","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-06-03T19:27:45Z","receivedAt":"2022-06-03T19:27:57Z","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>> Further, since this failure is outside of any `test_expect_success` or\n>> `test_expect_failure`, the error message about this is not even included\n>> in the weblogs (but of course it is included in the full logs that are\n>> included in the build artifacts). For the record, here is the error\n>> message:\n>\n> ...this part of it though seems like a pretty bad regression in your\n> merged-to-next js/ci-github-workflow-markup topic, which just happens to\n> be unearthed by this CI failure.\n\nIndeed it makes it impossible to figure it out things like this\ncase.  But ...\n\n> But this does look easy to \"solve\" with a quicker fix, just bringing\n> back the \"ci/print-test-failures.sh\" step so you can at least expand it,\n> and not have to go to the \"summary\" and download the *.zip of the log\n> itself. As that shows we still have the raw log there, it just didn't\n> make it to the new GitHub Markdown formatting mechanism.\n\n... it seems a solution is possible?  Care to send in a patch (or\nperhaps Dscho already has a counter-proposal)?\n\nThanks.\n"},{"id":"456648","messageId":"220604.86y1ydwcq1.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"xmqqpmjpeedq.fsf@gitster.g","subject":"Re: js/ci-github-workflow-markup output regression","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-06-03T23:13:08Z","receivedAt":"2022-06-03T23:26:24Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Jun 03 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>>> Further, since this failure is outside of any `test_expect_success` or\n>>> `test_expect_failure`, the error message about this is not even included\n>>> in the weblogs (but of course it is included in the full logs that are\n>>> included in the build artifacts). For the record, here is the error\n>>> message:\n>>\n>> ...this part of it though seems like a pretty bad regression in your\n>> merged-to-next js/ci-github-workflow-markup topic, which just happens to\n>> be unearthed by this CI failure.\n>\n> Indeed it makes it impossible to figure it out things like this\n> case.  But ...\n>\n>> But this does look easy to \"solve\" with a quicker fix, just bringing\n>> back the \"ci/print-test-failures.sh\" step so you can at least expand it,\n>> and not have to go to the \"summary\" and download the *.zip of the log\n>> itself. As that shows we still have the raw log there, it just didn't\n>> make it to the new GitHub Markdown formatting mechanism.\n>\n> ... it seems a solution is possible?  Care to send in a patch (or\n> perhaps Dscho already has a counter-proposal)?\n\nThe only thing I have at the moment is:\n\n    1. git revert -m 1 bd37e9e41f5\n    2. merge: https://lore.kernel.org/git/cover-v6-00.29-00000000000-20220525T094123Z-avarab@gmail.com/\n    3. merge: https://lore.kernel.org/git/cover-v6-00.14-00000000000-20220525T100743Z-avarab@gmail.com/\n\nI.e. to pick this in the sequence I'd proposed doing & have tested\nthoroughly.\n\nIt also addresses other noted some other regressions in \"next\", but as\nnoted e.g. in [A] there's other issues in \"next\", e.g. that even the\n\"raw\" trace logs are altered as a side-effect of running with\n--github-workflow-markup, and of course the major UX slowdowns.\n\nSo I think the better way forward would be to do that & leave out [B],\ni.e. make the new output format optional, and wait until outstanding\nissues are fixed until we flip the default.\n\nBut do I have something that neatly fixes the issue(s) on top of \"next\"\nwithout a revert & re-apply? No, sorry.\n\nIn case you want to go for that the resolution for [2] is [C], and a\n\"git rm ci/run-build-and-tests.sh ci/run-static-analysis.sh\".\n\nA. https://lore.kernel.org/git/patch-v6-13.14-fbe0d99c6b3-20220525T100743Z-avarab@gmail.com/\nB. https://lore.kernel.org/git/patch-v6-14.14-0b02b186c87-20220525T100743Z-avarab@gmail.com/\nC. \n\tdiff --git a/Makefile b/Makefile\n\tindex 19f3756f7eb..c2b0a728df5 100644\n\t--- a/Makefile\n\t+++ b/Makefile\n\t@@ -3618,3 +3618,4 @@ ci-static-analysis: ci-check-directional-formatting\n\t ci-static-analysis: check-builtins\n\t ci-static-analysis: check-coccicheck\n\t ci-static-analysis: hdr-check\n\t+ci-static-analysis: check-pot\n\tdiff --git a/ci/lib.sh b/ci/lib.sh\n\tindex 80e89f89b7f..9c54c1330e6 100755\n\t--- a/ci/lib.sh\n\t+++ b/ci/lib.sh\n\t@@ -270,7 +270,7 @@ linux-TEST-vars)\n\t        setenv --test GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS 1\n\t        setenv --test GIT_TEST_MULTI_PACK_INDEX 1\n\t        setenv --test GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP 1\n\t-       setenv --test GIT_TEST_ADD_I_USE_BUILTIN 1\n\t+       setenv --test GIT_TEST_ADD_I_USE_BUILTIN 0\n\t        setenv --test GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME master\n\t        setenv --test GIT_TEST_WRITE_REV_INDEX 1\n\t        setenv --test GIT_TEST_CHECKOUT_WORKERS 2\n\t\n"},{"id":"456808","messageId":"xmqqo7z4wcsw.fsf@gitster.g","threadId":"57383","inReplyTo":"220604.86y1ydwcq1.gmgdl@evledraar.gmail.com","subject":"Re: js/ci-github-workflow-markup output regression","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-06-07T18:25:51Z","receivedAt":"2022-06-07T20:08:05Z","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> On Fri, Jun 03 2022, Junio C Hamano wrote:\n>\n>> Indeed it makes it impossible to figure it out things like this\n>> case.  But ...\n>>\n>>> But this does look easy to \"solve\" with a quicker fix, just bringing\n>>> back the \"ci/print-test-failures.sh\" step so you can at least expand it,\n>>> and not have to go to the \"summary\" and download the *.zip of the log\n>>> itself. As that shows we still have the raw log there, it just didn't\n>>> make it to the new GitHub Markdown formatting mechanism.\n>>\n>> ... it seems a solution is possible?  Care to send in a patch (or\n>> perhaps Dscho already has a counter-proposal)?\n>\n> The only thing I have at the moment is:\n>\n>     1. git revert -m 1 bd37e9e41f5\n>     2. merge: https://lore.kernel.org/git/cover-v6-00.29-00000000000-20220525T094123Z-avarab@gmail.com/\n>     3. merge: https://lore.kernel.org/git/cover-v6-00.14-00000000000-20220525T100743Z-avarab@gmail.com/\n>\n> I.e. to pick this in the sequence I'd proposed doing & have tested\n> thoroughly.\n\nI know you two have difference in opinions, but throwing away\neverything the other party did and forcing your stuff in is not a\nvery effective way to work together.\n\n> It also addresses other noted some other regressions in \"next\", but as\n> noted e.g. in [A] there's other issues in \"next\", e.g. that even the\n> \"raw\" trace logs are altered as a side-effect of running with\n> --github-workflow-markup, and of course the major UX slowdowns.\n\nDscho?  I know you do not care about the UX slowdown as much as\nothers, but I am sure you do not consider what is in 'next' is\nperfect. It seems to need further work to go back to the feature\nparity with what it replaced.\n\n"},{"id":"456819","messageId":"220607.86tu8wt9vp.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"xmqqo7z4wcsw.fsf@gitster.g","subject":"Re: js/ci-github-workflow-markup output regression","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-06-07T21:40:31Z","receivedAt":"2022-06-08T00:28:20Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Jun 07 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> On Fri, Jun 03 2022, Junio C Hamano wrote:\n>>\n>>> Indeed it makes it impossible to figure it out things like this\n>>> case.  But ...\n>>>\n>>>> But this does look easy to \"solve\" with a quicker fix, just bringing\n>>>> back the \"ci/print-test-failures.sh\" step so you can at least expand it,\n>>>> and not have to go to the \"summary\" and download the *.zip of the log\n>>>> itself. As that shows we still have the raw log there, it just didn't\n>>>> make it to the new GitHub Markdown formatting mechanism.\n>>>\n>>> ... it seems a solution is possible?  Care to send in a patch (or\n>>> perhaps Dscho already has a counter-proposal)?\n>>\n>> The only thing I have at the moment is:\n>>\n>>     1. git revert -m 1 bd37e9e41f5\n>>     2. merge: https://lore.kernel.org/git/cover-v6-00.29-00000000000-20220525T094123Z-avarab@gmail.com/\n>>     3. merge: https://lore.kernel.org/git/cover-v6-00.14-00000000000-20220525T100743Z-avarab@gmail.com/\n>>\n>> I.e. to pick this in the sequence I'd proposed doing & have tested\n>> thoroughly.\n>\n> I know you two have difference in opinions, but throwing away\n> everything the other party did and forcing your stuff in is not a\n> very effective way to work together.\n\nI'm suggesting getting Johannes's patches in combined with the changes &\nbugfixes I'd proposed.\n\nSo no, not throwing that work away, it would (applied up to 14/14) give\nyou functionally the same end result that's on \"next\" now as far as the\nnew GitHub Markdown output is concerned. The [3] above has links to the\nrelevant CI output.\n\nI had tried to rebase the above [2] on top of \"next\" before this\ndiscussion started, I agree that would be ideal, but it's a much larger\nlogical change that I don't have time to pursue now.\n\nI.e. there's a reason I proposed doing it in that order, a logical\nrebasing of [2] on top of bd37e9e41f5 would involve a lot of backing out\nof the existing direction taken there. I.e. the whole part where the\nsplit by \"steps\" provides much of the ci/* specific code in bd37e9e41f5\nfor free.\n\n>> It also addresses other noted some other regressions in \"next\", but as\n>> noted e.g. in [A] there's other issues in \"next\", e.g. that even the\n>> \"raw\" trace logs are altered as a side-effect of running with\n>> --github-workflow-markup, and of course the major UX slowdowns.\n>\n> Dscho?  I know you do not care about the UX slowdown as much as\n> others, but I am sure you do not consider what is in 'next' is\n> perfect. It seems to need further work to go back to the feature\n> parity with what it replaced.\n\nJust to be clear [3] up to 14/14 would still exhibit this particular\nbug, but with 13/14 it wouldn't from the links in [3] the relevant\noutputs are:\n\n \"next\" (well, similar): https://github.com/avar/git/runs/6571972194?check_suite_focus=true\n [3] with 14/14: https://github.com/avar/git/runs/6588407676?check_suite_focus=true\n [3] with 13/14: https://github.com/avar/git/runs/6588579493?check_suite_focus=true\n\nI really would like to get is out of this long-running ci/ limbo,\nperhaps Johannes has some proposed patches, but I don't think fixing the\noutstanding bugs is going to be trivial or easy.  Some of it's hard to\ntease apart, e.g. the altered *.out logs seem to require some tricky\ntest-lib.sh and test-lib-functions.sh changes.\n\nI don't see why wouldn't get all of that code in now though, just hidden\nbehind a flag, that would take the pressure off dealing with the current\nregressions, [2] with 13/14 would do that. Then once those outstanding\nissues are fixed we'd just need the one-line 14/14 change to flip the\ndefault CI output.\n\nBut is it the smallest possible change on top of what's now in \"next\"?\nNo, of course not.\n\nBut I don't have those hypothetical patches, just the above. That's all\nI meant in reply to the \"care to send in a patch?\"  upthread.\n"},{"id":"456859","messageId":"nycvar.QRO.7.76.6.2206080922470.349@tvgsbejvaqbjf.bet","threadId":"57383","inReplyTo":"xmqqo7z4wcsw.fsf@gitster.g","subject":"Re: js/ci-github-workflow-markup output regression","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2022-06-08T08:04:20Z","receivedAt":"2022-06-08T08:53:39Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 7 Jun 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n> > On Fri, Jun 03 2022, Junio C Hamano wrote:\n> >\n> >> Indeed it makes it impossible to figure it out things like this case.\n> >> But ...\n> >>\n> >>> But this does look easy to \"solve\" with a quicker fix, just bringing\n> >>> back the \"ci/print-test-failures.sh\" step so you can at least expand\n> >>> it, and not have to go to the \"summary\" and download the *.zip of\n> >>> the log itself.\n\nI agree that re-adding the `ci/print-test-failures.sh` step makes sense,\ngiven the recent experience.\n\n> >>> As that shows we still have the raw log there, it just didn't make\n> >>> it to the new GitHub Markdown formatting mechanism.\n> >>\n> >> ... it seems a solution is possible?  Care to send in a patch (or\n> >> perhaps Dscho already has a counter-proposal)?\n\nI will work on this.\n\n> > The only thing I have at the moment is:\n> >\n> >     1. git revert -m 1 bd37e9e41f5\n> >     2. merge: https://lore.kernel.org/git/cover-v6-00.29-00000000000-20220525T094123Z-avarab@gmail.com/\n> >     3. merge: https://lore.kernel.org/git/cover-v6-00.14-00000000000-20220525T100743Z-avarab@gmail.com/\n> >\n> > I.e. to pick this in the sequence I'd proposed doing & have tested\n> > thoroughly.\n>\n> I know you two have difference in opinions, but throwing away everything\n> the other party did and forcing your stuff in is not a very effective\n> way to work together.\n\nI had already pointed out a rather terrible issue in that 29-strong patch\nseries: Dropping Azure Pipelines support makes it unnecessarily harder to\nwork on Git security issues. And it's not like we have an armada of people\nworking on those. I, for one, am pretty worn out from the recent work.\n\nIt might not be the intention of that patch series to make my life harder,\nbut it sure would be its impact. And intent does not excuse impact.\n\nI therefore had to conclude that the patch series in this form cannot be\nmerged.\n\nI recall that other reviews reached the same consensus, that this\n29-strong patch series is too unfocused on any particular goal. So maybe\ncalling this \"my opinion\" is not exactly fair.\n\n> > It also addresses other noted some other regressions in \"next\", but as\n> > noted e.g. in [A] there's other issues in \"next\", e.g. that even the\n> > \"raw\" trace logs are altered as a side-effect of running with\n> > --github-workflow-markup, and of course the major UX slowdowns.\n>\n> Dscho?  I know you do not care about the UX slowdown as much as\n> others, but I am sure you do not consider what is in 'next' is\n> perfect. It seems to need further work to go back to the feature\n> parity with what it replaced.\n\nIndeed, I do not consider it perfect. I even said so much in every\niteration's cover letter ;-)\n\nI will work on bringing back the `print-test-failures` step.\n\nCiao,\nDscho\n"},{"id":"456900","messageId":"nycvar.QRO.7.76.6.2206082352520.349@tvgsbejvaqbjf.bet","threadId":"57383","inReplyTo":"patch-v2-1.1-f2beb02dd29-20220603T102148Z-avarab@gmail.com","subject":"Re: [PATCH v2] ls-tree: test for the regression in 9c4d58ff2c3","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2022-06-08T21:55:28Z","receivedAt":"2022-06-08T21:56:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Fri, 3 Jun 2022, Ævar Arnfjörð Bjarmason wrote:\n\n> Range-diff against v1:\n> 1:  0fdfec624eb ! 1:  f2beb02dd29 ls-tree: test for the regression in 9c4d58ff2c3\n>     @@ t/t3105-ls-tree-output.sh (new)\n>      +'\n>      +\n>      +test_ls_tree_format_mode_output () {\n>     -+\tlocal opts=$1 &&\n>     ++\tlocal opts=\"$1\" &&\n\nThis is a minimal fix for the CI failure I reported, but the hefty\naddition of a dozen or so test cases (with the corresponding time penalty\nfor everybody who wishes to run the test suite) is still there, which\nseems quite excessive.\n\nCiao,\nJohannes\n"},{"id":"456963","messageId":"220609.86r13xsj00.gmgdl@evledraar.gmail.com","threadId":"57383","inReplyTo":"nycvar.QRO.7.76.6.2206080922470.349@tvgsbejvaqbjf.bet","subject":"Re: js/ci-github-workflow-markup output regression","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-06-09T19:43:45Z","receivedAt":"2022-06-09T20:02:31Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Jun 08 2022, Johannes Schindelin wrote:\n\n> Hi Junio,\n>\n> On Tue, 7 Jun 2022, Junio C Hamano wrote:\n>\n>> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>>\n>> > On Fri, Jun 03 2022, Junio C Hamano wrote:\n>> >\n>> >> Indeed it makes it impossible to figure it out things like this case.\n>> >> But ...\n>> >>\n>> >>> But this does look easy to \"solve\" with a quicker fix, just bringing\n>> >>> back the \"ci/print-test-failures.sh\" step so you can at least expand\n>> >>> it, and not have to go to the \"summary\" and download the *.zip of\n>> >>> the log itself.\n>\n> I agree that re-adding the `ci/print-test-failures.sh` step makes sense,\n> given the recent experience.\n>\n>> >>> As that shows we still have the raw log there, it just didn't make\n>> >>> it to the new GitHub Markdown formatting mechanism.\n>> >>\n>> >> ... it seems a solution is possible?  Care to send in a patch (or\n>> >> perhaps Dscho already has a counter-proposal)?\n>\n> I will work on this.\n>\n>> > The only thing I have at the moment is:\n>> >\n>> >     1. git revert -m 1 bd37e9e41f5\n>> >     2. merge: https://lore.kernel.org/git/cover-v6-00.29-00000000000-20220525T094123Z-avarab@gmail.com/\n>> >     3. merge: https://lore.kernel.org/git/cover-v6-00.14-00000000000-20220525T100743Z-avarab@gmail.com/\n>> >\n>> > I.e. to pick this in the sequence I'd proposed doing & have tested\n>> > thoroughly.\n>>\n>> I know you two have difference in opinions, but throwing away everything\n>> the other party did and forcing your stuff in is not a very effective\n>> way to work together.\n>\n> I had already pointed out a rather terrible issue in that 29-strong patch\n> series: Dropping Azure Pipelines support makes it unnecessarily harder to\n> work on Git security issues. And it's not like we have an armada of people\n> working on those. I, for one, am pretty worn out from the recent work.\n>\n> It might not be the intention of that patch series to make my life harder,\n> but it sure would be its impact. And intent does not excuse impact.\n>\n> I therefore had to conclude that the patch series in this form cannot be\n> merged.\n\nYou raised the same concern in February, and I made some practical\nsuggestions for how to proceed with that in:\n\n    https://lore.kernel.org/git/220222.86y2236ndp.gmgdl@evledraar.gmail.com/\n\nYou didn't reply, and here was a reminder in late March:\n\n    https://lore.kernel.org/git/cover-v2-00.25-00000000000-20220325T182534Z-avarab@gmail.com/\n\nYou then had a similar concern, and I replied again in early April\nsaying I'd be happy to acomodate you, if you could reply to that\noriginal E-Mail and clarify what you'd like exactly:\n\n    https://lore.kernel.org/git/220406.86bkxeecoi.gmgdl@evledraar.gmail.com/\n\nAnd here's a mention of it again in late April:\n\n    https://lore.kernel.org/git/220421.86fsm66zmz.gmgdl@evledraar.gmail.com/\n\nThen a few weeks ago you brought up the same point, and I replied again\nyou to please reply to that E-mail from back in February:\n\n    https://lore.kernel.org/git/220524.86y1yrwaw0.gmgdl@evledraar.gmail.com/\n\nSo really Johannes, I'm completely fine with accommodating you.\n\nBut when you suggest that some ad-hoc combination of out-of-tree code\nand not-used-in-tree code you have must not be touched *and* you're\nseemingly unwilling to figure out some way forward you're not being very\nhelpful.\n\nInstead you just keep repeating that something must not be changed, and\nwhen it's pointed out to you that that would block someone else's\npatches, but would you be OK with X, Y or Z you seemingly just blackhole\nthe replies you get.\n\nSo can you reply to that this time? Thanks.\n\n> I recall that other reviews reached the same consensus, that this\n> 29-strong patch series is too unfocused on any particular goal. So maybe\n> calling this \"my opinion\" is not exactly fair.\n\nYes, that's something others brought up, but it's unrelated to the Azure\nissue you're raising here. That change was contained within the first 6\nor so patches of that series.\n"}]}