{"thread":{"id":"18927","subject":"[PATCH] read_tree(): pass \"int stage\" as context to read_tree_recursive()","startedAt":"2009-04-18T00:29:28Z","lastAt":"2021-04-02T21:08:32Z","messageCount":262,"participants":["Nguyễn Thái Ngọc Duy","Ævar Arnfjörð Bjarmason","Elijah Newren","Junio C Hamano","Ramsay Jones"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"111554","messageId":"1240014568-3675-1-git-send-email-pclouds@gmail.com","threadId":"18927","inReplyTo":null,"subject":"[PATCH] read_tree(): pass \"int stage\" as context to read_tree_recursive()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2009-04-18T00:29:28Z","receivedAt":"2009-04-18T00:29:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Back in history, read_tree_recursive() did not have context argument to\npass custom parameters through. To support read_tree(), it took \"stage\"\nfrom read_tree() and pass it to read_tree_fn_t function.  Then context\nargument was added but \"stage\" remains as a read_tree_fn_t argument.\n\nThis patch converts read_tree() to pass stage as a context instead,\nthus remove the last usage of \"stage\" argument in\nread_tree_recursive() and read_tree_fn_t. When the opportunity comes,\n\"stage\" should be removed from read_tree_fn_t and read_tree_recursive().\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n tree.c |    6 +++---\n tree.h |    1 +\n 2 files changed, 4 insertions(+), 3 deletions(-)\n\ndiff --git a/tree.c b/tree.c\nindex 0d703a0..02f2ca0 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -31,7 +31,7 @@ static int read_one_entry_opt(const unsigned char *sha1, const char *base, int b\n \n static int read_one_entry(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage, void *context)\n {\n-\treturn read_one_entry_opt(sha1, base, baselen, pathname, mode, stage,\n+\treturn read_one_entry_opt(sha1, base, baselen, pathname, mode, *(int *)context,\n \t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n }\n \n@@ -41,7 +41,7 @@ static int read_one_entry(const unsigned char *sha1, const char *base, int basel\n  */\n static int read_one_entry_quick(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage, void *context)\n {\n-\treturn read_one_entry_opt(sha1, base, baselen, pathname, mode, stage,\n+\treturn read_one_entry_opt(sha1, base, baselen, pathname, mode, *(int *)context,\n \t\t\t\t  ADD_CACHE_JUST_APPEND);\n }\n \n@@ -206,7 +206,7 @@ int read_tree(struct tree *tree, int stage, const char **match)\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(tree, \"\", 0, stage, match, fn, NULL);\n+\terr = read_tree_recursive(tree, \"\", 0, 0, match, fn, &stage);\n \tif (fn == read_one_entry || err)\n \t\treturn err;\n \ndiff --git a/tree.h b/tree.h\nindex 2ff01a4..d0f4c9b 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -21,6 +21,7 @@ int parse_tree(struct tree *tree);\n struct tree *parse_tree_indirect(const unsigned char *sha1);\n \n #define READ_TREE_RECURSIVE 1\n+/* FIXME: remove \"int stage\" argument from read_tree_fn_t */\n typedef int (*read_tree_fn_t)(const unsigned char *, const char *, int, const char *, unsigned int, int, void *);\n \n extern int read_tree_recursive(struct tree *tree,\n-- \n1.6.2.2.693.g5a1be\n"},{"id":"418401","messageId":"20210306193458.20633-1-avarab@gmail.com","threadId":"18927","inReplyTo":"1240014568-3675-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 0/7] Move the read_tree() function to its only user","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-06T19:34:51Z","receivedAt":"2021-03-06T19:35:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This is a small cleanup series to remove move the last user of\nread_tree() over to read_tree_recursive(), and while we're at it\nadjust the API of read_tree_fn_t to its current use-case.\n\nI found out after writing this that there had been a FIXME comment\n(never made it into git.git) about this from mid-2009:\nhttps://lore.kernel.org/git/1240014568-3675-1-git-send-email-pclouds@gmail.com/\n\nÆvar Arnfjörð Bjarmason (7):\n  tree.c API: move read_tree() into builtin/ls-files.c\n  ls-files: don't needlessly pass around stage variable\n  ls-files: remove cache juggling + sorting\n  merge-ort: move cmp_cache_name_compare() from tree.c\n  ls-files: refactor read_one_entry_quick() to use a strbuf\n  tree.h API: remove support for starting at prefix != \"\"\n  tree.h API: remove \"stage\" parameter from read_tree_recursive()\n\n archive.c          |  13 +++---\n builtin/checkout.c |   4 +-\n builtin/log.c      |   6 +--\n builtin/ls-files.c |  28 +++++++++++-\n builtin/ls-tree.c  |   4 +-\n merge-ort.c        |  10 ++++\n merge-recursive.c  |   4 +-\n tree.c             | 112 ++-------------------------------------------\n tree.h             |  11 +----\n 9 files changed, 60 insertions(+), 132 deletions(-)\n\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418402","messageId":"20210306193458.20633-2-avarab@gmail.com","threadId":"18927","inReplyTo":"1240014568-3675-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/7] tree.c API: move read_tree() into builtin/ls-files.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-06T19:34:52Z","receivedAt":"2021-03-06T19:35:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since the read_tree() API was added around the same time as\nread_tree_recursive() in 94537c78a82 (Move \"read_tree()\" to\n\"tree.c\"[...], 2005-04-22) and b12ec373b8e ([PATCH] Teach read-tree\nabout commit objects, 2005-04-20) things have gradually migrated over\nto the read_tree_recursive() version.\n\nNow builtin/ls-files.c is the last user of this code, let's move all\nthe relevant code there. This allows for subsequent simplification of\nit, and an eventual move to read_tree_recursive().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++\n tree.c             | 89 ---------------------------------------------\n tree.h             |  4 --\n 3 files changed, 91 insertions(+), 93 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex f6f9e483b27..a4458622813 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -12,6 +12,7 @@\n #include \"dir.h\"\n #include \"builtin.h\"\n #include \"tree.h\"\n+#include \"cache-tree.h\"\n #include \"parse-options.h\"\n #include \"resolve-undo.h\"\n #include \"string-list.h\"\n@@ -420,6 +421,96 @@ static int get_common_prefix_len(const char *common_prefix)\n \treturn common_prefix_len;\n }\n \n+static int read_one_entry_opt(struct index_state *istate,\n+\t\t\t      const struct object_id *oid,\n+\t\t\t      const char *base, int baselen,\n+\t\t\t      const char *pathname,\n+\t\t\t      unsigned mode, int stage, int opt)\n+{\n+\tint len;\n+\tstruct cache_entry *ce;\n+\n+\tif (S_ISDIR(mode))\n+\t\treturn READ_TREE_RECURSIVE;\n+\n+\tlen = strlen(pathname);\n+\tce = make_empty_cache_entry(istate, baselen + len);\n+\n+\tce->ce_mode = create_ce_mode(mode);\n+\tce->ce_flags = create_ce_flags(stage);\n+\tce->ce_namelen = baselen + len;\n+\tmemcpy(ce->name, base, baselen);\n+\tmemcpy(ce->name + baselen, pathname, len+1);\n+\toidcpy(&ce->oid, oid);\n+\treturn add_index_entry(istate, ce, opt);\n+}\n+\n+static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n+\t\t\t  const char *pathname, unsigned mode, int stage,\n+\t\t\t  void *context)\n+{\n+\tstruct index_state *istate = context;\n+\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\t\t\t\t  mode, stage,\n+\t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n+}\n+\n+/*\n+ * This is used when the caller knows there is no existing entries at\n+ * the stage that will conflict with the entry being added.\n+ */\n+static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n+\t\t\t\tconst char *pathname, unsigned mode, int stage,\n+\t\t\t\tvoid *context)\n+{\n+\tstruct index_state *istate = context;\n+\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\t\t\t\t  mode, stage,\n+\t\t\t\t  ADD_CACHE_JUST_APPEND);\n+}\n+\n+\n+static int read_tree(struct repository *r, struct tree *tree, int stage,\n+\t\t     struct pathspec *match, struct index_state *istate)\n+{\n+\tread_tree_fn_t fn = NULL;\n+\tint i, err;\n+\n+\t/*\n+\t * Currently the only existing callers of this function all\n+\t * call it with stage=1 and after making sure there is nothing\n+\t * at that stage; we could always use read_one_entry_quick().\n+\t *\n+\t * But when we decide to straighten out git-read-tree not to\n+\t * use unpack_trees() in some cases, this will probably start\n+\t * to matter.\n+\t */\n+\n+\t/*\n+\t * See if we have cache entry at the stage.  If so,\n+\t * do it the original slow way, otherwise, append and then\n+\t * sort at the end.\n+\t */\n+\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n+\t\tconst struct cache_entry *ce = istate->cache[i];\n+\t\tif (ce_stage(ce) == stage)\n+\t\t\tfn = read_one_entry;\n+\t}\n+\n+\tif (!fn)\n+\t\tfn = read_one_entry_quick;\n+\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n+\tif (fn == read_one_entry || err)\n+\t\treturn err;\n+\n+\t/*\n+\t * Sort the cache entry -- we need to nuke the cache tree, though.\n+\t */\n+\tcache_tree_free(&istate->cache_tree);\n+\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n+\treturn 0;\n+}\n+\n /*\n  * Read the tree specified with --with-tree option\n  * (typically, HEAD) into stage #1 and then\ndiff --git a/tree.c b/tree.c\nindex a52479812ce..a6c12f2745a 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -11,54 +11,6 @@\n \n const char *tree_type = \"tree\";\n \n-static int read_one_entry_opt(struct index_state *istate,\n-\t\t\t      const struct object_id *oid,\n-\t\t\t      const char *base, int baselen,\n-\t\t\t      const char *pathname,\n-\t\t\t      unsigned mode, int stage, int opt)\n-{\n-\tint len;\n-\tstruct cache_entry *ce;\n-\n-\tif (S_ISDIR(mode))\n-\t\treturn READ_TREE_RECURSIVE;\n-\n-\tlen = strlen(pathname);\n-\tce = make_empty_cache_entry(istate, baselen + len);\n-\n-\tce->ce_mode = create_ce_mode(mode);\n-\tce->ce_flags = create_ce_flags(stage);\n-\tce->ce_namelen = baselen + len;\n-\tmemcpy(ce->name, base, baselen);\n-\tmemcpy(ce->name + baselen, pathname, len+1);\n-\toidcpy(&ce->oid, oid);\n-\treturn add_index_entry(istate, ce, opt);\n-}\n-\n-static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\t  const char *pathname, unsigned mode, int stage,\n-\t\t\t  void *context)\n-{\n-\tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode, stage,\n-\t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n-}\n-\n-/*\n- * This is used when the caller knows there is no existing entries at\n- * the stage that will conflict with the entry being added.\n- */\n-static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n-\t\t\t\tconst char *pathname, unsigned mode, int stage,\n-\t\t\t\tvoid *context)\n-{\n-\tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode, stage,\n-\t\t\t\t  ADD_CACHE_JUST_APPEND);\n-}\n-\n static int read_tree_1(struct repository *r,\n \t\t       struct tree *tree, struct strbuf *base,\n \t\t       int stage, const struct pathspec *pathspec,\n@@ -154,47 +106,6 @@ int cmp_cache_name_compare(const void *a_, const void *b_)\n \t\t\t\t  ce2->name, ce2->ce_namelen, ce_stage(ce2));\n }\n \n-int read_tree(struct repository *r, struct tree *tree, int stage,\n-\t      struct pathspec *match, struct index_state *istate)\n-{\n-\tread_tree_fn_t fn = NULL;\n-\tint i, err;\n-\n-\t/*\n-\t * Currently the only existing callers of this function all\n-\t * call it with stage=1 and after making sure there is nothing\n-\t * at that stage; we could always use read_one_entry_quick().\n-\t *\n-\t * But when we decide to straighten out git-read-tree not to\n-\t * use unpack_trees() in some cases, this will probably start\n-\t * to matter.\n-\t */\n-\n-\t/*\n-\t * See if we have cache entry at the stage.  If so,\n-\t * do it the original slow way, otherwise, append and then\n-\t * sort at the end.\n-\t */\n-\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n-\t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == stage)\n-\t\t\tfn = read_one_entry;\n-\t}\n-\n-\tif (!fn)\n-\t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n-\tif (fn == read_one_entry || err)\n-\t\treturn err;\n-\n-\t/*\n-\t * Sort the cache entry -- we need to nuke the cache tree, though.\n-\t */\n-\tcache_tree_free(&istate->cache_tree);\n-\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n-\treturn 0;\n-}\n-\n struct tree *lookup_tree(struct repository *r, const struct object_id *oid)\n {\n \tstruct object *obj = lookup_object(r, oid);\ndiff --git a/tree.h b/tree.h\nindex 3eb0484cbf2..84d66b45538 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -39,8 +39,4 @@ int read_tree_recursive(struct repository *r,\n \t\t\tint stage, const struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context);\n \n-int read_tree(struct repository *r, struct tree *tree,\n-\t      int stage, struct pathspec *pathspec,\n-\t      struct index_state *istate);\n-\n #endif /* TREE_H */\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418403","messageId":"20210306193458.20633-6-avarab@gmail.com","threadId":"18927","inReplyTo":"1240014568-3675-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 5/7] ls-files: refactor read_one_entry_quick() to use a strbuf","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-06T19:34:56Z","receivedAt":"2021-03-06T19:35:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor the code in read_one_entry_quick() that used \"base\" and\n\"baselen\" to just use the \"buf\" and \"len\" fields in the \"base\" strbuf\ndirectly. Having the \"basebuf\" variable was a transitory step in\nmoving away from the old read_tree() in tree.c.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 12 +++++-------\n 1 file changed, 5 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex f5239437809..c0349a7b206 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -421,14 +421,12 @@ static int get_common_prefix_len(const char *common_prefix)\n }\n \n static int read_one_entry_quick(const struct object_id *oid,\n-\t\t\t\tstruct strbuf *basebuf,\n+\t\t\t\tstruct strbuf *base,\n \t\t\t\tconst char *pathname,\n \t\t\t\tunsigned mode,\n \t\t\t\tint stage, void *context)\n {\n \tstruct index_state *istate = context;\n-\tconst char *base = basebuf->buf;\n-\tconst int baselen = basebuf->len;\n \tint len;\n \tstruct cache_entry *ce;\n \n@@ -436,13 +434,13 @@ static int read_one_entry_quick(const struct object_id *oid,\n \t\treturn READ_TREE_RECURSIVE;\n \n \tlen = strlen(pathname);\n-\tce = make_empty_cache_entry(istate, baselen + len);\n+\tce = make_empty_cache_entry(istate, base->len + len);\n \n \tce->ce_mode = create_ce_mode(mode);\n \tce->ce_flags = create_ce_flags(stage);\n-\tce->ce_namelen = baselen + len;\n-\tmemcpy(ce->name, base, baselen);\n-\tmemcpy(ce->name + baselen, pathname, len+1);\n+\tce->ce_namelen = base->len + len;\n+\tmemcpy(ce->name, base->buf, base->len);\n+\tmemcpy(ce->name + base->len, pathname, len+1);\n \toidcpy(&ce->oid, oid);\n \treturn add_index_entry(istate, ce, ADD_CACHE_JUST_APPEND);\n }\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418404","messageId":"20210306193458.20633-3-avarab@gmail.com","threadId":"18927","inReplyTo":"1240014568-3675-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/7] ls-files: don't needlessly pass around stage variable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-06T19:34:53Z","receivedAt":"2021-03-06T19:35:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that read_tree() has been moved to ls-files.c we can get rid of\nthe stage != 1 case that'll never happen.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 17 ++++-------------\n 1 file changed, 4 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex a4458622813..74d572a3e4a 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -470,21 +470,12 @@ static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base\n }\n \n \n-static int read_tree(struct repository *r, struct tree *tree, int stage,\n+static int read_tree(struct repository *r, struct tree *tree,\n \t\t     struct pathspec *match, struct index_state *istate)\n {\n \tread_tree_fn_t fn = NULL;\n \tint i, err;\n \n-\t/*\n-\t * Currently the only existing callers of this function all\n-\t * call it with stage=1 and after making sure there is nothing\n-\t * at that stage; we could always use read_one_entry_quick().\n-\t *\n-\t * But when we decide to straighten out git-read-tree not to\n-\t * use unpack_trees() in some cases, this will probably start\n-\t * to matter.\n-\t */\n \n \t/*\n \t * See if we have cache entry at the stage.  If so,\n@@ -493,13 +484,13 @@ static int read_tree(struct repository *r, struct tree *tree, int stage,\n \t */\n \tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n \t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == stage)\n+\t\tif (ce_stage(ce) == 1)\n \t\t\tfn = read_one_entry;\n \t}\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n+\terr = read_tree_recursive(r, tree, \"\", 0, 1, match, fn, istate);\n \tif (fn == read_one_entry || err)\n \t\treturn err;\n \n@@ -549,7 +540,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n \t} else\n \t\tmemset(&pathspec, 0, sizeof(pathspec));\n-\tif (read_tree(the_repository, tree, 1, &pathspec, istate))\n+\tif (read_tree(the_repository, tree, &pathspec, istate))\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \n \tfor (i = 0; i < istate->cache_nr; i++) {\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418405","messageId":"20210306193458.20633-4-avarab@gmail.com","threadId":"18927","inReplyTo":"1240014568-3675-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/7] ls-files: remove cache juggling + sorting","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-06T19:34:54Z","receivedAt":"2021-03-06T19:35:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove the \"ce_stage(ce) == 1\" and \"Sort the cache entry\" code from\nread_tree(), which allows us to remove the function entirely and move\nover to read_tree_recursive().\n\nI don't think the \"Sort the cached entry\" code was needed here, see\naf3785dc5a7 (Optimize \"diff --cached\" performance., 2007-08-09) for\nthe use-case it was intended for. The only user of this code is\n\"ls-files --with-tree\", which isn't the sort of use-case that needs to\ncare about \"ce_stage(ce) != 0\" or sorting tree entries.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 76 +++++++---------------------------------------\n 1 file changed, 11 insertions(+), 65 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 74d572a3e4a..f5239437809 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -12,7 +12,6 @@\n #include \"dir.h\"\n #include \"builtin.h\"\n #include \"tree.h\"\n-#include \"cache-tree.h\"\n #include \"parse-options.h\"\n #include \"resolve-undo.h\"\n #include \"string-list.h\"\n@@ -421,12 +420,15 @@ static int get_common_prefix_len(const char *common_prefix)\n \treturn common_prefix_len;\n }\n \n-static int read_one_entry_opt(struct index_state *istate,\n-\t\t\t      const struct object_id *oid,\n-\t\t\t      const char *base, int baselen,\n-\t\t\t      const char *pathname,\n-\t\t\t      unsigned mode, int stage, int opt)\n+static int read_one_entry_quick(const struct object_id *oid,\n+\t\t\t\tstruct strbuf *basebuf,\n+\t\t\t\tconst char *pathname,\n+\t\t\t\tunsigned mode,\n+\t\t\t\tint stage, void *context)\n {\n+\tstruct index_state *istate = context;\n+\tconst char *base = basebuf->buf;\n+\tconst int baselen = basebuf->len;\n \tint len;\n \tstruct cache_entry *ce;\n \n@@ -442,64 +444,7 @@ static int read_one_entry_opt(struct index_state *istate,\n \tmemcpy(ce->name, base, baselen);\n \tmemcpy(ce->name + baselen, pathname, len+1);\n \toidcpy(&ce->oid, oid);\n-\treturn add_index_entry(istate, ce, opt);\n-}\n-\n-static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\t  const char *pathname, unsigned mode, int stage,\n-\t\t\t  void *context)\n-{\n-\tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode, stage,\n-\t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n-}\n-\n-/*\n- * This is used when the caller knows there is no existing entries at\n- * the stage that will conflict with the entry being added.\n- */\n-static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n-\t\t\t\tconst char *pathname, unsigned mode, int stage,\n-\t\t\t\tvoid *context)\n-{\n-\tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode, stage,\n-\t\t\t\t  ADD_CACHE_JUST_APPEND);\n-}\n-\n-\n-static int read_tree(struct repository *r, struct tree *tree,\n-\t\t     struct pathspec *match, struct index_state *istate)\n-{\n-\tread_tree_fn_t fn = NULL;\n-\tint i, err;\n-\n-\n-\t/*\n-\t * See if we have cache entry at the stage.  If so,\n-\t * do it the original slow way, otherwise, append and then\n-\t * sort at the end.\n-\t */\n-\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n-\t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == 1)\n-\t\t\tfn = read_one_entry;\n-\t}\n-\n-\tif (!fn)\n-\t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, 1, match, fn, istate);\n-\tif (fn == read_one_entry || err)\n-\t\treturn err;\n-\n-\t/*\n-\t * Sort the cache entry -- we need to nuke the cache tree, though.\n-\t */\n-\tcache_tree_free(&istate->cache_tree);\n-\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n-\treturn 0;\n+\treturn add_index_entry(istate, ce, ADD_CACHE_JUST_APPEND);\n }\n \n /*\n@@ -540,7 +485,8 @@ void overlay_tree_on_index(struct index_state *istate,\n \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n \t} else\n \t\tmemset(&pathspec, 0, sizeof(pathspec));\n-\tif (read_tree(the_repository, tree, &pathspec, istate))\n+\tif (read_tree_recursive(the_repository, tree, \"\", 0, 1,\n+\t\t\t\t&pathspec, read_one_entry_quick, istate))\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \n \tfor (i = 0; i < istate->cache_nr; i++) {\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418406","messageId":"20210306193458.20633-7-avarab@gmail.com","threadId":"18927","inReplyTo":"1240014568-3675-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 6/7] tree.h API: remove support for starting at prefix != \"\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-06T19:34:57Z","receivedAt":"2021-03-06T19:35:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Every caller or the read_tree_recursive() function hardcoded a\nstarting point of \"\" in the tree. So let's simply remove that\nparameter.\n\nIt might be useful in the future to get this functionality back,\nthere's no reason we won't have a read_tree_recursive() use-case that\nwould want to start in a subdirectory.\n\nBut if and when that happens we can just add something like a\nread_tree_recursive_subdir() and have both read_tree_recursive() and\nthat function be a thin wrapper for read_tree_1().\n\nIn the meantime there's no reason to keep around what amounts to dead\ncode just in case we need it in the future.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          | 8 ++++----\n builtin/checkout.c | 2 +-\n builtin/log.c      | 4 ++--\n builtin/ls-files.c | 2 +-\n builtin/ls-tree.c  | 2 +-\n merge-recursive.c  | 2 +-\n tree.c             | 2 --\n tree.h             | 1 -\n 8 files changed, 10 insertions(+), 13 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 5919d9e5050..9394f170f7f 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -316,8 +316,8 @@ int write_archive_entries(struct archiver_args *args,\n \t\tgit_attr_set_direction(GIT_ATTR_INDEX);\n \t}\n \n-\terr = read_tree_recursive(args->repo, args->tree, \"\",\n-\t\t\t\t  0, 0, &args->pathspec,\n+\terr = read_tree_recursive(args->repo, args->tree,\n+\t\t\t\t  0, &args->pathspec,\n \t\t\t\t  queue_or_write_archive_entry,\n \t\t\t\t  &context);\n \tif (err == READ_TREE_RECURSIVE)\n@@ -405,8 +405,8 @@ static int path_exists(struct archiver_args *args, const char *path)\n \tctx.args = args;\n \tparse_pathspec(&ctx.pathspec, 0, 0, \"\", paths);\n \tctx.pathspec.recursive = 1;\n-\tret = read_tree_recursive(args->repo, args->tree, \"\",\n-\t\t\t\t  0, 0, &ctx.pathspec,\n+\tret = read_tree_recursive(args->repo, args->tree,\n+\t\t\t\t  0, &ctx.pathspec,\n \t\t\t\t  reject_entry, &ctx);\n \tclear_pathspec(&ctx.pathspec);\n \treturn ret != 0;\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2d6550bc3c8..21b742c0f07 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -155,7 +155,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,\n \n static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)\n {\n-\tread_tree_recursive(the_repository, tree, \"\", 0, 0,\n+\tread_tree_recursive(the_repository, tree, 0,\n \t\t\t    pathspec, update_some, NULL);\n \n \t/* update the index with the given tree's info\ndiff --git a/builtin/log.c b/builtin/log.c\nindex f67b67d80ed..ffa3fb8c286 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -681,8 +681,8 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n-\t\t\tread_tree_recursive(the_repository, (struct tree *)o, \"\",\n-\t\t\t\t\t    0, 0, &match_all, show_tree_object,\n+\t\t\tread_tree_recursive(the_repository, (struct tree *)o,\n+\t\t\t\t\t    0, &match_all, show_tree_object,\n \t\t\t\t\t    rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex c0349a7b206..2c609428eea 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -483,7 +483,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n \t} else\n \t\tmemset(&pathspec, 0, sizeof(pathspec));\n-\tif (read_tree_recursive(the_repository, tree, \"\", 0, 1,\n+\tif (read_tree_recursive(the_repository, tree, 1,\n \t\t\t\t&pathspec, read_one_entry_quick, istate))\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 7cad3f24ebd..7d3fb2e6d0f 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -185,6 +185,6 @@ 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_recursive(the_repository, tree, \"\", 0, 0,\n+\treturn !!read_tree_recursive(the_repository, tree, 0,\n \t\t\t\t     &pathspec, show_tree, NULL);\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex b052974f191..fa7602ff0f2 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -473,7 +473,7 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n {\n \tstruct pathspec match_all;\n \tmemset(&match_all, 0, sizeof(match_all));\n-\tread_tree_recursive(opt->repo, tree, \"\", 0, 0,\n+\tread_tree_recursive(opt->repo, tree, 0,\n \t\t\t    &match_all, save_files_dirs, opt);\n }\n \ndiff --git a/tree.c b/tree.c\nindex c1bde9314d0..285633892c2 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -82,14 +82,12 @@ static int read_tree_1(struct repository *r,\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tconst char *base, int baselen,\n \t\t\tint stage, const struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\tstrbuf_add(&sb, base, baselen);\n \tret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context);\n \tstrbuf_release(&sb);\n \treturn ret;\ndiff --git a/tree.h b/tree.h\nindex 34549c86c9f..9a0fd3221e3 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -33,7 +33,6 @@ typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const c\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tconst char *base, int baselen,\n \t\t\tint stage, const struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context);\n \n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418407","messageId":"20210306193458.20633-8-avarab@gmail.com","threadId":"18927","inReplyTo":"1240014568-3675-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 7/7] tree.h API: remove \"stage\" parameter from read_tree_recursive()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-06T19:34:58Z","receivedAt":"2021-03-06T19:35:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The read_tree_recursive() function took a \"stage\" parameter that is\npassed through as-is. As it turns out nothing used this parameter in a\nway that they couldn't just move to the callback function they\ndefined, so let's get rid of it.\n\nIf anyone needs to pass such information in the future they can use\nthe \"void *context\" parameter.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          |  9 +++++----\n builtin/checkout.c |  4 ++--\n builtin/log.c      |  4 ++--\n builtin/ls-files.c |  6 +++---\n builtin/ls-tree.c  |  4 ++--\n merge-recursive.c  |  4 ++--\n tree.c             | 10 +++++-----\n tree.h             |  4 ++--\n 8 files changed, 23 insertions(+), 22 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 9394f170f7f..6669a4bd147 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -231,9 +231,10 @@ static int write_directory(struct archiver_context *c)\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\tstruct strbuf *base, const char *filename,\n-\t\tunsigned mode, int stage, void *context)\n+\t\tunsigned mode, void *context)\n {\n \tstruct archiver_context *c = context;\n+\tint stage = 0;\n \n \twhile (c->bottom &&\n \t       !(base->len >= c->bottom->len &&\n@@ -317,7 +318,7 @@ int write_archive_entries(struct archiver_args *args,\n \t}\n \n \terr = read_tree_recursive(args->repo, args->tree,\n-\t\t\t\t  0, &args->pathspec,\n+\t\t\t\t  &args->pathspec,\n \t\t\t\t  queue_or_write_archive_entry,\n \t\t\t\t  &context);\n \tif (err == READ_TREE_RECURSIVE)\n@@ -378,7 +379,7 @@ struct path_exists_context {\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n \t\t\tconst char *filename, unsigned mode,\n-\t\t\tint stage, void *context)\n+\t\t\tvoid *context)\n {\n \tint ret = -1;\n \tstruct path_exists_context *ctx = context;\n@@ -406,7 +407,7 @@ static int path_exists(struct archiver_args *args, const char *path)\n \tparse_pathspec(&ctx.pathspec, 0, 0, \"\", paths);\n \tctx.pathspec.recursive = 1;\n \tret = read_tree_recursive(args->repo, args->tree,\n-\t\t\t\t  0, &ctx.pathspec,\n+\t\t\t\t  &ctx.pathspec,\n \t\t\t\t  reject_entry, &ctx);\n \tclear_pathspec(&ctx.pathspec);\n \treturn ret != 0;\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 21b742c0f07..2c2d58a230f 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -114,7 +114,7 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n }\n \n static int update_some(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tint len;\n \tstruct cache_entry *ce;\n@@ -155,7 +155,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,\n \n static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)\n {\n-\tread_tree_recursive(the_repository, tree, 0,\n+\tread_tree_recursive(the_repository, tree,\n \t\t\t    pathspec, update_some, NULL);\n \n \t/* update the index with the given tree's info\ndiff --git a/builtin/log.c b/builtin/log.c\nindex ffa3fb8c286..58acb2b76ab 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -599,7 +599,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n \n static int show_tree_object(const struct object_id *oid,\n \t\tstruct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tFILE *file = context;\n \tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n@@ -682,7 +682,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n \t\t\tread_tree_recursive(the_repository, (struct tree *)o,\n-\t\t\t\t\t    0, &match_all, show_tree_object,\n+\t\t\t\t\t    &match_all, show_tree_object,\n \t\t\t\t\t    rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 2c609428eea..8ba13b69c97 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -424,7 +424,7 @@ static int read_one_entry_quick(const struct object_id *oid,\n \t\t\t\tstruct strbuf *base,\n \t\t\t\tconst char *pathname,\n \t\t\t\tunsigned mode,\n-\t\t\t\tint stage, void *context)\n+\t\t\t\tvoid *context)\n {\n \tstruct index_state *istate = context;\n \tint len;\n@@ -437,7 +437,7 @@ static int read_one_entry_quick(const struct object_id *oid,\n \tce = make_empty_cache_entry(istate, base->len + len);\n \n \tce->ce_mode = create_ce_mode(mode);\n-\tce->ce_flags = create_ce_flags(stage);\n+\tce->ce_flags = create_ce_flags(1);\n \tce->ce_namelen = base->len + len;\n \tmemcpy(ce->name, base->buf, base->len);\n \tmemcpy(ce->name + base->len, pathname, len+1);\n@@ -483,7 +483,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n \t} else\n \t\tmemset(&pathspec, 0, sizeof(pathspec));\n-\tif (read_tree_recursive(the_repository, tree, 1,\n+\tif (read_tree_recursive(the_repository, tree,\n \t\t\t\t&pathspec, read_one_entry_quick, istate))\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 7d3fb2e6d0f..dbb31217beb 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -62,7 +62,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n }\n \n static int show_tree(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tint retval = 0;\n \tint baselen;\n@@ -185,6 +185,6 @@ 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_recursive(the_repository, tree, 0,\n+\treturn !!read_tree_recursive(the_repository, tree,\n \t\t\t\t     &pathspec, show_tree, NULL);\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex fa7602ff0f2..1593f374495 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,7 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   unsigned int mode, int stage, void *context)\n+\t\t\t   unsigned int mode, void *context)\n {\n \tstruct path_hashmap_entry *entry;\n \tint baselen = base->len;\n@@ -473,7 +473,7 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n {\n \tstruct pathspec match_all;\n \tmemset(&match_all, 0, sizeof(match_all));\n-\tread_tree_recursive(opt->repo, tree, 0,\n+\tread_tree_recursive(opt->repo, tree,\n \t\t\t    &match_all, save_files_dirs, opt);\n }\n \ndiff --git a/tree.c b/tree.c\nindex 285633892c2..3de41e37364 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -12,7 +12,7 @@ const char *tree_type = \"tree\";\n \n static int read_tree_1(struct repository *r,\n \t\t       struct tree *tree, struct strbuf *base,\n-\t\t       int stage, const struct pathspec *pathspec,\n+\t\t       const struct pathspec *pathspec,\n \t\t       read_tree_fn_t fn, void *context)\n {\n \tstruct tree_desc desc;\n@@ -37,7 +37,7 @@ static int read_tree_1(struct repository *r,\n \t\t}\n \n \t\tswitch (fn(&entry.oid, base,\n-\t\t\t   entry.path, entry.mode, stage, context)) {\n+\t\t\t   entry.path, entry.mode, context)) {\n \t\tcase 0:\n \t\t\tcontinue;\n \t\tcase READ_TREE_RECURSIVE:\n@@ -71,7 +71,7 @@ static int read_tree_1(struct repository *r,\n \t\tstrbuf_add(base, entry.path, len);\n \t\tstrbuf_addch(base, '/');\n \t\tretval = read_tree_1(r, lookup_tree(r, &oid),\n-\t\t\t\t     base, stage, pathspec,\n+\t\t\t\t     base, pathspec,\n \t\t\t\t     fn, context);\n \t\tstrbuf_setlen(base, oldlen);\n \t\tif (retval)\n@@ -82,13 +82,13 @@ static int read_tree_1(struct repository *r,\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tint stage, const struct pathspec *pathspec,\n+\t\t\tconst struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\tret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context);\n+\tret = read_tree_1(r, tree, &sb, pathspec, fn, context);\n \tstrbuf_release(&sb);\n \treturn ret;\n }\ndiff --git a/tree.h b/tree.h\nindex 9a0fd3221e3..e7b851c6abd 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -29,11 +29,11 @@ void free_tree_buffer(struct tree *tree);\n struct tree *parse_tree_indirect(const struct object_id *oid);\n \n #define READ_TREE_RECURSIVE 1\n-typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, int, void *);\n+typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tint stage, const struct pathspec *pathspec,\n+\t\t\tconst struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context);\n \n #endif /* TREE_H */\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418408","messageId":"20210306193458.20633-5-avarab@gmail.com","threadId":"18927","inReplyTo":"1240014568-3675-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 4/7] merge-ort: move cmp_cache_name_compare() from tree.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-06T19:34:55Z","receivedAt":"2021-03-06T19:35:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Move the cmp_cache_name_compare() function from tree.c. Now that we've\nstopped using it in builtin/ls-files.c the merge-ort.c code is its\nonly user, let's just have it own it instead of having this API which\nstraddles tree.h and cache-tree.h in tree.c itself.\n\nSee these commits for its recent introduction in merge-ort.c::\n\n - 70912f66de7 (tree: enable cmp_cache_name_compare() to be used\n   elsewhere, 2020-12-13)\n\n - ef2b3693870 (merge-ort: add implementation of\n   record_conflicted_index_entries(), 2020-12-13)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n merge-ort.c | 10 ++++++++++\n tree.c      | 11 -----------\n tree.h      |  2 --\n 3 files changed, 10 insertions(+), 13 deletions(-)\n\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 603d30c5217..d7b3ced1bec 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -3099,6 +3099,16 @@ static int checkout(struct merge_options *opt,\n \treturn ret;\n }\n \n+static int cmp_cache_name_compare(const void *a_, const void *b_)\n+{\n+\tconst struct cache_entry *ce1, *ce2;\n+\n+\tce1 = *((const struct cache_entry **)a_);\n+\tce2 = *((const struct cache_entry **)b_);\n+\treturn cache_name_stage_compare(ce1->name, ce1->ce_namelen, ce_stage(ce1),\n+\t\t\t\t  ce2->name, ce2->ce_namelen, ce_stage(ce2));\n+}\n+\n static int record_conflicted_index_entries(struct merge_options *opt,\n \t\t\t\t\t   struct index_state *index,\n \t\t\t\t\t   struct strmap *paths,\ndiff --git a/tree.c b/tree.c\nindex a6c12f2745a..c1bde9314d0 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -1,5 +1,4 @@\n #include \"cache.h\"\n-#include \"cache-tree.h\"\n #include \"tree.h\"\n #include \"object-store.h\"\n #include \"blob.h\"\n@@ -96,16 +95,6 @@ int read_tree_recursive(struct repository *r,\n \treturn ret;\n }\n \n-int cmp_cache_name_compare(const void *a_, const void *b_)\n-{\n-\tconst struct cache_entry *ce1, *ce2;\n-\n-\tce1 = *((const struct cache_entry **)a_);\n-\tce2 = *((const struct cache_entry **)b_);\n-\treturn cache_name_stage_compare(ce1->name, ce1->ce_namelen, ce_stage(ce1),\n-\t\t\t\t  ce2->name, ce2->ce_namelen, ce_stage(ce2));\n-}\n-\n struct tree *lookup_tree(struct repository *r, const struct object_id *oid)\n {\n \tstruct object *obj = lookup_object(r, oid);\ndiff --git a/tree.h b/tree.h\nindex 84d66b45538..34549c86c9f 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -28,8 +28,6 @@ void free_tree_buffer(struct tree *tree);\n /* Parses and returns the tree in the given ent, chasing tags and commits. */\n struct tree *parse_tree_indirect(const struct object_id *oid);\n \n-int cmp_cache_name_compare(const void *a_, const void *b_);\n-\n #define READ_TREE_RECURSIVE 1\n typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, int, void *);\n \n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418409","messageId":"CABPp-BF982muRS4GO=zYegvetQyrPMwaEM3uEBvcbPRP=krfmQ@mail.gmail.com","threadId":"18927","inReplyTo":"20210306193458.20633-4-avarab@gmail.com","subject":"Re: [PATCH 3/7] ls-files: remove cache juggling + sorting","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-06T21:37:03Z","receivedAt":"2021-03-06T21:37:50Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sat, Mar 6, 2021 at 11:35 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> Remove the \"ce_stage(ce) == 1\" and \"Sort the cache entry\" code from\n> read_tree(), which allows us to remove the function entirely and move\n> over to read_tree_recursive().\n\nRemoving ce_stage(ce) == 1 and the use of read_one_entry() as the\nread_tree_fn_t, keeping read_one_entry_quick() as read_tree_fn_t in\nall cases.\n\nThis basically means you are assuming there are no entries with\nce_stage(ce) == 1 to start with; what if the user calls `ls-files\n--with-tree` when in the middle of a merge conflict?  Will those\nentries be duplicated and/or overwritten?\n\n> I don't think the \"Sort the cached entry\" code was needed here, see\n> af3785dc5a7 (Optimize \"diff --cached\" performance., 2007-08-09) for\n> the use-case it was intended for. The only user of this code is\n> \"ls-files --with-tree\", which isn't the sort of use-case that needs to\n> care about \"ce_stage(ce) != 0\" or sorting tree entries.\n\nCould you spell this out more?  I don't understand.  The initial index\nmight be something like (using the syntax of filename, stage):\n    file   0\n    random   0\n    some   0\nand after read_tree_recursive() reading into stage 1, wouldn't it be\nsomething like\n    file   0\n    random   0\n    some   0\n    file   1\n    some   1\n    strange   1\nwhen the ordering should be\n    file   0\n    file   1\n    random   0\n    some   0\n    some   1\n    strange   1\n?\n\nI'm probably missing something here.\n\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/ls-files.c | 76 +++++++---------------------------------------\n>  1 file changed, 11 insertions(+), 65 deletions(-)\n>\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index 74d572a3e4a..f5239437809 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -12,7 +12,6 @@\n>  #include \"dir.h\"\n>  #include \"builtin.h\"\n>  #include \"tree.h\"\n> -#include \"cache-tree.h\"\n>  #include \"parse-options.h\"\n>  #include \"resolve-undo.h\"\n>  #include \"string-list.h\"\n> @@ -421,12 +420,15 @@ static int get_common_prefix_len(const char *common_prefix)\n>         return common_prefix_len;\n>  }\n>\n> -static int read_one_entry_opt(struct index_state *istate,\n> -                             const struct object_id *oid,\n> -                             const char *base, int baselen,\n> -                             const char *pathname,\n> -                             unsigned mode, int stage, int opt)\n> +static int read_one_entry_quick(const struct object_id *oid,\n> +                               struct strbuf *basebuf,\n> +                               const char *pathname,\n> +                               unsigned mode,\n> +                               int stage, void *context)\n>  {\n> +       struct index_state *istate = context;\n> +       const char *base = basebuf->buf;\n> +       const int baselen = basebuf->len;\n>         int len;\n>         struct cache_entry *ce;\n>\n> @@ -442,64 +444,7 @@ static int read_one_entry_opt(struct index_state *istate,\n>         memcpy(ce->name, base, baselen);\n>         memcpy(ce->name + baselen, pathname, len+1);\n>         oidcpy(&ce->oid, oid);\n> -       return add_index_entry(istate, ce, opt);\n> -}\n> -\n> -static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n> -                         const char *pathname, unsigned mode, int stage,\n> -                         void *context)\n> -{\n> -       struct index_state *istate = context;\n> -       return read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n> -                                 mode, stage,\n> -                                 ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n> -}\n> -\n> -/*\n> - * This is used when the caller knows there is no existing entries at\n> - * the stage that will conflict with the entry being added.\n> - */\n> -static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n> -                               const char *pathname, unsigned mode, int stage,\n> -                               void *context)\n> -{\n> -       struct index_state *istate = context;\n> -       return read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n> -                                 mode, stage,\n> -                                 ADD_CACHE_JUST_APPEND);\n> -}\n> -\n> -\n> -static int read_tree(struct repository *r, struct tree *tree,\n> -                    struct pathspec *match, struct index_state *istate)\n> -{\n> -       read_tree_fn_t fn = NULL;\n> -       int i, err;\n> -\n> -\n> -       /*\n> -        * See if we have cache entry at the stage.  If so,\n> -        * do it the original slow way, otherwise, append and then\n> -        * sort at the end.\n> -        */\n> -       for (i = 0; !fn && i < istate->cache_nr; i++) {\n> -               const struct cache_entry *ce = istate->cache[i];\n> -               if (ce_stage(ce) == 1)\n> -                       fn = read_one_entry;\n> -       }\n> -\n> -       if (!fn)\n> -               fn = read_one_entry_quick;\n> -       err = read_tree_recursive(r, tree, \"\", 0, 1, match, fn, istate);\n> -       if (fn == read_one_entry || err)\n> -               return err;\n> -\n> -       /*\n> -        * Sort the cache entry -- we need to nuke the cache tree, though.\n> -        */\n> -       cache_tree_free(&istate->cache_tree);\n> -       QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n> -       return 0;\n> +       return add_index_entry(istate, ce, ADD_CACHE_JUST_APPEND);\n>  }\n>\n>  /*\n> @@ -540,7 +485,8 @@ void overlay_tree_on_index(struct index_state *istate,\n>                                PATHSPEC_PREFER_CWD, prefix, matchbuf);\n>         } else\n>                 memset(&pathspec, 0, sizeof(pathspec));\n> -       if (read_tree(the_repository, tree, &pathspec, istate))\n> +       if (read_tree_recursive(the_repository, tree, \"\", 0, 1,\n> +                               &pathspec, read_one_entry_quick, istate))\n>                 die(\"unable to read tree entries %s\", tree_name);\n>\n>         for (i = 0; i < istate->cache_nr; i++) {\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n"},{"id":"418410","messageId":"CABPp-BGj2k=idn8wqpek-3m5NwNbSPCNM6mFXVyFMTDoWu44JA@mail.gmail.com","threadId":"18927","inReplyTo":"20210306193458.20633-7-avarab@gmail.com","subject":"Re: [PATCH 6/7] tree.h API: remove support for starting at prefix != \"\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-06T21:55:49Z","receivedAt":"2021-03-06T21:57:02Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sat, Mar 6, 2021 at 11:35 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> Every caller or the read_tree_recursive() function hardcoded a\n\ns/or/of/?\n\n> starting point of \"\" in the tree. So let's simply remove that\n> parameter.\n\nInteresting.  It appears that read_tree_recursive() was the last\nfunction to call read_tree_recursive() with a starting point other\nthan \"\", but that was changed in commit ffd31f661d (\"Reimplement\nread_tree_recursive() using tree_entry_interesting()\", 2011-03-25).\n\nThe last caller of read_tree_recursive() other than itself to call it\nwith base != \"\", was ebfbdb340a (\"Git archive and trailing \"/\" in\nprefix\", 2009-10-08).\n\n> It might be useful in the future to get this functionality back,\n> there's no reason we won't have a read_tree_recursive() use-case that\n> would want to start in a subdirectory.\n\nThis paragraph is very hard to parse.\n\n> But if and when that happens we can just add something like a\n> read_tree_recursive_subdir() and have both read_tree_recursive() and\n> that function be a thin wrapper for read_tree_1().\n\nStarting with \"But if and when that happens\" suggests this shouldn't\nbe an independent paragraph.\n\n> In the meantime there's no reason to keep around what amounts to dead\n> code just in case we need it in the future.\n\nMakes sense to me.\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  archive.c          | 8 ++++----\n>  builtin/checkout.c | 2 +-\n>  builtin/log.c      | 4 ++--\n>  builtin/ls-files.c | 2 +-\n>  builtin/ls-tree.c  | 2 +-\n>  merge-recursive.c  | 2 +-\n>  tree.c             | 2 --\n>  tree.h             | 1 -\n>  8 files changed, 10 insertions(+), 13 deletions(-)\n>\n> diff --git a/archive.c b/archive.c\n> index 5919d9e5050..9394f170f7f 100644\n> --- a/archive.c\n> +++ b/archive.c\n> @@ -316,8 +316,8 @@ int write_archive_entries(struct archiver_args *args,\n>                 git_attr_set_direction(GIT_ATTR_INDEX);\n>         }\n>\n> -       err = read_tree_recursive(args->repo, args->tree, \"\",\n> -                                 0, 0, &args->pathspec,\n> +       err = read_tree_recursive(args->repo, args->tree,\n> +                                 0, &args->pathspec,\n>                                   queue_or_write_archive_entry,\n>                                   &context);\n>         if (err == READ_TREE_RECURSIVE)\n> @@ -405,8 +405,8 @@ static int path_exists(struct archiver_args *args, const char *path)\n>         ctx.args = args;\n>         parse_pathspec(&ctx.pathspec, 0, 0, \"\", paths);\n>         ctx.pathspec.recursive = 1;\n> -       ret = read_tree_recursive(args->repo, args->tree, \"\",\n> -                                 0, 0, &ctx.pathspec,\n> +       ret = read_tree_recursive(args->repo, args->tree,\n> +                                 0, &ctx.pathspec,\n>                                   reject_entry, &ctx);\n>         clear_pathspec(&ctx.pathspec);\n>         return ret != 0;\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 2d6550bc3c8..21b742c0f07 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -155,7 +155,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,\n>\n>  static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)\n>  {\n> -       read_tree_recursive(the_repository, tree, \"\", 0, 0,\n> +       read_tree_recursive(the_repository, tree, 0,\n>                             pathspec, update_some, NULL);\n>\n>         /* update the index with the given tree's info\n> diff --git a/builtin/log.c b/builtin/log.c\n> index f67b67d80ed..ffa3fb8c286 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -681,8 +681,8 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n>                                         diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n>                                         name,\n>                                         diff_get_color_opt(&rev.diffopt, DIFF_RESET));\n> -                       read_tree_recursive(the_repository, (struct tree *)o, \"\",\n> -                                           0, 0, &match_all, show_tree_object,\n> +                       read_tree_recursive(the_repository, (struct tree *)o,\n> +                                           0, &match_all, show_tree_object,\n>                                             rev.diffopt.file);\n>                         rev.shown_one = 1;\n>                         break;\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index c0349a7b206..2c609428eea 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -483,7 +483,7 @@ void overlay_tree_on_index(struct index_state *istate,\n>                                PATHSPEC_PREFER_CWD, prefix, matchbuf);\n>         } else\n>                 memset(&pathspec, 0, sizeof(pathspec));\n> -       if (read_tree_recursive(the_repository, tree, \"\", 0, 1,\n> +       if (read_tree_recursive(the_repository, tree, 1,\n>                                 &pathspec, read_one_entry_quick, istate))\n>                 die(\"unable to read tree entries %s\", tree_name);\n>\n> diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\n> index 7cad3f24ebd..7d3fb2e6d0f 100644\n> --- a/builtin/ls-tree.c\n> +++ b/builtin/ls-tree.c\n> @@ -185,6 +185,6 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n>         tree = parse_tree_indirect(&oid);\n>         if (!tree)\n>                 die(\"not a tree object\");\n> -       return !!read_tree_recursive(the_repository, tree, \"\", 0, 0,\n> +       return !!read_tree_recursive(the_repository, tree, 0,\n>                                      &pathspec, show_tree, NULL);\n>  }\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index b052974f191..fa7602ff0f2 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -473,7 +473,7 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n>  {\n>         struct pathspec match_all;\n>         memset(&match_all, 0, sizeof(match_all));\n> -       read_tree_recursive(opt->repo, tree, \"\", 0, 0,\n> +       read_tree_recursive(opt->repo, tree, 0,\n>                             &match_all, save_files_dirs, opt);\n>  }\n>\n> diff --git a/tree.c b/tree.c\n> index c1bde9314d0..285633892c2 100644\n> --- a/tree.c\n> +++ b/tree.c\n> @@ -82,14 +82,12 @@ static int read_tree_1(struct repository *r,\n>\n>  int read_tree_recursive(struct repository *r,\n>                         struct tree *tree,\n> -                       const char *base, int baselen,\n>                         int stage, const struct pathspec *pathspec,\n>                         read_tree_fn_t fn, void *context)\n>  {\n>         struct strbuf sb = STRBUF_INIT;\n>         int ret;\n>\n> -       strbuf_add(&sb, base, baselen);\n>         ret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context);\n>         strbuf_release(&sb);\n>         return ret;\n> diff --git a/tree.h b/tree.h\n> index 34549c86c9f..9a0fd3221e3 100644\n> --- a/tree.h\n> +++ b/tree.h\n> @@ -33,7 +33,6 @@ typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const c\n>\n>  int read_tree_recursive(struct repository *r,\n>                         struct tree *tree,\n> -                       const char *base, int baselen,\n>                         int stage, const struct pathspec *pathspec,\n>                         read_tree_fn_t fn, void *context);\n>\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n\nLooks good.\n"},{"id":"418411","messageId":"CABPp-BHTPmW+uwHum7p0PsWbMpM2uGX7CP=KoZ+W3PRzxjQxWw@mail.gmail.com","threadId":"18927","inReplyTo":"20210306193458.20633-1-avarab@gmail.com","subject":"Re: [PATCH 0/7] Move the read_tree() function to its only user","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-06T22:06:37Z","receivedAt":"2021-03-06T22:07:44Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sat, Mar 6, 2021 at 11:35 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> This is a small cleanup series to remove move the last user of\n> read_tree() over to read_tree_recursive(), and while we're at it\n> adjust the API of read_tree_fn_t to its current use-case.\n>\n> I found out after writing this that there had been a FIXME comment\n> (never made it into git.git) about this from mid-2009:\n> https://lore.kernel.org/git/1240014568-3675-1-git-send-email-pclouds@gmail.com/\n>\n> Ævar Arnfjörð Bjarmason (7):\n>   tree.c API: move read_tree() into builtin/ls-files.c\n>   ls-files: don't needlessly pass around stage variable\n>   ls-files: remove cache juggling + sorting\n>   merge-ort: move cmp_cache_name_compare() from tree.c\n>   ls-files: refactor read_one_entry_quick() to use a strbuf\n>   tree.h API: remove support for starting at prefix != \"\"\n>   tree.h API: remove \"stage\" parameter from read_tree_recursive()\n\nI read over all 7 patches.  For the most part, they look good.\n\nSome of the wording in the commit message for patch 6 seemed confusing\nand could use some touch-ups; it may also be worth documenting when\nthe code stopped having any callers with a base other than \"\".\n\nFor patch 3 (the actual removal of read_tree()), either I'm\nmisunderstanding something, or the changes look problematic and\nunsafe.  I'm hoping it's the former.\n"},{"id":"418427","messageId":"20210308022138.28166-3-avarab@gmail.com","threadId":"18927","inReplyTo":"20210306193458.20633-1-avarab@gmail.com","subject":"[PATCH v2 2/6] tree.c API: move read_tree() into builtin/ls-files.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T02:21:34Z","receivedAt":"2021-03-08T02:22:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since the read_tree() API was added around the same time as\nread_tree_recursive() in 94537c78a82 (Move \"read_tree()\" to\n\"tree.c\"[...], 2005-04-22) and b12ec373b8e ([PATCH] Teach read-tree\nabout commit objects, 2005-04-20) things have gradually migrated over\nto the read_tree_recursive() version.\n\nNow builtin/ls-files.c is the last user of this code, let's move all\nthe relevant code there. This allows for subsequent simplification of\nit, and an eventual move to read_tree_recursive().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++\n tree.c             | 89 ---------------------------------------------\n tree.h             |  5 ---\n 3 files changed, 91 insertions(+), 94 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex f6f9e483b27..a4458622813 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -12,6 +12,7 @@\n #include \"dir.h\"\n #include \"builtin.h\"\n #include \"tree.h\"\n+#include \"cache-tree.h\"\n #include \"parse-options.h\"\n #include \"resolve-undo.h\"\n #include \"string-list.h\"\n@@ -420,6 +421,96 @@ static int get_common_prefix_len(const char *common_prefix)\n \treturn common_prefix_len;\n }\n \n+static int read_one_entry_opt(struct index_state *istate,\n+\t\t\t      const struct object_id *oid,\n+\t\t\t      const char *base, int baselen,\n+\t\t\t      const char *pathname,\n+\t\t\t      unsigned mode, int stage, int opt)\n+{\n+\tint len;\n+\tstruct cache_entry *ce;\n+\n+\tif (S_ISDIR(mode))\n+\t\treturn READ_TREE_RECURSIVE;\n+\n+\tlen = strlen(pathname);\n+\tce = make_empty_cache_entry(istate, baselen + len);\n+\n+\tce->ce_mode = create_ce_mode(mode);\n+\tce->ce_flags = create_ce_flags(stage);\n+\tce->ce_namelen = baselen + len;\n+\tmemcpy(ce->name, base, baselen);\n+\tmemcpy(ce->name + baselen, pathname, len+1);\n+\toidcpy(&ce->oid, oid);\n+\treturn add_index_entry(istate, ce, opt);\n+}\n+\n+static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n+\t\t\t  const char *pathname, unsigned mode, int stage,\n+\t\t\t  void *context)\n+{\n+\tstruct index_state *istate = context;\n+\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\t\t\t\t  mode, stage,\n+\t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n+}\n+\n+/*\n+ * This is used when the caller knows there is no existing entries at\n+ * the stage that will conflict with the entry being added.\n+ */\n+static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n+\t\t\t\tconst char *pathname, unsigned mode, int stage,\n+\t\t\t\tvoid *context)\n+{\n+\tstruct index_state *istate = context;\n+\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\t\t\t\t  mode, stage,\n+\t\t\t\t  ADD_CACHE_JUST_APPEND);\n+}\n+\n+\n+static int read_tree(struct repository *r, struct tree *tree, int stage,\n+\t\t     struct pathspec *match, struct index_state *istate)\n+{\n+\tread_tree_fn_t fn = NULL;\n+\tint i, err;\n+\n+\t/*\n+\t * Currently the only existing callers of this function all\n+\t * call it with stage=1 and after making sure there is nothing\n+\t * at that stage; we could always use read_one_entry_quick().\n+\t *\n+\t * But when we decide to straighten out git-read-tree not to\n+\t * use unpack_trees() in some cases, this will probably start\n+\t * to matter.\n+\t */\n+\n+\t/*\n+\t * See if we have cache entry at the stage.  If so,\n+\t * do it the original slow way, otherwise, append and then\n+\t * sort at the end.\n+\t */\n+\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n+\t\tconst struct cache_entry *ce = istate->cache[i];\n+\t\tif (ce_stage(ce) == stage)\n+\t\t\tfn = read_one_entry;\n+\t}\n+\n+\tif (!fn)\n+\t\tfn = read_one_entry_quick;\n+\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n+\tif (fn == read_one_entry || err)\n+\t\treturn err;\n+\n+\t/*\n+\t * Sort the cache entry -- we need to nuke the cache tree, though.\n+\t */\n+\tcache_tree_free(&istate->cache_tree);\n+\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n+\treturn 0;\n+}\n+\n /*\n  * Read the tree specified with --with-tree option\n  * (typically, HEAD) into stage #1 and then\ndiff --git a/tree.c b/tree.c\nindex a52479812ce..a6c12f2745a 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -11,54 +11,6 @@\n \n const char *tree_type = \"tree\";\n \n-static int read_one_entry_opt(struct index_state *istate,\n-\t\t\t      const struct object_id *oid,\n-\t\t\t      const char *base, int baselen,\n-\t\t\t      const char *pathname,\n-\t\t\t      unsigned mode, int stage, int opt)\n-{\n-\tint len;\n-\tstruct cache_entry *ce;\n-\n-\tif (S_ISDIR(mode))\n-\t\treturn READ_TREE_RECURSIVE;\n-\n-\tlen = strlen(pathname);\n-\tce = make_empty_cache_entry(istate, baselen + len);\n-\n-\tce->ce_mode = create_ce_mode(mode);\n-\tce->ce_flags = create_ce_flags(stage);\n-\tce->ce_namelen = baselen + len;\n-\tmemcpy(ce->name, base, baselen);\n-\tmemcpy(ce->name + baselen, pathname, len+1);\n-\toidcpy(&ce->oid, oid);\n-\treturn add_index_entry(istate, ce, opt);\n-}\n-\n-static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\t  const char *pathname, unsigned mode, int stage,\n-\t\t\t  void *context)\n-{\n-\tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode, stage,\n-\t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n-}\n-\n-/*\n- * This is used when the caller knows there is no existing entries at\n- * the stage that will conflict with the entry being added.\n- */\n-static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n-\t\t\t\tconst char *pathname, unsigned mode, int stage,\n-\t\t\t\tvoid *context)\n-{\n-\tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode, stage,\n-\t\t\t\t  ADD_CACHE_JUST_APPEND);\n-}\n-\n static int read_tree_1(struct repository *r,\n \t\t       struct tree *tree, struct strbuf *base,\n \t\t       int stage, const struct pathspec *pathspec,\n@@ -154,47 +106,6 @@ int cmp_cache_name_compare(const void *a_, const void *b_)\n \t\t\t\t  ce2->name, ce2->ce_namelen, ce_stage(ce2));\n }\n \n-int read_tree(struct repository *r, struct tree *tree, int stage,\n-\t      struct pathspec *match, struct index_state *istate)\n-{\n-\tread_tree_fn_t fn = NULL;\n-\tint i, err;\n-\n-\t/*\n-\t * Currently the only existing callers of this function all\n-\t * call it with stage=1 and after making sure there is nothing\n-\t * at that stage; we could always use read_one_entry_quick().\n-\t *\n-\t * But when we decide to straighten out git-read-tree not to\n-\t * use unpack_trees() in some cases, this will probably start\n-\t * to matter.\n-\t */\n-\n-\t/*\n-\t * See if we have cache entry at the stage.  If so,\n-\t * do it the original slow way, otherwise, append and then\n-\t * sort at the end.\n-\t */\n-\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n-\t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == stage)\n-\t\t\tfn = read_one_entry;\n-\t}\n-\n-\tif (!fn)\n-\t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n-\tif (fn == read_one_entry || err)\n-\t\treturn err;\n-\n-\t/*\n-\t * Sort the cache entry -- we need to nuke the cache tree, though.\n-\t */\n-\tcache_tree_free(&istate->cache_tree);\n-\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n-\treturn 0;\n-}\n-\n struct tree *lookup_tree(struct repository *r, const struct object_id *oid)\n {\n \tstruct object *obj = lookup_object(r, oid);\ndiff --git a/tree.h b/tree.h\nindex 3eb0484cbf2..6b0b1dc211a 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -38,9 +38,4 @@ int read_tree_recursive(struct repository *r,\n \t\t\tconst char *base, int baselen,\n \t\t\tint stage, const struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context);\n-\n-int read_tree(struct repository *r, struct tree *tree,\n-\t      int stage, struct pathspec *pathspec,\n-\t      struct index_state *istate);\n-\n #endif /* TREE_H */\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418426","messageId":"20210308022138.28166-2-avarab@gmail.com","threadId":"18927","inReplyTo":"20210306193458.20633-1-avarab@gmail.com","subject":"[PATCH v2 1/6] ls-files tests: add meaningful --with-tree tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T02:21:33Z","receivedAt":"2021-03-08T02:22:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add tests for \"ls-files --with-tree\". There was effectively no\ncoverage for any normal usage of this command, only the tests added in\n54e1abce90e (Add test case for ls-files --with-tree, 2007-10-03) for\nan obscure bug.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3060-ls-files-with-tree.sh | 41 +++++++++++++++++++++++++++++++++++\n 1 file changed, 41 insertions(+)\n\ndiff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh\nindex 52ed665fcd2..b257c792a46 100755\n--- a/t/t3060-ls-files-with-tree.sh\n+++ b/t/t3060-ls-files-with-tree.sh\n@@ -47,6 +47,12 @@ test_expect_success setup '\n \tgit add .\n '\n \n+test_expect_success 'usage' '\n+\ttest_expect_code 128 git ls-files --with-tree=HEAD -u &&\n+\ttest_expect_code 128 git ls-files --with-tree=HEAD -s &&\n+\ttest_expect_code 128 git ls-files --recurse-submodules --with-tree=HEAD\n+'\n+\n test_expect_success 'git ls-files --with-tree should succeed from subdir' '\n \t# We have to run from a sub-directory to trigger prune_path\n \t# Then we finally get to run our --with-tree test\n@@ -60,4 +66,39 @@ test_expect_success \\\n     'git ls-files --with-tree should add entries from named tree.' \\\n     'test_cmp expected output'\n \n+test_expect_success 'no duplicates in --with-tree output' '\n+\tgit ls-files --with-tree=HEAD >actual &&\n+\tsort -u actual >expected &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'setup: output in a conflict' '\n+\ttest_create_repo conflict &&\n+\ttest_commit -C conflict BASE file &&\n+\ttest_commit -C conflict A file foo &&\n+\tgit -C conflict reset --hard BASE &&\n+\ttest_commit -C conflict B file bar\n+'\n+\n+test_expect_success 'output in a conflict' '\n+\ttest_must_fail git -C conflict merge A B &&\n+\tcat >expected <<-\\EOF &&\n+\tfile\n+\tfile\n+\tfile\n+\tfile\n+\tEOF\n+\tgit -C conflict ls-files --with-tree=HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'output with removed .git/index' '\n+\tcat >expected <<-\\EOF &&\n+\tfile\n+\tEOF\n+\trm conflict/.git/index &&\n+\tgit -C conflict ls-files --with-tree=HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418428","messageId":"20210308022138.28166-4-avarab@gmail.com","threadId":"18927","inReplyTo":"20210306193458.20633-1-avarab@gmail.com","subject":"[PATCH v2 3/6] ls-files: don't needlessly pass around stage variable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T02:21:35Z","receivedAt":"2021-03-08T02:22:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that read_tree() has been moved to ls-files.c we can get rid of\nthe stage != 1 case that'll never happen.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 17 ++++-------------\n 1 file changed, 4 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex a4458622813..74d572a3e4a 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -470,21 +470,12 @@ static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base\n }\n \n \n-static int read_tree(struct repository *r, struct tree *tree, int stage,\n+static int read_tree(struct repository *r, struct tree *tree,\n \t\t     struct pathspec *match, struct index_state *istate)\n {\n \tread_tree_fn_t fn = NULL;\n \tint i, err;\n \n-\t/*\n-\t * Currently the only existing callers of this function all\n-\t * call it with stage=1 and after making sure there is nothing\n-\t * at that stage; we could always use read_one_entry_quick().\n-\t *\n-\t * But when we decide to straighten out git-read-tree not to\n-\t * use unpack_trees() in some cases, this will probably start\n-\t * to matter.\n-\t */\n \n \t/*\n \t * See if we have cache entry at the stage.  If so,\n@@ -493,13 +484,13 @@ static int read_tree(struct repository *r, struct tree *tree, int stage,\n \t */\n \tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n \t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == stage)\n+\t\tif (ce_stage(ce) == 1)\n \t\t\tfn = read_one_entry;\n \t}\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n+\terr = read_tree_recursive(r, tree, \"\", 0, 1, match, fn, istate);\n \tif (fn == read_one_entry || err)\n \t\treturn err;\n \n@@ -549,7 +540,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n \t} else\n \t\tmemset(&pathspec, 0, sizeof(pathspec));\n-\tif (read_tree(the_repository, tree, 1, &pathspec, istate))\n+\tif (read_tree(the_repository, tree, &pathspec, istate))\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \n \tfor (i = 0; i < istate->cache_nr; i++) {\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418429","messageId":"20210308022138.28166-1-avarab@gmail.com","threadId":"18927","inReplyTo":"20210306193458.20633-1-avarab@gmail.com","subject":"[PATCH v2 0/6] Move the read_tree() function to its only user","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T02:21:32Z","receivedAt":"2021-03-08T02:22:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"v1 of this series over-removed code supporting the \"ls-files\n--with-tree=*\" parameter. In v2 there's no change to its behavior,\njust refactoring away of read_tree() from the tree.c API and the\ncleanup of read_tree_recursive(). Thanks to Elijah for spotting that.\n\nI've added a test at the start of this series that would have caught\nthat regression in v1 (and more).\n\n1. https://lore.kernel.org/git/CABPp-BF982muRS4GO=zYegvetQyrPMwaEM3uEBvcbPRP=krfmQ@mail.gmail.com/\n\nÆvar Arnfjörð Bjarmason (6):\n  ls-files tests: add meaningful --with-tree tests\n  tree.c API: move read_tree() into builtin/ls-files.c\n  ls-files: don't needlessly pass around stage variable\n  ls-files: refactor away read_tree()\n  tree.h API: remove support for starting at prefix != \"\"\n  tree.h API: remove \"stage\" parameter from read_tree_recursive()\n\n archive.c                     |  13 +++--\n builtin/checkout.c            |   4 +-\n builtin/log.c                 |   6 +-\n builtin/ls-files.c            |  76 ++++++++++++++++++++++++-\n builtin/ls-tree.c             |   4 +-\n merge-recursive.c             |   4 +-\n t/t3060-ls-files-with-tree.sh |  41 ++++++++++++++\n tree.c                        | 101 ++--------------------------------\n tree.h                        |  10 +---\n 9 files changed, 139 insertions(+), 120 deletions(-)\n\nRange-diff:\n-:  ----------- > 1:  6416da0dee2 ls-files tests: add meaningful --with-tree tests\n1:  020534164d3 ! 2:  765001b44cd tree.c API: move read_tree() into builtin/ls-files.c\n    @@ tree.c: int cmp_cache_name_compare(const void *a_, const void *b_)\n     \n      ## tree.h ##\n     @@ tree.h: int read_tree_recursive(struct repository *r,\n    + \t\t\tconst char *base, int baselen,\n      \t\t\tint stage, const struct pathspec *pathspec,\n      \t\t\tread_tree_fn_t fn, void *context);\n    - \n    +-\n     -int read_tree(struct repository *r, struct tree *tree,\n     -\t      int stage, struct pathspec *pathspec,\n     -\t      struct index_state *istate);\n2:  6aa6ba2fbb5 = 3:  a71ffba7d04 ls-files: don't needlessly pass around stage variable\n3:  4f27e5d2970 ! 4:  e78d1810b89 ls-files: remove cache juggling + sorting\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    ls-files: remove cache juggling + sorting\n    +    ls-files: refactor away read_tree()\n     \n    -    Remove the \"ce_stage(ce) == 1\" and \"Sort the cache entry\" code from\n    -    read_tree(), which allows us to remove the function entirely and move\n    -    over to read_tree_recursive().\n    +    Refactor away the read_tree() function into its only user,\n    +    overlay_tree_on_index().\n     \n    -    I don't think the \"Sort the cached entry\" code was needed here, see\n    -    af3785dc5a7 (Optimize \"diff --cached\" performance., 2007-08-09) for\n    -    the use-case it was intended for. The only user of this code is\n    -    \"ls-files --with-tree\", which isn't the sort of use-case that needs to\n    -    care about \"ce_stage(ce) != 0\" or sorting tree entries.\n    +    First, change read_one_entry_opt() to use the strbuf parameter\n    +    read_tree_recursive() passes down in place. This finishes up a partial\n    +    refactoring started in 6a0b0b6de99 (tree.c: update read_tree_recursive\n    +    callback to pass strbuf as base, 2014-11-30).\n    +\n    +    Moving the rest into overlay_tree_on_index() makes this index juggling\n    +    we're doing easier to read.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/ls-files.c ##\n    -@@\n    - #include \"dir.h\"\n    - #include \"builtin.h\"\n    - #include \"tree.h\"\n    --#include \"cache-tree.h\"\n    - #include \"parse-options.h\"\n    - #include \"resolve-undo.h\"\n    - #include \"string-list.h\"\n     @@ builtin/ls-files.c: static int get_common_prefix_len(const char *common_prefix)\n    - \treturn common_prefix_len;\n    - }\n      \n    --static int read_one_entry_opt(struct index_state *istate,\n    --\t\t\t      const struct object_id *oid,\n    + static int read_one_entry_opt(struct index_state *istate,\n    + \t\t\t      const struct object_id *oid,\n     -\t\t\t      const char *base, int baselen,\n    --\t\t\t      const char *pathname,\n    --\t\t\t      unsigned mode, int stage, int opt)\n    -+static int read_one_entry_quick(const struct object_id *oid,\n    -+\t\t\t\tstruct strbuf *basebuf,\n    -+\t\t\t\tconst char *pathname,\n    -+\t\t\t\tunsigned mode,\n    -+\t\t\t\tint stage, void *context)\n    ++\t\t\t      struct strbuf *base,\n    + \t\t\t      const char *pathname,\n    + \t\t\t      unsigned mode, int stage, int opt)\n      {\n    -+\tstruct index_state *istate = context;\n    -+\tconst char *base = basebuf->buf;\n    -+\tconst int baselen = basebuf->len;\n    - \tint len;\n    - \tstruct cache_entry *ce;\n    - \n     @@ builtin/ls-files.c: static int read_one_entry_opt(struct index_state *istate,\n    - \tmemcpy(ce->name, base, baselen);\n    - \tmemcpy(ce->name + baselen, pathname, len+1);\n    + \t\treturn READ_TREE_RECURSIVE;\n    + \n    + \tlen = strlen(pathname);\n    +-\tce = make_empty_cache_entry(istate, baselen + len);\n    ++\tce = make_empty_cache_entry(istate, base->len + len);\n    + \n    + \tce->ce_mode = create_ce_mode(mode);\n    + \tce->ce_flags = create_ce_flags(stage);\n    +-\tce->ce_namelen = baselen + len;\n    +-\tmemcpy(ce->name, base, baselen);\n    +-\tmemcpy(ce->name + baselen, pathname, len+1);\n    ++\tce->ce_namelen = base->len + len;\n    ++\tmemcpy(ce->name, base->buf, base->len);\n    ++\tmemcpy(ce->name + base->len, pathname, len+1);\n      \toidcpy(&ce->oid, oid);\n    --\treturn add_index_entry(istate, ce, opt);\n    --}\n    --\n    --static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n    --\t\t\t  const char *pathname, unsigned mode, int stage,\n    --\t\t\t  void *context)\n    --{\n    --\tstruct index_state *istate = context;\n    + \treturn add_index_entry(istate, ce, opt);\n    + }\n    +@@ builtin/ls-files.c: static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n    + \t\t\t  void *context)\n    + {\n    + \tstruct index_state *istate = context;\n     -\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n    --\t\t\t\t  mode, stage,\n    --\t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n    --}\n    --\n    --/*\n    -- * This is used when the caller knows there is no existing entries at\n    -- * the stage that will conflict with the entry being added.\n    -- */\n    --static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n    --\t\t\t\tconst char *pathname, unsigned mode, int stage,\n    --\t\t\t\tvoid *context)\n    --{\n    --\tstruct index_state *istate = context;\n    ++\treturn read_one_entry_opt(istate, oid, base, pathname,\n    + \t\t\t\t  mode, stage,\n    + \t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n    + }\n    +@@ builtin/ls-files.c: static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base\n    + \t\t\t\tvoid *context)\n    + {\n    + \tstruct index_state *istate = context;\n     -\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n    --\t\t\t\t  mode, stage,\n    --\t\t\t\t  ADD_CACHE_JUST_APPEND);\n    --}\n    --\n    ++\treturn read_one_entry_opt(istate, oid, base, pathname,\n    + \t\t\t\t  mode, stage,\n    + \t\t\t\t  ADD_CACHE_JUST_APPEND);\n    + }\n    + \n     -\n     -static int read_tree(struct repository *r, struct tree *tree,\n     -\t\t     struct pathspec *match, struct index_state *istate)\n    @@ builtin/ls-files.c: static int read_one_entry_opt(struct index_state *istate,\n     -\tcache_tree_free(&istate->cache_tree);\n     -\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n     -\treturn 0;\n    -+\treturn add_index_entry(istate, ce, ADD_CACHE_JUST_APPEND);\n    - }\n    - \n    +-}\n    +-\n      /*\n    +  * Read the tree specified with --with-tree option\n    +  * (typically, HEAD) into stage #1 and then\n    +@@ builtin/ls-files.c: void overlay_tree_on_index(struct index_state *istate,\n    + \tstruct pathspec pathspec;\n    + \tstruct cache_entry *last_stage0 = NULL;\n    + \tint i;\n    ++\tread_tree_fn_t fn = NULL;\n    ++\tint err;\n    + \n    + \tif (get_oid(tree_name, &oid))\n    + \t\tdie(\"tree-ish %s not found.\", tree_name);\n     @@ builtin/ls-files.c: void overlay_tree_on_index(struct index_state *istate,\n      \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n      \t} else\n      \t\tmemset(&pathspec, 0, sizeof(pathspec));\n     -\tif (read_tree(the_repository, tree, &pathspec, istate))\n    -+\tif (read_tree_recursive(the_repository, tree, \"\", 0, 1,\n    -+\t\t\t\t&pathspec, read_one_entry_quick, istate))\n    ++\n    ++\t/*\n    ++\t * See if we have cache entry at the stage.  If so,\n    ++\t * do it the original slow way, otherwise, append and then\n    ++\t * sort at the end.\n    ++\t */\n    ++\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n    ++\t\tconst struct cache_entry *ce = istate->cache[i];\n    ++\t\tif (ce_stage(ce) == 1)\n    ++\t\t\tfn = read_one_entry;\n    ++\t}\n    ++\n    ++\tif (!fn)\n    ++\t\tfn = read_one_entry_quick;\n    ++\terr = read_tree_recursive(the_repository, tree, \"\", 0, 1, &pathspec, fn, istate);\n    ++\tif (err)\n      \t\tdie(\"unable to read tree entries %s\", tree_name);\n      \n    ++\t/*\n    ++\t * Sort the cache entry -- we need to nuke the cache tree, though.\n    ++\t */\n    ++\tif (fn == read_one_entry_quick) {\n    ++\t\tcache_tree_free(&istate->cache_tree);\n    ++\t\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n    ++\t}\n    ++\n      \tfor (i = 0; i < istate->cache_nr; i++) {\n    + \t\tstruct cache_entry *ce = istate->cache[i];\n    + \t\tswitch (ce_stage(ce)) {\n4:  33810d3c10c < -:  ----------- merge-ort: move cmp_cache_name_compare() from tree.c\n5:  fb10246b85b < -:  ----------- ls-files: refactor read_one_entry_quick() to use a strbuf\n6:  0c065615aec ! 5:  05eecdd7519 tree.h API: remove support for starting at prefix != \"\"\n    @@ Metadata\n      ## Commit message ##\n         tree.h API: remove support for starting at prefix != \"\"\n     \n    -    Every caller or the read_tree_recursive() function hardcoded a\n    +    Every caller of the read_tree_recursive() function hardcoded a\n         starting point of \"\" in the tree. So let's simply remove that\n         parameter.\n     \n    -    It might be useful in the future to get this functionality back,\n    -    there's no reason we won't have a read_tree_recursive() use-case that\n    -    would want to start in a subdirectory.\n    +    The last function to call read_tree_recursive() with a non-\"\" path was\n    +    read_tree_recursive() itself, but that was changed in\n    +    ffd31f661d5 (Reimplement read_tree_recursive() using\n    +    tree_entry_interesting(), 2011-03-25).\n     \n    -    But if and when that happens we can just add something like a\n    -    read_tree_recursive_subdir() and have both read_tree_recursive() and\n    -    that function be a thin wrapper for read_tree_1().\n    +    If in the future we need to support recursively reading trees without\n    +    starting at the root we can easily add a read_tree_recursive_subdir(),\n    +    and make that function a thin wrapper for read_tree_1().\n     \n         In the meantime there's no reason to keep around what amounts to dead\n    -    code just in case we need it in the future.\n    +    code, just in case we need it in the future.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ builtin/log.c: int cmd_show(int argc, const char **argv, const char *prefix)\n     \n      ## builtin/ls-files.c ##\n     @@ builtin/ls-files.c: void overlay_tree_on_index(struct index_state *istate,\n    - \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n    - \t} else\n    - \t\tmemset(&pathspec, 0, sizeof(pathspec));\n    --\tif (read_tree_recursive(the_repository, tree, \"\", 0, 1,\n    -+\tif (read_tree_recursive(the_repository, tree, 1,\n    - \t\t\t\t&pathspec, read_one_entry_quick, istate))\n    + \n    + \tif (!fn)\n    + \t\tfn = read_one_entry_quick;\n    +-\terr = read_tree_recursive(the_repository, tree, \"\", 0, 1, &pathspec, fn, istate);\n    ++\terr = read_tree_recursive(the_repository, tree, 1, &pathspec, fn, istate);\n    + \tif (err)\n      \t\tdie(\"unable to read tree entries %s\", tree_name);\n      \n     \n    @@ tree.h: typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *,\n     -\t\t\tconst char *base, int baselen,\n      \t\t\tint stage, const struct pathspec *pathspec,\n      \t\t\tread_tree_fn_t fn, void *context);\n    - \n    + #endif /* TREE_H */\n7:  9685c7c5c50 ! 6:  fcecc82e1c8 tree.h API: remove \"stage\" parameter from read_tree_recursive()\n    @@ builtin/log.c: int cmd_show(int argc, const char **argv, const char *prefix)\n      \t\t\tbreak;\n     \n      ## builtin/ls-files.c ##\n    -@@ builtin/ls-files.c: static int read_one_entry_quick(const struct object_id *oid,\n    - \t\t\t\tstruct strbuf *base,\n    - \t\t\t\tconst char *pathname,\n    - \t\t\t\tunsigned mode,\n    --\t\t\t\tint stage, void *context)\n    -+\t\t\t\tvoid *context)\n    +@@ builtin/ls-files.c: static int read_one_entry_opt(struct index_state *istate,\n    + \t\t\t      const struct object_id *oid,\n    + \t\t\t      struct strbuf *base,\n    + \t\t\t      const char *pathname,\n    +-\t\t\t      unsigned mode, int stage, int opt)\n    ++\t\t\t      unsigned mode, int opt)\n      {\n    - \tstruct index_state *istate = context;\n      \tint len;\n    -@@ builtin/ls-files.c: static int read_one_entry_quick(const struct object_id *oid,\n    + \tstruct cache_entry *ce;\n    +@@ builtin/ls-files.c: static int read_one_entry_opt(struct index_state *istate,\n      \tce = make_empty_cache_entry(istate, base->len + len);\n      \n      \tce->ce_mode = create_ce_mode(mode);\n    @@ builtin/ls-files.c: static int read_one_entry_quick(const struct object_id *oid,\n      \tce->ce_namelen = base->len + len;\n      \tmemcpy(ce->name, base->buf, base->len);\n      \tmemcpy(ce->name + base->len, pathname, len+1);\n    +@@ builtin/ls-files.c: static int read_one_entry_opt(struct index_state *istate,\n    + }\n    + \n    + static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n    +-\t\t\t  const char *pathname, unsigned mode, int stage,\n    ++\t\t\t  const char *pathname, unsigned mode,\n    + \t\t\t  void *context)\n    + {\n    + \tstruct index_state *istate = context;\n    + \treturn read_one_entry_opt(istate, oid, base, pathname,\n    +-\t\t\t\t  mode, stage,\n    ++\t\t\t\t  mode,\n    + \t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n    + }\n    + \n    +@@ builtin/ls-files.c: static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n    +  * the stage that will conflict with the entry being added.\n    +  */\n    + static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n    +-\t\t\t\tconst char *pathname, unsigned mode, int stage,\n    ++\t\t\t\tconst char *pathname, unsigned mode,\n    + \t\t\t\tvoid *context)\n    + {\n    + \tstruct index_state *istate = context;\n    + \treturn read_one_entry_opt(istate, oid, base, pathname,\n    +-\t\t\t\t  mode, stage,\n    ++\t\t\t\t  mode,\n    + \t\t\t\t  ADD_CACHE_JUST_APPEND);\n    + }\n    + \n     @@ builtin/ls-files.c: void overlay_tree_on_index(struct index_state *istate,\n    - \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n    - \t} else\n    - \t\tmemset(&pathspec, 0, sizeof(pathspec));\n    --\tif (read_tree_recursive(the_repository, tree, 1,\n    -+\tif (read_tree_recursive(the_repository, tree,\n    - \t\t\t\t&pathspec, read_one_entry_quick, istate))\n    + \n    + \tif (!fn)\n    + \t\tfn = read_one_entry_quick;\n    +-\terr = read_tree_recursive(the_repository, tree, 1, &pathspec, fn, istate);\n    ++\terr = read_tree_recursive(the_repository, tree, &pathspec, fn, istate);\n    + \tif (err)\n      \t\tdie(\"unable to read tree entries %s\", tree_name);\n      \n     \n    @@ tree.c: static int read_tree_1(struct repository *r,\n      }\n     \n      ## tree.h ##\n    -@@ tree.h: void free_tree_buffer(struct tree *tree);\n    - struct tree *parse_tree_indirect(const struct object_id *oid);\n    +@@ tree.h: struct tree *parse_tree_indirect(const struct object_id *oid);\n    + int cmp_cache_name_compare(const void *a_, const void *b_);\n      \n      #define READ_TREE_RECURSIVE 1\n     -typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, int, void *);\n    @@ tree.h: void free_tree_buffer(struct tree *tree);\n     -\t\t\tint stage, const struct pathspec *pathspec,\n     +\t\t\tconst struct pathspec *pathspec,\n      \t\t\tread_tree_fn_t fn, void *context);\n    - \n      #endif /* TREE_H */\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418430","messageId":"20210308022138.28166-6-avarab@gmail.com","threadId":"18927","inReplyTo":"20210306193458.20633-1-avarab@gmail.com","subject":"[PATCH v2 5/6] tree.h API: remove support for starting at prefix != \"\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T02:21:37Z","receivedAt":"2021-03-08T02:22:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Every caller of the read_tree_recursive() function hardcoded a\nstarting point of \"\" in the tree. So let's simply remove that\nparameter.\n\nThe last function to call read_tree_recursive() with a non-\"\" path was\nread_tree_recursive() itself, but that was changed in\nffd31f661d5 (Reimplement read_tree_recursive() using\ntree_entry_interesting(), 2011-03-25).\n\nIf in the future we need to support recursively reading trees without\nstarting at the root we can easily add a read_tree_recursive_subdir(),\nand make that function a thin wrapper for read_tree_1().\n\nIn the meantime there's no reason to keep around what amounts to dead\ncode, just in case we need it in the future.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          | 8 ++++----\n builtin/checkout.c | 2 +-\n builtin/log.c      | 4 ++--\n builtin/ls-files.c | 2 +-\n builtin/ls-tree.c  | 2 +-\n merge-recursive.c  | 2 +-\n tree.c             | 2 --\n tree.h             | 1 -\n 8 files changed, 10 insertions(+), 13 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 5919d9e5050..9394f170f7f 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -316,8 +316,8 @@ int write_archive_entries(struct archiver_args *args,\n \t\tgit_attr_set_direction(GIT_ATTR_INDEX);\n \t}\n \n-\terr = read_tree_recursive(args->repo, args->tree, \"\",\n-\t\t\t\t  0, 0, &args->pathspec,\n+\terr = read_tree_recursive(args->repo, args->tree,\n+\t\t\t\t  0, &args->pathspec,\n \t\t\t\t  queue_or_write_archive_entry,\n \t\t\t\t  &context);\n \tif (err == READ_TREE_RECURSIVE)\n@@ -405,8 +405,8 @@ static int path_exists(struct archiver_args *args, const char *path)\n \tctx.args = args;\n \tparse_pathspec(&ctx.pathspec, 0, 0, \"\", paths);\n \tctx.pathspec.recursive = 1;\n-\tret = read_tree_recursive(args->repo, args->tree, \"\",\n-\t\t\t\t  0, 0, &ctx.pathspec,\n+\tret = read_tree_recursive(args->repo, args->tree,\n+\t\t\t\t  0, &ctx.pathspec,\n \t\t\t\t  reject_entry, &ctx);\n \tclear_pathspec(&ctx.pathspec);\n \treturn ret != 0;\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2d6550bc3c8..21b742c0f07 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -155,7 +155,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,\n \n static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)\n {\n-\tread_tree_recursive(the_repository, tree, \"\", 0, 0,\n+\tread_tree_recursive(the_repository, tree, 0,\n \t\t\t    pathspec, update_some, NULL);\n \n \t/* update the index with the given tree's info\ndiff --git a/builtin/log.c b/builtin/log.c\nindex f67b67d80ed..ffa3fb8c286 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -681,8 +681,8 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n-\t\t\tread_tree_recursive(the_repository, (struct tree *)o, \"\",\n-\t\t\t\t\t    0, 0, &match_all, show_tree_object,\n+\t\t\tread_tree_recursive(the_repository, (struct tree *)o,\n+\t\t\t\t\t    0, &match_all, show_tree_object,\n \t\t\t\t\t    rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex db53e2c8e6d..cd432ac03cd 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -523,7 +523,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(the_repository, tree, \"\", 0, 1, &pathspec, fn, istate);\n+\terr = read_tree_recursive(the_repository, tree, 1, &pathspec, fn, istate);\n \tif (err)\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 7cad3f24ebd..7d3fb2e6d0f 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -185,6 +185,6 @@ 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_recursive(the_repository, tree, \"\", 0, 0,\n+\treturn !!read_tree_recursive(the_repository, tree, 0,\n \t\t\t\t     &pathspec, show_tree, NULL);\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex b052974f191..fa7602ff0f2 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -473,7 +473,7 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n {\n \tstruct pathspec match_all;\n \tmemset(&match_all, 0, sizeof(match_all));\n-\tread_tree_recursive(opt->repo, tree, \"\", 0, 0,\n+\tread_tree_recursive(opt->repo, tree, 0,\n \t\t\t    &match_all, save_files_dirs, opt);\n }\n \ndiff --git a/tree.c b/tree.c\nindex a6c12f2745a..04eb11aed31 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -83,14 +83,12 @@ static int read_tree_1(struct repository *r,\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tconst char *base, int baselen,\n \t\t\tint stage, const struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\tstrbuf_add(&sb, base, baselen);\n \tret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context);\n \tstrbuf_release(&sb);\n \treturn ret;\ndiff --git a/tree.h b/tree.h\nindex 6b0b1dc211a..5252b5139dd 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -35,7 +35,6 @@ typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const c\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tconst char *base, int baselen,\n \t\t\tint stage, const struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context);\n #endif /* TREE_H */\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418431","messageId":"20210308022138.28166-7-avarab@gmail.com","threadId":"18927","inReplyTo":"20210306193458.20633-1-avarab@gmail.com","subject":"[PATCH v2 6/6] tree.h API: remove \"stage\" parameter from read_tree_recursive()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T02:21:38Z","receivedAt":"2021-03-08T02:22:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The read_tree_recursive() function took a \"stage\" parameter that is\npassed through as-is. As it turns out nothing used this parameter in a\nway that they couldn't just move to the callback function they\ndefined, so let's get rid of it.\n\nIf anyone needs to pass such information in the future they can use\nthe \"void *context\" parameter.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          |  9 +++++----\n builtin/checkout.c |  4 ++--\n builtin/log.c      |  4 ++--\n builtin/ls-files.c | 14 +++++++-------\n builtin/ls-tree.c  |  4 ++--\n merge-recursive.c  |  4 ++--\n tree.c             | 10 +++++-----\n tree.h             |  4 ++--\n 8 files changed, 27 insertions(+), 26 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 9394f170f7f..6669a4bd147 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -231,9 +231,10 @@ static int write_directory(struct archiver_context *c)\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\tstruct strbuf *base, const char *filename,\n-\t\tunsigned mode, int stage, void *context)\n+\t\tunsigned mode, void *context)\n {\n \tstruct archiver_context *c = context;\n+\tint stage = 0;\n \n \twhile (c->bottom &&\n \t       !(base->len >= c->bottom->len &&\n@@ -317,7 +318,7 @@ int write_archive_entries(struct archiver_args *args,\n \t}\n \n \terr = read_tree_recursive(args->repo, args->tree,\n-\t\t\t\t  0, &args->pathspec,\n+\t\t\t\t  &args->pathspec,\n \t\t\t\t  queue_or_write_archive_entry,\n \t\t\t\t  &context);\n \tif (err == READ_TREE_RECURSIVE)\n@@ -378,7 +379,7 @@ struct path_exists_context {\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n \t\t\tconst char *filename, unsigned mode,\n-\t\t\tint stage, void *context)\n+\t\t\tvoid *context)\n {\n \tint ret = -1;\n \tstruct path_exists_context *ctx = context;\n@@ -406,7 +407,7 @@ static int path_exists(struct archiver_args *args, const char *path)\n \tparse_pathspec(&ctx.pathspec, 0, 0, \"\", paths);\n \tctx.pathspec.recursive = 1;\n \tret = read_tree_recursive(args->repo, args->tree,\n-\t\t\t\t  0, &ctx.pathspec,\n+\t\t\t\t  &ctx.pathspec,\n \t\t\t\t  reject_entry, &ctx);\n \tclear_pathspec(&ctx.pathspec);\n \treturn ret != 0;\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 21b742c0f07..2c2d58a230f 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -114,7 +114,7 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n }\n \n static int update_some(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tint len;\n \tstruct cache_entry *ce;\n@@ -155,7 +155,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,\n \n static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)\n {\n-\tread_tree_recursive(the_repository, tree, 0,\n+\tread_tree_recursive(the_repository, tree,\n \t\t\t    pathspec, update_some, NULL);\n \n \t/* update the index with the given tree's info\ndiff --git a/builtin/log.c b/builtin/log.c\nindex ffa3fb8c286..58acb2b76ab 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -599,7 +599,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n \n static int show_tree_object(const struct object_id *oid,\n \t\tstruct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tFILE *file = context;\n \tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n@@ -682,7 +682,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n \t\t\tread_tree_recursive(the_repository, (struct tree *)o,\n-\t\t\t\t\t    0, &match_all, show_tree_object,\n+\t\t\t\t\t    &match_all, show_tree_object,\n \t\t\t\t\t    rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex cd432ac03cd..fa9b01b6cc7 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -425,7 +425,7 @@ static int read_one_entry_opt(struct index_state *istate,\n \t\t\t      const struct object_id *oid,\n \t\t\t      struct strbuf *base,\n \t\t\t      const char *pathname,\n-\t\t\t      unsigned mode, int stage, int opt)\n+\t\t\t      unsigned mode, int opt)\n {\n \tint len;\n \tstruct cache_entry *ce;\n@@ -437,7 +437,7 @@ static int read_one_entry_opt(struct index_state *istate,\n \tce = make_empty_cache_entry(istate, base->len + len);\n \n \tce->ce_mode = create_ce_mode(mode);\n-\tce->ce_flags = create_ce_flags(stage);\n+\tce->ce_flags = create_ce_flags(1);\n \tce->ce_namelen = base->len + len;\n \tmemcpy(ce->name, base->buf, base->len);\n \tmemcpy(ce->name + base->len, pathname, len+1);\n@@ -446,12 +446,12 @@ static int read_one_entry_opt(struct index_state *istate,\n }\n \n static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\t  const char *pathname, unsigned mode, int stage,\n+\t\t\t  const char *pathname, unsigned mode,\n \t\t\t  void *context)\n {\n \tstruct index_state *istate = context;\n \treturn read_one_entry_opt(istate, oid, base, pathname,\n-\t\t\t\t  mode, stage,\n+\t\t\t\t  mode,\n \t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n }\n \n@@ -460,12 +460,12 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n  * the stage that will conflict with the entry being added.\n  */\n static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n-\t\t\t\tconst char *pathname, unsigned mode, int stage,\n+\t\t\t\tconst char *pathname, unsigned mode,\n \t\t\t\tvoid *context)\n {\n \tstruct index_state *istate = context;\n \treturn read_one_entry_opt(istate, oid, base, pathname,\n-\t\t\t\t  mode, stage,\n+\t\t\t\t  mode,\n \t\t\t\t  ADD_CACHE_JUST_APPEND);\n }\n \n@@ -523,7 +523,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(the_repository, tree, 1, &pathspec, fn, istate);\n+\terr = read_tree_recursive(the_repository, tree, &pathspec, fn, istate);\n \tif (err)\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 7d3fb2e6d0f..dbb31217beb 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -62,7 +62,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n }\n \n static int show_tree(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tint retval = 0;\n \tint baselen;\n@@ -185,6 +185,6 @@ 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_recursive(the_repository, tree, 0,\n+\treturn !!read_tree_recursive(the_repository, tree,\n \t\t\t\t     &pathspec, show_tree, NULL);\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex fa7602ff0f2..1593f374495 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,7 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   unsigned int mode, int stage, void *context)\n+\t\t\t   unsigned int mode, void *context)\n {\n \tstruct path_hashmap_entry *entry;\n \tint baselen = base->len;\n@@ -473,7 +473,7 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n {\n \tstruct pathspec match_all;\n \tmemset(&match_all, 0, sizeof(match_all));\n-\tread_tree_recursive(opt->repo, tree, 0,\n+\tread_tree_recursive(opt->repo, tree,\n \t\t\t    &match_all, save_files_dirs, opt);\n }\n \ndiff --git a/tree.c b/tree.c\nindex 04eb11aed31..fb4985f22ca 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -13,7 +13,7 @@ const char *tree_type = \"tree\";\n \n static int read_tree_1(struct repository *r,\n \t\t       struct tree *tree, struct strbuf *base,\n-\t\t       int stage, const struct pathspec *pathspec,\n+\t\t       const struct pathspec *pathspec,\n \t\t       read_tree_fn_t fn, void *context)\n {\n \tstruct tree_desc desc;\n@@ -38,7 +38,7 @@ static int read_tree_1(struct repository *r,\n \t\t}\n \n \t\tswitch (fn(&entry.oid, base,\n-\t\t\t   entry.path, entry.mode, stage, context)) {\n+\t\t\t   entry.path, entry.mode, context)) {\n \t\tcase 0:\n \t\t\tcontinue;\n \t\tcase READ_TREE_RECURSIVE:\n@@ -72,7 +72,7 @@ static int read_tree_1(struct repository *r,\n \t\tstrbuf_add(base, entry.path, len);\n \t\tstrbuf_addch(base, '/');\n \t\tretval = read_tree_1(r, lookup_tree(r, &oid),\n-\t\t\t\t     base, stage, pathspec,\n+\t\t\t\t     base, pathspec,\n \t\t\t\t     fn, context);\n \t\tstrbuf_setlen(base, oldlen);\n \t\tif (retval)\n@@ -83,13 +83,13 @@ static int read_tree_1(struct repository *r,\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tint stage, const struct pathspec *pathspec,\n+\t\t\tconst struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\tret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context);\n+\tret = read_tree_1(r, tree, &sb, pathspec, fn, context);\n \tstrbuf_release(&sb);\n \treturn ret;\n }\ndiff --git a/tree.h b/tree.h\nindex 5252b5139dd..1309ab997e5 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -31,10 +31,10 @@ struct tree *parse_tree_indirect(const struct object_id *oid);\n int cmp_cache_name_compare(const void *a_, const void *b_);\n \n #define READ_TREE_RECURSIVE 1\n-typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, int, void *);\n+typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tint stage, const struct pathspec *pathspec,\n+\t\t\tconst struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context);\n #endif /* TREE_H */\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418432","messageId":"20210308022138.28166-5-avarab@gmail.com","threadId":"18927","inReplyTo":"20210306193458.20633-1-avarab@gmail.com","subject":"[PATCH v2 4/6] ls-files: refactor away read_tree()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T02:21:36Z","receivedAt":"2021-03-08T02:22:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor away the read_tree() function into its only user,\noverlay_tree_on_index().\n\nFirst, change read_one_entry_opt() to use the strbuf parameter\nread_tree_recursive() passes down in place. This finishes up a partial\nrefactoring started in 6a0b0b6de99 (tree.c: update read_tree_recursive\ncallback to pass strbuf as base, 2014-11-30).\n\nMoving the rest into overlay_tree_on_index() makes this index juggling\nwe're doing easier to read.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 74 +++++++++++++++++++++-------------------------\n 1 file changed, 33 insertions(+), 41 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 74d572a3e4a..db53e2c8e6d 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -423,7 +423,7 @@ static int get_common_prefix_len(const char *common_prefix)\n \n static int read_one_entry_opt(struct index_state *istate,\n \t\t\t      const struct object_id *oid,\n-\t\t\t      const char *base, int baselen,\n+\t\t\t      struct strbuf *base,\n \t\t\t      const char *pathname,\n \t\t\t      unsigned mode, int stage, int opt)\n {\n@@ -434,13 +434,13 @@ static int read_one_entry_opt(struct index_state *istate,\n \t\treturn READ_TREE_RECURSIVE;\n \n \tlen = strlen(pathname);\n-\tce = make_empty_cache_entry(istate, baselen + len);\n+\tce = make_empty_cache_entry(istate, base->len + len);\n \n \tce->ce_mode = create_ce_mode(mode);\n \tce->ce_flags = create_ce_flags(stage);\n-\tce->ce_namelen = baselen + len;\n-\tmemcpy(ce->name, base, baselen);\n-\tmemcpy(ce->name + baselen, pathname, len+1);\n+\tce->ce_namelen = base->len + len;\n+\tmemcpy(ce->name, base->buf, base->len);\n+\tmemcpy(ce->name + base->len, pathname, len+1);\n \toidcpy(&ce->oid, oid);\n \treturn add_index_entry(istate, ce, opt);\n }\n@@ -450,7 +450,7 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n \t\t\t  void *context)\n {\n \tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\treturn read_one_entry_opt(istate, oid, base, pathname,\n \t\t\t\t  mode, stage,\n \t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n }\n@@ -464,44 +464,11 @@ static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base\n \t\t\t\tvoid *context)\n {\n \tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\treturn read_one_entry_opt(istate, oid, base, pathname,\n \t\t\t\t  mode, stage,\n \t\t\t\t  ADD_CACHE_JUST_APPEND);\n }\n \n-\n-static int read_tree(struct repository *r, struct tree *tree,\n-\t\t     struct pathspec *match, struct index_state *istate)\n-{\n-\tread_tree_fn_t fn = NULL;\n-\tint i, err;\n-\n-\n-\t/*\n-\t * See if we have cache entry at the stage.  If so,\n-\t * do it the original slow way, otherwise, append and then\n-\t * sort at the end.\n-\t */\n-\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n-\t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == 1)\n-\t\t\tfn = read_one_entry;\n-\t}\n-\n-\tif (!fn)\n-\t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, 1, match, fn, istate);\n-\tif (fn == read_one_entry || err)\n-\t\treturn err;\n-\n-\t/*\n-\t * Sort the cache entry -- we need to nuke the cache tree, though.\n-\t */\n-\tcache_tree_free(&istate->cache_tree);\n-\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n-\treturn 0;\n-}\n-\n /*\n  * Read the tree specified with --with-tree option\n  * (typically, HEAD) into stage #1 and then\n@@ -518,6 +485,8 @@ void overlay_tree_on_index(struct index_state *istate,\n \tstruct pathspec pathspec;\n \tstruct cache_entry *last_stage0 = NULL;\n \tint i;\n+\tread_tree_fn_t fn = NULL;\n+\tint err;\n \n \tif (get_oid(tree_name, &oid))\n \t\tdie(\"tree-ish %s not found.\", tree_name);\n@@ -540,9 +509,32 @@ void overlay_tree_on_index(struct index_state *istate,\n \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n \t} else\n \t\tmemset(&pathspec, 0, sizeof(pathspec));\n-\tif (read_tree(the_repository, tree, &pathspec, istate))\n+\n+\t/*\n+\t * See if we have cache entry at the stage.  If so,\n+\t * do it the original slow way, otherwise, append and then\n+\t * sort at the end.\n+\t */\n+\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n+\t\tconst struct cache_entry *ce = istate->cache[i];\n+\t\tif (ce_stage(ce) == 1)\n+\t\t\tfn = read_one_entry;\n+\t}\n+\n+\tif (!fn)\n+\t\tfn = read_one_entry_quick;\n+\terr = read_tree_recursive(the_repository, tree, \"\", 0, 1, &pathspec, fn, istate);\n+\tif (err)\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \n+\t/*\n+\t * Sort the cache entry -- we need to nuke the cache tree, though.\n+\t */\n+\tif (fn == read_one_entry_quick) {\n+\t\tcache_tree_free(&istate->cache_tree);\n+\t\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n+\t}\n+\n \tfor (i = 0; i < istate->cache_nr; i++) {\n \t\tstruct cache_entry *ce = istate->cache[i];\n \t\tswitch (ce_stage(ce)) {\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418448","messageId":"20210308150650.18626-3-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 02/30] notes & match-trees: use name_entry's \"pathlen\" member","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:22Z","receivedAt":"2021-03-08T15:08:14Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change code that was doing a strlen() on the \"path\" from a name_entry\nstruct to instead use the pathlen given to us by decode_tree_entry().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c | 7 +++----\n notes.c       | 4 ++--\n 2 files changed, 5 insertions(+), 6 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex f6c194c1cca..1011357ad0c 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -197,9 +197,10 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \twhile (desc.size) {\n \t\tconst char *name;\n \t\tunsigned short mode;\n+\t\tint len = tree_entry_len(&desc.entry);\n \n \t\ttree_entry_extract(&desc, &name, &mode);\n-\t\tif (strlen(name) == toplen &&\n+\t\tif (len == toplen &&\n \t\t    !memcmp(name, prefix, toplen)) {\n \t\t\tif (!S_ISDIR(mode))\n \t\t\t\tdie(\"entry %s in tree %s is not a tree\", name,\n@@ -214,9 +215,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \t\t\t *   - to discard the \"const\"; this is OK because we\n \t\t\t *     know it points into our non-const \"buf\"\n \t\t\t */\n-\t\t\trewrite_here = (unsigned char *)(desc.entry.path +\n-\t\t\t\t\t\t\t strlen(desc.entry.path) +\n-\t\t\t\t\t\t\t 1);\n+\t\t\trewrite_here = (unsigned char *)(name + len + 1);\n \t\t\tbreak;\n \t\t}\n \t\tupdate_tree_entry(&desc);\ndiff --git a/notes.c b/notes.c\nindex d5ac081e76d..0a5b4fa1dbb 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -413,7 +413,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \twhile (tree_entry(&desc, &entry)) {\n \t\tunsigned char type;\n \t\tstruct leaf_node *l;\n-\t\tsize_t path_len = strlen(entry.path);\n+\t\tint path_len = entry.pathlen;\n \n \t\tif (path_len == 2 * (hashsz - prefix_len)) {\n \t\t\t/* This is potentially the remainder of the SHA-1 */\n@@ -483,7 +483,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n \t\t\t\tstrbuf_addch(&non_note_path, '/');\n \t\t\t}\n-\t\t\tstrbuf_addstr(&non_note_path, entry.path);\n+\t\t\tstrbuf_add(&non_note_path, entry.path, path_len);\n \t\t\tadd_non_note(t, strbuf_detach(&non_note_path, NULL),\n \t\t\t\t     entry.mode, entry.oid.hash);\n \t\t}\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418449","messageId":"20210308150650.18626-1-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 00/30] tree-walk: mostly \"mode\" to \"enum object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:20Z","receivedAt":"2021-03-08T15:08:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This large series goes on top of my 6 patch series for\nread_tree_recursive() as this one further refactors that function. See\nhttps://lore.kernel.org/git/20210308022138.28166-1-avarab@gmail.com/\nfor that series.\n\nI noticed that since 2014 or so we haven't been doing the fsck checks\nfor bad file modes in trees. This series fixes that. I plan to add\ntests etc. for that in another follow-up series.\n\nI wanted to get this out for review sooner than later, particularly\nsince the fsck testing will probably get me down another refactoring\npath (fsck testing in general in this area is pretty bad...).\n\nAs noted in 30/30 it would have been way easier to simply do an\nisolated fix for that bug by introducing some fsck-specific API for\nraw tree reading.\n\nBut I thought the bug was symptomatic of a wider problem in our\ncodebase. Namely that we pass around the tree's mode *a lot*.\n\nBut almost everything that then deals with the mode doesn't per-se\ncare about the mode bits in the tree, but using them to map that mode\nto a tree entry for one of of OBJ_{BLOB,TREE,COMMIT}.\n\nSo this is a large refactoring of all users of the widely used\ntree-walk.h API to \"enum obj2ect_type\", finally in 29/30 I rename the\nfield to a scary \"raw_mode\".\n\nAt that point we have just ~30-50 grep hits left for \"raw_mode\" in the\ncodebase (depending on whether we count names in function parameters).\n\nHopefully being in that state alleviates e.g. Elijah's concerns\nexpressed in\nhttps://lore.kernel.org/git/CABPp-BEdu1PqV5W=FuL0f08iFhGzvzV8oSUybNj4eF0aAwTnAw@mail.gmail.com/\nI agree that doing the equivalent of 30/30 on top of master would be\nway too scary, but once we're at 29/30 I think it's sane.\n\nI tested this in combination with his on-list series to add more\nmerge-ort testing:\nhttps://lore.kernel.org/git/pull.973.git.git.1614905738.gitgitgadget@gmail.com/\n\nI found a regression I'd caused in the merge-ort.c code with those\ntests, fixed here. See the comment in merge-ort.c in 30/30.\n\nÆvar Arnfjörð Bjarmason (30):\n  diff.c: remove redundant canon_mode() call\n  notes & match-trees: use name_entry's \"pathlen\" member\n  cache.h: add a comment to object_type()\n  tree-walk.h: add object_type member to name_entry\n  tree-walk.c: migrate to using new \"object_type\" field when possible\n  cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n  tree-walk.h users: switch object_type(...) to new .object_type\n  tree.h: format argument lists of read_tree_recursive() users\n  tree.h users: format argument lists in archive.c\n  archive: get rid of 'stage' parameter\n  tree.h API: make read_tree_fn_t take an \"enum object_type\"\n  tree-walk.h users: migrate \"p->mode &&\" pattern\n  tree-walk.h users: refactor chained \"mode\" if/else into switch\n  tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n  merge-tree tests: test for the mode comparison in same_entry()\n  merge-ort: correct reference to test in 62fdec17a11\n  fsck.c: switch on \"object_type\" in fsck_walk_tree()\n  tree-walk.h users: use temporary variable(s) for \"mode\"\n  tree-walk.h API: formatting changes for subsequent commit\n  tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n  tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n  tree-walk.h API: Add get_tree_entry_type()\n  tree-walk.h API: add a get_tree_entry_path() function\n  tree-walk.h API: document and format tree_entry_extract()\n  tree-entry.h API: rename tree_entry_extract() to\n    tree_entry_extract_mode()\n  tree-walk.h API: add a tree_entry_extract_all() function\n  tree-walk.h API: add a tree_entry_extract_type() function\n  tree-walk.h API users: rename \"struct name_entry\"'s \"mode\" to\n    \"raw_mode\"\n  tree.h API users: rename read_tree_fn_t's \"mode\" to \"raw_mode\"\n  tree-walk.h API: move canon_mode() back out of decode_tree_entry()\n\n archive.c              | 51 +++++++++++++-----------\n blame.c                |  9 +++--\n builtin/checkout.c     |  7 +++-\n builtin/fast-import.c  |  8 ++--\n builtin/grep.c         |  6 +--\n builtin/log.c          |  7 ++--\n builtin/ls-files.c     | 13 +++---\n builtin/ls-tree.c      | 18 ++++-----\n builtin/merge-tree.c   | 32 +++++++++------\n builtin/mktree.c       |  4 +-\n builtin/pack-objects.c |  6 +--\n builtin/reflog.c       |  3 +-\n builtin/rm.c           |  2 +-\n builtin/update-index.c |  7 +++-\n cache-tree.c           |  2 +-\n cache.h                | 11 ++++--\n combine-diff.c         |  8 ++--\n delta-islands.c        |  2 +-\n diff.c                 |  2 +-\n fsck.c                 | 23 +++++------\n http-push.c            |  6 ++-\n line-log.c             |  2 +-\n list-objects.c         | 20 +++++++---\n match-trees.c          | 52 ++++++++++++------------\n merge-ort.c            | 34 ++++++++++------\n merge-recursive.c      | 33 ++++++++--------\n notes.c                | 15 +++----\n object-name.c          |  7 ++--\n pack-bitmap-write.c    |  8 ++--\n read-cache.c           | 16 ++++----\n revision.c             | 12 ++++--\n t/t4300-merge-tree.sh  | 44 +++++++++++++++++++++\n tree-diff.c            | 44 ++++++++++++---------\n tree-walk.c            | 89 +++++++++++++++++++++++++++++++-----------\n tree-walk.h            | 67 ++++++++++++++++++++++++++-----\n tree.c                 | 19 +++++----\n tree.h                 |  5 ++-\n unpack-trees.c         | 30 ++++++++------\n walker.c               | 22 ++++++-----\n 39 files changed, 482 insertions(+), 264 deletions(-)\n\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418450","messageId":"20210308150650.18626-2-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 01/30] diff.c: remove redundant canon_mode() call","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:21Z","receivedAt":"2021-03-08T15:08:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove a call to canon_mode() from fill_filespec(). This has been\nredundant since the tree-walk.c API supplies it pre-canonicalized\nsince 7146e66f086 (tree-walk: finally switch over tree descriptors to\ncontain a pre-parsed entry, 2014-02-06).\n\nThis call to the predecessor of canon_mode() was added back in\n4130b995719 ([PATCH] Diff updates to express type changes,\n2005-05-26).\n\nThis was the only such call in the codebase. The rest are all either\none of these sorts of forms:\n\n    canon_mode(st.st_mode); /* a stat(2) struct */\n    canon_mode(S_IFREG | 0644) /* A compile-time literal */\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n diff.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/diff.c b/diff.c\nindex 6956f5e335c..bf46e6a4d8c 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -3846,7 +3846,7 @@ void fill_filespec(struct diff_filespec *spec, const struct object_id *oid,\n \t\t   int oid_valid, unsigned short mode)\n {\n \tif (mode) {\n-\t\tspec->mode = canon_mode(mode);\n+\t\tspec->mode = mode;\n \t\toidcpy(&spec->oid, oid);\n \t\tspec->oid_valid = oid_valid;\n \t}\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418451","messageId":"20210308150650.18626-4-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 03/30] cache.h: add a comment to object_type()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:23Z","receivedAt":"2021-03-08T15:08:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a comment to the object_type() function to explain what it\nreturns, and whet the \"mode\" is in the \"else\" case.\n\nThe object_type() function dates back to 4d1012c3709 (Fix rev-list\nwhen showing objects involving submodules, 2007-11-11). It's not\nimmediately obvious to someone looking at its history and how it's\ncome to be used.\n\nDespite what Linus noted in 4d1012c3709 (Fix rev-list when showing\nobjects involving submodules, 2007-11-11) about wanting to move away\nfrom users of object_type() relying on S_ISLNK(mode) being true here\nwe do currently rely on that. If this is changed to a condition to\nonly return OBJ_BLOB on S_ISREG(mode) then t4008, t4023 and t7415 will\nhave failing tests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n cache.h | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/cache.h b/cache.h\nindex d9281496140..e513f0ee5b4 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -451,11 +451,16 @@ enum object_type {\n \tOBJ_MAX\n };\n \n+/*\n+ * object_type() returns an object of a type that'll appear in a tree,\n+ * so no OBJ_TAG is possible. This is mostly (and dates back to)\n+ * consumers of the tree-walk.h API's \"mode\" field.\n+ */\n static inline enum object_type object_type(unsigned int mode)\n {\n \treturn S_ISDIR(mode) ? OBJ_TREE :\n \t\tS_ISGITLINK(mode) ? OBJ_COMMIT :\n-\t\tOBJ_BLOB;\n+\t\tOBJ_BLOB; /* S_ISREG(mode) || S_ISLNK(mode) */\n }\n \n /* Double-check local_repo_env below if you add to this list. */\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418452","messageId":"20210308150650.18626-5-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 04/30] tree-walk.h: add object_type member to name_entry","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:24Z","receivedAt":"2021-03-08T15:08:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Most users of the tree walking API don't care what the specific mode\nof an object in a tree is (e.g. if it's executable), they care if it's\none of OBJ_{TREE,BLOB,COMMIT}.\n\nLet's add an \"object_type\" enum to the \"name_entry\" struct to help\nsuch callers.\n\nIdeally we'd have some subset of \"enum object_type\" here with just\nthose three entries, so we could rely on the C compiler to\nexhaustively check our \"switch\" statements, but I don't know how to\ncreate such a enum subset without re-labeling OBJ_{TREE,BLOB,COMMIT}\nto e.g. \"NE_OBJ_*\" (an enum is just an int under the hood, so you can\nuse such a struct with \"OBJ_*\", but the compiler will complain...).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.c | 4 +++-\n tree-walk.h | 2 ++\n 2 files changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 2d6226d5f18..b210967b73b 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -47,7 +47,9 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l\n \n \t/* Initialize the descriptor entry */\n \tdesc->entry.path = path;\n-\tdesc->entry.mode = canon_mode(mode);\n+\tmode = canon_mode(mode);\n+\tdesc->entry.mode = mode;\n+\tdesc->entry.object_type = object_type(mode);\n \tdesc->entry.pathlen = len - 1;\n \thashcpy(desc->entry.oid.hash, (const unsigned char *)path + len);\n \ndiff --git a/tree-walk.h b/tree-walk.h\nindex a5058469e9b..9f3825d2773 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -17,6 +17,8 @@ struct name_entry {\n \tconst char *path;\n \tint pathlen;\n \tunsigned int mode;\n+\t/* simple 'mode': Only OBJ_{BLOB,TREE,COMMIT} */\n+\tenum object_type object_type;\n };\n \n /**\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418453","messageId":"20210308150650.18626-6-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 05/30] tree-walk.c: migrate to using new \"object_type\" field when possible","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:25Z","receivedAt":"2021-03-08T15:08:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.c | 24 +++++++++++++-----------\n 1 file changed, 13 insertions(+), 11 deletions(-)\n\ndiff --git a/tree-walk.c b/tree-walk.c\nindex b210967b73b..6e9161901d8 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -521,7 +521,7 @@ int traverse_trees(struct index_state *istate,\n \t\t\tif (!entry[i].path)\n \t\t\t\tcontinue;\n \t\t\tmask |= 1ul << i;\n-\t\t\tif (S_ISDIR(entry[i].mode))\n+\t\t\tif (entry[i].object_type == OBJ_TREE)\n \t\t\t\tdirmask |= 1ul << i;\n \t\t\te = &entry[i];\n \t\t}\n@@ -892,8 +892,8 @@ static int match_entry(const struct pathspec_item *item,\n \t\t * nothing else (to handle 'submod/' and 'submod'\n \t\t * uniformly).\n \t\t */\n-\t\tif (!S_ISDIR(entry->mode) &&\n-\t\t    (!S_ISGITLINK(entry->mode) || matchlen > pathlen + 1))\n+\t\tif (entry->object_type != OBJ_TREE &&\n+\t\t    (entry->object_type != OBJ_COMMIT || matchlen > pathlen + 1))\n \t\t\treturn 0;\n \t}\n \n@@ -1038,7 +1038,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t    ps->max_depth == -1)\n \t\t\treturn all_entries_interesting;\n \t\treturn within_depth(base->buf + base_offset, baselen,\n-\t\t\t\t    !!S_ISDIR(entry->mode),\n+\t\t\t\t    entry->object_type == OBJ_TREE,\n \t\t\t\t    ps->max_depth) ?\n \t\t\tentry_interesting : entry_not_interesting;\n \t}\n@@ -1071,7 +1071,7 @@ static enum interesting do_match(struct index_state *istate,\n \n \t\t\tif (within_depth(base_str + matchlen + 1,\n \t\t\t\t\t baselen - matchlen - 1,\n-\t\t\t\t\t !!S_ISDIR(entry->mode),\n+\t\t\t\t\t entry->object_type == OBJ_TREE,\n \t\t\t\t\t ps->max_depth))\n \t\t\t\tgoto interesting;\n \t\t\telse\n@@ -1094,7 +1094,8 @@ static enum interesting do_match(struct index_state *istate,\n \t\t\t\t * Match all directories. We'll try to\n \t\t\t\t * match files later on.\n \t\t\t\t */\n-\t\t\t\tif (ps->recursive && S_ISDIR(entry->mode))\n+\t\t\t\tif (ps->recursive &&\n+\t\t\t\t    entry->object_type == OBJ_TREE)\n \t\t\t\t\treturn entry_interesting;\n \n \t\t\t\t/*\n@@ -1105,7 +1106,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t\t\t * be performed in the submodule itself.\n \t\t\t\t */\n \t\t\t\tif (ps->recurse_submodules &&\n-\t\t\t\t    S_ISGITLINK(entry->mode) &&\n+\t\t\t\t    entry->object_type == OBJ_COMMIT &&\n \t\t\t\t    !ps_strncmp(item, match + baselen,\n \t\t\t\t\t\tentry->path,\n \t\t\t\t\t\titem->nowildcard_len - baselen))\n@@ -1154,7 +1155,8 @@ static enum interesting do_match(struct index_state *istate,\n \t\t * character.  More accurate matching can then\n \t\t * be performed in the submodule itself.\n \t\t */\n-\t\tif (ps->recurse_submodules && S_ISGITLINK(entry->mode) &&\n+\t\tif (ps->recurse_submodules &&\n+\t\t    entry->object_type == OBJ_COMMIT &&\n \t\t    !ps_strncmp(item, match, base->buf + base_offset,\n \t\t\t\titem->nowildcard_len)) {\n \t\t\tstrbuf_setlen(base, base_offset + baselen);\n@@ -1170,7 +1172,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t * in future, see\n \t\t * https://lore.kernel.org/git/7vmxo5l2g4.fsf@alter.siamese.dyndns.org/\n \t\t */\n-\t\tif (ps->recursive && S_ISDIR(entry->mode))\n+\t\tif (ps->recursive && entry->object_type == OBJ_TREE)\n \t\t\treturn entry_interesting;\n \t\tcontinue;\n interesting:\n@@ -1193,7 +1195,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t\t * can probably return all_entries_interesting or\n \t\t\t * all_entries_not_interesting here if matched.\n \t\t\t */\n-\t\t\tif (S_ISDIR(entry->mode))\n+\t\t\tif (entry->object_type == OBJ_TREE)\n \t\t\t\treturn entry_interesting;\n \n \t\t\tstrbuf_add(base, entry->path, pathlen);\n@@ -1269,7 +1271,7 @@ enum interesting tree_entry_interesting(struct index_state *istate,\n \t\treturn positive;\n \n \t/* #15, #19 */\n-\tif (S_ISDIR(entry->mode) &&\n+\tif (entry->object_type == OBJ_TREE &&\n \t    positive >= entry_interesting &&\n \t    negative == entry_interesting)\n \t\treturn entry_interesting;\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418455","messageId":"20210308150650.18626-13-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 12/30] tree-walk.h users: migrate \"p->mode &&\" pattern","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:32Z","receivedAt":"2021-03-08T15:08:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change code that dpends on \"p->mode\" either being a valid mode or zero\nto use a p->object_type comparison to \"OBJ_NONE\".\n\nThe object_type() function in cache.h will not return OBJ_NONE, but in\nthis these API users are implicitly relying on the memzero() that\nhappens in setup_traverse_info().\n\nSince OBJ_NONE is \"0\" we can also rely on that being zero'd out here,\nalong with the rest of the structure. I think this is slightly less\nclever than \"mode not set\", and helps to get rid of more uses of\n\"mode\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/merge-tree.c | 9 +++++----\n merge-ort.c          | 2 +-\n unpack-trees.c       | 4 ++--\n 3 files changed, 8 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex de8520778d2..2de34c2d485 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -214,7 +214,7 @@ static void unresolved_directory(const struct traverse_info *info,\n \tvoid *buf0, *buf1, *buf2;\n \n \tfor (p = n; p < n + 3; p++) {\n-\t\tif (p->mode && S_ISDIR(p->mode))\n+\t\tif (p->object_type == OBJ_TREE)\n \t\t\tbreak;\n \t}\n \tif (n + 3 <= p)\n@@ -222,7 +222,7 @@ static void unresolved_directory(const struct traverse_info *info,\n \n \tnewbase = traverse_path(info, p);\n \n-#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL)\n+#define ENTRY_OID(e) (((e)->object_type == OBJ_TREE) ? &(e)->oid : NULL)\n \tbuf0 = fill_tree_descriptor(r, t + 0, ENTRY_OID(n + 0));\n \tbuf1 = fill_tree_descriptor(r, t + 1, ENTRY_OID(n + 1));\n \tbuf2 = fill_tree_descriptor(r, t + 2, ENTRY_OID(n + 2));\n@@ -242,7 +242,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n \tconst char *path;\n \tstruct merge_list *link;\n \n-\tif (!n->mode)\n+\tif (n->object_type == OBJ_NONE)\n \t\treturn entry;\n \tif (entry)\n \t\tpath = entry->path;\n@@ -265,7 +265,8 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3])\n \t\t * Treat missing entries as directories so that we return\n \t\t * after unresolved_directory has handled this.\n \t\t */\n-\t\tif (!n[i].mode || S_ISDIR(n[i].mode))\n+\t\tif (n[i].object_type == OBJ_NONE ||\n+\t\t    n[i].object_type == OBJ_TREE)\n \t\t\tdirmask |= (1 << i);\n \t}\n \ndiff --git a/merge-ort.c b/merge-ort.c\nindex 4075d13aaab..4375027914c 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -668,7 +668,7 @@ static int collect_merge_info_callback(int n,\n \t * setup_path_info() for tracking.\n \t */\n \tp = names;\n-\twhile (!p->mode)\n+\twhile (p->object_type == OBJ_NONE)\n \t\tp++;\n \tlen = traverse_path_len(info, p->pathlen);\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 802f7771d75..92105135522 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -859,7 +859,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n \t}\n \n \tp = names;\n-\twhile (!p->mode)\n+\twhile (p->object_type == OBJ_NONE)\n \t\tp++;\n \n \tnewinfo = *info;\n@@ -1239,7 +1239,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n \tconst struct name_entry *p = names;\n \n \t/* Find first entry with a real name (we could use \"mask\" too) */\n-\twhile (!p->mode)\n+\twhile (p->object_type == OBJ_NONE)\n \t\tp++;\n \n \tif (o->debug_unpack)\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418456","messageId":"20210308150650.18626-7-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 06/30] cache.h: have base_name_compare() take \"is tree?\", not \"mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:26Z","receivedAt":"2021-03-08T15:08:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the base_name_compare() API and the related df_name_compare()\nfunction to take a boolean argument indicating whether the entry is a\ntree or not, instead of having them call S_ISDIR(mode) on their own.\n\nThis makes use of the new \"object_type\" field in the \"name_entry\".\n\nThe API being modified here was originally added way back in\n958ba6c96eb (Introduce \"base_name_compare()\" helper function,\n2005-05-20).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fast-import.c |  8 ++++----\n builtin/mktree.c      |  4 ++--\n cache.h               |  4 ++--\n combine-diff.c        |  8 +++++---\n match-trees.c         |  6 ++++--\n merge-ort.c           |  4 ++--\n merge-recursive.c     |  6 +++---\n read-cache.c          | 16 ++++++++--------\n tree-diff.c           |  7 +++++--\n unpack-trees.c        | 15 ++++++++-------\n 10 files changed, 43 insertions(+), 35 deletions(-)\n\ndiff --git a/builtin/fast-import.c b/builtin/fast-import.c\nindex dd4d09ceceb..ce4613c1595 100644\n--- a/builtin/fast-import.c\n+++ b/builtin/fast-import.c\n@@ -1288,8 +1288,8 @@ static int tecmp0 (const void *_a, const void *_b)\n \tstruct tree_entry *a = *((struct tree_entry**)_a);\n \tstruct tree_entry *b = *((struct tree_entry**)_b);\n \treturn base_name_compare(\n-\t\ta->name->str_dat, a->name->str_len, a->versions[0].mode,\n-\t\tb->name->str_dat, b->name->str_len, b->versions[0].mode);\n+\t\ta->name->str_dat, a->name->str_len, 1,\n+\t\tb->name->str_dat, b->name->str_len, 1);\n }\n \n static int tecmp1 (const void *_a, const void *_b)\n@@ -1297,8 +1297,8 @@ static int tecmp1 (const void *_a, const void *_b)\n \tstruct tree_entry *a = *((struct tree_entry**)_a);\n \tstruct tree_entry *b = *((struct tree_entry**)_b);\n \treturn base_name_compare(\n-\t\ta->name->str_dat, a->name->str_len, a->versions[1].mode,\n-\t\tb->name->str_dat, b->name->str_len, b->versions[1].mode);\n+\t\ta->name->str_dat, a->name->str_len, 1,\n+\t\tb->name->str_dat, b->name->str_len, 1);\n }\n \n static void mktree(struct tree_content *t, int v, struct strbuf *b)\ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex 891991b00d6..2c1973229ac 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -37,8 +37,8 @@ static int ent_compare(const void *a_, const void *b_)\n {\n \tstruct treeent *a = *(struct treeent **)a_;\n \tstruct treeent *b = *(struct treeent **)b_;\n-\treturn base_name_compare(a->name, a->len, a->mode,\n-\t\t\t\t b->name, b->len, b->mode);\n+\treturn base_name_compare(a->name, a->len, S_ISDIR(a->mode),\n+\t\t\t\t b->name, b->len, S_ISDIR(b->mode));\n }\n \n static void write_tree(struct object_id *oid)\ndiff --git a/cache.h b/cache.h\nindex e513f0ee5b4..49994dae916 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1506,8 +1506,8 @@ int repo_interpret_branch_name(struct repository *r,\n \n int validate_headref(const char *ref);\n \n-int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n-int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n+int base_name_compare(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2);\n+int df_name_compare(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2);\n int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);\n int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);\n \ndiff --git a/combine-diff.c b/combine-diff.c\nindex 9228aebc16b..64d7aaf1710 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -16,11 +16,13 @@\n static int compare_paths(const struct combine_diff_path *one,\n \t\t\t  const struct diff_filespec *two)\n {\n-\tif (!S_ISDIR(one->mode) && !S_ISDIR(two->mode))\n+\tint isdir_one = S_ISDIR(one->mode);\n+\tint isdir_two = S_ISDIR(two->mode);\n+\tif (!isdir_one && !isdir_two)\n \t\treturn strcmp(one->path, two->path);\n \n-\treturn base_name_compare(one->path, strlen(one->path), one->mode,\n-\t\t\t\t two->path, strlen(two->path), two->mode);\n+\treturn base_name_compare(one->path, strlen(one->path), isdir_one,\n+\t\t\t\t two->path, strlen(two->path), isdir_two);\n }\n \n static int filename_changed(char status)\ndiff --git a/match-trees.c b/match-trees.c\nindex 1011357ad0c..4d124a0fd1b 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -67,8 +67,10 @@ static void *fill_tree_desc_strict(struct tree_desc *desc,\n static int base_name_entries_compare(const struct name_entry *a,\n \t\t\t\t     const struct name_entry *b)\n {\n-\treturn base_name_compare(a->path, tree_entry_len(a), a->mode,\n-\t\t\t\t b->path, tree_entry_len(b), b->mode);\n+\tint isdira = a->object_type == OBJ_TREE;\n+\tint isdirb = b->object_type == OBJ_TREE;\n+\treturn base_name_compare(a->path, tree_entry_len(a), isdira,\n+\t\t\t\t b->path, tree_entry_len(b), isdirb);\n }\n \n /*\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 603d30c5217..4075d13aaab 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -2390,8 +2390,8 @@ static int tree_entry_order(const void *a_, const void *b_)\n \n \tconst struct merged_info *ami = a->util;\n \tconst struct merged_info *bmi = b->util;\n-\treturn base_name_compare(a->string, strlen(a->string), ami->result.mode,\n-\t\t\t\t b->string, strlen(b->string), bmi->result.mode);\n+\treturn base_name_compare(a->string, strlen(a->string), S_ISDIR(ami->result.mode),\n+\t\t\t\t b->string, strlen(b->string), S_ISDIR(bmi->result.mode));\n }\n \n static void write_tree(struct object_id *result_oid,\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1593f374495..0baa6b5ca56 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -554,12 +554,12 @@ static int string_list_df_name_compare(const char *one, const char *two)\n \t *\n \t * To achieve this, we sort with df_name_compare and provide\n \t * the mode S_IFDIR so that D/F conflicts will sort correctly.\n-\t * We use the mode S_IFDIR for everything else for simplicity,\n+\t * We say we have a directory for everything else for simplicity,\n \t * since in other cases any changes in their order due to\n \t * sorting cause no problems for us.\n \t */\n-\tint cmp = df_name_compare(one, onelen, S_IFDIR,\n-\t\t\t\t  two, twolen, S_IFDIR);\n+\tint cmp = df_name_compare(one, onelen, 1, two, twolen, 1);\n+\n \t/*\n \t * Now that 'foo' and 'foo/bar' compare equal, we have to make sure\n \t * that 'foo' comes before 'foo/bar'.\ndiff --git a/read-cache.c b/read-cache.c\nindex 1e9a50c6c73..edfce1f0cb8 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -462,8 +462,8 @@ int ie_modified(struct index_state *istate,\n \treturn 0;\n }\n \n-int base_name_compare(const char *name1, int len1, int mode1,\n-\t\t      const char *name2, int len2, int mode2)\n+int base_name_compare(const char *name1, int len1, int isdir1,\n+\t\t      const char *name2, int len2, int isdir2)\n {\n \tunsigned char c1, c2;\n \tint len = len1 < len2 ? len1 : len2;\n@@ -474,9 +474,9 @@ int base_name_compare(const char *name1, int len1, int mode1,\n \t\treturn cmp;\n \tc1 = name1[len];\n \tc2 = name2[len];\n-\tif (!c1 && S_ISDIR(mode1))\n+\tif (!c1 && isdir1)\n \t\tc1 = '/';\n-\tif (!c2 && S_ISDIR(mode2))\n+\tif (!c2 && isdir2)\n \t\tc2 = '/';\n \treturn (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;\n }\n@@ -491,8 +491,8 @@ int base_name_compare(const char *name1, int len1, int mode1,\n  * This is used by routines that want to traverse the git namespace\n  * but then handle conflicting entries together when possible.\n  */\n-int df_name_compare(const char *name1, int len1, int mode1,\n-\t\t    const char *name2, int len2, int mode2)\n+int df_name_compare(const char *name1, int len1, int isdir1,\n+\t\t    const char *name2, int len2, int isdir2)\n {\n \tint len = len1 < len2 ? len1 : len2, cmp;\n \tunsigned char c1, c2;\n@@ -504,10 +504,10 @@ int df_name_compare(const char *name1, int len1, int mode1,\n \tif (len1 == len2)\n \t\treturn 0;\n \tc1 = name1[len];\n-\tif (!c1 && S_ISDIR(mode1))\n+\tif (!c1 && isdir1)\n \t\tc1 = '/';\n \tc2 = name2[len];\n-\tif (!c2 && S_ISDIR(mode2))\n+\tif (!c2 && isdir2)\n \t\tc2 = '/';\n \tif (c1 == '/' && !c2)\n \t\treturn 0;\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 7cebbb327e2..f133834597c 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -50,6 +50,7 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)\n {\n \tstruct name_entry *e1, *e2;\n \tint cmp;\n+\tint e1_is_tree, e2_is_tree;\n \n \t/* empty descriptors sort after valid tree entries */\n \tif (!t1->size)\n@@ -58,9 +59,11 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)\n \t\treturn -1;\n \n \te1 = &t1->entry;\n+\te1_is_tree = e1->object_type == OBJ_TREE;\n \te2 = &t2->entry;\n-\tcmp = base_name_compare(e1->path, tree_entry_len(e1), e1->mode,\n-\t\t\t\te2->path, tree_entry_len(e2), e2->mode);\n+\te2_is_tree = e2->object_type == OBJ_TREE;\n+\tcmp = base_name_compare(e1->path, tree_entry_len(e1), e1_is_tree,\n+\t\t\t\te2->path, tree_entry_len(e2), e2_is_tree);\n \treturn cmp;\n }\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex f5f668f532d..802f7771d75 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -922,7 +922,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \t\t\t\t      const struct traverse_info *info,\n \t\t\t\t      const char *name, size_t namelen,\n-\t\t\t\t      unsigned mode)\n+\t\t\t\t      unsigned is_tree)\n {\n \tint pathlen, ce_len;\n \tconst char *ce_name;\n@@ -930,7 +930,7 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \tif (info->prev) {\n \t\tint cmp = do_compare_entry_piecewise(ce, info->prev,\n \t\t\t\t\t\t     info->name, info->namelen,\n-\t\t\t\t\t\t     info->mode);\n+\t\t\t\t\t\t     S_ISDIR(info->mode));\n \t\tif (cmp)\n \t\t\treturn cmp;\n \t}\n@@ -944,13 +944,13 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \tce_len -= pathlen;\n \tce_name = ce->name + pathlen;\n \n-\treturn df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n+\treturn df_name_compare(ce_name, ce_len, 0, name, namelen, is_tree);\n }\n \n static int do_compare_entry(const struct cache_entry *ce,\n \t\t\t    const struct traverse_info *info,\n \t\t\t    const char *name, size_t namelen,\n-\t\t\t    unsigned mode)\n+\t\t\t    unsigned is_tree)\n {\n \tint pathlen, ce_len;\n \tconst char *ce_name;\n@@ -962,7 +962,7 @@ static int do_compare_entry(const struct cache_entry *ce,\n \t * it is quicker to use the precomputed version.\n \t */\n \tif (!info->traverse_path)\n-\t\treturn do_compare_entry_piecewise(ce, info, name, namelen, mode);\n+\t\treturn do_compare_entry_piecewise(ce, info, name, namelen, is_tree);\n \n \tcmp = strncmp(ce->name, info->traverse_path, info->pathlen);\n \tif (cmp)\n@@ -977,12 +977,13 @@ static int do_compare_entry(const struct cache_entry *ce,\n \tce_len -= pathlen;\n \tce_name = ce->name + pathlen;\n \n-\treturn df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n+\treturn df_name_compare(ce_name, ce_len, 0, name, namelen, is_tree);\n }\n \n static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)\n {\n-\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);\n+\tint is_tree = n->object_type == OBJ_TREE;\n+\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, is_tree);\n \tif (cmp)\n \t\treturn cmp;\n \n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418462","messageId":"20210308150650.18626-8-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 07/30] tree-walk.h users: switch object_type(...) to new .object_type","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:27Z","receivedAt":"2021-03-08T15:08:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change uses of object_type(entry.mode) to use the new\nentry.object_type field.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/pack-objects.c |  2 +-\n http-push.c            |  6 ++++--\n pack-bitmap-write.c    |  8 +++++---\n revision.c             | 12 ++++++++----\n 4 files changed, 18 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 6d62aaf59a0..d3ba1d4a4a6 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -1534,7 +1534,7 @@ static void add_pbase_object(struct tree_desc *tree,\n \t\t\treturn;\n \t\tif (name[cmplen] != '/') {\n \t\t\tadd_object_entry(&entry.oid,\n-\t\t\t\t\t object_type(entry.mode),\n+\t\t\t\t\t entry.object_type,\n \t\t\t\t\t fullname, 1);\n \t\t\treturn;\n \t\t}\ndiff --git a/http-push.c b/http-push.c\nindex 6a4a43e07f2..234b79a5dba 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1314,7 +1314,7 @@ static struct object_list **process_tree(struct tree *tree,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry))\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tp = process_tree(lookup_tree(the_repository, &entry.oid),\n \t\t\t\t\t p);\n@@ -1323,9 +1323,11 @@ static struct object_list **process_tree(struct tree *tree,\n \t\t\tp = process_blob(lookup_blob(the_repository, &entry.oid),\n \t\t\t\t\t p);\n \t\t\tbreak;\n-\t\tdefault:\n+\t\tcase OBJ_COMMIT:\n \t\t\t/* Subproject commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \n \tfree_tree_buffer(tree);\ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex 88d9e696a54..ac32bf2242c 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -353,7 +353,7 @@ static void fill_bitmap_tree(struct bitmap *bitmap,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry)) {\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tfill_bitmap_tree(bitmap,\n \t\t\t\t\t lookup_tree(the_repository, &entry.oid));\n@@ -361,9 +361,11 @@ static void fill_bitmap_tree(struct bitmap *bitmap,\n \t\tcase OBJ_BLOB:\n \t\t\tbitmap_set(bitmap, find_object_pos(&entry.oid));\n \t\t\tbreak;\n-\t\tdefault:\n-\t\t\t/* Gitlink, etc; not reachable */\n+\t\tcase OBJ_COMMIT:\n+\t\t\t/* submodule commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n \ndiff --git a/revision.c b/revision.c\nindex b78733f5089..1db4e4e90a2 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -72,16 +72,18 @@ static void mark_tree_contents_uninteresting(struct repository *r,\n \n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \twhile (tree_entry(&desc, &entry)) {\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tmark_tree_uninteresting(r, lookup_tree(r, &entry.oid));\n \t\t\tbreak;\n \t\tcase OBJ_BLOB:\n \t\t\tmark_blob_uninteresting(lookup_blob(r, &entry.oid));\n \t\t\tbreak;\n-\t\tdefault:\n+\t\tcase OBJ_COMMIT:\n \t\t\t/* Subproject commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n \n@@ -179,7 +181,7 @@ static void add_children_by_path(struct repository *r,\n \n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \twhile (tree_entry(&desc, &entry)) {\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tpaths_and_oids_insert(map, entry.path, &entry.oid);\n \n@@ -196,9 +198,11 @@ static void add_children_by_path(struct repository *r,\n \t\t\t\t\tchild->object.flags |= UNINTERESTING;\n \t\t\t}\n \t\t\tbreak;\n-\t\tdefault:\n+\t\tcase OBJ_COMMIT:\n \t\t\t/* Subproject commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n \n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418471","messageId":"20210308150650.18626-9-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 08/30] tree.h: format argument lists of read_tree_recursive() users","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:28Z","receivedAt":"2021-03-08T15:08:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for adding a new argument to read_tree_fn_t re-indent\nand format the argument list of read_tree_recursive() callbacks, and\nthe relevant functions they call.\n\nThis is a whitespace-only change to make reading subsequent commits\neasier. I'll be adding a new argument on the \"mode\" line, so leave\nspace on it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          | 13 ++++++++-----\n builtin/checkout.c |  4 +++-\n builtin/log.c      |  5 +++--\n builtin/ls-tree.c  |  4 +++-\n merge-recursive.c  |  3 ++-\n tree.h             |  5 ++++-\n 6 files changed, 23 insertions(+), 11 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 6669a4bd147..db69a8acadb 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -138,8 +138,9 @@ static int check_attr_export_subst(const struct attr_check *check)\n }\n \n static int write_archive_entry(const struct object_id *oid, const char *base,\n-\t\tint baselen, const char *filename, unsigned mode, int stage,\n-\t\tvoid *context)\n+\t\t\t       int baselen, const char *filename,\n+\t\t\t       unsigned mode,\n+\t\t\t       int stage, void *context)\n {\n \tstatic struct strbuf path = STRBUF_INIT;\n \tstruct archiver_context *c = context;\n@@ -230,8 +231,9 @@ static int write_directory(struct archiver_context *c)\n }\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n-\t\tstruct strbuf *base, const char *filename,\n-\t\tunsigned mode, void *context)\n+\t\t\t\t\tstruct strbuf *base, const char *filename,\n+\t\t\t\t\tunsigned mode,\n+\t\t\t\t\tvoid *context)\n {\n \tstruct archiver_context *c = context;\n \tint stage = 0;\n@@ -378,7 +380,8 @@ struct path_exists_context {\n };\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\tconst char *filename, unsigned mode,\n+\t\t\tconst char *filename,\n+\t\t\tunsigned mode,\n \t\t\tvoid *context)\n {\n \tint ret = -1;\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2c2d58a230f..a78b54624b0 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -114,7 +114,9 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n }\n \n static int update_some(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, void *context)\n+\t\t       const char *pathname,\n+\t\t       unsigned mode,\n+\t\t       void *context)\n {\n \tint len;\n \tstruct cache_entry *ce;\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 58acb2b76ab..3766f553971 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -598,8 +598,9 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n }\n \n static int show_tree_object(const struct object_id *oid,\n-\t\tstruct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, void *context)\n+\t\t\t    struct strbuf *base, const char *pathname,\n+\t\t\t    unsigned mode,\n+\t\t\t    void *context)\n {\n \tFILE *file = context;\n \tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex dbb31217beb..aaa41e66234 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -62,7 +62,9 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\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+\t\t     const char *pathname,\n+\t\t     unsigned mode,\n+\t\t     void *context)\n {\n \tint retval = 0;\n \tint baselen;\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 0baa6b5ca56..aa12543ecc9 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,8 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   unsigned int mode, void *context)\n+\t\t\t   unsigned int mode,\n+\t\t\t   void *context)\n {\n \tstruct path_hashmap_entry *entry;\n \tint baselen = base->len;\ndiff --git a/tree.h b/tree.h\nindex 1309ab997e5..a7030e52679 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -31,7 +31,10 @@ struct tree *parse_tree_indirect(const struct object_id *oid);\n int cmp_cache_name_compare(const void *a_, const void *b_);\n \n #define READ_TREE_RECURSIVE 1\n-typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n+typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *,\n+\t\t\t      const char *,\n+\t\t\t      unsigned int,\n+\t\t\t      void *);\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418454","messageId":"20210308150650.18626-10-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 09/30] tree.h users: format argument lists in archive.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:29Z","receivedAt":"2021-03-08T15:08:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Re-indent and re-flow the argument lists archive.c has downstream of\nits read_tree_recursive() call to make subsequent commits easier to\nread, as I only expect to be modifying the \"stage\" and \"mode\" lines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c | 25 +++++++++++++++++--------\n 1 file changed, 17 insertions(+), 8 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex db69a8acadb..e245c0d5a54 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -140,7 +140,8 @@ static int check_attr_export_subst(const struct attr_check *check)\n static int write_archive_entry(const struct object_id *oid, const char *base,\n \t\t\t       int baselen, const char *filename,\n \t\t\t       unsigned mode,\n-\t\t\t       int stage, void *context)\n+\t\t\t       int stage,\n+\t\t\t       void *context)\n {\n \tstatic struct strbuf path = STRBUF_INIT;\n \tstruct archiver_context *c = context;\n@@ -197,8 +198,10 @@ static int write_archive_entry(const struct object_id *oid, const char *base,\n }\n \n static void queue_directory(const unsigned char *sha1,\n-\t\tstruct strbuf *base, const char *filename,\n-\t\tunsigned mode, int stage, struct archiver_context *c)\n+\t\t\t    struct strbuf *base, const char *filename,\n+\t\t\t    unsigned mode,\n+\t\t\t    int stage,\n+\t\t\t    struct archiver_context *c)\n {\n \tstruct directory *d;\n \tsize_t len = st_add4(base->len, 1, strlen(filename), 1);\n@@ -224,8 +227,10 @@ static int write_directory(struct archiver_context *c)\n \tret =\n \t\twrite_directory(c) ||\n \t\twrite_archive_entry(&d->oid, d->path, d->baselen,\n-\t\t\t\t    d->path + d->baselen, d->mode,\n-\t\t\t\t    d->stage, c) != READ_TREE_RECURSIVE;\n+\t\t\t\t    d->path + d->baselen,\n+\t\t\t\t    d->mode,\n+\t\t\t\t    d->stage,\n+\t\t\t\t    c) != READ_TREE_RECURSIVE;\n \tfree(d);\n \treturn ret ? -1 : 0;\n }\n@@ -259,14 +264,18 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\tif (check_attr_export_ignore(check))\n \t\t\treturn 0;\n \t\tqueue_directory(oid->hash, base, filename,\n-\t\t\t\tmode, stage, c);\n+\t\t\t\tmode,\n+\t\t\t\tstage,\n+\t\t\t\tc);\n \t\treturn READ_TREE_RECURSIVE;\n \t}\n \n \tif (write_directory(c))\n \t\treturn -1;\n-\treturn write_archive_entry(oid, base->buf, base->len, filename, mode,\n-\t\t\t\t   stage, context);\n+\treturn write_archive_entry(oid, base->buf, base->len, filename,\n+\t\t\t\t   mode,\n+\t\t\t\t   stage,\n+\t\t\t\t   context);\n }\n \n struct extra_file_info {\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418457","messageId":"20210308150650.18626-14-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 13/30] tree-walk.h users: refactor chained \"mode\" if/else into switch","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:33Z","receivedAt":"2021-03-08T15:08:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor a couple of \"switch\" statements that previously relied on\n\"entry.mode\" to switch on \"entry.object_type\" instead.\n\nThis is more obvious, and allows us to explicitly handle all the OBJ_*\ncases, not just have a wildcard \"else\". That doesn't matter for the\nbehavior of this code, but for its readability and maintainability.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n list-objects.c | 20 ++++++++++++++------\n walker.c       | 22 +++++++++++++---------\n 2 files changed, 27 insertions(+), 15 deletions(-)\n\ndiff --git a/list-objects.c b/list-objects.c\nindex e19589baa04..37434ba89d3 100644\n--- a/list-objects.c\n+++ b/list-objects.c\n@@ -111,6 +111,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry)) {\n+\t\tstruct tree *t;\n+\t\tstruct blob *b;\n+\n \t\tif (match != all_entries_interesting) {\n \t\t\tmatch = tree_entry_interesting(ctx->revs->repo->index,\n \t\t\t\t\t\t       &entry, base, 0,\n@@ -121,8 +124,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n \t\t\t\tcontinue;\n \t\t}\n \n-\t\tif (S_ISDIR(entry.mode)) {\n-\t\t\tstruct tree *t = lookup_tree(ctx->revs->repo, &entry.oid);\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_TREE:\n+\t\t\tt = lookup_tree(ctx->revs->repo, &entry.oid);\n \t\t\tif (!t) {\n \t\t\t\tdie(_(\"entry '%s' in tree %s has tree mode, \"\n \t\t\t\t      \"but is not a tree\"),\n@@ -130,12 +134,13 @@ static void process_tree_contents(struct traversal_context *ctx,\n \t\t\t}\n \t\t\tt->object.flags |= NOT_USER_GIVEN;\n \t\t\tprocess_tree(ctx, t, base, entry.path);\n-\t\t}\n-\t\telse if (S_ISGITLINK(entry.mode))\n+\t\t\tbreak;\n+\t\tcase OBJ_COMMIT:\n \t\t\tprocess_gitlink(ctx, entry.oid.hash,\n \t\t\t\t\tbase, entry.path);\n-\t\telse {\n-\t\t\tstruct blob *b = lookup_blob(ctx->revs->repo, &entry.oid);\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n+\t\t\tb = lookup_blob(ctx->revs->repo, &entry.oid);\n \t\t\tif (!b) {\n \t\t\t\tdie(_(\"entry '%s' in tree %s has blob mode, \"\n \t\t\t\t      \"but is not a blob\"),\n@@ -143,6 +148,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n \t\t\t}\n \t\t\tb->object.flags |= NOT_USER_GIVEN;\n \t\t\tprocess_blob(ctx, b, base, entry.path);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n }\ndiff --git a/walker.c b/walker.c\nindex 4984bf8b3d6..7ba757244e6 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -45,21 +45,25 @@ static int process_tree(struct walker *walker, struct tree *tree)\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \twhile (tree_entry(&desc, &entry)) {\n \t\tstruct object *obj = NULL;\n+\t\tstruct tree *tree;\n+\t\tstruct blob *blob;\n \n-\t\t/* submodule commits are not stored in the superproject */\n-\t\tif (S_ISGITLINK(entry.mode))\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_COMMIT:\n+\t\t\t/* submodule commits are not stored in the superproject */\n \t\t\tcontinue;\n-\t\tif (S_ISDIR(entry.mode)) {\n-\t\t\tstruct tree *tree = lookup_tree(the_repository,\n-\t\t\t\t\t\t\t&entry.oid);\n+\t\tcase OBJ_TREE:\n+\t\t\ttree = lookup_tree(the_repository, &entry.oid);\n \t\t\tif (tree)\n \t\t\t\tobj = &tree->object;\n-\t\t}\n-\t\telse {\n-\t\t\tstruct blob *blob = lookup_blob(the_repository,\n-\t\t\t\t\t\t\t&entry.oid);\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n+\t\t\tblob = lookup_blob(the_repository, &entry.oid);\n \t\t\tif (blob)\n \t\t\t\tobj = &blob->object;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t\tif (!obj || process(walker, obj))\n \t\t\treturn -1;\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418458","messageId":"20210308150650.18626-12-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 11/30] tree.h API: make read_tree_fn_t take an \"enum object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:31Z","receivedAt":"2021-03-08T15:08:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Most of the users of the read_tree_fn_t callback do not care about the\n\"mode\" per-se, they just care what type it resolves to.\n\nAmend this callback mechanism added in 3c5e8468a93 (ls-tree: major\nrewrite to do pathspec, 2005-11-26) to pass the object_type, and use\nit whenever possible.\n\nIn the archive.c code we could go much deeper with this refactoring,\nafter getting the \"mode\" that code will pass it around itself and into\narchive-{tar,zip}.c. As far as I can tell we could drop the mode\nearly, and just pass \"enum_object_type type, int is_executable\". That\nwould be slightly redundant space-wise, but would assure us that we're\nnot writing out raw modes found in trees, but are normalizing them.\n\nBut that particular refactoring would be larger than what I'm trying\nto accomplish here, so let's leave it for now.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          |  8 ++++----\n builtin/checkout.c |  4 ++--\n builtin/log.c      |  4 ++--\n builtin/ls-files.c |  6 ++++--\n builtin/ls-tree.c  | 12 +++++-------\n merge-recursive.c  |  6 ++++--\n tree.c             | 19 ++++++++++++-------\n tree.h             |  2 +-\n 8 files changed, 34 insertions(+), 27 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 26534a30d3b..64abe736f93 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -232,7 +232,7 @@ static int write_directory(struct archiver_context *c)\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\t\t\t\tstruct strbuf *base, const char *filename,\n-\t\t\t\t\tunsigned mode,\n+\t\t\t\t\tenum object_type object_type, unsigned mode,\n \t\t\t\t\tvoid *context)\n {\n \tstruct archiver_context *c = context;\n@@ -245,7 +245,7 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\tc->bottom = next;\n \t}\n \n-\tif (S_ISDIR(mode)) {\n+\tif (object_type == OBJ_TREE) {\n \t\tsize_t baselen = base->len;\n \t\tconst struct attr_check *check;\n \n@@ -382,13 +382,13 @@ struct path_exists_context {\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n \t\t\tconst char *filename,\n-\t\t\tunsigned mode,\n+\t\t\tenum object_type object_type, unsigned mode,\n \t\t\tvoid *context)\n {\n \tint ret = -1;\n \tstruct path_exists_context *ctx = context;\n \n-\tif (S_ISDIR(mode)) {\n+\tif (object_type == OBJ_TREE) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tstrbuf_addbuf(&sb, base);\n \t\tstrbuf_addstr(&sb, filename);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex a78b54624b0..d4adfdb5046 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -115,14 +115,14 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n \n static int update_some(const struct object_id *oid, struct strbuf *base,\n \t\t       const char *pathname,\n-\t\t       unsigned mode,\n+\t\t       enum object_type object_type, unsigned mode,\n \t\t       void *context)\n {\n \tint len;\n \tstruct cache_entry *ce;\n \tint pos;\n \n-\tif (S_ISDIR(mode))\n+\tif (object_type == OBJ_TREE)\n \t\treturn READ_TREE_RECURSIVE;\n \n \tlen = base->len + strlen(pathname);\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 3766f553971..19a916221d5 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -599,11 +599,11 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n \n static int show_tree_object(const struct object_id *oid,\n \t\t\t    struct strbuf *base, const char *pathname,\n-\t\t\t    unsigned mode,\n+\t\t\t    enum object_type object_type, unsigned mode,\n \t\t\t    void *context)\n {\n \tFILE *file = context;\n-\tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n+\tfprintf(file, \"%s%s\\n\", pathname, object_type == OBJ_TREE ? \"/\" : \"\");\n \treturn 0;\n }\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex fa9b01b6cc7..f38df439410 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -446,7 +446,8 @@ static int read_one_entry_opt(struct index_state *istate,\n }\n \n static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\t  const char *pathname, unsigned mode,\n+\t\t\t  const char *pathname,\n+\t\t\t  enum object_type object_type, unsigned mode,\n \t\t\t  void *context)\n {\n \tstruct index_state *istate = context;\n@@ -460,7 +461,8 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n  * the stage that will conflict with the entry being added.\n  */\n static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n-\t\t\t\tconst char *pathname, unsigned mode,\n+\t\t\t\tconst char *pathname,\n+\t\t\t\tenum object_type object_type, unsigned mode,\n \t\t\t\tvoid *context)\n {\n \tstruct index_state *istate = context;\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex aaa41e66234..c6ec3ca751e 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -63,14 +63,13 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n \n static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t     const char *pathname,\n-\t\t     unsigned mode,\n+\t\t     enum object_type object_type, unsigned mode,\n \t\t     void *context)\n {\n \tint retval = 0;\n \tint baselen;\n-\tconst char *type = blob_type;\n \n-\tif (S_ISGITLINK(mode)) {\n+\tif (object_type == OBJ_COMMIT) {\n \t\t/*\n \t\t * Maybe we want to have some recursive version here?\n \t\t *\n@@ -80,22 +79,21 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\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} else if (object_type == OBJ_TREE) {\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}\n \telse if (ls_options & LS_TREE_ONLY)\n \t\treturn 0;\n \n \tif (!(ls_options & LS_NAME_ONLY)) {\n+\t\tconst char *type = type_name(object_type);\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 (object_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),\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex aa12543ecc9..31c080538ef 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,7 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   unsigned int mode,\n+\t\t\t   enum object_type object_type, unsigned int mode,\n \t\t\t   void *context)\n {\n \tstruct path_hashmap_entry *entry;\n@@ -467,7 +467,9 @@ static int save_files_dirs(const struct object_id *oid,\n \thashmap_add(&opt->priv->current_file_dir_set, &entry->e);\n \n \tstrbuf_setlen(base, baselen);\n-\treturn (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);\n+\tif (object_type != OBJ_TREE)\n+\t\treturn 0;\n+\treturn READ_TREE_RECURSIVE;\n }\n \n static void get_files_dirs(struct merge_options *opt, struct tree *tree)\ndiff --git a/tree.c b/tree.c\nindex fb4985f22ca..e4402fad69b 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -28,6 +28,8 @@ static int read_tree_1(struct repository *r,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry)) {\n+\t\tstruct commit *commit;\n+\n \t\tif (retval != all_entries_interesting) {\n \t\t\tretval = tree_entry_interesting(r->index, &entry,\n \t\t\t\t\t\t\tbase, 0, pathspec);\n@@ -38,7 +40,7 @@ static int read_tree_1(struct repository *r,\n \t\t}\n \n \t\tswitch (fn(&entry.oid, base,\n-\t\t\t   entry.path, entry.mode, context)) {\n+\t\t\t   entry.path, entry.object_type, entry.mode, context)) {\n \t\tcase 0:\n \t\t\tcontinue;\n \t\tcase READ_TREE_RECURSIVE:\n@@ -47,11 +49,11 @@ static int read_tree_1(struct repository *r,\n \t\t\treturn -1;\n \t\t}\n \n-\t\tif (S_ISDIR(entry.mode))\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_TREE:\n \t\t\toidcpy(&oid, &entry.oid);\n-\t\telse if (S_ISGITLINK(entry.mode)) {\n-\t\t\tstruct commit *commit;\n-\n+\t\t\tbreak;\n+\t\tcase OBJ_COMMIT:\n \t\t\tcommit = lookup_commit(r, &entry.oid);\n \t\t\tif (!commit)\n \t\t\t\tdie(\"Commit %s in submodule path %s%s not found\",\n@@ -64,9 +66,12 @@ static int read_tree_1(struct repository *r,\n \t\t\t\t    base->buf, entry.path);\n \n \t\t\toidcpy(&oid, get_commit_tree_oid(commit));\n-\t\t}\n-\t\telse\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n \t\t\tcontinue;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n+\t\t}\n \n \t\tlen = tree_entry_len(&entry);\n \t\tstrbuf_add(base, entry.path, len);\ndiff --git a/tree.h b/tree.h\nindex a7030e52679..eba51417d26 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -33,7 +33,7 @@ int cmp_cache_name_compare(const void *a_, const void *b_);\n #define READ_TREE_RECURSIVE 1\n typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *,\n \t\t\t      const char *,\n-\t\t\t      unsigned int,\n+\t\t\t      enum object_type, unsigned int,\n \t\t\t      void *);\n \n int read_tree_recursive(struct repository *r,\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418459","messageId":"20210308150650.18626-11-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 10/30] archive: get rid of 'stage' parameter","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:30Z","receivedAt":"2021-03-08T15:08:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Stop passing the \"stage\" parameter around in archive.c. This parameter\nexisted because the read_tree_recursive() function used to provide it,\nbut no longer does. See my in-flight commit to remove it. (tree.h API:\nremove \"stage\" parameter from read_tree_recursive(), 2021-03-06).\n\nAs can be seen in 562e25abea9 (archive: centralize archive entry\nwriting, 2008-07-14) and ed22b4173bd (archive: support filtering paths\nwith glob, 2014-09-21) it was never used by this code. We simply added\nit as a boilerplate, and then later added it to our own \"directory\n\"struct.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c | 8 --------\n 1 file changed, 8 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex e245c0d5a54..26534a30d3b 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -107,7 +107,6 @@ struct directory {\n \tstruct object_id oid;\n \tint baselen, len;\n \tunsigned mode;\n-\tint stage;\n \tchar path[FLEX_ARRAY];\n };\n \n@@ -140,7 +139,6 @@ static int check_attr_export_subst(const struct attr_check *check)\n static int write_archive_entry(const struct object_id *oid, const char *base,\n \t\t\t       int baselen, const char *filename,\n \t\t\t       unsigned mode,\n-\t\t\t       int stage,\n \t\t\t       void *context)\n {\n \tstatic struct strbuf path = STRBUF_INIT;\n@@ -200,7 +198,6 @@ static int write_archive_entry(const struct object_id *oid, const char *base,\n static void queue_directory(const unsigned char *sha1,\n \t\t\t    struct strbuf *base, const char *filename,\n \t\t\t    unsigned mode,\n-\t\t\t    int stage,\n \t\t\t    struct archiver_context *c)\n {\n \tstruct directory *d;\n@@ -209,7 +206,6 @@ static void queue_directory(const unsigned char *sha1,\n \td->up\t   = c->bottom;\n \td->baselen = base->len;\n \td->mode\t   = mode;\n-\td->stage   = stage;\n \tc->bottom  = d;\n \td->len = xsnprintf(d->path, len, \"%.*s%s/\", (int)base->len, base->buf, filename);\n \thashcpy(d->oid.hash, sha1);\n@@ -229,7 +225,6 @@ static int write_directory(struct archiver_context *c)\n \t\twrite_archive_entry(&d->oid, d->path, d->baselen,\n \t\t\t\t    d->path + d->baselen,\n \t\t\t\t    d->mode,\n-\t\t\t\t    d->stage,\n \t\t\t\t    c) != READ_TREE_RECURSIVE;\n \tfree(d);\n \treturn ret ? -1 : 0;\n@@ -241,7 +236,6 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\t\t\t\tvoid *context)\n {\n \tstruct archiver_context *c = context;\n-\tint stage = 0;\n \n \twhile (c->bottom &&\n \t       !(base->len >= c->bottom->len &&\n@@ -265,7 +259,6 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\t\treturn 0;\n \t\tqueue_directory(oid->hash, base, filename,\n \t\t\t\tmode,\n-\t\t\t\tstage,\n \t\t\t\tc);\n \t\treturn READ_TREE_RECURSIVE;\n \t}\n@@ -274,7 +267,6 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\treturn -1;\n \treturn write_archive_entry(oid, base->buf, base->len, filename,\n \t\t\t\t   mode,\n-\t\t\t\t   stage,\n \t\t\t\t   context);\n }\n \n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418460","messageId":"20210308150650.18626-22-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 21/30] tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:41Z","receivedAt":"2021-03-08T15:08:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor code added in 85e51b783c3 (Make \"subtree\" part more\northogonal to the rest of merge-recursive., 2008-06-30) to make it\nobvious that we don't care about the \"mode\" here outside of the if\nstatement it appears in.\n\nThat's opposed to the sub1 & sub2 variables, where we use the two\nobject ids later in this function.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c | 10 +++++-----\n 1 file changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex ba4aabf39d1..4f02768c01e 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -317,17 +317,17 @@ void shift_tree_by(struct repository *r,\n \t\t   const char *shift_prefix)\n {\n \tstruct object_id sub1, sub2;\n-\tunsigned short mode1, mode2;\n+\tunsigned short tmp;\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n-\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &mode1) &&\n-\t    S_ISDIR(mode1))\n+\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n+\t    S_ISDIR(tmp))\n \t\tcandidate |= 1;\n \n \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n-\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &mode2) &&\n-\t    S_ISDIR(mode2))\n+\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n+\t    S_ISDIR(tmp))\n \t\tcandidate |= 2;\n \n \tif (candidate == 3) {\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418463","messageId":"20210308150650.18626-19-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 18/30] tree-walk.h users: use temporary variable(s) for \"mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:38Z","receivedAt":"2021-03-08T15:08:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for an eventual rename of the \"mode\" field, add\ntemporary variable(s) in those places where it's used more than once.\n\nThis will make a subsequent commits easier to read., since we're only\ngoing to need to modify the line on which the assignment happens.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/merge-tree.c | 12 +++++++++---\n match-trees.c        | 13 +++++++------\n merge-ort.c          |  5 +++--\n notes.c              |  3 ++-\n tree-diff.c          | 13 ++++++++-----\n unpack-trees.c       |  3 ++-\n 6 files changed, 31 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex 12cb317c1ba..eec5b906561 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -190,14 +190,17 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s\n {\n \tstruct merge_list *orig, *final;\n \tconst char *path;\n+\tunsigned int orig_mode, final_mode;\n \n \t/* If it's already ours, don't bother showing it */\n \tif (!ours)\n \t\treturn;\n \n \tpath = traverse_path(info, result);\n-\torig = create_entry(2, ours->mode, &ours->oid, path);\n-\tfinal = create_entry(0, result->mode, &result->oid, path);\n+\torig_mode = ours->mode;\n+\torig = create_entry(2, orig_mode, &ours->oid, path);\n+\tfinal_mode = result->mode;\n+\tfinal = create_entry(0, final_mode, &result->oid, path);\n \n \tfinal->link = orig;\n \n@@ -241,6 +244,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n {\n \tconst char *path;\n \tstruct merge_list *link;\n+\tunsigned int link_mode;\n \n \tif (n->object_type == OBJ_NONE)\n \t\treturn entry;\n@@ -248,7 +252,9 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n \t\tpath = entry->path;\n \telse\n \t\tpath = traverse_path(info, n);\n-\tlink = create_entry(stage, n->mode, &n->oid, path);\n+\tlink_mode = n->mode;\n+\tlink = create_entry(stage, link_mode, &n->oid, path);\n+\n \tlink->link = entry;\n \treturn link;\n }\ndiff --git a/match-trees.c b/match-trees.c\nindex 4d124a0fd1b..07159172b63 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -86,6 +86,8 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha\n \n \tfor (;;) {\n \t\tint cmp;\n+\t\tunsigned int one_mode = one.entry.mode;\n+\t\tunsigned int two_mode = two.entry.mode;\n \n \t\tif (one.size && two.size)\n \t\t\tcmp = base_name_entries_compare(&one.entry, &two.entry);\n@@ -100,22 +102,21 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha\n \n \t\tif (cmp < 0) {\n \t\t\t/* path1 does not appear in two */\n-\t\t\tscore += score_missing(one.entry.mode);\n+\t\t\tscore += score_missing(one_mode);\n \t\t\tupdate_tree_entry(&one);\n \t\t} else if (cmp > 0) {\n \t\t\t/* path2 does not appear in one */\n-\t\t\tscore += score_missing(two.entry.mode);\n+\t\t\tscore += score_missing(two_mode);\n \t\t\tupdate_tree_entry(&two);\n \t\t} else {\n+\n \t\t\t/* path appears in both */\n \t\t\tif (!oideq(&one.entry.oid, &two.entry.oid)) {\n \t\t\t\t/* they are different */\n-\t\t\t\tscore += score_differs(one.entry.mode,\n-\t\t\t\t\t\t       two.entry.mode);\n+\t\t\t\tscore += score_differs(one_mode, two_mode);\n \t\t\t} else {\n \t\t\t\t/* same subtree or blob */\n-\t\t\t\tscore += score_matches(one.entry.mode,\n-\t\t\t\t\t\t       two.entry.mode);\n+\t\t\t\tscore += score_matches(one_mode, two_mode);\n \t\t\t}\n \t\t\tupdate_tree_entry(&one);\n \t\t\tupdate_tree_entry(&two);\ndiff --git a/merge-ort.c b/merge-ort.c\nindex e54be179bd5..cad10436504 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -544,11 +544,12 @@ static void add_pair(struct merge_options *opt,\n \tstruct diff_filespec *one, *two;\n \tstruct rename_info *renames = &opt->priv->renames;\n \tint names_idx = is_add ? side : 0;\n+\tconst struct object_id *oid = &names[names_idx].oid;\n+\tunsigned int mode = names[names_idx].mode;\n \n \tone = alloc_filespec(pathname);\n \ttwo = alloc_filespec(pathname);\n-\tfill_filespec(is_add ? two : one,\n-\t\t      &names[names_idx].oid, 1, names[names_idx].mode);\n+\tfill_filespec(is_add ? two : one, oid, 1, mode);\n \tdiff_queue(&renames->pairs[side], one, two);\n }\n \ndiff --git a/notes.c b/notes.c\nindex d631dc5623e..688d03ee9cd 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -478,6 +478,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\tstruct strbuf non_note_path = STRBUF_INIT;\n \t\t\tconst char *q = oid_to_hex(&subtree->key_oid);\n \t\t\tsize_t i;\n+\t\t\tunsigned int mode = entry.mode;\n \t\t\tfor (i = 0; i < prefix_len; i++) {\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n@@ -485,7 +486,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t}\n \t\t\tstrbuf_add(&non_note_path, entry.path, path_len);\n \t\t\tadd_non_note(t, strbuf_detach(&non_note_path, NULL),\n-\t\t\t\t     entry.mode, entry.oid.hash);\n+\t\t\t\t     mode, entry.oid.hash);\n \t\t}\n \t}\n \tfree(buf);\ndiff --git a/tree-diff.c b/tree-diff.c\nindex f133834597c..f145ff84c68 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -466,17 +466,19 @@ static struct combine_diff_path *ll_diff_tree_paths(\n \t\ttp[0].entry.mode &= ~S_IFXMIN_NEQ;\n \n \t\tfor (i = 1; i < nparent; ++i) {\n+\t\t\tunsigned int mode = tp[i].entry.mode;\n \t\t\tcmp = tree_entry_pathcmp(&tp[i], &tp[imin]);\n \t\t\tif (cmp < 0) {\n \t\t\t\timin = i;\n-\t\t\t\ttp[i].entry.mode &= ~S_IFXMIN_NEQ;\n+\t\t\t\tmode &= ~S_IFXMIN_NEQ;\n \t\t\t}\n \t\t\telse if (cmp == 0) {\n-\t\t\t\ttp[i].entry.mode &= ~S_IFXMIN_NEQ;\n+\t\t\t\tmode &= ~S_IFXMIN_NEQ;\n \t\t\t}\n \t\t\telse {\n-\t\t\t\ttp[i].entry.mode |= S_IFXMIN_NEQ;\n+\t\t\t\tmode |= S_IFXMIN_NEQ;\n \t\t\t}\n+\t\t\ttp[i].entry.mode = mode;\n \t\t}\n \n \t\t/* fixup markings for entries before imin */\n@@ -493,13 +495,14 @@ static struct combine_diff_path *ll_diff_tree_paths(\n \t\t\t/* are either pi > p[imin] or diff(t,pi) != ø ? */\n \t\t\tif (!opt->flags.find_copies_harder) {\n \t\t\t\tfor (i = 0; i < nparent; ++i) {\n+\t\t\t\t\tunsigned int mode = tp[i].entry.mode;\n \t\t\t\t\t/* p[i] > p[imin] */\n-\t\t\t\t\tif (tp[i].entry.mode & S_IFXMIN_NEQ)\n+\t\t\t\t\tif (mode & S_IFXMIN_NEQ)\n \t\t\t\t\t\tcontinue;\n \n \t\t\t\t\t/* diff(t,pi) != ø */\n \t\t\t\t\tif (!oideq(&t.entry.oid, &tp[i].entry.oid) ||\n-\t\t\t\t\t    (t.entry.mode != tp[i].entry.mode))\n+\t\t\t\t\t    (t.entry.mode != mode))\n \t\t\t\t\t\tcontinue;\n \n \t\t\t\t\tgoto skip_emit_t_tp;\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex e24302f45ba..9471c19de72 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1020,8 +1020,9 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,\n \t\tis_transient ?\n \t\tmake_empty_transient_cache_entry(len) :\n \t\tmake_empty_cache_entry(istate, len);\n+\tunsigned int mode = n->mode;\n \n-\tce->ce_mode = create_ce_mode(n->mode);\n+\tce->ce_mode = create_ce_mode(mode);\n \tce->ce_flags = create_ce_flags(stage);\n \tce->ce_namelen = len;\n \toidcpy(&ce->oid, &n->oid);\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418464","messageId":"20210308150650.18626-21-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 20/30] tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:40Z","receivedAt":"2021-03-08T15:08:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the get_tree_entry() function to get_tree_entry_mode(). This\nchange is only a search-replacement of the name and indentation of the\nargument lists.\n\nA subsequent commits will add get_tree_entry_type() and\nget_tree_entry_all() functions. Those changes will be much easier to\nread if we do this rename first.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c              |  8 ++++----\n blame.c                |  6 +++---\n builtin/rm.c           |  2 +-\n builtin/update-index.c |  2 +-\n line-log.c             |  2 +-\n match-trees.c          |  6 +++---\n merge-recursive.c      | 18 +++++++++---------\n notes.c                |  2 +-\n object-name.c          |  6 +++---\n tree-walk.c            | 14 +++++++-------\n tree-walk.h            |  6 +++---\n 11 files changed, 36 insertions(+), 36 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 64abe736f93..95fa759e1fb 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -482,10 +482,10 @@ static void parse_treeish_arg(const char **argv,\n \t\tunsigned short mode;\n \t\tint err;\n \n-\t\terr = get_tree_entry(ar_args->repo,\n-\t\t\t\t     &tree->object.oid,\n-\t\t\t\t     prefix, &tree_oid,\n-\t\t\t\t     &mode);\n+\t\terr = get_tree_entry_mode(ar_args->repo,\n+\t\t\t\t\t  &tree->object.oid,\n+\t\t\t\t\t  prefix, &tree_oid,\n+\t\t\t\t\t  &mode);\n \t\tif (err || !S_ISDIR(mode))\n \t\t\tdie(_(\"current working directory is untracked\"));\n \ndiff --git a/blame.c b/blame.c\nindex 83babc41d08..9e0543e13d4 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -102,8 +102,8 @@ static void verify_working_tree_path(struct repository *r,\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n \t\tstruct object_id blob_oid;\n \t\tunsigned short mode;\n-\t\tint ret = get_tree_entry(r, commit_oid, path, &blob_oid,\n-\t\t\t\t\t &mode);\n+\t\tint ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n+\t\t\t\t\t      &mode);\n \n \t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n \t\t\treturn;\n@@ -1239,7 +1239,7 @@ static int fill_blob_sha1_and_mode(struct repository *r,\n {\n \tif (!is_null_oid(&origin->blob_oid))\n \t\treturn 0;\n-\tif (get_tree_entry(r, &origin->commit->object.oid, origin->path, &origin->blob_oid, &origin->mode))\n+\tif (get_tree_entry_mode(r, &origin->commit->object.oid, origin->path, &origin->blob_oid, &origin->mode))\n \t\tgoto error_out;\n \tif (oid_object_info(r, &origin->blob_oid, NULL) != OBJ_BLOB)\n \t\tgoto error_out;\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex 4858631e0f0..4617388b29a 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -179,7 +179,7 @@ static int check_local_mod(struct object_id *head, int index_only)\n \t\t * way as changed from the HEAD.\n \t\t */\n \t\tif (no_head\n-\t\t     || get_tree_entry(the_repository, head, name, &oid, &mode)\n+\t\t     || get_tree_entry_mode(the_repository, head, name, &oid, &mode)\n \t\t     || ce->ce_mode != create_ce_mode(mode)\n \t\t     || !oideq(&ce->oid, &oid))\n \t\t\tstaged_changes = 1;\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 79087bccea4..070510d6a88 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -603,7 +603,7 @@ static struct cache_entry *read_one_ent(const char *which,\n \tstruct object_id oid;\n \tstruct cache_entry *ce;\n \n-\tif (get_tree_entry(the_repository, ent, path, &oid, &mode)) {\n+\tif (get_tree_entry_mode(the_repository, ent, path, &oid, &mode)) {\n \t\tif (which)\n \t\t\terror(\"%s: not in %s branch.\", path, which);\n \t\treturn NULL;\ndiff --git a/line-log.c b/line-log.c\nindex 75c8b1acfff..4790dda4607 100644\n--- a/line-log.c\n+++ b/line-log.c\n@@ -503,7 +503,7 @@ static void fill_blob_sha1(struct repository *r, struct commit *commit,\n \tunsigned short mode;\n \tstruct object_id oid;\n \n-\tif (get_tree_entry(r, &commit->object.oid, spec->path, &oid, &mode))\n+\tif (get_tree_entry_mode(r, &commit->object.oid, spec->path, &oid, &mode))\n \t\tdie(\"There is no path %s in the commit\", spec->path);\n \tfill_filespec(spec, &oid, 1, mode);\n \ndiff --git a/match-trees.c b/match-trees.c\nindex 07159172b63..ba4aabf39d1 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -293,7 +293,7 @@ void shift_tree(struct repository *r,\n \t\tif (!*del_prefix)\n \t\t\treturn;\n \n-\t\tif (get_tree_entry(r, hash2, del_prefix, shifted, &mode))\n+\t\tif (get_tree_entry_mode(r, hash2, del_prefix, shifted, &mode))\n \t\t\tdie(\"cannot find path %s in tree %s\",\n \t\t\t    del_prefix, oid_to_hex(hash2));\n \t\treturn;\n@@ -321,12 +321,12 @@ void shift_tree_by(struct repository *r,\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n-\tif (!get_tree_entry(r, hash1, shift_prefix, &sub1, &mode1) &&\n+\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &mode1) &&\n \t    S_ISDIR(mode1))\n \t\tcandidate |= 1;\n \n \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n-\tif (!get_tree_entry(r, hash2, shift_prefix, &sub2, &mode2) &&\n+\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &mode2) &&\n \t    S_ISDIR(mode2))\n \t\tcandidate |= 2;\n \ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 31c080538ef..0e891360e7e 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -487,7 +487,7 @@ static int get_tree_entry_if_blob(struct repository *r,\n {\n \tint ret;\n \n-\tret = get_tree_entry(r, tree, path, &dfs->oid, &dfs->mode);\n+\tret = get_tree_entry_mode(r, tree, path, &dfs->oid, &dfs->mode);\n \tif (S_ISDIR(dfs->mode)) {\n \t\toidcpy(&dfs->oid, &null_oid);\n \t\tdfs->mode = 0;\n@@ -1886,9 +1886,9 @@ static int tree_has_path(struct repository *r, struct tree *tree,\n \tstruct object_id hashy;\n \tunsigned short mode_o;\n \n-\treturn !get_tree_entry(r,\n-\t\t\t       &tree->object.oid, path,\n-\t\t\t       &hashy, &mode_o);\n+\treturn !get_tree_entry_mode(r,\n+\t\t\t\t    &tree->object.oid, path,\n+\t\t\t\t    &hashy, &mode_o);\n }\n \n /*\n@@ -2541,11 +2541,11 @@ static void apply_directory_rename_modifications(struct merge_options *opt,\n \t * the various handle_rename_*() functions update the index\n \t * explicitly rather than relying on unpack_trees() to have done it.\n \t */\n-\tget_tree_entry(opt->repo,\n-\t\t       &tree->object.oid,\n-\t\t       pair->two->path,\n-\t\t       &re->dst_entry->stages[stage].oid,\n-\t\t       &re->dst_entry->stages[stage].mode);\n+\tget_tree_entry_mode(opt->repo,\n+\t\t\t    &tree->object.oid,\n+\t\t\t    pair->two->path,\n+\t\t\t    &re->dst_entry->stages[stage].oid,\n+\t\t\t    &re->dst_entry->stages[stage].mode);\n \n \t/*\n \t * Record the original change status (or 'type' of change).  If it\ndiff --git a/notes.c b/notes.c\nindex 688d03ee9cd..ef138606146 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -1021,7 +1021,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\treturn;\n \tif (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))\n \t\tdie(\"Cannot use notes ref %s\", notes_ref);\n-\tif (get_tree_entry(the_repository, &object_oid, \"\", &oid, &mode))\n+\tif (get_tree_entry_mode(the_repository, &object_oid, \"\", &oid, &mode))\n \t\tdie(\"Failed to read notes tree referenced by %s (%s)\",\n \t\t    notes_ref, oid_to_hex(&object_oid));\n \ndiff --git a/object-name.c b/object-name.c\nindex 64202de60b1..7e3b2d6d739 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1704,7 +1704,7 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \tif (is_missing_file_error(errno)) {\n \t\tchar *fullname = xstrfmt(\"%s%s\", prefix, filename);\n \n-\t\tif (!get_tree_entry(r, tree_oid, fullname, &oid, &mode)) {\n+\t\tif (!get_tree_entry_mode(r, tree_oid, fullname, &oid, &mode)) {\n \t\t\tdie(_(\"path '%s' exists, but not '%s'\\n\"\n \t\t\t    \"hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?\"),\n \t\t\t    fullname,\n@@ -1903,8 +1903,8 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo,\n \t\t\t\t\tfilename, oid, &oc->symlink_path,\n \t\t\t\t\t&oc->mode);\n \t\t\t} else {\n-\t\t\t\tret = get_tree_entry(repo, &tree_oid, filename, oid,\n-\t\t\t\t\t\t     &oc->mode);\n+\t\t\t\tret = get_tree_entry_mode(repo, &tree_oid, filename, oid,\n+\t\t\t\t\t\t\t  &oc->mode);\n \t\t\t\tif (ret && only_to_die) {\n \t\t\t\t\tdiagnose_invalid_oid_path(repo, prefix,\n \t\t\t\t\t\t\t\t   filename,\ndiff --git a/tree-walk.c b/tree-walk.c\nindex e88187e3714..7819ff3e0ec 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -591,17 +591,17 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\toidcpy(result, &oid);\n \t\t\treturn 0;\n \t\t}\n-\t\treturn get_tree_entry(r, &oid, name + entrylen, result,\n-\t\t\t\t      mode);\n+\t\treturn get_tree_entry_mode(r, &oid, name + entrylen, result,\n+\t\t\t\t\t   mode);\n \t}\n \treturn -1;\n }\n \n-int get_tree_entry(struct repository *r,\n-\t\t   const struct object_id *tree_oid,\n-\t\t   const char *name,\n-\t\t   struct object_id *oid,\n-\t\t   unsigned short *mode)\n+int get_tree_entry_mode(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid,\n+\t\t\tunsigned short *mode)\n {\n \tint retval;\n \tvoid *tree;\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 478a659ee2b..eb9b9de6ccc 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -174,9 +174,9 @@ struct traverse_info {\n  * The third and fourth parameters are set to the entry's sha1 and\n  * mode respectively.\n  */\n-int get_tree_entry(struct repository *, const struct object_id *, const char *,\n-\t\t   struct object_id *,\n-\t\t   unsigned short *);\n+int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n+\t\t\tstruct object_id *,\n+\t\t\tunsigned short *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418465","messageId":"20210308150650.18626-17-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 16/30] merge-ort: correct reference to test in 62fdec17a11","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:36Z","receivedAt":"2021-03-08T15:08:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a comment added in 62fdec17a11 (merge-ort: flesh out\nimplementation of handle_content_merge(), 2021-01-01).\n\nThe test being referred to here was moved from t6036 in\n919df319555 (Collect merge-related tests to t64xx, 2020-08-10).\n\nIt has also had the plural of \"mode\" in the name ever since being\nintroduced in 5d1daf30cce (t6036: add a failed conflict detection\ncase: regular files, different modes, 2018-06-30).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n merge-ort.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 4375027914c..e54be179bd5 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -1079,7 +1079,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t/*\n \t\t * FIXME: If opt->priv->call_depth && !clean, then we really\n \t\t * should not make result->mode match either a->mode or\n-\t\t * b->mode; that causes t6036 \"check conflicting mode for\n+\t\t * b->mode; that causes t6416 \"check conflicting modes for\n \t\t * regular file\" to fail.  It would be best to use some other\n \t\t * mode, but we'll confuse all kinds of stuff if we use one\n \t\t * where S_ISREG(result->mode) isn't true, and if we use\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418467","messageId":"20210308150650.18626-23-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 22/30] tree-walk.h API: Add get_tree_entry_type()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:42Z","receivedAt":"2021-03-08T15:08:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a get_tree_entry_type() helper function to compliment the existing\nget_tree_entry(). Move those users of get_tree_entry_type() who didn't\ncare about the mode specifically, but just want to know whether the\ntree entry is one of OBJ_{BLOB,COMMIT,TREE} over to it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c     |  8 ++++----\n blame.c       |  8 ++++----\n match-trees.c | 10 +++++-----\n tree-walk.c   | 47 ++++++++++++++++++++++++++++++++++++-----------\n tree-walk.h   | 15 +++++++++++++--\n 5 files changed, 62 insertions(+), 26 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 95fa759e1fb..bc8f1c7546f 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -479,14 +479,14 @@ static void parse_treeish_arg(const char **argv,\n \n \tif (prefix) {\n \t\tstruct object_id tree_oid;\n-\t\tunsigned short mode;\n+\t\tenum object_type object_type;\n \t\tint err;\n \n-\t\terr = get_tree_entry_mode(ar_args->repo,\n+\t\terr = get_tree_entry_type(ar_args->repo,\n \t\t\t\t\t  &tree->object.oid,\n \t\t\t\t\t  prefix, &tree_oid,\n-\t\t\t\t\t  &mode);\n-\t\tif (err || !S_ISDIR(mode))\n+\t\t\t\t\t  &object_type);\n+\t\tif (err || object_type != OBJ_TREE)\n \t\t\tdie(_(\"current working directory is untracked\"));\n \n \t\ttree = parse_tree_indirect(&tree_oid);\ndiff --git a/blame.c b/blame.c\nindex 9e0543e13d4..4944582dc3c 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -101,11 +101,11 @@ static void verify_working_tree_path(struct repository *r,\n \tfor (parents = work_tree->parents; parents; parents = parents->next) {\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n \t\tstruct object_id blob_oid;\n-\t\tunsigned short mode;\n-\t\tint ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n-\t\t\t\t\t      &mode);\n+\t\tenum object_type object_type;\n+\t\tint ret = get_tree_entry_type(r, commit_oid, path, &blob_oid,\n+\t\t\t\t\t      &object_type);\n \n-\t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n+\t\tif (!ret && object_type == OBJ_BLOB)\n \t\t\treturn;\n \t}\n \ndiff --git a/match-trees.c b/match-trees.c\nindex 4f02768c01e..ce3f811ec04 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -317,17 +317,17 @@ void shift_tree_by(struct repository *r,\n \t\t   const char *shift_prefix)\n {\n \tstruct object_id sub1, sub2;\n-\tunsigned short tmp;\n+\tenum object_type tmp;\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n-\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n-\t    S_ISDIR(tmp))\n+\tif (!get_tree_entry_type(r, hash1, shift_prefix, &sub1, &tmp) &&\n+\t    tmp == OBJ_TREE)\n \t\tcandidate |= 1;\n \n \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n-\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n-\t    S_ISDIR(tmp))\n+\tif (!get_tree_entry_type(r, hash2, shift_prefix, &sub2, &tmp) &&\n+\t    tmp == OBJ_TREE)\n \t\tcandidate |= 2;\n \n \tif (candidate == 3) {\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 7819ff3e0ec..0ad3d80593e 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -561,7 +561,8 @@ struct dir_state {\n \n static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\t   const char *name, struct object_id *result,\n-\t\t\t   unsigned short *mode)\n+\t\t\t   unsigned short *mode,\n+\t\t\t   enum object_type *object_type)\n {\n \tint namelen = strlen(name);\n \twhile (t->size) {\n@@ -585,23 +586,24 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t}\n \t\tif (name[entrylen] != '/')\n \t\t\tcontinue;\n-\t\tif (!S_ISDIR(*mode))\n+\t\tif (*object_type != OBJ_TREE)\n \t\t\tbreak;\n \t\tif (++entrylen == namelen) {\n \t\t\toidcpy(result, &oid);\n \t\t\treturn 0;\n \t\t}\n-\t\treturn get_tree_entry_mode(r, &oid, name + entrylen, result,\n-\t\t\t\t\t   mode);\n+\t\treturn get_tree_entry_all(r, &oid, name + entrylen, result,\n+\t\t\t\t\t  mode, object_type);\n \t}\n \treturn -1;\n }\n \n-int get_tree_entry_mode(struct repository *r,\n-\t\t\tconst struct object_id *tree_oid,\n-\t\t\tconst char *name,\n-\t\t\tstruct object_id *oid,\n-\t\t\tunsigned short *mode)\n+int get_tree_entry_all(struct repository *r,\n+\t\t       const struct object_id *tree_oid,\n+\t\t       const char *name,\n+\t\t       struct object_id *oid,\n+\t\t       unsigned short *mode,\n+\t\t       enum object_type *object_type)\n {\n \tint retval;\n \tvoid *tree;\n@@ -624,12 +626,34 @@ int get_tree_entry_mode(struct repository *r,\n \t\tstruct tree_desc t;\n \t\tinit_tree_desc(&t, tree, size);\n \t\tretval = find_tree_entry(r, &t, name, oid,\n-\t\t\t\t\t mode);\n+\t\t\t\t\t mode, object_type);\n \t}\n \tfree(tree);\n \treturn retval;\n }\n \n+int get_tree_entry_mode(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid,\n+\t\t\tunsigned short *mode)\n+{\n+\tenum object_type object_type;\n+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n+\t\t\t\t  mode, &object_type);\n+}\n+\n+int get_tree_entry_type(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid,\n+\t\t\tenum object_type *object_type)\n+{\n+\tunsigned short mode;\n+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n+\t\t\t\t  &mode, object_type);\n+}\n+\n /*\n  * This is Linux's built-in max for the number of symlinks to follow.\n  * That limit, of course, does not affect git, but it's a reasonable\n@@ -674,6 +698,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n \t\tint find_result;\n \t\tchar *first_slash;\n \t\tchar *remainder = NULL;\n+\t\tenum object_type object_type;\n \n \t\tif (!t.buffer) {\n \t\t\tvoid *tree;\n@@ -751,7 +776,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n \t\t/* Look up the first (or only) path component in the tree. */\n \t\tfind_result = find_tree_entry(r, &t, namebuf.buf,\n \t\t\t\t\t      &current_tree_oid,\n-\t\t\t\t\t      mode);\n+\t\t\t\t\t      mode, &object_type);\n \t\tif (find_result) {\n \t\t\tgoto done;\n \t\t}\ndiff --git a/tree-walk.h b/tree-walk.h\nindex eb9b9de6ccc..5db38fcb575 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -171,12 +171,23 @@ struct traverse_info {\n  * Find an entry in a tree given a pathname and the sha1 of a tree to\n  * search. Returns 0 if the entry is found and -1 otherwise.\n  *\n- * The third and fourth parameters are set to the entry's sha1 and\n- * mode respectively.\n+ * There are variants of this function depending on what fields in the\n+ * \"struct name_entry\" you'd like. You always need to pointer to an\n+ * appropriate variable to fill in (NULL won't do!):\n+ *\n+ * get_tree_entry_mode(): unsigned int mode\n+ * get_tree_entry_type(): enum object_type\n+ * get_tree_entry_all(): unsigned int mode, enum object_type\n  */\n int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n \t\t\tunsigned short *);\n+int get_tree_entry_type(struct repository *, const struct object_id *, const char *,\n+\t\t\tstruct object_id *,\n+\t\t\tenum object_type *);\n+int get_tree_entry_all(struct repository *, const struct object_id *, const char *,\n+\t\t       struct object_id *,\n+\t\t       unsigned short *, enum object_type *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418468","messageId":"20210308150650.18626-15-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 14/30] tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:34Z","receivedAt":"2021-03-08T15:08:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor more users of the \"entry.mode\" field to use the new\n\"entry.object_type\" field.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/grep.c         | 6 +++---\n builtin/merge-tree.c   | 9 +++++----\n builtin/pack-objects.c | 4 ++--\n builtin/reflog.c       | 3 ++-\n cache-tree.c           | 2 +-\n delta-islands.c        | 2 +-\n notes.c                | 4 ++--\n unpack-trees.c         | 2 +-\n 8 files changed, 17 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 4e91a253ac3..5a317cdd2f4 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -587,10 +587,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,\n \n \t\tstrbuf_add(base, entry.path, te_len);\n \n-\t\tif (S_ISREG(entry.mode)) {\n+\t\tif (entry.object_type == OBJ_BLOB) {\n \t\t\thit |= grep_oid(opt, &entry.oid, base->buf, tn_len,\n \t\t\t\t\t check_attr ? base->buf + tn_len : NULL);\n-\t\t} else if (S_ISDIR(entry.mode)) {\n+\t\t} else if (entry.object_type == OBJ_TREE) {\n \t\t\tenum object_type type;\n \t\t\tstruct tree_desc sub;\n \t\t\tvoid *data;\n@@ -606,7 +606,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,\n \t\t\thit |= grep_tree(opt, pathspec, &sub, base, tn_len,\n \t\t\t\t\t check_attr);\n \t\t\tfree(data);\n-\t\t} else if (recurse_submodules && S_ISGITLINK(entry.mode)) {\n+\t\t} else if (recurse_submodules && entry.object_type == OBJ_COMMIT) {\n \t\t\thit |= grep_submodule(opt, pathspec, &entry.oid,\n \t\t\t\t\t      base->buf, base->buf + tn_len,\n \t\t\t\t\t      1); /* ignored */\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex 2de34c2d485..12cb317c1ba 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -275,11 +275,11 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3])\n \tif (dirmask == mask)\n \t\treturn;\n \n-\tif (n[2].mode && !S_ISDIR(n[2].mode))\n+\tif (n[2].object_type != OBJ_TREE)\n \t\tentry = link_entry(3, info, n + 2, entry);\n-\tif (n[1].mode && !S_ISDIR(n[1].mode))\n+\tif (n[1].object_type != OBJ_TREE)\n \t\tentry = link_entry(2, info, n + 1, entry);\n-\tif (n[0].mode && !S_ISDIR(n[0].mode))\n+\tif (n[0].object_type != OBJ_TREE)\n \t\tentry = link_entry(1, info, n + 0, entry);\n \n \tadd_merge_entry(entry);\n@@ -324,7 +324,8 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s\n \t}\n \n \tif (same_entry(entry+0, entry+1)) {\n-\t\tif (!is_null_oid(&entry[2].oid) && !S_ISDIR(entry[2].mode)) {\n+\t\tif (!is_null_oid(&entry[2].oid) &&\n+\t\t    entry[2].object_type != OBJ_TREE) {\n \t\t\t/* We did not touch, they modified -- take theirs */\n \t\t\tresolve(info, entry+1, entry+2);\n \t\t\treturn mask;\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex d3ba1d4a4a6..f92722c12d4 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -1524,7 +1524,7 @@ static void add_pbase_object(struct tree_desc *tree,\n \tint cmp;\n \n \twhile (tree_entry(tree,&entry)) {\n-\t\tif (S_ISGITLINK(entry.mode))\n+\t\tif (entry.object_type == OBJ_COMMIT)\n \t\t\tcontinue;\n \t\tcmp = tree_entry_len(&entry) != cmplen ? 1 :\n \t\t      memcmp(name, entry.path, cmplen);\n@@ -1538,7 +1538,7 @@ static void add_pbase_object(struct tree_desc *tree,\n \t\t\t\t\t fullname, 1);\n \t\t\treturn;\n \t\t}\n-\t\tif (S_ISDIR(entry.mode)) {\n+\t\tif (entry.object_type == OBJ_TREE) {\n \t\t\tstruct tree_desc sub;\n \t\t\tstruct pbase_tree_cache *tree;\n \t\t\tconst char *down = name+cmplen+1;\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 09541d1c804..bcbca82aa90 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -95,7 +95,8 @@ static int tree_is_complete(const struct object_id *oid)\n \tcomplete = 1;\n \twhile (tree_entry(&desc, &entry)) {\n \t\tif (!has_object_file(&entry.oid) ||\n-\t\t    (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {\n+\t\t    (entry.object_type == OBJ_TREE &&\n+\t\t     !tree_is_complete(&entry.oid))) {\n \t\t\ttree->object.flags |= INCOMPLETE;\n \t\t\tcomplete = 0;\n \t\t}\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 2fb483d3c08..fbe93dd2a5f 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -726,7 +726,7 @@ static void prime_cache_tree_rec(struct repository *r,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \tcnt = 0;\n \twhile (tree_entry(&desc, &entry)) {\n-\t\tif (!S_ISDIR(entry.mode))\n+\t\tif (entry.object_type != OBJ_TREE)\n \t\t\tcnt++;\n \t\telse {\n \t\t\tstruct cache_tree_sub *sub;\ndiff --git a/delta-islands.c b/delta-islands.c\nindex aa98b2e5414..e7cf93acbe3 100644\n--- a/delta-islands.c\n+++ b/delta-islands.c\n@@ -293,7 +293,7 @@ void resolve_tree_islands(struct repository *r,\n \t\twhile (tree_entry(&desc, &entry)) {\n \t\t\tstruct object *obj;\n \n-\t\t\tif (S_ISGITLINK(entry.mode))\n+\t\t\tif (entry.object_type == OBJ_COMMIT)\n \t\t\t\tcontinue;\n \n \t\t\tobj = lookup_object(r, &entry.oid);\ndiff --git a/notes.c b/notes.c\nindex 0a5b4fa1dbb..d631dc5623e 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -418,7 +418,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\tif (path_len == 2 * (hashsz - prefix_len)) {\n \t\t\t/* This is potentially the remainder of the SHA-1 */\n \n-\t\t\tif (!S_ISREG(entry.mode))\n+\t\t\tif (entry.object_type != OBJ_BLOB)\n \t\t\t\t/* notes must be blobs */\n \t\t\t\tgoto handle_non_note;\n \n@@ -431,7 +431,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t/* This is potentially an internal node */\n \t\t\tsize_t len = prefix_len;\n \n-\t\t\tif (!S_ISDIR(entry.mode))\n+\t\t\tif (entry.object_type != OBJ_TREE)\n \t\t\t\t/* internal nodes must be trees */\n \t\t\t\tgoto handle_non_note;\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 92105135522..e24302f45ba 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1297,7 +1297,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n \tif (dirmask) {\n \t\t/* special case: \"diff-index --cached\" looking at a tree */\n \t\tif (o->diff_index_cached &&\n-\t\t    n == 1 && dirmask == 1 && S_ISDIR(names->mode)) {\n+\t\t    n == 1 && dirmask == 1 && names->object_type == OBJ_TREE) {\n \t\t\tint matches;\n \t\t\tmatches = cache_tree_matches_traversal(o->src_index->cache_tree,\n \t\t\t\t\t\t\t       names, info);\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418474","messageId":"20210308150650.18626-20-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 19/30] tree-walk.h API: formatting changes for subsequent commit","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:39Z","receivedAt":"2021-03-08T15:08:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Do formatting (mainly whitespace) changes of code around the\nget_tree_entry() function to make a subsequent change where we'll add\na sister function easier to read.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n blame.c     |  5 +++--\n tree-walk.c |  9 ++++++---\n tree-walk.h | 12 ++++++++----\n 3 files changed, 17 insertions(+), 9 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex a5044fcfaa6..83babc41d08 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -102,9 +102,10 @@ static void verify_working_tree_path(struct repository *r,\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n \t\tstruct object_id blob_oid;\n \t\tunsigned short mode;\n+\t\tint ret = get_tree_entry(r, commit_oid, path, &blob_oid,\n+\t\t\t\t\t &mode);\n \n-\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode) &&\n-\t\t    oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n+\t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n \t\t\treturn;\n \t}\n \ndiff --git a/tree-walk.c b/tree-walk.c\nindex 6e9161901d8..e88187e3714 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -591,7 +591,8 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\toidcpy(result, &oid);\n \t\t\treturn 0;\n \t\t}\n-\t\treturn get_tree_entry(r, &oid, name + entrylen, result, mode);\n+\t\treturn get_tree_entry(r, &oid, name + entrylen, result,\n+\t\t\t\t      mode);\n \t}\n \treturn -1;\n }\n@@ -622,7 +623,8 @@ int get_tree_entry(struct repository *r,\n \t} else {\n \t\tstruct tree_desc t;\n \t\tinit_tree_desc(&t, tree, size);\n-\t\tretval = find_tree_entry(r, &t, name, oid, mode);\n+\t\tretval = find_tree_entry(r, &t, name, oid,\n+\t\t\t\t\t mode);\n \t}\n \tfree(tree);\n \treturn retval;\n@@ -748,7 +750,8 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n \n \t\t/* Look up the first (or only) path component in the tree. */\n \t\tfind_result = find_tree_entry(r, &t, namebuf.buf,\n-\t\t\t\t\t      &current_tree_oid, mode);\n+\t\t\t\t\t      &current_tree_oid,\n+\t\t\t\t\t      mode);\n \t\tif (find_result) {\n \t\t\tgoto done;\n \t\t}\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 9f3825d2773..478a659ee2b 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -169,10 +169,14 @@ struct traverse_info {\n \n /**\n  * Find an entry in a tree given a pathname and the sha1 of a tree to\n- * search. Returns 0 if the entry is found and -1 otherwise. The third\n- * and fourth parameters are set to the entry's sha1 and mode respectively.\n- */\n-int get_tree_entry(struct repository *, const struct object_id *, const char *, struct object_id *, unsigned short *);\n+ * search. Returns 0 if the entry is found and -1 otherwise.\n+ *\n+ * The third and fourth parameters are set to the entry's sha1 and\n+ * mode respectively.\n+ */\n+int get_tree_entry(struct repository *, const struct object_id *, const char *,\n+\t\t   struct object_id *,\n+\t\t   unsigned short *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418475","messageId":"20210308150650.18626-18-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 17/30] fsck.c: switch on \"object_type\" in fsck_walk_tree()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:37Z","receivedAt":"2021-03-08T15:08:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since 7146e66f086 (tree-walk: finally switch over tree descriptors to\ncontain a pre-parsed entry, 2014-02-06) the \"mode\" is validated such\nthat we'll never reach the \"else\" clause here.\n\nGood for us that fsck_tree() has its own FSCK_MSG_BAD_FILEMODE check\nwhich we can use, added way back in 64071805eda (git-fsck-cache: be\nstricter about \"tree\" objects, 2005-07-27).\n\nExcept it really doesn't due to a regression in 7146e66f086. A\nfollow-up commit will address that, but for now we can simply rewrite\nthis code like the rest of the s/entry.mode/entry.object_type/g\nchanges I'm making.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n fsck.c | 21 +++++++++------------\n 1 file changed, 9 insertions(+), 12 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex e3030f3b358..7c74c49d329 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -396,28 +396,25 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op\n \t\tstruct object *obj;\n \t\tint result;\n \n-\t\tif (S_ISGITLINK(entry.mode))\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_COMMIT:\n \t\t\tcontinue;\n-\n-\t\tif (S_ISDIR(entry.mode)) {\n+\t\tcase OBJ_TREE:\n \t\t\tobj = (struct object *)lookup_tree(the_repository, &entry.oid);\n \t\t\tif (name && obj)\n \t\t\t\tfsck_put_object_name(options, &entry.oid, \"%s%s/\",\n \t\t\t\t\t\t     name, entry.path);\n-\t\t\tresult = options->walk(obj, OBJ_TREE, data, options);\n-\t\t}\n-\t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n \t\t\tobj = (struct object *)lookup_blob(the_repository, &entry.oid);\n \t\t\tif (name && obj)\n \t\t\t\tfsck_put_object_name(options, &entry.oid, \"%s%s\",\n \t\t\t\t\t\t     name, entry.path);\n-\t\t\tresult = options->walk(obj, OBJ_BLOB, data, options);\n-\t\t}\n-\t\telse {\n-\t\t\tresult = error(\"in tree %s: entry %s has bad mode %.6o\",\n-\t\t\t\t       fsck_describe_object(options, &tree->object.oid),\n-\t\t\t\t       entry.path, entry.mode);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n+\t\tresult = options->walk(obj, entry.object_type, data, options);\n \t\tif (result < 0)\n \t\t\treturn result;\n \t\tif (!res)\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418476","messageId":"20210308150650.18626-16-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 15/30] merge-tree tests: test for the mode comparison in same_entry()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:35Z","receivedAt":"2021-03-08T15:08:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test to stress the \"a->mode == b->mode\" comparison in\nmerge-tree.c's same_entry().\n\nThat code was initially added by Linus in 33deb63a36f (Add\n\"merge-tree\" helper program. Maybe it's retarded, maybe it's helpful.,\n2005-04-14), and then again in its current form in\n492e0759bfe (Handling large files with GIT, 2006-02-14).\n\nHowever, nothing was testing that we handled this case\ncorrectly. Simply removing the mode comparison left all tests passing,\nbut as seen here it's important that we don't think a path with the\nsame content but different modes is the same_entry().\n\nThe rest of this series will touch code that's relevant to this, but\nwon't change its behavior. This test is just something I came up with\nin testing whether the mode test in same_entry() was needed at all.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4300-merge-tree.sh | 44 +++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 44 insertions(+)\n\ndiff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh\nindex e59601e5fe9..f783d784d02 100755\n--- a/t/t4300-merge-tree.sh\n+++ b/t/t4300-merge-tree.sh\n@@ -40,6 +40,25 @@ test_expect_success 'file add A, B (same)' '\n \ttest_must_be_empty actual\n '\n \n+test_expect_success 'file add A, B (different mode)' '\n+\tgit reset --hard initial &&\n+\ttest_commit \"add-a-b-same-diff-mode-A\" \"ONE\" \"AAA\" &&\n+\tgit reset --hard initial &&\n+\techo AAA >ONE &&\n+\ttest_chmod +x ONE &&\n+\ttest_tick &&\n+\tgit commit -m\"add-a-b-same-diff-mode-B\" &&\n+\tgit tag \"add-a-b-same-diff-mode-B\" HEAD &&\n+\tgit merge-tree initial add-a-b-same-diff-mode-A add-a-b-same-diff-mode-B >actual &&\n+\tcat >expected <<EXPECTED &&\n+added in both\n+  our    100644 $(git rev-parse add-a-b-same-diff-mode-A:ONE) ONE\n+  their  100755 $(git rev-parse add-a-b-same-diff-mode-B:ONE) ONE\n+EXPECTED\n+\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'file add A, B (different)' '\n \tgit reset --hard initial &&\n \ttest_commit \"add-a-b-diff-A\" \"ONE\" \"AAA\" &&\n@@ -61,6 +80,31 @@ EXPECTED\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'file add A, B (different and different mode)' '\n+\tgit reset --hard initial &&\n+\ttest_commit \"add-a-b-diff-diff-mode-A\" \"ONE\" \"AAA\" &&\n+\tgit reset --hard initial &&\n+\techo BBB >ONE &&\n+\ttest_chmod +x ONE &&\n+\ttest_tick &&\n+\tgit commit -m\"add-a-b-diff-diff-mode-B\" &&\n+\tgit tag \"add-a-b-diff-diff-mode-B\" &&\n+\tgit merge-tree initial add-a-b-diff-diff-mode-A add-a-b-diff-diff-mode-B >actual &&\n+\tcat >expected <<EXPECTED &&\n+added in both\n+  our    100644 $(git rev-parse add-a-b-diff-diff-mode-A:ONE) ONE\n+  their  100755 $(git rev-parse add-a-b-diff-diff-mode-B:ONE) ONE\n+@@ -1 +1,5 @@\n++<<<<<<< .our\n+ AAA\n++=======\n++BBB\n++>>>>>>> .their\n+EXPECTED\n+\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'file change A, !B' '\n \tgit reset --hard initial &&\n \ttest_commit \"change-a-not-b\" \"initial-file\" \"BBB\" &&\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418461","messageId":"20210308150650.18626-25-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 24/30] tree-walk.h API: document and format tree_entry_extract()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:44Z","receivedAt":"2021-03-08T15:08:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Document and format the argument list of the tree_entry_extract()\nfunction in preparation for adding a sister function.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.h | 14 ++++++++++----\n 1 file changed, 10 insertions(+), 4 deletions(-)\n\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 1bfa839b275..61fdcb166d2 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -40,11 +40,17 @@ struct tree_desc {\n \n /**\n  * Decode the entry currently being visited (the one pointed to by\n- * `tree_desc's` `entry` member) and return the sha1 of the entry. The\n- * `pathp` and `modep` arguments are set to the entry's pathname and mode\n- * respectively.\n+ * `tree_desc's` `entry` member) and return the OID of the entry.\n+\n+ * There are variants of this function depending on what fields in the\n+ * \"struct name_entry\" you'd like. You always need to pointer to an\n+ * appropriate variable to fill in (NULL won't do!):\n+ *\n+ * tree_entry_extract_mode(): const char *path, unsigned int mode\n  */\n-static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned short *modep)\n+static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,\n+\t\t\t\t\t\t\t const char **pathp,\n+\t\t\t\t\t\t\t unsigned short *modep)\n {\n \t*pathp = desc->entry.path;\n \t*modep = desc->entry.mode;\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418466","messageId":"20210308150650.18626-26-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 25/30] tree-entry.h API: rename tree_entry_extract() to tree_entry_extract_mode()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:45Z","receivedAt":"2021-03-08T15:08:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As with the recent split of the get_tree_entry() function, rename the\ntree_entry_extract() function to *_mode() in preparation for adding\nother variants of it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n fsck.c        | 2 +-\n match-trees.c | 4 ++--\n tree-diff.c   | 4 ++--\n tree-walk.c   | 2 +-\n tree-walk.h   | 6 +++---\n 5 files changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 7c74c49d329..11678ba5826 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -670,7 +670,7 @@ static int fsck_tree(const struct object_id *oid,\n \t\tconst char *name, *backslash;\n \t\tconst struct object_id *oid;\n \n-\t\toid = tree_entry_extract(&desc, &name, &mode);\n+\t\toid = tree_entry_extract_mode(&desc, &name, &mode);\n \n \t\thas_null_sha1 |= is_null_oid(oid);\n \t\thas_full_path |= !!strchr(name, '/');\ndiff --git a/match-trees.c b/match-trees.c\nindex 60a17b92d70..3d2c74a44ac 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -146,7 +146,7 @@ static void match_trees(const struct object_id *hash1,\n \t\tunsigned short mode;\n \t\tint score;\n \n-\t\telem = tree_entry_extract(&one, &path, &mode);\n+\t\telem = tree_entry_extract_mode(&one, &path, &mode);\n \t\tif (!S_ISDIR(mode))\n \t\t\tgoto next;\n \t\tscore = score_trees(elem, hash2);\n@@ -202,7 +202,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \t\tunsigned short mode;\n \t\tint len = tree_entry_len(&desc.entry);\n \n-\t\ttree_entry_extract(&desc, &name, &mode);\n+\t\ttree_entry_extract_mode(&desc, &name, &mode);\n \t\tif (len == toplen &&\n \t\t    !memcmp(name, prefix, toplen)) {\n \t\t\tif (!S_ISDIR(mode))\ndiff --git a/tree-diff.c b/tree-diff.c\nindex f145ff84c68..b37348b7908 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -196,7 +196,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \n \tif (t) {\n \t\t/* path present in resulting tree */\n-\t\toid = tree_entry_extract(t, &path, &mode);\n+\t\toid = tree_entry_extract_mode(t, &path, &mode);\n \t\tpathlen = tree_entry_len(&t->entry);\n \t\tisdir = S_ISDIR(mode);\n \t} else {\n@@ -207,7 +207,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \t\t * 1) all modes for tp[i]=tp[imin] should be the same wrt\n \t\t *    S_ISDIR, thanks to base_name_compare().\n \t\t */\n-\t\ttree_entry_extract(&tp[imin], &path, &mode);\n+\t\ttree_entry_extract_mode(&tp[imin], &path, &mode);\n \t\tpathlen = tree_entry_len(&tp[imin].entry);\n \n \t\tisdir = S_ISDIR(mode);\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 83737634770..e613f273767 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -570,7 +570,7 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\tstruct object_id oid;\n \t\tint entrylen, cmp;\n \n-\t\toidcpy(&oid, tree_entry_extract(t, &entry, mode));\n+\t\toidcpy(&oid, tree_entry_extract_mode(t, &entry, mode));\n \t\tentrylen = tree_entry_len(&t->entry);\n \t\tupdate_tree_entry(t);\n \t\tif (entrylen > namelen)\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 61fdcb166d2..892e77eda23 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -48,9 +48,9 @@ struct tree_desc {\n  *\n  * tree_entry_extract_mode(): const char *path, unsigned int mode\n  */\n-static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,\n-\t\t\t\t\t\t\t const char **pathp,\n-\t\t\t\t\t\t\t unsigned short *modep)\n+static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *desc,\n+\t\t\t\t\t\t\t      const char **pathp,\n+\t\t\t\t\t\t\t      unsigned short *modep)\n {\n \t*pathp = desc->entry.path;\n \t*modep = desc->entry.mode;\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418469","messageId":"20210308150650.18626-24-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 23/30] tree-walk.h API: add a get_tree_entry_path() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:43Z","receivedAt":"2021-03-08T15:08:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a get_tree_entry_path() variant in addition to\nget_tree_entry_path_{mode,type,all}(). This is for those callers that\nneed neither the mode nor \"enum object_type\" parameters filled for\nthem.\n\nThere's callers here which doesn't need the \"struct object_id\" filled\neither, and provides a throwaway variable for us.\n\nSee the following commits for the introduction of such code that's\nbeing modified here:\n\n - shift_tree(): 68faf68938e (A new merge stragety 'subtree'.,\n    2007-02-15) for the shift_tree()\n\n - tree_has_path(): 96e7ffbdc31 (merge-recursive: check for directory\n   level conflicts, 2018-04-19)\n\n - init_notes(): fd53c9eb445 (Speed up git notes lookup, 2009-10-09)\n\n - diagnose_invalid_oid_path(): 009fee4774d (Detailed diagnosis when\n   parsing an object name fails., 2009-12-07)\n\nThose could potentially be refactored too, but I've got to stop at\nsome point, and right now I'm focusing downstream code that depends on\n\"mode\" (or \"enum object_type\").\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c     |  4 +---\n merge-recursive.c |  6 ++----\n notes.c           |  3 +--\n object-name.c     |  3 +--\n tree-walk.c       | 11 +++++++++++\n tree-walk.h       |  3 +++\n 6 files changed, 19 insertions(+), 11 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex ce3f811ec04..60a17b92d70 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -288,12 +288,10 @@ void shift_tree(struct repository *r,\n \n \tif (add_score < del_score) {\n \t\t/* We need to pick a subtree of two */\n-\t\tunsigned short mode;\n-\n \t\tif (!*del_prefix)\n \t\t\treturn;\n \n-\t\tif (get_tree_entry_mode(r, hash2, del_prefix, shifted, &mode))\n+\t\tif (get_tree_entry_path(r, hash2, del_prefix, shifted))\n \t\t\tdie(\"cannot find path %s in tree %s\",\n \t\t\t    del_prefix, oid_to_hex(hash2));\n \t\treturn;\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 0e891360e7e..b26d9d418f9 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1884,11 +1884,9 @@ static int tree_has_path(struct repository *r, struct tree *tree,\n \t\t\t const char *path)\n {\n \tstruct object_id hashy;\n-\tunsigned short mode_o;\n-\n-\treturn !get_tree_entry_mode(r,\n+\treturn !get_tree_entry_path(r,\n \t\t\t\t    &tree->object.oid, path,\n-\t\t\t\t    &hashy, &mode_o);\n+\t\t\t\t    &hashy);\n }\n \n /*\ndiff --git a/notes.c b/notes.c\nindex ef138606146..aa46cb2b09e 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -994,7 +994,6 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\tcombine_notes_fn combine_notes, int flags)\n {\n \tstruct object_id oid, object_oid;\n-\tunsigned short mode;\n \tstruct leaf_node root_tree;\n \n \tif (!t)\n@@ -1021,7 +1020,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\treturn;\n \tif (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))\n \t\tdie(\"Cannot use notes ref %s\", notes_ref);\n-\tif (get_tree_entry_mode(the_repository, &object_oid, \"\", &oid, &mode))\n+\tif (get_tree_entry_path(the_repository, &object_oid, \"\", &oid))\n \t\tdie(\"Failed to read notes tree referenced by %s (%s)\",\n \t\t    notes_ref, oid_to_hex(&object_oid));\n \ndiff --git a/object-name.c b/object-name.c\nindex 7e3b2d6d739..9ff5f83c1ff 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1693,7 +1693,6 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \t\t\t\t      int object_name_len)\n {\n \tstruct object_id oid;\n-\tunsigned short mode;\n \n \tif (!prefix)\n \t\tprefix = \"\";\n@@ -1704,7 +1703,7 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \tif (is_missing_file_error(errno)) {\n \t\tchar *fullname = xstrfmt(\"%s%s\", prefix, filename);\n \n-\t\tif (!get_tree_entry_mode(r, tree_oid, fullname, &oid, &mode)) {\n+\t\tif (!get_tree_entry_path(r, tree_oid, fullname, &oid)) {\n \t\t\tdie(_(\"path '%s' exists, but not '%s'\\n\"\n \t\t\t    \"hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?\"),\n \t\t\t    fullname,\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 0ad3d80593e..83737634770 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -632,6 +632,17 @@ int get_tree_entry_all(struct repository *r,\n \treturn retval;\n }\n \n+int get_tree_entry_path(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid)\n+{\n+\tunsigned short mode;\n+\tenum object_type object_type;\n+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n+\t\t\t\t  &mode, &object_type);\n+}\n+\n int get_tree_entry_mode(struct repository *r,\n \t\t\tconst struct object_id *tree_oid,\n \t\t\tconst char *name,\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 5db38fcb575..1bfa839b275 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -175,10 +175,13 @@ struct traverse_info {\n  * \"struct name_entry\" you'd like. You always need to pointer to an\n  * appropriate variable to fill in (NULL won't do!):\n  *\n+ * get_tree_entry_path(): <no extra argument, just get the common 'path'>\n  * get_tree_entry_mode(): unsigned int mode\n  * get_tree_entry_type(): enum object_type\n  * get_tree_entry_all(): unsigned int mode, enum object_type\n  */\n+int get_tree_entry_path(struct repository *, const struct object_id *, const char *,\n+\t\t\tstruct object_id *);\n int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n \t\t\tunsigned short *);\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418470","messageId":"20210308150650.18626-27-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 26/30] tree-walk.h API: add a tree_entry_extract_all() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:46Z","receivedAt":"2021-03-08T15:08:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a tree_entry_extract_all() sibling function to the existing\ntree_entry_extract_mode().\n\nHaving the OBJ_{BLOB,TREE,COMMIT} when you have the \"mode\" is strictly\nspeaking redundant, but hopefully makes it easier to read the\ncode. We'll now see which parts of the code are checking the types,\nv.s. those that care about the mode specifically.\n\nOnly the first use of tree_entry_extract_mode() in emit_path() is\nconverted here, the other branch will use a new\nget_tree_entry_mode_type() introduced in a subsequent commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/update-index.c |  6 ++++--\n tree-diff.c            |  5 +++--\n tree-walk.c            |  3 ++-\n tree-walk.h            | 12 ++++++++++++\n 4 files changed, 21 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 070510d6a88..b489a876392 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -599,16 +599,18 @@ static struct cache_entry *read_one_ent(const char *which,\n \t\t\t\t\tstruct object_id *ent, const char *path,\n \t\t\t\t\tint namelen, int stage)\n {\n+\tenum object_type object_type;\n \tunsigned short mode;\n \tstruct object_id oid;\n \tstruct cache_entry *ce;\n \n-\tif (get_tree_entry_mode(the_repository, ent, path, &oid, &mode)) {\n+\tif (get_tree_entry_all(the_repository, ent, path, &oid,\n+\t\t\t       &mode, &object_type)) {\n \t\tif (which)\n \t\t\terror(\"%s: not in %s branch.\", path, which);\n \t\treturn NULL;\n \t}\n-\tif (mode == S_IFDIR) {\n+\tif (object_type == OBJ_TREE) {\n \t\tif (which)\n \t\t\terror(\"%s: not a blob in %s branch.\", path, which);\n \t\treturn NULL;\ndiff --git a/tree-diff.c b/tree-diff.c\nindex b37348b7908..b25095c1164 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -195,10 +195,11 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \tassert(t || tp);\n \n \tif (t) {\n+\t\tenum object_type object_type;\n \t\t/* path present in resulting tree */\n-\t\toid = tree_entry_extract_mode(t, &path, &mode);\n+\t\toid = tree_entry_extract_all(t, &path, &mode, &object_type);\n \t\tpathlen = tree_entry_len(&t->entry);\n-\t\tisdir = S_ISDIR(mode);\n+\t\tisdir = object_type == OBJ_TREE;\n \t} else {\n \t\t/*\n \t\t * a path was removed - take path from imin parent. Also take\ndiff --git a/tree-walk.c b/tree-walk.c\nindex e613f273767..12e0ed4e250 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -570,7 +570,8 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\tstruct object_id oid;\n \t\tint entrylen, cmp;\n \n-\t\toidcpy(&oid, tree_entry_extract_mode(t, &entry, mode));\n+\t\toidcpy(&oid, tree_entry_extract_all(t, &entry, mode, object_type));\n+\n \t\tentrylen = tree_entry_len(&t->entry);\n \t\tupdate_tree_entry(t);\n \t\tif (entrylen > namelen)\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 892e77eda23..06ad40ab2f1 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -47,6 +47,7 @@ struct tree_desc {\n  * appropriate variable to fill in (NULL won't do!):\n  *\n  * tree_entry_extract_mode(): const char *path, unsigned int mode\n+ * tree_entry_extract_all(): const char *path, unsigned int mode, enum object_type\n  */\n static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *desc,\n \t\t\t\t\t\t\t      const char **pathp,\n@@ -57,6 +58,17 @@ static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *\n \treturn &desc->entry.oid;\n }\n \n+static inline const struct object_id *tree_entry_extract_all(struct tree_desc *desc,\n+\t\t\t\t\t\t\t     const char **pathp,\n+\t\t\t\t\t\t\t     unsigned short *modep,\n+\t\t\t\t\t\t\t     enum object_type *object_typep)\n+{\n+\t*pathp = desc->entry.path;\n+\t*modep = desc->entry.mode;\n+\t*object_typep = desc->entry.object_type;\n+\treturn &desc->entry.oid;\n+}\n+\n /**\n  * Calculate the length of a tree entry's pathname. This utilizes the\n  * memory structure of a tree entry to avoid the overhead of using a\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418472","messageId":"20210308150650.18626-29-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 28/30] tree-walk.h API users: rename \"struct name_entry\"'s \"mode\" to \"raw_mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:48Z","receivedAt":"2021-03-08T15:08:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that most of the users of the \"mode\" variable have been moved to\nuse \"object_type\" instead let's rename it to \"raw_mode\" in preparation\nfor a revert of 7146e66f086 (tree-walk: finally switch over tree\ndescriptors to contain a pre-parsed entry, 2014-02-06).\n\nThis will allow API users who care about the actual mode bits in tree\nobjects to get access to them, such as fsck, the merge algorithm etc.\n\nBut most users will not want to have such potentially un-sanitized, so\nlet's indicate that by giving the variable a more scary name.\n\nI am not renaming the variables being assigned to, i.e. it's now going\nto be \"int mode = entry.raw_mode\", not \"int raw_mode = [...]\". This is\nbecause we're going to be getting a sanitized \"mode\" via\n\"canon_mode()\" in many of these functions soon, so renaming the local\nvariable back and forth will result in needless churn.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/merge-tree.c |  8 ++++----\n match-trees.c        |  4 ++--\n merge-ort.c          | 12 ++++++------\n notes.c              |  2 +-\n tree-diff.c          | 22 +++++++++++-----------\n tree-walk.c          |  2 +-\n tree-walk.h          |  6 +++---\n tree.c               |  2 +-\n unpack-trees.c       |  6 +++---\n 9 files changed, 32 insertions(+), 32 deletions(-)\n\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex eec5b906561..b4e736e4b72 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -160,7 +160,7 @@ static int same_entry(struct name_entry *a, struct name_entry *b)\n \treturn\t!is_null_oid(&a->oid) &&\n \t\t!is_null_oid(&b->oid) &&\n \t\toideq(&a->oid, &b->oid) &&\n-\t\ta->mode == b->mode;\n+\t\ta->raw_mode == b->raw_mode;\n }\n \n static int both_empty(struct name_entry *a, struct name_entry *b)\n@@ -197,9 +197,9 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s\n \t\treturn;\n \n \tpath = traverse_path(info, result);\n-\torig_mode = ours->mode;\n+\torig_mode = ours->raw_mode;\n \torig = create_entry(2, orig_mode, &ours->oid, path);\n-\tfinal_mode = result->mode;\n+\tfinal_mode = result->raw_mode;\n \tfinal = create_entry(0, final_mode, &result->oid, path);\n \n \tfinal->link = orig;\n@@ -252,7 +252,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n \t\tpath = entry->path;\n \telse\n \t\tpath = traverse_path(info, n);\n-\tlink_mode = n->mode;\n+\tlink_mode = n->raw_mode;\n \tlink = create_entry(stage, link_mode, &n->oid, path);\n \n \tlink->link = entry;\ndiff --git a/match-trees.c b/match-trees.c\nindex 0636f6e58e9..d45c76ffa79 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -86,8 +86,8 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha\n \n \tfor (;;) {\n \t\tint cmp;\n-\t\tunsigned int one_mode = one.entry.mode;\n-\t\tunsigned int two_mode = two.entry.mode;\n+\t\tunsigned int one_mode = one.entry.raw_mode;\n+\t\tunsigned int two_mode = two.entry.raw_mode;\n \n \t\tif (one.size && two.size)\n \t\t\tcmp = base_name_entries_compare(&one.entry, &two.entry);\ndiff --git a/merge-ort.c b/merge-ort.c\nindex cad10436504..ea20bbe2af3 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -502,7 +502,7 @@ static void setup_path_info(struct merge_options *opt,\n \tmi->basename_offset = current_dir_name_len;\n \tmi->clean = !!resolved;\n \tif (resolved) {\n-\t\tmi->result.mode = merged_version->mode;\n+\t\tmi->result.mode = merged_version->raw_mode;\n \t\toidcpy(&mi->result.oid, &merged_version->oid);\n \t\tmi->is_null = !!is_null;\n \t} else {\n@@ -512,7 +512,7 @@ static void setup_path_info(struct merge_options *opt,\n \t\tASSIGN_AND_VERIFY_CI(ci, mi);\n \t\tfor (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {\n \t\t\tci->pathnames[i] = fullpath;\n-\t\t\tci->stages[i].mode = names[i].mode;\n+\t\t\tci->stages[i].mode = names[i].raw_mode;\n \t\t\toidcpy(&ci->stages[i].oid, &names[i].oid);\n \t\t}\n \t\tci->filemask = filemask;\n@@ -545,7 +545,7 @@ static void add_pair(struct merge_options *opt,\n \tstruct rename_info *renames = &opt->priv->renames;\n \tint names_idx = is_add ? side : 0;\n \tconst struct object_id *oid = &names[names_idx].oid;\n-\tunsigned int mode = names[names_idx].mode;\n+\tunsigned int mode = names[names_idx].raw_mode;\n \n \tone = alloc_filespec(pathname);\n \ttwo = alloc_filespec(pathname);\n@@ -616,13 +616,13 @@ static int collect_merge_info_callback(int n,\n \tunsigned side1_null = !(mask & 2);\n \tunsigned side2_null = !(mask & 4);\n \tunsigned side1_matches_mbase = (!side1_null && !mbase_null &&\n-\t\t\t\t\tnames[0].mode == names[1].mode &&\n+\t\t\t\t\tnames[0].raw_mode == names[1].raw_mode &&\n \t\t\t\t\toideq(&names[0].oid, &names[1].oid));\n \tunsigned side2_matches_mbase = (!side2_null && !mbase_null &&\n-\t\t\t\t\tnames[0].mode == names[2].mode &&\n+\t\t\t\t\tnames[0].raw_mode == names[2].raw_mode &&\n \t\t\t\t\toideq(&names[0].oid, &names[2].oid));\n \tunsigned sides_match = (!side1_null && !side2_null &&\n-\t\t\t\tnames[1].mode == names[2].mode &&\n+\t\t\t\tnames[1].raw_mode == names[2].raw_mode &&\n \t\t\t\toideq(&names[1].oid, &names[2].oid));\n \n \t/*\ndiff --git a/notes.c b/notes.c\nindex aa46cb2b09e..2817325651a 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -478,7 +478,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\tstruct strbuf non_note_path = STRBUF_INIT;\n \t\t\tconst char *q = oid_to_hex(&subtree->key_oid);\n \t\t\tsize_t i;\n-\t\t\tunsigned int mode = entry.mode;\n+\t\t\tunsigned int mode = entry.raw_mode;\n \t\t\tfor (i = 0; i < prefix_len; i++) {\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 10c92d39c42..df8301d806a 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -232,7 +232,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \t\t\t * tp[i] is valid, if present and if tp[i]==tp[imin] -\n \t\t\t * otherwise, we should ignore it.\n \t\t\t */\n-\t\t\tint tpi_valid = tp && !(tp[i].entry.mode & S_IFXMIN_NEQ);\n+\t\t\tint tpi_valid = tp && !(tp[i].entry.raw_mode & S_IFXMIN_NEQ);\n \n \t\t\tconst struct object_id *oid_i;\n \t\t\tunsigned mode_i;\n@@ -245,7 +245,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \n \t\t\tif (tpi_valid) {\n \t\t\t\toid_i = &tp[i].entry.oid;\n-\t\t\t\tmode_i = tp[i].entry.mode;\n+\t\t\t\tmode_i = tp[i].entry.raw_mode;\n \t\t\t}\n \t\t\telse {\n \t\t\t\toid_i = &null_oid;\n@@ -283,7 +283,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \t\tFAST_ARRAY_ALLOC(parents_oid, nparent);\n \t\tfor (i = 0; i < nparent; ++i) {\n \t\t\t/* same rule as in emitthis */\n-\t\t\tint tpi_valid = tp && !(tp[i].entry.mode & S_IFXMIN_NEQ);\n+\t\t\tint tpi_valid = tp && !(tp[i].entry.raw_mode & S_IFXMIN_NEQ);\n \n \t\t\tparents_oid[i] = tpi_valid ? &tp[i].entry.oid : NULL;\n \t\t}\n@@ -404,7 +404,7 @@ static inline void update_tp_entries(struct tree_desc *tp, int nparent)\n {\n \tint i;\n \tfor (i = 0; i < nparent; ++i)\n-\t\tif (!(tp[i].entry.mode & S_IFXMIN_NEQ))\n+\t\tif (!(tp[i].entry.raw_mode & S_IFXMIN_NEQ))\n \t\t\tupdate_tree_entry(&tp[i]);\n }\n \n@@ -465,10 +465,10 @@ static struct combine_diff_path *ll_diff_tree_paths(\n \t\t * mark entries whether they =p[imin] along the way\n \t\t */\n \t\timin = 0;\n-\t\ttp[0].entry.mode &= ~S_IFXMIN_NEQ;\n+\t\ttp[0].entry.raw_mode &= ~S_IFXMIN_NEQ;\n \n \t\tfor (i = 1; i < nparent; ++i) {\n-\t\t\tunsigned int mode = tp[i].entry.mode;\n+\t\t\tunsigned int mode = tp[i].entry.raw_mode;\n \t\t\tcmp = tree_entry_pathcmp(&tp[i], &tp[imin]);\n \t\t\tif (cmp < 0) {\n \t\t\t\timin = i;\n@@ -480,12 +480,12 @@ static struct combine_diff_path *ll_diff_tree_paths(\n \t\t\telse {\n \t\t\t\tmode |= S_IFXMIN_NEQ;\n \t\t\t}\n-\t\t\ttp[i].entry.mode = mode;\n+\t\t\ttp[i].entry.raw_mode = mode;\n \t\t}\n \n \t\t/* fixup markings for entries before imin */\n \t\tfor (i = 0; i < imin; ++i)\n-\t\t\ttp[i].entry.mode |= S_IFXMIN_NEQ;\t/* pi > p[imin] */\n+\t\t\ttp[i].entry.raw_mode |= S_IFXMIN_NEQ;\t/* pi > p[imin] */\n \n \n \n@@ -497,14 +497,14 @@ static struct combine_diff_path *ll_diff_tree_paths(\n \t\t\t/* are either pi > p[imin] or diff(t,pi) != ø ? */\n \t\t\tif (!opt->flags.find_copies_harder) {\n \t\t\t\tfor (i = 0; i < nparent; ++i) {\n-\t\t\t\t\tunsigned int mode = tp[i].entry.mode;\n+\t\t\t\t\tunsigned int mode = tp[i].entry.raw_mode;\n \t\t\t\t\t/* p[i] > p[imin] */\n \t\t\t\t\tif (mode & S_IFXMIN_NEQ)\n \t\t\t\t\t\tcontinue;\n \n \t\t\t\t\t/* diff(t,pi) != ø */\n \t\t\t\t\tif (!oideq(&t.entry.oid, &tp[i].entry.oid) ||\n-\t\t\t\t\t    (t.entry.mode != mode))\n+\t\t\t\t\t    (t.entry.raw_mode != mode))\n \t\t\t\t\t\tcontinue;\n \n \t\t\t\t\tgoto skip_emit_t_tp;\n@@ -536,7 +536,7 @@ static struct combine_diff_path *ll_diff_tree_paths(\n \t\t\t/* ∀i pi=p[imin] -> D += \"-p[imin]\" */\n \t\t\tif (!opt->flags.find_copies_harder) {\n \t\t\t\tfor (i = 0; i < nparent; ++i)\n-\t\t\t\t\tif (tp[i].entry.mode & S_IFXMIN_NEQ)\n+\t\t\t\t\tif (tp[i].entry.raw_mode & S_IFXMIN_NEQ)\n \t\t\t\t\t\tgoto skip_emit_tp;\n \t\t\t}\n \ndiff --git a/tree-walk.c b/tree-walk.c\nindex 12e0ed4e250..099a9b3bd77 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -48,7 +48,7 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l\n \t/* Initialize the descriptor entry */\n \tdesc->entry.path = path;\n \tmode = canon_mode(mode);\n-\tdesc->entry.mode = mode;\n+\tdesc->entry.raw_mode = mode;\n \tdesc->entry.object_type = object_type(mode);\n \tdesc->entry.pathlen = len - 1;\n \thashcpy(desc->entry.oid.hash, (const unsigned char *)path + len);\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 1f69e57db4c..885ced74258 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -16,7 +16,7 @@ struct name_entry {\n \tstruct object_id oid;\n \tconst char *path;\n \tint pathlen;\n-\tunsigned int mode;\n+\tunsigned int raw_mode;\n \t/* simple 'mode': Only OBJ_{BLOB,TREE,COMMIT} */\n \tenum object_type object_type;\n };\n@@ -55,7 +55,7 @@ static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *\n \t\t\t\t\t\t\t      unsigned short *modep)\n {\n \t*pathp = desc->entry.path;\n-\t*modep = desc->entry.mode;\n+\t*modep = desc->entry.raw_mode;\n \treturn &desc->entry.oid;\n }\n \n@@ -75,7 +75,7 @@ static inline const struct object_id *tree_entry_extract_all(struct tree_desc *d\n \t\t\t\t\t\t\t     enum object_type *object_typep)\n {\n \t*pathp = desc->entry.path;\n-\t*modep = desc->entry.mode;\n+\t*modep = desc->entry.raw_mode;\n \t*object_typep = desc->entry.object_type;\n \treturn &desc->entry.oid;\n }\ndiff --git a/tree.c b/tree.c\nindex e4402fad69b..215d17e1295 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -40,7 +40,7 @@ static int read_tree_1(struct repository *r,\n \t\t}\n \n \t\tswitch (fn(&entry.oid, base,\n-\t\t\t   entry.path, entry.object_type, entry.mode, context)) {\n+\t\t\t   entry.path, entry.object_type, entry.raw_mode, context)) {\n \t\tcase 0:\n \t\t\tcontinue;\n \t\tcase READ_TREE_RECURSIVE:\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 9471c19de72..dcdf8130745 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -867,7 +867,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n \tnewinfo.pathspec = info->pathspec;\n \tnewinfo.name = p->path;\n \tnewinfo.namelen = p->pathlen;\n-\tnewinfo.mode = p->mode;\n+\tnewinfo.mode = p->raw_mode;\n \tnewinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1);\n \tnewinfo.df_conflicts |= df_conflicts;\n \n@@ -1020,7 +1020,7 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,\n \t\tis_transient ?\n \t\tmake_empty_transient_cache_entry(len) :\n \t\tmake_empty_cache_entry(istate, len);\n-\tunsigned int mode = n->mode;\n+\tunsigned int mode = n->raw_mode;\n \n \tce->ce_mode = create_ce_mode(mode);\n \tce->ce_flags = create_ce_flags(stage);\n@@ -1209,7 +1209,7 @@ static void debug_path(struct traverse_info *info)\n static void debug_name_entry(int i, struct name_entry *n)\n {\n \tprintf(\"ent#%d %06o %s\\n\", i,\n-\t       n->path ? n->mode : 0,\n+\t       n->path ? n->raw_mode : 0,\n \t       n->path ? n->path : \"(missing)\");\n }\n \n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418473","messageId":"20210308150650.18626-30-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 29/30] tree.h API users: rename read_tree_fn_t's \"mode\" to \"raw_mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:49Z","receivedAt":"2021-03-08T15:08:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the \"mode\" variable passed to read_tree_fn_t callbacks to\n\"raw_mode\". This variable comes to us from the tree-walk.h API. By\nrenaming this variable we can easily see where its downstream users\nare in a subsequent commit where we'll sprinkle some canon_mode()\nhere.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          |  5 +++--\n builtin/log.c      |  2 +-\n builtin/ls-files.c | 11 ++++++-----\n builtin/ls-tree.c  |  6 +++---\n merge-recursive.c  |  2 +-\n 5 files changed, 14 insertions(+), 12 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex bc8f1c7546f..5b85aae8106 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -232,10 +232,11 @@ static int write_directory(struct archiver_context *c)\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\t\t\t\tstruct strbuf *base, const char *filename,\n-\t\t\t\t\tenum object_type object_type, unsigned mode,\n+\t\t\t\t\tenum object_type object_type, unsigned raw_mode,\n \t\t\t\t\tvoid *context)\n {\n \tstruct archiver_context *c = context;\n+\tunsigned mode = raw_mode;\n \n \twhile (c->bottom &&\n \t       !(base->len >= c->bottom->len &&\n@@ -382,7 +383,7 @@ struct path_exists_context {\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n \t\t\tconst char *filename,\n-\t\t\tenum object_type object_type, unsigned mode,\n+\t\t\tenum object_type object_type, unsigned raw_mode,\n \t\t\tvoid *context)\n {\n \tint ret = -1;\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 19a916221d5..c3ef1b3e22d 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -599,7 +599,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n \n static int show_tree_object(const struct object_id *oid,\n \t\t\t    struct strbuf *base, const char *pathname,\n-\t\t\t    enum object_type object_type, unsigned mode,\n+\t\t\t    enum object_type object_type, unsigned raw_mode,\n \t\t\t    void *context)\n {\n \tFILE *file = context;\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex f38df439410..391e6a9f141 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -425,10 +425,11 @@ static int read_one_entry_opt(struct index_state *istate,\n \t\t\t      const struct object_id *oid,\n \t\t\t      struct strbuf *base,\n \t\t\t      const char *pathname,\n-\t\t\t      unsigned mode, int opt)\n+\t\t\t      unsigned raw_mode, int opt)\n {\n \tint len;\n \tstruct cache_entry *ce;\n+\tunsigned mode = raw_mode;\n \n \tif (S_ISDIR(mode))\n \t\treturn READ_TREE_RECURSIVE;\n@@ -447,12 +448,12 @@ static int read_one_entry_opt(struct index_state *istate,\n \n static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n \t\t\t  const char *pathname,\n-\t\t\t  enum object_type object_type, unsigned mode,\n+\t\t\t  enum object_type object_type, unsigned raw_mode,\n \t\t\t  void *context)\n {\n \tstruct index_state *istate = context;\n \treturn read_one_entry_opt(istate, oid, base, pathname,\n-\t\t\t\t  mode,\n+\t\t\t\t  raw_mode,\n \t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n }\n \n@@ -462,12 +463,12 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n  */\n static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n \t\t\t\tconst char *pathname,\n-\t\t\t\tenum object_type object_type, unsigned mode,\n+\t\t\t\tenum object_type object_type, unsigned raw_mode,\n \t\t\t\tvoid *context)\n {\n \tstruct index_state *istate = context;\n \treturn read_one_entry_opt(istate, oid, base, pathname,\n-\t\t\t\t  mode,\n+\t\t\t\t  raw_mode,\n \t\t\t\t  ADD_CACHE_JUST_APPEND);\n }\n \ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex c6ec3ca751e..3f84603d391 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -63,7 +63,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n \n static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t     const char *pathname,\n-\t\t     enum object_type object_type, unsigned mode,\n+\t\t     enum object_type object_type, unsigned raw_mode,\n \t\t     void *context)\n {\n \tint retval = 0;\n@@ -103,11 +103,11 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\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\tprintf(\"%06o %s %s %7s\\t\", mode, type,\n+\t\t\tprintf(\"%06o %s %s %7s\\t\", raw_mode, 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\", raw_mode, type,\n \t\t\t       find_unique_abbrev(oid, abbrev));\n \t}\n \tbaselen = base->len;\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex b26d9d418f9..30fbe72ca06 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,7 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   enum object_type object_type, unsigned int mode,\n+\t\t\t   enum object_type object_type, unsigned int raw_mode,\n \t\t\t   void *context)\n {\n \tstruct path_hashmap_entry *entry;\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418477","messageId":"20210308150650.18626-31-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 30/30] tree-walk.h API: move canon_mode() back out of decode_tree_entry()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:50Z","receivedAt":"2021-03-08T15:08:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Move the canon_mode() call back out of decode_tree_entry(), and\ninstead make it the responsibility of its callers to canonicalize the\ntree modes we get.\n\nThis effectively reverts 7146e66f086 (tree-walk: finally switch over\ntree descriptors to contain a pre-parsed entry, 2014-02-06), with the\nrecent of most callers away from \"mode\" (now \"raw_mode\") towards \"enum\nobject_id\" in recent commit the motivation for that commit effectively\ndoesn't exist anymore.\n\nI.e. I'm not adding the canon_mode() call back to\ntree_entry_extract(), instead it's now become sane to move this\nresponsibility to those callers that still care about the \"raw_mode\".\n\nThat change was meant as a pure optimization change, but it actually\nintroduced a subtle bug. We were left without any low-level API to get\nnon-standard mode bits out of trees. Having non-standard modes isn't\nthe norm, and fsck should warn about it.\n\nExcept after 7146e66f086 it couldn't anymore, since the modes\nfsck_tree() got would be pre-sanitized for it. I believe that fsck\nissue is per-se a serious bug, the \"bad mode\" was a default warning,\nnot an error.\n\nThis change makes that fsck check work again, why aren't there any\ntest changes for fsck here? Because we didn't have a test for that\nfsck feature in the first place, which is why the regression in\n7146e66f086 snuck by us. A follow-up commit will add such a test.\n\nIt is possible that this commit is introducing some subtle regression\nthat I've missed.\n\nWe are now propagating the \"raw_mode\" outside of everything downstream\nof decode_tree_entry(), which is everything we have that decodes\ntrees. It's our most low-level tree decoding API.\n\nAs shown here we rely parsing out a \"raw\" (and possibly something fsck\nwould complain about) mode as-is, but when we run merge, add something\nnew to the index, create an archive etc. we don't want to propagate\nthat bad mode when we create new data. We want to canon_mode() it.\n\nI'm also pretty sure that we don't have good enough test coverage for\nthose scenarios. We barely have tests for these bad mode bits at\nall (not even one for fsck). We definitely are not testing all\nmerge/index/archive etc. interactions.\n\nStill, I think this change is worth it overall, because:\n\n 1. We must have a way to get at these raw modes in some way, even if\n    just for fsck. There's also other things that care, see e.g. the\n    FIXME comment in 62fdec17a11 (merge-ort: flesh out implementation of\n    handle_content_merge(), 2021-01-01)\n\n 2. #1 is not a justification for this change, I could have e.g. just\n    added the ability to pass some \"want_raw\" flag into\n    decode_tree_entry() for use in fsck. But I think with the migration\n    of most tree iteration towards \"enum object_type\" it's become worth\n    it.\n\n 3. Yes our test coverage sucks, but before 7146e66f086 we were also\n    spreading what's now the \"raw_mode\" all over the place. That commit\n    was first released with Git v2.0.0 in mid-2014. A while ago for sure,\n    but most of this code existed in something approximating its current\n    form then. This isn't new territory.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c              |  2 +-\n builtin/checkout.c     |  1 +\n builtin/ls-files.c     |  2 +-\n builtin/merge-tree.c   |  6 +++---\n builtin/update-index.c |  1 +\n merge-ort.c            | 13 ++++++++++++-\n notes.c                |  1 +\n tree-walk.c            |  1 -\n unpack-trees.c         |  4 +++-\n 9 files changed, 23 insertions(+), 8 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 5b85aae8106..8083f15f3ba 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -236,7 +236,7 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\t\t\t\tvoid *context)\n {\n \tstruct archiver_context *c = context;\n-\tunsigned mode = raw_mode;\n+\tunsigned mode = canon_mode(raw_mode);\n \n \twhile (c->bottom &&\n \t       !(base->len >= c->bottom->len &&\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex d4adfdb5046..7f25b955616 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -132,6 +132,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,\n \tmemcpy(ce->name + base->len, pathname, len - base->len);\n \tce->ce_flags = create_ce_flags(0) | CE_UPDATE;\n \tce->ce_namelen = len;\n+\tmode = canon_mode(mode);\n \tce->ce_mode = create_ce_mode(mode);\n \n \t/*\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 391e6a9f141..926523d77a7 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -429,7 +429,7 @@ static int read_one_entry_opt(struct index_state *istate,\n {\n \tint len;\n \tstruct cache_entry *ce;\n-\tunsigned mode = raw_mode;\n+\tunsigned mode = canon_mode(raw_mode);\n \n \tif (S_ISDIR(mode))\n \t\treturn READ_TREE_RECURSIVE;\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex b4e736e4b72..f8733a86eb7 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -197,9 +197,9 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s\n \t\treturn;\n \n \tpath = traverse_path(info, result);\n-\torig_mode = ours->raw_mode;\n+\torig_mode = canon_mode(ours->raw_mode);\n \torig = create_entry(2, orig_mode, &ours->oid, path);\n-\tfinal_mode = result->raw_mode;\n+\tfinal_mode = canon_mode(result->raw_mode);\n \tfinal = create_entry(0, final_mode, &result->oid, path);\n \n \tfinal->link = orig;\n@@ -252,7 +252,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n \t\tpath = entry->path;\n \telse\n \t\tpath = traverse_path(info, n);\n-\tlink_mode = n->raw_mode;\n+\tlink_mode = canon_mode(n->raw_mode);\n \tlink = create_entry(stage, link_mode, &n->oid, path);\n \n \tlink->link = entry;\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex b489a876392..1996fdd97af 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -621,6 +621,7 @@ static struct cache_entry *read_one_ent(const char *which,\n \tmemcpy(ce->name, path, namelen);\n \tce->ce_flags = create_ce_flags(stage);\n \tce->ce_namelen = namelen;\n+\tmode = canon_mode(mode);\n \tce->ce_mode = create_ce_mode(mode);\n \treturn ce;\n }\ndiff --git a/merge-ort.c b/merge-ort.c\nindex ea20bbe2af3..d1e8a2823e0 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -502,7 +502,7 @@ static void setup_path_info(struct merge_options *opt,\n \tmi->basename_offset = current_dir_name_len;\n \tmi->clean = !!resolved;\n \tif (resolved) {\n-\t\tmi->result.mode = merged_version->raw_mode;\n+\t\tmi->result.mode = canon_mode(merged_version->raw_mode);\n \t\toidcpy(&mi->result.oid, &merged_version->oid);\n \t\tmi->is_null = !!is_null;\n \t} else {\n@@ -512,6 +512,16 @@ static void setup_path_info(struct merge_options *opt,\n \t\tASSIGN_AND_VERIFY_CI(ci, mi);\n \t\tfor (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {\n \t\t\tci->pathnames[i] = fullpath;\n+\t\t\t/*\n+\t\t\t * We must not use canon_mode() here. Will\n+\t\t\t * fail on an the is_null assertion in\n+\t\t\t * 6a02dd90c99 (merge-ort: add a preliminary\n+\t\t\t * simple process_entries() implementation,\n+\t\t\t * 2020-12-13) when combined with the tests in\n+\t\t\t * \"[PATCH 00/11] Complete merge-ort\n+\t\t\t * implementation...almost\" (see\n+\t\t\t * https://lore.kernel.org/git/pull.973.git.git.1614905738.gitgitgadget@gmail.com/)\n+\t\t\t */\n \t\t\tci->stages[i].mode = names[i].raw_mode;\n \t\t\toidcpy(&ci->stages[i].oid, &names[i].oid);\n \t\t}\n@@ -546,6 +556,7 @@ static void add_pair(struct merge_options *opt,\n \tint names_idx = is_add ? side : 0;\n \tconst struct object_id *oid = &names[names_idx].oid;\n \tunsigned int mode = names[names_idx].raw_mode;\n+\tmode = canon_mode(mode);\n \n \tone = alloc_filespec(pathname);\n \ttwo = alloc_filespec(pathname);\ndiff --git a/notes.c b/notes.c\nindex 2817325651a..78b1b38d36b 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -479,6 +479,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\tconst char *q = oid_to_hex(&subtree->key_oid);\n \t\t\tsize_t i;\n \t\t\tunsigned int mode = entry.raw_mode;\n+\t\t\tmode = canon_mode(mode);\n \t\t\tfor (i = 0; i < prefix_len; i++) {\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 099a9b3bd77..3175430d049 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -47,7 +47,6 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l\n \n \t/* Initialize the descriptor entry */\n \tdesc->entry.path = path;\n-\tmode = canon_mode(mode);\n \tdesc->entry.raw_mode = mode;\n \tdesc->entry.object_type = object_type(mode);\n \tdesc->entry.pathlen = len - 1;\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex dcdf8130745..2fb346714b3 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -868,6 +868,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n \tnewinfo.name = p->path;\n \tnewinfo.namelen = p->pathlen;\n \tnewinfo.mode = p->raw_mode;\n+\tnewinfo.mode = canon_mode(newinfo.mode);\n \tnewinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1);\n \tnewinfo.df_conflicts |= df_conflicts;\n \n@@ -1020,7 +1021,8 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,\n \t\tis_transient ?\n \t\tmake_empty_transient_cache_entry(len) :\n \t\tmake_empty_cache_entry(istate, len);\n-\tunsigned int mode = n->raw_mode;\n+\tunsigned int mode = canon_mode(n->raw_mode);\n+\tmode = canon_mode(mode);\n \n \tce->ce_mode = create_ce_mode(mode);\n \tce->ce_flags = create_ce_flags(stage);\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418478","messageId":"20210308150650.18626-28-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH 27/30] tree-walk.h API: add a tree_entry_extract_type() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-08T15:06:47Z","receivedAt":"2021-03-08T15:08:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add and use a tree_entry_extract_type() function. There were callers\nof tree_entry_extract() which didn't care about the mode, but just the\ntype in the tree entry.\n\nIn emit_path() the \"mode\" variable was not used after the \"isdir\"\nassignment, as can be seen in the diff with it being set to 0.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c | 12 ++++++------\n tree-diff.c   |  5 +++--\n tree-walk.h   | 11 +++++++++++\n 3 files changed, 20 insertions(+), 8 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex 3d2c74a44ac..0636f6e58e9 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -143,11 +143,11 @@ static void match_trees(const struct object_id *hash1,\n \twhile (one.size) {\n \t\tconst char *path;\n \t\tconst struct object_id *elem;\n-\t\tunsigned short mode;\n+\t\tenum object_type object_type;\n \t\tint score;\n \n-\t\telem = tree_entry_extract_mode(&one, &path, &mode);\n-\t\tif (!S_ISDIR(mode))\n+\t\telem = tree_entry_extract_type(&one, &path, &object_type);\n+\t\tif (object_type != OBJ_TREE)\n \t\t\tgoto next;\n \t\tscore = score_trees(elem, hash2);\n \t\tif (*best_score < score) {\n@@ -198,14 +198,14 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \n \trewrite_here = NULL;\n \twhile (desc.size) {\n+\t\tenum object_type object_type;\n \t\tconst char *name;\n-\t\tunsigned short mode;\n \t\tint len = tree_entry_len(&desc.entry);\n \n-\t\ttree_entry_extract_mode(&desc, &name, &mode);\n+\t\ttree_entry_extract_type(&desc, &name, &object_type);\n \t\tif (len == toplen &&\n \t\t    !memcmp(name, prefix, toplen)) {\n-\t\t\tif (!S_ISDIR(mode))\n+\t\t\tif (object_type != OBJ_TREE)\n \t\t\t\tdie(\"entry %s in tree %s is not a tree\", name,\n \t\t\t\t    oid_to_hex(oid1));\n \ndiff --git a/tree-diff.c b/tree-diff.c\nindex b25095c1164..10c92d39c42 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -208,10 +208,11 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \t\t * 1) all modes for tp[i]=tp[imin] should be the same wrt\n \t\t *    S_ISDIR, thanks to base_name_compare().\n \t\t */\n-\t\ttree_entry_extract_mode(&tp[imin], &path, &mode);\n+\t\tenum object_type object_type;\n+\t\ttree_entry_extract_type(&tp[imin], &path, &object_type);\n \t\tpathlen = tree_entry_len(&tp[imin].entry);\n \n-\t\tisdir = S_ISDIR(mode);\n+\t\tisdir = object_type == OBJ_TREE;\n \t\toid = NULL;\n \t\tmode = 0;\n \t}\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 06ad40ab2f1..1f69e57db4c 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -47,6 +47,7 @@ struct tree_desc {\n  * appropriate variable to fill in (NULL won't do!):\n  *\n  * tree_entry_extract_mode(): const char *path, unsigned int mode\n+ * tree_entry_extract_type(): const char *path, enum object_type\n  * tree_entry_extract_all(): const char *path, unsigned int mode, enum object_type\n  */\n static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *desc,\n@@ -58,6 +59,16 @@ static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *\n \treturn &desc->entry.oid;\n }\n \n+static inline const struct object_id *tree_entry_extract_type(struct tree_desc *desc,\n+\t\t\t\t\t\t\t      const char **pathp,\n+\t\t\t\t\t\t\t      enum object_type *object_typep)\n+{\n+\t*pathp = desc->entry.path;\n+\t*object_typep = desc->entry.object_type;\n+\treturn &desc->entry.oid;\n+}\n+\n+\n static inline const struct object_id *tree_entry_extract_all(struct tree_desc *desc,\n \t\t\t\t\t\t\t     const char **pathp,\n \t\t\t\t\t\t\t     unsigned short *modep,\n-- \n2.31.0.rc0.126.g04f22c5b82\n\n"},{"id":"418487","messageId":"xmqq5z217c64.fsf@gitster.c.googlers.com","threadId":"18927","inReplyTo":"20210308022138.28166-3-avarab@gmail.com","subject":"Re: [PATCH v2 2/6] tree.c API: move read_tree() into builtin/ls-files.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-08T18:06:27Z","receivedAt":"2021-03-08T18:07:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Now builtin/ls-files.c is the last user of this code, let's move all\n> the relevant code there. This allows for subsequent simplification of\n> it, and an eventual move to read_tree_recursive().\n\nA nice goal to keep in mind while reading the series.\n\nIs one already a subset of the other, or does the surviving version\nneed to gain a bit more feature or performance to replace the other?\n\nThanks.\n"},{"id":"418488","messageId":"xmqq1rcp7bmp.fsf@gitster.c.googlers.com","threadId":"18927","inReplyTo":"20210308022138.28166-4-avarab@gmail.com","subject":"Re: [PATCH v2 3/6] ls-files: don't needlessly pass around stage variable","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-08T18:18:06Z","receivedAt":"2021-03-08T18:19:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Now that read_tree() has been moved to ls-files.c we can get rid of\n> the stage != 1 case that'll never happen.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/ls-files.c | 17 ++++-------------\n>  1 file changed, 4 insertions(+), 13 deletions(-)\n>\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index a4458622813..74d572a3e4a 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -470,21 +470,12 @@ static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base\n>  }\n>  \n>  \n> -static int read_tree(struct repository *r, struct tree *tree, int stage,\n> +static int read_tree(struct repository *r, struct tree *tree,\n>  \t\t     struct pathspec *match, struct index_state *istate)\n>  {\n>  \tread_tree_fn_t fn = NULL;\n>  \tint i, err;\n>  \n> -\t/*\n> -\t * Currently the only existing callers of this function all\n> -\t * call it with stage=1 and after making sure there is nothing\n> -\t * at that stage; we could always use read_one_entry_quick().\n> -\t *\n> -\t * But when we decide to straighten out git-read-tree not to\n> -\t * use unpack_trees() in some cases, this will probably start\n> -\t * to matter.\n> -\t */\n\nThe \"hoist everything to stage#1, read and compare\" the commit that\nintroduced this comment talks about (cf. af3785dc5a7) was entirely\nrewritten by d1f2d7e8 (Make run_diff_index() use unpack_trees(), not\nread_tree(), 2008-01-19); this clean-up is long overdue.\n\nLooking good.\n\n\n\n\n\n>  \t/*\n>  \t * See if we have cache entry at the stage.  If so,\n> @@ -493,13 +484,13 @@ static int read_tree(struct repository *r, struct tree *tree, int stage,\n>  \t */\n>  \tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n>  \t\tconst struct cache_entry *ce = istate->cache[i];\n> -\t\tif (ce_stage(ce) == stage)\n> +\t\tif (ce_stage(ce) == 1)\n>  \t\t\tfn = read_one_entry;\n>  \t}\n>  \n>  \tif (!fn)\n>  \t\tfn = read_one_entry_quick;\n> -\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n> +\terr = read_tree_recursive(r, tree, \"\", 0, 1, match, fn, istate);\n>  \tif (fn == read_one_entry || err)\n>  \t\treturn err;\n>  \n> @@ -549,7 +540,7 @@ void overlay_tree_on_index(struct index_state *istate,\n>  \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n>  \t} else\n>  \t\tmemset(&pathspec, 0, sizeof(pathspec));\n> -\tif (read_tree(the_repository, tree, 1, &pathspec, istate))\n> +\tif (read_tree(the_repository, tree, &pathspec, istate))\n>  \t\tdie(\"unable to read tree entries %s\", tree_name);\n>  \n>  \tfor (i = 0; i < istate->cache_nr; i++) {\n"},{"id":"418489","messageId":"xmqqwnuh5x0j.fsf@gitster.c.googlers.com","threadId":"18927","inReplyTo":"20210308022138.28166-5-avarab@gmail.com","subject":"Re: [PATCH v2 4/6] ls-files: refactor away read_tree()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-08T18:19:08Z","receivedAt":"2021-03-08T18:19:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Refactor away the read_tree() function into its only user,\n> overlay_tree_on_index().\n\nNice.\n"},{"id":"418510","messageId":"CABPp-BEfEQZzymkTGVTJ7cHjaPfXNeek+ikm5C0YYD2YYjpsFw@mail.gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"Re: [PATCH v2 0/6] Move the read_tree() function to its only user","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-08T19:18:51Z","receivedAt":"2021-03-08T19:19:51Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Mar 7, 2021 at 6:22 PM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> v1 of this series over-removed code supporting the \"ls-files\n> --with-tree=*\" parameter. In v2 there's no change to its behavior,\n> just refactoring away of read_tree() from the tree.c API and the\n> cleanup of read_tree_recursive(). Thanks to Elijah for spotting that.\n>\n> I've added a test at the start of this series that would have caught\n> that regression in v1 (and more).\n>\n> 1. https://lore.kernel.org/git/CABPp-BF982muRS4GO=zYegvetQyrPMwaEM3uEBvcbPRP=krfmQ@mail.gmail.com/\n\nThis addresses my concerns with v1; so:\n\nReviewed-by: Elijah Newren <newren@gmail.com>\n\n>\n> Ævar Arnfjörð Bjarmason (6):\n>   ls-files tests: add meaningful --with-tree tests\n>   tree.c API: move read_tree() into builtin/ls-files.c\n>   ls-files: don't needlessly pass around stage variable\n>   ls-files: refactor away read_tree()\n>   tree.h API: remove support for starting at prefix != \"\"\n>   tree.h API: remove \"stage\" parameter from read_tree_recursive()\n>\n>  archive.c                     |  13 +++--\n>  builtin/checkout.c            |   4 +-\n>  builtin/log.c                 |   6 +-\n>  builtin/ls-files.c            |  76 ++++++++++++++++++++++++-\n>  builtin/ls-tree.c             |   4 +-\n>  merge-recursive.c             |   4 +-\n>  t/t3060-ls-files-with-tree.sh |  41 ++++++++++++++\n>  tree.c                        | 101 ++--------------------------------\n>  tree.h                        |  10 +---\n>  9 files changed, 139 insertions(+), 120 deletions(-)\n>\n> Range-diff:\n> -:  ----------- > 1:  6416da0dee2 ls-files tests: add meaningful --with-tree tests\n> 1:  020534164d3 ! 2:  765001b44cd tree.c API: move read_tree() into builtin/ls-files.c\n>     @@ tree.c: int cmp_cache_name_compare(const void *a_, const void *b_)\n>\n>       ## tree.h ##\n>      @@ tree.h: int read_tree_recursive(struct repository *r,\n>     +                   const char *base, int baselen,\n>                         int stage, const struct pathspec *pathspec,\n>                         read_tree_fn_t fn, void *context);\n>     -\n>     +-\n>      -int read_tree(struct repository *r, struct tree *tree,\n>      -        int stage, struct pathspec *pathspec,\n>      -        struct index_state *istate);\n> 2:  6aa6ba2fbb5 = 3:  a71ffba7d04 ls-files: don't needlessly pass around stage variable\n> 3:  4f27e5d2970 ! 4:  e78d1810b89 ls-files: remove cache juggling + sorting\n>     @@ Metadata\n>      Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n>       ## Commit message ##\n>     -    ls-files: remove cache juggling + sorting\n>     +    ls-files: refactor away read_tree()\n>\n>     -    Remove the \"ce_stage(ce) == 1\" and \"Sort the cache entry\" code from\n>     -    read_tree(), which allows us to remove the function entirely and move\n>     -    over to read_tree_recursive().\n>     +    Refactor away the read_tree() function into its only user,\n>     +    overlay_tree_on_index().\n>\n>     -    I don't think the \"Sort the cached entry\" code was needed here, see\n>     -    af3785dc5a7 (Optimize \"diff --cached\" performance., 2007-08-09) for\n>     -    the use-case it was intended for. The only user of this code is\n>     -    \"ls-files --with-tree\", which isn't the sort of use-case that needs to\n>     -    care about \"ce_stage(ce) != 0\" or sorting tree entries.\n>     +    First, change read_one_entry_opt() to use the strbuf parameter\n>     +    read_tree_recursive() passes down in place. This finishes up a partial\n>     +    refactoring started in 6a0b0b6de99 (tree.c: update read_tree_recursive\n>     +    callback to pass strbuf as base, 2014-11-30).\n>     +\n>     +    Moving the rest into overlay_tree_on_index() makes this index juggling\n>     +    we're doing easier to read.\n>\n>          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n>       ## builtin/ls-files.c ##\n>     -@@\n>     - #include \"dir.h\"\n>     - #include \"builtin.h\"\n>     - #include \"tree.h\"\n>     --#include \"cache-tree.h\"\n>     - #include \"parse-options.h\"\n>     - #include \"resolve-undo.h\"\n>     - #include \"string-list.h\"\n>      @@ builtin/ls-files.c: static int get_common_prefix_len(const char *common_prefix)\n>     -   return common_prefix_len;\n>     - }\n>\n>     --static int read_one_entry_opt(struct index_state *istate,\n>     --                        const struct object_id *oid,\n>     + static int read_one_entry_opt(struct index_state *istate,\n>     +                         const struct object_id *oid,\n>      -                        const char *base, int baselen,\n>     --                        const char *pathname,\n>     --                        unsigned mode, int stage, int opt)\n>     -+static int read_one_entry_quick(const struct object_id *oid,\n>     -+                          struct strbuf *basebuf,\n>     -+                          const char *pathname,\n>     -+                          unsigned mode,\n>     -+                          int stage, void *context)\n>     ++                        struct strbuf *base,\n>     +                         const char *pathname,\n>     +                         unsigned mode, int stage, int opt)\n>       {\n>     -+  struct index_state *istate = context;\n>     -+  const char *base = basebuf->buf;\n>     -+  const int baselen = basebuf->len;\n>     -   int len;\n>     -   struct cache_entry *ce;\n>     -\n>      @@ builtin/ls-files.c: static int read_one_entry_opt(struct index_state *istate,\n>     -   memcpy(ce->name, base, baselen);\n>     -   memcpy(ce->name + baselen, pathname, len+1);\n>     +           return READ_TREE_RECURSIVE;\n>     +\n>     +   len = strlen(pathname);\n>     +-  ce = make_empty_cache_entry(istate, baselen + len);\n>     ++  ce = make_empty_cache_entry(istate, base->len + len);\n>     +\n>     +   ce->ce_mode = create_ce_mode(mode);\n>     +   ce->ce_flags = create_ce_flags(stage);\n>     +-  ce->ce_namelen = baselen + len;\n>     +-  memcpy(ce->name, base, baselen);\n>     +-  memcpy(ce->name + baselen, pathname, len+1);\n>     ++  ce->ce_namelen = base->len + len;\n>     ++  memcpy(ce->name, base->buf, base->len);\n>     ++  memcpy(ce->name + base->len, pathname, len+1);\n>         oidcpy(&ce->oid, oid);\n>     --  return add_index_entry(istate, ce, opt);\n>     --}\n>     --\n>     --static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n>     --                    const char *pathname, unsigned mode, int stage,\n>     --                    void *context)\n>     --{\n>     --  struct index_state *istate = context;\n>     +   return add_index_entry(istate, ce, opt);\n>     + }\n>     +@@ builtin/ls-files.c: static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n>     +                     void *context)\n>     + {\n>     +   struct index_state *istate = context;\n>      -  return read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n>     --                            mode, stage,\n>     --                            ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n>     --}\n>     --\n>     --/*\n>     -- * This is used when the caller knows there is no existing entries at\n>     -- * the stage that will conflict with the entry being added.\n>     -- */\n>     --static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n>     --                          const char *pathname, unsigned mode, int stage,\n>     --                          void *context)\n>     --{\n>     --  struct index_state *istate = context;\n>     ++  return read_one_entry_opt(istate, oid, base, pathname,\n>     +                             mode, stage,\n>     +                             ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n>     + }\n>     +@@ builtin/ls-files.c: static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base\n>     +                           void *context)\n>     + {\n>     +   struct index_state *istate = context;\n>      -  return read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n>     --                            mode, stage,\n>     --                            ADD_CACHE_JUST_APPEND);\n>     --}\n>     --\n>     ++  return read_one_entry_opt(istate, oid, base, pathname,\n>     +                             mode, stage,\n>     +                             ADD_CACHE_JUST_APPEND);\n>     + }\n>     +\n>      -\n>      -static int read_tree(struct repository *r, struct tree *tree,\n>      -               struct pathspec *match, struct index_state *istate)\n>     @@ builtin/ls-files.c: static int read_one_entry_opt(struct index_state *istate,\n>      -  cache_tree_free(&istate->cache_tree);\n>      -  QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n>      -  return 0;\n>     -+  return add_index_entry(istate, ce, ADD_CACHE_JUST_APPEND);\n>     - }\n>     -\n>     +-}\n>     +-\n>       /*\n>     +  * Read the tree specified with --with-tree option\n>     +  * (typically, HEAD) into stage #1 and then\n>     +@@ builtin/ls-files.c: void overlay_tree_on_index(struct index_state *istate,\n>     +   struct pathspec pathspec;\n>     +   struct cache_entry *last_stage0 = NULL;\n>     +   int i;\n>     ++  read_tree_fn_t fn = NULL;\n>     ++  int err;\n>     +\n>     +   if (get_oid(tree_name, &oid))\n>     +           die(\"tree-ish %s not found.\", tree_name);\n>      @@ builtin/ls-files.c: void overlay_tree_on_index(struct index_state *istate,\n>                                PATHSPEC_PREFER_CWD, prefix, matchbuf);\n>         } else\n>                 memset(&pathspec, 0, sizeof(pathspec));\n>      -  if (read_tree(the_repository, tree, &pathspec, istate))\n>     -+  if (read_tree_recursive(the_repository, tree, \"\", 0, 1,\n>     -+                          &pathspec, read_one_entry_quick, istate))\n>     ++\n>     ++  /*\n>     ++   * See if we have cache entry at the stage.  If so,\n>     ++   * do it the original slow way, otherwise, append and then\n>     ++   * sort at the end.\n>     ++   */\n>     ++  for (i = 0; !fn && i < istate->cache_nr; i++) {\n>     ++          const struct cache_entry *ce = istate->cache[i];\n>     ++          if (ce_stage(ce) == 1)\n>     ++                  fn = read_one_entry;\n>     ++  }\n>     ++\n>     ++  if (!fn)\n>     ++          fn = read_one_entry_quick;\n>     ++  err = read_tree_recursive(the_repository, tree, \"\", 0, 1, &pathspec, fn, istate);\n>     ++  if (err)\n>                 die(\"unable to read tree entries %s\", tree_name);\n>\n>     ++  /*\n>     ++   * Sort the cache entry -- we need to nuke the cache tree, though.\n>     ++   */\n>     ++  if (fn == read_one_entry_quick) {\n>     ++          cache_tree_free(&istate->cache_tree);\n>     ++          QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n>     ++  }\n>     ++\n>         for (i = 0; i < istate->cache_nr; i++) {\n>     +           struct cache_entry *ce = istate->cache[i];\n>     +           switch (ce_stage(ce)) {\n> 4:  33810d3c10c < -:  ----------- merge-ort: move cmp_cache_name_compare() from tree.c\n> 5:  fb10246b85b < -:  ----------- ls-files: refactor read_one_entry_quick() to use a strbuf\n> 6:  0c065615aec ! 5:  05eecdd7519 tree.h API: remove support for starting at prefix != \"\"\n>     @@ Metadata\n>       ## Commit message ##\n>          tree.h API: remove support for starting at prefix != \"\"\n>\n>     -    Every caller or the read_tree_recursive() function hardcoded a\n>     +    Every caller of the read_tree_recursive() function hardcoded a\n>          starting point of \"\" in the tree. So let's simply remove that\n>          parameter.\n>\n>     -    It might be useful in the future to get this functionality back,\n>     -    there's no reason we won't have a read_tree_recursive() use-case that\n>     -    would want to start in a subdirectory.\n>     +    The last function to call read_tree_recursive() with a non-\"\" path was\n>     +    read_tree_recursive() itself, but that was changed in\n>     +    ffd31f661d5 (Reimplement read_tree_recursive() using\n>     +    tree_entry_interesting(), 2011-03-25).\n>\n>     -    But if and when that happens we can just add something like a\n>     -    read_tree_recursive_subdir() and have both read_tree_recursive() and\n>     -    that function be a thin wrapper for read_tree_1().\n>     +    If in the future we need to support recursively reading trees without\n>     +    starting at the root we can easily add a read_tree_recursive_subdir(),\n>     +    and make that function a thin wrapper for read_tree_1().\n>\n>          In the meantime there's no reason to keep around what amounts to dead\n>     -    code just in case we need it in the future.\n>     +    code, just in case we need it in the future.\n>\n>          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n>     @@ builtin/log.c: int cmd_show(int argc, const char **argv, const char *prefix)\n>\n>       ## builtin/ls-files.c ##\n>      @@ builtin/ls-files.c: void overlay_tree_on_index(struct index_state *istate,\n>     -                          PATHSPEC_PREFER_CWD, prefix, matchbuf);\n>     -   } else\n>     -           memset(&pathspec, 0, sizeof(pathspec));\n>     --  if (read_tree_recursive(the_repository, tree, \"\", 0, 1,\n>     -+  if (read_tree_recursive(the_repository, tree, 1,\n>     -                           &pathspec, read_one_entry_quick, istate))\n>     +\n>     +   if (!fn)\n>     +           fn = read_one_entry_quick;\n>     +-  err = read_tree_recursive(the_repository, tree, \"\", 0, 1, &pathspec, fn, istate);\n>     ++  err = read_tree_recursive(the_repository, tree, 1, &pathspec, fn, istate);\n>     +   if (err)\n>                 die(\"unable to read tree entries %s\", tree_name);\n>\n>\n>     @@ tree.h: typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *,\n>      -                  const char *base, int baselen,\n>                         int stage, const struct pathspec *pathspec,\n>                         read_tree_fn_t fn, void *context);\n>     -\n>     + #endif /* TREE_H */\n> 7:  9685c7c5c50 ! 6:  fcecc82e1c8 tree.h API: remove \"stage\" parameter from read_tree_recursive()\n>     @@ builtin/log.c: int cmd_show(int argc, const char **argv, const char *prefix)\n>                         break;\n>\n>       ## builtin/ls-files.c ##\n>     -@@ builtin/ls-files.c: static int read_one_entry_quick(const struct object_id *oid,\n>     -                           struct strbuf *base,\n>     -                           const char *pathname,\n>     -                           unsigned mode,\n>     --                          int stage, void *context)\n>     -+                          void *context)\n>     +@@ builtin/ls-files.c: static int read_one_entry_opt(struct index_state *istate,\n>     +                         const struct object_id *oid,\n>     +                         struct strbuf *base,\n>     +                         const char *pathname,\n>     +-                        unsigned mode, int stage, int opt)\n>     ++                        unsigned mode, int opt)\n>       {\n>     -   struct index_state *istate = context;\n>         int len;\n>     -@@ builtin/ls-files.c: static int read_one_entry_quick(const struct object_id *oid,\n>     +   struct cache_entry *ce;\n>     +@@ builtin/ls-files.c: static int read_one_entry_opt(struct index_state *istate,\n>         ce = make_empty_cache_entry(istate, base->len + len);\n>\n>         ce->ce_mode = create_ce_mode(mode);\n>     @@ builtin/ls-files.c: static int read_one_entry_quick(const struct object_id *oid,\n>         ce->ce_namelen = base->len + len;\n>         memcpy(ce->name, base->buf, base->len);\n>         memcpy(ce->name + base->len, pathname, len+1);\n>     +@@ builtin/ls-files.c: static int read_one_entry_opt(struct index_state *istate,\n>     + }\n>     +\n>     + static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n>     +-                    const char *pathname, unsigned mode, int stage,\n>     ++                    const char *pathname, unsigned mode,\n>     +                     void *context)\n>     + {\n>     +   struct index_state *istate = context;\n>     +   return read_one_entry_opt(istate, oid, base, pathname,\n>     +-                            mode, stage,\n>     ++                            mode,\n>     +                             ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n>     + }\n>     +\n>     +@@ builtin/ls-files.c: static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n>     +  * the stage that will conflict with the entry being added.\n>     +  */\n>     + static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n>     +-                          const char *pathname, unsigned mode, int stage,\n>     ++                          const char *pathname, unsigned mode,\n>     +                           void *context)\n>     + {\n>     +   struct index_state *istate = context;\n>     +   return read_one_entry_opt(istate, oid, base, pathname,\n>     +-                            mode, stage,\n>     ++                            mode,\n>     +                             ADD_CACHE_JUST_APPEND);\n>     + }\n>     +\n>      @@ builtin/ls-files.c: void overlay_tree_on_index(struct index_state *istate,\n>     -                          PATHSPEC_PREFER_CWD, prefix, matchbuf);\n>     -   } else\n>     -           memset(&pathspec, 0, sizeof(pathspec));\n>     --  if (read_tree_recursive(the_repository, tree, 1,\n>     -+  if (read_tree_recursive(the_repository, tree,\n>     -                           &pathspec, read_one_entry_quick, istate))\n>     +\n>     +   if (!fn)\n>     +           fn = read_one_entry_quick;\n>     +-  err = read_tree_recursive(the_repository, tree, 1, &pathspec, fn, istate);\n>     ++  err = read_tree_recursive(the_repository, tree, &pathspec, fn, istate);\n>     +   if (err)\n>                 die(\"unable to read tree entries %s\", tree_name);\n>\n>\n>     @@ tree.c: static int read_tree_1(struct repository *r,\n>       }\n>\n>       ## tree.h ##\n>     -@@ tree.h: void free_tree_buffer(struct tree *tree);\n>     - struct tree *parse_tree_indirect(const struct object_id *oid);\n>     +@@ tree.h: struct tree *parse_tree_indirect(const struct object_id *oid);\n>     + int cmp_cache_name_compare(const void *a_, const void *b_);\n>\n>       #define READ_TREE_RECURSIVE 1\n>      -typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, int, void *);\n>     @@ tree.h: void free_tree_buffer(struct tree *tree);\n>      -                  int stage, const struct pathspec *pathspec,\n>      +                  const struct pathspec *pathspec,\n>                         read_tree_fn_t fn, void *context);\n>     -\n>       #endif /* TREE_H */\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n>\n"},{"id":"418561","messageId":"CABPp-BHPR7SZHtuyROBc=c_g2nGyV_RA3xv9vmNXJw3v9KJurQ@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"Re: [PATCH 00/30] tree-walk: mostly \"mode\" to \"enum object_type\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T00:10:40Z","receivedAt":"2021-03-09T00:11:29Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> This large series goes on top of my 6 patch series for\n> read_tree_recursive() as this one further refactors that function. See\n> https://lore.kernel.org/git/20210308022138.28166-1-avarab@gmail.com/\n> for that series.\n>\n> I noticed that since 2014 or so we haven't been doing the fsck checks\n> for bad file modes in trees. This series fixes that. I plan to add\n> tests etc. for that in another follow-up series.\n>\n> I wanted to get this out for review sooner than later, particularly\n> since the fsck testing will probably get me down another refactoring\n> path (fsck testing in general in this area is pretty bad...).\n>\n> As noted in 30/30 it would have been way easier to simply do an\n> isolated fix for that bug by introducing some fsck-specific API for\n> raw tree reading.\n>\n> But I thought the bug was symptomatic of a wider problem in our\n> codebase. Namely that we pass around the tree's mode *a lot*.\n>\n> But almost everything that then deals with the mode doesn't per-se\n> care about the mode bits in the tree, but using them to map that mode\n> to a tree entry for one of of OBJ_{BLOB,TREE,COMMIT}.\n>\n> So this is a large refactoring of all users of the widely used\n> tree-walk.h API to \"enum obj2ect_type\", finally in 29/30 I rename the\n> field to a scary \"raw_mode\".\n>\n> At that point we have just ~30-50 grep hits left for \"raw_mode\" in the\n> codebase (depending on whether we count names in function parameters).\n>\n> Hopefully being in that state alleviates e.g. Elijah's concerns\n> expressed in\n> https://lore.kernel.org/git/CABPp-BEdu1PqV5W=FuL0f08iFhGzvzV8oSUybNj4eF0aAwTnAw@mail.gmail.com/\n> I agree that doing the equivalent of 30/30 on top of master would be\n> way too scary, but once we're at 29/30 I think it's sane.\n>\n> I tested this in combination with his on-list series to add more\n> merge-ort testing:\n> https://lore.kernel.org/git/pull.973.git.git.1614905738.gitgitgadget@gmail.com/\n>\n> I found a regression I'd caused in the merge-ort.c code with those\n> tests, fixed here. See the comment in merge-ort.c in 30/30.\n\nI'll start reading through this series, but two quick early notes:\n  - I was worried about touching the tree-walk code message up\nperformance.  Since collect_rename_info() in merge-ort.c takes the\nmost time (and it's mostly a tree-walk), I was worried this would\nregress performance for me.  A couple runs of my mega-renames big\nrebase testcase suggests performance is not that different, so my\nfears look unfounded.  (Note: I tested in combination with all my\nperformance improvements, because otherwise tree-walking would just be\nin the noise of overall runtime.)\n  - There's a textual conflict with this series and my sets of patch\nseries that finish off the ort implementation[1], but it's just two\nlines that are pretty easy to resolve.\n\n[1] https://github.com/gitgitgadget/git/pulls?q=is%3Aopen+is%3Apr+author%3Anewren+Optimization+batch\n\n\n> Ævar Arnfjörð Bjarmason (30):\n>   diff.c: remove redundant canon_mode() call\n>   notes & match-trees: use name_entry's \"pathlen\" member\n>   cache.h: add a comment to object_type()\n>   tree-walk.h: add object_type member to name_entry\n>   tree-walk.c: migrate to using new \"object_type\" field when possible\n>   cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n>   tree-walk.h users: switch object_type(...) to new .object_type\n>   tree.h: format argument lists of read_tree_recursive() users\n>   tree.h users: format argument lists in archive.c\n>   archive: get rid of 'stage' parameter\n>   tree.h API: make read_tree_fn_t take an \"enum object_type\"\n>   tree-walk.h users: migrate \"p->mode &&\" pattern\n>   tree-walk.h users: refactor chained \"mode\" if/else into switch\n>   tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n>   merge-tree tests: test for the mode comparison in same_entry()\n>   merge-ort: correct reference to test in 62fdec17a11\n>   fsck.c: switch on \"object_type\" in fsck_walk_tree()\n>   tree-walk.h users: use temporary variable(s) for \"mode\"\n>   tree-walk.h API: formatting changes for subsequent commit\n>   tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n>   tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n>   tree-walk.h API: Add get_tree_entry_type()\n>   tree-walk.h API: add a get_tree_entry_path() function\n>   tree-walk.h API: document and format tree_entry_extract()\n>   tree-entry.h API: rename tree_entry_extract() to\n>     tree_entry_extract_mode()\n>   tree-walk.h API: add a tree_entry_extract_all() function\n>   tree-walk.h API: add a tree_entry_extract_type() function\n>   tree-walk.h API users: rename \"struct name_entry\"'s \"mode\" to\n>     \"raw_mode\"\n>   tree.h API users: rename read_tree_fn_t's \"mode\" to \"raw_mode\"\n>   tree-walk.h API: move canon_mode() back out of decode_tree_entry()\n>\n>  archive.c              | 51 +++++++++++++-----------\n>  blame.c                |  9 +++--\n>  builtin/checkout.c     |  7 +++-\n>  builtin/fast-import.c  |  8 ++--\n>  builtin/grep.c         |  6 +--\n>  builtin/log.c          |  7 ++--\n>  builtin/ls-files.c     | 13 +++---\n>  builtin/ls-tree.c      | 18 ++++-----\n>  builtin/merge-tree.c   | 32 +++++++++------\n>  builtin/mktree.c       |  4 +-\n>  builtin/pack-objects.c |  6 +--\n>  builtin/reflog.c       |  3 +-\n>  builtin/rm.c           |  2 +-\n>  builtin/update-index.c |  7 +++-\n>  cache-tree.c           |  2 +-\n>  cache.h                | 11 ++++--\n>  combine-diff.c         |  8 ++--\n>  delta-islands.c        |  2 +-\n>  diff.c                 |  2 +-\n>  fsck.c                 | 23 +++++------\n>  http-push.c            |  6 ++-\n>  line-log.c             |  2 +-\n>  list-objects.c         | 20 +++++++---\n>  match-trees.c          | 52 ++++++++++++------------\n>  merge-ort.c            | 34 ++++++++++------\n>  merge-recursive.c      | 33 ++++++++--------\n>  notes.c                | 15 +++----\n>  object-name.c          |  7 ++--\n>  pack-bitmap-write.c    |  8 ++--\n>  read-cache.c           | 16 ++++----\n>  revision.c             | 12 ++++--\n>  t/t4300-merge-tree.sh  | 44 +++++++++++++++++++++\n>  tree-diff.c            | 44 ++++++++++++---------\n>  tree-walk.c            | 89 +++++++++++++++++++++++++++++++-----------\n>  tree-walk.h            | 67 ++++++++++++++++++++++++++-----\n>  tree.c                 | 19 +++++----\n>  tree.h                 |  5 ++-\n>  unpack-trees.c         | 30 ++++++++------\n>  walker.c               | 22 ++++++-----\n>  39 files changed, 482 insertions(+), 264 deletions(-)\n>\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n>\n"},{"id":"418613","messageId":"CABPp-BEDwXuLEMiwRscafe-MRfBSGYdonKAu97WZSWe575GvMA@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-4-avarab@gmail.com","subject":"Re: [PATCH 03/30] cache.h: add a comment to object_type()","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T16:40:06Z","receivedAt":"2021-03-09T16:41:05Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Add a comment to the object_type() function to explain what it\n> returns, and whet the \"mode\" is in the \"else\" case.\n\ns/whet/what/ ?\n\n\n>\n> The object_type() function dates back to 4d1012c3709 (Fix rev-list\n> when showing objects involving submodules, 2007-11-11). It's not\n> immediately obvious to someone looking at its history and how it's\n> come to be used.\n>\n> Despite what Linus noted in 4d1012c3709 (Fix rev-list when showing\n> objects involving submodules, 2007-11-11) about wanting to move away\n> from users of object_type() relying on S_ISLNK(mode) being true here\n> we do currently rely on that. If this is changed to a condition to\n> only return OBJ_BLOB on S_ISREG(mode) then t4008, t4023 and t7415 will\n> have failing tests.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  cache.h | 7 ++++++-\n>  1 file changed, 6 insertions(+), 1 deletion(-)\n>\n> diff --git a/cache.h b/cache.h\n> index d9281496140..e513f0ee5b4 100644\n> --- a/cache.h\n> +++ b/cache.h\n> @@ -451,11 +451,16 @@ enum object_type {\n>         OBJ_MAX\n>  };\n>\n> +/*\n> + * object_type() returns an object of a type that'll appear in a tree,\n> + * so no OBJ_TAG is possible. This is mostly (and dates back to)\n> + * consumers of the tree-walk.h API's \"mode\" field.\n> + */\n>  static inline enum object_type object_type(unsigned int mode)\n>  {\n>         return S_ISDIR(mode) ? OBJ_TREE :\n>                 S_ISGITLINK(mode) ? OBJ_COMMIT :\n> -               OBJ_BLOB;\n> +               OBJ_BLOB; /* S_ISREG(mode) || S_ISLNK(mode) */\n>  }\n>\n>  /* Double-check local_repo_env below if you add to this list. */\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n"},{"id":"418614","messageId":"CABPp-BG2Odp9J+nSRoKXQEaThjoyd62-PVPB4=OBikx0d3jueA@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-6-avarab@gmail.com","subject":"Re: [PATCH 05/30] tree-walk.c: migrate to using new \"object_type\" field when possible","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T16:44:02Z","receivedAt":"2021-03-09T16:45:23Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  tree-walk.c | 24 +++++++++++++-----------\n>  1 file changed, 13 insertions(+), 11 deletions(-)\n>\n> diff --git a/tree-walk.c b/tree-walk.c\n> index b210967b73b..6e9161901d8 100644\n> --- a/tree-walk.c\n> +++ b/tree-walk.c\n> @@ -521,7 +521,7 @@ int traverse_trees(struct index_state *istate,\n>                         if (!entry[i].path)\n>                                 continue;\n>                         mask |= 1ul << i;\n> -                       if (S_ISDIR(entry[i].mode))\n> +                       if (entry[i].object_type == OBJ_TREE)\n>                                 dirmask |= 1ul << i;\n>                         e = &entry[i];\n>                 }\n> @@ -892,8 +892,8 @@ static int match_entry(const struct pathspec_item *item,\n>                  * nothing else (to handle 'submod/' and 'submod'\n>                  * uniformly).\n>                  */\n> -               if (!S_ISDIR(entry->mode) &&\n> -                   (!S_ISGITLINK(entry->mode) || matchlen > pathlen + 1))\n> +               if (entry->object_type != OBJ_TREE &&\n> +                   (entry->object_type != OBJ_COMMIT || matchlen > pathlen + 1))\n>                         return 0;\n>         }\n>\n> @@ -1038,7 +1038,7 @@ static enum interesting do_match(struct index_state *istate,\n>                     ps->max_depth == -1)\n>                         return all_entries_interesting;\n>                 return within_depth(base->buf + base_offset, baselen,\n> -                                   !!S_ISDIR(entry->mode),\n> +                                   entry->object_type == OBJ_TREE,\n>                                     ps->max_depth) ?\n>                         entry_interesting : entry_not_interesting;\n>         }\n> @@ -1071,7 +1071,7 @@ static enum interesting do_match(struct index_state *istate,\n>\n>                         if (within_depth(base_str + matchlen + 1,\n>                                          baselen - matchlen - 1,\n> -                                        !!S_ISDIR(entry->mode),\n> +                                        entry->object_type == OBJ_TREE,\n>                                          ps->max_depth))\n>                                 goto interesting;\n>                         else\n> @@ -1094,7 +1094,8 @@ static enum interesting do_match(struct index_state *istate,\n>                                  * Match all directories. We'll try to\n>                                  * match files later on.\n>                                  */\n> -                               if (ps->recursive && S_ISDIR(entry->mode))\n> +                               if (ps->recursive &&\n> +                                   entry->object_type == OBJ_TREE)\n>                                         return entry_interesting;\n>\n>                                 /*\n> @@ -1105,7 +1106,7 @@ static enum interesting do_match(struct index_state *istate,\n>                                  * be performed in the submodule itself.\n>                                  */\n>                                 if (ps->recurse_submodules &&\n> -                                   S_ISGITLINK(entry->mode) &&\n> +                                   entry->object_type == OBJ_COMMIT &&\n>                                     !ps_strncmp(item, match + baselen,\n>                                                 entry->path,\n>                                                 item->nowildcard_len - baselen))\n> @@ -1154,7 +1155,8 @@ static enum interesting do_match(struct index_state *istate,\n>                  * character.  More accurate matching can then\n>                  * be performed in the submodule itself.\n>                  */\n> -               if (ps->recurse_submodules && S_ISGITLINK(entry->mode) &&\n> +               if (ps->recurse_submodules &&\n> +                   entry->object_type == OBJ_COMMIT &&\n>                     !ps_strncmp(item, match, base->buf + base_offset,\n>                                 item->nowildcard_len)) {\n>                         strbuf_setlen(base, base_offset + baselen);\n> @@ -1170,7 +1172,7 @@ static enum interesting do_match(struct index_state *istate,\n>                  * in future, see\n>                  * https://lore.kernel.org/git/7vmxo5l2g4.fsf@alter.siamese.dyndns.org/\n>                  */\n> -               if (ps->recursive && S_ISDIR(entry->mode))\n> +               if (ps->recursive && entry->object_type == OBJ_TREE)\n>                         return entry_interesting;\n>                 continue;\n>  interesting:\n> @@ -1193,7 +1195,7 @@ static enum interesting do_match(struct index_state *istate,\n>                          * can probably return all_entries_interesting or\n>                          * all_entries_not_interesting here if matched.\n>                          */\n> -                       if (S_ISDIR(entry->mode))\n> +                       if (entry->object_type == OBJ_TREE)\n>                                 return entry_interesting;\n>\n>                         strbuf_add(base, entry->path, pathlen);\n> @@ -1269,7 +1271,7 @@ enum interesting tree_entry_interesting(struct index_state *istate,\n>                 return positive;\n>\n>         /* #15, #19 */\n> -       if (S_ISDIR(entry->mode) &&\n> +       if (entry->object_type == OBJ_TREE &&\n>             positive >= entry_interesting &&\n>             negative == entry_interesting)\n>                 return entry_interesting;\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n\nTo me, this commit shows the advantage of the new field; the new code\nlooks much more readable.\n"},{"id":"418615","messageId":"CABPp-BEZfG_pNNTWsnxEa1xG+kgpQfpXj=KHBkCBAMikrXj-aA@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-7-avarab@gmail.com","subject":"Re: [PATCH 06/30] cache.h: have base_name_compare() take \"is tree?\", not \"mode\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T16:56:59Z","receivedAt":"2021-03-09T16:57:48Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Change the base_name_compare() API and the related df_name_compare()\n> function to take a boolean argument indicating whether the entry is a\n> tree or not, instead of having them call S_ISDIR(mode) on their own.\n>\n> This makes use of the new \"object_type\" field in the \"name_entry\".\n>\n> The API being modified here was originally added way back in\n> 958ba6c96eb (Introduce \"base_name_compare()\" helper function,\n> 2005-05-20).\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/fast-import.c |  8 ++++----\n>  builtin/mktree.c      |  4 ++--\n>  cache.h               |  4 ++--\n>  combine-diff.c        |  8 +++++---\n>  match-trees.c         |  6 ++++--\n>  merge-ort.c           |  4 ++--\n>  merge-recursive.c     |  6 +++---\n>  read-cache.c          | 16 ++++++++--------\n>  tree-diff.c           |  7 +++++--\n>  unpack-trees.c        | 15 ++++++++-------\n>  10 files changed, 43 insertions(+), 35 deletions(-)\n>\n> diff --git a/builtin/fast-import.c b/builtin/fast-import.c\n> index dd4d09ceceb..ce4613c1595 100644\n> --- a/builtin/fast-import.c\n> +++ b/builtin/fast-import.c\n> @@ -1288,8 +1288,8 @@ static int tecmp0 (const void *_a, const void *_b)\n>         struct tree_entry *a = *((struct tree_entry**)_a);\n>         struct tree_entry *b = *((struct tree_entry**)_b);\n>         return base_name_compare(\n> -               a->name->str_dat, a->name->str_len, a->versions[0].mode,\n> -               b->name->str_dat, b->name->str_len, b->versions[0].mode);\n> +               a->name->str_dat, a->name->str_len, 1,\n> +               b->name->str_dat, b->name->str_len, 1);\n>  }\n>\n>  static int tecmp1 (const void *_a, const void *_b)\n> @@ -1297,8 +1297,8 @@ static int tecmp1 (const void *_a, const void *_b)\n>         struct tree_entry *a = *((struct tree_entry**)_a);\n>         struct tree_entry *b = *((struct tree_entry**)_b);\n>         return base_name_compare(\n> -               a->name->str_dat, a->name->str_len, a->versions[1].mode,\n> -               b->name->str_dat, b->name->str_len, b->versions[1].mode);\n> +               a->name->str_dat, a->name->str_len, 1,\n> +               b->name->str_dat, b->name->str_len, 1);\n>  }\n\nYou've treated all files as directories, and in doing so broken the\nsorting order of paths like the following\n    foo\n    foo.txt\nfoo needs to sort after foo.txt if it's a directory, and before\nfoo.txt if it's a file; that's the whole reason base_name_compare()\nwants to know is_tree-ness.\n\nIt looks like we have a whole in our regression test coverage, since\nthey didn't catch this breakage.  (git will accept and work with trees\nin the wrong sort-order, but it can create confusing diffs and\nwhatnot.)\n\n>  static void mktree(struct tree_content *t, int v, struct strbuf *b)\n> diff --git a/builtin/mktree.c b/builtin/mktree.c\n> index 891991b00d6..2c1973229ac 100644\n> --- a/builtin/mktree.c\n> +++ b/builtin/mktree.c\n> @@ -37,8 +37,8 @@ static int ent_compare(const void *a_, const void *b_)\n>  {\n>         struct treeent *a = *(struct treeent **)a_;\n>         struct treeent *b = *(struct treeent **)b_;\n> -       return base_name_compare(a->name, a->len, a->mode,\n> -                                b->name, b->len, b->mode);\n> +       return base_name_compare(a->name, a->len, S_ISDIR(a->mode),\n> +                                b->name, b->len, S_ISDIR(b->mode));\n>  }\n>\n>  static void write_tree(struct object_id *oid)\n> diff --git a/cache.h b/cache.h\n> index e513f0ee5b4..49994dae916 100644\n> --- a/cache.h\n> +++ b/cache.h\n> @@ -1506,8 +1506,8 @@ int repo_interpret_branch_name(struct repository *r,\n>\n>  int validate_headref(const char *ref);\n>\n> -int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n> -int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n> +int base_name_compare(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2);\n> +int df_name_compare(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2);\n\nA very minor nit/question:\n\nDo we want to call this isdir or istree?  I know we use S_ISDIR() but\nthat's because it's a pre-existing macro pre-dating git.  As you\nmentioned in your commit message, we want to know whether the object\nin question is a git \"tree\", so I'm inclined to go with istree.\n\nBut, I'm probably bikeshedding here.\n\n>  int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);\n>  int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);\n>\n> diff --git a/combine-diff.c b/combine-diff.c\n> index 9228aebc16b..64d7aaf1710 100644\n> --- a/combine-diff.c\n> +++ b/combine-diff.c\n> @@ -16,11 +16,13 @@\n>  static int compare_paths(const struct combine_diff_path *one,\n>                           const struct diff_filespec *two)\n>  {\n> -       if (!S_ISDIR(one->mode) && !S_ISDIR(two->mode))\n> +       int isdir_one = S_ISDIR(one->mode);\n> +       int isdir_two = S_ISDIR(two->mode);\n> +       if (!isdir_one && !isdir_two)\n>                 return strcmp(one->path, two->path);\n>\n> -       return base_name_compare(one->path, strlen(one->path), one->mode,\n> -                                two->path, strlen(two->path), two->mode);\n> +       return base_name_compare(one->path, strlen(one->path), isdir_one,\n> +                                two->path, strlen(two->path), isdir_two);\n>  }\n>\n>  static int filename_changed(char status)\n> diff --git a/match-trees.c b/match-trees.c\n> index 1011357ad0c..4d124a0fd1b 100644\n> --- a/match-trees.c\n> +++ b/match-trees.c\n> @@ -67,8 +67,10 @@ static void *fill_tree_desc_strict(struct tree_desc *desc,\n>  static int base_name_entries_compare(const struct name_entry *a,\n>                                      const struct name_entry *b)\n>  {\n> -       return base_name_compare(a->path, tree_entry_len(a), a->mode,\n> -                                b->path, tree_entry_len(b), b->mode);\n> +       int isdira = a->object_type == OBJ_TREE;\n> +       int isdirb = b->object_type == OBJ_TREE;\n> +       return base_name_compare(a->path, tree_entry_len(a), isdira,\n> +                                b->path, tree_entry_len(b), isdirb);\n>  }\n>\n>  /*\n> diff --git a/merge-ort.c b/merge-ort.c\n> index 603d30c5217..4075d13aaab 100644\n> --- a/merge-ort.c\n> +++ b/merge-ort.c\n> @@ -2390,8 +2390,8 @@ static int tree_entry_order(const void *a_, const void *b_)\n>\n>         const struct merged_info *ami = a->util;\n>         const struct merged_info *bmi = b->util;\n> -       return base_name_compare(a->string, strlen(a->string), ami->result.mode,\n> -                                b->string, strlen(b->string), bmi->result.mode);\n> +       return base_name_compare(a->string, strlen(a->string), S_ISDIR(ami->result.mode),\n> +                                b->string, strlen(b->string), S_ISDIR(bmi->result.mode));\n>  }\n>\n>  static void write_tree(struct object_id *result_oid,\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index 1593f374495..0baa6b5ca56 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -554,12 +554,12 @@ static int string_list_df_name_compare(const char *one, const char *two)\n>          *\n>          * To achieve this, we sort with df_name_compare and provide\n>          * the mode S_IFDIR so that D/F conflicts will sort correctly.\n> -        * We use the mode S_IFDIR for everything else for simplicity,\n> +        * We say we have a directory for everything else for simplicity,\n>          * since in other cases any changes in their order due to\n>          * sorting cause no problems for us.\n>          */\n> -       int cmp = df_name_compare(one, onelen, S_IFDIR,\n> -                                 two, twolen, S_IFDIR);\n> +       int cmp = df_name_compare(one, onelen, 1, two, twolen, 1);\n> +\n>         /*\n>          * Now that 'foo' and 'foo/bar' compare equal, we have to make sure\n>          * that 'foo' comes before 'foo/bar'.\n> diff --git a/read-cache.c b/read-cache.c\n> index 1e9a50c6c73..edfce1f0cb8 100644\n> --- a/read-cache.c\n> +++ b/read-cache.c\n> @@ -462,8 +462,8 @@ int ie_modified(struct index_state *istate,\n>         return 0;\n>  }\n>\n> -int base_name_compare(const char *name1, int len1, int mode1,\n> -                     const char *name2, int len2, int mode2)\n> +int base_name_compare(const char *name1, int len1, int isdir1,\n> +                     const char *name2, int len2, int isdir2)\n>  {\n>         unsigned char c1, c2;\n>         int len = len1 < len2 ? len1 : len2;\n> @@ -474,9 +474,9 @@ int base_name_compare(const char *name1, int len1, int mode1,\n>                 return cmp;\n>         c1 = name1[len];\n>         c2 = name2[len];\n> -       if (!c1 && S_ISDIR(mode1))\n> +       if (!c1 && isdir1)\n>                 c1 = '/';\n> -       if (!c2 && S_ISDIR(mode2))\n> +       if (!c2 && isdir2)\n>                 c2 = '/';\n>         return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;\n>  }\n> @@ -491,8 +491,8 @@ int base_name_compare(const char *name1, int len1, int mode1,\n>   * This is used by routines that want to traverse the git namespace\n>   * but then handle conflicting entries together when possible.\n>   */\n> -int df_name_compare(const char *name1, int len1, int mode1,\n> -                   const char *name2, int len2, int mode2)\n> +int df_name_compare(const char *name1, int len1, int isdir1,\n> +                   const char *name2, int len2, int isdir2)\n>  {\n>         int len = len1 < len2 ? len1 : len2, cmp;\n>         unsigned char c1, c2;\n> @@ -504,10 +504,10 @@ int df_name_compare(const char *name1, int len1, int mode1,\n>         if (len1 == len2)\n>                 return 0;\n>         c1 = name1[len];\n> -       if (!c1 && S_ISDIR(mode1))\n> +       if (!c1 && isdir1)\n>                 c1 = '/';\n>         c2 = name2[len];\n> -       if (!c2 && S_ISDIR(mode2))\n> +       if (!c2 && isdir2)\n>                 c2 = '/';\n>         if (c1 == '/' && !c2)\n>                 return 0;\n> diff --git a/tree-diff.c b/tree-diff.c\n> index 7cebbb327e2..f133834597c 100644\n> --- a/tree-diff.c\n> +++ b/tree-diff.c\n> @@ -50,6 +50,7 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)\n>  {\n>         struct name_entry *e1, *e2;\n>         int cmp;\n> +       int e1_is_tree, e2_is_tree;\n>\n>         /* empty descriptors sort after valid tree entries */\n>         if (!t1->size)\n> @@ -58,9 +59,11 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)\n>                 return -1;\n>\n>         e1 = &t1->entry;\n> +       e1_is_tree = e1->object_type == OBJ_TREE;\n\nThis would read slightly clearer with parenthesis around the right hand side.\n\n>         e2 = &t2->entry;\n> -       cmp = base_name_compare(e1->path, tree_entry_len(e1), e1->mode,\n> -                               e2->path, tree_entry_len(e2), e2->mode);\n> +       e2_is_tree = e2->object_type == OBJ_TREE;\n\nSame.\n\n> +       cmp = base_name_compare(e1->path, tree_entry_len(e1), e1_is_tree,\n> +                               e2->path, tree_entry_len(e2), e2_is_tree);\n>         return cmp;\n>  }\n>\n> diff --git a/unpack-trees.c b/unpack-trees.c\n> index f5f668f532d..802f7771d75 100644\n> --- a/unpack-trees.c\n> +++ b/unpack-trees.c\n> @@ -922,7 +922,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n>  static int do_compare_entry_piecewise(const struct cache_entry *ce,\n>                                       const struct traverse_info *info,\n>                                       const char *name, size_t namelen,\n> -                                     unsigned mode)\n> +                                     unsigned is_tree)\n\nisdir elsewhere and is_tree here?  ;-)\n\n>  {\n>         int pathlen, ce_len;\n>         const char *ce_name;\n> @@ -930,7 +930,7 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce,\n>         if (info->prev) {\n>                 int cmp = do_compare_entry_piecewise(ce, info->prev,\n>                                                      info->name, info->namelen,\n> -                                                    info->mode);\n> +                                                    S_ISDIR(info->mode));\n>                 if (cmp)\n>                         return cmp;\n>         }\n> @@ -944,13 +944,13 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce,\n>         ce_len -= pathlen;\n>         ce_name = ce->name + pathlen;\n>\n> -       return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n> +       return df_name_compare(ce_name, ce_len, 0, name, namelen, is_tree);\n>  }\n>\n>  static int do_compare_entry(const struct cache_entry *ce,\n>                             const struct traverse_info *info,\n>                             const char *name, size_t namelen,\n> -                           unsigned mode)\n> +                           unsigned is_tree)\n>  {\n>         int pathlen, ce_len;\n>         const char *ce_name;\n> @@ -962,7 +962,7 @@ static int do_compare_entry(const struct cache_entry *ce,\n>          * it is quicker to use the precomputed version.\n>          */\n>         if (!info->traverse_path)\n> -               return do_compare_entry_piecewise(ce, info, name, namelen, mode);\n> +               return do_compare_entry_piecewise(ce, info, name, namelen, is_tree);\n>\n>         cmp = strncmp(ce->name, info->traverse_path, info->pathlen);\n>         if (cmp)\n> @@ -977,12 +977,13 @@ static int do_compare_entry(const struct cache_entry *ce,\n>         ce_len -= pathlen;\n>         ce_name = ce->name + pathlen;\n>\n> -       return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n> +       return df_name_compare(ce_name, ce_len, 0, name, namelen, is_tree);\n>  }\n>\n>  static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)\n>  {\n> -       int cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);\n> +       int is_tree = n->object_type == OBJ_TREE;\n\nAnother place where parentheses would speed my reading comprehension\njust slightly.\n\n\n> +       int cmp = do_compare_entry(ce, info, n->path, n->pathlen, is_tree);\n>         if (cmp)\n>                 return cmp;\n>\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n"},{"id":"418616","messageId":"CABPp-BF7X5pqNYJXyQYs6BSC_QN4T_mTNmJ_v9=D9rOXnwyy3A@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-10-avarab@gmail.com","subject":"Re: [PATCH 09/30] tree.h users: format argument lists in archive.c","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T17:04:08Z","receivedAt":"2021-03-09T17:04:59Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Re-indent and re-flow the argument lists archive.c has downstream of\n> its read_tree_recursive() call to make subsequent commits easier to\n> read, as I only expect to be modifying the \"stage\" and \"mode\" lines.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  archive.c | 25 +++++++++++++++++--------\n>  1 file changed, 17 insertions(+), 8 deletions(-)\n>\n> diff --git a/archive.c b/archive.c\n> index db69a8acadb..e245c0d5a54 100644\n> --- a/archive.c\n> +++ b/archive.c\n> @@ -140,7 +140,8 @@ static int check_attr_export_subst(const struct attr_check *check)\n>  static int write_archive_entry(const struct object_id *oid, const char *base,\n>                                int baselen, const char *filename,\n>                                unsigned mode,\n> -                              int stage, void *context)\n> +                              int stage,\n> +                              void *context)\n\nYou just barely changed this function signature in the previous patch\nto split long lines, but left stage & context on the same line.  It\nseems like this change should be squashed into the previous patch.\n\n>  {\n>         static struct strbuf path = STRBUF_INIT;\n>         struct archiver_context *c = context;\n> @@ -197,8 +198,10 @@ static int write_archive_entry(const struct object_id *oid, const char *base,\n>  }\n>\n>  static void queue_directory(const unsigned char *sha1,\n> -               struct strbuf *base, const char *filename,\n> -               unsigned mode, int stage, struct archiver_context *c)\n> +                           struct strbuf *base, const char *filename,\n> +                           unsigned mode,\n> +                           int stage,\n> +                           struct archiver_context *c)\n>  {\n>         struct directory *d;\n>         size_t len = st_add4(base->len, 1, strlen(filename), 1);\n> @@ -224,8 +227,10 @@ static int write_directory(struct archiver_context *c)\n>         ret =\n>                 write_directory(c) ||\n>                 write_archive_entry(&d->oid, d->path, d->baselen,\n> -                                   d->path + d->baselen, d->mode,\n> -                                   d->stage, c) != READ_TREE_RECURSIVE;\n> +                                   d->path + d->baselen,\n> +                                   d->mode,\n> +                                   d->stage,\n> +                                   c) != READ_TREE_RECURSIVE;\n>         free(d);\n>         return ret ? -1 : 0;\n>  }\n> @@ -259,14 +264,18 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n>                 if (check_attr_export_ignore(check))\n>                         return 0;\n>                 queue_directory(oid->hash, base, filename,\n> -                               mode, stage, c);\n> +                               mode,\n> +                               stage,\n> +                               c);\n>                 return READ_TREE_RECURSIVE;\n>         }\n>\n>         if (write_directory(c))\n>                 return -1;\n> -       return write_archive_entry(oid, base->buf, base->len, filename, mode,\n> -                                  stage, context);\n> +       return write_archive_entry(oid, base->buf, base->len, filename,\n> +                                  mode,\n> +                                  stage,\n> +                                  context);\n>  }\n>\n>  struct extra_file_info {\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n>\n"},{"id":"418617","messageId":"CABPp-BHKMxvWJno=4sUBDS3cC=gEC+utbL0=0gxMEibcb_k-4w@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-13-avarab@gmail.com","subject":"Re: [PATCH 12/30] tree-walk.h users: migrate \"p->mode &&\" pattern","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T17:09:47Z","receivedAt":"2021-03-09T17:10:52Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Change code that dpends on \"p->mode\" either being a valid mode or zero\n\ns/dpends/depends/\n\n> to use a p->object_type comparison to \"OBJ_NONE\".\n>\n> The object_type() function in cache.h will not return OBJ_NONE, but in\n> this these API users are implicitly relying on the memzero() that\n> happens in setup_traverse_info().\n\nThis is a bit hard to parse.  Perhaps just remove \"in this\"?\n\n> Since OBJ_NONE is \"0\" we can also rely on that being zero'd out here,\n> along with the rest of the structure. I think this is slightly less\n> clever than \"mode not set\", and helps to get rid of more uses of\n> \"mode\".\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/merge-tree.c | 9 +++++----\n>  merge-ort.c          | 2 +-\n>  unpack-trees.c       | 4 ++--\n>  3 files changed, 8 insertions(+), 7 deletions(-)\n>\n> diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\n> index de8520778d2..2de34c2d485 100644\n> --- a/builtin/merge-tree.c\n> +++ b/builtin/merge-tree.c\n> @@ -214,7 +214,7 @@ static void unresolved_directory(const struct traverse_info *info,\n>         void *buf0, *buf1, *buf2;\n>\n>         for (p = n; p < n + 3; p++) {\n> -               if (p->mode && S_ISDIR(p->mode))\n> +               if (p->object_type == OBJ_TREE)\n>                         break;\n>         }\n>         if (n + 3 <= p)\n> @@ -222,7 +222,7 @@ static void unresolved_directory(const struct traverse_info *info,\n>\n>         newbase = traverse_path(info, p);\n>\n> -#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL)\n> +#define ENTRY_OID(e) (((e)->object_type == OBJ_TREE) ? &(e)->oid : NULL)\n>         buf0 = fill_tree_descriptor(r, t + 0, ENTRY_OID(n + 0));\n>         buf1 = fill_tree_descriptor(r, t + 1, ENTRY_OID(n + 1));\n>         buf2 = fill_tree_descriptor(r, t + 2, ENTRY_OID(n + 2));\n> @@ -242,7 +242,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n>         const char *path;\n>         struct merge_list *link;\n>\n> -       if (!n->mode)\n> +       if (n->object_type == OBJ_NONE)\n>                 return entry;\n>         if (entry)\n>                 path = entry->path;\n> @@ -265,7 +265,8 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3])\n>                  * Treat missing entries as directories so that we return\n>                  * after unresolved_directory has handled this.\n>                  */\n> -               if (!n[i].mode || S_ISDIR(n[i].mode))\n> +               if (n[i].object_type == OBJ_NONE ||\n> +                   n[i].object_type == OBJ_TREE)\n>                         dirmask |= (1 << i);\n>         }\n>\n> diff --git a/merge-ort.c b/merge-ort.c\n> index 4075d13aaab..4375027914c 100644\n> --- a/merge-ort.c\n> +++ b/merge-ort.c\n> @@ -668,7 +668,7 @@ static int collect_merge_info_callback(int n,\n>          * setup_path_info() for tracking.\n>          */\n>         p = names;\n> -       while (!p->mode)\n> +       while (p->object_type == OBJ_NONE)\n>                 p++;\n>         len = traverse_path_len(info, p->pathlen);\n>\n> diff --git a/unpack-trees.c b/unpack-trees.c\n> index 802f7771d75..92105135522 100644\n> --- a/unpack-trees.c\n> +++ b/unpack-trees.c\n> @@ -859,7 +859,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n>         }\n>\n>         p = names;\n> -       while (!p->mode)\n> +       while (p->object_type == OBJ_NONE)\n>                 p++;\n>\n>         newinfo = *info;\n> @@ -1239,7 +1239,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n>         const struct name_entry *p = names;\n>\n>         /* Find first entry with a real name (we could use \"mask\" too) */\n> -       while (!p->mode)\n> +       while (p->object_type == OBJ_NONE)\n>                 p++;\n>\n>         if (o->debug_unpack)\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n"},{"id":"418618","messageId":"CABPp-BEC+Hi+EXF_QK0-o9oqmMnVu3_mX-GN0m24vY_NgLSnVw@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-14-avarab@gmail.com","subject":"Re: [PATCH 13/30] tree-walk.h users: refactor chained \"mode\" if/else into switch","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T17:11:39Z","receivedAt":"2021-03-09T17:12:29Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Refactor a couple of \"switch\" statements that previously relied on\n> \"entry.mode\" to switch on \"entry.object_type\" instead.\n>\n> This is more obvious, and allows us to explicitly handle all the OBJ_*\n> cases, not just have a wildcard \"else\". That doesn't matter for the\n> behavior of this code, but for its readability and maintainability.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  list-objects.c | 20 ++++++++++++++------\n>  walker.c       | 22 +++++++++++++---------\n>  2 files changed, 27 insertions(+), 15 deletions(-)\n>\n> diff --git a/list-objects.c b/list-objects.c\n> index e19589baa04..37434ba89d3 100644\n> --- a/list-objects.c\n> +++ b/list-objects.c\n> @@ -111,6 +111,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n>         init_tree_desc(&desc, tree->buffer, tree->size);\n>\n>         while (tree_entry(&desc, &entry)) {\n> +               struct tree *t;\n> +               struct blob *b;\n> +\n>                 if (match != all_entries_interesting) {\n>                         match = tree_entry_interesting(ctx->revs->repo->index,\n>                                                        &entry, base, 0,\n> @@ -121,8 +124,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n>                                 continue;\n>                 }\n>\n> -               if (S_ISDIR(entry.mode)) {\n> -                       struct tree *t = lookup_tree(ctx->revs->repo, &entry.oid);\n> +               switch (entry.object_type) {\n> +               case OBJ_TREE:\n> +                       t = lookup_tree(ctx->revs->repo, &entry.oid);\n>                         if (!t) {\n>                                 die(_(\"entry '%s' in tree %s has tree mode, \"\n>                                       \"but is not a tree\"),\n> @@ -130,12 +134,13 @@ static void process_tree_contents(struct traversal_context *ctx,\n>                         }\n>                         t->object.flags |= NOT_USER_GIVEN;\n>                         process_tree(ctx, t, base, entry.path);\n> -               }\n> -               else if (S_ISGITLINK(entry.mode))\n> +                       break;\n> +               case OBJ_COMMIT:\n>                         process_gitlink(ctx, entry.oid.hash,\n>                                         base, entry.path);\n> -               else {\n> -                       struct blob *b = lookup_blob(ctx->revs->repo, &entry.oid);\n> +                       break;\n> +               case OBJ_BLOB:\n> +                       b = lookup_blob(ctx->revs->repo, &entry.oid);\n>                         if (!b) {\n>                                 die(_(\"entry '%s' in tree %s has blob mode, \"\n>                                       \"but is not a blob\"),\n> @@ -143,6 +148,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n>                         }\n>                         b->object.flags |= NOT_USER_GIVEN;\n>                         process_blob(ctx, b, base, entry.path);\n> +                       break;\n> +               default:\n> +                       BUG(\"unreachable\");\n>                 }\n>         }\n>  }\n> diff --git a/walker.c b/walker.c\n> index 4984bf8b3d6..7ba757244e6 100644\n> --- a/walker.c\n> +++ b/walker.c\n> @@ -45,21 +45,25 @@ static int process_tree(struct walker *walker, struct tree *tree)\n>         init_tree_desc(&desc, tree->buffer, tree->size);\n>         while (tree_entry(&desc, &entry)) {\n>                 struct object *obj = NULL;\n> +               struct tree *tree;\n> +               struct blob *blob;\n>\n> -               /* submodule commits are not stored in the superproject */\n> -               if (S_ISGITLINK(entry.mode))\n> +               switch (entry.object_type) {\n> +               case OBJ_COMMIT:\n> +                       /* submodule commits are not stored in the superproject */\n>                         continue;\n> -               if (S_ISDIR(entry.mode)) {\n> -                       struct tree *tree = lookup_tree(the_repository,\n> -                                                       &entry.oid);\n> +               case OBJ_TREE:\n> +                       tree = lookup_tree(the_repository, &entry.oid);\n>                         if (tree)\n>                                 obj = &tree->object;\n> -               }\n> -               else {\n> -                       struct blob *blob = lookup_blob(the_repository,\n> -                                                       &entry.oid);\n> +                       break;\n> +               case OBJ_BLOB:\n> +                       blob = lookup_blob(the_repository, &entry.oid);\n>                         if (blob)\n>                                 obj = &blob->object;\n> +                       break;\n> +               default:\n> +                       BUG(\"unreachable\");\n>                 }\n>                 if (!obj || process(walker, obj))\n>                         return -1;\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n\nThis does make it nicer.  :-)\n"},{"id":"418619","messageId":"CABPp-BHfH4v4xEc4MPv24tRoW1pPrXSCFfqCpSSyGOGT_-1kCA@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-16-avarab@gmail.com","subject":"Re: [PATCH 15/30] merge-tree tests: test for the mode comparison in same_entry()","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T17:19:19Z","receivedAt":"2021-03-09T17:20:32Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Add a test to stress the \"a->mode == b->mode\" comparison in\n> merge-tree.c's same_entry().\n\nI can't remember now; did you remove the preliminary canon_mode()?  If\nso, does this check actually need to be generalized?\n\nFor example, if someone has a git repository where a mode on a file in\none part of history is 100666, and on the other is 100644, then the\nequality comparison no longer is satisfied because the modes haven't\nbeen canonicalized.  Yet it's clear how the merge-tree should resolve\nit -- both are regular, non-executable files, so the merged mode\nshould be 100644.\n\n> That code was initially added by Linus in 33deb63a36f (Add\n> \"merge-tree\" helper program. Maybe it's retarded, maybe it's helpful.,\n> 2005-04-14), and then again in its current form in\n> 492e0759bfe (Handling large files with GIT, 2006-02-14).\n>\n> However, nothing was testing that we handled this case\n> correctly. Simply removing the mode comparison left all tests passing,\n> but as seen here it's important that we don't think a path with the\n> same content but different modes is the same_entry().\n>\n> The rest of this series will touch code that's relevant to this, but\n> won't change its behavior. This test is just something I came up with\n> in testing whether the mode test in same_entry() was needed at all.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t4300-merge-tree.sh | 44 +++++++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 44 insertions(+)\n>\n> diff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh\n> index e59601e5fe9..f783d784d02 100755\n> --- a/t/t4300-merge-tree.sh\n> +++ b/t/t4300-merge-tree.sh\n> @@ -40,6 +40,25 @@ test_expect_success 'file add A, B (same)' '\n>         test_must_be_empty actual\n>  '\n>\n> +test_expect_success 'file add A, B (different mode)' '\n> +       git reset --hard initial &&\n> +       test_commit \"add-a-b-same-diff-mode-A\" \"ONE\" \"AAA\" &&\n> +       git reset --hard initial &&\n> +       echo AAA >ONE &&\n> +       test_chmod +x ONE &&\n> +       test_tick &&\n> +       git commit -m\"add-a-b-same-diff-mode-B\" &&\n> +       git tag \"add-a-b-same-diff-mode-B\" HEAD &&\n> +       git merge-tree initial add-a-b-same-diff-mode-A add-a-b-same-diff-mode-B >actual &&\n> +       cat >expected <<EXPECTED &&\n> +added in both\n> +  our    100644 $(git rev-parse add-a-b-same-diff-mode-A:ONE) ONE\n> +  their  100755 $(git rev-parse add-a-b-same-diff-mode-B:ONE) ONE\n> +EXPECTED\n> +\n> +       test_cmp expected actual\n> +'\n> +\n>  test_expect_success 'file add A, B (different)' '\n>         git reset --hard initial &&\n>         test_commit \"add-a-b-diff-A\" \"ONE\" \"AAA\" &&\n> @@ -61,6 +80,31 @@ EXPECTED\n>         test_cmp expected actual\n>  '\n>\n> +test_expect_success 'file add A, B (different and different mode)' '\n> +       git reset --hard initial &&\n> +       test_commit \"add-a-b-diff-diff-mode-A\" \"ONE\" \"AAA\" &&\n> +       git reset --hard initial &&\n> +       echo BBB >ONE &&\n> +       test_chmod +x ONE &&\n> +       test_tick &&\n> +       git commit -m\"add-a-b-diff-diff-mode-B\" &&\n> +       git tag \"add-a-b-diff-diff-mode-B\" &&\n> +       git merge-tree initial add-a-b-diff-diff-mode-A add-a-b-diff-diff-mode-B >actual &&\n> +       cat >expected <<EXPECTED &&\n> +added in both\n> +  our    100644 $(git rev-parse add-a-b-diff-diff-mode-A:ONE) ONE\n> +  their  100755 $(git rev-parse add-a-b-diff-diff-mode-B:ONE) ONE\n> +@@ -1 +1,5 @@\n> ++<<<<<<< .our\n> + AAA\n> ++=======\n> ++BBB\n> ++>>>>>>> .their\n> +EXPECTED\n> +\n> +       test_cmp expected actual\n> +'\n> +\n>  test_expect_success 'file change A, !B' '\n>         git reset --hard initial &&\n>         test_commit \"change-a-not-b\" \"initial-file\" \"BBB\" &&\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n"},{"id":"418620","messageId":"CABPp-BFvjHZWebkAHOYcLDZK77tKULXFhJc25rXB52C4GMRLSw@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-17-avarab@gmail.com","subject":"Re: [PATCH 16/30] merge-ort: correct reference to test in 62fdec17a11","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T17:22:18Z","receivedAt":"2021-03-09T17:23:14Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Fix a comment added in 62fdec17a11 (merge-ort: flesh out\n> implementation of handle_content_merge(), 2021-01-01).\n>\n> The test being referred to here was moved from t6036 in\n> 919df319555 (Collect merge-related tests to t64xx, 2020-08-10).\n>\n> It has also had the plural of \"mode\" in the name ever since being\n> introduced in 5d1daf30cce (t6036: add a failed conflict detection\n> case: regular files, different modes, 2018-06-30).\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  merge-ort.c | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n>\n> diff --git a/merge-ort.c b/merge-ort.c\n> index 4375027914c..e54be179bd5 100644\n> --- a/merge-ort.c\n> +++ b/merge-ort.c\n> @@ -1079,7 +1079,7 @@ static int handle_content_merge(struct merge_options *opt,\n>                 /*\n>                  * FIXME: If opt->priv->call_depth && !clean, then we really\n>                  * should not make result->mode match either a->mode or\n> -                * b->mode; that causes t6036 \"check conflicting mode for\n> +                * b->mode; that causes t6416 \"check conflicting modes for\n>                  * regular file\" to fail.  It would be best to use some other\n>                  * mode, but we'll confuse all kinds of stuff if we use one\n>                  * where S_ISREG(result->mode) isn't true, and if we use\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n\nOops.  Thanks for cleaning this up.\n"},{"id":"418621","messageId":"CABPp-BE+wdDK3OSWooUKLBVZA=bfRm3P6XWWTEFSSXj=yk0ZLg@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-22-avarab@gmail.com","subject":"Re: [PATCH 21/30] tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T17:47:45Z","receivedAt":"2021-03-09T17:48:56Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n\nSince there's only one API user, shouldn't the subject be\n\"match-trees: use tmp...\" instead of \"tree-walk.h API users: use\ntmp...\" ?\n\n>\n> Refactor code added in 85e51b783c3 (Make \"subtree\" part more\n> orthogonal to the rest of merge-recursive., 2008-06-30) to make it\n> obvious that we don't care about the \"mode\" here outside of the if\n> statement it appears in.\n>\n> That's opposed to the sub1 & sub2 variables, where we use the two\n> object ids later in this function.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  match-trees.c | 10 +++++-----\n>  1 file changed, 5 insertions(+), 5 deletions(-)\n>\n> diff --git a/match-trees.c b/match-trees.c\n> index ba4aabf39d1..4f02768c01e 100644\n> --- a/match-trees.c\n> +++ b/match-trees.c\n> @@ -317,17 +317,17 @@ void shift_tree_by(struct repository *r,\n>                    const char *shift_prefix)\n>  {\n>         struct object_id sub1, sub2;\n> -       unsigned short mode1, mode2;\n> +       unsigned short tmp;\n>         unsigned candidate = 0;\n>\n>         /* Can hash2 be a tree at shift_prefix in tree hash1? */\n> -       if (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &mode1) &&\n> -           S_ISDIR(mode1))\n> +       if (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n> +           S_ISDIR(tmp))\n>                 candidate |= 1;\n>\n>         /* Can hash1 be a tree at shift_prefix in tree hash2? */\n> -       if (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &mode2) &&\n> -           S_ISDIR(mode2))\n> +       if (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n> +           S_ISDIR(tmp))\n>                 candidate |= 2;\n>\n>         if (candidate == 3) {\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n"},{"id":"418623","messageId":"CABPp-BExOo7b3OEMrCmCjAzrxf2iaSP-s75Ay6ZheTjmA5bWxQ@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-23-avarab@gmail.com","subject":"Re: [PATCH 22/30] tree-walk.h API: Add get_tree_entry_type()","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T17:56:00Z","receivedAt":"2021-03-09T17:56:59Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Add a get_tree_entry_type() helper function to compliment the existing\n> get_tree_entry(). Move those users of get_tree_entry_type() who didn't\n> care about the mode specifically, but just want to know whether the\n> tree entry is one of OBJ_{BLOB,COMMIT,TREE} over to it.\n\nYou added not one but two new functions (get_tree_entry_all() is the\nother); should probably call out both in the commit message.\n\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  archive.c     |  8 ++++----\n>  blame.c       |  8 ++++----\n>  match-trees.c | 10 +++++-----\n>  tree-walk.c   | 47 ++++++++++++++++++++++++++++++++++++-----------\n>  tree-walk.h   | 15 +++++++++++++--\n>  5 files changed, 62 insertions(+), 26 deletions(-)\n>\n> diff --git a/archive.c b/archive.c\n> index 95fa759e1fb..bc8f1c7546f 100644\n> --- a/archive.c\n> +++ b/archive.c\n> @@ -479,14 +479,14 @@ static void parse_treeish_arg(const char **argv,\n>\n>         if (prefix) {\n>                 struct object_id tree_oid;\n> -               unsigned short mode;\n> +               enum object_type object_type;\n>                 int err;\n>\n> -               err = get_tree_entry_mode(ar_args->repo,\n> +               err = get_tree_entry_type(ar_args->repo,\n>                                           &tree->object.oid,\n>                                           prefix, &tree_oid,\n> -                                         &mode);\n> -               if (err || !S_ISDIR(mode))\n> +                                         &object_type);\n> +               if (err || object_type != OBJ_TREE)\n>                         die(_(\"current working directory is untracked\"));\n>\n>                 tree = parse_tree_indirect(&tree_oid);\n> diff --git a/blame.c b/blame.c\n> index 9e0543e13d4..4944582dc3c 100644\n> --- a/blame.c\n> +++ b/blame.c\n> @@ -101,11 +101,11 @@ static void verify_working_tree_path(struct repository *r,\n>         for (parents = work_tree->parents; parents; parents = parents->next) {\n>                 const struct object_id *commit_oid = &parents->item->object.oid;\n>                 struct object_id blob_oid;\n> -               unsigned short mode;\n> -               int ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n> -                                             &mode);\n> +               enum object_type object_type;\n> +               int ret = get_tree_entry_type(r, commit_oid, path, &blob_oid,\n> +                                             &object_type);\n>\n> -               if (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n> +               if (!ret && object_type == OBJ_BLOB)\n\nTesting my understanding -- does this mean that even before your patch\nthat the oid_object_info() call wasn't needed?  In particular, we\ncould have replaced it with something like (!S_ISGITLNK(mode) &&\n(S_ISREG(mode) || S_ISLNK(mode)))?\n\n>                         return;\n>         }\n>\n> diff --git a/match-trees.c b/match-trees.c\n> index 4f02768c01e..ce3f811ec04 100644\n> --- a/match-trees.c\n> +++ b/match-trees.c\n> @@ -317,17 +317,17 @@ void shift_tree_by(struct repository *r,\n>                    const char *shift_prefix)\n>  {\n>         struct object_id sub1, sub2;\n> -       unsigned short tmp;\n> +       enum object_type tmp;\n>         unsigned candidate = 0;\n>\n>         /* Can hash2 be a tree at shift_prefix in tree hash1? */\n> -       if (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n> -           S_ISDIR(tmp))\n> +       if (!get_tree_entry_type(r, hash1, shift_prefix, &sub1, &tmp) &&\n> +           tmp == OBJ_TREE)\n>                 candidate |= 1;\n>\n>         /* Can hash1 be a tree at shift_prefix in tree hash2? */\n> -       if (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n> -           S_ISDIR(tmp))\n> +       if (!get_tree_entry_type(r, hash2, shift_prefix, &sub2, &tmp) &&\n> +           tmp == OBJ_TREE)\n>                 candidate |= 2;\n>\n>         if (candidate == 3) {\n> diff --git a/tree-walk.c b/tree-walk.c\n> index 7819ff3e0ec..0ad3d80593e 100644\n> --- a/tree-walk.c\n> +++ b/tree-walk.c\n> @@ -561,7 +561,8 @@ struct dir_state {\n>\n>  static int find_tree_entry(struct repository *r, struct tree_desc *t,\n>                            const char *name, struct object_id *result,\n> -                          unsigned short *mode)\n> +                          unsigned short *mode,\n> +                          enum object_type *object_type)\n>  {\n>         int namelen = strlen(name);\n>         while (t->size) {\n> @@ -585,23 +586,24 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n>                 }\n>                 if (name[entrylen] != '/')\n>                         continue;\n> -               if (!S_ISDIR(*mode))\n> +               if (*object_type != OBJ_TREE)\n>                         break;\n>                 if (++entrylen == namelen) {\n>                         oidcpy(result, &oid);\n>                         return 0;\n>                 }\n> -               return get_tree_entry_mode(r, &oid, name + entrylen, result,\n> -                                          mode);\n> +               return get_tree_entry_all(r, &oid, name + entrylen, result,\n> +                                         mode, object_type);\n>         }\n>         return -1;\n>  }\n>\n> -int get_tree_entry_mode(struct repository *r,\n> -                       const struct object_id *tree_oid,\n> -                       const char *name,\n> -                       struct object_id *oid,\n> -                       unsigned short *mode)\n> +int get_tree_entry_all(struct repository *r,\n> +                      const struct object_id *tree_oid,\n> +                      const char *name,\n> +                      struct object_id *oid,\n> +                      unsigned short *mode,\n> +                      enum object_type *object_type)\n>  {\n>         int retval;\n>         void *tree;\n> @@ -624,12 +626,34 @@ int get_tree_entry_mode(struct repository *r,\n>                 struct tree_desc t;\n>                 init_tree_desc(&t, tree, size);\n>                 retval = find_tree_entry(r, &t, name, oid,\n> -                                        mode);\n> +                                        mode, object_type);\n>         }\n>         free(tree);\n>         return retval;\n>  }\n>\n> +int get_tree_entry_mode(struct repository *r,\n> +                       const struct object_id *tree_oid,\n> +                       const char *name,\n> +                       struct object_id *oid,\n> +                       unsigned short *mode)\n> +{\n> +       enum object_type object_type;\n> +       return get_tree_entry_all(r, tree_oid, name, oid,\n> +                                 mode, &object_type);\n> +}\n> +\n> +int get_tree_entry_type(struct repository *r,\n> +                       const struct object_id *tree_oid,\n> +                       const char *name,\n> +                       struct object_id *oid,\n> +                       enum object_type *object_type)\n> +{\n> +       unsigned short mode;\n> +       return get_tree_entry_all(r, tree_oid, name, oid,\n> +                                 &mode, object_type);\n> +}\n> +\n>  /*\n>   * This is Linux's built-in max for the number of symlinks to follow.\n>   * That limit, of course, does not affect git, but it's a reasonable\n> @@ -674,6 +698,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n>                 int find_result;\n>                 char *first_slash;\n>                 char *remainder = NULL;\n> +               enum object_type object_type;\n>\n>                 if (!t.buffer) {\n>                         void *tree;\n> @@ -751,7 +776,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n>                 /* Look up the first (or only) path component in the tree. */\n>                 find_result = find_tree_entry(r, &t, namebuf.buf,\n>                                               &current_tree_oid,\n> -                                             mode);\n> +                                             mode, &object_type);\n>                 if (find_result) {\n>                         goto done;\n>                 }\n> diff --git a/tree-walk.h b/tree-walk.h\n> index eb9b9de6ccc..5db38fcb575 100644\n> --- a/tree-walk.h\n> +++ b/tree-walk.h\n> @@ -171,12 +171,23 @@ struct traverse_info {\n>   * Find an entry in a tree given a pathname and the sha1 of a tree to\n>   * search. Returns 0 if the entry is found and -1 otherwise.\n>   *\n> - * The third and fourth parameters are set to the entry's sha1 and\n> - * mode respectively.\n> + * There are variants of this function depending on what fields in the\n> + * \"struct name_entry\" you'd like. You always need to pointer to an\n\ns/to pointer to an/a pointer to an/ ?\n\n> + * appropriate variable to fill in (NULL won't do!):\n> + *\n> + * get_tree_entry_mode(): unsigned int mode\n> + * get_tree_entry_type(): enum object_type\n> + * get_tree_entry_all(): unsigned int mode, enum object_type\n>   */\n>  int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n>                         struct object_id *,\n>                         unsigned short *);\n> +int get_tree_entry_type(struct repository *, const struct object_id *, const char *,\n> +                       struct object_id *,\n> +                       enum object_type *);\n> +int get_tree_entry_all(struct repository *, const struct object_id *, const char *,\n> +                      struct object_id *,\n> +                      unsigned short *, enum object_type *);\n>\n>  /**\n>   * Generate the full pathname of a tree entry based from the root of the\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n>\n"},{"id":"418626","messageId":"CABPp-BF6pNbnVoYi3+iO=RRPskg1w6V=iJbKFiDYJxWM7ZFAwA@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-24-avarab@gmail.com","subject":"Re: [PATCH 23/30] tree-walk.h API: add a get_tree_entry_path() function","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T18:17:08Z","receivedAt":"2021-03-09T18:18:03Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Add a get_tree_entry_path() variant in addition to\n> get_tree_entry_path_{mode,type,all}(). This is for those callers that\n> need neither the mode nor \"enum object_type\" parameters filled for\n> them.\n>\n> There's callers here which doesn't need the \"struct object_id\" filled\n> either, and provides a throwaway variable for us.\n\nI think I understood this sentence, but perhaps we could reword it?\nSomething like:\n\nThere are callers here which don't need the \"struct object_id\" filled;\nforcing callers to pass one just requires they create a throwaway\nvariable.\n\n>\n> See the following commits for the introduction of such code that's\n> being modified here:\n>\n>  - shift_tree(): 68faf68938e (A new merge stragety 'subtree'.,\n>     2007-02-15) for the shift_tree()\n\nWhat a lovely commit.  I especially appreciate this gem:\n\n\"Heuristics galore!  That's the git way ;-).\"\n\n>\n>  - tree_has_path(): 96e7ffbdc31 (merge-recursive: check for directory\n>    level conflicts, 2018-04-19)\n>\n>  - init_notes(): fd53c9eb445 (Speed up git notes lookup, 2009-10-09)\n>\n>  - diagnose_invalid_oid_path(): 009fee4774d (Detailed diagnosis when\n>    parsing an object name fails., 2009-12-07)\n>\n> Those could potentially be refactored too, but I've got to stop at\n> some point, and right now I'm focusing downstream code that depends on\n> \"mode\" (or \"enum object_type\").\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  match-trees.c     |  4 +---\n>  merge-recursive.c |  6 ++----\n>  notes.c           |  3 +--\n>  object-name.c     |  3 +--\n>  tree-walk.c       | 11 +++++++++++\n>  tree-walk.h       |  3 +++\n>  6 files changed, 19 insertions(+), 11 deletions(-)\n>\n> diff --git a/match-trees.c b/match-trees.c\n> index ce3f811ec04..60a17b92d70 100644\n> --- a/match-trees.c\n> +++ b/match-trees.c\n> @@ -288,12 +288,10 @@ void shift_tree(struct repository *r,\n>\n>         if (add_score < del_score) {\n>                 /* We need to pick a subtree of two */\n> -               unsigned short mode;\n> -\n>                 if (!*del_prefix)\n>                         return;\n>\n> -               if (get_tree_entry_mode(r, hash2, del_prefix, shifted, &mode))\n> +               if (get_tree_entry_path(r, hash2, del_prefix, shifted))\n>                         die(\"cannot find path %s in tree %s\",\n>                             del_prefix, oid_to_hex(hash2));\n>                 return;\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index 0e891360e7e..b26d9d418f9 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -1884,11 +1884,9 @@ static int tree_has_path(struct repository *r, struct tree *tree,\n>                          const char *path)\n>  {\n>         struct object_id hashy;\n> -       unsigned short mode_o;\n> -\n> -       return !get_tree_entry_mode(r,\n> +       return !get_tree_entry_path(r,\n>                                     &tree->object.oid, path,\n> -                                   &hashy, &mode_o);\n> +                                   &hashy);\n\nPoor Hashimoto has lost half his name and is now stuck with the\nnickname of Hashy...  ;-)\n\n(The mode was just a temporary and the name \"mode_o\" had no real\nmeaning; it was there just because I mis-remembered Quasimodo's name\nat the time as Hashimodo and found \"hashy mode_o\" slightly amusing.)\n\n>  }\n>\n>  /*\n> diff --git a/notes.c b/notes.c\n> index ef138606146..aa46cb2b09e 100644\n> --- a/notes.c\n> +++ b/notes.c\n> @@ -994,7 +994,6 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n>                 combine_notes_fn combine_notes, int flags)\n>  {\n>         struct object_id oid, object_oid;\n> -       unsigned short mode;\n>         struct leaf_node root_tree;\n>\n>         if (!t)\n> @@ -1021,7 +1020,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n>                 return;\n>         if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))\n>                 die(\"Cannot use notes ref %s\", notes_ref);\n> -       if (get_tree_entry_mode(the_repository, &object_oid, \"\", &oid, &mode))\n> +       if (get_tree_entry_path(the_repository, &object_oid, \"\", &oid))\n>                 die(\"Failed to read notes tree referenced by %s (%s)\",\n>                     notes_ref, oid_to_hex(&object_oid));\n>\n> diff --git a/object-name.c b/object-name.c\n> index 7e3b2d6d739..9ff5f83c1ff 100644\n> --- a/object-name.c\n> +++ b/object-name.c\n> @@ -1693,7 +1693,6 @@ static void diagnose_invalid_oid_path(struct repository *r,\n>                                       int object_name_len)\n>  {\n>         struct object_id oid;\n> -       unsigned short mode;\n>\n>         if (!prefix)\n>                 prefix = \"\";\n> @@ -1704,7 +1703,7 @@ static void diagnose_invalid_oid_path(struct repository *r,\n>         if (is_missing_file_error(errno)) {\n>                 char *fullname = xstrfmt(\"%s%s\", prefix, filename);\n>\n> -               if (!get_tree_entry_mode(r, tree_oid, fullname, &oid, &mode)) {\n> +               if (!get_tree_entry_path(r, tree_oid, fullname, &oid)) {\n>                         die(_(\"path '%s' exists, but not '%s'\\n\"\n>                             \"hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?\"),\n>                             fullname,\n> diff --git a/tree-walk.c b/tree-walk.c\n> index 0ad3d80593e..83737634770 100644\n> --- a/tree-walk.c\n> +++ b/tree-walk.c\n> @@ -632,6 +632,17 @@ int get_tree_entry_all(struct repository *r,\n>         return retval;\n>  }\n>\n> +int get_tree_entry_path(struct repository *r,\n> +                       const struct object_id *tree_oid,\n> +                       const char *name,\n> +                       struct object_id *oid)\n> +{\n> +       unsigned short mode;\n> +       enum object_type object_type;\n> +       return get_tree_entry_all(r, tree_oid, name, oid,\n> +                                 &mode, &object_type);\n> +}\n> +\n>  int get_tree_entry_mode(struct repository *r,\n>                         const struct object_id *tree_oid,\n>                         const char *name,\n> diff --git a/tree-walk.h b/tree-walk.h\n> index 5db38fcb575..1bfa839b275 100644\n> --- a/tree-walk.h\n> +++ b/tree-walk.h\n> @@ -175,10 +175,13 @@ struct traverse_info {\n>   * \"struct name_entry\" you'd like. You always need to pointer to an\n>   * appropriate variable to fill in (NULL won't do!):\n>   *\n> + * get_tree_entry_path(): <no extra argument, just get the common 'path'>\n>   * get_tree_entry_mode(): unsigned int mode\n>   * get_tree_entry_type(): enum object_type\n>   * get_tree_entry_all(): unsigned int mode, enum object_type\n>   */\n> +int get_tree_entry_path(struct repository *, const struct object_id *, const char *,\n> +                       struct object_id *);\n>  int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n>                         struct object_id *,\n>                         unsigned short *);\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n"},{"id":"418628","messageId":"CABPp-BFBK=iT8uupa15XMJ6kfTfPFUgyJ59byRcd8e1Eb7cA1g@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-25-avarab@gmail.com","subject":"Re: [PATCH 24/30] tree-walk.h API: document and format tree_entry_extract()","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T18:28:49Z","receivedAt":"2021-03-09T18:29:54Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Document and format the argument list of the tree_entry_extract()\n> function in preparation for adding a sister function.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  tree-walk.h | 14 ++++++++++----\n>  1 file changed, 10 insertions(+), 4 deletions(-)\n>\n> diff --git a/tree-walk.h b/tree-walk.h\n> index 1bfa839b275..61fdcb166d2 100644\n> --- a/tree-walk.h\n> +++ b/tree-walk.h\n> @@ -40,11 +40,17 @@ struct tree_desc {\n>\n>  /**\n>   * Decode the entry currently being visited (the one pointed to by\n> - * `tree_desc's` `entry` member) and return the sha1 of the entry. The\n> - * `pathp` and `modep` arguments are set to the entry's pathname and mode\n> - * respectively.\n> + * `tree_desc's` `entry` member) and return the OID of the entry.\n> +\n> + * There are variants of this function depending on what fields in the\n> + * \"struct name_entry\" you'd like. You always need to pointer to an\n\ns/need to pointer/need a pointer/\n\n> + * appropriate variable to fill in (NULL won't do!):\n> + *\n> + * tree_entry_extract_mode(): const char *path, unsigned int mode\n\nIndenting this over slightly might make it easier to read\n\n>   */\n> -static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned short *modep)\n> +static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,\n> +                                                        const char **pathp,\n> +                                                        unsigned short *modep)\n>  {\n>         *pathp = desc->entry.path;\n>         *modep = desc->entry.mode;\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n>\n"},{"id":"418629","messageId":"CABPp-BFDzfy-NOkLFJq-8h7L81jEYo7hava_2wUr2L-h1q4YKQ@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-27-avarab@gmail.com","subject":"Re: [PATCH 26/30] tree-walk.h API: add a tree_entry_extract_all() function","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T18:30:29Z","receivedAt":"2021-03-09T18:31:33Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Add a tree_entry_extract_all() sibling function to the existing\n> tree_entry_extract_mode().\n>\n> Having the OBJ_{BLOB,TREE,COMMIT} when you have the \"mode\" is strictly\n> speaking redundant, but hopefully makes it easier to read the\n> code. We'll now see which parts of the code are checking the types,\n> v.s. those that care about the mode specifically.\n>\n> Only the first use of tree_entry_extract_mode() in emit_path() is\n> converted here, the other branch will use a new\n> get_tree_entry_mode_type() introduced in a subsequent commit.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/update-index.c |  6 ++++--\n>  tree-diff.c            |  5 +++--\n>  tree-walk.c            |  3 ++-\n>  tree-walk.h            | 12 ++++++++++++\n>  4 files changed, 21 insertions(+), 5 deletions(-)\n>\n> diff --git a/builtin/update-index.c b/builtin/update-index.c\n> index 070510d6a88..b489a876392 100644\n> --- a/builtin/update-index.c\n> +++ b/builtin/update-index.c\n> @@ -599,16 +599,18 @@ static struct cache_entry *read_one_ent(const char *which,\n>                                         struct object_id *ent, const char *path,\n>                                         int namelen, int stage)\n>  {\n> +       enum object_type object_type;\n>         unsigned short mode;\n>         struct object_id oid;\n>         struct cache_entry *ce;\n>\n> -       if (get_tree_entry_mode(the_repository, ent, path, &oid, &mode)) {\n> +       if (get_tree_entry_all(the_repository, ent, path, &oid,\n> +                              &mode, &object_type)) {\n>                 if (which)\n>                         error(\"%s: not in %s branch.\", path, which);\n>                 return NULL;\n>         }\n> -       if (mode == S_IFDIR) {\n> +       if (object_type == OBJ_TREE) {\n>                 if (which)\n>                         error(\"%s: not a blob in %s branch.\", path, which);\n>                 return NULL;\n> diff --git a/tree-diff.c b/tree-diff.c\n> index b37348b7908..b25095c1164 100644\n> --- a/tree-diff.c\n> +++ b/tree-diff.c\n> @@ -195,10 +195,11 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n>         assert(t || tp);\n>\n>         if (t) {\n> +               enum object_type object_type;\n>                 /* path present in resulting tree */\n> -               oid = tree_entry_extract_mode(t, &path, &mode);\n> +               oid = tree_entry_extract_all(t, &path, &mode, &object_type);\n>                 pathlen = tree_entry_len(&t->entry);\n> -               isdir = S_ISDIR(mode);\n> +               isdir = object_type == OBJ_TREE;\n\nNot worth a reroll, but parenthesis around the right-hand side would\nmake it a bit faster to parse and understand.\n\n>         } else {\n>                 /*\n>                  * a path was removed - take path from imin parent. Also take\n> diff --git a/tree-walk.c b/tree-walk.c\n> index e613f273767..12e0ed4e250 100644\n> --- a/tree-walk.c\n> +++ b/tree-walk.c\n> @@ -570,7 +570,8 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n>                 struct object_id oid;\n>                 int entrylen, cmp;\n>\n> -               oidcpy(&oid, tree_entry_extract_mode(t, &entry, mode));\n> +               oidcpy(&oid, tree_entry_extract_all(t, &entry, mode, object_type));\n> +\n>                 entrylen = tree_entry_len(&t->entry);\n>                 update_tree_entry(t);\n>                 if (entrylen > namelen)\n> diff --git a/tree-walk.h b/tree-walk.h\n> index 892e77eda23..06ad40ab2f1 100644\n> --- a/tree-walk.h\n> +++ b/tree-walk.h\n> @@ -47,6 +47,7 @@ struct tree_desc {\n>   * appropriate variable to fill in (NULL won't do!):\n>   *\n>   * tree_entry_extract_mode(): const char *path, unsigned int mode\n> + * tree_entry_extract_all(): const char *path, unsigned int mode, enum object_type\n>   */\n>  static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *desc,\n>                                                               const char **pathp,\n> @@ -57,6 +58,17 @@ static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *\n>         return &desc->entry.oid;\n>  }\n>\n> +static inline const struct object_id *tree_entry_extract_all(struct tree_desc *desc,\n> +                                                            const char **pathp,\n> +                                                            unsigned short *modep,\n> +                                                            enum object_type *object_typep)\n> +{\n> +       *pathp = desc->entry.path;\n> +       *modep = desc->entry.mode;\n> +       *object_typep = desc->entry.object_type;\n> +       return &desc->entry.oid;\n> +}\n> +\n>  /**\n>   * Calculate the length of a tree entry's pathname. This utilizes the\n>   * memory structure of a tree entry to avoid the overhead of using a\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n>\n"},{"id":"418630","messageId":"CABPp-BFrVCZF-tWUXqJckbEvNPxjFxtTE3_uUpkLv_ARTb2X6g@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-29-avarab@gmail.com","subject":"Re: [PATCH 28/30] tree-walk.h API users: rename \"struct name_entry\"'s \"mode\" to \"raw_mode\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T18:53:53Z","receivedAt":"2021-03-09T18:55:00Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Now that most of the users of the \"mode\" variable have been moved to\n> use \"object_type\" instead let's rename it to \"raw_mode\" in preparation\n> for a revert of 7146e66f086 (tree-walk: finally switch over tree\n> descriptors to contain a pre-parsed entry, 2014-02-06).\n>\n> This will allow API users who care about the actual mode bits in tree\n> objects to get access to them, such as fsck, the merge algorithm etc.\n\nIf you're going to feed the raw mode bits through to the merge\nalgorithm, then the merge algorithm logic needs to change in several\nplaces to accommodate have raw modes instead of canonicalized modes.\nI'm also curious what the other places are.  For example, will the\ndiff machinery show a diff now for files whose raw_mode differs even\nif the canonicalized mode does not?  This would be a behavioral\nchange, but I'm getting ahead of myself since I should probably wait\nuntil the subsequent patches to see what you did.\n\n> But most users will not want to have such potentially un-sanitized, so\n> let's indicate that by giving the variable a more scary name.\n>\n> I am not renaming the variables being assigned to, i.e. it's now going\n> to be \"int mode = entry.raw_mode\", not \"int raw_mode = [...]\". This is\n> because we're going to be getting a sanitized \"mode\" via\n> \"canon_mode()\" in many of these functions soon, so renaming the local\n> variable back and forth will result in needless churn.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/merge-tree.c |  8 ++++----\n>  match-trees.c        |  4 ++--\n>  merge-ort.c          | 12 ++++++------\n>  notes.c              |  2 +-\n>  tree-diff.c          | 22 +++++++++++-----------\n>  tree-walk.c          |  2 +-\n>  tree-walk.h          |  6 +++---\n>  tree.c               |  2 +-\n>  unpack-trees.c       |  6 +++---\n>  9 files changed, 32 insertions(+), 32 deletions(-)\n>\n> diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\n> index eec5b906561..b4e736e4b72 100644\n> --- a/builtin/merge-tree.c\n> +++ b/builtin/merge-tree.c\n> @@ -160,7 +160,7 @@ static int same_entry(struct name_entry *a, struct name_entry *b)\n>         return  !is_null_oid(&a->oid) &&\n>                 !is_null_oid(&b->oid) &&\n>                 oideq(&a->oid, &b->oid) &&\n> -               a->mode == b->mode;\n> +               a->raw_mode == b->raw_mode;\n\nThis looks like a case that probably should operate on canonicalized modes.\n\n>  }\n>\n>  static int both_empty(struct name_entry *a, struct name_entry *b)\n> @@ -197,9 +197,9 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s\n>                 return;\n>\n>         path = traverse_path(info, result);\n> -       orig_mode = ours->mode;\n> +       orig_mode = ours->raw_mode;\n>         orig = create_entry(2, orig_mode, &ours->oid, path);\n> -       final_mode = result->mode;\n> +       final_mode = result->raw_mode;\n>         final = create_entry(0, final_mode, &result->oid, path);\n>\n>         final->link = orig;\n> @@ -252,7 +252,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n>                 path = entry->path;\n>         else\n>                 path = traverse_path(info, n);\n> -       link_mode = n->mode;\n> +       link_mode = n->raw_mode;\n>         link = create_entry(stage, link_mode, &n->oid, path);\n>\n>         link->link = entry;\n> diff --git a/match-trees.c b/match-trees.c\n> index 0636f6e58e9..d45c76ffa79 100644\n> --- a/match-trees.c\n> +++ b/match-trees.c\n> @@ -86,8 +86,8 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha\n>\n>         for (;;) {\n>                 int cmp;\n> -               unsigned int one_mode = one.entry.mode;\n> -               unsigned int two_mode = two.entry.mode;\n> +               unsigned int one_mode = one.entry.raw_mode;\n> +               unsigned int two_mode = two.entry.raw_mode;\n>\n>                 if (one.size && two.size)\n>                         cmp = base_name_entries_compare(&one.entry, &two.entry);\n> diff --git a/merge-ort.c b/merge-ort.c\n> index cad10436504..ea20bbe2af3 100644\n> --- a/merge-ort.c\n> +++ b/merge-ort.c\n> @@ -502,7 +502,7 @@ static void setup_path_info(struct merge_options *opt,\n>         mi->basename_offset = current_dir_name_len;\n>         mi->clean = !!resolved;\n>         if (resolved) {\n> -               mi->result.mode = merged_version->mode;\n> +               mi->result.mode = merged_version->raw_mode;\n\nThis seems scary.\n\n>                 oidcpy(&mi->result.oid, &merged_version->oid);\n>                 mi->is_null = !!is_null;\n>         } else {\n> @@ -512,7 +512,7 @@ static void setup_path_info(struct merge_options *opt,\n>                 ASSIGN_AND_VERIFY_CI(ci, mi);\n>                 for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {\n>                         ci->pathnames[i] = fullpath;\n> -                       ci->stages[i].mode = names[i].mode;\n> +                       ci->stages[i].mode = names[i].raw_mode;\n\nlikewise.\n\n>                         oidcpy(&ci->stages[i].oid, &names[i].oid);\n>                 }\n>                 ci->filemask = filemask;\n> @@ -545,7 +545,7 @@ static void add_pair(struct merge_options *opt,\n>         struct rename_info *renames = &opt->priv->renames;\n>         int names_idx = is_add ? side : 0;\n>         const struct object_id *oid = &names[names_idx].oid;\n> -       unsigned int mode = names[names_idx].mode;\n> +       unsigned int mode = names[names_idx].raw_mode;\n>\n>         one = alloc_filespec(pathname);\n>         two = alloc_filespec(pathname);\n> @@ -616,13 +616,13 @@ static int collect_merge_info_callback(int n,\n>         unsigned side1_null = !(mask & 2);\n>         unsigned side2_null = !(mask & 4);\n>         unsigned side1_matches_mbase = (!side1_null && !mbase_null &&\n> -                                       names[0].mode == names[1].mode &&\n> +                                       names[0].raw_mode == names[1].raw_mode &&\n>                                         oideq(&names[0].oid, &names[1].oid));\n>         unsigned side2_matches_mbase = (!side2_null && !mbase_null &&\n> -                                       names[0].mode == names[2].mode &&\n> +                                       names[0].raw_mode == names[2].raw_mode &&\n>                                         oideq(&names[0].oid, &names[2].oid));\n>         unsigned sides_match = (!side1_null && !side2_null &&\n> -                               names[1].mode == names[2].mode &&\n> +                               names[1].raw_mode == names[2].raw_mode &&\n>                                 oideq(&names[1].oid, &names[2].oid));\n\nIf these three really start operating on the raw_mode, then this logic\nwill be wrong.  100644 and 100666 should be considered \"equal and\nmatching\" for the purposes used here.\n\n>         /*\n> diff --git a/notes.c b/notes.c\n> index aa46cb2b09e..2817325651a 100644\n> --- a/notes.c\n> +++ b/notes.c\n> @@ -478,7 +478,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n>                         struct strbuf non_note_path = STRBUF_INIT;\n>                         const char *q = oid_to_hex(&subtree->key_oid);\n>                         size_t i;\n> -                       unsigned int mode = entry.mode;\n> +                       unsigned int mode = entry.raw_mode;\n>                         for (i = 0; i < prefix_len; i++) {\n>                                 strbuf_addch(&non_note_path, *q++);\n>                                 strbuf_addch(&non_note_path, *q++);\n> diff --git a/tree-diff.c b/tree-diff.c\n> index 10c92d39c42..df8301d806a 100644\n> --- a/tree-diff.c\n> +++ b/tree-diff.c\n> @@ -232,7 +232,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n>                          * tp[i] is valid, if present and if tp[i]==tp[imin] -\n>                          * otherwise, we should ignore it.\n>                          */\n> -                       int tpi_valid = tp && !(tp[i].entry.mode & S_IFXMIN_NEQ);\n> +                       int tpi_valid = tp && !(tp[i].entry.raw_mode & S_IFXMIN_NEQ);\n>\n>                         const struct object_id *oid_i;\n>                         unsigned mode_i;\n> @@ -245,7 +245,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n>\n>                         if (tpi_valid) {\n>                                 oid_i = &tp[i].entry.oid;\n> -                               mode_i = tp[i].entry.mode;\n> +                               mode_i = tp[i].entry.raw_mode;\n\nI don't know tree-diff enough but I'm wondering if it has similar\nissues to merge-ort.\n\n>                         }\n>                         else {\n>                                 oid_i = &null_oid;\n> @@ -283,7 +283,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n>                 FAST_ARRAY_ALLOC(parents_oid, nparent);\n>                 for (i = 0; i < nparent; ++i) {\n>                         /* same rule as in emitthis */\n> -                       int tpi_valid = tp && !(tp[i].entry.mode & S_IFXMIN_NEQ);\n> +                       int tpi_valid = tp && !(tp[i].entry.raw_mode & S_IFXMIN_NEQ);\n>\n>                         parents_oid[i] = tpi_valid ? &tp[i].entry.oid : NULL;\n>                 }\n> @@ -404,7 +404,7 @@ static inline void update_tp_entries(struct tree_desc *tp, int nparent)\n>  {\n>         int i;\n>         for (i = 0; i < nparent; ++i)\n> -               if (!(tp[i].entry.mode & S_IFXMIN_NEQ))\n> +               if (!(tp[i].entry.raw_mode & S_IFXMIN_NEQ))\n>                         update_tree_entry(&tp[i]);\n>  }\n>\n> @@ -465,10 +465,10 @@ static struct combine_diff_path *ll_diff_tree_paths(\n>                  * mark entries whether they =p[imin] along the way\n>                  */\n>                 imin = 0;\n> -               tp[0].entry.mode &= ~S_IFXMIN_NEQ;\n> +               tp[0].entry.raw_mode &= ~S_IFXMIN_NEQ;\n>\n>                 for (i = 1; i < nparent; ++i) {\n> -                       unsigned int mode = tp[i].entry.mode;\n> +                       unsigned int mode = tp[i].entry.raw_mode;\n>                         cmp = tree_entry_pathcmp(&tp[i], &tp[imin]);\n>                         if (cmp < 0) {\n>                                 imin = i;\n> @@ -480,12 +480,12 @@ static struct combine_diff_path *ll_diff_tree_paths(\n>                         else {\n>                                 mode |= S_IFXMIN_NEQ;\n>                         }\n> -                       tp[i].entry.mode = mode;\n> +                       tp[i].entry.raw_mode = mode;\n>                 }\n>\n>                 /* fixup markings for entries before imin */\n>                 for (i = 0; i < imin; ++i)\n> -                       tp[i].entry.mode |= S_IFXMIN_NEQ;       /* pi > p[imin] */\n> +                       tp[i].entry.raw_mode |= S_IFXMIN_NEQ;   /* pi > p[imin] */\n>\n>\n>\n> @@ -497,14 +497,14 @@ static struct combine_diff_path *ll_diff_tree_paths(\n>                         /* are either pi > p[imin] or diff(t,pi) != ø ? */\n>                         if (!opt->flags.find_copies_harder) {\n>                                 for (i = 0; i < nparent; ++i) {\n> -                                       unsigned int mode = tp[i].entry.mode;\n> +                                       unsigned int mode = tp[i].entry.raw_mode;\n>                                         /* p[i] > p[imin] */\n>                                         if (mode & S_IFXMIN_NEQ)\n>                                                 continue;\n>\n>                                         /* diff(t,pi) != ø */\n>                                         if (!oideq(&t.entry.oid, &tp[i].entry.oid) ||\n> -                                           (t.entry.mode != mode))\n> +                                           (t.entry.raw_mode != mode))\n>                                                 continue;\n>\n>                                         goto skip_emit_t_tp;\n> @@ -536,7 +536,7 @@ static struct combine_diff_path *ll_diff_tree_paths(\n>                         /* ∀i pi=p[imin] -> D += \"-p[imin]\" */\n>                         if (!opt->flags.find_copies_harder) {\n>                                 for (i = 0; i < nparent; ++i)\n> -                                       if (tp[i].entry.mode & S_IFXMIN_NEQ)\n> +                                       if (tp[i].entry.raw_mode & S_IFXMIN_NEQ)\n>                                                 goto skip_emit_tp;\n>                         }\n>\n> diff --git a/tree-walk.c b/tree-walk.c\n> index 12e0ed4e250..099a9b3bd77 100644\n> --- a/tree-walk.c\n> +++ b/tree-walk.c\n> @@ -48,7 +48,7 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l\n>         /* Initialize the descriptor entry */\n>         desc->entry.path = path;\n>         mode = canon_mode(mode);\n> -       desc->entry.mode = mode;\n> +       desc->entry.raw_mode = mode;\n>         desc->entry.object_type = object_type(mode);\n>         desc->entry.pathlen = len - 1;\n>         hashcpy(desc->entry.oid.hash, (const unsigned char *)path + len);\n> diff --git a/tree-walk.h b/tree-walk.h\n> index 1f69e57db4c..885ced74258 100644\n> --- a/tree-walk.h\n> +++ b/tree-walk.h\n> @@ -16,7 +16,7 @@ struct name_entry {\n>         struct object_id oid;\n>         const char *path;\n>         int pathlen;\n> -       unsigned int mode;\n> +       unsigned int raw_mode;\n>         /* simple 'mode': Only OBJ_{BLOB,TREE,COMMIT} */\n>         enum object_type object_type;\n>  };\n> @@ -55,7 +55,7 @@ static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *\n>                                                               unsigned short *modep)\n>  {\n>         *pathp = desc->entry.path;\n> -       *modep = desc->entry.mode;\n> +       *modep = desc->entry.raw_mode;\n>         return &desc->entry.oid;\n>  }\n>\n> @@ -75,7 +75,7 @@ static inline const struct object_id *tree_entry_extract_all(struct tree_desc *d\n>                                                              enum object_type *object_typep)\n>  {\n>         *pathp = desc->entry.path;\n> -       *modep = desc->entry.mode;\n> +       *modep = desc->entry.raw_mode;\n>         *object_typep = desc->entry.object_type;\n>         return &desc->entry.oid;\n>  }\n> diff --git a/tree.c b/tree.c\n> index e4402fad69b..215d17e1295 100644\n> --- a/tree.c\n> +++ b/tree.c\n> @@ -40,7 +40,7 @@ static int read_tree_1(struct repository *r,\n>                 }\n>\n>                 switch (fn(&entry.oid, base,\n> -                          entry.path, entry.object_type, entry.mode, context)) {\n> +                          entry.path, entry.object_type, entry.raw_mode, context)) {\n>                 case 0:\n>                         continue;\n>                 case READ_TREE_RECURSIVE:\n> diff --git a/unpack-trees.c b/unpack-trees.c\n> index 9471c19de72..dcdf8130745 100644\n> --- a/unpack-trees.c\n> +++ b/unpack-trees.c\n> @@ -867,7 +867,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n>         newinfo.pathspec = info->pathspec;\n>         newinfo.name = p->path;\n>         newinfo.namelen = p->pathlen;\n> -       newinfo.mode = p->mode;\n> +       newinfo.mode = p->raw_mode;\n\nOh boy...unpack-trees too?\n\nWell, all of my worries may be unfounded depending on what you do in\nsubsequent commits.\n\n>         newinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1);\n>         newinfo.df_conflicts |= df_conflicts;\n>\n> @@ -1020,7 +1020,7 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,\n>                 is_transient ?\n>                 make_empty_transient_cache_entry(len) :\n>                 make_empty_cache_entry(istate, len);\n> -       unsigned int mode = n->mode;\n> +       unsigned int mode = n->raw_mode;\n>\n>         ce->ce_mode = create_ce_mode(mode);\n>         ce->ce_flags = create_ce_flags(stage);\n> @@ -1209,7 +1209,7 @@ static void debug_path(struct traverse_info *info)\n>  static void debug_name_entry(int i, struct name_entry *n)\n>  {\n>         printf(\"ent#%d %06o %s\\n\", i,\n> -              n->path ? n->mode : 0,\n> +              n->path ? n->raw_mode : 0,\n>                n->path ? n->path : \"(missing)\");\n>  }\n>\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n>\n"},{"id":"418632","messageId":"CABPp-BGGGrYs4HSmT4kq__90K8OxDKkF_Nb8nLAhouLONYxsEA@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-30-avarab@gmail.com","subject":"Re: [PATCH 29/30] tree.h API users: rename read_tree_fn_t's \"mode\" to \"raw_mode\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T19:02:39Z","receivedAt":"2021-03-09T19:03:39Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Rename the \"mode\" variable passed to read_tree_fn_t callbacks to\n> \"raw_mode\". This variable comes to us from the tree-walk.h API. By\n> renaming this variable we can easily see where its downstream users\n> are in a subsequent commit where we'll sprinkle some canon_mode()\n> here.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  archive.c          |  5 +++--\n>  builtin/log.c      |  2 +-\n>  builtin/ls-files.c | 11 ++++++-----\n>  builtin/ls-tree.c  |  6 +++---\n>  merge-recursive.c  |  2 +-\n>  5 files changed, 14 insertions(+), 12 deletions(-)\n>\n> diff --git a/archive.c b/archive.c\n> index bc8f1c7546f..5b85aae8106 100644\n> --- a/archive.c\n> +++ b/archive.c\n> @@ -232,10 +232,11 @@ static int write_directory(struct archiver_context *c)\n>\n>  static int queue_or_write_archive_entry(const struct object_id *oid,\n>                                         struct strbuf *base, const char *filename,\n> -                                       enum object_type object_type, unsigned mode,\n> +                                       enum object_type object_type, unsigned raw_mode,\n>                                         void *context)\n>  {\n>         struct archiver_context *c = context;\n> +       unsigned mode = raw_mode;\n>\n>         while (c->bottom &&\n>                !(base->len >= c->bottom->len &&\n> @@ -382,7 +383,7 @@ struct path_exists_context {\n>\n>  static int reject_entry(const struct object_id *oid, struct strbuf *base,\n>                         const char *filename,\n> -                       enum object_type object_type, unsigned mode,\n> +                       enum object_type object_type, unsigned raw_mode,\n>                         void *context)\n>  {\n>         int ret = -1;\n> diff --git a/builtin/log.c b/builtin/log.c\n> index 19a916221d5..c3ef1b3e22d 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -599,7 +599,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n>\n>  static int show_tree_object(const struct object_id *oid,\n>                             struct strbuf *base, const char *pathname,\n> -                           enum object_type object_type, unsigned mode,\n> +                           enum object_type object_type, unsigned raw_mode,\n\nThis was surprising to me and had me worried, but it looks like this\nfunction doesn't even use mode or raw_mode.\n\n>                             void *context)\n>  {\n>         FILE *file = context;\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index f38df439410..391e6a9f141 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -425,10 +425,11 @@ static int read_one_entry_opt(struct index_state *istate,\n>                               const struct object_id *oid,\n>                               struct strbuf *base,\n>                               const char *pathname,\n> -                             unsigned mode, int opt)\n> +                             unsigned raw_mode, int opt)\n>  {\n>         int len;\n>         struct cache_entry *ce;\n> +       unsigned mode = raw_mode;\n\nI was about to comment, but checked out the code in question and it\nlooks like you've modified further.  So perhaps I should wait until\nthe last patch in the series.\n\n>\n>         if (S_ISDIR(mode))\n>                 return READ_TREE_RECURSIVE;\n> @@ -447,12 +448,12 @@ static int read_one_entry_opt(struct index_state *istate,\n>\n>  static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n>                           const char *pathname,\n> -                         enum object_type object_type, unsigned mode,\n> +                         enum object_type object_type, unsigned raw_mode,\n>                           void *context)\n>  {\n>         struct index_state *istate = context;\n>         return read_one_entry_opt(istate, oid, base, pathname,\n> -                                 mode,\n> +                                 raw_mode,\n>                                   ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n>  }\n>\n> @@ -462,12 +463,12 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n>   */\n>  static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n>                                 const char *pathname,\n> -                               enum object_type object_type, unsigned mode,\n> +                               enum object_type object_type, unsigned raw_mode,\n>                                 void *context)\n>  {\n>         struct index_state *istate = context;\n>         return read_one_entry_opt(istate, oid, base, pathname,\n> -                                 mode,\n> +                                 raw_mode,\n>                                   ADD_CACHE_JUST_APPEND);\n>  }\n>\n> diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\n> index c6ec3ca751e..3f84603d391 100644\n> --- a/builtin/ls-tree.c\n> +++ b/builtin/ls-tree.c\n> @@ -63,7 +63,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n>\n>  static int show_tree(const struct object_id *oid, struct strbuf *base,\n>                      const char *pathname,\n> -                    enum object_type object_type, unsigned mode,\n> +                    enum object_type object_type, unsigned raw_mode,\n>                      void *context)\n>  {\n>         int retval = 0;\n> @@ -103,11 +103,11 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\n>                                                   \"%\"PRIuMAX, (uintmax_t)size);\n>                         } else\n>                                 xsnprintf(size_text, sizeof(size_text), \"-\");\n> -                       printf(\"%06o %s %s %7s\\t\", mode, type,\n> +                       printf(\"%06o %s %s %7s\\t\", raw_mode, type,\n>                                find_unique_abbrev(oid, abbrev),\n>                                size_text);\n>                 } else\n> -                       printf(\"%06o %s %s\\t\", mode, type,\n> +                       printf(\"%06o %s %s\\t\", raw_mode, type,\n>                                find_unique_abbrev(oid, abbrev));\n\nThis looks like a behavioral change.  It might be desirable, but\nshouldn't it be called out and documented with a testcase?\n\n(Or is there no change yet because the raw_mode isn't actually yet raw?)\n\n>         }\n>         baselen = base->len;\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index b26d9d418f9..30fbe72ca06 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -453,7 +453,7 @@ static void unpack_trees_finish(struct merge_options *opt)\n>\n>  static int save_files_dirs(const struct object_id *oid,\n>                            struct strbuf *base, const char *path,\n> -                          enum object_type object_type, unsigned int mode,\n> +                          enum object_type object_type, unsigned int raw_mode,\n>                            void *context)\n>  {\n>         struct path_hashmap_entry *entry;\n\nOh, man, merge-recursive.c gets modes from the index, from the diff\nmachinery, and from its own direct calls (get_tree_entry*() calls).\nAnd might have comparisons in all kinds of places.  I'd be _very_\nleery, much more so than with merge-ort.c, of having it deal with raw\nmodes.\n"},{"id":"418638","messageId":"CABPp-BEPkukGz32rrro1hzMvSQzX4v7U17CAcV-G2NS6v0u55g@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-31-avarab@gmail.com","subject":"Re: [PATCH 30/30] tree-walk.h API: move canon_mode() back out of decode_tree_entry()","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T20:23:40Z","receivedAt":"2021-03-09T20:24:59Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> Move the canon_mode() call back out of decode_tree_entry(), and\n> instead make it the responsibility of its callers to canonicalize the\n> tree modes we get.\n>\n> This effectively reverts 7146e66f086 (tree-walk: finally switch over\n> tree descriptors to contain a pre-parsed entry, 2014-02-06), with the\n> recent of most callers away from \"mode\" (now \"raw_mode\") towards \"enum\n> object_id\" in recent commit the motivation for that commit effectively\n> doesn't exist anymore.\n>\n> I.e. I'm not adding the canon_mode() call back to\n> tree_entry_extract(), instead it's now become sane to move this\n> responsibility to those callers that still care about the \"raw_mode\".\n>\n> That change was meant as a pure optimization change, but it actually\n> introduced a subtle bug. We were left without any low-level API to get\n> non-standard mode bits out of trees. Having non-standard modes isn't\n> the norm, and fsck should warn about it.\n>\n> Except after 7146e66f086 it couldn't anymore, since the modes\n> fsck_tree() got would be pre-sanitized for it. I believe that fsck\n> issue is per-se a serious bug, the \"bad mode\" was a default warning,\n> not an error.\n>\n> This change makes that fsck check work again, why aren't there any\n> test changes for fsck here? Because we didn't have a test for that\n> fsck feature in the first place, which is why the regression in\n> 7146e66f086 snuck by us. A follow-up commit will add such a test.\n>\n> It is possible that this commit is introducing some subtle regression\n> that I've missed.\n\nI'm sure there are at least a few, and I suspect many more.  This is a\nsuper scary change to me, even if it's only about corner cases that\nshould only exist in repositories that had objects created with super\nold (or new and broken) git clients.\n\n> We are now propagating the \"raw_mode\" outside of everything downstream\n> of decode_tree_entry(), which is everything we have that decodes\n> trees. It's our most low-level tree decoding API.\n>\n> As shown here we rely parsing out a \"raw\" (and possibly something fsck\n> would complain about) mode as-is, but when we run merge, add something\n> new to the index, create an archive etc. we don't want to propagate\n> that bad mode when we create new data. We want to canon_mode() it.\n>\n> I'm also pretty sure that we don't have good enough test coverage for\n> those scenarios. We barely have tests for these bad mode bits at\n> all (not even one for fsck). We definitely are not testing all\n> merge/index/archive etc. interactions.\n>\n> Still, I think this change is worth it overall, because:\n>\n>  1. We must have a way to get at these raw modes in some way, even if\n>     just for fsck. There's also other things that care, see e.g. the\n>     FIXME comment in 62fdec17a11 (merge-ort: flesh out implementation of\n>     handle_content_merge(), 2021-01-01)\n>\n>  2. #1 is not a justification for this change, I could have e.g. just\n>     added the ability to pass some \"want_raw\" flag into\n>     decode_tree_entry() for use in fsck. But I think with the migration\n>     of most tree iteration towards \"enum object_type\" it's become worth\n>     it.\n\nThis seems like the safer route.  Then we can slowly convert callers\nover to using the want_raw as we audit them and add appropriate tests.\n\n>  3. Yes our test coverage sucks, but before 7146e66f086 we were also\n>     spreading what's now the \"raw_mode\" all over the place. That commit\n>     was first released with Git v2.0.0 in mid-2014. A while ago for sure,\n>     but most of this code existed in something approximating its current\n>     form then. This isn't new territory.\n\nThat's very helpful to know, actually.  That does lower my worry some.\nBut the fact that it was released as part of v2.0.0, a new major\nrelease, suggests we knew there were potential breaking changes.  And\ncode has been built on top of those assumptions for quite a few years,\nso we might again break things by reverting back, and having it be\npart of a non-major release is worrisome without appropriate tests and\naudits of the relevant code paths.\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  archive.c              |  2 +-\n>  builtin/checkout.c     |  1 +\n>  builtin/ls-files.c     |  2 +-\n>  builtin/merge-tree.c   |  6 +++---\n>  builtin/update-index.c |  1 +\n>  merge-ort.c            | 13 ++++++++++++-\n>  notes.c                |  1 +\n>  tree-walk.c            |  1 -\n>  unpack-trees.c         |  4 +++-\n\nNo tests...oh boy.  You did mention this in the commit message, but\nI'm still having a hard time getting past making a very low level\nchange like this without either tests or bumping the major version\nnumber of git.\n\n>  9 files changed, 23 insertions(+), 8 deletions(-)\n>\n> diff --git a/archive.c b/archive.c\n> index 5b85aae8106..8083f15f3ba 100644\n> --- a/archive.c\n> +++ b/archive.c\n> @@ -236,7 +236,7 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n>                                         void *context)\n>  {\n>         struct archiver_context *c = context;\n> -       unsigned mode = raw_mode;\n> +       unsigned mode = canon_mode(raw_mode);\n>\n>         while (c->bottom &&\n>                !(base->len >= c->bottom->len &&\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index d4adfdb5046..7f25b955616 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -132,6 +132,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,\n>         memcpy(ce->name + base->len, pathname, len - base->len);\n>         ce->ce_flags = create_ce_flags(0) | CE_UPDATE;\n>         ce->ce_namelen = len;\n> +       mode = canon_mode(mode);\n>         ce->ce_mode = create_ce_mode(mode);\n>\n>         /*\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index 391e6a9f141..926523d77a7 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -429,7 +429,7 @@ static int read_one_entry_opt(struct index_state *istate,\n>  {\n>         int len;\n>         struct cache_entry *ce;\n> -       unsigned mode = raw_mode;\n> +       unsigned mode = canon_mode(raw_mode);\n>\n>         if (S_ISDIR(mode))\n>                 return READ_TREE_RECURSIVE;\n> diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\n> index b4e736e4b72..f8733a86eb7 100644\n> --- a/builtin/merge-tree.c\n> +++ b/builtin/merge-tree.c\n> @@ -197,9 +197,9 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s\n>                 return;\n>\n>         path = traverse_path(info, result);\n> -       orig_mode = ours->raw_mode;\n> +       orig_mode = canon_mode(ours->raw_mode);\n>         orig = create_entry(2, orig_mode, &ours->oid, path);\n> -       final_mode = result->raw_mode;\n> +       final_mode = canon_mode(result->raw_mode);\n>         final = create_entry(0, final_mode, &result->oid, path);\n>\n>         final->link = orig;\n> @@ -252,7 +252,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n>                 path = entry->path;\n>         else\n>                 path = traverse_path(info, n);\n> -       link_mode = n->raw_mode;\n> +       link_mode = canon_mode(n->raw_mode);\n>         link = create_entry(stage, link_mode, &n->oid, path);\n>\n>         link->link = entry;\n> diff --git a/builtin/update-index.c b/builtin/update-index.c\n> index b489a876392..1996fdd97af 100644\n> --- a/builtin/update-index.c\n> +++ b/builtin/update-index.c\n> @@ -621,6 +621,7 @@ static struct cache_entry *read_one_ent(const char *which,\n>         memcpy(ce->name, path, namelen);\n>         ce->ce_flags = create_ce_flags(stage);\n>         ce->ce_namelen = namelen;\n> +       mode = canon_mode(mode);\n>         ce->ce_mode = create_ce_mode(mode);\n>         return ce;\n>  }\n> diff --git a/merge-ort.c b/merge-ort.c\n> index ea20bbe2af3..d1e8a2823e0 100644\n> --- a/merge-ort.c\n> +++ b/merge-ort.c\n> @@ -502,7 +502,7 @@ static void setup_path_info(struct merge_options *opt,\n>         mi->basename_offset = current_dir_name_len;\n>         mi->clean = !!resolved;\n>         if (resolved) {\n> -               mi->result.mode = merged_version->raw_mode;\n> +               mi->result.mode = canon_mode(merged_version->raw_mode);\n\ncanon_mode() will change a mode of 0 into S_IFGITLINK, so this is\nwrong.  It's wrong in a way that probably doesn't matter, since\nis_null will be true and that is checked in preference to mode == 0\nelsewhere, but the code should still be more careful.\n\n>                 oidcpy(&mi->result.oid, &merged_version->oid);\n>                 mi->is_null = !!is_null;\n>         } else {\n> @@ -512,6 +512,16 @@ static void setup_path_info(struct merge_options *opt,\n>                 ASSIGN_AND_VERIFY_CI(ci, mi);\n>                 for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {\n>                         ci->pathnames[i] = fullpath;\n> +                       /*\n> +                        * We must not use canon_mode() here. Will\n> +                        * fail on an the is_null assertion in\n> +                        * 6a02dd90c99 (merge-ort: add a preliminary\n> +                        * simple process_entries() implementation,\n> +                        * 2020-12-13) when combined with the tests in\n> +                        * \"[PATCH 00/11] Complete merge-ort\n> +                        * implementation...almost\" (see\n> +                        * https://lore.kernel.org/git/pull.973.git.git.1614905738.gitgitgadget@gmail.com/)\n> +                        */\n>                         ci->stages[i].mode = names[i].raw_mode;\n\nI suspect the mapping of 0 to S_IFGITLINK might be the culprit here;\nwe're just lucky an assertion happened to catch this one.  (And\nunlucky that nothing caught the one above.)\n\nIf you added a special check for that case and used canon_mode()\notherwise, I still think it'd be problematic, because the *only* value\nof using raw_mode in the first place was that one special corner case\nFIXME comment in handle_content_merge() -- that FIXME could only be\nhandled by allowing a wider range of modes, and if you canon_mode()\nhere, then handle_content_merge() only gets canonicalized modes.  At\nthat point, we might as well ask for special API from traverse_trees()\nto just canonicalize for us (which, actually, sounds enticing).\n\n>                         oidcpy(&ci->stages[i].oid, &names[i].oid);\n>                 }\n> @@ -546,6 +556,7 @@ static void add_pair(struct merge_options *opt,\n>         int names_idx = is_add ? side : 0;\n>         const struct object_id *oid = &names[names_idx].oid;\n>         unsigned int mode = names[names_idx].raw_mode;\n> +       mode = canon_mode(mode);\n\nThis is unnecessary; diffcore-rename.c only cares whether S_ISREG(mode).\n\n\nGiven that these are the only changes you made to merge-ort.c, and\nthis is the last patch in the series, my earlier comments and worries\nabout merge-ort from a few patches ago all look like justified\nconcerns and make me pretty sure you've introduced some regressions\nrelating to those.  (Namely, the fact that things comparing modes now\nhave to worry not about equality, but equality under\ncanonicalization.)\n\n>         one = alloc_filespec(pathname);\n>         two = alloc_filespec(pathname);\n> diff --git a/notes.c b/notes.c\n> index 2817325651a..78b1b38d36b 100644\n> --- a/notes.c\n> +++ b/notes.c\n> @@ -479,6 +479,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n>                         const char *q = oid_to_hex(&subtree->key_oid);\n>                         size_t i;\n>                         unsigned int mode = entry.raw_mode;\n> +                       mode = canon_mode(mode);\n>                         for (i = 0; i < prefix_len; i++) {\n>                                 strbuf_addch(&non_note_path, *q++);\n>                                 strbuf_addch(&non_note_path, *q++);\n> diff --git a/tree-walk.c b/tree-walk.c\n> index 099a9b3bd77..3175430d049 100644\n> --- a/tree-walk.c\n> +++ b/tree-walk.c\n> @@ -47,7 +47,6 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l\n>\n>         /* Initialize the descriptor entry */\n>         desc->entry.path = path;\n> -       mode = canon_mode(mode);\n>         desc->entry.raw_mode = mode;\n>         desc->entry.object_type = object_type(mode);\n>         desc->entry.pathlen = len - 1;\n> diff --git a/unpack-trees.c b/unpack-trees.c\n> index dcdf8130745..2fb346714b3 100644\n> --- a/unpack-trees.c\n> +++ b/unpack-trees.c\n> @@ -868,6 +868,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n>         newinfo.name = p->path;\n>         newinfo.namelen = p->pathlen;\n>         newinfo.mode = p->raw_mode;\n> +       newinfo.mode = canon_mode(newinfo.mode);\n>         newinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1);\n>         newinfo.df_conflicts |= df_conflicts;\n>\n> @@ -1020,7 +1021,8 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,\n>                 is_transient ?\n>                 make_empty_transient_cache_entry(len) :\n>                 make_empty_cache_entry(istate, len);\n> -       unsigned int mode = n->raw_mode;\n> +       unsigned int mode = canon_mode(n->raw_mode);\n> +       mode = canon_mode(mode);\n\nIt looks like nearly everything in unpack_trees() goes through\ncreate_ce_entry() so perhaps just these two hunks are good enough to\nmake sure unpack-trees.c and all its callers are dealing with\ncanonicalized modes.\n\nExcept now merge-recursive.c is some weird hybrid were some of its\nmodes come from unpack-trees (canonicalized), some come diff_tree_oid\n(non-canonicalized as far as I can tell above) and various calls to\nget_tree_entry_* (also non-canonicalized).  I suspect there are\nvarious bugs there, similar in nature to the ones in merge-ort but\nmuch harder to stamp out given the facts that merge-recursive gets\nmodes from many more places, and the fact that merge-recursive tends\nto do many more comparisons due to checking for each type of\ncombination of conflicts and having special code for each one\nresulting in combinatorial increasing number of nearly-duplicated\ncodepaths.  merge-recursive is probably a caller that really needs a\nway to request that all calls it makes (directly or indirectly) to any\ntree walking just return canonicalized modes.\n\n>\n>         ce->ce_mode = create_ce_mode(mode);\n>         ce->ce_flags = create_ce_flags(stage);\n> --\n\nThe fact that there are no canon_mode() calls in the diff machinery\nmakes me wonder if we've changed the behavior for diff/log whenever\nthere is an object with old modes around.  Perhaps that's a desired\nchange, but it seems like it should certainly be tested and\ndocumented.  What about all callers of the diff machinery, though?\nAre they prepared for such diffs?  One simple example is that\nfast-export uses the revision walking and diff machinery, and without\ncanonicalized modes anymore, it would just print the raw modes.\nfast-import sanely won't accept raw modes; it only wants canonical\nmodes.  So with this change, people may no longer be able to\nround-trip fast-export output through fast-import on their existing\nrepositories.\n\nI also don't like the fact that there is no canonicalization of modes\nbefore writing objects to the git object store.  I believe mktree,\nmerge-ort, notes (unless your one change to that file is enough but\nI'm suspect that it is) are all affected.  match-trees appears to be\naffected, but is only called by merge-ort and merge-recursive to\ncreate a temporary merge-base, though the \"temporary\" tree object will\ncontinue to live in the git object store and could be accessed by\nusers.  cache-tree.c also writes trees out, but only using ce_mode\nwhich looks like it is everywhere set by calls to e.g.\ncreate_ce_mode(...).  fast-import also writes direct tree objects, but\nit doesn't get its modes from reading tree objects, and sanitizes the\ninputs it does get.  So, perhaps most tree writers are still sane, but\nI'm certain merge-ort would need updates and I suspect mktree and\nnotes do too.  I think match-trees ought to be updated just for\ncleanliness.  And I may have missed other places that directly write\ntrees out.\n\nI'm worried there's a number of other regressions lurking; those are\njust ones I thought about in areas of the code I'm more familiar with.\n"},{"id":"418639","messageId":"CABPp-BGqcG7Hpz1=Kf5wNMsTTdxVccGyRVmq5ShYOMG0phcqDg@mail.gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"Re: [PATCH 00/30] tree-walk: mostly \"mode\" to \"enum object_type\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-09T20:41:41Z","receivedAt":"2021-03-09T20:43:00Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> This large series goes on top of my 6 patch series for\n> read_tree_recursive() as this one further refactors that function. See\n> https://lore.kernel.org/git/20210308022138.28166-1-avarab@gmail.com/\n> for that series.\n>\n> I noticed that since 2014 or so we haven't been doing the fsck checks\n> for bad file modes in trees. This series fixes that. I plan to add\n> tests etc. for that in another follow-up series.\n>\n> I wanted to get this out for review sooner than later, particularly\n> since the fsck testing will probably get me down another refactoring\n> path (fsck testing in general in this area is pretty bad...).\n>\n> As noted in 30/30 it would have been way easier to simply do an\n> isolated fix for that bug by introducing some fsck-specific API for\n> raw tree reading.\n>\n> But I thought the bug was symptomatic of a wider problem in our\n> codebase. Namely that we pass around the tree's mode *a lot*.\n>\n> But almost everything that then deals with the mode doesn't per-se\n> care about the mode bits in the tree, but using them to map that mode\n> to a tree entry for one of of OBJ_{BLOB,TREE,COMMIT}.\n>\n> So this is a large refactoring of all users of the widely used\n> tree-walk.h API to \"enum obj2ect_type\", finally in 29/30 I rename the\n> field to a scary \"raw_mode\".\n>\n> At that point we have just ~30-50 grep hits left for \"raw_mode\" in the\n> codebase (depending on whether we count names in function parameters).\n>\n> Hopefully being in that state alleviates e.g. Elijah's concerns\n> expressed in\n> https://lore.kernel.org/git/CABPp-BEdu1PqV5W=FuL0f08iFhGzvzV8oSUybNj4eF0aAwTnAw@mail.gmail.com/\n> I agree that doing the equivalent of 30/30 on top of master would be\n> way too scary, but once we're at 29/30 I think it's sane.\n\nIt's partially less scary (good cleanups that help make things\nclearer, your comment about the code having been in a similar state\nonce upon a time in patch 30), but in some ways even more scary (after\nreading through 30/30 and readily noticing a few missing areas and\nstarting to dig and finding several more).\n\n>\n> I tested this in combination with his on-list series to add more\n> merge-ort testing:\n> https://lore.kernel.org/git/pull.973.git.git.1614905738.gitgitgadget@gmail.com/\n>\n> I found a regression I'd caused in the merge-ort.c code with those\n> tests, fixed here. See the comment in merge-ort.c in 30/30.\n\nI've read through the whole series now.  It is nicely structured, and\nhas lots of good cleanups.  Unfortunately, there are also some clear\nregressions noted in my comments on both patches 6 and 30.\n\nI'm particularly worried with patch 30's basic plan; I think it'd be\nfar safer to have the tree-walking default to returning canonicalized\nmodes but allowing callers to request it be off.  I think each caller\nis going to need someone to audit the particular path for whether it\ncan be safely switched over to using raw modes on a case-by-case basis\nand with the introduction of new tests.  Some callers probably aren't\nworth the effort (e.g. merge-recursive).  Others might be, but require\na fair amount of work or other trade-offs.  I'm split about whether\nmerge-ort should consider it.  Using raw_modes might allow me to fix\none funny corner case issue in merge-ort that to my knowledge no user\nhas ever hit in practice, but I'm not sure fixing that testcase is\nworth it.  To fix it, we'd also have to allow writing tree objects\nwith non-canonicalized modes to the object store (for a \"temporary\"\ntree defining the virtual merge-base); that means new objects with\n\"broken\"/\"non-canonicalized\" modes being written that users can\naccess.\n\n> Ævar Arnfjörð Bjarmason (30):\n>   diff.c: remove redundant canon_mode() call\n>   notes & match-trees: use name_entry's \"pathlen\" member\n>   cache.h: add a comment to object_type()\n>   tree-walk.h: add object_type member to name_entry\n>   tree-walk.c: migrate to using new \"object_type\" field when possible\n>   cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n>   tree-walk.h users: switch object_type(...) to new .object_type\n>   tree.h: format argument lists of read_tree_recursive() users\n>   tree.h users: format argument lists in archive.c\n>   archive: get rid of 'stage' parameter\n>   tree.h API: make read_tree_fn_t take an \"enum object_type\"\n>   tree-walk.h users: migrate \"p->mode &&\" pattern\n>   tree-walk.h users: refactor chained \"mode\" if/else into switch\n>   tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n>   merge-tree tests: test for the mode comparison in same_entry()\n>   merge-ort: correct reference to test in 62fdec17a11\n>   fsck.c: switch on \"object_type\" in fsck_walk_tree()\n>   tree-walk.h users: use temporary variable(s) for \"mode\"\n>   tree-walk.h API: formatting changes for subsequent commit\n>   tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n>   tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n>   tree-walk.h API: Add get_tree_entry_type()\n>   tree-walk.h API: add a get_tree_entry_path() function\n>   tree-walk.h API: document and format tree_entry_extract()\n>   tree-entry.h API: rename tree_entry_extract() to\n>     tree_entry_extract_mode()\n>   tree-walk.h API: add a tree_entry_extract_all() function\n>   tree-walk.h API: add a tree_entry_extract_type() function\n>   tree-walk.h API users: rename \"struct name_entry\"'s \"mode\" to\n>     \"raw_mode\"\n>   tree.h API users: rename read_tree_fn_t's \"mode\" to \"raw_mode\"\n>   tree-walk.h API: move canon_mode() back out of decode_tree_entry()\n>\n>  archive.c              | 51 +++++++++++++-----------\n>  blame.c                |  9 +++--\n>  builtin/checkout.c     |  7 +++-\n>  builtin/fast-import.c  |  8 ++--\n>  builtin/grep.c         |  6 +--\n>  builtin/log.c          |  7 ++--\n>  builtin/ls-files.c     | 13 +++---\n>  builtin/ls-tree.c      | 18 ++++-----\n>  builtin/merge-tree.c   | 32 +++++++++------\n>  builtin/mktree.c       |  4 +-\n>  builtin/pack-objects.c |  6 +--\n>  builtin/reflog.c       |  3 +-\n>  builtin/rm.c           |  2 +-\n>  builtin/update-index.c |  7 +++-\n>  cache-tree.c           |  2 +-\n>  cache.h                | 11 ++++--\n>  combine-diff.c         |  8 ++--\n>  delta-islands.c        |  2 +-\n>  diff.c                 |  2 +-\n>  fsck.c                 | 23 +++++------\n>  http-push.c            |  6 ++-\n>  line-log.c             |  2 +-\n>  list-objects.c         | 20 +++++++---\n>  match-trees.c          | 52 ++++++++++++------------\n>  merge-ort.c            | 34 ++++++++++------\n>  merge-recursive.c      | 33 ++++++++--------\n>  notes.c                | 15 +++----\n>  object-name.c          |  7 ++--\n>  pack-bitmap-write.c    |  8 ++--\n>  read-cache.c           | 16 ++++----\n>  revision.c             | 12 ++++--\n>  t/t4300-merge-tree.sh  | 44 +++++++++++++++++++++\n>  tree-diff.c            | 44 ++++++++++++---------\n>  tree-walk.c            | 89 +++++++++++++++++++++++++++++++-----------\n>  tree-walk.h            | 67 ++++++++++++++++++++++++++-----\n>  tree.c                 | 19 +++++----\n>  tree.h                 |  5 ++-\n>  unpack-trees.c         | 30 ++++++++------\n>  walker.c               | 22 ++++++-----\n>  39 files changed, 482 insertions(+), 264 deletions(-)\n>\n> --\n> 2.31.0.rc0.126.g04f22c5b82\n>\n"},{"id":"418656","messageId":"87blbshucn.fsf@evledraar.gmail.com","threadId":"18927","inReplyTo":"CABPp-BGqcG7Hpz1=Kf5wNMsTTdxVccGyRVmq5ShYOMG0phcqDg@mail.gmail.com","subject":"Re: [PATCH 00/30] tree-walk: mostly \"mode\" to \"enum object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-09T21:48:08Z","receivedAt":"2021-03-09T21:48:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Mar 09 2021, Elijah Newren wrote:\n\n> On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>>\n>> This large series goes on top of my 6 patch series for\n>> read_tree_recursive() as this one further refactors that function. See\n>> https://lore.kernel.org/git/20210308022138.28166-1-avarab@gmail.com/\n>> for that series.\n>>\n>> I noticed that since 2014 or so we haven't been doing the fsck checks\n>> for bad file modes in trees. This series fixes that. I plan to add\n>> tests etc. for that in another follow-up series.\n>>\n>> I wanted to get this out for review sooner than later, particularly\n>> since the fsck testing will probably get me down another refactoring\n>> path (fsck testing in general in this area is pretty bad...).\n>>\n>> As noted in 30/30 it would have been way easier to simply do an\n>> isolated fix for that bug by introducing some fsck-specific API for\n>> raw tree reading.\n>>\n>> But I thought the bug was symptomatic of a wider problem in our\n>> codebase. Namely that we pass around the tree's mode *a lot*.\n>>\n>> But almost everything that then deals with the mode doesn't per-se\n>> care about the mode bits in the tree, but using them to map that mode\n>> to a tree entry for one of of OBJ_{BLOB,TREE,COMMIT}.\n>>\n>> So this is a large refactoring of all users of the widely used\n>> tree-walk.h API to \"enum obj2ect_type\", finally in 29/30 I rename the\n>> field to a scary \"raw_mode\".\n>>\n>> At that point we have just ~30-50 grep hits left for \"raw_mode\" in the\n>> codebase (depending on whether we count names in function parameters).\n>>\n>> Hopefully being in that state alleviates e.g. Elijah's concerns\n>> expressed in\n>> https://lore.kernel.org/git/CABPp-BEdu1PqV5W=FuL0f08iFhGzvzV8oSUybNj4eF0aAwTnAw@mail.gmail.com/\n>> I agree that doing the equivalent of 30/30 on top of master would be\n>> way too scary, but once we're at 29/30 I think it's sane.\n>\n> It's partially less scary (good cleanups that help make things\n> clearer, your comment about the code having been in a similar state\n> once upon a time in patch 30), but in some ways even more scary (after\n> reading through 30/30 and readily noticing a few missing areas and\n> starting to dig and finding several more).\n\nThanks a lot for your reviews. I'm going to let this sit for a fair bit\nlonger before any re-roll, especially with the rc period, other eyeballs\non this most welcome though :)\n\n>>\n>> I tested this in combination with his on-list series to add more\n>> merge-ort testing:\n>> https://lore.kernel.org/git/pull.973.git.git.1614905738.gitgitgadget@gmail.com/\n>>\n>> I found a regression I'd caused in the merge-ort.c code with those\n>> tests, fixed here. See the comment in merge-ort.c in 30/30.\n>\n> I've read through the whole series now.  It is nicely structured, and\n> has lots of good cleanups.  Unfortunately, there are also some clear\n> regressions noted in my comments on both patches 6 and 30.\n>\n> I'm particularly worried with patch 30's basic plan; I think it'd be\n> far safer to have the tree-walking default to returning canonicalized\n> modes but allowing callers to request it be off.  I think each caller\n> is going to need someone to audit the particular path for whether it\n> can be safely switched over to using raw modes on a case-by-case basis\n> and with the introduction of new tests.  Some callers probably aren't\n> worth the effort (e.g. merge-recursive).  Others might be, but require\n> a fair amount of work or other trade-offs.  I'm split about whether\n> merge-ort should consider it.  Using raw_modes might allow me to fix\n> one funny corner case issue in merge-ort that to my knowledge no user\n> has ever hit in practice, but I'm not sure fixing that testcase is\n> worth it.  To fix it, we'd also have to allow writing tree objects\n> with non-canonicalized modes to the object store (for a \"temporary\"\n> tree defining the virtual merge-base); that means new objects with\n> \"broken\"/\"non-canonicalized\" modes being written that users can\n> access.\n\nTo add a bit to your worries, I think your \"[...]does lower my worry\nsome[...]\" in 30/30 is unfortunately based on some unintentional lying\non my part.\n\nI.e. my \"yes our test coverage sucks, but[...]\" was based on some\nmisreading of the history. Here's a correction:\n\n7146e66f086 didn't break the fsck check in mid-2014, it had been broken\nfor much longer. See Jeff King's late-2014 E-mail about it here (which\nI've just now discovered):\nhttps://lore.kernel.org/git/20140923154751.GA19319@peff.net/#t\n\nBasically, decode_tree_entry() didn't sanitize the mode, but we did that\nin the tree_entry_extract() function, which is what fsck.c was using all\nalong, so it was always getting pre-sanitized modes.\n\nBut I think I was mostly right, somewhat by accident, AFAICT this was\nthe state of things back then:\n    \n    $ git grep '\\b(init_tree_desc|fill_tree_descriptor|update_tree_entry|update_tree_entry|tree_entry)\\(' -- '*.c' | wc -l\n    78\n    $ git grep '\\b(get_tree_entry|tree_entry_extract)\\(' -- '*.c' | wc -l\n    25\n\nThose were the API functions that gave you the un-canonical and\ncanonical mode, respectively.\n\nBut yes, I agree that it's probably a bit too scary.\n\nWhere I was mainly trying to get to with 30/30 was that for any future\ncode we'd more carefully review this whole \"raw_mode\" thing just because\nof its name.\n\nSo what do you think about a version of 30/30 where existing API users\nimmediately call canon_mode() upon calling the current API functions?\n\nIt would be ugly and verbose now, but the benefit would be that we'd eye\nany future change that deals with this \"raw_mode\" with more suspicion,\nand likely convert it to use the object_type instead.\n\nOr I could just back out of this whole 29-30 step and just add a \"bare\"\nAPI for fsck in particular.\n\nI'm partial to renaming it to *something* just to make it more grep-able\nthough, we have a \"mode\" in all sorts of structs all over the place...\n\n>> Ævar Arnfjörð Bjarmason (30):\n>>   diff.c: remove redundant canon_mode() call\n>>   notes & match-trees: use name_entry's \"pathlen\" member\n>>   cache.h: add a comment to object_type()\n>>   tree-walk.h: add object_type member to name_entry\n>>   tree-walk.c: migrate to using new \"object_type\" field when possible\n>>   cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n>>   tree-walk.h users: switch object_type(...) to new .object_type\n>>   tree.h: format argument lists of read_tree_recursive() users\n>>   tree.h users: format argument lists in archive.c\n>>   archive: get rid of 'stage' parameter\n>>   tree.h API: make read_tree_fn_t take an \"enum object_type\"\n>>   tree-walk.h users: migrate \"p->mode &&\" pattern\n>>   tree-walk.h users: refactor chained \"mode\" if/else into switch\n>>   tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n>>   merge-tree tests: test for the mode comparison in same_entry()\n>>   merge-ort: correct reference to test in 62fdec17a11\n>>   fsck.c: switch on \"object_type\" in fsck_walk_tree()\n>>   tree-walk.h users: use temporary variable(s) for \"mode\"\n>>   tree-walk.h API: formatting changes for subsequent commit\n>>   tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n>>   tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n>>   tree-walk.h API: Add get_tree_entry_type()\n>>   tree-walk.h API: add a get_tree_entry_path() function\n>>   tree-walk.h API: document and format tree_entry_extract()\n>>   tree-entry.h API: rename tree_entry_extract() to\n>>     tree_entry_extract_mode()\n>>   tree-walk.h API: add a tree_entry_extract_all() function\n>>   tree-walk.h API: add a tree_entry_extract_type() function\n>>   tree-walk.h API users: rename \"struct name_entry\"'s \"mode\" to\n>>     \"raw_mode\"\n>>   tree.h API users: rename read_tree_fn_t's \"mode\" to \"raw_mode\"\n>>   tree-walk.h API: move canon_mode() back out of decode_tree_entry()\n>>\n>>  archive.c              | 51 +++++++++++++-----------\n>>  blame.c                |  9 +++--\n>>  builtin/checkout.c     |  7 +++-\n>>  builtin/fast-import.c  |  8 ++--\n>>  builtin/grep.c         |  6 +--\n>>  builtin/log.c          |  7 ++--\n>>  builtin/ls-files.c     | 13 +++---\n>>  builtin/ls-tree.c      | 18 ++++-----\n>>  builtin/merge-tree.c   | 32 +++++++++------\n>>  builtin/mktree.c       |  4 +-\n>>  builtin/pack-objects.c |  6 +--\n>>  builtin/reflog.c       |  3 +-\n>>  builtin/rm.c           |  2 +-\n>>  builtin/update-index.c |  7 +++-\n>>  cache-tree.c           |  2 +-\n>>  cache.h                | 11 ++++--\n>>  combine-diff.c         |  8 ++--\n>>  delta-islands.c        |  2 +-\n>>  diff.c                 |  2 +-\n>>  fsck.c                 | 23 +++++------\n>>  http-push.c            |  6 ++-\n>>  line-log.c             |  2 +-\n>>  list-objects.c         | 20 +++++++---\n>>  match-trees.c          | 52 ++++++++++++------------\n>>  merge-ort.c            | 34 ++++++++++------\n>>  merge-recursive.c      | 33 ++++++++--------\n>>  notes.c                | 15 +++----\n>>  object-name.c          |  7 ++--\n>>  pack-bitmap-write.c    |  8 ++--\n>>  read-cache.c           | 16 ++++----\n>>  revision.c             | 12 ++++--\n>>  t/t4300-merge-tree.sh  | 44 +++++++++++++++++++++\n>>  tree-diff.c            | 44 ++++++++++++---------\n>>  tree-walk.c            | 89 +++++++++++++++++++++++++++++++-----------\n>>  tree-walk.h            | 67 ++++++++++++++++++++++++++-----\n>>  tree.c                 | 19 +++++----\n>>  tree.h                 |  5 ++-\n>>  unpack-trees.c         | 30 ++++++++------\n>>  walker.c               | 22 ++++++-----\n>>  39 files changed, 482 insertions(+), 264 deletions(-)\n>>\n>> --\n>> 2.31.0.rc0.126.g04f22c5b82\n>>\n\n"},{"id":"418932","messageId":"CABPp-BH1HChQjAri3Agt0Lo1qrXwb5Dj45LgmL6SakEmdvj1kA@mail.gmail.com","threadId":"18927","inReplyTo":"87blbshucn.fsf@evledraar.gmail.com","subject":"Re: [PATCH 00/30] tree-walk: mostly \"mode\" to \"enum object_type\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-12T06:44:50Z","receivedAt":"2021-03-12T06:45:39Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Mar 9, 2021 at 1:48 PM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n> On Tue, Mar 09 2021, Elijah Newren wrote:\n>\n> > On Mon, Mar 8, 2021 at 7:07 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n> >>\n> >> This large series goes on top of my 6 patch series for\n> >> read_tree_recursive() as this one further refactors that function. See\n> >> https://lore.kernel.org/git/20210308022138.28166-1-avarab@gmail.com/\n> >> for that series.\n> >>\n> >> I noticed that since 2014 or so we haven't been doing the fsck checks\n> >> for bad file modes in trees. This series fixes that. I plan to add\n> >> tests etc. for that in another follow-up series.\n> >>\n> >> I wanted to get this out for review sooner than later, particularly\n> >> since the fsck testing will probably get me down another refactoring\n> >> path (fsck testing in general in this area is pretty bad...).\n> >>\n> >> As noted in 30/30 it would have been way easier to simply do an\n> >> isolated fix for that bug by introducing some fsck-specific API for\n> >> raw tree reading.\n> >>\n> >> But I thought the bug was symptomatic of a wider problem in our\n> >> codebase. Namely that we pass around the tree's mode *a lot*.\n> >>\n> >> But almost everything that then deals with the mode doesn't per-se\n> >> care about the mode bits in the tree, but using them to map that mode\n> >> to a tree entry for one of of OBJ_{BLOB,TREE,COMMIT}.\n> >>\n> >> So this is a large refactoring of all users of the widely used\n> >> tree-walk.h API to \"enum obj2ect_type\", finally in 29/30 I rename the\n> >> field to a scary \"raw_mode\".\n> >>\n> >> At that point we have just ~30-50 grep hits left for \"raw_mode\" in the\n> >> codebase (depending on whether we count names in function parameters).\n> >>\n> >> Hopefully being in that state alleviates e.g. Elijah's concerns\n> >> expressed in\n> >> https://lore.kernel.org/git/CABPp-BEdu1PqV5W=FuL0f08iFhGzvzV8oSUybNj4eF0aAwTnAw@mail.gmail.com/\n> >> I agree that doing the equivalent of 30/30 on top of master would be\n> >> way too scary, but once we're at 29/30 I think it's sane.\n> >\n> > It's partially less scary (good cleanups that help make things\n> > clearer, your comment about the code having been in a similar state\n> > once upon a time in patch 30), but in some ways even more scary (after\n> > reading through 30/30 and readily noticing a few missing areas and\n> > starting to dig and finding several more).\n>\n> Thanks a lot for your reviews. I'm going to let this sit for a fair bit\n> longer before any re-roll, especially with the rc period, other eyeballs\n> on this most welcome though :)\n>\n> >>\n> >> I tested this in combination with his on-list series to add more\n> >> merge-ort testing:\n> >> https://lore.kernel.org/git/pull.973.git.git.1614905738.gitgitgadget@gmail.com/\n> >>\n> >> I found a regression I'd caused in the merge-ort.c code with those\n> >> tests, fixed here. See the comment in merge-ort.c in 30/30.\n> >\n> > I've read through the whole series now.  It is nicely structured, and\n> > has lots of good cleanups.  Unfortunately, there are also some clear\n> > regressions noted in my comments on both patches 6 and 30.\n> >\n> > I'm particularly worried with patch 30's basic plan; I think it'd be\n> > far safer to have the tree-walking default to returning canonicalized\n> > modes but allowing callers to request it be off.  I think each caller\n> > is going to need someone to audit the particular path for whether it\n> > can be safely switched over to using raw modes on a case-by-case basis\n> > and with the introduction of new tests.  Some callers probably aren't\n> > worth the effort (e.g. merge-recursive).  Others might be, but require\n> > a fair amount of work or other trade-offs.  I'm split about whether\n> > merge-ort should consider it.  Using raw_modes might allow me to fix\n> > one funny corner case issue in merge-ort that to my knowledge no user\n> > has ever hit in practice, but I'm not sure fixing that testcase is\n> > worth it.  To fix it, we'd also have to allow writing tree objects\n> > with non-canonicalized modes to the object store (for a \"temporary\"\n> > tree defining the virtual merge-base); that means new objects with\n> > \"broken\"/\"non-canonicalized\" modes being written that users can\n> > access.\n>\n> To add a bit to your worries, I think your \"[...]does lower my worry\n> some[...]\" in 30/30 is unfortunately based on some unintentional lying\n> on my part.\n>\n> I.e. my \"yes our test coverage sucks, but[...]\" was based on some\n> misreading of the history. Here's a correction:\n>\n> 7146e66f086 didn't break the fsck check in mid-2014, it had been broken\n> for much longer. See Jeff King's late-2014 E-mail about it here (which\n> I've just now discovered):\n> https://lore.kernel.org/git/20140923154751.GA19319@peff.net/#t\n>\n> Basically, decode_tree_entry() didn't sanitize the mode, but we did that\n> in the tree_entry_extract() function, which is what fsck.c was using all\n> along, so it was always getting pre-sanitized modes.\n>\n> But I think I was mostly right, somewhat by accident, AFAICT this was\n> the state of things back then:\n>\n>     $ git grep '\\b(init_tree_desc|fill_tree_descriptor|update_tree_entry|update_tree_entry|tree_entry)\\(' -- '*.c' | wc -l\n>     78\n>     $ git grep '\\b(get_tree_entry|tree_entry_extract)\\(' -- '*.c' | wc -l\n>     25\n>\n> Those were the API functions that gave you the un-canonical and\n> canonical mode, respectively.\n>\n> But yes, I agree that it's probably a bit too scary.\n>\n> Where I was mainly trying to get to with 30/30 was that for any future\n> code we'd more carefully review this whole \"raw_mode\" thing just because\n> of its name.\n>\n> So what do you think about a version of 30/30 where existing API users\n> immediately call canon_mode() upon calling the current API functions?\n>\n> It would be ugly and verbose now, but the benefit would be that we'd eye\n> any future change that deals with this \"raw_mode\" with more suspicion,\n> and likely convert it to use the object_type instead.\n\nI don't have any strong objections to this.  I'm less worried about\nugly and verbose now, than I am in missing cases while converting;\nit'd be easy to get some of the conversions wrong accidentally\n(similar to the mode->is_tree changes for fast-import in patch 06 of\nthis series or the original read_tree() removal patch before you fixed\nit up in v2), so I think it'd need to be done with some care.\n\n> Or I could just back out of this whole 29-30 step and just add a \"bare\"\n> API for fsck in particular.\n\nThat'd be fine as well, and certainly less concerning; perhaps it's\neven the first step.  But I don't think we have to do it this way,\njust that if we move towards raw_mode it needs to be done with a lot\nof caution.\n\n> I'm partial to renaming it to *something* just to make it more grep-able\n> though, we have a \"mode\" in all sorts of structs all over the place...\n>\n> >> Ævar Arnfjörð Bjarmason (30):\n> >>   diff.c: remove redundant canon_mode() call\n> >>   notes & match-trees: use name_entry's \"pathlen\" member\n> >>   cache.h: add a comment to object_type()\n> >>   tree-walk.h: add object_type member to name_entry\n> >>   tree-walk.c: migrate to using new \"object_type\" field when possible\n> >>   cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n> >>   tree-walk.h users: switch object_type(...) to new .object_type\n> >>   tree.h: format argument lists of read_tree_recursive() users\n> >>   tree.h users: format argument lists in archive.c\n> >>   archive: get rid of 'stage' parameter\n> >>   tree.h API: make read_tree_fn_t take an \"enum object_type\"\n> >>   tree-walk.h users: migrate \"p->mode &&\" pattern\n> >>   tree-walk.h users: refactor chained \"mode\" if/else into switch\n> >>   tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n> >>   merge-tree tests: test for the mode comparison in same_entry()\n> >>   merge-ort: correct reference to test in 62fdec17a11\n> >>   fsck.c: switch on \"object_type\" in fsck_walk_tree()\n> >>   tree-walk.h users: use temporary variable(s) for \"mode\"\n> >>   tree-walk.h API: formatting changes for subsequent commit\n> >>   tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n> >>   tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n> >>   tree-walk.h API: Add get_tree_entry_type()\n> >>   tree-walk.h API: add a get_tree_entry_path() function\n> >>   tree-walk.h API: document and format tree_entry_extract()\n> >>   tree-entry.h API: rename tree_entry_extract() to\n> >>     tree_entry_extract_mode()\n> >>   tree-walk.h API: add a tree_entry_extract_all() function\n> >>   tree-walk.h API: add a tree_entry_extract_type() function\n> >>   tree-walk.h API users: rename \"struct name_entry\"'s \"mode\" to\n> >>     \"raw_mode\"\n> >>   tree.h API users: rename read_tree_fn_t's \"mode\" to \"raw_mode\"\n> >>   tree-walk.h API: move canon_mode() back out of decode_tree_entry()\n> >>\n> >>  archive.c              | 51 +++++++++++++-----------\n> >>  blame.c                |  9 +++--\n> >>  builtin/checkout.c     |  7 +++-\n> >>  builtin/fast-import.c  |  8 ++--\n> >>  builtin/grep.c         |  6 +--\n> >>  builtin/log.c          |  7 ++--\n> >>  builtin/ls-files.c     | 13 +++---\n> >>  builtin/ls-tree.c      | 18 ++++-----\n> >>  builtin/merge-tree.c   | 32 +++++++++------\n> >>  builtin/mktree.c       |  4 +-\n> >>  builtin/pack-objects.c |  6 +--\n> >>  builtin/reflog.c       |  3 +-\n> >>  builtin/rm.c           |  2 +-\n> >>  builtin/update-index.c |  7 +++-\n> >>  cache-tree.c           |  2 +-\n> >>  cache.h                | 11 ++++--\n> >>  combine-diff.c         |  8 ++--\n> >>  delta-islands.c        |  2 +-\n> >>  diff.c                 |  2 +-\n> >>  fsck.c                 | 23 +++++------\n> >>  http-push.c            |  6 ++-\n> >>  line-log.c             |  2 +-\n> >>  list-objects.c         | 20 +++++++---\n> >>  match-trees.c          | 52 ++++++++++++------------\n> >>  merge-ort.c            | 34 ++++++++++------\n> >>  merge-recursive.c      | 33 ++++++++--------\n> >>  notes.c                | 15 +++----\n> >>  object-name.c          |  7 ++--\n> >>  pack-bitmap-write.c    |  8 ++--\n> >>  read-cache.c           | 16 ++++----\n> >>  revision.c             | 12 ++++--\n> >>  t/t4300-merge-tree.sh  | 44 +++++++++++++++++++++\n> >>  tree-diff.c            | 44 ++++++++++++---------\n> >>  tree-walk.c            | 89 +++++++++++++++++++++++++++++++-----------\n> >>  tree-walk.h            | 67 ++++++++++++++++++++++++++-----\n> >>  tree.c                 | 19 +++++----\n> >>  tree.h                 |  5 ++-\n> >>  unpack-trees.c         | 30 ++++++++------\n> >>  walker.c               | 22 ++++++-----\n> >>  39 files changed, 482 insertions(+), 264 deletions(-)\n> >>\n> >> --\n> >> 2.31.0.rc0.126.g04f22c5b82\n"},{"id":"419001","messageId":"xmqq8s6s6od9.fsf@gitster.g","threadId":"18927","inReplyTo":"20210308022138.28166-3-avarab@gmail.com","subject":"Re: [PATCH v2 2/6] tree.c API: move read_tree() into builtin/ls-files.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-12T21:41:54Z","receivedAt":"2021-03-12T21:43:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Now builtin/ls-files.c is the last user of this code, let's move all\n> the relevant code there. This allows for subsequent simplification of\n> it, and an eventual move to read_tree_recursive().\n\nThere is a comment in cache.h that has long been a tad stale but not\nwrong.  This finally makes it incorrect.\n\n#define ADD_CACHE_JUST_APPEND 8\t\t/* Append only; tree.c::read_tree() */\n\nIt would be sufficient to remove the mention of tree.c::read_tree().\nIt does use this bit, but there are a few others, and there is not\nmuch point in singling out the oldest user the bit was invented for.\n\n"},{"id":"419205","messageId":"20210315234344.28427-2-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH v3 1/9] ls-files tests: add meaningful --with-tree tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-15T23:43:36Z","receivedAt":"2021-03-15T23:44:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add tests for \"ls-files --with-tree\". There was effectively no\ncoverage for any normal usage of this command, only the tests added in\n54e1abce90e (Add test case for ls-files --with-tree, 2007-10-03) for\nan obscure bug.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3060-ls-files-with-tree.sh | 41 +++++++++++++++++++++++++++++++++++\n 1 file changed, 41 insertions(+)\n\ndiff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh\nindex 52ed665fcd2..b257c792a46 100755\n--- a/t/t3060-ls-files-with-tree.sh\n+++ b/t/t3060-ls-files-with-tree.sh\n@@ -47,6 +47,12 @@ test_expect_success setup '\n \tgit add .\n '\n \n+test_expect_success 'usage' '\n+\ttest_expect_code 128 git ls-files --with-tree=HEAD -u &&\n+\ttest_expect_code 128 git ls-files --with-tree=HEAD -s &&\n+\ttest_expect_code 128 git ls-files --recurse-submodules --with-tree=HEAD\n+'\n+\n test_expect_success 'git ls-files --with-tree should succeed from subdir' '\n \t# We have to run from a sub-directory to trigger prune_path\n \t# Then we finally get to run our --with-tree test\n@@ -60,4 +66,39 @@ test_expect_success \\\n     'git ls-files --with-tree should add entries from named tree.' \\\n     'test_cmp expected output'\n \n+test_expect_success 'no duplicates in --with-tree output' '\n+\tgit ls-files --with-tree=HEAD >actual &&\n+\tsort -u actual >expected &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'setup: output in a conflict' '\n+\ttest_create_repo conflict &&\n+\ttest_commit -C conflict BASE file &&\n+\ttest_commit -C conflict A file foo &&\n+\tgit -C conflict reset --hard BASE &&\n+\ttest_commit -C conflict B file bar\n+'\n+\n+test_expect_success 'output in a conflict' '\n+\ttest_must_fail git -C conflict merge A B &&\n+\tcat >expected <<-\\EOF &&\n+\tfile\n+\tfile\n+\tfile\n+\tfile\n+\tEOF\n+\tgit -C conflict ls-files --with-tree=HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'output with removed .git/index' '\n+\tcat >expected <<-\\EOF &&\n+\tfile\n+\tEOF\n+\trm conflict/.git/index &&\n+\tgit -C conflict ls-files --with-tree=HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419206","messageId":"20210315234344.28427-1-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH v3 0/9] read_tree() and read_tree_recursive() refactoring","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-15T23:43:35Z","receivedAt":"2021-03-15T23:44:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"A v3 of a refactoring of tree.c. See v2 at\nhttps://lore.kernel.org/git/20210308022138.28166-1-avarab@gmail.com/\n\nThis brings back pretty much the old read_tree_recursive() under the\nname read_tree_at() as suggested in\nhttps://lore.kernel.org/git/xmqqft106sok.fsf@gitster.g/\n\nÆvar Arnfjörð Bjarmason (9):\n  ls-files tests: add meaningful --with-tree tests\n  tree.c API: move read_tree() into builtin/ls-files.c\n  ls-files: don't needlessly pass around stage variable\n  ls-files: refactor away read_tree()\n  tree.h API: remove support for starting at prefix != \"\"\n  tree.h API: remove \"stage\" parameter from read_tree_recursive()\n  tree.h API: rename read_tree_recursive() to read_tree()\n  show tests: add test for \"git show <tree>\"\n  tree.h API: expose read_tree_1() as read_tree_at()\n\n archive.c                     |  19 +++---\n builtin/checkout.c            |   8 +--\n builtin/log.c                 |   8 +--\n builtin/ls-files.c            |  76 +++++++++++++++++++++-\n builtin/ls-tree.c             |   6 +-\n cache.h                       |   2 +-\n merge-recursive.c             |   6 +-\n t/t3060-ls-files-with-tree.sh |  41 ++++++++++++\n t/t7007-show.sh               |  39 ++++++++++++\n tree.c                        | 117 ++++------------------------------\n tree.h                        |  24 +++----\n 11 files changed, 205 insertions(+), 141 deletions(-)\n\nRange-diff:\n 1:  6416da0dee2 =  1:  b338f2c01a4 ls-files tests: add meaningful --with-tree tests\n 2:  765001b44cd !  2:  4578b83944c tree.c API: move read_tree() into builtin/ls-files.c\n    @@ builtin/ls-files.c: static int get_common_prefix_len(const char *common_prefix)\n       * Read the tree specified with --with-tree option\n       * (typically, HEAD) into stage #1 and then\n     \n    + ## cache.h ##\n    +@@ cache.h: static inline int index_pos_to_insert_pos(uintmax_t pos)\n    + #define ADD_CACHE_OK_TO_ADD 1\t\t/* Ok to add */\n    + #define ADD_CACHE_OK_TO_REPLACE 2\t/* Ok to replace file/directory */\n    + #define ADD_CACHE_SKIP_DFCHECK 4\t/* Ok to skip DF conflict checks */\n    +-#define ADD_CACHE_JUST_APPEND 8\t\t/* Append only; tree.c::read_tree() */\n    ++#define ADD_CACHE_JUST_APPEND 8\t\t/* Append only */\n    + #define ADD_CACHE_NEW_ONLY 16\t\t/* Do not replace existing ones */\n    + #define ADD_CACHE_KEEP_CACHE_TREE 32\t/* Do not invalidate cache-tree */\n    + #define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */\n    +\n      ## tree.c ##\n     @@\n      \n 3:  a71ffba7d04 =  3:  33656ff63b8 ls-files: don't needlessly pass around stage variable\n 4:  e78d1810b89 =  4:  1c96d5d3611 ls-files: refactor away read_tree()\n 5:  05eecdd7519 !  5:  367cb99224b tree.h API: remove support for starting at prefix != \"\"\n    @@ Commit message\n         ffd31f661d5 (Reimplement read_tree_recursive() using\n         tree_entry_interesting(), 2011-03-25).\n     \n    -    If in the future we need to support recursively reading trees without\n    -    starting at the root we can easily add a read_tree_recursive_subdir(),\n    -    and make that function a thin wrapper for read_tree_1().\n    +    As it turns out (Murphy's law and all) we're just about to gain a new\n    +    in-tree user that would need this parameter[1]. Let's remove it anyway\n    +    as the common case is going to be to not supply it, A later commit\n    +    will bring back this functionality in different form.\n     \n    -    In the meantime there's no reason to keep around what amounts to dead\n    -    code, just in case we need it in the future.\n    +    1. https://lore.kernel.org/git/xmqqft106sok.fsf@gitster.g/\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n 6:  fcecc82e1c8 =  6:  38e36780e22 tree.h API: remove \"stage\" parameter from read_tree_recursive()\n -:  ----------- >  7:  859902ffd83 tree.h API: rename read_tree_recursive() to read_tree()\n -:  ----------- >  8:  a63c9b49f13 show tests: add test for \"git show <tree>\"\n -:  ----------- >  9:  570642c8625 tree.h API: expose read_tree_1() as read_tree_at()\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419207","messageId":"20210315234344.28427-4-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH v3 3/9] ls-files: don't needlessly pass around stage variable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-15T23:43:38Z","receivedAt":"2021-03-15T23:44:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that read_tree() has been moved to ls-files.c we can get rid of\nthe stage != 1 case that'll never happen.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 17 ++++-------------\n 1 file changed, 4 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex a4458622813..74d572a3e4a 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -470,21 +470,12 @@ static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base\n }\n \n \n-static int read_tree(struct repository *r, struct tree *tree, int stage,\n+static int read_tree(struct repository *r, struct tree *tree,\n \t\t     struct pathspec *match, struct index_state *istate)\n {\n \tread_tree_fn_t fn = NULL;\n \tint i, err;\n \n-\t/*\n-\t * Currently the only existing callers of this function all\n-\t * call it with stage=1 and after making sure there is nothing\n-\t * at that stage; we could always use read_one_entry_quick().\n-\t *\n-\t * But when we decide to straighten out git-read-tree not to\n-\t * use unpack_trees() in some cases, this will probably start\n-\t * to matter.\n-\t */\n \n \t/*\n \t * See if we have cache entry at the stage.  If so,\n@@ -493,13 +484,13 @@ static int read_tree(struct repository *r, struct tree *tree, int stage,\n \t */\n \tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n \t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == stage)\n+\t\tif (ce_stage(ce) == 1)\n \t\t\tfn = read_one_entry;\n \t}\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n+\terr = read_tree_recursive(r, tree, \"\", 0, 1, match, fn, istate);\n \tif (fn == read_one_entry || err)\n \t\treturn err;\n \n@@ -549,7 +540,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n \t} else\n \t\tmemset(&pathspec, 0, sizeof(pathspec));\n-\tif (read_tree(the_repository, tree, 1, &pathspec, istate))\n+\tif (read_tree(the_repository, tree, &pathspec, istate))\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \n \tfor (i = 0; i < istate->cache_nr; i++) {\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419208","messageId":"20210315234344.28427-5-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH v3 4/9] ls-files: refactor away read_tree()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-15T23:43:39Z","receivedAt":"2021-03-15T23:44:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor away the read_tree() function into its only user,\noverlay_tree_on_index().\n\nFirst, change read_one_entry_opt() to use the strbuf parameter\nread_tree_recursive() passes down in place. This finishes up a partial\nrefactoring started in 6a0b0b6de99 (tree.c: update read_tree_recursive\ncallback to pass strbuf as base, 2014-11-30).\n\nMoving the rest into overlay_tree_on_index() makes this index juggling\nwe're doing easier to read.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 74 +++++++++++++++++++++-------------------------\n 1 file changed, 33 insertions(+), 41 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 74d572a3e4a..db53e2c8e6d 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -423,7 +423,7 @@ static int get_common_prefix_len(const char *common_prefix)\n \n static int read_one_entry_opt(struct index_state *istate,\n \t\t\t      const struct object_id *oid,\n-\t\t\t      const char *base, int baselen,\n+\t\t\t      struct strbuf *base,\n \t\t\t      const char *pathname,\n \t\t\t      unsigned mode, int stage, int opt)\n {\n@@ -434,13 +434,13 @@ static int read_one_entry_opt(struct index_state *istate,\n \t\treturn READ_TREE_RECURSIVE;\n \n \tlen = strlen(pathname);\n-\tce = make_empty_cache_entry(istate, baselen + len);\n+\tce = make_empty_cache_entry(istate, base->len + len);\n \n \tce->ce_mode = create_ce_mode(mode);\n \tce->ce_flags = create_ce_flags(stage);\n-\tce->ce_namelen = baselen + len;\n-\tmemcpy(ce->name, base, baselen);\n-\tmemcpy(ce->name + baselen, pathname, len+1);\n+\tce->ce_namelen = base->len + len;\n+\tmemcpy(ce->name, base->buf, base->len);\n+\tmemcpy(ce->name + base->len, pathname, len+1);\n \toidcpy(&ce->oid, oid);\n \treturn add_index_entry(istate, ce, opt);\n }\n@@ -450,7 +450,7 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n \t\t\t  void *context)\n {\n \tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\treturn read_one_entry_opt(istate, oid, base, pathname,\n \t\t\t\t  mode, stage,\n \t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n }\n@@ -464,44 +464,11 @@ static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base\n \t\t\t\tvoid *context)\n {\n \tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\treturn read_one_entry_opt(istate, oid, base, pathname,\n \t\t\t\t  mode, stage,\n \t\t\t\t  ADD_CACHE_JUST_APPEND);\n }\n \n-\n-static int read_tree(struct repository *r, struct tree *tree,\n-\t\t     struct pathspec *match, struct index_state *istate)\n-{\n-\tread_tree_fn_t fn = NULL;\n-\tint i, err;\n-\n-\n-\t/*\n-\t * See if we have cache entry at the stage.  If so,\n-\t * do it the original slow way, otherwise, append and then\n-\t * sort at the end.\n-\t */\n-\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n-\t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == 1)\n-\t\t\tfn = read_one_entry;\n-\t}\n-\n-\tif (!fn)\n-\t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, 1, match, fn, istate);\n-\tif (fn == read_one_entry || err)\n-\t\treturn err;\n-\n-\t/*\n-\t * Sort the cache entry -- we need to nuke the cache tree, though.\n-\t */\n-\tcache_tree_free(&istate->cache_tree);\n-\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n-\treturn 0;\n-}\n-\n /*\n  * Read the tree specified with --with-tree option\n  * (typically, HEAD) into stage #1 and then\n@@ -518,6 +485,8 @@ void overlay_tree_on_index(struct index_state *istate,\n \tstruct pathspec pathspec;\n \tstruct cache_entry *last_stage0 = NULL;\n \tint i;\n+\tread_tree_fn_t fn = NULL;\n+\tint err;\n \n \tif (get_oid(tree_name, &oid))\n \t\tdie(\"tree-ish %s not found.\", tree_name);\n@@ -540,9 +509,32 @@ void overlay_tree_on_index(struct index_state *istate,\n \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n \t} else\n \t\tmemset(&pathspec, 0, sizeof(pathspec));\n-\tif (read_tree(the_repository, tree, &pathspec, istate))\n+\n+\t/*\n+\t * See if we have cache entry at the stage.  If so,\n+\t * do it the original slow way, otherwise, append and then\n+\t * sort at the end.\n+\t */\n+\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n+\t\tconst struct cache_entry *ce = istate->cache[i];\n+\t\tif (ce_stage(ce) == 1)\n+\t\t\tfn = read_one_entry;\n+\t}\n+\n+\tif (!fn)\n+\t\tfn = read_one_entry_quick;\n+\terr = read_tree_recursive(the_repository, tree, \"\", 0, 1, &pathspec, fn, istate);\n+\tif (err)\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \n+\t/*\n+\t * Sort the cache entry -- we need to nuke the cache tree, though.\n+\t */\n+\tif (fn == read_one_entry_quick) {\n+\t\tcache_tree_free(&istate->cache_tree);\n+\t\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n+\t}\n+\n \tfor (i = 0; i < istate->cache_nr; i++) {\n \t\tstruct cache_entry *ce = istate->cache[i];\n \t\tswitch (ce_stage(ce)) {\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419209","messageId":"20210315234344.28427-3-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH v3 2/9] tree.c API: move read_tree() into builtin/ls-files.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-15T23:43:37Z","receivedAt":"2021-03-15T23:44:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since the read_tree() API was added around the same time as\nread_tree_recursive() in 94537c78a82 (Move \"read_tree()\" to\n\"tree.c\"[...], 2005-04-22) and b12ec373b8e ([PATCH] Teach read-tree\nabout commit objects, 2005-04-20) things have gradually migrated over\nto the read_tree_recursive() version.\n\nNow builtin/ls-files.c is the last user of this code, let's move all\nthe relevant code there. This allows for subsequent simplification of\nit, and an eventual move to read_tree_recursive().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++\n cache.h            |  2 +-\n tree.c             | 89 ---------------------------------------------\n tree.h             |  5 ---\n 4 files changed, 92 insertions(+), 95 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex f6f9e483b27..a4458622813 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -12,6 +12,7 @@\n #include \"dir.h\"\n #include \"builtin.h\"\n #include \"tree.h\"\n+#include \"cache-tree.h\"\n #include \"parse-options.h\"\n #include \"resolve-undo.h\"\n #include \"string-list.h\"\n@@ -420,6 +421,96 @@ static int get_common_prefix_len(const char *common_prefix)\n \treturn common_prefix_len;\n }\n \n+static int read_one_entry_opt(struct index_state *istate,\n+\t\t\t      const struct object_id *oid,\n+\t\t\t      const char *base, int baselen,\n+\t\t\t      const char *pathname,\n+\t\t\t      unsigned mode, int stage, int opt)\n+{\n+\tint len;\n+\tstruct cache_entry *ce;\n+\n+\tif (S_ISDIR(mode))\n+\t\treturn READ_TREE_RECURSIVE;\n+\n+\tlen = strlen(pathname);\n+\tce = make_empty_cache_entry(istate, baselen + len);\n+\n+\tce->ce_mode = create_ce_mode(mode);\n+\tce->ce_flags = create_ce_flags(stage);\n+\tce->ce_namelen = baselen + len;\n+\tmemcpy(ce->name, base, baselen);\n+\tmemcpy(ce->name + baselen, pathname, len+1);\n+\toidcpy(&ce->oid, oid);\n+\treturn add_index_entry(istate, ce, opt);\n+}\n+\n+static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n+\t\t\t  const char *pathname, unsigned mode, int stage,\n+\t\t\t  void *context)\n+{\n+\tstruct index_state *istate = context;\n+\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\t\t\t\t  mode, stage,\n+\t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n+}\n+\n+/*\n+ * This is used when the caller knows there is no existing entries at\n+ * the stage that will conflict with the entry being added.\n+ */\n+static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n+\t\t\t\tconst char *pathname, unsigned mode, int stage,\n+\t\t\t\tvoid *context)\n+{\n+\tstruct index_state *istate = context;\n+\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\t\t\t\t  mode, stage,\n+\t\t\t\t  ADD_CACHE_JUST_APPEND);\n+}\n+\n+\n+static int read_tree(struct repository *r, struct tree *tree, int stage,\n+\t\t     struct pathspec *match, struct index_state *istate)\n+{\n+\tread_tree_fn_t fn = NULL;\n+\tint i, err;\n+\n+\t/*\n+\t * Currently the only existing callers of this function all\n+\t * call it with stage=1 and after making sure there is nothing\n+\t * at that stage; we could always use read_one_entry_quick().\n+\t *\n+\t * But when we decide to straighten out git-read-tree not to\n+\t * use unpack_trees() in some cases, this will probably start\n+\t * to matter.\n+\t */\n+\n+\t/*\n+\t * See if we have cache entry at the stage.  If so,\n+\t * do it the original slow way, otherwise, append and then\n+\t * sort at the end.\n+\t */\n+\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n+\t\tconst struct cache_entry *ce = istate->cache[i];\n+\t\tif (ce_stage(ce) == stage)\n+\t\t\tfn = read_one_entry;\n+\t}\n+\n+\tif (!fn)\n+\t\tfn = read_one_entry_quick;\n+\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n+\tif (fn == read_one_entry || err)\n+\t\treturn err;\n+\n+\t/*\n+\t * Sort the cache entry -- we need to nuke the cache tree, though.\n+\t */\n+\tcache_tree_free(&istate->cache_tree);\n+\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n+\treturn 0;\n+}\n+\n /*\n  * Read the tree specified with --with-tree option\n  * (typically, HEAD) into stage #1 and then\ndiff --git a/cache.h b/cache.h\nindex 6fda8091f11..c2f8a8eadf6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -803,7 +803,7 @@ static inline int index_pos_to_insert_pos(uintmax_t pos)\n #define ADD_CACHE_OK_TO_ADD 1\t\t/* Ok to add */\n #define ADD_CACHE_OK_TO_REPLACE 2\t/* Ok to replace file/directory */\n #define ADD_CACHE_SKIP_DFCHECK 4\t/* Ok to skip DF conflict checks */\n-#define ADD_CACHE_JUST_APPEND 8\t\t/* Append only; tree.c::read_tree() */\n+#define ADD_CACHE_JUST_APPEND 8\t\t/* Append only */\n #define ADD_CACHE_NEW_ONLY 16\t\t/* Do not replace existing ones */\n #define ADD_CACHE_KEEP_CACHE_TREE 32\t/* Do not invalidate cache-tree */\n #define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */\ndiff --git a/tree.c b/tree.c\nindex a52479812ce..a6c12f2745a 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -11,54 +11,6 @@\n \n const char *tree_type = \"tree\";\n \n-static int read_one_entry_opt(struct index_state *istate,\n-\t\t\t      const struct object_id *oid,\n-\t\t\t      const char *base, int baselen,\n-\t\t\t      const char *pathname,\n-\t\t\t      unsigned mode, int stage, int opt)\n-{\n-\tint len;\n-\tstruct cache_entry *ce;\n-\n-\tif (S_ISDIR(mode))\n-\t\treturn READ_TREE_RECURSIVE;\n-\n-\tlen = strlen(pathname);\n-\tce = make_empty_cache_entry(istate, baselen + len);\n-\n-\tce->ce_mode = create_ce_mode(mode);\n-\tce->ce_flags = create_ce_flags(stage);\n-\tce->ce_namelen = baselen + len;\n-\tmemcpy(ce->name, base, baselen);\n-\tmemcpy(ce->name + baselen, pathname, len+1);\n-\toidcpy(&ce->oid, oid);\n-\treturn add_index_entry(istate, ce, opt);\n-}\n-\n-static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\t  const char *pathname, unsigned mode, int stage,\n-\t\t\t  void *context)\n-{\n-\tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode, stage,\n-\t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n-}\n-\n-/*\n- * This is used when the caller knows there is no existing entries at\n- * the stage that will conflict with the entry being added.\n- */\n-static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n-\t\t\t\tconst char *pathname, unsigned mode, int stage,\n-\t\t\t\tvoid *context)\n-{\n-\tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode, stage,\n-\t\t\t\t  ADD_CACHE_JUST_APPEND);\n-}\n-\n static int read_tree_1(struct repository *r,\n \t\t       struct tree *tree, struct strbuf *base,\n \t\t       int stage, const struct pathspec *pathspec,\n@@ -154,47 +106,6 @@ int cmp_cache_name_compare(const void *a_, const void *b_)\n \t\t\t\t  ce2->name, ce2->ce_namelen, ce_stage(ce2));\n }\n \n-int read_tree(struct repository *r, struct tree *tree, int stage,\n-\t      struct pathspec *match, struct index_state *istate)\n-{\n-\tread_tree_fn_t fn = NULL;\n-\tint i, err;\n-\n-\t/*\n-\t * Currently the only existing callers of this function all\n-\t * call it with stage=1 and after making sure there is nothing\n-\t * at that stage; we could always use read_one_entry_quick().\n-\t *\n-\t * But when we decide to straighten out git-read-tree not to\n-\t * use unpack_trees() in some cases, this will probably start\n-\t * to matter.\n-\t */\n-\n-\t/*\n-\t * See if we have cache entry at the stage.  If so,\n-\t * do it the original slow way, otherwise, append and then\n-\t * sort at the end.\n-\t */\n-\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n-\t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == stage)\n-\t\t\tfn = read_one_entry;\n-\t}\n-\n-\tif (!fn)\n-\t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n-\tif (fn == read_one_entry || err)\n-\t\treturn err;\n-\n-\t/*\n-\t * Sort the cache entry -- we need to nuke the cache tree, though.\n-\t */\n-\tcache_tree_free(&istate->cache_tree);\n-\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n-\treturn 0;\n-}\n-\n struct tree *lookup_tree(struct repository *r, const struct object_id *oid)\n {\n \tstruct object *obj = lookup_object(r, oid);\ndiff --git a/tree.h b/tree.h\nindex 3eb0484cbf2..6b0b1dc211a 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -38,9 +38,4 @@ int read_tree_recursive(struct repository *r,\n \t\t\tconst char *base, int baselen,\n \t\t\tint stage, const struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context);\n-\n-int read_tree(struct repository *r, struct tree *tree,\n-\t      int stage, struct pathspec *pathspec,\n-\t      struct index_state *istate);\n-\n #endif /* TREE_H */\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419210","messageId":"20210315234344.28427-9-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH v3 8/9] show tests: add test for \"git show <tree>\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-15T23:43:43Z","receivedAt":"2021-03-15T23:45:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add missing tests for showing a tree with \"git show\". Let's test for\nshowing a tree, two trees, and that doing so doesn't recurse.\n\nThe only tests for this code added in 5d7eeee2ac6 (git-show: grok\nblobs, trees and tags, too, 2006-12-14) were the tests in\nt7701-repack-unpack-unreachable.sh added in ccc1297226b (repack:\nmodify behavior of -A option to leave unreferenced objects unpacked,\n2008-05-09).\n\nLet's add this common mode of operation to the \"show\" tests\nthemselves. It's more obvious, and the tests in\nt7701-repack-unpack-unreachable.sh happily parse if we start buggily\nemitting trees recursively.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t7007-show.sh | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/t/t7007-show.sh b/t/t7007-show.sh\nindex 42d3db62468..37ce718b231 100755\n--- a/t/t7007-show.sh\n+++ b/t/t7007-show.sh\n@@ -38,6 +38,45 @@ test_expect_success 'showing two commits' '\n \ttest_cmp expect actual.filtered\n '\n \n+test_expect_success 'showing a tree' '\n+\tcat >expected <<-EOF &&\n+\ttree main1:\n+\n+\tmain1.t\n+\tEOF\n+\tgit show main1: >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'showing two trees' '\n+\tcat >expected <<-EOF &&\n+\ttree main1:\n+\n+\tmain1.t\n+\n+\ttree main2:\n+\n+\tmain1.t\n+\tmain2.t\n+\tEOF\n+\tgit show main1: main2: >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'showing a trees is not recursive' '\n+\tgit worktree add not-recursive main1 &&\n+\tmkdir not-recursive/a &&\n+\ttest_commit -C not-recursive a/file &&\n+\tcat >expected <<-EOF &&\n+\ttree a/file:\n+\n+\ta/\n+\tmain1.t\n+\tEOF\n+\tgit -C not-recursive show a/file: >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'showing a range walks (linear)' '\n \tcat >expect <<-EOF &&\n \tcommit $(git rev-parse main3)\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419211","messageId":"20210315234344.28427-6-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH v3 5/9] tree.h API: remove support for starting at prefix != \"\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-15T23:43:40Z","receivedAt":"2021-03-15T23:45:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Every caller of the read_tree_recursive() function hardcoded a\nstarting point of \"\" in the tree. So let's simply remove that\nparameter.\n\nThe last function to call read_tree_recursive() with a non-\"\" path was\nread_tree_recursive() itself, but that was changed in\nffd31f661d5 (Reimplement read_tree_recursive() using\ntree_entry_interesting(), 2011-03-25).\n\nAs it turns out (Murphy's law and all) we're just about to gain a new\nin-tree user that would need this parameter[1]. Let's remove it anyway\nas the common case is going to be to not supply it, A later commit\nwill bring back this functionality in different form.\n\n1. https://lore.kernel.org/git/xmqqft106sok.fsf@gitster.g/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          | 8 ++++----\n builtin/checkout.c | 2 +-\n builtin/log.c      | 4 ++--\n builtin/ls-files.c | 2 +-\n builtin/ls-tree.c  | 2 +-\n merge-recursive.c  | 2 +-\n tree.c             | 2 --\n tree.h             | 1 -\n 8 files changed, 10 insertions(+), 13 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 5919d9e5050..9394f170f7f 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -316,8 +316,8 @@ int write_archive_entries(struct archiver_args *args,\n \t\tgit_attr_set_direction(GIT_ATTR_INDEX);\n \t}\n \n-\terr = read_tree_recursive(args->repo, args->tree, \"\",\n-\t\t\t\t  0, 0, &args->pathspec,\n+\terr = read_tree_recursive(args->repo, args->tree,\n+\t\t\t\t  0, &args->pathspec,\n \t\t\t\t  queue_or_write_archive_entry,\n \t\t\t\t  &context);\n \tif (err == READ_TREE_RECURSIVE)\n@@ -405,8 +405,8 @@ static int path_exists(struct archiver_args *args, const char *path)\n \tctx.args = args;\n \tparse_pathspec(&ctx.pathspec, 0, 0, \"\", paths);\n \tctx.pathspec.recursive = 1;\n-\tret = read_tree_recursive(args->repo, args->tree, \"\",\n-\t\t\t\t  0, 0, &ctx.pathspec,\n+\tret = read_tree_recursive(args->repo, args->tree,\n+\t\t\t\t  0, &ctx.pathspec,\n \t\t\t\t  reject_entry, &ctx);\n \tclear_pathspec(&ctx.pathspec);\n \treturn ret != 0;\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2d6550bc3c8..21b742c0f07 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -155,7 +155,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,\n \n static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)\n {\n-\tread_tree_recursive(the_repository, tree, \"\", 0, 0,\n+\tread_tree_recursive(the_repository, tree, 0,\n \t\t\t    pathspec, update_some, NULL);\n \n \t/* update the index with the given tree's info\ndiff --git a/builtin/log.c b/builtin/log.c\nindex f67b67d80ed..ffa3fb8c286 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -681,8 +681,8 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n-\t\t\tread_tree_recursive(the_repository, (struct tree *)o, \"\",\n-\t\t\t\t\t    0, 0, &match_all, show_tree_object,\n+\t\t\tread_tree_recursive(the_repository, (struct tree *)o,\n+\t\t\t\t\t    0, &match_all, show_tree_object,\n \t\t\t\t\t    rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex db53e2c8e6d..cd432ac03cd 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -523,7 +523,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(the_repository, tree, \"\", 0, 1, &pathspec, fn, istate);\n+\terr = read_tree_recursive(the_repository, tree, 1, &pathspec, fn, istate);\n \tif (err)\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 7cad3f24ebd..7d3fb2e6d0f 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -185,6 +185,6 @@ 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_recursive(the_repository, tree, \"\", 0, 0,\n+\treturn !!read_tree_recursive(the_repository, tree, 0,\n \t\t\t\t     &pathspec, show_tree, NULL);\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex b052974f191..fa7602ff0f2 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -473,7 +473,7 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n {\n \tstruct pathspec match_all;\n \tmemset(&match_all, 0, sizeof(match_all));\n-\tread_tree_recursive(opt->repo, tree, \"\", 0, 0,\n+\tread_tree_recursive(opt->repo, tree, 0,\n \t\t\t    &match_all, save_files_dirs, opt);\n }\n \ndiff --git a/tree.c b/tree.c\nindex a6c12f2745a..04eb11aed31 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -83,14 +83,12 @@ static int read_tree_1(struct repository *r,\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tconst char *base, int baselen,\n \t\t\tint stage, const struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\tstrbuf_add(&sb, base, baselen);\n \tret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context);\n \tstrbuf_release(&sb);\n \treturn ret;\ndiff --git a/tree.h b/tree.h\nindex 6b0b1dc211a..5252b5139dd 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -35,7 +35,6 @@ typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const c\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tconst char *base, int baselen,\n \t\t\tint stage, const struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context);\n #endif /* TREE_H */\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419212","messageId":"20210315234344.28427-10-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH v3 9/9] tree.h API: expose read_tree_1() as read_tree_at()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-15T23:43:44Z","receivedAt":"2021-03-15T23:45:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the static read_tree_1() function to read_tree_at(). This will\nallow for the old read_tree_recursive() mode of operation where we\nstart at a given path instead of \"\".\n\nSee [1] for the discussion of one such future in-tree user, unlike the\nold read_tree_recursive() this function takes a strbuf. Since that's\nwhat read_tree_1() used internally this should allow us to avoid\ncasting and/or reallocations in the future.\n\n1. https://lore.kernel.org/git/xmqqft106sok.fsf@gitster.g/#t\n---\n tree.c | 16 ++++++++--------\n tree.h |  6 ++++++\n 2 files changed, 14 insertions(+), 8 deletions(-)\n\ndiff --git a/tree.c b/tree.c\nindex f6de250d7ff..6a2a52967e4 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -11,10 +11,10 @@\n \n const char *tree_type = \"tree\";\n \n-static int read_tree_1(struct repository *r,\n-\t\t       struct tree *tree, struct strbuf *base,\n-\t\t       const struct pathspec *pathspec,\n-\t\t       read_tree_fn_t fn, void *context)\n+int read_tree_at(struct repository *r,\n+\t\t struct tree *tree, struct strbuf *base,\n+\t\t const struct pathspec *pathspec,\n+\t\t read_tree_fn_t fn, void *context)\n {\n \tstruct tree_desc desc;\n \tstruct name_entry entry;\n@@ -71,9 +71,9 @@ static int read_tree_1(struct repository *r,\n \t\tlen = tree_entry_len(&entry);\n \t\tstrbuf_add(base, entry.path, len);\n \t\tstrbuf_addch(base, '/');\n-\t\tretval = read_tree_1(r, lookup_tree(r, &oid),\n-\t\t\t\t     base, pathspec,\n-\t\t\t\t     fn, context);\n+\t\tretval = read_tree_at(r, lookup_tree(r, &oid),\n+\t\t\t\t      base, pathspec,\n+\t\t\t\t      fn, context);\n \t\tstrbuf_setlen(base, oldlen);\n \t\tif (retval)\n \t\t\treturn -1;\n@@ -89,7 +89,7 @@ int read_tree(struct repository *r,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\tret = read_tree_1(r, tree, &sb, pathspec, fn, context);\n+\tret = read_tree_at(r, tree, &sb, pathspec, fn, context);\n \tstrbuf_release(&sb);\n \treturn ret;\n }\ndiff --git a/tree.h b/tree.h\nindex 4fb713774a7..f0b079d2e91 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -33,6 +33,12 @@ int cmp_cache_name_compare(const void *a_, const void *b_);\n #define READ_TREE_RECURSIVE 1\n typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n \n+int read_tree_at(struct repository *r,\n+\t\t struct tree *tree,\n+\t\t struct strbuf *at,\n+\t\t const struct pathspec *pathspec,\n+\t\t read_tree_fn_t fn, void *context);\n+\n int read_tree(struct repository *r,\n \t      struct tree *tree,\n \t      const struct pathspec *pathspec,\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419213","messageId":"20210315234344.28427-8-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH v3 7/9] tree.h API: rename read_tree_recursive() to read_tree()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-15T23:43:42Z","receivedAt":"2021-03-15T23:45:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the read_tree_recursive() function to just read_tree(). We had\nanother read_tree() function that I've refactored away in preceding\nsteps, since all in-tree users read trees recursively with a callback\nwe can change the name to signify that this is the norm.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          | 14 +++++++-------\n builtin/checkout.c |  6 +++---\n builtin/log.c      |  6 +++---\n builtin/ls-files.c |  2 +-\n builtin/ls-tree.c  |  4 ++--\n merge-recursive.c  |  4 ++--\n tree.c             |  8 ++++----\n tree.h             |  8 ++++----\n 8 files changed, 26 insertions(+), 26 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 6669a4bd147..c19300ecb9b 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -317,10 +317,10 @@ int write_archive_entries(struct archiver_args *args,\n \t\tgit_attr_set_direction(GIT_ATTR_INDEX);\n \t}\n \n-\terr = read_tree_recursive(args->repo, args->tree,\n-\t\t\t\t  &args->pathspec,\n-\t\t\t\t  queue_or_write_archive_entry,\n-\t\t\t\t  &context);\n+\terr = read_tree(args->repo, args->tree,\n+\t\t\t&args->pathspec,\n+\t\t\tqueue_or_write_archive_entry,\n+\t\t\t&context);\n \tif (err == READ_TREE_RECURSIVE)\n \t\terr = 0;\n \twhile (context.bottom) {\n@@ -406,9 +406,9 @@ static int path_exists(struct archiver_args *args, const char *path)\n \tctx.args = args;\n \tparse_pathspec(&ctx.pathspec, 0, 0, \"\", paths);\n \tctx.pathspec.recursive = 1;\n-\tret = read_tree_recursive(args->repo, args->tree,\n-\t\t\t\t  &ctx.pathspec,\n-\t\t\t\t  reject_entry, &ctx);\n+\tret = read_tree(args->repo, args->tree,\n+\t\t\t&ctx.pathspec,\n+\t\t\treject_entry, &ctx);\n \tclear_pathspec(&ctx.pathspec);\n \treturn ret != 0;\n }\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2c2d58a230f..0e663905200 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -155,8 +155,8 @@ static int update_some(const struct object_id *oid, struct strbuf *base,\n \n static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)\n {\n-\tread_tree_recursive(the_repository, tree,\n-\t\t\t    pathspec, update_some, NULL);\n+\tread_tree(the_repository, tree,\n+\t\t  pathspec, update_some, NULL);\n \n \t/* update the index with the given tree's info\n \t * for all args, expanding wildcards, and exit\n@@ -322,7 +322,7 @@ static void mark_ce_for_checkout_overlay(struct cache_entry *ce,\n \t * If it comes from the tree-ish, we already know it\n \t * matches the pathspec and could just stamp\n \t * CE_MATCHED to it from update_some(). But we still\n-\t * need ps_matched and read_tree_recursive (and\n+\t * need ps_matched and read_tree (and\n \t * eventually tree_entry_interesting) cannot fill\n \t * ps_matched yet. Once it can, we can avoid calling\n \t * match_pathspec() for _all_ entries when\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 58acb2b76ab..980de590638 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -681,9 +681,9 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n-\t\t\tread_tree_recursive(the_repository, (struct tree *)o,\n-\t\t\t\t\t    &match_all, show_tree_object,\n-\t\t\t\t\t    rev.diffopt.file);\n+\t\t\tread_tree(the_repository, (struct tree *)o,\n+\t\t\t\t  &match_all, show_tree_object,\n+\t\t\t\t  rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\n \t\tcase OBJ_COMMIT:\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex fa9b01b6cc7..13bcc2d8473 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -523,7 +523,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(the_repository, tree, &pathspec, fn, istate);\n+\terr = read_tree(the_repository, tree, &pathspec, fn, istate);\n \tif (err)\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex dbb31217beb..3a442631c71 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -185,6 +185,6 @@ 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_recursive(the_repository, tree,\n-\t\t\t\t     &pathspec, show_tree, NULL);\n+\treturn !!read_tree(the_repository, tree,\n+\t\t\t   &pathspec, show_tree, NULL);\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1593f374495..3d9207455b3 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -473,8 +473,8 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n {\n \tstruct pathspec match_all;\n \tmemset(&match_all, 0, sizeof(match_all));\n-\tread_tree_recursive(opt->repo, tree,\n-\t\t\t    &match_all, save_files_dirs, opt);\n+\tread_tree(opt->repo, tree,\n+\t\t  &match_all, save_files_dirs, opt);\n }\n \n static int get_tree_entry_if_blob(struct repository *r,\ndiff --git a/tree.c b/tree.c\nindex fb4985f22ca..f6de250d7ff 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -81,10 +81,10 @@ static int read_tree_1(struct repository *r,\n \treturn 0;\n }\n \n-int read_tree_recursive(struct repository *r,\n-\t\t\tstruct tree *tree,\n-\t\t\tconst struct pathspec *pathspec,\n-\t\t\tread_tree_fn_t fn, void *context)\n+int read_tree(struct repository *r,\n+\t      struct tree *tree,\n+\t      const struct pathspec *pathspec,\n+\t      read_tree_fn_t fn, void *context)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\ndiff --git a/tree.h b/tree.h\nindex 1309ab997e5..4fb713774a7 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -33,8 +33,8 @@ int cmp_cache_name_compare(const void *a_, const void *b_);\n #define READ_TREE_RECURSIVE 1\n typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n \n-int read_tree_recursive(struct repository *r,\n-\t\t\tstruct tree *tree,\n-\t\t\tconst struct pathspec *pathspec,\n-\t\t\tread_tree_fn_t fn, void *context);\n+int read_tree(struct repository *r,\n+\t      struct tree *tree,\n+\t      const struct pathspec *pathspec,\n+\t      read_tree_fn_t fn, void *context);\n #endif /* TREE_H */\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419214","messageId":"20210315234344.28427-7-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308022138.28166-1-avarab@gmail.com","subject":"[PATCH v3 6/9] tree.h API: remove \"stage\" parameter from read_tree_recursive()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-15T23:43:41Z","receivedAt":"2021-03-15T23:45:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The read_tree_recursive() function took a \"stage\" parameter that is\npassed through as-is. As it turns out nothing used this parameter in a\nway that they couldn't just move to the callback function they\ndefined, so let's get rid of it.\n\nIf anyone needs to pass such information in the future they can use\nthe \"void *context\" parameter.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          |  9 +++++----\n builtin/checkout.c |  4 ++--\n builtin/log.c      |  4 ++--\n builtin/ls-files.c | 14 +++++++-------\n builtin/ls-tree.c  |  4 ++--\n merge-recursive.c  |  4 ++--\n tree.c             | 10 +++++-----\n tree.h             |  4 ++--\n 8 files changed, 27 insertions(+), 26 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 9394f170f7f..6669a4bd147 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -231,9 +231,10 @@ static int write_directory(struct archiver_context *c)\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\tstruct strbuf *base, const char *filename,\n-\t\tunsigned mode, int stage, void *context)\n+\t\tunsigned mode, void *context)\n {\n \tstruct archiver_context *c = context;\n+\tint stage = 0;\n \n \twhile (c->bottom &&\n \t       !(base->len >= c->bottom->len &&\n@@ -317,7 +318,7 @@ int write_archive_entries(struct archiver_args *args,\n \t}\n \n \terr = read_tree_recursive(args->repo, args->tree,\n-\t\t\t\t  0, &args->pathspec,\n+\t\t\t\t  &args->pathspec,\n \t\t\t\t  queue_or_write_archive_entry,\n \t\t\t\t  &context);\n \tif (err == READ_TREE_RECURSIVE)\n@@ -378,7 +379,7 @@ struct path_exists_context {\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n \t\t\tconst char *filename, unsigned mode,\n-\t\t\tint stage, void *context)\n+\t\t\tvoid *context)\n {\n \tint ret = -1;\n \tstruct path_exists_context *ctx = context;\n@@ -406,7 +407,7 @@ static int path_exists(struct archiver_args *args, const char *path)\n \tparse_pathspec(&ctx.pathspec, 0, 0, \"\", paths);\n \tctx.pathspec.recursive = 1;\n \tret = read_tree_recursive(args->repo, args->tree,\n-\t\t\t\t  0, &ctx.pathspec,\n+\t\t\t\t  &ctx.pathspec,\n \t\t\t\t  reject_entry, &ctx);\n \tclear_pathspec(&ctx.pathspec);\n \treturn ret != 0;\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 21b742c0f07..2c2d58a230f 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -114,7 +114,7 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n }\n \n static int update_some(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tint len;\n \tstruct cache_entry *ce;\n@@ -155,7 +155,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,\n \n static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)\n {\n-\tread_tree_recursive(the_repository, tree, 0,\n+\tread_tree_recursive(the_repository, tree,\n \t\t\t    pathspec, update_some, NULL);\n \n \t/* update the index with the given tree's info\ndiff --git a/builtin/log.c b/builtin/log.c\nindex ffa3fb8c286..58acb2b76ab 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -599,7 +599,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n \n static int show_tree_object(const struct object_id *oid,\n \t\tstruct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tFILE *file = context;\n \tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n@@ -682,7 +682,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n \t\t\tread_tree_recursive(the_repository, (struct tree *)o,\n-\t\t\t\t\t    0, &match_all, show_tree_object,\n+\t\t\t\t\t    &match_all, show_tree_object,\n \t\t\t\t\t    rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex cd432ac03cd..fa9b01b6cc7 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -425,7 +425,7 @@ static int read_one_entry_opt(struct index_state *istate,\n \t\t\t      const struct object_id *oid,\n \t\t\t      struct strbuf *base,\n \t\t\t      const char *pathname,\n-\t\t\t      unsigned mode, int stage, int opt)\n+\t\t\t      unsigned mode, int opt)\n {\n \tint len;\n \tstruct cache_entry *ce;\n@@ -437,7 +437,7 @@ static int read_one_entry_opt(struct index_state *istate,\n \tce = make_empty_cache_entry(istate, base->len + len);\n \n \tce->ce_mode = create_ce_mode(mode);\n-\tce->ce_flags = create_ce_flags(stage);\n+\tce->ce_flags = create_ce_flags(1);\n \tce->ce_namelen = base->len + len;\n \tmemcpy(ce->name, base->buf, base->len);\n \tmemcpy(ce->name + base->len, pathname, len+1);\n@@ -446,12 +446,12 @@ static int read_one_entry_opt(struct index_state *istate,\n }\n \n static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\t  const char *pathname, unsigned mode, int stage,\n+\t\t\t  const char *pathname, unsigned mode,\n \t\t\t  void *context)\n {\n \tstruct index_state *istate = context;\n \treturn read_one_entry_opt(istate, oid, base, pathname,\n-\t\t\t\t  mode, stage,\n+\t\t\t\t  mode,\n \t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n }\n \n@@ -460,12 +460,12 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n  * the stage that will conflict with the entry being added.\n  */\n static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n-\t\t\t\tconst char *pathname, unsigned mode, int stage,\n+\t\t\t\tconst char *pathname, unsigned mode,\n \t\t\t\tvoid *context)\n {\n \tstruct index_state *istate = context;\n \treturn read_one_entry_opt(istate, oid, base, pathname,\n-\t\t\t\t  mode, stage,\n+\t\t\t\t  mode,\n \t\t\t\t  ADD_CACHE_JUST_APPEND);\n }\n \n@@ -523,7 +523,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(the_repository, tree, 1, &pathspec, fn, istate);\n+\terr = read_tree_recursive(the_repository, tree, &pathspec, fn, istate);\n \tif (err)\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 7d3fb2e6d0f..dbb31217beb 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -62,7 +62,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n }\n \n static int show_tree(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tint retval = 0;\n \tint baselen;\n@@ -185,6 +185,6 @@ 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_recursive(the_repository, tree, 0,\n+\treturn !!read_tree_recursive(the_repository, tree,\n \t\t\t\t     &pathspec, show_tree, NULL);\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex fa7602ff0f2..1593f374495 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,7 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   unsigned int mode, int stage, void *context)\n+\t\t\t   unsigned int mode, void *context)\n {\n \tstruct path_hashmap_entry *entry;\n \tint baselen = base->len;\n@@ -473,7 +473,7 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n {\n \tstruct pathspec match_all;\n \tmemset(&match_all, 0, sizeof(match_all));\n-\tread_tree_recursive(opt->repo, tree, 0,\n+\tread_tree_recursive(opt->repo, tree,\n \t\t\t    &match_all, save_files_dirs, opt);\n }\n \ndiff --git a/tree.c b/tree.c\nindex 04eb11aed31..fb4985f22ca 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -13,7 +13,7 @@ const char *tree_type = \"tree\";\n \n static int read_tree_1(struct repository *r,\n \t\t       struct tree *tree, struct strbuf *base,\n-\t\t       int stage, const struct pathspec *pathspec,\n+\t\t       const struct pathspec *pathspec,\n \t\t       read_tree_fn_t fn, void *context)\n {\n \tstruct tree_desc desc;\n@@ -38,7 +38,7 @@ static int read_tree_1(struct repository *r,\n \t\t}\n \n \t\tswitch (fn(&entry.oid, base,\n-\t\t\t   entry.path, entry.mode, stage, context)) {\n+\t\t\t   entry.path, entry.mode, context)) {\n \t\tcase 0:\n \t\t\tcontinue;\n \t\tcase READ_TREE_RECURSIVE:\n@@ -72,7 +72,7 @@ static int read_tree_1(struct repository *r,\n \t\tstrbuf_add(base, entry.path, len);\n \t\tstrbuf_addch(base, '/');\n \t\tretval = read_tree_1(r, lookup_tree(r, &oid),\n-\t\t\t\t     base, stage, pathspec,\n+\t\t\t\t     base, pathspec,\n \t\t\t\t     fn, context);\n \t\tstrbuf_setlen(base, oldlen);\n \t\tif (retval)\n@@ -83,13 +83,13 @@ static int read_tree_1(struct repository *r,\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tint stage, const struct pathspec *pathspec,\n+\t\t\tconst struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\tret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context);\n+\tret = read_tree_1(r, tree, &sb, pathspec, fn, context);\n \tstrbuf_release(&sb);\n \treturn ret;\n }\ndiff --git a/tree.h b/tree.h\nindex 5252b5139dd..1309ab997e5 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -31,10 +31,10 @@ struct tree *parse_tree_indirect(const struct object_id *oid);\n int cmp_cache_name_compare(const void *a_, const void *b_);\n \n #define READ_TREE_RECURSIVE 1\n-typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, int, void *);\n+typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tint stage, const struct pathspec *pathspec,\n+\t\t\tconst struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context);\n #endif /* TREE_H */\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419234","messageId":"20210316021312.13927-3-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 02/29] notes & match-trees: use name_entry's \"pathlen\" member","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:45Z","receivedAt":"2021-03-16T02:14:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change code that was doing a strlen() on the \"path\" from a name_entry\nstruct to instead use the pathlen given to us by decode_tree_entry().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c | 7 +++----\n notes.c       | 4 ++--\n 2 files changed, 5 insertions(+), 6 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex f6c194c1cca..1011357ad0c 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -197,9 +197,10 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \twhile (desc.size) {\n \t\tconst char *name;\n \t\tunsigned short mode;\n+\t\tint len = tree_entry_len(&desc.entry);\n \n \t\ttree_entry_extract(&desc, &name, &mode);\n-\t\tif (strlen(name) == toplen &&\n+\t\tif (len == toplen &&\n \t\t    !memcmp(name, prefix, toplen)) {\n \t\t\tif (!S_ISDIR(mode))\n \t\t\t\tdie(\"entry %s in tree %s is not a tree\", name,\n@@ -214,9 +215,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \t\t\t *   - to discard the \"const\"; this is OK because we\n \t\t\t *     know it points into our non-const \"buf\"\n \t\t\t */\n-\t\t\trewrite_here = (unsigned char *)(desc.entry.path +\n-\t\t\t\t\t\t\t strlen(desc.entry.path) +\n-\t\t\t\t\t\t\t 1);\n+\t\t\trewrite_here = (unsigned char *)(name + len + 1);\n \t\t\tbreak;\n \t\t}\n \t\tupdate_tree_entry(&desc);\ndiff --git a/notes.c b/notes.c\nindex d5ac081e76d..0a5b4fa1dbb 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -413,7 +413,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \twhile (tree_entry(&desc, &entry)) {\n \t\tunsigned char type;\n \t\tstruct leaf_node *l;\n-\t\tsize_t path_len = strlen(entry.path);\n+\t\tint path_len = entry.pathlen;\n \n \t\tif (path_len == 2 * (hashsz - prefix_len)) {\n \t\t\t/* This is potentially the remainder of the SHA-1 */\n@@ -483,7 +483,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n \t\t\t\tstrbuf_addch(&non_note_path, '/');\n \t\t\t}\n-\t\t\tstrbuf_addstr(&non_note_path, entry.path);\n+\t\t\tstrbuf_add(&non_note_path, entry.path, path_len);\n \t\t\tadd_non_note(t, strbuf_detach(&non_note_path, NULL),\n \t\t\t\t     entry.mode, entry.oid.hash);\n \t\t}\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419235","messageId":"20210316021312.13927-1-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 00/29] tree-walk: mostly replace \"mode\" with \"enum object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:43Z","receivedAt":"2021-03-16T02:14:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"A v2 of the big tree-walk.[ch] refactoring series, goals etc. at the\nv1 at:\nhttps://lore.kernel.org/git/20210308150650.18626-1-avarab@gmail.com/\n\nIt is based on my just-re-rolled v3 read_tree_recursive() series:\nhttps://lore.kernel.org/git/20210315234344.28427-1-avarab@gmail.com/\n\nThis version should address all the feedback on v1 for patces 1-27/30,\nthanks to Elijah for very valuable comments on v1.\n\nIt's mostly small nits here and there, except:\n\n - I found that the change I'd made to update-index.c was buggy at the\n   point it was introduced in the series, the object_type would be\n   uninitialized. There were/are no tests for that, but I've moved\n   things around so we don't have that bug anymore.\n\n - Elijah had a comment on whether we needed oid_object_info() in\n   blame.c. As it turns out we don't need a \"is blob?\" check at all\n   there. There's a new 28/29 to refactor that small part of blame.c,\n   along with a test.\n\nWhat this re-roll *does not* include is the final 28-30/30 part of v1\nto s/mode/raw_mode/g and move canonical_mode() out of tree-walk.h.\n\nSo a follow-up series will still be needed to fix the fsck.c check for\nbad modes, but I wanted to split that tricker change off from this\nrather big initial refactoring.\n\nÆvar Arnfjörð Bjarmason (29):\n  diff.c: remove redundant canon_mode() call\n  notes & match-trees: use name_entry's \"pathlen\" member\n  cache.h: add a comment to object_type()\n  tree-walk.h: add object_type member to name_entry\n  tree-walk.c: migrate to using new \"object_type\" field when possible\n  cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n  tree-walk.h users: switch object_type(...) to new .object_type\n  tree.h: format argument lists of read_tree_recursive() users\n  tree.h users: format argument lists in archive.c\n  archive: get rid of 'stage' parameter\n  tree.h API: make read_tree_fn_t take an \"enum object_type\"\n  tree-walk.h users: migrate \"p->mode &&\" pattern\n  tree-walk.h users: refactor chained \"mode\" if/else into switch\n  tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n  merge-tree tests: test for the mode comparison in same_entry()\n  merge-ort: correct reference to test in 62fdec17a11\n  fsck.c: switch on \"object_type\" in fsck_walk_tree()\n  tree-walk.h users: use temporary variable(s) for \"mode\"\n  tree-walk.h API: formatting changes for subsequent commit\n  tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n  tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n  tree-walk.h API: add get_tree_entry_type()\n  tree-walk.h API: document and format tree_entry_extract()\n  tree-entry.h API: rename tree_entry_extract() to\n    tree_entry_extract_mode()\n  tree-walk.h API: add a tree_entry_extract_all() function\n  tree-walk.h API: add get_tree_entry_all()\n  tree-walk.h API: add a get_tree_entry_path() function\n  blame: emit a better error on 'git blame directory'\n  tree-walk.h API: add a tree_entry_extract_type() function\n\n archive.c                       | 50 +++++++++---------\n blame.c                         |  9 ++--\n builtin/checkout.c              |  6 ++-\n builtin/fast-import.c           |  8 +--\n builtin/grep.c                  |  6 +--\n builtin/log.c                   |  7 +--\n builtin/ls-files.c              |  6 ++-\n builtin/ls-tree.c               | 14 +++---\n builtin/merge-tree.c            | 30 +++++++----\n builtin/mktree.c                |  4 +-\n builtin/pack-objects.c          |  6 +--\n builtin/reflog.c                |  3 +-\n builtin/rm.c                    |  2 +-\n builtin/update-index.c          |  6 ++-\n cache-tree.c                    |  2 +-\n cache.h                         | 11 ++--\n combine-diff.c                  |  8 +--\n delta-islands.c                 |  2 +-\n diff.c                          |  2 +-\n fsck.c                          | 23 ++++-----\n http-push.c                     |  6 ++-\n line-log.c                      |  2 +-\n list-objects.c                  | 20 +++++---\n match-trees.c                   | 52 +++++++++----------\n merge-ort.c                     | 13 ++---\n merge-recursive.c               | 33 ++++++------\n notes.c                         | 14 +++---\n object-name.c                   |  7 ++-\n pack-bitmap-write.c             |  8 +--\n read-cache.c                    | 16 +++---\n revision.c                      | 12 +++--\n t/t4300-merge-tree.sh           | 44 ++++++++++++++++\n t/t8004-blame-with-conflicts.sh | 20 ++++++++\n tree-diff.c                     | 30 +++++++----\n tree-walk.c                     | 89 ++++++++++++++++++++++++---------\n tree-walk.h                     | 63 ++++++++++++++++++++---\n tree.c                          | 19 ++++---\n tree.h                          |  5 +-\n unpack-trees.c                  | 24 +++++----\n walker.c                        | 22 ++++----\n 40 files changed, 460 insertions(+), 244 deletions(-)\n\nRange-diff:\n 1:  e5df57c3440 =  1:  f9bbc30f69f diff.c: remove redundant canon_mode() call\n 2:  8c2500bbf35 =  2:  187fc2c3e64 notes & match-trees: use name_entry's \"pathlen\" member\n 3:  3d98e0c132f !  3:  311637c5583 cache.h: add a comment to object_type()\n    @@ Commit message\n         cache.h: add a comment to object_type()\n     \n         Add a comment to the object_type() function to explain what it\n    -    returns, and whet the \"mode\" is in the \"else\" case.\n    +    returns, and what the \"mode\" is in the \"else\" case.\n     \n         The object_type() function dates back to 4d1012c3709 (Fix rev-list\n         when showing objects involving submodules, 2007-11-11). It's not\n 4:  ce5808b317c =  4:  fecfe3d462c tree-walk.h: add object_type member to name_entry\n 5:  18f26531acf =  5:  db961ab5e8d tree-walk.c: migrate to using new \"object_type\" field when possible\n 6:  55e2640b815 !  6:  df2fc76161d cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n    @@ cache.h: int repo_interpret_branch_name(struct repository *r,\n      \n     -int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n     -int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n    -+int base_name_compare(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2);\n    -+int df_name_compare(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2);\n    ++int base_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n    ++int df_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n      int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);\n      int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);\n      \n    @@ combine-diff.c\n      \t\t\t  const struct diff_filespec *two)\n      {\n     -\tif (!S_ISDIR(one->mode) && !S_ISDIR(two->mode))\n    -+\tint isdir_one = S_ISDIR(one->mode);\n    -+\tint isdir_two = S_ISDIR(two->mode);\n    -+\tif (!isdir_one && !isdir_two)\n    ++\tint istree_one = S_ISDIR(one->mode);\n    ++\tint istree_two = S_ISDIR(two->mode);\n    ++\tif (!istree_one && !istree_two)\n      \t\treturn strcmp(one->path, two->path);\n      \n     -\treturn base_name_compare(one->path, strlen(one->path), one->mode,\n     -\t\t\t\t two->path, strlen(two->path), two->mode);\n    -+\treturn base_name_compare(one->path, strlen(one->path), isdir_one,\n    -+\t\t\t\t two->path, strlen(two->path), isdir_two);\n    ++\treturn base_name_compare(one->path, strlen(one->path), istree_one,\n    ++\t\t\t\t two->path, strlen(two->path), istree_two);\n      }\n      \n      static int filename_changed(char status)\n    @@ match-trees.c: static void *fill_tree_desc_strict(struct tree_desc *desc,\n      {\n     -\treturn base_name_compare(a->path, tree_entry_len(a), a->mode,\n     -\t\t\t\t b->path, tree_entry_len(b), b->mode);\n    -+\tint isdira = a->object_type == OBJ_TREE;\n    -+\tint isdirb = b->object_type == OBJ_TREE;\n    -+\treturn base_name_compare(a->path, tree_entry_len(a), isdira,\n    -+\t\t\t\t b->path, tree_entry_len(b), isdirb);\n    ++\tint istree_a = (a->object_type == OBJ_TREE);\n    ++\tint istree_b = (b->object_type == OBJ_TREE);\n    ++\treturn base_name_compare(a->path, tree_entry_len(a), istree_a,\n    ++\t\t\t\t b->path, tree_entry_len(b), istree_b);\n      }\n      \n      /*\n    @@ read-cache.c: int ie_modified(struct index_state *istate,\n      \n     -int base_name_compare(const char *name1, int len1, int mode1,\n     -\t\t      const char *name2, int len2, int mode2)\n    -+int base_name_compare(const char *name1, int len1, int isdir1,\n    -+\t\t      const char *name2, int len2, int isdir2)\n    ++int base_name_compare(const char *name1, int len1, int istree1,\n    ++\t\t      const char *name2, int len2, int istree2)\n      {\n      \tunsigned char c1, c2;\n      \tint len = len1 < len2 ? len1 : len2;\n    @@ read-cache.c: int base_name_compare(const char *name1, int len1, int mode1,\n      \tc1 = name1[len];\n      \tc2 = name2[len];\n     -\tif (!c1 && S_ISDIR(mode1))\n    -+\tif (!c1 && isdir1)\n    ++\tif (!c1 && istree1)\n      \t\tc1 = '/';\n     -\tif (!c2 && S_ISDIR(mode2))\n    -+\tif (!c2 && isdir2)\n    ++\tif (!c2 && istree2)\n      \t\tc2 = '/';\n      \treturn (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;\n      }\n    @@ read-cache.c: int base_name_compare(const char *name1, int len1, int mode1,\n       */\n     -int df_name_compare(const char *name1, int len1, int mode1,\n     -\t\t    const char *name2, int len2, int mode2)\n    -+int df_name_compare(const char *name1, int len1, int isdir1,\n    -+\t\t    const char *name2, int len2, int isdir2)\n    ++int df_name_compare(const char *name1, int len1, int istree1,\n    ++\t\t    const char *name2, int len2, int istree2)\n      {\n      \tint len = len1 < len2 ? len1 : len2, cmp;\n      \tunsigned char c1, c2;\n    @@ read-cache.c: int df_name_compare(const char *name1, int len1, int mode1,\n      \t\treturn 0;\n      \tc1 = name1[len];\n     -\tif (!c1 && S_ISDIR(mode1))\n    -+\tif (!c1 && isdir1)\n    ++\tif (!c1 && istree1)\n      \t\tc1 = '/';\n      \tc2 = name2[len];\n     -\tif (!c2 && S_ISDIR(mode2))\n    -+\tif (!c2 && isdir2)\n    ++\tif (!c2 && istree2)\n      \t\tc2 = '/';\n      \tif (c1 == '/' && !c2)\n      \t\treturn 0;\n    @@ tree-diff.c: static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_des\n      {\n      \tstruct name_entry *e1, *e2;\n      \tint cmp;\n    -+\tint e1_is_tree, e2_is_tree;\n    ++\tint istree_e1, istree_e2;\n      \n      \t/* empty descriptors sort after valid tree entries */\n      \tif (!t1->size)\n    @@ tree-diff.c: static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_des\n      \t\treturn -1;\n      \n      \te1 = &t1->entry;\n    -+\te1_is_tree = e1->object_type == OBJ_TREE;\n    ++\tistree_e1 = (e1->object_type == OBJ_TREE);\n      \te2 = &t2->entry;\n     -\tcmp = base_name_compare(e1->path, tree_entry_len(e1), e1->mode,\n     -\t\t\t\te2->path, tree_entry_len(e2), e2->mode);\n    -+\te2_is_tree = e2->object_type == OBJ_TREE;\n    -+\tcmp = base_name_compare(e1->path, tree_entry_len(e1), e1_is_tree,\n    -+\t\t\t\te2->path, tree_entry_len(e2), e2_is_tree);\n    ++\tistree_e2 = (e2->object_type == OBJ_TREE);\n    ++\tcmp = base_name_compare(e1->path, tree_entry_len(e1), istree_e1,\n    ++\t\t\t\te2->path, tree_entry_len(e2), istree_e2);\n      \treturn cmp;\n      }\n      \n    @@ unpack-trees.c: static int traverse_trees_recursive(int n, unsigned long dirmask\n      \t\t\t\t      const struct traverse_info *info,\n      \t\t\t\t      const char *name, size_t namelen,\n     -\t\t\t\t      unsigned mode)\n    -+\t\t\t\t      unsigned is_tree)\n    ++\t\t\t\t      unsigned istree)\n      {\n      \tint pathlen, ce_len;\n      \tconst char *ce_name;\n    @@ unpack-trees.c: static int do_compare_entry_piecewise(const struct cache_entry *\n      \tce_name = ce->name + pathlen;\n      \n     -\treturn df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n    -+\treturn df_name_compare(ce_name, ce_len, 0, name, namelen, is_tree);\n    ++\treturn df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n      }\n      \n      static int do_compare_entry(const struct cache_entry *ce,\n      \t\t\t    const struct traverse_info *info,\n      \t\t\t    const char *name, size_t namelen,\n     -\t\t\t    unsigned mode)\n    -+\t\t\t    unsigned is_tree)\n    ++\t\t\t    unsigned istree)\n      {\n      \tint pathlen, ce_len;\n      \tconst char *ce_name;\n    @@ unpack-trees.c: static int do_compare_entry(const struct cache_entry *ce,\n      \t */\n      \tif (!info->traverse_path)\n     -\t\treturn do_compare_entry_piecewise(ce, info, name, namelen, mode);\n    -+\t\treturn do_compare_entry_piecewise(ce, info, name, namelen, is_tree);\n    ++\t\treturn do_compare_entry_piecewise(ce, info, name, namelen, istree);\n      \n      \tcmp = strncmp(ce->name, info->traverse_path, info->pathlen);\n      \tif (cmp)\n    @@ unpack-trees.c: static int do_compare_entry(const struct cache_entry *ce,\n      \tce_name = ce->name + pathlen;\n      \n     -\treturn df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n    -+\treturn df_name_compare(ce_name, ce_len, 0, name, namelen, is_tree);\n    ++\treturn df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n      }\n      \n      static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)\n      {\n     -\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);\n    -+\tint is_tree = n->object_type == OBJ_TREE;\n    -+\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, is_tree);\n    ++\tint istree = (n->object_type == OBJ_TREE);\n    ++\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, istree);\n      \tif (cmp)\n      \t\treturn cmp;\n      \n 7:  abc128f6cb9 =  7:  49d5da8c086 tree-walk.h users: switch object_type(...) to new .object_type\n 8:  dcf13faf3cd !  8:  c9d209d496a tree.h: format argument lists of read_tree_recursive() users\n    @@ archive.c: static int check_attr_export_subst(const struct attr_check *check)\n     -\t\tvoid *context)\n     +\t\t\t       int baselen, const char *filename,\n     +\t\t\t       unsigned mode,\n    -+\t\t\t       int stage, void *context)\n    ++\t\t\t       int stage,\n    ++\t\t\t       void *context)\n      {\n      \tstatic struct strbuf path = STRBUF_INIT;\n      \tstruct archiver_context *c = context;\n    @@ tree.h: struct tree *parse_tree_indirect(const struct object_id *oid);\n     +\t\t\t      unsigned int,\n     +\t\t\t      void *);\n      \n    - int read_tree_recursive(struct repository *r,\n    - \t\t\tstruct tree *tree,\n    + int read_tree_at(struct repository *r,\n    + \t\t struct tree *tree,\n 9:  b33fcf82349 !  9:  a6d2660fe14 tree.h users: format argument lists in archive.c\n    @@ Commit message\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## archive.c ##\n    -@@ archive.c: static int check_attr_export_subst(const struct attr_check *check)\n    - static int write_archive_entry(const struct object_id *oid, const char *base,\n    - \t\t\t       int baselen, const char *filename,\n    - \t\t\t       unsigned mode,\n    --\t\t\t       int stage, void *context)\n    -+\t\t\t       int stage,\n    -+\t\t\t       void *context)\n    - {\n    - \tstatic struct strbuf path = STRBUF_INIT;\n    - \tstruct archiver_context *c = context;\n     @@ archive.c: static int write_archive_entry(const struct object_id *oid, const char *base,\n      }\n      \n10:  6a20d3c058f = 10:  15f7f89acca archive: get rid of 'stage' parameter\n11:  a7f7444917c ! 11:  7a71404ea3f tree.h API: make read_tree_fn_t take an \"enum object_type\"\n    @@ merge-recursive.c: static int save_files_dirs(const struct object_id *oid,\n      static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n     \n      ## tree.c ##\n    -@@ tree.c: static int read_tree_1(struct repository *r,\n    +@@ tree.c: int read_tree_at(struct repository *r,\n      \tinit_tree_desc(&desc, tree->buffer, tree->size);\n      \n      \twhile (tree_entry(&desc, &entry)) {\n    @@ tree.c: static int read_tree_1(struct repository *r,\n      \t\tif (retval != all_entries_interesting) {\n      \t\t\tretval = tree_entry_interesting(r->index, &entry,\n      \t\t\t\t\t\t\tbase, 0, pathspec);\n    -@@ tree.c: static int read_tree_1(struct repository *r,\n    +@@ tree.c: int read_tree_at(struct repository *r,\n      \t\t}\n      \n      \t\tswitch (fn(&entry.oid, base,\n    @@ tree.c: static int read_tree_1(struct repository *r,\n      \t\tcase 0:\n      \t\t\tcontinue;\n      \t\tcase READ_TREE_RECURSIVE:\n    -@@ tree.c: static int read_tree_1(struct repository *r,\n    +@@ tree.c: int read_tree_at(struct repository *r,\n      \t\t\treturn -1;\n      \t\t}\n      \n    @@ tree.c: static int read_tree_1(struct repository *r,\n      \t\t\tcommit = lookup_commit(r, &entry.oid);\n      \t\t\tif (!commit)\n      \t\t\t\tdie(\"Commit %s in submodule path %s%s not found\",\n    -@@ tree.c: static int read_tree_1(struct repository *r,\n    +@@ tree.c: int read_tree_at(struct repository *r,\n      \t\t\t\t    base->buf, entry.path);\n      \n      \t\t\toidcpy(&oid, get_commit_tree_oid(commit));\n    @@ tree.h: int cmp_cache_name_compare(const void *a_, const void *b_);\n     +\t\t\t      enum object_type, unsigned int,\n      \t\t\t      void *);\n      \n    - int read_tree_recursive(struct repository *r,\n    + int read_tree_at(struct repository *r,\n12:  625c643513d ! 12:  64dc9364bae tree-walk.h users: migrate \"p->mode &&\" pattern\n    @@ Metadata\n      ## Commit message ##\n         tree-walk.h users: migrate \"p->mode &&\" pattern\n     \n    -    Change code that dpends on \"p->mode\" either being a valid mode or zero\n    -    to use a p->object_type comparison to \"OBJ_NONE\".\n    +    Change code that depends on \"p->mode\" either being a valid mode or\n    +    zero to use a p->object_type comparison to \"OBJ_NONE\".\n     \n    -    The object_type() function in cache.h will not return OBJ_NONE, but in\n    -    this these API users are implicitly relying on the memzero() that\n    -    happens in setup_traverse_info().\n    +    The object_type() function in cache.h will not return OBJ_NONE, but\n    +    these API users are implicitly relying on the memzero() that happens\n    +    in setup_traverse_info().\n     \n         Since OBJ_NONE is \"0\" we can also rely on that being zero'd out here,\n         along with the rest of the structure. I think this is slightly less\n13:  37b28c7feff = 13:  93ed3edbbd5 tree-walk.h users: refactor chained \"mode\" if/else into switch\n14:  e0b8ec6e291 = 14:  7aa48aa34c3 tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n15:  0cd162c43d7 = 15:  3ae81621dcf merge-tree tests: test for the mode comparison in same_entry()\n16:  f8ce666d4a7 = 16:  4249ad5c4de merge-ort: correct reference to test in 62fdec17a11\n17:  4963902ba97 = 17:  e5e17505dde fsck.c: switch on \"object_type\" in fsck_walk_tree()\n18:  d74e6778009 = 18:  3f0b884f1fd tree-walk.h users: use temporary variable(s) for \"mode\"\n19:  d39db486d4e = 19:  174167613bb tree-walk.h API: formatting changes for subsequent commit\n20:  69eb956b1ab = 20:  ec76db613f2 tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n21:  cc56453e600 = 21:  11e34941729 tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n22:  ca9e3b3ad00 ! 22:  b31c106557f tree-walk.h API: Add get_tree_entry_type()\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    tree-walk.h API: Add get_tree_entry_type()\n    +    tree-walk.h API: add get_tree_entry_type()\n     \n         Add a get_tree_entry_type() helper function to compliment the existing\n    -    get_tree_entry(). Move those users of get_tree_entry_type() who didn't\n    -    care about the mode specifically, but just want to know whether the\n    -    tree entry is one of OBJ_{BLOB,COMMIT,TREE} over to it.\n    +    get_tree_entry(), and a static get_tree_entry_all() which it uses internally.\n    +\n    +    Move those users of get_tree_entry_type() who didn't care about the\n    +    mode specifically, but just want to know whether the tree entry is one\n    +    of OBJ_{BLOB,COMMIT,TREE} over to the new get_tree_entry_type().\n    +\n    +    The get_tree_entry_all() function itself will be made non-static in a\n    +    subsequent commit. I'm leaving its argument list indented accordingly\n    +    to reduce churn when I do so.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ archive.c: static void parse_treeish_arg(const char **argv,\n      \n      \t\ttree = parse_tree_indirect(&tree_oid);\n     \n    - ## blame.c ##\n    -@@ blame.c: static void verify_working_tree_path(struct repository *r,\n    - \tfor (parents = work_tree->parents; parents; parents = parents->next) {\n    - \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n    - \t\tstruct object_id blob_oid;\n    --\t\tunsigned short mode;\n    --\t\tint ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n    --\t\t\t\t\t      &mode);\n    -+\t\tenum object_type object_type;\n    -+\t\tint ret = get_tree_entry_type(r, commit_oid, path, &blob_oid,\n    -+\t\t\t\t\t      &object_type);\n    - \n    --\t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n    -+\t\tif (!ret && object_type == OBJ_BLOB)\n    - \t\t\treturn;\n    - \t}\n    - \n    -\n      ## match-trees.c ##\n     @@ match-trees.c: void shift_tree_by(struct repository *r,\n      \t\t   const char *shift_prefix)\n    @@ match-trees.c: void shift_tree_by(struct repository *r,\n     \n      ## tree-walk.c ##\n     @@ tree-walk.c: struct dir_state {\n    + \tstruct object_id oid;\n    + };\n      \n    ++static int get_tree_entry_all(struct repository *r,\n    ++\t\t\t      const struct object_id *tree_oid,\n    ++\t\t\t      const char *name,\n    ++\t\t\t      struct object_id *oid,\n    ++\t\t\t      unsigned short *mode,\n    ++\t\t\t      enum object_type *object_type);\n    ++\n      static int find_tree_entry(struct repository *r, struct tree_desc *t,\n      \t\t\t   const char *name, struct object_id *result,\n     -\t\t\t   unsigned short *mode)\n    @@ tree-walk.c: static int find_tree_entry(struct repository *r, struct tree_desc *\n     -\t\t\tconst char *name,\n     -\t\t\tstruct object_id *oid,\n     -\t\t\tunsigned short *mode)\n    -+int get_tree_entry_all(struct repository *r,\n    ++static int get_tree_entry_all(struct repository *r,\n     +\t\t       const struct object_id *tree_oid,\n     +\t\t       const char *name,\n     +\t\t       struct object_id *oid,\n    @@ tree-walk.h: struct traverse_info {\n     - * The third and fourth parameters are set to the entry's sha1 and\n     - * mode respectively.\n     + * There are variants of this function depending on what fields in the\n    -+ * \"struct name_entry\" you'd like. You always need to pointer to an\n    ++ * \"struct name_entry\" you'd like. You always need a pointer to an\n     + * appropriate variable to fill in (NULL won't do!):\n     + *\n     + * get_tree_entry_mode(): unsigned int mode\n     + * get_tree_entry_type(): enum object_type\n    -+ * get_tree_entry_all(): unsigned int mode, enum object_type\n       */\n      int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n      \t\t\tstruct object_id *,\n    @@ tree-walk.h: struct traverse_info {\n     +int get_tree_entry_type(struct repository *, const struct object_id *, const char *,\n     +\t\t\tstruct object_id *,\n     +\t\t\tenum object_type *);\n    -+int get_tree_entry_all(struct repository *, const struct object_id *, const char *,\n    -+\t\t       struct object_id *,\n    -+\t\t       unsigned short *, enum object_type *);\n      \n      /**\n       * Generate the full pathname of a tree entry based from the root of the\n24:  5986f494aa1 ! 23:  304d5d4d1af tree-walk.h API: document and format tree_entry_extract()\n    @@ tree-walk.h: struct tree_desc {\n     - * `pathp` and `modep` arguments are set to the entry's pathname and mode\n     - * respectively.\n     + * `tree_desc's` `entry` member) and return the OID of the entry.\n    -+\n    ++ *\n     + * There are variants of this function depending on what fields in the\n    -+ * \"struct name_entry\" you'd like. You always need to pointer to an\n    ++ * \"struct name_entry\" you'd like. You always need a pointer to an\n     + * appropriate variable to fill in (NULL won't do!):\n     + *\n     + * tree_entry_extract_mode(): const char *path, unsigned int mode\n25:  9b604a193b8 ! 24:  346453df356 tree-entry.h API: rename tree_entry_extract() to tree_entry_extract_mode()\n    @@ tree-diff.c: static struct combine_diff_path *emit_path(struct combine_diff_path\n      \n      \t\tisdir = S_ISDIR(mode);\n     \n    - ## tree-walk.c ##\n    -@@ tree-walk.c: static int find_tree_entry(struct repository *r, struct tree_desc *t,\n    - \t\tstruct object_id oid;\n    - \t\tint entrylen, cmp;\n    - \n    --\t\toidcpy(&oid, tree_entry_extract(t, &entry, mode));\n    -+\t\toidcpy(&oid, tree_entry_extract_mode(t, &entry, mode));\n    - \t\tentrylen = tree_entry_len(&t->entry);\n    - \t\tupdate_tree_entry(t);\n    - \t\tif (entrylen > namelen)\n    -\n      ## tree-walk.h ##\n     @@ tree-walk.h: struct tree_desc {\n       *\n26:  40878d04550 ! 25:  dd012b661e5 tree-walk.h API: add a tree_entry_extract_all() function\n    @@ Commit message\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    - ## builtin/update-index.c ##\n    -@@ builtin/update-index.c: static struct cache_entry *read_one_ent(const char *which,\n    - \t\t\t\t\tstruct object_id *ent, const char *path,\n    - \t\t\t\t\tint namelen, int stage)\n    - {\n    -+\tenum object_type object_type;\n    - \tunsigned short mode;\n    - \tstruct object_id oid;\n    - \tstruct cache_entry *ce;\n    - \n    --\tif (get_tree_entry_mode(the_repository, ent, path, &oid, &mode)) {\n    -+\tif (get_tree_entry_all(the_repository, ent, path, &oid,\n    -+\t\t\t       &mode, &object_type)) {\n    - \t\tif (which)\n    - \t\t\terror(\"%s: not in %s branch.\", path, which);\n    - \t\treturn NULL;\n    - \t}\n    --\tif (mode == S_IFDIR) {\n    -+\tif (object_type == OBJ_TREE) {\n    - \t\tif (which)\n    - \t\t\terror(\"%s: not a blob in %s branch.\", path, which);\n    - \t\treturn NULL;\n    -\n      ## tree-diff.c ##\n     @@ tree-diff.c: static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n      \tassert(t || tp);\n    @@ tree-diff.c: static struct combine_diff_path *emit_path(struct combine_diff_path\n     +\t\toid = tree_entry_extract_all(t, &path, &mode, &object_type);\n      \t\tpathlen = tree_entry_len(&t->entry);\n     -\t\tisdir = S_ISDIR(mode);\n    -+\t\tisdir = object_type == OBJ_TREE;\n    ++\t\tisdir = (object_type == OBJ_TREE);\n      \t} else {\n      \t\t/*\n      \t\t * a path was removed - take path from imin parent. Also take\n    @@ tree-walk.c: static int find_tree_entry(struct repository *r, struct tree_desc *\n      \t\tstruct object_id oid;\n      \t\tint entrylen, cmp;\n      \n    --\t\toidcpy(&oid, tree_entry_extract_mode(t, &entry, mode));\n    +-\t\toidcpy(&oid, tree_entry_extract(t, &entry, mode));\n     +\t\toidcpy(&oid, tree_entry_extract_all(t, &entry, mode, object_type));\n    -+\n      \t\tentrylen = tree_entry_len(&t->entry);\n      \t\tupdate_tree_entry(t);\n      \t\tif (entrylen > namelen)\n -:  ----------- > 26:  b6ee8410e38 tree-walk.h API: add get_tree_entry_all()\n23:  6b864e066d9 ! 27:  5c98afd9e7a tree-walk.h API: add a get_tree_entry_path() function\n    @@ tree-walk.c: int get_tree_entry_all(struct repository *r,\n     \n      ## tree-walk.h ##\n     @@ tree-walk.h: struct traverse_info {\n    -  * \"struct name_entry\" you'd like. You always need to pointer to an\n    +  * \"struct name_entry\" you'd like. You always need a pointer to an\n       * appropriate variable to fill in (NULL won't do!):\n       *\n     + * get_tree_entry_path(): <no extra argument, just get the common 'path'>\n -:  ----------- > 28:  3e7e0f7eb85 blame: emit a better error on 'git blame directory'\n27:  e4a6fae1ae0 = 29:  ac1ccf13570 tree-walk.h API: add a tree_entry_extract_type() function\n28:  766b4460a95 <  -:  ----------- tree-walk.h API users: rename \"struct name_entry\"'s \"mode\" to \"raw_mode\"\n29:  4bdf94ae5c1 <  -:  ----------- tree.h API users: rename read_tree_fn_t's \"mode\" to \"raw_mode\"\n30:  9d049fdbd00 <  -:  ----------- tree-walk.h API: move canon_mode() back out of decode_tree_entry()\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419236","messageId":"20210316021312.13927-4-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 03/29] cache.h: add a comment to object_type()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:46Z","receivedAt":"2021-03-16T02:14:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a comment to the object_type() function to explain what it\nreturns, and what the \"mode\" is in the \"else\" case.\n\nThe object_type() function dates back to 4d1012c3709 (Fix rev-list\nwhen showing objects involving submodules, 2007-11-11). It's not\nimmediately obvious to someone looking at its history and how it's\ncome to be used.\n\nDespite what Linus noted in 4d1012c3709 (Fix rev-list when showing\nobjects involving submodules, 2007-11-11) about wanting to move away\nfrom users of object_type() relying on S_ISLNK(mode) being true here\nwe do currently rely on that. If this is changed to a condition to\nonly return OBJ_BLOB on S_ISREG(mode) then t4008, t4023 and t7415 will\nhave failing tests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n cache.h | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/cache.h b/cache.h\nindex c2f8a8eadf6..ae0c0bef5c2 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -451,11 +451,16 @@ enum object_type {\n \tOBJ_MAX\n };\n \n+/*\n+ * object_type() returns an object of a type that'll appear in a tree,\n+ * so no OBJ_TAG is possible. This is mostly (and dates back to)\n+ * consumers of the tree-walk.h API's \"mode\" field.\n+ */\n static inline enum object_type object_type(unsigned int mode)\n {\n \treturn S_ISDIR(mode) ? OBJ_TREE :\n \t\tS_ISGITLINK(mode) ? OBJ_COMMIT :\n-\t\tOBJ_BLOB;\n+\t\tOBJ_BLOB; /* S_ISREG(mode) || S_ISLNK(mode) */\n }\n \n /* Double-check local_repo_env below if you add to this list. */\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419237","messageId":"20210316021312.13927-6-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 05/29] tree-walk.c: migrate to using new \"object_type\" field when possible","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:48Z","receivedAt":"2021-03-16T02:14:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.c | 24 +++++++++++++-----------\n 1 file changed, 13 insertions(+), 11 deletions(-)\n\ndiff --git a/tree-walk.c b/tree-walk.c\nindex b210967b73b..6e9161901d8 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -521,7 +521,7 @@ int traverse_trees(struct index_state *istate,\n \t\t\tif (!entry[i].path)\n \t\t\t\tcontinue;\n \t\t\tmask |= 1ul << i;\n-\t\t\tif (S_ISDIR(entry[i].mode))\n+\t\t\tif (entry[i].object_type == OBJ_TREE)\n \t\t\t\tdirmask |= 1ul << i;\n \t\t\te = &entry[i];\n \t\t}\n@@ -892,8 +892,8 @@ static int match_entry(const struct pathspec_item *item,\n \t\t * nothing else (to handle 'submod/' and 'submod'\n \t\t * uniformly).\n \t\t */\n-\t\tif (!S_ISDIR(entry->mode) &&\n-\t\t    (!S_ISGITLINK(entry->mode) || matchlen > pathlen + 1))\n+\t\tif (entry->object_type != OBJ_TREE &&\n+\t\t    (entry->object_type != OBJ_COMMIT || matchlen > pathlen + 1))\n \t\t\treturn 0;\n \t}\n \n@@ -1038,7 +1038,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t    ps->max_depth == -1)\n \t\t\treturn all_entries_interesting;\n \t\treturn within_depth(base->buf + base_offset, baselen,\n-\t\t\t\t    !!S_ISDIR(entry->mode),\n+\t\t\t\t    entry->object_type == OBJ_TREE,\n \t\t\t\t    ps->max_depth) ?\n \t\t\tentry_interesting : entry_not_interesting;\n \t}\n@@ -1071,7 +1071,7 @@ static enum interesting do_match(struct index_state *istate,\n \n \t\t\tif (within_depth(base_str + matchlen + 1,\n \t\t\t\t\t baselen - matchlen - 1,\n-\t\t\t\t\t !!S_ISDIR(entry->mode),\n+\t\t\t\t\t entry->object_type == OBJ_TREE,\n \t\t\t\t\t ps->max_depth))\n \t\t\t\tgoto interesting;\n \t\t\telse\n@@ -1094,7 +1094,8 @@ static enum interesting do_match(struct index_state *istate,\n \t\t\t\t * Match all directories. We'll try to\n \t\t\t\t * match files later on.\n \t\t\t\t */\n-\t\t\t\tif (ps->recursive && S_ISDIR(entry->mode))\n+\t\t\t\tif (ps->recursive &&\n+\t\t\t\t    entry->object_type == OBJ_TREE)\n \t\t\t\t\treturn entry_interesting;\n \n \t\t\t\t/*\n@@ -1105,7 +1106,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t\t\t * be performed in the submodule itself.\n \t\t\t\t */\n \t\t\t\tif (ps->recurse_submodules &&\n-\t\t\t\t    S_ISGITLINK(entry->mode) &&\n+\t\t\t\t    entry->object_type == OBJ_COMMIT &&\n \t\t\t\t    !ps_strncmp(item, match + baselen,\n \t\t\t\t\t\tentry->path,\n \t\t\t\t\t\titem->nowildcard_len - baselen))\n@@ -1154,7 +1155,8 @@ static enum interesting do_match(struct index_state *istate,\n \t\t * character.  More accurate matching can then\n \t\t * be performed in the submodule itself.\n \t\t */\n-\t\tif (ps->recurse_submodules && S_ISGITLINK(entry->mode) &&\n+\t\tif (ps->recurse_submodules &&\n+\t\t    entry->object_type == OBJ_COMMIT &&\n \t\t    !ps_strncmp(item, match, base->buf + base_offset,\n \t\t\t\titem->nowildcard_len)) {\n \t\t\tstrbuf_setlen(base, base_offset + baselen);\n@@ -1170,7 +1172,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t * in future, see\n \t\t * https://lore.kernel.org/git/7vmxo5l2g4.fsf@alter.siamese.dyndns.org/\n \t\t */\n-\t\tif (ps->recursive && S_ISDIR(entry->mode))\n+\t\tif (ps->recursive && entry->object_type == OBJ_TREE)\n \t\t\treturn entry_interesting;\n \t\tcontinue;\n interesting:\n@@ -1193,7 +1195,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t\t * can probably return all_entries_interesting or\n \t\t\t * all_entries_not_interesting here if matched.\n \t\t\t */\n-\t\t\tif (S_ISDIR(entry->mode))\n+\t\t\tif (entry->object_type == OBJ_TREE)\n \t\t\t\treturn entry_interesting;\n \n \t\t\tstrbuf_add(base, entry->path, pathlen);\n@@ -1269,7 +1271,7 @@ enum interesting tree_entry_interesting(struct index_state *istate,\n \t\treturn positive;\n \n \t/* #15, #19 */\n-\tif (S_ISDIR(entry->mode) &&\n+\tif (entry->object_type == OBJ_TREE &&\n \t    positive >= entry_interesting &&\n \t    negative == entry_interesting)\n \t\treturn entry_interesting;\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419238","messageId":"20210316021312.13927-7-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 06/29] cache.h: have base_name_compare() take \"is tree?\", not \"mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:49Z","receivedAt":"2021-03-16T02:14:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the base_name_compare() API and the related df_name_compare()\nfunction to take a boolean argument indicating whether the entry is a\ntree or not, instead of having them call S_ISDIR(mode) on their own.\n\nThis makes use of the new \"object_type\" field in the \"name_entry\".\n\nThe API being modified here was originally added way back in\n958ba6c96eb (Introduce \"base_name_compare()\" helper function,\n2005-05-20).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fast-import.c |  8 ++++----\n builtin/mktree.c      |  4 ++--\n cache.h               |  4 ++--\n combine-diff.c        |  8 +++++---\n match-trees.c         |  6 ++++--\n merge-ort.c           |  4 ++--\n merge-recursive.c     |  6 +++---\n read-cache.c          | 16 ++++++++--------\n tree-diff.c           |  7 +++++--\n unpack-trees.c        | 15 ++++++++-------\n 10 files changed, 43 insertions(+), 35 deletions(-)\n\ndiff --git a/builtin/fast-import.c b/builtin/fast-import.c\nindex dd4d09ceceb..ce4613c1595 100644\n--- a/builtin/fast-import.c\n+++ b/builtin/fast-import.c\n@@ -1288,8 +1288,8 @@ static int tecmp0 (const void *_a, const void *_b)\n \tstruct tree_entry *a = *((struct tree_entry**)_a);\n \tstruct tree_entry *b = *((struct tree_entry**)_b);\n \treturn base_name_compare(\n-\t\ta->name->str_dat, a->name->str_len, a->versions[0].mode,\n-\t\tb->name->str_dat, b->name->str_len, b->versions[0].mode);\n+\t\ta->name->str_dat, a->name->str_len, 1,\n+\t\tb->name->str_dat, b->name->str_len, 1);\n }\n \n static int tecmp1 (const void *_a, const void *_b)\n@@ -1297,8 +1297,8 @@ static int tecmp1 (const void *_a, const void *_b)\n \tstruct tree_entry *a = *((struct tree_entry**)_a);\n \tstruct tree_entry *b = *((struct tree_entry**)_b);\n \treturn base_name_compare(\n-\t\ta->name->str_dat, a->name->str_len, a->versions[1].mode,\n-\t\tb->name->str_dat, b->name->str_len, b->versions[1].mode);\n+\t\ta->name->str_dat, a->name->str_len, 1,\n+\t\tb->name->str_dat, b->name->str_len, 1);\n }\n \n static void mktree(struct tree_content *t, int v, struct strbuf *b)\ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex 891991b00d6..2c1973229ac 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -37,8 +37,8 @@ static int ent_compare(const void *a_, const void *b_)\n {\n \tstruct treeent *a = *(struct treeent **)a_;\n \tstruct treeent *b = *(struct treeent **)b_;\n-\treturn base_name_compare(a->name, a->len, a->mode,\n-\t\t\t\t b->name, b->len, b->mode);\n+\treturn base_name_compare(a->name, a->len, S_ISDIR(a->mode),\n+\t\t\t\t b->name, b->len, S_ISDIR(b->mode));\n }\n \n static void write_tree(struct object_id *oid)\ndiff --git a/cache.h b/cache.h\nindex ae0c0bef5c2..e38b1e1688c 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1506,8 +1506,8 @@ int repo_interpret_branch_name(struct repository *r,\n \n int validate_headref(const char *ref);\n \n-int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n-int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n+int base_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n+int df_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);\n int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);\n \ndiff --git a/combine-diff.c b/combine-diff.c\nindex 9228aebc16b..ad7058a6f5d 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -16,11 +16,13 @@\n static int compare_paths(const struct combine_diff_path *one,\n \t\t\t  const struct diff_filespec *two)\n {\n-\tif (!S_ISDIR(one->mode) && !S_ISDIR(two->mode))\n+\tint istree_one = S_ISDIR(one->mode);\n+\tint istree_two = S_ISDIR(two->mode);\n+\tif (!istree_one && !istree_two)\n \t\treturn strcmp(one->path, two->path);\n \n-\treturn base_name_compare(one->path, strlen(one->path), one->mode,\n-\t\t\t\t two->path, strlen(two->path), two->mode);\n+\treturn base_name_compare(one->path, strlen(one->path), istree_one,\n+\t\t\t\t two->path, strlen(two->path), istree_two);\n }\n \n static int filename_changed(char status)\ndiff --git a/match-trees.c b/match-trees.c\nindex 1011357ad0c..a28c19a62a5 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -67,8 +67,10 @@ static void *fill_tree_desc_strict(struct tree_desc *desc,\n static int base_name_entries_compare(const struct name_entry *a,\n \t\t\t\t     const struct name_entry *b)\n {\n-\treturn base_name_compare(a->path, tree_entry_len(a), a->mode,\n-\t\t\t\t b->path, tree_entry_len(b), b->mode);\n+\tint istree_a = (a->object_type == OBJ_TREE);\n+\tint istree_b = (b->object_type == OBJ_TREE);\n+\treturn base_name_compare(a->path, tree_entry_len(a), istree_a,\n+\t\t\t\t b->path, tree_entry_len(b), istree_b);\n }\n \n /*\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 603d30c5217..4075d13aaab 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -2390,8 +2390,8 @@ static int tree_entry_order(const void *a_, const void *b_)\n \n \tconst struct merged_info *ami = a->util;\n \tconst struct merged_info *bmi = b->util;\n-\treturn base_name_compare(a->string, strlen(a->string), ami->result.mode,\n-\t\t\t\t b->string, strlen(b->string), bmi->result.mode);\n+\treturn base_name_compare(a->string, strlen(a->string), S_ISDIR(ami->result.mode),\n+\t\t\t\t b->string, strlen(b->string), S_ISDIR(bmi->result.mode));\n }\n \n static void write_tree(struct object_id *result_oid,\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 3d9207455b3..1c9b08695a3 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -554,12 +554,12 @@ static int string_list_df_name_compare(const char *one, const char *two)\n \t *\n \t * To achieve this, we sort with df_name_compare and provide\n \t * the mode S_IFDIR so that D/F conflicts will sort correctly.\n-\t * We use the mode S_IFDIR for everything else for simplicity,\n+\t * We say we have a directory for everything else for simplicity,\n \t * since in other cases any changes in their order due to\n \t * sorting cause no problems for us.\n \t */\n-\tint cmp = df_name_compare(one, onelen, S_IFDIR,\n-\t\t\t\t  two, twolen, S_IFDIR);\n+\tint cmp = df_name_compare(one, onelen, 1, two, twolen, 1);\n+\n \t/*\n \t * Now that 'foo' and 'foo/bar' compare equal, we have to make sure\n \t * that 'foo' comes before 'foo/bar'.\ndiff --git a/read-cache.c b/read-cache.c\nindex 1e9a50c6c73..4257fbd8a08 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -462,8 +462,8 @@ int ie_modified(struct index_state *istate,\n \treturn 0;\n }\n \n-int base_name_compare(const char *name1, int len1, int mode1,\n-\t\t      const char *name2, int len2, int mode2)\n+int base_name_compare(const char *name1, int len1, int istree1,\n+\t\t      const char *name2, int len2, int istree2)\n {\n \tunsigned char c1, c2;\n \tint len = len1 < len2 ? len1 : len2;\n@@ -474,9 +474,9 @@ int base_name_compare(const char *name1, int len1, int mode1,\n \t\treturn cmp;\n \tc1 = name1[len];\n \tc2 = name2[len];\n-\tif (!c1 && S_ISDIR(mode1))\n+\tif (!c1 && istree1)\n \t\tc1 = '/';\n-\tif (!c2 && S_ISDIR(mode2))\n+\tif (!c2 && istree2)\n \t\tc2 = '/';\n \treturn (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;\n }\n@@ -491,8 +491,8 @@ int base_name_compare(const char *name1, int len1, int mode1,\n  * This is used by routines that want to traverse the git namespace\n  * but then handle conflicting entries together when possible.\n  */\n-int df_name_compare(const char *name1, int len1, int mode1,\n-\t\t    const char *name2, int len2, int mode2)\n+int df_name_compare(const char *name1, int len1, int istree1,\n+\t\t    const char *name2, int len2, int istree2)\n {\n \tint len = len1 < len2 ? len1 : len2, cmp;\n \tunsigned char c1, c2;\n@@ -504,10 +504,10 @@ int df_name_compare(const char *name1, int len1, int mode1,\n \tif (len1 == len2)\n \t\treturn 0;\n \tc1 = name1[len];\n-\tif (!c1 && S_ISDIR(mode1))\n+\tif (!c1 && istree1)\n \t\tc1 = '/';\n \tc2 = name2[len];\n-\tif (!c2 && S_ISDIR(mode2))\n+\tif (!c2 && istree2)\n \t\tc2 = '/';\n \tif (c1 == '/' && !c2)\n \t\treturn 0;\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 7cebbb327e2..6ec180331fb 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -50,6 +50,7 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)\n {\n \tstruct name_entry *e1, *e2;\n \tint cmp;\n+\tint istree_e1, istree_e2;\n \n \t/* empty descriptors sort after valid tree entries */\n \tif (!t1->size)\n@@ -58,9 +59,11 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)\n \t\treturn -1;\n \n \te1 = &t1->entry;\n+\tistree_e1 = (e1->object_type == OBJ_TREE);\n \te2 = &t2->entry;\n-\tcmp = base_name_compare(e1->path, tree_entry_len(e1), e1->mode,\n-\t\t\t\te2->path, tree_entry_len(e2), e2->mode);\n+\tistree_e2 = (e2->object_type == OBJ_TREE);\n+\tcmp = base_name_compare(e1->path, tree_entry_len(e1), istree_e1,\n+\t\t\t\te2->path, tree_entry_len(e2), istree_e2);\n \treturn cmp;\n }\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex eb8fcda31ba..d9d573df986 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -925,7 +925,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \t\t\t\t      const struct traverse_info *info,\n \t\t\t\t      const char *name, size_t namelen,\n-\t\t\t\t      unsigned mode)\n+\t\t\t\t      unsigned istree)\n {\n \tint pathlen, ce_len;\n \tconst char *ce_name;\n@@ -933,7 +933,7 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \tif (info->prev) {\n \t\tint cmp = do_compare_entry_piecewise(ce, info->prev,\n \t\t\t\t\t\t     info->name, info->namelen,\n-\t\t\t\t\t\t     info->mode);\n+\t\t\t\t\t\t     S_ISDIR(info->mode));\n \t\tif (cmp)\n \t\t\treturn cmp;\n \t}\n@@ -947,13 +947,13 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \tce_len -= pathlen;\n \tce_name = ce->name + pathlen;\n \n-\treturn df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n+\treturn df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n }\n \n static int do_compare_entry(const struct cache_entry *ce,\n \t\t\t    const struct traverse_info *info,\n \t\t\t    const char *name, size_t namelen,\n-\t\t\t    unsigned mode)\n+\t\t\t    unsigned istree)\n {\n \tint pathlen, ce_len;\n \tconst char *ce_name;\n@@ -965,7 +965,7 @@ static int do_compare_entry(const struct cache_entry *ce,\n \t * it is quicker to use the precomputed version.\n \t */\n \tif (!info->traverse_path)\n-\t\treturn do_compare_entry_piecewise(ce, info, name, namelen, mode);\n+\t\treturn do_compare_entry_piecewise(ce, info, name, namelen, istree);\n \n \tcmp = strncmp(ce->name, info->traverse_path, info->pathlen);\n \tif (cmp)\n@@ -980,12 +980,13 @@ static int do_compare_entry(const struct cache_entry *ce,\n \tce_len -= pathlen;\n \tce_name = ce->name + pathlen;\n \n-\treturn df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n+\treturn df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n }\n \n static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)\n {\n-\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);\n+\tint istree = (n->object_type == OBJ_TREE);\n+\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, istree);\n \tif (cmp)\n \t\treturn cmp;\n \n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419239","messageId":"20210316021312.13927-2-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 01/29] diff.c: remove redundant canon_mode() call","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:44Z","receivedAt":"2021-03-16T02:14:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove a call to canon_mode() from fill_filespec(). This has been\nredundant since the tree-walk.c API supplies it pre-canonicalized\nsince 7146e66f086 (tree-walk: finally switch over tree descriptors to\ncontain a pre-parsed entry, 2014-02-06).\n\nThis call to the predecessor of canon_mode() was added back in\n4130b995719 ([PATCH] Diff updates to express type changes,\n2005-05-26).\n\nThis was the only such call in the codebase. The rest are all either\none of these sorts of forms:\n\n    canon_mode(st.st_mode); /* a stat(2) struct */\n    canon_mode(S_IFREG | 0644) /* A compile-time literal */\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n diff.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/diff.c b/diff.c\nindex 6956f5e335c..bf46e6a4d8c 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -3846,7 +3846,7 @@ void fill_filespec(struct diff_filespec *spec, const struct object_id *oid,\n \t\t   int oid_valid, unsigned short mode)\n {\n \tif (mode) {\n-\t\tspec->mode = canon_mode(mode);\n+\t\tspec->mode = mode;\n \t\toidcpy(&spec->oid, oid);\n \t\tspec->oid_valid = oid_valid;\n \t}\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419240","messageId":"20210316021312.13927-5-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 04/29] tree-walk.h: add object_type member to name_entry","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:47Z","receivedAt":"2021-03-16T02:14:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Most users of the tree walking API don't care what the specific mode\nof an object in a tree is (e.g. if it's executable), they care if it's\none of OBJ_{TREE,BLOB,COMMIT}.\n\nLet's add an \"object_type\" enum to the \"name_entry\" struct to help\nsuch callers.\n\nIdeally we'd have some subset of \"enum object_type\" here with just\nthose three entries, so we could rely on the C compiler to\nexhaustively check our \"switch\" statements, but I don't know how to\ncreate such a enum subset without re-labeling OBJ_{TREE,BLOB,COMMIT}\nto e.g. \"NE_OBJ_*\" (an enum is just an int under the hood, so you can\nuse such a struct with \"OBJ_*\", but the compiler will complain...).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.c | 4 +++-\n tree-walk.h | 2 ++\n 2 files changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 2d6226d5f18..b210967b73b 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -47,7 +47,9 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l\n \n \t/* Initialize the descriptor entry */\n \tdesc->entry.path = path;\n-\tdesc->entry.mode = canon_mode(mode);\n+\tmode = canon_mode(mode);\n+\tdesc->entry.mode = mode;\n+\tdesc->entry.object_type = object_type(mode);\n \tdesc->entry.pathlen = len - 1;\n \thashcpy(desc->entry.oid.hash, (const unsigned char *)path + len);\n \ndiff --git a/tree-walk.h b/tree-walk.h\nindex a5058469e9b..9f3825d2773 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -17,6 +17,8 @@ struct name_entry {\n \tconst char *path;\n \tint pathlen;\n \tunsigned int mode;\n+\t/* simple 'mode': Only OBJ_{BLOB,TREE,COMMIT} */\n+\tenum object_type object_type;\n };\n \n /**\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419241","messageId":"20210316021312.13927-11-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 10/29] archive: get rid of 'stage' parameter","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:53Z","receivedAt":"2021-03-16T02:14:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Stop passing the \"stage\" parameter around in archive.c. This parameter\nexisted because the read_tree_recursive() function used to provide it,\nbut no longer does. See my in-flight commit to remove it. (tree.h API:\nremove \"stage\" parameter from read_tree_recursive(), 2021-03-06).\n\nAs can be seen in 562e25abea9 (archive: centralize archive entry\nwriting, 2008-07-14) and ed22b4173bd (archive: support filtering paths\nwith glob, 2014-09-21) it was never used by this code. We simply added\nit as a boilerplate, and then later added it to our own \"directory\n\"struct.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c | 8 --------\n 1 file changed, 8 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 8ff97751c23..529623167c9 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -107,7 +107,6 @@ struct directory {\n \tstruct object_id oid;\n \tint baselen, len;\n \tunsigned mode;\n-\tint stage;\n \tchar path[FLEX_ARRAY];\n };\n \n@@ -140,7 +139,6 @@ static int check_attr_export_subst(const struct attr_check *check)\n static int write_archive_entry(const struct object_id *oid, const char *base,\n \t\t\t       int baselen, const char *filename,\n \t\t\t       unsigned mode,\n-\t\t\t       int stage,\n \t\t\t       void *context)\n {\n \tstatic struct strbuf path = STRBUF_INIT;\n@@ -200,7 +198,6 @@ static int write_archive_entry(const struct object_id *oid, const char *base,\n static void queue_directory(const unsigned char *sha1,\n \t\t\t    struct strbuf *base, const char *filename,\n \t\t\t    unsigned mode,\n-\t\t\t    int stage,\n \t\t\t    struct archiver_context *c)\n {\n \tstruct directory *d;\n@@ -209,7 +206,6 @@ static void queue_directory(const unsigned char *sha1,\n \td->up\t   = c->bottom;\n \td->baselen = base->len;\n \td->mode\t   = mode;\n-\td->stage   = stage;\n \tc->bottom  = d;\n \td->len = xsnprintf(d->path, len, \"%.*s%s/\", (int)base->len, base->buf, filename);\n \thashcpy(d->oid.hash, sha1);\n@@ -229,7 +225,6 @@ static int write_directory(struct archiver_context *c)\n \t\twrite_archive_entry(&d->oid, d->path, d->baselen,\n \t\t\t\t    d->path + d->baselen,\n \t\t\t\t    d->mode,\n-\t\t\t\t    d->stage,\n \t\t\t\t    c) != READ_TREE_RECURSIVE;\n \tfree(d);\n \treturn ret ? -1 : 0;\n@@ -241,7 +236,6 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\t\t\t\tvoid *context)\n {\n \tstruct archiver_context *c = context;\n-\tint stage = 0;\n \n \twhile (c->bottom &&\n \t       !(base->len >= c->bottom->len &&\n@@ -265,7 +259,6 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\t\treturn 0;\n \t\tqueue_directory(oid->hash, base, filename,\n \t\t\t\tmode,\n-\t\t\t\tstage,\n \t\t\t\tc);\n \t\treturn READ_TREE_RECURSIVE;\n \t}\n@@ -274,7 +267,6 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\treturn -1;\n \treturn write_archive_entry(oid, base->buf, base->len, filename,\n \t\t\t\t   mode,\n-\t\t\t\t   stage,\n \t\t\t\t   context);\n }\n \n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419242","messageId":"20210316021312.13927-8-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 07/29] tree-walk.h users: switch object_type(...) to new .object_type","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:50Z","receivedAt":"2021-03-16T02:14:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change uses of object_type(entry.mode) to use the new\nentry.object_type field.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/pack-objects.c |  2 +-\n http-push.c            |  6 ++++--\n pack-bitmap-write.c    |  8 +++++---\n revision.c             | 12 ++++++++----\n 4 files changed, 18 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 6d62aaf59a0..d3ba1d4a4a6 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -1534,7 +1534,7 @@ static void add_pbase_object(struct tree_desc *tree,\n \t\t\treturn;\n \t\tif (name[cmplen] != '/') {\n \t\t\tadd_object_entry(&entry.oid,\n-\t\t\t\t\t object_type(entry.mode),\n+\t\t\t\t\t entry.object_type,\n \t\t\t\t\t fullname, 1);\n \t\t\treturn;\n \t\t}\ndiff --git a/http-push.c b/http-push.c\nindex 6a4a43e07f2..234b79a5dba 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1314,7 +1314,7 @@ static struct object_list **process_tree(struct tree *tree,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry))\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tp = process_tree(lookup_tree(the_repository, &entry.oid),\n \t\t\t\t\t p);\n@@ -1323,9 +1323,11 @@ static struct object_list **process_tree(struct tree *tree,\n \t\t\tp = process_blob(lookup_blob(the_repository, &entry.oid),\n \t\t\t\t\t p);\n \t\t\tbreak;\n-\t\tdefault:\n+\t\tcase OBJ_COMMIT:\n \t\t\t/* Subproject commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \n \tfree_tree_buffer(tree);\ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex 88d9e696a54..ac32bf2242c 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -353,7 +353,7 @@ static void fill_bitmap_tree(struct bitmap *bitmap,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry)) {\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tfill_bitmap_tree(bitmap,\n \t\t\t\t\t lookup_tree(the_repository, &entry.oid));\n@@ -361,9 +361,11 @@ static void fill_bitmap_tree(struct bitmap *bitmap,\n \t\tcase OBJ_BLOB:\n \t\t\tbitmap_set(bitmap, find_object_pos(&entry.oid));\n \t\t\tbreak;\n-\t\tdefault:\n-\t\t\t/* Gitlink, etc; not reachable */\n+\t\tcase OBJ_COMMIT:\n+\t\t\t/* submodule commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n \ndiff --git a/revision.c b/revision.c\nindex b78733f5089..1db4e4e90a2 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -72,16 +72,18 @@ static void mark_tree_contents_uninteresting(struct repository *r,\n \n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \twhile (tree_entry(&desc, &entry)) {\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tmark_tree_uninteresting(r, lookup_tree(r, &entry.oid));\n \t\t\tbreak;\n \t\tcase OBJ_BLOB:\n \t\t\tmark_blob_uninteresting(lookup_blob(r, &entry.oid));\n \t\t\tbreak;\n-\t\tdefault:\n+\t\tcase OBJ_COMMIT:\n \t\t\t/* Subproject commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n \n@@ -179,7 +181,7 @@ static void add_children_by_path(struct repository *r,\n \n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \twhile (tree_entry(&desc, &entry)) {\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tpaths_and_oids_insert(map, entry.path, &entry.oid);\n \n@@ -196,9 +198,11 @@ static void add_children_by_path(struct repository *r,\n \t\t\t\t\tchild->object.flags |= UNINTERESTING;\n \t\t\t}\n \t\t\tbreak;\n-\t\tdefault:\n+\t\tcase OBJ_COMMIT:\n \t\t\t/* Subproject commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n \n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419243","messageId":"20210316021312.13927-14-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 13/29] tree-walk.h users: refactor chained \"mode\" if/else into switch","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:56Z","receivedAt":"2021-03-16T02:14:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor a couple of \"switch\" statements that previously relied on\n\"entry.mode\" to switch on \"entry.object_type\" instead.\n\nThis is more obvious, and allows us to explicitly handle all the OBJ_*\ncases, not just have a wildcard \"else\". That doesn't matter for the\nbehavior of this code, but for its readability and maintainability.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n list-objects.c | 20 ++++++++++++++------\n walker.c       | 22 +++++++++++++---------\n 2 files changed, 27 insertions(+), 15 deletions(-)\n\ndiff --git a/list-objects.c b/list-objects.c\nindex e19589baa04..37434ba89d3 100644\n--- a/list-objects.c\n+++ b/list-objects.c\n@@ -111,6 +111,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry)) {\n+\t\tstruct tree *t;\n+\t\tstruct blob *b;\n+\n \t\tif (match != all_entries_interesting) {\n \t\t\tmatch = tree_entry_interesting(ctx->revs->repo->index,\n \t\t\t\t\t\t       &entry, base, 0,\n@@ -121,8 +124,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n \t\t\t\tcontinue;\n \t\t}\n \n-\t\tif (S_ISDIR(entry.mode)) {\n-\t\t\tstruct tree *t = lookup_tree(ctx->revs->repo, &entry.oid);\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_TREE:\n+\t\t\tt = lookup_tree(ctx->revs->repo, &entry.oid);\n \t\t\tif (!t) {\n \t\t\t\tdie(_(\"entry '%s' in tree %s has tree mode, \"\n \t\t\t\t      \"but is not a tree\"),\n@@ -130,12 +134,13 @@ static void process_tree_contents(struct traversal_context *ctx,\n \t\t\t}\n \t\t\tt->object.flags |= NOT_USER_GIVEN;\n \t\t\tprocess_tree(ctx, t, base, entry.path);\n-\t\t}\n-\t\telse if (S_ISGITLINK(entry.mode))\n+\t\t\tbreak;\n+\t\tcase OBJ_COMMIT:\n \t\t\tprocess_gitlink(ctx, entry.oid.hash,\n \t\t\t\t\tbase, entry.path);\n-\t\telse {\n-\t\t\tstruct blob *b = lookup_blob(ctx->revs->repo, &entry.oid);\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n+\t\t\tb = lookup_blob(ctx->revs->repo, &entry.oid);\n \t\t\tif (!b) {\n \t\t\t\tdie(_(\"entry '%s' in tree %s has blob mode, \"\n \t\t\t\t      \"but is not a blob\"),\n@@ -143,6 +148,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n \t\t\t}\n \t\t\tb->object.flags |= NOT_USER_GIVEN;\n \t\t\tprocess_blob(ctx, b, base, entry.path);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n }\ndiff --git a/walker.c b/walker.c\nindex 4984bf8b3d6..7ba757244e6 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -45,21 +45,25 @@ static int process_tree(struct walker *walker, struct tree *tree)\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \twhile (tree_entry(&desc, &entry)) {\n \t\tstruct object *obj = NULL;\n+\t\tstruct tree *tree;\n+\t\tstruct blob *blob;\n \n-\t\t/* submodule commits are not stored in the superproject */\n-\t\tif (S_ISGITLINK(entry.mode))\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_COMMIT:\n+\t\t\t/* submodule commits are not stored in the superproject */\n \t\t\tcontinue;\n-\t\tif (S_ISDIR(entry.mode)) {\n-\t\t\tstruct tree *tree = lookup_tree(the_repository,\n-\t\t\t\t\t\t\t&entry.oid);\n+\t\tcase OBJ_TREE:\n+\t\t\ttree = lookup_tree(the_repository, &entry.oid);\n \t\t\tif (tree)\n \t\t\t\tobj = &tree->object;\n-\t\t}\n-\t\telse {\n-\t\t\tstruct blob *blob = lookup_blob(the_repository,\n-\t\t\t\t\t\t\t&entry.oid);\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n+\t\t\tblob = lookup_blob(the_repository, &entry.oid);\n \t\t\tif (blob)\n \t\t\t\tobj = &blob->object;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t\tif (!obj || process(walker, obj))\n \t\t\treturn -1;\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419244","messageId":"20210316021312.13927-9-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 08/29] tree.h: format argument lists of read_tree_recursive() users","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:51Z","receivedAt":"2021-03-16T02:14:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for adding a new argument to read_tree_fn_t re-indent\nand format the argument list of read_tree_recursive() callbacks, and\nthe relevant functions they call.\n\nThis is a whitespace-only change to make reading subsequent commits\neasier. I'll be adding a new argument on the \"mode\" line, so leave\nspace on it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          | 14 +++++++++-----\n builtin/checkout.c |  4 +++-\n builtin/log.c      |  5 +++--\n builtin/ls-tree.c  |  4 +++-\n merge-recursive.c  |  3 ++-\n tree.h             |  5 ++++-\n 6 files changed, 24 insertions(+), 11 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex c19300ecb9b..77d1238df98 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -138,8 +138,10 @@ static int check_attr_export_subst(const struct attr_check *check)\n }\n \n static int write_archive_entry(const struct object_id *oid, const char *base,\n-\t\tint baselen, const char *filename, unsigned mode, int stage,\n-\t\tvoid *context)\n+\t\t\t       int baselen, const char *filename,\n+\t\t\t       unsigned mode,\n+\t\t\t       int stage,\n+\t\t\t       void *context)\n {\n \tstatic struct strbuf path = STRBUF_INIT;\n \tstruct archiver_context *c = context;\n@@ -230,8 +232,9 @@ static int write_directory(struct archiver_context *c)\n }\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n-\t\tstruct strbuf *base, const char *filename,\n-\t\tunsigned mode, void *context)\n+\t\t\t\t\tstruct strbuf *base, const char *filename,\n+\t\t\t\t\tunsigned mode,\n+\t\t\t\t\tvoid *context)\n {\n \tstruct archiver_context *c = context;\n \tint stage = 0;\n@@ -378,7 +381,8 @@ struct path_exists_context {\n };\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\tconst char *filename, unsigned mode,\n+\t\t\tconst char *filename,\n+\t\t\tunsigned mode,\n \t\t\tvoid *context)\n {\n \tint ret = -1;\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0e663905200..0887352db2a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -114,7 +114,9 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n }\n \n static int update_some(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, void *context)\n+\t\t       const char *pathname,\n+\t\t       unsigned mode,\n+\t\t       void *context)\n {\n \tint len;\n \tstruct cache_entry *ce;\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 980de590638..b7b76856a9f 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -598,8 +598,9 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n }\n \n static int show_tree_object(const struct object_id *oid,\n-\t\tstruct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, void *context)\n+\t\t\t    struct strbuf *base, const char *pathname,\n+\t\t\t    unsigned mode,\n+\t\t\t    void *context)\n {\n \tFILE *file = context;\n \tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3a442631c71..8d5c3fd0582 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -62,7 +62,9 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\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+\t\t     const char *pathname,\n+\t\t     unsigned mode,\n+\t\t     void *context)\n {\n \tint retval = 0;\n \tint baselen;\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1c9b08695a3..689a3e00070 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,8 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   unsigned int mode, void *context)\n+\t\t\t   unsigned int mode,\n+\t\t\t   void *context)\n {\n \tstruct path_hashmap_entry *entry;\n \tint baselen = base->len;\ndiff --git a/tree.h b/tree.h\nindex f0b079d2e91..c1a936fdc49 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -31,7 +31,10 @@ struct tree *parse_tree_indirect(const struct object_id *oid);\n int cmp_cache_name_compare(const void *a_, const void *b_);\n \n #define READ_TREE_RECURSIVE 1\n-typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n+typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *,\n+\t\t\t      const char *,\n+\t\t\t      unsigned int,\n+\t\t\t      void *);\n \n int read_tree_at(struct repository *r,\n \t\t struct tree *tree,\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419245","messageId":"20210316021312.13927-12-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 11/29] tree.h API: make read_tree_fn_t take an \"enum object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:54Z","receivedAt":"2021-03-16T02:14:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Most of the users of the read_tree_fn_t callback do not care about the\n\"mode\" per-se, they just care what type it resolves to.\n\nAmend this callback mechanism added in 3c5e8468a93 (ls-tree: major\nrewrite to do pathspec, 2005-11-26) to pass the object_type, and use\nit whenever possible.\n\nIn the archive.c code we could go much deeper with this refactoring,\nafter getting the \"mode\" that code will pass it around itself and into\narchive-{tar,zip}.c. As far as I can tell we could drop the mode\nearly, and just pass \"enum_object_type type, int is_executable\". That\nwould be slightly redundant space-wise, but would assure us that we're\nnot writing out raw modes found in trees, but are normalizing them.\n\nBut that particular refactoring would be larger than what I'm trying\nto accomplish here, so let's leave it for now.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          |  8 ++++----\n builtin/checkout.c |  4 ++--\n builtin/log.c      |  4 ++--\n builtin/ls-files.c |  6 ++++--\n builtin/ls-tree.c  | 12 +++++-------\n merge-recursive.c  |  6 ++++--\n tree.c             | 19 ++++++++++++-------\n tree.h             |  2 +-\n 8 files changed, 34 insertions(+), 27 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 529623167c9..254e15c8d03 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -232,7 +232,7 @@ static int write_directory(struct archiver_context *c)\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\t\t\t\tstruct strbuf *base, const char *filename,\n-\t\t\t\t\tunsigned mode,\n+\t\t\t\t\tenum object_type object_type, unsigned mode,\n \t\t\t\t\tvoid *context)\n {\n \tstruct archiver_context *c = context;\n@@ -245,7 +245,7 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\tc->bottom = next;\n \t}\n \n-\tif (S_ISDIR(mode)) {\n+\tif (object_type == OBJ_TREE) {\n \t\tsize_t baselen = base->len;\n \t\tconst struct attr_check *check;\n \n@@ -382,13 +382,13 @@ struct path_exists_context {\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n \t\t\tconst char *filename,\n-\t\t\tunsigned mode,\n+\t\t\tenum object_type object_type, unsigned mode,\n \t\t\tvoid *context)\n {\n \tint ret = -1;\n \tstruct path_exists_context *ctx = context;\n \n-\tif (S_ISDIR(mode)) {\n+\tif (object_type == OBJ_TREE) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tstrbuf_addbuf(&sb, base);\n \t\tstrbuf_addstr(&sb, filename);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0887352db2a..95b2b0edf22 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -115,14 +115,14 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n \n static int update_some(const struct object_id *oid, struct strbuf *base,\n \t\t       const char *pathname,\n-\t\t       unsigned mode,\n+\t\t       enum object_type object_type, unsigned mode,\n \t\t       void *context)\n {\n \tint len;\n \tstruct cache_entry *ce;\n \tint pos;\n \n-\tif (S_ISDIR(mode))\n+\tif (object_type == OBJ_TREE)\n \t\treturn READ_TREE_RECURSIVE;\n \n \tlen = base->len + strlen(pathname);\ndiff --git a/builtin/log.c b/builtin/log.c\nindex b7b76856a9f..c25f75472ff 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -599,11 +599,11 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n \n static int show_tree_object(const struct object_id *oid,\n \t\t\t    struct strbuf *base, const char *pathname,\n-\t\t\t    unsigned mode,\n+\t\t\t    enum object_type object_type, unsigned mode,\n \t\t\t    void *context)\n {\n \tFILE *file = context;\n-\tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n+\tfprintf(file, \"%s%s\\n\", pathname, object_type == OBJ_TREE ? \"/\" : \"\");\n \treturn 0;\n }\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 13bcc2d8473..b954d08ae41 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -446,7 +446,8 @@ static int read_one_entry_opt(struct index_state *istate,\n }\n \n static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\t  const char *pathname, unsigned mode,\n+\t\t\t  const char *pathname,\n+\t\t\t  enum object_type object_type, unsigned mode,\n \t\t\t  void *context)\n {\n \tstruct index_state *istate = context;\n@@ -460,7 +461,8 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n  * the stage that will conflict with the entry being added.\n  */\n static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n-\t\t\t\tconst char *pathname, unsigned mode,\n+\t\t\t\tconst char *pathname,\n+\t\t\t\tenum object_type object_type, unsigned mode,\n \t\t\t\tvoid *context)\n {\n \tstruct index_state *istate = context;\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 8d5c3fd0582..7176d2ae065 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -63,14 +63,13 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n \n static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t     const char *pathname,\n-\t\t     unsigned mode,\n+\t\t     enum object_type object_type, unsigned mode,\n \t\t     void *context)\n {\n \tint retval = 0;\n \tint baselen;\n-\tconst char *type = blob_type;\n \n-\tif (S_ISGITLINK(mode)) {\n+\tif (object_type == OBJ_COMMIT) {\n \t\t/*\n \t\t * Maybe we want to have some recursive version here?\n \t\t *\n@@ -80,22 +79,21 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\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} else if (object_type == OBJ_TREE) {\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}\n \telse if (ls_options & LS_TREE_ONLY)\n \t\treturn 0;\n \n \tif (!(ls_options & LS_NAME_ONLY)) {\n+\t\tconst char *type = type_name(object_type);\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 (object_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),\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 689a3e00070..df4b369902f 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,7 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   unsigned int mode,\n+\t\t\t   enum object_type object_type, unsigned int mode,\n \t\t\t   void *context)\n {\n \tstruct path_hashmap_entry *entry;\n@@ -467,7 +467,9 @@ static int save_files_dirs(const struct object_id *oid,\n \thashmap_add(&opt->priv->current_file_dir_set, &entry->e);\n \n \tstrbuf_setlen(base, baselen);\n-\treturn (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);\n+\tif (object_type != OBJ_TREE)\n+\t\treturn 0;\n+\treturn READ_TREE_RECURSIVE;\n }\n \n static void get_files_dirs(struct merge_options *opt, struct tree *tree)\ndiff --git a/tree.c b/tree.c\nindex 6a2a52967e4..c840bf2ea24 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -28,6 +28,8 @@ int read_tree_at(struct repository *r,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry)) {\n+\t\tstruct commit *commit;\n+\n \t\tif (retval != all_entries_interesting) {\n \t\t\tretval = tree_entry_interesting(r->index, &entry,\n \t\t\t\t\t\t\tbase, 0, pathspec);\n@@ -38,7 +40,7 @@ int read_tree_at(struct repository *r,\n \t\t}\n \n \t\tswitch (fn(&entry.oid, base,\n-\t\t\t   entry.path, entry.mode, context)) {\n+\t\t\t   entry.path, entry.object_type, entry.mode, context)) {\n \t\tcase 0:\n \t\t\tcontinue;\n \t\tcase READ_TREE_RECURSIVE:\n@@ -47,11 +49,11 @@ int read_tree_at(struct repository *r,\n \t\t\treturn -1;\n \t\t}\n \n-\t\tif (S_ISDIR(entry.mode))\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_TREE:\n \t\t\toidcpy(&oid, &entry.oid);\n-\t\telse if (S_ISGITLINK(entry.mode)) {\n-\t\t\tstruct commit *commit;\n-\n+\t\t\tbreak;\n+\t\tcase OBJ_COMMIT:\n \t\t\tcommit = lookup_commit(r, &entry.oid);\n \t\t\tif (!commit)\n \t\t\t\tdie(\"Commit %s in submodule path %s%s not found\",\n@@ -64,9 +66,12 @@ int read_tree_at(struct repository *r,\n \t\t\t\t    base->buf, entry.path);\n \n \t\t\toidcpy(&oid, get_commit_tree_oid(commit));\n-\t\t}\n-\t\telse\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n \t\t\tcontinue;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n+\t\t}\n \n \t\tlen = tree_entry_len(&entry);\n \t\tstrbuf_add(base, entry.path, len);\ndiff --git a/tree.h b/tree.h\nindex c1a936fdc49..12edb316f0c 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -33,7 +33,7 @@ int cmp_cache_name_compare(const void *a_, const void *b_);\n #define READ_TREE_RECURSIVE 1\n typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *,\n \t\t\t      const char *,\n-\t\t\t      unsigned int,\n+\t\t\t      enum object_type, unsigned int,\n \t\t\t      void *);\n \n int read_tree_at(struct repository *r,\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419246","messageId":"20210316021312.13927-16-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 15/29] merge-tree tests: test for the mode comparison in same_entry()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:58Z","receivedAt":"2021-03-16T02:14:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test to stress the \"a->mode == b->mode\" comparison in\nmerge-tree.c's same_entry().\n\nThat code was initially added by Linus in 33deb63a36f (Add\n\"merge-tree\" helper program. Maybe it's retarded, maybe it's helpful.,\n2005-04-14), and then again in its current form in\n492e0759bfe (Handling large files with GIT, 2006-02-14).\n\nHowever, nothing was testing that we handled this case\ncorrectly. Simply removing the mode comparison left all tests passing,\nbut as seen here it's important that we don't think a path with the\nsame content but different modes is the same_entry().\n\nThe rest of this series will touch code that's relevant to this, but\nwon't change its behavior. This test is just something I came up with\nin testing whether the mode test in same_entry() was needed at all.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4300-merge-tree.sh | 44 +++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 44 insertions(+)\n\ndiff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh\nindex e59601e5fe9..f783d784d02 100755\n--- a/t/t4300-merge-tree.sh\n+++ b/t/t4300-merge-tree.sh\n@@ -40,6 +40,25 @@ test_expect_success 'file add A, B (same)' '\n \ttest_must_be_empty actual\n '\n \n+test_expect_success 'file add A, B (different mode)' '\n+\tgit reset --hard initial &&\n+\ttest_commit \"add-a-b-same-diff-mode-A\" \"ONE\" \"AAA\" &&\n+\tgit reset --hard initial &&\n+\techo AAA >ONE &&\n+\ttest_chmod +x ONE &&\n+\ttest_tick &&\n+\tgit commit -m\"add-a-b-same-diff-mode-B\" &&\n+\tgit tag \"add-a-b-same-diff-mode-B\" HEAD &&\n+\tgit merge-tree initial add-a-b-same-diff-mode-A add-a-b-same-diff-mode-B >actual &&\n+\tcat >expected <<EXPECTED &&\n+added in both\n+  our    100644 $(git rev-parse add-a-b-same-diff-mode-A:ONE) ONE\n+  their  100755 $(git rev-parse add-a-b-same-diff-mode-B:ONE) ONE\n+EXPECTED\n+\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'file add A, B (different)' '\n \tgit reset --hard initial &&\n \ttest_commit \"add-a-b-diff-A\" \"ONE\" \"AAA\" &&\n@@ -61,6 +80,31 @@ EXPECTED\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'file add A, B (different and different mode)' '\n+\tgit reset --hard initial &&\n+\ttest_commit \"add-a-b-diff-diff-mode-A\" \"ONE\" \"AAA\" &&\n+\tgit reset --hard initial &&\n+\techo BBB >ONE &&\n+\ttest_chmod +x ONE &&\n+\ttest_tick &&\n+\tgit commit -m\"add-a-b-diff-diff-mode-B\" &&\n+\tgit tag \"add-a-b-diff-diff-mode-B\" &&\n+\tgit merge-tree initial add-a-b-diff-diff-mode-A add-a-b-diff-diff-mode-B >actual &&\n+\tcat >expected <<EXPECTED &&\n+added in both\n+  our    100644 $(git rev-parse add-a-b-diff-diff-mode-A:ONE) ONE\n+  their  100755 $(git rev-parse add-a-b-diff-diff-mode-B:ONE) ONE\n+@@ -1 +1,5 @@\n++<<<<<<< .our\n+ AAA\n++=======\n++BBB\n++>>>>>>> .their\n+EXPECTED\n+\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'file change A, !B' '\n \tgit reset --hard initial &&\n \ttest_commit \"change-a-not-b\" \"initial-file\" \"BBB\" &&\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419247","messageId":"20210316021312.13927-15-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 14/29] tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:57Z","receivedAt":"2021-03-16T02:14:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor more users of the \"entry.mode\" field to use the new\n\"entry.object_type\" field.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/grep.c         | 6 +++---\n builtin/merge-tree.c   | 9 +++++----\n builtin/pack-objects.c | 4 ++--\n builtin/reflog.c       | 3 ++-\n cache-tree.c           | 2 +-\n delta-islands.c        | 2 +-\n notes.c                | 4 ++--\n unpack-trees.c         | 2 +-\n 8 files changed, 17 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 4e91a253ac3..5a317cdd2f4 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -587,10 +587,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,\n \n \t\tstrbuf_add(base, entry.path, te_len);\n \n-\t\tif (S_ISREG(entry.mode)) {\n+\t\tif (entry.object_type == OBJ_BLOB) {\n \t\t\thit |= grep_oid(opt, &entry.oid, base->buf, tn_len,\n \t\t\t\t\t check_attr ? base->buf + tn_len : NULL);\n-\t\t} else if (S_ISDIR(entry.mode)) {\n+\t\t} else if (entry.object_type == OBJ_TREE) {\n \t\t\tenum object_type type;\n \t\t\tstruct tree_desc sub;\n \t\t\tvoid *data;\n@@ -606,7 +606,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,\n \t\t\thit |= grep_tree(opt, pathspec, &sub, base, tn_len,\n \t\t\t\t\t check_attr);\n \t\t\tfree(data);\n-\t\t} else if (recurse_submodules && S_ISGITLINK(entry.mode)) {\n+\t\t} else if (recurse_submodules && entry.object_type == OBJ_COMMIT) {\n \t\t\thit |= grep_submodule(opt, pathspec, &entry.oid,\n \t\t\t\t\t      base->buf, base->buf + tn_len,\n \t\t\t\t\t      1); /* ignored */\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex 2de34c2d485..12cb317c1ba 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -275,11 +275,11 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3])\n \tif (dirmask == mask)\n \t\treturn;\n \n-\tif (n[2].mode && !S_ISDIR(n[2].mode))\n+\tif (n[2].object_type != OBJ_TREE)\n \t\tentry = link_entry(3, info, n + 2, entry);\n-\tif (n[1].mode && !S_ISDIR(n[1].mode))\n+\tif (n[1].object_type != OBJ_TREE)\n \t\tentry = link_entry(2, info, n + 1, entry);\n-\tif (n[0].mode && !S_ISDIR(n[0].mode))\n+\tif (n[0].object_type != OBJ_TREE)\n \t\tentry = link_entry(1, info, n + 0, entry);\n \n \tadd_merge_entry(entry);\n@@ -324,7 +324,8 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s\n \t}\n \n \tif (same_entry(entry+0, entry+1)) {\n-\t\tif (!is_null_oid(&entry[2].oid) && !S_ISDIR(entry[2].mode)) {\n+\t\tif (!is_null_oid(&entry[2].oid) &&\n+\t\t    entry[2].object_type != OBJ_TREE) {\n \t\t\t/* We did not touch, they modified -- take theirs */\n \t\t\tresolve(info, entry+1, entry+2);\n \t\t\treturn mask;\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex d3ba1d4a4a6..f92722c12d4 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -1524,7 +1524,7 @@ static void add_pbase_object(struct tree_desc *tree,\n \tint cmp;\n \n \twhile (tree_entry(tree,&entry)) {\n-\t\tif (S_ISGITLINK(entry.mode))\n+\t\tif (entry.object_type == OBJ_COMMIT)\n \t\t\tcontinue;\n \t\tcmp = tree_entry_len(&entry) != cmplen ? 1 :\n \t\t      memcmp(name, entry.path, cmplen);\n@@ -1538,7 +1538,7 @@ static void add_pbase_object(struct tree_desc *tree,\n \t\t\t\t\t fullname, 1);\n \t\t\treturn;\n \t\t}\n-\t\tif (S_ISDIR(entry.mode)) {\n+\t\tif (entry.object_type == OBJ_TREE) {\n \t\t\tstruct tree_desc sub;\n \t\t\tstruct pbase_tree_cache *tree;\n \t\t\tconst char *down = name+cmplen+1;\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 09541d1c804..bcbca82aa90 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -95,7 +95,8 @@ static int tree_is_complete(const struct object_id *oid)\n \tcomplete = 1;\n \twhile (tree_entry(&desc, &entry)) {\n \t\tif (!has_object_file(&entry.oid) ||\n-\t\t    (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {\n+\t\t    (entry.object_type == OBJ_TREE &&\n+\t\t     !tree_is_complete(&entry.oid))) {\n \t\t\ttree->object.flags |= INCOMPLETE;\n \t\t\tcomplete = 0;\n \t\t}\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 2fb483d3c08..fbe93dd2a5f 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -726,7 +726,7 @@ static void prime_cache_tree_rec(struct repository *r,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \tcnt = 0;\n \twhile (tree_entry(&desc, &entry)) {\n-\t\tif (!S_ISDIR(entry.mode))\n+\t\tif (entry.object_type != OBJ_TREE)\n \t\t\tcnt++;\n \t\telse {\n \t\t\tstruct cache_tree_sub *sub;\ndiff --git a/delta-islands.c b/delta-islands.c\nindex aa98b2e5414..e7cf93acbe3 100644\n--- a/delta-islands.c\n+++ b/delta-islands.c\n@@ -293,7 +293,7 @@ void resolve_tree_islands(struct repository *r,\n \t\twhile (tree_entry(&desc, &entry)) {\n \t\t\tstruct object *obj;\n \n-\t\t\tif (S_ISGITLINK(entry.mode))\n+\t\t\tif (entry.object_type == OBJ_COMMIT)\n \t\t\t\tcontinue;\n \n \t\t\tobj = lookup_object(r, &entry.oid);\ndiff --git a/notes.c b/notes.c\nindex 0a5b4fa1dbb..d631dc5623e 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -418,7 +418,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\tif (path_len == 2 * (hashsz - prefix_len)) {\n \t\t\t/* This is potentially the remainder of the SHA-1 */\n \n-\t\t\tif (!S_ISREG(entry.mode))\n+\t\t\tif (entry.object_type != OBJ_BLOB)\n \t\t\t\t/* notes must be blobs */\n \t\t\t\tgoto handle_non_note;\n \n@@ -431,7 +431,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t/* This is potentially an internal node */\n \t\t\tsize_t len = prefix_len;\n \n-\t\t\tif (!S_ISDIR(entry.mode))\n+\t\t\tif (entry.object_type != OBJ_TREE)\n \t\t\t\t/* internal nodes must be trees */\n \t\t\t\tgoto handle_non_note;\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 802de46228b..33de78e32c8 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1300,7 +1300,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n \tif (dirmask) {\n \t\t/* special case: \"diff-index --cached\" looking at a tree */\n \t\tif (o->diff_index_cached &&\n-\t\t    n == 1 && dirmask == 1 && S_ISDIR(names->mode)) {\n+\t\t    n == 1 && dirmask == 1 && names->object_type == OBJ_TREE) {\n \t\t\tint matches;\n \t\t\tmatches = cache_tree_matches_traversal(o->src_index->cache_tree,\n \t\t\t\t\t\t\t       names, info);\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419248","messageId":"20210316021312.13927-17-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 16/29] merge-ort: correct reference to test in 62fdec17a11","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:59Z","receivedAt":"2021-03-16T02:14:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a comment added in 62fdec17a11 (merge-ort: flesh out\nimplementation of handle_content_merge(), 2021-01-01).\n\nThe test being referred to here was moved from t6036 in\n919df319555 (Collect merge-related tests to t64xx, 2020-08-10).\n\nIt has also had the plural of \"mode\" in the name ever since being\nintroduced in 5d1daf30cce (t6036: add a failed conflict detection\ncase: regular files, different modes, 2018-06-30).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n merge-ort.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 4375027914c..e54be179bd5 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -1079,7 +1079,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t/*\n \t\t * FIXME: If opt->priv->call_depth && !clean, then we really\n \t\t * should not make result->mode match either a->mode or\n-\t\t * b->mode; that causes t6036 \"check conflicting mode for\n+\t\t * b->mode; that causes t6416 \"check conflicting modes for\n \t\t * regular file\" to fail.  It would be best to use some other\n \t\t * mode, but we'll confuse all kinds of stuff if we use one\n \t\t * where S_ISREG(result->mode) isn't true, and if we use\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419249","messageId":"20210316021312.13927-19-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 18/29] tree-walk.h users: use temporary variable(s) for \"mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:13:01Z","receivedAt":"2021-03-16T02:14:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for an eventual rename of the \"mode\" field, add\ntemporary variable(s) in those places where it's used more than once.\n\nThis will make a subsequent commits easier to read., since we're only\ngoing to need to modify the line on which the assignment happens.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/merge-tree.c | 12 +++++++++---\n match-trees.c        | 13 +++++++------\n merge-ort.c          |  5 +++--\n notes.c              |  3 ++-\n tree-diff.c          | 13 ++++++++-----\n unpack-trees.c       |  3 ++-\n 6 files changed, 31 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex 12cb317c1ba..eec5b906561 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -190,14 +190,17 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s\n {\n \tstruct merge_list *orig, *final;\n \tconst char *path;\n+\tunsigned int orig_mode, final_mode;\n \n \t/* If it's already ours, don't bother showing it */\n \tif (!ours)\n \t\treturn;\n \n \tpath = traverse_path(info, result);\n-\torig = create_entry(2, ours->mode, &ours->oid, path);\n-\tfinal = create_entry(0, result->mode, &result->oid, path);\n+\torig_mode = ours->mode;\n+\torig = create_entry(2, orig_mode, &ours->oid, path);\n+\tfinal_mode = result->mode;\n+\tfinal = create_entry(0, final_mode, &result->oid, path);\n \n \tfinal->link = orig;\n \n@@ -241,6 +244,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n {\n \tconst char *path;\n \tstruct merge_list *link;\n+\tunsigned int link_mode;\n \n \tif (n->object_type == OBJ_NONE)\n \t\treturn entry;\n@@ -248,7 +252,9 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n \t\tpath = entry->path;\n \telse\n \t\tpath = traverse_path(info, n);\n-\tlink = create_entry(stage, n->mode, &n->oid, path);\n+\tlink_mode = n->mode;\n+\tlink = create_entry(stage, link_mode, &n->oid, path);\n+\n \tlink->link = entry;\n \treturn link;\n }\ndiff --git a/match-trees.c b/match-trees.c\nindex a28c19a62a5..f3e192ca74d 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -86,6 +86,8 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha\n \n \tfor (;;) {\n \t\tint cmp;\n+\t\tunsigned int one_mode = one.entry.mode;\n+\t\tunsigned int two_mode = two.entry.mode;\n \n \t\tif (one.size && two.size)\n \t\t\tcmp = base_name_entries_compare(&one.entry, &two.entry);\n@@ -100,22 +102,21 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha\n \n \t\tif (cmp < 0) {\n \t\t\t/* path1 does not appear in two */\n-\t\t\tscore += score_missing(one.entry.mode);\n+\t\t\tscore += score_missing(one_mode);\n \t\t\tupdate_tree_entry(&one);\n \t\t} else if (cmp > 0) {\n \t\t\t/* path2 does not appear in one */\n-\t\t\tscore += score_missing(two.entry.mode);\n+\t\t\tscore += score_missing(two_mode);\n \t\t\tupdate_tree_entry(&two);\n \t\t} else {\n+\n \t\t\t/* path appears in both */\n \t\t\tif (!oideq(&one.entry.oid, &two.entry.oid)) {\n \t\t\t\t/* they are different */\n-\t\t\t\tscore += score_differs(one.entry.mode,\n-\t\t\t\t\t\t       two.entry.mode);\n+\t\t\t\tscore += score_differs(one_mode, two_mode);\n \t\t\t} else {\n \t\t\t\t/* same subtree or blob */\n-\t\t\t\tscore += score_matches(one.entry.mode,\n-\t\t\t\t\t\t       two.entry.mode);\n+\t\t\t\tscore += score_matches(one_mode, two_mode);\n \t\t\t}\n \t\t\tupdate_tree_entry(&one);\n \t\t\tupdate_tree_entry(&two);\ndiff --git a/merge-ort.c b/merge-ort.c\nindex e54be179bd5..cad10436504 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -544,11 +544,12 @@ static void add_pair(struct merge_options *opt,\n \tstruct diff_filespec *one, *two;\n \tstruct rename_info *renames = &opt->priv->renames;\n \tint names_idx = is_add ? side : 0;\n+\tconst struct object_id *oid = &names[names_idx].oid;\n+\tunsigned int mode = names[names_idx].mode;\n \n \tone = alloc_filespec(pathname);\n \ttwo = alloc_filespec(pathname);\n-\tfill_filespec(is_add ? two : one,\n-\t\t      &names[names_idx].oid, 1, names[names_idx].mode);\n+\tfill_filespec(is_add ? two : one, oid, 1, mode);\n \tdiff_queue(&renames->pairs[side], one, two);\n }\n \ndiff --git a/notes.c b/notes.c\nindex d631dc5623e..688d03ee9cd 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -478,6 +478,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\tstruct strbuf non_note_path = STRBUF_INIT;\n \t\t\tconst char *q = oid_to_hex(&subtree->key_oid);\n \t\t\tsize_t i;\n+\t\t\tunsigned int mode = entry.mode;\n \t\t\tfor (i = 0; i < prefix_len; i++) {\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n@@ -485,7 +486,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t}\n \t\t\tstrbuf_add(&non_note_path, entry.path, path_len);\n \t\t\tadd_non_note(t, strbuf_detach(&non_note_path, NULL),\n-\t\t\t\t     entry.mode, entry.oid.hash);\n+\t\t\t\t     mode, entry.oid.hash);\n \t\t}\n \t}\n \tfree(buf);\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 6ec180331fb..088ed52d6a3 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -466,17 +466,19 @@ static struct combine_diff_path *ll_diff_tree_paths(\n \t\ttp[0].entry.mode &= ~S_IFXMIN_NEQ;\n \n \t\tfor (i = 1; i < nparent; ++i) {\n+\t\t\tunsigned int mode = tp[i].entry.mode;\n \t\t\tcmp = tree_entry_pathcmp(&tp[i], &tp[imin]);\n \t\t\tif (cmp < 0) {\n \t\t\t\timin = i;\n-\t\t\t\ttp[i].entry.mode &= ~S_IFXMIN_NEQ;\n+\t\t\t\tmode &= ~S_IFXMIN_NEQ;\n \t\t\t}\n \t\t\telse if (cmp == 0) {\n-\t\t\t\ttp[i].entry.mode &= ~S_IFXMIN_NEQ;\n+\t\t\t\tmode &= ~S_IFXMIN_NEQ;\n \t\t\t}\n \t\t\telse {\n-\t\t\t\ttp[i].entry.mode |= S_IFXMIN_NEQ;\n+\t\t\t\tmode |= S_IFXMIN_NEQ;\n \t\t\t}\n+\t\t\ttp[i].entry.mode = mode;\n \t\t}\n \n \t\t/* fixup markings for entries before imin */\n@@ -493,13 +495,14 @@ static struct combine_diff_path *ll_diff_tree_paths(\n \t\t\t/* are either pi > p[imin] or diff(t,pi) != ø ? */\n \t\t\tif (!opt->flags.find_copies_harder) {\n \t\t\t\tfor (i = 0; i < nparent; ++i) {\n+\t\t\t\t\tunsigned int mode = tp[i].entry.mode;\n \t\t\t\t\t/* p[i] > p[imin] */\n-\t\t\t\t\tif (tp[i].entry.mode & S_IFXMIN_NEQ)\n+\t\t\t\t\tif (mode & S_IFXMIN_NEQ)\n \t\t\t\t\t\tcontinue;\n \n \t\t\t\t\t/* diff(t,pi) != ø */\n \t\t\t\t\tif (!oideq(&t.entry.oid, &tp[i].entry.oid) ||\n-\t\t\t\t\t    (t.entry.mode != tp[i].entry.mode))\n+\t\t\t\t\t    (t.entry.mode != mode))\n \t\t\t\t\t\tcontinue;\n \n \t\t\t\t\tgoto skip_emit_t_tp;\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 33de78e32c8..2319b114ba0 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1023,8 +1023,9 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,\n \t\tis_transient ?\n \t\tmake_empty_transient_cache_entry(len) :\n \t\tmake_empty_cache_entry(istate, len);\n+\tunsigned int mode = n->mode;\n \n-\tce->ce_mode = create_ce_mode(n->mode);\n+\tce->ce_mode = create_ce_mode(mode);\n \tce->ce_flags = create_ce_flags(stage);\n \tce->ce_namelen = len;\n \toidcpy(&ce->oid, &n->oid);\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419250","messageId":"20210316021312.13927-18-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 17/29] fsck.c: switch on \"object_type\" in fsck_walk_tree()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:13:00Z","receivedAt":"2021-03-16T02:14:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since 7146e66f086 (tree-walk: finally switch over tree descriptors to\ncontain a pre-parsed entry, 2014-02-06) the \"mode\" is validated such\nthat we'll never reach the \"else\" clause here.\n\nGood for us that fsck_tree() has its own FSCK_MSG_BAD_FILEMODE check\nwhich we can use, added way back in 64071805eda (git-fsck-cache: be\nstricter about \"tree\" objects, 2005-07-27).\n\nExcept it really doesn't due to a regression in 7146e66f086. A\nfollow-up commit will address that, but for now we can simply rewrite\nthis code like the rest of the s/entry.mode/entry.object_type/g\nchanges I'm making.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n fsck.c | 21 +++++++++------------\n 1 file changed, 9 insertions(+), 12 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex e3030f3b358..7c74c49d329 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -396,28 +396,25 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op\n \t\tstruct object *obj;\n \t\tint result;\n \n-\t\tif (S_ISGITLINK(entry.mode))\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_COMMIT:\n \t\t\tcontinue;\n-\n-\t\tif (S_ISDIR(entry.mode)) {\n+\t\tcase OBJ_TREE:\n \t\t\tobj = (struct object *)lookup_tree(the_repository, &entry.oid);\n \t\t\tif (name && obj)\n \t\t\t\tfsck_put_object_name(options, &entry.oid, \"%s%s/\",\n \t\t\t\t\t\t     name, entry.path);\n-\t\t\tresult = options->walk(obj, OBJ_TREE, data, options);\n-\t\t}\n-\t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n \t\t\tobj = (struct object *)lookup_blob(the_repository, &entry.oid);\n \t\t\tif (name && obj)\n \t\t\t\tfsck_put_object_name(options, &entry.oid, \"%s%s\",\n \t\t\t\t\t\t     name, entry.path);\n-\t\t\tresult = options->walk(obj, OBJ_BLOB, data, options);\n-\t\t}\n-\t\telse {\n-\t\t\tresult = error(\"in tree %s: entry %s has bad mode %.6o\",\n-\t\t\t\t       fsck_describe_object(options, &tree->object.oid),\n-\t\t\t\t       entry.path, entry.mode);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n+\t\tresult = options->walk(obj, entry.object_type, data, options);\n \t\tif (result < 0)\n \t\t\treturn result;\n \t\tif (!res)\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419251","messageId":"20210316021312.13927-21-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 20/29] tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:13:03Z","receivedAt":"2021-03-16T02:14:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the get_tree_entry() function to get_tree_entry_mode(). This\nchange is only a search-replacement of the name and indentation of the\nargument lists.\n\nA subsequent commits will add get_tree_entry_type() and\nget_tree_entry_all() functions. Those changes will be much easier to\nread if we do this rename first.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c              |  8 ++++----\n blame.c                |  6 +++---\n builtin/rm.c           |  2 +-\n builtin/update-index.c |  2 +-\n line-log.c             |  2 +-\n match-trees.c          |  6 +++---\n merge-recursive.c      | 18 +++++++++---------\n notes.c                |  2 +-\n object-name.c          |  6 +++---\n tree-walk.c            | 14 +++++++-------\n tree-walk.h            |  6 +++---\n 11 files changed, 36 insertions(+), 36 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 254e15c8d03..ab031079580 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -482,10 +482,10 @@ static void parse_treeish_arg(const char **argv,\n \t\tunsigned short mode;\n \t\tint err;\n \n-\t\terr = get_tree_entry(ar_args->repo,\n-\t\t\t\t     &tree->object.oid,\n-\t\t\t\t     prefix, &tree_oid,\n-\t\t\t\t     &mode);\n+\t\terr = get_tree_entry_mode(ar_args->repo,\n+\t\t\t\t\t  &tree->object.oid,\n+\t\t\t\t\t  prefix, &tree_oid,\n+\t\t\t\t\t  &mode);\n \t\tif (err || !S_ISDIR(mode))\n \t\t\tdie(_(\"current working directory is untracked\"));\n \ndiff --git a/blame.c b/blame.c\nindex 83babc41d08..9e0543e13d4 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -102,8 +102,8 @@ static void verify_working_tree_path(struct repository *r,\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n \t\tstruct object_id blob_oid;\n \t\tunsigned short mode;\n-\t\tint ret = get_tree_entry(r, commit_oid, path, &blob_oid,\n-\t\t\t\t\t &mode);\n+\t\tint ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n+\t\t\t\t\t      &mode);\n \n \t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n \t\t\treturn;\n@@ -1239,7 +1239,7 @@ static int fill_blob_sha1_and_mode(struct repository *r,\n {\n \tif (!is_null_oid(&origin->blob_oid))\n \t\treturn 0;\n-\tif (get_tree_entry(r, &origin->commit->object.oid, origin->path, &origin->blob_oid, &origin->mode))\n+\tif (get_tree_entry_mode(r, &origin->commit->object.oid, origin->path, &origin->blob_oid, &origin->mode))\n \t\tgoto error_out;\n \tif (oid_object_info(r, &origin->blob_oid, NULL) != OBJ_BLOB)\n \t\tgoto error_out;\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex 4858631e0f0..4617388b29a 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -179,7 +179,7 @@ static int check_local_mod(struct object_id *head, int index_only)\n \t\t * way as changed from the HEAD.\n \t\t */\n \t\tif (no_head\n-\t\t     || get_tree_entry(the_repository, head, name, &oid, &mode)\n+\t\t     || get_tree_entry_mode(the_repository, head, name, &oid, &mode)\n \t\t     || ce->ce_mode != create_ce_mode(mode)\n \t\t     || !oideq(&ce->oid, &oid))\n \t\t\tstaged_changes = 1;\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 79087bccea4..070510d6a88 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -603,7 +603,7 @@ static struct cache_entry *read_one_ent(const char *which,\n \tstruct object_id oid;\n \tstruct cache_entry *ce;\n \n-\tif (get_tree_entry(the_repository, ent, path, &oid, &mode)) {\n+\tif (get_tree_entry_mode(the_repository, ent, path, &oid, &mode)) {\n \t\tif (which)\n \t\t\terror(\"%s: not in %s branch.\", path, which);\n \t\treturn NULL;\ndiff --git a/line-log.c b/line-log.c\nindex 75c8b1acfff..4790dda4607 100644\n--- a/line-log.c\n+++ b/line-log.c\n@@ -503,7 +503,7 @@ static void fill_blob_sha1(struct repository *r, struct commit *commit,\n \tunsigned short mode;\n \tstruct object_id oid;\n \n-\tif (get_tree_entry(r, &commit->object.oid, spec->path, &oid, &mode))\n+\tif (get_tree_entry_mode(r, &commit->object.oid, spec->path, &oid, &mode))\n \t\tdie(\"There is no path %s in the commit\", spec->path);\n \tfill_filespec(spec, &oid, 1, mode);\n \ndiff --git a/match-trees.c b/match-trees.c\nindex f3e192ca74d..0faacd8f4ae 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -293,7 +293,7 @@ void shift_tree(struct repository *r,\n \t\tif (!*del_prefix)\n \t\t\treturn;\n \n-\t\tif (get_tree_entry(r, hash2, del_prefix, shifted, &mode))\n+\t\tif (get_tree_entry_mode(r, hash2, del_prefix, shifted, &mode))\n \t\t\tdie(\"cannot find path %s in tree %s\",\n \t\t\t    del_prefix, oid_to_hex(hash2));\n \t\treturn;\n@@ -321,12 +321,12 @@ void shift_tree_by(struct repository *r,\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n-\tif (!get_tree_entry(r, hash1, shift_prefix, &sub1, &mode1) &&\n+\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &mode1) &&\n \t    S_ISDIR(mode1))\n \t\tcandidate |= 1;\n \n \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n-\tif (!get_tree_entry(r, hash2, shift_prefix, &sub2, &mode2) &&\n+\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &mode2) &&\n \t    S_ISDIR(mode2))\n \t\tcandidate |= 2;\n \ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex df4b369902f..bbbb68e15bc 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -487,7 +487,7 @@ static int get_tree_entry_if_blob(struct repository *r,\n {\n \tint ret;\n \n-\tret = get_tree_entry(r, tree, path, &dfs->oid, &dfs->mode);\n+\tret = get_tree_entry_mode(r, tree, path, &dfs->oid, &dfs->mode);\n \tif (S_ISDIR(dfs->mode)) {\n \t\toidcpy(&dfs->oid, &null_oid);\n \t\tdfs->mode = 0;\n@@ -1886,9 +1886,9 @@ static int tree_has_path(struct repository *r, struct tree *tree,\n \tstruct object_id hashy;\n \tunsigned short mode_o;\n \n-\treturn !get_tree_entry(r,\n-\t\t\t       &tree->object.oid, path,\n-\t\t\t       &hashy, &mode_o);\n+\treturn !get_tree_entry_mode(r,\n+\t\t\t\t    &tree->object.oid, path,\n+\t\t\t\t    &hashy, &mode_o);\n }\n \n /*\n@@ -2541,11 +2541,11 @@ static void apply_directory_rename_modifications(struct merge_options *opt,\n \t * the various handle_rename_*() functions update the index\n \t * explicitly rather than relying on unpack_trees() to have done it.\n \t */\n-\tget_tree_entry(opt->repo,\n-\t\t       &tree->object.oid,\n-\t\t       pair->two->path,\n-\t\t       &re->dst_entry->stages[stage].oid,\n-\t\t       &re->dst_entry->stages[stage].mode);\n+\tget_tree_entry_mode(opt->repo,\n+\t\t\t    &tree->object.oid,\n+\t\t\t    pair->two->path,\n+\t\t\t    &re->dst_entry->stages[stage].oid,\n+\t\t\t    &re->dst_entry->stages[stage].mode);\n \n \t/*\n \t * Record the original change status (or 'type' of change).  If it\ndiff --git a/notes.c b/notes.c\nindex 688d03ee9cd..ef138606146 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -1021,7 +1021,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\treturn;\n \tif (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))\n \t\tdie(\"Cannot use notes ref %s\", notes_ref);\n-\tif (get_tree_entry(the_repository, &object_oid, \"\", &oid, &mode))\n+\tif (get_tree_entry_mode(the_repository, &object_oid, \"\", &oid, &mode))\n \t\tdie(\"Failed to read notes tree referenced by %s (%s)\",\n \t\t    notes_ref, oid_to_hex(&object_oid));\n \ndiff --git a/object-name.c b/object-name.c\nindex 64202de60b1..7e3b2d6d739 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1704,7 +1704,7 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \tif (is_missing_file_error(errno)) {\n \t\tchar *fullname = xstrfmt(\"%s%s\", prefix, filename);\n \n-\t\tif (!get_tree_entry(r, tree_oid, fullname, &oid, &mode)) {\n+\t\tif (!get_tree_entry_mode(r, tree_oid, fullname, &oid, &mode)) {\n \t\t\tdie(_(\"path '%s' exists, but not '%s'\\n\"\n \t\t\t    \"hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?\"),\n \t\t\t    fullname,\n@@ -1903,8 +1903,8 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo,\n \t\t\t\t\tfilename, oid, &oc->symlink_path,\n \t\t\t\t\t&oc->mode);\n \t\t\t} else {\n-\t\t\t\tret = get_tree_entry(repo, &tree_oid, filename, oid,\n-\t\t\t\t\t\t     &oc->mode);\n+\t\t\t\tret = get_tree_entry_mode(repo, &tree_oid, filename, oid,\n+\t\t\t\t\t\t\t  &oc->mode);\n \t\t\t\tif (ret && only_to_die) {\n \t\t\t\t\tdiagnose_invalid_oid_path(repo, prefix,\n \t\t\t\t\t\t\t\t   filename,\ndiff --git a/tree-walk.c b/tree-walk.c\nindex e88187e3714..7819ff3e0ec 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -591,17 +591,17 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\toidcpy(result, &oid);\n \t\t\treturn 0;\n \t\t}\n-\t\treturn get_tree_entry(r, &oid, name + entrylen, result,\n-\t\t\t\t      mode);\n+\t\treturn get_tree_entry_mode(r, &oid, name + entrylen, result,\n+\t\t\t\t\t   mode);\n \t}\n \treturn -1;\n }\n \n-int get_tree_entry(struct repository *r,\n-\t\t   const struct object_id *tree_oid,\n-\t\t   const char *name,\n-\t\t   struct object_id *oid,\n-\t\t   unsigned short *mode)\n+int get_tree_entry_mode(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid,\n+\t\t\tunsigned short *mode)\n {\n \tint retval;\n \tvoid *tree;\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 478a659ee2b..eb9b9de6ccc 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -174,9 +174,9 @@ struct traverse_info {\n  * The third and fourth parameters are set to the entry's sha1 and\n  * mode respectively.\n  */\n-int get_tree_entry(struct repository *, const struct object_id *, const char *,\n-\t\t   struct object_id *,\n-\t\t   unsigned short *);\n+int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n+\t\t\tstruct object_id *,\n+\t\t\tunsigned short *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419252","messageId":"20210316021312.13927-22-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 21/29] tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:13:04Z","receivedAt":"2021-03-16T02:14:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor code added in 85e51b783c3 (Make \"subtree\" part more\northogonal to the rest of merge-recursive., 2008-06-30) to make it\nobvious that we don't care about the \"mode\" here outside of the if\nstatement it appears in.\n\nThat's opposed to the sub1 & sub2 variables, where we use the two\nobject ids later in this function.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c | 10 +++++-----\n 1 file changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex 0faacd8f4ae..e84f993a460 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -317,17 +317,17 @@ void shift_tree_by(struct repository *r,\n \t\t   const char *shift_prefix)\n {\n \tstruct object_id sub1, sub2;\n-\tunsigned short mode1, mode2;\n+\tunsigned short tmp;\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n-\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &mode1) &&\n-\t    S_ISDIR(mode1))\n+\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n+\t    S_ISDIR(tmp))\n \t\tcandidate |= 1;\n \n \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n-\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &mode2) &&\n-\t    S_ISDIR(mode2))\n+\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n+\t    S_ISDIR(tmp))\n \t\tcandidate |= 2;\n \n \tif (candidate == 3) {\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419253","messageId":"20210316021312.13927-24-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 23/29] tree-walk.h API: document and format tree_entry_extract()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:13:06Z","receivedAt":"2021-03-16T02:14:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Document and format the argument list of the tree_entry_extract()\nfunction in preparation for adding a sister function.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.h | 14 ++++++++++----\n 1 file changed, 10 insertions(+), 4 deletions(-)\n\ndiff --git a/tree-walk.h b/tree-walk.h\nindex f569960c6fb..f51485250fb 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -40,11 +40,17 @@ struct tree_desc {\n \n /**\n  * Decode the entry currently being visited (the one pointed to by\n- * `tree_desc's` `entry` member) and return the sha1 of the entry. The\n- * `pathp` and `modep` arguments are set to the entry's pathname and mode\n- * respectively.\n+ * `tree_desc's` `entry` member) and return the OID of the entry.\n+ *\n+ * There are variants of this function depending on what fields in the\n+ * \"struct name_entry\" you'd like. You always need a pointer to an\n+ * appropriate variable to fill in (NULL won't do!):\n+ *\n+ * tree_entry_extract_mode(): const char *path, unsigned int mode\n  */\n-static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned short *modep)\n+static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,\n+\t\t\t\t\t\t\t const char **pathp,\n+\t\t\t\t\t\t\t unsigned short *modep)\n {\n \t*pathp = desc->entry.path;\n \t*modep = desc->entry.mode;\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419254","messageId":"20210316021312.13927-23-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 22/29] tree-walk.h API: add get_tree_entry_type()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:13:05Z","receivedAt":"2021-03-16T02:14:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a get_tree_entry_type() helper function to compliment the existing\nget_tree_entry(), and a static get_tree_entry_all() which it uses internally.\n\nMove those users of get_tree_entry_type() who didn't care about the\nmode specifically, but just want to know whether the tree entry is one\nof OBJ_{BLOB,COMMIT,TREE} over to the new get_tree_entry_type().\n\nThe get_tree_entry_all() function itself will be made non-static in a\nsubsequent commit. I'm leaving its argument list indented accordingly\nto reduce churn when I do so.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c     |  8 ++++----\n match-trees.c | 10 +++++-----\n tree-walk.c   | 54 ++++++++++++++++++++++++++++++++++++++++-----------\n tree-walk.h   | 11 +++++++++--\n 4 files changed, 61 insertions(+), 22 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex ab031079580..d6aef83b692 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -479,14 +479,14 @@ static void parse_treeish_arg(const char **argv,\n \n \tif (prefix) {\n \t\tstruct object_id tree_oid;\n-\t\tunsigned short mode;\n+\t\tenum object_type object_type;\n \t\tint err;\n \n-\t\terr = get_tree_entry_mode(ar_args->repo,\n+\t\terr = get_tree_entry_type(ar_args->repo,\n \t\t\t\t\t  &tree->object.oid,\n \t\t\t\t\t  prefix, &tree_oid,\n-\t\t\t\t\t  &mode);\n-\t\tif (err || !S_ISDIR(mode))\n+\t\t\t\t\t  &object_type);\n+\t\tif (err || object_type != OBJ_TREE)\n \t\t\tdie(_(\"current working directory is untracked\"));\n \n \t\ttree = parse_tree_indirect(&tree_oid);\ndiff --git a/match-trees.c b/match-trees.c\nindex e84f993a460..3177558313e 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -317,17 +317,17 @@ void shift_tree_by(struct repository *r,\n \t\t   const char *shift_prefix)\n {\n \tstruct object_id sub1, sub2;\n-\tunsigned short tmp;\n+\tenum object_type tmp;\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n-\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n-\t    S_ISDIR(tmp))\n+\tif (!get_tree_entry_type(r, hash1, shift_prefix, &sub1, &tmp) &&\n+\t    tmp == OBJ_TREE)\n \t\tcandidate |= 1;\n \n \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n-\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n-\t    S_ISDIR(tmp))\n+\tif (!get_tree_entry_type(r, hash2, shift_prefix, &sub2, &tmp) &&\n+\t    tmp == OBJ_TREE)\n \t\tcandidate |= 2;\n \n \tif (candidate == 3) {\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 7819ff3e0ec..46ce1ba8069 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -559,9 +559,17 @@ struct dir_state {\n \tstruct object_id oid;\n };\n \n+static int get_tree_entry_all(struct repository *r,\n+\t\t\t      const struct object_id *tree_oid,\n+\t\t\t      const char *name,\n+\t\t\t      struct object_id *oid,\n+\t\t\t      unsigned short *mode,\n+\t\t\t      enum object_type *object_type);\n+\n static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\t   const char *name, struct object_id *result,\n-\t\t\t   unsigned short *mode)\n+\t\t\t   unsigned short *mode,\n+\t\t\t   enum object_type *object_type)\n {\n \tint namelen = strlen(name);\n \twhile (t->size) {\n@@ -585,23 +593,24 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t}\n \t\tif (name[entrylen] != '/')\n \t\t\tcontinue;\n-\t\tif (!S_ISDIR(*mode))\n+\t\tif (*object_type != OBJ_TREE)\n \t\t\tbreak;\n \t\tif (++entrylen == namelen) {\n \t\t\toidcpy(result, &oid);\n \t\t\treturn 0;\n \t\t}\n-\t\treturn get_tree_entry_mode(r, &oid, name + entrylen, result,\n-\t\t\t\t\t   mode);\n+\t\treturn get_tree_entry_all(r, &oid, name + entrylen, result,\n+\t\t\t\t\t  mode, object_type);\n \t}\n \treturn -1;\n }\n \n-int get_tree_entry_mode(struct repository *r,\n-\t\t\tconst struct object_id *tree_oid,\n-\t\t\tconst char *name,\n-\t\t\tstruct object_id *oid,\n-\t\t\tunsigned short *mode)\n+static int get_tree_entry_all(struct repository *r,\n+\t\t       const struct object_id *tree_oid,\n+\t\t       const char *name,\n+\t\t       struct object_id *oid,\n+\t\t       unsigned short *mode,\n+\t\t       enum object_type *object_type)\n {\n \tint retval;\n \tvoid *tree;\n@@ -624,12 +633,34 @@ int get_tree_entry_mode(struct repository *r,\n \t\tstruct tree_desc t;\n \t\tinit_tree_desc(&t, tree, size);\n \t\tretval = find_tree_entry(r, &t, name, oid,\n-\t\t\t\t\t mode);\n+\t\t\t\t\t mode, object_type);\n \t}\n \tfree(tree);\n \treturn retval;\n }\n \n+int get_tree_entry_mode(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid,\n+\t\t\tunsigned short *mode)\n+{\n+\tenum object_type object_type;\n+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n+\t\t\t\t  mode, &object_type);\n+}\n+\n+int get_tree_entry_type(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid,\n+\t\t\tenum object_type *object_type)\n+{\n+\tunsigned short mode;\n+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n+\t\t\t\t  &mode, object_type);\n+}\n+\n /*\n  * This is Linux's built-in max for the number of symlinks to follow.\n  * That limit, of course, does not affect git, but it's a reasonable\n@@ -674,6 +705,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n \t\tint find_result;\n \t\tchar *first_slash;\n \t\tchar *remainder = NULL;\n+\t\tenum object_type object_type;\n \n \t\tif (!t.buffer) {\n \t\t\tvoid *tree;\n@@ -751,7 +783,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n \t\t/* Look up the first (or only) path component in the tree. */\n \t\tfind_result = find_tree_entry(r, &t, namebuf.buf,\n \t\t\t\t\t      &current_tree_oid,\n-\t\t\t\t\t      mode);\n+\t\t\t\t\t      mode, &object_type);\n \t\tif (find_result) {\n \t\t\tgoto done;\n \t\t}\ndiff --git a/tree-walk.h b/tree-walk.h\nindex eb9b9de6ccc..f569960c6fb 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -171,12 +171,19 @@ struct traverse_info {\n  * Find an entry in a tree given a pathname and the sha1 of a tree to\n  * search. Returns 0 if the entry is found and -1 otherwise.\n  *\n- * The third and fourth parameters are set to the entry's sha1 and\n- * mode respectively.\n+ * There are variants of this function depending on what fields in the\n+ * \"struct name_entry\" you'd like. You always need a pointer to an\n+ * appropriate variable to fill in (NULL won't do!):\n+ *\n+ * get_tree_entry_mode(): unsigned int mode\n+ * get_tree_entry_type(): enum object_type\n  */\n int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n \t\t\tunsigned short *);\n+int get_tree_entry_type(struct repository *, const struct object_id *, const char *,\n+\t\t\tstruct object_id *,\n+\t\t\tenum object_type *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419257","messageId":"20210316021312.13927-10-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 09/29] tree.h users: format argument lists in archive.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:52Z","receivedAt":"2021-03-16T02:14:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Re-indent and re-flow the argument lists archive.c has downstream of\nits read_tree_recursive() call to make subsequent commits easier to\nread, as I only expect to be modifying the \"stage\" and \"mode\" lines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c | 22 +++++++++++++++-------\n 1 file changed, 15 insertions(+), 7 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 77d1238df98..8ff97751c23 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -198,8 +198,10 @@ static int write_archive_entry(const struct object_id *oid, const char *base,\n }\n \n static void queue_directory(const unsigned char *sha1,\n-\t\tstruct strbuf *base, const char *filename,\n-\t\tunsigned mode, int stage, struct archiver_context *c)\n+\t\t\t    struct strbuf *base, const char *filename,\n+\t\t\t    unsigned mode,\n+\t\t\t    int stage,\n+\t\t\t    struct archiver_context *c)\n {\n \tstruct directory *d;\n \tsize_t len = st_add4(base->len, 1, strlen(filename), 1);\n@@ -225,8 +227,10 @@ static int write_directory(struct archiver_context *c)\n \tret =\n \t\twrite_directory(c) ||\n \t\twrite_archive_entry(&d->oid, d->path, d->baselen,\n-\t\t\t\t    d->path + d->baselen, d->mode,\n-\t\t\t\t    d->stage, c) != READ_TREE_RECURSIVE;\n+\t\t\t\t    d->path + d->baselen,\n+\t\t\t\t    d->mode,\n+\t\t\t\t    d->stage,\n+\t\t\t\t    c) != READ_TREE_RECURSIVE;\n \tfree(d);\n \treturn ret ? -1 : 0;\n }\n@@ -260,14 +264,18 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\tif (check_attr_export_ignore(check))\n \t\t\treturn 0;\n \t\tqueue_directory(oid->hash, base, filename,\n-\t\t\t\tmode, stage, c);\n+\t\t\t\tmode,\n+\t\t\t\tstage,\n+\t\t\t\tc);\n \t\treturn READ_TREE_RECURSIVE;\n \t}\n \n \tif (write_directory(c))\n \t\treturn -1;\n-\treturn write_archive_entry(oid, base->buf, base->len, filename, mode,\n-\t\t\t\t   stage, context);\n+\treturn write_archive_entry(oid, base->buf, base->len, filename,\n+\t\t\t\t   mode,\n+\t\t\t\t   stage,\n+\t\t\t\t   context);\n }\n \n struct extra_file_info {\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419255","messageId":"20210316021312.13927-27-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 26/29] tree-walk.h API: add get_tree_entry_all()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:13:09Z","receivedAt":"2021-03-16T02:14:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a get_tree_entry_all() function and use it in the one caller who\ncares about both the mode and the object type. Refactor it accordingly\nto make it clear which parts care about the mode, and which about the\nobject_type.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/update-index.c | 6 ++++--\n tree-walk.c            | 9 +--------\n tree-walk.h            | 4 ++++\n 3 files changed, 9 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 070510d6a88..b489a876392 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -599,16 +599,18 @@ static struct cache_entry *read_one_ent(const char *which,\n \t\t\t\t\tstruct object_id *ent, const char *path,\n \t\t\t\t\tint namelen, int stage)\n {\n+\tenum object_type object_type;\n \tunsigned short mode;\n \tstruct object_id oid;\n \tstruct cache_entry *ce;\n \n-\tif (get_tree_entry_mode(the_repository, ent, path, &oid, &mode)) {\n+\tif (get_tree_entry_all(the_repository, ent, path, &oid,\n+\t\t\t       &mode, &object_type)) {\n \t\tif (which)\n \t\t\terror(\"%s: not in %s branch.\", path, which);\n \t\treturn NULL;\n \t}\n-\tif (mode == S_IFDIR) {\n+\tif (object_type == OBJ_TREE) {\n \t\tif (which)\n \t\t\terror(\"%s: not a blob in %s branch.\", path, which);\n \t\treturn NULL;\ndiff --git a/tree-walk.c b/tree-walk.c\nindex f4473276c9f..a90dbf87af4 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -559,13 +559,6 @@ struct dir_state {\n \tstruct object_id oid;\n };\n \n-static int get_tree_entry_all(struct repository *r,\n-\t\t\t      const struct object_id *tree_oid,\n-\t\t\t      const char *name,\n-\t\t\t      struct object_id *oid,\n-\t\t\t      unsigned short *mode,\n-\t\t\t      enum object_type *object_type);\n-\n static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\t   const char *name, struct object_id *result,\n \t\t\t   unsigned short *mode,\n@@ -605,7 +598,7 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \treturn -1;\n }\n \n-static int get_tree_entry_all(struct repository *r,\n+int get_tree_entry_all(struct repository *r,\n \t\t       const struct object_id *tree_oid,\n \t\t       const char *name,\n \t\t       struct object_id *oid,\ndiff --git a/tree-walk.h b/tree-walk.h\nindex a4c54871747..55ef88ef2e5 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -195,6 +195,7 @@ struct traverse_info {\n  *\n  * get_tree_entry_mode(): unsigned int mode\n  * get_tree_entry_type(): enum object_type\n+ * get_tree_entry_all(): unsigned int mode, enum object_type\n  */\n int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n@@ -202,6 +203,9 @@ int get_tree_entry_mode(struct repository *, const struct object_id *, const cha\n int get_tree_entry_type(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n \t\t\tenum object_type *);\n+int get_tree_entry_all(struct repository *, const struct object_id *, const char *,\n+\t\t       struct object_id *,\n+\t\t       unsigned short *, enum object_type *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419256","messageId":"20210316021312.13927-25-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 24/29] tree-entry.h API: rename tree_entry_extract() to tree_entry_extract_mode()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:13:07Z","receivedAt":"2021-03-16T02:14:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As with the recent split of the get_tree_entry() function, rename the\ntree_entry_extract() function to *_mode() in preparation for adding\nother variants of it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n fsck.c        | 2 +-\n match-trees.c | 4 ++--\n tree-diff.c   | 4 ++--\n tree-walk.h   | 6 +++---\n 4 files changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 7c74c49d329..11678ba5826 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -670,7 +670,7 @@ static int fsck_tree(const struct object_id *oid,\n \t\tconst char *name, *backslash;\n \t\tconst struct object_id *oid;\n \n-\t\toid = tree_entry_extract(&desc, &name, &mode);\n+\t\toid = tree_entry_extract_mode(&desc, &name, &mode);\n \n \t\thas_null_sha1 |= is_null_oid(oid);\n \t\thas_full_path |= !!strchr(name, '/');\ndiff --git a/match-trees.c b/match-trees.c\nindex 3177558313e..2afa4968109 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -146,7 +146,7 @@ static void match_trees(const struct object_id *hash1,\n \t\tunsigned short mode;\n \t\tint score;\n \n-\t\telem = tree_entry_extract(&one, &path, &mode);\n+\t\telem = tree_entry_extract_mode(&one, &path, &mode);\n \t\tif (!S_ISDIR(mode))\n \t\t\tgoto next;\n \t\tscore = score_trees(elem, hash2);\n@@ -202,7 +202,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \t\tunsigned short mode;\n \t\tint len = tree_entry_len(&desc.entry);\n \n-\t\ttree_entry_extract(&desc, &name, &mode);\n+\t\ttree_entry_extract_mode(&desc, &name, &mode);\n \t\tif (len == toplen &&\n \t\t    !memcmp(name, prefix, toplen)) {\n \t\t\tif (!S_ISDIR(mode))\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 088ed52d6a3..65c7e4dbc8b 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -196,7 +196,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \n \tif (t) {\n \t\t/* path present in resulting tree */\n-\t\toid = tree_entry_extract(t, &path, &mode);\n+\t\toid = tree_entry_extract_mode(t, &path, &mode);\n \t\tpathlen = tree_entry_len(&t->entry);\n \t\tisdir = S_ISDIR(mode);\n \t} else {\n@@ -207,7 +207,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \t\t * 1) all modes for tp[i]=tp[imin] should be the same wrt\n \t\t *    S_ISDIR, thanks to base_name_compare().\n \t\t */\n-\t\ttree_entry_extract(&tp[imin], &path, &mode);\n+\t\ttree_entry_extract_mode(&tp[imin], &path, &mode);\n \t\tpathlen = tree_entry_len(&tp[imin].entry);\n \n \t\tisdir = S_ISDIR(mode);\ndiff --git a/tree-walk.h b/tree-walk.h\nindex f51485250fb..805cda649ee 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -48,9 +48,9 @@ struct tree_desc {\n  *\n  * tree_entry_extract_mode(): const char *path, unsigned int mode\n  */\n-static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,\n-\t\t\t\t\t\t\t const char **pathp,\n-\t\t\t\t\t\t\t unsigned short *modep)\n+static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *desc,\n+\t\t\t\t\t\t\t      const char **pathp,\n+\t\t\t\t\t\t\t      unsigned short *modep)\n {\n \t*pathp = desc->entry.path;\n \t*modep = desc->entry.mode;\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419258","messageId":"20210316021312.13927-26-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 25/29] tree-walk.h API: add a tree_entry_extract_all() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:13:08Z","receivedAt":"2021-03-16T02:14:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a tree_entry_extract_all() sibling function to the existing\ntree_entry_extract_mode().\n\nHaving the OBJ_{BLOB,TREE,COMMIT} when you have the \"mode\" is strictly\nspeaking redundant, but hopefully makes it easier to read the\ncode. We'll now see which parts of the code are checking the types,\nv.s. those that care about the mode specifically.\n\nOnly the first use of tree_entry_extract_mode() in emit_path() is\nconverted here, the other branch will use a new\nget_tree_entry_mode_type() introduced in a subsequent commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-diff.c |  5 +++--\n tree-walk.c |  2 +-\n tree-walk.h | 12 ++++++++++++\n 3 files changed, 16 insertions(+), 3 deletions(-)\n\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 65c7e4dbc8b..918ad95fa61 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -195,10 +195,11 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \tassert(t || tp);\n \n \tif (t) {\n+\t\tenum object_type object_type;\n \t\t/* path present in resulting tree */\n-\t\toid = tree_entry_extract_mode(t, &path, &mode);\n+\t\toid = tree_entry_extract_all(t, &path, &mode, &object_type);\n \t\tpathlen = tree_entry_len(&t->entry);\n-\t\tisdir = S_ISDIR(mode);\n+\t\tisdir = (object_type == OBJ_TREE);\n \t} else {\n \t\t/*\n \t\t * a path was removed - take path from imin parent. Also take\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 46ce1ba8069..f4473276c9f 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -577,7 +577,7 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\tstruct object_id oid;\n \t\tint entrylen, cmp;\n \n-\t\toidcpy(&oid, tree_entry_extract(t, &entry, mode));\n+\t\toidcpy(&oid, tree_entry_extract_all(t, &entry, mode, object_type));\n \t\tentrylen = tree_entry_len(&t->entry);\n \t\tupdate_tree_entry(t);\n \t\tif (entrylen > namelen)\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 805cda649ee..a4c54871747 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -47,6 +47,7 @@ struct tree_desc {\n  * appropriate variable to fill in (NULL won't do!):\n  *\n  * tree_entry_extract_mode(): const char *path, unsigned int mode\n+ * tree_entry_extract_all(): const char *path, unsigned int mode, enum object_type\n  */\n static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *desc,\n \t\t\t\t\t\t\t      const char **pathp,\n@@ -57,6 +58,17 @@ static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *\n \treturn &desc->entry.oid;\n }\n \n+static inline const struct object_id *tree_entry_extract_all(struct tree_desc *desc,\n+\t\t\t\t\t\t\t     const char **pathp,\n+\t\t\t\t\t\t\t     unsigned short *modep,\n+\t\t\t\t\t\t\t     enum object_type *object_typep)\n+{\n+\t*pathp = desc->entry.path;\n+\t*modep = desc->entry.mode;\n+\t*object_typep = desc->entry.object_type;\n+\treturn &desc->entry.oid;\n+}\n+\n /**\n  * Calculate the length of a tree entry's pathname. This utilizes the\n  * memory structure of a tree entry to avoid the overhead of using a\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419259","messageId":"20210316021312.13927-13-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 12/29] tree-walk.h users: migrate \"p->mode &&\" pattern","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:12:55Z","receivedAt":"2021-03-16T02:14:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change code that depends on \"p->mode\" either being a valid mode or\nzero to use a p->object_type comparison to \"OBJ_NONE\".\n\nThe object_type() function in cache.h will not return OBJ_NONE, but\nthese API users are implicitly relying on the memzero() that happens\nin setup_traverse_info().\n\nSince OBJ_NONE is \"0\" we can also rely on that being zero'd out here,\nalong with the rest of the structure. I think this is slightly less\nclever than \"mode not set\", and helps to get rid of more uses of\n\"mode\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/merge-tree.c | 9 +++++----\n merge-ort.c          | 2 +-\n unpack-trees.c       | 4 ++--\n 3 files changed, 8 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex de8520778d2..2de34c2d485 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -214,7 +214,7 @@ static void unresolved_directory(const struct traverse_info *info,\n \tvoid *buf0, *buf1, *buf2;\n \n \tfor (p = n; p < n + 3; p++) {\n-\t\tif (p->mode && S_ISDIR(p->mode))\n+\t\tif (p->object_type == OBJ_TREE)\n \t\t\tbreak;\n \t}\n \tif (n + 3 <= p)\n@@ -222,7 +222,7 @@ static void unresolved_directory(const struct traverse_info *info,\n \n \tnewbase = traverse_path(info, p);\n \n-#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL)\n+#define ENTRY_OID(e) (((e)->object_type == OBJ_TREE) ? &(e)->oid : NULL)\n \tbuf0 = fill_tree_descriptor(r, t + 0, ENTRY_OID(n + 0));\n \tbuf1 = fill_tree_descriptor(r, t + 1, ENTRY_OID(n + 1));\n \tbuf2 = fill_tree_descriptor(r, t + 2, ENTRY_OID(n + 2));\n@@ -242,7 +242,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n \tconst char *path;\n \tstruct merge_list *link;\n \n-\tif (!n->mode)\n+\tif (n->object_type == OBJ_NONE)\n \t\treturn entry;\n \tif (entry)\n \t\tpath = entry->path;\n@@ -265,7 +265,8 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3])\n \t\t * Treat missing entries as directories so that we return\n \t\t * after unresolved_directory has handled this.\n \t\t */\n-\t\tif (!n[i].mode || S_ISDIR(n[i].mode))\n+\t\tif (n[i].object_type == OBJ_NONE ||\n+\t\t    n[i].object_type == OBJ_TREE)\n \t\t\tdirmask |= (1 << i);\n \t}\n \ndiff --git a/merge-ort.c b/merge-ort.c\nindex 4075d13aaab..4375027914c 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -668,7 +668,7 @@ static int collect_merge_info_callback(int n,\n \t * setup_path_info() for tracking.\n \t */\n \tp = names;\n-\twhile (!p->mode)\n+\twhile (p->object_type == OBJ_NONE)\n \t\tp++;\n \tlen = traverse_path_len(info, p->pathlen);\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex d9d573df986..802de46228b 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -862,7 +862,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n \t}\n \n \tp = names;\n-\twhile (!p->mode)\n+\twhile (p->object_type == OBJ_NONE)\n \t\tp++;\n \n \tnewinfo = *info;\n@@ -1242,7 +1242,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n \tconst struct name_entry *p = names;\n \n \t/* Find first entry with a real name (we could use \"mask\" too) */\n-\twhile (!p->mode)\n+\twhile (p->object_type == OBJ_NONE)\n \t\tp++;\n \n \tif (o->debug_unpack)\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419260","messageId":"20210316021312.13927-28-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 27/29] tree-walk.h API: add a get_tree_entry_path() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:13:10Z","receivedAt":"2021-03-16T02:14:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a get_tree_entry_path() variant in addition to\nget_tree_entry_path_{mode,type,all}(). This is for those callers that\nneed neither the mode nor \"enum object_type\" parameters filled for\nthem.\n\nThere's callers here which doesn't need the \"struct object_id\" filled\neither, and provides a throwaway variable for us.\n\nSee the following commits for the introduction of such code that's\nbeing modified here:\n\n - shift_tree(): 68faf68938e (A new merge stragety 'subtree'.,\n    2007-02-15) for the shift_tree()\n\n - tree_has_path(): 96e7ffbdc31 (merge-recursive: check for directory\n   level conflicts, 2018-04-19)\n\n - init_notes(): fd53c9eb445 (Speed up git notes lookup, 2009-10-09)\n\n - diagnose_invalid_oid_path(): 009fee4774d (Detailed diagnosis when\n   parsing an object name fails., 2009-12-07)\n\nThose could potentially be refactored too, but I've got to stop at\nsome point, and right now I'm focusing downstream code that depends on\n\"mode\" (or \"enum object_type\").\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c     |  4 +---\n merge-recursive.c |  6 ++----\n notes.c           |  3 +--\n object-name.c     |  3 +--\n tree-walk.c       | 11 +++++++++++\n tree-walk.h       |  3 +++\n 6 files changed, 19 insertions(+), 11 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex 2afa4968109..25bfb46fb02 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -288,12 +288,10 @@ void shift_tree(struct repository *r,\n \n \tif (add_score < del_score) {\n \t\t/* We need to pick a subtree of two */\n-\t\tunsigned short mode;\n-\n \t\tif (!*del_prefix)\n \t\t\treturn;\n \n-\t\tif (get_tree_entry_mode(r, hash2, del_prefix, shifted, &mode))\n+\t\tif (get_tree_entry_path(r, hash2, del_prefix, shifted))\n \t\t\tdie(\"cannot find path %s in tree %s\",\n \t\t\t    del_prefix, oid_to_hex(hash2));\n \t\treturn;\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex bbbb68e15bc..83d2b8b8440 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1884,11 +1884,9 @@ static int tree_has_path(struct repository *r, struct tree *tree,\n \t\t\t const char *path)\n {\n \tstruct object_id hashy;\n-\tunsigned short mode_o;\n-\n-\treturn !get_tree_entry_mode(r,\n+\treturn !get_tree_entry_path(r,\n \t\t\t\t    &tree->object.oid, path,\n-\t\t\t\t    &hashy, &mode_o);\n+\t\t\t\t    &hashy);\n }\n \n /*\ndiff --git a/notes.c b/notes.c\nindex ef138606146..aa46cb2b09e 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -994,7 +994,6 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\tcombine_notes_fn combine_notes, int flags)\n {\n \tstruct object_id oid, object_oid;\n-\tunsigned short mode;\n \tstruct leaf_node root_tree;\n \n \tif (!t)\n@@ -1021,7 +1020,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\treturn;\n \tif (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))\n \t\tdie(\"Cannot use notes ref %s\", notes_ref);\n-\tif (get_tree_entry_mode(the_repository, &object_oid, \"\", &oid, &mode))\n+\tif (get_tree_entry_path(the_repository, &object_oid, \"\", &oid))\n \t\tdie(\"Failed to read notes tree referenced by %s (%s)\",\n \t\t    notes_ref, oid_to_hex(&object_oid));\n \ndiff --git a/object-name.c b/object-name.c\nindex 7e3b2d6d739..9ff5f83c1ff 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1693,7 +1693,6 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \t\t\t\t      int object_name_len)\n {\n \tstruct object_id oid;\n-\tunsigned short mode;\n \n \tif (!prefix)\n \t\tprefix = \"\";\n@@ -1704,7 +1703,7 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \tif (is_missing_file_error(errno)) {\n \t\tchar *fullname = xstrfmt(\"%s%s\", prefix, filename);\n \n-\t\tif (!get_tree_entry_mode(r, tree_oid, fullname, &oid, &mode)) {\n+\t\tif (!get_tree_entry_path(r, tree_oid, fullname, &oid)) {\n \t\t\tdie(_(\"path '%s' exists, but not '%s'\\n\"\n \t\t\t    \"hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?\"),\n \t\t\t    fullname,\ndiff --git a/tree-walk.c b/tree-walk.c\nindex a90dbf87af4..fa846535dfb 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -632,6 +632,17 @@ int get_tree_entry_all(struct repository *r,\n \treturn retval;\n }\n \n+int get_tree_entry_path(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid)\n+{\n+\tunsigned short mode;\n+\tenum object_type object_type;\n+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n+\t\t\t\t  &mode, &object_type);\n+}\n+\n int get_tree_entry_mode(struct repository *r,\n \t\t\tconst struct object_id *tree_oid,\n \t\t\tconst char *name,\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 55ef88ef2e5..efcd7ccd10e 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -193,10 +193,13 @@ struct traverse_info {\n  * \"struct name_entry\" you'd like. You always need a pointer to an\n  * appropriate variable to fill in (NULL won't do!):\n  *\n+ * get_tree_entry_path(): <no extra argument, just get the common 'path'>\n  * get_tree_entry_mode(): unsigned int mode\n  * get_tree_entry_type(): enum object_type\n  * get_tree_entry_all(): unsigned int mode, enum object_type\n  */\n+int get_tree_entry_path(struct repository *, const struct object_id *, const char *,\n+\t\t\tstruct object_id *);\n int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n \t\t\tunsigned short *);\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419261","messageId":"20210316021312.13927-29-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 28/29] blame: emit a better error on 'git blame directory'","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:13:11Z","receivedAt":"2021-03-16T02:14:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change an early check for non-blobs in verify_working_tree_path() to\nlet any such objects pass, and instead die shortly thereafter in the\nfake_working_tree_commit() caller's type check.\n\nNow e.g. doing \"git blame t\" in git.git emits:\n\n    fatal: unsupported file type t\n\nInstead of:\n\n    fatal: no such path 't' in HEAD\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n blame.c                         |  8 ++------\n t/t8004-blame-with-conflicts.sh | 20 ++++++++++++++++++++\n 2 files changed, 22 insertions(+), 6 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 9e0543e13d4..7da162cd582 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -100,12 +100,8 @@ static void verify_working_tree_path(struct repository *r,\n \n \tfor (parents = work_tree->parents; parents; parents = parents->next) {\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n-\t\tstruct object_id blob_oid;\n-\t\tunsigned short mode;\n-\t\tint ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n-\t\t\t\t\t      &mode);\n-\n-\t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n+\t\tstruct object_id oid;\n+\t\tif (!get_tree_entry_path(r, commit_oid, path, &oid))\n \t\t\treturn;\n \t}\n \ndiff --git a/t/t8004-blame-with-conflicts.sh b/t/t8004-blame-with-conflicts.sh\nindex 35414a53363..6caa504a0ea 100755\n--- a/t/t8004-blame-with-conflicts.sh\n+++ b/t/t8004-blame-with-conflicts.sh\n@@ -73,4 +73,24 @@ test_expect_success 'blame does not crash with conflicted file in stages 1,3' '\n \tgit blame file1\n '\n \n+test_expect_success 'setup second case' '\n+\tgit merge --abort\n+'\n+\n+test_expect_success 'blame on directory/file conflict' '\n+\tmkdir d &&\n+\ttest_commit second &&\n+\ttest_commit d/file &&\n+\ttest_must_fail git blame d 2>expected &&\n+\n+\tgit reset --hard second &&\n+\t>d &&\n+\tgit add d &&\n+\tgit commit -m\"a not-a-dir\" &&\n+\ttest_must_fail git merge d/file &&\n+\n+\ttest_must_fail git blame d 2>actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419262","messageId":"20210316021312.13927-30-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 29/29] tree-walk.h API: add a tree_entry_extract_type() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:13:12Z","receivedAt":"2021-03-16T02:14:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add and use a tree_entry_extract_type() function. There were callers\nof tree_entry_extract() which didn't care about the mode, but just the\ntype in the tree entry.\n\nIn emit_path() the \"mode\" variable was not used after the \"isdir\"\nassignment, as can be seen in the diff with it being set to 0.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c | 12 ++++++------\n tree-diff.c   |  5 +++--\n tree-walk.h   | 11 +++++++++++\n 3 files changed, 20 insertions(+), 8 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex 25bfb46fb02..89109659aa3 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -143,11 +143,11 @@ static void match_trees(const struct object_id *hash1,\n \twhile (one.size) {\n \t\tconst char *path;\n \t\tconst struct object_id *elem;\n-\t\tunsigned short mode;\n+\t\tenum object_type object_type;\n \t\tint score;\n \n-\t\telem = tree_entry_extract_mode(&one, &path, &mode);\n-\t\tif (!S_ISDIR(mode))\n+\t\telem = tree_entry_extract_type(&one, &path, &object_type);\n+\t\tif (object_type != OBJ_TREE)\n \t\t\tgoto next;\n \t\tscore = score_trees(elem, hash2);\n \t\tif (*best_score < score) {\n@@ -198,14 +198,14 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \n \trewrite_here = NULL;\n \twhile (desc.size) {\n+\t\tenum object_type object_type;\n \t\tconst char *name;\n-\t\tunsigned short mode;\n \t\tint len = tree_entry_len(&desc.entry);\n \n-\t\ttree_entry_extract_mode(&desc, &name, &mode);\n+\t\ttree_entry_extract_type(&desc, &name, &object_type);\n \t\tif (len == toplen &&\n \t\t    !memcmp(name, prefix, toplen)) {\n-\t\t\tif (!S_ISDIR(mode))\n+\t\t\tif (object_type != OBJ_TREE)\n \t\t\t\tdie(\"entry %s in tree %s is not a tree\", name,\n \t\t\t\t    oid_to_hex(oid1));\n \ndiff --git a/tree-diff.c b/tree-diff.c\nindex 918ad95fa61..8409374f0ba 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -208,10 +208,11 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \t\t * 1) all modes for tp[i]=tp[imin] should be the same wrt\n \t\t *    S_ISDIR, thanks to base_name_compare().\n \t\t */\n-\t\ttree_entry_extract_mode(&tp[imin], &path, &mode);\n+\t\tenum object_type object_type;\n+\t\ttree_entry_extract_type(&tp[imin], &path, &object_type);\n \t\tpathlen = tree_entry_len(&tp[imin].entry);\n \n-\t\tisdir = S_ISDIR(mode);\n+\t\tisdir = object_type == OBJ_TREE;\n \t\toid = NULL;\n \t\tmode = 0;\n \t}\ndiff --git a/tree-walk.h b/tree-walk.h\nindex efcd7ccd10e..f5102ed5427 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -47,6 +47,7 @@ struct tree_desc {\n  * appropriate variable to fill in (NULL won't do!):\n  *\n  * tree_entry_extract_mode(): const char *path, unsigned int mode\n+ * tree_entry_extract_type(): const char *path, enum object_type\n  * tree_entry_extract_all(): const char *path, unsigned int mode, enum object_type\n  */\n static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *desc,\n@@ -58,6 +59,16 @@ static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *\n \treturn &desc->entry.oid;\n }\n \n+static inline const struct object_id *tree_entry_extract_type(struct tree_desc *desc,\n+\t\t\t\t\t\t\t      const char **pathp,\n+\t\t\t\t\t\t\t      enum object_type *object_typep)\n+{\n+\t*pathp = desc->entry.path;\n+\t*object_typep = desc->entry.object_type;\n+\treturn &desc->entry.oid;\n+}\n+\n+\n static inline const struct object_id *tree_entry_extract_all(struct tree_desc *desc,\n \t\t\t\t\t\t\t     const char **pathp,\n \t\t\t\t\t\t\t     unsigned short *modep,\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419263","messageId":"20210316021312.13927-20-avarab@gmail.com","threadId":"18927","inReplyTo":"20210308150650.18626-1-avarab@gmail.com","subject":"[PATCH v2 19/29] tree-walk.h API: formatting changes for subsequent commit","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T02:13:02Z","receivedAt":"2021-03-16T02:14:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Do formatting (mainly whitespace) changes of code around the\nget_tree_entry() function to make a subsequent change where we'll add\na sister function easier to read.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n blame.c     |  5 +++--\n tree-walk.c |  9 ++++++---\n tree-walk.h | 12 ++++++++----\n 3 files changed, 17 insertions(+), 9 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex a5044fcfaa6..83babc41d08 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -102,9 +102,10 @@ static void verify_working_tree_path(struct repository *r,\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n \t\tstruct object_id blob_oid;\n \t\tunsigned short mode;\n+\t\tint ret = get_tree_entry(r, commit_oid, path, &blob_oid,\n+\t\t\t\t\t &mode);\n \n-\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode) &&\n-\t\t    oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n+\t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n \t\t\treturn;\n \t}\n \ndiff --git a/tree-walk.c b/tree-walk.c\nindex 6e9161901d8..e88187e3714 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -591,7 +591,8 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\toidcpy(result, &oid);\n \t\t\treturn 0;\n \t\t}\n-\t\treturn get_tree_entry(r, &oid, name + entrylen, result, mode);\n+\t\treturn get_tree_entry(r, &oid, name + entrylen, result,\n+\t\t\t\t      mode);\n \t}\n \treturn -1;\n }\n@@ -622,7 +623,8 @@ int get_tree_entry(struct repository *r,\n \t} else {\n \t\tstruct tree_desc t;\n \t\tinit_tree_desc(&t, tree, size);\n-\t\tretval = find_tree_entry(r, &t, name, oid, mode);\n+\t\tretval = find_tree_entry(r, &t, name, oid,\n+\t\t\t\t\t mode);\n \t}\n \tfree(tree);\n \treturn retval;\n@@ -748,7 +750,8 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n \n \t\t/* Look up the first (or only) path component in the tree. */\n \t\tfind_result = find_tree_entry(r, &t, namebuf.buf,\n-\t\t\t\t\t      &current_tree_oid, mode);\n+\t\t\t\t\t      &current_tree_oid,\n+\t\t\t\t\t      mode);\n \t\tif (find_result) {\n \t\t\tgoto done;\n \t\t}\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 9f3825d2773..478a659ee2b 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -169,10 +169,14 @@ struct traverse_info {\n \n /**\n  * Find an entry in a tree given a pathname and the sha1 of a tree to\n- * search. Returns 0 if the entry is found and -1 otherwise. The third\n- * and fourth parameters are set to the entry's sha1 and mode respectively.\n- */\n-int get_tree_entry(struct repository *, const struct object_id *, const char *, struct object_id *, unsigned short *);\n+ * search. Returns 0 if the entry is found and -1 otherwise.\n+ *\n+ * The third and fourth parameters are set to the entry's sha1 and\n+ * mode respectively.\n+ */\n+int get_tree_entry(struct repository *, const struct object_id *, const char *,\n+\t\t   struct object_id *,\n+\t\t   unsigned short *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.rc2.211.g1d0b8788b3\n\n"},{"id":"419272","messageId":"CABPp-BFXoCs-WXfPjPrQ8YLL2YXG5-Dd85LLnvE_yOKQ5NVD7Q@mail.gmail.com","threadId":"18927","inReplyTo":"20210315234344.28427-9-avarab@gmail.com","subject":"Re: [PATCH v3 8/9] show tests: add test for \"git show <tree>\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-16T05:19:48Z","receivedAt":"2021-03-16T05:20:39Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 15, 2021 at 4:44 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> Add missing tests for showing a tree with \"git show\". Let's test for\n> showing a tree, two trees, and that doing so doesn't recurse.\n>\n> The only tests for this code added in 5d7eeee2ac6 (git-show: grok\n> blobs, trees and tags, too, 2006-12-14) were the tests in\n> t7701-repack-unpack-unreachable.sh added in ccc1297226b (repack:\n> modify behavior of -A option to leave unreferenced objects unpacked,\n> 2008-05-09).\n>\n> Let's add this common mode of operation to the \"show\" tests\n> themselves. It's more obvious, and the tests in\n> t7701-repack-unpack-unreachable.sh happily parse if we start buggily\n> emitting trees recursively.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t7007-show.sh | 39 +++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 39 insertions(+)\n>\n> diff --git a/t/t7007-show.sh b/t/t7007-show.sh\n> index 42d3db62468..37ce718b231 100755\n> --- a/t/t7007-show.sh\n> +++ b/t/t7007-show.sh\n> @@ -38,6 +38,45 @@ test_expect_success 'showing two commits' '\n>         test_cmp expect actual.filtered\n>  '\n>\n> +test_expect_success 'showing a tree' '\n> +       cat >expected <<-EOF &&\n> +       tree main1:\n> +\n> +       main1.t\n> +       EOF\n> +       git show main1: >actual &&\n\nI had used COMMIT:PATH syntax before, but I was unaware PATH could be\nempty to refer to the toplevel tree.\n\nStill, it seems main1^{tree} might be a bit clearer?\n\n> +       test_cmp expected actual\n> +'\n> +\n> +test_expect_success 'showing two trees' '\n> +       cat >expected <<-EOF &&\n> +       tree main1:\n> +\n> +       main1.t\n> +\n> +       tree main2:\n> +\n> +       main1.t\n> +       main2.t\n> +       EOF\n> +       git show main1: main2: >actual &&\n> +       test_cmp expected actual\n> +'\n> +\n> +test_expect_success 'showing a trees is not recursive' '\n> +       git worktree add not-recursive main1 &&\n> +       mkdir not-recursive/a &&\n> +       test_commit -C not-recursive a/file &&\n> +       cat >expected <<-EOF &&\n> +       tree a/file:\n> +\n> +       a/\n> +       main1.t\n> +       EOF\n> +       git -C not-recursive show a/file: >actual &&\n\na/file: was really confusing to me.  Perhaps I should have\nknown/remembered that a/file was both a filename and a tagname (I\ndislike that part of test_commit), and you are just using that tag but\nI had to fetch your changes and run them and play around to figure out\nwhat was going on here.\n\nCan we just use \"HEAD^{tree}\" instead of \"a/file:\" ?\n\n> +       test_cmp expected actual\n> +'\n> +\n>  test_expect_success 'showing a range walks (linear)' '\n>         cat >expect <<-EOF &&\n>         commit $(git rev-parse main3)\n> --\n> 2.31.0.rc2.211.g1d0b8788b3\n"},{"id":"419273","messageId":"CABPp-BHWDffkqXsisp9E-FJPR4PmByPWuxWkAir24WpqUu43Lg@mail.gmail.com","threadId":"18927","inReplyTo":"20210315234344.28427-1-avarab@gmail.com","subject":"Re: [PATCH v3 0/9] read_tree() and read_tree_recursive() refactoring","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-16T05:37:32Z","receivedAt":"2021-03-16T05:38:47Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 15, 2021 at 4:44 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> A v3 of a refactoring of tree.c. See v2 at\n> https://lore.kernel.org/git/20210308022138.28166-1-avarab@gmail.com/\n>\n> This brings back pretty much the old read_tree_recursive() under the\n> name read_tree_at() as suggested in\n> https://lore.kernel.org/git/xmqqft106sok.fsf@gitster.g/\n\nPatch 8/9 was a bit harder for me to read; I'm not sure if that means\nthe patch needs improvement or just reflects a personal failing.  :-)\nBut ultimately, it's minor and the rest of the series looks good.\nWith or without a few tweaks to that patch:\n\nReviewed-by: Elijah Newren <newren@gmail.com>\n"},{"id":"419277","messageId":"CABPp-BGE+e1er6qFuG90j9+eVG34O8TN=imX=jtcb+jbQjN1QQ@mail.gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-7-avarab@gmail.com","subject":"Re: [PATCH v2 06/29] cache.h: have base_name_compare() take \"is tree?\", not \"mode\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-16T05:49:22Z","receivedAt":"2021-03-16T05:50:32Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 15, 2021 at 7:13 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> Change the base_name_compare() API and the related df_name_compare()\n> function to take a boolean argument indicating whether the entry is a\n> tree or not, instead of having them call S_ISDIR(mode) on their own.\n>\n> This makes use of the new \"object_type\" field in the \"name_entry\".\n>\n> The API being modified here was originally added way back in\n> 958ba6c96eb (Introduce \"base_name_compare()\" helper function,\n> 2005-05-20).\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/fast-import.c |  8 ++++----\n>  builtin/mktree.c      |  4 ++--\n>  cache.h               |  4 ++--\n>  combine-diff.c        |  8 +++++---\n>  match-trees.c         |  6 ++++--\n>  merge-ort.c           |  4 ++--\n>  merge-recursive.c     |  6 +++---\n>  read-cache.c          | 16 ++++++++--------\n>  tree-diff.c           |  7 +++++--\n>  unpack-trees.c        | 15 ++++++++-------\n>  10 files changed, 43 insertions(+), 35 deletions(-)\n>\n> diff --git a/builtin/fast-import.c b/builtin/fast-import.c\n> index dd4d09ceceb..ce4613c1595 100644\n> --- a/builtin/fast-import.c\n> +++ b/builtin/fast-import.c\n> @@ -1288,8 +1288,8 @@ static int tecmp0 (const void *_a, const void *_b)\n>         struct tree_entry *a = *((struct tree_entry**)_a);\n>         struct tree_entry *b = *((struct tree_entry**)_b);\n>         return base_name_compare(\n> -               a->name->str_dat, a->name->str_len, a->versions[0].mode,\n> -               b->name->str_dat, b->name->str_len, b->versions[0].mode);\n> +               a->name->str_dat, a->name->str_len, 1,\n> +               b->name->str_dat, b->name->str_len, 1);\n>  }\n>\n>  static int tecmp1 (const void *_a, const void *_b)\n> @@ -1297,8 +1297,8 @@ static int tecmp1 (const void *_a, const void *_b)\n>         struct tree_entry *a = *((struct tree_entry**)_a);\n>         struct tree_entry *b = *((struct tree_entry**)_b);\n>         return base_name_compare(\n> -               a->name->str_dat, a->name->str_len, a->versions[1].mode,\n> -               b->name->str_dat, b->name->str_len, b->versions[1].mode);\n> +               a->name->str_dat, a->name->str_len, 1,\n> +               b->name->str_dat, b->name->str_len, 1);\n>  }\n\nUm...these last two hunks have not changed since the last round.  They\nintroduce a nasty bug, as mentioned in my review last time and\nhighlighted in my response on the cover letter.  They need to be\nfixed, and more testcases verifying their correct operation should be\nadded.\n\nThis is the kind of thing that makes me really scared about these\nrefactorings; it's too easy to introduce changes that will write\nsemi-broken objects in the wild, which we will then have to deal with\nfor the rest of forever.\n\n>  static void mktree(struct tree_content *t, int v, struct strbuf *b)\n> diff --git a/builtin/mktree.c b/builtin/mktree.c\n> index 891991b00d6..2c1973229ac 100644\n> --- a/builtin/mktree.c\n> +++ b/builtin/mktree.c\n> @@ -37,8 +37,8 @@ static int ent_compare(const void *a_, const void *b_)\n>  {\n>         struct treeent *a = *(struct treeent **)a_;\n>         struct treeent *b = *(struct treeent **)b_;\n> -       return base_name_compare(a->name, a->len, a->mode,\n> -                                b->name, b->len, b->mode);\n> +       return base_name_compare(a->name, a->len, S_ISDIR(a->mode),\n> +                                b->name, b->len, S_ISDIR(b->mode));\n>  }\n>\n>  static void write_tree(struct object_id *oid)\n> diff --git a/cache.h b/cache.h\n> index ae0c0bef5c2..e38b1e1688c 100644\n> --- a/cache.h\n> +++ b/cache.h\n> @@ -1506,8 +1506,8 @@ int repo_interpret_branch_name(struct repository *r,\n>\n>  int validate_headref(const char *ref);\n>\n> -int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n> -int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n> +int base_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n> +int df_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n>  int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);\n>  int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);\n>\n> diff --git a/combine-diff.c b/combine-diff.c\n> index 9228aebc16b..ad7058a6f5d 100644\n> --- a/combine-diff.c\n> +++ b/combine-diff.c\n> @@ -16,11 +16,13 @@\n>  static int compare_paths(const struct combine_diff_path *one,\n>                           const struct diff_filespec *two)\n>  {\n> -       if (!S_ISDIR(one->mode) && !S_ISDIR(two->mode))\n> +       int istree_one = S_ISDIR(one->mode);\n> +       int istree_two = S_ISDIR(two->mode);\n> +       if (!istree_one && !istree_two)\n>                 return strcmp(one->path, two->path);\n>\n> -       return base_name_compare(one->path, strlen(one->path), one->mode,\n> -                                two->path, strlen(two->path), two->mode);\n> +       return base_name_compare(one->path, strlen(one->path), istree_one,\n> +                                two->path, strlen(two->path), istree_two);\n>  }\n>\n>  static int filename_changed(char status)\n> diff --git a/match-trees.c b/match-trees.c\n> index 1011357ad0c..a28c19a62a5 100644\n> --- a/match-trees.c\n> +++ b/match-trees.c\n> @@ -67,8 +67,10 @@ static void *fill_tree_desc_strict(struct tree_desc *desc,\n>  static int base_name_entries_compare(const struct name_entry *a,\n>                                      const struct name_entry *b)\n>  {\n> -       return base_name_compare(a->path, tree_entry_len(a), a->mode,\n> -                                b->path, tree_entry_len(b), b->mode);\n> +       int istree_a = (a->object_type == OBJ_TREE);\n> +       int istree_b = (b->object_type == OBJ_TREE);\n> +       return base_name_compare(a->path, tree_entry_len(a), istree_a,\n> +                                b->path, tree_entry_len(b), istree_b);\n>  }\n>\n>  /*\n> diff --git a/merge-ort.c b/merge-ort.c\n> index 603d30c5217..4075d13aaab 100644\n> --- a/merge-ort.c\n> +++ b/merge-ort.c\n> @@ -2390,8 +2390,8 @@ static int tree_entry_order(const void *a_, const void *b_)\n>\n>         const struct merged_info *ami = a->util;\n>         const struct merged_info *bmi = b->util;\n> -       return base_name_compare(a->string, strlen(a->string), ami->result.mode,\n> -                                b->string, strlen(b->string), bmi->result.mode);\n> +       return base_name_compare(a->string, strlen(a->string), S_ISDIR(ami->result.mode),\n> +                                b->string, strlen(b->string), S_ISDIR(bmi->result.mode));\n>  }\n>\n>  static void write_tree(struct object_id *result_oid,\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index 3d9207455b3..1c9b08695a3 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -554,12 +554,12 @@ static int string_list_df_name_compare(const char *one, const char *two)\n>          *\n>          * To achieve this, we sort with df_name_compare and provide\n>          * the mode S_IFDIR so that D/F conflicts will sort correctly.\n> -        * We use the mode S_IFDIR for everything else for simplicity,\n> +        * We say we have a directory for everything else for simplicity,\n>          * since in other cases any changes in their order due to\n>          * sorting cause no problems for us.\n>          */\n> -       int cmp = df_name_compare(one, onelen, S_IFDIR,\n> -                                 two, twolen, S_IFDIR);\n> +       int cmp = df_name_compare(one, onelen, 1, two, twolen, 1);\n> +\n>         /*\n>          * Now that 'foo' and 'foo/bar' compare equal, we have to make sure\n>          * that 'foo' comes before 'foo/bar'.\n> diff --git a/read-cache.c b/read-cache.c\n> index 1e9a50c6c73..4257fbd8a08 100644\n> --- a/read-cache.c\n> +++ b/read-cache.c\n> @@ -462,8 +462,8 @@ int ie_modified(struct index_state *istate,\n>         return 0;\n>  }\n>\n> -int base_name_compare(const char *name1, int len1, int mode1,\n> -                     const char *name2, int len2, int mode2)\n> +int base_name_compare(const char *name1, int len1, int istree1,\n> +                     const char *name2, int len2, int istree2)\n>  {\n>         unsigned char c1, c2;\n>         int len = len1 < len2 ? len1 : len2;\n> @@ -474,9 +474,9 @@ int base_name_compare(const char *name1, int len1, int mode1,\n>                 return cmp;\n>         c1 = name1[len];\n>         c2 = name2[len];\n> -       if (!c1 && S_ISDIR(mode1))\n> +       if (!c1 && istree1)\n>                 c1 = '/';\n> -       if (!c2 && S_ISDIR(mode2))\n> +       if (!c2 && istree2)\n>                 c2 = '/';\n>         return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;\n>  }\n> @@ -491,8 +491,8 @@ int base_name_compare(const char *name1, int len1, int mode1,\n>   * This is used by routines that want to traverse the git namespace\n>   * but then handle conflicting entries together when possible.\n>   */\n> -int df_name_compare(const char *name1, int len1, int mode1,\n> -                   const char *name2, int len2, int mode2)\n> +int df_name_compare(const char *name1, int len1, int istree1,\n> +                   const char *name2, int len2, int istree2)\n>  {\n>         int len = len1 < len2 ? len1 : len2, cmp;\n>         unsigned char c1, c2;\n> @@ -504,10 +504,10 @@ int df_name_compare(const char *name1, int len1, int mode1,\n>         if (len1 == len2)\n>                 return 0;\n>         c1 = name1[len];\n> -       if (!c1 && S_ISDIR(mode1))\n> +       if (!c1 && istree1)\n>                 c1 = '/';\n>         c2 = name2[len];\n> -       if (!c2 && S_ISDIR(mode2))\n> +       if (!c2 && istree2)\n>                 c2 = '/';\n>         if (c1 == '/' && !c2)\n>                 return 0;\n> diff --git a/tree-diff.c b/tree-diff.c\n> index 7cebbb327e2..6ec180331fb 100644\n> --- a/tree-diff.c\n> +++ b/tree-diff.c\n> @@ -50,6 +50,7 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)\n>  {\n>         struct name_entry *e1, *e2;\n>         int cmp;\n> +       int istree_e1, istree_e2;\n>\n>         /* empty descriptors sort after valid tree entries */\n>         if (!t1->size)\n> @@ -58,9 +59,11 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)\n>                 return -1;\n>\n>         e1 = &t1->entry;\n> +       istree_e1 = (e1->object_type == OBJ_TREE);\n>         e2 = &t2->entry;\n> -       cmp = base_name_compare(e1->path, tree_entry_len(e1), e1->mode,\n> -                               e2->path, tree_entry_len(e2), e2->mode);\n> +       istree_e2 = (e2->object_type == OBJ_TREE);\n> +       cmp = base_name_compare(e1->path, tree_entry_len(e1), istree_e1,\n> +                               e2->path, tree_entry_len(e2), istree_e2);\n>         return cmp;\n>  }\n>\n> diff --git a/unpack-trees.c b/unpack-trees.c\n> index eb8fcda31ba..d9d573df986 100644\n> --- a/unpack-trees.c\n> +++ b/unpack-trees.c\n> @@ -925,7 +925,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n>  static int do_compare_entry_piecewise(const struct cache_entry *ce,\n>                                       const struct traverse_info *info,\n>                                       const char *name, size_t namelen,\n> -                                     unsigned mode)\n> +                                     unsigned istree)\n>  {\n>         int pathlen, ce_len;\n>         const char *ce_name;\n> @@ -933,7 +933,7 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce,\n>         if (info->prev) {\n>                 int cmp = do_compare_entry_piecewise(ce, info->prev,\n>                                                      info->name, info->namelen,\n> -                                                    info->mode);\n> +                                                    S_ISDIR(info->mode));\n>                 if (cmp)\n>                         return cmp;\n>         }\n> @@ -947,13 +947,13 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce,\n>         ce_len -= pathlen;\n>         ce_name = ce->name + pathlen;\n>\n> -       return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n> +       return df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n>  }\n>\n>  static int do_compare_entry(const struct cache_entry *ce,\n>                             const struct traverse_info *info,\n>                             const char *name, size_t namelen,\n> -                           unsigned mode)\n> +                           unsigned istree)\n>  {\n>         int pathlen, ce_len;\n>         const char *ce_name;\n> @@ -965,7 +965,7 @@ static int do_compare_entry(const struct cache_entry *ce,\n>          * it is quicker to use the precomputed version.\n>          */\n>         if (!info->traverse_path)\n> -               return do_compare_entry_piecewise(ce, info, name, namelen, mode);\n> +               return do_compare_entry_piecewise(ce, info, name, namelen, istree);\n>\n>         cmp = strncmp(ce->name, info->traverse_path, info->pathlen);\n>         if (cmp)\n> @@ -980,12 +980,13 @@ static int do_compare_entry(const struct cache_entry *ce,\n>         ce_len -= pathlen;\n>         ce_name = ce->name + pathlen;\n>\n> -       return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n> +       return df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n>  }\n>\n>  static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)\n>  {\n> -       int cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);\n> +       int istree = (n->object_type == OBJ_TREE);\n> +       int cmp = do_compare_entry(ce, info, n->path, n->pathlen, istree);\n>         if (cmp)\n>                 return cmp;\n>\n> --\n> 2.31.0.rc2.211.g1d0b8788b3\n\nThe isdir -> istree updates are nice; thanks.\n"},{"id":"419280","messageId":"CABPp-BHT0O+o=V1CCgJYkdL7ZYYTpUbXZ6iKRCYoi402-3W9zw@mail.gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-22-avarab@gmail.com","subject":"Re: [PATCH v2 21/29] tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-16T06:34:39Z","receivedAt":"2021-03-16T06:35:54Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 15, 2021 at 7:13 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n\nAs with the previous round, why do you have the subject as \"tree-walk:\n...\" instead of \"match-trees: ...\" ?\n\n>\n> Refactor code added in 85e51b783c3 (Make \"subtree\" part more\n> orthogonal to the rest of merge-recursive., 2008-06-30) to make it\n> obvious that we don't care about the \"mode\" here outside of the if\n> statement it appears in.\n>\n> That's opposed to the sub1 & sub2 variables, where we use the two\n> object ids later in this function.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  match-trees.c | 10 +++++-----\n>  1 file changed, 5 insertions(+), 5 deletions(-)\n>\n> diff --git a/match-trees.c b/match-trees.c\n> index 0faacd8f4ae..e84f993a460 100644\n> --- a/match-trees.c\n> +++ b/match-trees.c\n> @@ -317,17 +317,17 @@ void shift_tree_by(struct repository *r,\n>                    const char *shift_prefix)\n>  {\n>         struct object_id sub1, sub2;\n> -       unsigned short mode1, mode2;\n> +       unsigned short tmp;\n>         unsigned candidate = 0;\n>\n>         /* Can hash2 be a tree at shift_prefix in tree hash1? */\n> -       if (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &mode1) &&\n> -           S_ISDIR(mode1))\n> +       if (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n> +           S_ISDIR(tmp))\n>                 candidate |= 1;\n>\n>         /* Can hash1 be a tree at shift_prefix in tree hash2? */\n> -       if (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &mode2) &&\n> -           S_ISDIR(mode2))\n> +       if (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n> +           S_ISDIR(tmp))\n>                 candidate |= 2;\n>\n>         if (candidate == 3) {\n> --\n> 2.31.0.rc2.211.g1d0b8788b3\n>\n"},{"id":"419282","messageId":"CABPp-BF3SOzz+C1YW73kYgz2P8h2pSgYSmc8FJMOygAHVdoeFw@mail.gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-28-avarab@gmail.com","subject":"Re: [PATCH v2 27/29] tree-walk.h API: add a get_tree_entry_path() function","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-16T06:50:56Z","receivedAt":"2021-03-16T06:51:53Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 15, 2021 at 7:13 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> Add a get_tree_entry_path() variant in addition to\n> get_tree_entry_path_{mode,type,all}(). This is for those callers that\n> need neither the mode nor \"enum object_type\" parameters filled for\n> them.\n>\n> There's callers here which doesn't need the \"struct object_id\" filled\n> either, and provides a throwaway variable for us.\n\nAs with the previous round, this paragraph does not parse well.  My\nsuggestion on the last round was:\n\nThere are callers here which don't need the \"struct object_id\" filled;\nforcing callers to pass one just requires they create a throwaway\nvariable.\n\nyou don't have to take it, but the paragraph needs rewording one way or another.\n\n>\n> See the following commits for the introduction of such code that's\n> being modified here:\n>\n>  - shift_tree(): 68faf68938e (A new merge stragety 'subtree'.,\n>     2007-02-15) for the shift_tree()\n>\n>  - tree_has_path(): 96e7ffbdc31 (merge-recursive: check for directory\n>    level conflicts, 2018-04-19)\n>\n>  - init_notes(): fd53c9eb445 (Speed up git notes lookup, 2009-10-09)\n>\n>  - diagnose_invalid_oid_path(): 009fee4774d (Detailed diagnosis when\n>    parsing an object name fails., 2009-12-07)\n>\n> Those could potentially be refactored too, but I've got to stop at\n> some point, and right now I'm focusing downstream code that depends on\n> \"mode\" (or \"enum object_type\").\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  match-trees.c     |  4 +---\n>  merge-recursive.c |  6 ++----\n>  notes.c           |  3 +--\n>  object-name.c     |  3 +--\n>  tree-walk.c       | 11 +++++++++++\n>  tree-walk.h       |  3 +++\n>  6 files changed, 19 insertions(+), 11 deletions(-)\n>\n> diff --git a/match-trees.c b/match-trees.c\n> index 2afa4968109..25bfb46fb02 100644\n> --- a/match-trees.c\n> +++ b/match-trees.c\n> @@ -288,12 +288,10 @@ void shift_tree(struct repository *r,\n>\n>         if (add_score < del_score) {\n>                 /* We need to pick a subtree of two */\n> -               unsigned short mode;\n> -\n>                 if (!*del_prefix)\n>                         return;\n>\n> -               if (get_tree_entry_mode(r, hash2, del_prefix, shifted, &mode))\n> +               if (get_tree_entry_path(r, hash2, del_prefix, shifted))\n>                         die(\"cannot find path %s in tree %s\",\n>                             del_prefix, oid_to_hex(hash2));\n>                 return;\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index bbbb68e15bc..83d2b8b8440 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -1884,11 +1884,9 @@ static int tree_has_path(struct repository *r, struct tree *tree,\n>                          const char *path)\n>  {\n>         struct object_id hashy;\n> -       unsigned short mode_o;\n> -\n> -       return !get_tree_entry_mode(r,\n> +       return !get_tree_entry_path(r,\n>                                     &tree->object.oid, path,\n> -                                   &hashy, &mode_o);\n> +                                   &hashy);\n>  }\n>\n>  /*\n> diff --git a/notes.c b/notes.c\n> index ef138606146..aa46cb2b09e 100644\n> --- a/notes.c\n> +++ b/notes.c\n> @@ -994,7 +994,6 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n>                 combine_notes_fn combine_notes, int flags)\n>  {\n>         struct object_id oid, object_oid;\n> -       unsigned short mode;\n>         struct leaf_node root_tree;\n>\n>         if (!t)\n> @@ -1021,7 +1020,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n>                 return;\n>         if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))\n>                 die(\"Cannot use notes ref %s\", notes_ref);\n> -       if (get_tree_entry_mode(the_repository, &object_oid, \"\", &oid, &mode))\n> +       if (get_tree_entry_path(the_repository, &object_oid, \"\", &oid))\n>                 die(\"Failed to read notes tree referenced by %s (%s)\",\n>                     notes_ref, oid_to_hex(&object_oid));\n>\n> diff --git a/object-name.c b/object-name.c\n> index 7e3b2d6d739..9ff5f83c1ff 100644\n> --- a/object-name.c\n> +++ b/object-name.c\n> @@ -1693,7 +1693,6 @@ static void diagnose_invalid_oid_path(struct repository *r,\n>                                       int object_name_len)\n>  {\n>         struct object_id oid;\n> -       unsigned short mode;\n>\n>         if (!prefix)\n>                 prefix = \"\";\n> @@ -1704,7 +1703,7 @@ static void diagnose_invalid_oid_path(struct repository *r,\n>         if (is_missing_file_error(errno)) {\n>                 char *fullname = xstrfmt(\"%s%s\", prefix, filename);\n>\n> -               if (!get_tree_entry_mode(r, tree_oid, fullname, &oid, &mode)) {\n> +               if (!get_tree_entry_path(r, tree_oid, fullname, &oid)) {\n>                         die(_(\"path '%s' exists, but not '%s'\\n\"\n>                             \"hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?\"),\n>                             fullname,\n> diff --git a/tree-walk.c b/tree-walk.c\n> index a90dbf87af4..fa846535dfb 100644\n> --- a/tree-walk.c\n> +++ b/tree-walk.c\n> @@ -632,6 +632,17 @@ int get_tree_entry_all(struct repository *r,\n>         return retval;\n>  }\n>\n> +int get_tree_entry_path(struct repository *r,\n> +                       const struct object_id *tree_oid,\n> +                       const char *name,\n> +                       struct object_id *oid)\n> +{\n> +       unsigned short mode;\n> +       enum object_type object_type;\n> +       return get_tree_entry_all(r, tree_oid, name, oid,\n> +                                 &mode, &object_type);\n> +}\n> +\n>  int get_tree_entry_mode(struct repository *r,\n>                         const struct object_id *tree_oid,\n>                         const char *name,\n> diff --git a/tree-walk.h b/tree-walk.h\n> index 55ef88ef2e5..efcd7ccd10e 100644\n> --- a/tree-walk.h\n> +++ b/tree-walk.h\n> @@ -193,10 +193,13 @@ struct traverse_info {\n>   * \"struct name_entry\" you'd like. You always need a pointer to an\n>   * appropriate variable to fill in (NULL won't do!):\n>   *\n> + * get_tree_entry_path(): <no extra argument, just get the common 'path'>\n>   * get_tree_entry_mode(): unsigned int mode\n>   * get_tree_entry_type(): enum object_type\n>   * get_tree_entry_all(): unsigned int mode, enum object_type\n>   */\n> +int get_tree_entry_path(struct repository *, const struct object_id *, const char *,\n> +                       struct object_id *);\n>  int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n>                         struct object_id *,\n>                         unsigned short *);\n> --\n> 2.31.0.rc2.211.g1d0b8788b3\n>\n"},{"id":"419283","messageId":"CABPp-BGsGsq93Zw7aSqDQVSrBvc+VGu91JZh-foFpuCNqJt5_Q@mail.gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-29-avarab@gmail.com","subject":"Re: [PATCH v2 28/29] blame: emit a better error on 'git blame directory'","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-16T06:55:50Z","receivedAt":"2021-03-16T06:56:41Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 15, 2021 at 7:13 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> Change an early check for non-blobs in verify_working_tree_path() to\n> let any such objects pass, and instead die shortly thereafter in the\n> fake_working_tree_commit() caller's type check.\n>\n> Now e.g. doing \"git blame t\" in git.git emits:\n>\n>     fatal: unsupported file type t\n>\n> Instead of:\n>\n>     fatal: no such path 't' in HEAD\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  blame.c                         |  8 ++------\n>  t/t8004-blame-with-conflicts.sh | 20 ++++++++++++++++++++\n>  2 files changed, 22 insertions(+), 6 deletions(-)\n>\n> diff --git a/blame.c b/blame.c\n> index 9e0543e13d4..7da162cd582 100644\n> --- a/blame.c\n> +++ b/blame.c\n> @@ -100,12 +100,8 @@ static void verify_working_tree_path(struct repository *r,\n>\n>         for (parents = work_tree->parents; parents; parents = parents->next) {\n>                 const struct object_id *commit_oid = &parents->item->object.oid;\n> -               struct object_id blob_oid;\n> -               unsigned short mode;\n> -               int ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n> -                                             &mode);\n> -\n> -               if (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n> +               struct object_id oid;\n> +               if (!get_tree_entry_path(r, commit_oid, path, &oid))\n>                         return;\n>         }\n>\n> diff --git a/t/t8004-blame-with-conflicts.sh b/t/t8004-blame-with-conflicts.sh\n> index 35414a53363..6caa504a0ea 100755\n> --- a/t/t8004-blame-with-conflicts.sh\n> +++ b/t/t8004-blame-with-conflicts.sh\n> @@ -73,4 +73,24 @@ test_expect_success 'blame does not crash with conflicted file in stages 1,3' '\n>         git blame file1\n>  '\n>\n> +test_expect_success 'setup second case' '\n> +       git merge --abort\n> +'\n> +\n> +test_expect_success 'blame on directory/file conflict' '\n> +       mkdir d &&\n> +       test_commit second &&\n> +       test_commit d/file &&\n> +       test_must_fail git blame d 2>expected &&\n> +\n> +       git reset --hard second &&\n> +       >d &&\n> +       git add d &&\n> +       git commit -m\"a not-a-dir\" &&\n> +       test_must_fail git merge d/file &&\n> +\n> +       test_must_fail git blame d 2>actual &&\n> +       test_cmp expected actual\n> +'\n> +\n>  test_done\n\nGiven that the commit message says the change was about modifying the\nerror message shown, why does the new test not check for the error\nmessage?\n"},{"id":"419284","messageId":"CABPp-BHnz-_633UZycnaD1Mj9vrhSJ4RA6yUr4F7dvZwyOJBiA@mail.gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"Re: [PATCH v2 00/29] tree-walk: mostly replace \"mode\" with \"enum object_type\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-16T07:04:00Z","receivedAt":"2021-03-16T07:05:14Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Mon, Mar 15, 2021 at 7:13 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> A v2 of the big tree-walk.[ch] refactoring series, goals etc. at the\n> v1 at:\n> https://lore.kernel.org/git/20210308150650.18626-1-avarab@gmail.com/\n>\n> It is based on my just-re-rolled v3 read_tree_recursive() series:\n> https://lore.kernel.org/git/20210315234344.28427-1-avarab@gmail.com/\n>\n> This version should address all the feedback on v1 for patces 1-27/30,\n> thanks to Elijah for very valuable comments on v1.\n>\n> It's mostly small nits here and there, except:\n>\n>  - I found that the change I'd made to update-index.c was buggy at the\n>    point it was introduced in the series, the object_type would be\n>    uninitialized. There were/are no tests for that, but I've moved\n>    things around so we don't have that bug anymore.\n>\n>  - Elijah had a comment on whether we needed oid_object_info() in\n>    blame.c. As it turns out we don't need a \"is blob?\" check at all\n>    there. There's a new 28/29 to refactor that small part of blame.c,\n>    along with a test.\n>\n> What this re-roll *does not* include is the final 28-30/30 part of v1\n> to s/mode/raw_mode/g and move canonical_mode() out of tree-walk.h.\n>\n> So a follow-up series will still be needed to fix the fsck.c check for\n> bad modes, but I wanted to split that tricker change off from this\n> rather big initial refactoring.\n\nSplitting those trickier bits off makes sense.\n\nThis series looks really good and addresses most of my feedback from\nv1.  There were two commit message wording comments in patches 21 and\n27 leftover from the previous round, and a nasty bug introduced in\npatch 6 still left from the previous round.\n\nI also had a question on the new testcase in patch 28.\n\nOther than that, though, the series looks good to me.\n\n>\n> Ævar Arnfjörð Bjarmason (29):\n>   diff.c: remove redundant canon_mode() call\n>   notes & match-trees: use name_entry's \"pathlen\" member\n>   cache.h: add a comment to object_type()\n>   tree-walk.h: add object_type member to name_entry\n>   tree-walk.c: migrate to using new \"object_type\" field when possible\n>   cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n>   tree-walk.h users: switch object_type(...) to new .object_type\n>   tree.h: format argument lists of read_tree_recursive() users\n>   tree.h users: format argument lists in archive.c\n>   archive: get rid of 'stage' parameter\n>   tree.h API: make read_tree_fn_t take an \"enum object_type\"\n>   tree-walk.h users: migrate \"p->mode &&\" pattern\n>   tree-walk.h users: refactor chained \"mode\" if/else into switch\n>   tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n>   merge-tree tests: test for the mode comparison in same_entry()\n>   merge-ort: correct reference to test in 62fdec17a11\n>   fsck.c: switch on \"object_type\" in fsck_walk_tree()\n>   tree-walk.h users: use temporary variable(s) for \"mode\"\n>   tree-walk.h API: formatting changes for subsequent commit\n>   tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n>   tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n>   tree-walk.h API: add get_tree_entry_type()\n>   tree-walk.h API: document and format tree_entry_extract()\n>   tree-entry.h API: rename tree_entry_extract() to\n>     tree_entry_extract_mode()\n>   tree-walk.h API: add a tree_entry_extract_all() function\n>   tree-walk.h API: add get_tree_entry_all()\n>   tree-walk.h API: add a get_tree_entry_path() function\n>   blame: emit a better error on 'git blame directory'\n>   tree-walk.h API: add a tree_entry_extract_type() function\n>\n>  archive.c                       | 50 +++++++++---------\n>  blame.c                         |  9 ++--\n>  builtin/checkout.c              |  6 ++-\n>  builtin/fast-import.c           |  8 +--\n>  builtin/grep.c                  |  6 +--\n>  builtin/log.c                   |  7 +--\n>  builtin/ls-files.c              |  6 ++-\n>  builtin/ls-tree.c               | 14 +++---\n>  builtin/merge-tree.c            | 30 +++++++----\n>  builtin/mktree.c                |  4 +-\n>  builtin/pack-objects.c          |  6 +--\n>  builtin/reflog.c                |  3 +-\n>  builtin/rm.c                    |  2 +-\n>  builtin/update-index.c          |  6 ++-\n>  cache-tree.c                    |  2 +-\n>  cache.h                         | 11 ++--\n>  combine-diff.c                  |  8 +--\n>  delta-islands.c                 |  2 +-\n>  diff.c                          |  2 +-\n>  fsck.c                          | 23 ++++-----\n>  http-push.c                     |  6 ++-\n>  line-log.c                      |  2 +-\n>  list-objects.c                  | 20 +++++---\n>  match-trees.c                   | 52 +++++++++----------\n>  merge-ort.c                     | 13 ++---\n>  merge-recursive.c               | 33 ++++++------\n>  notes.c                         | 14 +++---\n>  object-name.c                   |  7 ++-\n>  pack-bitmap-write.c             |  8 +--\n>  read-cache.c                    | 16 +++---\n>  revision.c                      | 12 +++--\n>  t/t4300-merge-tree.sh           | 44 ++++++++++++++++\n>  t/t8004-blame-with-conflicts.sh | 20 ++++++++\n>  tree-diff.c                     | 30 +++++++----\n>  tree-walk.c                     | 89 ++++++++++++++++++++++++---------\n>  tree-walk.h                     | 63 ++++++++++++++++++++---\n>  tree.c                          | 19 ++++---\n>  tree.h                          |  5 +-\n>  unpack-trees.c                  | 24 +++++----\n>  walker.c                        | 22 ++++----\n>  40 files changed, 460 insertions(+), 244 deletions(-)\n>\n> Range-diff:\n>  1:  e5df57c3440 =  1:  f9bbc30f69f diff.c: remove redundant canon_mode() call\n>  2:  8c2500bbf35 =  2:  187fc2c3e64 notes & match-trees: use name_entry's \"pathlen\" member\n>  3:  3d98e0c132f !  3:  311637c5583 cache.h: add a comment to object_type()\n>     @@ Commit message\n>          cache.h: add a comment to object_type()\n>\n>          Add a comment to the object_type() function to explain what it\n>     -    returns, and whet the \"mode\" is in the \"else\" case.\n>     +    returns, and what the \"mode\" is in the \"else\" case.\n>\n>          The object_type() function dates back to 4d1012c3709 (Fix rev-list\n>          when showing objects involving submodules, 2007-11-11). It's not\n>  4:  ce5808b317c =  4:  fecfe3d462c tree-walk.h: add object_type member to name_entry\n>  5:  18f26531acf =  5:  db961ab5e8d tree-walk.c: migrate to using new \"object_type\" field when possible\n>  6:  55e2640b815 !  6:  df2fc76161d cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n>     @@ cache.h: int repo_interpret_branch_name(struct repository *r,\n>\n>      -int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n>      -int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n>     -+int base_name_compare(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2);\n>     -+int df_name_compare(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2);\n>     ++int base_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n>     ++int df_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n>       int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);\n>       int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);\n>\n>     @@ combine-diff.c\n>                           const struct diff_filespec *two)\n>       {\n>      -  if (!S_ISDIR(one->mode) && !S_ISDIR(two->mode))\n>     -+  int isdir_one = S_ISDIR(one->mode);\n>     -+  int isdir_two = S_ISDIR(two->mode);\n>     -+  if (!isdir_one && !isdir_two)\n>     ++  int istree_one = S_ISDIR(one->mode);\n>     ++  int istree_two = S_ISDIR(two->mode);\n>     ++  if (!istree_one && !istree_two)\n>                 return strcmp(one->path, two->path);\n>\n>      -  return base_name_compare(one->path, strlen(one->path), one->mode,\n>      -                           two->path, strlen(two->path), two->mode);\n>     -+  return base_name_compare(one->path, strlen(one->path), isdir_one,\n>     -+                           two->path, strlen(two->path), isdir_two);\n>     ++  return base_name_compare(one->path, strlen(one->path), istree_one,\n>     ++                           two->path, strlen(two->path), istree_two);\n>       }\n>\n>       static int filename_changed(char status)\n>     @@ match-trees.c: static void *fill_tree_desc_strict(struct tree_desc *desc,\n>       {\n>      -  return base_name_compare(a->path, tree_entry_len(a), a->mode,\n>      -                           b->path, tree_entry_len(b), b->mode);\n>     -+  int isdira = a->object_type == OBJ_TREE;\n>     -+  int isdirb = b->object_type == OBJ_TREE;\n>     -+  return base_name_compare(a->path, tree_entry_len(a), isdira,\n>     -+                           b->path, tree_entry_len(b), isdirb);\n>     ++  int istree_a = (a->object_type == OBJ_TREE);\n>     ++  int istree_b = (b->object_type == OBJ_TREE);\n>     ++  return base_name_compare(a->path, tree_entry_len(a), istree_a,\n>     ++                           b->path, tree_entry_len(b), istree_b);\n>       }\n>\n>       /*\n>     @@ read-cache.c: int ie_modified(struct index_state *istate,\n>\n>      -int base_name_compare(const char *name1, int len1, int mode1,\n>      -                const char *name2, int len2, int mode2)\n>     -+int base_name_compare(const char *name1, int len1, int isdir1,\n>     -+                const char *name2, int len2, int isdir2)\n>     ++int base_name_compare(const char *name1, int len1, int istree1,\n>     ++                const char *name2, int len2, int istree2)\n>       {\n>         unsigned char c1, c2;\n>         int len = len1 < len2 ? len1 : len2;\n>     @@ read-cache.c: int base_name_compare(const char *name1, int len1, int mode1,\n>         c1 = name1[len];\n>         c2 = name2[len];\n>      -  if (!c1 && S_ISDIR(mode1))\n>     -+  if (!c1 && isdir1)\n>     ++  if (!c1 && istree1)\n>                 c1 = '/';\n>      -  if (!c2 && S_ISDIR(mode2))\n>     -+  if (!c2 && isdir2)\n>     ++  if (!c2 && istree2)\n>                 c2 = '/';\n>         return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;\n>       }\n>     @@ read-cache.c: int base_name_compare(const char *name1, int len1, int mode1,\n>        */\n>      -int df_name_compare(const char *name1, int len1, int mode1,\n>      -              const char *name2, int len2, int mode2)\n>     -+int df_name_compare(const char *name1, int len1, int isdir1,\n>     -+              const char *name2, int len2, int isdir2)\n>     ++int df_name_compare(const char *name1, int len1, int istree1,\n>     ++              const char *name2, int len2, int istree2)\n>       {\n>         int len = len1 < len2 ? len1 : len2, cmp;\n>         unsigned char c1, c2;\n>     @@ read-cache.c: int df_name_compare(const char *name1, int len1, int mode1,\n>                 return 0;\n>         c1 = name1[len];\n>      -  if (!c1 && S_ISDIR(mode1))\n>     -+  if (!c1 && isdir1)\n>     ++  if (!c1 && istree1)\n>                 c1 = '/';\n>         c2 = name2[len];\n>      -  if (!c2 && S_ISDIR(mode2))\n>     -+  if (!c2 && isdir2)\n>     ++  if (!c2 && istree2)\n>                 c2 = '/';\n>         if (c1 == '/' && !c2)\n>                 return 0;\n>     @@ tree-diff.c: static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_des\n>       {\n>         struct name_entry *e1, *e2;\n>         int cmp;\n>     -+  int e1_is_tree, e2_is_tree;\n>     ++  int istree_e1, istree_e2;\n>\n>         /* empty descriptors sort after valid tree entries */\n>         if (!t1->size)\n>     @@ tree-diff.c: static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_des\n>                 return -1;\n>\n>         e1 = &t1->entry;\n>     -+  e1_is_tree = e1->object_type == OBJ_TREE;\n>     ++  istree_e1 = (e1->object_type == OBJ_TREE);\n>         e2 = &t2->entry;\n>      -  cmp = base_name_compare(e1->path, tree_entry_len(e1), e1->mode,\n>      -                          e2->path, tree_entry_len(e2), e2->mode);\n>     -+  e2_is_tree = e2->object_type == OBJ_TREE;\n>     -+  cmp = base_name_compare(e1->path, tree_entry_len(e1), e1_is_tree,\n>     -+                          e2->path, tree_entry_len(e2), e2_is_tree);\n>     ++  istree_e2 = (e2->object_type == OBJ_TREE);\n>     ++  cmp = base_name_compare(e1->path, tree_entry_len(e1), istree_e1,\n>     ++                          e2->path, tree_entry_len(e2), istree_e2);\n>         return cmp;\n>       }\n>\n>     @@ unpack-trees.c: static int traverse_trees_recursive(int n, unsigned long dirmask\n>                                       const struct traverse_info *info,\n>                                       const char *name, size_t namelen,\n>      -                                unsigned mode)\n>     -+                                unsigned is_tree)\n>     ++                                unsigned istree)\n>       {\n>         int pathlen, ce_len;\n>         const char *ce_name;\n>     @@ unpack-trees.c: static int do_compare_entry_piecewise(const struct cache_entry *\n>         ce_name = ce->name + pathlen;\n>\n>      -  return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n>     -+  return df_name_compare(ce_name, ce_len, 0, name, namelen, is_tree);\n>     ++  return df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n>       }\n>\n>       static int do_compare_entry(const struct cache_entry *ce,\n>                             const struct traverse_info *info,\n>                             const char *name, size_t namelen,\n>      -                      unsigned mode)\n>     -+                      unsigned is_tree)\n>     ++                      unsigned istree)\n>       {\n>         int pathlen, ce_len;\n>         const char *ce_name;\n>     @@ unpack-trees.c: static int do_compare_entry(const struct cache_entry *ce,\n>          */\n>         if (!info->traverse_path)\n>      -          return do_compare_entry_piecewise(ce, info, name, namelen, mode);\n>     -+          return do_compare_entry_piecewise(ce, info, name, namelen, is_tree);\n>     ++          return do_compare_entry_piecewise(ce, info, name, namelen, istree);\n>\n>         cmp = strncmp(ce->name, info->traverse_path, info->pathlen);\n>         if (cmp)\n>     @@ unpack-trees.c: static int do_compare_entry(const struct cache_entry *ce,\n>         ce_name = ce->name + pathlen;\n>\n>      -  return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n>     -+  return df_name_compare(ce_name, ce_len, 0, name, namelen, is_tree);\n>     ++  return df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n>       }\n>\n>       static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)\n>       {\n>      -  int cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);\n>     -+  int is_tree = n->object_type == OBJ_TREE;\n>     -+  int cmp = do_compare_entry(ce, info, n->path, n->pathlen, is_tree);\n>     ++  int istree = (n->object_type == OBJ_TREE);\n>     ++  int cmp = do_compare_entry(ce, info, n->path, n->pathlen, istree);\n>         if (cmp)\n>                 return cmp;\n>\n>  7:  abc128f6cb9 =  7:  49d5da8c086 tree-walk.h users: switch object_type(...) to new .object_type\n>  8:  dcf13faf3cd !  8:  c9d209d496a tree.h: format argument lists of read_tree_recursive() users\n>     @@ archive.c: static int check_attr_export_subst(const struct attr_check *check)\n>      -          void *context)\n>      +                         int baselen, const char *filename,\n>      +                         unsigned mode,\n>     -+                         int stage, void *context)\n>     ++                         int stage,\n>     ++                         void *context)\n>       {\n>         static struct strbuf path = STRBUF_INIT;\n>         struct archiver_context *c = context;\n>     @@ tree.h: struct tree *parse_tree_indirect(const struct object_id *oid);\n>      +                        unsigned int,\n>      +                        void *);\n>\n>     - int read_tree_recursive(struct repository *r,\n>     -                   struct tree *tree,\n>     + int read_tree_at(struct repository *r,\n>     +            struct tree *tree,\n>  9:  b33fcf82349 !  9:  a6d2660fe14 tree.h users: format argument lists in archive.c\n>     @@ Commit message\n>          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n>       ## archive.c ##\n>     -@@ archive.c: static int check_attr_export_subst(const struct attr_check *check)\n>     - static int write_archive_entry(const struct object_id *oid, const char *base,\n>     -                          int baselen, const char *filename,\n>     -                          unsigned mode,\n>     --                         int stage, void *context)\n>     -+                         int stage,\n>     -+                         void *context)\n>     - {\n>     -   static struct strbuf path = STRBUF_INIT;\n>     -   struct archiver_context *c = context;\n>      @@ archive.c: static int write_archive_entry(const struct object_id *oid, const char *base,\n>       }\n>\n> 10:  6a20d3c058f = 10:  15f7f89acca archive: get rid of 'stage' parameter\n> 11:  a7f7444917c ! 11:  7a71404ea3f tree.h API: make read_tree_fn_t take an \"enum object_type\"\n>     @@ merge-recursive.c: static int save_files_dirs(const struct object_id *oid,\n>       static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n>\n>       ## tree.c ##\n>     -@@ tree.c: static int read_tree_1(struct repository *r,\n>     +@@ tree.c: int read_tree_at(struct repository *r,\n>         init_tree_desc(&desc, tree->buffer, tree->size);\n>\n>         while (tree_entry(&desc, &entry)) {\n>     @@ tree.c: static int read_tree_1(struct repository *r,\n>                 if (retval != all_entries_interesting) {\n>                         retval = tree_entry_interesting(r->index, &entry,\n>                                                         base, 0, pathspec);\n>     -@@ tree.c: static int read_tree_1(struct repository *r,\n>     +@@ tree.c: int read_tree_at(struct repository *r,\n>                 }\n>\n>                 switch (fn(&entry.oid, base,\n>     @@ tree.c: static int read_tree_1(struct repository *r,\n>                 case 0:\n>                         continue;\n>                 case READ_TREE_RECURSIVE:\n>     -@@ tree.c: static int read_tree_1(struct repository *r,\n>     +@@ tree.c: int read_tree_at(struct repository *r,\n>                         return -1;\n>                 }\n>\n>     @@ tree.c: static int read_tree_1(struct repository *r,\n>                         commit = lookup_commit(r, &entry.oid);\n>                         if (!commit)\n>                                 die(\"Commit %s in submodule path %s%s not found\",\n>     -@@ tree.c: static int read_tree_1(struct repository *r,\n>     +@@ tree.c: int read_tree_at(struct repository *r,\n>                                     base->buf, entry.path);\n>\n>                         oidcpy(&oid, get_commit_tree_oid(commit));\n>     @@ tree.h: int cmp_cache_name_compare(const void *a_, const void *b_);\n>      +                        enum object_type, unsigned int,\n>                               void *);\n>\n>     - int read_tree_recursive(struct repository *r,\n>     + int read_tree_at(struct repository *r,\n> 12:  625c643513d ! 12:  64dc9364bae tree-walk.h users: migrate \"p->mode &&\" pattern\n>     @@ Metadata\n>       ## Commit message ##\n>          tree-walk.h users: migrate \"p->mode &&\" pattern\n>\n>     -    Change code that dpends on \"p->mode\" either being a valid mode or zero\n>     -    to use a p->object_type comparison to \"OBJ_NONE\".\n>     +    Change code that depends on \"p->mode\" either being a valid mode or\n>     +    zero to use a p->object_type comparison to \"OBJ_NONE\".\n>\n>     -    The object_type() function in cache.h will not return OBJ_NONE, but in\n>     -    this these API users are implicitly relying on the memzero() that\n>     -    happens in setup_traverse_info().\n>     +    The object_type() function in cache.h will not return OBJ_NONE, but\n>     +    these API users are implicitly relying on the memzero() that happens\n>     +    in setup_traverse_info().\n>\n>          Since OBJ_NONE is \"0\" we can also rely on that being zero'd out here,\n>          along with the rest of the structure. I think this is slightly less\n> 13:  37b28c7feff = 13:  93ed3edbbd5 tree-walk.h users: refactor chained \"mode\" if/else into switch\n> 14:  e0b8ec6e291 = 14:  7aa48aa34c3 tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n> 15:  0cd162c43d7 = 15:  3ae81621dcf merge-tree tests: test for the mode comparison in same_entry()\n> 16:  f8ce666d4a7 = 16:  4249ad5c4de merge-ort: correct reference to test in 62fdec17a11\n> 17:  4963902ba97 = 17:  e5e17505dde fsck.c: switch on \"object_type\" in fsck_walk_tree()\n> 18:  d74e6778009 = 18:  3f0b884f1fd tree-walk.h users: use temporary variable(s) for \"mode\"\n> 19:  d39db486d4e = 19:  174167613bb tree-walk.h API: formatting changes for subsequent commit\n> 20:  69eb956b1ab = 20:  ec76db613f2 tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n> 21:  cc56453e600 = 21:  11e34941729 tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n> 22:  ca9e3b3ad00 ! 22:  b31c106557f tree-walk.h API: Add get_tree_entry_type()\n>     @@ Metadata\n>      Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n>       ## Commit message ##\n>     -    tree-walk.h API: Add get_tree_entry_type()\n>     +    tree-walk.h API: add get_tree_entry_type()\n>\n>          Add a get_tree_entry_type() helper function to compliment the existing\n>     -    get_tree_entry(). Move those users of get_tree_entry_type() who didn't\n>     -    care about the mode specifically, but just want to know whether the\n>     -    tree entry is one of OBJ_{BLOB,COMMIT,TREE} over to it.\n>     +    get_tree_entry(), and a static get_tree_entry_all() which it uses internally.\n>     +\n>     +    Move those users of get_tree_entry_type() who didn't care about the\n>     +    mode specifically, but just want to know whether the tree entry is one\n>     +    of OBJ_{BLOB,COMMIT,TREE} over to the new get_tree_entry_type().\n>     +\n>     +    The get_tree_entry_all() function itself will be made non-static in a\n>     +    subsequent commit. I'm leaving its argument list indented accordingly\n>     +    to reduce churn when I do so.\n>\n>          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n>     @@ archive.c: static void parse_treeish_arg(const char **argv,\n>\n>                 tree = parse_tree_indirect(&tree_oid);\n>\n>     - ## blame.c ##\n>     -@@ blame.c: static void verify_working_tree_path(struct repository *r,\n>     -   for (parents = work_tree->parents; parents; parents = parents->next) {\n>     -           const struct object_id *commit_oid = &parents->item->object.oid;\n>     -           struct object_id blob_oid;\n>     --          unsigned short mode;\n>     --          int ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n>     --                                        &mode);\n>     -+          enum object_type object_type;\n>     -+          int ret = get_tree_entry_type(r, commit_oid, path, &blob_oid,\n>     -+                                        &object_type);\n>     -\n>     --          if (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n>     -+          if (!ret && object_type == OBJ_BLOB)\n>     -                   return;\n>     -   }\n>     -\n>     -\n>       ## match-trees.c ##\n>      @@ match-trees.c: void shift_tree_by(struct repository *r,\n>                    const char *shift_prefix)\n>     @@ match-trees.c: void shift_tree_by(struct repository *r,\n>\n>       ## tree-walk.c ##\n>      @@ tree-walk.c: struct dir_state {\n>     +   struct object_id oid;\n>     + };\n>\n>     ++static int get_tree_entry_all(struct repository *r,\n>     ++                        const struct object_id *tree_oid,\n>     ++                        const char *name,\n>     ++                        struct object_id *oid,\n>     ++                        unsigned short *mode,\n>     ++                        enum object_type *object_type);\n>     ++\n>       static int find_tree_entry(struct repository *r, struct tree_desc *t,\n>                            const char *name, struct object_id *result,\n>      -                     unsigned short *mode)\n>     @@ tree-walk.c: static int find_tree_entry(struct repository *r, struct tree_desc *\n>      -                  const char *name,\n>      -                  struct object_id *oid,\n>      -                  unsigned short *mode)\n>     -+int get_tree_entry_all(struct repository *r,\n>     ++static int get_tree_entry_all(struct repository *r,\n>      +                 const struct object_id *tree_oid,\n>      +                 const char *name,\n>      +                 struct object_id *oid,\n>     @@ tree-walk.h: struct traverse_info {\n>      - * The third and fourth parameters are set to the entry's sha1 and\n>      - * mode respectively.\n>      + * There are variants of this function depending on what fields in the\n>     -+ * \"struct name_entry\" you'd like. You always need to pointer to an\n>     ++ * \"struct name_entry\" you'd like. You always need a pointer to an\n>      + * appropriate variable to fill in (NULL won't do!):\n>      + *\n>      + * get_tree_entry_mode(): unsigned int mode\n>      + * get_tree_entry_type(): enum object_type\n>     -+ * get_tree_entry_all(): unsigned int mode, enum object_type\n>        */\n>       int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n>                         struct object_id *,\n>     @@ tree-walk.h: struct traverse_info {\n>      +int get_tree_entry_type(struct repository *, const struct object_id *, const char *,\n>      +                  struct object_id *,\n>      +                  enum object_type *);\n>     -+int get_tree_entry_all(struct repository *, const struct object_id *, const char *,\n>     -+                 struct object_id *,\n>     -+                 unsigned short *, enum object_type *);\n>\n>       /**\n>        * Generate the full pathname of a tree entry based from the root of the\n> 24:  5986f494aa1 ! 23:  304d5d4d1af tree-walk.h API: document and format tree_entry_extract()\n>     @@ tree-walk.h: struct tree_desc {\n>      - * `pathp` and `modep` arguments are set to the entry's pathname and mode\n>      - * respectively.\n>      + * `tree_desc's` `entry` member) and return the OID of the entry.\n>     -+\n>     ++ *\n>      + * There are variants of this function depending on what fields in the\n>     -+ * \"struct name_entry\" you'd like. You always need to pointer to an\n>     ++ * \"struct name_entry\" you'd like. You always need a pointer to an\n>      + * appropriate variable to fill in (NULL won't do!):\n>      + *\n>      + * tree_entry_extract_mode(): const char *path, unsigned int mode\n> 25:  9b604a193b8 ! 24:  346453df356 tree-entry.h API: rename tree_entry_extract() to tree_entry_extract_mode()\n>     @@ tree-diff.c: static struct combine_diff_path *emit_path(struct combine_diff_path\n>\n>                 isdir = S_ISDIR(mode);\n>\n>     - ## tree-walk.c ##\n>     -@@ tree-walk.c: static int find_tree_entry(struct repository *r, struct tree_desc *t,\n>     -           struct object_id oid;\n>     -           int entrylen, cmp;\n>     -\n>     --          oidcpy(&oid, tree_entry_extract(t, &entry, mode));\n>     -+          oidcpy(&oid, tree_entry_extract_mode(t, &entry, mode));\n>     -           entrylen = tree_entry_len(&t->entry);\n>     -           update_tree_entry(t);\n>     -           if (entrylen > namelen)\n>     -\n>       ## tree-walk.h ##\n>      @@ tree-walk.h: struct tree_desc {\n>        *\n> 26:  40878d04550 ! 25:  dd012b661e5 tree-walk.h API: add a tree_entry_extract_all() function\n>     @@ Commit message\n>\n>          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n>     - ## builtin/update-index.c ##\n>     -@@ builtin/update-index.c: static struct cache_entry *read_one_ent(const char *which,\n>     -                                   struct object_id *ent, const char *path,\n>     -                                   int namelen, int stage)\n>     - {\n>     -+  enum object_type object_type;\n>     -   unsigned short mode;\n>     -   struct object_id oid;\n>     -   struct cache_entry *ce;\n>     -\n>     --  if (get_tree_entry_mode(the_repository, ent, path, &oid, &mode)) {\n>     -+  if (get_tree_entry_all(the_repository, ent, path, &oid,\n>     -+                         &mode, &object_type)) {\n>     -           if (which)\n>     -                   error(\"%s: not in %s branch.\", path, which);\n>     -           return NULL;\n>     -   }\n>     --  if (mode == S_IFDIR) {\n>     -+  if (object_type == OBJ_TREE) {\n>     -           if (which)\n>     -                   error(\"%s: not a blob in %s branch.\", path, which);\n>     -           return NULL;\n>     -\n>       ## tree-diff.c ##\n>      @@ tree-diff.c: static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n>         assert(t || tp);\n>     @@ tree-diff.c: static struct combine_diff_path *emit_path(struct combine_diff_path\n>      +          oid = tree_entry_extract_all(t, &path, &mode, &object_type);\n>                 pathlen = tree_entry_len(&t->entry);\n>      -          isdir = S_ISDIR(mode);\n>     -+          isdir = object_type == OBJ_TREE;\n>     ++          isdir = (object_type == OBJ_TREE);\n>         } else {\n>                 /*\n>                  * a path was removed - take path from imin parent. Also take\n>     @@ tree-walk.c: static int find_tree_entry(struct repository *r, struct tree_desc *\n>                 struct object_id oid;\n>                 int entrylen, cmp;\n>\n>     --          oidcpy(&oid, tree_entry_extract_mode(t, &entry, mode));\n>     +-          oidcpy(&oid, tree_entry_extract(t, &entry, mode));\n>      +          oidcpy(&oid, tree_entry_extract_all(t, &entry, mode, object_type));\n>     -+\n>                 entrylen = tree_entry_len(&t->entry);\n>                 update_tree_entry(t);\n>                 if (entrylen > namelen)\n>  -:  ----------- > 26:  b6ee8410e38 tree-walk.h API: add get_tree_entry_all()\n> 23:  6b864e066d9 ! 27:  5c98afd9e7a tree-walk.h API: add a get_tree_entry_path() function\n>     @@ tree-walk.c: int get_tree_entry_all(struct repository *r,\n>\n>       ## tree-walk.h ##\n>      @@ tree-walk.h: struct traverse_info {\n>     -  * \"struct name_entry\" you'd like. You always need to pointer to an\n>     +  * \"struct name_entry\" you'd like. You always need a pointer to an\n>        * appropriate variable to fill in (NULL won't do!):\n>        *\n>      + * get_tree_entry_path(): <no extra argument, just get the common 'path'>\n>  -:  ----------- > 28:  3e7e0f7eb85 blame: emit a better error on 'git blame directory'\n> 27:  e4a6fae1ae0 = 29:  ac1ccf13570 tree-walk.h API: add a tree_entry_extract_type() function\n> 28:  766b4460a95 <  -:  ----------- tree-walk.h API users: rename \"struct name_entry\"'s \"mode\" to \"raw_mode\"\n> 29:  4bdf94ae5c1 <  -:  ----------- tree.h API users: rename read_tree_fn_t's \"mode\" to \"raw_mode\"\n> 30:  9d049fdbd00 <  -:  ----------- tree-walk.h API: move canon_mode() back out of decode_tree_entry()\n> --\n> 2.31.0.rc2.211.g1d0b8788b3\n>\n"},{"id":"419286","messageId":"87blbjfqkh.fsf@evledraar.gmail.com","threadId":"18927","inReplyTo":"CABPp-BHnz-_633UZycnaD1Mj9vrhSJ4RA6yUr4F7dvZwyOJBiA@mail.gmail.com","subject":"Re: [PATCH v2 00/29] tree-walk: mostly replace \"mode\" with \"enum object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T08:30:54Z","receivedAt":"2021-03-16T08:31:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Mar 16 2021, Elijah Newren wrote:\n\n> On Mon, Mar 15, 2021 at 7:13 PM Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>>\n>> A v2 of the big tree-walk.[ch] refactoring series, goals etc. at the\n>> v1 at:\n>> https://lore.kernel.org/git/20210308150650.18626-1-avarab@gmail.com/\n>>\n>> It is based on my just-re-rolled v3 read_tree_recursive() series:\n>> https://lore.kernel.org/git/20210315234344.28427-1-avarab@gmail.com/\n>>\n>> This version should address all the feedback on v1 for patces 1-27/30,\n>> thanks to Elijah for very valuable comments on v1.\n>>\n>> It's mostly small nits here and there, except:\n>>\n>>  - I found that the change I'd made to update-index.c was buggy at the\n>>    point it was introduced in the series, the object_type would be\n>>    uninitialized. There were/are no tests for that, but I've moved\n>>    things around so we don't have that bug anymore.\n>>\n>>  - Elijah had a comment on whether we needed oid_object_info() in\n>>    blame.c. As it turns out we don't need a \"is blob?\" check at all\n>>    there. There's a new 28/29 to refactor that small part of blame.c,\n>>    along with a test.\n>>\n>> What this re-roll *does not* include is the final 28-30/30 part of v1\n>> to s/mode/raw_mode/g and move canonical_mode() out of tree-walk.h.\n>>\n>> So a follow-up series will still be needed to fix the fsck.c check for\n>> bad modes, but I wanted to split that tricker change off from this\n>> rather big initial refactoring.\n>\n> Splitting those trickier bits off makes sense.\n>\n> This series looks really good and addresses most of my feedback from\n> v1.  There were two commit message wording comments in patches 21 and\n> 27 leftover from the previous round, and a nasty bug introduced in\n> patch 6 still left from the previous round.\n>\n> I also had a question on the new testcase in patch 28.\n>\n> Other than that, though, the series looks good to me.\n\nSorry about that, and thanks for spotting those.\n\nFor what it's worth I *did* actually address all your feedback from the\nlast round and would have prominently noted if I'd left the issue you\nnoted in v1's 6/30 in v2.\n\nThe problem is that version never made it out of my local tree.\n\nI was rebasing this in multiple passes, and I think the immediate\nculprit is that I've recently aliased \"git commit --amend\" to \"git ca\"\nand \"git rebase --continue\" as \"git rc\" as a UI experiment.\n\nI then managed to mix the two up, so I aborted during the middle of one\nof my rebase passes, then when I came back to the computer managed to\nforget what state I should be in, knowing I'd dealt with 06/30 already\netc., and happily continued.\n\nAnd then in trying to re-remember how to massage git-format-patch to\napply the correct range-diff invocation option I managed to miss that\nbefore sending it out.\n\nAnyway. I'm just noting the details of that local screwup I think it's\nimportant to accurately summarize changes from the last iteration in CL\nor patches when getting feedback like that.\n\nI'll go over your v1+v2 feedback again and submit a v3 soon. It's taking\nme a bit longer than I'd have wanted because of course Murphy's law\ndictates that the first thing I got in my git.git this morning was a\nwarning about too many loose objects, and before having read my E-Mail I\nthought \"do I have anything outstanding but un-referenced?\", \"no!\", and\nran \"git gc --prune=now\".\n\n>>\n>> Ævar Arnfjörð Bjarmason (29):\n>>   diff.c: remove redundant canon_mode() call\n>>   notes & match-trees: use name_entry's \"pathlen\" member\n>>   cache.h: add a comment to object_type()\n>>   tree-walk.h: add object_type member to name_entry\n>>   tree-walk.c: migrate to using new \"object_type\" field when possible\n>>   cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n>>   tree-walk.h users: switch object_type(...) to new .object_type\n>>   tree.h: format argument lists of read_tree_recursive() users\n>>   tree.h users: format argument lists in archive.c\n>>   archive: get rid of 'stage' parameter\n>>   tree.h API: make read_tree_fn_t take an \"enum object_type\"\n>>   tree-walk.h users: migrate \"p->mode &&\" pattern\n>>   tree-walk.h users: refactor chained \"mode\" if/else into switch\n>>   tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n>>   merge-tree tests: test for the mode comparison in same_entry()\n>>   merge-ort: correct reference to test in 62fdec17a11\n>>   fsck.c: switch on \"object_type\" in fsck_walk_tree()\n>>   tree-walk.h users: use temporary variable(s) for \"mode\"\n>>   tree-walk.h API: formatting changes for subsequent commit\n>>   tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n>>   tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n>>   tree-walk.h API: add get_tree_entry_type()\n>>   tree-walk.h API: document and format tree_entry_extract()\n>>   tree-entry.h API: rename tree_entry_extract() to\n>>     tree_entry_extract_mode()\n>>   tree-walk.h API: add a tree_entry_extract_all() function\n>>   tree-walk.h API: add get_tree_entry_all()\n>>   tree-walk.h API: add a get_tree_entry_path() function\n>>   blame: emit a better error on 'git blame directory'\n>>   tree-walk.h API: add a tree_entry_extract_type() function\n>>\n>>  archive.c                       | 50 +++++++++---------\n>>  blame.c                         |  9 ++--\n>>  builtin/checkout.c              |  6 ++-\n>>  builtin/fast-import.c           |  8 +--\n>>  builtin/grep.c                  |  6 +--\n>>  builtin/log.c                   |  7 +--\n>>  builtin/ls-files.c              |  6 ++-\n>>  builtin/ls-tree.c               | 14 +++---\n>>  builtin/merge-tree.c            | 30 +++++++----\n>>  builtin/mktree.c                |  4 +-\n>>  builtin/pack-objects.c          |  6 +--\n>>  builtin/reflog.c                |  3 +-\n>>  builtin/rm.c                    |  2 +-\n>>  builtin/update-index.c          |  6 ++-\n>>  cache-tree.c                    |  2 +-\n>>  cache.h                         | 11 ++--\n>>  combine-diff.c                  |  8 +--\n>>  delta-islands.c                 |  2 +-\n>>  diff.c                          |  2 +-\n>>  fsck.c                          | 23 ++++-----\n>>  http-push.c                     |  6 ++-\n>>  line-log.c                      |  2 +-\n>>  list-objects.c                  | 20 +++++---\n>>  match-trees.c                   | 52 +++++++++----------\n>>  merge-ort.c                     | 13 ++---\n>>  merge-recursive.c               | 33 ++++++------\n>>  notes.c                         | 14 +++---\n>>  object-name.c                   |  7 ++-\n>>  pack-bitmap-write.c             |  8 +--\n>>  read-cache.c                    | 16 +++---\n>>  revision.c                      | 12 +++--\n>>  t/t4300-merge-tree.sh           | 44 ++++++++++++++++\n>>  t/t8004-blame-with-conflicts.sh | 20 ++++++++\n>>  tree-diff.c                     | 30 +++++++----\n>>  tree-walk.c                     | 89 ++++++++++++++++++++++++---------\n>>  tree-walk.h                     | 63 ++++++++++++++++++++---\n>>  tree.c                          | 19 ++++---\n>>  tree.h                          |  5 +-\n>>  unpack-trees.c                  | 24 +++++----\n>>  walker.c                        | 22 ++++----\n>>  40 files changed, 460 insertions(+), 244 deletions(-)\n>>\n>> Range-diff:\n>>  1:  e5df57c3440 =  1:  f9bbc30f69f diff.c: remove redundant canon_mode() call\n>>  2:  8c2500bbf35 =  2:  187fc2c3e64 notes & match-trees: use name_entry's \"pathlen\" member\n>>  3:  3d98e0c132f !  3:  311637c5583 cache.h: add a comment to object_type()\n>>     @@ Commit message\n>>          cache.h: add a comment to object_type()\n>>\n>>          Add a comment to the object_type() function to explain what it\n>>     -    returns, and whet the \"mode\" is in the \"else\" case.\n>>     +    returns, and what the \"mode\" is in the \"else\" case.\n>>\n>>          The object_type() function dates back to 4d1012c3709 (Fix rev-list\n>>          when showing objects involving submodules, 2007-11-11). It's not\n>>  4:  ce5808b317c =  4:  fecfe3d462c tree-walk.h: add object_type member to name_entry\n>>  5:  18f26531acf =  5:  db961ab5e8d tree-walk.c: migrate to using new \"object_type\" field when possible\n>>  6:  55e2640b815 !  6:  df2fc76161d cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n>>     @@ cache.h: int repo_interpret_branch_name(struct repository *r,\n>>\n>>      -int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n>>      -int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n>>     -+int base_name_compare(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2);\n>>     -+int df_name_compare(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2);\n>>     ++int base_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n>>     ++int df_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n>>       int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);\n>>       int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);\n>>\n>>     @@ combine-diff.c\n>>                           const struct diff_filespec *two)\n>>       {\n>>      -  if (!S_ISDIR(one->mode) && !S_ISDIR(two->mode))\n>>     -+  int isdir_one = S_ISDIR(one->mode);\n>>     -+  int isdir_two = S_ISDIR(two->mode);\n>>     -+  if (!isdir_one && !isdir_two)\n>>     ++  int istree_one = S_ISDIR(one->mode);\n>>     ++  int istree_two = S_ISDIR(two->mode);\n>>     ++  if (!istree_one && !istree_two)\n>>                 return strcmp(one->path, two->path);\n>>\n>>      -  return base_name_compare(one->path, strlen(one->path), one->mode,\n>>      -                           two->path, strlen(two->path), two->mode);\n>>     -+  return base_name_compare(one->path, strlen(one->path), isdir_one,\n>>     -+                           two->path, strlen(two->path), isdir_two);\n>>     ++  return base_name_compare(one->path, strlen(one->path), istree_one,\n>>     ++                           two->path, strlen(two->path), istree_two);\n>>       }\n>>\n>>       static int filename_changed(char status)\n>>     @@ match-trees.c: static void *fill_tree_desc_strict(struct tree_desc *desc,\n>>       {\n>>      -  return base_name_compare(a->path, tree_entry_len(a), a->mode,\n>>      -                           b->path, tree_entry_len(b), b->mode);\n>>     -+  int isdira = a->object_type == OBJ_TREE;\n>>     -+  int isdirb = b->object_type == OBJ_TREE;\n>>     -+  return base_name_compare(a->path, tree_entry_len(a), isdira,\n>>     -+                           b->path, tree_entry_len(b), isdirb);\n>>     ++  int istree_a = (a->object_type == OBJ_TREE);\n>>     ++  int istree_b = (b->object_type == OBJ_TREE);\n>>     ++  return base_name_compare(a->path, tree_entry_len(a), istree_a,\n>>     ++                           b->path, tree_entry_len(b), istree_b);\n>>       }\n>>\n>>       /*\n>>     @@ read-cache.c: int ie_modified(struct index_state *istate,\n>>\n>>      -int base_name_compare(const char *name1, int len1, int mode1,\n>>      -                const char *name2, int len2, int mode2)\n>>     -+int base_name_compare(const char *name1, int len1, int isdir1,\n>>     -+                const char *name2, int len2, int isdir2)\n>>     ++int base_name_compare(const char *name1, int len1, int istree1,\n>>     ++                const char *name2, int len2, int istree2)\n>>       {\n>>         unsigned char c1, c2;\n>>         int len = len1 < len2 ? len1 : len2;\n>>     @@ read-cache.c: int base_name_compare(const char *name1, int len1, int mode1,\n>>         c1 = name1[len];\n>>         c2 = name2[len];\n>>      -  if (!c1 && S_ISDIR(mode1))\n>>     -+  if (!c1 && isdir1)\n>>     ++  if (!c1 && istree1)\n>>                 c1 = '/';\n>>      -  if (!c2 && S_ISDIR(mode2))\n>>     -+  if (!c2 && isdir2)\n>>     ++  if (!c2 && istree2)\n>>                 c2 = '/';\n>>         return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;\n>>       }\n>>     @@ read-cache.c: int base_name_compare(const char *name1, int len1, int mode1,\n>>        */\n>>      -int df_name_compare(const char *name1, int len1, int mode1,\n>>      -              const char *name2, int len2, int mode2)\n>>     -+int df_name_compare(const char *name1, int len1, int isdir1,\n>>     -+              const char *name2, int len2, int isdir2)\n>>     ++int df_name_compare(const char *name1, int len1, int istree1,\n>>     ++              const char *name2, int len2, int istree2)\n>>       {\n>>         int len = len1 < len2 ? len1 : len2, cmp;\n>>         unsigned char c1, c2;\n>>     @@ read-cache.c: int df_name_compare(const char *name1, int len1, int mode1,\n>>                 return 0;\n>>         c1 = name1[len];\n>>      -  if (!c1 && S_ISDIR(mode1))\n>>     -+  if (!c1 && isdir1)\n>>     ++  if (!c1 && istree1)\n>>                 c1 = '/';\n>>         c2 = name2[len];\n>>      -  if (!c2 && S_ISDIR(mode2))\n>>     -+  if (!c2 && isdir2)\n>>     ++  if (!c2 && istree2)\n>>                 c2 = '/';\n>>         if (c1 == '/' && !c2)\n>>                 return 0;\n>>     @@ tree-diff.c: static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_des\n>>       {\n>>         struct name_entry *e1, *e2;\n>>         int cmp;\n>>     -+  int e1_is_tree, e2_is_tree;\n>>     ++  int istree_e1, istree_e2;\n>>\n>>         /* empty descriptors sort after valid tree entries */\n>>         if (!t1->size)\n>>     @@ tree-diff.c: static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_des\n>>                 return -1;\n>>\n>>         e1 = &t1->entry;\n>>     -+  e1_is_tree = e1->object_type == OBJ_TREE;\n>>     ++  istree_e1 = (e1->object_type == OBJ_TREE);\n>>         e2 = &t2->entry;\n>>      -  cmp = base_name_compare(e1->path, tree_entry_len(e1), e1->mode,\n>>      -                          e2->path, tree_entry_len(e2), e2->mode);\n>>     -+  e2_is_tree = e2->object_type == OBJ_TREE;\n>>     -+  cmp = base_name_compare(e1->path, tree_entry_len(e1), e1_is_tree,\n>>     -+                          e2->path, tree_entry_len(e2), e2_is_tree);\n>>     ++  istree_e2 = (e2->object_type == OBJ_TREE);\n>>     ++  cmp = base_name_compare(e1->path, tree_entry_len(e1), istree_e1,\n>>     ++                          e2->path, tree_entry_len(e2), istree_e2);\n>>         return cmp;\n>>       }\n>>\n>>     @@ unpack-trees.c: static int traverse_trees_recursive(int n, unsigned long dirmask\n>>                                       const struct traverse_info *info,\n>>                                       const char *name, size_t namelen,\n>>      -                                unsigned mode)\n>>     -+                                unsigned is_tree)\n>>     ++                                unsigned istree)\n>>       {\n>>         int pathlen, ce_len;\n>>         const char *ce_name;\n>>     @@ unpack-trees.c: static int do_compare_entry_piecewise(const struct cache_entry *\n>>         ce_name = ce->name + pathlen;\n>>\n>>      -  return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n>>     -+  return df_name_compare(ce_name, ce_len, 0, name, namelen, is_tree);\n>>     ++  return df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n>>       }\n>>\n>>       static int do_compare_entry(const struct cache_entry *ce,\n>>                             const struct traverse_info *info,\n>>                             const char *name, size_t namelen,\n>>      -                      unsigned mode)\n>>     -+                      unsigned is_tree)\n>>     ++                      unsigned istree)\n>>       {\n>>         int pathlen, ce_len;\n>>         const char *ce_name;\n>>     @@ unpack-trees.c: static int do_compare_entry(const struct cache_entry *ce,\n>>          */\n>>         if (!info->traverse_path)\n>>      -          return do_compare_entry_piecewise(ce, info, name, namelen, mode);\n>>     -+          return do_compare_entry_piecewise(ce, info, name, namelen, is_tree);\n>>     ++          return do_compare_entry_piecewise(ce, info, name, namelen, istree);\n>>\n>>         cmp = strncmp(ce->name, info->traverse_path, info->pathlen);\n>>         if (cmp)\n>>     @@ unpack-trees.c: static int do_compare_entry(const struct cache_entry *ce,\n>>         ce_name = ce->name + pathlen;\n>>\n>>      -  return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n>>     -+  return df_name_compare(ce_name, ce_len, 0, name, namelen, is_tree);\n>>     ++  return df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n>>       }\n>>\n>>       static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)\n>>       {\n>>      -  int cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);\n>>     -+  int is_tree = n->object_type == OBJ_TREE;\n>>     -+  int cmp = do_compare_entry(ce, info, n->path, n->pathlen, is_tree);\n>>     ++  int istree = (n->object_type == OBJ_TREE);\n>>     ++  int cmp = do_compare_entry(ce, info, n->path, n->pathlen, istree);\n>>         if (cmp)\n>>                 return cmp;\n>>\n>>  7:  abc128f6cb9 =  7:  49d5da8c086 tree-walk.h users: switch object_type(...) to new .object_type\n>>  8:  dcf13faf3cd !  8:  c9d209d496a tree.h: format argument lists of read_tree_recursive() users\n>>     @@ archive.c: static int check_attr_export_subst(const struct attr_check *check)\n>>      -          void *context)\n>>      +                         int baselen, const char *filename,\n>>      +                         unsigned mode,\n>>     -+                         int stage, void *context)\n>>     ++                         int stage,\n>>     ++                         void *context)\n>>       {\n>>         static struct strbuf path = STRBUF_INIT;\n>>         struct archiver_context *c = context;\n>>     @@ tree.h: struct tree *parse_tree_indirect(const struct object_id *oid);\n>>      +                        unsigned int,\n>>      +                        void *);\n>>\n>>     - int read_tree_recursive(struct repository *r,\n>>     -                   struct tree *tree,\n>>     + int read_tree_at(struct repository *r,\n>>     +            struct tree *tree,\n>>  9:  b33fcf82349 !  9:  a6d2660fe14 tree.h users: format argument lists in archive.c\n>>     @@ Commit message\n>>          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>>\n>>       ## archive.c ##\n>>     -@@ archive.c: static int check_attr_export_subst(const struct attr_check *check)\n>>     - static int write_archive_entry(const struct object_id *oid, const char *base,\n>>     -                          int baselen, const char *filename,\n>>     -                          unsigned mode,\n>>     --                         int stage, void *context)\n>>     -+                         int stage,\n>>     -+                         void *context)\n>>     - {\n>>     -   static struct strbuf path = STRBUF_INIT;\n>>     -   struct archiver_context *c = context;\n>>      @@ archive.c: static int write_archive_entry(const struct object_id *oid, const char *base,\n>>       }\n>>\n>> 10:  6a20d3c058f = 10:  15f7f89acca archive: get rid of 'stage' parameter\n>> 11:  a7f7444917c ! 11:  7a71404ea3f tree.h API: make read_tree_fn_t take an \"enum object_type\"\n>>     @@ merge-recursive.c: static int save_files_dirs(const struct object_id *oid,\n>>       static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n>>\n>>       ## tree.c ##\n>>     -@@ tree.c: static int read_tree_1(struct repository *r,\n>>     +@@ tree.c: int read_tree_at(struct repository *r,\n>>         init_tree_desc(&desc, tree->buffer, tree->size);\n>>\n>>         while (tree_entry(&desc, &entry)) {\n>>     @@ tree.c: static int read_tree_1(struct repository *r,\n>>                 if (retval != all_entries_interesting) {\n>>                         retval = tree_entry_interesting(r->index, &entry,\n>>                                                         base, 0, pathspec);\n>>     -@@ tree.c: static int read_tree_1(struct repository *r,\n>>     +@@ tree.c: int read_tree_at(struct repository *r,\n>>                 }\n>>\n>>                 switch (fn(&entry.oid, base,\n>>     @@ tree.c: static int read_tree_1(struct repository *r,\n>>                 case 0:\n>>                         continue;\n>>                 case READ_TREE_RECURSIVE:\n>>     -@@ tree.c: static int read_tree_1(struct repository *r,\n>>     +@@ tree.c: int read_tree_at(struct repository *r,\n>>                         return -1;\n>>                 }\n>>\n>>     @@ tree.c: static int read_tree_1(struct repository *r,\n>>                         commit = lookup_commit(r, &entry.oid);\n>>                         if (!commit)\n>>                                 die(\"Commit %s in submodule path %s%s not found\",\n>>     -@@ tree.c: static int read_tree_1(struct repository *r,\n>>     +@@ tree.c: int read_tree_at(struct repository *r,\n>>                                     base->buf, entry.path);\n>>\n>>                         oidcpy(&oid, get_commit_tree_oid(commit));\n>>     @@ tree.h: int cmp_cache_name_compare(const void *a_, const void *b_);\n>>      +                        enum object_type, unsigned int,\n>>                               void *);\n>>\n>>     - int read_tree_recursive(struct repository *r,\n>>     + int read_tree_at(struct repository *r,\n>> 12:  625c643513d ! 12:  64dc9364bae tree-walk.h users: migrate \"p->mode &&\" pattern\n>>     @@ Metadata\n>>       ## Commit message ##\n>>          tree-walk.h users: migrate \"p->mode &&\" pattern\n>>\n>>     -    Change code that dpends on \"p->mode\" either being a valid mode or zero\n>>     -    to use a p->object_type comparison to \"OBJ_NONE\".\n>>     +    Change code that depends on \"p->mode\" either being a valid mode or\n>>     +    zero to use a p->object_type comparison to \"OBJ_NONE\".\n>>\n>>     -    The object_type() function in cache.h will not return OBJ_NONE, but in\n>>     -    this these API users are implicitly relying on the memzero() that\n>>     -    happens in setup_traverse_info().\n>>     +    The object_type() function in cache.h will not return OBJ_NONE, but\n>>     +    these API users are implicitly relying on the memzero() that happens\n>>     +    in setup_traverse_info().\n>>\n>>          Since OBJ_NONE is \"0\" we can also rely on that being zero'd out here,\n>>          along with the rest of the structure. I think this is slightly less\n>> 13:  37b28c7feff = 13:  93ed3edbbd5 tree-walk.h users: refactor chained \"mode\" if/else into switch\n>> 14:  e0b8ec6e291 = 14:  7aa48aa34c3 tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n>> 15:  0cd162c43d7 = 15:  3ae81621dcf merge-tree tests: test for the mode comparison in same_entry()\n>> 16:  f8ce666d4a7 = 16:  4249ad5c4de merge-ort: correct reference to test in 62fdec17a11\n>> 17:  4963902ba97 = 17:  e5e17505dde fsck.c: switch on \"object_type\" in fsck_walk_tree()\n>> 18:  d74e6778009 = 18:  3f0b884f1fd tree-walk.h users: use temporary variable(s) for \"mode\"\n>> 19:  d39db486d4e = 19:  174167613bb tree-walk.h API: formatting changes for subsequent commit\n>> 20:  69eb956b1ab = 20:  ec76db613f2 tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n>> 21:  cc56453e600 = 21:  11e34941729 tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n>> 22:  ca9e3b3ad00 ! 22:  b31c106557f tree-walk.h API: Add get_tree_entry_type()\n>>     @@ Metadata\n>>      Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>>\n>>       ## Commit message ##\n>>     -    tree-walk.h API: Add get_tree_entry_type()\n>>     +    tree-walk.h API: add get_tree_entry_type()\n>>\n>>          Add a get_tree_entry_type() helper function to compliment the existing\n>>     -    get_tree_entry(). Move those users of get_tree_entry_type() who didn't\n>>     -    care about the mode specifically, but just want to know whether the\n>>     -    tree entry is one of OBJ_{BLOB,COMMIT,TREE} over to it.\n>>     +    get_tree_entry(), and a static get_tree_entry_all() which it uses internally.\n>>     +\n>>     +    Move those users of get_tree_entry_type() who didn't care about the\n>>     +    mode specifically, but just want to know whether the tree entry is one\n>>     +    of OBJ_{BLOB,COMMIT,TREE} over to the new get_tree_entry_type().\n>>     +\n>>     +    The get_tree_entry_all() function itself will be made non-static in a\n>>     +    subsequent commit. I'm leaving its argument list indented accordingly\n>>     +    to reduce churn when I do so.\n>>\n>>          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>>\n>>     @@ archive.c: static void parse_treeish_arg(const char **argv,\n>>\n>>                 tree = parse_tree_indirect(&tree_oid);\n>>\n>>     - ## blame.c ##\n>>     -@@ blame.c: static void verify_working_tree_path(struct repository *r,\n>>     -   for (parents = work_tree->parents; parents; parents = parents->next) {\n>>     -           const struct object_id *commit_oid = &parents->item->object.oid;\n>>     -           struct object_id blob_oid;\n>>     --          unsigned short mode;\n>>     --          int ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n>>     --                                        &mode);\n>>     -+          enum object_type object_type;\n>>     -+          int ret = get_tree_entry_type(r, commit_oid, path, &blob_oid,\n>>     -+                                        &object_type);\n>>     -\n>>     --          if (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n>>     -+          if (!ret && object_type == OBJ_BLOB)\n>>     -                   return;\n>>     -   }\n>>     -\n>>     -\n>>       ## match-trees.c ##\n>>      @@ match-trees.c: void shift_tree_by(struct repository *r,\n>>                    const char *shift_prefix)\n>>     @@ match-trees.c: void shift_tree_by(struct repository *r,\n>>\n>>       ## tree-walk.c ##\n>>      @@ tree-walk.c: struct dir_state {\n>>     +   struct object_id oid;\n>>     + };\n>>\n>>     ++static int get_tree_entry_all(struct repository *r,\n>>     ++                        const struct object_id *tree_oid,\n>>     ++                        const char *name,\n>>     ++                        struct object_id *oid,\n>>     ++                        unsigned short *mode,\n>>     ++                        enum object_type *object_type);\n>>     ++\n>>       static int find_tree_entry(struct repository *r, struct tree_desc *t,\n>>                            const char *name, struct object_id *result,\n>>      -                     unsigned short *mode)\n>>     @@ tree-walk.c: static int find_tree_entry(struct repository *r, struct tree_desc *\n>>      -                  const char *name,\n>>      -                  struct object_id *oid,\n>>      -                  unsigned short *mode)\n>>     -+int get_tree_entry_all(struct repository *r,\n>>     ++static int get_tree_entry_all(struct repository *r,\n>>      +                 const struct object_id *tree_oid,\n>>      +                 const char *name,\n>>      +                 struct object_id *oid,\n>>     @@ tree-walk.h: struct traverse_info {\n>>      - * The third and fourth parameters are set to the entry's sha1 and\n>>      - * mode respectively.\n>>      + * There are variants of this function depending on what fields in the\n>>     -+ * \"struct name_entry\" you'd like. You always need to pointer to an\n>>     ++ * \"struct name_entry\" you'd like. You always need a pointer to an\n>>      + * appropriate variable to fill in (NULL won't do!):\n>>      + *\n>>      + * get_tree_entry_mode(): unsigned int mode\n>>      + * get_tree_entry_type(): enum object_type\n>>     -+ * get_tree_entry_all(): unsigned int mode, enum object_type\n>>        */\n>>       int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n>>                         struct object_id *,\n>>     @@ tree-walk.h: struct traverse_info {\n>>      +int get_tree_entry_type(struct repository *, const struct object_id *, const char *,\n>>      +                  struct object_id *,\n>>      +                  enum object_type *);\n>>     -+int get_tree_entry_all(struct repository *, const struct object_id *, const char *,\n>>     -+                 struct object_id *,\n>>     -+                 unsigned short *, enum object_type *);\n>>\n>>       /**\n>>        * Generate the full pathname of a tree entry based from the root of the\n>> 24:  5986f494aa1 ! 23:  304d5d4d1af tree-walk.h API: document and format tree_entry_extract()\n>>     @@ tree-walk.h: struct tree_desc {\n>>      - * `pathp` and `modep` arguments are set to the entry's pathname and mode\n>>      - * respectively.\n>>      + * `tree_desc's` `entry` member) and return the OID of the entry.\n>>     -+\n>>     ++ *\n>>      + * There are variants of this function depending on what fields in the\n>>     -+ * \"struct name_entry\" you'd like. You always need to pointer to an\n>>     ++ * \"struct name_entry\" you'd like. You always need a pointer to an\n>>      + * appropriate variable to fill in (NULL won't do!):\n>>      + *\n>>      + * tree_entry_extract_mode(): const char *path, unsigned int mode\n>> 25:  9b604a193b8 ! 24:  346453df356 tree-entry.h API: rename tree_entry_extract() to tree_entry_extract_mode()\n>>     @@ tree-diff.c: static struct combine_diff_path *emit_path(struct combine_diff_path\n>>\n>>                 isdir = S_ISDIR(mode);\n>>\n>>     - ## tree-walk.c ##\n>>     -@@ tree-walk.c: static int find_tree_entry(struct repository *r, struct tree_desc *t,\n>>     -           struct object_id oid;\n>>     -           int entrylen, cmp;\n>>     -\n>>     --          oidcpy(&oid, tree_entry_extract(t, &entry, mode));\n>>     -+          oidcpy(&oid, tree_entry_extract_mode(t, &entry, mode));\n>>     -           entrylen = tree_entry_len(&t->entry);\n>>     -           update_tree_entry(t);\n>>     -           if (entrylen > namelen)\n>>     -\n>>       ## tree-walk.h ##\n>>      @@ tree-walk.h: struct tree_desc {\n>>        *\n>> 26:  40878d04550 ! 25:  dd012b661e5 tree-walk.h API: add a tree_entry_extract_all() function\n>>     @@ Commit message\n>>\n>>          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>>\n>>     - ## builtin/update-index.c ##\n>>     -@@ builtin/update-index.c: static struct cache_entry *read_one_ent(const char *which,\n>>     -                                   struct object_id *ent, const char *path,\n>>     -                                   int namelen, int stage)\n>>     - {\n>>     -+  enum object_type object_type;\n>>     -   unsigned short mode;\n>>     -   struct object_id oid;\n>>     -   struct cache_entry *ce;\n>>     -\n>>     --  if (get_tree_entry_mode(the_repository, ent, path, &oid, &mode)) {\n>>     -+  if (get_tree_entry_all(the_repository, ent, path, &oid,\n>>     -+                         &mode, &object_type)) {\n>>     -           if (which)\n>>     -                   error(\"%s: not in %s branch.\", path, which);\n>>     -           return NULL;\n>>     -   }\n>>     --  if (mode == S_IFDIR) {\n>>     -+  if (object_type == OBJ_TREE) {\n>>     -           if (which)\n>>     -                   error(\"%s: not a blob in %s branch.\", path, which);\n>>     -           return NULL;\n>>     -\n>>       ## tree-diff.c ##\n>>      @@ tree-diff.c: static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n>>         assert(t || tp);\n>>     @@ tree-diff.c: static struct combine_diff_path *emit_path(struct combine_diff_path\n>>      +          oid = tree_entry_extract_all(t, &path, &mode, &object_type);\n>>                 pathlen = tree_entry_len(&t->entry);\n>>      -          isdir = S_ISDIR(mode);\n>>     -+          isdir = object_type == OBJ_TREE;\n>>     ++          isdir = (object_type == OBJ_TREE);\n>>         } else {\n>>                 /*\n>>                  * a path was removed - take path from imin parent. Also take\n>>     @@ tree-walk.c: static int find_tree_entry(struct repository *r, struct tree_desc *\n>>                 struct object_id oid;\n>>                 int entrylen, cmp;\n>>\n>>     --          oidcpy(&oid, tree_entry_extract_mode(t, &entry, mode));\n>>     +-          oidcpy(&oid, tree_entry_extract(t, &entry, mode));\n>>      +          oidcpy(&oid, tree_entry_extract_all(t, &entry, mode, object_type));\n>>     -+\n>>                 entrylen = tree_entry_len(&t->entry);\n>>                 update_tree_entry(t);\n>>                 if (entrylen > namelen)\n>>  -:  ----------- > 26:  b6ee8410e38 tree-walk.h API: add get_tree_entry_all()\n>> 23:  6b864e066d9 ! 27:  5c98afd9e7a tree-walk.h API: add a get_tree_entry_path() function\n>>     @@ tree-walk.c: int get_tree_entry_all(struct repository *r,\n>>\n>>       ## tree-walk.h ##\n>>      @@ tree-walk.h: struct traverse_info {\n>>     -  * \"struct name_entry\" you'd like. You always need to pointer to an\n>>     +  * \"struct name_entry\" you'd like. You always need a pointer to an\n>>        * appropriate variable to fill in (NULL won't do!):\n>>        *\n>>      + * get_tree_entry_path(): <no extra argument, just get the common 'path'>\n>>  -:  ----------- > 28:  3e7e0f7eb85 blame: emit a better error on 'git blame directory'\n>> 27:  e4a6fae1ae0 = 29:  ac1ccf13570 tree-walk.h API: add a tree_entry_extract_type() function\n>> 28:  766b4460a95 <  -:  ----------- tree-walk.h API users: rename \"struct name_entry\"'s \"mode\" to \"raw_mode\"\n>> 29:  4bdf94ae5c1 <  -:  ----------- tree.h API users: rename read_tree_fn_t's \"mode\" to \"raw_mode\"\n>> 30:  9d049fdbd00 <  -:  ----------- tree-walk.h API: move canon_mode() back out of decode_tree_entry()\n>> --\n>> 2.31.0.rc2.211.g1d0b8788b3\n>>\n\n"},{"id":"419308","messageId":"20210316155244.28328-1-avarab@gmail.com","threadId":"18927","inReplyTo":"20210315234344.28427-1-avarab@gmail.com","subject":"[PATCH v4 0/9] read_tree() and read_tree_recursive() refactoring","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:52:35Z","receivedAt":"2021-03-16T15:53:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The only changes since v3[1] is a fix for a small test nit by\nElijah[2].\n\n1. https://lore.kernel.org/git/20210315234344.28427-1-avarab@gmail.com/\n2. https://lore.kernel.org/git/CABPp-BHWDffkqXsisp9E-FJPR4PmByPWuxWkAir24WpqUu43Lg@mail.gmail.com/\n\nÆvar Arnfjörð Bjarmason (9):\n  ls-files tests: add meaningful --with-tree tests\n  tree.c API: move read_tree() into builtin/ls-files.c\n  ls-files: don't needlessly pass around stage variable\n  ls-files: refactor away read_tree()\n  tree.h API: remove support for starting at prefix != \"\"\n  tree.h API: remove \"stage\" parameter from read_tree_recursive()\n  tree.h API: rename read_tree_recursive() to read_tree()\n  show tests: add test for \"git show <tree>\"\n  tree.h API: expose read_tree_1() as read_tree_at()\n\n archive.c                     |  19 +++---\n builtin/checkout.c            |   8 +--\n builtin/log.c                 |   8 +--\n builtin/ls-files.c            |  76 +++++++++++++++++++++-\n builtin/ls-tree.c             |   6 +-\n cache.h                       |   2 +-\n merge-recursive.c             |   6 +-\n t/t3060-ls-files-with-tree.sh |  41 ++++++++++++\n t/t7007-show.sh               |  39 ++++++++++++\n tree.c                        | 117 ++++------------------------------\n tree.h                        |  24 +++----\n 11 files changed, 205 insertions(+), 141 deletions(-)\n\nRange-diff:\n -:  ---------- >  1:  b338f2c01a ls-files tests: add meaningful --with-tree tests\n -:  ---------- >  2:  4578b83944 tree.c API: move read_tree() into builtin/ls-files.c\n -:  ---------- >  3:  33656ff63b ls-files: don't needlessly pass around stage variable\n -:  ---------- >  4:  1c96d5d361 ls-files: refactor away read_tree()\n -:  ---------- >  5:  367cb99224 tree.h API: remove support for starting at prefix != \"\"\n -:  ---------- >  6:  38e36780e2 tree.h API: remove \"stage\" parameter from read_tree_recursive()\n -:  ---------- >  7:  859902ffd8 tree.h API: rename read_tree_recursive() to read_tree()\n 1:  a63c9b49f1 !  8:  8a6bebde23 show tests: add test for \"git show <tree>\"\n    @@ t/t7007-show.sh: test_expect_success 'showing two commits' '\n     +\n     +test_expect_success 'showing two trees' '\n     +\tcat >expected <<-EOF &&\n    -+\ttree main1:\n    ++\ttree main1^{tree}\n     +\n     +\tmain1.t\n     +\n    -+\ttree main2:\n    ++\ttree main2^{tree}\n     +\n     +\tmain1.t\n     +\tmain2.t\n     +\tEOF\n    -+\tgit show main1: main2: >actual &&\n    ++\tgit show main1^{tree} main2^{tree} >actual &&\n     +\ttest_cmp expected actual\n     +'\n     +\n    @@ t/t7007-show.sh: test_expect_success 'showing two commits' '\n     +\tmkdir not-recursive/a &&\n     +\ttest_commit -C not-recursive a/file &&\n     +\tcat >expected <<-EOF &&\n    -+\ttree a/file:\n    ++\ttree HEAD^{tree}\n     +\n     +\ta/\n     +\tmain1.t\n     +\tEOF\n    -+\tgit -C not-recursive show a/file: >actual &&\n    ++\tgit -C not-recursive show HEAD^{tree} >actual &&\n     +\ttest_cmp expected actual\n     +'\n     +\n 2:  570642c862 =  9:  29996dd82b tree.h API: expose read_tree_1() as read_tree_at()\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419309","messageId":"20210316155244.28328-2-avarab@gmail.com","threadId":"18927","inReplyTo":"20210315234344.28427-1-avarab@gmail.com","subject":"[PATCH v4 1/9] ls-files tests: add meaningful --with-tree tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:52:36Z","receivedAt":"2021-03-16T15:53:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add tests for \"ls-files --with-tree\". There was effectively no\ncoverage for any normal usage of this command, only the tests added in\n54e1abce90e (Add test case for ls-files --with-tree, 2007-10-03) for\nan obscure bug.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3060-ls-files-with-tree.sh | 41 +++++++++++++++++++++++++++++++++++\n 1 file changed, 41 insertions(+)\n\ndiff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh\nindex 52ed665fcd..b257c792a4 100755\n--- a/t/t3060-ls-files-with-tree.sh\n+++ b/t/t3060-ls-files-with-tree.sh\n@@ -47,6 +47,12 @@ test_expect_success setup '\n \tgit add .\n '\n \n+test_expect_success 'usage' '\n+\ttest_expect_code 128 git ls-files --with-tree=HEAD -u &&\n+\ttest_expect_code 128 git ls-files --with-tree=HEAD -s &&\n+\ttest_expect_code 128 git ls-files --recurse-submodules --with-tree=HEAD\n+'\n+\n test_expect_success 'git ls-files --with-tree should succeed from subdir' '\n \t# We have to run from a sub-directory to trigger prune_path\n \t# Then we finally get to run our --with-tree test\n@@ -60,4 +66,39 @@ test_expect_success \\\n     'git ls-files --with-tree should add entries from named tree.' \\\n     'test_cmp expected output'\n \n+test_expect_success 'no duplicates in --with-tree output' '\n+\tgit ls-files --with-tree=HEAD >actual &&\n+\tsort -u actual >expected &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'setup: output in a conflict' '\n+\ttest_create_repo conflict &&\n+\ttest_commit -C conflict BASE file &&\n+\ttest_commit -C conflict A file foo &&\n+\tgit -C conflict reset --hard BASE &&\n+\ttest_commit -C conflict B file bar\n+'\n+\n+test_expect_success 'output in a conflict' '\n+\ttest_must_fail git -C conflict merge A B &&\n+\tcat >expected <<-\\EOF &&\n+\tfile\n+\tfile\n+\tfile\n+\tfile\n+\tEOF\n+\tgit -C conflict ls-files --with-tree=HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'output with removed .git/index' '\n+\tcat >expected <<-\\EOF &&\n+\tfile\n+\tEOF\n+\trm conflict/.git/index &&\n+\tgit -C conflict ls-files --with-tree=HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419310","messageId":"20210316155244.28328-4-avarab@gmail.com","threadId":"18927","inReplyTo":"20210315234344.28427-1-avarab@gmail.com","subject":"[PATCH v4 3/9] ls-files: don't needlessly pass around stage variable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:52:38Z","receivedAt":"2021-03-16T15:53:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that read_tree() has been moved to ls-files.c we can get rid of\nthe stage != 1 case that'll never happen.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 17 ++++-------------\n 1 file changed, 4 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex a445862281..74d572a3e4 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -470,21 +470,12 @@ static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base\n }\n \n \n-static int read_tree(struct repository *r, struct tree *tree, int stage,\n+static int read_tree(struct repository *r, struct tree *tree,\n \t\t     struct pathspec *match, struct index_state *istate)\n {\n \tread_tree_fn_t fn = NULL;\n \tint i, err;\n \n-\t/*\n-\t * Currently the only existing callers of this function all\n-\t * call it with stage=1 and after making sure there is nothing\n-\t * at that stage; we could always use read_one_entry_quick().\n-\t *\n-\t * But when we decide to straighten out git-read-tree not to\n-\t * use unpack_trees() in some cases, this will probably start\n-\t * to matter.\n-\t */\n \n \t/*\n \t * See if we have cache entry at the stage.  If so,\n@@ -493,13 +484,13 @@ static int read_tree(struct repository *r, struct tree *tree, int stage,\n \t */\n \tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n \t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == stage)\n+\t\tif (ce_stage(ce) == 1)\n \t\t\tfn = read_one_entry;\n \t}\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n+\terr = read_tree_recursive(r, tree, \"\", 0, 1, match, fn, istate);\n \tif (fn == read_one_entry || err)\n \t\treturn err;\n \n@@ -549,7 +540,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n \t} else\n \t\tmemset(&pathspec, 0, sizeof(pathspec));\n-\tif (read_tree(the_repository, tree, 1, &pathspec, istate))\n+\tif (read_tree(the_repository, tree, &pathspec, istate))\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \n \tfor (i = 0; i < istate->cache_nr; i++) {\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419311","messageId":"20210316155244.28328-5-avarab@gmail.com","threadId":"18927","inReplyTo":"20210315234344.28427-1-avarab@gmail.com","subject":"[PATCH v4 4/9] ls-files: refactor away read_tree()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:52:39Z","receivedAt":"2021-03-16T15:53:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor away the read_tree() function into its only user,\noverlay_tree_on_index().\n\nFirst, change read_one_entry_opt() to use the strbuf parameter\nread_tree_recursive() passes down in place. This finishes up a partial\nrefactoring started in 6a0b0b6de99 (tree.c: update read_tree_recursive\ncallback to pass strbuf as base, 2014-11-30).\n\nMoving the rest into overlay_tree_on_index() makes this index juggling\nwe're doing easier to read.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 74 +++++++++++++++++++++-------------------------\n 1 file changed, 33 insertions(+), 41 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 74d572a3e4..db53e2c8e6 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -423,7 +423,7 @@ static int get_common_prefix_len(const char *common_prefix)\n \n static int read_one_entry_opt(struct index_state *istate,\n \t\t\t      const struct object_id *oid,\n-\t\t\t      const char *base, int baselen,\n+\t\t\t      struct strbuf *base,\n \t\t\t      const char *pathname,\n \t\t\t      unsigned mode, int stage, int opt)\n {\n@@ -434,13 +434,13 @@ static int read_one_entry_opt(struct index_state *istate,\n \t\treturn READ_TREE_RECURSIVE;\n \n \tlen = strlen(pathname);\n-\tce = make_empty_cache_entry(istate, baselen + len);\n+\tce = make_empty_cache_entry(istate, base->len + len);\n \n \tce->ce_mode = create_ce_mode(mode);\n \tce->ce_flags = create_ce_flags(stage);\n-\tce->ce_namelen = baselen + len;\n-\tmemcpy(ce->name, base, baselen);\n-\tmemcpy(ce->name + baselen, pathname, len+1);\n+\tce->ce_namelen = base->len + len;\n+\tmemcpy(ce->name, base->buf, base->len);\n+\tmemcpy(ce->name + base->len, pathname, len+1);\n \toidcpy(&ce->oid, oid);\n \treturn add_index_entry(istate, ce, opt);\n }\n@@ -450,7 +450,7 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n \t\t\t  void *context)\n {\n \tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\treturn read_one_entry_opt(istate, oid, base, pathname,\n \t\t\t\t  mode, stage,\n \t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n }\n@@ -464,44 +464,11 @@ static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base\n \t\t\t\tvoid *context)\n {\n \tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\treturn read_one_entry_opt(istate, oid, base, pathname,\n \t\t\t\t  mode, stage,\n \t\t\t\t  ADD_CACHE_JUST_APPEND);\n }\n \n-\n-static int read_tree(struct repository *r, struct tree *tree,\n-\t\t     struct pathspec *match, struct index_state *istate)\n-{\n-\tread_tree_fn_t fn = NULL;\n-\tint i, err;\n-\n-\n-\t/*\n-\t * See if we have cache entry at the stage.  If so,\n-\t * do it the original slow way, otherwise, append and then\n-\t * sort at the end.\n-\t */\n-\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n-\t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == 1)\n-\t\t\tfn = read_one_entry;\n-\t}\n-\n-\tif (!fn)\n-\t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, 1, match, fn, istate);\n-\tif (fn == read_one_entry || err)\n-\t\treturn err;\n-\n-\t/*\n-\t * Sort the cache entry -- we need to nuke the cache tree, though.\n-\t */\n-\tcache_tree_free(&istate->cache_tree);\n-\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n-\treturn 0;\n-}\n-\n /*\n  * Read the tree specified with --with-tree option\n  * (typically, HEAD) into stage #1 and then\n@@ -518,6 +485,8 @@ void overlay_tree_on_index(struct index_state *istate,\n \tstruct pathspec pathspec;\n \tstruct cache_entry *last_stage0 = NULL;\n \tint i;\n+\tread_tree_fn_t fn = NULL;\n+\tint err;\n \n \tif (get_oid(tree_name, &oid))\n \t\tdie(\"tree-ish %s not found.\", tree_name);\n@@ -540,9 +509,32 @@ void overlay_tree_on_index(struct index_state *istate,\n \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n \t} else\n \t\tmemset(&pathspec, 0, sizeof(pathspec));\n-\tif (read_tree(the_repository, tree, &pathspec, istate))\n+\n+\t/*\n+\t * See if we have cache entry at the stage.  If so,\n+\t * do it the original slow way, otherwise, append and then\n+\t * sort at the end.\n+\t */\n+\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n+\t\tconst struct cache_entry *ce = istate->cache[i];\n+\t\tif (ce_stage(ce) == 1)\n+\t\t\tfn = read_one_entry;\n+\t}\n+\n+\tif (!fn)\n+\t\tfn = read_one_entry_quick;\n+\terr = read_tree_recursive(the_repository, tree, \"\", 0, 1, &pathspec, fn, istate);\n+\tif (err)\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \n+\t/*\n+\t * Sort the cache entry -- we need to nuke the cache tree, though.\n+\t */\n+\tif (fn == read_one_entry_quick) {\n+\t\tcache_tree_free(&istate->cache_tree);\n+\t\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n+\t}\n+\n \tfor (i = 0; i < istate->cache_nr; i++) {\n \t\tstruct cache_entry *ce = istate->cache[i];\n \t\tswitch (ce_stage(ce)) {\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419312","messageId":"20210316155244.28328-3-avarab@gmail.com","threadId":"18927","inReplyTo":"20210315234344.28427-1-avarab@gmail.com","subject":"[PATCH v4 2/9] tree.c API: move read_tree() into builtin/ls-files.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:52:37Z","receivedAt":"2021-03-16T15:53:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since the read_tree() API was added around the same time as\nread_tree_recursive() in 94537c78a82 (Move \"read_tree()\" to\n\"tree.c\"[...], 2005-04-22) and b12ec373b8e ([PATCH] Teach read-tree\nabout commit objects, 2005-04-20) things have gradually migrated over\nto the read_tree_recursive() version.\n\nNow builtin/ls-files.c is the last user of this code, let's move all\nthe relevant code there. This allows for subsequent simplification of\nit, and an eventual move to read_tree_recursive().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++\n cache.h            |  2 +-\n tree.c             | 89 ---------------------------------------------\n tree.h             |  5 ---\n 4 files changed, 92 insertions(+), 95 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex f6f9e483b2..a445862281 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -12,6 +12,7 @@\n #include \"dir.h\"\n #include \"builtin.h\"\n #include \"tree.h\"\n+#include \"cache-tree.h\"\n #include \"parse-options.h\"\n #include \"resolve-undo.h\"\n #include \"string-list.h\"\n@@ -420,6 +421,96 @@ static int get_common_prefix_len(const char *common_prefix)\n \treturn common_prefix_len;\n }\n \n+static int read_one_entry_opt(struct index_state *istate,\n+\t\t\t      const struct object_id *oid,\n+\t\t\t      const char *base, int baselen,\n+\t\t\t      const char *pathname,\n+\t\t\t      unsigned mode, int stage, int opt)\n+{\n+\tint len;\n+\tstruct cache_entry *ce;\n+\n+\tif (S_ISDIR(mode))\n+\t\treturn READ_TREE_RECURSIVE;\n+\n+\tlen = strlen(pathname);\n+\tce = make_empty_cache_entry(istate, baselen + len);\n+\n+\tce->ce_mode = create_ce_mode(mode);\n+\tce->ce_flags = create_ce_flags(stage);\n+\tce->ce_namelen = baselen + len;\n+\tmemcpy(ce->name, base, baselen);\n+\tmemcpy(ce->name + baselen, pathname, len+1);\n+\toidcpy(&ce->oid, oid);\n+\treturn add_index_entry(istate, ce, opt);\n+}\n+\n+static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n+\t\t\t  const char *pathname, unsigned mode, int stage,\n+\t\t\t  void *context)\n+{\n+\tstruct index_state *istate = context;\n+\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\t\t\t\t  mode, stage,\n+\t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n+}\n+\n+/*\n+ * This is used when the caller knows there is no existing entries at\n+ * the stage that will conflict with the entry being added.\n+ */\n+static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n+\t\t\t\tconst char *pathname, unsigned mode, int stage,\n+\t\t\t\tvoid *context)\n+{\n+\tstruct index_state *istate = context;\n+\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\t\t\t\t  mode, stage,\n+\t\t\t\t  ADD_CACHE_JUST_APPEND);\n+}\n+\n+\n+static int read_tree(struct repository *r, struct tree *tree, int stage,\n+\t\t     struct pathspec *match, struct index_state *istate)\n+{\n+\tread_tree_fn_t fn = NULL;\n+\tint i, err;\n+\n+\t/*\n+\t * Currently the only existing callers of this function all\n+\t * call it with stage=1 and after making sure there is nothing\n+\t * at that stage; we could always use read_one_entry_quick().\n+\t *\n+\t * But when we decide to straighten out git-read-tree not to\n+\t * use unpack_trees() in some cases, this will probably start\n+\t * to matter.\n+\t */\n+\n+\t/*\n+\t * See if we have cache entry at the stage.  If so,\n+\t * do it the original slow way, otherwise, append and then\n+\t * sort at the end.\n+\t */\n+\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n+\t\tconst struct cache_entry *ce = istate->cache[i];\n+\t\tif (ce_stage(ce) == stage)\n+\t\t\tfn = read_one_entry;\n+\t}\n+\n+\tif (!fn)\n+\t\tfn = read_one_entry_quick;\n+\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n+\tif (fn == read_one_entry || err)\n+\t\treturn err;\n+\n+\t/*\n+\t * Sort the cache entry -- we need to nuke the cache tree, though.\n+\t */\n+\tcache_tree_free(&istate->cache_tree);\n+\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n+\treturn 0;\n+}\n+\n /*\n  * Read the tree specified with --with-tree option\n  * (typically, HEAD) into stage #1 and then\ndiff --git a/cache.h b/cache.h\nindex 6fda8091f1..c2f8a8eadf 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -803,7 +803,7 @@ static inline int index_pos_to_insert_pos(uintmax_t pos)\n #define ADD_CACHE_OK_TO_ADD 1\t\t/* Ok to add */\n #define ADD_CACHE_OK_TO_REPLACE 2\t/* Ok to replace file/directory */\n #define ADD_CACHE_SKIP_DFCHECK 4\t/* Ok to skip DF conflict checks */\n-#define ADD_CACHE_JUST_APPEND 8\t\t/* Append only; tree.c::read_tree() */\n+#define ADD_CACHE_JUST_APPEND 8\t\t/* Append only */\n #define ADD_CACHE_NEW_ONLY 16\t\t/* Do not replace existing ones */\n #define ADD_CACHE_KEEP_CACHE_TREE 32\t/* Do not invalidate cache-tree */\n #define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */\ndiff --git a/tree.c b/tree.c\nindex a52479812c..a6c12f2745 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -11,54 +11,6 @@\n \n const char *tree_type = \"tree\";\n \n-static int read_one_entry_opt(struct index_state *istate,\n-\t\t\t      const struct object_id *oid,\n-\t\t\t      const char *base, int baselen,\n-\t\t\t      const char *pathname,\n-\t\t\t      unsigned mode, int stage, int opt)\n-{\n-\tint len;\n-\tstruct cache_entry *ce;\n-\n-\tif (S_ISDIR(mode))\n-\t\treturn READ_TREE_RECURSIVE;\n-\n-\tlen = strlen(pathname);\n-\tce = make_empty_cache_entry(istate, baselen + len);\n-\n-\tce->ce_mode = create_ce_mode(mode);\n-\tce->ce_flags = create_ce_flags(stage);\n-\tce->ce_namelen = baselen + len;\n-\tmemcpy(ce->name, base, baselen);\n-\tmemcpy(ce->name + baselen, pathname, len+1);\n-\toidcpy(&ce->oid, oid);\n-\treturn add_index_entry(istate, ce, opt);\n-}\n-\n-static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\t  const char *pathname, unsigned mode, int stage,\n-\t\t\t  void *context)\n-{\n-\tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode, stage,\n-\t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n-}\n-\n-/*\n- * This is used when the caller knows there is no existing entries at\n- * the stage that will conflict with the entry being added.\n- */\n-static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n-\t\t\t\tconst char *pathname, unsigned mode, int stage,\n-\t\t\t\tvoid *context)\n-{\n-\tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode, stage,\n-\t\t\t\t  ADD_CACHE_JUST_APPEND);\n-}\n-\n static int read_tree_1(struct repository *r,\n \t\t       struct tree *tree, struct strbuf *base,\n \t\t       int stage, const struct pathspec *pathspec,\n@@ -154,47 +106,6 @@ int cmp_cache_name_compare(const void *a_, const void *b_)\n \t\t\t\t  ce2->name, ce2->ce_namelen, ce_stage(ce2));\n }\n \n-int read_tree(struct repository *r, struct tree *tree, int stage,\n-\t      struct pathspec *match, struct index_state *istate)\n-{\n-\tread_tree_fn_t fn = NULL;\n-\tint i, err;\n-\n-\t/*\n-\t * Currently the only existing callers of this function all\n-\t * call it with stage=1 and after making sure there is nothing\n-\t * at that stage; we could always use read_one_entry_quick().\n-\t *\n-\t * But when we decide to straighten out git-read-tree not to\n-\t * use unpack_trees() in some cases, this will probably start\n-\t * to matter.\n-\t */\n-\n-\t/*\n-\t * See if we have cache entry at the stage.  If so,\n-\t * do it the original slow way, otherwise, append and then\n-\t * sort at the end.\n-\t */\n-\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n-\t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == stage)\n-\t\t\tfn = read_one_entry;\n-\t}\n-\n-\tif (!fn)\n-\t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n-\tif (fn == read_one_entry || err)\n-\t\treturn err;\n-\n-\t/*\n-\t * Sort the cache entry -- we need to nuke the cache tree, though.\n-\t */\n-\tcache_tree_free(&istate->cache_tree);\n-\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n-\treturn 0;\n-}\n-\n struct tree *lookup_tree(struct repository *r, const struct object_id *oid)\n {\n \tstruct object *obj = lookup_object(r, oid);\ndiff --git a/tree.h b/tree.h\nindex 3eb0484cbf..6b0b1dc211 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -38,9 +38,4 @@ int read_tree_recursive(struct repository *r,\n \t\t\tconst char *base, int baselen,\n \t\t\tint stage, const struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context);\n-\n-int read_tree(struct repository *r, struct tree *tree,\n-\t      int stage, struct pathspec *pathspec,\n-\t      struct index_state *istate);\n-\n #endif /* TREE_H */\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419314","messageId":"20210316155244.28328-6-avarab@gmail.com","threadId":"18927","inReplyTo":"20210315234344.28427-1-avarab@gmail.com","subject":"[PATCH v4 5/9] tree.h API: remove support for starting at prefix != \"\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:52:40Z","receivedAt":"2021-03-16T15:53:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Every caller of the read_tree_recursive() function hardcoded a\nstarting point of \"\" in the tree. So let's simply remove that\nparameter.\n\nThe last function to call read_tree_recursive() with a non-\"\" path was\nread_tree_recursive() itself, but that was changed in\nffd31f661d5 (Reimplement read_tree_recursive() using\ntree_entry_interesting(), 2011-03-25).\n\nAs it turns out (Murphy's law and all) we're just about to gain a new\nin-tree user that would need this parameter[1]. Let's remove it anyway\nas the common case is going to be to not supply it, A later commit\nwill bring back this functionality in different form.\n\n1. https://lore.kernel.org/git/xmqqft106sok.fsf@gitster.g/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          | 8 ++++----\n builtin/checkout.c | 2 +-\n builtin/log.c      | 4 ++--\n builtin/ls-files.c | 2 +-\n builtin/ls-tree.c  | 2 +-\n merge-recursive.c  | 2 +-\n tree.c             | 2 --\n tree.h             | 1 -\n 8 files changed, 10 insertions(+), 13 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 5919d9e505..9394f170f7 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -316,8 +316,8 @@ int write_archive_entries(struct archiver_args *args,\n \t\tgit_attr_set_direction(GIT_ATTR_INDEX);\n \t}\n \n-\terr = read_tree_recursive(args->repo, args->tree, \"\",\n-\t\t\t\t  0, 0, &args->pathspec,\n+\terr = read_tree_recursive(args->repo, args->tree,\n+\t\t\t\t  0, &args->pathspec,\n \t\t\t\t  queue_or_write_archive_entry,\n \t\t\t\t  &context);\n \tif (err == READ_TREE_RECURSIVE)\n@@ -405,8 +405,8 @@ static int path_exists(struct archiver_args *args, const char *path)\n \tctx.args = args;\n \tparse_pathspec(&ctx.pathspec, 0, 0, \"\", paths);\n \tctx.pathspec.recursive = 1;\n-\tret = read_tree_recursive(args->repo, args->tree, \"\",\n-\t\t\t\t  0, 0, &ctx.pathspec,\n+\tret = read_tree_recursive(args->repo, args->tree,\n+\t\t\t\t  0, &ctx.pathspec,\n \t\t\t\t  reject_entry, &ctx);\n \tclear_pathspec(&ctx.pathspec);\n \treturn ret != 0;\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2d6550bc3c..21b742c0f0 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -155,7 +155,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,\n \n static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)\n {\n-\tread_tree_recursive(the_repository, tree, \"\", 0, 0,\n+\tread_tree_recursive(the_repository, tree, 0,\n \t\t\t    pathspec, update_some, NULL);\n \n \t/* update the index with the given tree's info\ndiff --git a/builtin/log.c b/builtin/log.c\nindex f67b67d80e..ffa3fb8c28 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -681,8 +681,8 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n-\t\t\tread_tree_recursive(the_repository, (struct tree *)o, \"\",\n-\t\t\t\t\t    0, 0, &match_all, show_tree_object,\n+\t\t\tread_tree_recursive(the_repository, (struct tree *)o,\n+\t\t\t\t\t    0, &match_all, show_tree_object,\n \t\t\t\t\t    rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex db53e2c8e6..cd432ac03c 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -523,7 +523,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(the_repository, tree, \"\", 0, 1, &pathspec, fn, istate);\n+\terr = read_tree_recursive(the_repository, tree, 1, &pathspec, fn, istate);\n \tif (err)\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 7cad3f24eb..7d3fb2e6d0 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -185,6 +185,6 @@ 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_recursive(the_repository, tree, \"\", 0, 0,\n+\treturn !!read_tree_recursive(the_repository, tree, 0,\n \t\t\t\t     &pathspec, show_tree, NULL);\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex b052974f19..fa7602ff0f 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -473,7 +473,7 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n {\n \tstruct pathspec match_all;\n \tmemset(&match_all, 0, sizeof(match_all));\n-\tread_tree_recursive(opt->repo, tree, \"\", 0, 0,\n+\tread_tree_recursive(opt->repo, tree, 0,\n \t\t\t    &match_all, save_files_dirs, opt);\n }\n \ndiff --git a/tree.c b/tree.c\nindex a6c12f2745..04eb11aed3 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -83,14 +83,12 @@ static int read_tree_1(struct repository *r,\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tconst char *base, int baselen,\n \t\t\tint stage, const struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\tstrbuf_add(&sb, base, baselen);\n \tret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context);\n \tstrbuf_release(&sb);\n \treturn ret;\ndiff --git a/tree.h b/tree.h\nindex 6b0b1dc211..5252b5139d 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -35,7 +35,6 @@ typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const c\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tconst char *base, int baselen,\n \t\t\tint stage, const struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context);\n #endif /* TREE_H */\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419317","messageId":"20210316155244.28328-7-avarab@gmail.com","threadId":"18927","inReplyTo":"20210315234344.28427-1-avarab@gmail.com","subject":"[PATCH v4 6/9] tree.h API: remove \"stage\" parameter from read_tree_recursive()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:52:41Z","receivedAt":"2021-03-16T15:53:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The read_tree_recursive() function took a \"stage\" parameter that is\npassed through as-is. As it turns out nothing used this parameter in a\nway that they couldn't just move to the callback function they\ndefined, so let's get rid of it.\n\nIf anyone needs to pass such information in the future they can use\nthe \"void *context\" parameter.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          |  9 +++++----\n builtin/checkout.c |  4 ++--\n builtin/log.c      |  4 ++--\n builtin/ls-files.c | 14 +++++++-------\n builtin/ls-tree.c  |  4 ++--\n merge-recursive.c  |  4 ++--\n tree.c             | 10 +++++-----\n tree.h             |  4 ++--\n 8 files changed, 27 insertions(+), 26 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 9394f170f7..6669a4bd14 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -231,9 +231,10 @@ static int write_directory(struct archiver_context *c)\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\tstruct strbuf *base, const char *filename,\n-\t\tunsigned mode, int stage, void *context)\n+\t\tunsigned mode, void *context)\n {\n \tstruct archiver_context *c = context;\n+\tint stage = 0;\n \n \twhile (c->bottom &&\n \t       !(base->len >= c->bottom->len &&\n@@ -317,7 +318,7 @@ int write_archive_entries(struct archiver_args *args,\n \t}\n \n \terr = read_tree_recursive(args->repo, args->tree,\n-\t\t\t\t  0, &args->pathspec,\n+\t\t\t\t  &args->pathspec,\n \t\t\t\t  queue_or_write_archive_entry,\n \t\t\t\t  &context);\n \tif (err == READ_TREE_RECURSIVE)\n@@ -378,7 +379,7 @@ struct path_exists_context {\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n \t\t\tconst char *filename, unsigned mode,\n-\t\t\tint stage, void *context)\n+\t\t\tvoid *context)\n {\n \tint ret = -1;\n \tstruct path_exists_context *ctx = context;\n@@ -406,7 +407,7 @@ static int path_exists(struct archiver_args *args, const char *path)\n \tparse_pathspec(&ctx.pathspec, 0, 0, \"\", paths);\n \tctx.pathspec.recursive = 1;\n \tret = read_tree_recursive(args->repo, args->tree,\n-\t\t\t\t  0, &ctx.pathspec,\n+\t\t\t\t  &ctx.pathspec,\n \t\t\t\t  reject_entry, &ctx);\n \tclear_pathspec(&ctx.pathspec);\n \treturn ret != 0;\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 21b742c0f0..2c2d58a230 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -114,7 +114,7 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n }\n \n static int update_some(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tint len;\n \tstruct cache_entry *ce;\n@@ -155,7 +155,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,\n \n static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)\n {\n-\tread_tree_recursive(the_repository, tree, 0,\n+\tread_tree_recursive(the_repository, tree,\n \t\t\t    pathspec, update_some, NULL);\n \n \t/* update the index with the given tree's info\ndiff --git a/builtin/log.c b/builtin/log.c\nindex ffa3fb8c28..58acb2b76a 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -599,7 +599,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n \n static int show_tree_object(const struct object_id *oid,\n \t\tstruct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tFILE *file = context;\n \tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n@@ -682,7 +682,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n \t\t\tread_tree_recursive(the_repository, (struct tree *)o,\n-\t\t\t\t\t    0, &match_all, show_tree_object,\n+\t\t\t\t\t    &match_all, show_tree_object,\n \t\t\t\t\t    rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex cd432ac03c..fa9b01b6cc 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -425,7 +425,7 @@ static int read_one_entry_opt(struct index_state *istate,\n \t\t\t      const struct object_id *oid,\n \t\t\t      struct strbuf *base,\n \t\t\t      const char *pathname,\n-\t\t\t      unsigned mode, int stage, int opt)\n+\t\t\t      unsigned mode, int opt)\n {\n \tint len;\n \tstruct cache_entry *ce;\n@@ -437,7 +437,7 @@ static int read_one_entry_opt(struct index_state *istate,\n \tce = make_empty_cache_entry(istate, base->len + len);\n \n \tce->ce_mode = create_ce_mode(mode);\n-\tce->ce_flags = create_ce_flags(stage);\n+\tce->ce_flags = create_ce_flags(1);\n \tce->ce_namelen = base->len + len;\n \tmemcpy(ce->name, base->buf, base->len);\n \tmemcpy(ce->name + base->len, pathname, len+1);\n@@ -446,12 +446,12 @@ static int read_one_entry_opt(struct index_state *istate,\n }\n \n static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\t  const char *pathname, unsigned mode, int stage,\n+\t\t\t  const char *pathname, unsigned mode,\n \t\t\t  void *context)\n {\n \tstruct index_state *istate = context;\n \treturn read_one_entry_opt(istate, oid, base, pathname,\n-\t\t\t\t  mode, stage,\n+\t\t\t\t  mode,\n \t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n }\n \n@@ -460,12 +460,12 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n  * the stage that will conflict with the entry being added.\n  */\n static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n-\t\t\t\tconst char *pathname, unsigned mode, int stage,\n+\t\t\t\tconst char *pathname, unsigned mode,\n \t\t\t\tvoid *context)\n {\n \tstruct index_state *istate = context;\n \treturn read_one_entry_opt(istate, oid, base, pathname,\n-\t\t\t\t  mode, stage,\n+\t\t\t\t  mode,\n \t\t\t\t  ADD_CACHE_JUST_APPEND);\n }\n \n@@ -523,7 +523,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(the_repository, tree, 1, &pathspec, fn, istate);\n+\terr = read_tree_recursive(the_repository, tree, &pathspec, fn, istate);\n \tif (err)\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 7d3fb2e6d0..dbb31217be 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -62,7 +62,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n }\n \n static int show_tree(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tint retval = 0;\n \tint baselen;\n@@ -185,6 +185,6 @@ 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_recursive(the_repository, tree, 0,\n+\treturn !!read_tree_recursive(the_repository, tree,\n \t\t\t\t     &pathspec, show_tree, NULL);\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex fa7602ff0f..1593f37449 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,7 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   unsigned int mode, int stage, void *context)\n+\t\t\t   unsigned int mode, void *context)\n {\n \tstruct path_hashmap_entry *entry;\n \tint baselen = base->len;\n@@ -473,7 +473,7 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n {\n \tstruct pathspec match_all;\n \tmemset(&match_all, 0, sizeof(match_all));\n-\tread_tree_recursive(opt->repo, tree, 0,\n+\tread_tree_recursive(opt->repo, tree,\n \t\t\t    &match_all, save_files_dirs, opt);\n }\n \ndiff --git a/tree.c b/tree.c\nindex 04eb11aed3..fb4985f22c 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -13,7 +13,7 @@ const char *tree_type = \"tree\";\n \n static int read_tree_1(struct repository *r,\n \t\t       struct tree *tree, struct strbuf *base,\n-\t\t       int stage, const struct pathspec *pathspec,\n+\t\t       const struct pathspec *pathspec,\n \t\t       read_tree_fn_t fn, void *context)\n {\n \tstruct tree_desc desc;\n@@ -38,7 +38,7 @@ static int read_tree_1(struct repository *r,\n \t\t}\n \n \t\tswitch (fn(&entry.oid, base,\n-\t\t\t   entry.path, entry.mode, stage, context)) {\n+\t\t\t   entry.path, entry.mode, context)) {\n \t\tcase 0:\n \t\t\tcontinue;\n \t\tcase READ_TREE_RECURSIVE:\n@@ -72,7 +72,7 @@ static int read_tree_1(struct repository *r,\n \t\tstrbuf_add(base, entry.path, len);\n \t\tstrbuf_addch(base, '/');\n \t\tretval = read_tree_1(r, lookup_tree(r, &oid),\n-\t\t\t\t     base, stage, pathspec,\n+\t\t\t\t     base, pathspec,\n \t\t\t\t     fn, context);\n \t\tstrbuf_setlen(base, oldlen);\n \t\tif (retval)\n@@ -83,13 +83,13 @@ static int read_tree_1(struct repository *r,\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tint stage, const struct pathspec *pathspec,\n+\t\t\tconst struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\tret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context);\n+\tret = read_tree_1(r, tree, &sb, pathspec, fn, context);\n \tstrbuf_release(&sb);\n \treturn ret;\n }\ndiff --git a/tree.h b/tree.h\nindex 5252b5139d..1309ab997e 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -31,10 +31,10 @@ struct tree *parse_tree_indirect(const struct object_id *oid);\n int cmp_cache_name_compare(const void *a_, const void *b_);\n \n #define READ_TREE_RECURSIVE 1\n-typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, int, void *);\n+typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n \n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n-\t\t\tint stage, const struct pathspec *pathspec,\n+\t\t\tconst struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context);\n #endif /* TREE_H */\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419313","messageId":"20210316155244.28328-9-avarab@gmail.com","threadId":"18927","inReplyTo":"20210315234344.28427-1-avarab@gmail.com","subject":"[PATCH v4 8/9] show tests: add test for \"git show <tree>\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:52:43Z","receivedAt":"2021-03-16T15:53:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add missing tests for showing a tree with \"git show\". Let's test for\nshowing a tree, two trees, and that doing so doesn't recurse.\n\nThe only tests for this code added in 5d7eeee2ac6 (git-show: grok\nblobs, trees and tags, too, 2006-12-14) were the tests in\nt7701-repack-unpack-unreachable.sh added in ccc1297226b (repack:\nmodify behavior of -A option to leave unreferenced objects unpacked,\n2008-05-09).\n\nLet's add this common mode of operation to the \"show\" tests\nthemselves. It's more obvious, and the tests in\nt7701-repack-unpack-unreachable.sh happily parse if we start buggily\nemitting trees recursively.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t7007-show.sh | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/t/t7007-show.sh b/t/t7007-show.sh\nindex 42d3db6246..d6cc69e0f2 100755\n--- a/t/t7007-show.sh\n+++ b/t/t7007-show.sh\n@@ -38,6 +38,45 @@ test_expect_success 'showing two commits' '\n \ttest_cmp expect actual.filtered\n '\n \n+test_expect_success 'showing a tree' '\n+\tcat >expected <<-EOF &&\n+\ttree main1:\n+\n+\tmain1.t\n+\tEOF\n+\tgit show main1: >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'showing two trees' '\n+\tcat >expected <<-EOF &&\n+\ttree main1^{tree}\n+\n+\tmain1.t\n+\n+\ttree main2^{tree}\n+\n+\tmain1.t\n+\tmain2.t\n+\tEOF\n+\tgit show main1^{tree} main2^{tree} >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'showing a trees is not recursive' '\n+\tgit worktree add not-recursive main1 &&\n+\tmkdir not-recursive/a &&\n+\ttest_commit -C not-recursive a/file &&\n+\tcat >expected <<-EOF &&\n+\ttree HEAD^{tree}\n+\n+\ta/\n+\tmain1.t\n+\tEOF\n+\tgit -C not-recursive show HEAD^{tree} >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'showing a range walks (linear)' '\n \tcat >expect <<-EOF &&\n \tcommit $(git rev-parse main3)\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419315","messageId":"20210316155244.28328-10-avarab@gmail.com","threadId":"18927","inReplyTo":"20210315234344.28427-1-avarab@gmail.com","subject":"[PATCH v4 9/9] tree.h API: expose read_tree_1() as read_tree_at()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:52:44Z","receivedAt":"2021-03-16T15:53:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the static read_tree_1() function to read_tree_at(). This will\nallow for the old read_tree_recursive() mode of operation where we\nstart at a given path instead of \"\".\n\nSee [1] for the discussion of one such future in-tree user, unlike the\nold read_tree_recursive() this function takes a strbuf. Since that's\nwhat read_tree_1() used internally this should allow us to avoid\ncasting and/or reallocations in the future.\n\n1. https://lore.kernel.org/git/xmqqft106sok.fsf@gitster.g/#t\n---\n tree.c | 16 ++++++++--------\n tree.h |  6 ++++++\n 2 files changed, 14 insertions(+), 8 deletions(-)\n\ndiff --git a/tree.c b/tree.c\nindex f6de250d7f..6a2a52967e 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -11,10 +11,10 @@\n \n const char *tree_type = \"tree\";\n \n-static int read_tree_1(struct repository *r,\n-\t\t       struct tree *tree, struct strbuf *base,\n-\t\t       const struct pathspec *pathspec,\n-\t\t       read_tree_fn_t fn, void *context)\n+int read_tree_at(struct repository *r,\n+\t\t struct tree *tree, struct strbuf *base,\n+\t\t const struct pathspec *pathspec,\n+\t\t read_tree_fn_t fn, void *context)\n {\n \tstruct tree_desc desc;\n \tstruct name_entry entry;\n@@ -71,9 +71,9 @@ static int read_tree_1(struct repository *r,\n \t\tlen = tree_entry_len(&entry);\n \t\tstrbuf_add(base, entry.path, len);\n \t\tstrbuf_addch(base, '/');\n-\t\tretval = read_tree_1(r, lookup_tree(r, &oid),\n-\t\t\t\t     base, pathspec,\n-\t\t\t\t     fn, context);\n+\t\tretval = read_tree_at(r, lookup_tree(r, &oid),\n+\t\t\t\t      base, pathspec,\n+\t\t\t\t      fn, context);\n \t\tstrbuf_setlen(base, oldlen);\n \t\tif (retval)\n \t\t\treturn -1;\n@@ -89,7 +89,7 @@ int read_tree(struct repository *r,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\n \n-\tret = read_tree_1(r, tree, &sb, pathspec, fn, context);\n+\tret = read_tree_at(r, tree, &sb, pathspec, fn, context);\n \tstrbuf_release(&sb);\n \treturn ret;\n }\ndiff --git a/tree.h b/tree.h\nindex 4fb713774a..f0b079d2e9 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -33,6 +33,12 @@ int cmp_cache_name_compare(const void *a_, const void *b_);\n #define READ_TREE_RECURSIVE 1\n typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n \n+int read_tree_at(struct repository *r,\n+\t\t struct tree *tree,\n+\t\t struct strbuf *at,\n+\t\t const struct pathspec *pathspec,\n+\t\t read_tree_fn_t fn, void *context);\n+\n int read_tree(struct repository *r,\n \t      struct tree *tree,\n \t      const struct pathspec *pathspec,\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419316","messageId":"20210316155244.28328-8-avarab@gmail.com","threadId":"18927","inReplyTo":"20210315234344.28427-1-avarab@gmail.com","subject":"[PATCH v4 7/9] tree.h API: rename read_tree_recursive() to read_tree()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:52:42Z","receivedAt":"2021-03-16T15:53:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the read_tree_recursive() function to just read_tree(). We had\nanother read_tree() function that I've refactored away in preceding\nsteps, since all in-tree users read trees recursively with a callback\nwe can change the name to signify that this is the norm.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          | 14 +++++++-------\n builtin/checkout.c |  6 +++---\n builtin/log.c      |  6 +++---\n builtin/ls-files.c |  2 +-\n builtin/ls-tree.c  |  4 ++--\n merge-recursive.c  |  4 ++--\n tree.c             |  8 ++++----\n tree.h             |  8 ++++----\n 8 files changed, 26 insertions(+), 26 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 6669a4bd14..c19300ecb9 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -317,10 +317,10 @@ int write_archive_entries(struct archiver_args *args,\n \t\tgit_attr_set_direction(GIT_ATTR_INDEX);\n \t}\n \n-\terr = read_tree_recursive(args->repo, args->tree,\n-\t\t\t\t  &args->pathspec,\n-\t\t\t\t  queue_or_write_archive_entry,\n-\t\t\t\t  &context);\n+\terr = read_tree(args->repo, args->tree,\n+\t\t\t&args->pathspec,\n+\t\t\tqueue_or_write_archive_entry,\n+\t\t\t&context);\n \tif (err == READ_TREE_RECURSIVE)\n \t\terr = 0;\n \twhile (context.bottom) {\n@@ -406,9 +406,9 @@ static int path_exists(struct archiver_args *args, const char *path)\n \tctx.args = args;\n \tparse_pathspec(&ctx.pathspec, 0, 0, \"\", paths);\n \tctx.pathspec.recursive = 1;\n-\tret = read_tree_recursive(args->repo, args->tree,\n-\t\t\t\t  &ctx.pathspec,\n-\t\t\t\t  reject_entry, &ctx);\n+\tret = read_tree(args->repo, args->tree,\n+\t\t\t&ctx.pathspec,\n+\t\t\treject_entry, &ctx);\n \tclear_pathspec(&ctx.pathspec);\n \treturn ret != 0;\n }\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2c2d58a230..0e66390520 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -155,8 +155,8 @@ static int update_some(const struct object_id *oid, struct strbuf *base,\n \n static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)\n {\n-\tread_tree_recursive(the_repository, tree,\n-\t\t\t    pathspec, update_some, NULL);\n+\tread_tree(the_repository, tree,\n+\t\t  pathspec, update_some, NULL);\n \n \t/* update the index with the given tree's info\n \t * for all args, expanding wildcards, and exit\n@@ -322,7 +322,7 @@ static void mark_ce_for_checkout_overlay(struct cache_entry *ce,\n \t * If it comes from the tree-ish, we already know it\n \t * matches the pathspec and could just stamp\n \t * CE_MATCHED to it from update_some(). But we still\n-\t * need ps_matched and read_tree_recursive (and\n+\t * need ps_matched and read_tree (and\n \t * eventually tree_entry_interesting) cannot fill\n \t * ps_matched yet. Once it can, we can avoid calling\n \t * match_pathspec() for _all_ entries when\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 58acb2b76a..980de59063 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -681,9 +681,9 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n-\t\t\tread_tree_recursive(the_repository, (struct tree *)o,\n-\t\t\t\t\t    &match_all, show_tree_object,\n-\t\t\t\t\t    rev.diffopt.file);\n+\t\t\tread_tree(the_repository, (struct tree *)o,\n+\t\t\t\t  &match_all, show_tree_object,\n+\t\t\t\t  rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\n \t\tcase OBJ_COMMIT:\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex fa9b01b6cc..13bcc2d847 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -523,7 +523,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(the_repository, tree, &pathspec, fn, istate);\n+\terr = read_tree(the_repository, tree, &pathspec, fn, istate);\n \tif (err)\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex dbb31217be..3a442631c7 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -185,6 +185,6 @@ 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_recursive(the_repository, tree,\n-\t\t\t\t     &pathspec, show_tree, NULL);\n+\treturn !!read_tree(the_repository, tree,\n+\t\t\t   &pathspec, show_tree, NULL);\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1593f37449..3d9207455b 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -473,8 +473,8 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n {\n \tstruct pathspec match_all;\n \tmemset(&match_all, 0, sizeof(match_all));\n-\tread_tree_recursive(opt->repo, tree,\n-\t\t\t    &match_all, save_files_dirs, opt);\n+\tread_tree(opt->repo, tree,\n+\t\t  &match_all, save_files_dirs, opt);\n }\n \n static int get_tree_entry_if_blob(struct repository *r,\ndiff --git a/tree.c b/tree.c\nindex fb4985f22c..f6de250d7f 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -81,10 +81,10 @@ static int read_tree_1(struct repository *r,\n \treturn 0;\n }\n \n-int read_tree_recursive(struct repository *r,\n-\t\t\tstruct tree *tree,\n-\t\t\tconst struct pathspec *pathspec,\n-\t\t\tread_tree_fn_t fn, void *context)\n+int read_tree(struct repository *r,\n+\t      struct tree *tree,\n+\t      const struct pathspec *pathspec,\n+\t      read_tree_fn_t fn, void *context)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret;\ndiff --git a/tree.h b/tree.h\nindex 1309ab997e..4fb713774a 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -33,8 +33,8 @@ int cmp_cache_name_compare(const void *a_, const void *b_);\n #define READ_TREE_RECURSIVE 1\n typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n \n-int read_tree_recursive(struct repository *r,\n-\t\t\tstruct tree *tree,\n-\t\t\tconst struct pathspec *pathspec,\n-\t\t\tread_tree_fn_t fn, void *context);\n+int read_tree(struct repository *r,\n+\t      struct tree *tree,\n+\t      const struct pathspec *pathspec,\n+\t      read_tree_fn_t fn, void *context);\n #endif /* TREE_H */\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419318","messageId":"20210316155829.31242-2-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 01/32] diff.c: remove redundant canon_mode() call","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:57:58Z","receivedAt":"2021-03-16T15:59:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove a call to canon_mode() from fill_filespec(). This has been\nredundant since the tree-walk.c API supplies it pre-canonicalized\nsince 7146e66f086 (tree-walk: finally switch over tree descriptors to\ncontain a pre-parsed entry, 2014-02-06).\n\nThis call to the predecessor of canon_mode() was added back in\n4130b995719 ([PATCH] Diff updates to express type changes,\n2005-05-26).\n\nThis was the only such call in the codebase. The rest are all either\none of these sorts of forms:\n\n    canon_mode(st.st_mode); /* a stat(2) struct */\n    canon_mode(S_IFREG | 0644) /* A compile-time literal */\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n diff.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/diff.c b/diff.c\nindex 6956f5e335..bf46e6a4d8 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -3846,7 +3846,7 @@ void fill_filespec(struct diff_filespec *spec, const struct object_id *oid,\n \t\t   int oid_valid, unsigned short mode)\n {\n \tif (mode) {\n-\t\tspec->mode = canon_mode(mode);\n+\t\tspec->mode = mode;\n \t\toidcpy(&spec->oid, oid);\n \t\tspec->oid_valid = oid_valid;\n \t}\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419319","messageId":"20210316155829.31242-3-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 02/32] notes & match-trees: use name_entry's \"pathlen\" member","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:57:59Z","receivedAt":"2021-03-16T15:59:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change code that was doing a strlen() on the \"path\" from a name_entry\nstruct to instead use the pathlen given to us by decode_tree_entry().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c | 7 +++----\n notes.c       | 4 ++--\n 2 files changed, 5 insertions(+), 6 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex f6c194c1cc..1011357ad0 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -197,9 +197,10 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \twhile (desc.size) {\n \t\tconst char *name;\n \t\tunsigned short mode;\n+\t\tint len = tree_entry_len(&desc.entry);\n \n \t\ttree_entry_extract(&desc, &name, &mode);\n-\t\tif (strlen(name) == toplen &&\n+\t\tif (len == toplen &&\n \t\t    !memcmp(name, prefix, toplen)) {\n \t\t\tif (!S_ISDIR(mode))\n \t\t\t\tdie(\"entry %s in tree %s is not a tree\", name,\n@@ -214,9 +215,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \t\t\t *   - to discard the \"const\"; this is OK because we\n \t\t\t *     know it points into our non-const \"buf\"\n \t\t\t */\n-\t\t\trewrite_here = (unsigned char *)(desc.entry.path +\n-\t\t\t\t\t\t\t strlen(desc.entry.path) +\n-\t\t\t\t\t\t\t 1);\n+\t\t\trewrite_here = (unsigned char *)(name + len + 1);\n \t\t\tbreak;\n \t\t}\n \t\tupdate_tree_entry(&desc);\ndiff --git a/notes.c b/notes.c\nindex d5ac081e76..0a5b4fa1db 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -413,7 +413,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \twhile (tree_entry(&desc, &entry)) {\n \t\tunsigned char type;\n \t\tstruct leaf_node *l;\n-\t\tsize_t path_len = strlen(entry.path);\n+\t\tint path_len = entry.pathlen;\n \n \t\tif (path_len == 2 * (hashsz - prefix_len)) {\n \t\t\t/* This is potentially the remainder of the SHA-1 */\n@@ -483,7 +483,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n \t\t\t\tstrbuf_addch(&non_note_path, '/');\n \t\t\t}\n-\t\t\tstrbuf_addstr(&non_note_path, entry.path);\n+\t\t\tstrbuf_add(&non_note_path, entry.path, path_len);\n \t\t\tadd_non_note(t, strbuf_detach(&non_note_path, NULL),\n \t\t\t\t     entry.mode, entry.oid.hash);\n \t\t}\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419320","messageId":"20210316155829.31242-4-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 03/32] cache.h: add a comment to object_type()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:00Z","receivedAt":"2021-03-16T15:59:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a comment to the object_type() function to explain what it\nreturns, and what the \"mode\" is in the \"else\" case.\n\nThe object_type() function dates back to 4d1012c3709 (Fix rev-list\nwhen showing objects involving submodules, 2007-11-11). It's not\nimmediately obvious to someone looking at its history and how it's\ncome to be used.\n\nDespite what Linus noted in 4d1012c3709 (Fix rev-list when showing\nobjects involving submodules, 2007-11-11) about wanting to move away\nfrom users of object_type() relying on S_ISLNK(mode) being true here\nwe do currently rely on that. If this is changed to a condition to\nonly return OBJ_BLOB on S_ISREG(mode) then t4008, t4023 and t7415 will\nhave failing tests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n cache.h | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/cache.h b/cache.h\nindex c2f8a8eadf..ae0c0bef5c 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -451,11 +451,16 @@ enum object_type {\n \tOBJ_MAX\n };\n \n+/*\n+ * object_type() returns an object of a type that'll appear in a tree,\n+ * so no OBJ_TAG is possible. This is mostly (and dates back to)\n+ * consumers of the tree-walk.h API's \"mode\" field.\n+ */\n static inline enum object_type object_type(unsigned int mode)\n {\n \treturn S_ISDIR(mode) ? OBJ_TREE :\n \t\tS_ISGITLINK(mode) ? OBJ_COMMIT :\n-\t\tOBJ_BLOB;\n+\t\tOBJ_BLOB; /* S_ISREG(mode) || S_ISLNK(mode) */\n }\n \n /* Double-check local_repo_env below if you add to this list. */\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419321","messageId":"20210316155829.31242-1-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 00/32] tree-walk: mostly replace \"mode\" with \"enum object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:57:57Z","receivedAt":"2021-03-16T15:59:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The v2 of this series was broken as noted in\nhttps://lore.kernel.org/git/87blbjfqkh.fsf@evledraar.gmail.com/\n\nHere's a fixed version. This should actually address all of Elijah's\ncomments on v1 + v2.\n\nIt's up to 32 patches because I added some tests for the\nmostly-never-been-tested code being changed in 09/32.\n\nÆvar Arnfjörð Bjarmason (32):\n  diff.c: remove redundant canon_mode() call\n  notes & match-trees: use name_entry's \"pathlen\" member\n  cache.h: add a comment to object_type()\n  tree-walk.h: add object_type member to name_entry\n  tree-walk.c: migrate to using new \"object_type\" field when possible\n  fast-import tests: test for sorting dir/file foo v.s. foo.txt\n  mktree tests: test that \"mode\" is passed when sorting\n  diff tests: test that \"mode\" is passed when sorting\n  cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n  tree-walk.h users: switch object_type(...) to new .object_type\n  tree.h: format argument lists of read_tree_recursive() users\n  tree.h users: format argument lists in archive.c\n  archive: get rid of 'stage' parameter\n  tree.h API: make read_tree_fn_t take an \"enum object_type\"\n  tree-walk.h users: migrate \"p->mode &&\" pattern\n  tree-walk.h users: refactor chained \"mode\" if/else into switch\n  tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n  merge-tree tests: test for the mode comparison in same_entry()\n  merge-ort: correct reference to test in 62fdec17a11\n  fsck.c: switch on \"object_type\" in fsck_walk_tree()\n  tree-walk.h users: use temporary variable(s) for \"mode\"\n  tree-walk.h API: formatting changes for subsequent commit\n  tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n  match-trees: use \"tmp\" for mode in shift_tree_by()\n  tree-walk.h API: add get_tree_entry_type()\n  tree-walk.h API: document and format tree_entry_extract()\n  tree-entry.h API: rename tree_entry_extract() to\n    tree_entry_extract_mode()\n  tree-walk.h API: add a tree_entry_extract_all() function\n  tree-walk.h API: add get_tree_entry_all()\n  tree-walk.h API: add a get_tree_entry_path() function\n  blame: emit a better error on 'git blame directory'\n  tree-walk.h API: add a tree_entry_extract_type() function\n\n archive.c                       | 50 +++++++++---------\n blame.c                         |  9 ++--\n builtin/checkout.c              |  6 ++-\n builtin/fast-import.c           | 12 +++--\n builtin/grep.c                  |  6 +--\n builtin/log.c                   |  7 +--\n builtin/ls-files.c              |  6 ++-\n builtin/ls-tree.c               | 14 +++---\n builtin/merge-tree.c            | 30 +++++++----\n builtin/mktree.c                |  4 +-\n builtin/pack-objects.c          |  6 +--\n builtin/reflog.c                |  3 +-\n builtin/rm.c                    |  2 +-\n builtin/update-index.c          |  6 ++-\n cache-tree.c                    |  2 +-\n cache.h                         | 11 ++--\n combine-diff.c                  |  8 +--\n delta-islands.c                 |  2 +-\n diff.c                          |  2 +-\n fsck.c                          | 23 ++++-----\n http-push.c                     |  6 ++-\n line-log.c                      |  2 +-\n list-objects.c                  | 20 +++++---\n match-trees.c                   | 52 +++++++++----------\n merge-ort.c                     | 13 ++---\n merge-recursive.c               | 33 ++++++------\n notes.c                         | 14 +++---\n object-name.c                   |  7 ++-\n pack-bitmap-write.c             |  8 +--\n read-cache.c                    | 16 +++---\n revision.c                      | 12 +++--\n t/t1450-fsck.sh                 | 66 ++++++++++++++++++++++++\n t/t4300-merge-tree.sh           | 44 ++++++++++++++++\n t/t8004-blame-with-conflicts.sh | 21 ++++++++\n t/t9300-fast-import.sh          | 87 ++++++++++++++++++++++++++++++++\n tree-diff.c                     | 30 +++++++----\n tree-walk.c                     | 89 ++++++++++++++++++++++++---------\n tree-walk.h                     | 63 ++++++++++++++++++++---\n tree.c                          | 19 ++++---\n tree.h                          |  5 +-\n unpack-trees.c                  | 24 +++++----\n walker.c                        | 22 ++++----\n 42 files changed, 618 insertions(+), 244 deletions(-)\n\nRange-diff:\n 1:  f9bbc30f69 =  1:  26bc38fbdd diff.c: remove redundant canon_mode() call\n 2:  187fc2c3e6 =  2:  8502cf8134 notes & match-trees: use name_entry's \"pathlen\" member\n 3:  311637c558 =  3:  68133fa6aa cache.h: add a comment to object_type()\n 4:  fecfe3d462 =  4:  9f714d6c01 tree-walk.h: add object_type member to name_entry\n 5:  db961ab5e8 =  5:  6dbf2b0a6a tree-walk.c: migrate to using new \"object_type\" field when possible\n -:  ---------- >  6:  354a8e9a2a fast-import tests: test for sorting dir/file foo v.s. foo.txt\n -:  ---------- >  7:  e2331df28e mktree tests: test that \"mode\" is passed when sorting\n -:  ---------- >  8:  9e9486c2ea diff tests: test that \"mode\" is passed when sorting\n 6:  df2fc76161 !  9:  be5c713336 cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n    @@ Commit message\n         958ba6c96eb (Introduce \"base_name_compare()\" helper function,\n         2005-05-20).\n     \n    +    None of these comparison functions used to have tests, but with\n    +    preceding commits some of them now do. I thought the remainder was\n    +    trivial enough to review without tests, and didn't want to spend more\n    +    time on them.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/fast-import.c ##\n     @@ builtin/fast-import.c: static int tecmp0 (const void *_a, const void *_b)\n    + {\n      \tstruct tree_entry *a = *((struct tree_entry**)_a);\n      \tstruct tree_entry *b = *((struct tree_entry**)_b);\n    ++\tint istree_a = S_ISDIR(a->versions[0].mode);\n    ++\tint istree_b = S_ISDIR(b->versions[0].mode);\n      \treturn base_name_compare(\n     -\t\ta->name->str_dat, a->name->str_len, a->versions[0].mode,\n     -\t\tb->name->str_dat, b->name->str_len, b->versions[0].mode);\n    -+\t\ta->name->str_dat, a->name->str_len, 1,\n    -+\t\tb->name->str_dat, b->name->str_len, 1);\n    ++\t\ta->name->str_dat, a->name->str_len, istree_a,\n    ++\t\tb->name->str_dat, b->name->str_len, istree_b);\n      }\n      \n      static int tecmp1 (const void *_a, const void *_b)\n    -@@ builtin/fast-import.c: static int tecmp1 (const void *_a, const void *_b)\n    + {\n      \tstruct tree_entry *a = *((struct tree_entry**)_a);\n      \tstruct tree_entry *b = *((struct tree_entry**)_b);\n    ++\tint istree_a = S_ISDIR(a->versions[1].mode);\n    ++\tint istree_b = S_ISDIR(b->versions[1].mode);\n      \treturn base_name_compare(\n     -\t\ta->name->str_dat, a->name->str_len, a->versions[1].mode,\n     -\t\tb->name->str_dat, b->name->str_len, b->versions[1].mode);\n    -+\t\ta->name->str_dat, a->name->str_len, 1,\n    -+\t\tb->name->str_dat, b->name->str_len, 1);\n    ++\t\ta->name->str_dat, a->name->str_len, istree_a,\n    ++\t\tb->name->str_dat, b->name->str_len, istree_b);\n      }\n      \n      static void mktree(struct tree_content *t, int v, struct strbuf *b)\n 7:  49d5da8c08 = 10:  43623edddf tree-walk.h users: switch object_type(...) to new .object_type\n 8:  c9d209d496 = 11:  030898f884 tree.h: format argument lists of read_tree_recursive() users\n 9:  a6d2660fe1 = 12:  0ce197950b tree.h users: format argument lists in archive.c\n10:  15f7f89acc = 13:  051c9f32ac archive: get rid of 'stage' parameter\n11:  7a71404ea3 = 14:  bc73994b4c tree.h API: make read_tree_fn_t take an \"enum object_type\"\n12:  64dc9364ba = 15:  e2b0964228 tree-walk.h users: migrate \"p->mode &&\" pattern\n13:  93ed3edbbd = 16:  29dbc4292e tree-walk.h users: refactor chained \"mode\" if/else into switch\n14:  7aa48aa34c = 17:  ada6d05176 tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n15:  3ae81621dc = 18:  01860daa55 merge-tree tests: test for the mode comparison in same_entry()\n16:  4249ad5c4d = 19:  e4be48fb50 merge-ort: correct reference to test in 62fdec17a11\n17:  e5e17505dd = 20:  189d2550fb fsck.c: switch on \"object_type\" in fsck_walk_tree()\n18:  3f0b884f1f = 21:  3b7b12e6f7 tree-walk.h users: use temporary variable(s) for \"mode\"\n19:  174167613b = 22:  6c3a07a327 tree-walk.h API: formatting changes for subsequent commit\n20:  ec76db613f = 23:  879eb3da2a tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n21:  11e3494172 ! 24:  d2fa360ab9 tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n    +    match-trees: use \"tmp\" for mode in shift_tree_by()\n     \n         Refactor code added in 85e51b783c3 (Make \"subtree\" part more\n         orthogonal to the rest of merge-recursive., 2008-06-30) to make it\n22:  b31c106557 = 25:  cc50cfcf51 tree-walk.h API: add get_tree_entry_type()\n23:  304d5d4d1a = 26:  f642a35482 tree-walk.h API: document and format tree_entry_extract()\n24:  346453df35 = 27:  a0bcb59fa5 tree-entry.h API: rename tree_entry_extract() to tree_entry_extract_mode()\n25:  dd012b661e = 28:  c7d4ba7734 tree-walk.h API: add a tree_entry_extract_all() function\n26:  b6ee8410e3 = 29:  1d5421d67a tree-walk.h API: add get_tree_entry_all()\n27:  5c98afd9e7 ! 30:  a3e1063ac4 tree-walk.h API: add a get_tree_entry_path() function\n    @@ Commit message\n         need neither the mode nor \"enum object_type\" parameters filled for\n         them.\n     \n    -    There's callers here which doesn't need the \"struct object_id\" filled\n    -    either, and provides a throwaway variable for us.\n    +    There are callers here which don't need the \"struct object_id\" filled;\n    +    forcing callers to pass one just requires they create a throwaway\n    +    variable.\n     \n         See the following commits for the introduction of such code that's\n         being modified here:\n     \n    -     - shift_tree(): 68faf68938e (A new merge stragety 'subtree'.,\n    +     - shift_tree(): 68faf68938e (A new merge stragety[sic] 'subtree'.,\n             2007-02-15) for the shift_tree()\n     \n          - tree_has_path(): 96e7ffbdc31 (merge-recursive: check for directory\n28:  3e7e0f7eb8 ! 31:  da3dd6dd53 blame: emit a better error on 'git blame directory'\n    @@ Commit message\n     \n             fatal: no such path 't' in HEAD\n     \n    +    The main point of this test is to assert that we're not doing\n    +    something uniquely bad when in a conflicted merge. See\n    +    cd8ae20195 (git-blame shouldn't crash if run in an unmerged tree,\n    +    2007-10-18) and 9aeaab6811 (blame: allow \"blame file\" in the middle of\n    +    a conflicted merge, 2012-09-11) for the bug the t8004 test was\n    +    originally meant to address.\n    +\n    +    But when extending it let's grep out the specific error message for\n    +    good measure. Having to change it in the future (e.g. as part of my\n    +    parallel series to improve such 'OID does not match type' messages) is\n    +    a small price for ensuring it doesn't regress.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## blame.c ##\n    @@ t/t8004-blame-with-conflicts.sh: test_expect_success 'blame does not crash with\n     +\ttest_commit second &&\n     +\ttest_commit d/file &&\n     +\ttest_must_fail git blame d 2>expected &&\n    ++\tgrep \"unsupported file type d\" expected &&\n     +\n     +\tgit reset --hard second &&\n     +\t>d &&\n29:  ac1ccf1357 = 32:  80e5cb0b30 tree-walk.h API: add a tree_entry_extract_type() function\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419322","messageId":"20210316155829.31242-6-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 05/32] tree-walk.c: migrate to using new \"object_type\" field when possible","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:02Z","receivedAt":"2021-03-16T15:59:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.c | 24 +++++++++++++-----------\n 1 file changed, 13 insertions(+), 11 deletions(-)\n\ndiff --git a/tree-walk.c b/tree-walk.c\nindex b210967b73..6e9161901d 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -521,7 +521,7 @@ int traverse_trees(struct index_state *istate,\n \t\t\tif (!entry[i].path)\n \t\t\t\tcontinue;\n \t\t\tmask |= 1ul << i;\n-\t\t\tif (S_ISDIR(entry[i].mode))\n+\t\t\tif (entry[i].object_type == OBJ_TREE)\n \t\t\t\tdirmask |= 1ul << i;\n \t\t\te = &entry[i];\n \t\t}\n@@ -892,8 +892,8 @@ static int match_entry(const struct pathspec_item *item,\n \t\t * nothing else (to handle 'submod/' and 'submod'\n \t\t * uniformly).\n \t\t */\n-\t\tif (!S_ISDIR(entry->mode) &&\n-\t\t    (!S_ISGITLINK(entry->mode) || matchlen > pathlen + 1))\n+\t\tif (entry->object_type != OBJ_TREE &&\n+\t\t    (entry->object_type != OBJ_COMMIT || matchlen > pathlen + 1))\n \t\t\treturn 0;\n \t}\n \n@@ -1038,7 +1038,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t    ps->max_depth == -1)\n \t\t\treturn all_entries_interesting;\n \t\treturn within_depth(base->buf + base_offset, baselen,\n-\t\t\t\t    !!S_ISDIR(entry->mode),\n+\t\t\t\t    entry->object_type == OBJ_TREE,\n \t\t\t\t    ps->max_depth) ?\n \t\t\tentry_interesting : entry_not_interesting;\n \t}\n@@ -1071,7 +1071,7 @@ static enum interesting do_match(struct index_state *istate,\n \n \t\t\tif (within_depth(base_str + matchlen + 1,\n \t\t\t\t\t baselen - matchlen - 1,\n-\t\t\t\t\t !!S_ISDIR(entry->mode),\n+\t\t\t\t\t entry->object_type == OBJ_TREE,\n \t\t\t\t\t ps->max_depth))\n \t\t\t\tgoto interesting;\n \t\t\telse\n@@ -1094,7 +1094,8 @@ static enum interesting do_match(struct index_state *istate,\n \t\t\t\t * Match all directories. We'll try to\n \t\t\t\t * match files later on.\n \t\t\t\t */\n-\t\t\t\tif (ps->recursive && S_ISDIR(entry->mode))\n+\t\t\t\tif (ps->recursive &&\n+\t\t\t\t    entry->object_type == OBJ_TREE)\n \t\t\t\t\treturn entry_interesting;\n \n \t\t\t\t/*\n@@ -1105,7 +1106,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t\t\t * be performed in the submodule itself.\n \t\t\t\t */\n \t\t\t\tif (ps->recurse_submodules &&\n-\t\t\t\t    S_ISGITLINK(entry->mode) &&\n+\t\t\t\t    entry->object_type == OBJ_COMMIT &&\n \t\t\t\t    !ps_strncmp(item, match + baselen,\n \t\t\t\t\t\tentry->path,\n \t\t\t\t\t\titem->nowildcard_len - baselen))\n@@ -1154,7 +1155,8 @@ static enum interesting do_match(struct index_state *istate,\n \t\t * character.  More accurate matching can then\n \t\t * be performed in the submodule itself.\n \t\t */\n-\t\tif (ps->recurse_submodules && S_ISGITLINK(entry->mode) &&\n+\t\tif (ps->recurse_submodules &&\n+\t\t    entry->object_type == OBJ_COMMIT &&\n \t\t    !ps_strncmp(item, match, base->buf + base_offset,\n \t\t\t\titem->nowildcard_len)) {\n \t\t\tstrbuf_setlen(base, base_offset + baselen);\n@@ -1170,7 +1172,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t * in future, see\n \t\t * https://lore.kernel.org/git/7vmxo5l2g4.fsf@alter.siamese.dyndns.org/\n \t\t */\n-\t\tif (ps->recursive && S_ISDIR(entry->mode))\n+\t\tif (ps->recursive && entry->object_type == OBJ_TREE)\n \t\t\treturn entry_interesting;\n \t\tcontinue;\n interesting:\n@@ -1193,7 +1195,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t\t * can probably return all_entries_interesting or\n \t\t\t * all_entries_not_interesting here if matched.\n \t\t\t */\n-\t\t\tif (S_ISDIR(entry->mode))\n+\t\t\tif (entry->object_type == OBJ_TREE)\n \t\t\t\treturn entry_interesting;\n \n \t\t\tstrbuf_add(base, entry->path, pathlen);\n@@ -1269,7 +1271,7 @@ enum interesting tree_entry_interesting(struct index_state *istate,\n \t\treturn positive;\n \n \t/* #15, #19 */\n-\tif (S_ISDIR(entry->mode) &&\n+\tif (entry->object_type == OBJ_TREE &&\n \t    positive >= entry_interesting &&\n \t    negative == entry_interesting)\n \t\treturn entry_interesting;\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419324","messageId":"20210316155829.31242-5-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 04/32] tree-walk.h: add object_type member to name_entry","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:01Z","receivedAt":"2021-03-16T15:59:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Most users of the tree walking API don't care what the specific mode\nof an object in a tree is (e.g. if it's executable), they care if it's\none of OBJ_{TREE,BLOB,COMMIT}.\n\nLet's add an \"object_type\" enum to the \"name_entry\" struct to help\nsuch callers.\n\nIdeally we'd have some subset of \"enum object_type\" here with just\nthose three entries, so we could rely on the C compiler to\nexhaustively check our \"switch\" statements, but I don't know how to\ncreate such a enum subset without re-labeling OBJ_{TREE,BLOB,COMMIT}\nto e.g. \"NE_OBJ_*\" (an enum is just an int under the hood, so you can\nuse such a struct with \"OBJ_*\", but the compiler will complain...).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.c | 4 +++-\n tree-walk.h | 2 ++\n 2 files changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 2d6226d5f1..b210967b73 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -47,7 +47,9 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l\n \n \t/* Initialize the descriptor entry */\n \tdesc->entry.path = path;\n-\tdesc->entry.mode = canon_mode(mode);\n+\tmode = canon_mode(mode);\n+\tdesc->entry.mode = mode;\n+\tdesc->entry.object_type = object_type(mode);\n \tdesc->entry.pathlen = len - 1;\n \thashcpy(desc->entry.oid.hash, (const unsigned char *)path + len);\n \ndiff --git a/tree-walk.h b/tree-walk.h\nindex a5058469e9..9f3825d277 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -17,6 +17,8 @@ struct name_entry {\n \tconst char *path;\n \tint pathlen;\n \tunsigned int mode;\n+\t/* simple 'mode': Only OBJ_{BLOB,TREE,COMMIT} */\n+\tenum object_type object_type;\n };\n \n /**\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419323","messageId":"20210316155829.31242-8-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 07/32] mktree tests: test that \"mode\" is passed when sorting","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:04Z","receivedAt":"2021-03-16T15:59:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for the mode being passed to ent_compare(). That code dates\nback to 83f50539a9 (git-mktree: reverse of git-ls-tree., 2006-02-20)\nand there's never been a test for that particular edge case. Now we\nhave one.\n\nI don't see how anything could run into this in practice. In order for\nthat mode sorting to matter as a tiebreaker we need to have a\nduplicate entry in the tree, i.e. two \"foo\" entries, one a blob and\none a tree. This asserts that if that happens we'll sort on the modes\nwe encounter in such an invalid entry, i.e. we expect the tree entry\nbefore the blob.\n\nAs shown here we'd need to disable the fsck.duplicateEntries error to\nget to the point of running \"mktree\", so I doubt anyone's pushing this\nsort of data around.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t1450-fsck.sh | 44 ++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 44 insertions(+)\n\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 5071ac63a5..46125190b4 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -318,6 +318,50 @@ test_expect_success 'tree entry with type mismatch' '\n \ttest_i18ngrep ! \"dangling blob\" out\n '\n \n+test_expect_success 'tree entry with duplicate type mismatching objects' '\n+\ttest_create_repo duplicate-entry &&\n+\t(\n+\t\tcd duplicate-entry &&\n+\t\tblob=\"$(printf \"foo\" | git hash-object -w --stdin)\" &&\n+\t\ttree=\"$(printf \"100644 blob $blob\\tfoo\" | git mktree)\" &&\n+\t\tcommit=\"$(git commit-tree $tree -m \"first commit\")\" &&\n+\t\tgit cat-file commit $commit >good-commit &&\n+\n+\t\t# First bad commit, wrong type, but in the right order\n+\t\tprintf \"40000 A\\0$(echo $tree | hex2oct)\" >broken-tree-A &&\n+\t\tprintf \"100644 A\\0$(echo $blob | hex2oct)\" >broken-tree-B &&\n+\t\tcat broken-tree-A broken-tree-B >broken-tree.1 &&\n+\t\tbroken_tree1=\"$(git hash-object -w --literally -t tree broken-tree.1)\" &&\n+\t\tbad_commit1=\"$(git commit-tree $broken_tree1 -m \"bad commit 1\")\" &&\n+\t\tgit cat-file commit $bad_commit1 >bad-commit.1 &&\n+\t\tgit update-ref refs/heads/broken-commit-1 $bad_commit1 &&\n+\n+\t\ttest_must_fail git fsck &&\n+\t\tgit -c fsck.duplicateEntries=warn fsck 2>err &&\n+\t\tgrep \" in tree .*$broken_tree1: duplicateEntries\" err &&\n+\n+\t\t# Second bad commits, wrong types and order\n+\t\tcat broken-tree-B broken-tree-A >broken-tree.2 &&\n+\t\tbroken_tree2=\"$(git hash-object -w --literally -t tree broken-tree.2)\" &&\n+\t\tbad_commit2=\"$(git commit-tree $broken_tree2 -m \"bad commit 2\")\" &&\n+\t\tgit cat-file commit $bad_commit2 >bad-commit.2 &&\n+\t\tgit update-ref refs/heads/broken-commit-2 $bad_commit2 &&\n+\n+\t\ttest_must_fail git fsck &&\n+\t\tgit -c fsck.duplicateEntries=warn fsck 2>err &&\n+\t\tgrep \" in tree .*$broken_tree2: duplicateEntries\" err &&\n+\n+\t\t# git mktree should \"fix\" the order of this already broken data\n+\t\tgit ls-tree broken-commit-1 >broken-tree-1-ls &&\n+\t\tgit ls-tree broken-commit-2 >broken-tree-2-ls &&\n+\t\t! test_cmp broken-tree-1-ls broken-tree-2-ls &&\n+\n+\t\tgit mktree <broken-tree-1-ls >broken-mktree-1 &&\n+\t\tgit mktree <broken-tree-2-ls >broken-mktree-2 &&\n+\t\ttest_cmp broken-mktree-1 broken-mktree-2\n+\t)\n+'\n+\n test_expect_success 'tag pointing to nonexistent' '\n \tbadoid=$(test_oid deadbeef) &&\n \tcat >invalid-tag <<-EOF &&\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419325","messageId":"20210316155829.31242-9-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 08/32] diff tests: test that \"mode\" is passed when sorting","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:05Z","receivedAt":"2021-03-16T15:59:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Piggy-back on the recently added fsck tests for mode comparisons in\nmktree and assert that diff-tree also does the right thing in this\nimplausible scenario.\n\nAs with the other tests I've added in preceding commits, these tests\nwill fail if the mode is the same or reversed, respectively.\n\nThe diff-tree code being tested here was originally added back in\n.9174026cfe (Add \"diff-tree\" program to show which files have changed\nbetween two trees., 2005-04-09).\n\nUnlike the other tests I've added there are existing tests for both of\nthese scenarios. Breaking that function as described above will make\ntests in t4002-diff-basic.sh, t6409-merge-subtree.sh and\nt4037-diff-r-t-dirs.sh fail.\n\nI think it's good to have tests for this regardless, so let's add\nthese.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t1450-fsck.sh | 22 ++++++++++++++++++++++\n 1 file changed, 22 insertions(+)\n\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 46125190b4..5dd842bb82 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -362,6 +362,28 @@ test_expect_success 'tree entry with duplicate type mismatching objects' '\n \t)\n '\n \n+test_expect_success 'diff-tree stressing tree-diff.c::tree_entry_pathcmp(), not the same type' '\n+\tzero=$(test_oid zero) &&\n+\tgit -C duplicate-entry diff-tree broken-commit-1 broken-commit-2 >1-to-2 &&\n+\tgrep \"$zero\" 1-to-2 >lines &&\n+\ttest_line_count = 2 lines &&\n+\n+\tgit -C duplicate-entry diff-tree broken-commit-2 broken-commit-1 >2-to-1 &&\n+\tgrep \"$zero\" 2-to-1 >lines &&\n+\ttest_line_count = 2 lines\n+'\n+\n+test_expect_success 'diff-tree stressing tree-diff.c::tree_entry_pathcmp(), types not reversed' '\n+\tblob_ok=$(git -C duplicate-entry rev-parse broken-commit-2:A) &&\n+\tgit -C duplicate-entry diff-tree --diff-filter=A broken-commit-1 broken-commit-2 >1-to-2 &&\n+\tgrep \"$blob_ok\" 1-to-2 &&\n+\ttest_line_count = 1 1-to-2 &&\n+\n+\tgit -C duplicate-entry diff-tree --diff-filter=A broken-commit-2 broken-commit-1 >2-to-1 &&\n+\tgrep \"$blob_ok\" 2-to-1 &&\n+\ttest_line_count = 1 2-to-1\n+'\n+\n test_expect_success 'tag pointing to nonexistent' '\n \tbadoid=$(test_oid deadbeef) &&\n \tcat >invalid-tag <<-EOF &&\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419326","messageId":"20210316155829.31242-14-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 13/32] archive: get rid of 'stage' parameter","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:10Z","receivedAt":"2021-03-16T15:59:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Stop passing the \"stage\" parameter around in archive.c. This parameter\nexisted because the read_tree_recursive() function used to provide it,\nbut no longer does. See my in-flight commit to remove it. (tree.h API:\nremove \"stage\" parameter from read_tree_recursive(), 2021-03-06).\n\nAs can be seen in 562e25abea9 (archive: centralize archive entry\nwriting, 2008-07-14) and ed22b4173bd (archive: support filtering paths\nwith glob, 2014-09-21) it was never used by this code. We simply added\nit as a boilerplate, and then later added it to our own \"directory\n\"struct.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c | 8 --------\n 1 file changed, 8 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 8ff97751c2..529623167c 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -107,7 +107,6 @@ struct directory {\n \tstruct object_id oid;\n \tint baselen, len;\n \tunsigned mode;\n-\tint stage;\n \tchar path[FLEX_ARRAY];\n };\n \n@@ -140,7 +139,6 @@ static int check_attr_export_subst(const struct attr_check *check)\n static int write_archive_entry(const struct object_id *oid, const char *base,\n \t\t\t       int baselen, const char *filename,\n \t\t\t       unsigned mode,\n-\t\t\t       int stage,\n \t\t\t       void *context)\n {\n \tstatic struct strbuf path = STRBUF_INIT;\n@@ -200,7 +198,6 @@ static int write_archive_entry(const struct object_id *oid, const char *base,\n static void queue_directory(const unsigned char *sha1,\n \t\t\t    struct strbuf *base, const char *filename,\n \t\t\t    unsigned mode,\n-\t\t\t    int stage,\n \t\t\t    struct archiver_context *c)\n {\n \tstruct directory *d;\n@@ -209,7 +206,6 @@ static void queue_directory(const unsigned char *sha1,\n \td->up\t   = c->bottom;\n \td->baselen = base->len;\n \td->mode\t   = mode;\n-\td->stage   = stage;\n \tc->bottom  = d;\n \td->len = xsnprintf(d->path, len, \"%.*s%s/\", (int)base->len, base->buf, filename);\n \thashcpy(d->oid.hash, sha1);\n@@ -229,7 +225,6 @@ static int write_directory(struct archiver_context *c)\n \t\twrite_archive_entry(&d->oid, d->path, d->baselen,\n \t\t\t\t    d->path + d->baselen,\n \t\t\t\t    d->mode,\n-\t\t\t\t    d->stage,\n \t\t\t\t    c) != READ_TREE_RECURSIVE;\n \tfree(d);\n \treturn ret ? -1 : 0;\n@@ -241,7 +236,6 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\t\t\t\tvoid *context)\n {\n \tstruct archiver_context *c = context;\n-\tint stage = 0;\n \n \twhile (c->bottom &&\n \t       !(base->len >= c->bottom->len &&\n@@ -265,7 +259,6 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\t\treturn 0;\n \t\tqueue_directory(oid->hash, base, filename,\n \t\t\t\tmode,\n-\t\t\t\tstage,\n \t\t\t\tc);\n \t\treturn READ_TREE_RECURSIVE;\n \t}\n@@ -274,7 +267,6 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\treturn -1;\n \treturn write_archive_entry(oid, base->buf, base->len, filename,\n \t\t\t\t   mode,\n-\t\t\t\t   stage,\n \t\t\t\t   context);\n }\n \n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419327","messageId":"20210316155829.31242-11-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 10/32] tree-walk.h users: switch object_type(...) to new .object_type","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:07Z","receivedAt":"2021-03-16T15:59:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change uses of object_type(entry.mode) to use the new\nentry.object_type field.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/pack-objects.c |  2 +-\n http-push.c            |  6 ++++--\n pack-bitmap-write.c    |  8 +++++---\n revision.c             | 12 ++++++++----\n 4 files changed, 18 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 6d62aaf59a..d3ba1d4a4a 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -1534,7 +1534,7 @@ static void add_pbase_object(struct tree_desc *tree,\n \t\t\treturn;\n \t\tif (name[cmplen] != '/') {\n \t\t\tadd_object_entry(&entry.oid,\n-\t\t\t\t\t object_type(entry.mode),\n+\t\t\t\t\t entry.object_type,\n \t\t\t\t\t fullname, 1);\n \t\t\treturn;\n \t\t}\ndiff --git a/http-push.c b/http-push.c\nindex 6a4a43e07f..234b79a5db 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1314,7 +1314,7 @@ static struct object_list **process_tree(struct tree *tree,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry))\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tp = process_tree(lookup_tree(the_repository, &entry.oid),\n \t\t\t\t\t p);\n@@ -1323,9 +1323,11 @@ static struct object_list **process_tree(struct tree *tree,\n \t\t\tp = process_blob(lookup_blob(the_repository, &entry.oid),\n \t\t\t\t\t p);\n \t\t\tbreak;\n-\t\tdefault:\n+\t\tcase OBJ_COMMIT:\n \t\t\t/* Subproject commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \n \tfree_tree_buffer(tree);\ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex 88d9e696a5..ac32bf2242 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -353,7 +353,7 @@ static void fill_bitmap_tree(struct bitmap *bitmap,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry)) {\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tfill_bitmap_tree(bitmap,\n \t\t\t\t\t lookup_tree(the_repository, &entry.oid));\n@@ -361,9 +361,11 @@ static void fill_bitmap_tree(struct bitmap *bitmap,\n \t\tcase OBJ_BLOB:\n \t\t\tbitmap_set(bitmap, find_object_pos(&entry.oid));\n \t\t\tbreak;\n-\t\tdefault:\n-\t\t\t/* Gitlink, etc; not reachable */\n+\t\tcase OBJ_COMMIT:\n+\t\t\t/* submodule commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n \ndiff --git a/revision.c b/revision.c\nindex b78733f508..1db4e4e90a 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -72,16 +72,18 @@ static void mark_tree_contents_uninteresting(struct repository *r,\n \n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \twhile (tree_entry(&desc, &entry)) {\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tmark_tree_uninteresting(r, lookup_tree(r, &entry.oid));\n \t\t\tbreak;\n \t\tcase OBJ_BLOB:\n \t\t\tmark_blob_uninteresting(lookup_blob(r, &entry.oid));\n \t\t\tbreak;\n-\t\tdefault:\n+\t\tcase OBJ_COMMIT:\n \t\t\t/* Subproject commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n \n@@ -179,7 +181,7 @@ static void add_children_by_path(struct repository *r,\n \n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \twhile (tree_entry(&desc, &entry)) {\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tpaths_and_oids_insert(map, entry.path, &entry.oid);\n \n@@ -196,9 +198,11 @@ static void add_children_by_path(struct repository *r,\n \t\t\t\t\tchild->object.flags |= UNINTERESTING;\n \t\t\t}\n \t\t\tbreak;\n-\t\tdefault:\n+\t\tcase OBJ_COMMIT:\n \t\t\t/* Subproject commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n \n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419328","messageId":"20210316155829.31242-10-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 09/32] cache.h: have base_name_compare() take \"is tree?\", not \"mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:06Z","receivedAt":"2021-03-16T15:59:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the base_name_compare() API and the related df_name_compare()\nfunction to take a boolean argument indicating whether the entry is a\ntree or not, instead of having them call S_ISDIR(mode) on their own.\n\nThis makes use of the new \"object_type\" field in the \"name_entry\".\n\nThe API being modified here was originally added way back in\n958ba6c96eb (Introduce \"base_name_compare()\" helper function,\n2005-05-20).\n\nNone of these comparison functions used to have tests, but with\npreceding commits some of them now do. I thought the remainder was\ntrivial enough to review without tests, and didn't want to spend more\ntime on them.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fast-import.c | 12 ++++++++----\n builtin/mktree.c      |  4 ++--\n cache.h               |  4 ++--\n combine-diff.c        |  8 +++++---\n match-trees.c         |  6 ++++--\n merge-ort.c           |  4 ++--\n merge-recursive.c     |  6 +++---\n read-cache.c          | 16 ++++++++--------\n tree-diff.c           |  7 +++++--\n unpack-trees.c        | 15 ++++++++-------\n 10 files changed, 47 insertions(+), 35 deletions(-)\n\ndiff --git a/builtin/fast-import.c b/builtin/fast-import.c\nindex dd4d09cece..7a89510059 100644\n--- a/builtin/fast-import.c\n+++ b/builtin/fast-import.c\n@@ -1287,18 +1287,22 @@ static int tecmp0 (const void *_a, const void *_b)\n {\n \tstruct tree_entry *a = *((struct tree_entry**)_a);\n \tstruct tree_entry *b = *((struct tree_entry**)_b);\n+\tint istree_a = S_ISDIR(a->versions[0].mode);\n+\tint istree_b = S_ISDIR(b->versions[0].mode);\n \treturn base_name_compare(\n-\t\ta->name->str_dat, a->name->str_len, a->versions[0].mode,\n-\t\tb->name->str_dat, b->name->str_len, b->versions[0].mode);\n+\t\ta->name->str_dat, a->name->str_len, istree_a,\n+\t\tb->name->str_dat, b->name->str_len, istree_b);\n }\n \n static int tecmp1 (const void *_a, const void *_b)\n {\n \tstruct tree_entry *a = *((struct tree_entry**)_a);\n \tstruct tree_entry *b = *((struct tree_entry**)_b);\n+\tint istree_a = S_ISDIR(a->versions[1].mode);\n+\tint istree_b = S_ISDIR(b->versions[1].mode);\n \treturn base_name_compare(\n-\t\ta->name->str_dat, a->name->str_len, a->versions[1].mode,\n-\t\tb->name->str_dat, b->name->str_len, b->versions[1].mode);\n+\t\ta->name->str_dat, a->name->str_len, istree_a,\n+\t\tb->name->str_dat, b->name->str_len, istree_b);\n }\n \n static void mktree(struct tree_content *t, int v, struct strbuf *b)\ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex 891991b00d..2c1973229a 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -37,8 +37,8 @@ static int ent_compare(const void *a_, const void *b_)\n {\n \tstruct treeent *a = *(struct treeent **)a_;\n \tstruct treeent *b = *(struct treeent **)b_;\n-\treturn base_name_compare(a->name, a->len, a->mode,\n-\t\t\t\t b->name, b->len, b->mode);\n+\treturn base_name_compare(a->name, a->len, S_ISDIR(a->mode),\n+\t\t\t\t b->name, b->len, S_ISDIR(b->mode));\n }\n \n static void write_tree(struct object_id *oid)\ndiff --git a/cache.h b/cache.h\nindex ae0c0bef5c..e38b1e1688 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1506,8 +1506,8 @@ int repo_interpret_branch_name(struct repository *r,\n \n int validate_headref(const char *ref);\n \n-int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n-int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n+int base_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n+int df_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);\n int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);\n \ndiff --git a/combine-diff.c b/combine-diff.c\nindex 9228aebc16..ad7058a6f5 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -16,11 +16,13 @@\n static int compare_paths(const struct combine_diff_path *one,\n \t\t\t  const struct diff_filespec *two)\n {\n-\tif (!S_ISDIR(one->mode) && !S_ISDIR(two->mode))\n+\tint istree_one = S_ISDIR(one->mode);\n+\tint istree_two = S_ISDIR(two->mode);\n+\tif (!istree_one && !istree_two)\n \t\treturn strcmp(one->path, two->path);\n \n-\treturn base_name_compare(one->path, strlen(one->path), one->mode,\n-\t\t\t\t two->path, strlen(two->path), two->mode);\n+\treturn base_name_compare(one->path, strlen(one->path), istree_one,\n+\t\t\t\t two->path, strlen(two->path), istree_two);\n }\n \n static int filename_changed(char status)\ndiff --git a/match-trees.c b/match-trees.c\nindex 1011357ad0..a28c19a62a 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -67,8 +67,10 @@ static void *fill_tree_desc_strict(struct tree_desc *desc,\n static int base_name_entries_compare(const struct name_entry *a,\n \t\t\t\t     const struct name_entry *b)\n {\n-\treturn base_name_compare(a->path, tree_entry_len(a), a->mode,\n-\t\t\t\t b->path, tree_entry_len(b), b->mode);\n+\tint istree_a = (a->object_type == OBJ_TREE);\n+\tint istree_b = (b->object_type == OBJ_TREE);\n+\treturn base_name_compare(a->path, tree_entry_len(a), istree_a,\n+\t\t\t\t b->path, tree_entry_len(b), istree_b);\n }\n \n /*\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 603d30c521..4075d13aaa 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -2390,8 +2390,8 @@ static int tree_entry_order(const void *a_, const void *b_)\n \n \tconst struct merged_info *ami = a->util;\n \tconst struct merged_info *bmi = b->util;\n-\treturn base_name_compare(a->string, strlen(a->string), ami->result.mode,\n-\t\t\t\t b->string, strlen(b->string), bmi->result.mode);\n+\treturn base_name_compare(a->string, strlen(a->string), S_ISDIR(ami->result.mode),\n+\t\t\t\t b->string, strlen(b->string), S_ISDIR(bmi->result.mode));\n }\n \n static void write_tree(struct object_id *result_oid,\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 3d9207455b..1c9b08695a 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -554,12 +554,12 @@ static int string_list_df_name_compare(const char *one, const char *two)\n \t *\n \t * To achieve this, we sort with df_name_compare and provide\n \t * the mode S_IFDIR so that D/F conflicts will sort correctly.\n-\t * We use the mode S_IFDIR for everything else for simplicity,\n+\t * We say we have a directory for everything else for simplicity,\n \t * since in other cases any changes in their order due to\n \t * sorting cause no problems for us.\n \t */\n-\tint cmp = df_name_compare(one, onelen, S_IFDIR,\n-\t\t\t\t  two, twolen, S_IFDIR);\n+\tint cmp = df_name_compare(one, onelen, 1, two, twolen, 1);\n+\n \t/*\n \t * Now that 'foo' and 'foo/bar' compare equal, we have to make sure\n \t * that 'foo' comes before 'foo/bar'.\ndiff --git a/read-cache.c b/read-cache.c\nindex 1e9a50c6c7..4257fbd8a0 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -462,8 +462,8 @@ int ie_modified(struct index_state *istate,\n \treturn 0;\n }\n \n-int base_name_compare(const char *name1, int len1, int mode1,\n-\t\t      const char *name2, int len2, int mode2)\n+int base_name_compare(const char *name1, int len1, int istree1,\n+\t\t      const char *name2, int len2, int istree2)\n {\n \tunsigned char c1, c2;\n \tint len = len1 < len2 ? len1 : len2;\n@@ -474,9 +474,9 @@ int base_name_compare(const char *name1, int len1, int mode1,\n \t\treturn cmp;\n \tc1 = name1[len];\n \tc2 = name2[len];\n-\tif (!c1 && S_ISDIR(mode1))\n+\tif (!c1 && istree1)\n \t\tc1 = '/';\n-\tif (!c2 && S_ISDIR(mode2))\n+\tif (!c2 && istree2)\n \t\tc2 = '/';\n \treturn (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;\n }\n@@ -491,8 +491,8 @@ int base_name_compare(const char *name1, int len1, int mode1,\n  * This is used by routines that want to traverse the git namespace\n  * but then handle conflicting entries together when possible.\n  */\n-int df_name_compare(const char *name1, int len1, int mode1,\n-\t\t    const char *name2, int len2, int mode2)\n+int df_name_compare(const char *name1, int len1, int istree1,\n+\t\t    const char *name2, int len2, int istree2)\n {\n \tint len = len1 < len2 ? len1 : len2, cmp;\n \tunsigned char c1, c2;\n@@ -504,10 +504,10 @@ int df_name_compare(const char *name1, int len1, int mode1,\n \tif (len1 == len2)\n \t\treturn 0;\n \tc1 = name1[len];\n-\tif (!c1 && S_ISDIR(mode1))\n+\tif (!c1 && istree1)\n \t\tc1 = '/';\n \tc2 = name2[len];\n-\tif (!c2 && S_ISDIR(mode2))\n+\tif (!c2 && istree2)\n \t\tc2 = '/';\n \tif (c1 == '/' && !c2)\n \t\treturn 0;\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 7cebbb327e..6ec180331f 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -50,6 +50,7 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)\n {\n \tstruct name_entry *e1, *e2;\n \tint cmp;\n+\tint istree_e1, istree_e2;\n \n \t/* empty descriptors sort after valid tree entries */\n \tif (!t1->size)\n@@ -58,9 +59,11 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)\n \t\treturn -1;\n \n \te1 = &t1->entry;\n+\tistree_e1 = (e1->object_type == OBJ_TREE);\n \te2 = &t2->entry;\n-\tcmp = base_name_compare(e1->path, tree_entry_len(e1), e1->mode,\n-\t\t\t\te2->path, tree_entry_len(e2), e2->mode);\n+\tistree_e2 = (e2->object_type == OBJ_TREE);\n+\tcmp = base_name_compare(e1->path, tree_entry_len(e1), istree_e1,\n+\t\t\t\te2->path, tree_entry_len(e2), istree_e2);\n \treturn cmp;\n }\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex eb8fcda31b..d9d573df98 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -925,7 +925,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \t\t\t\t      const struct traverse_info *info,\n \t\t\t\t      const char *name, size_t namelen,\n-\t\t\t\t      unsigned mode)\n+\t\t\t\t      unsigned istree)\n {\n \tint pathlen, ce_len;\n \tconst char *ce_name;\n@@ -933,7 +933,7 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \tif (info->prev) {\n \t\tint cmp = do_compare_entry_piecewise(ce, info->prev,\n \t\t\t\t\t\t     info->name, info->namelen,\n-\t\t\t\t\t\t     info->mode);\n+\t\t\t\t\t\t     S_ISDIR(info->mode));\n \t\tif (cmp)\n \t\t\treturn cmp;\n \t}\n@@ -947,13 +947,13 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \tce_len -= pathlen;\n \tce_name = ce->name + pathlen;\n \n-\treturn df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n+\treturn df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n }\n \n static int do_compare_entry(const struct cache_entry *ce,\n \t\t\t    const struct traverse_info *info,\n \t\t\t    const char *name, size_t namelen,\n-\t\t\t    unsigned mode)\n+\t\t\t    unsigned istree)\n {\n \tint pathlen, ce_len;\n \tconst char *ce_name;\n@@ -965,7 +965,7 @@ static int do_compare_entry(const struct cache_entry *ce,\n \t * it is quicker to use the precomputed version.\n \t */\n \tif (!info->traverse_path)\n-\t\treturn do_compare_entry_piecewise(ce, info, name, namelen, mode);\n+\t\treturn do_compare_entry_piecewise(ce, info, name, namelen, istree);\n \n \tcmp = strncmp(ce->name, info->traverse_path, info->pathlen);\n \tif (cmp)\n@@ -980,12 +980,13 @@ static int do_compare_entry(const struct cache_entry *ce,\n \tce_len -= pathlen;\n \tce_name = ce->name + pathlen;\n \n-\treturn df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n+\treturn df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n }\n \n static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)\n {\n-\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);\n+\tint istree = (n->object_type == OBJ_TREE);\n+\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, istree);\n \tif (cmp)\n \t\treturn cmp;\n \n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419329","messageId":"20210316155829.31242-7-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 06/32] fast-import tests: test for sorting dir/file foo v.s. foo.txt","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:03Z","receivedAt":"2021-03-16T15:59:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a missing test for the sorting of \"foo\" v.s. \"foo.txt\" where \"foo\"\ncan be either a file or a directory. If it's a file then \"foo\" should\nsort before \"foo.txt\", and the other way around if \"foo\" is a\ndirectory.\n\nSee [1] for a reply to a patch of mine introducing such a\nregression. We now finally have a test for this code added back in\n463acbe1c6 (Added tree and commit writing to fast-import.,\n2006-08-14).\n\nThis tests both the tecmp1() and tecmp0() functions introduced inn\n4cabf8583f (Implemented tree delta compression in fast-import.,\n2006-08-28).\n\nThis will catch cases where the \"mode\" is the same, or reversed\nbetween a & b in both functions.\n\nThere was an existing test for the tecmp1() function(s) just above\nthis one added here.\n\nThat existing test was added in e741130386 (New fast-import test case\nfor valid tree sorting, 2007-03-12) and would catch cases where\nentries were reversed, but not if their mode (or rather, type) was the\nthe same or otherwise wrong value.\n\nThere were no tests at all for the tecmp0() function. As with the\ntecmp1() test the new test will catch cases where the \"mode\" is the\nsame (e.g. \"1\"), or if the two are reversed. It won't catch a \"return\n0\" from the function (i.e. already sorted), that case requires\ntecmp1() to also be broken.\n\n1. https://lore.kernel.org/git/CABPp-BEZfG_pNNTWsnxEa1xG+kgpQfpXj=KHBkCBAMikrXj-aA@mail.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t9300-fast-import.sh | 87 ++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 87 insertions(+)\n\ndiff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh\nindex 5c47ac4465..8bafb8cc51 100755\n--- a/t/t9300-fast-import.sh\n+++ b/t/t9300-fast-import.sh\n@@ -955,6 +955,93 @@ test_expect_success 'L: verify internal tree sorting' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'L: verify internal tree sorting on bad input (tecmp1)' '\n+\ttest_create_repo L1-0 &&\n+\n+\tcat >input <<-INPUT_END &&\n+\tblob\n+\tmark :1\n+\tdata 0\n+\n+\treset refs/heads/L1-0\n+\tcommit refs/heads/L1-0\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tcreate L1-0\n+\tCOMMIT\n+\tM 100644 :1 x.txt\n+\tM 100644 :1 x/y.txt\n+\tM 100644 :1 z.txt\n+\tM 100644 :1 z\n+\tINPUT_END\n+\n+\tcat >expected <<-EXPECT_END &&\n+\tx.txt\n+\tx\n+\tz\n+\tz.txt\n+\tEXPECT_END\n+\n+\tgit -C L1-0 fast-import <input &&\n+\tgit -C L1-0 ls-tree L1-0 >tmp &&\n+\tcut -f 2 <tmp >actual &&\n+\ttest_cmp expected actual &&\n+\tgit -C L1-0 fsck 2>err &&\n+\t# Would happen if tecmp1() were broken\n+\t! grep \"error in tree .*: treeNotSorted: \" err\n+'\n+\n+test_expect_success 'L: verify internal tree sorting on bad input (tecmp0)' '\n+\n+\ttest_create_repo L1-1 &&\n+\n+\tcat >input <<-INPUT_END &&\n+\tblob\n+\tmark :1\n+\tdata <<EOF\n+\tsome data\n+\tEOF\n+\n+\treset refs/heads/L1-1\n+\tcommit refs/heads/L1-1\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tcreate L1-1\n+\tCOMMIT\n+\tM 100644 :1 x.txt\n+\tM 100644 :1 x/y.txt\n+\tM 100644 :1 z.txt\n+\tM 100644 :1 z\n+\n+\tcommit refs/heads/L1-1\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tupdate L1-1\n+\tCOMMIT\n+\tM 100644 :1 another.txt\n+\tM 100644 :1 x.txt\n+\tM 100644 :1 x/y.txt\n+\tM 100644 :1 z.txt\n+\tM 100644 :1 z\n+\tINPUT_END\n+\n+\tcat >expected <<-EXPECT_END &&\n+\tanother.txt\n+\tx.txt\n+\tx\n+\tz\n+\tz.txt\n+\tEXPECT_END\n+\n+\tgit -C L1-1 fast-import <input &&\n+\tgit -C L1-1 ls-tree L1-1 >tmp 2>err &&\n+\t# Would happen if tecmp0() passed a fixed mode\n+\t! grep \"fatal: not a tree object\" err &&\n+\tcut -f 2 <tmp >actual &&\n+\ttest_cmp expected actual &&\n+\tgit -C L1-1 fsck\n+'\n+\n test_expect_success 'L: nested tree copy does not corrupt deltas' '\n \tcat >input <<-INPUT_END &&\n \tblob\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419330","messageId":"20210316155829.31242-13-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 12/32] tree.h users: format argument lists in archive.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:09Z","receivedAt":"2021-03-16T15:59:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Re-indent and re-flow the argument lists archive.c has downstream of\nits read_tree_recursive() call to make subsequent commits easier to\nread, as I only expect to be modifying the \"stage\" and \"mode\" lines.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c | 22 +++++++++++++++-------\n 1 file changed, 15 insertions(+), 7 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 77d1238df9..8ff97751c2 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -198,8 +198,10 @@ static int write_archive_entry(const struct object_id *oid, const char *base,\n }\n \n static void queue_directory(const unsigned char *sha1,\n-\t\tstruct strbuf *base, const char *filename,\n-\t\tunsigned mode, int stage, struct archiver_context *c)\n+\t\t\t    struct strbuf *base, const char *filename,\n+\t\t\t    unsigned mode,\n+\t\t\t    int stage,\n+\t\t\t    struct archiver_context *c)\n {\n \tstruct directory *d;\n \tsize_t len = st_add4(base->len, 1, strlen(filename), 1);\n@@ -225,8 +227,10 @@ static int write_directory(struct archiver_context *c)\n \tret =\n \t\twrite_directory(c) ||\n \t\twrite_archive_entry(&d->oid, d->path, d->baselen,\n-\t\t\t\t    d->path + d->baselen, d->mode,\n-\t\t\t\t    d->stage, c) != READ_TREE_RECURSIVE;\n+\t\t\t\t    d->path + d->baselen,\n+\t\t\t\t    d->mode,\n+\t\t\t\t    d->stage,\n+\t\t\t\t    c) != READ_TREE_RECURSIVE;\n \tfree(d);\n \treturn ret ? -1 : 0;\n }\n@@ -260,14 +264,18 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\tif (check_attr_export_ignore(check))\n \t\t\treturn 0;\n \t\tqueue_directory(oid->hash, base, filename,\n-\t\t\t\tmode, stage, c);\n+\t\t\t\tmode,\n+\t\t\t\tstage,\n+\t\t\t\tc);\n \t\treturn READ_TREE_RECURSIVE;\n \t}\n \n \tif (write_directory(c))\n \t\treturn -1;\n-\treturn write_archive_entry(oid, base->buf, base->len, filename, mode,\n-\t\t\t\t   stage, context);\n+\treturn write_archive_entry(oid, base->buf, base->len, filename,\n+\t\t\t\t   mode,\n+\t\t\t\t   stage,\n+\t\t\t\t   context);\n }\n \n struct extra_file_info {\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419331","messageId":"20210316155829.31242-12-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 11/32] tree.h: format argument lists of read_tree_recursive() users","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:08Z","receivedAt":"2021-03-16T15:59:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for adding a new argument to read_tree_fn_t re-indent\nand format the argument list of read_tree_recursive() callbacks, and\nthe relevant functions they call.\n\nThis is a whitespace-only change to make reading subsequent commits\neasier. I'll be adding a new argument on the \"mode\" line, so leave\nspace on it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          | 14 +++++++++-----\n builtin/checkout.c |  4 +++-\n builtin/log.c      |  5 +++--\n builtin/ls-tree.c  |  4 +++-\n merge-recursive.c  |  3 ++-\n tree.h             |  5 ++++-\n 6 files changed, 24 insertions(+), 11 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex c19300ecb9..77d1238df9 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -138,8 +138,10 @@ static int check_attr_export_subst(const struct attr_check *check)\n }\n \n static int write_archive_entry(const struct object_id *oid, const char *base,\n-\t\tint baselen, const char *filename, unsigned mode, int stage,\n-\t\tvoid *context)\n+\t\t\t       int baselen, const char *filename,\n+\t\t\t       unsigned mode,\n+\t\t\t       int stage,\n+\t\t\t       void *context)\n {\n \tstatic struct strbuf path = STRBUF_INIT;\n \tstruct archiver_context *c = context;\n@@ -230,8 +232,9 @@ static int write_directory(struct archiver_context *c)\n }\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n-\t\tstruct strbuf *base, const char *filename,\n-\t\tunsigned mode, void *context)\n+\t\t\t\t\tstruct strbuf *base, const char *filename,\n+\t\t\t\t\tunsigned mode,\n+\t\t\t\t\tvoid *context)\n {\n \tstruct archiver_context *c = context;\n \tint stage = 0;\n@@ -378,7 +381,8 @@ struct path_exists_context {\n };\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\tconst char *filename, unsigned mode,\n+\t\t\tconst char *filename,\n+\t\t\tunsigned mode,\n \t\t\tvoid *context)\n {\n \tint ret = -1;\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0e66390520..0887352db2 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -114,7 +114,9 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n }\n \n static int update_some(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, void *context)\n+\t\t       const char *pathname,\n+\t\t       unsigned mode,\n+\t\t       void *context)\n {\n \tint len;\n \tstruct cache_entry *ce;\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 980de59063..b7b76856a9 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -598,8 +598,9 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n }\n \n static int show_tree_object(const struct object_id *oid,\n-\t\tstruct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, void *context)\n+\t\t\t    struct strbuf *base, const char *pathname,\n+\t\t\t    unsigned mode,\n+\t\t\t    void *context)\n {\n \tFILE *file = context;\n \tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3a442631c7..8d5c3fd058 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -62,7 +62,9 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\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+\t\t     const char *pathname,\n+\t\t     unsigned mode,\n+\t\t     void *context)\n {\n \tint retval = 0;\n \tint baselen;\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1c9b08695a..689a3e0007 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,8 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   unsigned int mode, void *context)\n+\t\t\t   unsigned int mode,\n+\t\t\t   void *context)\n {\n \tstruct path_hashmap_entry *entry;\n \tint baselen = base->len;\ndiff --git a/tree.h b/tree.h\nindex f0b079d2e9..c1a936fdc4 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -31,7 +31,10 @@ struct tree *parse_tree_indirect(const struct object_id *oid);\n int cmp_cache_name_compare(const void *a_, const void *b_);\n \n #define READ_TREE_RECURSIVE 1\n-typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n+typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *,\n+\t\t\t      const char *,\n+\t\t\t      unsigned int,\n+\t\t\t      void *);\n \n int read_tree_at(struct repository *r,\n \t\t struct tree *tree,\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419332","messageId":"20210316155829.31242-15-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 14/32] tree.h API: make read_tree_fn_t take an \"enum object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:11Z","receivedAt":"2021-03-16T16:00:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Most of the users of the read_tree_fn_t callback do not care about the\n\"mode\" per-se, they just care what type it resolves to.\n\nAmend this callback mechanism added in 3c5e8468a93 (ls-tree: major\nrewrite to do pathspec, 2005-11-26) to pass the object_type, and use\nit whenever possible.\n\nIn the archive.c code we could go much deeper with this refactoring,\nafter getting the \"mode\" that code will pass it around itself and into\narchive-{tar,zip}.c. As far as I can tell we could drop the mode\nearly, and just pass \"enum_object_type type, int is_executable\". That\nwould be slightly redundant space-wise, but would assure us that we're\nnot writing out raw modes found in trees, but are normalizing them.\n\nBut that particular refactoring would be larger than what I'm trying\nto accomplish here, so let's leave it for now.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          |  8 ++++----\n builtin/checkout.c |  4 ++--\n builtin/log.c      |  4 ++--\n builtin/ls-files.c |  6 ++++--\n builtin/ls-tree.c  | 12 +++++-------\n merge-recursive.c  |  6 ++++--\n tree.c             | 19 ++++++++++++-------\n tree.h             |  2 +-\n 8 files changed, 34 insertions(+), 27 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 529623167c..254e15c8d0 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -232,7 +232,7 @@ static int write_directory(struct archiver_context *c)\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\t\t\t\tstruct strbuf *base, const char *filename,\n-\t\t\t\t\tunsigned mode,\n+\t\t\t\t\tenum object_type object_type, unsigned mode,\n \t\t\t\t\tvoid *context)\n {\n \tstruct archiver_context *c = context;\n@@ -245,7 +245,7 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\tc->bottom = next;\n \t}\n \n-\tif (S_ISDIR(mode)) {\n+\tif (object_type == OBJ_TREE) {\n \t\tsize_t baselen = base->len;\n \t\tconst struct attr_check *check;\n \n@@ -382,13 +382,13 @@ struct path_exists_context {\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n \t\t\tconst char *filename,\n-\t\t\tunsigned mode,\n+\t\t\tenum object_type object_type, unsigned mode,\n \t\t\tvoid *context)\n {\n \tint ret = -1;\n \tstruct path_exists_context *ctx = context;\n \n-\tif (S_ISDIR(mode)) {\n+\tif (object_type == OBJ_TREE) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tstrbuf_addbuf(&sb, base);\n \t\tstrbuf_addstr(&sb, filename);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0887352db2..95b2b0edf2 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -115,14 +115,14 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n \n static int update_some(const struct object_id *oid, struct strbuf *base,\n \t\t       const char *pathname,\n-\t\t       unsigned mode,\n+\t\t       enum object_type object_type, unsigned mode,\n \t\t       void *context)\n {\n \tint len;\n \tstruct cache_entry *ce;\n \tint pos;\n \n-\tif (S_ISDIR(mode))\n+\tif (object_type == OBJ_TREE)\n \t\treturn READ_TREE_RECURSIVE;\n \n \tlen = base->len + strlen(pathname);\ndiff --git a/builtin/log.c b/builtin/log.c\nindex b7b76856a9..c25f75472f 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -599,11 +599,11 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n \n static int show_tree_object(const struct object_id *oid,\n \t\t\t    struct strbuf *base, const char *pathname,\n-\t\t\t    unsigned mode,\n+\t\t\t    enum object_type object_type, unsigned mode,\n \t\t\t    void *context)\n {\n \tFILE *file = context;\n-\tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n+\tfprintf(file, \"%s%s\\n\", pathname, object_type == OBJ_TREE ? \"/\" : \"\");\n \treturn 0;\n }\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 13bcc2d847..b954d08ae4 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -446,7 +446,8 @@ static int read_one_entry_opt(struct index_state *istate,\n }\n \n static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\t  const char *pathname, unsigned mode,\n+\t\t\t  const char *pathname,\n+\t\t\t  enum object_type object_type, unsigned mode,\n \t\t\t  void *context)\n {\n \tstruct index_state *istate = context;\n@@ -460,7 +461,8 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n  * the stage that will conflict with the entry being added.\n  */\n static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n-\t\t\t\tconst char *pathname, unsigned mode,\n+\t\t\t\tconst char *pathname,\n+\t\t\t\tenum object_type object_type, unsigned mode,\n \t\t\t\tvoid *context)\n {\n \tstruct index_state *istate = context;\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 8d5c3fd058..7176d2ae06 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -63,14 +63,13 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n \n static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t     const char *pathname,\n-\t\t     unsigned mode,\n+\t\t     enum object_type object_type, unsigned mode,\n \t\t     void *context)\n {\n \tint retval = 0;\n \tint baselen;\n-\tconst char *type = blob_type;\n \n-\tif (S_ISGITLINK(mode)) {\n+\tif (object_type == OBJ_COMMIT) {\n \t\t/*\n \t\t * Maybe we want to have some recursive version here?\n \t\t *\n@@ -80,22 +79,21 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\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} else if (object_type == OBJ_TREE) {\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}\n \telse if (ls_options & LS_TREE_ONLY)\n \t\treturn 0;\n \n \tif (!(ls_options & LS_NAME_ONLY)) {\n+\t\tconst char *type = type_name(object_type);\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 (object_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),\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 689a3e0007..df4b369902 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,7 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   unsigned int mode,\n+\t\t\t   enum object_type object_type, unsigned int mode,\n \t\t\t   void *context)\n {\n \tstruct path_hashmap_entry *entry;\n@@ -467,7 +467,9 @@ static int save_files_dirs(const struct object_id *oid,\n \thashmap_add(&opt->priv->current_file_dir_set, &entry->e);\n \n \tstrbuf_setlen(base, baselen);\n-\treturn (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);\n+\tif (object_type != OBJ_TREE)\n+\t\treturn 0;\n+\treturn READ_TREE_RECURSIVE;\n }\n \n static void get_files_dirs(struct merge_options *opt, struct tree *tree)\ndiff --git a/tree.c b/tree.c\nindex 6a2a52967e..c840bf2ea2 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -28,6 +28,8 @@ int read_tree_at(struct repository *r,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry)) {\n+\t\tstruct commit *commit;\n+\n \t\tif (retval != all_entries_interesting) {\n \t\t\tretval = tree_entry_interesting(r->index, &entry,\n \t\t\t\t\t\t\tbase, 0, pathspec);\n@@ -38,7 +40,7 @@ int read_tree_at(struct repository *r,\n \t\t}\n \n \t\tswitch (fn(&entry.oid, base,\n-\t\t\t   entry.path, entry.mode, context)) {\n+\t\t\t   entry.path, entry.object_type, entry.mode, context)) {\n \t\tcase 0:\n \t\t\tcontinue;\n \t\tcase READ_TREE_RECURSIVE:\n@@ -47,11 +49,11 @@ int read_tree_at(struct repository *r,\n \t\t\treturn -1;\n \t\t}\n \n-\t\tif (S_ISDIR(entry.mode))\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_TREE:\n \t\t\toidcpy(&oid, &entry.oid);\n-\t\telse if (S_ISGITLINK(entry.mode)) {\n-\t\t\tstruct commit *commit;\n-\n+\t\t\tbreak;\n+\t\tcase OBJ_COMMIT:\n \t\t\tcommit = lookup_commit(r, &entry.oid);\n \t\t\tif (!commit)\n \t\t\t\tdie(\"Commit %s in submodule path %s%s not found\",\n@@ -64,9 +66,12 @@ int read_tree_at(struct repository *r,\n \t\t\t\t    base->buf, entry.path);\n \n \t\t\toidcpy(&oid, get_commit_tree_oid(commit));\n-\t\t}\n-\t\telse\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n \t\t\tcontinue;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n+\t\t}\n \n \t\tlen = tree_entry_len(&entry);\n \t\tstrbuf_add(base, entry.path, len);\ndiff --git a/tree.h b/tree.h\nindex c1a936fdc4..12edb316f0 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -33,7 +33,7 @@ int cmp_cache_name_compare(const void *a_, const void *b_);\n #define READ_TREE_RECURSIVE 1\n typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *,\n \t\t\t      const char *,\n-\t\t\t      unsigned int,\n+\t\t\t      enum object_type, unsigned int,\n \t\t\t      void *);\n \n int read_tree_at(struct repository *r,\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419333","messageId":"20210316155829.31242-17-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 16/32] tree-walk.h users: refactor chained \"mode\" if/else into switch","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:13Z","receivedAt":"2021-03-16T16:00:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor a couple of \"switch\" statements that previously relied on\n\"entry.mode\" to switch on \"entry.object_type\" instead.\n\nThis is more obvious, and allows us to explicitly handle all the OBJ_*\ncases, not just have a wildcard \"else\". That doesn't matter for the\nbehavior of this code, but for its readability and maintainability.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n list-objects.c | 20 ++++++++++++++------\n walker.c       | 22 +++++++++++++---------\n 2 files changed, 27 insertions(+), 15 deletions(-)\n\ndiff --git a/list-objects.c b/list-objects.c\nindex e19589baa0..37434ba89d 100644\n--- a/list-objects.c\n+++ b/list-objects.c\n@@ -111,6 +111,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry)) {\n+\t\tstruct tree *t;\n+\t\tstruct blob *b;\n+\n \t\tif (match != all_entries_interesting) {\n \t\t\tmatch = tree_entry_interesting(ctx->revs->repo->index,\n \t\t\t\t\t\t       &entry, base, 0,\n@@ -121,8 +124,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n \t\t\t\tcontinue;\n \t\t}\n \n-\t\tif (S_ISDIR(entry.mode)) {\n-\t\t\tstruct tree *t = lookup_tree(ctx->revs->repo, &entry.oid);\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_TREE:\n+\t\t\tt = lookup_tree(ctx->revs->repo, &entry.oid);\n \t\t\tif (!t) {\n \t\t\t\tdie(_(\"entry '%s' in tree %s has tree mode, \"\n \t\t\t\t      \"but is not a tree\"),\n@@ -130,12 +134,13 @@ static void process_tree_contents(struct traversal_context *ctx,\n \t\t\t}\n \t\t\tt->object.flags |= NOT_USER_GIVEN;\n \t\t\tprocess_tree(ctx, t, base, entry.path);\n-\t\t}\n-\t\telse if (S_ISGITLINK(entry.mode))\n+\t\t\tbreak;\n+\t\tcase OBJ_COMMIT:\n \t\t\tprocess_gitlink(ctx, entry.oid.hash,\n \t\t\t\t\tbase, entry.path);\n-\t\telse {\n-\t\t\tstruct blob *b = lookup_blob(ctx->revs->repo, &entry.oid);\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n+\t\t\tb = lookup_blob(ctx->revs->repo, &entry.oid);\n \t\t\tif (!b) {\n \t\t\t\tdie(_(\"entry '%s' in tree %s has blob mode, \"\n \t\t\t\t      \"but is not a blob\"),\n@@ -143,6 +148,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n \t\t\t}\n \t\t\tb->object.flags |= NOT_USER_GIVEN;\n \t\t\tprocess_blob(ctx, b, base, entry.path);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n }\ndiff --git a/walker.c b/walker.c\nindex 4984bf8b3d..7ba757244e 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -45,21 +45,25 @@ static int process_tree(struct walker *walker, struct tree *tree)\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \twhile (tree_entry(&desc, &entry)) {\n \t\tstruct object *obj = NULL;\n+\t\tstruct tree *tree;\n+\t\tstruct blob *blob;\n \n-\t\t/* submodule commits are not stored in the superproject */\n-\t\tif (S_ISGITLINK(entry.mode))\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_COMMIT:\n+\t\t\t/* submodule commits are not stored in the superproject */\n \t\t\tcontinue;\n-\t\tif (S_ISDIR(entry.mode)) {\n-\t\t\tstruct tree *tree = lookup_tree(the_repository,\n-\t\t\t\t\t\t\t&entry.oid);\n+\t\tcase OBJ_TREE:\n+\t\t\ttree = lookup_tree(the_repository, &entry.oid);\n \t\t\tif (tree)\n \t\t\t\tobj = &tree->object;\n-\t\t}\n-\t\telse {\n-\t\t\tstruct blob *blob = lookup_blob(the_repository,\n-\t\t\t\t\t\t\t&entry.oid);\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n+\t\t\tblob = lookup_blob(the_repository, &entry.oid);\n \t\t\tif (blob)\n \t\t\t\tobj = &blob->object;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t\tif (!obj || process(walker, obj))\n \t\t\treturn -1;\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419334","messageId":"20210316155829.31242-18-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 17/32] tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:14Z","receivedAt":"2021-03-16T16:00:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor more users of the \"entry.mode\" field to use the new\n\"entry.object_type\" field.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/grep.c         | 6 +++---\n builtin/merge-tree.c   | 9 +++++----\n builtin/pack-objects.c | 4 ++--\n builtin/reflog.c       | 3 ++-\n cache-tree.c           | 2 +-\n delta-islands.c        | 2 +-\n notes.c                | 4 ++--\n unpack-trees.c         | 2 +-\n 8 files changed, 17 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 4e91a253ac..5a317cdd2f 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -587,10 +587,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,\n \n \t\tstrbuf_add(base, entry.path, te_len);\n \n-\t\tif (S_ISREG(entry.mode)) {\n+\t\tif (entry.object_type == OBJ_BLOB) {\n \t\t\thit |= grep_oid(opt, &entry.oid, base->buf, tn_len,\n \t\t\t\t\t check_attr ? base->buf + tn_len : NULL);\n-\t\t} else if (S_ISDIR(entry.mode)) {\n+\t\t} else if (entry.object_type == OBJ_TREE) {\n \t\t\tenum object_type type;\n \t\t\tstruct tree_desc sub;\n \t\t\tvoid *data;\n@@ -606,7 +606,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,\n \t\t\thit |= grep_tree(opt, pathspec, &sub, base, tn_len,\n \t\t\t\t\t check_attr);\n \t\t\tfree(data);\n-\t\t} else if (recurse_submodules && S_ISGITLINK(entry.mode)) {\n+\t\t} else if (recurse_submodules && entry.object_type == OBJ_COMMIT) {\n \t\t\thit |= grep_submodule(opt, pathspec, &entry.oid,\n \t\t\t\t\t      base->buf, base->buf + tn_len,\n \t\t\t\t\t      1); /* ignored */\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex 2de34c2d48..12cb317c1b 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -275,11 +275,11 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3])\n \tif (dirmask == mask)\n \t\treturn;\n \n-\tif (n[2].mode && !S_ISDIR(n[2].mode))\n+\tif (n[2].object_type != OBJ_TREE)\n \t\tentry = link_entry(3, info, n + 2, entry);\n-\tif (n[1].mode && !S_ISDIR(n[1].mode))\n+\tif (n[1].object_type != OBJ_TREE)\n \t\tentry = link_entry(2, info, n + 1, entry);\n-\tif (n[0].mode && !S_ISDIR(n[0].mode))\n+\tif (n[0].object_type != OBJ_TREE)\n \t\tentry = link_entry(1, info, n + 0, entry);\n \n \tadd_merge_entry(entry);\n@@ -324,7 +324,8 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s\n \t}\n \n \tif (same_entry(entry+0, entry+1)) {\n-\t\tif (!is_null_oid(&entry[2].oid) && !S_ISDIR(entry[2].mode)) {\n+\t\tif (!is_null_oid(&entry[2].oid) &&\n+\t\t    entry[2].object_type != OBJ_TREE) {\n \t\t\t/* We did not touch, they modified -- take theirs */\n \t\t\tresolve(info, entry+1, entry+2);\n \t\t\treturn mask;\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex d3ba1d4a4a..f92722c12d 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -1524,7 +1524,7 @@ static void add_pbase_object(struct tree_desc *tree,\n \tint cmp;\n \n \twhile (tree_entry(tree,&entry)) {\n-\t\tif (S_ISGITLINK(entry.mode))\n+\t\tif (entry.object_type == OBJ_COMMIT)\n \t\t\tcontinue;\n \t\tcmp = tree_entry_len(&entry) != cmplen ? 1 :\n \t\t      memcmp(name, entry.path, cmplen);\n@@ -1538,7 +1538,7 @@ static void add_pbase_object(struct tree_desc *tree,\n \t\t\t\t\t fullname, 1);\n \t\t\treturn;\n \t\t}\n-\t\tif (S_ISDIR(entry.mode)) {\n+\t\tif (entry.object_type == OBJ_TREE) {\n \t\t\tstruct tree_desc sub;\n \t\t\tstruct pbase_tree_cache *tree;\n \t\t\tconst char *down = name+cmplen+1;\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 09541d1c80..bcbca82aa9 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -95,7 +95,8 @@ static int tree_is_complete(const struct object_id *oid)\n \tcomplete = 1;\n \twhile (tree_entry(&desc, &entry)) {\n \t\tif (!has_object_file(&entry.oid) ||\n-\t\t    (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {\n+\t\t    (entry.object_type == OBJ_TREE &&\n+\t\t     !tree_is_complete(&entry.oid))) {\n \t\t\ttree->object.flags |= INCOMPLETE;\n \t\t\tcomplete = 0;\n \t\t}\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 2fb483d3c0..fbe93dd2a5 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -726,7 +726,7 @@ static void prime_cache_tree_rec(struct repository *r,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \tcnt = 0;\n \twhile (tree_entry(&desc, &entry)) {\n-\t\tif (!S_ISDIR(entry.mode))\n+\t\tif (entry.object_type != OBJ_TREE)\n \t\t\tcnt++;\n \t\telse {\n \t\t\tstruct cache_tree_sub *sub;\ndiff --git a/delta-islands.c b/delta-islands.c\nindex aa98b2e541..e7cf93acbe 100644\n--- a/delta-islands.c\n+++ b/delta-islands.c\n@@ -293,7 +293,7 @@ void resolve_tree_islands(struct repository *r,\n \t\twhile (tree_entry(&desc, &entry)) {\n \t\t\tstruct object *obj;\n \n-\t\t\tif (S_ISGITLINK(entry.mode))\n+\t\t\tif (entry.object_type == OBJ_COMMIT)\n \t\t\t\tcontinue;\n \n \t\t\tobj = lookup_object(r, &entry.oid);\ndiff --git a/notes.c b/notes.c\nindex 0a5b4fa1db..d631dc5623 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -418,7 +418,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\tif (path_len == 2 * (hashsz - prefix_len)) {\n \t\t\t/* This is potentially the remainder of the SHA-1 */\n \n-\t\t\tif (!S_ISREG(entry.mode))\n+\t\t\tif (entry.object_type != OBJ_BLOB)\n \t\t\t\t/* notes must be blobs */\n \t\t\t\tgoto handle_non_note;\n \n@@ -431,7 +431,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t/* This is potentially an internal node */\n \t\t\tsize_t len = prefix_len;\n \n-\t\t\tif (!S_ISDIR(entry.mode))\n+\t\t\tif (entry.object_type != OBJ_TREE)\n \t\t\t\t/* internal nodes must be trees */\n \t\t\t\tgoto handle_non_note;\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 802de46228..33de78e32c 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1300,7 +1300,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n \tif (dirmask) {\n \t\t/* special case: \"diff-index --cached\" looking at a tree */\n \t\tif (o->diff_index_cached &&\n-\t\t    n == 1 && dirmask == 1 && S_ISDIR(names->mode)) {\n+\t\t    n == 1 && dirmask == 1 && names->object_type == OBJ_TREE) {\n \t\t\tint matches;\n \t\t\tmatches = cache_tree_matches_traversal(o->src_index->cache_tree,\n \t\t\t\t\t\t\t       names, info);\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419336","messageId":"20210316155829.31242-16-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 15/32] tree-walk.h users: migrate \"p->mode &&\" pattern","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:12Z","receivedAt":"2021-03-16T16:00:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change code that depends on \"p->mode\" either being a valid mode or\nzero to use a p->object_type comparison to \"OBJ_NONE\".\n\nThe object_type() function in cache.h will not return OBJ_NONE, but\nthese API users are implicitly relying on the memzero() that happens\nin setup_traverse_info().\n\nSince OBJ_NONE is \"0\" we can also rely on that being zero'd out here,\nalong with the rest of the structure. I think this is slightly less\nclever than \"mode not set\", and helps to get rid of more uses of\n\"mode\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/merge-tree.c | 9 +++++----\n merge-ort.c          | 2 +-\n unpack-trees.c       | 4 ++--\n 3 files changed, 8 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex de8520778d..2de34c2d48 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -214,7 +214,7 @@ static void unresolved_directory(const struct traverse_info *info,\n \tvoid *buf0, *buf1, *buf2;\n \n \tfor (p = n; p < n + 3; p++) {\n-\t\tif (p->mode && S_ISDIR(p->mode))\n+\t\tif (p->object_type == OBJ_TREE)\n \t\t\tbreak;\n \t}\n \tif (n + 3 <= p)\n@@ -222,7 +222,7 @@ static void unresolved_directory(const struct traverse_info *info,\n \n \tnewbase = traverse_path(info, p);\n \n-#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL)\n+#define ENTRY_OID(e) (((e)->object_type == OBJ_TREE) ? &(e)->oid : NULL)\n \tbuf0 = fill_tree_descriptor(r, t + 0, ENTRY_OID(n + 0));\n \tbuf1 = fill_tree_descriptor(r, t + 1, ENTRY_OID(n + 1));\n \tbuf2 = fill_tree_descriptor(r, t + 2, ENTRY_OID(n + 2));\n@@ -242,7 +242,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n \tconst char *path;\n \tstruct merge_list *link;\n \n-\tif (!n->mode)\n+\tif (n->object_type == OBJ_NONE)\n \t\treturn entry;\n \tif (entry)\n \t\tpath = entry->path;\n@@ -265,7 +265,8 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3])\n \t\t * Treat missing entries as directories so that we return\n \t\t * after unresolved_directory has handled this.\n \t\t */\n-\t\tif (!n[i].mode || S_ISDIR(n[i].mode))\n+\t\tif (n[i].object_type == OBJ_NONE ||\n+\t\t    n[i].object_type == OBJ_TREE)\n \t\t\tdirmask |= (1 << i);\n \t}\n \ndiff --git a/merge-ort.c b/merge-ort.c\nindex 4075d13aaa..4375027914 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -668,7 +668,7 @@ static int collect_merge_info_callback(int n,\n \t * setup_path_info() for tracking.\n \t */\n \tp = names;\n-\twhile (!p->mode)\n+\twhile (p->object_type == OBJ_NONE)\n \t\tp++;\n \tlen = traverse_path_len(info, p->pathlen);\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex d9d573df98..802de46228 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -862,7 +862,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n \t}\n \n \tp = names;\n-\twhile (!p->mode)\n+\twhile (p->object_type == OBJ_NONE)\n \t\tp++;\n \n \tnewinfo = *info;\n@@ -1242,7 +1242,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n \tconst struct name_entry *p = names;\n \n \t/* Find first entry with a real name (we could use \"mask\" too) */\n-\twhile (!p->mode)\n+\twhile (p->object_type == OBJ_NONE)\n \t\tp++;\n \n \tif (o->debug_unpack)\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419335","messageId":"20210316155829.31242-19-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 18/32] merge-tree tests: test for the mode comparison in same_entry()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:15Z","receivedAt":"2021-03-16T16:00:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test to stress the \"a->mode == b->mode\" comparison in\nmerge-tree.c's same_entry().\n\nThat code was initially added by Linus in 33deb63a36f (Add\n\"merge-tree\" helper program. Maybe it's retarded, maybe it's helpful.,\n2005-04-14), and then again in its current form in\n492e0759bfe (Handling large files with GIT, 2006-02-14).\n\nHowever, nothing was testing that we handled this case\ncorrectly. Simply removing the mode comparison left all tests passing,\nbut as seen here it's important that we don't think a path with the\nsame content but different modes is the same_entry().\n\nThe rest of this series will touch code that's relevant to this, but\nwon't change its behavior. This test is just something I came up with\nin testing whether the mode test in same_entry() was needed at all.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4300-merge-tree.sh | 44 +++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 44 insertions(+)\n\ndiff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh\nindex e59601e5fe..f783d784d0 100755\n--- a/t/t4300-merge-tree.sh\n+++ b/t/t4300-merge-tree.sh\n@@ -40,6 +40,25 @@ test_expect_success 'file add A, B (same)' '\n \ttest_must_be_empty actual\n '\n \n+test_expect_success 'file add A, B (different mode)' '\n+\tgit reset --hard initial &&\n+\ttest_commit \"add-a-b-same-diff-mode-A\" \"ONE\" \"AAA\" &&\n+\tgit reset --hard initial &&\n+\techo AAA >ONE &&\n+\ttest_chmod +x ONE &&\n+\ttest_tick &&\n+\tgit commit -m\"add-a-b-same-diff-mode-B\" &&\n+\tgit tag \"add-a-b-same-diff-mode-B\" HEAD &&\n+\tgit merge-tree initial add-a-b-same-diff-mode-A add-a-b-same-diff-mode-B >actual &&\n+\tcat >expected <<EXPECTED &&\n+added in both\n+  our    100644 $(git rev-parse add-a-b-same-diff-mode-A:ONE) ONE\n+  their  100755 $(git rev-parse add-a-b-same-diff-mode-B:ONE) ONE\n+EXPECTED\n+\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'file add A, B (different)' '\n \tgit reset --hard initial &&\n \ttest_commit \"add-a-b-diff-A\" \"ONE\" \"AAA\" &&\n@@ -61,6 +80,31 @@ EXPECTED\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'file add A, B (different and different mode)' '\n+\tgit reset --hard initial &&\n+\ttest_commit \"add-a-b-diff-diff-mode-A\" \"ONE\" \"AAA\" &&\n+\tgit reset --hard initial &&\n+\techo BBB >ONE &&\n+\ttest_chmod +x ONE &&\n+\ttest_tick &&\n+\tgit commit -m\"add-a-b-diff-diff-mode-B\" &&\n+\tgit tag \"add-a-b-diff-diff-mode-B\" &&\n+\tgit merge-tree initial add-a-b-diff-diff-mode-A add-a-b-diff-diff-mode-B >actual &&\n+\tcat >expected <<EXPECTED &&\n+added in both\n+  our    100644 $(git rev-parse add-a-b-diff-diff-mode-A:ONE) ONE\n+  their  100755 $(git rev-parse add-a-b-diff-diff-mode-B:ONE) ONE\n+@@ -1 +1,5 @@\n++<<<<<<< .our\n+ AAA\n++=======\n++BBB\n++>>>>>>> .their\n+EXPECTED\n+\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'file change A, !B' '\n \tgit reset --hard initial &&\n \ttest_commit \"change-a-not-b\" \"initial-file\" \"BBB\" &&\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419337","messageId":"20210316155829.31242-20-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 19/32] merge-ort: correct reference to test in 62fdec17a11","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:16Z","receivedAt":"2021-03-16T16:00:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a comment added in 62fdec17a11 (merge-ort: flesh out\nimplementation of handle_content_merge(), 2021-01-01).\n\nThe test being referred to here was moved from t6036 in\n919df319555 (Collect merge-related tests to t64xx, 2020-08-10).\n\nIt has also had the plural of \"mode\" in the name ever since being\nintroduced in 5d1daf30cce (t6036: add a failed conflict detection\ncase: regular files, different modes, 2018-06-30).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n merge-ort.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 4375027914..e54be179bd 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -1079,7 +1079,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t/*\n \t\t * FIXME: If opt->priv->call_depth && !clean, then we really\n \t\t * should not make result->mode match either a->mode or\n-\t\t * b->mode; that causes t6036 \"check conflicting mode for\n+\t\t * b->mode; that causes t6416 \"check conflicting modes for\n \t\t * regular file\" to fail.  It would be best to use some other\n \t\t * mode, but we'll confuse all kinds of stuff if we use one\n \t\t * where S_ISREG(result->mode) isn't true, and if we use\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419338","messageId":"20210316155829.31242-23-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 22/32] tree-walk.h API: formatting changes for subsequent commit","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:19Z","receivedAt":"2021-03-16T16:00:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Do formatting (mainly whitespace) changes of code around the\nget_tree_entry() function to make a subsequent change where we'll add\na sister function easier to read.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n blame.c     |  5 +++--\n tree-walk.c |  9 ++++++---\n tree-walk.h | 12 ++++++++----\n 3 files changed, 17 insertions(+), 9 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex a5044fcfaa..83babc41d0 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -102,9 +102,10 @@ static void verify_working_tree_path(struct repository *r,\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n \t\tstruct object_id blob_oid;\n \t\tunsigned short mode;\n+\t\tint ret = get_tree_entry(r, commit_oid, path, &blob_oid,\n+\t\t\t\t\t &mode);\n \n-\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode) &&\n-\t\t    oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n+\t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n \t\t\treturn;\n \t}\n \ndiff --git a/tree-walk.c b/tree-walk.c\nindex 6e9161901d..e88187e371 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -591,7 +591,8 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\toidcpy(result, &oid);\n \t\t\treturn 0;\n \t\t}\n-\t\treturn get_tree_entry(r, &oid, name + entrylen, result, mode);\n+\t\treturn get_tree_entry(r, &oid, name + entrylen, result,\n+\t\t\t\t      mode);\n \t}\n \treturn -1;\n }\n@@ -622,7 +623,8 @@ int get_tree_entry(struct repository *r,\n \t} else {\n \t\tstruct tree_desc t;\n \t\tinit_tree_desc(&t, tree, size);\n-\t\tretval = find_tree_entry(r, &t, name, oid, mode);\n+\t\tretval = find_tree_entry(r, &t, name, oid,\n+\t\t\t\t\t mode);\n \t}\n \tfree(tree);\n \treturn retval;\n@@ -748,7 +750,8 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n \n \t\t/* Look up the first (or only) path component in the tree. */\n \t\tfind_result = find_tree_entry(r, &t, namebuf.buf,\n-\t\t\t\t\t      &current_tree_oid, mode);\n+\t\t\t\t\t      &current_tree_oid,\n+\t\t\t\t\t      mode);\n \t\tif (find_result) {\n \t\t\tgoto done;\n \t\t}\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 9f3825d277..478a659ee2 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -169,10 +169,14 @@ struct traverse_info {\n \n /**\n  * Find an entry in a tree given a pathname and the sha1 of a tree to\n- * search. Returns 0 if the entry is found and -1 otherwise. The third\n- * and fourth parameters are set to the entry's sha1 and mode respectively.\n- */\n-int get_tree_entry(struct repository *, const struct object_id *, const char *, struct object_id *, unsigned short *);\n+ * search. Returns 0 if the entry is found and -1 otherwise.\n+ *\n+ * The third and fourth parameters are set to the entry's sha1 and\n+ * mode respectively.\n+ */\n+int get_tree_entry(struct repository *, const struct object_id *, const char *,\n+\t\t   struct object_id *,\n+\t\t   unsigned short *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419339","messageId":"20210316155829.31242-21-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 20/32] fsck.c: switch on \"object_type\" in fsck_walk_tree()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:17Z","receivedAt":"2021-03-16T16:00:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since 7146e66f086 (tree-walk: finally switch over tree descriptors to\ncontain a pre-parsed entry, 2014-02-06) the \"mode\" is validated such\nthat we'll never reach the \"else\" clause here.\n\nGood for us that fsck_tree() has its own FSCK_MSG_BAD_FILEMODE check\nwhich we can use, added way back in 64071805eda (git-fsck-cache: be\nstricter about \"tree\" objects, 2005-07-27).\n\nExcept it really doesn't due to a regression in 7146e66f086. A\nfollow-up commit will address that, but for now we can simply rewrite\nthis code like the rest of the s/entry.mode/entry.object_type/g\nchanges I'm making.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n fsck.c | 21 +++++++++------------\n 1 file changed, 9 insertions(+), 12 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex e3030f3b35..7c74c49d32 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -396,28 +396,25 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op\n \t\tstruct object *obj;\n \t\tint result;\n \n-\t\tif (S_ISGITLINK(entry.mode))\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_COMMIT:\n \t\t\tcontinue;\n-\n-\t\tif (S_ISDIR(entry.mode)) {\n+\t\tcase OBJ_TREE:\n \t\t\tobj = (struct object *)lookup_tree(the_repository, &entry.oid);\n \t\t\tif (name && obj)\n \t\t\t\tfsck_put_object_name(options, &entry.oid, \"%s%s/\",\n \t\t\t\t\t\t     name, entry.path);\n-\t\t\tresult = options->walk(obj, OBJ_TREE, data, options);\n-\t\t}\n-\t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n \t\t\tobj = (struct object *)lookup_blob(the_repository, &entry.oid);\n \t\t\tif (name && obj)\n \t\t\t\tfsck_put_object_name(options, &entry.oid, \"%s%s\",\n \t\t\t\t\t\t     name, entry.path);\n-\t\t\tresult = options->walk(obj, OBJ_BLOB, data, options);\n-\t\t}\n-\t\telse {\n-\t\t\tresult = error(\"in tree %s: entry %s has bad mode %.6o\",\n-\t\t\t\t       fsck_describe_object(options, &tree->object.oid),\n-\t\t\t\t       entry.path, entry.mode);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n+\t\tresult = options->walk(obj, entry.object_type, data, options);\n \t\tif (result < 0)\n \t\t\treturn result;\n \t\tif (!res)\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419340","messageId":"20210316155829.31242-22-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 21/32] tree-walk.h users: use temporary variable(s) for \"mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:18Z","receivedAt":"2021-03-16T16:00:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for an eventual rename of the \"mode\" field, add\ntemporary variable(s) in those places where it's used more than once.\n\nThis will make a subsequent commits easier to read., since we're only\ngoing to need to modify the line on which the assignment happens.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/merge-tree.c | 12 +++++++++---\n match-trees.c        | 13 +++++++------\n merge-ort.c          |  5 +++--\n notes.c              |  3 ++-\n tree-diff.c          | 13 ++++++++-----\n unpack-trees.c       |  3 ++-\n 6 files changed, 31 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex 12cb317c1b..eec5b90656 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -190,14 +190,17 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s\n {\n \tstruct merge_list *orig, *final;\n \tconst char *path;\n+\tunsigned int orig_mode, final_mode;\n \n \t/* If it's already ours, don't bother showing it */\n \tif (!ours)\n \t\treturn;\n \n \tpath = traverse_path(info, result);\n-\torig = create_entry(2, ours->mode, &ours->oid, path);\n-\tfinal = create_entry(0, result->mode, &result->oid, path);\n+\torig_mode = ours->mode;\n+\torig = create_entry(2, orig_mode, &ours->oid, path);\n+\tfinal_mode = result->mode;\n+\tfinal = create_entry(0, final_mode, &result->oid, path);\n \n \tfinal->link = orig;\n \n@@ -241,6 +244,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n {\n \tconst char *path;\n \tstruct merge_list *link;\n+\tunsigned int link_mode;\n \n \tif (n->object_type == OBJ_NONE)\n \t\treturn entry;\n@@ -248,7 +252,9 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n \t\tpath = entry->path;\n \telse\n \t\tpath = traverse_path(info, n);\n-\tlink = create_entry(stage, n->mode, &n->oid, path);\n+\tlink_mode = n->mode;\n+\tlink = create_entry(stage, link_mode, &n->oid, path);\n+\n \tlink->link = entry;\n \treturn link;\n }\ndiff --git a/match-trees.c b/match-trees.c\nindex a28c19a62a..f3e192ca74 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -86,6 +86,8 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha\n \n \tfor (;;) {\n \t\tint cmp;\n+\t\tunsigned int one_mode = one.entry.mode;\n+\t\tunsigned int two_mode = two.entry.mode;\n \n \t\tif (one.size && two.size)\n \t\t\tcmp = base_name_entries_compare(&one.entry, &two.entry);\n@@ -100,22 +102,21 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha\n \n \t\tif (cmp < 0) {\n \t\t\t/* path1 does not appear in two */\n-\t\t\tscore += score_missing(one.entry.mode);\n+\t\t\tscore += score_missing(one_mode);\n \t\t\tupdate_tree_entry(&one);\n \t\t} else if (cmp > 0) {\n \t\t\t/* path2 does not appear in one */\n-\t\t\tscore += score_missing(two.entry.mode);\n+\t\t\tscore += score_missing(two_mode);\n \t\t\tupdate_tree_entry(&two);\n \t\t} else {\n+\n \t\t\t/* path appears in both */\n \t\t\tif (!oideq(&one.entry.oid, &two.entry.oid)) {\n \t\t\t\t/* they are different */\n-\t\t\t\tscore += score_differs(one.entry.mode,\n-\t\t\t\t\t\t       two.entry.mode);\n+\t\t\t\tscore += score_differs(one_mode, two_mode);\n \t\t\t} else {\n \t\t\t\t/* same subtree or blob */\n-\t\t\t\tscore += score_matches(one.entry.mode,\n-\t\t\t\t\t\t       two.entry.mode);\n+\t\t\t\tscore += score_matches(one_mode, two_mode);\n \t\t\t}\n \t\t\tupdate_tree_entry(&one);\n \t\t\tupdate_tree_entry(&two);\ndiff --git a/merge-ort.c b/merge-ort.c\nindex e54be179bd..cad1043650 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -544,11 +544,12 @@ static void add_pair(struct merge_options *opt,\n \tstruct diff_filespec *one, *two;\n \tstruct rename_info *renames = &opt->priv->renames;\n \tint names_idx = is_add ? side : 0;\n+\tconst struct object_id *oid = &names[names_idx].oid;\n+\tunsigned int mode = names[names_idx].mode;\n \n \tone = alloc_filespec(pathname);\n \ttwo = alloc_filespec(pathname);\n-\tfill_filespec(is_add ? two : one,\n-\t\t      &names[names_idx].oid, 1, names[names_idx].mode);\n+\tfill_filespec(is_add ? two : one, oid, 1, mode);\n \tdiff_queue(&renames->pairs[side], one, two);\n }\n \ndiff --git a/notes.c b/notes.c\nindex d631dc5623..688d03ee9c 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -478,6 +478,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\tstruct strbuf non_note_path = STRBUF_INIT;\n \t\t\tconst char *q = oid_to_hex(&subtree->key_oid);\n \t\t\tsize_t i;\n+\t\t\tunsigned int mode = entry.mode;\n \t\t\tfor (i = 0; i < prefix_len; i++) {\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n@@ -485,7 +486,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t}\n \t\t\tstrbuf_add(&non_note_path, entry.path, path_len);\n \t\t\tadd_non_note(t, strbuf_detach(&non_note_path, NULL),\n-\t\t\t\t     entry.mode, entry.oid.hash);\n+\t\t\t\t     mode, entry.oid.hash);\n \t\t}\n \t}\n \tfree(buf);\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 6ec180331f..088ed52d6a 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -466,17 +466,19 @@ static struct combine_diff_path *ll_diff_tree_paths(\n \t\ttp[0].entry.mode &= ~S_IFXMIN_NEQ;\n \n \t\tfor (i = 1; i < nparent; ++i) {\n+\t\t\tunsigned int mode = tp[i].entry.mode;\n \t\t\tcmp = tree_entry_pathcmp(&tp[i], &tp[imin]);\n \t\t\tif (cmp < 0) {\n \t\t\t\timin = i;\n-\t\t\t\ttp[i].entry.mode &= ~S_IFXMIN_NEQ;\n+\t\t\t\tmode &= ~S_IFXMIN_NEQ;\n \t\t\t}\n \t\t\telse if (cmp == 0) {\n-\t\t\t\ttp[i].entry.mode &= ~S_IFXMIN_NEQ;\n+\t\t\t\tmode &= ~S_IFXMIN_NEQ;\n \t\t\t}\n \t\t\telse {\n-\t\t\t\ttp[i].entry.mode |= S_IFXMIN_NEQ;\n+\t\t\t\tmode |= S_IFXMIN_NEQ;\n \t\t\t}\n+\t\t\ttp[i].entry.mode = mode;\n \t\t}\n \n \t\t/* fixup markings for entries before imin */\n@@ -493,13 +495,14 @@ static struct combine_diff_path *ll_diff_tree_paths(\n \t\t\t/* are either pi > p[imin] or diff(t,pi) != ø ? */\n \t\t\tif (!opt->flags.find_copies_harder) {\n \t\t\t\tfor (i = 0; i < nparent; ++i) {\n+\t\t\t\t\tunsigned int mode = tp[i].entry.mode;\n \t\t\t\t\t/* p[i] > p[imin] */\n-\t\t\t\t\tif (tp[i].entry.mode & S_IFXMIN_NEQ)\n+\t\t\t\t\tif (mode & S_IFXMIN_NEQ)\n \t\t\t\t\t\tcontinue;\n \n \t\t\t\t\t/* diff(t,pi) != ø */\n \t\t\t\t\tif (!oideq(&t.entry.oid, &tp[i].entry.oid) ||\n-\t\t\t\t\t    (t.entry.mode != tp[i].entry.mode))\n+\t\t\t\t\t    (t.entry.mode != mode))\n \t\t\t\t\t\tcontinue;\n \n \t\t\t\t\tgoto skip_emit_t_tp;\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 33de78e32c..2319b114ba 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1023,8 +1023,9 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,\n \t\tis_transient ?\n \t\tmake_empty_transient_cache_entry(len) :\n \t\tmake_empty_cache_entry(istate, len);\n+\tunsigned int mode = n->mode;\n \n-\tce->ce_mode = create_ce_mode(n->mode);\n+\tce->ce_mode = create_ce_mode(mode);\n \tce->ce_flags = create_ce_flags(stage);\n \tce->ce_namelen = len;\n \toidcpy(&ce->oid, &n->oid);\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419341","messageId":"20210316155829.31242-24-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 23/32] tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:20Z","receivedAt":"2021-03-16T16:00:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the get_tree_entry() function to get_tree_entry_mode(). This\nchange is only a search-replacement of the name and indentation of the\nargument lists.\n\nA subsequent commits will add get_tree_entry_type() and\nget_tree_entry_all() functions. Those changes will be much easier to\nread if we do this rename first.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c              |  8 ++++----\n blame.c                |  6 +++---\n builtin/rm.c           |  2 +-\n builtin/update-index.c |  2 +-\n line-log.c             |  2 +-\n match-trees.c          |  6 +++---\n merge-recursive.c      | 18 +++++++++---------\n notes.c                |  2 +-\n object-name.c          |  6 +++---\n tree-walk.c            | 14 +++++++-------\n tree-walk.h            |  6 +++---\n 11 files changed, 36 insertions(+), 36 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 254e15c8d0..ab03107958 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -482,10 +482,10 @@ static void parse_treeish_arg(const char **argv,\n \t\tunsigned short mode;\n \t\tint err;\n \n-\t\terr = get_tree_entry(ar_args->repo,\n-\t\t\t\t     &tree->object.oid,\n-\t\t\t\t     prefix, &tree_oid,\n-\t\t\t\t     &mode);\n+\t\terr = get_tree_entry_mode(ar_args->repo,\n+\t\t\t\t\t  &tree->object.oid,\n+\t\t\t\t\t  prefix, &tree_oid,\n+\t\t\t\t\t  &mode);\n \t\tif (err || !S_ISDIR(mode))\n \t\t\tdie(_(\"current working directory is untracked\"));\n \ndiff --git a/blame.c b/blame.c\nindex 83babc41d0..9e0543e13d 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -102,8 +102,8 @@ static void verify_working_tree_path(struct repository *r,\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n \t\tstruct object_id blob_oid;\n \t\tunsigned short mode;\n-\t\tint ret = get_tree_entry(r, commit_oid, path, &blob_oid,\n-\t\t\t\t\t &mode);\n+\t\tint ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n+\t\t\t\t\t      &mode);\n \n \t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n \t\t\treturn;\n@@ -1239,7 +1239,7 @@ static int fill_blob_sha1_and_mode(struct repository *r,\n {\n \tif (!is_null_oid(&origin->blob_oid))\n \t\treturn 0;\n-\tif (get_tree_entry(r, &origin->commit->object.oid, origin->path, &origin->blob_oid, &origin->mode))\n+\tif (get_tree_entry_mode(r, &origin->commit->object.oid, origin->path, &origin->blob_oid, &origin->mode))\n \t\tgoto error_out;\n \tif (oid_object_info(r, &origin->blob_oid, NULL) != OBJ_BLOB)\n \t\tgoto error_out;\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex 4858631e0f..4617388b29 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -179,7 +179,7 @@ static int check_local_mod(struct object_id *head, int index_only)\n \t\t * way as changed from the HEAD.\n \t\t */\n \t\tif (no_head\n-\t\t     || get_tree_entry(the_repository, head, name, &oid, &mode)\n+\t\t     || get_tree_entry_mode(the_repository, head, name, &oid, &mode)\n \t\t     || ce->ce_mode != create_ce_mode(mode)\n \t\t     || !oideq(&ce->oid, &oid))\n \t\t\tstaged_changes = 1;\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 79087bccea..070510d6a8 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -603,7 +603,7 @@ static struct cache_entry *read_one_ent(const char *which,\n \tstruct object_id oid;\n \tstruct cache_entry *ce;\n \n-\tif (get_tree_entry(the_repository, ent, path, &oid, &mode)) {\n+\tif (get_tree_entry_mode(the_repository, ent, path, &oid, &mode)) {\n \t\tif (which)\n \t\t\terror(\"%s: not in %s branch.\", path, which);\n \t\treturn NULL;\ndiff --git a/line-log.c b/line-log.c\nindex 75c8b1acff..4790dda460 100644\n--- a/line-log.c\n+++ b/line-log.c\n@@ -503,7 +503,7 @@ static void fill_blob_sha1(struct repository *r, struct commit *commit,\n \tunsigned short mode;\n \tstruct object_id oid;\n \n-\tif (get_tree_entry(r, &commit->object.oid, spec->path, &oid, &mode))\n+\tif (get_tree_entry_mode(r, &commit->object.oid, spec->path, &oid, &mode))\n \t\tdie(\"There is no path %s in the commit\", spec->path);\n \tfill_filespec(spec, &oid, 1, mode);\n \ndiff --git a/match-trees.c b/match-trees.c\nindex f3e192ca74..0faacd8f4a 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -293,7 +293,7 @@ void shift_tree(struct repository *r,\n \t\tif (!*del_prefix)\n \t\t\treturn;\n \n-\t\tif (get_tree_entry(r, hash2, del_prefix, shifted, &mode))\n+\t\tif (get_tree_entry_mode(r, hash2, del_prefix, shifted, &mode))\n \t\t\tdie(\"cannot find path %s in tree %s\",\n \t\t\t    del_prefix, oid_to_hex(hash2));\n \t\treturn;\n@@ -321,12 +321,12 @@ void shift_tree_by(struct repository *r,\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n-\tif (!get_tree_entry(r, hash1, shift_prefix, &sub1, &mode1) &&\n+\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &mode1) &&\n \t    S_ISDIR(mode1))\n \t\tcandidate |= 1;\n \n \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n-\tif (!get_tree_entry(r, hash2, shift_prefix, &sub2, &mode2) &&\n+\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &mode2) &&\n \t    S_ISDIR(mode2))\n \t\tcandidate |= 2;\n \ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex df4b369902..bbbb68e15b 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -487,7 +487,7 @@ static int get_tree_entry_if_blob(struct repository *r,\n {\n \tint ret;\n \n-\tret = get_tree_entry(r, tree, path, &dfs->oid, &dfs->mode);\n+\tret = get_tree_entry_mode(r, tree, path, &dfs->oid, &dfs->mode);\n \tif (S_ISDIR(dfs->mode)) {\n \t\toidcpy(&dfs->oid, &null_oid);\n \t\tdfs->mode = 0;\n@@ -1886,9 +1886,9 @@ static int tree_has_path(struct repository *r, struct tree *tree,\n \tstruct object_id hashy;\n \tunsigned short mode_o;\n \n-\treturn !get_tree_entry(r,\n-\t\t\t       &tree->object.oid, path,\n-\t\t\t       &hashy, &mode_o);\n+\treturn !get_tree_entry_mode(r,\n+\t\t\t\t    &tree->object.oid, path,\n+\t\t\t\t    &hashy, &mode_o);\n }\n \n /*\n@@ -2541,11 +2541,11 @@ static void apply_directory_rename_modifications(struct merge_options *opt,\n \t * the various handle_rename_*() functions update the index\n \t * explicitly rather than relying on unpack_trees() to have done it.\n \t */\n-\tget_tree_entry(opt->repo,\n-\t\t       &tree->object.oid,\n-\t\t       pair->two->path,\n-\t\t       &re->dst_entry->stages[stage].oid,\n-\t\t       &re->dst_entry->stages[stage].mode);\n+\tget_tree_entry_mode(opt->repo,\n+\t\t\t    &tree->object.oid,\n+\t\t\t    pair->two->path,\n+\t\t\t    &re->dst_entry->stages[stage].oid,\n+\t\t\t    &re->dst_entry->stages[stage].mode);\n \n \t/*\n \t * Record the original change status (or 'type' of change).  If it\ndiff --git a/notes.c b/notes.c\nindex 688d03ee9c..ef13860614 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -1021,7 +1021,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\treturn;\n \tif (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))\n \t\tdie(\"Cannot use notes ref %s\", notes_ref);\n-\tif (get_tree_entry(the_repository, &object_oid, \"\", &oid, &mode))\n+\tif (get_tree_entry_mode(the_repository, &object_oid, \"\", &oid, &mode))\n \t\tdie(\"Failed to read notes tree referenced by %s (%s)\",\n \t\t    notes_ref, oid_to_hex(&object_oid));\n \ndiff --git a/object-name.c b/object-name.c\nindex 64202de60b..7e3b2d6d73 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1704,7 +1704,7 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \tif (is_missing_file_error(errno)) {\n \t\tchar *fullname = xstrfmt(\"%s%s\", prefix, filename);\n \n-\t\tif (!get_tree_entry(r, tree_oid, fullname, &oid, &mode)) {\n+\t\tif (!get_tree_entry_mode(r, tree_oid, fullname, &oid, &mode)) {\n \t\t\tdie(_(\"path '%s' exists, but not '%s'\\n\"\n \t\t\t    \"hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?\"),\n \t\t\t    fullname,\n@@ -1903,8 +1903,8 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo,\n \t\t\t\t\tfilename, oid, &oc->symlink_path,\n \t\t\t\t\t&oc->mode);\n \t\t\t} else {\n-\t\t\t\tret = get_tree_entry(repo, &tree_oid, filename, oid,\n-\t\t\t\t\t\t     &oc->mode);\n+\t\t\t\tret = get_tree_entry_mode(repo, &tree_oid, filename, oid,\n+\t\t\t\t\t\t\t  &oc->mode);\n \t\t\t\tif (ret && only_to_die) {\n \t\t\t\t\tdiagnose_invalid_oid_path(repo, prefix,\n \t\t\t\t\t\t\t\t   filename,\ndiff --git a/tree-walk.c b/tree-walk.c\nindex e88187e371..7819ff3e0e 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -591,17 +591,17 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\toidcpy(result, &oid);\n \t\t\treturn 0;\n \t\t}\n-\t\treturn get_tree_entry(r, &oid, name + entrylen, result,\n-\t\t\t\t      mode);\n+\t\treturn get_tree_entry_mode(r, &oid, name + entrylen, result,\n+\t\t\t\t\t   mode);\n \t}\n \treturn -1;\n }\n \n-int get_tree_entry(struct repository *r,\n-\t\t   const struct object_id *tree_oid,\n-\t\t   const char *name,\n-\t\t   struct object_id *oid,\n-\t\t   unsigned short *mode)\n+int get_tree_entry_mode(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid,\n+\t\t\tunsigned short *mode)\n {\n \tint retval;\n \tvoid *tree;\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 478a659ee2..eb9b9de6cc 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -174,9 +174,9 @@ struct traverse_info {\n  * The third and fourth parameters are set to the entry's sha1 and\n  * mode respectively.\n  */\n-int get_tree_entry(struct repository *, const struct object_id *, const char *,\n-\t\t   struct object_id *,\n-\t\t   unsigned short *);\n+int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n+\t\t\tstruct object_id *,\n+\t\t\tunsigned short *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419342","messageId":"20210316155829.31242-25-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 24/32] match-trees: use \"tmp\" for mode in shift_tree_by()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:21Z","receivedAt":"2021-03-16T16:00:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor code added in 85e51b783c3 (Make \"subtree\" part more\northogonal to the rest of merge-recursive., 2008-06-30) to make it\nobvious that we don't care about the \"mode\" here outside of the if\nstatement it appears in.\n\nThat's opposed to the sub1 & sub2 variables, where we use the two\nobject ids later in this function.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c | 10 +++++-----\n 1 file changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex 0faacd8f4a..e84f993a46 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -317,17 +317,17 @@ void shift_tree_by(struct repository *r,\n \t\t   const char *shift_prefix)\n {\n \tstruct object_id sub1, sub2;\n-\tunsigned short mode1, mode2;\n+\tunsigned short tmp;\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n-\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &mode1) &&\n-\t    S_ISDIR(mode1))\n+\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n+\t    S_ISDIR(tmp))\n \t\tcandidate |= 1;\n \n \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n-\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &mode2) &&\n-\t    S_ISDIR(mode2))\n+\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n+\t    S_ISDIR(tmp))\n \t\tcandidate |= 2;\n \n \tif (candidate == 3) {\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419343","messageId":"20210316155829.31242-26-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 25/32] tree-walk.h API: add get_tree_entry_type()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:22Z","receivedAt":"2021-03-16T16:00:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a get_tree_entry_type() helper function to compliment the existing\nget_tree_entry(), and a static get_tree_entry_all() which it uses internally.\n\nMove those users of get_tree_entry_type() who didn't care about the\nmode specifically, but just want to know whether the tree entry is one\nof OBJ_{BLOB,COMMIT,TREE} over to the new get_tree_entry_type().\n\nThe get_tree_entry_all() function itself will be made non-static in a\nsubsequent commit. I'm leaving its argument list indented accordingly\nto reduce churn when I do so.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c     |  8 ++++----\n match-trees.c | 10 +++++-----\n tree-walk.c   | 54 ++++++++++++++++++++++++++++++++++++++++-----------\n tree-walk.h   | 11 +++++++++--\n 4 files changed, 61 insertions(+), 22 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex ab03107958..d6aef83b69 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -479,14 +479,14 @@ static void parse_treeish_arg(const char **argv,\n \n \tif (prefix) {\n \t\tstruct object_id tree_oid;\n-\t\tunsigned short mode;\n+\t\tenum object_type object_type;\n \t\tint err;\n \n-\t\terr = get_tree_entry_mode(ar_args->repo,\n+\t\terr = get_tree_entry_type(ar_args->repo,\n \t\t\t\t\t  &tree->object.oid,\n \t\t\t\t\t  prefix, &tree_oid,\n-\t\t\t\t\t  &mode);\n-\t\tif (err || !S_ISDIR(mode))\n+\t\t\t\t\t  &object_type);\n+\t\tif (err || object_type != OBJ_TREE)\n \t\t\tdie(_(\"current working directory is untracked\"));\n \n \t\ttree = parse_tree_indirect(&tree_oid);\ndiff --git a/match-trees.c b/match-trees.c\nindex e84f993a46..3177558313 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -317,17 +317,17 @@ void shift_tree_by(struct repository *r,\n \t\t   const char *shift_prefix)\n {\n \tstruct object_id sub1, sub2;\n-\tunsigned short tmp;\n+\tenum object_type tmp;\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n-\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n-\t    S_ISDIR(tmp))\n+\tif (!get_tree_entry_type(r, hash1, shift_prefix, &sub1, &tmp) &&\n+\t    tmp == OBJ_TREE)\n \t\tcandidate |= 1;\n \n \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n-\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n-\t    S_ISDIR(tmp))\n+\tif (!get_tree_entry_type(r, hash2, shift_prefix, &sub2, &tmp) &&\n+\t    tmp == OBJ_TREE)\n \t\tcandidate |= 2;\n \n \tif (candidate == 3) {\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 7819ff3e0e..46ce1ba806 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -559,9 +559,17 @@ struct dir_state {\n \tstruct object_id oid;\n };\n \n+static int get_tree_entry_all(struct repository *r,\n+\t\t\t      const struct object_id *tree_oid,\n+\t\t\t      const char *name,\n+\t\t\t      struct object_id *oid,\n+\t\t\t      unsigned short *mode,\n+\t\t\t      enum object_type *object_type);\n+\n static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\t   const char *name, struct object_id *result,\n-\t\t\t   unsigned short *mode)\n+\t\t\t   unsigned short *mode,\n+\t\t\t   enum object_type *object_type)\n {\n \tint namelen = strlen(name);\n \twhile (t->size) {\n@@ -585,23 +593,24 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t}\n \t\tif (name[entrylen] != '/')\n \t\t\tcontinue;\n-\t\tif (!S_ISDIR(*mode))\n+\t\tif (*object_type != OBJ_TREE)\n \t\t\tbreak;\n \t\tif (++entrylen == namelen) {\n \t\t\toidcpy(result, &oid);\n \t\t\treturn 0;\n \t\t}\n-\t\treturn get_tree_entry_mode(r, &oid, name + entrylen, result,\n-\t\t\t\t\t   mode);\n+\t\treturn get_tree_entry_all(r, &oid, name + entrylen, result,\n+\t\t\t\t\t  mode, object_type);\n \t}\n \treturn -1;\n }\n \n-int get_tree_entry_mode(struct repository *r,\n-\t\t\tconst struct object_id *tree_oid,\n-\t\t\tconst char *name,\n-\t\t\tstruct object_id *oid,\n-\t\t\tunsigned short *mode)\n+static int get_tree_entry_all(struct repository *r,\n+\t\t       const struct object_id *tree_oid,\n+\t\t       const char *name,\n+\t\t       struct object_id *oid,\n+\t\t       unsigned short *mode,\n+\t\t       enum object_type *object_type)\n {\n \tint retval;\n \tvoid *tree;\n@@ -624,12 +633,34 @@ int get_tree_entry_mode(struct repository *r,\n \t\tstruct tree_desc t;\n \t\tinit_tree_desc(&t, tree, size);\n \t\tretval = find_tree_entry(r, &t, name, oid,\n-\t\t\t\t\t mode);\n+\t\t\t\t\t mode, object_type);\n \t}\n \tfree(tree);\n \treturn retval;\n }\n \n+int get_tree_entry_mode(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid,\n+\t\t\tunsigned short *mode)\n+{\n+\tenum object_type object_type;\n+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n+\t\t\t\t  mode, &object_type);\n+}\n+\n+int get_tree_entry_type(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid,\n+\t\t\tenum object_type *object_type)\n+{\n+\tunsigned short mode;\n+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n+\t\t\t\t  &mode, object_type);\n+}\n+\n /*\n  * This is Linux's built-in max for the number of symlinks to follow.\n  * That limit, of course, does not affect git, but it's a reasonable\n@@ -674,6 +705,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n \t\tint find_result;\n \t\tchar *first_slash;\n \t\tchar *remainder = NULL;\n+\t\tenum object_type object_type;\n \n \t\tif (!t.buffer) {\n \t\t\tvoid *tree;\n@@ -751,7 +783,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n \t\t/* Look up the first (or only) path component in the tree. */\n \t\tfind_result = find_tree_entry(r, &t, namebuf.buf,\n \t\t\t\t\t      &current_tree_oid,\n-\t\t\t\t\t      mode);\n+\t\t\t\t\t      mode, &object_type);\n \t\tif (find_result) {\n \t\t\tgoto done;\n \t\t}\ndiff --git a/tree-walk.h b/tree-walk.h\nindex eb9b9de6cc..f569960c6f 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -171,12 +171,19 @@ struct traverse_info {\n  * Find an entry in a tree given a pathname and the sha1 of a tree to\n  * search. Returns 0 if the entry is found and -1 otherwise.\n  *\n- * The third and fourth parameters are set to the entry's sha1 and\n- * mode respectively.\n+ * There are variants of this function depending on what fields in the\n+ * \"struct name_entry\" you'd like. You always need a pointer to an\n+ * appropriate variable to fill in (NULL won't do!):\n+ *\n+ * get_tree_entry_mode(): unsigned int mode\n+ * get_tree_entry_type(): enum object_type\n  */\n int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n \t\t\tunsigned short *);\n+int get_tree_entry_type(struct repository *, const struct object_id *, const char *,\n+\t\t\tstruct object_id *,\n+\t\t\tenum object_type *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419344","messageId":"20210316155829.31242-27-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 26/32] tree-walk.h API: document and format tree_entry_extract()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:23Z","receivedAt":"2021-03-16T16:00:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Document and format the argument list of the tree_entry_extract()\nfunction in preparation for adding a sister function.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.h | 14 ++++++++++----\n 1 file changed, 10 insertions(+), 4 deletions(-)\n\ndiff --git a/tree-walk.h b/tree-walk.h\nindex f569960c6f..f51485250f 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -40,11 +40,17 @@ struct tree_desc {\n \n /**\n  * Decode the entry currently being visited (the one pointed to by\n- * `tree_desc's` `entry` member) and return the sha1 of the entry. The\n- * `pathp` and `modep` arguments are set to the entry's pathname and mode\n- * respectively.\n+ * `tree_desc's` `entry` member) and return the OID of the entry.\n+ *\n+ * There are variants of this function depending on what fields in the\n+ * \"struct name_entry\" you'd like. You always need a pointer to an\n+ * appropriate variable to fill in (NULL won't do!):\n+ *\n+ * tree_entry_extract_mode(): const char *path, unsigned int mode\n  */\n-static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned short *modep)\n+static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,\n+\t\t\t\t\t\t\t const char **pathp,\n+\t\t\t\t\t\t\t unsigned short *modep)\n {\n \t*pathp = desc->entry.path;\n \t*modep = desc->entry.mode;\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419345","messageId":"20210316155829.31242-30-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 29/32] tree-walk.h API: add get_tree_entry_all()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:26Z","receivedAt":"2021-03-16T16:00:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a get_tree_entry_all() function and use it in the one caller who\ncares about both the mode and the object type. Refactor it accordingly\nto make it clear which parts care about the mode, and which about the\nobject_type.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/update-index.c | 6 ++++--\n tree-walk.c            | 9 +--------\n tree-walk.h            | 4 ++++\n 3 files changed, 9 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 070510d6a8..b489a87639 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -599,16 +599,18 @@ static struct cache_entry *read_one_ent(const char *which,\n \t\t\t\t\tstruct object_id *ent, const char *path,\n \t\t\t\t\tint namelen, int stage)\n {\n+\tenum object_type object_type;\n \tunsigned short mode;\n \tstruct object_id oid;\n \tstruct cache_entry *ce;\n \n-\tif (get_tree_entry_mode(the_repository, ent, path, &oid, &mode)) {\n+\tif (get_tree_entry_all(the_repository, ent, path, &oid,\n+\t\t\t       &mode, &object_type)) {\n \t\tif (which)\n \t\t\terror(\"%s: not in %s branch.\", path, which);\n \t\treturn NULL;\n \t}\n-\tif (mode == S_IFDIR) {\n+\tif (object_type == OBJ_TREE) {\n \t\tif (which)\n \t\t\terror(\"%s: not a blob in %s branch.\", path, which);\n \t\treturn NULL;\ndiff --git a/tree-walk.c b/tree-walk.c\nindex f4473276c9..a90dbf87af 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -559,13 +559,6 @@ struct dir_state {\n \tstruct object_id oid;\n };\n \n-static int get_tree_entry_all(struct repository *r,\n-\t\t\t      const struct object_id *tree_oid,\n-\t\t\t      const char *name,\n-\t\t\t      struct object_id *oid,\n-\t\t\t      unsigned short *mode,\n-\t\t\t      enum object_type *object_type);\n-\n static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\t   const char *name, struct object_id *result,\n \t\t\t   unsigned short *mode,\n@@ -605,7 +598,7 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \treturn -1;\n }\n \n-static int get_tree_entry_all(struct repository *r,\n+int get_tree_entry_all(struct repository *r,\n \t\t       const struct object_id *tree_oid,\n \t\t       const char *name,\n \t\t       struct object_id *oid,\ndiff --git a/tree-walk.h b/tree-walk.h\nindex a4c5487174..55ef88ef2e 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -195,6 +195,7 @@ struct traverse_info {\n  *\n  * get_tree_entry_mode(): unsigned int mode\n  * get_tree_entry_type(): enum object_type\n+ * get_tree_entry_all(): unsigned int mode, enum object_type\n  */\n int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n@@ -202,6 +203,9 @@ int get_tree_entry_mode(struct repository *, const struct object_id *, const cha\n int get_tree_entry_type(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n \t\t\tenum object_type *);\n+int get_tree_entry_all(struct repository *, const struct object_id *, const char *,\n+\t\t       struct object_id *,\n+\t\t       unsigned short *, enum object_type *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419346","messageId":"20210316155829.31242-29-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 28/32] tree-walk.h API: add a tree_entry_extract_all() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:25Z","receivedAt":"2021-03-16T16:00:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a tree_entry_extract_all() sibling function to the existing\ntree_entry_extract_mode().\n\nHaving the OBJ_{BLOB,TREE,COMMIT} when you have the \"mode\" is strictly\nspeaking redundant, but hopefully makes it easier to read the\ncode. We'll now see which parts of the code are checking the types,\nv.s. those that care about the mode specifically.\n\nOnly the first use of tree_entry_extract_mode() in emit_path() is\nconverted here, the other branch will use a new\nget_tree_entry_mode_type() introduced in a subsequent commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-diff.c |  5 +++--\n tree-walk.c |  2 +-\n tree-walk.h | 12 ++++++++++++\n 3 files changed, 16 insertions(+), 3 deletions(-)\n\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 65c7e4dbc8..918ad95fa6 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -195,10 +195,11 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \tassert(t || tp);\n \n \tif (t) {\n+\t\tenum object_type object_type;\n \t\t/* path present in resulting tree */\n-\t\toid = tree_entry_extract_mode(t, &path, &mode);\n+\t\toid = tree_entry_extract_all(t, &path, &mode, &object_type);\n \t\tpathlen = tree_entry_len(&t->entry);\n-\t\tisdir = S_ISDIR(mode);\n+\t\tisdir = (object_type == OBJ_TREE);\n \t} else {\n \t\t/*\n \t\t * a path was removed - take path from imin parent. Also take\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 46ce1ba806..f4473276c9 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -577,7 +577,7 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\tstruct object_id oid;\n \t\tint entrylen, cmp;\n \n-\t\toidcpy(&oid, tree_entry_extract(t, &entry, mode));\n+\t\toidcpy(&oid, tree_entry_extract_all(t, &entry, mode, object_type));\n \t\tentrylen = tree_entry_len(&t->entry);\n \t\tupdate_tree_entry(t);\n \t\tif (entrylen > namelen)\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 805cda649e..a4c5487174 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -47,6 +47,7 @@ struct tree_desc {\n  * appropriate variable to fill in (NULL won't do!):\n  *\n  * tree_entry_extract_mode(): const char *path, unsigned int mode\n+ * tree_entry_extract_all(): const char *path, unsigned int mode, enum object_type\n  */\n static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *desc,\n \t\t\t\t\t\t\t      const char **pathp,\n@@ -57,6 +58,17 @@ static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *\n \treturn &desc->entry.oid;\n }\n \n+static inline const struct object_id *tree_entry_extract_all(struct tree_desc *desc,\n+\t\t\t\t\t\t\t     const char **pathp,\n+\t\t\t\t\t\t\t     unsigned short *modep,\n+\t\t\t\t\t\t\t     enum object_type *object_typep)\n+{\n+\t*pathp = desc->entry.path;\n+\t*modep = desc->entry.mode;\n+\t*object_typep = desc->entry.object_type;\n+\treturn &desc->entry.oid;\n+}\n+\n /**\n  * Calculate the length of a tree entry's pathname. This utilizes the\n  * memory structure of a tree entry to avoid the overhead of using a\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419347","messageId":"20210316155829.31242-28-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 27/32] tree-entry.h API: rename tree_entry_extract() to tree_entry_extract_mode()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:24Z","receivedAt":"2021-03-16T16:00:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As with the recent split of the get_tree_entry() function, rename the\ntree_entry_extract() function to *_mode() in preparation for adding\nother variants of it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n fsck.c        | 2 +-\n match-trees.c | 4 ++--\n tree-diff.c   | 4 ++--\n tree-walk.h   | 6 +++---\n 4 files changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 7c74c49d32..11678ba582 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -670,7 +670,7 @@ static int fsck_tree(const struct object_id *oid,\n \t\tconst char *name, *backslash;\n \t\tconst struct object_id *oid;\n \n-\t\toid = tree_entry_extract(&desc, &name, &mode);\n+\t\toid = tree_entry_extract_mode(&desc, &name, &mode);\n \n \t\thas_null_sha1 |= is_null_oid(oid);\n \t\thas_full_path |= !!strchr(name, '/');\ndiff --git a/match-trees.c b/match-trees.c\nindex 3177558313..2afa496810 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -146,7 +146,7 @@ static void match_trees(const struct object_id *hash1,\n \t\tunsigned short mode;\n \t\tint score;\n \n-\t\telem = tree_entry_extract(&one, &path, &mode);\n+\t\telem = tree_entry_extract_mode(&one, &path, &mode);\n \t\tif (!S_ISDIR(mode))\n \t\t\tgoto next;\n \t\tscore = score_trees(elem, hash2);\n@@ -202,7 +202,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \t\tunsigned short mode;\n \t\tint len = tree_entry_len(&desc.entry);\n \n-\t\ttree_entry_extract(&desc, &name, &mode);\n+\t\ttree_entry_extract_mode(&desc, &name, &mode);\n \t\tif (len == toplen &&\n \t\t    !memcmp(name, prefix, toplen)) {\n \t\t\tif (!S_ISDIR(mode))\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 088ed52d6a..65c7e4dbc8 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -196,7 +196,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \n \tif (t) {\n \t\t/* path present in resulting tree */\n-\t\toid = tree_entry_extract(t, &path, &mode);\n+\t\toid = tree_entry_extract_mode(t, &path, &mode);\n \t\tpathlen = tree_entry_len(&t->entry);\n \t\tisdir = S_ISDIR(mode);\n \t} else {\n@@ -207,7 +207,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \t\t * 1) all modes for tp[i]=tp[imin] should be the same wrt\n \t\t *    S_ISDIR, thanks to base_name_compare().\n \t\t */\n-\t\ttree_entry_extract(&tp[imin], &path, &mode);\n+\t\ttree_entry_extract_mode(&tp[imin], &path, &mode);\n \t\tpathlen = tree_entry_len(&tp[imin].entry);\n \n \t\tisdir = S_ISDIR(mode);\ndiff --git a/tree-walk.h b/tree-walk.h\nindex f51485250f..805cda649e 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -48,9 +48,9 @@ struct tree_desc {\n  *\n  * tree_entry_extract_mode(): const char *path, unsigned int mode\n  */\n-static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,\n-\t\t\t\t\t\t\t const char **pathp,\n-\t\t\t\t\t\t\t unsigned short *modep)\n+static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *desc,\n+\t\t\t\t\t\t\t      const char **pathp,\n+\t\t\t\t\t\t\t      unsigned short *modep)\n {\n \t*pathp = desc->entry.path;\n \t*modep = desc->entry.mode;\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419348","messageId":"20210316155829.31242-32-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 31/32] blame: emit a better error on 'git blame directory'","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:28Z","receivedAt":"2021-03-16T16:00:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change an early check for non-blobs in verify_working_tree_path() to\nlet any such objects pass, and instead die shortly thereafter in the\nfake_working_tree_commit() caller's type check.\n\nNow e.g. doing \"git blame t\" in git.git emits:\n\n    fatal: unsupported file type t\n\nInstead of:\n\n    fatal: no such path 't' in HEAD\n\nThe main point of this test is to assert that we're not doing\nsomething uniquely bad when in a conflicted merge. See\ncd8ae20195 (git-blame shouldn't crash if run in an unmerged tree,\n2007-10-18) and 9aeaab6811 (blame: allow \"blame file\" in the middle of\na conflicted merge, 2012-09-11) for the bug the t8004 test was\noriginally meant to address.\n\nBut when extending it let's grep out the specific error message for\ngood measure. Having to change it in the future (e.g. as part of my\nparallel series to improve such 'OID does not match type' messages) is\na small price for ensuring it doesn't regress.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n blame.c                         |  8 ++------\n t/t8004-blame-with-conflicts.sh | 21 +++++++++++++++++++++\n 2 files changed, 23 insertions(+), 6 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 9e0543e13d..7da162cd58 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -100,12 +100,8 @@ static void verify_working_tree_path(struct repository *r,\n \n \tfor (parents = work_tree->parents; parents; parents = parents->next) {\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n-\t\tstruct object_id blob_oid;\n-\t\tunsigned short mode;\n-\t\tint ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n-\t\t\t\t\t      &mode);\n-\n-\t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n+\t\tstruct object_id oid;\n+\t\tif (!get_tree_entry_path(r, commit_oid, path, &oid))\n \t\t\treturn;\n \t}\n \ndiff --git a/t/t8004-blame-with-conflicts.sh b/t/t8004-blame-with-conflicts.sh\nindex 35414a5336..5e3dea35a5 100755\n--- a/t/t8004-blame-with-conflicts.sh\n+++ b/t/t8004-blame-with-conflicts.sh\n@@ -73,4 +73,25 @@ test_expect_success 'blame does not crash with conflicted file in stages 1,3' '\n \tgit blame file1\n '\n \n+test_expect_success 'setup second case' '\n+\tgit merge --abort\n+'\n+\n+test_expect_success 'blame on directory/file conflict' '\n+\tmkdir d &&\n+\ttest_commit second &&\n+\ttest_commit d/file &&\n+\ttest_must_fail git blame d 2>expected &&\n+\tgrep \"unsupported file type d\" expected &&\n+\n+\tgit reset --hard second &&\n+\t>d &&\n+\tgit add d &&\n+\tgit commit -m\"a not-a-dir\" &&\n+\ttest_must_fail git merge d/file &&\n+\n+\ttest_must_fail git blame d 2>actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419349","messageId":"20210316155829.31242-31-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 30/32] tree-walk.h API: add a get_tree_entry_path() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:27Z","receivedAt":"2021-03-16T16:00:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a get_tree_entry_path() variant in addition to\nget_tree_entry_path_{mode,type,all}(). This is for those callers that\nneed neither the mode nor \"enum object_type\" parameters filled for\nthem.\n\nThere are callers here which don't need the \"struct object_id\" filled;\nforcing callers to pass one just requires they create a throwaway\nvariable.\n\nSee the following commits for the introduction of such code that's\nbeing modified here:\n\n - shift_tree(): 68faf68938e (A new merge stragety[sic] 'subtree'.,\n    2007-02-15) for the shift_tree()\n\n - tree_has_path(): 96e7ffbdc31 (merge-recursive: check for directory\n   level conflicts, 2018-04-19)\n\n - init_notes(): fd53c9eb445 (Speed up git notes lookup, 2009-10-09)\n\n - diagnose_invalid_oid_path(): 009fee4774d (Detailed diagnosis when\n   parsing an object name fails., 2009-12-07)\n\nThose could potentially be refactored too, but I've got to stop at\nsome point, and right now I'm focusing downstream code that depends on\n\"mode\" (or \"enum object_type\").\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c     |  4 +---\n merge-recursive.c |  6 ++----\n notes.c           |  3 +--\n object-name.c     |  3 +--\n tree-walk.c       | 11 +++++++++++\n tree-walk.h       |  3 +++\n 6 files changed, 19 insertions(+), 11 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex 2afa496810..25bfb46fb0 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -288,12 +288,10 @@ void shift_tree(struct repository *r,\n \n \tif (add_score < del_score) {\n \t\t/* We need to pick a subtree of two */\n-\t\tunsigned short mode;\n-\n \t\tif (!*del_prefix)\n \t\t\treturn;\n \n-\t\tif (get_tree_entry_mode(r, hash2, del_prefix, shifted, &mode))\n+\t\tif (get_tree_entry_path(r, hash2, del_prefix, shifted))\n \t\t\tdie(\"cannot find path %s in tree %s\",\n \t\t\t    del_prefix, oid_to_hex(hash2));\n \t\treturn;\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex bbbb68e15b..83d2b8b844 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1884,11 +1884,9 @@ static int tree_has_path(struct repository *r, struct tree *tree,\n \t\t\t const char *path)\n {\n \tstruct object_id hashy;\n-\tunsigned short mode_o;\n-\n-\treturn !get_tree_entry_mode(r,\n+\treturn !get_tree_entry_path(r,\n \t\t\t\t    &tree->object.oid, path,\n-\t\t\t\t    &hashy, &mode_o);\n+\t\t\t\t    &hashy);\n }\n \n /*\ndiff --git a/notes.c b/notes.c\nindex ef13860614..aa46cb2b09 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -994,7 +994,6 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\tcombine_notes_fn combine_notes, int flags)\n {\n \tstruct object_id oid, object_oid;\n-\tunsigned short mode;\n \tstruct leaf_node root_tree;\n \n \tif (!t)\n@@ -1021,7 +1020,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\treturn;\n \tif (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))\n \t\tdie(\"Cannot use notes ref %s\", notes_ref);\n-\tif (get_tree_entry_mode(the_repository, &object_oid, \"\", &oid, &mode))\n+\tif (get_tree_entry_path(the_repository, &object_oid, \"\", &oid))\n \t\tdie(\"Failed to read notes tree referenced by %s (%s)\",\n \t\t    notes_ref, oid_to_hex(&object_oid));\n \ndiff --git a/object-name.c b/object-name.c\nindex 7e3b2d6d73..9ff5f83c1f 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1693,7 +1693,6 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \t\t\t\t      int object_name_len)\n {\n \tstruct object_id oid;\n-\tunsigned short mode;\n \n \tif (!prefix)\n \t\tprefix = \"\";\n@@ -1704,7 +1703,7 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \tif (is_missing_file_error(errno)) {\n \t\tchar *fullname = xstrfmt(\"%s%s\", prefix, filename);\n \n-\t\tif (!get_tree_entry_mode(r, tree_oid, fullname, &oid, &mode)) {\n+\t\tif (!get_tree_entry_path(r, tree_oid, fullname, &oid)) {\n \t\t\tdie(_(\"path '%s' exists, but not '%s'\\n\"\n \t\t\t    \"hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?\"),\n \t\t\t    fullname,\ndiff --git a/tree-walk.c b/tree-walk.c\nindex a90dbf87af..fa846535df 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -632,6 +632,17 @@ int get_tree_entry_all(struct repository *r,\n \treturn retval;\n }\n \n+int get_tree_entry_path(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid)\n+{\n+\tunsigned short mode;\n+\tenum object_type object_type;\n+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n+\t\t\t\t  &mode, &object_type);\n+}\n+\n int get_tree_entry_mode(struct repository *r,\n \t\t\tconst struct object_id *tree_oid,\n \t\t\tconst char *name,\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 55ef88ef2e..efcd7ccd10 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -193,10 +193,13 @@ struct traverse_info {\n  * \"struct name_entry\" you'd like. You always need a pointer to an\n  * appropriate variable to fill in (NULL won't do!):\n  *\n+ * get_tree_entry_path(): <no extra argument, just get the common 'path'>\n  * get_tree_entry_mode(): unsigned int mode\n  * get_tree_entry_type(): enum object_type\n  * get_tree_entry_all(): unsigned int mode, enum object_type\n  */\n+int get_tree_entry_path(struct repository *, const struct object_id *, const char *,\n+\t\t\tstruct object_id *);\n int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n \t\t\tunsigned short *);\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419350","messageId":"20210316155829.31242-33-avarab@gmail.com","threadId":"18927","inReplyTo":"20210316021312.13927-1-avarab@gmail.com","subject":"[PATCH v3 32/32] tree-walk.h API: add a tree_entry_extract_type() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T15:58:29Z","receivedAt":"2021-03-16T16:00:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add and use a tree_entry_extract_type() function. There were callers\nof tree_entry_extract() which didn't care about the mode, but just the\ntype in the tree entry.\n\nIn emit_path() the \"mode\" variable was not used after the \"isdir\"\nassignment, as can be seen in the diff with it being set to 0.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c | 12 ++++++------\n tree-diff.c   |  5 +++--\n tree-walk.h   | 11 +++++++++++\n 3 files changed, 20 insertions(+), 8 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex 25bfb46fb0..89109659aa 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -143,11 +143,11 @@ static void match_trees(const struct object_id *hash1,\n \twhile (one.size) {\n \t\tconst char *path;\n \t\tconst struct object_id *elem;\n-\t\tunsigned short mode;\n+\t\tenum object_type object_type;\n \t\tint score;\n \n-\t\telem = tree_entry_extract_mode(&one, &path, &mode);\n-\t\tif (!S_ISDIR(mode))\n+\t\telem = tree_entry_extract_type(&one, &path, &object_type);\n+\t\tif (object_type != OBJ_TREE)\n \t\t\tgoto next;\n \t\tscore = score_trees(elem, hash2);\n \t\tif (*best_score < score) {\n@@ -198,14 +198,14 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \n \trewrite_here = NULL;\n \twhile (desc.size) {\n+\t\tenum object_type object_type;\n \t\tconst char *name;\n-\t\tunsigned short mode;\n \t\tint len = tree_entry_len(&desc.entry);\n \n-\t\ttree_entry_extract_mode(&desc, &name, &mode);\n+\t\ttree_entry_extract_type(&desc, &name, &object_type);\n \t\tif (len == toplen &&\n \t\t    !memcmp(name, prefix, toplen)) {\n-\t\t\tif (!S_ISDIR(mode))\n+\t\t\tif (object_type != OBJ_TREE)\n \t\t\t\tdie(\"entry %s in tree %s is not a tree\", name,\n \t\t\t\t    oid_to_hex(oid1));\n \ndiff --git a/tree-diff.c b/tree-diff.c\nindex 918ad95fa6..8409374f0b 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -208,10 +208,11 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \t\t * 1) all modes for tp[i]=tp[imin] should be the same wrt\n \t\t *    S_ISDIR, thanks to base_name_compare().\n \t\t */\n-\t\ttree_entry_extract_mode(&tp[imin], &path, &mode);\n+\t\tenum object_type object_type;\n+\t\ttree_entry_extract_type(&tp[imin], &path, &object_type);\n \t\tpathlen = tree_entry_len(&tp[imin].entry);\n \n-\t\tisdir = S_ISDIR(mode);\n+\t\tisdir = object_type == OBJ_TREE;\n \t\toid = NULL;\n \t\tmode = 0;\n \t}\ndiff --git a/tree-walk.h b/tree-walk.h\nindex efcd7ccd10..f5102ed542 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -47,6 +47,7 @@ struct tree_desc {\n  * appropriate variable to fill in (NULL won't do!):\n  *\n  * tree_entry_extract_mode(): const char *path, unsigned int mode\n+ * tree_entry_extract_type(): const char *path, enum object_type\n  * tree_entry_extract_all(): const char *path, unsigned int mode, enum object_type\n  */\n static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *desc,\n@@ -58,6 +59,16 @@ static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *\n \treturn &desc->entry.oid;\n }\n \n+static inline const struct object_id *tree_entry_extract_type(struct tree_desc *desc,\n+\t\t\t\t\t\t\t      const char **pathp,\n+\t\t\t\t\t\t\t      enum object_type *object_typep)\n+{\n+\t*pathp = desc->entry.path;\n+\t*object_typep = desc->entry.object_type;\n+\treturn &desc->entry.oid;\n+}\n+\n+\n static inline const struct object_id *tree_entry_extract_all(struct tree_desc *desc,\n \t\t\t\t\t\t\t     const char **pathp,\n \t\t\t\t\t\t\t     unsigned short *modep,\n-- \n2.31.0.256.gf0ddda3145\n\n"},{"id":"419398","messageId":"CABPp-BGnc+e9SBJfufL1zFLf0hoPsBT=+Hivi7ESWyX5ez8cUg@mail.gmail.com","threadId":"18927","inReplyTo":"20210316155829.31242-1-avarab@gmail.com","subject":"Re: [PATCH v3 00/32] tree-walk: mostly replace \"mode\" with \"enum object_type\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-03-16T17:28:11Z","receivedAt":"2021-03-16T17:29:15Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Mar 16, 2021 at 8:58 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> The v2 of this series was broken as noted in\n> https://lore.kernel.org/git/87blbjfqkh.fsf@evledraar.gmail.com/\n>\n> Here's a fixed version. This should actually address all of Elijah's\n> comments on v1 + v2.\n\nYep, this one looks good to me.  Thanks!\n\n>\n> It's up to 32 patches because I added some tests for the\n> mostly-never-been-tested code being changed in 09/32.\n>\n> Ævar Arnfjörð Bjarmason (32):\n>   diff.c: remove redundant canon_mode() call\n>   notes & match-trees: use name_entry's \"pathlen\" member\n>   cache.h: add a comment to object_type()\n>   tree-walk.h: add object_type member to name_entry\n>   tree-walk.c: migrate to using new \"object_type\" field when possible\n>   fast-import tests: test for sorting dir/file foo v.s. foo.txt\n>   mktree tests: test that \"mode\" is passed when sorting\n>   diff tests: test that \"mode\" is passed when sorting\n>   cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n>   tree-walk.h users: switch object_type(...) to new .object_type\n>   tree.h: format argument lists of read_tree_recursive() users\n>   tree.h users: format argument lists in archive.c\n>   archive: get rid of 'stage' parameter\n>   tree.h API: make read_tree_fn_t take an \"enum object_type\"\n>   tree-walk.h users: migrate \"p->mode &&\" pattern\n>   tree-walk.h users: refactor chained \"mode\" if/else into switch\n>   tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n>   merge-tree tests: test for the mode comparison in same_entry()\n>   merge-ort: correct reference to test in 62fdec17a11\n>   fsck.c: switch on \"object_type\" in fsck_walk_tree()\n>   tree-walk.h users: use temporary variable(s) for \"mode\"\n>   tree-walk.h API: formatting changes for subsequent commit\n>   tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n>   match-trees: use \"tmp\" for mode in shift_tree_by()\n>   tree-walk.h API: add get_tree_entry_type()\n>   tree-walk.h API: document and format tree_entry_extract()\n>   tree-entry.h API: rename tree_entry_extract() to\n>     tree_entry_extract_mode()\n>   tree-walk.h API: add a tree_entry_extract_all() function\n>   tree-walk.h API: add get_tree_entry_all()\n>   tree-walk.h API: add a get_tree_entry_path() function\n>   blame: emit a better error on 'git blame directory'\n>   tree-walk.h API: add a tree_entry_extract_type() function\n>\n>  archive.c                       | 50 +++++++++---------\n>  blame.c                         |  9 ++--\n>  builtin/checkout.c              |  6 ++-\n>  builtin/fast-import.c           | 12 +++--\n>  builtin/grep.c                  |  6 +--\n>  builtin/log.c                   |  7 +--\n>  builtin/ls-files.c              |  6 ++-\n>  builtin/ls-tree.c               | 14 +++---\n>  builtin/merge-tree.c            | 30 +++++++----\n>  builtin/mktree.c                |  4 +-\n>  builtin/pack-objects.c          |  6 +--\n>  builtin/reflog.c                |  3 +-\n>  builtin/rm.c                    |  2 +-\n>  builtin/update-index.c          |  6 ++-\n>  cache-tree.c                    |  2 +-\n>  cache.h                         | 11 ++--\n>  combine-diff.c                  |  8 +--\n>  delta-islands.c                 |  2 +-\n>  diff.c                          |  2 +-\n>  fsck.c                          | 23 ++++-----\n>  http-push.c                     |  6 ++-\n>  line-log.c                      |  2 +-\n>  list-objects.c                  | 20 +++++---\n>  match-trees.c                   | 52 +++++++++----------\n>  merge-ort.c                     | 13 ++---\n>  merge-recursive.c               | 33 ++++++------\n>  notes.c                         | 14 +++---\n>  object-name.c                   |  7 ++-\n>  pack-bitmap-write.c             |  8 +--\n>  read-cache.c                    | 16 +++---\n>  revision.c                      | 12 +++--\n>  t/t1450-fsck.sh                 | 66 ++++++++++++++++++++++++\n>  t/t4300-merge-tree.sh           | 44 ++++++++++++++++\n>  t/t8004-blame-with-conflicts.sh | 21 ++++++++\n>  t/t9300-fast-import.sh          | 87 ++++++++++++++++++++++++++++++++\n>  tree-diff.c                     | 30 +++++++----\n>  tree-walk.c                     | 89 ++++++++++++++++++++++++---------\n>  tree-walk.h                     | 63 ++++++++++++++++++++---\n>  tree.c                          | 19 ++++---\n>  tree.h                          |  5 +-\n>  unpack-trees.c                  | 24 +++++----\n>  walker.c                        | 22 ++++----\n>  42 files changed, 618 insertions(+), 244 deletions(-)\n>\n> Range-diff:\n>  1:  f9bbc30f69 =  1:  26bc38fbdd diff.c: remove redundant canon_mode() call\n>  2:  187fc2c3e6 =  2:  8502cf8134 notes & match-trees: use name_entry's \"pathlen\" member\n>  3:  311637c558 =  3:  68133fa6aa cache.h: add a comment to object_type()\n>  4:  fecfe3d462 =  4:  9f714d6c01 tree-walk.h: add object_type member to name_entry\n>  5:  db961ab5e8 =  5:  6dbf2b0a6a tree-walk.c: migrate to using new \"object_type\" field when possible\n>  -:  ---------- >  6:  354a8e9a2a fast-import tests: test for sorting dir/file foo v.s. foo.txt\n>  -:  ---------- >  7:  e2331df28e mktree tests: test that \"mode\" is passed when sorting\n>  -:  ---------- >  8:  9e9486c2ea diff tests: test that \"mode\" is passed when sorting\n>  6:  df2fc76161 !  9:  be5c713336 cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n>     @@ Commit message\n>          958ba6c96eb (Introduce \"base_name_compare()\" helper function,\n>          2005-05-20).\n>\n>     +    None of these comparison functions used to have tests, but with\n>     +    preceding commits some of them now do. I thought the remainder was\n>     +    trivial enough to review without tests, and didn't want to spend more\n>     +    time on them.\n>     +\n>          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n>       ## builtin/fast-import.c ##\n>      @@ builtin/fast-import.c: static int tecmp0 (const void *_a, const void *_b)\n>     + {\n>         struct tree_entry *a = *((struct tree_entry**)_a);\n>         struct tree_entry *b = *((struct tree_entry**)_b);\n>     ++  int istree_a = S_ISDIR(a->versions[0].mode);\n>     ++  int istree_b = S_ISDIR(b->versions[0].mode);\n>         return base_name_compare(\n>      -          a->name->str_dat, a->name->str_len, a->versions[0].mode,\n>      -          b->name->str_dat, b->name->str_len, b->versions[0].mode);\n>     -+          a->name->str_dat, a->name->str_len, 1,\n>     -+          b->name->str_dat, b->name->str_len, 1);\n>     ++          a->name->str_dat, a->name->str_len, istree_a,\n>     ++          b->name->str_dat, b->name->str_len, istree_b);\n>       }\n>\n>       static int tecmp1 (const void *_a, const void *_b)\n>     -@@ builtin/fast-import.c: static int tecmp1 (const void *_a, const void *_b)\n>     + {\n>         struct tree_entry *a = *((struct tree_entry**)_a);\n>         struct tree_entry *b = *((struct tree_entry**)_b);\n>     ++  int istree_a = S_ISDIR(a->versions[1].mode);\n>     ++  int istree_b = S_ISDIR(b->versions[1].mode);\n>         return base_name_compare(\n>      -          a->name->str_dat, a->name->str_len, a->versions[1].mode,\n>      -          b->name->str_dat, b->name->str_len, b->versions[1].mode);\n>     -+          a->name->str_dat, a->name->str_len, 1,\n>     -+          b->name->str_dat, b->name->str_len, 1);\n>     ++          a->name->str_dat, a->name->str_len, istree_a,\n>     ++          b->name->str_dat, b->name->str_len, istree_b);\n>       }\n>\n>       static void mktree(struct tree_content *t, int v, struct strbuf *b)\n>  7:  49d5da8c08 = 10:  43623edddf tree-walk.h users: switch object_type(...) to new .object_type\n>  8:  c9d209d496 = 11:  030898f884 tree.h: format argument lists of read_tree_recursive() users\n>  9:  a6d2660fe1 = 12:  0ce197950b tree.h users: format argument lists in archive.c\n> 10:  15f7f89acc = 13:  051c9f32ac archive: get rid of 'stage' parameter\n> 11:  7a71404ea3 = 14:  bc73994b4c tree.h API: make read_tree_fn_t take an \"enum object_type\"\n> 12:  64dc9364ba = 15:  e2b0964228 tree-walk.h users: migrate \"p->mode &&\" pattern\n> 13:  93ed3edbbd = 16:  29dbc4292e tree-walk.h users: refactor chained \"mode\" if/else into switch\n> 14:  7aa48aa34c = 17:  ada6d05176 tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n> 15:  3ae81621dc = 18:  01860daa55 merge-tree tests: test for the mode comparison in same_entry()\n> 16:  4249ad5c4d = 19:  e4be48fb50 merge-ort: correct reference to test in 62fdec17a11\n> 17:  e5e17505dd = 20:  189d2550fb fsck.c: switch on \"object_type\" in fsck_walk_tree()\n> 18:  3f0b884f1f = 21:  3b7b12e6f7 tree-walk.h users: use temporary variable(s) for \"mode\"\n> 19:  174167613b = 22:  6c3a07a327 tree-walk.h API: formatting changes for subsequent commit\n> 20:  ec76db613f = 23:  879eb3da2a tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n> 21:  11e3494172 ! 24:  d2fa360ab9 tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n>     @@ Metadata\n>      Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n>       ## Commit message ##\n>     -    tree-walk.h API users: use \"tmp\" for mode in shift_tree_by()\n>     +    match-trees: use \"tmp\" for mode in shift_tree_by()\n>\n>          Refactor code added in 85e51b783c3 (Make \"subtree\" part more\n>          orthogonal to the rest of merge-recursive., 2008-06-30) to make it\n> 22:  b31c106557 = 25:  cc50cfcf51 tree-walk.h API: add get_tree_entry_type()\n> 23:  304d5d4d1a = 26:  f642a35482 tree-walk.h API: document and format tree_entry_extract()\n> 24:  346453df35 = 27:  a0bcb59fa5 tree-entry.h API: rename tree_entry_extract() to tree_entry_extract_mode()\n> 25:  dd012b661e = 28:  c7d4ba7734 tree-walk.h API: add a tree_entry_extract_all() function\n> 26:  b6ee8410e3 = 29:  1d5421d67a tree-walk.h API: add get_tree_entry_all()\n> 27:  5c98afd9e7 ! 30:  a3e1063ac4 tree-walk.h API: add a get_tree_entry_path() function\n>     @@ Commit message\n>          need neither the mode nor \"enum object_type\" parameters filled for\n>          them.\n>\n>     -    There's callers here which doesn't need the \"struct object_id\" filled\n>     -    either, and provides a throwaway variable for us.\n>     +    There are callers here which don't need the \"struct object_id\" filled;\n>     +    forcing callers to pass one just requires they create a throwaway\n>     +    variable.\n>\n>          See the following commits for the introduction of such code that's\n>          being modified here:\n>\n>     -     - shift_tree(): 68faf68938e (A new merge stragety 'subtree'.,\n>     +     - shift_tree(): 68faf68938e (A new merge stragety[sic] 'subtree'.,\n>              2007-02-15) for the shift_tree()\n>\n>           - tree_has_path(): 96e7ffbdc31 (merge-recursive: check for directory\n> 28:  3e7e0f7eb8 ! 31:  da3dd6dd53 blame: emit a better error on 'git blame directory'\n>     @@ Commit message\n>\n>              fatal: no such path 't' in HEAD\n>\n>     +    The main point of this test is to assert that we're not doing\n>     +    something uniquely bad when in a conflicted merge. See\n>     +    cd8ae20195 (git-blame shouldn't crash if run in an unmerged tree,\n>     +    2007-10-18) and 9aeaab6811 (blame: allow \"blame file\" in the middle of\n>     +    a conflicted merge, 2012-09-11) for the bug the t8004 test was\n>     +    originally meant to address.\n>     +\n>     +    But when extending it let's grep out the specific error message for\n>     +    good measure. Having to change it in the future (e.g. as part of my\n>     +    parallel series to improve such 'OID does not match type' messages) is\n>     +    a small price for ensuring it doesn't regress.\n>     +\n>          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n>       ## blame.c ##\n>     @@ t/t8004-blame-with-conflicts.sh: test_expect_success 'blame does not crash with\n>      +  test_commit second &&\n>      +  test_commit d/file &&\n>      +  test_must_fail git blame d 2>expected &&\n>     ++  grep \"unsupported file type d\" expected &&\n>      +\n>      +  git reset --hard second &&\n>      +  >d &&\n> 29:  ac1ccf1357 = 32:  80e5cb0b30 tree-walk.h API: add a tree_entry_extract_type() function\n> --\n> 2.31.0.256.gf0ddda3145\n>\n"},{"id":"419484","messageId":"xmqqpmzxy939.fsf@gitster.g","threadId":"18927","inReplyTo":"20210315234344.28427-1-avarab@gmail.com","subject":"Re: [PATCH v3 0/9] read_tree() and read_tree_recursive() refactoring","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-17T17:38:02Z","receivedAt":"2021-03-17T17:38:58Z","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> A v3 of a refactoring of tree.c. See v2 at\n> https://lore.kernel.org/git/20210308022138.28166-1-avarab@gmail.com/\n>\n> This brings back pretty much the old read_tree_recursive() under the\n> name read_tree_at() as suggested in\n> https://lore.kernel.org/git/xmqqft106sok.fsf@gitster.g/\n>\n> Ævar Arnfjörð Bjarmason (9):\n>   ls-files tests: add meaningful --with-tree tests\n>   tree.c API: move read_tree() into builtin/ls-files.c\n>   ls-files: don't needlessly pass around stage variable\n>   ls-files: refactor away read_tree()\n>   tree.h API: remove support for starting at prefix != \"\"\n>   tree.h API: remove \"stage\" parameter from read_tree_recursive()\n>   tree.h API: rename read_tree_recursive() to read_tree()\n>   show tests: add test for \"git show <tree>\"\n>   tree.h API: expose read_tree_1() as read_tree_at()\n\nThe end-result looks good, but it seeps through the structure of the\nseries that the steps #7-#9 are afterthought of fixing a mistake\nmade in the step #5.  If #4 refactors away and removes read_tree()\nand makes the name available, a natural next step would be to\nintroduce a thin-wrapper read_tree() around read_tree_recursive(),\nwhich is to be used by majority of callers that do not need\nnon-empty prefix and stage, and that would become step #5 if I were\ndoing the series.  That step makes the codebase ready to change the\ncallers of read_tree_recursive() to call the simpler read_tree(),\nwhich would be step #6.  The read_tree_recursive() would only have\none caller (i.e. the new read_tree() which is a castrated version of\nread_tree_recursive()) and renaming it to read_tree_at() would be\nanother simple step #7.\n\n>  4:  e78d1810b89 =  4:  1c96d5d3611 ls-files: refactor away read_tree()\n>  5:  05eecdd7519 !  5:  367cb99224b tree.h API: remove support for starting at prefix != \"\"\n>     @@ Commit message\n>          ffd31f661d5 (Reimplement read_tree_recursive() using\n>          tree_entry_interesting(), 2011-03-25).\n>      \n>     -    If in the future we need to support recursively reading trees without\n>     -    starting at the root we can easily add a read_tree_recursive_subdir(),\n>     -    and make that function a thin wrapper for read_tree_1().\n>     +    As it turns out (Murphy's law and all) we're just about to gain a new\n>     +    in-tree user that would need this parameter[1]. Let's remove it anyway\n>     +    as the common case is going to be to not supply it, A later commit\n>     +    will bring back this functionality in different form.\n>      \n>     -    In the meantime there's no reason to keep around what amounts to dead\n>     -    code, just in case we need it in the future.\n>     +    1. https://lore.kernel.org/git/xmqqft106sok.fsf@gitster.g/\n>      \n>          Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>      \n>  6:  fcecc82e1c8 =  6:  38e36780e22 tree.h API: remove \"stage\" parameter from read_tree_recursive()\n>  -:  ----------- >  7:  859902ffd83 tree.h API: rename read_tree_recursive() to read_tree()\n>  -:  ----------- >  8:  a63c9b49f13 show tests: add test for \"git show <tree>\"\n>  -:  ----------- >  9:  570642c8625 tree.h API: expose read_tree_1() as read_tree_at()\n"},{"id":"419783","messageId":"f37d7705cf091def00ab94b1be6d6daecc220229.1616279653.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616279653.git.avarab@gmail.com","subject":"[PATCH v5 2/8] ls-files tests: add meaningful --with-tree tests","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-20T22:37:45Z","receivedAt":"2021-03-20T22:39:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add tests for \"ls-files --with-tree\". There was effectively no\ncoverage for any normal usage of this command, only the tests added in\n54e1abce90e (Add test case for ls-files --with-tree, 2007-10-03) for\nan obscure bug.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t3060-ls-files-with-tree.sh | 41 +++++++++++++++++++++++++++++++++++\n 1 file changed, 41 insertions(+)\n\ndiff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh\nindex 52ed665fcd2..b257c792a46 100755\n--- a/t/t3060-ls-files-with-tree.sh\n+++ b/t/t3060-ls-files-with-tree.sh\n@@ -47,6 +47,12 @@ test_expect_success setup '\n \tgit add .\n '\n \n+test_expect_success 'usage' '\n+\ttest_expect_code 128 git ls-files --with-tree=HEAD -u &&\n+\ttest_expect_code 128 git ls-files --with-tree=HEAD -s &&\n+\ttest_expect_code 128 git ls-files --recurse-submodules --with-tree=HEAD\n+'\n+\n test_expect_success 'git ls-files --with-tree should succeed from subdir' '\n \t# We have to run from a sub-directory to trigger prune_path\n \t# Then we finally get to run our --with-tree test\n@@ -60,4 +66,39 @@ test_expect_success \\\n     'git ls-files --with-tree should add entries from named tree.' \\\n     'test_cmp expected output'\n \n+test_expect_success 'no duplicates in --with-tree output' '\n+\tgit ls-files --with-tree=HEAD >actual &&\n+\tsort -u actual >expected &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'setup: output in a conflict' '\n+\ttest_create_repo conflict &&\n+\ttest_commit -C conflict BASE file &&\n+\ttest_commit -C conflict A file foo &&\n+\tgit -C conflict reset --hard BASE &&\n+\ttest_commit -C conflict B file bar\n+'\n+\n+test_expect_success 'output in a conflict' '\n+\ttest_must_fail git -C conflict merge A B &&\n+\tcat >expected <<-\\EOF &&\n+\tfile\n+\tfile\n+\tfile\n+\tfile\n+\tEOF\n+\tgit -C conflict ls-files --with-tree=HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'output with removed .git/index' '\n+\tcat >expected <<-\\EOF &&\n+\tfile\n+\tEOF\n+\trm conflict/.git/index &&\n+\tgit -C conflict ls-files --with-tree=HEAD >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419784","messageId":"cover.1616279653.git.avarab@gmail.com","threadId":"18927","inReplyTo":"xmqqpmzxy939.fsf@gitster.g","subject":"[PATCH v5 0/8] read_tree() and read_tree_recursive() refactoring","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-20T22:37:43Z","receivedAt":"2021-03-20T22:39:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"A refactoring of v4[1], not quite as as suggested by Junio in[2], but\nwhich I think makes this whole thing less confusing and more\nstraightforward.\n\nI now start by entirely getting rid of the meaningful passing of the\n\"stage\" variable before removing it and the path variable entirely in\n8/8, but before that in 7/8 I've introduced the read_tree_at()\nfunction Derrick Stolee needs for his in-flight series.\n\n1. http://lore.kernel.org/git/20210316155244.28328-1-avarab@gmail.com\n2. https://lore.kernel.org/git/xmqqpmzxy939.fsf@gitster.g/\n\nÆvar Arnfjörð Bjarmason (8):\n  show tests: add test for \"git show <tree>\"\n  ls-files tests: add meaningful --with-tree tests\n  tree.c API: move read_tree() into builtin/ls-files.c\n  ls-files: don't needlessly pass around stage variable\n  ls-files: refactor away read_tree()\n  archive: stop passing \"stage\" through read_tree_recursive()\n  tree.h API: expose read_tree_1() as read_tree_at()\n  tree.h API: simplify read_tree_recursive() signature\n\n archive.c                     |  30 ++++-----\n builtin/checkout.c            |   8 +--\n builtin/log.c                 |   8 +--\n builtin/ls-files.c            |  75 ++++++++++++++++++++-\n builtin/ls-tree.c             |   6 +-\n cache.h                       |   2 +-\n merge-recursive.c             |   6 +-\n t/t3060-ls-files-with-tree.sh |  41 ++++++++++++\n t/t7007-show.sh               |  39 +++++++++++\n tree.c                        | 119 ++++------------------------------\n tree.h                        |  18 ++---\n 11 files changed, 205 insertions(+), 147 deletions(-)\n\nRange-diff:\n 8:  8a6bebde234 !  1:  c307eb53331 show tests: add test for \"git show <tree>\"\n    @@ Commit message\n     \n         Let's add this common mode of operation to the \"show\" tests\n         themselves. It's more obvious, and the tests in\n    -    t7701-repack-unpack-unreachable.sh happily parse if we start buggily\n    +    t7701-repack-unpack-unreachable.sh happily pass if we start buggily\n         emitting trees recursively.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n 1:  b338f2c01a4 =  2:  f37d7705cf0 ls-files tests: add meaningful --with-tree tests\n 2:  4578b83944c =  3:  6291d8a1b5e tree.c API: move read_tree() into builtin/ls-files.c\n 3:  33656ff63b8 !  4:  466b518e915 ls-files: don't needlessly pass around stage variable\n    @@ Commit message\n         Now that read_tree() has been moved to ls-files.c we can get rid of\n         the stage != 1 case that'll never happen.\n     \n    +    Let's not use read_tree_recursive() as a pass-through to pass \"stage =\n    +    1\" either. For now we'll pass an unused \"stage = 0\" for consistency\n    +    with other read_tree_recursive() callers, that argument will be\n    +    removed in a follow-up commit.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/ls-files.c ##\n    +@@ builtin/ls-files.c: static int read_one_entry_opt(struct index_state *istate,\n    + \t\t\t      const struct object_id *oid,\n    + \t\t\t      const char *base, int baselen,\n    + \t\t\t      const char *pathname,\n    +-\t\t\t      unsigned mode, int stage, int opt)\n    ++\t\t\t      unsigned mode, int opt)\n    + {\n    + \tint len;\n    + \tstruct cache_entry *ce;\n    +@@ builtin/ls-files.c: static int read_one_entry_opt(struct index_state *istate,\n    + \tce = make_empty_cache_entry(istate, baselen + len);\n    + \n    + \tce->ce_mode = create_ce_mode(mode);\n    +-\tce->ce_flags = create_ce_flags(stage);\n    ++\tce->ce_flags = create_ce_flags(1);\n    + \tce->ce_namelen = baselen + len;\n    + \tmemcpy(ce->name, base, baselen);\n    + \tmemcpy(ce->name + baselen, pathname, len+1);\n    +@@ builtin/ls-files.c: static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n    + {\n    + \tstruct index_state *istate = context;\n    + \treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n    +-\t\t\t\t  mode, stage,\n    ++\t\t\t\t  mode,\n    + \t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n    + }\n    + \n     @@ builtin/ls-files.c: static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base\n    + {\n    + \tstruct index_state *istate = context;\n    + \treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n    +-\t\t\t\t  mode, stage,\n    ++\t\t\t\t  mode,\n    + \t\t\t\t  ADD_CACHE_JUST_APPEND);\n      }\n      \n      \n    @@ builtin/ls-files.c: static int read_tree(struct repository *r, struct tree *tree\n      \tif (!fn)\n      \t\tfn = read_one_entry_quick;\n     -\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n    -+\terr = read_tree_recursive(r, tree, \"\", 0, 1, match, fn, istate);\n    ++\terr = read_tree_recursive(r, tree, \"\", 0, 0, match, fn, istate);\n      \tif (fn == read_one_entry || err)\n      \t\treturn err;\n      \n 4:  1c96d5d3611 !  5:  30c3abfe9b9 ls-files: refactor away read_tree()\n    @@ builtin/ls-files.c: static int get_common_prefix_len(const char *common_prefix)\n     -\t\t\t      const char *base, int baselen,\n     +\t\t\t      struct strbuf *base,\n      \t\t\t      const char *pathname,\n    - \t\t\t      unsigned mode, int stage, int opt)\n    + \t\t\t      unsigned mode, int opt)\n      {\n     @@ builtin/ls-files.c: static int read_one_entry_opt(struct index_state *istate,\n      \t\treturn READ_TREE_RECURSIVE;\n    @@ builtin/ls-files.c: static int read_one_entry_opt(struct index_state *istate,\n     +\tce = make_empty_cache_entry(istate, base->len + len);\n      \n      \tce->ce_mode = create_ce_mode(mode);\n    - \tce->ce_flags = create_ce_flags(stage);\n    + \tce->ce_flags = create_ce_flags(1);\n     -\tce->ce_namelen = baselen + len;\n     -\tmemcpy(ce->name, base, baselen);\n     -\tmemcpy(ce->name + baselen, pathname, len+1);\n    @@ builtin/ls-files.c: static int read_one_entry(const struct object_id *oid, struc\n      \tstruct index_state *istate = context;\n     -\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n     +\treturn read_one_entry_opt(istate, oid, base, pathname,\n    - \t\t\t\t  mode, stage,\n    + \t\t\t\t  mode,\n      \t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n      }\n     @@ builtin/ls-files.c: static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base\n    @@ builtin/ls-files.c: static int read_one_entry_quick(const struct object_id *oid,\n      {\n      \tstruct index_state *istate = context;\n     -\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n    -+\treturn read_one_entry_opt(istate, oid, base, pathname,\n    - \t\t\t\t  mode, stage,\n    - \t\t\t\t  ADD_CACHE_JUST_APPEND);\n    - }\n    - \n    +-\t\t\t\t  mode,\n    +-\t\t\t\t  ADD_CACHE_JUST_APPEND);\n    +-}\n    +-\n     -\n     -static int read_tree(struct repository *r, struct tree *tree,\n     -\t\t     struct pathspec *match, struct index_state *istate)\n    @@ builtin/ls-files.c: static int read_one_entry_quick(const struct object_id *oid,\n     -\n     -\tif (!fn)\n     -\t\tfn = read_one_entry_quick;\n    --\terr = read_tree_recursive(r, tree, \"\", 0, 1, match, fn, istate);\n    +-\terr = read_tree_recursive(r, tree, \"\", 0, 0, match, fn, istate);\n     -\tif (fn == read_one_entry || err)\n     -\t\treturn err;\n     -\n    @@ builtin/ls-files.c: static int read_one_entry_quick(const struct object_id *oid,\n     -\tcache_tree_free(&istate->cache_tree);\n     -\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n     -\treturn 0;\n    --}\n    --\n    ++\treturn read_one_entry_opt(istate, oid, base, pathname,\n    ++\t\t\t\t  mode, ADD_CACHE_JUST_APPEND);\n    + }\n    + \n      /*\n    -  * Read the tree specified with --with-tree option\n    -  * (typically, HEAD) into stage #1 and then\n     @@ builtin/ls-files.c: void overlay_tree_on_index(struct index_state *istate,\n      \tstruct pathspec pathspec;\n      \tstruct cache_entry *last_stage0 = NULL;\n 5:  367cb99224b <  -:  ----------- tree.h API: remove support for starting at prefix != \"\"\n 6:  38e36780e22 <  -:  ----------- tree.h API: remove \"stage\" parameter from read_tree_recursive()\n 7:  859902ffd83 <  -:  ----------- tree.h API: rename read_tree_recursive() to read_tree()\n -:  ----------- >  6:  02c42be9249 archive: stop passing \"stage\" through read_tree_recursive()\n 9:  29996dd82bc !  7:  d55e8d4042b tree.h API: expose read_tree_1() as read_tree_at()\n    @@ Metadata\n      ## Commit message ##\n         tree.h API: expose read_tree_1() as read_tree_at()\n     \n    -    Rename the static read_tree_1() function to read_tree_at(). This will\n    -    allow for the old read_tree_recursive() mode of operation where we\n    -    start at a given path instead of \"\".\n    +    Rename the static read_tree_1() function to read_tree_at(). This\n    +    function works just like read_tree_recursive(), except you provide\n    +    your own strbuf.\n     \n    -    See [1] for the discussion of one such future in-tree user, unlike the\n    -    old read_tree_recursive() this function takes a strbuf. Since that's\n    -    what read_tree_1() used internally this should allow us to avoid\n    -    casting and/or reallocations in the future.\n    +    This step doesn't make much sense now, but in follow-up commits I'll\n    +    remove the base/baselen/stage arguments to read_tree_recursive(). At\n    +    that point an anticipated in-tree user[1] for the old\n    +    read_tree_recursive() couldn't provide a path to start the\n    +    traversal.\n     \n    -    1. https://lore.kernel.org/git/xmqqft106sok.fsf@gitster.g/#t\n    +    Let's give them a function to do so with an API that makes more sense\n    +    for them, by taking a strbuf we should be able to avoid more casting\n    +    and/or reallocations in the future.\n    +\n    +    1. https://lore.kernel.org/git/xmqqft106sok.fsf@gitster.g\n     \n      ## tree.c ##\n     @@\n    @@ tree.c\n      \n     -static int read_tree_1(struct repository *r,\n     -\t\t       struct tree *tree, struct strbuf *base,\n    --\t\t       const struct pathspec *pathspec,\n    +-\t\t       int stage, const struct pathspec *pathspec,\n     -\t\t       read_tree_fn_t fn, void *context)\n     +int read_tree_at(struct repository *r,\n     +\t\t struct tree *tree, struct strbuf *base,\n    ++\t\t int stage,\n     +\t\t const struct pathspec *pathspec,\n     +\t\t read_tree_fn_t fn, void *context)\n      {\n    @@ tree.c: static int read_tree_1(struct repository *r,\n      \t\tstrbuf_add(base, entry.path, len);\n      \t\tstrbuf_addch(base, '/');\n     -\t\tretval = read_tree_1(r, lookup_tree(r, &oid),\n    --\t\t\t\t     base, pathspec,\n    +-\t\t\t\t     base, stage, pathspec,\n     -\t\t\t\t     fn, context);\n     +\t\tretval = read_tree_at(r, lookup_tree(r, &oid),\n    -+\t\t\t\t      base, pathspec,\n    ++\t\t\t\t      base, stage, pathspec,\n     +\t\t\t\t      fn, context);\n      \t\tstrbuf_setlen(base, oldlen);\n      \t\tif (retval)\n      \t\t\treturn -1;\n    -@@ tree.c: int read_tree(struct repository *r,\n    - \tstruct strbuf sb = STRBUF_INIT;\n    +@@ tree.c: int read_tree_recursive(struct repository *r,\n      \tint ret;\n      \n    --\tret = read_tree_1(r, tree, &sb, pathspec, fn, context);\n    -+\tret = read_tree_at(r, tree, &sb, pathspec, fn, context);\n    + \tstrbuf_add(&sb, base, baselen);\n    +-\tret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context);\n    ++\tret = read_tree_at(r, tree, &sb, stage, pathspec, fn, context);\n      \tstrbuf_release(&sb);\n      \treturn ret;\n      }\n    @@ tree.c: int read_tree(struct repository *r,\n      ## tree.h ##\n     @@ tree.h: int cmp_cache_name_compare(const void *a_, const void *b_);\n      #define READ_TREE_RECURSIVE 1\n    - typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n    + typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, int, void *);\n      \n     +int read_tree_at(struct repository *r,\n    -+\t\t struct tree *tree,\n    -+\t\t struct strbuf *at,\n    ++\t\t struct tree *tree, struct strbuf *base,\n    ++\t\t int stage,\n     +\t\t const struct pathspec *pathspec,\n     +\t\t read_tree_fn_t fn, void *context);\n     +\n    - int read_tree(struct repository *r,\n    - \t      struct tree *tree,\n    - \t      const struct pathspec *pathspec,\n    + int read_tree_recursive(struct repository *r,\n    + \t\t\tstruct tree *tree,\n    + \t\t\tconst char *base, int baselen,\n -:  ----------- >  8:  fa157d8baee tree.h API: simplify read_tree_recursive() signature\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419785","messageId":"c307eb53331c3f9c6132aa8c1a493dd5d37ec324.1616279653.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616279653.git.avarab@gmail.com","subject":"[PATCH v5 1/8] show tests: add test for \"git show <tree>\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-20T22:37:44Z","receivedAt":"2021-03-20T22:39:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add missing tests for showing a tree with \"git show\". Let's test for\nshowing a tree, two trees, and that doing so doesn't recurse.\n\nThe only tests for this code added in 5d7eeee2ac6 (git-show: grok\nblobs, trees and tags, too, 2006-12-14) were the tests in\nt7701-repack-unpack-unreachable.sh added in ccc1297226b (repack:\nmodify behavior of -A option to leave unreferenced objects unpacked,\n2008-05-09).\n\nLet's add this common mode of operation to the \"show\" tests\nthemselves. It's more obvious, and the tests in\nt7701-repack-unpack-unreachable.sh happily pass if we start buggily\nemitting trees recursively.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t7007-show.sh | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/t/t7007-show.sh b/t/t7007-show.sh\nindex 42d3db62468..d6cc69e0f2c 100755\n--- a/t/t7007-show.sh\n+++ b/t/t7007-show.sh\n@@ -38,6 +38,45 @@ test_expect_success 'showing two commits' '\n \ttest_cmp expect actual.filtered\n '\n \n+test_expect_success 'showing a tree' '\n+\tcat >expected <<-EOF &&\n+\ttree main1:\n+\n+\tmain1.t\n+\tEOF\n+\tgit show main1: >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'showing two trees' '\n+\tcat >expected <<-EOF &&\n+\ttree main1^{tree}\n+\n+\tmain1.t\n+\n+\ttree main2^{tree}\n+\n+\tmain1.t\n+\tmain2.t\n+\tEOF\n+\tgit show main1^{tree} main2^{tree} >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'showing a trees is not recursive' '\n+\tgit worktree add not-recursive main1 &&\n+\tmkdir not-recursive/a &&\n+\ttest_commit -C not-recursive a/file &&\n+\tcat >expected <<-EOF &&\n+\ttree HEAD^{tree}\n+\n+\ta/\n+\tmain1.t\n+\tEOF\n+\tgit -C not-recursive show HEAD^{tree} >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'showing a range walks (linear)' '\n \tcat >expect <<-EOF &&\n \tcommit $(git rev-parse main3)\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419786","messageId":"466b518e915c6cf5bc1f61610a0a4ee41e875adb.1616279653.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616279653.git.avarab@gmail.com","subject":"[PATCH v5 4/8] ls-files: don't needlessly pass around stage variable","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-20T22:37:47Z","receivedAt":"2021-03-20T22:39:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that read_tree() has been moved to ls-files.c we can get rid of\nthe stage != 1 case that'll never happen.\n\nLet's not use read_tree_recursive() as a pass-through to pass \"stage =\n1\" either. For now we'll pass an unused \"stage = 0\" for consistency\nwith other read_tree_recursive() callers, that argument will be\nremoved in a follow-up commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 25 ++++++++-----------------\n 1 file changed, 8 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex a4458622813..3149a2769a3 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -425,7 +425,7 @@ static int read_one_entry_opt(struct index_state *istate,\n \t\t\t      const struct object_id *oid,\n \t\t\t      const char *base, int baselen,\n \t\t\t      const char *pathname,\n-\t\t\t      unsigned mode, int stage, int opt)\n+\t\t\t      unsigned mode, int opt)\n {\n \tint len;\n \tstruct cache_entry *ce;\n@@ -437,7 +437,7 @@ static int read_one_entry_opt(struct index_state *istate,\n \tce = make_empty_cache_entry(istate, baselen + len);\n \n \tce->ce_mode = create_ce_mode(mode);\n-\tce->ce_flags = create_ce_flags(stage);\n+\tce->ce_flags = create_ce_flags(1);\n \tce->ce_namelen = baselen + len;\n \tmemcpy(ce->name, base, baselen);\n \tmemcpy(ce->name + baselen, pathname, len+1);\n@@ -451,7 +451,7 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n {\n \tstruct index_state *istate = context;\n \treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode, stage,\n+\t\t\t\t  mode,\n \t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n }\n \n@@ -465,26 +465,17 @@ static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base\n {\n \tstruct index_state *istate = context;\n \treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode, stage,\n+\t\t\t\t  mode,\n \t\t\t\t  ADD_CACHE_JUST_APPEND);\n }\n \n \n-static int read_tree(struct repository *r, struct tree *tree, int stage,\n+static int read_tree(struct repository *r, struct tree *tree,\n \t\t     struct pathspec *match, struct index_state *istate)\n {\n \tread_tree_fn_t fn = NULL;\n \tint i, err;\n \n-\t/*\n-\t * Currently the only existing callers of this function all\n-\t * call it with stage=1 and after making sure there is nothing\n-\t * at that stage; we could always use read_one_entry_quick().\n-\t *\n-\t * But when we decide to straighten out git-read-tree not to\n-\t * use unpack_trees() in some cases, this will probably start\n-\t * to matter.\n-\t */\n \n \t/*\n \t * See if we have cache entry at the stage.  If so,\n@@ -493,13 +484,13 @@ static int read_tree(struct repository *r, struct tree *tree, int stage,\n \t */\n \tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n \t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == stage)\n+\t\tif (ce_stage(ce) == 1)\n \t\t\tfn = read_one_entry;\n \t}\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n+\terr = read_tree_recursive(r, tree, \"\", 0, 0, match, fn, istate);\n \tif (fn == read_one_entry || err)\n \t\treturn err;\n \n@@ -549,7 +540,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n \t} else\n \t\tmemset(&pathspec, 0, sizeof(pathspec));\n-\tif (read_tree(the_repository, tree, 1, &pathspec, istate))\n+\tif (read_tree(the_repository, tree, &pathspec, istate))\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \n \tfor (i = 0; i < istate->cache_nr; i++) {\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419787","messageId":"6291d8a1b5ebb537d3842713f22f038125f57b38.1616279653.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616279653.git.avarab@gmail.com","subject":"[PATCH v5 3/8] tree.c API: move read_tree() into builtin/ls-files.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-20T22:37:46Z","receivedAt":"2021-03-20T22:39:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since the read_tree() API was added around the same time as\nread_tree_recursive() in 94537c78a82 (Move \"read_tree()\" to\n\"tree.c\"[...], 2005-04-22) and b12ec373b8e ([PATCH] Teach read-tree\nabout commit objects, 2005-04-20) things have gradually migrated over\nto the read_tree_recursive() version.\n\nNow builtin/ls-files.c is the last user of this code, let's move all\nthe relevant code there. This allows for subsequent simplification of\nit, and an eventual move to read_tree_recursive().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++\n cache.h            |  2 +-\n tree.c             | 89 ---------------------------------------------\n tree.h             |  5 ---\n 4 files changed, 92 insertions(+), 95 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex f6f9e483b27..a4458622813 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -12,6 +12,7 @@\n #include \"dir.h\"\n #include \"builtin.h\"\n #include \"tree.h\"\n+#include \"cache-tree.h\"\n #include \"parse-options.h\"\n #include \"resolve-undo.h\"\n #include \"string-list.h\"\n@@ -420,6 +421,96 @@ static int get_common_prefix_len(const char *common_prefix)\n \treturn common_prefix_len;\n }\n \n+static int read_one_entry_opt(struct index_state *istate,\n+\t\t\t      const struct object_id *oid,\n+\t\t\t      const char *base, int baselen,\n+\t\t\t      const char *pathname,\n+\t\t\t      unsigned mode, int stage, int opt)\n+{\n+\tint len;\n+\tstruct cache_entry *ce;\n+\n+\tif (S_ISDIR(mode))\n+\t\treturn READ_TREE_RECURSIVE;\n+\n+\tlen = strlen(pathname);\n+\tce = make_empty_cache_entry(istate, baselen + len);\n+\n+\tce->ce_mode = create_ce_mode(mode);\n+\tce->ce_flags = create_ce_flags(stage);\n+\tce->ce_namelen = baselen + len;\n+\tmemcpy(ce->name, base, baselen);\n+\tmemcpy(ce->name + baselen, pathname, len+1);\n+\toidcpy(&ce->oid, oid);\n+\treturn add_index_entry(istate, ce, opt);\n+}\n+\n+static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n+\t\t\t  const char *pathname, unsigned mode, int stage,\n+\t\t\t  void *context)\n+{\n+\tstruct index_state *istate = context;\n+\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\t\t\t\t  mode, stage,\n+\t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n+}\n+\n+/*\n+ * This is used when the caller knows there is no existing entries at\n+ * the stage that will conflict with the entry being added.\n+ */\n+static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n+\t\t\t\tconst char *pathname, unsigned mode, int stage,\n+\t\t\t\tvoid *context)\n+{\n+\tstruct index_state *istate = context;\n+\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\t\t\t\t  mode, stage,\n+\t\t\t\t  ADD_CACHE_JUST_APPEND);\n+}\n+\n+\n+static int read_tree(struct repository *r, struct tree *tree, int stage,\n+\t\t     struct pathspec *match, struct index_state *istate)\n+{\n+\tread_tree_fn_t fn = NULL;\n+\tint i, err;\n+\n+\t/*\n+\t * Currently the only existing callers of this function all\n+\t * call it with stage=1 and after making sure there is nothing\n+\t * at that stage; we could always use read_one_entry_quick().\n+\t *\n+\t * But when we decide to straighten out git-read-tree not to\n+\t * use unpack_trees() in some cases, this will probably start\n+\t * to matter.\n+\t */\n+\n+\t/*\n+\t * See if we have cache entry at the stage.  If so,\n+\t * do it the original slow way, otherwise, append and then\n+\t * sort at the end.\n+\t */\n+\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n+\t\tconst struct cache_entry *ce = istate->cache[i];\n+\t\tif (ce_stage(ce) == stage)\n+\t\t\tfn = read_one_entry;\n+\t}\n+\n+\tif (!fn)\n+\t\tfn = read_one_entry_quick;\n+\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n+\tif (fn == read_one_entry || err)\n+\t\treturn err;\n+\n+\t/*\n+\t * Sort the cache entry -- we need to nuke the cache tree, though.\n+\t */\n+\tcache_tree_free(&istate->cache_tree);\n+\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n+\treturn 0;\n+}\n+\n /*\n  * Read the tree specified with --with-tree option\n  * (typically, HEAD) into stage #1 and then\ndiff --git a/cache.h b/cache.h\nindex 6fda8091f11..c2f8a8eadf6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -803,7 +803,7 @@ static inline int index_pos_to_insert_pos(uintmax_t pos)\n #define ADD_CACHE_OK_TO_ADD 1\t\t/* Ok to add */\n #define ADD_CACHE_OK_TO_REPLACE 2\t/* Ok to replace file/directory */\n #define ADD_CACHE_SKIP_DFCHECK 4\t/* Ok to skip DF conflict checks */\n-#define ADD_CACHE_JUST_APPEND 8\t\t/* Append only; tree.c::read_tree() */\n+#define ADD_CACHE_JUST_APPEND 8\t\t/* Append only */\n #define ADD_CACHE_NEW_ONLY 16\t\t/* Do not replace existing ones */\n #define ADD_CACHE_KEEP_CACHE_TREE 32\t/* Do not invalidate cache-tree */\n #define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */\ndiff --git a/tree.c b/tree.c\nindex a52479812ce..a6c12f2745a 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -11,54 +11,6 @@\n \n const char *tree_type = \"tree\";\n \n-static int read_one_entry_opt(struct index_state *istate,\n-\t\t\t      const struct object_id *oid,\n-\t\t\t      const char *base, int baselen,\n-\t\t\t      const char *pathname,\n-\t\t\t      unsigned mode, int stage, int opt)\n-{\n-\tint len;\n-\tstruct cache_entry *ce;\n-\n-\tif (S_ISDIR(mode))\n-\t\treturn READ_TREE_RECURSIVE;\n-\n-\tlen = strlen(pathname);\n-\tce = make_empty_cache_entry(istate, baselen + len);\n-\n-\tce->ce_mode = create_ce_mode(mode);\n-\tce->ce_flags = create_ce_flags(stage);\n-\tce->ce_namelen = baselen + len;\n-\tmemcpy(ce->name, base, baselen);\n-\tmemcpy(ce->name + baselen, pathname, len+1);\n-\toidcpy(&ce->oid, oid);\n-\treturn add_index_entry(istate, ce, opt);\n-}\n-\n-static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\t  const char *pathname, unsigned mode, int stage,\n-\t\t\t  void *context)\n-{\n-\tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode, stage,\n-\t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n-}\n-\n-/*\n- * This is used when the caller knows there is no existing entries at\n- * the stage that will conflict with the entry being added.\n- */\n-static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n-\t\t\t\tconst char *pathname, unsigned mode, int stage,\n-\t\t\t\tvoid *context)\n-{\n-\tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode, stage,\n-\t\t\t\t  ADD_CACHE_JUST_APPEND);\n-}\n-\n static int read_tree_1(struct repository *r,\n \t\t       struct tree *tree, struct strbuf *base,\n \t\t       int stage, const struct pathspec *pathspec,\n@@ -154,47 +106,6 @@ int cmp_cache_name_compare(const void *a_, const void *b_)\n \t\t\t\t  ce2->name, ce2->ce_namelen, ce_stage(ce2));\n }\n \n-int read_tree(struct repository *r, struct tree *tree, int stage,\n-\t      struct pathspec *match, struct index_state *istate)\n-{\n-\tread_tree_fn_t fn = NULL;\n-\tint i, err;\n-\n-\t/*\n-\t * Currently the only existing callers of this function all\n-\t * call it with stage=1 and after making sure there is nothing\n-\t * at that stage; we could always use read_one_entry_quick().\n-\t *\n-\t * But when we decide to straighten out git-read-tree not to\n-\t * use unpack_trees() in some cases, this will probably start\n-\t * to matter.\n-\t */\n-\n-\t/*\n-\t * See if we have cache entry at the stage.  If so,\n-\t * do it the original slow way, otherwise, append and then\n-\t * sort at the end.\n-\t */\n-\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n-\t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == stage)\n-\t\t\tfn = read_one_entry;\n-\t}\n-\n-\tif (!fn)\n-\t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, stage, match, fn, istate);\n-\tif (fn == read_one_entry || err)\n-\t\treturn err;\n-\n-\t/*\n-\t * Sort the cache entry -- we need to nuke the cache tree, though.\n-\t */\n-\tcache_tree_free(&istate->cache_tree);\n-\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n-\treturn 0;\n-}\n-\n struct tree *lookup_tree(struct repository *r, const struct object_id *oid)\n {\n \tstruct object *obj = lookup_object(r, oid);\ndiff --git a/tree.h b/tree.h\nindex 3eb0484cbf2..6b0b1dc211a 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -38,9 +38,4 @@ int read_tree_recursive(struct repository *r,\n \t\t\tconst char *base, int baselen,\n \t\t\tint stage, const struct pathspec *pathspec,\n \t\t\tread_tree_fn_t fn, void *context);\n-\n-int read_tree(struct repository *r, struct tree *tree,\n-\t      int stage, struct pathspec *pathspec,\n-\t      struct index_state *istate);\n-\n #endif /* TREE_H */\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419788","messageId":"30c3abfe9b9af8202fb69bb67267cbbd5d09b60a.1616279653.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616279653.git.avarab@gmail.com","subject":"[PATCH v5 5/8] ls-files: refactor away read_tree()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-20T22:37:48Z","receivedAt":"2021-03-20T22:39:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor away the read_tree() function into its only user,\noverlay_tree_on_index().\n\nFirst, change read_one_entry_opt() to use the strbuf parameter\nread_tree_recursive() passes down in place. This finishes up a partial\nrefactoring started in 6a0b0b6de99 (tree.c: update read_tree_recursive\ncallback to pass strbuf as base, 2014-11-30).\n\nMoving the rest into overlay_tree_on_index() makes this index juggling\nwe're doing easier to read.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/ls-files.c | 77 ++++++++++++++++++++--------------------------\n 1 file changed, 34 insertions(+), 43 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 3149a2769a3..aa153423b80 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -423,7 +423,7 @@ static int get_common_prefix_len(const char *common_prefix)\n \n static int read_one_entry_opt(struct index_state *istate,\n \t\t\t      const struct object_id *oid,\n-\t\t\t      const char *base, int baselen,\n+\t\t\t      struct strbuf *base,\n \t\t\t      const char *pathname,\n \t\t\t      unsigned mode, int opt)\n {\n@@ -434,13 +434,13 @@ static int read_one_entry_opt(struct index_state *istate,\n \t\treturn READ_TREE_RECURSIVE;\n \n \tlen = strlen(pathname);\n-\tce = make_empty_cache_entry(istate, baselen + len);\n+\tce = make_empty_cache_entry(istate, base->len + len);\n \n \tce->ce_mode = create_ce_mode(mode);\n \tce->ce_flags = create_ce_flags(1);\n-\tce->ce_namelen = baselen + len;\n-\tmemcpy(ce->name, base, baselen);\n-\tmemcpy(ce->name + baselen, pathname, len+1);\n+\tce->ce_namelen = base->len + len;\n+\tmemcpy(ce->name, base->buf, base->len);\n+\tmemcpy(ce->name + base->len, pathname, len+1);\n \toidcpy(&ce->oid, oid);\n \treturn add_index_entry(istate, ce, opt);\n }\n@@ -450,7 +450,7 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n \t\t\t  void *context)\n {\n \tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n+\treturn read_one_entry_opt(istate, oid, base, pathname,\n \t\t\t\t  mode,\n \t\t\t\t  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);\n }\n@@ -464,42 +464,8 @@ static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base\n \t\t\t\tvoid *context)\n {\n \tstruct index_state *istate = context;\n-\treturn read_one_entry_opt(istate, oid, base->buf, base->len, pathname,\n-\t\t\t\t  mode,\n-\t\t\t\t  ADD_CACHE_JUST_APPEND);\n-}\n-\n-\n-static int read_tree(struct repository *r, struct tree *tree,\n-\t\t     struct pathspec *match, struct index_state *istate)\n-{\n-\tread_tree_fn_t fn = NULL;\n-\tint i, err;\n-\n-\n-\t/*\n-\t * See if we have cache entry at the stage.  If so,\n-\t * do it the original slow way, otherwise, append and then\n-\t * sort at the end.\n-\t */\n-\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n-\t\tconst struct cache_entry *ce = istate->cache[i];\n-\t\tif (ce_stage(ce) == 1)\n-\t\t\tfn = read_one_entry;\n-\t}\n-\n-\tif (!fn)\n-\t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(r, tree, \"\", 0, 0, match, fn, istate);\n-\tif (fn == read_one_entry || err)\n-\t\treturn err;\n-\n-\t/*\n-\t * Sort the cache entry -- we need to nuke the cache tree, though.\n-\t */\n-\tcache_tree_free(&istate->cache_tree);\n-\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n-\treturn 0;\n+\treturn read_one_entry_opt(istate, oid, base, pathname,\n+\t\t\t\t  mode, ADD_CACHE_JUST_APPEND);\n }\n \n /*\n@@ -518,6 +484,8 @@ void overlay_tree_on_index(struct index_state *istate,\n \tstruct pathspec pathspec;\n \tstruct cache_entry *last_stage0 = NULL;\n \tint i;\n+\tread_tree_fn_t fn = NULL;\n+\tint err;\n \n \tif (get_oid(tree_name, &oid))\n \t\tdie(\"tree-ish %s not found.\", tree_name);\n@@ -540,9 +508,32 @@ void overlay_tree_on_index(struct index_state *istate,\n \t\t\t       PATHSPEC_PREFER_CWD, prefix, matchbuf);\n \t} else\n \t\tmemset(&pathspec, 0, sizeof(pathspec));\n-\tif (read_tree(the_repository, tree, &pathspec, istate))\n+\n+\t/*\n+\t * See if we have cache entry at the stage.  If so,\n+\t * do it the original slow way, otherwise, append and then\n+\t * sort at the end.\n+\t */\n+\tfor (i = 0; !fn && i < istate->cache_nr; i++) {\n+\t\tconst struct cache_entry *ce = istate->cache[i];\n+\t\tif (ce_stage(ce) == 1)\n+\t\t\tfn = read_one_entry;\n+\t}\n+\n+\tif (!fn)\n+\t\tfn = read_one_entry_quick;\n+\terr = read_tree_recursive(the_repository, tree, \"\", 0, 1, &pathspec, fn, istate);\n+\tif (err)\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \n+\t/*\n+\t * Sort the cache entry -- we need to nuke the cache tree, though.\n+\t */\n+\tif (fn == read_one_entry_quick) {\n+\t\tcache_tree_free(&istate->cache_tree);\n+\t\tQSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);\n+\t}\n+\n \tfor (i = 0; i < istate->cache_nr; i++) {\n \t\tstruct cache_entry *ce = istate->cache[i];\n \t\tswitch (ce_stage(ce)) {\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419789","messageId":"02c42be924953e7172639a82d8005bcb664bb8b4.1616279653.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616279653.git.avarab@gmail.com","subject":"[PATCH v5 6/8] archive: stop passing \"stage\" through read_tree_recursive()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-20T22:37:49Z","receivedAt":"2021-03-20T22:39:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The \"stage\" variable being passed around in the archive code has only\never been an elaborate way to hardcode the value \"0\".\n\nThis code was added in its original form in e4fbbfe9ecc (Add\ngit-zip-tree, 2006-08-26), at which point a hardcoded \"0\" would be\npassed down through read_tree_recursive() to write_zip_entry().\n\nIt was then diligently added to the \"struct directory\" in\ned22b4173bd (archive: support filtering paths with glob, 2014-09-21),\nbut we were still not doing anything except passing it around as-is.\n\nLet's stop doing that in the code internal to archive.c, we'll still\nfeed \"0\" to read_tree_recursive() itself, but won't use it. That we're\nproviding it at all to read_tree_recursive() will be changed in a\nfollow-up commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c | 16 +++++++---------\n 1 file changed, 7 insertions(+), 9 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 5919d9e5050..4f271331543 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -107,7 +107,6 @@ struct directory {\n \tstruct object_id oid;\n \tint baselen, len;\n \tunsigned mode;\n-\tint stage;\n \tchar path[FLEX_ARRAY];\n };\n \n@@ -138,7 +137,7 @@ static int check_attr_export_subst(const struct attr_check *check)\n }\n \n static int write_archive_entry(const struct object_id *oid, const char *base,\n-\t\tint baselen, const char *filename, unsigned mode, int stage,\n+\t\tint baselen, const char *filename, unsigned mode,\n \t\tvoid *context)\n {\n \tstatic struct strbuf path = STRBUF_INIT;\n@@ -197,7 +196,7 @@ static int write_archive_entry(const struct object_id *oid, const char *base,\n \n static void queue_directory(const unsigned char *sha1,\n \t\tstruct strbuf *base, const char *filename,\n-\t\tunsigned mode, int stage, struct archiver_context *c)\n+\t\tunsigned mode, struct archiver_context *c)\n {\n \tstruct directory *d;\n \tsize_t len = st_add4(base->len, 1, strlen(filename), 1);\n@@ -205,7 +204,6 @@ static void queue_directory(const unsigned char *sha1,\n \td->up\t   = c->bottom;\n \td->baselen = base->len;\n \td->mode\t   = mode;\n-\td->stage   = stage;\n \tc->bottom  = d;\n \td->len = xsnprintf(d->path, len, \"%.*s%s/\", (int)base->len, base->buf, filename);\n \thashcpy(d->oid.hash, sha1);\n@@ -224,7 +222,7 @@ static int write_directory(struct archiver_context *c)\n \t\twrite_directory(c) ||\n \t\twrite_archive_entry(&d->oid, d->path, d->baselen,\n \t\t\t\t    d->path + d->baselen, d->mode,\n-\t\t\t\t    d->stage, c) != READ_TREE_RECURSIVE;\n+\t\t\t\t    c) != READ_TREE_RECURSIVE;\n \tfree(d);\n \treturn ret ? -1 : 0;\n }\n@@ -256,14 +254,14 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\tif (check_attr_export_ignore(check))\n \t\t\treturn 0;\n \t\tqueue_directory(oid->hash, base, filename,\n-\t\t\t\tmode, stage, c);\n+\t\t\t\tmode, c);\n \t\treturn READ_TREE_RECURSIVE;\n \t}\n \n \tif (write_directory(c))\n \t\treturn -1;\n \treturn write_archive_entry(oid, base->buf, base->len, filename, mode,\n-\t\t\t\t   stage, context);\n+\t\t\t\t   context);\n }\n \n struct extra_file_info {\n@@ -377,8 +375,8 @@ struct path_exists_context {\n };\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\tconst char *filename, unsigned mode,\n-\t\t\tint stage, void *context)\n+\t\t\tconst char *filename, unsigned mode, int stage,\n+\t\t\tvoid *context)\n {\n \tint ret = -1;\n \tstruct path_exists_context *ctx = context;\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419790","messageId":"d55e8d4042ba77f9fcc204afd74a8badfe83502d.1616279653.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616279653.git.avarab@gmail.com","subject":"[PATCH v5 7/8] tree.h API: expose read_tree_1() as read_tree_at()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-20T22:37:50Z","receivedAt":"2021-03-20T22:39:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the static read_tree_1() function to read_tree_at(). This\nfunction works just like read_tree_recursive(), except you provide\nyour own strbuf.\n\nThis step doesn't make much sense now, but in follow-up commits I'll\nremove the base/baselen/stage arguments to read_tree_recursive(). At\nthat point an anticipated in-tree user[1] for the old\nread_tree_recursive() couldn't provide a path to start the\ntraversal.\n\nLet's give them a function to do so with an API that makes more sense\nfor them, by taking a strbuf we should be able to avoid more casting\nand/or reallocations in the future.\n\n1. https://lore.kernel.org/git/xmqqft106sok.fsf@gitster.g\n---\n tree.c | 17 +++++++++--------\n tree.h |  6 ++++++\n 2 files changed, 15 insertions(+), 8 deletions(-)\n\ndiff --git a/tree.c b/tree.c\nindex a6c12f2745a..f67c2153054 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -11,10 +11,11 @@\n \n const char *tree_type = \"tree\";\n \n-static int read_tree_1(struct repository *r,\n-\t\t       struct tree *tree, struct strbuf *base,\n-\t\t       int stage, const struct pathspec *pathspec,\n-\t\t       read_tree_fn_t fn, void *context)\n+int read_tree_at(struct repository *r,\n+\t\t struct tree *tree, struct strbuf *base,\n+\t\t int stage,\n+\t\t const struct pathspec *pathspec,\n+\t\t read_tree_fn_t fn, void *context)\n {\n \tstruct tree_desc desc;\n \tstruct name_entry entry;\n@@ -71,9 +72,9 @@ static int read_tree_1(struct repository *r,\n \t\tlen = tree_entry_len(&entry);\n \t\tstrbuf_add(base, entry.path, len);\n \t\tstrbuf_addch(base, '/');\n-\t\tretval = read_tree_1(r, lookup_tree(r, &oid),\n-\t\t\t\t     base, stage, pathspec,\n-\t\t\t\t     fn, context);\n+\t\tretval = read_tree_at(r, lookup_tree(r, &oid),\n+\t\t\t\t      base, stage, pathspec,\n+\t\t\t\t      fn, context);\n \t\tstrbuf_setlen(base, oldlen);\n \t\tif (retval)\n \t\t\treturn -1;\n@@ -91,7 +92,7 @@ int read_tree_recursive(struct repository *r,\n \tint ret;\n \n \tstrbuf_add(&sb, base, baselen);\n-\tret = read_tree_1(r, tree, &sb, stage, pathspec, fn, context);\n+\tret = read_tree_at(r, tree, &sb, stage, pathspec, fn, context);\n \tstrbuf_release(&sb);\n \treturn ret;\n }\ndiff --git a/tree.h b/tree.h\nindex 6b0b1dc211a..123fc41efe6 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -33,6 +33,12 @@ int cmp_cache_name_compare(const void *a_, const void *b_);\n #define READ_TREE_RECURSIVE 1\n typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, int, void *);\n \n+int read_tree_at(struct repository *r,\n+\t\t struct tree *tree, struct strbuf *base,\n+\t\t int stage,\n+\t\t const struct pathspec *pathspec,\n+\t\t read_tree_fn_t fn, void *context);\n+\n int read_tree_recursive(struct repository *r,\n \t\t\tstruct tree *tree,\n \t\t\tconst char *base, int baselen,\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419791","messageId":"fa157d8baeea3e4686b6df04b3cd9e9bcc3cd9a0.1616279653.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616279653.git.avarab@gmail.com","subject":"[PATCH v5 8/8] tree.h API: simplify read_tree_recursive() signature","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-20T22:37:51Z","receivedAt":"2021-03-20T22:39:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Simplify the signature of read_tree_recursive() to omit the \"base\",\n\"baselen\" and \"stage\" arguments. No callers of it use these parameters\nfor anything anymore.\n\nThe last function to call read_tree_recursive() with a non-\"\" path was\nread_tree_recursive() itself, but that was changed in\nffd31f661d5 (Reimplement read_tree_recursive() using\ntree_entry_interesting(), 2011-03-25).\n\nThe last user of the \"stage\" parameter went away in the last commit,\nand even that use was mere boilerplate.\n\nSo let's remove those and rename the read_tree_recursive() function to\njust read_tree(). We had another read_tree() function that I've\nrefactored away in preceding commits, since all in-tree users read\ntrees recursively with a callback we can change the name to signify\nthat this is the norm.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          | 18 +++++++++---------\n builtin/checkout.c |  8 ++++----\n builtin/log.c      |  8 ++++----\n builtin/ls-files.c |  6 +++---\n builtin/ls-tree.c  |  6 +++---\n merge-recursive.c  |  6 +++---\n tree.c             | 19 +++++++------------\n tree.h             | 13 ++++++-------\n 8 files changed, 39 insertions(+), 45 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 4f271331543..4921dc8a69f 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -229,7 +229,7 @@ static int write_directory(struct archiver_context *c)\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\tstruct strbuf *base, const char *filename,\n-\t\tunsigned mode, int stage, void *context)\n+\t\tunsigned mode, void *context)\n {\n \tstruct archiver_context *c = context;\n \n@@ -314,10 +314,10 @@ int write_archive_entries(struct archiver_args *args,\n \t\tgit_attr_set_direction(GIT_ATTR_INDEX);\n \t}\n \n-\terr = read_tree_recursive(args->repo, args->tree, \"\",\n-\t\t\t\t  0, 0, &args->pathspec,\n-\t\t\t\t  queue_or_write_archive_entry,\n-\t\t\t\t  &context);\n+\terr = read_tree(args->repo, args->tree,\n+\t\t\t&args->pathspec,\n+\t\t\tqueue_or_write_archive_entry,\n+\t\t\t&context);\n \tif (err == READ_TREE_RECURSIVE)\n \t\terr = 0;\n \twhile (context.bottom) {\n@@ -375,7 +375,7 @@ struct path_exists_context {\n };\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\tconst char *filename, unsigned mode, int stage,\n+\t\t\tconst char *filename, unsigned mode,\n \t\t\tvoid *context)\n {\n \tint ret = -1;\n@@ -403,9 +403,9 @@ static int path_exists(struct archiver_args *args, const char *path)\n \tctx.args = args;\n \tparse_pathspec(&ctx.pathspec, 0, 0, \"\", paths);\n \tctx.pathspec.recursive = 1;\n-\tret = read_tree_recursive(args->repo, args->tree, \"\",\n-\t\t\t\t  0, 0, &ctx.pathspec,\n-\t\t\t\t  reject_entry, &ctx);\n+\tret = read_tree(args->repo, args->tree,\n+\t\t\t&ctx.pathspec,\n+\t\t\treject_entry, &ctx);\n \tclear_pathspec(&ctx.pathspec);\n \treturn ret != 0;\n }\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2d6550bc3c8..0e663905200 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -114,7 +114,7 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n }\n \n static int update_some(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tint len;\n \tstruct cache_entry *ce;\n@@ -155,8 +155,8 @@ static int update_some(const struct object_id *oid, struct strbuf *base,\n \n static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)\n {\n-\tread_tree_recursive(the_repository, tree, \"\", 0, 0,\n-\t\t\t    pathspec, update_some, NULL);\n+\tread_tree(the_repository, tree,\n+\t\t  pathspec, update_some, NULL);\n \n \t/* update the index with the given tree's info\n \t * for all args, expanding wildcards, and exit\n@@ -322,7 +322,7 @@ static void mark_ce_for_checkout_overlay(struct cache_entry *ce,\n \t * If it comes from the tree-ish, we already know it\n \t * matches the pathspec and could just stamp\n \t * CE_MATCHED to it from update_some(). But we still\n-\t * need ps_matched and read_tree_recursive (and\n+\t * need ps_matched and read_tree (and\n \t * eventually tree_entry_interesting) cannot fill\n \t * ps_matched yet. Once it can, we can avoid calling\n \t * match_pathspec() for _all_ entries when\ndiff --git a/builtin/log.c b/builtin/log.c\nindex f67b67d80ed..980de590638 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -599,7 +599,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n \n static int show_tree_object(const struct object_id *oid,\n \t\tstruct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tFILE *file = context;\n \tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n@@ -681,9 +681,9 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_COMMIT),\n \t\t\t\t\tname,\n \t\t\t\t\tdiff_get_color_opt(&rev.diffopt, DIFF_RESET));\n-\t\t\tread_tree_recursive(the_repository, (struct tree *)o, \"\",\n-\t\t\t\t\t    0, 0, &match_all, show_tree_object,\n-\t\t\t\t\t    rev.diffopt.file);\n+\t\t\tread_tree(the_repository, (struct tree *)o,\n+\t\t\t\t  &match_all, show_tree_object,\n+\t\t\t\t  rev.diffopt.file);\n \t\t\trev.shown_one = 1;\n \t\t\tbreak;\n \t\tcase OBJ_COMMIT:\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex aa153423b80..60a2913a01e 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -446,7 +446,7 @@ static int read_one_entry_opt(struct index_state *istate,\n }\n \n static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\t  const char *pathname, unsigned mode, int stage,\n+\t\t\t  const char *pathname, unsigned mode,\n \t\t\t  void *context)\n {\n \tstruct index_state *istate = context;\n@@ -460,7 +460,7 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n  * the stage that will conflict with the entry being added.\n  */\n static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n-\t\t\t\tconst char *pathname, unsigned mode, int stage,\n+\t\t\t\tconst char *pathname, unsigned mode,\n \t\t\t\tvoid *context)\n {\n \tstruct index_state *istate = context;\n@@ -522,7 +522,7 @@ void overlay_tree_on_index(struct index_state *istate,\n \n \tif (!fn)\n \t\tfn = read_one_entry_quick;\n-\terr = read_tree_recursive(the_repository, tree, \"\", 0, 1, &pathspec, fn, istate);\n+\terr = read_tree(the_repository, tree, &pathspec, fn, istate);\n \tif (err)\n \t\tdie(\"unable to read tree entries %s\", tree_name);\n \ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 7cad3f24ebd..3a442631c71 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -62,7 +62,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n }\n \n static int show_tree(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, int stage, void *context)\n+\t\tconst char *pathname, unsigned mode, void *context)\n {\n \tint retval = 0;\n \tint baselen;\n@@ -185,6 +185,6 @@ 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_recursive(the_repository, tree, \"\", 0, 0,\n-\t\t\t\t     &pathspec, show_tree, NULL);\n+\treturn !!read_tree(the_repository, tree,\n+\t\t\t   &pathspec, show_tree, NULL);\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex b69e694d986..ed31f9496cb 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,7 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   unsigned int mode, int stage, void *context)\n+\t\t\t   unsigned int mode, void *context)\n {\n \tstruct path_hashmap_entry *entry;\n \tint baselen = base->len;\n@@ -473,8 +473,8 @@ static void get_files_dirs(struct merge_options *opt, struct tree *tree)\n {\n \tstruct pathspec match_all;\n \tmemset(&match_all, 0, sizeof(match_all));\n-\tread_tree_recursive(opt->repo, tree, \"\", 0, 0,\n-\t\t\t    &match_all, save_files_dirs, opt);\n+\tread_tree(opt->repo, tree,\n+\t\t  &match_all, save_files_dirs, opt);\n }\n \n static int get_tree_entry_if_blob(struct repository *r,\ndiff --git a/tree.c b/tree.c\nindex f67c2153054..410e3b477e5 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -13,7 +13,6 @@ const char *tree_type = \"tree\";\n \n int read_tree_at(struct repository *r,\n \t\t struct tree *tree, struct strbuf *base,\n-\t\t int stage,\n \t\t const struct pathspec *pathspec,\n \t\t read_tree_fn_t fn, void *context)\n {\n@@ -39,7 +38,7 @@ int read_tree_at(struct repository *r,\n \t\t}\n \n \t\tswitch (fn(&entry.oid, base,\n-\t\t\t   entry.path, entry.mode, stage, context)) {\n+\t\t\t   entry.path, entry.mode, context)) {\n \t\tcase 0:\n \t\t\tcontinue;\n \t\tcase READ_TREE_RECURSIVE:\n@@ -73,7 +72,7 @@ int read_tree_at(struct repository *r,\n \t\tstrbuf_add(base, entry.path, len);\n \t\tstrbuf_addch(base, '/');\n \t\tretval = read_tree_at(r, lookup_tree(r, &oid),\n-\t\t\t\t      base, stage, pathspec,\n+\t\t\t\t      base, pathspec,\n \t\t\t\t      fn, context);\n \t\tstrbuf_setlen(base, oldlen);\n \t\tif (retval)\n@@ -82,17 +81,13 @@ int read_tree_at(struct repository *r,\n \treturn 0;\n }\n \n-int read_tree_recursive(struct repository *r,\n-\t\t\tstruct tree *tree,\n-\t\t\tconst char *base, int baselen,\n-\t\t\tint stage, const struct pathspec *pathspec,\n-\t\t\tread_tree_fn_t fn, void *context)\n+int read_tree(struct repository *r,\n+\t      struct tree *tree,\n+\t      const struct pathspec *pathspec,\n+\t      read_tree_fn_t fn, void *context)\n {\n \tstruct strbuf sb = STRBUF_INIT;\n-\tint ret;\n-\n-\tstrbuf_add(&sb, base, baselen);\n-\tret = read_tree_at(r, tree, &sb, stage, pathspec, fn, context);\n+\tint ret = read_tree_at(r, tree, &sb, pathspec, fn, context);\n \tstrbuf_release(&sb);\n \treturn ret;\n }\ndiff --git a/tree.h b/tree.h\nindex 123fc41efe6..6efff003e21 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -31,17 +31,16 @@ struct tree *parse_tree_indirect(const struct object_id *oid);\n int cmp_cache_name_compare(const void *a_, const void *b_);\n \n #define READ_TREE_RECURSIVE 1\n-typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, int, void *);\n+typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n \n int read_tree_at(struct repository *r,\n \t\t struct tree *tree, struct strbuf *base,\n-\t\t int stage,\n \t\t const struct pathspec *pathspec,\n \t\t read_tree_fn_t fn, void *context);\n \n-int read_tree_recursive(struct repository *r,\n-\t\t\tstruct tree *tree,\n-\t\t\tconst char *base, int baselen,\n-\t\t\tint stage, const struct pathspec *pathspec,\n-\t\t\tread_tree_fn_t fn, void *context);\n+int read_tree(struct repository *r,\n+\t      struct tree *tree,\n+\t      const struct pathspec *pathspec,\n+\t      read_tree_fn_t fn, void *context);\n+\n #endif /* TREE_H */\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419792","messageId":"xmqqy2ehctjo.fsf@gitster.g","threadId":"18927","inReplyTo":"cover.1616279653.git.avarab@gmail.com","subject":"Re: [PATCH v5 0/8] read_tree() and read_tree_recursive() refactoring","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-20T23:08:27Z","receivedAt":"2021-03-20T23:09:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> A refactoring of v4[1], not quite as as suggested by Junio in[2], but\n> which I think makes this whole thing less confusing and more\n> straightforward.\n\nYeah, I admit that I gave only a cursory look, but the progression\npresented in this round makes a lot of sense to me.\n\nThanks, will replace.\n"},{"id":"419793","messageId":"0390b1e9dedd89ecffd4b7b4ecc68e3ac8f66147.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 01/29] notes & match-trees: use name_entry's \"pathlen\" member","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:34Z","receivedAt":"2021-03-21T00:02:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change code that was doing a strlen() on the \"path\" from a name_entry\nstruct to instead use the pathlen given to us by decode_tree_entry().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c | 7 +++----\n notes.c       | 4 ++--\n 2 files changed, 5 insertions(+), 6 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex f6c194c1cca..1011357ad0c 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -197,9 +197,10 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \twhile (desc.size) {\n \t\tconst char *name;\n \t\tunsigned short mode;\n+\t\tint len = tree_entry_len(&desc.entry);\n \n \t\ttree_entry_extract(&desc, &name, &mode);\n-\t\tif (strlen(name) == toplen &&\n+\t\tif (len == toplen &&\n \t\t    !memcmp(name, prefix, toplen)) {\n \t\t\tif (!S_ISDIR(mode))\n \t\t\t\tdie(\"entry %s in tree %s is not a tree\", name,\n@@ -214,9 +215,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \t\t\t *   - to discard the \"const\"; this is OK because we\n \t\t\t *     know it points into our non-const \"buf\"\n \t\t\t */\n-\t\t\trewrite_here = (unsigned char *)(desc.entry.path +\n-\t\t\t\t\t\t\t strlen(desc.entry.path) +\n-\t\t\t\t\t\t\t 1);\n+\t\t\trewrite_here = (unsigned char *)(name + len + 1);\n \t\t\tbreak;\n \t\t}\n \t\tupdate_tree_entry(&desc);\ndiff --git a/notes.c b/notes.c\nindex a19e4ad7943..e2fec12a39e 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -413,7 +413,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \twhile (tree_entry(&desc, &entry)) {\n \t\tunsigned char type;\n \t\tstruct leaf_node *l;\n-\t\tsize_t path_len = strlen(entry.path);\n+\t\tint path_len = entry.pathlen;\n \n \t\tif (path_len == 2 * (hashsz - prefix_len)) {\n \t\t\t/* This is potentially the remainder of the SHA-1 */\n@@ -483,7 +483,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n \t\t\t\tstrbuf_addch(&non_note_path, '/');\n \t\t\t}\n-\t\t\tstrbuf_addstr(&non_note_path, entry.path);\n+\t\t\tstrbuf_add(&non_note_path, entry.path, path_len);\n \t\t\tadd_non_note(t, strbuf_detach(&non_note_path, NULL),\n \t\t\t\t     entry.mode, entry.oid.hash);\n \t\t}\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419795","messageId":"cover.1616282533.git.avarab@gmail.com","threadId":"18927","inReplyTo":"20210316155829.31242-1-avarab@gmail.com","subject":"[PATCH v4 00/29] tree-walk: mostly replace \"mode\" with \"enum object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:33Z","receivedAt":"2021-03-21T00:02:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"A re-roll of v3 of this series[1] and on top of (and requires) my\njust-submitted v5 re-roll of the read_tree() refactoring series[2].\n\nThere was a regression in 1/32 of the old series. Removing the\ncanon_mode() call in diff.c didn't account for us needing to\ncanonicalize \"diff --no-index\" modes. There were no tests for this,\nand it failed or not depending on the FS modes in the git.git checkout\nbeing tested. This fixes the CI smoke coming from this series.\n\nI plan to submit another set of patches to fix git diff --no-index's\ntest coverage, but for this series it was best to just eject it.\n\nI'm confident that this is in a good shape now. There is now no\ncanon_mode() change in this series, just refactoring of\ns/mode/object_type/g.\n\nAs discussed in earlier rounds the point of this prep series is to get\nto a point where we can expose an API to fix long-standin blindspots\nin fsck checks.\n\nThis is a long journey to get to that point, and isn't strictly\nnecessary to fix tht issue. But I think it's worth it since it leaves\nthe API use in a much better state where it's clear which things\nactually require the mode bits, and which are just asking about the\nobject type of tree entries.\n\nI dropped a couple of more patches to do with archive.c, in my\nre-rolled [2] I'm now dealing with the \"stage\" parameter there, so\nthere's no need to do that here anymore.\n\n1. https://lore.kernel.org/git/20210316155829.31242-1-avarab@gmail.com/\n2. https://lore.kernel.org/git/xmqqy2ehctjo.fsf@gitster.g\n\nÆvar Arnfjörð Bjarmason (29):\n  notes & match-trees: use name_entry's \"pathlen\" member\n  cache.h: add a comment to object_type()\n  tree-walk.h: add object_type member to name_entry\n  tree-walk.c: migrate to using new \"object_type\" field when possible\n  fast-import tests: test for sorting dir/file foo v.s. foo.txt\n  mktree tests: test that \"mode\" is passed when sorting\n  diff tests: test that \"mode\" is passed when sorting\n  cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n  tree-walk.h users: switch object_type(...) to new .object_type\n  tree.h: format argument lists of read_tree_recursive() users\n  tree.h API: make read_tree_fn_t take an \"enum object_type\"\n  tree-walk.h users: migrate \"p->mode &&\" pattern\n  tree-walk.h users: refactor chained \"mode\" if/else into switch\n  tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n  merge-tree tests: test for the mode comparison in same_entry()\n  merge-ort: correct reference to test in 62fdec17a11\n  fsck.c: switch on \"object_type\" in fsck_walk_tree()\n  tree-walk.h users: use temporary variable(s) for \"mode\"\n  tree-walk.h API: formatting changes for subsequent commit\n  tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n  match-trees: use \"tmp\" for mode in shift_tree_by()\n  tree-walk.h API: add get_tree_entry_type()\n  tree-walk.h API: document and format tree_entry_extract()\n  tree-entry.h API: rename tree_entry_extract() to\n    tree_entry_extract_mode()\n  tree-walk.h API: add a tree_entry_extract_all() function\n  tree-walk.h API: add get_tree_entry_all()\n  tree-walk.h API: add a get_tree_entry_path() function\n  blame: emit a better error on 'git blame directory'\n  tree-walk.h API: add a tree_entry_extract_type() function\n\n archive.c                       | 29 ++++++-----\n blame.c                         |  9 ++--\n builtin/checkout.c              |  6 ++-\n builtin/fast-import.c           | 12 +++--\n builtin/grep.c                  |  6 +--\n builtin/log.c                   |  7 +--\n builtin/ls-files.c              |  6 ++-\n builtin/ls-tree.c               | 14 +++---\n builtin/merge-tree.c            | 30 +++++++----\n builtin/mktree.c                |  4 +-\n builtin/pack-objects.c          |  6 +--\n builtin/reflog.c                |  3 +-\n builtin/rm.c                    |  2 +-\n builtin/update-index.c          |  6 ++-\n cache-tree.c                    |  2 +-\n cache.h                         | 11 ++--\n combine-diff.c                  |  8 +--\n delta-islands.c                 |  2 +-\n fsck.c                          | 23 ++++-----\n http-push.c                     |  6 ++-\n line-log.c                      |  2 +-\n list-objects.c                  | 20 +++++---\n match-trees.c                   | 52 +++++++++----------\n merge-ort.c                     | 13 ++---\n merge-recursive.c               | 33 ++++++------\n notes.c                         | 14 +++---\n object-name.c                   |  7 ++-\n pack-bitmap-write.c             |  8 +--\n read-cache.c                    | 16 +++---\n revision.c                      | 12 +++--\n t/t1450-fsck.sh                 | 66 ++++++++++++++++++++++++\n t/t4300-merge-tree.sh           | 44 ++++++++++++++++\n t/t8004-blame-with-conflicts.sh | 21 ++++++++\n t/t9300-fast-import.sh          | 87 ++++++++++++++++++++++++++++++++\n tree-diff.c                     | 30 +++++++----\n tree-walk.c                     | 89 ++++++++++++++++++++++++---------\n tree-walk.h                     | 63 ++++++++++++++++++++---\n tree.c                          | 19 ++++---\n tree.h                          |  5 +-\n unpack-trees.c                  | 24 +++++----\n walker.c                        | 22 ++++----\n 41 files changed, 606 insertions(+), 233 deletions(-)\n\nRange-diff:\n 1:  26bc38fbdd0 <  -:  ----------- diff.c: remove redundant canon_mode() call\n 2:  8502cf8134b =  1:  0390b1e9ded notes & match-trees: use name_entry's \"pathlen\" member\n 3:  68133fa6aaa =  2:  186f7a7a44b cache.h: add a comment to object_type()\n 4:  9f714d6c01f =  3:  4fed508f3cb tree-walk.h: add object_type member to name_entry\n 5:  6dbf2b0a6aa =  4:  c557b67231b tree-walk.c: migrate to using new \"object_type\" field when possible\n 6:  354a8e9a2a1 =  5:  7016008554a fast-import tests: test for sorting dir/file foo v.s. foo.txt\n 7:  e2331df28e1 =  6:  b0546197b1b mktree tests: test that \"mode\" is passed when sorting\n 8:  9e9486c2ea0 =  7:  73e92ac187d diff tests: test that \"mode\" is passed when sorting\n 9:  be5c713336a =  8:  1b6a10f814c cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n10:  43623edddfb =  9:  f8912a409b5 tree-walk.h users: switch object_type(...) to new .object_type\n11:  030898f884c ! 10:  df43f1a76ac tree.h: format argument lists of read_tree_recursive() users\n    @@ archive.c: static int check_attr_export_subst(const struct attr_check *check)\n      }\n      \n      static int write_archive_entry(const struct object_id *oid, const char *base,\n    --\t\tint baselen, const char *filename, unsigned mode, int stage,\n    +-\t\tint baselen, const char *filename, unsigned mode,\n     -\t\tvoid *context)\n     +\t\t\t       int baselen, const char *filename,\n     +\t\t\t       unsigned mode,\n    -+\t\t\t       int stage,\n     +\t\t\t       void *context)\n      {\n      \tstatic struct strbuf path = STRBUF_INIT;\n    @@ archive.c: static int write_directory(struct archiver_context *c)\n     +\t\t\t\t\tvoid *context)\n      {\n      \tstruct archiver_context *c = context;\n    - \tint stage = 0;\n    + \n     @@ archive.c: struct path_exists_context {\n      };\n      \n    @@ tree.h: struct tree *parse_tree_indirect(const struct object_id *oid);\n     +\t\t\t      void *);\n      \n      int read_tree_at(struct repository *r,\n    - \t\t struct tree *tree,\n    + \t\t struct tree *tree, struct strbuf *base,\n12:  0ce197950b0 <  -:  ----------- tree.h users: format argument lists in archive.c\n13:  051c9f32ac6 <  -:  ----------- archive: get rid of 'stage' parameter\n14:  bc73994b4c7 = 11:  152c6d88542 tree.h API: make read_tree_fn_t take an \"enum object_type\"\n15:  e2b0964228f = 12:  1c2f65cb674 tree-walk.h users: migrate \"p->mode &&\" pattern\n16:  29dbc4292e0 = 13:  163922d427c tree-walk.h users: refactor chained \"mode\" if/else into switch\n17:  ada6d051769 = 14:  21df7c668be tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n18:  01860daa556 = 15:  592fecb7abc merge-tree tests: test for the mode comparison in same_entry()\n19:  e4be48fb50f = 16:  092472f3c8d merge-ort: correct reference to test in 62fdec17a11\n20:  189d2550fb8 = 17:  685da1abbdc fsck.c: switch on \"object_type\" in fsck_walk_tree()\n21:  3b7b12e6f79 = 18:  4babecb7855 tree-walk.h users: use temporary variable(s) for \"mode\"\n22:  6c3a07a3279 = 19:  7251654dd52 tree-walk.h API: formatting changes for subsequent commit\n23:  879eb3da2a4 = 20:  9e521a3cbf2 tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n24:  d2fa360ab9e = 21:  40f37e99cd9 match-trees: use \"tmp\" for mode in shift_tree_by()\n25:  cc50cfcf517 = 22:  7f699bf2d5c tree-walk.h API: add get_tree_entry_type()\n26:  f642a35482b = 23:  7ab7576cb16 tree-walk.h API: document and format tree_entry_extract()\n27:  a0bcb59fa57 = 24:  a1bd81d64aa tree-entry.h API: rename tree_entry_extract() to tree_entry_extract_mode()\n28:  c7d4ba77340 = 25:  ce7c19ad39c tree-walk.h API: add a tree_entry_extract_all() function\n29:  1d5421d67af = 26:  95eec961be8 tree-walk.h API: add get_tree_entry_all()\n30:  a3e1063ac45 = 27:  a2a34fd3e2d tree-walk.h API: add a get_tree_entry_path() function\n31:  da3dd6dd532 = 28:  81da5490221 blame: emit a better error on 'git blame directory'\n32:  80e5cb0b30c = 29:  4d51da4ea39 tree-walk.h API: add a tree_entry_extract_type() function\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419796","messageId":"186f7a7a44b7d296b83421d703c74be5668b5d8a.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 02/29] cache.h: add a comment to object_type()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:35Z","receivedAt":"2021-03-21T00:02:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a comment to the object_type() function to explain what it\nreturns, and what the \"mode\" is in the \"else\" case.\n\nThe object_type() function dates back to 4d1012c3709 (Fix rev-list\nwhen showing objects involving submodules, 2007-11-11). It's not\nimmediately obvious to someone looking at its history and how it's\ncome to be used.\n\nDespite what Linus noted in 4d1012c3709 (Fix rev-list when showing\nobjects involving submodules, 2007-11-11) about wanting to move away\nfrom users of object_type() relying on S_ISLNK(mode) being true here\nwe do currently rely on that. If this is changed to a condition to\nonly return OBJ_BLOB on S_ISREG(mode) then t4008, t4023 and t7415 will\nhave failing tests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n cache.h | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/cache.h b/cache.h\nindex c2f8a8eadf6..ae0c0bef5c2 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -451,11 +451,16 @@ enum object_type {\n \tOBJ_MAX\n };\n \n+/*\n+ * object_type() returns an object of a type that'll appear in a tree,\n+ * so no OBJ_TAG is possible. This is mostly (and dates back to)\n+ * consumers of the tree-walk.h API's \"mode\" field.\n+ */\n static inline enum object_type object_type(unsigned int mode)\n {\n \treturn S_ISDIR(mode) ? OBJ_TREE :\n \t\tS_ISGITLINK(mode) ? OBJ_COMMIT :\n-\t\tOBJ_BLOB;\n+\t\tOBJ_BLOB; /* S_ISREG(mode) || S_ISLNK(mode) */\n }\n \n /* Double-check local_repo_env below if you add to this list. */\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419798","messageId":"7016008554aacc68b70d9bea92c454b1ac53e39c.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 05/29] fast-import tests: test for sorting dir/file foo v.s. foo.txt","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:38Z","receivedAt":"2021-03-21T00:02:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a missing test for the sorting of \"foo\" v.s. \"foo.txt\" where \"foo\"\ncan be either a file or a directory. If it's a file then \"foo\" should\nsort before \"foo.txt\", and the other way around if \"foo\" is a\ndirectory.\n\nSee [1] for a reply to a patch of mine introducing such a\nregression. We now finally have a test for this code added back in\n463acbe1c6 (Added tree and commit writing to fast-import.,\n2006-08-14).\n\nThis tests both the tecmp1() and tecmp0() functions introduced inn\n4cabf8583f (Implemented tree delta compression in fast-import.,\n2006-08-28).\n\nThis will catch cases where the \"mode\" is the same, or reversed\nbetween a & b in both functions.\n\nThere was an existing test for the tecmp1() function(s) just above\nthis one added here.\n\nThat existing test was added in e741130386 (New fast-import test case\nfor valid tree sorting, 2007-03-12) and would catch cases where\nentries were reversed, but not if their mode (or rather, type) was the\nthe same or otherwise wrong value.\n\nThere were no tests at all for the tecmp0() function. As with the\ntecmp1() test the new test will catch cases where the \"mode\" is the\nsame (e.g. \"1\"), or if the two are reversed. It won't catch a \"return\n0\" from the function (i.e. already sorted), that case requires\ntecmp1() to also be broken.\n\n1. https://lore.kernel.org/git/CABPp-BEZfG_pNNTWsnxEa1xG+kgpQfpXj=KHBkCBAMikrXj-aA@mail.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t9300-fast-import.sh | 87 ++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 87 insertions(+)\n\ndiff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh\nindex 5c47ac4465c..8bafb8cc515 100755\n--- a/t/t9300-fast-import.sh\n+++ b/t/t9300-fast-import.sh\n@@ -955,6 +955,93 @@ test_expect_success 'L: verify internal tree sorting' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'L: verify internal tree sorting on bad input (tecmp1)' '\n+\ttest_create_repo L1-0 &&\n+\n+\tcat >input <<-INPUT_END &&\n+\tblob\n+\tmark :1\n+\tdata 0\n+\n+\treset refs/heads/L1-0\n+\tcommit refs/heads/L1-0\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tcreate L1-0\n+\tCOMMIT\n+\tM 100644 :1 x.txt\n+\tM 100644 :1 x/y.txt\n+\tM 100644 :1 z.txt\n+\tM 100644 :1 z\n+\tINPUT_END\n+\n+\tcat >expected <<-EXPECT_END &&\n+\tx.txt\n+\tx\n+\tz\n+\tz.txt\n+\tEXPECT_END\n+\n+\tgit -C L1-0 fast-import <input &&\n+\tgit -C L1-0 ls-tree L1-0 >tmp &&\n+\tcut -f 2 <tmp >actual &&\n+\ttest_cmp expected actual &&\n+\tgit -C L1-0 fsck 2>err &&\n+\t# Would happen if tecmp1() were broken\n+\t! grep \"error in tree .*: treeNotSorted: \" err\n+'\n+\n+test_expect_success 'L: verify internal tree sorting on bad input (tecmp0)' '\n+\n+\ttest_create_repo L1-1 &&\n+\n+\tcat >input <<-INPUT_END &&\n+\tblob\n+\tmark :1\n+\tdata <<EOF\n+\tsome data\n+\tEOF\n+\n+\treset refs/heads/L1-1\n+\tcommit refs/heads/L1-1\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tcreate L1-1\n+\tCOMMIT\n+\tM 100644 :1 x.txt\n+\tM 100644 :1 x/y.txt\n+\tM 100644 :1 z.txt\n+\tM 100644 :1 z\n+\n+\tcommit refs/heads/L1-1\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tupdate L1-1\n+\tCOMMIT\n+\tM 100644 :1 another.txt\n+\tM 100644 :1 x.txt\n+\tM 100644 :1 x/y.txt\n+\tM 100644 :1 z.txt\n+\tM 100644 :1 z\n+\tINPUT_END\n+\n+\tcat >expected <<-EXPECT_END &&\n+\tanother.txt\n+\tx.txt\n+\tx\n+\tz\n+\tz.txt\n+\tEXPECT_END\n+\n+\tgit -C L1-1 fast-import <input &&\n+\tgit -C L1-1 ls-tree L1-1 >tmp 2>err &&\n+\t# Would happen if tecmp0() passed a fixed mode\n+\t! grep \"fatal: not a tree object\" err &&\n+\tcut -f 2 <tmp >actual &&\n+\ttest_cmp expected actual &&\n+\tgit -C L1-1 fsck\n+'\n+\n test_expect_success 'L: nested tree copy does not corrupt deltas' '\n \tcat >input <<-INPUT_END &&\n \tblob\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419815","messageId":"c557b67231b3bf9dae3d298e4b9ffc58de1c413f.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 04/29] tree-walk.c: migrate to using new \"object_type\" field when possible","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:37Z","receivedAt":"2021-03-21T00:02:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.c | 24 +++++++++++++-----------\n 1 file changed, 13 insertions(+), 11 deletions(-)\n\ndiff --git a/tree-walk.c b/tree-walk.c\nindex b210967b73b..6e9161901d8 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -521,7 +521,7 @@ int traverse_trees(struct index_state *istate,\n \t\t\tif (!entry[i].path)\n \t\t\t\tcontinue;\n \t\t\tmask |= 1ul << i;\n-\t\t\tif (S_ISDIR(entry[i].mode))\n+\t\t\tif (entry[i].object_type == OBJ_TREE)\n \t\t\t\tdirmask |= 1ul << i;\n \t\t\te = &entry[i];\n \t\t}\n@@ -892,8 +892,8 @@ static int match_entry(const struct pathspec_item *item,\n \t\t * nothing else (to handle 'submod/' and 'submod'\n \t\t * uniformly).\n \t\t */\n-\t\tif (!S_ISDIR(entry->mode) &&\n-\t\t    (!S_ISGITLINK(entry->mode) || matchlen > pathlen + 1))\n+\t\tif (entry->object_type != OBJ_TREE &&\n+\t\t    (entry->object_type != OBJ_COMMIT || matchlen > pathlen + 1))\n \t\t\treturn 0;\n \t}\n \n@@ -1038,7 +1038,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t    ps->max_depth == -1)\n \t\t\treturn all_entries_interesting;\n \t\treturn within_depth(base->buf + base_offset, baselen,\n-\t\t\t\t    !!S_ISDIR(entry->mode),\n+\t\t\t\t    entry->object_type == OBJ_TREE,\n \t\t\t\t    ps->max_depth) ?\n \t\t\tentry_interesting : entry_not_interesting;\n \t}\n@@ -1071,7 +1071,7 @@ static enum interesting do_match(struct index_state *istate,\n \n \t\t\tif (within_depth(base_str + matchlen + 1,\n \t\t\t\t\t baselen - matchlen - 1,\n-\t\t\t\t\t !!S_ISDIR(entry->mode),\n+\t\t\t\t\t entry->object_type == OBJ_TREE,\n \t\t\t\t\t ps->max_depth))\n \t\t\t\tgoto interesting;\n \t\t\telse\n@@ -1094,7 +1094,8 @@ static enum interesting do_match(struct index_state *istate,\n \t\t\t\t * Match all directories. We'll try to\n \t\t\t\t * match files later on.\n \t\t\t\t */\n-\t\t\t\tif (ps->recursive && S_ISDIR(entry->mode))\n+\t\t\t\tif (ps->recursive &&\n+\t\t\t\t    entry->object_type == OBJ_TREE)\n \t\t\t\t\treturn entry_interesting;\n \n \t\t\t\t/*\n@@ -1105,7 +1106,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t\t\t * be performed in the submodule itself.\n \t\t\t\t */\n \t\t\t\tif (ps->recurse_submodules &&\n-\t\t\t\t    S_ISGITLINK(entry->mode) &&\n+\t\t\t\t    entry->object_type == OBJ_COMMIT &&\n \t\t\t\t    !ps_strncmp(item, match + baselen,\n \t\t\t\t\t\tentry->path,\n \t\t\t\t\t\titem->nowildcard_len - baselen))\n@@ -1154,7 +1155,8 @@ static enum interesting do_match(struct index_state *istate,\n \t\t * character.  More accurate matching can then\n \t\t * be performed in the submodule itself.\n \t\t */\n-\t\tif (ps->recurse_submodules && S_ISGITLINK(entry->mode) &&\n+\t\tif (ps->recurse_submodules &&\n+\t\t    entry->object_type == OBJ_COMMIT &&\n \t\t    !ps_strncmp(item, match, base->buf + base_offset,\n \t\t\t\titem->nowildcard_len)) {\n \t\t\tstrbuf_setlen(base, base_offset + baselen);\n@@ -1170,7 +1172,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t * in future, see\n \t\t * https://lore.kernel.org/git/7vmxo5l2g4.fsf@alter.siamese.dyndns.org/\n \t\t */\n-\t\tif (ps->recursive && S_ISDIR(entry->mode))\n+\t\tif (ps->recursive && entry->object_type == OBJ_TREE)\n \t\t\treturn entry_interesting;\n \t\tcontinue;\n interesting:\n@@ -1193,7 +1195,7 @@ static enum interesting do_match(struct index_state *istate,\n \t\t\t * can probably return all_entries_interesting or\n \t\t\t * all_entries_not_interesting here if matched.\n \t\t\t */\n-\t\t\tif (S_ISDIR(entry->mode))\n+\t\t\tif (entry->object_type == OBJ_TREE)\n \t\t\t\treturn entry_interesting;\n \n \t\t\tstrbuf_add(base, entry->path, pathlen);\n@@ -1269,7 +1271,7 @@ enum interesting tree_entry_interesting(struct index_state *istate,\n \t\treturn positive;\n \n \t/* #15, #19 */\n-\tif (S_ISDIR(entry->mode) &&\n+\tif (entry->object_type == OBJ_TREE &&\n \t    positive >= entry_interesting &&\n \t    negative == entry_interesting)\n \t\treturn entry_interesting;\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419816","messageId":"1b6a10f814c17e6c303b553cc060022e6d954dc7.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 08/29] cache.h: have base_name_compare() take \"is tree?\", not \"mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:41Z","receivedAt":"2021-03-21T00:02:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the base_name_compare() API and the related df_name_compare()\nfunction to take a boolean argument indicating whether the entry is a\ntree or not, instead of having them call S_ISDIR(mode) on their own.\n\nThis makes use of the new \"object_type\" field in the \"name_entry\".\n\nThe API being modified here was originally added way back in\n958ba6c96eb (Introduce \"base_name_compare()\" helper function,\n2005-05-20).\n\nNone of these comparison functions used to have tests, but with\npreceding commits some of them now do. I thought the remainder was\ntrivial enough to review without tests, and didn't want to spend more\ntime on them.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fast-import.c | 12 ++++++++----\n builtin/mktree.c      |  4 ++--\n cache.h               |  4 ++--\n combine-diff.c        |  8 +++++---\n match-trees.c         |  6 ++++--\n merge-ort.c           |  4 ++--\n merge-recursive.c     |  6 +++---\n read-cache.c          | 16 ++++++++--------\n tree-diff.c           |  7 +++++--\n unpack-trees.c        | 15 ++++++++-------\n 10 files changed, 47 insertions(+), 35 deletions(-)\n\ndiff --git a/builtin/fast-import.c b/builtin/fast-import.c\nindex 3afa81cf9ac..2fb56d6e9b7 100644\n--- a/builtin/fast-import.c\n+++ b/builtin/fast-import.c\n@@ -1287,18 +1287,22 @@ static int tecmp0 (const void *_a, const void *_b)\n {\n \tstruct tree_entry *a = *((struct tree_entry**)_a);\n \tstruct tree_entry *b = *((struct tree_entry**)_b);\n+\tint istree_a = S_ISDIR(a->versions[0].mode);\n+\tint istree_b = S_ISDIR(b->versions[0].mode);\n \treturn base_name_compare(\n-\t\ta->name->str_dat, a->name->str_len, a->versions[0].mode,\n-\t\tb->name->str_dat, b->name->str_len, b->versions[0].mode);\n+\t\ta->name->str_dat, a->name->str_len, istree_a,\n+\t\tb->name->str_dat, b->name->str_len, istree_b);\n }\n \n static int tecmp1 (const void *_a, const void *_b)\n {\n \tstruct tree_entry *a = *((struct tree_entry**)_a);\n \tstruct tree_entry *b = *((struct tree_entry**)_b);\n+\tint istree_a = S_ISDIR(a->versions[1].mode);\n+\tint istree_b = S_ISDIR(b->versions[1].mode);\n \treturn base_name_compare(\n-\t\ta->name->str_dat, a->name->str_len, a->versions[1].mode,\n-\t\tb->name->str_dat, b->name->str_len, b->versions[1].mode);\n+\t\ta->name->str_dat, a->name->str_len, istree_a,\n+\t\tb->name->str_dat, b->name->str_len, istree_b);\n }\n \n static void mktree(struct tree_content *t, int v, struct strbuf *b)\ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex 891991b00d6..2c1973229ac 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -37,8 +37,8 @@ static int ent_compare(const void *a_, const void *b_)\n {\n \tstruct treeent *a = *(struct treeent **)a_;\n \tstruct treeent *b = *(struct treeent **)b_;\n-\treturn base_name_compare(a->name, a->len, a->mode,\n-\t\t\t\t b->name, b->len, b->mode);\n+\treturn base_name_compare(a->name, a->len, S_ISDIR(a->mode),\n+\t\t\t\t b->name, b->len, S_ISDIR(b->mode));\n }\n \n static void write_tree(struct object_id *oid)\ndiff --git a/cache.h b/cache.h\nindex ae0c0bef5c2..e38b1e1688c 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1506,8 +1506,8 @@ int repo_interpret_branch_name(struct repository *r,\n \n int validate_headref(const char *ref);\n \n-int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n-int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n+int base_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n+int df_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);\n int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);\n \ndiff --git a/combine-diff.c b/combine-diff.c\nindex 06635f91bc2..155d52971e6 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -16,11 +16,13 @@\n static int compare_paths(const struct combine_diff_path *one,\n \t\t\t  const struct diff_filespec *two)\n {\n-\tif (!S_ISDIR(one->mode) && !S_ISDIR(two->mode))\n+\tint istree_one = S_ISDIR(one->mode);\n+\tint istree_two = S_ISDIR(two->mode);\n+\tif (!istree_one && !istree_two)\n \t\treturn strcmp(one->path, two->path);\n \n-\treturn base_name_compare(one->path, strlen(one->path), one->mode,\n-\t\t\t\t two->path, strlen(two->path), two->mode);\n+\treturn base_name_compare(one->path, strlen(one->path), istree_one,\n+\t\t\t\t two->path, strlen(two->path), istree_two);\n }\n \n static int filename_changed(char status)\ndiff --git a/match-trees.c b/match-trees.c\nindex 1011357ad0c..a28c19a62a5 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -67,8 +67,10 @@ static void *fill_tree_desc_strict(struct tree_desc *desc,\n static int base_name_entries_compare(const struct name_entry *a,\n \t\t\t\t     const struct name_entry *b)\n {\n-\treturn base_name_compare(a->path, tree_entry_len(a), a->mode,\n-\t\t\t\t b->path, tree_entry_len(b), b->mode);\n+\tint istree_a = (a->object_type == OBJ_TREE);\n+\tint istree_b = (b->object_type == OBJ_TREE);\n+\treturn base_name_compare(a->path, tree_entry_len(a), istree_a,\n+\t\t\t\t b->path, tree_entry_len(b), istree_b);\n }\n \n /*\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 92dea35e57a..68eaa8f294f 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -2389,8 +2389,8 @@ static int tree_entry_order(const void *a_, const void *b_)\n \n \tconst struct merged_info *ami = a->util;\n \tconst struct merged_info *bmi = b->util;\n-\treturn base_name_compare(a->string, strlen(a->string), ami->result.mode,\n-\t\t\t\t b->string, strlen(b->string), bmi->result.mode);\n+\treturn base_name_compare(a->string, strlen(a->string), S_ISDIR(ami->result.mode),\n+\t\t\t\t b->string, strlen(b->string), S_ISDIR(bmi->result.mode));\n }\n \n static void write_tree(struct object_id *result_oid,\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex ed31f9496cb..97520a88646 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -554,12 +554,12 @@ static int string_list_df_name_compare(const char *one, const char *two)\n \t *\n \t * To achieve this, we sort with df_name_compare and provide\n \t * the mode S_IFDIR so that D/F conflicts will sort correctly.\n-\t * We use the mode S_IFDIR for everything else for simplicity,\n+\t * We say we have a directory for everything else for simplicity,\n \t * since in other cases any changes in their order due to\n \t * sorting cause no problems for us.\n \t */\n-\tint cmp = df_name_compare(one, onelen, S_IFDIR,\n-\t\t\t\t  two, twolen, S_IFDIR);\n+\tint cmp = df_name_compare(one, onelen, 1, two, twolen, 1);\n+\n \t/*\n \t * Now that 'foo' and 'foo/bar' compare equal, we have to make sure\n \t * that 'foo' comes before 'foo/bar'.\ndiff --git a/read-cache.c b/read-cache.c\nindex 5a907af2fb5..6e0b41ed175 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -462,8 +462,8 @@ int ie_modified(struct index_state *istate,\n \treturn 0;\n }\n \n-int base_name_compare(const char *name1, int len1, int mode1,\n-\t\t      const char *name2, int len2, int mode2)\n+int base_name_compare(const char *name1, int len1, int istree1,\n+\t\t      const char *name2, int len2, int istree2)\n {\n \tunsigned char c1, c2;\n \tint len = len1 < len2 ? len1 : len2;\n@@ -474,9 +474,9 @@ int base_name_compare(const char *name1, int len1, int mode1,\n \t\treturn cmp;\n \tc1 = name1[len];\n \tc2 = name2[len];\n-\tif (!c1 && S_ISDIR(mode1))\n+\tif (!c1 && istree1)\n \t\tc1 = '/';\n-\tif (!c2 && S_ISDIR(mode2))\n+\tif (!c2 && istree2)\n \t\tc2 = '/';\n \treturn (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;\n }\n@@ -491,8 +491,8 @@ int base_name_compare(const char *name1, int len1, int mode1,\n  * This is used by routines that want to traverse the git namespace\n  * but then handle conflicting entries together when possible.\n  */\n-int df_name_compare(const char *name1, int len1, int mode1,\n-\t\t    const char *name2, int len2, int mode2)\n+int df_name_compare(const char *name1, int len1, int istree1,\n+\t\t    const char *name2, int len2, int istree2)\n {\n \tint len = len1 < len2 ? len1 : len2, cmp;\n \tunsigned char c1, c2;\n@@ -504,10 +504,10 @@ int df_name_compare(const char *name1, int len1, int mode1,\n \tif (len1 == len2)\n \t\treturn 0;\n \tc1 = name1[len];\n-\tif (!c1 && S_ISDIR(mode1))\n+\tif (!c1 && istree1)\n \t\tc1 = '/';\n \tc2 = name2[len];\n-\tif (!c2 && S_ISDIR(mode2))\n+\tif (!c2 && istree2)\n \t\tc2 = '/';\n \tif (c1 == '/' && !c2)\n \t\treturn 0;\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 7cebbb327e2..6ec180331fb 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -50,6 +50,7 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)\n {\n \tstruct name_entry *e1, *e2;\n \tint cmp;\n+\tint istree_e1, istree_e2;\n \n \t/* empty descriptors sort after valid tree entries */\n \tif (!t1->size)\n@@ -58,9 +59,11 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)\n \t\treturn -1;\n \n \te1 = &t1->entry;\n+\tistree_e1 = (e1->object_type == OBJ_TREE);\n \te2 = &t2->entry;\n-\tcmp = base_name_compare(e1->path, tree_entry_len(e1), e1->mode,\n-\t\t\t\te2->path, tree_entry_len(e2), e2->mode);\n+\tistree_e2 = (e2->object_type == OBJ_TREE);\n+\tcmp = base_name_compare(e1->path, tree_entry_len(e1), istree_e1,\n+\t\t\t\te2->path, tree_entry_len(e2), istree_e2);\n \treturn cmp;\n }\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 9298fe1d9b3..ea1ce7894ba 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -925,7 +925,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \t\t\t\t      const struct traverse_info *info,\n \t\t\t\t      const char *name, size_t namelen,\n-\t\t\t\t      unsigned mode)\n+\t\t\t\t      unsigned istree)\n {\n \tint pathlen, ce_len;\n \tconst char *ce_name;\n@@ -933,7 +933,7 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \tif (info->prev) {\n \t\tint cmp = do_compare_entry_piecewise(ce, info->prev,\n \t\t\t\t\t\t     info->name, info->namelen,\n-\t\t\t\t\t\t     info->mode);\n+\t\t\t\t\t\t     S_ISDIR(info->mode));\n \t\tif (cmp)\n \t\t\treturn cmp;\n \t}\n@@ -947,13 +947,13 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \tce_len -= pathlen;\n \tce_name = ce->name + pathlen;\n \n-\treturn df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n+\treturn df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n }\n \n static int do_compare_entry(const struct cache_entry *ce,\n \t\t\t    const struct traverse_info *info,\n \t\t\t    const char *name, size_t namelen,\n-\t\t\t    unsigned mode)\n+\t\t\t    unsigned istree)\n {\n \tint pathlen, ce_len;\n \tconst char *ce_name;\n@@ -965,7 +965,7 @@ static int do_compare_entry(const struct cache_entry *ce,\n \t * it is quicker to use the precomputed version.\n \t */\n \tif (!info->traverse_path)\n-\t\treturn do_compare_entry_piecewise(ce, info, name, namelen, mode);\n+\t\treturn do_compare_entry_piecewise(ce, info, name, namelen, istree);\n \n \tcmp = strncmp(ce->name, info->traverse_path, info->pathlen);\n \tif (cmp)\n@@ -980,12 +980,13 @@ static int do_compare_entry(const struct cache_entry *ce,\n \tce_len -= pathlen;\n \tce_name = ce->name + pathlen;\n \n-\treturn df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n+\treturn df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n }\n \n static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)\n {\n-\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);\n+\tint istree = (n->object_type == OBJ_TREE);\n+\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, istree);\n \tif (cmp)\n \t\treturn cmp;\n \n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419797","messageId":"f8912a409b59cbcbacc164e78e05f8acc2649906.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 09/29] tree-walk.h users: switch object_type(...) to new .object_type","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:42Z","receivedAt":"2021-03-21T00:02:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change uses of object_type(entry.mode) to use the new\nentry.object_type field.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/pack-objects.c |  2 +-\n http-push.c            |  6 ++++--\n pack-bitmap-write.c    |  8 +++++---\n revision.c             | 12 ++++++++----\n 4 files changed, 18 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 4bb602688c1..12fcbb3b8d4 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -1534,7 +1534,7 @@ static void add_pbase_object(struct tree_desc *tree,\n \t\t\treturn;\n \t\tif (name[cmplen] != '/') {\n \t\t\tadd_object_entry(&entry.oid,\n-\t\t\t\t\t object_type(entry.mode),\n+\t\t\t\t\t entry.object_type,\n \t\t\t\t\t fullname, 1);\n \t\t\treturn;\n \t\t}\ndiff --git a/http-push.c b/http-push.c\nindex b60d5fcc85d..aeb011de8d4 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1314,7 +1314,7 @@ static struct object_list **process_tree(struct tree *tree,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry))\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tp = process_tree(lookup_tree(the_repository, &entry.oid),\n \t\t\t\t\t p);\n@@ -1323,9 +1323,11 @@ static struct object_list **process_tree(struct tree *tree,\n \t\t\tp = process_blob(lookup_blob(the_repository, &entry.oid),\n \t\t\t\t\t p);\n \t\t\tbreak;\n-\t\tdefault:\n+\t\tcase OBJ_COMMIT:\n \t\t\t/* Subproject commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \n \tfree_tree_buffer(tree);\ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex 88d9e696a54..ac32bf2242c 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -353,7 +353,7 @@ static void fill_bitmap_tree(struct bitmap *bitmap,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry)) {\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tfill_bitmap_tree(bitmap,\n \t\t\t\t\t lookup_tree(the_repository, &entry.oid));\n@@ -361,9 +361,11 @@ static void fill_bitmap_tree(struct bitmap *bitmap,\n \t\tcase OBJ_BLOB:\n \t\t\tbitmap_set(bitmap, find_object_pos(&entry.oid));\n \t\t\tbreak;\n-\t\tdefault:\n-\t\t\t/* Gitlink, etc; not reachable */\n+\t\tcase OBJ_COMMIT:\n+\t\t\t/* submodule commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n \ndiff --git a/revision.c b/revision.c\nindex 99c859f7971..aba74cb65a2 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -72,16 +72,18 @@ static void mark_tree_contents_uninteresting(struct repository *r,\n \n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \twhile (tree_entry(&desc, &entry)) {\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tmark_tree_uninteresting(r, lookup_tree(r, &entry.oid));\n \t\t\tbreak;\n \t\tcase OBJ_BLOB:\n \t\t\tmark_blob_uninteresting(lookup_blob(r, &entry.oid));\n \t\t\tbreak;\n-\t\tdefault:\n+\t\tcase OBJ_COMMIT:\n \t\t\t/* Subproject commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n \n@@ -179,7 +181,7 @@ static void add_children_by_path(struct repository *r,\n \n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \twhile (tree_entry(&desc, &entry)) {\n-\t\tswitch (object_type(entry.mode)) {\n+\t\tswitch (entry.object_type) {\n \t\tcase OBJ_TREE:\n \t\t\tpaths_and_oids_insert(map, entry.path, &entry.oid);\n \n@@ -196,9 +198,11 @@ static void add_children_by_path(struct repository *r,\n \t\t\t\t\tchild->object.flags |= UNINTERESTING;\n \t\t\t}\n \t\t\tbreak;\n-\t\tdefault:\n+\t\tcase OBJ_COMMIT:\n \t\t\t/* Subproject commit - not in this repository */\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n \n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419799","messageId":"592fecb7abc55ff17eed6b3bcc58a6d9991a337e.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 15/29] merge-tree tests: test for the mode comparison in same_entry()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:48Z","receivedAt":"2021-03-21T00:02:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test to stress the \"a->mode == b->mode\" comparison in\nmerge-tree.c's same_entry().\n\nThat code was initially added by Linus in 33deb63a36f (Add\n\"merge-tree\" helper program. Maybe it's retarded, maybe it's helpful.,\n2005-04-14), and then again in its current form in\n492e0759bfe (Handling large files with GIT, 2006-02-14).\n\nHowever, nothing was testing that we handled this case\ncorrectly. Simply removing the mode comparison left all tests passing,\nbut as seen here it's important that we don't think a path with the\nsame content but different modes is the same_entry().\n\nThe rest of this series will touch code that's relevant to this, but\nwon't change its behavior. This test is just something I came up with\nin testing whether the mode test in same_entry() was needed at all.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4300-merge-tree.sh | 44 +++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 44 insertions(+)\n\ndiff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh\nindex e59601e5fe9..f783d784d02 100755\n--- a/t/t4300-merge-tree.sh\n+++ b/t/t4300-merge-tree.sh\n@@ -40,6 +40,25 @@ test_expect_success 'file add A, B (same)' '\n \ttest_must_be_empty actual\n '\n \n+test_expect_success 'file add A, B (different mode)' '\n+\tgit reset --hard initial &&\n+\ttest_commit \"add-a-b-same-diff-mode-A\" \"ONE\" \"AAA\" &&\n+\tgit reset --hard initial &&\n+\techo AAA >ONE &&\n+\ttest_chmod +x ONE &&\n+\ttest_tick &&\n+\tgit commit -m\"add-a-b-same-diff-mode-B\" &&\n+\tgit tag \"add-a-b-same-diff-mode-B\" HEAD &&\n+\tgit merge-tree initial add-a-b-same-diff-mode-A add-a-b-same-diff-mode-B >actual &&\n+\tcat >expected <<EXPECTED &&\n+added in both\n+  our    100644 $(git rev-parse add-a-b-same-diff-mode-A:ONE) ONE\n+  their  100755 $(git rev-parse add-a-b-same-diff-mode-B:ONE) ONE\n+EXPECTED\n+\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'file add A, B (different)' '\n \tgit reset --hard initial &&\n \ttest_commit \"add-a-b-diff-A\" \"ONE\" \"AAA\" &&\n@@ -61,6 +80,31 @@ EXPECTED\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'file add A, B (different and different mode)' '\n+\tgit reset --hard initial &&\n+\ttest_commit \"add-a-b-diff-diff-mode-A\" \"ONE\" \"AAA\" &&\n+\tgit reset --hard initial &&\n+\techo BBB >ONE &&\n+\ttest_chmod +x ONE &&\n+\ttest_tick &&\n+\tgit commit -m\"add-a-b-diff-diff-mode-B\" &&\n+\tgit tag \"add-a-b-diff-diff-mode-B\" &&\n+\tgit merge-tree initial add-a-b-diff-diff-mode-A add-a-b-diff-diff-mode-B >actual &&\n+\tcat >expected <<EXPECTED &&\n+added in both\n+  our    100644 $(git rev-parse add-a-b-diff-diff-mode-A:ONE) ONE\n+  their  100755 $(git rev-parse add-a-b-diff-diff-mode-B:ONE) ONE\n+@@ -1 +1,5 @@\n++<<<<<<< .our\n+ AAA\n++=======\n++BBB\n++>>>>>>> .their\n+EXPECTED\n+\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'file change A, !B' '\n \tgit reset --hard initial &&\n \ttest_commit \"change-a-not-b\" \"initial-file\" \"BBB\" &&\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419800","messageId":"b0546197b1bf02db941eeb3aa818344452d21da9.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 06/29] mktree tests: test that \"mode\" is passed when sorting","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:39Z","receivedAt":"2021-03-21T00:02:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for the mode being passed to ent_compare(). That code dates\nback to 83f50539a9 (git-mktree: reverse of git-ls-tree., 2006-02-20)\nand there's never been a test for that particular edge case. Now we\nhave one.\n\nI don't see how anything could run into this in practice. In order for\nthat mode sorting to matter as a tiebreaker we need to have a\nduplicate entry in the tree, i.e. two \"foo\" entries, one a blob and\none a tree. This asserts that if that happens we'll sort on the modes\nwe encounter in such an invalid entry, i.e. we expect the tree entry\nbefore the blob.\n\nAs shown here we'd need to disable the fsck.duplicateEntries error to\nget to the point of running \"mktree\", so I doubt anyone's pushing this\nsort of data around.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t1450-fsck.sh | 44 ++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 44 insertions(+)\n\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 5071ac63a5b..46125190b45 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -318,6 +318,50 @@ test_expect_success 'tree entry with type mismatch' '\n \ttest_i18ngrep ! \"dangling blob\" out\n '\n \n+test_expect_success 'tree entry with duplicate type mismatching objects' '\n+\ttest_create_repo duplicate-entry &&\n+\t(\n+\t\tcd duplicate-entry &&\n+\t\tblob=\"$(printf \"foo\" | git hash-object -w --stdin)\" &&\n+\t\ttree=\"$(printf \"100644 blob $blob\\tfoo\" | git mktree)\" &&\n+\t\tcommit=\"$(git commit-tree $tree -m \"first commit\")\" &&\n+\t\tgit cat-file commit $commit >good-commit &&\n+\n+\t\t# First bad commit, wrong type, but in the right order\n+\t\tprintf \"40000 A\\0$(echo $tree | hex2oct)\" >broken-tree-A &&\n+\t\tprintf \"100644 A\\0$(echo $blob | hex2oct)\" >broken-tree-B &&\n+\t\tcat broken-tree-A broken-tree-B >broken-tree.1 &&\n+\t\tbroken_tree1=\"$(git hash-object -w --literally -t tree broken-tree.1)\" &&\n+\t\tbad_commit1=\"$(git commit-tree $broken_tree1 -m \"bad commit 1\")\" &&\n+\t\tgit cat-file commit $bad_commit1 >bad-commit.1 &&\n+\t\tgit update-ref refs/heads/broken-commit-1 $bad_commit1 &&\n+\n+\t\ttest_must_fail git fsck &&\n+\t\tgit -c fsck.duplicateEntries=warn fsck 2>err &&\n+\t\tgrep \" in tree .*$broken_tree1: duplicateEntries\" err &&\n+\n+\t\t# Second bad commits, wrong types and order\n+\t\tcat broken-tree-B broken-tree-A >broken-tree.2 &&\n+\t\tbroken_tree2=\"$(git hash-object -w --literally -t tree broken-tree.2)\" &&\n+\t\tbad_commit2=\"$(git commit-tree $broken_tree2 -m \"bad commit 2\")\" &&\n+\t\tgit cat-file commit $bad_commit2 >bad-commit.2 &&\n+\t\tgit update-ref refs/heads/broken-commit-2 $bad_commit2 &&\n+\n+\t\ttest_must_fail git fsck &&\n+\t\tgit -c fsck.duplicateEntries=warn fsck 2>err &&\n+\t\tgrep \" in tree .*$broken_tree2: duplicateEntries\" err &&\n+\n+\t\t# git mktree should \"fix\" the order of this already broken data\n+\t\tgit ls-tree broken-commit-1 >broken-tree-1-ls &&\n+\t\tgit ls-tree broken-commit-2 >broken-tree-2-ls &&\n+\t\t! test_cmp broken-tree-1-ls broken-tree-2-ls &&\n+\n+\t\tgit mktree <broken-tree-1-ls >broken-mktree-1 &&\n+\t\tgit mktree <broken-tree-2-ls >broken-mktree-2 &&\n+\t\ttest_cmp broken-mktree-1 broken-mktree-2\n+\t)\n+'\n+\n test_expect_success 'tag pointing to nonexistent' '\n \tbadoid=$(test_oid deadbeef) &&\n \tcat >invalid-tag <<-EOF &&\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419801","messageId":"152c6d88542cbf88367f5a67b677c9ad293d28c0.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 11/29] tree.h API: make read_tree_fn_t take an \"enum object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:44Z","receivedAt":"2021-03-21T00:02:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Most of the users of the read_tree_fn_t callback do not care about the\n\"mode\" per-se, they just care what type it resolves to.\n\nAmend this callback mechanism added in 3c5e8468a93 (ls-tree: major\nrewrite to do pathspec, 2005-11-26) to pass the object_type, and use\nit whenever possible.\n\nIn the archive.c code we could go much deeper with this refactoring,\nafter getting the \"mode\" that code will pass it around itself and into\narchive-{tar,zip}.c. As far as I can tell we could drop the mode\nearly, and just pass \"enum_object_type type, int is_executable\". That\nwould be slightly redundant space-wise, but would assure us that we're\nnot writing out raw modes found in trees, but are normalizing them.\n\nBut that particular refactoring would be larger than what I'm trying\nto accomplish here, so let's leave it for now.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          |  8 ++++----\n builtin/checkout.c |  4 ++--\n builtin/log.c      |  4 ++--\n builtin/ls-files.c |  6 ++++--\n builtin/ls-tree.c  | 12 +++++-------\n merge-recursive.c  |  6 ++++--\n tree.c             | 19 ++++++++++++-------\n tree.h             |  2 +-\n 8 files changed, 34 insertions(+), 27 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex feed5c02fb2..ca28d6ca3b8 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -230,7 +230,7 @@ static int write_directory(struct archiver_context *c)\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\t\t\t\tstruct strbuf *base, const char *filename,\n-\t\t\t\t\tunsigned mode,\n+\t\t\t\t\tenum object_type object_type, unsigned mode,\n \t\t\t\t\tvoid *context)\n {\n \tstruct archiver_context *c = context;\n@@ -243,7 +243,7 @@ static int queue_or_write_archive_entry(const struct object_id *oid,\n \t\tc->bottom = next;\n \t}\n \n-\tif (S_ISDIR(mode)) {\n+\tif (object_type == OBJ_TREE) {\n \t\tsize_t baselen = base->len;\n \t\tconst struct attr_check *check;\n \n@@ -378,13 +378,13 @@ struct path_exists_context {\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n \t\t\tconst char *filename,\n-\t\t\tunsigned mode,\n+\t\t\tenum object_type object_type, unsigned mode,\n \t\t\tvoid *context)\n {\n \tint ret = -1;\n \tstruct path_exists_context *ctx = context;\n \n-\tif (S_ISDIR(mode)) {\n+\tif (object_type == OBJ_TREE) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tstrbuf_addbuf(&sb, base);\n \t\tstrbuf_addstr(&sb, filename);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0887352db2a..95b2b0edf22 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -115,14 +115,14 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n \n static int update_some(const struct object_id *oid, struct strbuf *base,\n \t\t       const char *pathname,\n-\t\t       unsigned mode,\n+\t\t       enum object_type object_type, unsigned mode,\n \t\t       void *context)\n {\n \tint len;\n \tstruct cache_entry *ce;\n \tint pos;\n \n-\tif (S_ISDIR(mode))\n+\tif (object_type == OBJ_TREE)\n \t\treturn READ_TREE_RECURSIVE;\n \n \tlen = base->len + strlen(pathname);\ndiff --git a/builtin/log.c b/builtin/log.c\nindex b7b76856a9f..c25f75472ff 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -599,11 +599,11 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n \n static int show_tree_object(const struct object_id *oid,\n \t\t\t    struct strbuf *base, const char *pathname,\n-\t\t\t    unsigned mode,\n+\t\t\t    enum object_type object_type, unsigned mode,\n \t\t\t    void *context)\n {\n \tFILE *file = context;\n-\tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\n+\tfprintf(file, \"%s%s\\n\", pathname, object_type == OBJ_TREE ? \"/\" : \"\");\n \treturn 0;\n }\n \ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 60a2913a01e..cb217870cbd 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -446,7 +446,8 @@ static int read_one_entry_opt(struct index_state *istate,\n }\n \n static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\t  const char *pathname, unsigned mode,\n+\t\t\t  const char *pathname,\n+\t\t\t  enum object_type object_type, unsigned mode,\n \t\t\t  void *context)\n {\n \tstruct index_state *istate = context;\n@@ -460,7 +461,8 @@ static int read_one_entry(const struct object_id *oid, struct strbuf *base,\n  * the stage that will conflict with the entry being added.\n  */\n static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,\n-\t\t\t\tconst char *pathname, unsigned mode,\n+\t\t\t\tconst char *pathname,\n+\t\t\t\tenum object_type object_type, unsigned mode,\n \t\t\t\tvoid *context)\n {\n \tstruct index_state *istate = context;\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 8d5c3fd0582..7176d2ae065 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -63,14 +63,13 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\n \n static int show_tree(const struct object_id *oid, struct strbuf *base,\n \t\t     const char *pathname,\n-\t\t     unsigned mode,\n+\t\t     enum object_type object_type, unsigned mode,\n \t\t     void *context)\n {\n \tint retval = 0;\n \tint baselen;\n-\tconst char *type = blob_type;\n \n-\tif (S_ISGITLINK(mode)) {\n+\tif (object_type == OBJ_COMMIT) {\n \t\t/*\n \t\t * Maybe we want to have some recursive version here?\n \t\t *\n@@ -80,22 +79,21 @@ static int show_tree(const struct object_id *oid, struct strbuf *base,\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} else if (object_type == OBJ_TREE) {\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}\n \telse if (ls_options & LS_TREE_ONLY)\n \t\treturn 0;\n \n \tif (!(ls_options & LS_NAME_ONLY)) {\n+\t\tconst char *type = type_name(object_type);\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 (object_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),\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 57198b29fe7..6bc1e659a7b 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,7 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   unsigned int mode,\n+\t\t\t   enum object_type object_type, unsigned int mode,\n \t\t\t   void *context)\n {\n \tstruct path_hashmap_entry *entry;\n@@ -467,7 +467,9 @@ static int save_files_dirs(const struct object_id *oid,\n \thashmap_add(&opt->priv->current_file_dir_set, &entry->e);\n \n \tstrbuf_setlen(base, baselen);\n-\treturn (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);\n+\tif (object_type != OBJ_TREE)\n+\t\treturn 0;\n+\treturn READ_TREE_RECURSIVE;\n }\n \n static void get_files_dirs(struct merge_options *opt, struct tree *tree)\ndiff --git a/tree.c b/tree.c\nindex 410e3b477e5..f48b170090f 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -28,6 +28,8 @@ int read_tree_at(struct repository *r,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry)) {\n+\t\tstruct commit *commit;\n+\n \t\tif (retval != all_entries_interesting) {\n \t\t\tretval = tree_entry_interesting(r->index, &entry,\n \t\t\t\t\t\t\tbase, 0, pathspec);\n@@ -38,7 +40,7 @@ int read_tree_at(struct repository *r,\n \t\t}\n \n \t\tswitch (fn(&entry.oid, base,\n-\t\t\t   entry.path, entry.mode, context)) {\n+\t\t\t   entry.path, entry.object_type, entry.mode, context)) {\n \t\tcase 0:\n \t\t\tcontinue;\n \t\tcase READ_TREE_RECURSIVE:\n@@ -47,11 +49,11 @@ int read_tree_at(struct repository *r,\n \t\t\treturn -1;\n \t\t}\n \n-\t\tif (S_ISDIR(entry.mode))\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_TREE:\n \t\t\toidcpy(&oid, &entry.oid);\n-\t\telse if (S_ISGITLINK(entry.mode)) {\n-\t\t\tstruct commit *commit;\n-\n+\t\t\tbreak;\n+\t\tcase OBJ_COMMIT:\n \t\t\tcommit = lookup_commit(r, &entry.oid);\n \t\t\tif (!commit)\n \t\t\t\tdie(\"Commit %s in submodule path %s%s not found\",\n@@ -64,9 +66,12 @@ int read_tree_at(struct repository *r,\n \t\t\t\t    base->buf, entry.path);\n \n \t\t\toidcpy(&oid, get_commit_tree_oid(commit));\n-\t\t}\n-\t\telse\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n \t\t\tcontinue;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n+\t\t}\n \n \t\tlen = tree_entry_len(&entry);\n \t\tstrbuf_add(base, entry.path, len);\ndiff --git a/tree.h b/tree.h\nindex 10c8637ab3e..1675da7e85c 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -33,7 +33,7 @@ int cmp_cache_name_compare(const void *a_, const void *b_);\n #define READ_TREE_RECURSIVE 1\n typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *,\n \t\t\t      const char *,\n-\t\t\t      unsigned int,\n+\t\t\t      enum object_type, unsigned int,\n \t\t\t      void *);\n \n int read_tree_at(struct repository *r,\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419802","messageId":"4fed508f3cbb106c6359d31ca53d53e078fadfd7.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 03/29] tree-walk.h: add object_type member to name_entry","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:36Z","receivedAt":"2021-03-21T00:02:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Most users of the tree walking API don't care what the specific mode\nof an object in a tree is (e.g. if it's executable), they care if it's\none of OBJ_{TREE,BLOB,COMMIT}.\n\nLet's add an \"object_type\" enum to the \"name_entry\" struct to help\nsuch callers.\n\nIdeally we'd have some subset of \"enum object_type\" here with just\nthose three entries, so we could rely on the C compiler to\nexhaustively check our \"switch\" statements, but I don't know how to\ncreate such a enum subset without re-labeling OBJ_{TREE,BLOB,COMMIT}\nto e.g. \"NE_OBJ_*\" (an enum is just an int under the hood, so you can\nuse such a struct with \"OBJ_*\", but the compiler will complain...).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.c | 4 +++-\n tree-walk.h | 2 ++\n 2 files changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 2d6226d5f18..b210967b73b 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -47,7 +47,9 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l\n \n \t/* Initialize the descriptor entry */\n \tdesc->entry.path = path;\n-\tdesc->entry.mode = canon_mode(mode);\n+\tmode = canon_mode(mode);\n+\tdesc->entry.mode = mode;\n+\tdesc->entry.object_type = object_type(mode);\n \tdesc->entry.pathlen = len - 1;\n \thashcpy(desc->entry.oid.hash, (const unsigned char *)path + len);\n \ndiff --git a/tree-walk.h b/tree-walk.h\nindex a5058469e9b..9f3825d2773 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -17,6 +17,8 @@ struct name_entry {\n \tconst char *path;\n \tint pathlen;\n \tunsigned int mode;\n+\t/* simple 'mode': Only OBJ_{BLOB,TREE,COMMIT} */\n+\tenum object_type object_type;\n };\n \n /**\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419804","messageId":"163922d427c69d6fb62dea0ed8bb3a112379c7e7.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 13/29] tree-walk.h users: refactor chained \"mode\" if/else into switch","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:46Z","receivedAt":"2021-03-21T00:02:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor a couple of \"switch\" statements that previously relied on\n\"entry.mode\" to switch on \"entry.object_type\" instead.\n\nThis is more obvious, and allows us to explicitly handle all the OBJ_*\ncases, not just have a wildcard \"else\". That doesn't matter for the\nbehavior of this code, but for its readability and maintainability.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n list-objects.c | 20 ++++++++++++++------\n walker.c       | 22 +++++++++++++---------\n 2 files changed, 27 insertions(+), 15 deletions(-)\n\ndiff --git a/list-objects.c b/list-objects.c\nindex e19589baa04..37434ba89d3 100644\n--- a/list-objects.c\n+++ b/list-objects.c\n@@ -111,6 +111,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \n \twhile (tree_entry(&desc, &entry)) {\n+\t\tstruct tree *t;\n+\t\tstruct blob *b;\n+\n \t\tif (match != all_entries_interesting) {\n \t\t\tmatch = tree_entry_interesting(ctx->revs->repo->index,\n \t\t\t\t\t\t       &entry, base, 0,\n@@ -121,8 +124,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n \t\t\t\tcontinue;\n \t\t}\n \n-\t\tif (S_ISDIR(entry.mode)) {\n-\t\t\tstruct tree *t = lookup_tree(ctx->revs->repo, &entry.oid);\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_TREE:\n+\t\t\tt = lookup_tree(ctx->revs->repo, &entry.oid);\n \t\t\tif (!t) {\n \t\t\t\tdie(_(\"entry '%s' in tree %s has tree mode, \"\n \t\t\t\t      \"but is not a tree\"),\n@@ -130,12 +134,13 @@ static void process_tree_contents(struct traversal_context *ctx,\n \t\t\t}\n \t\t\tt->object.flags |= NOT_USER_GIVEN;\n \t\t\tprocess_tree(ctx, t, base, entry.path);\n-\t\t}\n-\t\telse if (S_ISGITLINK(entry.mode))\n+\t\t\tbreak;\n+\t\tcase OBJ_COMMIT:\n \t\t\tprocess_gitlink(ctx, entry.oid.hash,\n \t\t\t\t\tbase, entry.path);\n-\t\telse {\n-\t\t\tstruct blob *b = lookup_blob(ctx->revs->repo, &entry.oid);\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n+\t\t\tb = lookup_blob(ctx->revs->repo, &entry.oid);\n \t\t\tif (!b) {\n \t\t\t\tdie(_(\"entry '%s' in tree %s has blob mode, \"\n \t\t\t\t      \"but is not a blob\"),\n@@ -143,6 +148,9 @@ static void process_tree_contents(struct traversal_context *ctx,\n \t\t\t}\n \t\t\tb->object.flags |= NOT_USER_GIVEN;\n \t\t\tprocess_blob(ctx, b, base, entry.path);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t}\n }\ndiff --git a/walker.c b/walker.c\nindex 4984bf8b3d6..7ba757244e6 100644\n--- a/walker.c\n+++ b/walker.c\n@@ -45,21 +45,25 @@ static int process_tree(struct walker *walker, struct tree *tree)\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \twhile (tree_entry(&desc, &entry)) {\n \t\tstruct object *obj = NULL;\n+\t\tstruct tree *tree;\n+\t\tstruct blob *blob;\n \n-\t\t/* submodule commits are not stored in the superproject */\n-\t\tif (S_ISGITLINK(entry.mode))\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_COMMIT:\n+\t\t\t/* submodule commits are not stored in the superproject */\n \t\t\tcontinue;\n-\t\tif (S_ISDIR(entry.mode)) {\n-\t\t\tstruct tree *tree = lookup_tree(the_repository,\n-\t\t\t\t\t\t\t&entry.oid);\n+\t\tcase OBJ_TREE:\n+\t\t\ttree = lookup_tree(the_repository, &entry.oid);\n \t\t\tif (tree)\n \t\t\t\tobj = &tree->object;\n-\t\t}\n-\t\telse {\n-\t\t\tstruct blob *blob = lookup_blob(the_repository,\n-\t\t\t\t\t\t\t&entry.oid);\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n+\t\t\tblob = lookup_blob(the_repository, &entry.oid);\n \t\t\tif (blob)\n \t\t\t\tobj = &blob->object;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n \t\tif (!obj || process(walker, obj))\n \t\t\treturn -1;\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419805","messageId":"21df7c668bedcd6e6008e783c2c6c1eb7cd13431.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 14/29] tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:47Z","receivedAt":"2021-03-21T00:02:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor more users of the \"entry.mode\" field to use the new\n\"entry.object_type\" field.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/grep.c         | 6 +++---\n builtin/merge-tree.c   | 9 +++++----\n builtin/pack-objects.c | 4 ++--\n builtin/reflog.c       | 3 ++-\n cache-tree.c           | 2 +-\n delta-islands.c        | 2 +-\n notes.c                | 4 ++--\n unpack-trees.c         | 2 +-\n 8 files changed, 17 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex ccd8d08f3dd..6349bbf59ee 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -587,10 +587,10 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,\n \n \t\tstrbuf_add(base, entry.path, te_len);\n \n-\t\tif (S_ISREG(entry.mode)) {\n+\t\tif (entry.object_type == OBJ_BLOB) {\n \t\t\thit |= grep_oid(opt, &entry.oid, base->buf, tn_len,\n \t\t\t\t\t check_attr ? base->buf + tn_len : NULL);\n-\t\t} else if (S_ISDIR(entry.mode)) {\n+\t\t} else if (entry.object_type == OBJ_TREE) {\n \t\t\tenum object_type type;\n \t\t\tstruct tree_desc sub;\n \t\t\tvoid *data;\n@@ -606,7 +606,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,\n \t\t\thit |= grep_tree(opt, pathspec, &sub, base, tn_len,\n \t\t\t\t\t check_attr);\n \t\t\tfree(data);\n-\t\t} else if (recurse_submodules && S_ISGITLINK(entry.mode)) {\n+\t\t} else if (recurse_submodules && entry.object_type == OBJ_COMMIT) {\n \t\t\thit |= grep_submodule(opt, pathspec, &entry.oid,\n \t\t\t\t\t      base->buf, base->buf + tn_len,\n \t\t\t\t\t      1); /* ignored */\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex 2de34c2d485..12cb317c1ba 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -275,11 +275,11 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3])\n \tif (dirmask == mask)\n \t\treturn;\n \n-\tif (n[2].mode && !S_ISDIR(n[2].mode))\n+\tif (n[2].object_type != OBJ_TREE)\n \t\tentry = link_entry(3, info, n + 2, entry);\n-\tif (n[1].mode && !S_ISDIR(n[1].mode))\n+\tif (n[1].object_type != OBJ_TREE)\n \t\tentry = link_entry(2, info, n + 1, entry);\n-\tif (n[0].mode && !S_ISDIR(n[0].mode))\n+\tif (n[0].object_type != OBJ_TREE)\n \t\tentry = link_entry(1, info, n + 0, entry);\n \n \tadd_merge_entry(entry);\n@@ -324,7 +324,8 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s\n \t}\n \n \tif (same_entry(entry+0, entry+1)) {\n-\t\tif (!is_null_oid(&entry[2].oid) && !S_ISDIR(entry[2].mode)) {\n+\t\tif (!is_null_oid(&entry[2].oid) &&\n+\t\t    entry[2].object_type != OBJ_TREE) {\n \t\t\t/* We did not touch, they modified -- take theirs */\n \t\t\tresolve(info, entry+1, entry+2);\n \t\t\treturn mask;\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 12fcbb3b8d4..6079d1d7935 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -1524,7 +1524,7 @@ static void add_pbase_object(struct tree_desc *tree,\n \tint cmp;\n \n \twhile (tree_entry(tree,&entry)) {\n-\t\tif (S_ISGITLINK(entry.mode))\n+\t\tif (entry.object_type == OBJ_COMMIT)\n \t\t\tcontinue;\n \t\tcmp = tree_entry_len(&entry) != cmplen ? 1 :\n \t\t      memcmp(name, entry.path, cmplen);\n@@ -1538,7 +1538,7 @@ static void add_pbase_object(struct tree_desc *tree,\n \t\t\t\t\t fullname, 1);\n \t\t\treturn;\n \t\t}\n-\t\tif (S_ISDIR(entry.mode)) {\n+\t\tif (entry.object_type == OBJ_TREE) {\n \t\t\tstruct tree_desc sub;\n \t\t\tstruct pbase_tree_cache *tree;\n \t\t\tconst char *down = name+cmplen+1;\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 09541d1c804..bcbca82aa90 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -95,7 +95,8 @@ static int tree_is_complete(const struct object_id *oid)\n \tcomplete = 1;\n \twhile (tree_entry(&desc, &entry)) {\n \t\tif (!has_object_file(&entry.oid) ||\n-\t\t    (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {\n+\t\t    (entry.object_type == OBJ_TREE &&\n+\t\t     !tree_is_complete(&entry.oid))) {\n \t\t\ttree->object.flags |= INCOMPLETE;\n \t\t\tcomplete = 0;\n \t\t}\ndiff --git a/cache-tree.c b/cache-tree.c\nindex add1f077131..c15a6829585 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -726,7 +726,7 @@ static void prime_cache_tree_rec(struct repository *r,\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \tcnt = 0;\n \twhile (tree_entry(&desc, &entry)) {\n-\t\tif (!S_ISDIR(entry.mode))\n+\t\tif (entry.object_type != OBJ_TREE)\n \t\t\tcnt++;\n \t\telse {\n \t\t\tstruct cache_tree_sub *sub;\ndiff --git a/delta-islands.c b/delta-islands.c\nindex aa98b2e5414..e7cf93acbe3 100644\n--- a/delta-islands.c\n+++ b/delta-islands.c\n@@ -293,7 +293,7 @@ void resolve_tree_islands(struct repository *r,\n \t\twhile (tree_entry(&desc, &entry)) {\n \t\t\tstruct object *obj;\n \n-\t\t\tif (S_ISGITLINK(entry.mode))\n+\t\t\tif (entry.object_type == OBJ_COMMIT)\n \t\t\t\tcontinue;\n \n \t\t\tobj = lookup_object(r, &entry.oid);\ndiff --git a/notes.c b/notes.c\nindex e2fec12a39e..8b03ace52bf 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -418,7 +418,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\tif (path_len == 2 * (hashsz - prefix_len)) {\n \t\t\t/* This is potentially the remainder of the SHA-1 */\n \n-\t\t\tif (!S_ISREG(entry.mode))\n+\t\t\tif (entry.object_type != OBJ_BLOB)\n \t\t\t\t/* notes must be blobs */\n \t\t\t\tgoto handle_non_note;\n \n@@ -431,7 +431,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t/* This is potentially an internal node */\n \t\t\tsize_t len = prefix_len;\n \n-\t\t\tif (!S_ISDIR(entry.mode))\n+\t\t\tif (entry.object_type != OBJ_TREE)\n \t\t\t\t/* internal nodes must be trees */\n \t\t\t\tgoto handle_non_note;\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex e6cb505fcb0..72b2cf5a741 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1300,7 +1300,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n \tif (dirmask) {\n \t\t/* special case: \"diff-index --cached\" looking at a tree */\n \t\tif (o->diff_index_cached &&\n-\t\t    n == 1 && dirmask == 1 && S_ISDIR(names->mode)) {\n+\t\t    n == 1 && dirmask == 1 && names->object_type == OBJ_TREE) {\n \t\t\tint matches;\n \t\t\tmatches = cache_tree_matches_traversal(o->src_index->cache_tree,\n \t\t\t\t\t\t\t       names, info);\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419809","messageId":"1c2f65cb674c538eb01152a6a26c64899cef1743.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 12/29] tree-walk.h users: migrate \"p->mode &&\" pattern","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:45Z","receivedAt":"2021-03-21T00:02:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change code that depends on \"p->mode\" either being a valid mode or\nzero to use a p->object_type comparison to \"OBJ_NONE\".\n\nThe object_type() function in cache.h will not return OBJ_NONE, but\nthese API users are implicitly relying on the memzero() that happens\nin setup_traverse_info().\n\nSince OBJ_NONE is \"0\" we can also rely on that being zero'd out here,\nalong with the rest of the structure. I think this is slightly less\nclever than \"mode not set\", and helps to get rid of more uses of\n\"mode\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/merge-tree.c | 9 +++++----\n merge-ort.c          | 2 +-\n unpack-trees.c       | 4 ++--\n 3 files changed, 8 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex de8520778d2..2de34c2d485 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -214,7 +214,7 @@ static void unresolved_directory(const struct traverse_info *info,\n \tvoid *buf0, *buf1, *buf2;\n \n \tfor (p = n; p < n + 3; p++) {\n-\t\tif (p->mode && S_ISDIR(p->mode))\n+\t\tif (p->object_type == OBJ_TREE)\n \t\t\tbreak;\n \t}\n \tif (n + 3 <= p)\n@@ -222,7 +222,7 @@ static void unresolved_directory(const struct traverse_info *info,\n \n \tnewbase = traverse_path(info, p);\n \n-#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL)\n+#define ENTRY_OID(e) (((e)->object_type == OBJ_TREE) ? &(e)->oid : NULL)\n \tbuf0 = fill_tree_descriptor(r, t + 0, ENTRY_OID(n + 0));\n \tbuf1 = fill_tree_descriptor(r, t + 1, ENTRY_OID(n + 1));\n \tbuf2 = fill_tree_descriptor(r, t + 2, ENTRY_OID(n + 2));\n@@ -242,7 +242,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n \tconst char *path;\n \tstruct merge_list *link;\n \n-\tif (!n->mode)\n+\tif (n->object_type == OBJ_NONE)\n \t\treturn entry;\n \tif (entry)\n \t\tpath = entry->path;\n@@ -265,7 +265,8 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3])\n \t\t * Treat missing entries as directories so that we return\n \t\t * after unresolved_directory has handled this.\n \t\t */\n-\t\tif (!n[i].mode || S_ISDIR(n[i].mode))\n+\t\tif (n[i].object_type == OBJ_NONE ||\n+\t\t    n[i].object_type == OBJ_TREE)\n \t\t\tdirmask |= (1 << i);\n \t}\n \ndiff --git a/merge-ort.c b/merge-ort.c\nindex 68eaa8f294f..052231efdb2 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -668,7 +668,7 @@ static int collect_merge_info_callback(int n,\n \t * setup_path_info() for tracking.\n \t */\n \tp = names;\n-\twhile (!p->mode)\n+\twhile (p->object_type == OBJ_NONE)\n \t\tp++;\n \tlen = traverse_path_len(info, p->pathlen);\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex ea1ce7894ba..e6cb505fcb0 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -862,7 +862,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n \t}\n \n \tp = names;\n-\twhile (!p->mode)\n+\twhile (p->object_type == OBJ_NONE)\n \t\tp++;\n \n \tnewinfo = *info;\n@@ -1242,7 +1242,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n \tconst struct name_entry *p = names;\n \n \t/* Find first entry with a real name (we could use \"mask\" too) */\n-\twhile (!p->mode)\n+\twhile (p->object_type == OBJ_NONE)\n \t\tp++;\n \n \tif (o->debug_unpack)\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419810","messageId":"df43f1a76ac1ee484f5a3052244a9a2c4ea494bb.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 10/29] tree.h: format argument lists of read_tree_recursive() users","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:43Z","receivedAt":"2021-03-21T00:02:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for adding a new argument to read_tree_fn_t re-indent\nand format the argument list of read_tree_recursive() callbacks, and\nthe relevant functions they call.\n\nThis is a whitespace-only change to make reading subsequent commits\neasier. I'll be adding a new argument on the \"mode\" line, so leave\nspace on it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          | 13 ++++++++-----\n builtin/checkout.c |  4 +++-\n builtin/log.c      |  5 +++--\n builtin/ls-tree.c  |  4 +++-\n merge-recursive.c  |  3 ++-\n tree.h             |  5 ++++-\n 6 files changed, 23 insertions(+), 11 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 4921dc8a69f..feed5c02fb2 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -137,8 +137,9 @@ static int check_attr_export_subst(const struct attr_check *check)\n }\n \n static int write_archive_entry(const struct object_id *oid, const char *base,\n-\t\tint baselen, const char *filename, unsigned mode,\n-\t\tvoid *context)\n+\t\t\t       int baselen, const char *filename,\n+\t\t\t       unsigned mode,\n+\t\t\t       void *context)\n {\n \tstatic struct strbuf path = STRBUF_INIT;\n \tstruct archiver_context *c = context;\n@@ -228,8 +229,9 @@ static int write_directory(struct archiver_context *c)\n }\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n-\t\tstruct strbuf *base, const char *filename,\n-\t\tunsigned mode, void *context)\n+\t\t\t\t\tstruct strbuf *base, const char *filename,\n+\t\t\t\t\tunsigned mode,\n+\t\t\t\t\tvoid *context)\n {\n \tstruct archiver_context *c = context;\n \n@@ -375,7 +377,8 @@ struct path_exists_context {\n };\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\tconst char *filename, unsigned mode,\n+\t\t\tconst char *filename,\n+\t\t\tunsigned mode,\n \t\t\tvoid *context)\n {\n \tint ret = -1;\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0e663905200..0887352db2a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -114,7 +114,9 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n }\n \n static int update_some(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, void *context)\n+\t\t       const char *pathname,\n+\t\t       unsigned mode,\n+\t\t       void *context)\n {\n \tint len;\n \tstruct cache_entry *ce;\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 980de590638..b7b76856a9f 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -598,8 +598,9 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n }\n \n static int show_tree_object(const struct object_id *oid,\n-\t\tstruct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, void *context)\n+\t\t\t    struct strbuf *base, const char *pathname,\n+\t\t\t    unsigned mode,\n+\t\t\t    void *context)\n {\n \tFILE *file = context;\n \tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3a442631c71..8d5c3fd0582 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -62,7 +62,9 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\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+\t\t     const char *pathname,\n+\t\t     unsigned mode,\n+\t\t     void *context)\n {\n \tint retval = 0;\n \tint baselen;\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 97520a88646..57198b29fe7 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,8 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   unsigned int mode, void *context)\n+\t\t\t   unsigned int mode,\n+\t\t\t   void *context)\n {\n \tstruct path_hashmap_entry *entry;\n \tint baselen = base->len;\ndiff --git a/tree.h b/tree.h\nindex 6efff003e21..10c8637ab3e 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -31,7 +31,10 @@ struct tree *parse_tree_indirect(const struct object_id *oid);\n int cmp_cache_name_compare(const void *a_, const void *b_);\n \n #define READ_TREE_RECURSIVE 1\n-typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n+typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *,\n+\t\t\t      const char *,\n+\t\t\t      unsigned int,\n+\t\t\t      void *);\n \n int read_tree_at(struct repository *r,\n \t\t struct tree *tree, struct strbuf *base,\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419811","messageId":"4babecb78552a0fc111c6826ffbb125f5c65a4cb.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 18/29] tree-walk.h users: use temporary variable(s) for \"mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:51Z","receivedAt":"2021-03-21T00:02:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for an eventual rename of the \"mode\" field, add\ntemporary variable(s) in those places where it's used more than once.\n\nThis will make a subsequent commits easier to read., since we're only\ngoing to need to modify the line on which the assignment happens.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/merge-tree.c | 12 +++++++++---\n match-trees.c        | 13 +++++++------\n merge-ort.c          |  5 +++--\n notes.c              |  3 ++-\n tree-diff.c          | 13 ++++++++-----\n unpack-trees.c       |  3 ++-\n 6 files changed, 31 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex 12cb317c1ba..eec5b906561 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -190,14 +190,17 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s\n {\n \tstruct merge_list *orig, *final;\n \tconst char *path;\n+\tunsigned int orig_mode, final_mode;\n \n \t/* If it's already ours, don't bother showing it */\n \tif (!ours)\n \t\treturn;\n \n \tpath = traverse_path(info, result);\n-\torig = create_entry(2, ours->mode, &ours->oid, path);\n-\tfinal = create_entry(0, result->mode, &result->oid, path);\n+\torig_mode = ours->mode;\n+\torig = create_entry(2, orig_mode, &ours->oid, path);\n+\tfinal_mode = result->mode;\n+\tfinal = create_entry(0, final_mode, &result->oid, path);\n \n \tfinal->link = orig;\n \n@@ -241,6 +244,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n {\n \tconst char *path;\n \tstruct merge_list *link;\n+\tunsigned int link_mode;\n \n \tif (n->object_type == OBJ_NONE)\n \t\treturn entry;\n@@ -248,7 +252,9 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n \t\tpath = entry->path;\n \telse\n \t\tpath = traverse_path(info, n);\n-\tlink = create_entry(stage, n->mode, &n->oid, path);\n+\tlink_mode = n->mode;\n+\tlink = create_entry(stage, link_mode, &n->oid, path);\n+\n \tlink->link = entry;\n \treturn link;\n }\ndiff --git a/match-trees.c b/match-trees.c\nindex a28c19a62a5..f3e192ca74d 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -86,6 +86,8 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha\n \n \tfor (;;) {\n \t\tint cmp;\n+\t\tunsigned int one_mode = one.entry.mode;\n+\t\tunsigned int two_mode = two.entry.mode;\n \n \t\tif (one.size && two.size)\n \t\t\tcmp = base_name_entries_compare(&one.entry, &two.entry);\n@@ -100,22 +102,21 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha\n \n \t\tif (cmp < 0) {\n \t\t\t/* path1 does not appear in two */\n-\t\t\tscore += score_missing(one.entry.mode);\n+\t\t\tscore += score_missing(one_mode);\n \t\t\tupdate_tree_entry(&one);\n \t\t} else if (cmp > 0) {\n \t\t\t/* path2 does not appear in one */\n-\t\t\tscore += score_missing(two.entry.mode);\n+\t\t\tscore += score_missing(two_mode);\n \t\t\tupdate_tree_entry(&two);\n \t\t} else {\n+\n \t\t\t/* path appears in both */\n \t\t\tif (!oideq(&one.entry.oid, &two.entry.oid)) {\n \t\t\t\t/* they are different */\n-\t\t\t\tscore += score_differs(one.entry.mode,\n-\t\t\t\t\t\t       two.entry.mode);\n+\t\t\t\tscore += score_differs(one_mode, two_mode);\n \t\t\t} else {\n \t\t\t\t/* same subtree or blob */\n-\t\t\t\tscore += score_matches(one.entry.mode,\n-\t\t\t\t\t\t       two.entry.mode);\n+\t\t\t\tscore += score_matches(one_mode, two_mode);\n \t\t\t}\n \t\t\tupdate_tree_entry(&one);\n \t\t\tupdate_tree_entry(&two);\ndiff --git a/merge-ort.c b/merge-ort.c\nindex c9047e279c6..4ef03e605a3 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -544,11 +544,12 @@ static void add_pair(struct merge_options *opt,\n \tstruct diff_filespec *one, *two;\n \tstruct rename_info *renames = &opt->priv->renames;\n \tint names_idx = is_add ? side : 0;\n+\tconst struct object_id *oid = &names[names_idx].oid;\n+\tunsigned int mode = names[names_idx].mode;\n \n \tone = alloc_filespec(pathname);\n \ttwo = alloc_filespec(pathname);\n-\tfill_filespec(is_add ? two : one,\n-\t\t      &names[names_idx].oid, 1, names[names_idx].mode);\n+\tfill_filespec(is_add ? two : one, oid, 1, mode);\n \tdiff_queue(&renames->pairs[side], one, two);\n }\n \ndiff --git a/notes.c b/notes.c\nindex 8b03ace52bf..970650431fb 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -478,6 +478,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\tstruct strbuf non_note_path = STRBUF_INIT;\n \t\t\tconst char *q = oid_to_hex(&subtree->key_oid);\n \t\t\tsize_t i;\n+\t\t\tunsigned int mode = entry.mode;\n \t\t\tfor (i = 0; i < prefix_len; i++) {\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n@@ -485,7 +486,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t}\n \t\t\tstrbuf_add(&non_note_path, entry.path, path_len);\n \t\t\tadd_non_note(t, strbuf_detach(&non_note_path, NULL),\n-\t\t\t\t     entry.mode, entry.oid.hash);\n+\t\t\t\t     mode, entry.oid.hash);\n \t\t}\n \t}\n \tfree(buf);\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 6ec180331fb..088ed52d6a3 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -466,17 +466,19 @@ static struct combine_diff_path *ll_diff_tree_paths(\n \t\ttp[0].entry.mode &= ~S_IFXMIN_NEQ;\n \n \t\tfor (i = 1; i < nparent; ++i) {\n+\t\t\tunsigned int mode = tp[i].entry.mode;\n \t\t\tcmp = tree_entry_pathcmp(&tp[i], &tp[imin]);\n \t\t\tif (cmp < 0) {\n \t\t\t\timin = i;\n-\t\t\t\ttp[i].entry.mode &= ~S_IFXMIN_NEQ;\n+\t\t\t\tmode &= ~S_IFXMIN_NEQ;\n \t\t\t}\n \t\t\telse if (cmp == 0) {\n-\t\t\t\ttp[i].entry.mode &= ~S_IFXMIN_NEQ;\n+\t\t\t\tmode &= ~S_IFXMIN_NEQ;\n \t\t\t}\n \t\t\telse {\n-\t\t\t\ttp[i].entry.mode |= S_IFXMIN_NEQ;\n+\t\t\t\tmode |= S_IFXMIN_NEQ;\n \t\t\t}\n+\t\t\ttp[i].entry.mode = mode;\n \t\t}\n \n \t\t/* fixup markings for entries before imin */\n@@ -493,13 +495,14 @@ static struct combine_diff_path *ll_diff_tree_paths(\n \t\t\t/* are either pi > p[imin] or diff(t,pi) != ø ? */\n \t\t\tif (!opt->flags.find_copies_harder) {\n \t\t\t\tfor (i = 0; i < nparent; ++i) {\n+\t\t\t\t\tunsigned int mode = tp[i].entry.mode;\n \t\t\t\t\t/* p[i] > p[imin] */\n-\t\t\t\t\tif (tp[i].entry.mode & S_IFXMIN_NEQ)\n+\t\t\t\t\tif (mode & S_IFXMIN_NEQ)\n \t\t\t\t\t\tcontinue;\n \n \t\t\t\t\t/* diff(t,pi) != ø */\n \t\t\t\t\tif (!oideq(&t.entry.oid, &tp[i].entry.oid) ||\n-\t\t\t\t\t    (t.entry.mode != tp[i].entry.mode))\n+\t\t\t\t\t    (t.entry.mode != mode))\n \t\t\t\t\t\tcontinue;\n \n \t\t\t\t\tgoto skip_emit_t_tp;\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 72b2cf5a741..4653e61c3e3 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1023,8 +1023,9 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,\n \t\tis_transient ?\n \t\tmake_empty_transient_cache_entry(len) :\n \t\tmake_empty_cache_entry(istate, len);\n+\tunsigned int mode = n->mode;\n \n-\tce->ce_mode = create_ce_mode(n->mode);\n+\tce->ce_mode = create_ce_mode(mode);\n \tce->ce_flags = create_ce_flags(stage);\n \tce->ce_namelen = len;\n \toidcpy(&ce->oid, &n->oid);\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419817","messageId":"685da1abbdc639099731caec2a25980d0fe12616.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 17/29] fsck.c: switch on \"object_type\" in fsck_walk_tree()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:50Z","receivedAt":"2021-03-21T00:02:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since 7146e66f086 (tree-walk: finally switch over tree descriptors to\ncontain a pre-parsed entry, 2014-02-06) the \"mode\" is validated such\nthat we'll never reach the \"else\" clause here.\n\nGood for us that fsck_tree() has its own FSCK_MSG_BAD_FILEMODE check\nwhich we can use, added way back in 64071805eda (git-fsck-cache: be\nstricter about \"tree\" objects, 2005-07-27).\n\nExcept it really doesn't due to a regression in 7146e66f086. A\nfollow-up commit will address that, but for now we can simply rewrite\nthis code like the rest of the s/entry.mode/entry.object_type/g\nchanges I'm making.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n fsck.c | 21 +++++++++------------\n 1 file changed, 9 insertions(+), 12 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex e3030f3b358..7c74c49d329 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -396,28 +396,25 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op\n \t\tstruct object *obj;\n \t\tint result;\n \n-\t\tif (S_ISGITLINK(entry.mode))\n+\t\tswitch (entry.object_type) {\n+\t\tcase OBJ_COMMIT:\n \t\t\tcontinue;\n-\n-\t\tif (S_ISDIR(entry.mode)) {\n+\t\tcase OBJ_TREE:\n \t\t\tobj = (struct object *)lookup_tree(the_repository, &entry.oid);\n \t\t\tif (name && obj)\n \t\t\t\tfsck_put_object_name(options, &entry.oid, \"%s%s/\",\n \t\t\t\t\t\t     name, entry.path);\n-\t\t\tresult = options->walk(obj, OBJ_TREE, data, options);\n-\t\t}\n-\t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {\n+\t\t\tbreak;\n+\t\tcase OBJ_BLOB:\n \t\t\tobj = (struct object *)lookup_blob(the_repository, &entry.oid);\n \t\t\tif (name && obj)\n \t\t\t\tfsck_put_object_name(options, &entry.oid, \"%s%s\",\n \t\t\t\t\t\t     name, entry.path);\n-\t\t\tresult = options->walk(obj, OBJ_BLOB, data, options);\n-\t\t}\n-\t\telse {\n-\t\t\tresult = error(\"in tree %s: entry %s has bad mode %.6o\",\n-\t\t\t\t       fsck_describe_object(options, &tree->object.oid),\n-\t\t\t\t       entry.path, entry.mode);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"unreachable\");\n \t\t}\n+\t\tresult = options->walk(obj, entry.object_type, data, options);\n \t\tif (result < 0)\n \t\t\treturn result;\n \t\tif (!res)\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419819","messageId":"73e92ac187d3bdf82fe81b3a0ccd97ebe1fefa11.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 07/29] diff tests: test that \"mode\" is passed when sorting","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:40Z","receivedAt":"2021-03-21T00:02:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Piggy-back on the recently added fsck tests for mode comparisons in\nmktree and assert that diff-tree also does the right thing in this\nimplausible scenario.\n\nAs with the other tests I've added in preceding commits, these tests\nwill fail if the mode is the same or reversed, respectively.\n\nThe diff-tree code being tested here was originally added back in\n.9174026cfe (Add \"diff-tree\" program to show which files have changed\nbetween two trees., 2005-04-09).\n\nUnlike the other tests I've added there are existing tests for both of\nthese scenarios. Breaking that function as described above will make\ntests in t4002-diff-basic.sh, t6409-merge-subtree.sh and\nt4037-diff-r-t-dirs.sh fail.\n\nI think it's good to have tests for this regardless, so let's add\nthese.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t1450-fsck.sh | 22 ++++++++++++++++++++++\n 1 file changed, 22 insertions(+)\n\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 46125190b45..5dd842bb82c 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -362,6 +362,28 @@ test_expect_success 'tree entry with duplicate type mismatching objects' '\n \t)\n '\n \n+test_expect_success 'diff-tree stressing tree-diff.c::tree_entry_pathcmp(), not the same type' '\n+\tzero=$(test_oid zero) &&\n+\tgit -C duplicate-entry diff-tree broken-commit-1 broken-commit-2 >1-to-2 &&\n+\tgrep \"$zero\" 1-to-2 >lines &&\n+\ttest_line_count = 2 lines &&\n+\n+\tgit -C duplicate-entry diff-tree broken-commit-2 broken-commit-1 >2-to-1 &&\n+\tgrep \"$zero\" 2-to-1 >lines &&\n+\ttest_line_count = 2 lines\n+'\n+\n+test_expect_success 'diff-tree stressing tree-diff.c::tree_entry_pathcmp(), types not reversed' '\n+\tblob_ok=$(git -C duplicate-entry rev-parse broken-commit-2:A) &&\n+\tgit -C duplicate-entry diff-tree --diff-filter=A broken-commit-1 broken-commit-2 >1-to-2 &&\n+\tgrep \"$blob_ok\" 1-to-2 &&\n+\ttest_line_count = 1 1-to-2 &&\n+\n+\tgit -C duplicate-entry diff-tree --diff-filter=A broken-commit-2 broken-commit-1 >2-to-1 &&\n+\tgrep \"$blob_ok\" 2-to-1 &&\n+\ttest_line_count = 1 2-to-1\n+'\n+\n test_expect_success 'tag pointing to nonexistent' '\n \tbadoid=$(test_oid deadbeef) &&\n \tcat >invalid-tag <<-EOF &&\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419821","messageId":"092472f3c8d92e7cd0195ab3068bd84eaabc9f9c.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 16/29] merge-ort: correct reference to test in 62fdec17a11","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:49Z","receivedAt":"2021-03-21T00:02:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a comment added in 62fdec17a11 (merge-ort: flesh out\nimplementation of handle_content_merge(), 2021-01-01).\n\nThe test being referred to here was moved from t6036 in\n919df319555 (Collect merge-related tests to t64xx, 2020-08-10).\n\nIt has also had the plural of \"mode\" in the name ever since being\nintroduced in 5d1daf30cce (t6036: add a failed conflict detection\ncase: regular files, different modes, 2018-06-30).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n merge-ort.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 052231efdb2..c9047e279c6 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -1079,7 +1079,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t/*\n \t\t * FIXME: If opt->priv->call_depth && !clean, then we really\n \t\t * should not make result->mode match either a->mode or\n-\t\t * b->mode; that causes t6036 \"check conflicting mode for\n+\t\t * b->mode; that causes t6416 \"check conflicting modes for\n \t\t * regular file\" to fail.  It would be best to use some other\n \t\t * mode, but we'll confuse all kinds of stuff if we use one\n \t\t * where S_ISREG(result->mode) isn't true, and if we use\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419803","messageId":"40f37e99cd9a5384330a1155f6892b0d64b7bc49.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 21/29] match-trees: use \"tmp\" for mode in shift_tree_by()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:54Z","receivedAt":"2021-03-21T00:02:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor code added in 85e51b783c3 (Make \"subtree\" part more\northogonal to the rest of merge-recursive., 2008-06-30) to make it\nobvious that we don't care about the \"mode\" here outside of the if\nstatement it appears in.\n\nThat's opposed to the sub1 & sub2 variables, where we use the two\nobject ids later in this function.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c | 10 +++++-----\n 1 file changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex 0faacd8f4ae..e84f993a460 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -317,17 +317,17 @@ void shift_tree_by(struct repository *r,\n \t\t   const char *shift_prefix)\n {\n \tstruct object_id sub1, sub2;\n-\tunsigned short mode1, mode2;\n+\tunsigned short tmp;\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n-\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &mode1) &&\n-\t    S_ISDIR(mode1))\n+\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n+\t    S_ISDIR(tmp))\n \t\tcandidate |= 1;\n \n \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n-\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &mode2) &&\n-\t    S_ISDIR(mode2))\n+\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n+\t    S_ISDIR(tmp))\n \t\tcandidate |= 2;\n \n \tif (candidate == 3) {\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419806","messageId":"7251654dd52ec131328ca4e2daef966dbb14f899.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 19/29] tree-walk.h API: formatting changes for subsequent commit","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:52Z","receivedAt":"2021-03-21T00:02:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Do formatting (mainly whitespace) changes of code around the\nget_tree_entry() function to make a subsequent change where we'll add\na sister function easier to read.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n blame.c     |  5 +++--\n tree-walk.c |  9 ++++++---\n tree-walk.h | 12 ++++++++----\n 3 files changed, 17 insertions(+), 9 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 5018bb8fb2c..6ed1520e619 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -102,9 +102,10 @@ static void verify_working_tree_path(struct repository *r,\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n \t\tstruct object_id blob_oid;\n \t\tunsigned short mode;\n+\t\tint ret = get_tree_entry(r, commit_oid, path, &blob_oid,\n+\t\t\t\t\t &mode);\n \n-\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode) &&\n-\t\t    oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n+\t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n \t\t\treturn;\n \t}\n \ndiff --git a/tree-walk.c b/tree-walk.c\nindex 6e9161901d8..e88187e3714 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -591,7 +591,8 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\toidcpy(result, &oid);\n \t\t\treturn 0;\n \t\t}\n-\t\treturn get_tree_entry(r, &oid, name + entrylen, result, mode);\n+\t\treturn get_tree_entry(r, &oid, name + entrylen, result,\n+\t\t\t\t      mode);\n \t}\n \treturn -1;\n }\n@@ -622,7 +623,8 @@ int get_tree_entry(struct repository *r,\n \t} else {\n \t\tstruct tree_desc t;\n \t\tinit_tree_desc(&t, tree, size);\n-\t\tretval = find_tree_entry(r, &t, name, oid, mode);\n+\t\tretval = find_tree_entry(r, &t, name, oid,\n+\t\t\t\t\t mode);\n \t}\n \tfree(tree);\n \treturn retval;\n@@ -748,7 +750,8 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n \n \t\t/* Look up the first (or only) path component in the tree. */\n \t\tfind_result = find_tree_entry(r, &t, namebuf.buf,\n-\t\t\t\t\t      &current_tree_oid, mode);\n+\t\t\t\t\t      &current_tree_oid,\n+\t\t\t\t\t      mode);\n \t\tif (find_result) {\n \t\t\tgoto done;\n \t\t}\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 9f3825d2773..478a659ee2b 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -169,10 +169,14 @@ struct traverse_info {\n \n /**\n  * Find an entry in a tree given a pathname and the sha1 of a tree to\n- * search. Returns 0 if the entry is found and -1 otherwise. The third\n- * and fourth parameters are set to the entry's sha1 and mode respectively.\n- */\n-int get_tree_entry(struct repository *, const struct object_id *, const char *, struct object_id *, unsigned short *);\n+ * search. Returns 0 if the entry is found and -1 otherwise.\n+ *\n+ * The third and fourth parameters are set to the entry's sha1 and\n+ * mode respectively.\n+ */\n+int get_tree_entry(struct repository *, const struct object_id *, const char *,\n+\t\t   struct object_id *,\n+\t\t   unsigned short *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419807","messageId":"7ab7576cb16727d9aa9edb24a2c26b1f0b1e8880.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 23/29] tree-walk.h API: document and format tree_entry_extract()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:56Z","receivedAt":"2021-03-21T00:02:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Document and format the argument list of the tree_entry_extract()\nfunction in preparation for adding a sister function.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.h | 14 ++++++++++----\n 1 file changed, 10 insertions(+), 4 deletions(-)\n\ndiff --git a/tree-walk.h b/tree-walk.h\nindex f569960c6fb..f51485250fb 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -40,11 +40,17 @@ struct tree_desc {\n \n /**\n  * Decode the entry currently being visited (the one pointed to by\n- * `tree_desc's` `entry` member) and return the sha1 of the entry. The\n- * `pathp` and `modep` arguments are set to the entry's pathname and mode\n- * respectively.\n+ * `tree_desc's` `entry` member) and return the OID of the entry.\n+ *\n+ * There are variants of this function depending on what fields in the\n+ * \"struct name_entry\" you'd like. You always need a pointer to an\n+ * appropriate variable to fill in (NULL won't do!):\n+ *\n+ * tree_entry_extract_mode(): const char *path, unsigned int mode\n  */\n-static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned short *modep)\n+static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,\n+\t\t\t\t\t\t\t const char **pathp,\n+\t\t\t\t\t\t\t unsigned short *modep)\n {\n \t*pathp = desc->entry.path;\n \t*modep = desc->entry.mode;\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419808","messageId":"7f699bf2d5c94121b214b9357b11182c096acfa9.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 22/29] tree-walk.h API: add get_tree_entry_type()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:55Z","receivedAt":"2021-03-21T00:02:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a get_tree_entry_type() helper function to compliment the existing\nget_tree_entry(), and a static get_tree_entry_all() which it uses internally.\n\nMove those users of get_tree_entry_type() who didn't care about the\nmode specifically, but just want to know whether the tree entry is one\nof OBJ_{BLOB,COMMIT,TREE} over to the new get_tree_entry_type().\n\nThe get_tree_entry_all() function itself will be made non-static in a\nsubsequent commit. I'm leaving its argument list indented accordingly\nto reduce churn when I do so.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c     |  8 ++++----\n match-trees.c | 10 +++++-----\n tree-walk.c   | 54 ++++++++++++++++++++++++++++++++++++++++-----------\n tree-walk.h   | 11 +++++++++--\n 4 files changed, 61 insertions(+), 22 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 14f8e694596..fb2791f4328 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -475,14 +475,14 @@ static void parse_treeish_arg(const char **argv,\n \n \tif (prefix) {\n \t\tstruct object_id tree_oid;\n-\t\tunsigned short mode;\n+\t\tenum object_type object_type;\n \t\tint err;\n \n-\t\terr = get_tree_entry_mode(ar_args->repo,\n+\t\terr = get_tree_entry_type(ar_args->repo,\n \t\t\t\t\t  &tree->object.oid,\n \t\t\t\t\t  prefix, &tree_oid,\n-\t\t\t\t\t  &mode);\n-\t\tif (err || !S_ISDIR(mode))\n+\t\t\t\t\t  &object_type);\n+\t\tif (err || object_type != OBJ_TREE)\n \t\t\tdie(_(\"current working directory is untracked\"));\n \n \t\ttree = parse_tree_indirect(&tree_oid);\ndiff --git a/match-trees.c b/match-trees.c\nindex e84f993a460..3177558313e 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -317,17 +317,17 @@ void shift_tree_by(struct repository *r,\n \t\t   const char *shift_prefix)\n {\n \tstruct object_id sub1, sub2;\n-\tunsigned short tmp;\n+\tenum object_type tmp;\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n-\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n-\t    S_ISDIR(tmp))\n+\tif (!get_tree_entry_type(r, hash1, shift_prefix, &sub1, &tmp) &&\n+\t    tmp == OBJ_TREE)\n \t\tcandidate |= 1;\n \n \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n-\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n-\t    S_ISDIR(tmp))\n+\tif (!get_tree_entry_type(r, hash2, shift_prefix, &sub2, &tmp) &&\n+\t    tmp == OBJ_TREE)\n \t\tcandidate |= 2;\n \n \tif (candidate == 3) {\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 7819ff3e0ec..46ce1ba8069 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -559,9 +559,17 @@ struct dir_state {\n \tstruct object_id oid;\n };\n \n+static int get_tree_entry_all(struct repository *r,\n+\t\t\t      const struct object_id *tree_oid,\n+\t\t\t      const char *name,\n+\t\t\t      struct object_id *oid,\n+\t\t\t      unsigned short *mode,\n+\t\t\t      enum object_type *object_type);\n+\n static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\t   const char *name, struct object_id *result,\n-\t\t\t   unsigned short *mode)\n+\t\t\t   unsigned short *mode,\n+\t\t\t   enum object_type *object_type)\n {\n \tint namelen = strlen(name);\n \twhile (t->size) {\n@@ -585,23 +593,24 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t}\n \t\tif (name[entrylen] != '/')\n \t\t\tcontinue;\n-\t\tif (!S_ISDIR(*mode))\n+\t\tif (*object_type != OBJ_TREE)\n \t\t\tbreak;\n \t\tif (++entrylen == namelen) {\n \t\t\toidcpy(result, &oid);\n \t\t\treturn 0;\n \t\t}\n-\t\treturn get_tree_entry_mode(r, &oid, name + entrylen, result,\n-\t\t\t\t\t   mode);\n+\t\treturn get_tree_entry_all(r, &oid, name + entrylen, result,\n+\t\t\t\t\t  mode, object_type);\n \t}\n \treturn -1;\n }\n \n-int get_tree_entry_mode(struct repository *r,\n-\t\t\tconst struct object_id *tree_oid,\n-\t\t\tconst char *name,\n-\t\t\tstruct object_id *oid,\n-\t\t\tunsigned short *mode)\n+static int get_tree_entry_all(struct repository *r,\n+\t\t       const struct object_id *tree_oid,\n+\t\t       const char *name,\n+\t\t       struct object_id *oid,\n+\t\t       unsigned short *mode,\n+\t\t       enum object_type *object_type)\n {\n \tint retval;\n \tvoid *tree;\n@@ -624,12 +633,34 @@ int get_tree_entry_mode(struct repository *r,\n \t\tstruct tree_desc t;\n \t\tinit_tree_desc(&t, tree, size);\n \t\tretval = find_tree_entry(r, &t, name, oid,\n-\t\t\t\t\t mode);\n+\t\t\t\t\t mode, object_type);\n \t}\n \tfree(tree);\n \treturn retval;\n }\n \n+int get_tree_entry_mode(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid,\n+\t\t\tunsigned short *mode)\n+{\n+\tenum object_type object_type;\n+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n+\t\t\t\t  mode, &object_type);\n+}\n+\n+int get_tree_entry_type(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid,\n+\t\t\tenum object_type *object_type)\n+{\n+\tunsigned short mode;\n+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n+\t\t\t\t  &mode, object_type);\n+}\n+\n /*\n  * This is Linux's built-in max for the number of symlinks to follow.\n  * That limit, of course, does not affect git, but it's a reasonable\n@@ -674,6 +705,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n \t\tint find_result;\n \t\tchar *first_slash;\n \t\tchar *remainder = NULL;\n+\t\tenum object_type object_type;\n \n \t\tif (!t.buffer) {\n \t\t\tvoid *tree;\n@@ -751,7 +783,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n \t\t/* Look up the first (or only) path component in the tree. */\n \t\tfind_result = find_tree_entry(r, &t, namebuf.buf,\n \t\t\t\t\t      &current_tree_oid,\n-\t\t\t\t\t      mode);\n+\t\t\t\t\t      mode, &object_type);\n \t\tif (find_result) {\n \t\t\tgoto done;\n \t\t}\ndiff --git a/tree-walk.h b/tree-walk.h\nindex eb9b9de6ccc..f569960c6fb 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -171,12 +171,19 @@ struct traverse_info {\n  * Find an entry in a tree given a pathname and the sha1 of a tree to\n  * search. Returns 0 if the entry is found and -1 otherwise.\n  *\n- * The third and fourth parameters are set to the entry's sha1 and\n- * mode respectively.\n+ * There are variants of this function depending on what fields in the\n+ * \"struct name_entry\" you'd like. You always need a pointer to an\n+ * appropriate variable to fill in (NULL won't do!):\n+ *\n+ * get_tree_entry_mode(): unsigned int mode\n+ * get_tree_entry_type(): enum object_type\n  */\n int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n \t\t\tunsigned short *);\n+int get_tree_entry_type(struct repository *, const struct object_id *, const char *,\n+\t\t\tstruct object_id *,\n+\t\t\tenum object_type *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419812","messageId":"9e521a3cbf23e16d49396793d33d10494dbdc799.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 20/29] tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:53Z","receivedAt":"2021-03-21T00:02:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the get_tree_entry() function to get_tree_entry_mode(). This\nchange is only a search-replacement of the name and indentation of the\nargument lists.\n\nA subsequent commits will add get_tree_entry_type() and\nget_tree_entry_all() functions. Those changes will be much easier to\nread if we do this rename first.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c              |  8 ++++----\n blame.c                |  6 +++---\n builtin/rm.c           |  2 +-\n builtin/update-index.c |  2 +-\n line-log.c             |  2 +-\n match-trees.c          |  6 +++---\n merge-recursive.c      | 18 +++++++++---------\n notes.c                |  2 +-\n object-name.c          |  6 +++---\n tree-walk.c            | 14 +++++++-------\n tree-walk.h            |  6 +++---\n 11 files changed, 36 insertions(+), 36 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex ca28d6ca3b8..14f8e694596 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -478,10 +478,10 @@ static void parse_treeish_arg(const char **argv,\n \t\tunsigned short mode;\n \t\tint err;\n \n-\t\terr = get_tree_entry(ar_args->repo,\n-\t\t\t\t     &tree->object.oid,\n-\t\t\t\t     prefix, &tree_oid,\n-\t\t\t\t     &mode);\n+\t\terr = get_tree_entry_mode(ar_args->repo,\n+\t\t\t\t\t  &tree->object.oid,\n+\t\t\t\t\t  prefix, &tree_oid,\n+\t\t\t\t\t  &mode);\n \t\tif (err || !S_ISDIR(mode))\n \t\t\tdie(_(\"current working directory is untracked\"));\n \ndiff --git a/blame.c b/blame.c\nindex 6ed1520e619..13825b86e65 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -102,8 +102,8 @@ static void verify_working_tree_path(struct repository *r,\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n \t\tstruct object_id blob_oid;\n \t\tunsigned short mode;\n-\t\tint ret = get_tree_entry(r, commit_oid, path, &blob_oid,\n-\t\t\t\t\t &mode);\n+\t\tint ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n+\t\t\t\t\t      &mode);\n \n \t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n \t\t\treturn;\n@@ -1239,7 +1239,7 @@ static int fill_blob_sha1_and_mode(struct repository *r,\n {\n \tif (!is_null_oid(&origin->blob_oid))\n \t\treturn 0;\n-\tif (get_tree_entry(r, &origin->commit->object.oid, origin->path, &origin->blob_oid, &origin->mode))\n+\tif (get_tree_entry_mode(r, &origin->commit->object.oid, origin->path, &origin->blob_oid, &origin->mode))\n \t\tgoto error_out;\n \tif (oid_object_info(r, &origin->blob_oid, NULL) != OBJ_BLOB)\n \t\tgoto error_out;\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex 4858631e0f0..4617388b29a 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -179,7 +179,7 @@ static int check_local_mod(struct object_id *head, int index_only)\n \t\t * way as changed from the HEAD.\n \t\t */\n \t\tif (no_head\n-\t\t     || get_tree_entry(the_repository, head, name, &oid, &mode)\n+\t\t     || get_tree_entry_mode(the_repository, head, name, &oid, &mode)\n \t\t     || ce->ce_mode != create_ce_mode(mode)\n \t\t     || !oideq(&ce->oid, &oid))\n \t\t\tstaged_changes = 1;\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 79087bccea4..070510d6a88 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -603,7 +603,7 @@ static struct cache_entry *read_one_ent(const char *which,\n \tstruct object_id oid;\n \tstruct cache_entry *ce;\n \n-\tif (get_tree_entry(the_repository, ent, path, &oid, &mode)) {\n+\tif (get_tree_entry_mode(the_repository, ent, path, &oid, &mode)) {\n \t\tif (which)\n \t\t\terror(\"%s: not in %s branch.\", path, which);\n \t\treturn NULL;\ndiff --git a/line-log.c b/line-log.c\nindex 51d93310a4d..d8ba9229212 100644\n--- a/line-log.c\n+++ b/line-log.c\n@@ -503,7 +503,7 @@ static void fill_blob_sha1(struct repository *r, struct commit *commit,\n \tunsigned short mode;\n \tstruct object_id oid;\n \n-\tif (get_tree_entry(r, &commit->object.oid, spec->path, &oid, &mode))\n+\tif (get_tree_entry_mode(r, &commit->object.oid, spec->path, &oid, &mode))\n \t\tdie(\"There is no path %s in the commit\", spec->path);\n \tfill_filespec(spec, &oid, 1, mode);\n \ndiff --git a/match-trees.c b/match-trees.c\nindex f3e192ca74d..0faacd8f4ae 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -293,7 +293,7 @@ void shift_tree(struct repository *r,\n \t\tif (!*del_prefix)\n \t\t\treturn;\n \n-\t\tif (get_tree_entry(r, hash2, del_prefix, shifted, &mode))\n+\t\tif (get_tree_entry_mode(r, hash2, del_prefix, shifted, &mode))\n \t\t\tdie(\"cannot find path %s in tree %s\",\n \t\t\t    del_prefix, oid_to_hex(hash2));\n \t\treturn;\n@@ -321,12 +321,12 @@ void shift_tree_by(struct repository *r,\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n-\tif (!get_tree_entry(r, hash1, shift_prefix, &sub1, &mode1) &&\n+\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &mode1) &&\n \t    S_ISDIR(mode1))\n \t\tcandidate |= 1;\n \n \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n-\tif (!get_tree_entry(r, hash2, shift_prefix, &sub2, &mode2) &&\n+\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &mode2) &&\n \t    S_ISDIR(mode2))\n \t\tcandidate |= 2;\n \ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 6bc1e659a7b..f5cccb8deab 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -487,7 +487,7 @@ static int get_tree_entry_if_blob(struct repository *r,\n {\n \tint ret;\n \n-\tret = get_tree_entry(r, tree, path, &dfs->oid, &dfs->mode);\n+\tret = get_tree_entry_mode(r, tree, path, &dfs->oid, &dfs->mode);\n \tif (S_ISDIR(dfs->mode)) {\n \t\toidcpy(&dfs->oid, &null_oid);\n \t\tdfs->mode = 0;\n@@ -1886,9 +1886,9 @@ static int tree_has_path(struct repository *r, struct tree *tree,\n \tstruct object_id hashy;\n \tunsigned short mode_o;\n \n-\treturn !get_tree_entry(r,\n-\t\t\t       &tree->object.oid, path,\n-\t\t\t       &hashy, &mode_o);\n+\treturn !get_tree_entry_mode(r,\n+\t\t\t\t    &tree->object.oid, path,\n+\t\t\t\t    &hashy, &mode_o);\n }\n \n /*\n@@ -2540,11 +2540,11 @@ static void apply_directory_rename_modifications(struct merge_options *opt,\n \t * the various handle_rename_*() functions update the index\n \t * explicitly rather than relying on unpack_trees() to have done it.\n \t */\n-\tget_tree_entry(opt->repo,\n-\t\t       &tree->object.oid,\n-\t\t       pair->two->path,\n-\t\t       &re->dst_entry->stages[stage].oid,\n-\t\t       &re->dst_entry->stages[stage].mode);\n+\tget_tree_entry_mode(opt->repo,\n+\t\t\t    &tree->object.oid,\n+\t\t\t    pair->two->path,\n+\t\t\t    &re->dst_entry->stages[stage].oid,\n+\t\t\t    &re->dst_entry->stages[stage].mode);\n \n \t/*\n \t * Record the original change status (or 'type' of change).  If it\ndiff --git a/notes.c b/notes.c\nindex 970650431fb..b7a5ddce3ae 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -1021,7 +1021,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\treturn;\n \tif (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))\n \t\tdie(\"Cannot use notes ref %s\", notes_ref);\n-\tif (get_tree_entry(the_repository, &object_oid, \"\", &oid, &mode))\n+\tif (get_tree_entry_mode(the_repository, &object_oid, \"\", &oid, &mode))\n \t\tdie(\"Failed to read notes tree referenced by %s (%s)\",\n \t\t    notes_ref, oid_to_hex(&object_oid));\n \ndiff --git a/object-name.c b/object-name.c\nindex 64202de60b1..7e3b2d6d739 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1704,7 +1704,7 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \tif (is_missing_file_error(errno)) {\n \t\tchar *fullname = xstrfmt(\"%s%s\", prefix, filename);\n \n-\t\tif (!get_tree_entry(r, tree_oid, fullname, &oid, &mode)) {\n+\t\tif (!get_tree_entry_mode(r, tree_oid, fullname, &oid, &mode)) {\n \t\t\tdie(_(\"path '%s' exists, but not '%s'\\n\"\n \t\t\t    \"hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?\"),\n \t\t\t    fullname,\n@@ -1903,8 +1903,8 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo,\n \t\t\t\t\tfilename, oid, &oc->symlink_path,\n \t\t\t\t\t&oc->mode);\n \t\t\t} else {\n-\t\t\t\tret = get_tree_entry(repo, &tree_oid, filename, oid,\n-\t\t\t\t\t\t     &oc->mode);\n+\t\t\t\tret = get_tree_entry_mode(repo, &tree_oid, filename, oid,\n+\t\t\t\t\t\t\t  &oc->mode);\n \t\t\t\tif (ret && only_to_die) {\n \t\t\t\t\tdiagnose_invalid_oid_path(repo, prefix,\n \t\t\t\t\t\t\t\t   filename,\ndiff --git a/tree-walk.c b/tree-walk.c\nindex e88187e3714..7819ff3e0ec 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -591,17 +591,17 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\toidcpy(result, &oid);\n \t\t\treturn 0;\n \t\t}\n-\t\treturn get_tree_entry(r, &oid, name + entrylen, result,\n-\t\t\t\t      mode);\n+\t\treturn get_tree_entry_mode(r, &oid, name + entrylen, result,\n+\t\t\t\t\t   mode);\n \t}\n \treturn -1;\n }\n \n-int get_tree_entry(struct repository *r,\n-\t\t   const struct object_id *tree_oid,\n-\t\t   const char *name,\n-\t\t   struct object_id *oid,\n-\t\t   unsigned short *mode)\n+int get_tree_entry_mode(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid,\n+\t\t\tunsigned short *mode)\n {\n \tint retval;\n \tvoid *tree;\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 478a659ee2b..eb9b9de6ccc 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -174,9 +174,9 @@ struct traverse_info {\n  * The third and fourth parameters are set to the entry's sha1 and\n  * mode respectively.\n  */\n-int get_tree_entry(struct repository *, const struct object_id *, const char *,\n-\t\t   struct object_id *,\n-\t\t   unsigned short *);\n+int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n+\t\t\tstruct object_id *,\n+\t\t\tunsigned short *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419813","messageId":"a1bd81d64aa9f42e70848a0a0c2abc896b475e6f.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 24/29] tree-entry.h API: rename tree_entry_extract() to tree_entry_extract_mode()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:57Z","receivedAt":"2021-03-21T00:02:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As with the recent split of the get_tree_entry() function, rename the\ntree_entry_extract() function to *_mode() in preparation for adding\nother variants of it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n fsck.c        | 2 +-\n match-trees.c | 4 ++--\n tree-diff.c   | 4 ++--\n tree-walk.h   | 6 +++---\n 4 files changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 7c74c49d329..11678ba5826 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -670,7 +670,7 @@ static int fsck_tree(const struct object_id *oid,\n \t\tconst char *name, *backslash;\n \t\tconst struct object_id *oid;\n \n-\t\toid = tree_entry_extract(&desc, &name, &mode);\n+\t\toid = tree_entry_extract_mode(&desc, &name, &mode);\n \n \t\thas_null_sha1 |= is_null_oid(oid);\n \t\thas_full_path |= !!strchr(name, '/');\ndiff --git a/match-trees.c b/match-trees.c\nindex 3177558313e..2afa4968109 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -146,7 +146,7 @@ static void match_trees(const struct object_id *hash1,\n \t\tunsigned short mode;\n \t\tint score;\n \n-\t\telem = tree_entry_extract(&one, &path, &mode);\n+\t\telem = tree_entry_extract_mode(&one, &path, &mode);\n \t\tif (!S_ISDIR(mode))\n \t\t\tgoto next;\n \t\tscore = score_trees(elem, hash2);\n@@ -202,7 +202,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \t\tunsigned short mode;\n \t\tint len = tree_entry_len(&desc.entry);\n \n-\t\ttree_entry_extract(&desc, &name, &mode);\n+\t\ttree_entry_extract_mode(&desc, &name, &mode);\n \t\tif (len == toplen &&\n \t\t    !memcmp(name, prefix, toplen)) {\n \t\t\tif (!S_ISDIR(mode))\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 088ed52d6a3..65c7e4dbc8b 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -196,7 +196,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \n \tif (t) {\n \t\t/* path present in resulting tree */\n-\t\toid = tree_entry_extract(t, &path, &mode);\n+\t\toid = tree_entry_extract_mode(t, &path, &mode);\n \t\tpathlen = tree_entry_len(&t->entry);\n \t\tisdir = S_ISDIR(mode);\n \t} else {\n@@ -207,7 +207,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \t\t * 1) all modes for tp[i]=tp[imin] should be the same wrt\n \t\t *    S_ISDIR, thanks to base_name_compare().\n \t\t */\n-\t\ttree_entry_extract(&tp[imin], &path, &mode);\n+\t\ttree_entry_extract_mode(&tp[imin], &path, &mode);\n \t\tpathlen = tree_entry_len(&tp[imin].entry);\n \n \t\tisdir = S_ISDIR(mode);\ndiff --git a/tree-walk.h b/tree-walk.h\nindex f51485250fb..805cda649ee 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -48,9 +48,9 @@ struct tree_desc {\n  *\n  * tree_entry_extract_mode(): const char *path, unsigned int mode\n  */\n-static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,\n-\t\t\t\t\t\t\t const char **pathp,\n-\t\t\t\t\t\t\t unsigned short *modep)\n+static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *desc,\n+\t\t\t\t\t\t\t      const char **pathp,\n+\t\t\t\t\t\t\t      unsigned short *modep)\n {\n \t*pathp = desc->entry.path;\n \t*modep = desc->entry.mode;\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419814","messageId":"81da54902217891f8115cbad9194c88535615d65.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 28/29] blame: emit a better error on 'git blame directory'","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:01:01Z","receivedAt":"2021-03-21T00:02:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change an early check for non-blobs in verify_working_tree_path() to\nlet any such objects pass, and instead die shortly thereafter in the\nfake_working_tree_commit() caller's type check.\n\nNow e.g. doing \"git blame t\" in git.git emits:\n\n    fatal: unsupported file type t\n\nInstead of:\n\n    fatal: no such path 't' in HEAD\n\nThe main point of this test is to assert that we're not doing\nsomething uniquely bad when in a conflicted merge. See\ncd8ae20195 (git-blame shouldn't crash if run in an unmerged tree,\n2007-10-18) and 9aeaab6811 (blame: allow \"blame file\" in the middle of\na conflicted merge, 2012-09-11) for the bug the t8004 test was\noriginally meant to address.\n\nBut when extending it let's grep out the specific error message for\ngood measure. Having to change it in the future (e.g. as part of my\nparallel series to improve such 'OID does not match type' messages) is\na small price for ensuring it doesn't regress.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n blame.c                         |  8 ++------\n t/t8004-blame-with-conflicts.sh | 21 +++++++++++++++++++++\n 2 files changed, 23 insertions(+), 6 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 13825b86e65..3c3cb2f8f96 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -100,12 +100,8 @@ static void verify_working_tree_path(struct repository *r,\n \n \tfor (parents = work_tree->parents; parents; parents = parents->next) {\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n-\t\tstruct object_id blob_oid;\n-\t\tunsigned short mode;\n-\t\tint ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n-\t\t\t\t\t      &mode);\n-\n-\t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n+\t\tstruct object_id oid;\n+\t\tif (!get_tree_entry_path(r, commit_oid, path, &oid))\n \t\t\treturn;\n \t}\n \ndiff --git a/t/t8004-blame-with-conflicts.sh b/t/t8004-blame-with-conflicts.sh\nindex 35414a53363..5e3dea35a50 100755\n--- a/t/t8004-blame-with-conflicts.sh\n+++ b/t/t8004-blame-with-conflicts.sh\n@@ -73,4 +73,25 @@ test_expect_success 'blame does not crash with conflicted file in stages 1,3' '\n \tgit blame file1\n '\n \n+test_expect_success 'setup second case' '\n+\tgit merge --abort\n+'\n+\n+test_expect_success 'blame on directory/file conflict' '\n+\tmkdir d &&\n+\ttest_commit second &&\n+\ttest_commit d/file &&\n+\ttest_must_fail git blame d 2>expected &&\n+\tgrep \"unsupported file type d\" expected &&\n+\n+\tgit reset --hard second &&\n+\t>d &&\n+\tgit add d &&\n+\tgit commit -m\"a not-a-dir\" &&\n+\ttest_must_fail git merge d/file &&\n+\n+\ttest_must_fail git blame d 2>actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419818","messageId":"4d51da4ea39c11ac56972da8fa4e5becff7294f7.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 29/29] tree-walk.h API: add a tree_entry_extract_type() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:01:02Z","receivedAt":"2021-03-21T00:02:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add and use a tree_entry_extract_type() function. There were callers\nof tree_entry_extract() which didn't care about the mode, but just the\ntype in the tree entry.\n\nIn emit_path() the \"mode\" variable was not used after the \"isdir\"\nassignment, as can be seen in the diff with it being set to 0.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c | 12 ++++++------\n tree-diff.c   |  5 +++--\n tree-walk.h   | 11 +++++++++++\n 3 files changed, 20 insertions(+), 8 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex 25bfb46fb02..89109659aa3 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -143,11 +143,11 @@ static void match_trees(const struct object_id *hash1,\n \twhile (one.size) {\n \t\tconst char *path;\n \t\tconst struct object_id *elem;\n-\t\tunsigned short mode;\n+\t\tenum object_type object_type;\n \t\tint score;\n \n-\t\telem = tree_entry_extract_mode(&one, &path, &mode);\n-\t\tif (!S_ISDIR(mode))\n+\t\telem = tree_entry_extract_type(&one, &path, &object_type);\n+\t\tif (object_type != OBJ_TREE)\n \t\t\tgoto next;\n \t\tscore = score_trees(elem, hash2);\n \t\tif (*best_score < score) {\n@@ -198,14 +198,14 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \n \trewrite_here = NULL;\n \twhile (desc.size) {\n+\t\tenum object_type object_type;\n \t\tconst char *name;\n-\t\tunsigned short mode;\n \t\tint len = tree_entry_len(&desc.entry);\n \n-\t\ttree_entry_extract_mode(&desc, &name, &mode);\n+\t\ttree_entry_extract_type(&desc, &name, &object_type);\n \t\tif (len == toplen &&\n \t\t    !memcmp(name, prefix, toplen)) {\n-\t\t\tif (!S_ISDIR(mode))\n+\t\t\tif (object_type != OBJ_TREE)\n \t\t\t\tdie(\"entry %s in tree %s is not a tree\", name,\n \t\t\t\t    oid_to_hex(oid1));\n \ndiff --git a/tree-diff.c b/tree-diff.c\nindex 918ad95fa61..8409374f0ba 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -208,10 +208,11 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \t\t * 1) all modes for tp[i]=tp[imin] should be the same wrt\n \t\t *    S_ISDIR, thanks to base_name_compare().\n \t\t */\n-\t\ttree_entry_extract_mode(&tp[imin], &path, &mode);\n+\t\tenum object_type object_type;\n+\t\ttree_entry_extract_type(&tp[imin], &path, &object_type);\n \t\tpathlen = tree_entry_len(&tp[imin].entry);\n \n-\t\tisdir = S_ISDIR(mode);\n+\t\tisdir = object_type == OBJ_TREE;\n \t\toid = NULL;\n \t\tmode = 0;\n \t}\ndiff --git a/tree-walk.h b/tree-walk.h\nindex efcd7ccd10e..f5102ed5427 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -47,6 +47,7 @@ struct tree_desc {\n  * appropriate variable to fill in (NULL won't do!):\n  *\n  * tree_entry_extract_mode(): const char *path, unsigned int mode\n+ * tree_entry_extract_type(): const char *path, enum object_type\n  * tree_entry_extract_all(): const char *path, unsigned int mode, enum object_type\n  */\n static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *desc,\n@@ -58,6 +59,16 @@ static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *\n \treturn &desc->entry.oid;\n }\n \n+static inline const struct object_id *tree_entry_extract_type(struct tree_desc *desc,\n+\t\t\t\t\t\t\t      const char **pathp,\n+\t\t\t\t\t\t\t      enum object_type *object_typep)\n+{\n+\t*pathp = desc->entry.path;\n+\t*object_typep = desc->entry.object_type;\n+\treturn &desc->entry.oid;\n+}\n+\n+\n static inline const struct object_id *tree_entry_extract_all(struct tree_desc *desc,\n \t\t\t\t\t\t\t     const char **pathp,\n \t\t\t\t\t\t\t     unsigned short *modep,\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419820","messageId":"ce7c19ad39c882a6f883ea3acceb99e7771c9a87.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 25/29] tree-walk.h API: add a tree_entry_extract_all() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:58Z","receivedAt":"2021-03-21T00:02:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a tree_entry_extract_all() sibling function to the existing\ntree_entry_extract_mode().\n\nHaving the OBJ_{BLOB,TREE,COMMIT} when you have the \"mode\" is strictly\nspeaking redundant, but hopefully makes it easier to read the\ncode. We'll now see which parts of the code are checking the types,\nv.s. those that care about the mode specifically.\n\nOnly the first use of tree_entry_extract_mode() in emit_path() is\nconverted here, the other branch will use a new\nget_tree_entry_mode_type() introduced in a subsequent commit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-diff.c |  5 +++--\n tree-walk.c |  2 +-\n tree-walk.h | 12 ++++++++++++\n 3 files changed, 16 insertions(+), 3 deletions(-)\n\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 65c7e4dbc8b..918ad95fa61 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -195,10 +195,11 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \tassert(t || tp);\n \n \tif (t) {\n+\t\tenum object_type object_type;\n \t\t/* path present in resulting tree */\n-\t\toid = tree_entry_extract_mode(t, &path, &mode);\n+\t\toid = tree_entry_extract_all(t, &path, &mode, &object_type);\n \t\tpathlen = tree_entry_len(&t->entry);\n-\t\tisdir = S_ISDIR(mode);\n+\t\tisdir = (object_type == OBJ_TREE);\n \t} else {\n \t\t/*\n \t\t * a path was removed - take path from imin parent. Also take\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 46ce1ba8069..f4473276c9f 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -577,7 +577,7 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\tstruct object_id oid;\n \t\tint entrylen, cmp;\n \n-\t\toidcpy(&oid, tree_entry_extract(t, &entry, mode));\n+\t\toidcpy(&oid, tree_entry_extract_all(t, &entry, mode, object_type));\n \t\tentrylen = tree_entry_len(&t->entry);\n \t\tupdate_tree_entry(t);\n \t\tif (entrylen > namelen)\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 805cda649ee..a4c54871747 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -47,6 +47,7 @@ struct tree_desc {\n  * appropriate variable to fill in (NULL won't do!):\n  *\n  * tree_entry_extract_mode(): const char *path, unsigned int mode\n+ * tree_entry_extract_all(): const char *path, unsigned int mode, enum object_type\n  */\n static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *desc,\n \t\t\t\t\t\t\t      const char **pathp,\n@@ -57,6 +58,17 @@ static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *\n \treturn &desc->entry.oid;\n }\n \n+static inline const struct object_id *tree_entry_extract_all(struct tree_desc *desc,\n+\t\t\t\t\t\t\t     const char **pathp,\n+\t\t\t\t\t\t\t     unsigned short *modep,\n+\t\t\t\t\t\t\t     enum object_type *object_typep)\n+{\n+\t*pathp = desc->entry.path;\n+\t*modep = desc->entry.mode;\n+\t*object_typep = desc->entry.object_type;\n+\treturn &desc->entry.oid;\n+}\n+\n /**\n  * Calculate the length of a tree entry's pathname. This utilizes the\n  * memory structure of a tree entry to avoid the overhead of using a\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419822","messageId":"95eec961be8b49e4f2f68ee92f24e95fe6edc04d.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 26/29] tree-walk.h API: add get_tree_entry_all()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:00:59Z","receivedAt":"2021-03-21T00:02:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a get_tree_entry_all() function and use it in the one caller who\ncares about both the mode and the object type. Refactor it accordingly\nto make it clear which parts care about the mode, and which about the\nobject_type.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/update-index.c | 6 ++++--\n tree-walk.c            | 9 +--------\n tree-walk.h            | 4 ++++\n 3 files changed, 9 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 070510d6a88..b489a876392 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -599,16 +599,18 @@ static struct cache_entry *read_one_ent(const char *which,\n \t\t\t\t\tstruct object_id *ent, const char *path,\n \t\t\t\t\tint namelen, int stage)\n {\n+\tenum object_type object_type;\n \tunsigned short mode;\n \tstruct object_id oid;\n \tstruct cache_entry *ce;\n \n-\tif (get_tree_entry_mode(the_repository, ent, path, &oid, &mode)) {\n+\tif (get_tree_entry_all(the_repository, ent, path, &oid,\n+\t\t\t       &mode, &object_type)) {\n \t\tif (which)\n \t\t\terror(\"%s: not in %s branch.\", path, which);\n \t\treturn NULL;\n \t}\n-\tif (mode == S_IFDIR) {\n+\tif (object_type == OBJ_TREE) {\n \t\tif (which)\n \t\t\terror(\"%s: not a blob in %s branch.\", path, which);\n \t\treturn NULL;\ndiff --git a/tree-walk.c b/tree-walk.c\nindex f4473276c9f..a90dbf87af4 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -559,13 +559,6 @@ struct dir_state {\n \tstruct object_id oid;\n };\n \n-static int get_tree_entry_all(struct repository *r,\n-\t\t\t      const struct object_id *tree_oid,\n-\t\t\t      const char *name,\n-\t\t\t      struct object_id *oid,\n-\t\t\t      unsigned short *mode,\n-\t\t\t      enum object_type *object_type);\n-\n static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\t   const char *name, struct object_id *result,\n \t\t\t   unsigned short *mode,\n@@ -605,7 +598,7 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \treturn -1;\n }\n \n-static int get_tree_entry_all(struct repository *r,\n+int get_tree_entry_all(struct repository *r,\n \t\t       const struct object_id *tree_oid,\n \t\t       const char *name,\n \t\t       struct object_id *oid,\ndiff --git a/tree-walk.h b/tree-walk.h\nindex a4c54871747..55ef88ef2e5 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -195,6 +195,7 @@ struct traverse_info {\n  *\n  * get_tree_entry_mode(): unsigned int mode\n  * get_tree_entry_type(): enum object_type\n+ * get_tree_entry_all(): unsigned int mode, enum object_type\n  */\n int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n@@ -202,6 +203,9 @@ int get_tree_entry_mode(struct repository *, const struct object_id *, const cha\n int get_tree_entry_type(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n \t\t\tenum object_type *);\n+int get_tree_entry_all(struct repository *, const struct object_id *, const char *,\n+\t\t       struct object_id *,\n+\t\t       unsigned short *, enum object_type *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419823","messageId":"a2a34fd3e2de7ecd2c85cbfd13a2defd435e9c7b.1616282534.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"[PATCH v4 27/29] tree-walk.h API: add a get_tree_entry_path() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:01:00Z","receivedAt":"2021-03-21T00:02:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a get_tree_entry_path() variant in addition to\nget_tree_entry_path_{mode,type,all}(). This is for those callers that\nneed neither the mode nor \"enum object_type\" parameters filled for\nthem.\n\nThere are callers here which don't need the \"struct object_id\" filled;\nforcing callers to pass one just requires they create a throwaway\nvariable.\n\nSee the following commits for the introduction of such code that's\nbeing modified here:\n\n - shift_tree(): 68faf68938e (A new merge stragety[sic] 'subtree'.,\n    2007-02-15) for the shift_tree()\n\n - tree_has_path(): 96e7ffbdc31 (merge-recursive: check for directory\n   level conflicts, 2018-04-19)\n\n - init_notes(): fd53c9eb445 (Speed up git notes lookup, 2009-10-09)\n\n - diagnose_invalid_oid_path(): 009fee4774d (Detailed diagnosis when\n   parsing an object name fails., 2009-12-07)\n\nThose could potentially be refactored too, but I've got to stop at\nsome point, and right now I'm focusing downstream code that depends on\n\"mode\" (or \"enum object_type\").\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c     |  4 +---\n merge-recursive.c |  6 ++----\n notes.c           |  3 +--\n object-name.c     |  3 +--\n tree-walk.c       | 11 +++++++++++\n tree-walk.h       |  3 +++\n 6 files changed, 19 insertions(+), 11 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex 2afa4968109..25bfb46fb02 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -288,12 +288,10 @@ void shift_tree(struct repository *r,\n \n \tif (add_score < del_score) {\n \t\t/* We need to pick a subtree of two */\n-\t\tunsigned short mode;\n-\n \t\tif (!*del_prefix)\n \t\t\treturn;\n \n-\t\tif (get_tree_entry_mode(r, hash2, del_prefix, shifted, &mode))\n+\t\tif (get_tree_entry_path(r, hash2, del_prefix, shifted))\n \t\t\tdie(\"cannot find path %s in tree %s\",\n \t\t\t    del_prefix, oid_to_hex(hash2));\n \t\treturn;\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex f5cccb8deab..e46eea9688e 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1884,11 +1884,9 @@ static int tree_has_path(struct repository *r, struct tree *tree,\n \t\t\t const char *path)\n {\n \tstruct object_id hashy;\n-\tunsigned short mode_o;\n-\n-\treturn !get_tree_entry_mode(r,\n+\treturn !get_tree_entry_path(r,\n \t\t\t\t    &tree->object.oid, path,\n-\t\t\t\t    &hashy, &mode_o);\n+\t\t\t\t    &hashy);\n }\n \n /*\ndiff --git a/notes.c b/notes.c\nindex b7a5ddce3ae..7ffae3a0f62 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -994,7 +994,6 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\tcombine_notes_fn combine_notes, int flags)\n {\n \tstruct object_id oid, object_oid;\n-\tunsigned short mode;\n \tstruct leaf_node root_tree;\n \n \tif (!t)\n@@ -1021,7 +1020,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\treturn;\n \tif (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))\n \t\tdie(\"Cannot use notes ref %s\", notes_ref);\n-\tif (get_tree_entry_mode(the_repository, &object_oid, \"\", &oid, &mode))\n+\tif (get_tree_entry_path(the_repository, &object_oid, \"\", &oid))\n \t\tdie(\"Failed to read notes tree referenced by %s (%s)\",\n \t\t    notes_ref, oid_to_hex(&object_oid));\n \ndiff --git a/object-name.c b/object-name.c\nindex 7e3b2d6d739..9ff5f83c1ff 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1693,7 +1693,6 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \t\t\t\t      int object_name_len)\n {\n \tstruct object_id oid;\n-\tunsigned short mode;\n \n \tif (!prefix)\n \t\tprefix = \"\";\n@@ -1704,7 +1703,7 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \tif (is_missing_file_error(errno)) {\n \t\tchar *fullname = xstrfmt(\"%s%s\", prefix, filename);\n \n-\t\tif (!get_tree_entry_mode(r, tree_oid, fullname, &oid, &mode)) {\n+\t\tif (!get_tree_entry_path(r, tree_oid, fullname, &oid)) {\n \t\t\tdie(_(\"path '%s' exists, but not '%s'\\n\"\n \t\t\t    \"hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?\"),\n \t\t\t    fullname,\ndiff --git a/tree-walk.c b/tree-walk.c\nindex a90dbf87af4..fa846535dfb 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -632,6 +632,17 @@ int get_tree_entry_all(struct repository *r,\n \treturn retval;\n }\n \n+int get_tree_entry_path(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid)\n+{\n+\tunsigned short mode;\n+\tenum object_type object_type;\n+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n+\t\t\t\t  &mode, &object_type);\n+}\n+\n int get_tree_entry_mode(struct repository *r,\n \t\t\tconst struct object_id *tree_oid,\n \t\t\tconst char *name,\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 55ef88ef2e5..efcd7ccd10e 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -193,10 +193,13 @@ struct traverse_info {\n  * \"struct name_entry\" you'd like. You always need a pointer to an\n  * appropriate variable to fill in (NULL won't do!):\n  *\n+ * get_tree_entry_path(): <no extra argument, just get the common 'path'>\n  * get_tree_entry_mode(): unsigned int mode\n  * get_tree_entry_type(): enum object_type\n  * get_tree_entry_all(): unsigned int mode, enum object_type\n  */\n+int get_tree_entry_path(struct repository *, const struct object_id *, const char *,\n+\t\t\tstruct object_id *);\n int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n \t\t\tunsigned short *);\n-- \n2.31.0.286.gc175f2cb894\n\n"},{"id":"419826","messageId":"xmqqtup5cnlu.fsf@gitster.g","threadId":"18927","inReplyTo":"cover.1616282533.git.avarab@gmail.com","subject":"Re: [PATCH v4 00/29] tree-walk: mostly replace \"mode\" with \"enum object_type\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-21T01:16:45Z","receivedAt":"2021-03-21T02:12:24Z","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> A re-roll of v3 of this series[1] and on top of (and requires) my\n> just-submitted v5 re-roll of the read_tree() refactoring series[2].\n>\n> There was a regression in 1/32 of the old series. Removing the\n> canon_mode() call in diff.c didn't account for us needing to\n> canonicalize \"diff --no-index\" modes. There were no tests for this,\n> and it failed or not depending on the FS modes in the git.git checkout\n> being tested. This fixes the CI smoke coming from this series.\n\nSorry, but quite honestly, I am not quite sure what value this\nentire code churn is trying to add to the codebase.\n\nThe function signature of read_tree_fn_t callback function gets\nchanged from the mode bits (which is capable to express differences\nbetween regular files, executable files and symbolic links) to \"enum\nobject_type\" (which can only say \"this is a blob\"), which is a\nregression, no?  \n\nA callback can no longer do things like this, for example:\n\nstatic int add_path_to_index(const struct object_id *oid,\n\t\t\t     struct strbuf *base, const char *path,\n\t\t\t     unsigned int mode, void *context)\n{\n\tstruct index_state *istate = (struct index_state *)context;\n\tstruct cache_entry *ce;\n\tsize_t len = base->len;\n\n\tif (S_ISDIR(mode))\n\t\treturn READ_TREE_RECURSIVE;\n\n\tstrbuf_addstr(base, path);\n\n\tce = make_cache_entry(istate, mode, oid, base->buf, 0, 0);\n\tce->ce_flags |= CE_SKIP_WORKTREE;\n\tset_index_entry(istate, istate->cache_nr++, ce);\n\n\tstrbuf_setlen(base, len);\n\treturn 0;\n}\n\nwhere executableness or symlinkshood is lost.\n\nThis probably is the third time I caught similar \"let's lose\ninformation passed through the call chain as nobody seems to need\nit\" mistakes in the iterations of this series, and that is two times\ntoo many.  We should learn from our earlier mistakes---tweaking of\nthe API that happens to be still OK with the current codebase can be\neither a needless churn that loses useful expressiveness from the\nAPI, or a useful clean-up to kill dead parameter or excess\nflexibility.\n\nAnd these three incidents we have seen so far are the former.\n\nThanks.\n\n\n\n\n\n"},{"id":"419847","messageId":"87wnu0r8tq.fsf@evledraar.gmail.com","threadId":"18927","inReplyTo":"xmqqtup5cnlu.fsf@gitster.g","subject":"Re: [PATCH v4 00/29] tree-walk: mostly replace \"mode\" with \"enum object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T12:26:57Z","receivedAt":"2021-03-21T12:27:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Mar 21 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> A re-roll of v3 of this series[1] and on top of (and requires) my\n>> just-submitted v5 re-roll of the read_tree() refactoring series[2].\n>>\n>> There was a regression in 1/32 of the old series. Removing the\n>> canon_mode() call in diff.c didn't account for us needing to\n>> canonicalize \"diff --no-index\" modes. There were no tests for this,\n>> and it failed or not depending on the FS modes in the git.git checkout\n>> being tested. This fixes the CI smoke coming from this series.\n>\n> Sorry, but quite honestly, I am not quite sure what value this\n> entire code churn is trying to add to the codebase.\n>\n> The function signature of read_tree_fn_t callback function gets\n> changed from the mode bits (which is capable to express differences\n> between regular files, executable files and symbolic links) to \"enum\n> object_type\" (which can only say \"this is a blob\"), which is a\n> regression, no?  \n>\n> A callback can no longer do things like this, for example:\n>\n> static int add_path_to_index(const struct object_id *oid,\n> \t\t\t     struct strbuf *base, const char *path,\n> \t\t\t     unsigned int mode, void *context)\n> {\n> \tstruct index_state *istate = (struct index_state *)context;\n> \tstruct cache_entry *ce;\n> \tsize_t len = base->len;\n>\n> \tif (S_ISDIR(mode))\n> \t\treturn READ_TREE_RECURSIVE;\n>\n> \tstrbuf_addstr(base, path);\n>\n> \tce = make_cache_entry(istate, mode, oid, base->buf, 0, 0);\n> \tce->ce_flags |= CE_SKIP_WORKTREE;\n> \tset_index_entry(istate, istate->cache_nr++, ce);\n>\n> \tstrbuf_setlen(base, len);\n> \treturn 0;\n> }\n>\n> where executableness or symlinkshood is lost.\n\nYes, that would be a serious regression. I agree that all these\nfunctions/callbacks etc. should have a way to get at the mode bits.\n\nI'm adding \"enum object_type\", not removing the \"mode\" parameter in\nread_tree_fn_t. This function (which is in \"seen\" as 03316f20347\n(sparse-index: implement ensure_full_index(), 2021-03-16)) works just\nfine in combination with this series.\n\nThe other APIs modified here all retain the ability to give you the mode\nbit, they (the tree-walk.h changes) just optionally give you the option\nof getting just the type (or just the path), and as it turns out most\nusers of the API can be converted over to that.\n\n> This probably is the third time I caught similar \"let's lose\n> information passed through the call chain as nobody seems to need\n> it\" mistakes in the iterations of this series, and that is two times\n> too many.  We should learn from our earlier mistakes---tweaking of\n> the API that happens to be still OK with the current codebase can be\n> either a needless churn that loses useful expressiveness from the\n> API, or a useful clean-up to kill dead parameter or excess\n> flexibility.\n>\n> And these three incidents we have seen so far are the former.\n\nThe current codebase will allow you to stick arbitrary mode bits in\ntrees, we have an fsck check to prevent that which doesn't work. I had a\nsummary of this in v1, but should probably have provided a recap[1].\n\nThis series is an admittedly long journey towards fixing that. I've got\nunsubmitted patchen on top that make that fsck check work again.\n\nI think the root cause of these bugs and other ones I've found along the\nway (some of which I'm not quite comfortable discussing the details of\non the public list yet) is that the tree walking API is unnecessarily\nlow-level for most callers.\n\nMost of those callers don't care about the details of the mode bits, but\nare just traversing a tree and doing something with one the object\ntypes.\n\nAs opposed to having a mode, but do you want a mode as-is from a tree,\nnormalized to canon_mode() (for writing?) etc. I think being able to\nclearly tell apart those callers from the simpler ones is a clear win.\n\nSo I'm hoping you'll bear with me & this series, sorry about the\nbreakages so far, in my slight defense they were all subtle testing\nblind spots (but we now have tests!).\n\n1. https://lore.kernel.org/git/20210308150650.18626-1-avarab@gmail.com/\n"},{"id":"419849","messageId":"cover.1616330120.git.avarab@gmail.com","threadId":"18927","inReplyTo":"87wnu0r8tq.fsf@evledraar.gmail.com","subject":"[PATCH 0/2] diff --no-index: fix test blind spots","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T12:39:21Z","receivedAt":"2021-03-21T12:41:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"A trivial series to add tests for \"git diff --no-index\"'s mode\nhandling, and a missing test for how --exit-code behaves in\ncombination with --no-index while I was at it.\n\nThis would have caught the regression I inadvertently introduced\nin[1]. It's related to my much larger tree-walk.h series[2], but\napplies independently of that. I wanted to split it off as there were\nno conflicts or behavior changes related to canon_mode() in that\nseries anymore.\n\n1. https://lore.kernel.org/git/20210316155829.31242-2-avarab@gmail.com/\n2. https://lore.kernel.org/git/cover.1616282533.git.avarab@gmail.com/\n\n\nÆvar Arnfjörð Bjarmason (2):\n  diff --no-index tests: add test for --exit-code\n  diff --no-index tests: test mode normalization\n\n t/t4053-diff-no-index.sh | 37 ++++++++++++++++++++++++++++++++++++-\n 1 file changed, 36 insertions(+), 1 deletion(-)\n\n-- \n2.31.0.282.gcc1ec606501\n\n"},{"id":"419850","messageId":"2dcc8bccf9733c6cc072bdb55a5104f16084e5c3.1616330120.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616330120.git.avarab@gmail.com","subject":"[PATCH 2/2] diff --no-index tests: test mode normalization","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T12:39:23Z","receivedAt":"2021-03-21T12:41:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"When \"git diff --no-index X Y\" is run the modes of the files being\ndiffer are normalized by canon_mode() in fill_filespec().\n\nI recently broke that behavior in a patch of mine[1] which would pass\nall tests, or not, depending on the umask of the git.git checkout.\n\nLet's test for this explicitly. Arguably this should not be the\nbehavior of \"git diff --no-index\". We aren't diffing our own objects\nor the index, so it might be useful to show mode differences between\nfiles.\n\nOn the other hand diff(1) does not do that, and it would be needlessly\ndistracting when e.g. diffing an extracted tar archive whose contents\nis the same, but whose file modes are different.\n\n1. https://lore.kernel.org/git/20210316155829.31242-2-avarab@gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4053-diff-no-index.sh | 30 ++++++++++++++++++++++++++++++\n 1 file changed, 30 insertions(+)\n\ndiff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh\nindex 9b7a8ebfd3f..6fae18612fc 100755\n--- a/t/t4053-diff-no-index.sh\n+++ b/t/t4053-diff-no-index.sh\n@@ -149,4 +149,34 @@ test_expect_success 'diff --no-index allows external diff' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'diff --no-index normalizes mode: no changes' '\n+\techo foo >x &&\n+\tcp x y &&\n+\tgit diff --no-index x y >out &&\n+\ttest_must_be_empty out\n+'\n+\n+test_expect_success 'diff --no-index normalizes mode: chmod +x' '\n+\tchmod +x y &&\n+\tcat >expected <<-\\EOF &&\n+\tdiff --git a/x b/y\n+\told mode 100644\n+\tnew mode 100755\n+\tEOF\n+\ttest_expect_code 1 git diff --no-index x y >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'diff --no-index normalizes: mode not like git mode' '\n+\tchmod 666 x &&\n+\tchmod 777 y &&\n+\tcat >expected <<-\\EOF &&\n+\tdiff --git a/x b/y\n+\told mode 100644\n+\tnew mode 100755\n+\tEOF\n+\ttest_expect_code 1 git diff --no-index x y >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.31.0.282.gcc1ec606501\n\n"},{"id":"419851","messageId":"a6e4ed6c3f1d37170d7e99a2fab9c90662cceb19.1616330120.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616330120.git.avarab@gmail.com","subject":"[PATCH 1/2] diff --no-index tests: add test for --exit-code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T12:39:22Z","receivedAt":"2021-03-21T12:41:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for --exit-code working with --no-index. There's no reason\nto suppose it wouldn't, but we weren't testing for it anywhere in our\ntests. Let's fix that blind spot.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4053-diff-no-index.sh | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh\nindex 0168946b639..9b7a8ebfd3f 100755\n--- a/t/t4053-diff-no-index.sh\n+++ b/t/t4053-diff-no-index.sh\n@@ -16,7 +16,12 @@ test_expect_success 'setup' '\n \techo 1 >non/git/b\n '\n \n-test_expect_success 'git diff --no-index directories' '\n+test_expect_success 'git diff --no-index --exit-code' '\n+\tgit diff --no-index --exit-code a/1 non/git/a &&\n+\ttest_expect_code 1 git diff --no-index --exit-code a/1 a/2\n+'\n+\n+Test_expect_success 'git diff --no-index directories' '\n \ttest_expect_code 1 git diff --no-index a b >cnt &&\n \ttest_line_count = 14 cnt\n '\n-- \n2.31.0.282.gcc1ec606501\n\n"},{"id":"419873","messageId":"xmqqlfagbfbk.fsf@gitster.g","threadId":"18927","inReplyTo":"87wnu0r8tq.fsf@evledraar.gmail.com","subject":"Re: [PATCH v4 00/29] tree-walk: mostly replace \"mode\" with \"enum object_type\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-21T17:13:19Z","receivedAt":"2021-03-21T17:14:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Yes, that would be a serious regression. I agree that all these\n> functions/callbacks etc. should have a way to get at the mode bits.\n>\n> I'm adding \"enum object_type\", not removing the \"mode\" parameter in\n> read_tree_fn_t. This function (which is in \"seen\" as 03316f20347\n> (sparse-index: implement ensure_full_index(), 2021-03-16)) works just\n> fine in combination with this series.\n>\n> The other APIs modified here all retain the ability to give you the mode\n> bit, they (the tree-walk.h changes) just optionally give you the option\n> of getting just the type (or just the path), and as it turns out most\n> users of the API can be converted over to that.\n\nI have a vague feeling that such an approach may be still repeating\nthe same mistake.\n\nIf the original premise is that \"unsigned mode\" bit can be abused to\nfeed impossible values like 0100653 to the system and the code\nshould catch it, ...\n\n> The current codebase will allow you to stick arbitrary mode bits in\n> trees, ...\n\n... then I would understand it if the approach is to introduce a\ndistinct type that enumerates all possible mode bit values (and\nnothing else), and have the compiler validate the callchain, so that\nnobody can pass bogus mode bits (and when reading mode bits fields\nin existing objects, the one that converts from the series of octal\nbytes to that distsinct type would notice and complain).  And we've\nalready seen from this particular breakages that the \"distinct type\"\nappropriate to be used for that purpose is not \"enum object_type\".\nIt is not expressive enough to enumerate all possible mode bit\nvalues (besides, an enum is interchangeable with an int, so there\nisn't much protection we would be getting from the compiler---we\ncould use a small struct of a new type, and have one static const\ninstance for each possible mode bit combination, I guess, but the\npoint here is that insisting on using \"enum object_type\" seems to be\nthe source of the problem).\n\nI am afraid that it is even worse to pass both object type and\n\"unsigned mode\" together.  It would still leave room for a bug to\npass nonsense mode bits, which we said we wanted to catch in our\noriginal mission statement.  In addition, we now have a new room for\na bug, which is to pass an inconsistent pair of object type and mode\nto the callchain.  Somebody would need to say \"yuck, we got a mode\n100644 but the type says TREE\" now, in addition to validating if the\nmode is sensible, which we should be doing somehow, no?\n\nSo, I am not sure how these changes are making anything better.\n\n> ... I had a\n> summary of this in v1, but should probably have provided a recap[1].\n\nOh, absolutely.  When we iterate, we should be welcoming to those\nwho missed earlier iterations.\n\n"},{"id":"419879","messageId":"29da572f-8d94-000e-ffc0-fded4b56d3c3@ramsayjones.plus.com","threadId":"18927","inReplyTo":"a6e4ed6c3f1d37170d7e99a2fab9c90662cceb19.1616330120.git.avarab@gmail.com","subject":"Re: [PATCH 1/2] diff --no-index tests: add test for --exit-code","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2021-03-21T18:33:58Z","receivedAt":"2021-03-21T18:42:34Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 21/03/2021 12:39, Ævar Arnfjörð Bjarmason wrote:\n> Add a test for --exit-code working with --no-index. There's no reason\n> to suppose it wouldn't, but we weren't testing for it anywhere in our\n> tests. Let's fix that blind spot.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t4053-diff-no-index.sh | 7 ++++++-\n>  1 file changed, 6 insertions(+), 1 deletion(-)\n> \n> diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh\n> index 0168946b639..9b7a8ebfd3f 100755\n> --- a/t/t4053-diff-no-index.sh\n> +++ b/t/t4053-diff-no-index.sh\n> @@ -16,7 +16,12 @@ test_expect_success 'setup' '\n>  \techo 1 >non/git/b\n>  '\n>  \n> -test_expect_success 'git diff --no-index directories' '\n> +test_expect_success 'git diff --no-index --exit-code' '\n> +\tgit diff --no-index --exit-code a/1 non/git/a &&\n> +\ttest_expect_code 1 git diff --no-index --exit-code a/1 a/2\n> +'\n> +\n> +Test_expect_success 'git diff --no-index directories' '\n\nI assume that s/test/Test/ was not intended. ;-)\n\nATB,\nRamsay Jones\n\n>  \ttest_expect_code 1 git diff --no-index a b >cnt &&\n>  \ttest_line_count = 14 cnt\n>  '\n> \n"},{"id":"419880","messageId":"87o8fcqrg8.fsf@evledraar.gmail.com","threadId":"18927","inReplyTo":"xmqqlfagbfbk.fsf@gitster.g","subject":"Re: [PATCH v4 00/29] tree-walk: mostly replace \"mode\" with \"enum object_type\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T18:42:15Z","receivedAt":"2021-03-21T18:43:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Mar 21 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> Yes, that would be a serious regression. I agree that all these\n>> functions/callbacks etc. should have a way to get at the mode bits.\n>>\n>> I'm adding \"enum object_type\", not removing the \"mode\" parameter in\n>> read_tree_fn_t. This function (which is in \"seen\" as 03316f20347\n>> (sparse-index: implement ensure_full_index(), 2021-03-16)) works just\n>> fine in combination with this series.\n>>\n>> The other APIs modified here all retain the ability to give you the mode\n>> bit, they (the tree-walk.h changes) just optionally give you the option\n>> of getting just the type (or just the path), and as it turns out most\n>> users of the API can be converted over to that.\n>\n> I have a vague feeling that such an approach may be still repeating\n> the same mistake.\n>\n> If the original premise is that \"unsigned mode\" bit can be abused to\n> feed impossible values like 0100653 to the system and the code\n> should catch it, ...\n>\n>> The current codebase will allow you to stick arbitrary mode bits in\n>> trees, ...\n>\n> ... then I would understand it if the approach is to introduce a\n> distinct type that enumerates all possible mode bit values (and\n> nothing else), and have the compiler validate the callchain, so that\n> nobody can pass bogus mode bits (and when reading mode bits fields\n> in existing objects, the one that converts from the series of octal\n> bytes to that distsinct type would notice and complain).  And we've\n> already seen from this particular breakages that the \"distinct type\"\n> appropriate to be used for that purpose is not \"enum object_type\".\n> It is not expressive enough to enumerate all possible mode bit\n> values (besides, an enum is interchangeable with an int, so there\n> isn't much protection we would be getting from the compiler---we\n> could use a small struct of a new type, and have one static const\n> instance for each possible mode bit combination, I guess, but the\n> point here is that insisting on using \"enum object_type\" seems to be\n> the source of the problem).\n\nYes, this is a good suggestion, but one which'll be much easier to do\nafter this series. Since the majority of callers don't care about the\nraw mode bits or anything except if the entry is a blob/tree/commit.\n\n> I am afraid that it is even worse to pass both object type and\n> \"unsigned mode\" together.  It would still leave room for a bug to\n> pass nonsense mode bits, which we said we wanted to catch in our\n> original mission statement.  In addition, we now have a new room for\n> a bug, which is to pass an inconsistent pair of object type and mode\n> to the callchain.  Somebody would need to say \"yuck, we got a mode\n> 100644 but the type says TREE\" now, in addition to validating if the\n> mode is sensible, which we should be doing somehow, no?\n\nIndeed. This goes back to my \"bear with me\" comment upthread. I'm\nplanning to fix these cases too, and starting by moving most things to\n\"enum object_type\" makes that a lot easier.\n\nRight now we simply trust the mode bits, but that opens the door to the\nsame sort of bug I'm fixing in another series (that I need to re-roll)\nwhere we have a \"type commit\" and \"object ID\" in a tag, but the ID is\nreally a tree or whatever.\n\nWe then get confused because we trusted the invalid metadata over\nchecking the type of the object ID we have in the object store.\n\nSo this particular series doesn't fix this bug at the end of it, the\n\"object_type\" in tree-walk.c is just a function of deriving it from the\nmode bits.\n\nBut once I've audited the cases where we're really just acting on the\ntype and don't have or use the mode (so we know we're not re-writing the\nmode somewhere) then we would set the object_type to the actual type, as\nin what we'd get from oid_object_info(ID).\n\nThen we can e.g. scream murder about a mode/type mismatch in fsck, but\nstill be able to list/diff/inspect etc. that object in any codepath that\njust wants to e.g. recursively iterate over the tree.\n\n\n\n> So, I am not sure how these changes are making anything better.\n>\n>> ... I had a\n>> summary of this in v1, but should probably have provided a recap[1].\n>\n> Oh, absolutely.  When we iterate, we should be welcoming to those\n> who missed earlier iterations.\n\n"},{"id":"419888","messageId":"xmqqa6qw9op2.fsf@gitster.g","threadId":"18927","inReplyTo":"29da572f-8d94-000e-ffc0-fded4b56d3c3@ramsayjones.plus.com","subject":"Re: [PATCH 1/2] diff --no-index tests: add test for --exit-code","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-21T21:33:45Z","receivedAt":"2021-03-21T21:38:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ramsay Jones <ramsay@ramsayjones.plus.com> writes:\n\n> On 21/03/2021 12:39, Ævar Arnfjörð Bjarmason wrote:\n>> Add a test for --exit-code working with --no-index. There's no reason\n>> to suppose it wouldn't, but we weren't testing for it anywhere in our\n>> tests. Let's fix that blind spot.\n>> \n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>  t/t4053-diff-no-index.sh | 7 ++++++-\n>>  1 file changed, 6 insertions(+), 1 deletion(-)\n>> \n>> diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh\n>> index 0168946b639..9b7a8ebfd3f 100755\n>> --- a/t/t4053-diff-no-index.sh\n>> +++ b/t/t4053-diff-no-index.sh\n>> @@ -16,7 +16,12 @@ test_expect_success 'setup' '\n>>  \techo 1 >non/git/b\n>>  '\n>>  \n>> -test_expect_success 'git diff --no-index directories' '\n>> +test_expect_success 'git diff --no-index --exit-code' '\n>> +\tgit diff --no-index --exit-code a/1 non/git/a &&\n>> +\ttest_expect_code 1 git diff --no-index --exit-code a/1 a/2\n>> +'\n>> +\n>> +Test_expect_success 'git diff --no-index directories' '\n>\n> I assume that s/test/Test/ was not intended. ;-)\n\n;-)  \n\nLove to see reviewers are more careful than submitters' shells that\nare too lenient to allow such a test to pass before such a patch\ngets submitted.\n\n"},{"id":"419892","messageId":"cover.1616366036.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616330120.git.avarab@gmail.com","subject":"[PATCH v2 0/2] diff --no-index: fix test blind spots","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T22:36:18Z","receivedAt":"2021-03-21T22:39:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fixes a stupid s/Test/test/ typo in v1. Thanks Ramsey. I also noticed\na failure on one of the Windows CI's (curiously, not all?), so\nPOSIXPERM for these tests is another thing I forgot.\n\nI've also added a symlink diff test for good measure. I'm testing the\nfull output, but using the the approprite variables, so it passes\nunder both GIT_TEST_DEFAULT_HASH=sha1 & sha256.\n\nÆvar Arnfjörð Bjarmason (2):\n  diff --no-index tests: add test for --exit-code\n  diff --no-index tests: test mode normalization\n\n t/t4053-diff-no-index.sh | 60 ++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 60 insertions(+)\n\nRange-diff:\n1:  a6e4ed6c3f1 ! 1:  2dbc6c253e2 diff --no-index tests: add test for --exit-code\n    @@ t/t4053-diff-no-index.sh: test_expect_success 'setup' '\n      \techo 1 >non/git/b\n      '\n      \n    --test_expect_success 'git diff --no-index directories' '\n     +test_expect_success 'git diff --no-index --exit-code' '\n     +\tgit diff --no-index --exit-code a/1 non/git/a &&\n     +\ttest_expect_code 1 git diff --no-index --exit-code a/1 a/2\n     +'\n     +\n    -+Test_expect_success 'git diff --no-index directories' '\n    + test_expect_success 'git diff --no-index directories' '\n      \ttest_expect_code 1 git diff --no-index a b >cnt &&\n      \ttest_line_count = 14 cnt\n    - '\n2:  2dcc8bccf97 ! 2:  a1ab6a323f2 diff --no-index tests: test mode normalization\n    @@ t/t4053-diff-no-index.sh: test_expect_success 'diff --no-index allows external d\n     +\ttest_must_be_empty out\n     +'\n     +\n    -+test_expect_success 'diff --no-index normalizes mode: chmod +x' '\n    ++test_expect_success POSIXPERM 'diff --no-index normalizes mode: chmod +x' '\n     +\tchmod +x y &&\n     +\tcat >expected <<-\\EOF &&\n     +\tdiff --git a/x b/y\n    @@ t/t4053-diff-no-index.sh: test_expect_success 'diff --no-index allows external d\n     +\ttest_cmp expected actual\n     +'\n     +\n    -+test_expect_success 'diff --no-index normalizes: mode not like git mode' '\n    ++test_expect_success POSIXPERM 'diff --no-index normalizes: mode not like git mode' '\n     +\tchmod 666 x &&\n     +\tchmod 777 y &&\n     +\tcat >expected <<-\\EOF &&\n    @@ t/t4053-diff-no-index.sh: test_expect_success 'diff --no-index allows external d\n     +\ttest_expect_code 1 git diff --no-index x y >actual &&\n     +\ttest_cmp expected actual\n     +'\n    ++\n    ++test_expect_success POSIXPERM,SYMLINKS 'diff --no-index normalizes: mode not like git mode (symlink)' '\n    ++\tln -s y z &&\n    ++\tX_OID=$(git hash-object --stdin <x) &&\n    ++\tZ_OID=$(echo -n y | git hash-object --stdin) &&\n    ++\tcat >expected <<-EOF &&\n    ++\tdiff --git a/x b/x\n    ++\tdeleted file mode 100644\n    ++\tindex $X_OID..$ZERO_OID\n    ++\t--- a/x\n    ++\t+++ /dev/null\n    ++\t@@ -1 +0,0 @@\n    ++\t-foo\n    ++\tdiff --git a/z b/z\n    ++\tnew file mode 120000\n    ++\tindex $ZERO_OID..$Z_OID\n    ++\t--- /dev/null\n    ++\t+++ b/z\n    ++\t@@ -0,0 +1 @@\n    ++\t+y\n    ++\t\\ No newline at end of file\n    ++\tEOF\n    ++\ttest_expect_code 1 git -c core.abbrev=no diff --no-index x z >actual &&\n    ++\ttest_cmp expected actual\n    ++'\n     +\n      test_done\n-- \n2.31.0.285.gb40d23e604f\n\n"},{"id":"419893","messageId":"2dbc6c253e2725819602fd5a09f491a8bc14ad8e.1616366036.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616366036.git.avarab@gmail.com","subject":"[PATCH v2 1/2] diff --no-index tests: add test for --exit-code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T22:36:19Z","receivedAt":"2021-03-21T22:39:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for --exit-code working with --no-index. There's no reason\nto suppose it wouldn't, but we weren't testing for it anywhere in our\ntests. Let's fix that blind spot.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4053-diff-no-index.sh | 5 +++++\n 1 file changed, 5 insertions(+)\n\ndiff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh\nindex 0168946b639..44b932fbb20 100755\n--- a/t/t4053-diff-no-index.sh\n+++ b/t/t4053-diff-no-index.sh\n@@ -16,6 +16,11 @@ test_expect_success 'setup' '\n \techo 1 >non/git/b\n '\n \n+test_expect_success 'git diff --no-index --exit-code' '\n+\tgit diff --no-index --exit-code a/1 non/git/a &&\n+\ttest_expect_code 1 git diff --no-index --exit-code a/1 a/2\n+'\n+\n test_expect_success 'git diff --no-index directories' '\n \ttest_expect_code 1 git diff --no-index a b >cnt &&\n \ttest_line_count = 14 cnt\n-- \n2.31.0.285.gb40d23e604f\n\n"},{"id":"419894","messageId":"a1ab6a323f2e0aeed781a95d286740d3013ff5f0.1616366036.git.avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616366036.git.avarab@gmail.com","subject":"[PATCH v2 2/2] diff --no-index tests: test mode normalization","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T22:36:20Z","receivedAt":"2021-03-21T22:39:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"When \"git diff --no-index X Y\" is run the modes of the files being\ndiffer are normalized by canon_mode() in fill_filespec().\n\nI recently broke that behavior in a patch of mine[1] which would pass\nall tests, or not, depending on the umask of the git.git checkout.\n\nLet's test for this explicitly. Arguably this should not be the\nbehavior of \"git diff --no-index\". We aren't diffing our own objects\nor the index, so it might be useful to show mode differences between\nfiles.\n\nOn the other hand diff(1) does not do that, and it would be needlessly\ndistracting when e.g. diffing an extracted tar archive whose contents\nis the same, but whose file modes are different.\n\n1. https://lore.kernel.org/git/20210316155829.31242-2-avarab@gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4053-diff-no-index.sh | 55 ++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 55 insertions(+)\n\ndiff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh\nindex 44b932fbb20..39d1da1f362 100755\n--- a/t/t4053-diff-no-index.sh\n+++ b/t/t4053-diff-no-index.sh\n@@ -149,4 +149,59 @@ test_expect_success 'diff --no-index allows external diff' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'diff --no-index normalizes mode: no changes' '\n+\techo foo >x &&\n+\tcp x y &&\n+\tgit diff --no-index x y >out &&\n+\ttest_must_be_empty out\n+'\n+\n+test_expect_success POSIXPERM 'diff --no-index normalizes mode: chmod +x' '\n+\tchmod +x y &&\n+\tcat >expected <<-\\EOF &&\n+\tdiff --git a/x b/y\n+\told mode 100644\n+\tnew mode 100755\n+\tEOF\n+\ttest_expect_code 1 git diff --no-index x y >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success POSIXPERM 'diff --no-index normalizes: mode not like git mode' '\n+\tchmod 666 x &&\n+\tchmod 777 y &&\n+\tcat >expected <<-\\EOF &&\n+\tdiff --git a/x b/y\n+\told mode 100644\n+\tnew mode 100755\n+\tEOF\n+\ttest_expect_code 1 git diff --no-index x y >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success POSIXPERM,SYMLINKS 'diff --no-index normalizes: mode not like git mode (symlink)' '\n+\tln -s y z &&\n+\tX_OID=$(git hash-object --stdin <x) &&\n+\tZ_OID=$(echo -n y | git hash-object --stdin) &&\n+\tcat >expected <<-EOF &&\n+\tdiff --git a/x b/x\n+\tdeleted file mode 100644\n+\tindex $X_OID..$ZERO_OID\n+\t--- a/x\n+\t+++ /dev/null\n+\t@@ -1 +0,0 @@\n+\t-foo\n+\tdiff --git a/z b/z\n+\tnew file mode 120000\n+\tindex $ZERO_OID..$Z_OID\n+\t--- /dev/null\n+\t+++ b/z\n+\t@@ -0,0 +1 @@\n+\t+y\n+\t\\ No newline at end of file\n+\tEOF\n+\ttest_expect_code 1 git -c core.abbrev=no diff --no-index x z >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.31.0.285.gb40d23e604f\n\n"},{"id":"419895","messageId":"87k0q0qg8o.fsf@evledraar.gmail.com","threadId":"18927","inReplyTo":"xmqqa6qw9op2.fsf@gitster.g","subject":"Re: [PATCH 1/2] diff --no-index tests: add test for --exit-code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T22:44:23Z","receivedAt":"2021-03-21T22:45:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Mar 21 2021, Junio C Hamano wrote:\n\n> Ramsay Jones <ramsay@ramsayjones.plus.com> writes:\n>\n>> On 21/03/2021 12:39, Ævar Arnfjörð Bjarmason wrote:\n>>> Add a test for --exit-code working with --no-index. There's no reason\n>>> to suppose it wouldn't, but we weren't testing for it anywhere in our\n>>> tests. Let's fix that blind spot.\n>>> \n>>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>>> ---\n>>>  t/t4053-diff-no-index.sh | 7 ++++++-\n>>>  1 file changed, 6 insertions(+), 1 deletion(-)\n>>> \n>>> diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh\n>>> index 0168946b639..9b7a8ebfd3f 100755\n>>> --- a/t/t4053-diff-no-index.sh\n>>> +++ b/t/t4053-diff-no-index.sh\n>>> @@ -16,7 +16,12 @@ test_expect_success 'setup' '\n>>>  \techo 1 >non/git/b\n>>>  '\n>>>  \n>>> -test_expect_success 'git diff --no-index directories' '\n>>> +test_expect_success 'git diff --no-index --exit-code' '\n>>> +\tgit diff --no-index --exit-code a/1 non/git/a &&\n>>> +\ttest_expect_code 1 git diff --no-index --exit-code a/1 a/2\n>>> +'\n>>> +\n>>> +Test_expect_success 'git diff --no-index directories' '\n>>\n>> I assume that s/test/Test/ was not intended. ;-)\n>\n> ;-)  \n>\n> Love to see reviewers are more careful than submitters' shells that\n> are too lenient to allow such a test to pass before such a patch\n> gets submitted.\n\nSorry about that, and thanks Ramsey for spotting it.\n\nFWIW I don't think there's any shell you can run the tests with that\nwould catch this. We won't run the tests at all with \"set -e\"[1], and we\ndon't use \"set -e\" in the tests themselves by convention.\n\nSo you'll see a notice about an unknown Test_expect_success function in\nthe output if you're not blind to it as I apparently was, but the the\ntest will succeed, CI will pass etc.\n\n1. In this case the thing holding it back is the structure of the sanity\n   check added in 2006f0adaee (t/test-lib: make sure Git has already\n   been built, 2012-09-17), we'd proceed if it was part of the \"if\",\n   hrm...\n"},{"id":"419904","messageId":"xmqqblbb95jy.fsf@gitster.g","threadId":"18927","inReplyTo":"cover.1616366036.git.avarab@gmail.com","subject":"Re: [PATCH v2 0/2] diff --no-index: fix test blind spots","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-22T04:27:13Z","receivedAt":"2021-03-22T04:28:06Z","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> Fixes a stupid s/Test/test/ typo in v1. Thanks Ramsey. I also noticed\n> a failure on one of the Windows CI's (curiously, not all?), so\n> POSIXPERM for these tests is another thing I forgot.\n\nNice.  Thanks for a quick reroll.\n"},{"id":"419973","messageId":"xmqqsg4n56zm.fsf@gitster.g","threadId":"18927","inReplyTo":"a1ab6a323f2e0aeed781a95d286740d3013ff5f0.1616366036.git.avarab@gmail.com","subject":"Re: [PATCH v2 2/2] diff --no-index tests: test mode normalization","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-22T19:22:05Z","receivedAt":"2021-03-22T19:23:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> +test_expect_success POSIXPERM,SYMLINKS 'diff --no-index normalizes: mode not like git mode (symlink)' '\n> +\tln -s y z &&\n> +\tX_OID=$(git hash-object --stdin <x) &&\n> +\tZ_OID=$(echo -n y | git hash-object --stdin) &&\n\ns/echo -n/printf/\n\nt4053-diff-no-index.sh:185: error: echo with option is not portable\n(use printf): Z_OID=$(echo -n y | git hash-object --stdin) &&\ngmake[1]: *** [Makefile:95: test-lint-shell-syntax] Error 1\ngmake[1]: Leaving directory '/home/jch/w/buildfarm/jch/t'\ngmake: *** [Makefile:2855: test] Error 2\n"},{"id":"420043","messageId":"patch-2.3-e5bb094b783-20210323T163917Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-0.3-00000000000-20210323T163917Z-avarab@gmail.com","subject":"[PATCH v3 2/2] diff --no-index tests: test mode normalization","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-23T16:40:14Z","receivedAt":"2021-03-23T16:41:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"When \"git diff --no-index X Y\" is run the modes of the files being\ndiffer are normalized by canon_mode() in fill_filespec().\n\nI recently broke that behavior in a patch of mine[1] which would pass\nall tests, or not, depending on the umask of the git.git checkout.\n\nLet's test for this explicitly. Arguably this should not be the\nbehavior of \"git diff --no-index\". We aren't diffing our own objects\nor the index, so it might be useful to show mode differences between\nfiles.\n\nOn the other hand diff(1) does not do that, and it would be needlessly\ndistracting when e.g. diffing an extracted tar archive whose contents\nis the same, but whose file modes are different.\n\n1. https://lore.kernel.org/git/20210316155829.31242-2-avarab@gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4053-diff-no-index.sh | 55 ++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 55 insertions(+)\n\ndiff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh\nindex 44b932fbb20..5c5545f91bf 100755\n--- a/t/t4053-diff-no-index.sh\n+++ b/t/t4053-diff-no-index.sh\n@@ -149,4 +149,59 @@ test_expect_success 'diff --no-index allows external diff' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'diff --no-index normalizes mode: no changes' '\n+\techo foo >x &&\n+\tcp x y &&\n+\tgit diff --no-index x y >out &&\n+\ttest_must_be_empty out\n+'\n+\n+test_expect_success POSIXPERM 'diff --no-index normalizes mode: chmod +x' '\n+\tchmod +x y &&\n+\tcat >expected <<-\\EOF &&\n+\tdiff --git a/x b/y\n+\told mode 100644\n+\tnew mode 100755\n+\tEOF\n+\ttest_expect_code 1 git diff --no-index x y >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success POSIXPERM 'diff --no-index normalizes: mode not like git mode' '\n+\tchmod 666 x &&\n+\tchmod 777 y &&\n+\tcat >expected <<-\\EOF &&\n+\tdiff --git a/x b/y\n+\told mode 100644\n+\tnew mode 100755\n+\tEOF\n+\ttest_expect_code 1 git diff --no-index x y >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success POSIXPERM,SYMLINKS 'diff --no-index normalizes: mode not like git mode (symlink)' '\n+\tln -s y z &&\n+\tX_OID=$(git hash-object --stdin <x) &&\n+\tZ_OID=$(printf \"y\" | git hash-object --stdin) &&\n+\tcat >expected <<-EOF &&\n+\tdiff --git a/x b/x\n+\tdeleted file mode 100644\n+\tindex $X_OID..$ZERO_OID\n+\t--- a/x\n+\t+++ /dev/null\n+\t@@ -1 +0,0 @@\n+\t-foo\n+\tdiff --git a/z b/z\n+\tnew file mode 120000\n+\tindex $ZERO_OID..$Z_OID\n+\t--- /dev/null\n+\t+++ b/z\n+\t@@ -0,0 +1 @@\n+\t+y\n+\t\\ No newline at end of file\n+\tEOF\n+\ttest_expect_code 1 git -c core.abbrev=no diff --no-index x z >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.31.0.366.g871543fb182\n\n"},{"id":"420044","messageId":"cover-0.3-00000000000-20210323T163917Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover.1616366036.git.avarab@gmail.com","subject":"[PATCH v3 0/2] diff --no-index: fix test blind spots","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-23T16:40:12Z","receivedAt":"2021-03-23T16:41:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since v2[1]: s/echo -n/printf/ (and I re-adjusted my test setup to run\nthe linting..)\n\n1. https://lore.kernel.org/git/cover.1616366036.git.avarab@gmail.com/\n\nÆvar Arnfjörð Bjarmason (2):\n  diff --no-index tests: add test for --exit-code\n  diff --no-index tests: test mode normalization\n\n t/t4053-diff-no-index.sh | 60 ++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 60 insertions(+)\n\nRange-diff:\n1:  2dbc6c253e2 = 1:  7dd5c309a70 diff --no-index tests: add test for --exit-code\n2:  a1ab6a323f2 ! 2:  e5bb094b783 diff --no-index tests: test mode normalization\n    @@ t/t4053-diff-no-index.sh: test_expect_success 'diff --no-index allows external d\n     +test_expect_success POSIXPERM,SYMLINKS 'diff --no-index normalizes: mode not like git mode (symlink)' '\n     +\tln -s y z &&\n     +\tX_OID=$(git hash-object --stdin <x) &&\n    -+\tZ_OID=$(echo -n y | git hash-object --stdin) &&\n    ++\tZ_OID=$(printf \"y\" | git hash-object --stdin) &&\n     +\tcat >expected <<-EOF &&\n     +\tdiff --git a/x b/x\n     +\tdeleted file mode 100644\n-- \n2.31.0.366.g871543fb182\n\n"},{"id":"420045","messageId":"patch-1.3-7dd5c309a70-20210323T163917Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-0.3-00000000000-20210323T163917Z-avarab@gmail.com","subject":"[PATCH v3 1/2] diff --no-index tests: add test for --exit-code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-23T16:40:13Z","receivedAt":"2021-03-23T16:41:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for --exit-code working with --no-index. There's no reason\nto suppose it wouldn't, but we weren't testing for it anywhere in our\ntests. Let's fix that blind spot.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4053-diff-no-index.sh | 5 +++++\n 1 file changed, 5 insertions(+)\n\ndiff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh\nindex 0168946b639..44b932fbb20 100755\n--- a/t/t4053-diff-no-index.sh\n+++ b/t/t4053-diff-no-index.sh\n@@ -16,6 +16,11 @@ test_expect_success 'setup' '\n \techo 1 >non/git/b\n '\n \n+test_expect_success 'git diff --no-index --exit-code' '\n+\tgit diff --no-index --exit-code a/1 non/git/a &&\n+\ttest_expect_code 1 git diff --no-index --exit-code a/1 a/2\n+'\n+\n test_expect_success 'git diff --no-index directories' '\n \ttest_expect_code 1 git diff --no-index a b >cnt &&\n \ttest_line_count = 14 cnt\n-- \n2.31.0.366.g871543fb182\n\n"},{"id":"420046","messageId":"xmqqim5h4y2a.fsf@gitster.g","threadId":"18927","inReplyTo":"cover-0.3-00000000000-20210323T163917Z-avarab@gmail.com","subject":"Re: [PATCH v3 0/2] diff --no-index: fix test blind spots","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-23T16:47:09Z","receivedAt":"2021-03-23T16:48:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Since v2[1]: s/echo -n/printf/ (and I re-adjusted my test setup to run\n> the linting..)\n>\n> 1. https://lore.kernel.org/git/cover.1616366036.git.avarab@gmail.com/\n>\n> Ævar Arnfjörð Bjarmason (2):\n>   diff --no-index tests: add test for --exit-code\n>   diff --no-index tests: test mode normalization\n>\n>  t/t4053-diff-no-index.sh | 60 ++++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 60 insertions(+)\n>\n> Range-diff:\n> 1:  2dbc6c253e2 = 1:  7dd5c309a70 diff --no-index tests: add test for --exit-code\n> 2:  a1ab6a323f2 ! 2:  e5bb094b783 diff --no-index tests: test mode normalization\n>     @@ t/t4053-diff-no-index.sh: test_expect_success 'diff --no-index allows external d\n>      +test_expect_success POSIXPERM,SYMLINKS 'diff --no-index normalizes: mode not like git mode (symlink)' '\n>      +\tln -s y z &&\n>      +\tX_OID=$(git hash-object --stdin <x) &&\n>     -+\tZ_OID=$(echo -n y | git hash-object --stdin) &&\n>     ++\tZ_OID=$(printf \"y\" | git hash-object --stdin) &&\n>      +\tcat >expected <<-EOF &&\n>      +\tdiff --git a/x b/x\n>      +\tdeleted file mode 100644\n\nThanks for a quick turnaround, and sorry for forgetting to say\n\"locally munged, no need to resend\".\n"},{"id":"420726","messageId":"patch-01.19-a74e02ff0ba-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 01/18] cache.h: add a comment to object_type()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:29Z","receivedAt":"2021-03-31T19:10:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a comment to the object_type() function to explain what it\nreturns, and what the \"mode\" is in the \"else\" case.\n\nThe object_type() function dates back to 4d1012c3709 (Fix rev-list\nwhen showing objects involving submodules, 2007-11-11). It's not\nimmediately obvious to someone looking at its history and how it's\ncome to be used.\n\nDespite what Linus noted in 4d1012c3709 (Fix rev-list when showing\nobjects involving submodules, 2007-11-11) about wanting to move away\nfrom users of object_type() relying on S_ISLNK(mode) being true here\nwe do currently rely on that. If this is changed to a condition to\nonly return OBJ_BLOB on S_ISREG(mode) then t4008, t4023 and t7415 will\nhave failing tests.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n cache.h | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/cache.h b/cache.h\nindex 57f2285bba9..41e99bd9c63 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -451,11 +451,16 @@ enum object_type {\n \tOBJ_MAX\n };\n \n+/*\n+ * object_type() returns an object of a type that'll appear in a tree,\n+ * so no OBJ_TAG is possible. This is mostly (and dates back to)\n+ * consumers of the tree-walk.h API's \"mode\" field.\n+ */\n static inline enum object_type object_type(unsigned int mode)\n {\n \treturn S_ISDIR(mode) ? OBJ_TREE :\n \t\tS_ISGITLINK(mode) ? OBJ_COMMIT :\n-\t\tOBJ_BLOB;\n+\t\tOBJ_BLOB; /* S_ISREG(mode) || S_ISLNK(mode) */\n }\n \n /* Double-check local_repo_env below if you add to this list. */\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420727","messageId":"patch-02.19-57092359bbb-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 02/18] merge-ort: correct reference to test in 62fdec17a11","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:30Z","receivedAt":"2021-03-31T19:10:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a comment added in 62fdec17a11 (merge-ort: flesh out\nimplementation of handle_content_merge(), 2021-01-01).\n\nThe test being referred to here was moved from t6036 in\n919df319555 (Collect merge-related tests to t64xx, 2020-08-10).\n\nIt has also had the plural of \"mode\" in the name ever since being\nintroduced in 5d1daf30cce (t6036: add a failed conflict detection\ncase: regular files, different modes, 2018-06-30).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n merge-ort.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/merge-ort.c b/merge-ort.c\nindex ba35600d4e0..dd1f38e3c32 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -1073,7 +1073,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t\t/*\n \t\t * FIXME: If opt->priv->call_depth && !clean, then we really\n \t\t * should not make result->mode match either a->mode or\n-\t\t * b->mode; that causes t6036 \"check conflicting mode for\n+\t\t * b->mode; that causes t6416 \"check conflicting modes for\n \t\t * regular file\" to fail.  It would be best to use some other\n \t\t * mode, but we'll confuse all kinds of stuff if we use one\n \t\t * where S_ISREG(result->mode) isn't true, and if we use\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420728","messageId":"patch-05.19-b3289f7bbe5-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 05/18] mktree tests: test that \"mode\" is passed when sorting","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:33Z","receivedAt":"2021-03-31T19:10:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for the mode being passed to ent_compare(). That code dates\nback to 83f50539a9 (git-mktree: reverse of git-ls-tree., 2006-02-20)\nand there's never been a test for that particular edge case. Now we\nhave one.\n\nI don't see how anything could run into this in practice. In order for\nthat mode sorting to matter as a tiebreaker we need to have a\nduplicate entry in the tree, i.e. two \"foo\" entries, one a blob and\none a tree. This asserts that if that happens we'll sort on the modes\nwe encounter in such an invalid entry, i.e. we expect the tree entry\nbefore the blob.\n\nAs shown here we'd need to disable the fsck.duplicateEntries error to\nget to the point of running \"mktree\", so I doubt anyone's pushing this\nsort of data around.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t1450-fsck.sh | 44 ++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 44 insertions(+)\n\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 5071ac63a5b..46125190b45 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -318,6 +318,50 @@ test_expect_success 'tree entry with type mismatch' '\n \ttest_i18ngrep ! \"dangling blob\" out\n '\n \n+test_expect_success 'tree entry with duplicate type mismatching objects' '\n+\ttest_create_repo duplicate-entry &&\n+\t(\n+\t\tcd duplicate-entry &&\n+\t\tblob=\"$(printf \"foo\" | git hash-object -w --stdin)\" &&\n+\t\ttree=\"$(printf \"100644 blob $blob\\tfoo\" | git mktree)\" &&\n+\t\tcommit=\"$(git commit-tree $tree -m \"first commit\")\" &&\n+\t\tgit cat-file commit $commit >good-commit &&\n+\n+\t\t# First bad commit, wrong type, but in the right order\n+\t\tprintf \"40000 A\\0$(echo $tree | hex2oct)\" >broken-tree-A &&\n+\t\tprintf \"100644 A\\0$(echo $blob | hex2oct)\" >broken-tree-B &&\n+\t\tcat broken-tree-A broken-tree-B >broken-tree.1 &&\n+\t\tbroken_tree1=\"$(git hash-object -w --literally -t tree broken-tree.1)\" &&\n+\t\tbad_commit1=\"$(git commit-tree $broken_tree1 -m \"bad commit 1\")\" &&\n+\t\tgit cat-file commit $bad_commit1 >bad-commit.1 &&\n+\t\tgit update-ref refs/heads/broken-commit-1 $bad_commit1 &&\n+\n+\t\ttest_must_fail git fsck &&\n+\t\tgit -c fsck.duplicateEntries=warn fsck 2>err &&\n+\t\tgrep \" in tree .*$broken_tree1: duplicateEntries\" err &&\n+\n+\t\t# Second bad commits, wrong types and order\n+\t\tcat broken-tree-B broken-tree-A >broken-tree.2 &&\n+\t\tbroken_tree2=\"$(git hash-object -w --literally -t tree broken-tree.2)\" &&\n+\t\tbad_commit2=\"$(git commit-tree $broken_tree2 -m \"bad commit 2\")\" &&\n+\t\tgit cat-file commit $bad_commit2 >bad-commit.2 &&\n+\t\tgit update-ref refs/heads/broken-commit-2 $bad_commit2 &&\n+\n+\t\ttest_must_fail git fsck &&\n+\t\tgit -c fsck.duplicateEntries=warn fsck 2>err &&\n+\t\tgrep \" in tree .*$broken_tree2: duplicateEntries\" err &&\n+\n+\t\t# git mktree should \"fix\" the order of this already broken data\n+\t\tgit ls-tree broken-commit-1 >broken-tree-1-ls &&\n+\t\tgit ls-tree broken-commit-2 >broken-tree-2-ls &&\n+\t\t! test_cmp broken-tree-1-ls broken-tree-2-ls &&\n+\n+\t\tgit mktree <broken-tree-1-ls >broken-mktree-1 &&\n+\t\tgit mktree <broken-tree-2-ls >broken-mktree-2 &&\n+\t\ttest_cmp broken-mktree-1 broken-mktree-2\n+\t)\n+'\n+\n test_expect_success 'tag pointing to nonexistent' '\n \tbadoid=$(test_oid deadbeef) &&\n \tcat >invalid-tag <<-EOF &&\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420729","messageId":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"87o8fcqrg8.fsf@evledraar.gmail.com","subject":"[PATCH v5 00/18] tree-walk.h: slimmed down","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:28Z","receivedAt":"2021-03-31T19:10:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"These are preparatory changes to the tree-walk.h API to eventually fix\nlong-broken git-fsck mode checks.\n\nPer http://lore.kernel.org/git/xmqqtup5cnlu.fsf@gitster.g I think the\nprevious round of this was too big.\n\nSo here's a slimmed-down version which stops short of migrating\nanything to \"enum object_type\". There's just:\n\n * Tests\n * Renaming relevant API *() to *_mode() (the next series will propose a *_type())\n * Adding *_path() API for those users that don't need the \"mode\" the\n   *_mode() function provides\n * Comment updates, fixes to comments.\n * Whitespace changes that would be needed by the next series, but\n   which are a good idea anyway while we're at it, e.g. aligning\n   function declarations.\n\nÆvar Arnfjörð Bjarmason (18):\n  cache.h: add a comment to object_type()\n  merge-ort: correct reference to test in 62fdec17a11\n  cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n  fast-import tests: test for sorting dir/file foo v.s. foo.txt\n  mktree tests: test that \"mode\" is passed when sorting\n  diff tests: test that \"mode\" is passed when sorting\n  merge-tree tests: test for the mode comparison in same_entry()\n  blame: emit a better error on 'git blame directory'\n  notes & match-trees: use name_entry's \"pathlen\" member\n  tree-walk.h users: use temporary variable(s) for \"mode\"\n  match-trees: use \"tmp\" for mode in shift_tree_by()\n  tree.h: format argument lists of read_tree() users\n  tree-walk.h API: formatting changes for subsequent commit\n  tree-walk.h API doc: improve documentation of get_tree_entry()\n  tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n  tree-walk.h API: add a get_tree_entry_path() function\n  tree-walk.h API: document and format tree_entry_extract()\n  tree-entry.h API: rename tree_entry_extract() to\n    tree_entry_extract_mode()\n\n archive.c                       | 21 ++++----\n blame.c                         |  6 +--\n builtin/checkout.c              |  4 +-\n builtin/fast-import.c           | 12 +++--\n builtin/log.c                   |  5 +-\n builtin/ls-tree.c               |  4 +-\n builtin/merge-tree.c            | 12 +++--\n builtin/mktree.c                |  4 +-\n builtin/rm.c                    |  2 +-\n builtin/update-index.c          |  2 +-\n cache.h                         | 11 +++--\n combine-diff.c                  |  8 +--\n fsck.c                          |  2 +-\n line-log.c                      |  2 +-\n match-trees.c                   | 44 ++++++++---------\n merge-ort.c                     | 11 +++--\n merge-recursive.c               | 29 ++++++-----\n notes.c                         | 10 ++--\n object-name.c                   |  7 ++-\n read-cache.c                    | 16 +++---\n t/t1450-fsck.sh                 | 66 +++++++++++++++++++++++++\n t/t4300-merge-tree.sh           | 44 +++++++++++++++++\n t/t8004-blame-with-conflicts.sh | 21 ++++++++\n t/t9300-fast-import.sh          | 87 +++++++++++++++++++++++++++++++++\n tree-diff.c                     | 24 +++++----\n tree-walk.c                     | 30 ++++++++----\n tree-walk.h                     | 35 +++++++++----\n tree.h                          |  5 +-\n unpack-trees.c                  | 18 ++++---\n 29 files changed, 410 insertions(+), 132 deletions(-)\n\nRange-diff:\n 1:  c307eb53331 <  -:  ----------- show tests: add test for \"git show <tree>\"\n 2:  f37d7705cf0 <  -:  ----------- ls-files tests: add meaningful --with-tree tests\n 3:  6291d8a1b5e <  -:  ----------- tree.c API: move read_tree() into builtin/ls-files.c\n 4:  466b518e915 <  -:  ----------- ls-files: don't needlessly pass around stage variable\n 5:  30c3abfe9b9 <  -:  ----------- ls-files: refactor away read_tree()\n 6:  02c42be9249 <  -:  ----------- archive: stop passing \"stage\" through read_tree_recursive()\n 7:  d55e8d4042b <  -:  ----------- tree.h API: expose read_tree_1() as read_tree_at()\n 8:  fa157d8baee <  -:  ----------- tree.h API: simplify read_tree_recursive() signature\n10:  186f7a7a44b =  1:  a74e02ff0ba cache.h: add a comment to object_type()\n24:  092472f3c8d =  2:  57092359bbb merge-ort: correct reference to test in 62fdec17a11\n16:  1b6a10f814c !  3:  44c3fdd0085 cache.h: have base_name_compare() take \"is tree?\", not \"mode\"\n    @@ Commit message\n         function to take a boolean argument indicating whether the entry is a\n         tree or not, instead of having them call S_ISDIR(mode) on their own.\n     \n    -    This makes use of the new \"object_type\" field in the \"name_entry\".\n    +    This introduces no functional change, but makes later (not part of\n    +    this series) changes to abstract away \"object_type\" from \"mode\" more\n    +    readable.\n     \n         The API being modified here was originally added way back in\n         958ba6c96eb (Introduce \"base_name_compare()\" helper function,\n    @@ match-trees.c: static void *fill_tree_desc_strict(struct tree_desc *desc,\n      {\n     -\treturn base_name_compare(a->path, tree_entry_len(a), a->mode,\n     -\t\t\t\t b->path, tree_entry_len(b), b->mode);\n    -+\tint istree_a = (a->object_type == OBJ_TREE);\n    -+\tint istree_b = (b->object_type == OBJ_TREE);\n    ++\tint istree_a = S_ISDIR(a->mode);\n    ++\tint istree_b = S_ISDIR(b->mode);\n     +\treturn base_name_compare(a->path, tree_entry_len(a), istree_a,\n     +\t\t\t\t b->path, tree_entry_len(b), istree_b);\n      }\n    @@ tree-diff.c: static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_des\n      \t\treturn -1;\n      \n      \te1 = &t1->entry;\n    -+\tistree_e1 = (e1->object_type == OBJ_TREE);\n    ++\tistree_e1 = S_ISDIR(e1->mode);\n      \te2 = &t2->entry;\n     -\tcmp = base_name_compare(e1->path, tree_entry_len(e1), e1->mode,\n     -\t\t\t\te2->path, tree_entry_len(e2), e2->mode);\n    -+\tistree_e2 = (e2->object_type == OBJ_TREE);\n    ++\tistree_e2 = S_ISDIR(e2->mode);\n     +\tcmp = base_name_compare(e1->path, tree_entry_len(e1), istree_e1,\n     +\t\t\t\te2->path, tree_entry_len(e2), istree_e2);\n      \treturn cmp;\n    @@ unpack-trees.c: static int do_compare_entry(const struct cache_entry *ce,\n      static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)\n      {\n     -\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);\n    -+\tint istree = (n->object_type == OBJ_TREE);\n    ++\tint istree = S_ISDIR(n->mode);\n     +\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, istree);\n      \tif (cmp)\n      \t\treturn cmp;\n13:  7016008554a =  4:  2ff35f410cd fast-import tests: test for sorting dir/file foo v.s. foo.txt\n14:  b0546197b1b =  5:  b3289f7bbe5 mktree tests: test that \"mode\" is passed when sorting\n15:  73e92ac187d =  6:  0be013e47c4 diff tests: test that \"mode\" is passed when sorting\n23:  592fecb7abc !  7:  3367850559b merge-tree tests: test for the mode comparison in same_entry()\n    @@ Commit message\n         but as seen here it's important that we don't think a path with the\n         same content but different modes is the same_entry().\n     \n    -    The rest of this series will touch code that's relevant to this, but\n    -    won't change its behavior. This test is just something I came up with\n    -    in testing whether the mode test in same_entry() was needed at all.\n    -\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## t/t4300-merge-tree.sh ##\n36:  81da5490221 !  8:  3c2ca98716d blame: emit a better error on 'git blame directory'\n    @@ Commit message\n         something uniquely bad when in a conflicted merge. See\n         cd8ae20195 (git-blame shouldn't crash if run in an unmerged tree,\n         2007-10-18) and 9aeaab6811 (blame: allow \"blame file\" in the middle of\n    -    a conflicted merge, 2012-09-11) for the bug the t8004 test was\n    -    originally meant to address.\n    +    a conflicted merge, 2012-09-11) for the bug the test was originally\n    +    meant to address.\n     \n         But when extending it let's grep out the specific error message for\n         good measure. Having to change it in the future (e.g. as part of my\n    @@ Commit message\n     \n      ## blame.c ##\n     @@ blame.c: static void verify_working_tree_path(struct repository *r,\n    + \t\tstruct object_id blob_oid;\n    + \t\tunsigned short mode;\n      \n    - \tfor (parents = work_tree->parents; parents; parents = parents->next) {\n    - \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n    --\t\tstruct object_id blob_oid;\n    --\t\tunsigned short mode;\n    --\t\tint ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n    --\t\t\t\t\t      &mode);\n    --\n    --\t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n    -+\t\tstruct object_id oid;\n    -+\t\tif (!get_tree_entry_path(r, commit_oid, path, &oid))\n    +-\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode) &&\n    +-\t\t    oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n    ++\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode))\n      \t\t\treturn;\n      \t}\n      \n 9:  0390b1e9ded =  9:  749be26bf8e notes & match-trees: use name_entry's \"pathlen\" member\n11:  4fed508f3cb <  -:  ----------- tree-walk.h: add object_type member to name_entry\n12:  c557b67231b <  -:  ----------- tree-walk.c: migrate to using new \"object_type\" field when possible\n17:  f8912a409b5 <  -:  ----------- tree-walk.h users: switch object_type(...) to new .object_type\n26:  4babecb7855 ! 10:  5c0a49c188f tree-walk.h users: use temporary variable(s) for \"mode\"\n    @@ Commit message\n         In preparation for an eventual rename of the \"mode\" field, add\n         temporary variable(s) in those places where it's used more than once.\n     \n    -    This will make a subsequent commits easier to read., since we're only\n    +    This will make those later commits easier to read, since we're only\n         going to need to modify the line on which the assignment happens.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n    @@ builtin/merge-tree.c: static struct merge_list *link_entry(unsigned stage, const\n      \tstruct merge_list *link;\n     +\tunsigned int link_mode;\n      \n    - \tif (n->object_type == OBJ_NONE)\n    + \tif (!n->mode)\n      \t\treturn entry;\n     @@ builtin/merge-tree.c: static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n      \t\tpath = entry->path;\n29:  40f37e99cd9 ! 11:  e190ed39754 match-trees: use \"tmp\" for mode in shift_tree_by()\n    @@ match-trees.c: void shift_tree_by(struct repository *r,\n      \tunsigned candidate = 0;\n      \n      \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n    --\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &mode1) &&\n    +-\tif (!get_tree_entry(r, hash1, shift_prefix, &sub1, &mode1) &&\n     -\t    S_ISDIR(mode1))\n    -+\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n    ++\tif (!get_tree_entry(r, hash1, shift_prefix, &sub1, &tmp) &&\n     +\t    S_ISDIR(tmp))\n      \t\tcandidate |= 1;\n      \n      \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n    --\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &mode2) &&\n    +-\tif (!get_tree_entry(r, hash2, shift_prefix, &sub2, &mode2) &&\n     -\t    S_ISDIR(mode2))\n    -+\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n    ++\tif (!get_tree_entry(r, hash2, shift_prefix, &sub2, &tmp) &&\n     +\t    S_ISDIR(tmp))\n      \t\tcandidate |= 2;\n      \n18:  df43f1a76ac ! 12:  960e15bc7d5 tree.h: format argument lists of read_tree_recursive() users\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    tree.h: format argument lists of read_tree_recursive() users\n    +    tree.h: format argument lists of read_tree() users\n     \n    -    In preparation for adding a new argument to read_tree_fn_t re-indent\n    -    and format the argument list of read_tree_recursive() callbacks, and\n    +    Re-indent and format the argument list of read_tree() callbacks, and\n         the relevant functions they call.\n     \n    -    This is a whitespace-only change to make reading subsequent commits\n    -    easier. I'll be adding a new argument on the \"mode\" line, so leave\n    -    space on it.\n    +    This is a whitespace-only change to make changes in a later series\n    +    easier to read. In that series I'll be adding a new argument on the\n    +    \"mode\" line, so leave space on it for a new argument.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n19:  152c6d88542 <  -:  ----------- tree.h API: make read_tree_fn_t take an \"enum object_type\"\n20:  1c2f65cb674 <  -:  ----------- tree-walk.h users: migrate \"p->mode &&\" pattern\n21:  163922d427c <  -:  ----------- tree-walk.h users: refactor chained \"mode\" if/else into switch\n22:  21df7c668be <  -:  ----------- tree-walk.h users: migrate miscellaneous \"mode\" to \"object_type\"\n25:  685da1abbdc <  -:  ----------- fsck.c: switch on \"object_type\" in fsck_walk_tree()\n27:  7251654dd52 ! 13:  9c96d472472 tree-walk.h API: formatting changes for subsequent commit\n    @@ Commit message\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    - ## blame.c ##\n    -@@ blame.c: static void verify_working_tree_path(struct repository *r,\n    - \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n    - \t\tstruct object_id blob_oid;\n    - \t\tunsigned short mode;\n    -+\t\tint ret = get_tree_entry(r, commit_oid, path, &blob_oid,\n    -+\t\t\t\t\t &mode);\n    - \n    --\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode) &&\n    --\t\t    oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n    -+\t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n    - \t\t\treturn;\n    - \t}\n    - \n    -\n      ## tree-walk.c ##\n     @@ tree-walk.c: static int find_tree_entry(struct repository *r, struct tree_desc *t,\n      \t\t\toidcpy(result, &oid);\n30:  7f699bf2d5c ! 14:  44f16d74426 tree-walk.h API: add get_tree_entry_type()\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    tree-walk.h API: add get_tree_entry_type()\n    +    tree-walk.h API doc: improve documentation of get_tree_entry()\n     \n    -    Add a get_tree_entry_type() helper function to compliment the existing\n    -    get_tree_entry(), and a static get_tree_entry_all() which it uses internally.\n    -\n    -    Move those users of get_tree_entry_type() who didn't care about the\n    -    mode specifically, but just want to know whether the tree entry is one\n    -    of OBJ_{BLOB,COMMIT,TREE} over to the new get_tree_entry_type().\n    -\n    -    The get_tree_entry_all() function itself will be made non-static in a\n    -    subsequent commit. I'm leaving its argument list indented accordingly\n    -    to reduce churn when I do so.\n    +    Change a mention of sha1 to OID and change the comment to a listing of\n    +    functions discussed below, right now there's only one function, but\n    +    subsequent commits will add more.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    - ## archive.c ##\n    -@@ archive.c: static void parse_treeish_arg(const char **argv,\n    - \n    - \tif (prefix) {\n    - \t\tstruct object_id tree_oid;\n    --\t\tunsigned short mode;\n    -+\t\tenum object_type object_type;\n    - \t\tint err;\n    - \n    --\t\terr = get_tree_entry_mode(ar_args->repo,\n    -+\t\terr = get_tree_entry_type(ar_args->repo,\n    - \t\t\t\t\t  &tree->object.oid,\n    - \t\t\t\t\t  prefix, &tree_oid,\n    --\t\t\t\t\t  &mode);\n    --\t\tif (err || !S_ISDIR(mode))\n    -+\t\t\t\t\t  &object_type);\n    -+\t\tif (err || object_type != OBJ_TREE)\n    - \t\t\tdie(_(\"current working directory is untracked\"));\n    - \n    - \t\ttree = parse_tree_indirect(&tree_oid);\n    -\n    - ## match-trees.c ##\n    -@@ match-trees.c: void shift_tree_by(struct repository *r,\n    - \t\t   const char *shift_prefix)\n    - {\n    - \tstruct object_id sub1, sub2;\n    --\tunsigned short tmp;\n    -+\tenum object_type tmp;\n    - \tunsigned candidate = 0;\n    - \n    - \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n    --\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n    --\t    S_ISDIR(tmp))\n    -+\tif (!get_tree_entry_type(r, hash1, shift_prefix, &sub1, &tmp) &&\n    -+\t    tmp == OBJ_TREE)\n    - \t\tcandidate |= 1;\n    - \n    - \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n    --\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n    --\t    S_ISDIR(tmp))\n    -+\tif (!get_tree_entry_type(r, hash2, shift_prefix, &sub2, &tmp) &&\n    -+\t    tmp == OBJ_TREE)\n    - \t\tcandidate |= 2;\n    - \n    - \tif (candidate == 3) {\n    -\n    - ## tree-walk.c ##\n    -@@ tree-walk.c: struct dir_state {\n    - \tstruct object_id oid;\n    - };\n    - \n    -+static int get_tree_entry_all(struct repository *r,\n    -+\t\t\t      const struct object_id *tree_oid,\n    -+\t\t\t      const char *name,\n    -+\t\t\t      struct object_id *oid,\n    -+\t\t\t      unsigned short *mode,\n    -+\t\t\t      enum object_type *object_type);\n    -+\n    - static int find_tree_entry(struct repository *r, struct tree_desc *t,\n    - \t\t\t   const char *name, struct object_id *result,\n    --\t\t\t   unsigned short *mode)\n    -+\t\t\t   unsigned short *mode,\n    -+\t\t\t   enum object_type *object_type)\n    - {\n    - \tint namelen = strlen(name);\n    - \twhile (t->size) {\n    -@@ tree-walk.c: static int find_tree_entry(struct repository *r, struct tree_desc *t,\n    - \t\t}\n    - \t\tif (name[entrylen] != '/')\n    - \t\t\tcontinue;\n    --\t\tif (!S_ISDIR(*mode))\n    -+\t\tif (*object_type != OBJ_TREE)\n    - \t\t\tbreak;\n    - \t\tif (++entrylen == namelen) {\n    - \t\t\toidcpy(result, &oid);\n    - \t\t\treturn 0;\n    - \t\t}\n    --\t\treturn get_tree_entry_mode(r, &oid, name + entrylen, result,\n    --\t\t\t\t\t   mode);\n    -+\t\treturn get_tree_entry_all(r, &oid, name + entrylen, result,\n    -+\t\t\t\t\t  mode, object_type);\n    - \t}\n    - \treturn -1;\n    - }\n    - \n    --int get_tree_entry_mode(struct repository *r,\n    --\t\t\tconst struct object_id *tree_oid,\n    --\t\t\tconst char *name,\n    --\t\t\tstruct object_id *oid,\n    --\t\t\tunsigned short *mode)\n    -+static int get_tree_entry_all(struct repository *r,\n    -+\t\t       const struct object_id *tree_oid,\n    -+\t\t       const char *name,\n    -+\t\t       struct object_id *oid,\n    -+\t\t       unsigned short *mode,\n    -+\t\t       enum object_type *object_type)\n    - {\n    - \tint retval;\n    - \tvoid *tree;\n    -@@ tree-walk.c: int get_tree_entry_mode(struct repository *r,\n    - \t\tstruct tree_desc t;\n    - \t\tinit_tree_desc(&t, tree, size);\n    - \t\tretval = find_tree_entry(r, &t, name, oid,\n    --\t\t\t\t\t mode);\n    -+\t\t\t\t\t mode, object_type);\n    - \t}\n    - \tfree(tree);\n    - \treturn retval;\n    - }\n    - \n    -+int get_tree_entry_mode(struct repository *r,\n    -+\t\t\tconst struct object_id *tree_oid,\n    -+\t\t\tconst char *name,\n    -+\t\t\tstruct object_id *oid,\n    -+\t\t\tunsigned short *mode)\n    -+{\n    -+\tenum object_type object_type;\n    -+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n    -+\t\t\t\t  mode, &object_type);\n    -+}\n    -+\n    -+int get_tree_entry_type(struct repository *r,\n    -+\t\t\tconst struct object_id *tree_oid,\n    -+\t\t\tconst char *name,\n    -+\t\t\tstruct object_id *oid,\n    -+\t\t\tenum object_type *object_type)\n    -+{\n    -+\tunsigned short mode;\n    -+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n    -+\t\t\t\t  &mode, object_type);\n    -+}\n    -+\n    - /*\n    -  * This is Linux's built-in max for the number of symlinks to follow.\n    -  * That limit, of course, does not affect git, but it's a reasonable\n    -@@ tree-walk.c: enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n    - \t\tint find_result;\n    - \t\tchar *first_slash;\n    - \t\tchar *remainder = NULL;\n    -+\t\tenum object_type object_type;\n    - \n    - \t\tif (!t.buffer) {\n    - \t\t\tvoid *tree;\n    -@@ tree-walk.c: enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n    - \t\t/* Look up the first (or only) path component in the tree. */\n    - \t\tfind_result = find_tree_entry(r, &t, namebuf.buf,\n    - \t\t\t\t\t      &current_tree_oid,\n    --\t\t\t\t\t      mode);\n    -+\t\t\t\t\t      mode, &object_type);\n    - \t\tif (find_result) {\n    - \t\t\tgoto done;\n    - \t\t}\n    -\n      ## tree-walk.h ##\n     @@ tree-walk.h: struct traverse_info {\n    -  * Find an entry in a tree given a pathname and the sha1 of a tree to\n    + };\n    + \n    + /**\n    +- * Find an entry in a tree given a pathname and the sha1 of a tree to\n    ++ * Find an entry in a tree given a pathname and the OID of a tree to\n       * search. Returns 0 if the entry is found and -1 otherwise.\n       *\n     - * The third and fourth parameters are set to the entry's sha1 and\n     - * mode respectively.\n    -+ * There are variants of this function depending on what fields in the\n    -+ * \"struct name_entry\" you'd like. You always need a pointer to an\n    -+ * appropriate variable to fill in (NULL won't do!):\n    ++ * You always need a pointer to an appropriate variable to fill in\n    ++ * (NULL won't do!). That variable is:\n     + *\n    -+ * get_tree_entry_mode(): unsigned int mode\n    -+ * get_tree_entry_type(): enum object_type\n    ++ * get_tree_entry(): unsigned short mode\n       */\n    - int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n    - \t\t\tstruct object_id *,\n    - \t\t\tunsigned short *);\n    -+int get_tree_entry_type(struct repository *, const struct object_id *, const char *,\n    -+\t\t\tstruct object_id *,\n    -+\t\t\tenum object_type *);\n    - \n    - /**\n    -  * Generate the full pathname of a tree entry based from the root of the\n    + int get_tree_entry(struct repository *, const struct object_id *, const char *,\n    + \t\t   struct object_id *,\n28:  9e521a3cbf2 ! 15:  adbada0ade8 tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()\n    @@ Commit message\n         change is only a search-replacement of the name and indentation of the\n         argument lists.\n     \n    -    A subsequent commits will add get_tree_entry_type() and\n    -    get_tree_entry_all() functions. Those changes will be much easier to\n    -    read if we do this rename first.\n    +    Subsequent commits will add another get_tree_entry_*() function. Those\n    +    changes will be much easier to read if we do this rename first.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ blame.c: static void verify_working_tree_path(struct repository *r,\n      \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n      \t\tstruct object_id blob_oid;\n      \t\tunsigned short mode;\n    --\t\tint ret = get_tree_entry(r, commit_oid, path, &blob_oid,\n    --\t\t\t\t\t &mode);\n    -+\t\tint ret = get_tree_entry_mode(r, commit_oid, path, &blob_oid,\n    -+\t\t\t\t\t      &mode);\n    - \n    - \t\tif (!ret && oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n    +-\n    +-\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode))\n    ++\t\tif (!get_tree_entry_mode(r, commit_oid, path, &blob_oid, &mode))\n      \t\t\treturn;\n    + \t}\n    + \n     @@ blame.c: static int fill_blob_sha1_and_mode(struct repository *r,\n      {\n      \tif (!is_null_oid(&origin->blob_oid))\n    @@ match-trees.c: void shift_tree_by(struct repository *r,\n      \tunsigned candidate = 0;\n      \n      \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n    --\tif (!get_tree_entry(r, hash1, shift_prefix, &sub1, &mode1) &&\n    -+\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &mode1) &&\n    - \t    S_ISDIR(mode1))\n    +-\tif (!get_tree_entry(r, hash1, shift_prefix, &sub1, &tmp) &&\n    ++\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n    + \t    S_ISDIR(tmp))\n      \t\tcandidate |= 1;\n      \n      \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n    --\tif (!get_tree_entry(r, hash2, shift_prefix, &sub2, &mode2) &&\n    -+\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &mode2) &&\n    - \t    S_ISDIR(mode2))\n    +-\tif (!get_tree_entry(r, hash2, shift_prefix, &sub2, &tmp) &&\n    ++\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n    + \t    S_ISDIR(tmp))\n      \t\tcandidate |= 2;\n      \n     \n    @@ tree-walk.c: static int find_tree_entry(struct repository *r, struct tree_desc *\n     \n      ## tree-walk.h ##\n     @@ tree-walk.h: struct traverse_info {\n    -  * The third and fourth parameters are set to the entry's sha1 and\n    -  * mode respectively.\n    +  * You always need a pointer to an appropriate variable to fill in\n    +  * (NULL won't do!). That variable is:\n    +  *\n    +- * get_tree_entry(): unsigned short mode\n    ++ * get_tree_entry_mode(): unsigned short mode\n       */\n     -int get_tree_entry(struct repository *, const struct object_id *, const char *,\n     -\t\t   struct object_id *,\n35:  a2a34fd3e2d ! 16:  3ba77fd3a47 tree-walk.h API: add a get_tree_entry_path() function\n    @@ Commit message\n         tree-walk.h API: add a get_tree_entry_path() function\n     \n         Add a get_tree_entry_path() variant in addition to\n    -    get_tree_entry_path_{mode,type,all}(). This is for those callers that\n    -    need neither the mode nor \"enum object_type\" parameters filled for\n    -    them.\n    +    get_tree_entry_path_mode(). This is for those callers that don't need\n    +    the \"mode\" filled for them.\n     \n    -    There are callers here which don't need the \"struct object_id\" filled;\n    -    forcing callers to pass one just requires they create a throwaway\n    -    variable.\n    +    There are callers here which don't need the \"struct object_id\" filled\n    +    either, but let's leave that for now. I'm focusing downstream code\n    +    that depends on \"mode\" (or \"enum object_type\").\n     \n    -    See the following commits for the introduction of such code that's\n    -    being modified here:\n    +    The code being modified here was introduced in:\n     \n          - shift_tree(): 68faf68938e (A new merge stragety[sic] 'subtree'.,\n             2007-02-15) for the shift_tree()\n    @@ Commit message\n          - diagnose_invalid_oid_path(): 009fee4774d (Detailed diagnosis when\n            parsing an object name fails., 2009-12-07)\n     \n    -    Those could potentially be refactored too, but I've got to stop at\n    -    some point, and right now I'm focusing downstream code that depends on\n    -    \"mode\" (or \"enum object_type\").\n    -\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## match-trees.c ##\n    @@ object-name.c: static void diagnose_invalid_oid_path(struct repository *r,\n      \t\t\t    fullname,\n     \n      ## tree-walk.c ##\n    -@@ tree-walk.c: int get_tree_entry_all(struct repository *r,\n    +@@ tree-walk.c: int get_tree_entry_mode(struct repository *r,\n      \treturn retval;\n      }\n      \n    @@ tree-walk.c: int get_tree_entry_all(struct repository *r,\n     +\t\t\tstruct object_id *oid)\n     +{\n     +\tunsigned short mode;\n    -+\tenum object_type object_type;\n    -+\treturn get_tree_entry_all(r, tree_oid, name, oid,\n    -+\t\t\t\t  &mode, &object_type);\n    ++\treturn get_tree_entry_mode(r, tree_oid, name, oid, &mode);\n     +}\n     +\n    - int get_tree_entry_mode(struct repository *r,\n    - \t\t\tconst struct object_id *tree_oid,\n    - \t\t\tconst char *name,\n    + /*\n    +  * This is Linux's built-in max for the number of symlinks to follow.\n    +  * That limit, of course, does not affect git, but it's a reasonable\n     \n      ## tree-walk.h ##\n     @@ tree-walk.h: struct traverse_info {\n    -  * \"struct name_entry\" you'd like. You always need a pointer to an\n    -  * appropriate variable to fill in (NULL won't do!):\n    +  * You always need a pointer to an appropriate variable to fill in\n    +  * (NULL won't do!). That variable is:\n       *\n     + * get_tree_entry_path(): <no extra argument, just get the common 'path'>\n    -  * get_tree_entry_mode(): unsigned int mode\n    -  * get_tree_entry_type(): enum object_type\n    -  * get_tree_entry_all(): unsigned int mode, enum object_type\n    +  * get_tree_entry_mode(): unsigned short mode\n       */\n     +int get_tree_entry_path(struct repository *, const struct object_id *, const char *,\n     +\t\t\tstruct object_id *);\n31:  7ab7576cb16 ! 17:  dc895822828 tree-walk.h API: document and format tree_entry_extract()\n    @@ Commit message\n         tree-walk.h API: document and format tree_entry_extract()\n     \n         Document and format the argument list of the tree_entry_extract()\n    -    function in preparation for adding a sister function.\n    +    function in preparation for eventually adding a sister function.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ tree-walk.h: struct tree_desc {\n     + * \"struct name_entry\" you'd like. You always need a pointer to an\n     + * appropriate variable to fill in (NULL won't do!):\n     + *\n    -+ * tree_entry_extract_mode(): const char *path, unsigned int mode\n    ++ * tree_entry_extract(): const char *path, unsigned int mode\n       */\n     -static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned short *modep)\n     +static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,\n32:  a1bd81d64aa ! 18:  e961a0e8b5b tree-entry.h API: rename tree_entry_extract() to tree_entry_extract_mode()\n    @@ tree-diff.c: static struct combine_diff_path *emit_path(struct combine_diff_path\n      \n      \t\tisdir = S_ISDIR(mode);\n     \n    + ## tree-walk.c ##\n    +@@ tree-walk.c: static int find_tree_entry(struct repository *r, struct tree_desc *t,\n    + \t\tstruct object_id oid;\n    + \t\tint entrylen, cmp;\n    + \n    +-\t\toidcpy(&oid, tree_entry_extract(t, &entry, mode));\n    ++\t\toidcpy(&oid, tree_entry_extract_mode(t, &entry, mode));\n    + \t\tentrylen = tree_entry_len(&t->entry);\n    + \t\tupdate_tree_entry(t);\n    + \t\tif (entrylen > namelen)\n    +\n      ## tree-walk.h ##\n     @@ tree-walk.h: struct tree_desc {\n    +  * \"struct name_entry\" you'd like. You always need a pointer to an\n    +  * appropriate variable to fill in (NULL won't do!):\n       *\n    -  * tree_entry_extract_mode(): const char *path, unsigned int mode\n    +- * tree_entry_extract(): const char *path, unsigned int mode\n    ++ * tree_entry_extract_mode(): const char *path, unsigned int mode\n       */\n     -static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,\n     -\t\t\t\t\t\t\t const char **pathp,\n33:  ce7c19ad39c <  -:  ----------- tree-walk.h API: add a tree_entry_extract_all() function\n34:  95eec961be8 <  -:  ----------- tree-walk.h API: add get_tree_entry_all()\n37:  4d51da4ea39 <  -:  ----------- tree-walk.h API: add a tree_entry_extract_type() function\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420730","messageId":"patch-04.19-2ff35f410cd-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 04/18] fast-import tests: test for sorting dir/file foo v.s. foo.txt","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:32Z","receivedAt":"2021-03-31T19:10:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a missing test for the sorting of \"foo\" v.s. \"foo.txt\" where \"foo\"\ncan be either a file or a directory. If it's a file then \"foo\" should\nsort before \"foo.txt\", and the other way around if \"foo\" is a\ndirectory.\n\nSee [1] for a reply to a patch of mine introducing such a\nregression. We now finally have a test for this code added back in\n463acbe1c6 (Added tree and commit writing to fast-import.,\n2006-08-14).\n\nThis tests both the tecmp1() and tecmp0() functions introduced inn\n4cabf8583f (Implemented tree delta compression in fast-import.,\n2006-08-28).\n\nThis will catch cases where the \"mode\" is the same, or reversed\nbetween a & b in both functions.\n\nThere was an existing test for the tecmp1() function(s) just above\nthis one added here.\n\nThat existing test was added in e741130386 (New fast-import test case\nfor valid tree sorting, 2007-03-12) and would catch cases where\nentries were reversed, but not if their mode (or rather, type) was the\nthe same or otherwise wrong value.\n\nThere were no tests at all for the tecmp0() function. As with the\ntecmp1() test the new test will catch cases where the \"mode\" is the\nsame (e.g. \"1\"), or if the two are reversed. It won't catch a \"return\n0\" from the function (i.e. already sorted), that case requires\ntecmp1() to also be broken.\n\n1. https://lore.kernel.org/git/CABPp-BEZfG_pNNTWsnxEa1xG+kgpQfpXj=KHBkCBAMikrXj-aA@mail.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t9300-fast-import.sh | 87 ++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 87 insertions(+)\n\ndiff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh\nindex 5c47ac4465c..8bafb8cc515 100755\n--- a/t/t9300-fast-import.sh\n+++ b/t/t9300-fast-import.sh\n@@ -955,6 +955,93 @@ test_expect_success 'L: verify internal tree sorting' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'L: verify internal tree sorting on bad input (tecmp1)' '\n+\ttest_create_repo L1-0 &&\n+\n+\tcat >input <<-INPUT_END &&\n+\tblob\n+\tmark :1\n+\tdata 0\n+\n+\treset refs/heads/L1-0\n+\tcommit refs/heads/L1-0\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tcreate L1-0\n+\tCOMMIT\n+\tM 100644 :1 x.txt\n+\tM 100644 :1 x/y.txt\n+\tM 100644 :1 z.txt\n+\tM 100644 :1 z\n+\tINPUT_END\n+\n+\tcat >expected <<-EXPECT_END &&\n+\tx.txt\n+\tx\n+\tz\n+\tz.txt\n+\tEXPECT_END\n+\n+\tgit -C L1-0 fast-import <input &&\n+\tgit -C L1-0 ls-tree L1-0 >tmp &&\n+\tcut -f 2 <tmp >actual &&\n+\ttest_cmp expected actual &&\n+\tgit -C L1-0 fsck 2>err &&\n+\t# Would happen if tecmp1() were broken\n+\t! grep \"error in tree .*: treeNotSorted: \" err\n+'\n+\n+test_expect_success 'L: verify internal tree sorting on bad input (tecmp0)' '\n+\n+\ttest_create_repo L1-1 &&\n+\n+\tcat >input <<-INPUT_END &&\n+\tblob\n+\tmark :1\n+\tdata <<EOF\n+\tsome data\n+\tEOF\n+\n+\treset refs/heads/L1-1\n+\tcommit refs/heads/L1-1\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tcreate L1-1\n+\tCOMMIT\n+\tM 100644 :1 x.txt\n+\tM 100644 :1 x/y.txt\n+\tM 100644 :1 z.txt\n+\tM 100644 :1 z\n+\n+\tcommit refs/heads/L1-1\n+\tcommitter $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE\n+\tdata <<COMMIT\n+\tupdate L1-1\n+\tCOMMIT\n+\tM 100644 :1 another.txt\n+\tM 100644 :1 x.txt\n+\tM 100644 :1 x/y.txt\n+\tM 100644 :1 z.txt\n+\tM 100644 :1 z\n+\tINPUT_END\n+\n+\tcat >expected <<-EXPECT_END &&\n+\tanother.txt\n+\tx.txt\n+\tx\n+\tz\n+\tz.txt\n+\tEXPECT_END\n+\n+\tgit -C L1-1 fast-import <input &&\n+\tgit -C L1-1 ls-tree L1-1 >tmp 2>err &&\n+\t# Would happen if tecmp0() passed a fixed mode\n+\t! grep \"fatal: not a tree object\" err &&\n+\tcut -f 2 <tmp >actual &&\n+\ttest_cmp expected actual &&\n+\tgit -C L1-1 fsck\n+'\n+\n test_expect_success 'L: nested tree copy does not corrupt deltas' '\n \tcat >input <<-INPUT_END &&\n \tblob\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420735","messageId":"patch-03.19-44c3fdd0085-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 03/18] cache.h: have base_name_compare() take \"is tree?\", not \"mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:31Z","receivedAt":"2021-03-31T19:10:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the base_name_compare() API and the related df_name_compare()\nfunction to take a boolean argument indicating whether the entry is a\ntree or not, instead of having them call S_ISDIR(mode) on their own.\n\nThis introduces no functional change, but makes later (not part of\nthis series) changes to abstract away \"object_type\" from \"mode\" more\nreadable.\n\nThe API being modified here was originally added way back in\n958ba6c96eb (Introduce \"base_name_compare()\" helper function,\n2005-05-20).\n\nNone of these comparison functions used to have tests, but with\npreceding commits some of them now do. I thought the remainder was\ntrivial enough to review without tests, and didn't want to spend more\ntime on them.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/fast-import.c | 12 ++++++++----\n builtin/mktree.c      |  4 ++--\n cache.h               |  4 ++--\n combine-diff.c        |  8 +++++---\n match-trees.c         |  6 ++++--\n merge-ort.c           |  4 ++--\n merge-recursive.c     |  6 +++---\n read-cache.c          | 16 ++++++++--------\n tree-diff.c           |  7 +++++--\n unpack-trees.c        | 15 ++++++++-------\n 10 files changed, 47 insertions(+), 35 deletions(-)\n\ndiff --git a/builtin/fast-import.c b/builtin/fast-import.c\nindex 3afa81cf9ac..2fb56d6e9b7 100644\n--- a/builtin/fast-import.c\n+++ b/builtin/fast-import.c\n@@ -1287,18 +1287,22 @@ static int tecmp0 (const void *_a, const void *_b)\n {\n \tstruct tree_entry *a = *((struct tree_entry**)_a);\n \tstruct tree_entry *b = *((struct tree_entry**)_b);\n+\tint istree_a = S_ISDIR(a->versions[0].mode);\n+\tint istree_b = S_ISDIR(b->versions[0].mode);\n \treturn base_name_compare(\n-\t\ta->name->str_dat, a->name->str_len, a->versions[0].mode,\n-\t\tb->name->str_dat, b->name->str_len, b->versions[0].mode);\n+\t\ta->name->str_dat, a->name->str_len, istree_a,\n+\t\tb->name->str_dat, b->name->str_len, istree_b);\n }\n \n static int tecmp1 (const void *_a, const void *_b)\n {\n \tstruct tree_entry *a = *((struct tree_entry**)_a);\n \tstruct tree_entry *b = *((struct tree_entry**)_b);\n+\tint istree_a = S_ISDIR(a->versions[1].mode);\n+\tint istree_b = S_ISDIR(b->versions[1].mode);\n \treturn base_name_compare(\n-\t\ta->name->str_dat, a->name->str_len, a->versions[1].mode,\n-\t\tb->name->str_dat, b->name->str_len, b->versions[1].mode);\n+\t\ta->name->str_dat, a->name->str_len, istree_a,\n+\t\tb->name->str_dat, b->name->str_len, istree_b);\n }\n \n static void mktree(struct tree_content *t, int v, struct strbuf *b)\ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex 891991b00d6..2c1973229ac 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -37,8 +37,8 @@ static int ent_compare(const void *a_, const void *b_)\n {\n \tstruct treeent *a = *(struct treeent **)a_;\n \tstruct treeent *b = *(struct treeent **)b_;\n-\treturn base_name_compare(a->name, a->len, a->mode,\n-\t\t\t\t b->name, b->len, b->mode);\n+\treturn base_name_compare(a->name, a->len, S_ISDIR(a->mode),\n+\t\t\t\t b->name, b->len, S_ISDIR(b->mode));\n }\n \n static void write_tree(struct object_id *oid)\ndiff --git a/cache.h b/cache.h\nindex 41e99bd9c63..3bcea022ad2 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1506,8 +1506,8 @@ int repo_interpret_branch_name(struct repository *r,\n \n int validate_headref(const char *ref);\n \n-int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n-int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n+int base_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n+int df_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);\n int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);\n \ndiff --git a/combine-diff.c b/combine-diff.c\nindex 06635f91bc2..155d52971e6 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -16,11 +16,13 @@\n static int compare_paths(const struct combine_diff_path *one,\n \t\t\t  const struct diff_filespec *two)\n {\n-\tif (!S_ISDIR(one->mode) && !S_ISDIR(two->mode))\n+\tint istree_one = S_ISDIR(one->mode);\n+\tint istree_two = S_ISDIR(two->mode);\n+\tif (!istree_one && !istree_two)\n \t\treturn strcmp(one->path, two->path);\n \n-\treturn base_name_compare(one->path, strlen(one->path), one->mode,\n-\t\t\t\t two->path, strlen(two->path), two->mode);\n+\treturn base_name_compare(one->path, strlen(one->path), istree_one,\n+\t\t\t\t two->path, strlen(two->path), istree_two);\n }\n \n static int filename_changed(char status)\ndiff --git a/match-trees.c b/match-trees.c\nindex f6c194c1cca..48f3e0ed526 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -67,8 +67,10 @@ static void *fill_tree_desc_strict(struct tree_desc *desc,\n static int base_name_entries_compare(const struct name_entry *a,\n \t\t\t\t     const struct name_entry *b)\n {\n-\treturn base_name_compare(a->path, tree_entry_len(a), a->mode,\n-\t\t\t\t b->path, tree_entry_len(b), b->mode);\n+\tint istree_a = S_ISDIR(a->mode);\n+\tint istree_b = S_ISDIR(b->mode);\n+\treturn base_name_compare(a->path, tree_entry_len(a), istree_a,\n+\t\t\t\t b->path, tree_entry_len(b), istree_b);\n }\n \n /*\ndiff --git a/merge-ort.c b/merge-ort.c\nindex dd1f38e3c32..97b8670c0a5 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -2257,8 +2257,8 @@ static int tree_entry_order(const void *a_, const void *b_)\n \n \tconst struct merged_info *ami = a->util;\n \tconst struct merged_info *bmi = b->util;\n-\treturn base_name_compare(a->string, strlen(a->string), ami->result.mode,\n-\t\t\t\t b->string, strlen(b->string), bmi->result.mode);\n+\treturn base_name_compare(a->string, strlen(a->string), S_ISDIR(ami->result.mode),\n+\t\t\t\t b->string, strlen(b->string), S_ISDIR(bmi->result.mode));\n }\n \n static void write_tree(struct object_id *result_oid,\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex ed31f9496cb..97520a88646 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -554,12 +554,12 @@ static int string_list_df_name_compare(const char *one, const char *two)\n \t *\n \t * To achieve this, we sort with df_name_compare and provide\n \t * the mode S_IFDIR so that D/F conflicts will sort correctly.\n-\t * We use the mode S_IFDIR for everything else for simplicity,\n+\t * We say we have a directory for everything else for simplicity,\n \t * since in other cases any changes in their order due to\n \t * sorting cause no problems for us.\n \t */\n-\tint cmp = df_name_compare(one, onelen, S_IFDIR,\n-\t\t\t\t  two, twolen, S_IFDIR);\n+\tint cmp = df_name_compare(one, onelen, 1, two, twolen, 1);\n+\n \t/*\n \t * Now that 'foo' and 'foo/bar' compare equal, we have to make sure\n \t * that 'foo' comes before 'foo/bar'.\ndiff --git a/read-cache.c b/read-cache.c\nindex 5a907af2fb5..6e0b41ed175 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -462,8 +462,8 @@ int ie_modified(struct index_state *istate,\n \treturn 0;\n }\n \n-int base_name_compare(const char *name1, int len1, int mode1,\n-\t\t      const char *name2, int len2, int mode2)\n+int base_name_compare(const char *name1, int len1, int istree1,\n+\t\t      const char *name2, int len2, int istree2)\n {\n \tunsigned char c1, c2;\n \tint len = len1 < len2 ? len1 : len2;\n@@ -474,9 +474,9 @@ int base_name_compare(const char *name1, int len1, int mode1,\n \t\treturn cmp;\n \tc1 = name1[len];\n \tc2 = name2[len];\n-\tif (!c1 && S_ISDIR(mode1))\n+\tif (!c1 && istree1)\n \t\tc1 = '/';\n-\tif (!c2 && S_ISDIR(mode2))\n+\tif (!c2 && istree2)\n \t\tc2 = '/';\n \treturn (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;\n }\n@@ -491,8 +491,8 @@ int base_name_compare(const char *name1, int len1, int mode1,\n  * This is used by routines that want to traverse the git namespace\n  * but then handle conflicting entries together when possible.\n  */\n-int df_name_compare(const char *name1, int len1, int mode1,\n-\t\t    const char *name2, int len2, int mode2)\n+int df_name_compare(const char *name1, int len1, int istree1,\n+\t\t    const char *name2, int len2, int istree2)\n {\n \tint len = len1 < len2 ? len1 : len2, cmp;\n \tunsigned char c1, c2;\n@@ -504,10 +504,10 @@ int df_name_compare(const char *name1, int len1, int mode1,\n \tif (len1 == len2)\n \t\treturn 0;\n \tc1 = name1[len];\n-\tif (!c1 && S_ISDIR(mode1))\n+\tif (!c1 && istree1)\n \t\tc1 = '/';\n \tc2 = name2[len];\n-\tif (!c2 && S_ISDIR(mode2))\n+\tif (!c2 && istree2)\n \t\tc2 = '/';\n \tif (c1 == '/' && !c2)\n \t\treturn 0;\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 7cebbb327e2..644b4656421 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -50,6 +50,7 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)\n {\n \tstruct name_entry *e1, *e2;\n \tint cmp;\n+\tint istree_e1, istree_e2;\n \n \t/* empty descriptors sort after valid tree entries */\n \tif (!t1->size)\n@@ -58,9 +59,11 @@ static int tree_entry_pathcmp(struct tree_desc *t1, struct tree_desc *t2)\n \t\treturn -1;\n \n \te1 = &t1->entry;\n+\tistree_e1 = S_ISDIR(e1->mode);\n \te2 = &t2->entry;\n-\tcmp = base_name_compare(e1->path, tree_entry_len(e1), e1->mode,\n-\t\t\t\te2->path, tree_entry_len(e2), e2->mode);\n+\tistree_e2 = S_ISDIR(e2->mode);\n+\tcmp = base_name_compare(e1->path, tree_entry_len(e1), istree_e1,\n+\t\t\t\te2->path, tree_entry_len(e2), istree_e2);\n \treturn cmp;\n }\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 29029f34ed6..23c1640ae9a 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -925,7 +925,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \t\t\t\t      const struct traverse_info *info,\n \t\t\t\t      const char *name, size_t namelen,\n-\t\t\t\t      unsigned mode)\n+\t\t\t\t      unsigned istree)\n {\n \tint pathlen, ce_len;\n \tconst char *ce_name;\n@@ -933,7 +933,7 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \tif (info->prev) {\n \t\tint cmp = do_compare_entry_piecewise(ce, info->prev,\n \t\t\t\t\t\t     info->name, info->namelen,\n-\t\t\t\t\t\t     info->mode);\n+\t\t\t\t\t\t     S_ISDIR(info->mode));\n \t\tif (cmp)\n \t\t\treturn cmp;\n \t}\n@@ -947,13 +947,13 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce,\n \tce_len -= pathlen;\n \tce_name = ce->name + pathlen;\n \n-\treturn df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n+\treturn df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n }\n \n static int do_compare_entry(const struct cache_entry *ce,\n \t\t\t    const struct traverse_info *info,\n \t\t\t    const char *name, size_t namelen,\n-\t\t\t    unsigned mode)\n+\t\t\t    unsigned istree)\n {\n \tint pathlen, ce_len;\n \tconst char *ce_name;\n@@ -965,7 +965,7 @@ static int do_compare_entry(const struct cache_entry *ce,\n \t * it is quicker to use the precomputed version.\n \t */\n \tif (!info->traverse_path)\n-\t\treturn do_compare_entry_piecewise(ce, info, name, namelen, mode);\n+\t\treturn do_compare_entry_piecewise(ce, info, name, namelen, istree);\n \n \tcmp = strncmp(ce->name, info->traverse_path, info->pathlen);\n \tif (cmp)\n@@ -980,12 +980,13 @@ static int do_compare_entry(const struct cache_entry *ce,\n \tce_len -= pathlen;\n \tce_name = ce->name + pathlen;\n \n-\treturn df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);\n+\treturn df_name_compare(ce_name, ce_len, 0, name, namelen, istree);\n }\n \n static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)\n {\n-\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);\n+\tint istree = S_ISDIR(n->mode);\n+\tint cmp = do_compare_entry(ce, info, n->path, n->pathlen, istree);\n \tif (cmp)\n \t\treturn cmp;\n \n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420731","messageId":"patch-08.19-3c2ca98716d-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 08/18] blame: emit a better error on 'git blame directory'","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:36Z","receivedAt":"2021-03-31T19:10:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change an early check for non-blobs in verify_working_tree_path() to\nlet any such objects pass, and instead die shortly thereafter in the\nfake_working_tree_commit() caller's type check.\n\nNow e.g. doing \"git blame t\" in git.git emits:\n\n    fatal: unsupported file type t\n\nInstead of:\n\n    fatal: no such path 't' in HEAD\n\nThe main point of this test is to assert that we're not doing\nsomething uniquely bad when in a conflicted merge. See\ncd8ae20195 (git-blame shouldn't crash if run in an unmerged tree,\n2007-10-18) and 9aeaab6811 (blame: allow \"blame file\" in the middle of\na conflicted merge, 2012-09-11) for the bug the test was originally\nmeant to address.\n\nBut when extending it let's grep out the specific error message for\ngood measure. Having to change it in the future (e.g. as part of my\nparallel series to improve such 'OID does not match type' messages) is\na small price for ensuring it doesn't regress.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n blame.c                         |  3 +--\n t/t8004-blame-with-conflicts.sh | 21 +++++++++++++++++++++\n 2 files changed, 22 insertions(+), 2 deletions(-)\n\ndiff --git a/blame.c b/blame.c\nindex 5018bb8fb2c..d1fa3821a08 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -103,8 +103,7 @@ static void verify_working_tree_path(struct repository *r,\n \t\tstruct object_id blob_oid;\n \t\tunsigned short mode;\n \n-\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode) &&\n-\t\t    oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n+\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode))\n \t\t\treturn;\n \t}\n \ndiff --git a/t/t8004-blame-with-conflicts.sh b/t/t8004-blame-with-conflicts.sh\nindex 35414a53363..5e3dea35a50 100755\n--- a/t/t8004-blame-with-conflicts.sh\n+++ b/t/t8004-blame-with-conflicts.sh\n@@ -73,4 +73,25 @@ test_expect_success 'blame does not crash with conflicted file in stages 1,3' '\n \tgit blame file1\n '\n \n+test_expect_success 'setup second case' '\n+\tgit merge --abort\n+'\n+\n+test_expect_success 'blame on directory/file conflict' '\n+\tmkdir d &&\n+\ttest_commit second &&\n+\ttest_commit d/file &&\n+\ttest_must_fail git blame d 2>expected &&\n+\tgrep \"unsupported file type d\" expected &&\n+\n+\tgit reset --hard second &&\n+\t>d &&\n+\tgit add d &&\n+\tgit commit -m\"a not-a-dir\" &&\n+\ttest_must_fail git merge d/file &&\n+\n+\ttest_must_fail git blame d 2>actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420732","messageId":"patch-09.19-749be26bf8e-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 09/18] notes & match-trees: use name_entry's \"pathlen\" member","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:37Z","receivedAt":"2021-03-31T19:10:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change code that was doing a strlen() on the \"path\" from a name_entry\nstruct to instead use the pathlen given to us by decode_tree_entry().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c | 7 +++----\n notes.c       | 4 ++--\n 2 files changed, 5 insertions(+), 6 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex 48f3e0ed526..4ad0ca18256 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -199,9 +199,10 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \twhile (desc.size) {\n \t\tconst char *name;\n \t\tunsigned short mode;\n+\t\tint len = tree_entry_len(&desc.entry);\n \n \t\ttree_entry_extract(&desc, &name, &mode);\n-\t\tif (strlen(name) == toplen &&\n+\t\tif (len == toplen &&\n \t\t    !memcmp(name, prefix, toplen)) {\n \t\t\tif (!S_ISDIR(mode))\n \t\t\t\tdie(\"entry %s in tree %s is not a tree\", name,\n@@ -216,9 +217,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \t\t\t *   - to discard the \"const\"; this is OK because we\n \t\t\t *     know it points into our non-const \"buf\"\n \t\t\t */\n-\t\t\trewrite_here = (unsigned char *)(desc.entry.path +\n-\t\t\t\t\t\t\t strlen(desc.entry.path) +\n-\t\t\t\t\t\t\t 1);\n+\t\t\trewrite_here = (unsigned char *)(name + len + 1);\n \t\t\tbreak;\n \t\t}\n \t\tupdate_tree_entry(&desc);\ndiff --git a/notes.c b/notes.c\nindex a19e4ad7943..e2fec12a39e 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -413,7 +413,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \twhile (tree_entry(&desc, &entry)) {\n \t\tunsigned char type;\n \t\tstruct leaf_node *l;\n-\t\tsize_t path_len = strlen(entry.path);\n+\t\tint path_len = entry.pathlen;\n \n \t\tif (path_len == 2 * (hashsz - prefix_len)) {\n \t\t\t/* This is potentially the remainder of the SHA-1 */\n@@ -483,7 +483,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n \t\t\t\tstrbuf_addch(&non_note_path, '/');\n \t\t\t}\n-\t\t\tstrbuf_addstr(&non_note_path, entry.path);\n+\t\t\tstrbuf_add(&non_note_path, entry.path, path_len);\n \t\t\tadd_non_note(t, strbuf_detach(&non_note_path, NULL),\n \t\t\t\t     entry.mode, entry.oid.hash);\n \t\t}\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420733","messageId":"patch-10.19-5c0a49c188f-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 10/18] tree-walk.h users: use temporary variable(s) for \"mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:38Z","receivedAt":"2021-03-31T19:10:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for an eventual rename of the \"mode\" field, add\ntemporary variable(s) in those places where it's used more than once.\n\nThis will make those later commits easier to read, since we're only\ngoing to need to modify the line on which the assignment happens.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/merge-tree.c | 12 +++++++++---\n match-trees.c        | 13 +++++++------\n merge-ort.c          |  5 +++--\n notes.c              |  3 ++-\n tree-diff.c          | 13 ++++++++-----\n unpack-trees.c       |  3 ++-\n 6 files changed, 31 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/merge-tree.c b/builtin/merge-tree.c\nindex de8520778d2..c03353b5457 100644\n--- a/builtin/merge-tree.c\n+++ b/builtin/merge-tree.c\n@@ -190,14 +190,17 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s\n {\n \tstruct merge_list *orig, *final;\n \tconst char *path;\n+\tunsigned int orig_mode, final_mode;\n \n \t/* If it's already ours, don't bother showing it */\n \tif (!ours)\n \t\treturn;\n \n \tpath = traverse_path(info, result);\n-\torig = create_entry(2, ours->mode, &ours->oid, path);\n-\tfinal = create_entry(0, result->mode, &result->oid, path);\n+\torig_mode = ours->mode;\n+\torig = create_entry(2, orig_mode, &ours->oid, path);\n+\tfinal_mode = result->mode;\n+\tfinal = create_entry(0, final_mode, &result->oid, path);\n \n \tfinal->link = orig;\n \n@@ -241,6 +244,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n {\n \tconst char *path;\n \tstruct merge_list *link;\n+\tunsigned int link_mode;\n \n \tif (!n->mode)\n \t\treturn entry;\n@@ -248,7 +252,9 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info\n \t\tpath = entry->path;\n \telse\n \t\tpath = traverse_path(info, n);\n-\tlink = create_entry(stage, n->mode, &n->oid, path);\n+\tlink_mode = n->mode;\n+\tlink = create_entry(stage, link_mode, &n->oid, path);\n+\n \tlink->link = entry;\n \treturn link;\n }\ndiff --git a/match-trees.c b/match-trees.c\nindex 4ad0ca18256..a6796de442d 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -86,6 +86,8 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha\n \n \tfor (;;) {\n \t\tint cmp;\n+\t\tunsigned int one_mode = one.entry.mode;\n+\t\tunsigned int two_mode = two.entry.mode;\n \n \t\tif (one.size && two.size)\n \t\t\tcmp = base_name_entries_compare(&one.entry, &two.entry);\n@@ -100,22 +102,21 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha\n \n \t\tif (cmp < 0) {\n \t\t\t/* path1 does not appear in two */\n-\t\t\tscore += score_missing(one.entry.mode);\n+\t\t\tscore += score_missing(one_mode);\n \t\t\tupdate_tree_entry(&one);\n \t\t} else if (cmp > 0) {\n \t\t\t/* path2 does not appear in one */\n-\t\t\tscore += score_missing(two.entry.mode);\n+\t\t\tscore += score_missing(two_mode);\n \t\t\tupdate_tree_entry(&two);\n \t\t} else {\n+\n \t\t\t/* path appears in both */\n \t\t\tif (!oideq(&one.entry.oid, &two.entry.oid)) {\n \t\t\t\t/* they are different */\n-\t\t\t\tscore += score_differs(one.entry.mode,\n-\t\t\t\t\t\t       two.entry.mode);\n+\t\t\t\tscore += score_differs(one_mode, two_mode);\n \t\t\t} else {\n \t\t\t\t/* same subtree or blob */\n-\t\t\t\tscore += score_matches(one.entry.mode,\n-\t\t\t\t\t\t       two.entry.mode);\n+\t\t\t\tscore += score_matches(one_mode, two_mode);\n \t\t\t}\n \t\t\tupdate_tree_entry(&one);\n \t\t\tupdate_tree_entry(&two);\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 97b8670c0a5..de565a1f12e 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -538,11 +538,12 @@ static void add_pair(struct merge_options *opt,\n \tstruct diff_filespec *one, *two;\n \tstruct rename_info *renames = &opt->priv->renames;\n \tint names_idx = is_add ? side : 0;\n+\tconst struct object_id *oid = &names[names_idx].oid;\n+\tunsigned int mode = names[names_idx].mode;\n \n \tone = alloc_filespec(pathname);\n \ttwo = alloc_filespec(pathname);\n-\tfill_filespec(is_add ? two : one,\n-\t\t      &names[names_idx].oid, 1, names[names_idx].mode);\n+\tfill_filespec(is_add ? two : one, oid, 1, mode);\n \tdiff_queue(&renames->pairs[side], one, two);\n }\n \ndiff --git a/notes.c b/notes.c\nindex e2fec12a39e..00aa7d4115a 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -478,6 +478,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\tstruct strbuf non_note_path = STRBUF_INIT;\n \t\t\tconst char *q = oid_to_hex(&subtree->key_oid);\n \t\t\tsize_t i;\n+\t\t\tunsigned int mode = entry.mode;\n \t\t\tfor (i = 0; i < prefix_len; i++) {\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n@@ -485,7 +486,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t}\n \t\t\tstrbuf_add(&non_note_path, entry.path, path_len);\n \t\t\tadd_non_note(t, strbuf_detach(&non_note_path, NULL),\n-\t\t\t\t     entry.mode, entry.oid.hash);\n+\t\t\t\t     mode, entry.oid.hash);\n \t\t}\n \t}\n \tfree(buf);\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 644b4656421..b7a76cc2620 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -466,17 +466,19 @@ static struct combine_diff_path *ll_diff_tree_paths(\n \t\ttp[0].entry.mode &= ~S_IFXMIN_NEQ;\n \n \t\tfor (i = 1; i < nparent; ++i) {\n+\t\t\tunsigned int mode = tp[i].entry.mode;\n \t\t\tcmp = tree_entry_pathcmp(&tp[i], &tp[imin]);\n \t\t\tif (cmp < 0) {\n \t\t\t\timin = i;\n-\t\t\t\ttp[i].entry.mode &= ~S_IFXMIN_NEQ;\n+\t\t\t\tmode &= ~S_IFXMIN_NEQ;\n \t\t\t}\n \t\t\telse if (cmp == 0) {\n-\t\t\t\ttp[i].entry.mode &= ~S_IFXMIN_NEQ;\n+\t\t\t\tmode &= ~S_IFXMIN_NEQ;\n \t\t\t}\n \t\t\telse {\n-\t\t\t\ttp[i].entry.mode |= S_IFXMIN_NEQ;\n+\t\t\t\tmode |= S_IFXMIN_NEQ;\n \t\t\t}\n+\t\t\ttp[i].entry.mode = mode;\n \t\t}\n \n \t\t/* fixup markings for entries before imin */\n@@ -493,13 +495,14 @@ static struct combine_diff_path *ll_diff_tree_paths(\n \t\t\t/* are either pi > p[imin] or diff(t,pi) != ø ? */\n \t\t\tif (!opt->flags.find_copies_harder) {\n \t\t\t\tfor (i = 0; i < nparent; ++i) {\n+\t\t\t\t\tunsigned int mode = tp[i].entry.mode;\n \t\t\t\t\t/* p[i] > p[imin] */\n-\t\t\t\t\tif (tp[i].entry.mode & S_IFXMIN_NEQ)\n+\t\t\t\t\tif (mode & S_IFXMIN_NEQ)\n \t\t\t\t\t\tcontinue;\n \n \t\t\t\t\t/* diff(t,pi) != ø */\n \t\t\t\t\tif (!oideq(&t.entry.oid, &tp[i].entry.oid) ||\n-\t\t\t\t\t    (t.entry.mode != tp[i].entry.mode))\n+\t\t\t\t\t    (t.entry.mode != mode))\n \t\t\t\t\t\tcontinue;\n \n \t\t\t\t\tgoto skip_emit_t_tp;\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 23c1640ae9a..e804c7919b7 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1023,8 +1023,9 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,\n \t\tis_transient ?\n \t\tmake_empty_transient_cache_entry(len) :\n \t\tmake_empty_cache_entry(istate, len);\n+\tunsigned int mode = n->mode;\n \n-\tce->ce_mode = create_ce_mode(n->mode);\n+\tce->ce_mode = create_ce_mode(mode);\n \tce->ce_flags = create_ce_flags(stage);\n \tce->ce_namelen = len;\n \toidcpy(&ce->oid, &n->oid);\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420734","messageId":"patch-07.19-3367850559b-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 07/18] merge-tree tests: test for the mode comparison in same_entry()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:35Z","receivedAt":"2021-03-31T19:10:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test to stress the \"a->mode == b->mode\" comparison in\nmerge-tree.c's same_entry().\n\nThat code was initially added by Linus in 33deb63a36f (Add\n\"merge-tree\" helper program. Maybe it's retarded, maybe it's helpful.,\n2005-04-14), and then again in its current form in\n492e0759bfe (Handling large files with GIT, 2006-02-14).\n\nHowever, nothing was testing that we handled this case\ncorrectly. Simply removing the mode comparison left all tests passing,\nbut as seen here it's important that we don't think a path with the\nsame content but different modes is the same_entry().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t4300-merge-tree.sh | 44 +++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 44 insertions(+)\n\ndiff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh\nindex e59601e5fe9..f783d784d02 100755\n--- a/t/t4300-merge-tree.sh\n+++ b/t/t4300-merge-tree.sh\n@@ -40,6 +40,25 @@ test_expect_success 'file add A, B (same)' '\n \ttest_must_be_empty actual\n '\n \n+test_expect_success 'file add A, B (different mode)' '\n+\tgit reset --hard initial &&\n+\ttest_commit \"add-a-b-same-diff-mode-A\" \"ONE\" \"AAA\" &&\n+\tgit reset --hard initial &&\n+\techo AAA >ONE &&\n+\ttest_chmod +x ONE &&\n+\ttest_tick &&\n+\tgit commit -m\"add-a-b-same-diff-mode-B\" &&\n+\tgit tag \"add-a-b-same-diff-mode-B\" HEAD &&\n+\tgit merge-tree initial add-a-b-same-diff-mode-A add-a-b-same-diff-mode-B >actual &&\n+\tcat >expected <<EXPECTED &&\n+added in both\n+  our    100644 $(git rev-parse add-a-b-same-diff-mode-A:ONE) ONE\n+  their  100755 $(git rev-parse add-a-b-same-diff-mode-B:ONE) ONE\n+EXPECTED\n+\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'file add A, B (different)' '\n \tgit reset --hard initial &&\n \ttest_commit \"add-a-b-diff-A\" \"ONE\" \"AAA\" &&\n@@ -61,6 +80,31 @@ EXPECTED\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'file add A, B (different and different mode)' '\n+\tgit reset --hard initial &&\n+\ttest_commit \"add-a-b-diff-diff-mode-A\" \"ONE\" \"AAA\" &&\n+\tgit reset --hard initial &&\n+\techo BBB >ONE &&\n+\ttest_chmod +x ONE &&\n+\ttest_tick &&\n+\tgit commit -m\"add-a-b-diff-diff-mode-B\" &&\n+\tgit tag \"add-a-b-diff-diff-mode-B\" &&\n+\tgit merge-tree initial add-a-b-diff-diff-mode-A add-a-b-diff-diff-mode-B >actual &&\n+\tcat >expected <<EXPECTED &&\n+added in both\n+  our    100644 $(git rev-parse add-a-b-diff-diff-mode-A:ONE) ONE\n+  their  100755 $(git rev-parse add-a-b-diff-diff-mode-B:ONE) ONE\n+@@ -1 +1,5 @@\n++<<<<<<< .our\n+ AAA\n++=======\n++BBB\n++>>>>>>> .their\n+EXPECTED\n+\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'file change A, !B' '\n \tgit reset --hard initial &&\n \ttest_commit \"change-a-not-b\" \"initial-file\" \"BBB\" &&\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420736","messageId":"patch-16.19-3ba77fd3a47-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 16/18] tree-walk.h API: add a get_tree_entry_path() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:44Z","receivedAt":"2021-03-31T19:10:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a get_tree_entry_path() variant in addition to\nget_tree_entry_path_mode(). This is for those callers that don't need\nthe \"mode\" filled for them.\n\nThere are callers here which don't need the \"struct object_id\" filled\neither, but let's leave that for now. I'm focusing downstream code\nthat depends on \"mode\" (or \"enum object_type\").\n\nThe code being modified here was introduced in:\n\n - shift_tree(): 68faf68938e (A new merge stragety[sic] 'subtree'.,\n    2007-02-15) for the shift_tree()\n\n - tree_has_path(): 96e7ffbdc31 (merge-recursive: check for directory\n   level conflicts, 2018-04-19)\n\n - init_notes(): fd53c9eb445 (Speed up git notes lookup, 2009-10-09)\n\n - diagnose_invalid_oid_path(): 009fee4774d (Detailed diagnosis when\n   parsing an object name fails., 2009-12-07)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c     | 4 +---\n merge-recursive.c | 6 ++----\n notes.c           | 3 +--\n object-name.c     | 3 +--\n tree-walk.c       | 9 +++++++++\n tree-walk.h       | 3 +++\n 6 files changed, 17 insertions(+), 11 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex d4457eba997..240922f7080 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -288,12 +288,10 @@ void shift_tree(struct repository *r,\n \n \tif (add_score < del_score) {\n \t\t/* We need to pick a subtree of two */\n-\t\tunsigned short mode;\n-\n \t\tif (!*del_prefix)\n \t\t\treturn;\n \n-\t\tif (get_tree_entry_mode(r, hash2, del_prefix, shifted, &mode))\n+\t\tif (get_tree_entry_path(r, hash2, del_prefix, shifted))\n \t\t\tdie(\"cannot find path %s in tree %s\",\n \t\t\t    del_prefix, oid_to_hex(hash2));\n \t\treturn;\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex c2e9f5d22d0..28ed69ddfaa 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1882,11 +1882,9 @@ static int tree_has_path(struct repository *r, struct tree *tree,\n \t\t\t const char *path)\n {\n \tstruct object_id hashy;\n-\tunsigned short mode_o;\n-\n-\treturn !get_tree_entry_mode(r,\n+\treturn !get_tree_entry_path(r,\n \t\t\t\t    &tree->object.oid, path,\n-\t\t\t\t    &hashy, &mode_o);\n+\t\t\t\t    &hashy);\n }\n \n /*\ndiff --git a/notes.c b/notes.c\nindex e6244624055..6766b1b478f 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -994,7 +994,6 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\tcombine_notes_fn combine_notes, int flags)\n {\n \tstruct object_id oid, object_oid;\n-\tunsigned short mode;\n \tstruct leaf_node root_tree;\n \n \tif (!t)\n@@ -1021,7 +1020,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\treturn;\n \tif (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))\n \t\tdie(\"Cannot use notes ref %s\", notes_ref);\n-\tif (get_tree_entry_mode(the_repository, &object_oid, \"\", &oid, &mode))\n+\tif (get_tree_entry_path(the_repository, &object_oid, \"\", &oid))\n \t\tdie(\"Failed to read notes tree referenced by %s (%s)\",\n \t\t    notes_ref, oid_to_hex(&object_oid));\n \ndiff --git a/object-name.c b/object-name.c\nindex 7e3b2d6d739..9ff5f83c1ff 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1693,7 +1693,6 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \t\t\t\t      int object_name_len)\n {\n \tstruct object_id oid;\n-\tunsigned short mode;\n \n \tif (!prefix)\n \t\tprefix = \"\";\n@@ -1704,7 +1703,7 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \tif (is_missing_file_error(errno)) {\n \t\tchar *fullname = xstrfmt(\"%s%s\", prefix, filename);\n \n-\t\tif (!get_tree_entry_mode(r, tree_oid, fullname, &oid, &mode)) {\n+\t\tif (!get_tree_entry_path(r, tree_oid, fullname, &oid)) {\n \t\t\tdie(_(\"path '%s' exists, but not '%s'\\n\"\n \t\t\t    \"hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?\"),\n \t\t\t    fullname,\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 30e3bb64e49..d0dc0c35318 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -628,6 +628,15 @@ int get_tree_entry_mode(struct repository *r,\n \treturn retval;\n }\n \n+int get_tree_entry_path(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid)\n+{\n+\tunsigned short mode;\n+\treturn get_tree_entry_mode(r, tree_oid, name, oid, &mode);\n+}\n+\n /*\n  * This is Linux's built-in max for the number of symlinks to follow.\n  * That limit, of course, does not affect git, but it's a reasonable\ndiff --git a/tree-walk.h b/tree-walk.h\nindex a01f2f226ec..c60667cba8f 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -172,8 +172,11 @@ struct traverse_info {\n  * You always need a pointer to an appropriate variable to fill in\n  * (NULL won't do!). That variable is:\n  *\n+ * get_tree_entry_path(): <no extra argument, just get the common 'path'>\n  * get_tree_entry_mode(): unsigned short mode\n  */\n+int get_tree_entry_path(struct repository *, const struct object_id *, const char *,\n+\t\t\tstruct object_id *);\n int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n \t\t\tstruct object_id *,\n \t\t\tunsigned short *);\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420737","messageId":"patch-17.19-dc895822828-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 17/18] tree-walk.h API: document and format tree_entry_extract()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:45Z","receivedAt":"2021-03-31T19:10:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Document and format the argument list of the tree_entry_extract()\nfunction in preparation for eventually adding a sister function.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.h | 14 ++++++++++----\n 1 file changed, 10 insertions(+), 4 deletions(-)\n\ndiff --git a/tree-walk.h b/tree-walk.h\nindex c60667cba8f..52eb0d6b5b3 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -38,11 +38,17 @@ struct tree_desc {\n \n /**\n  * Decode the entry currently being visited (the one pointed to by\n- * `tree_desc's` `entry` member) and return the sha1 of the entry. The\n- * `pathp` and `modep` arguments are set to the entry's pathname and mode\n- * respectively.\n+ * `tree_desc's` `entry` member) and return the OID of the entry.\n+ *\n+ * There are variants of this function depending on what fields in the\n+ * \"struct name_entry\" you'd like. You always need a pointer to an\n+ * appropriate variable to fill in (NULL won't do!):\n+ *\n+ * tree_entry_extract(): const char *path, unsigned int mode\n  */\n-static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned short *modep)\n+static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,\n+\t\t\t\t\t\t\t const char **pathp,\n+\t\t\t\t\t\t\t unsigned short *modep)\n {\n \t*pathp = desc->entry.path;\n \t*modep = desc->entry.mode;\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420738","messageId":"patch-13.19-9c96d472472-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 13/18] tree-walk.h API: formatting changes for subsequent commit","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:41Z","receivedAt":"2021-03-31T19:10:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Do formatting (mainly whitespace) changes of code around the\nget_tree_entry() function to make a subsequent change where we'll add\na sister function easier to read.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.c |  9 ++++++---\n tree-walk.h | 12 ++++++++----\n 2 files changed, 14 insertions(+), 7 deletions(-)\n\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 2d6226d5f18..fc2ce547c4e 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -589,7 +589,8 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\toidcpy(result, &oid);\n \t\t\treturn 0;\n \t\t}\n-\t\treturn get_tree_entry(r, &oid, name + entrylen, result, mode);\n+\t\treturn get_tree_entry(r, &oid, name + entrylen, result,\n+\t\t\t\t      mode);\n \t}\n \treturn -1;\n }\n@@ -620,7 +621,8 @@ int get_tree_entry(struct repository *r,\n \t} else {\n \t\tstruct tree_desc t;\n \t\tinit_tree_desc(&t, tree, size);\n-\t\tretval = find_tree_entry(r, &t, name, oid, mode);\n+\t\tretval = find_tree_entry(r, &t, name, oid,\n+\t\t\t\t\t mode);\n \t}\n \tfree(tree);\n \treturn retval;\n@@ -746,7 +748,8 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,\n \n \t\t/* Look up the first (or only) path component in the tree. */\n \t\tfind_result = find_tree_entry(r, &t, namebuf.buf,\n-\t\t\t\t\t      &current_tree_oid, mode);\n+\t\t\t\t\t      &current_tree_oid,\n+\t\t\t\t\t      mode);\n \t\tif (find_result) {\n \t\t\tgoto done;\n \t\t}\ndiff --git a/tree-walk.h b/tree-walk.h\nindex a5058469e9b..09e40d9221d 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -167,10 +167,14 @@ struct traverse_info {\n \n /**\n  * Find an entry in a tree given a pathname and the sha1 of a tree to\n- * search. Returns 0 if the entry is found and -1 otherwise. The third\n- * and fourth parameters are set to the entry's sha1 and mode respectively.\n- */\n-int get_tree_entry(struct repository *, const struct object_id *, const char *, struct object_id *, unsigned short *);\n+ * search. Returns 0 if the entry is found and -1 otherwise.\n+ *\n+ * The third and fourth parameters are set to the entry's sha1 and\n+ * mode respectively.\n+ */\n+int get_tree_entry(struct repository *, const struct object_id *, const char *,\n+\t\t   struct object_id *,\n+\t\t   unsigned short *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420739","messageId":"patch-15.19-adbada0ade8-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 15/18] tree-walk.h API: rename get_tree_entry() to get_tree_entry_mode()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:43Z","receivedAt":"2021-03-31T19:10:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the get_tree_entry() function to get_tree_entry_mode(). This\nchange is only a search-replacement of the name and indentation of the\nargument lists.\n\nSubsequent commits will add another get_tree_entry_*() function. Those\nchanges will be much easier to read if we do this rename first.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c              |  8 ++++----\n blame.c                |  5 ++---\n builtin/rm.c           |  2 +-\n builtin/update-index.c |  2 +-\n line-log.c             |  2 +-\n match-trees.c          |  6 +++---\n merge-recursive.c      | 18 +++++++++---------\n notes.c                |  2 +-\n object-name.c          |  6 +++---\n tree-walk.c            | 14 +++++++-------\n tree-walk.h            |  8 ++++----\n 11 files changed, 36 insertions(+), 37 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 6af35a66b56..fa07fb442b4 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -475,10 +475,10 @@ static void parse_treeish_arg(const char **argv,\n \t\tunsigned short mode;\n \t\tint err;\n \n-\t\terr = get_tree_entry(ar_args->repo,\n-\t\t\t\t     &tree->object.oid,\n-\t\t\t\t     prefix, &tree_oid,\n-\t\t\t\t     &mode);\n+\t\terr = get_tree_entry_mode(ar_args->repo,\n+\t\t\t\t\t  &tree->object.oid,\n+\t\t\t\t\t  prefix, &tree_oid,\n+\t\t\t\t\t  &mode);\n \t\tif (err || !S_ISDIR(mode))\n \t\t\tdie(_(\"current working directory is untracked\"));\n \ndiff --git a/blame.c b/blame.c\nindex d1fa3821a08..462809095be 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -102,8 +102,7 @@ static void verify_working_tree_path(struct repository *r,\n \t\tconst struct object_id *commit_oid = &parents->item->object.oid;\n \t\tstruct object_id blob_oid;\n \t\tunsigned short mode;\n-\n-\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode))\n+\t\tif (!get_tree_entry_mode(r, commit_oid, path, &blob_oid, &mode))\n \t\t\treturn;\n \t}\n \n@@ -1237,7 +1236,7 @@ static int fill_blob_sha1_and_mode(struct repository *r,\n {\n \tif (!is_null_oid(&origin->blob_oid))\n \t\treturn 0;\n-\tif (get_tree_entry(r, &origin->commit->object.oid, origin->path, &origin->blob_oid, &origin->mode))\n+\tif (get_tree_entry_mode(r, &origin->commit->object.oid, origin->path, &origin->blob_oid, &origin->mode))\n \t\tgoto error_out;\n \tif (oid_object_info(r, &origin->blob_oid, NULL) != OBJ_BLOB)\n \t\tgoto error_out;\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex 4858631e0f0..4617388b29a 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -179,7 +179,7 @@ static int check_local_mod(struct object_id *head, int index_only)\n \t\t * way as changed from the HEAD.\n \t\t */\n \t\tif (no_head\n-\t\t     || get_tree_entry(the_repository, head, name, &oid, &mode)\n+\t\t     || get_tree_entry_mode(the_repository, head, name, &oid, &mode)\n \t\t     || ce->ce_mode != create_ce_mode(mode)\n \t\t     || !oideq(&ce->oid, &oid))\n \t\t\tstaged_changes = 1;\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 79087bccea4..070510d6a88 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -603,7 +603,7 @@ static struct cache_entry *read_one_ent(const char *which,\n \tstruct object_id oid;\n \tstruct cache_entry *ce;\n \n-\tif (get_tree_entry(the_repository, ent, path, &oid, &mode)) {\n+\tif (get_tree_entry_mode(the_repository, ent, path, &oid, &mode)) {\n \t\tif (which)\n \t\t\terror(\"%s: not in %s branch.\", path, which);\n \t\treturn NULL;\ndiff --git a/line-log.c b/line-log.c\nindex 51d93310a4d..d8ba9229212 100644\n--- a/line-log.c\n+++ b/line-log.c\n@@ -503,7 +503,7 @@ static void fill_blob_sha1(struct repository *r, struct commit *commit,\n \tunsigned short mode;\n \tstruct object_id oid;\n \n-\tif (get_tree_entry(r, &commit->object.oid, spec->path, &oid, &mode))\n+\tif (get_tree_entry_mode(r, &commit->object.oid, spec->path, &oid, &mode))\n \t\tdie(\"There is no path %s in the commit\", spec->path);\n \tfill_filespec(spec, &oid, 1, mode);\n \ndiff --git a/match-trees.c b/match-trees.c\nindex bdd16adb70a..d4457eba997 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -293,7 +293,7 @@ void shift_tree(struct repository *r,\n \t\tif (!*del_prefix)\n \t\t\treturn;\n \n-\t\tif (get_tree_entry(r, hash2, del_prefix, shifted, &mode))\n+\t\tif (get_tree_entry_mode(r, hash2, del_prefix, shifted, &mode))\n \t\t\tdie(\"cannot find path %s in tree %s\",\n \t\t\t    del_prefix, oid_to_hex(hash2));\n \t\treturn;\n@@ -321,12 +321,12 @@ void shift_tree_by(struct repository *r,\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n-\tif (!get_tree_entry(r, hash1, shift_prefix, &sub1, &tmp) &&\n+\tif (!get_tree_entry_mode(r, hash1, shift_prefix, &sub1, &tmp) &&\n \t    S_ISDIR(tmp))\n \t\tcandidate |= 1;\n \n \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n-\tif (!get_tree_entry(r, hash2, shift_prefix, &sub2, &tmp) &&\n+\tif (!get_tree_entry_mode(r, hash2, shift_prefix, &sub2, &tmp) &&\n \t    S_ISDIR(tmp))\n \t\tcandidate |= 2;\n \ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 57198b29fe7..c2e9f5d22d0 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -485,7 +485,7 @@ static int get_tree_entry_if_blob(struct repository *r,\n {\n \tint ret;\n \n-\tret = get_tree_entry(r, tree, path, &dfs->oid, &dfs->mode);\n+\tret = get_tree_entry_mode(r, tree, path, &dfs->oid, &dfs->mode);\n \tif (S_ISDIR(dfs->mode)) {\n \t\toidcpy(&dfs->oid, &null_oid);\n \t\tdfs->mode = 0;\n@@ -1884,9 +1884,9 @@ static int tree_has_path(struct repository *r, struct tree *tree,\n \tstruct object_id hashy;\n \tunsigned short mode_o;\n \n-\treturn !get_tree_entry(r,\n-\t\t\t       &tree->object.oid, path,\n-\t\t\t       &hashy, &mode_o);\n+\treturn !get_tree_entry_mode(r,\n+\t\t\t\t    &tree->object.oid, path,\n+\t\t\t\t    &hashy, &mode_o);\n }\n \n /*\n@@ -2538,11 +2538,11 @@ static void apply_directory_rename_modifications(struct merge_options *opt,\n \t * the various handle_rename_*() functions update the index\n \t * explicitly rather than relying on unpack_trees() to have done it.\n \t */\n-\tget_tree_entry(opt->repo,\n-\t\t       &tree->object.oid,\n-\t\t       pair->two->path,\n-\t\t       &re->dst_entry->stages[stage].oid,\n-\t\t       &re->dst_entry->stages[stage].mode);\n+\tget_tree_entry_mode(opt->repo,\n+\t\t\t    &tree->object.oid,\n+\t\t\t    pair->two->path,\n+\t\t\t    &re->dst_entry->stages[stage].oid,\n+\t\t\t    &re->dst_entry->stages[stage].mode);\n \n \t/*\n \t * Record the original change status (or 'type' of change).  If it\ndiff --git a/notes.c b/notes.c\nindex 00aa7d4115a..e6244624055 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -1021,7 +1021,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,\n \t\treturn;\n \tif (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid))\n \t\tdie(\"Cannot use notes ref %s\", notes_ref);\n-\tif (get_tree_entry(the_repository, &object_oid, \"\", &oid, &mode))\n+\tif (get_tree_entry_mode(the_repository, &object_oid, \"\", &oid, &mode))\n \t\tdie(\"Failed to read notes tree referenced by %s (%s)\",\n \t\t    notes_ref, oid_to_hex(&object_oid));\n \ndiff --git a/object-name.c b/object-name.c\nindex 64202de60b1..7e3b2d6d739 100644\n--- a/object-name.c\n+++ b/object-name.c\n@@ -1704,7 +1704,7 @@ static void diagnose_invalid_oid_path(struct repository *r,\n \tif (is_missing_file_error(errno)) {\n \t\tchar *fullname = xstrfmt(\"%s%s\", prefix, filename);\n \n-\t\tif (!get_tree_entry(r, tree_oid, fullname, &oid, &mode)) {\n+\t\tif (!get_tree_entry_mode(r, tree_oid, fullname, &oid, &mode)) {\n \t\t\tdie(_(\"path '%s' exists, but not '%s'\\n\"\n \t\t\t    \"hint: Did you mean '%.*s:%s' aka '%.*s:./%s'?\"),\n \t\t\t    fullname,\n@@ -1903,8 +1903,8 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo,\n \t\t\t\t\tfilename, oid, &oc->symlink_path,\n \t\t\t\t\t&oc->mode);\n \t\t\t} else {\n-\t\t\t\tret = get_tree_entry(repo, &tree_oid, filename, oid,\n-\t\t\t\t\t\t     &oc->mode);\n+\t\t\t\tret = get_tree_entry_mode(repo, &tree_oid, filename, oid,\n+\t\t\t\t\t\t\t  &oc->mode);\n \t\t\t\tif (ret && only_to_die) {\n \t\t\t\t\tdiagnose_invalid_oid_path(repo, prefix,\n \t\t\t\t\t\t\t\t   filename,\ndiff --git a/tree-walk.c b/tree-walk.c\nindex fc2ce547c4e..30e3bb64e49 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -589,17 +589,17 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\t\toidcpy(result, &oid);\n \t\t\treturn 0;\n \t\t}\n-\t\treturn get_tree_entry(r, &oid, name + entrylen, result,\n-\t\t\t\t      mode);\n+\t\treturn get_tree_entry_mode(r, &oid, name + entrylen, result,\n+\t\t\t\t\t   mode);\n \t}\n \treturn -1;\n }\n \n-int get_tree_entry(struct repository *r,\n-\t\t   const struct object_id *tree_oid,\n-\t\t   const char *name,\n-\t\t   struct object_id *oid,\n-\t\t   unsigned short *mode)\n+int get_tree_entry_mode(struct repository *r,\n+\t\t\tconst struct object_id *tree_oid,\n+\t\t\tconst char *name,\n+\t\t\tstruct object_id *oid,\n+\t\t\tunsigned short *mode)\n {\n \tint retval;\n \tvoid *tree;\ndiff --git a/tree-walk.h b/tree-walk.h\nindex cd8da84f56c..a01f2f226ec 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -172,11 +172,11 @@ struct traverse_info {\n  * You always need a pointer to an appropriate variable to fill in\n  * (NULL won't do!). That variable is:\n  *\n- * get_tree_entry(): unsigned short mode\n+ * get_tree_entry_mode(): unsigned short mode\n  */\n-int get_tree_entry(struct repository *, const struct object_id *, const char *,\n-\t\t   struct object_id *,\n-\t\t   unsigned short *);\n+int get_tree_entry_mode(struct repository *, const struct object_id *, const char *,\n+\t\t\tstruct object_id *,\n+\t\t\tunsigned short *);\n \n /**\n  * Generate the full pathname of a tree entry based from the root of the\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420741","messageId":"patch-06.19-0be013e47c4-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 06/18] diff tests: test that \"mode\" is passed when sorting","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:34Z","receivedAt":"2021-03-31T19:10:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Piggy-back on the recently added fsck tests for mode comparisons in\nmktree and assert that diff-tree also does the right thing in this\nimplausible scenario.\n\nAs with the other tests I've added in preceding commits, these tests\nwill fail if the mode is the same or reversed, respectively.\n\nThe diff-tree code being tested here was originally added back in\n.9174026cfe (Add \"diff-tree\" program to show which files have changed\nbetween two trees., 2005-04-09).\n\nUnlike the other tests I've added there are existing tests for both of\nthese scenarios. Breaking that function as described above will make\ntests in t4002-diff-basic.sh, t6409-merge-subtree.sh and\nt4037-diff-r-t-dirs.sh fail.\n\nI think it's good to have tests for this regardless, so let's add\nthese.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t1450-fsck.sh | 22 ++++++++++++++++++++++\n 1 file changed, 22 insertions(+)\n\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 46125190b45..5dd842bb82c 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -362,6 +362,28 @@ test_expect_success 'tree entry with duplicate type mismatching objects' '\n \t)\n '\n \n+test_expect_success 'diff-tree stressing tree-diff.c::tree_entry_pathcmp(), not the same type' '\n+\tzero=$(test_oid zero) &&\n+\tgit -C duplicate-entry diff-tree broken-commit-1 broken-commit-2 >1-to-2 &&\n+\tgrep \"$zero\" 1-to-2 >lines &&\n+\ttest_line_count = 2 lines &&\n+\n+\tgit -C duplicate-entry diff-tree broken-commit-2 broken-commit-1 >2-to-1 &&\n+\tgrep \"$zero\" 2-to-1 >lines &&\n+\ttest_line_count = 2 lines\n+'\n+\n+test_expect_success 'diff-tree stressing tree-diff.c::tree_entry_pathcmp(), types not reversed' '\n+\tblob_ok=$(git -C duplicate-entry rev-parse broken-commit-2:A) &&\n+\tgit -C duplicate-entry diff-tree --diff-filter=A broken-commit-1 broken-commit-2 >1-to-2 &&\n+\tgrep \"$blob_ok\" 1-to-2 &&\n+\ttest_line_count = 1 1-to-2 &&\n+\n+\tgit -C duplicate-entry diff-tree --diff-filter=A broken-commit-2 broken-commit-1 >2-to-1 &&\n+\tgrep \"$blob_ok\" 2-to-1 &&\n+\ttest_line_count = 1 2-to-1\n+'\n+\n test_expect_success 'tag pointing to nonexistent' '\n \tbadoid=$(test_oid deadbeef) &&\n \tcat >invalid-tag <<-EOF &&\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420742","messageId":"patch-12.19-960e15bc7d5-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 12/18] tree.h: format argument lists of read_tree() users","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:40Z","receivedAt":"2021-03-31T19:10:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Re-indent and format the argument list of read_tree() callbacks, and\nthe relevant functions they call.\n\nThis is a whitespace-only change to make changes in a later series\neasier to read. In that series I'll be adding a new argument on the\n\"mode\" line, so leave space on it for a new argument.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n archive.c          | 13 ++++++++-----\n builtin/checkout.c |  4 +++-\n builtin/log.c      |  5 +++--\n builtin/ls-tree.c  |  4 +++-\n merge-recursive.c  |  3 ++-\n tree.h             |  5 ++++-\n 6 files changed, 23 insertions(+), 11 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 295615580d7..6af35a66b56 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -134,8 +134,9 @@ static int check_attr_export_subst(const struct attr_check *check)\n }\n \n static int write_archive_entry(const struct object_id *oid, const char *base,\n-\t\tint baselen, const char *filename, unsigned mode,\n-\t\tvoid *context)\n+\t\t\t       int baselen, const char *filename,\n+\t\t\t       unsigned mode,\n+\t\t\t       void *context)\n {\n \tstatic struct strbuf path = STRBUF_INIT;\n \tstruct archiver_context *c = context;\n@@ -225,8 +226,9 @@ static int write_directory(struct archiver_context *c)\n }\n \n static int queue_or_write_archive_entry(const struct object_id *oid,\n-\t\tstruct strbuf *base, const char *filename,\n-\t\tunsigned mode, void *context)\n+\t\t\t\t\tstruct strbuf *base, const char *filename,\n+\t\t\t\t\tunsigned mode,\n+\t\t\t\t\tvoid *context)\n {\n \tstruct archiver_context *c = context;\n \n@@ -372,7 +374,8 @@ struct path_exists_context {\n };\n \n static int reject_entry(const struct object_id *oid, struct strbuf *base,\n-\t\t\tconst char *filename, unsigned mode,\n+\t\t\tconst char *filename,\n+\t\t\tunsigned mode,\n \t\t\tvoid *context)\n {\n \tint ret = -1;\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0e663905200..0887352db2a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -114,7 +114,9 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n }\n \n static int update_some(const struct object_id *oid, struct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, void *context)\n+\t\t       const char *pathname,\n+\t\t       unsigned mode,\n+\t\t       void *context)\n {\n \tint len;\n \tstruct cache_entry *ce;\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 980de590638..b7b76856a9f 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -598,8 +598,9 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)\n }\n \n static int show_tree_object(const struct object_id *oid,\n-\t\tstruct strbuf *base,\n-\t\tconst char *pathname, unsigned mode, void *context)\n+\t\t\t    struct strbuf *base, const char *pathname,\n+\t\t\t    unsigned mode,\n+\t\t\t    void *context)\n {\n \tFILE *file = context;\n \tfprintf(file, \"%s%s\\n\", pathname, S_ISDIR(mode) ? \"/\" : \"\");\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex 3a442631c71..8d5c3fd0582 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -62,7 +62,9 @@ static int show_recursive(const char *base, int baselen, const char *pathname)\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+\t\t     const char *pathname,\n+\t\t     unsigned mode,\n+\t\t     void *context)\n {\n \tint retval = 0;\n \tint baselen;\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 97520a88646..57198b29fe7 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -453,7 +453,8 @@ static void unpack_trees_finish(struct merge_options *opt)\n \n static int save_files_dirs(const struct object_id *oid,\n \t\t\t   struct strbuf *base, const char *path,\n-\t\t\t   unsigned int mode, void *context)\n+\t\t\t   unsigned int mode,\n+\t\t\t   void *context)\n {\n \tstruct path_hashmap_entry *entry;\n \tint baselen = base->len;\ndiff --git a/tree.h b/tree.h\nindex 6efff003e21..10c8637ab3e 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -31,7 +31,10 @@ struct tree *parse_tree_indirect(const struct object_id *oid);\n int cmp_cache_name_compare(const void *a_, const void *b_);\n \n #define READ_TREE_RECURSIVE 1\n-typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);\n+typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *,\n+\t\t\t      const char *,\n+\t\t\t      unsigned int,\n+\t\t\t      void *);\n \n int read_tree_at(struct repository *r,\n \t\t struct tree *tree, struct strbuf *base,\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420743","messageId":"patch-11.19-e190ed39754-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 11/18] match-trees: use \"tmp\" for mode in shift_tree_by()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:39Z","receivedAt":"2021-03-31T19:10:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor code added in 85e51b783c3 (Make \"subtree\" part more\northogonal to the rest of merge-recursive., 2008-06-30) to make it\nobvious that we don't care about the \"mode\" here outside of the if\nstatement it appears in.\n\nThat's opposed to the sub1 & sub2 variables, where we use the two\nobject ids later in this function.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n match-trees.c | 10 +++++-----\n 1 file changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/match-trees.c b/match-trees.c\nindex a6796de442d..bdd16adb70a 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -317,17 +317,17 @@ void shift_tree_by(struct repository *r,\n \t\t   const char *shift_prefix)\n {\n \tstruct object_id sub1, sub2;\n-\tunsigned short mode1, mode2;\n+\tunsigned short tmp;\n \tunsigned candidate = 0;\n \n \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n-\tif (!get_tree_entry(r, hash1, shift_prefix, &sub1, &mode1) &&\n-\t    S_ISDIR(mode1))\n+\tif (!get_tree_entry(r, hash1, shift_prefix, &sub1, &tmp) &&\n+\t    S_ISDIR(tmp))\n \t\tcandidate |= 1;\n \n \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n-\tif (!get_tree_entry(r, hash2, shift_prefix, &sub2, &mode2) &&\n-\t    S_ISDIR(mode2))\n+\tif (!get_tree_entry(r, hash2, shift_prefix, &sub2, &tmp) &&\n+\t    S_ISDIR(tmp))\n \t\tcandidate |= 2;\n \n \tif (candidate == 3) {\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420744","messageId":"patch-14.19-44f16d74426-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 14/18] tree-walk.h API doc: improve documentation of get_tree_entry()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:42Z","receivedAt":"2021-03-31T19:10:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change a mention of sha1 to OID and change the comment to a listing of\nfunctions discussed below, right now there's only one function, but\nsubsequent commits will add more.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n tree-walk.h | 8 +++++---\n 1 file changed, 5 insertions(+), 3 deletions(-)\n\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 09e40d9221d..cd8da84f56c 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -166,11 +166,13 @@ struct traverse_info {\n };\n \n /**\n- * Find an entry in a tree given a pathname and the sha1 of a tree to\n+ * Find an entry in a tree given a pathname and the OID of a tree to\n  * search. Returns 0 if the entry is found and -1 otherwise.\n  *\n- * The third and fourth parameters are set to the entry's sha1 and\n- * mode respectively.\n+ * You always need a pointer to an appropriate variable to fill in\n+ * (NULL won't do!). That variable is:\n+ *\n+ * get_tree_entry(): unsigned short mode\n  */\n int get_tree_entry(struct repository *, const struct object_id *, const char *,\n \t\t   struct object_id *,\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420740","messageId":"patch-18.19-e961a0e8b5b-20210331T190531Z-avarab@gmail.com","threadId":"18927","inReplyTo":"cover-00.19-00000000000-20210331T190531Z-avarab@gmail.com","subject":"[PATCH v5 18/18] tree-entry.h API: rename tree_entry_extract() to tree_entry_extract_mode()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-31T19:09:46Z","receivedAt":"2021-03-31T19:10:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As with the recent split of the get_tree_entry() function, rename the\ntree_entry_extract() function to *_mode() in preparation for adding\nother variants of it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n fsck.c        | 2 +-\n match-trees.c | 4 ++--\n tree-diff.c   | 4 ++--\n tree-walk.c   | 2 +-\n tree-walk.h   | 8 ++++----\n 5 files changed, 10 insertions(+), 10 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex e3030f3b358..b1d802cc30a 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -673,7 +673,7 @@ static int fsck_tree(const struct object_id *oid,\n \t\tconst char *name, *backslash;\n \t\tconst struct object_id *oid;\n \n-\t\toid = tree_entry_extract(&desc, &name, &mode);\n+\t\toid = tree_entry_extract_mode(&desc, &name, &mode);\n \n \t\thas_null_sha1 |= is_null_oid(oid);\n \t\thas_full_path |= !!strchr(name, '/');\ndiff --git a/match-trees.c b/match-trees.c\nindex 240922f7080..23ff89be2b2 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -146,7 +146,7 @@ static void match_trees(const struct object_id *hash1,\n \t\tunsigned short mode;\n \t\tint score;\n \n-\t\telem = tree_entry_extract(&one, &path, &mode);\n+\t\telem = tree_entry_extract_mode(&one, &path, &mode);\n \t\tif (!S_ISDIR(mode))\n \t\t\tgoto next;\n \t\tscore = score_trees(elem, hash2);\n@@ -202,7 +202,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \t\tunsigned short mode;\n \t\tint len = tree_entry_len(&desc.entry);\n \n-\t\ttree_entry_extract(&desc, &name, &mode);\n+\t\ttree_entry_extract_mode(&desc, &name, &mode);\n \t\tif (len == toplen &&\n \t\t    !memcmp(name, prefix, toplen)) {\n \t\t\tif (!S_ISDIR(mode))\ndiff --git a/tree-diff.c b/tree-diff.c\nindex b7a76cc2620..31f8a9331b1 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -196,7 +196,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \n \tif (t) {\n \t\t/* path present in resulting tree */\n-\t\toid = tree_entry_extract(t, &path, &mode);\n+\t\toid = tree_entry_extract_mode(t, &path, &mode);\n \t\tpathlen = tree_entry_len(&t->entry);\n \t\tisdir = S_ISDIR(mode);\n \t} else {\n@@ -207,7 +207,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \t\t * 1) all modes for tp[i]=tp[imin] should be the same wrt\n \t\t *    S_ISDIR, thanks to base_name_compare().\n \t\t */\n-\t\ttree_entry_extract(&tp[imin], &path, &mode);\n+\t\ttree_entry_extract_mode(&tp[imin], &path, &mode);\n \t\tpathlen = tree_entry_len(&tp[imin].entry);\n \n \t\tisdir = S_ISDIR(mode);\ndiff --git a/tree-walk.c b/tree-walk.c\nindex d0dc0c35318..533cb6a26b4 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -567,7 +567,7 @@ static int find_tree_entry(struct repository *r, struct tree_desc *t,\n \t\tstruct object_id oid;\n \t\tint entrylen, cmp;\n \n-\t\toidcpy(&oid, tree_entry_extract(t, &entry, mode));\n+\t\toidcpy(&oid, tree_entry_extract_mode(t, &entry, mode));\n \t\tentrylen = tree_entry_len(&t->entry);\n \t\tupdate_tree_entry(t);\n \t\tif (entrylen > namelen)\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 52eb0d6b5b3..ec1d6927205 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -44,11 +44,11 @@ struct tree_desc {\n  * \"struct name_entry\" you'd like. You always need a pointer to an\n  * appropriate variable to fill in (NULL won't do!):\n  *\n- * tree_entry_extract(): const char *path, unsigned int mode\n+ * tree_entry_extract_mode(): const char *path, unsigned int mode\n  */\n-static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,\n-\t\t\t\t\t\t\t const char **pathp,\n-\t\t\t\t\t\t\t unsigned short *modep)\n+static inline const struct object_id *tree_entry_extract_mode(struct tree_desc *desc,\n+\t\t\t\t\t\t\t      const char **pathp,\n+\t\t\t\t\t\t\t      unsigned short *modep)\n {\n \t*pathp = desc->entry.path;\n \t*modep = desc->entry.mode;\n-- \n2.31.1.474.g72d45d12706\n\n"},{"id":"420754","messageId":"xmqqpmzf54y4.fsf@gitster.g","threadId":"18927","inReplyTo":"patch-01.19-a74e02ff0ba-20210331T190531Z-avarab@gmail.com","subject":"Re: [PATCH v5 01/18] cache.h: add a comment to object_type()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-31T22:33:23Z","receivedAt":"2021-03-31T22:34:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Add a comment to the object_type() function to explain what it\n> returns, and what the \"mode\" is in the \"else\" case.\n>\n> The object_type() function dates back to 4d1012c3709 (Fix rev-list\n> when showing objects involving submodules, 2007-11-11). It's not\n> immediately obvious to someone looking at its history and how it's\n> come to be used.\n>\n> Despite what Linus noted in 4d1012c3709 (Fix rev-list when showing\n> objects involving submodules, 2007-11-11) about wanting to move away\n> from users of object_type() relying on S_ISLNK(mode) being true here\n> we do currently rely on that.\n\nYou are misreading Linus's comment here.\n\nThe comment is not about \"S_ISLNK()\" specifically.  It is about\nassuming that any \"S_ISx()\" macros that are designed to work on\nstat.st_mode would work the same way for our \"mode bits in tree\"\n(i.e. 'internal git state' in the commit message refers to this\nfact).  Platforms do not have to have 100xxx to be regular files,\nbut we did rely on that bit assignment.\n\nAnd then we invented S_ISGITLINK() that exists on nobody's\nstat.st_mode and have been assuming that would not collide\nwith any real S_IFMT bit assignment.\n\nAll of that has been patched with NEEDS_MODE_TRANSLATION band-aid\nquite some time ago, though, with d543d9c0 (compat: convert modes to\nuse portable file type values, 2014-12-03).\n\nSo, no, we do not quite rely on that anymore.\n\n> If this is changed to a condition to\n> only return OBJ_BLOB on S_ISREG(mode) then t4008, t4023 and t7415 will\n> have failing tests.\n\nSpecifically, the comment is not about symbolic links, and nobody\nwho reads the comment correctly would imagine that limiting BLOB\nto S_ISREG.  The comment merely was lamenting that\n\n\t\"If ISDIR, then tree and otherwise blob\" does not hold\n\tanymore since we added submodules; \"If ISDIR, then tree, and\n\tif ISGITLINK, then commit and otherwise blob\" would fix it,\n\tbut relying on ISDIR, ISGITLINK, etc. was a mistake that we\n\tcontinue to rely on even with this fix.\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n\nWhich means that a lot of the stuff in the proposed log message is\nfalse.  I do however think that ...\n\n>  cache.h | 7 ++++++-\n>  1 file changed, 6 insertions(+), 1 deletion(-)\n>\n> diff --git a/cache.h b/cache.h\n> index 57f2285bba9..41e99bd9c63 100644\n> --- a/cache.h\n> +++ b/cache.h\n> @@ -451,11 +451,16 @@ enum object_type {\n>  \tOBJ_MAX\n>  };\n\n\n> +/*\n> + * object_type() returns an object of a type that'll appear in a tree,\n> + * so no OBJ_TAG is possible. This is mostly (and dates back to)\n> + * consumers of the tree-walk.h API's \"mode\" field.\n> + */\n\n... this comment is correct and it is a good change to clarify what\n'mode' we are talking about here.\n\n>  static inline enum object_type object_type(unsigned int mode)\n>  {\n>  \treturn S_ISDIR(mode) ? OBJ_TREE :\n>  \t\tS_ISGITLINK(mode) ? OBJ_COMMIT :\n> -\t\tOBJ_BLOB;\n> +\t\tOBJ_BLOB; /* S_ISREG(mode) || S_ISLNK(mode) */\n\nFor futureproofing, it might not be a bad idea to do this:\n\n\tS_ISDIR(mode) ? OBJ_TREE :\n\tS_ISGITLINK(mode) ? OBJ_COMMIT :\n\t(S_ISREG(mode) || S_ISLNK(mode)) ? OBJ_BLOB :\n\tOBJ_ERROR;\n\nto anticipate new tree mode bits so that the need for a fix similar\nto 4d1012c3 (Fix rev-list when showing objects involving submodules,\n2007-11-11) is immediately noticed, but for now, code and comment\nclarification would be sufficient.\n\nThe proposed log message needs rewriting, though.\n\nThanks.\n\n\n"},{"id":"420756","messageId":"xmqqeefv53xu.fsf@gitster.g","threadId":"18927","inReplyTo":"patch-03.19-44c3fdd0085-20210331T190531Z-avarab@gmail.com","subject":"Re: [PATCH v5 03/18] cache.h: have base_name_compare() take \"is tree?\", not \"mode\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-31T22:55:09Z","receivedAt":"2021-03-31T22:56:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Change the base_name_compare() API and the related df_name_compare()\n> function to take a boolean argument indicating whether the entry is a\n> tree or not, instead of having them call S_ISDIR(mode) on their own.\n>\n> This introduces no functional change, but makes later (not part of\n> this series) changes to abstract away \"object_type\" from \"mode\" more\n> readable.\n>\n> The API being modified here was originally added way back in\n> 958ba6c96eb (Introduce \"base_name_compare()\" helper function,\n> 2005-05-20).\n>\n> None of these comparison functions used to have tests, but with\n> preceding commits some of them now do. I thought the remainder was\n> trivial enough to review without tests, and didn't want to spend more\n> time on them.\n\nPuzzled.  preceeding commits?\n\n> diff --git a/cache.h b/cache.h\n> index 41e99bd9c63..3bcea022ad2 100644\n> --- a/cache.h\n> +++ b/cache.h\n> @@ -1506,8 +1506,8 @@ int repo_interpret_branch_name(struct repository *r,\n>  \n>  int validate_headref(const char *ref);\n>  \n> -int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n> -int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n> +int base_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n> +int df_name_compare(const char *name1, int len1, int istree1, const char *name2, int len2, int istree2);\n>  int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);\n>  int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);\n\nHopefully there won't be any new callers to these comparison\nfunctions introduced while this topic is in flight.\n\nWithout seeing real users (not yet at this 3rd step in the 18 patch\nseries, anyway), this step feels to be a needless code churn whose\nonly effect is to increase risks of silent semantic conflicts that\ncompilers will not be able to help detecting.  It might make it\nslightly less error prone to add\n\n\tif (istree1 != 0 && istree1 != 1)\n\t\tBUG(\"%o?  unconverted caller?\", istree1);\n\tif (istree2 != 0 && istree2 != 1)\n\t\tBUG(\"%o?  unconverted caller?\", istree2);\n\nin the callee while the topic is still in flight.  Or do this in a\nrenamed function so that such a semantic conflict will be noticed by\nthe linker (although that would increase the busywork workload on\nthe integrator).\n\nI know steps like this in a long series (not limited to this series)\nmeans well, and we do encourage people to move necessary preliminary\nclean-up to the early part of a series, but they make me wonder \"can\nwe get to the point without 'clean-up while we are at it' steps that\nmay turn out to be more-or-less irrelevant?\"\n\nIn other words, we do encourage people to do necessary preliminary\nclean-up before the main part of a series, but it sometimes is\nunclear if an early \"clean-up\" patch in a long series is \"necessary\"\nor merely \"while at it\" that can be omitted.\n\nBut let me hold my judgment until I reach the end of the series to\nknow enough to say if this step is or is not \"necessary\" preliminary\nclean-up.\n\n> diff --git a/unpack-trees.c b/unpack-trees.c\n> index 29029f34ed6..23c1640ae9a 100644\n> --- a/unpack-trees.c\n> +++ b/unpack-trees.c\n> @@ -925,7 +925,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,\n>  static int do_compare_entry_piecewise(const struct cache_entry *ce,\n>  \t\t\t\t      const struct traverse_info *info,\n>  \t\t\t\t      const char *name, size_t namelen,\n> -\t\t\t\t      unsigned mode)\n> +\t\t\t\t      unsigned istree)\n\nHere, the \"istree\" boolean is expressed as unsigned, but in cache.h,\nit is expressed as int?  Why the discrepancy?\n"},{"id":"420757","messageId":"xmqqa6qj53ie.fsf@gitster.g","threadId":"18927","inReplyTo":"patch-05.19-b3289f7bbe5-20210331T190531Z-avarab@gmail.com","subject":"Re: [PATCH v5 05/18] mktree tests: test that \"mode\" is passed when sorting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-31T23:04:25Z","receivedAt":"2021-03-31T23:08:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Add a test for the mode being passed to ent_compare(). That code dates\n> back to 83f50539a9 (git-mktree: reverse of git-ls-tree., 2006-02-20)\n> and there's never been a test for that particular edge case. Now we\n> have one.\n>\n> I don't see how anything could run into this in practice. In order for\n> that mode sorting to matter as a tiebreaker we need to have a\n> duplicate entry in the tree, i.e. two \"foo\" entries, one a blob and\n> one a tree. This asserts that if that happens we'll sort on the modes\n> we encounter in such an invalid entry, i.e. we expect the tree entry\n> before the blob.\n\nIt is more like \"in a tree with 'foo.' and 'foo0' (both of which are\nblobs), a 'foo' that is a tree should appear after 'foo.' and before\n'foo0', because it sorts as if it were 'foo/'.  If 'foo' is a blob,\nit sorts the first before 'foo.' and 'foo0'\".  \n\nChecking a broken tree with both 'foo' (blob) and 'foo' (tree) that\ndoes not even pass fsck is not very valuable, but the sort order of\nthese two trees (i.e. one with foo as tree, the other with foo as\nblob) do need to be tested, as the unpack_trees merge machinery will\nneed to walk such pair of trees correctly.\n\nThanks.\n"},{"id":"420758","messageId":"xmqq5z166hxi.fsf@gitster.g","threadId":"18927","inReplyTo":"patch-06.19-0be013e47c4-20210331T190531Z-avarab@gmail.com","subject":"Re: [PATCH v5 06/18] diff tests: test that \"mode\" is passed when sorting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-31T23:07:37Z","receivedAt":"2021-03-31T23:08:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Piggy-back on the recently added fsck tests for mode comparisons in\n> mktree and assert that diff-tree also does the right thing in this\n> implausible scenario.\n\nThe same comment applies.  Just testing the order of \"foo\" (blob),\n\"foo.\" (blob), \"foo/\" (tree), and \"foo0\" (blob) in two comparisons\nwould be a more realistic test that has more practical value.\n\n"},{"id":"420759","messageId":"xmqq1rbu6hpa.fsf@gitster.g","threadId":"18927","inReplyTo":"patch-07.19-3367850559b-20210331T190531Z-avarab@gmail.com","subject":"Re: [PATCH v5 07/18] merge-tree tests: test for the mode comparison in same_entry()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-31T23:12:33Z","receivedAt":"2021-03-31T23:13:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Add a test to stress the \"a->mode == b->mode\" comparison in\n> merge-tree.c's same_entry().\n>\n> That code was initially added by Linus in 33deb63a36f (Add\n> \"merge-tree\" helper program. Maybe it's retarded, maybe it's helpful.,\n> 2005-04-14), and then again in its current form in\n> 492e0759bfe (Handling large files with GIT, 2006-02-14).\n>\n> However, nothing was testing that we handled this case\n> correctly. Simply removing the mode comparison left all tests passing,\n> but as seen here it's important that we don't think a path with the\n> same content but different modes is the same_entry().\n\nGood thinking.\n\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/t4300-merge-tree.sh | 44 +++++++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 44 insertions(+)\n>\n> diff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh\n> index e59601e5fe9..f783d784d02 100755\n> --- a/t/t4300-merge-tree.sh\n> +++ b/t/t4300-merge-tree.sh\n> @@ -40,6 +40,25 @@ test_expect_success 'file add A, B (same)' '\n>  \ttest_must_be_empty actual\n>  '\n>  \n> +test_expect_success 'file add A, B (different mode)' '\n> +\tgit reset --hard initial &&\n> +\ttest_commit \"add-a-b-same-diff-mode-A\" \"ONE\" \"AAA\" &&\n> +\tgit reset --hard initial &&\n> +\techo AAA >ONE &&\n> +\ttest_chmod +x ONE &&\n> +\ttest_tick &&\n> +\tgit commit -m\"add-a-b-same-diff-mode-B\" &&\n> +\tgit tag \"add-a-b-same-diff-mode-B\" HEAD &&\n> +\tgit merge-tree initial add-a-b-same-diff-mode-A add-a-b-same-diff-mode-B >actual &&\n> +\tcat >expected <<EXPECTED &&\n> +added in both\n> +  our    100644 $(git rev-parse add-a-b-same-diff-mode-A:ONE) ONE\n> +  their  100755 $(git rev-parse add-a-b-same-diff-mode-B:ONE) ONE\n> +EXPECTED\n\nCan we HT-indent the here text with \"<<-EOF\", unless existing tests\nin the same script predate the more recent convention?\n\n    ... goes and looks and realizes it is full of unindented\n    expectation ...\n\nNah, let's keep it that way.\n\n> +\ttest_cmp expected actual\n> +'\n> +\n>  test_expect_success 'file add A, B (different)' '\n>  \tgit reset --hard initial &&\n>  \ttest_commit \"add-a-b-diff-A\" \"ONE\" \"AAA\" &&\n> @@ -61,6 +80,31 @@ EXPECTED\n>  \ttest_cmp expected actual\n>  '\n>  \n> +test_expect_success 'file add A, B (different and different mode)' '\n> +\tgit reset --hard initial &&\n> +\ttest_commit \"add-a-b-diff-diff-mode-A\" \"ONE\" \"AAA\" &&\n> +\tgit reset --hard initial &&\n> +\techo BBB >ONE &&\n> +\ttest_chmod +x ONE &&\n> +\ttest_tick &&\n> +\tgit commit -m\"add-a-b-diff-diff-mode-B\" &&\n> +\tgit tag \"add-a-b-diff-diff-mode-B\" &&\n> +\tgit merge-tree initial add-a-b-diff-diff-mode-A add-a-b-diff-diff-mode-B >actual &&\n> +\tcat >expected <<EXPECTED &&\n> +added in both\n> +  our    100644 $(git rev-parse add-a-b-diff-diff-mode-A:ONE) ONE\n> +  their  100755 $(git rev-parse add-a-b-diff-diff-mode-B:ONE) ONE\n> +@@ -1 +1,5 @@\n> ++<<<<<<< .our\n> + AAA\n> ++=======\n> ++BBB\n> ++>>>>>>> .their\n> +EXPECTED\n\nNice.  So the mode-only conflict and mode plus contents conflict are\nboth shown in a sensible way.\n\nLooks good.\nThanks.\n"},{"id":"420760","messageId":"xmqqwntm52ht.fsf@gitster.g","threadId":"18927","inReplyTo":"patch-08.19-3c2ca98716d-20210331T190531Z-avarab@gmail.com","subject":"Re: [PATCH v5 08/18] blame: emit a better error on 'git blame directory'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-31T23:26:22Z","receivedAt":"2021-03-31T23:27:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Change an early check for non-blobs in verify_working_tree_path() to\n> let any such objects pass, and instead die shortly thereafter in the\n> fake_working_tree_commit() caller's type check.\n>\n> Now e.g. doing \"git blame t\" in git.git emits:\n>\n>     fatal: unsupported file type t\n>\n> Instead of:\n>\n>     fatal: no such path 't' in HEAD\n\nSorry, but I fail to see why \"unsupported file type t\" is quite an\nimprovement.  Is this one of these irrelevant clean-up while at it\nwhose benefit is unclear until much later, I have to wonder.\n\n> The main point of this test is to assert that we're not doing\n> something uniquely bad when in a conflicted merge. See\n\n\"this test\" refers to the logic \"it is OK to skip the check if one\nof the parents does have it as a blob\", introduced in 9aeaab68\n(blame: allow \"blame file\" in the middle of a conflicted merge,\n2012-09-11)?\n\n\n> -\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode) &&\n> -\t\t    oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n> +\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode))\n>  \t\t\treturn;\n>  \t}\n\nAt least, the original logic makes sense to me in that if an early\nparent has the path as a directory we do not declare it is OK but\nkeep going until we find a blob in a later parent before deciding to\nshort-cut.  I am not sure what the updated \"in this case we can\nbypass the real check\" condition even means.  Mechanically, it says\n\"if any parent has the path as any filesystem entity, even if it\nwere a directory, then it is OK\", but why?\n\nThanks.\n"},{"id":"420761","messageId":"xmqqsg4a527l.fsf@gitster.g","threadId":"18927","inReplyTo":"patch-09.19-749be26bf8e-20210331T190531Z-avarab@gmail.com","subject":"Re: [PATCH v5 09/18] notes & match-trees: use name_entry's \"pathlen\" member","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-31T23:32:30Z","receivedAt":"2021-03-31T23:33:39Z","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>  \twhile (desc.size) {\n>  \t\tconst char *name;\n>  \t\tunsigned short mode;\n> +\t\tint len = tree_entry_len(&desc.entry);\n>  \n>  \t\ttree_entry_extract(&desc, &name, &mode);\n> -\t\tif (strlen(name) == toplen &&\n> +\t\tif (len == toplen &&\n\nMakes sense.\n\n> @@ -216,9 +217,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n>  \t\t\t *   - to discard the \"const\"; this is OK because we\n>  \t\t\t *     know it points into our non-const \"buf\"\n>  \t\t\t */\n> -\t\t\trewrite_here = (unsigned char *)(desc.entry.path +\n> -\t\t\t\t\t\t\t strlen(desc.entry.path) +\n> -\t\t\t\t\t\t\t 1);\n> +\t\t\trewrite_here = (unsigned char *)(name + len + 1);\n\nSo does this.  The original using desc.entry.path even though it\ncalled extract to learn the name does not make much sense.\n\n>  \t\t\tbreak;\n>  \t\t}\n>  \t\tupdate_tree_entry(&desc);\n> diff --git a/notes.c b/notes.c\n> index a19e4ad7943..e2fec12a39e 100644\n> --- a/notes.c\n> +++ b/notes.c\n> @@ -413,7 +413,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n>  \twhile (tree_entry(&desc, &entry)) {\n>  \t\tunsigned char type;\n>  \t\tstruct leaf_node *l;\n> -\t\tsize_t path_len = strlen(entry.path);\n> +\t\tint path_len = entry.pathlen;\n\nOK.\n\n>  \t\tif (path_len == 2 * (hashsz - prefix_len)) {\n>  \t\t\t/* This is potentially the remainder of the SHA-1 */\n> @@ -483,7 +483,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n>  \t\t\t\tstrbuf_addch(&non_note_path, *q++);\n>  \t\t\t\tstrbuf_addch(&non_note_path, '/');\n>  \t\t\t}\n> -\t\t\tstrbuf_addstr(&non_note_path, entry.path);\n> +\t\t\tstrbuf_add(&non_note_path, entry.path, path_len);\n\nOK.\n\n>  \t\t\tadd_non_note(t, strbuf_detach(&non_note_path, NULL),\n>  \t\t\t\t     entry.mode, entry.oid.hash);\n>  \t\t}\n\nLooking good.\nThanks.\n"},{"id":"420762","messageId":"xmqqo8ey522r.fsf@gitster.g","threadId":"18927","inReplyTo":"patch-11.19-e190ed39754-20210331T190531Z-avarab@gmail.com","subject":"Re: [PATCH v5 11/18] match-trees: use \"tmp\" for mode in shift_tree_by()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-31T23:35:24Z","receivedAt":"2021-03-31T23:36:19Z","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> Refactor code added in 85e51b783c3 (Make \"subtree\" part more\n> orthogonal to the rest of merge-recursive., 2008-06-30) to make it\n> obvious that we don't care about the \"mode\" here outside of the if\n> statement it appears in.\n\nOK.\n\n>\n> That's opposed to the sub1 & sub2 variables, where we use the two\n> object ids later in this function.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  match-trees.c | 10 +++++-----\n>  1 file changed, 5 insertions(+), 5 deletions(-)\n>\n> diff --git a/match-trees.c b/match-trees.c\n> index a6796de442d..bdd16adb70a 100644\n> --- a/match-trees.c\n> +++ b/match-trees.c\n> @@ -317,17 +317,17 @@ void shift_tree_by(struct repository *r,\n>  \t\t   const char *shift_prefix)\n>  {\n>  \tstruct object_id sub1, sub2;\n> -\tunsigned short mode1, mode2;\n> +\tunsigned short tmp;\n>  \tunsigned candidate = 0;\n>  \n>  \t/* Can hash2 be a tree at shift_prefix in tree hash1? */\n> -\tif (!get_tree_entry(r, hash1, shift_prefix, &sub1, &mode1) &&\n> -\t    S_ISDIR(mode1))\n> +\tif (!get_tree_entry(r, hash1, shift_prefix, &sub1, &tmp) &&\n> +\t    S_ISDIR(tmp))\n>  \t\tcandidate |= 1;\n>  \n>  \t/* Can hash1 be a tree at shift_prefix in tree hash2? */\n> -\tif (!get_tree_entry(r, hash2, shift_prefix, &sub2, &mode2) &&\n> -\t    S_ISDIR(mode2))\n> +\tif (!get_tree_entry(r, hash2, shift_prefix, &sub2, &tmp) &&\n> +\t    S_ISDIR(tmp))\n>  \t\tcandidate |= 2;\n>  \n>  \tif (candidate == 3) {\n"},{"id":"420852","messageId":"xmqq4kgp3fyh.fsf@gitster.g","threadId":"18927","inReplyTo":"patch-14.19-44f16d74426-20210331T190531Z-avarab@gmail.com","subject":"Re: [PATCH v5 14/18] tree-walk.h API doc: improve documentation of get_tree_entry()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-01T20:30:46Z","receivedAt":"2021-04-01T20:30:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Change a mention of sha1 to OID and change the comment to a listing of\n> functions discussed below, right now there's only one function, but\n> subsequent commits will add more.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  tree-walk.h | 8 +++++---\n>  1 file changed, 5 insertions(+), 3 deletions(-)\n>\n> diff --git a/tree-walk.h b/tree-walk.h\n> index 09e40d9221d..cd8da84f56c 100644\n> --- a/tree-walk.h\n> +++ b/tree-walk.h\n> @@ -166,11 +166,13 @@ struct traverse_info {\n>  };\n>  \n>  /**\n> - * Find an entry in a tree given a pathname and the sha1 of a tree to\n> + * Find an entry in a tree given a pathname and the OID of a tree to\n>   * search. Returns 0 if the entry is found and -1 otherwise.\n>   *\n> - * The third and fourth parameters are set to the entry's sha1 and\n> - * mode respectively.\n> + * You always need a pointer to an appropriate variable to fill in\n> + * (NULL won't do!). That variable is:\n> + *\n> + * get_tree_entry(): unsigned short mode\n\nThe last part after \"That variable is:\" makes no sense.  Sent before\ncompleting?\n\nThe function takes a repository, tree object name, a path in that\ntree as input, and find the entry in the tree at the given path.\nIts finding is returned in the fourth and fifth parameters as\n\n    struct object_id *oid\n    unsigned short *mode\n\nBy the way, I think somebody forgot to update the description while\ninserting the \"struct repository *\" as the first parameter and that\nis where the \"third and fourth\" in the original comes from.\n\n"},{"id":"420853","messageId":"xmqqzgyh20vn.fsf@gitster.g","threadId":"18927","inReplyTo":"patch-16.19-3ba77fd3a47-20210331T190531Z-avarab@gmail.com","subject":"Re: [PATCH v5 16/18] tree-walk.h API: add a get_tree_entry_path() function","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-01T20:41:48Z","receivedAt":"2021-04-01T20:41:50Z","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> -\t\tif (get_tree_entry_mode(r, hash2, del_prefix, shifted, &mode))\n> +\t\tif (get_tree_entry_path(r, hash2, del_prefix, shifted))\n>  \t\t\tdie(\"cannot find path %s in tree %s\",\n>  \t\t\t    del_prefix, oid_to_hex(hash2));\n\nThe observation that many \"get_tree_entry()\" users do not need the\nmode bits and the code can be simplified by taking advantage of\nthat fact is good.\n\nIt does not automatically follow that it is a good implementation of\nthat simplification to introduce a variant that does not take the\nmode pointer.  The original function could have just been taught to\naccept NULL in the field the caller is not interested in, as in many\nother functions do.\n\nBesides, get_tree_entry_mode() vs get_tree_entry_path() does not\nmake any sense as pair of functions with contrasting names.  If it\nwere that unlike the former that returns mode, the latter returns\npath instead, it would have made sense, but that is not what is\ngoing on.  The former returns object name and mode, the latter only\nreturns object name without mode.\n\nIn any case, at this point in the series, it is highly dubious that\nan extra function is an improvement.  Teaching get_tree_entry() (do\nnot even rename it) to take NULL in *mode pointer would make a lot\nmore sense.\n"},{"id":"420854","messageId":"xmqqv99520o1.fsf@gitster.g","threadId":"18927","inReplyTo":"patch-17.19-dc895822828-20210331T190531Z-avarab@gmail.com","subject":"Re: [PATCH v5 17/18] tree-walk.h API: document and format tree_entry_extract()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-01T20:46:22Z","receivedAt":"2021-04-01T20:46:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Document and format the argument list of the tree_entry_extract()\n> function in preparation for eventually adding a sister function.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  tree-walk.h | 14 ++++++++++----\n>  1 file changed, 10 insertions(+), 4 deletions(-)\n>\n> diff --git a/tree-walk.h b/tree-walk.h\n> index c60667cba8f..52eb0d6b5b3 100644\n> --- a/tree-walk.h\n> +++ b/tree-walk.h\n> @@ -38,11 +38,17 @@ struct tree_desc {\n>  \n>  /**\n>   * Decode the entry currently being visited (the one pointed to by\n> - * `tree_desc's` `entry` member) and return the sha1 of the entry. The\n> - * `pathp` and `modep` arguments are set to the entry's pathname and mode\n> - * respectively.\n> + * `tree_desc's` `entry` member) and return the OID of the entry.\n> + *\n> + * There are variants of this function depending on what fields in the\n> + * \"struct name_entry\" you'd like. You always need a pointer to an\n> + * appropriate variable to fill in (NULL won't do!):\n\nThere aren't.  At least not yet.  So if you want to build a series\nlike this that does many little things in each step, stop this step\nat updating SHA-1 to OID and do nothing else, until you introduce\ndifferent variant.\n\nWhat is missing in the current description that is more worth\ndescribing for those who are new to the codebase is probably the\nfact that this is meant to be used in pairs with update_tree_entry().\nYou call extract, which does not change the entry at all, and when\nyou are done, update knows (from the data kept in the entry) where\nthe next entry is in memory and the entry is updated to point there.\n\nThanks.\n\n> + *\n> + * tree_entry_extract(): const char *path, unsigned int mode\n>   */\n> -static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned short *modep)\n> +static inline const struct object_id *tree_entry_extract(struct tree_desc *desc,\n> +\t\t\t\t\t\t\t const char **pathp,\n> +\t\t\t\t\t\t\t unsigned short *modep)\n>  {\n>  \t*pathp = desc->entry.path;\n>  \t*modep = desc->entry.mode;\n"},{"id":"420855","messageId":"xmqqr1jt20i7.fsf@gitster.g","threadId":"18927","inReplyTo":"patch-18.19-e961a0e8b5b-20210331T190531Z-avarab@gmail.com","subject":"Re: [PATCH v5 18/18] tree-entry.h API: rename tree_entry_extract() to tree_entry_extract_mode()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-01T20:49:52Z","receivedAt":"2021-04-01T20:49:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> As with the recent split of the get_tree_entry() function, rename the\n> tree_entry_extract() function to *_mode() in preparation for adding\n> other variants of it.\n\nI do not see a value in this approach over optionally accepting\nNULL, for the same reason why splitting get_tree_entry() does not\nshow a value over optionally accepting NULL.  At least at this point\nin the series.\n\nThere might be future changes that would benefit from having\nseparate functions, but I do not see how they cannot work with an\nupdated version of existing helper functions that optionally can\ntake NULL as \"don't care\" when they can with a different function\nwith one fewer parameter.\n"},{"id":"420868","messageId":"87blaxkpg6.fsf@evledraar.gmail.com","threadId":"18927","inReplyTo":"xmqqwntm52ht.fsf@gitster.g","subject":"Re: [PATCH v5 08/18] blame: emit a better error on 'git blame directory'","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-02T09:26:01Z","receivedAt":"2021-04-02T09:26:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Apr 01 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Change an early check for non-blobs in verify_working_tree_path() to\n>> let any such objects pass, and instead die shortly thereafter in the\n>> fake_working_tree_commit() caller's type check.\n>>\n>> Now e.g. doing \"git blame t\" in git.git emits:\n>>\n>>     fatal: unsupported file type t\n>>\n>> Instead of:\n>>\n>>     fatal: no such path 't' in HEAD\n>\n> Sorry, but I fail to see why \"unsupported file type t\" is quite an\n> improvement.  Is this one of these irrelevant clean-up while at it\n> whose benefit is unclear until much later, I have to wonder.\n\nBecause \"t\" is directory we can stat() and which exists in the index, so\nit makes more sense to fall through to the stat() codepath.\n\nI think the \"unsupported file type\" message is a bit odd, but it's the\nexisting one, perhaps changing it while we're at it to something like:\n\n    fatal: cannot 'blame' a directory\n\nWould be better, but in any case I think saying \"no such path X in HEAD\"\nwhen you can \"git show HEAD:t\" to see that there is such a path doesn't\nmake sense.\n\nI don't have a test for it here, but this change also makes this error\nbetter:\n\n    rm -rf contrib\n    git blame contrib\n\nBefore we'd say:\n\n    fatal: no such path 'contrib' in HEAD\n\nBut now we'll fall back to:\n\n    fatal: Cannot lstat 'contrib': No such file or directory\n\nWhich could also be reworded, but aside from the specific wording I\nthink not aborting early when we see \"this is not a blob\" is better.\n\n>> The main point of this test is to assert that we're not doing\n>> something uniquely bad when in a conflicted merge. See\n>\n> \"this test\" refers to the logic \"it is OK to skip the check if one\n> of the parents does have it as a blob\", introduced in 9aeaab68\n> (blame: allow \"blame file\" in the middle of a conflicted merge,\n> 2012-09-11)?\n\nYes, will clarify.\n\n>> -\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode) &&\n>> -\t\t    oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)\n>> +\t\tif (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode))\n>>  \t\t\treturn;\n>>  \t}\n>\n> At least, the original logic makes sense to me in that if an early\n> parent has the path as a directory we do not declare it is OK but\n> keep going until we find a blob in a later parent before deciding to\n> short-cut.  I am not sure what the updated \"in this case we can\n> bypass the real check\" condition even means.  Mechanically, it says\n> \"if any parent has the path as any filesystem entity, even if it\n> were a directory, then it is OK\", but why?\n\nBecause we'll fall down to code that's better at doing the rest of that\ncheck.\n\nLooking at this again, another thing this changes is the behavior of\n--contents, which I again think is an improvement. Let's say you:\n\n    rm Makefile &&\n    mkdir Makefile &&\n    touch Makefile/foo &&\n    git add Makefile &&\n    git commit -m\"foo\"\n\nI.e. turn a random file into a directory, then:\n\n    git show origin/master:Makefile | git blame --contents - Makefile\n\nWe'll now say:\n\n    fatal: no such path 'Makefile' in HEAD\n\nWith my patch we'll do what the user asked (and I think consistently\nwith the documentation) and pretend as though the stream on stdin was\nthe contents at the \"Makefile\" path.\n\nThe blame we show doesn't make much sense, it's all the lines in the\nfile with \"Not Committed Yet\", but that's another matter to do with the\nblame algorithm in general, e.g. if you flip Makefile back & forth\nbetween a file->dir->file it won't traverse past the dir->file move.\n"},{"id":"420869","messageId":"878s61kpdp.fsf@evledraar.gmail.com","threadId":"18927","inReplyTo":"xmqq4kgp3fyh.fsf@gitster.g","subject":"Re: [PATCH v5 14/18] tree-walk.h API doc: improve documentation of get_tree_entry()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-02T09:27:30Z","receivedAt":"2021-04-02T09:27:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Apr 01 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Change a mention of sha1 to OID and change the comment to a listing of\n>> functions discussed below, right now there's only one function, but\n>> subsequent commits will add more.\n>>\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>  tree-walk.h | 8 +++++---\n>>  1 file changed, 5 insertions(+), 3 deletions(-)\n>>\n>> diff --git a/tree-walk.h b/tree-walk.h\n>> index 09e40d9221d..cd8da84f56c 100644\n>> --- a/tree-walk.h\n>> +++ b/tree-walk.h\n>> @@ -166,11 +166,13 @@ struct traverse_info {\n>>  };\n>>  \n>>  /**\n>> - * Find an entry in a tree given a pathname and the sha1 of a tree to\n>> + * Find an entry in a tree given a pathname and the OID of a tree to\n>>   * search. Returns 0 if the entry is found and -1 otherwise.\n>>   *\n>> - * The third and fourth parameters are set to the entry's sha1 and\n>> - * mode respectively.\n>> + * You always need a pointer to an appropriate variable to fill in\n>> + * (NULL won't do!). That variable is:\n>> + *\n>> + * get_tree_entry(): unsigned short mode\n>\n> The last part after \"That variable is:\" makes no sense.  Sent before\n> completing?\n\nIt's meant to refer to the \"unsigned short mode\", but I'll rephrase.\n\n> The function takes a repository, tree object name, a path in that\n> tree as input, and find the entry in the tree at the given path.\n> Its finding is returned in the fourth and fifth parameters as\n>\n>     struct object_id *oid\n>     unsigned short *mode\n>\n> By the way, I think somebody forgot to update the description while\n> inserting the \"struct repository *\" as the first parameter and that\n> is where the \"third and fourth\" in the original comes from.\n\nAlso as a fix for this...\n"},{"id":"420870","messageId":"875z15kor5.fsf@evledraar.gmail.com","threadId":"18927","inReplyTo":"xmqqzgyh20vn.fsf@gitster.g","subject":"Re: [PATCH v5 16/18] tree-walk.h API: add a get_tree_entry_path() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-02T09:41:02Z","receivedAt":"2021-04-02T09:41:07Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Apr 01 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> -\t\tif (get_tree_entry_mode(r, hash2, del_prefix, shifted, &mode))\n>> +\t\tif (get_tree_entry_path(r, hash2, del_prefix, shifted))\n>>  \t\t\tdie(\"cannot find path %s in tree %s\",\n>>  \t\t\t    del_prefix, oid_to_hex(hash2));\n>\n> The observation that many \"get_tree_entry()\" users do not need the\n> mode bits and the code can be simplified by taking advantage of\n> that fact is good.\n>\n> It does not automatically follow that it is a good implementation of\n> that simplification to introduce a variant that does not take the\n> mode pointer.  The original function could have just been taught to\n> accept NULL in the field the caller is not interested in, as in many\n> other functions do.\n>\n> Besides, get_tree_entry_mode() vs get_tree_entry_path() does not\n> make any sense as pair of functions with contrasting names.  If it\n> were that unlike the former that returns mode, the latter returns\n> path instead, it would have made sense, but that is not what is\n> going on.  The former returns object name and mode, the latter only\n> returns object name without mode.\n>\n> In any case, at this point in the series, it is highly dubious that\n> an extra function is an improvement.  Teaching get_tree_entry() (do\n> not even rename it) to take NULL in *mode pointer would make a lot\n> more sense.\n\nThanks. Yes that makes a lot more sense.\n\nThis whole path of introducing multiple functions is an oversight of\nmine. I did it because in 7146e66f086 (tree-walk: finally switch over\ntree descriptors to contain a pre-parsed entry, 2014-02-06) we removed\ncanon_mode() from the inline function to make it trivially inline-able.\n\nSo I assumed without testing that introducing conditionals in that\nfunction would matter to a compiler, and wanted to not introduce any\nperformance regressions (however small) in this series.\n\nBut looking at the generated *.s code under GCC -O3 it makes no\ndifference if you've got an \"if (ptr) *ptr = xyz\" there. The compiler\nsees that and produces the same machine code.\n\nSo I'll re-roll this to do as you suggested, thanks.\n"},{"id":"420895","messageId":"xmqq5z144cop.fsf@gitster.g","threadId":"18927","inReplyTo":"87blaxkpg6.fsf@evledraar.gmail.com","subject":"Re: [PATCH v5 08/18] blame: emit a better error on 'git blame directory'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-02T21:08:22Z","receivedAt":"2021-04-02T21:08:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> On Thu, Apr 01 2021, Junio C Hamano wrote:\n>\n>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>>\n>>> Change an early check for non-blobs in verify_working_tree_path() to\n>>> let any such objects pass, and instead die shortly thereafter in the\n>>> fake_working_tree_commit() caller's type check.\n>>>\n>>> Now e.g. doing \"git blame t\" in git.git emits:\n>>>\n>>>     fatal: unsupported file type t\n>>>\n>>> Instead of:\n>>>\n>>>     fatal: no such path 't' in HEAD\n>>\n>> Sorry, but I fail to see why \"unsupported file type t\" is quite an\n>> improvement.  Is this one of these irrelevant clean-up while at it\n>> whose benefit is unclear until much later, I have to wonder.\n>\n> Because \"t\" is directory we can stat() and which exists in the index, so\n> it makes more sense to fall through to the stat() codepath.\n\n't' is not in the index, even though many things with 't/' prefix\nmay.\n\nBut my understanding of the point of that loop is to catch cases\nwhere one side may have 't' as a directory, the other side may have\nit as a blob, and we have a 't' regular file during a process of\nresolving a conflicted merge (perhaps the tentative resolution has\nalready been \"git add\"ed to the index).  So \"git blame t\" would want\nto start from the \"working tree state\", which is made into a virtual\ncommit, with two \"virtual\" parents, one is HEAD that has 't' as a\ndirectory but the other MERGE_HEAD may have 't' as a regular file,\nso \"blame\" should be able to follow the history of that regular file\nthrough the merge in progress.\n\nIf we change, like the proposed patch does, the loop to exit\nimmediately after we notice 't' exists in HEAD (as a tree), without\neven looking at the second parent to notice that it is a regular\nfile there, wouldn't that change the behaviour?\n\n> I think the \"unsupported file type\" message is a bit odd, but it's the\n> existing one, perhaps changing it while we're at it to something like:\n>\n>     fatal: cannot 'blame' a directory\n>\n> Would be better,...\n\nSure.  As long as the change does not break the original use case\nthe loop intended to support, that is fine, and the message would be\na lot better than the \"unsupported file type t\".\n"}]}