Skip to content

Commit 5476e1e

Browse files
jonathantanmygitster
authored andcommitted
fetch-pack: print and use dangling .gitmodules
Teach index-pack to print dangling .gitmodules links after its "keep" or "pack" line instead of declaring an error, and teach fetch-pack to check such lines printed. This allows the tree side of the .gitmodules link to be in one packfile and the blob side to be in another without failing the fsck check, because it is now fetch-pack which checks such objects after all packfiles have been downloaded and indexed (and not index-pack on an individual packfile, as it is before this commit). Signed-off-by: Jonathan Tan <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b664e9f commit 5476e1e

File tree

9 files changed

+165
-22
lines changed

9 files changed

+165
-22
lines changed

Documentation/git-index-pack.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,12 @@ OPTIONS
7878
Die if the pack contains broken links. For internal use only.
7979

8080
--fsck-objects::
81-
Die if the pack contains broken objects. For internal use only.
81+
For internal use only.
82+
+
83+
Die if the pack contains broken objects. If the pack contains a tree
84+
pointing to a .gitmodules blob that does not exist, prints the hash of
85+
that blob (for the caller to check) after the hash that goes into the
86+
name of the pack/idx file (see "Notes").
8287

8388
--threads=<n>::
8489
Specifies the number of threads to spawn when resolving

builtin/index-pack.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,6 +1693,22 @@ static void show_pack_info(int stat_only)
16931693
}
16941694
}
16951695

1696+
static int print_dangling_gitmodules(struct fsck_options *o,
1697+
const struct object_id *oid,
1698+
enum object_type object_type,
1699+
int msg_type, const char *message)
1700+
{
1701+
/*
1702+
* NEEDSWORK: Plumb the MSG_ID (from fsck.c) here and use it
1703+
* instead of relying on this string check.
1704+
*/
1705+
if (starts_with(message, "gitmodulesMissing")) {
1706+
printf("%s\n", oid_to_hex(oid));
1707+
return 0;
1708+
}
1709+
return fsck_error_function(o, oid, object_type, msg_type, message);
1710+
}
1711+
16961712
int cmd_index_pack(int argc, const char **argv, const char *prefix)
16971713
{
16981714
int i, fix_thin_pack = 0, verify = 0, stat_only = 0;
@@ -1888,8 +1904,13 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
18881904
else
18891905
close(input_fd);
18901906

1891-
if (do_fsck_object && fsck_finish(&fsck_options))
1892-
die(_("fsck error in pack objects"));
1907+
if (do_fsck_object) {
1908+
struct fsck_options fo = fsck_options;
1909+
1910+
fo.error_func = print_dangling_gitmodules;
1911+
if (fsck_finish(&fo))
1912+
die(_("fsck error in pack objects"));
1913+
}
18931914

18941915
free(objects);
18951916
strbuf_release(&index_name_buf);

builtin/receive-pack.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2275,7 +2275,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)
22752275
status = start_command(&child);
22762276
if (status)
22772277
return "index-pack fork failed";
2278-
pack_lockfile = index_pack_lockfile(child.out);
2278+
pack_lockfile = index_pack_lockfile(child.out, NULL);
22792279
close(child.out);
22802280
status = finish_command(&child);
22812281
if (status)

fetch-pack.c

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,26 @@ static void write_promisor_file(const char *keep_name,
796796
strbuf_release(&promisor_name);
797797
}
798798

799+
static void parse_gitmodules_oids(int fd, struct oidset *gitmodules_oids)
800+
{
801+
int len = the_hash_algo->hexsz + 1; /* hash + NL */
802+
803+
do {
804+
char hex_hash[GIT_MAX_HEXSZ + 1];
805+
int read_len = read_in_full(fd, hex_hash, len);
806+
struct object_id oid;
807+
const char *end;
808+
809+
if (!read_len)
810+
return;
811+
if (read_len != len)
812+
die("invalid length read %d", read_len);
813+
if (parse_oid_hex(hex_hash, &oid, &end) || *end != '\n')
814+
die("invalid hash");
815+
oidset_insert(gitmodules_oids, &oid);
816+
} while (1);
817+
}
818+
799819
/*
800820
* If packfile URIs were provided, pass a non-NULL pointer to index_pack_args.
801821
* The strings to pass as the --index-pack-arg arguments to http-fetch will be
@@ -804,14 +824,16 @@ static void write_promisor_file(const char *keep_name,
804824
static int get_pack(struct fetch_pack_args *args,
805825
int xd[2], struct string_list *pack_lockfiles,
806826
struct strvec *index_pack_args,
807-
struct ref **sought, int nr_sought)
827+
struct ref **sought, int nr_sought,
828+
struct oidset *gitmodules_oids)
808829
{
809830
struct async demux;
810831
int do_keep = args->keep_pack;
811832
const char *cmd_name;
812833
struct pack_header header;
813834
int pass_header = 0;
814835
struct child_process cmd = CHILD_PROCESS_INIT;
836+
int fsck_objects = 0;
815837
int ret;
816838

817839
memset(&demux, 0, sizeof(demux));
@@ -846,8 +868,15 @@ static int get_pack(struct fetch_pack_args *args,
846868
strvec_push(&cmd.args, alternate_shallow_file);
847869
}
848870

849-
if (do_keep || args->from_promisor || index_pack_args) {
850-
if (pack_lockfiles)
871+
if (fetch_fsck_objects >= 0
872+
? fetch_fsck_objects
873+
: transfer_fsck_objects >= 0
874+
? transfer_fsck_objects
875+
: 0)
876+
fsck_objects = 1;
877+
878+
if (do_keep || args->from_promisor || index_pack_args || fsck_objects) {
879+
if (pack_lockfiles || fsck_objects)
851880
cmd.out = -1;
852881
cmd_name = "index-pack";
853882
strvec_push(&cmd.args, cmd_name);
@@ -897,11 +926,7 @@ static int get_pack(struct fetch_pack_args *args,
897926
strvec_pushf(&cmd.args, "--pack_header=%"PRIu32",%"PRIu32,
898927
ntohl(header.hdr_version),
899928
ntohl(header.hdr_entries));
900-
if (fetch_fsck_objects >= 0
901-
? fetch_fsck_objects
902-
: transfer_fsck_objects >= 0
903-
? transfer_fsck_objects
904-
: 0) {
929+
if (fsck_objects) {
905930
if (args->from_promisor || index_pack_args)
906931
/*
907932
* We cannot use --strict in index-pack because it
@@ -925,10 +950,15 @@ static int get_pack(struct fetch_pack_args *args,
925950
cmd.git_cmd = 1;
926951
if (start_command(&cmd))
927952
die(_("fetch-pack: unable to fork off %s"), cmd_name);
928-
if (do_keep && pack_lockfiles) {
929-
char *pack_lockfile = index_pack_lockfile(cmd.out);
953+
if (do_keep && (pack_lockfiles || fsck_objects)) {
954+
int is_well_formed;
955+
char *pack_lockfile = index_pack_lockfile(cmd.out, &is_well_formed);
956+
957+
if (!is_well_formed)
958+
die(_("fetch-pack: invalid index-pack output"));
930959
if (pack_lockfile)
931960
string_list_append_nodup(pack_lockfiles, pack_lockfile);
961+
parse_gitmodules_oids(cmd.out, gitmodules_oids);
932962
close(cmd.out);
933963
}
934964

@@ -963,6 +993,22 @@ static int cmp_ref_by_name(const void *a_, const void *b_)
963993
return strcmp(a->name, b->name);
964994
}
965995

996+
static void fsck_gitmodules_oids(struct oidset *gitmodules_oids)
997+
{
998+
struct oidset_iter iter;
999+
const struct object_id *oid;
1000+
struct fsck_options fo = FSCK_OPTIONS_STRICT;
1001+
1002+
if (!oidset_size(gitmodules_oids))
1003+
return;
1004+
1005+
oidset_iter_init(gitmodules_oids, &iter);
1006+
while ((oid = oidset_iter_next(&iter)))
1007+
register_found_gitmodules(oid);
1008+
if (fsck_finish(&fo))
1009+
die("fsck failed");
1010+
}
1011+
9661012
static struct ref *do_fetch_pack(struct fetch_pack_args *args,
9671013
int fd[2],
9681014
const struct ref *orig_ref,
@@ -977,6 +1023,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
9771023
int agent_len;
9781024
struct fetch_negotiator negotiator_alloc;
9791025
struct fetch_negotiator *negotiator;
1026+
struct oidset gitmodules_oids = OIDSET_INIT;
9801027

9811028
negotiator = &negotiator_alloc;
9821029
fetch_negotiator_init(r, negotiator);
@@ -1092,8 +1139,10 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
10921139
alternate_shallow_file = setup_temporary_shallow(si->shallow);
10931140
else
10941141
alternate_shallow_file = NULL;
1095-
if (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought))
1142+
if (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,
1143+
&gitmodules_oids))
10961144
die(_("git fetch-pack: fetch failed."));
1145+
fsck_gitmodules_oids(&gitmodules_oids);
10971146

10981147
all_done:
10991148
if (negotiator)
@@ -1544,6 +1593,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
15441593
struct string_list packfile_uris = STRING_LIST_INIT_DUP;
15451594
int i;
15461595
struct strvec index_pack_args = STRVEC_INIT;
1596+
struct oidset gitmodules_oids = OIDSET_INIT;
15471597

15481598
negotiator = &negotiator_alloc;
15491599
fetch_negotiator_init(r, negotiator);
@@ -1634,7 +1684,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
16341684
process_section_header(&reader, "packfile", 0);
16351685
if (get_pack(args, fd, pack_lockfiles,
16361686
packfile_uris.nr ? &index_pack_args : NULL,
1637-
sought, nr_sought))
1687+
sought, nr_sought, &gitmodules_oids))
16381688
die(_("git fetch-pack: fetch failed."));
16391689
do_check_stateless_delimiter(args, &reader);
16401690

@@ -1677,6 +1727,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
16771727

16781728
packname[the_hash_algo->hexsz] = '\0';
16791729

1730+
parse_gitmodules_oids(cmd.out, &gitmodules_oids);
1731+
16801732
close(cmd.out);
16811733

16821734
if (finish_command(&cmd))
@@ -1696,6 +1748,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
16961748
string_list_clear(&packfile_uris, 0);
16971749
strvec_clear(&index_pack_args);
16981750

1751+
fsck_gitmodules_oids(&gitmodules_oids);
1752+
16991753
if (negotiator)
17001754
negotiator->release(negotiator);
17011755

fsck.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,11 @@ int fsck_error_function(struct fsck_options *o,
12431243
return 1;
12441244
}
12451245

1246+
void register_found_gitmodules(const struct object_id *oid)
1247+
{
1248+
oidset_insert(&gitmodules_found, oid);
1249+
}
1250+
12461251
int fsck_finish(struct fsck_options *options)
12471252
{
12481253
int ret = 0;

fsck.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options);
6262
int fsck_object(struct object *obj, void *data, unsigned long size,
6363
struct fsck_options *options);
6464

65+
void register_found_gitmodules(const struct object_id *oid);
66+
6567
/*
6668
* Some fsck checks are context-dependent, and may end up queued; run this
6769
* after completing all fsck_object() calls in order to resolve any remaining

pack-write.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ void fixup_pack_header_footer(int pack_fd,
272272
fsync_or_die(pack_fd, pack_name);
273273
}
274274

275-
char *index_pack_lockfile(int ip_out)
275+
char *index_pack_lockfile(int ip_out, int *is_well_formed)
276276
{
277277
char packname[GIT_MAX_HEXSZ + 6];
278278
const int len = the_hash_algo->hexsz + 6;
@@ -286,11 +286,17 @@ char *index_pack_lockfile(int ip_out)
286286
*/
287287
if (read_in_full(ip_out, packname, len) == len && packname[len-1] == '\n') {
288288
const char *name;
289+
290+
if (is_well_formed)
291+
*is_well_formed = 1;
289292
packname[len-1] = 0;
290293
if (skip_prefix(packname, "keep\t", &name))
291294
return xstrfmt("%s/pack/pack-%s.keep",
292295
get_object_directory(), name);
296+
return NULL;
293297
}
298+
if (is_well_formed)
299+
*is_well_formed = 0;
294300
return NULL;
295301
}
296302

pack.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ int verify_pack_index(struct packed_git *);
8585
int verify_pack(struct repository *, struct packed_git *, verify_fn fn, struct progress *, uint32_t);
8686
off_t write_pack_header(struct hashfile *f, uint32_t);
8787
void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
88-
char *index_pack_lockfile(int fd);
88+
char *index_pack_lockfile(int fd, int *is_well_formed);
8989

9090
/*
9191
* The "hdr" output buffer should be at least this big, which will handle sizes

t/t5702-protocol-v2.sh

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -847,8 +847,9 @@ test_expect_success 'part of packfile response provided as URI' '
847847
test -f hfound &&
848848
test -f h2found &&
849849
850-
# Ensure that there are exactly 6 files (3 .pack and 3 .idx).
851-
ls http_child/.git/objects/pack/* >filelist &&
850+
# Ensure that there are exactly 3 packfiles with associated .idx
851+
ls http_child/.git/objects/pack/*.pack \
852+
http_child/.git/objects/pack/*.idx >filelist &&
852853
test_line_count = 6 filelist
853854
'
854855

@@ -901,8 +902,9 @@ test_expect_success 'packfile-uri with transfer.fsckobjects' '
901902
-c fetch.uriprotocols=http,https \
902903
clone "$HTTPD_URL/smart/http_parent" http_child &&
903904
904-
# Ensure that there are exactly 4 files (2 .pack and 2 .idx).
905-
ls http_child/.git/objects/pack/* >filelist &&
905+
# Ensure that there are exactly 2 packfiles with associated .idx
906+
ls http_child/.git/objects/pack/*.pack \
907+
http_child/.git/objects/pack/*.idx >filelist &&
906908
test_line_count = 4 filelist
907909
'
908910

@@ -936,6 +938,54 @@ test_expect_success 'packfile-uri with transfer.fsckobjects fails on bad object'
936938
test_i18ngrep "invalid author/committer line - missing email" error
937939
'
938940

941+
test_expect_success 'packfile-uri with transfer.fsckobjects succeeds when .gitmodules is separate from tree' '
942+
P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
943+
rm -rf "$P" http_child &&
944+
945+
git init "$P" &&
946+
git -C "$P" config "uploadpack.allowsidebandall" "true" &&
947+
948+
echo "[submodule libfoo]" >"$P/.gitmodules" &&
949+
echo "path = include/foo" >>"$P/.gitmodules" &&
950+
echo "url = git://example.com/git/lib.git" >>"$P/.gitmodules" &&
951+
git -C "$P" add .gitmodules &&
952+
git -C "$P" commit -m x &&
953+
954+
configure_exclusion "$P" .gitmodules >h &&
955+
956+
sane_unset GIT_TEST_SIDEBAND_ALL &&
957+
git -c protocol.version=2 -c transfer.fsckobjects=1 \
958+
-c fetch.uriprotocols=http,https \
959+
clone "$HTTPD_URL/smart/http_parent" http_child &&
960+
961+
# Ensure that there are exactly 2 packfiles with associated .idx
962+
ls http_child/.git/objects/pack/*.pack \
963+
http_child/.git/objects/pack/*.idx >filelist &&
964+
test_line_count = 4 filelist
965+
'
966+
967+
test_expect_success 'packfile-uri with transfer.fsckobjects fails when .gitmodules separate from tree is invalid' '
968+
P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
969+
rm -rf "$P" http_child err &&
970+
971+
git init "$P" &&
972+
git -C "$P" config "uploadpack.allowsidebandall" "true" &&
973+
974+
echo "[submodule \"..\"]" >"$P/.gitmodules" &&
975+
echo "path = include/foo" >>"$P/.gitmodules" &&
976+
echo "url = git://example.com/git/lib.git" >>"$P/.gitmodules" &&
977+
git -C "$P" add .gitmodules &&
978+
git -C "$P" commit -m x &&
979+
980+
configure_exclusion "$P" .gitmodules >h &&
981+
982+
sane_unset GIT_TEST_SIDEBAND_ALL &&
983+
test_must_fail git -c protocol.version=2 -c transfer.fsckobjects=1 \
984+
-c fetch.uriprotocols=http,https \
985+
clone "$HTTPD_URL/smart/http_parent" http_child 2>err &&
986+
test_i18ngrep "disallowed submodule name" err
987+
'
988+
939989
# DO NOT add non-httpd-specific tests here, because the last part of this
940990
# test script is only executed when httpd is available and enabled.
941991

0 commit comments

Comments
 (0)