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

[PATCH 16/17] pack-objects: refactor path-walk delta phase

From
Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
Date
Oct 8, 2024, 14:12 UTC
Message-ID
<c3f24dc364793850d32b8b6d867b9ff2f9a6817d.1728396724.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.1813.git.1728396723.gitgitgadget@gmail.com>
From: Derrick Stolee <stolee@gmail.com>

Previously, the --path-walk option to 'git pack-objects' would compute deltas inline with the path-walk logic. This would make the progress indicator look like it is taking a long time to enumerate objects, and then very quickly computed deltas.

Instead of computing deltas on each region of objects organized by tree, store a list of regions corresponding to these groups. These can later be pulled from the list for delta compression before doing the "global" delta search.

This presents a new progress indicator that can be used in tests to verify that this stage is happening.

The current implementation is not integrated with threads, but could be done in a future update.

Since we do not attempt to sort objects by size until after exploring all trees, we can remove the previous change to t5530 due to a different error message appearing first.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
---
 builtin/pack-objects.c       | 81 +++++++++++++++++++++++++-----------
 pack-objects.h               | 12 ++++++
 t/t5300-pack-object.sh       |  8 +++-
 t/t5530-upload-pack-error.sh |  6 ---
 4 files changed, 74 insertions(+), 33 deletions(-)
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 6805a55c60d..5c413ac07e6 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -3204,6 +3204,50 @@ static int should_attempt_deltas(struct object_entry *entry)
 	return 1;
 }
 
+static void find_deltas_for_region(struct object_entry *list UNUSED,
+				   struct packing_region *region,
+				   unsigned int *processed)
+{
+	struct object_entry **delta_list;
+	uint32_t delta_list_nr = 0;
+
+	ALLOC_ARRAY(delta_list, region->nr);
+	for (uint32_t i = 0; i < region->nr; i++) {
+		struct object_entry *entry = to_pack.objects + region->start + i;
+		if (should_attempt_deltas(entry))
+			delta_list[delta_list_nr++] = entry;
+	}
+
+	QSORT(delta_list, delta_list_nr, type_size_sort);
+	find_deltas(delta_list, &delta_list_nr, window, depth, processed);
+	free(delta_list);
+}
+
+static void find_deltas_by_region(struct object_entry *list,
+				  struct packing_region *regions,
+				  uint32_t start, uint32_t nr)
+{
+	unsigned int processed = 0;
+	uint32_t progress_nr;
+
+	if (!nr)
+		return;
+
+	progress_nr = regions[nr - 1].start + regions[nr - 1].nr;
+
+	if (progress)
+		progress_state = start_progress(_("Compressing objects by path"),
+						progress_nr);
+
+	while (nr--)
+		find_deltas_for_region(list,
+				       &regions[start++],
+				       &processed);
+
+	display_progress(progress_state, progress_nr);
+	stop_progress(&progress_state);
+}
+
 static void prepare_pack(int window, int depth)
 {
 	struct object_entry **delta_list;
@@ -3228,6 +3272,10 @@ static void prepare_pack(int window, int depth)
 	if (!to_pack.nr_objects || !window || !depth)
 		return;
 
+	if (path_walk)
+		find_deltas_by_region(to_pack.objects, to_pack.regions,
+				      0, to_pack.nr_regions);
+
 	ALLOC_ARRAY(delta_list, to_pack.nr_objects);
 	nr_deltas = n = 0;
 
@@ -4165,10 +4213,8 @@ static int add_objects_by_path(const char *path,
 			       enum object_type type,
 			       void *data)
 {
-	struct object_entry **delta_list;
 	size_t oe_start = to_pack.nr_objects;
 	size_t oe_end;
-	unsigned int sub_list_size;
 	unsigned int *processed = data;
 
 	/*
@@ -4201,32 +4247,17 @@ static int add_objects_by_path(const char *path,
 	if (oe_end == oe_start || !window)
 		return 0;
 
-	sub_list_size = 0;
-	ALLOC_ARRAY(delta_list, oe_end - oe_start);
+	ALLOC_GROW(to_pack.regions,
+		   to_pack.nr_regions + 1,
+		   to_pack.nr_regions_alloc);
 
-	for (size_t i = 0; i < oe_end - oe_start; i++) {
-		struct object_entry *entry = to_pack.objects + oe_start + i;
+	to_pack.regions[to_pack.nr_regions].start = oe_start;
+	to_pack.regions[to_pack.nr_regions].nr = oe_end - oe_start;
+	to_pack.nr_regions++;
 
-		if (!should_attempt_deltas(entry))
-			continue;
+	*processed += oids->nr;
+	display_progress(progress_state, *processed);
 
-		delta_list[sub_list_size++] = entry;
-	}
-
-	/*
-	 * Find delta bases among this list of objects that all match the same
-	 * path. This causes the delta compression to be interleaved in the
-	 * object walk, which can lead to confusing progress indicators. This is
-	 * also incompatible with threaded delta calculations. In the future,
-	 * consider creating a list of regions in the full to_pack.objects array
-	 * that could be picked up by the threaded delta computation.
-	 */
-	if (sub_list_size && window) {
-		QSORT(delta_list, sub_list_size, type_size_sort);
-		find_deltas(delta_list, &sub_list_size, window, depth, processed);
-	}
-
-	free(delta_list);
 	return 0;
 }
 
diff --git a/pack-objects.h b/pack-objects.h
index b9898a4e64b..bde4ba19755 100644
--- a/pack-objects.h
+++ b/pack-objects.h
@@ -118,11 +118,23 @@ struct object_entry {
 	unsigned ext_base:1; /* delta_idx points outside packlist */
 };
 
+/**
+ * A packing region is a section of the packing_data.objects array
+ * as given by a starting index and a number of elements.
+ */
+struct packing_region {
+	uint32_t start;
+	uint32_t nr;
+};
+
 struct packing_data {
 	struct repository *repo;
 	struct object_entry *objects;
 	uint32_t nr_objects, nr_alloc;
 
+	struct packing_region *regions;
+	uint32_t nr_regions, nr_regions_alloc;
+
 	int32_t *index;
 	uint32_t index_size;
 
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 5f6914acae7..4f81613eab1 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -677,7 +677,9 @@ done
 # Basic "repack everything" test
 test_expect_success '--path-walk pack everything' '
 	git -C server rev-parse HEAD >in &&
-	git -C server pack-objects --stdout --revs --path-walk <in >out.pack &&
+	GIT_PROGRESS_DELAY=0 git -C server pack-objects \
+		--stdout --revs --path-walk --progress <in >out.pack 2>err &&
+	grep "Compressing objects by path" err &&
 	git -C server index-pack --stdin <out.pack
 '
 
@@ -687,7 +689,9 @@ test_expect_success '--path-walk thin pack' '
 	$(git -C server rev-parse HEAD)
 	^$(git -C server rev-parse HEAD~2)
 	EOF
-	git -C server pack-objects --thin --stdout --revs --path-walk <in >out.pack &&
+	GIT_PROGRESS_DELAY=0 git -C server pack-objects \
+		--thin --stdout --revs --path-walk --progress <in >out.pack 2>err &&
+	grep "Compressing objects by path" err &&
 	git -C server index-pack --fix-thin --stdin <out.pack
 '
 
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
index 356b96cb741..7172780d550 100755
--- a/t/t5530-upload-pack-error.sh
+++ b/t/t5530-upload-pack-error.sh
@@ -35,12 +35,6 @@ test_expect_success 'upload-pack fails due to error in pack-objects packing' '
 	hexsz=$(test_oid hexsz) &&
 	printf "%04xwant %s\n00000009done\n0000" \
 		$(($hexsz + 10)) $head >input &&
-
-	# The current implementation of path-walk causes a different
-	# error message. This will be changed by a future refactoring.
-	GIT_TEST_PACK_PATH_WALK=0 &&
-	export GIT_TEST_PACK_PATH_WALK &&
-
 	test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
 	test_grep "unable to read" output.err &&
 	test_grep "pack-objects died" output.err
-- 
gitgitgadget
Previous: Derrick Stolee via GitGitGadgetNext: Derrick Stolee via GitGitGadget
Message 25 of 55 in “pack-objects: add --path-walk option for better deltas”
  1. 00/17 pack-objects: add --path-walk option for better deltasDerrick Stolee via GitGitGadget, Oct 8, 2024
  2. 01/17 path-walk: introduce an object walk by pathDerrick Stolee via GitGitGadget, Oct 8, 2024
  3. 02/17 t6601: add helper for testing path-walk APIDerrick Stolee via GitGitGadget, Oct 8, 2024
  4. 03/17 path-walk: allow consumer to specify object typesDerrick Stolee via GitGitGadget, Oct 8, 2024
  5. 04/17 path-walk: allow visiting tagsDerrick Stolee via GitGitGadget, Oct 8, 2024
  6. 05/17 revision: create mark_trees_uninteresting_dense()Derrick Stolee via GitGitGadget, Oct 8, 2024
  7. 06/17 path-walk: add prune_all_uninteresting optionDerrick Stolee via GitGitGadget, Oct 8, 2024
  8. 07/17 pack-objects: extract should_attempt_deltas()Derrick Stolee via GitGitGadget, Oct 8, 2024
  9. 08/17 pack-objects: add --path-walk optionDerrick Stolee via GitGitGadget, Oct 8, 2024
  10. Jonathan TanOct 28, 2024
  11. Taylor BlauOct 29, 2024
  12. Jonathan TanOct 29, 2024
  13. Taylor BlauOct 29, 2024
  14. Derrick StoleeOct 31, 2024
  15. Derrick StoleeOct 31, 2024
  16. Taylor BlauOct 31, 2024
  17. Derrick StoleeOct 31, 2024
  18. 09/17 pack-objects: update usage to match docsDerrick Stolee via GitGitGadget, Oct 8, 2024
  19. 10/17 p5313: add performance tests for --path-walkDerrick Stolee via GitGitGadget, Oct 8, 2024
  20. 11/17 pack-objects: introduce GIT_TEST_PACK_PATH_WALKDerrick Stolee via GitGitGadget, Oct 8, 2024
  21. 12/17 repack: add --path-walk optionDerrick Stolee via GitGitGadget, Oct 8, 2024
  22. 13/17 repack: update usage to match docsDerrick Stolee via GitGitGadget, Oct 8, 2024
  23. 14/17 pack-objects: enable --path-walk via configDerrick Stolee via GitGitGadget, Oct 8, 2024
  24. 15/17 scalar: enable path-walk during push via configDerrick Stolee via GitGitGadget, Oct 8, 2024
  25. 16/17 pack-objects: refactor path-walk delta phaseDerrick Stolee via GitGitGadget, Oct 8, 2024
  26. 17/17 pack-objects: thread the path-based compressionDerrick Stolee via GitGitGadget, Oct 8, 2024
  27. 00/17 pack-objects: add --path-walk option for better deltasDerrick Stolee via GitGitGadget, Oct 20, 2024
  28. 01/17 path-walk: introduce an object walk by pathDerrick Stolee via GitGitGadget, Oct 20, 2024
  29. 02/17 t6601: add helper for testing path-walk APIDerrick Stolee via GitGitGadget, Oct 20, 2024
  30. 03/17 path-walk: allow consumer to specify object typesDerrick Stolee via GitGitGadget, Oct 20, 2024
  31. 04/17 path-walk: allow visiting tagsDerrick Stolee via GitGitGadget, Oct 20, 2024
  32. 05/17 revision: create mark_trees_uninteresting_dense()Derrick Stolee via GitGitGadget, Oct 20, 2024
  33. 06/17 path-walk: add prune_all_uninteresting optionDerrick Stolee via GitGitGadget, Oct 20, 2024
  34. 07/17 pack-objects: extract should_attempt_deltas()Derrick Stolee via GitGitGadget, Oct 20, 2024
  35. 09/17 pack-objects: update usage to match docsDerrick Stolee via GitGitGadget, Oct 20, 2024
  36. 08/17 pack-objects: add --path-walk optionDerrick Stolee via GitGitGadget, Oct 20, 2024
  37. 10/17 p5313: add performance tests for --path-walkDerrick Stolee via GitGitGadget, Oct 20, 2024
  38. 11/17 pack-objects: introduce GIT_TEST_PACK_PATH_WALKDerrick Stolee via GitGitGadget, Oct 20, 2024
  39. 12/17 repack: add --path-walk optionDerrick Stolee via GitGitGadget, Oct 20, 2024
  40. 13/17 repack: update usage to match docsDerrick Stolee via GitGitGadget, Oct 20, 2024
  41. 14/17 pack-objects: enable --path-walk via configDerrick Stolee via GitGitGadget, Oct 20, 2024
  42. 15/17 scalar: enable path-walk during push via configDerrick Stolee via GitGitGadget, Oct 20, 2024
  43. 16/17 pack-objects: refactor path-walk delta phaseDerrick Stolee via GitGitGadget, Oct 20, 2024
  44. 17/17 pack-objects: thread the path-based compressionDerrick Stolee via GitGitGadget, Oct 20, 2024
  45. Taylor BlauOct 21, 2024
  46. Derrick StoleeOct 24, 2024
  47. Taylor BlauOct 24, 2024
  48. Patrick SteinhardtOct 28, 2024
  49. Taylor BlauOct 28, 2024
  50. Derrick StoleeOct 28, 2024
  51. Taylor BlauOct 28, 2024
  52. Derrick StoleeOct 28, 2024
  53. Taylor BlauOct 29, 2024
  54. Derrick StoleeOct 31, 2024
  55. Taylor BlauOct 31, 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.