]> git.ipfire.org Git - thirdparty/git.git/commitdiff
http: accept HTTP 416 for complete partial packs
authorTed Nyman <tnyman@openai.com>
Mon, 27 Jul 2026 00:28:40 +0000 (17:28 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 27 Jul 2026 19:57:20 +0000 (12:57 -0700)
A resumed pack request may already have all bytes of the remote pack.
A server can respond to the resulting Range request with HTTP 416
instead of returning an empty response.

Accept that response in each pack-download caller and let index-pack
validate the completed staging file. This can happen without concurrent
downloads when a previous attempt completed the transfer but failed
before indexing it.

Add a regression test that seeds a complete partial pack and checks that
http-fetch indexes it after the server returns HTTP 416.

Signed-off-by: Ted Nyman <tnyman@openai.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
http-fetch.c
http-push.c
http-walker.c
t/t5550-http-fetch-dumb.sh

index 601a77c3c1020408e3678c261d60d7ea635f0856..05f68f306a5821da0b48fb3cb9f8e09b9eee91b4 100644 (file)
@@ -70,7 +70,8 @@ static void fetch_single_packfile(struct object_id *packfile_hash,
 
        if (start_active_slot(preq->slot)) {
                run_active_slot(preq->slot);
-               if (results.curl_result != CURLE_OK) {
+               if (results.curl_result != CURLE_OK &&
+                   results.http_code != 416) {
                        struct url_info url;
                        char *nurl = url_normalize(preq->url, &url);
                        if (!nurl || !git_env_bool("GIT_TRACE_REDACT", 1)) {
index 3c23cbba27a9ec7ca6ff6e2b259fa48bd392c77a..03dc8102a185fcd850fe6ed72876d20c408752e7 100644 (file)
@@ -595,7 +595,8 @@ static void finish_request(struct transfer_request *request)
 
        } else if (request->state == RUN_FETCH_PACKED) {
                int fail = 1;
-               if (request->curl_result != CURLE_OK) {
+               if (request->curl_result != CURLE_OK &&
+                   request->http_code != 416) {
                        fprintf(stderr, "Unable to get pack file %s\n%s",
                                request->url, curl_errorstr);
                } else {
index b58a3b2a92be38e63bdf63c1fb921f4af6a861b8..abafca84d654413ed610045c44b2022604f82805 100644 (file)
@@ -451,7 +451,8 @@ static int http_fetch_pack(struct walker *walker, struct alt_base *repo,
 
        if (start_active_slot(preq->slot)) {
                run_active_slot(preq->slot);
-               if (results.curl_result != CURLE_OK) {
+               if (results.curl_result != CURLE_OK &&
+                   results.http_code != 416) {
                        error("Unable to get pack file %s\n%s", preq->url,
                              curl_errorstr);
                        goto abort;
index b0080bf204789995f4feae464eca50ab0c463465..621f2fbf7327c56363b48655bd2ec6ce5d3d9cbb 100755 (executable)
@@ -293,6 +293,25 @@ test_expect_success 'http-fetch --packfile' '
        git -C packfileclient 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 &&
+               ls objects/pack/pack-*.pack) &&
+       packhash=$(basename "$p" .pack) &&
+       packhash=${packhash#pack-} &&
+       tmpfile="packfileclient-complete/.git/objects/pack/pack-$packhash.pack.temp" &&
+       cp "$HTTPD_DOCUMENT_ROOT_PATH/repo_pack.git/$p" "$tmpfile" &&
+       chmod u+w "$tmpfile" &&
+       GIT_TRACE_CURL="$TRASH_DIRECTORY/complete.trace" \
+       git -C packfileclient-complete http-fetch --packfile="$packhash" \
+               --index-pack-arg=index-pack \
+               --index-pack-arg=--stdin --index-pack-arg=--keep \
+               "$HTTPD_URL/dumb/repo_pack.git/$p" >out &&
+       test_grep "416 Requested Range Not Satisfiable" complete.trace &&
+       test_path_is_missing "$tmpfile" &&
+       git -C packfileclient-complete cat-file -e "$HASH"
+'
+
 test_expect_success 'fetch notices corrupt pack' '
        cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
        (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&