From 92f766d7fd506711d28ac7c0cfb949a24921654c Mon Sep 17 00:00:00 2001 From: Daniel Reynaud Date: Mon, 19 Oct 2026 11:49:55 -0700 Subject: [PATCH] http-fetch: reuse completed packs before downloading A packfile URI can name a pack that is already installed locally. We still download that pack because http-fetch only looks for a partial download. Open objects/pack/pack-.pack before starting HTTP. If we can open it, feed it to the requested index-pack command and report zero downloaded bytes. This preserves pack validation, index-pack output and keep-file handling while avoiding a second download. Signed-off-by: Daniel Reynaud --- http-fetch.c | 23 ++++++++++++++++++++++- t/t5550-http-fetch-dumb.sh | 24 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/http-fetch.c b/http-fetch.c index 920a1ff1b81f6d..9a8ae7e313cece 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -6,6 +6,8 @@ #include "gettext.h" #include "hex.h" #include "http.h" +#include "packfile.h" +#include "run-command.h" #include "walker.h" #include "setup.h" #include "strvec.h" @@ -82,7 +84,26 @@ static void fetch_single_packfile(struct object_id *packfile_hash, { struct http_pack_request *preq; struct packfile_progress progress = { 0 }; - int ret; + struct strbuf pack_path = STRBUF_INIT; + int fd, ret; + + odb_pack_name(the_repository, &pack_path, packfile_hash->hash, "pack"); + fd = git_open(pack_path.buf); + strbuf_release(&pack_path); + if (fd >= 0) { + struct child_process ip = CHILD_PROCESS_INIT; + + /* Retain index-pack validation, output and keep-file handling. */ + ip.git_cmd = 1; + ip.in = fd; + ip.clean_on_exit = 1; + strvec_pushv(&ip.args, index_pack_args); + if (report_progress) + report_packfile_progress(&progress); + if (run_command(&ip)) + die("index-pack failed for existing pack"); + return; + } http_init(NULL, url, 0); diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh index 4fdec75084f2f0..ef4ca942452c77 100755 --- a/t/t5550-http-fetch-dumb.sh +++ b/t/t5550-http-fetch-dumb.sh @@ -293,6 +293,30 @@ test_expect_success 'http-fetch --packfile' ' git -C packfileclient cat-file -e "$HASH" ' +test_expect_success 'http-fetch --packfile reuses a completed pack without HTTP' ' + git init packfileclient-reuse && + p=$(cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git && + ls objects/pack/pack-*.pack) && + git -C packfileclient-reuse index-pack --stdin --keep \ + <"$HTTPD_DOCUMENT_ROOT_PATH/repo_pack.git/$p" >out && + packhash=$(cut -f2 out) && + pack="packfileclient-reuse/.git/objects/pack/pack-$packhash" && + >reuse.trace && + GIT_TRACE_CURL="$TRASH_DIRECTORY/reuse.trace" \ + git -C packfileclient-reuse http-fetch --report-progress --packfile="$packhash" \ + --index-pack-arg=index-pack --index-pack-arg=--stdin \ + --index-pack-arg=--keep \ + "$HTTPD_URL/missing.pack" >out && + printf "bytes 0\npack\t%s\n" "$packhash" >expect && + test_cmp expect out && + test_must_be_empty reuse.trace && + test_path_is_file "$pack.pack" && + test_path_is_file "$pack.idx" && + test_path_is_file "$pack.keep" && + test_path_is_missing "$pack.pack.temp" && + git -C packfileclient-reuse cat-file -e "$HASH" +' + test_expect_success 'http-fetch --packfile accepts an already complete partial' ' git init packfileclient-complete && p=$(cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&