@matheusbsbs / matheus-bagliano / commits / dd4b732df7
Fork 0

upload-pack: send part of packfile response as uri

Teach upload-pack to send part of its packfile response as URIs. An administrator may configure a repository with one or more "uploadpack.blobpackfileuri" lines, each line containing an OID, a pack hash, and a URI. A client may configure fetch.uriprotocols to be a comma-separated list of protocols that it is willing to use to fetch additional packfiles - this list will be sent to the server. Whenever an object with one of those OIDs would appear in the packfile transmitted by upload-pack, the server may exclude that object, and instead send the URI. The client will then download the packs referred to by those URIs before performing the connectivity check. Signed-off-by: Jonathan Tan <jonathantanmy@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jonathan Tan committed Jun 10, 2020 at 13:57 UTC dd4b732df73b06878b81fce050e7dcac4366a38e
4 files changed +343 -10
builtin/pack-objects.c
+76
@@ -117,6 +117,8 @@ static unsigned long window_memory_limit = 0;
117
118 static struct list_objects_filter_options filter_options;
119
120 +static struct string_list uri_protocols = STRING_LIST_INIT_NODUP;
121 +
122 enum missing_action {
123 MA_ERROR = 0, /* fail if any missing objects are encountered */
124 MA_ALLOW_ANY, /* silently allow ALL missing objects */
@@ -125,6 +127,15 @@ enum missing_action {
127 static enum missing_action arg_missing_action;
128 static show_object_fn fn_show_object;
129
130 +struct configured_exclusion {
131 + struct oidmap_entry e;
132 + char *pack_hash_hex;
133 + char *uri;
134 +};
135 +static struct oidmap configured_exclusions;
136 +
137 +static struct oidset excluded_by_config;
138 +
139 /*
140 * stats
141 */
@@ -969,6 +980,25 @@ static void write_reused_pack(struct hashfile *f)
980 unuse_pack(&w_curs);
981 }
982
983 +static void write_excluded_by_configs(void)
984 +{
985 + struct oidset_iter iter;
986 + const struct object_id *oid;
987 +
988 + oidset_iter_init(&excluded_by_config, &iter);
989 + while ((oid = oidset_iter_next(&iter))) {
990 + struct configured_exclusion *ex =
991 + oidmap_get(&configured_exclusions, oid);
992 +
993 + if (!ex)
994 + BUG("configured exclusion wasn't configured");
995 + write_in_full(1, ex->pack_hash_hex, strlen(ex->pack_hash_hex));
996 + write_in_full(1, " ", 1);
997 + write_in_full(1, ex->uri, strlen(ex->uri));
998 + write_in_full(1, "\n", 1);
999 + }
1000 +}
1001 +
1002 static const char no_split_warning[] = N_(
1003 "disabling bitmap writing, packs are split due to pack.packSizeLimit"
1004 );
@@ -1266,6 +1296,25 @@ static int want_object_in_pack(const struct object_id *oid,
1296 }
1297 }
1298
1299 + if (uri_protocols.nr) {
1300 + struct configured_exclusion *ex =
1301 + oidmap_get(&configured_exclusions, oid);
1302 + int i;
1303 + const char *p;
1304 +
1305 + if (ex) {
1306 + for (i = 0; i < uri_protocols.nr; i++) {
1307 + if (skip_prefix(ex->uri,
1308 + uri_protocols.items[i].string,
1309 + &p) &&
1310 + *p == ':') {
1311 + oidset_insert(&excluded_by_config, oid);
1312 + return 0;
1313 + }
1314 + }
1315 + }
1316 + }
1317 +
1318 return 1;
1319 }
1320
@@ -2864,6 +2913,29 @@ static int git_pack_config(const char *k, const char *v, void *cb)
2913 pack_idx_opts.version);
2914 return 0;
2915 }
2916 + if (!strcmp(k, "uploadpack.blobpackfileuri")) {
2917 + struct configured_exclusion *ex = xmalloc(sizeof(*ex));
2918 + const char *oid_end, *pack_end;
2919 + /*
2920 + * Stores the pack hash. This is not a true object ID, but is
2921 + * of the same form.
2922 + */
2923 + struct object_id pack_hash;
2924 +
2925 + if (parse_oid_hex(v, &ex->e.oid, &oid_end) ||
2926 + *oid_end != ' ' ||
2927 + parse_oid_hex(oid_end + 1, &pack_hash, &pack_end) ||
2928 + *pack_end != ' ')
2929 + die(_("value of uploadpack.blobpackfileuri must be "
2930 + "of the form '<object-hash> <pack-hash> <uri>' (got '%s')"), v);
2931 + if (oidmap_get(&configured_exclusions, &ex->e.oid))
2932 + die(_("object already configured in another "
2933 + "uploadpack.blobpackfileuri (got '%s')"), v);
2934 + ex->pack_hash_hex = xcalloc(1, pack_end - oid_end);
2935 + memcpy(ex->pack_hash_hex, oid_end + 1, pack_end - oid_end - 1);
2936 + ex->uri = xstrdup(pack_end + 1);
2937 + oidmap_put(&configured_exclusions, ex);
2938 + }
2939 return git_default_config(k, v, cb);
2940 }
2941
@@ -3462,6 +3534,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
3534 N_("do not pack objects in promisor packfiles")),
3535 OPT_BOOL(0, "delta-islands", &use_delta_islands,
3536 N_("respect islands during delta compression")),
3537 + OPT_STRING_LIST(0, "uri-protocol", &uri_protocols,
3538 + N_("protocol"),
3539 + N_("exclude any configured uploadpack.blobpackfileuri with this protocol")),
3540 OPT_END(),
3541 };
3542
@@ -3650,6 +3725,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
3725 }
3726
3727 trace2_region_enter("pack-objects", "write-pack-file", the_repository);
3728 + write_excluded_by_configs();
3729 write_pack_file();
3730 trace2_region_leave("pack-objects", "write-pack-file", the_repository);
3731
fetch-pack.c
+108 -4
@@ -38,6 +38,7 @@ static int server_supports_filtering;
38 static struct shallow_lock shallow_lock;
39 static const char *alternate_shallow_file;
40 static struct strbuf fsck_msg_types = STRBUF_INIT;
41 +static struct string_list uri_protocols = STRING_LIST_INIT_DUP;
42
43 /* Remember to update object flag allocation in object.h */
44 #define COMPLETE (1U << 0)
@@ -795,6 +796,7 @@ static void write_promisor_file(const char *keep_name,
796
797 static int get_pack(struct fetch_pack_args *args,
798 int xd[2], struct string_list *pack_lockfiles,
799 + int only_packfile,
800 struct ref **sought, int nr_sought)
801 {
802 struct async demux;
@@ -855,8 +857,15 @@ static int get_pack(struct fetch_pack_args *args,
857 "--keep=fetch-pack %"PRIuMAX " on %s",
858 (uintmax_t)getpid(), hostname);
859 }
858 - if (args->check_self_contained_and_connected)
860 + if (only_packfile && args->check_self_contained_and_connected)
861 argv_array_push(&cmd.args, "--check-self-contained-and-connected");
862 + else
863 + /*
864 + * We cannot perform any connectivity checks because
865 + * not all packs have been downloaded; let the caller
866 + * have this responsibility.
867 + */
868 + args->check_self_contained_and_connected = 0;
869 /*
870 * If we're obtaining the filename of a lockfile, we'll use
871 * that filename to write a .promisor file with more
@@ -1068,7 +1077,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
1077 alternate_shallow_file = setup_temporary_shallow(si->shallow);
1078 else
1079 alternate_shallow_file = NULL;
1071 - if (get_pack(args, fd, pack_lockfiles, sought, nr_sought))
1080 + if (get_pack(args, fd, pack_lockfiles, 1, sought, nr_sought))
1081 die(_("git fetch-pack: fetch failed."));
1082
1083 all_done:
@@ -1222,6 +1231,26 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
1231 warning("filtering not recognized by server, ignoring");
1232 }
1233
1234 + if (server_supports_feature("fetch", "packfile-uris", 0)) {
1235 + int i;
1236 + struct strbuf to_send = STRBUF_INIT;
1237 +
1238 + for (i = 0; i < uri_protocols.nr; i++) {
1239 + const char *s = uri_protocols.items[i].string;
1240 +
1241 + if (!strcmp(s, "https") || !strcmp(s, "http")) {
1242 + if (to_send.len)
1243 + strbuf_addch(&to_send, ',');
1244 + strbuf_addstr(&to_send, s);
1245 + }
1246 + }
1247 + if (to_send.len) {
1248 + packet_buf_write(&req_buf, "packfile-uris %s",
1249 + to_send.buf);
1250 + strbuf_release(&to_send);
1251 + }
1252 + }
1253 +
1254 /* add wants */
1255 add_wants(args->no_dependents, wants, &req_buf);
1256
@@ -1444,6 +1473,21 @@ static void receive_wanted_refs(struct packet_reader *reader,
1473 die(_("error processing wanted refs: %d"), reader->status);
1474 }
1475
1476 +static void receive_packfile_uris(struct packet_reader *reader,
1477 + struct string_list *uris)
1478 +{
1479 + process_section_header(reader, "packfile-uris", 0);
1480 + while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
1481 + if (reader->pktlen < the_hash_algo->hexsz ||
1482 + reader->line[the_hash_algo->hexsz] != ' ')
1483 + die("expected '<hash> <uri>', got: %s\n", reader->line);
1484 +
1485 + string_list_append(uris, reader->line);
1486 + }
1487 + if (reader->status != PACKET_READ_DELIM)
1488 + die("expected DELIM");
1489 +}
1490 +
1491 enum fetch_state {
1492 FETCH_CHECK_LOCAL = 0,
1493 FETCH_SEND_REQUEST,
@@ -1470,6 +1514,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
1514 struct fetch_negotiator negotiator_alloc;
1515 struct fetch_negotiator *negotiator;
1516 int seen_ack = 0;
1517 + struct string_list packfile_uris = STRING_LIST_INIT_DUP;
1518 + int i;
1519
1520 if (args->no_dependents) {
1521 negotiator = NULL;
@@ -1558,9 +1604,12 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
1604 if (process_section_header(&reader, "wanted-refs", 1))
1605 receive_wanted_refs(&reader, sought, nr_sought);
1606
1561 - /* get the pack */
1607 + /* get the pack(s) */
1608 + if (process_section_header(&reader, "packfile-uris", 1))
1609 + receive_packfile_uris(&reader, &packfile_uris);
1610 process_section_header(&reader, "packfile", 0);
1563 - if (get_pack(args, fd, pack_lockfiles, sought, nr_sought))
1611 + if (get_pack(args, fd, pack_lockfiles,
1612 + !packfile_uris.nr, sought, nr_sought))
1613 die(_("git fetch-pack: fetch failed."));
1614
1615 state = FETCH_DONE;
@@ -1570,8 +1619,55 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
1619 }
1620 }
1621
1622 + for (i = 0; i < packfile_uris.nr; i++) {
1623 + struct child_process cmd = CHILD_PROCESS_INIT;
1624 + char packname[GIT_MAX_HEXSZ + 1];
1625 + const char *uri = packfile_uris.items[i].string +
1626 + the_hash_algo->hexsz + 1;
1627 +
1628 + argv_array_push(&cmd.args, "http-fetch");
1629 + argv_array_pushf(&cmd.args, "--packfile=%.*s",
1630 + (int) the_hash_algo->hexsz,
1631 + packfile_uris.items[i].string);
1632 + argv_array_push(&cmd.args, uri);
1633 + cmd.git_cmd = 1;
1634 + cmd.no_stdin = 1;
1635 + cmd.out = -1;
1636 + if (start_command(&cmd))
1637 + die("fetch-pack: unable to spawn http-fetch");
1638 +
1639 + if (read_in_full(cmd.out, packname, 5) < 0 ||
1640 + memcmp(packname, "keep\t", 5))
1641 + die("fetch-pack: expected keep then TAB at start of http-fetch output");
1642 +
1643 + if (read_in_full(cmd.out, packname,
1644 + the_hash_algo->hexsz + 1) < 0 ||
1645 + packname[the_hash_algo->hexsz] != '\n')
1646 + die("fetch-pack: expected hash then LF at end of http-fetch output");
1647 +
1648 + packname[the_hash_algo->hexsz] = '\0';
1649 +
1650 + close(cmd.out);
1651 +
1652 + if (finish_command(&cmd))
1653 + die("fetch-pack: unable to finish http-fetch");
1654 +
1655 + if (memcmp(packfile_uris.items[i].string, packname,
1656 + the_hash_algo->hexsz))
1657 + die("fetch-pack: pack downloaded from %s does not match expected hash %.*s",
1658 + uri, (int) the_hash_algo->hexsz,
1659 + packfile_uris.items[i].string);
1660 +
1661 + string_list_append_nodup(pack_lockfiles,
1662 + xstrfmt("%s/pack/pack-%s.keep",
1663 + get_object_directory(),
1664 + packname));
1665 + }
1666 + string_list_clear(&packfile_uris, 0);
1667 +
1668 if (negotiator)
1669 negotiator->release(negotiator);
1670 +
1671 oidset_clear(&common);
1672 return ref;
1673 }
@@ -1608,6 +1704,14 @@ static void fetch_pack_config(void)
1704 git_config_get_bool("repack.usedeltabaseoffset", &prefer_ofs_delta);
1705 git_config_get_bool("fetch.fsckobjects", &fetch_fsck_objects);
1706 git_config_get_bool("transfer.fsckobjects", &transfer_fsck_objects);
1707 + if (!uri_protocols.nr) {
1708 + char *str;
1709 +
1710 + if (!git_config_get_string("fetch.uriprotocols", &str) && str) {
1711 + string_list_split(&uri_protocols, str, ',', -1);
1712 + free(str);
1713 + }
1714 + }
1715
1716 git_config(fetch_pack_config_cb, NULL);
1717 }
t/t5702-protocol-v2.sh
+88
@@ -748,6 +748,94 @@ test_expect_success 'when server does not send "ready", expect FLUSH' '
748 test_i18ngrep "expected no other sections to be sent after no .ready." err
749 '
750
751 +configure_exclusion () {
752 + git -C "$1" hash-object "$2" >objh &&
753 + git -C "$1" pack-objects "$HTTPD_DOCUMENT_ROOT_PATH/mypack" <objh >packh &&
754 + git -C "$1" config --add \
755 + "uploadpack.blobpackfileuri" \
756 + "$(cat objh) $(cat packh) $HTTPD_URL/dumb/mypack-$(cat packh).pack" &&
757 + cat objh
758 +}
759 +
760 +test_expect_success 'part of packfile response provided as URI' '
761 + P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
762 + rm -rf "$P" http_child log &&
763 +
764 + git init "$P" &&
765 + git -C "$P" config "uploadpack.allowsidebandall" "true" &&
766 +
767 + echo my-blob >"$P/my-blob" &&
768 + git -C "$P" add my-blob &&
769 + echo other-blob >"$P/other-blob" &&
770 + git -C "$P" add other-blob &&
771 + git -C "$P" commit -m x &&
772 +
773 + configure_exclusion "$P" my-blob >h &&
774 + configure_exclusion "$P" other-blob >h2 &&
775 +
776 + GIT_TRACE=1 GIT_TRACE_PACKET="$(pwd)/log" GIT_TEST_SIDEBAND_ALL=1 \
777 + git -c protocol.version=2 \
778 + -c fetch.uriprotocols=http,https \
779 + clone "$HTTPD_URL/smart/http_parent" http_child &&
780 +
781 + # Ensure that my-blob and other-blob are in separate packfiles.
782 + for idx in http_child/.git/objects/pack/*.idx
783 + do
784 + git verify-pack --verbose $idx >out &&
785 + {
786 + grep "^[0-9a-f]\{16,\} " out || :
787 + } >out.objectlist &&
788 + if test_line_count = 1 out.objectlist
789 + then
790 + if grep $(cat h) out
791 + then
792 + >hfound
793 + fi &&
794 + if grep $(cat h2) out
795 + then
796 + >h2found
797 + fi
798 + fi
799 + done &&
800 + test -f hfound &&
801 + test -f h2found &&
802 +
803 + # Ensure that there are exactly 6 files (3 .pack and 3 .idx).
804 + ls http_child/.git/objects/pack/* >filelist &&
805 + test_line_count = 6 filelist
806 +'
807 +
808 +test_expect_success 'fetching with valid packfile URI but invalid hash fails' '
809 + P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
810 + rm -rf "$P" http_child log &&
811 +
812 + git init "$P" &&
813 + git -C "$P" config "uploadpack.allowsidebandall" "true" &&
814 +
815 + echo my-blob >"$P/my-blob" &&
816 + git -C "$P" add my-blob &&
817 + echo other-blob >"$P/other-blob" &&
818 + git -C "$P" add other-blob &&
819 + git -C "$P" commit -m x &&
820 +
821 + configure_exclusion "$P" my-blob >h &&
822 + # Configure a URL for other-blob. Just reuse the hash of the object as
823 + # the hash of the packfile, since the hash does not matter for this
824 + # test as long as it is not the hash of the pack, and it is of the
825 + # expected length.
826 + git -C "$P" hash-object other-blob >objh &&
827 + git -C "$P" pack-objects "$HTTPD_DOCUMENT_ROOT_PATH/mypack" <objh >packh &&
828 + git -C "$P" config --add \
829 + "uploadpack.blobpackfileuri" \
830 + "$(cat objh) $(cat objh) $HTTPD_URL/dumb/mypack-$(cat packh).pack" &&
831 +
832 + test_must_fail env GIT_TEST_SIDEBAND_ALL=1 \
833 + git -c protocol.version=2 \
834 + -c fetch.uriprotocols=http,https \
835 + clone "$HTTPD_URL/smart/http_parent" http_child 2>err &&
836 + test_i18ngrep "pack downloaded from.*does not match expected hash" err
837 +'
838 +
839 # DO NOT add non-httpd-specific tests here, because the last part of this
840 # test script is only executed when httpd is available and enabled.
841
upload-pack.c
+71 -6
@@ -83,6 +83,8 @@ struct upload_pack_data {
83 /* 0 for no sideband, otherwise DEFAULT_PACKET_MAX or LARGE_PACKET_MAX */
84 int use_sideband;
85
86 + struct string_list uri_protocols;
87 +
88 struct list_objects_filter_options filter_options;
89
90 struct packet_writer writer;
@@ -114,6 +116,7 @@ static void upload_pack_data_init(struct upload_pack_data *data)
116 struct oid_array haves = OID_ARRAY_INIT;
117 struct object_array shallows = OBJECT_ARRAY_INIT;
118 struct string_list deepen_not = STRING_LIST_INIT_DUP;
119 + struct string_list uri_protocols = STRING_LIST_INIT_DUP;
120
121 memset(data, 0, sizeof(*data));
122 data->symref = symref;
@@ -123,6 +126,7 @@ static void upload_pack_data_init(struct upload_pack_data *data)
126 data->haves = haves;
127 data->shallows = shallows;
128 data->deepen_not = deepen_not;
129 + data->uri_protocols = uri_protocols;
130 packet_writer_init(&data->writer, 1);
131
132 data->keepalive = 5;
@@ -176,10 +180,12 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
180 struct output_state {
181 char buffer[8193];
182 int used;
183 + unsigned packfile_uris_started : 1;
184 + unsigned packfile_started : 1;
185 };
186
187 static int relay_pack_data(int pack_objects_out, struct output_state *os,
182 - int use_sideband)
188 + int use_sideband, int write_packfile_line)
189 {
190 /*
191 * We keep the last byte to ourselves
@@ -200,6 +206,37 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os,
206 }
207 os->used += readsz;
208
209 + while (!os->packfile_started) {
210 + char *p;
211 + if (os->used >= 4 && !memcmp(os->buffer, "PACK", 4)) {
212 + os->packfile_started = 1;
213 + if (write_packfile_line) {
214 + if (os->packfile_uris_started)
215 + packet_delim(1);
216 + packet_write_fmt(1, "\1packfile\n");
217 + }
218 + break;
219 + }
220 + if ((p = memchr(os->buffer, '\n', os->used))) {
221 + if (!os->packfile_uris_started) {
222 + os->packfile_uris_started = 1;
223 + if (!write_packfile_line)
224 + BUG("packfile_uris requires sideband-all");
225 + packet_write_fmt(1, "\1packfile-uris\n");
226 + }
227 + *p = '\0';
228 + packet_write_fmt(1, "\1%s\n", os->buffer);
229 +
230 + os->used -= p - os->buffer + 1;
231 + memmove(os->buffer, p + 1, os->used);
232 + } else {
233 + /*
234 + * Incomplete line.
235 + */
236 + return readsz;
237 + }
238 + }
239 +
240 if (os->used > 1) {
241 send_client_data(1, os->buffer, os->used - 1, use_sideband);
242 os->buffer[0] = os->buffer[os->used - 1];
@@ -212,7 +249,8 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os,
249 return readsz;
250 }
251
215 -static void create_pack_file(struct upload_pack_data *pack_data)
252 +static void create_pack_file(struct upload_pack_data *pack_data,
253 + const struct string_list *uri_protocols)
254 {
255 struct child_process pack_objects = CHILD_PROCESS_INIT;
256 struct output_state output_state = { { 0 } };
@@ -262,6 +300,11 @@ static void create_pack_file(struct upload_pack_data *pack_data)
300 spec);
301 }
302 }
303 + if (uri_protocols) {
304 + for (i = 0; i < uri_protocols->nr; i++)
305 + argv_array_pushf(&pack_objects.args, "--uri-protocol=%s",
306 + uri_protocols->items[i].string);
307 + }
308
309 pack_objects.in = -1;
310 pack_objects.out = -1;
@@ -353,7 +396,8 @@ static void create_pack_file(struct upload_pack_data *pack_data)
396 if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
397 int result = relay_pack_data(pack_objects.out,
398 &output_state,
356 - pack_data->use_sideband);
399 + pack_data->use_sideband,
400 + !!uri_protocols);
401
402 if (result == 0) {
403 close(pack_objects.out);
@@ -1210,7 +1254,7 @@ void upload_pack(struct upload_pack_options *options)
1254 receive_needs(&data, &reader);
1255 if (data.want_obj.nr) {
1256 get_common_commits(&data, &reader);
1213 - create_pack_file(&data);
1257 + create_pack_file(&data, 0);
1258 }
1259 }
1260
@@ -1363,10 +1407,18 @@ static void process_args(struct packet_reader *request,
1407 continue;
1408 }
1409
1410 + if (skip_prefix(arg, "packfile-uris ", &p)) {
1411 + string_list_split(&data->uri_protocols, p, ',', -1);
1412 + continue;
1413 + }
1414 +
1415 /* ignore unknown lines maybe? */
1416 die("unexpected line: '%s'", arg);
1417 }
1418
1419 + if (data->uri_protocols.nr && !data->writer.use_sideband)
1420 + string_list_clear(&data->uri_protocols, 0);
1421 +
1422 if (request->status != PACKET_READ_FLUSH)
1423 die(_("expected flush after fetch arguments"));
1424 }
@@ -1553,8 +1605,12 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys,
1605 send_wanted_ref_info(&data);
1606 send_shallow_info(&data);
1607
1556 - packet_writer_write(&data.writer, "packfile\n");
1557 - create_pack_file(&data);
1608 + if (data.uri_protocols.nr) {
1609 + create_pack_file(&data, &data.uri_protocols);
1610 + } else {
1611 + packet_writer_write(&data.writer, "packfile\n");
1612 + create_pack_file(&data, NULL);
1613 + }
1614 state = FETCH_DONE;
1615 break;
1616 case FETCH_DONE:
@@ -1573,6 +1629,7 @@ int upload_pack_advertise(struct repository *r,
1629 int allow_filter_value;
1630 int allow_ref_in_want;
1631 int allow_sideband_all_value;
1632 + char *str = NULL;
1633
1634 strbuf_addstr(value, "shallow");
1635
@@ -1594,6 +1651,14 @@ int upload_pack_advertise(struct repository *r,
1651 &allow_sideband_all_value) &&
1652 allow_sideband_all_value))
1653 strbuf_addstr(value, " sideband-all");
1654 +
1655 + if (!repo_config_get_string(the_repository,
1656 + "uploadpack.blobpackfileuri",
1657 + &str) &&
1658 + str) {
1659 + strbuf_addstr(value, " packfile-uris");
1660 + free(str);
1661 + }
1662 }
1663
1664 return 1;