git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 11/17] mktree: overwrite duplicate entries

From
Victoria Dye via GitGitGadget <gitgitgadget@gmail.com>
Date
Jun 19, 2024, 21:57 UTC
Message-ID
<fb555658057f834d94f232f1d8b380a6304a3671.1718834285.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.1746.v2.git.1718834285.gitgitgadget@gmail.com>
From: Victoria Dye <vdye@github.com>

If multiple tree entries with the same name are provided as input to 'mktree', only write the last one to the tree. Entries are considered duplicates if they have identical names (*not* considering mode); if a blob and a tree with the same name are provided, only the last one will be written to the tree. A tree with duplicate entries is invalid (per 'git fsck'), so that condition should be avoided wherever possible.

Signed-off-by: Victoria Dye <vdye@github.com>
---
 Documentation/git-mktree.txt |  3 ++-
 builtin/mktree.c             | 45 ++++++++++++++++++++++++++++++++----
 t/t1010-mktree.sh            | 36 +++++++++++++++++++++++++++--
 3 files changed, 77 insertions(+), 7 deletions(-)
diff --git a/Documentation/git-mktree.txt b/Documentation/git-mktree.txt
index 5f3a6dfe38e..cf1fd82f754 100644
--- a/Documentation/git-mktree.txt
+++ b/Documentation/git-mktree.txt
@@ -54,7 +54,8 @@ cannot be represented in a tree object. The command will fail without
 writing the tree if a higher order stage is specified for any entry.
 
 The order of the tree entries is normalized by `mktree` so pre-sorting the
-input by path is not required.
+input by path is not required. Multiple entries provided with the same path
+are deduplicated, with only the last one specified added to the tree.
 
 GIT
 ---
diff --git a/builtin/mktree.c b/builtin/mktree.c
index 8f0af24b6b1..a91d3a7b028 100644
--- a/builtin/mktree.c
+++ b/builtin/mktree.c
@@ -15,6 +15,9 @@
 #include "object-store-ll.h"
 
 struct tree_entry {
+	/* Internal */
+	size_t order;
+
 	unsigned mode;
 	struct object_id oid;
 	int len;
@@ -74,15 +77,49 @@ static void append_to_tree(unsigned mode, struct object_id *oid, const char *pat
 	ent->len = len;
 	oidcpy(&ent->oid, oid);
 
+	ent->order = arr->nr;
 	tree_entry_array_push(arr, ent);
 }
 
-static int ent_compare(const void *a_, const void *b_)
+static int ent_compare(const void *a_, const void *b_, void *ctx)
 {
+	int cmp;
 	struct tree_entry *a = *(struct tree_entry **)a_;
 	struct tree_entry *b = *(struct tree_entry **)b_;
-	return base_name_compare(a->name, a->len, a->mode,
-				 b->name, b->len, b->mode);
+	int ignore_mode = *((int *)ctx);
+
+	if (ignore_mode)
+		cmp = name_compare(a->name, a->len, b->name, b->len);
+	else
+		cmp = base_name_compare(a->name, a->len, a->mode,
+					b->name, b->len, b->mode);
+	return cmp ? cmp : b->order - a->order;
+}
+
+static void sort_and_dedup_tree_entry_array(struct tree_entry_array *arr)
+{
+	size_t count = arr->nr;
+	struct tree_entry *prev = NULL;
+
+	int ignore_mode = 1;
+	QSORT_S(arr->entries, arr->nr, ent_compare, &ignore_mode);
+
+	arr->nr = 0;
+	for (size_t i = 0; i < count; i++) {
+		struct tree_entry *curr = arr->entries[i];
+		if (prev &&
+		    !name_compare(prev->name, prev->len,
+				  curr->name, curr->len)) {
+			FREE_AND_NULL(curr);
+		} else {
+			arr->entries[arr->nr++] = curr;
+			prev = curr;
+		}
+	}
+
+	/* Sort again to order the entries for tree insertion */
+	ignore_mode = 0;
+	QSORT_S(arr->entries, arr->nr, ent_compare, &ignore_mode);
 }
 
 static void write_tree(struct tree_entry_array *arr, struct object_id *oid)
@@ -90,7 +127,7 @@ static void write_tree(struct tree_entry_array *arr, struct object_id *oid)
 	struct strbuf buf;
 	size_t size = 0;
 
-	QSORT(arr->entries, arr->nr, ent_compare);
+	sort_and_dedup_tree_entry_array(arr);
 	for (size_t i = 0; i < arr->nr; i++)
 		size += 32 + arr->entries[i]->len;
 
diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh
index 7e750530455..08760141d6f 100755
--- a/t/t1010-mktree.sh
+++ b/t/t1010-mktree.sh
@@ -6,11 +6,16 @@ TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
-	for d in a a- a0
+	for d in folder folder- folder0
 	do
 		mkdir "$d" && echo "$d/one" >"$d/one" &&
 		git add "$d" || return 1
 	done &&
+	for f in before folder.txt later
+	do
+		echo "$f" >"$f" &&
+		git add "$f" || return 1
+	done &&
 	echo zero >one &&
 	git update-index --add --info-only one &&
 	git write-tree --missing-ok >tree.missing &&
@@ -171,7 +176,7 @@ test_expect_success '--literally can create invalid trees' '
 
 test_expect_success 'mktree validates path' '
 	tree_oid="$(cat tree)" &&
-	blob_oid="$(git rev-parse $tree_oid:a/one)" &&
+	blob_oid="$(git rev-parse $tree_oid:folder.txt)" &&
 	head_oid="$(git rev-parse HEAD)" &&
 
 	# Valid: tree with or without trailing slash, blob without trailing slash
@@ -202,4 +207,31 @@ test_expect_success 'mktree validates path' '
 	test_grep "invalid path ${SQ}.git/${SQ}" err
 '
 
+test_expect_success 'mktree with duplicate entries' '
+	tree_oid=$(cat tree) &&
+	folder_oid=$(git rev-parse ${tree_oid}:folder) &&
+	before_oid=$(git rev-parse ${tree_oid}:before) &&
+	head_oid=$(git rev-parse HEAD) &&
+
+	{
+		printf "100755 blob $before_oid\ttest\n" &&
+		printf "040000 tree $folder_oid\ttest-\n" &&
+		printf "160000 commit $head_oid\ttest.txt\n" &&
+		printf "040000 tree $folder_oid\ttest\n" &&
+		printf "100644 blob $before_oid\ttest0\n" &&
+		printf "160000 commit $head_oid\ttest-\n"
+	} >top.dup &&
+	git mktree <top.dup >tree.actual &&
+
+	{
+		printf "160000 commit $head_oid\ttest-\n" &&
+		printf "160000 commit $head_oid\ttest.txt\n" &&
+		printf "040000 tree $folder_oid\ttest\n" &&
+		printf "100644 blob $before_oid\ttest0\n"
+	} >expect &&
+	git ls-tree $(cat tree.actual) >actual &&
+
+	test_cmp expect actual
+'
+
 test_done
-- 
gitgitgadget
Previous: Victoria Dye via GitGitGadgetNext: Junio C Hamano
Message 52 of 65 in “mktree: support more flexible usage”
  1. 00/16 mktree: support more flexible usageVictoria Dye via GitGitGadget, Jun 11, 2024
  2. 01/16 mktree: use OPT_BOOLVictoria Dye via GitGitGadget, Jun 11, 2024
  3. 02/16 mktree: rename treeent to tree_entryVictoria Dye via GitGitGadget, Jun 11, 2024
  4. Patrick SteinhardtJun 12, 2024
  5. 03/16 mktree: use non-static tree_entry arrayVictoria Dye via GitGitGadget, Jun 11, 2024
  6. Eric SunshineJun 11, 2024
  7. Patrick SteinhardtJun 12, 2024
  8. 04/16 update-index: generalize 'read_index_info'Victoria Dye via GitGitGadget, Jun 11, 2024
  9. Junio C HamanoJun 11, 2024
  10. 06/16 index-info.c: parse object type in provided in read_index_infoVictoria Dye via GitGitGadget, Jun 11, 2024
  11. Junio C HamanoJun 12, 2024
  12. 05/16 index-info.c: identify empty input lines in read_index_infoVictoria Dye via GitGitGadget, Jun 11, 2024
  13. Junio C HamanoJun 11, 2024
  14. Victoria DyeJun 18, 2024
  15. 07/16 mktree: use read_index_info to read stdin linesVictoria Dye via GitGitGadget, Jun 11, 2024
  16. Junio C HamanoJun 12, 2024
  17. Patrick SteinhardtJun 12, 2024
  18. Junio C HamanoJun 12, 2024
  19. 08/16 mktree: add a --literally optionVictoria Dye via GitGitGadget, Jun 11, 2024
  20. Junio C HamanoJun 12, 2024
  21. 09/16 mktree: validate paths more carefullyVictoria Dye via GitGitGadget, Jun 11, 2024
  22. Junio C HamanoJun 12, 2024
  23. Victoria DyeJun 12, 2024
  24. Junio C HamanoJun 12, 2024
  25. 10/16 mktree: overwrite duplicate entriesVictoria Dye via GitGitGadget, Jun 11, 2024
  26. Patrick SteinhardtJun 12, 2024
  27. Victoria DyeJun 12, 2024
  28. 11/16 mktree: create tree using an in-core indexVictoria Dye via GitGitGadget, Jun 11, 2024
  29. Patrick SteinhardtJun 12, 2024
  30. 12/16 mktree: use iterator struct to add tree entries to indexVictoria Dye via GitGitGadget, Jun 11, 2024
  31. Patrick SteinhardtJun 12, 2024
  32. Victoria DyeJun 13, 2024
  33. 13/16 mktree: add directory-file conflict hashmapVictoria Dye via GitGitGadget, Jun 11, 2024
  34. 14/16 mktree: optionally add to an existing treeVictoria Dye via GitGitGadget, Jun 11, 2024
  35. Patrick SteinhardtJun 12, 2024
  36. Junio C HamanoJun 12, 2024
  37. Victoria DyeJun 17, 2024
  38. 15/16 mktree: allow deeper paths in inputVictoria Dye via GitGitGadget, Jun 11, 2024
  39. 16/16 mktree: remove entries when mode is 0Victoria Dye via GitGitGadget, Jun 11, 2024
  40. 00/17 mktree: support more flexible usageVictoria Dye via GitGitGadget, Jun 19, 2024
  41. 01/17 mktree: use OPT_BOOLVictoria Dye via GitGitGadget, Jun 19, 2024
  42. 02/17 mktree: rename treeent to tree_entryVictoria Dye via GitGitGadget, Jun 19, 2024
  43. 03/17 mktree: use non-static tree_entry arrayVictoria Dye via GitGitGadget, Jun 19, 2024
  44. 04/17 update-index: generalize 'read_index_info'Victoria Dye via GitGitGadget, Jun 19, 2024
  45. 05/17 index-info.c: return unrecognized lines to callerVictoria Dye via GitGitGadget, Jun 19, 2024
  46. 06/17 index-info.c: parse object type in provided in read_index_infoVictoria Dye via GitGitGadget, Jun 19, 2024
  47. 08/17 mktree.c: do not fail on mismatched submodule typeVictoria Dye via GitGitGadget, Jun 19, 2024
  48. 07/17 mktree: use read_index_info to read stdin linesVictoria Dye via GitGitGadget, Jun 19, 2024
  49. Junio C HamanoJun 20, 2024
  50. 09/17 mktree: add a --literally optionVictoria Dye via GitGitGadget, Jun 19, 2024
  51. 10/17 mktree: validate paths more carefullyVictoria Dye via GitGitGadget, Jun 19, 2024
  52. 11/17 mktree: overwrite duplicate entriesVictoria Dye via GitGitGadget, Jun 19, 2024
  53. Junio C HamanoJun 20, 2024
  54. 12/17 mktree: create tree using an in-core indexVictoria Dye via GitGitGadget, Jun 19, 2024
  55. Junio C HamanoJun 20, 2024
  56. 13/17 mktree: use iterator struct to add tree entries to indexVictoria Dye via GitGitGadget, Jun 19, 2024
  57. Junio C HamanoJun 26, 2024
  58. 14/17 mktree: add directory-file conflict hashmapVictoria Dye via GitGitGadget, Jun 19, 2024
  59. 15/17 mktree: optionally add to an existing treeVictoria Dye via GitGitGadget, Jun 19, 2024
  60. Junio C HamanoJun 26, 2024
  61. 16/17 mktree: allow deeper paths in inputVictoria Dye via GitGitGadget, Jun 19, 2024
  62. Junio C HamanoJun 27, 2024
  63. 17/17 mktree: remove entries when mode is 0Victoria Dye via GitGitGadget, Jun 19, 2024
  64. Junio C HamanoJun 25, 2024
  65. Junio C HamanoJul 10, 2024

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.