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

[PATCH v2 3/5] backfill: add --min-batch-size=<n> option

From
Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
Date
Dec 20, 2024, 16:29 UTC
Message-ID
<3fa32822dabb64266a0625c50530ae2ae0797de6.1734712193.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.1820.v2.git.1734712193.gitgitgadget@gmail.com>
From: Derrick Stolee <derrickstolee@github.com>

Users may want to specify a minimum batch size for their needs. This is only a minimum: the path-walk API provides a list of OIDs that correspond to the same path, and thus it is optimal to allow delta compression across those objects in a single server request.

We could consider limiting the request to have a maximum batch size in the future. For now, we let the path-walk API batches determine the boundaries.

To get a feeling for the value of specifying the --batch-size parameter, I tested a number of open source repositories available on GitHub. The procedure was generally:

 1. git clone --filter=blob:none <url>
 2. git backfill

Checking the number of packfiles and the size of the .git/objects/pack directory helps to identify the effects of different batch sizes.

For the Git repository, we get these results:
| Batch Size      | Pack Count | Pack Size | Time  |
|-----------------|------------|-----------|-------|
| (Initial clone) | 2          | 119 MB    |       |
| 25K             | 8          | 290 MB    | 24s   |
| 50K             | 5          | 290 MB    | 24s   |
| 100K            | 4          | 290 MB    | 29s   |

Other than the packfile counts decreasing as we need fewer batches, the size and time required is not changing much for this small example.

For the nodejs/node repository, we see these results:
| Batch Size      | Pack Count | Pack Size | Time   |
|-----------------|------------|-----------|--------|
| (Initial clone) | 2          | 330 MB    |        |
| 25K             | 19         | 1,222 MB  | 1m 22s |
| 50K             | 11         | 1,221 MB  | 1m 24s |
| 100K            | 7          | 1,223 MB  | 1m 40s |
| 250K            | 4          | 1,224 MB  | 2m 23s |
| 500K            | 3          | 1,216 MB  | 4m 38s |

Here, we don't have much difference in the size of the repo, though the 500K batch size results in a few MB gained. That comes at a cost of a much longer time. This extra time is due to server-side delta compression happening as the on-disk deltas don't appear to be reusable all the time. But for smaller batch sizes, the server is able to find reasonable deltas partly because we are asking for objects that appear in the same region of the directory tree and include all versions of a file at a specific path.

To contrast this example, I tested the microsoft/fluentui repo, which has been known to have inefficient packing due to name hash collisions. These results are found before GitHub had the opportunity to repack the server with more advanced name hash versions:

| Batch Size      | Pack Count | Pack Size | Time   |
|-----------------|------------|-----------|--------|
| (Initial clone) | 2          | 105 MB    |        |
| 5K              | 53         | 348 MB    | 2m 26s |
| 10K             | 28         | 365 MB    | 2m 22s |
| 15K             | 19         | 407 MB    | 2m 21s |
| 20K             | 15         | 393 MB    | 2m 28s |
| 25K             | 13         | 417 MB    | 2m 06s |
| 50K             | 8          | 509 MB    | 1m 34s |
| 100K            | 5          | 535 MB    | 1m 56s |
| 250K            | 4          | 698 MB    | 1m 33s |
| 500K            | 3          | 696 MB    | 1m 42s |

Here, a larger variety of batch sizes were chosen because of the great variation in results. By asking the server to download small batches corresponding to fewer paths at a time, the server is able to provide better compression for these batches than it would for a regular clone. A typical full clone for this repository would require 738 MB.

This example justifies the choice to batch requests by path name, leading to improved communication with a server that is not optimally packed.

Finally, the same experiment for the Linux repository had these results:
| Batch Size      | Pack Count | Pack Size | Time    |
|-----------------|------------|-----------|---------|
| (Initial clone) | 2          | 2,153 MB  |         |
| 25K             | 63         | 6,380 MB  | 14m 08s |
| 50K             | 58         | 6,126 MB  | 15m 11s |
| 100K            | 30         | 6,135 MB  | 18m 11s |
| 250K            | 14         | 6,146 MB  | 18m 22s |
| 500K            | 8          | 6,143 MB  | 33m 29s |

Even in this example, where the default name hash algorithm leads to decent compression of the Linux kernel repository, there is value for selecting a smaller batch size, to a limit. The 25K batch size has the fastest time, but uses 250 MB more than the 50K batch size. The 500K batch size took much more time due to server compression time and thus we should avoid large batch sizes like this.

Based on these experiments, a batch size of 50,000 was chosen as the default value.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
---
 Documentation/git-backfill.txt | 11 ++++++++++-
 builtin/backfill.c             | 10 ++++++----
 t/t5620-backfill.sh            | 18 ++++++++++++++++++
 3 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-backfill.txt b/Documentation/git-backfill.txt
index ece887831f6..e392517869c 100644
--- a/Documentation/git-backfill.txt
+++ b/Documentation/git-backfill.txt
@@ -9,7 +9,7 @@ git-backfill - Download missing objects in a partial clone
 SYNOPSIS
 --------
 [verse]
-'git backfill' [<options>]
+'git backfill' [--batch-size=<n>]
 
 DESCRIPTION
 -----------
@@ -45,6 +45,15 @@ time.
 By default, `git backfill` downloads all blobs reachable from the `HEAD`
 commit. This set can be restricted or expanded using various options.
 
+OPTIONS
+-------
+
+--min-batch-size=<n>::
+	Specify a minimum size for a batch of missing objects to request
+	from the server. This size may be exceeded by the last set of
+	blobs seen at a given path. The default minimum batch size is
+	50,000.
+
 SEE ALSO
 --------
 linkgit:git-clone[1].
diff --git a/builtin/backfill.c b/builtin/backfill.c
index 177fd4286c7..ddccececc36 100644
--- a/builtin/backfill.c
+++ b/builtin/backfill.c
@@ -21,14 +21,14 @@
 #include "path-walk.h"
 
 static const char * const builtin_backfill_usage[] = {
-	N_("git backfill [<options>]"),
+	N_("git backfill [--batch-size=<n>]"),
 	NULL
 };
 
 struct backfill_context {
 	struct repository *repo;
 	struct oid_array current_batch;
-	size_t batch_size;
+	size_t min_batch_size;
 };
 
 static void backfill_context_clear(struct backfill_context *ctx)
@@ -72,7 +72,7 @@ static int fill_missing_blobs(const char *path UNUSED,
 			oid_array_append(&ctx->current_batch, &list->oid[i]);
 	}
 
-	if (ctx->current_batch.nr >= ctx->batch_size)
+	if (ctx->current_batch.nr >= ctx->min_batch_size)
 		download_batch(ctx);
 
 	return 0;
@@ -111,9 +111,11 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
 	struct backfill_context ctx = {
 		.repo = repo,
 		.current_batch = OID_ARRAY_INIT,
-		.batch_size = 50000,
+		.min_batch_size = 50000,
 	};
 	struct option options[] = {
+		OPT_INTEGER(0, "min-batch-size", &ctx.min_batch_size,
+			    N_("Minimum number of objects to request at a time")),
 		OPT_END(),
 	};
 
diff --git a/t/t5620-backfill.sh b/t/t5620-backfill.sh
index 64326362d80..36107a51c54 100755
--- a/t/t5620-backfill.sh
+++ b/t/t5620-backfill.sh
@@ -59,6 +59,24 @@ test_expect_success 'do partial clone 1, backfill gets all objects' '
 	test_line_count = 0 revs2
 '
 
+test_expect_success 'do partial clone 2, backfill min batch size' '
+	git clone --no-checkout --filter=blob:none	\
+		--single-branch --branch=main 		\
+		"file://$(pwd)/srv.bare" backfill2 &&
+
+	GIT_TRACE2_EVENT="$(pwd)/batch-trace" git \
+		-C backfill2 backfill --min-batch-size=20 &&
+
+	# Batches were used
+	test_trace2_data promisor fetch_count 20 <batch-trace >matches &&
+	test_line_count = 2 matches &&
+	test_trace2_data promisor fetch_count 8 <batch-trace &&
+
+	# No more missing objects!
+	git -C backfill2 rev-list --quiet --objects --missing=print HEAD >revs2 &&
+	test_line_count = 0 revs2
+'
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
-- 
gitgitgadget
Previous: Derrick StoleeNext: Patrick Steinhardt
Message 23 of 39 in “PATH WALK III: Add 'git backfill' command”
  1. 0/5 PATH WALK III: Add 'git backfill' commandDerrick Stolee via GitGitGadget, Dec 6, 2024
  2. 1/5 backfill: add builtin boilerplateDerrick Stolee via GitGitGadget, Dec 6, 2024
  3. Patrick SteinhardtJan 16, 2025
  4. Junio C HamanoJan 16, 2025
  5. Derrick StoleeFeb 3, 2025
  6. 2/5 backfill: basic functionality and testsDerrick Stolee via GitGitGadget, Dec 6, 2024
  7. Patrick SteinhardtDec 16, 2024
  8. Derrick StoleeDec 18, 2024
  9. 3/5 backfill: add --batch-size=<n> optionDerrick Stolee via GitGitGadget, Dec 6, 2024
  10. Patrick SteinhardtDec 16, 2024
  11. Derrick StoleeDec 18, 2024
  12. Jean-Noël AVILAJan 19, 2025
  13. 4/5 backfill: add --sparse optionDerrick Stolee via GitGitGadget, Dec 6, 2024
  14. Patrick SteinhardtDec 16, 2024
  15. 5/5 backfill: assume --sparse when sparse-checkout is enabledDerrick Stolee via GitGitGadget, Dec 6, 2024
  16. Junio C HamanoDec 8, 2024
  17. Junio C HamanoDec 9, 2024
  18. 0/5 PATH WALK III: Add 'git backfill' commandDerrick Stolee via GitGitGadget, Dec 20, 2024
  19. 1/5 backfill: add builtin boilerplateDerrick Stolee via GitGitGadget, Dec 20, 2024
  20. 2/5 backfill: basic functionality and testsDerrick Stolee via GitGitGadget, Dec 20, 2024
  21. Patrick SteinhardtJan 16, 2025
  22. Derrick StoleeFeb 3, 2025
  23. 3/5 backfill: add --min-batch-size=<n> optionDerrick Stolee via GitGitGadget, Dec 20, 2024
  24. Patrick SteinhardtJan 16, 2025
  25. 4/5 backfill: add --sparse optionDerrick Stolee via GitGitGadget, Dec 20, 2024
  26. Patrick SteinhardtJan 16, 2025
  27. Derrick StoleeFeb 3, 2025
  28. 5/5 backfill: assume --sparse when sparse-checkout is enabledDerrick Stolee via GitGitGadget, Dec 20, 2024
  29. Patrick SteinhardtJan 16, 2025
  30. Junio C HamanoJan 17, 2025
  31. 0/5 PATH WALK III: Add 'git backfill' commandDerrick Stolee via GitGitGadget, Feb 3, 2025
  32. 1/5 backfill: add builtin boilerplateDerrick Stolee via GitGitGadget, Feb 3, 2025
  33. 2/5 backfill: basic functionality and testsDerrick Stolee via GitGitGadget, Feb 3, 2025
  34. 3/5 backfill: add --min-batch-size=<n> optionDerrick Stolee via GitGitGadget, Feb 3, 2025
  35. 4/5 backfill: add --sparse optionDerrick Stolee via GitGitGadget, Feb 3, 2025
  36. 5/5 backfill: assume --sparse when sparse-checkout is enabledDerrick Stolee via GitGitGadget, Feb 3, 2025
  37. Junio C HamanoFeb 4, 2025
  38. Patrick SteinhardtFeb 5, 2025
  39. Junio C HamanoFeb 5, 2025

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.