Skip to content

Commit 2e2e7e9

Browse files
committed
Merge branch 'jc/fetch-verify'
* jc/fetch-verify: fetch: verify we have everything we need before updating our ref rev-list --verify-object list-objects: pass callback data to show_objects()
2 parents ca0c976 + 6b67e0d commit 2e2e7e9

File tree

9 files changed

+107
-77
lines changed

9 files changed

+107
-77
lines changed

builtin/fetch.c

Lines changed: 63 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,64 @@ static int update_local_ref(struct ref *ref,
345345
}
346346
}
347347

348+
/*
349+
* The ref_map records the tips of the refs we are fetching. If
350+
*
351+
* $ git rev-list --verify-objects --stdin --not --all
352+
*
353+
* (feeding all the refs in ref_map on its standard input) does not
354+
* error out, that means everything reachable from these updated refs
355+
* locally exists and is connected to some of our existing refs.
356+
*
357+
* Returns 0 if everything is connected, non-zero otherwise.
358+
*/
359+
static int check_everything_connected(struct ref *ref_map, int quiet)
360+
{
361+
struct child_process rev_list;
362+
const char *argv[] = {"rev-list", "--verify-objects",
363+
"--stdin", "--not", "--all", NULL, NULL};
364+
char commit[41];
365+
struct ref *ref;
366+
int err = 0;
367+
368+
if (!ref_map)
369+
return 0;
370+
371+
if (quiet)
372+
argv[5] = "--quiet";
373+
374+
memset(&rev_list, 0, sizeof(rev_list));
375+
rev_list.argv = argv;
376+
rev_list.git_cmd = 1;
377+
rev_list.in = -1;
378+
rev_list.no_stdout = 1;
379+
rev_list.no_stderr = quiet;
380+
if (start_command(&rev_list))
381+
return error(_("Could not run 'git rev-list'"));
382+
383+
sigchain_push(SIGPIPE, SIG_IGN);
384+
385+
memcpy(commit + 40, "\n", 2);
386+
for (ref = ref_map; ref; ref = ref->next) {
387+
memcpy(commit, sha1_to_hex(ref->old_sha1), 40);
388+
if (write_in_full(rev_list.in, commit, 41) < 0) {
389+
if (errno != EPIPE && errno != EINVAL)
390+
error(_("failed write to rev-list: %s"),
391+
strerror(errno));
392+
err = -1;
393+
break;
394+
}
395+
}
396+
if (close(rev_list.in)) {
397+
error(_("failed to close rev-list's stdin: %s"), strerror(errno));
398+
err = -1;
399+
}
400+
401+
sigchain_pop(SIGPIPE);
402+
403+
return finish_command(&rev_list) || err;
404+
}
405+
348406
static int store_updated_refs(const char *raw_url, const char *remote_name,
349407
struct ref *ref_map)
350408
{
@@ -364,6 +422,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
364422
url = transport_anonymize_url(raw_url);
365423
else
366424
url = xstrdup("foreign");
425+
426+
if (check_everything_connected(ref_map, 0))
427+
return error(_("%s did not send all necessary objects\n"), url);
428+
367429
for (rm = ref_map; rm; rm = rm->next) {
368430
struct ref *ref = NULL;
369431

@@ -457,24 +519,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
457519
* We would want to bypass the object transfer altogether if
458520
* everything we are going to fetch already exists and is connected
459521
* locally.
460-
*
461-
* The refs we are going to fetch are in ref_map. If running
462-
*
463-
* $ git rev-list --objects --stdin --not --all
464-
*
465-
* (feeding all the refs in ref_map on its standard input)
466-
* does not error out, that means everything reachable from the
467-
* refs we are going to fetch exists and is connected to some of
468-
* our existing refs.
469522
*/
470523
static int quickfetch(struct ref *ref_map)
471524
{
472-
struct child_process revlist;
473-
struct ref *ref;
474-
int err;
475-
const char *argv[] = {"rev-list",
476-
"--quiet", "--objects", "--stdin", "--not", "--all", NULL};
477-
478525
/*
479526
* If we are deepening a shallow clone we already have these
480527
* objects reachable. Running rev-list here will return with
@@ -484,47 +531,7 @@ static int quickfetch(struct ref *ref_map)
484531
*/
485532
if (depth)
486533
return -1;
487-
488-
if (!ref_map)
489-
return 0;
490-
491-
memset(&revlist, 0, sizeof(revlist));
492-
revlist.argv = argv;
493-
revlist.git_cmd = 1;
494-
revlist.no_stdout = 1;
495-
revlist.no_stderr = 1;
496-
revlist.in = -1;
497-
498-
err = start_command(&revlist);
499-
if (err) {
500-
error(_("could not run rev-list"));
501-
return err;
502-
}
503-
504-
/*
505-
* If rev-list --stdin encounters an unknown commit, it terminates,
506-
* which will cause SIGPIPE in the write loop below.
507-
*/
508-
sigchain_push(SIGPIPE, SIG_IGN);
509-
510-
for (ref = ref_map; ref; ref = ref->next) {
511-
if (write_in_full(revlist.in, sha1_to_hex(ref->old_sha1), 40) < 0 ||
512-
write_str_in_full(revlist.in, "\n") < 0) {
513-
if (errno != EPIPE && errno != EINVAL)
514-
error(_("failed write to rev-list: %s"), strerror(errno));
515-
err = -1;
516-
break;
517-
}
518-
}
519-
520-
if (close(revlist.in)) {
521-
error(_("failed to close rev-list's stdin: %s"), strerror(errno));
522-
err = -1;
523-
}
524-
525-
sigchain_pop(SIGPIPE);
526-
527-
return finish_command(&revlist) || err;
534+
return check_everything_connected(ref_map, 1);
528535
}
529536

530537
static int fetch_refs(struct transport *transport, struct ref *ref_map)

builtin/pack-objects.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2073,7 +2073,9 @@ static void show_commit(struct commit *commit, void *data)
20732073
commit->object.flags |= OBJECT_ADDED;
20742074
}
20752075

2076-
static void show_object(struct object *obj, const struct name_path *path, const char *last)
2076+
static void show_object(struct object *obj,
2077+
const struct name_path *path, const char *last,
2078+
void *data)
20772079
{
20782080
char *name = path_name(path, last);
20792081

builtin/rev-list.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,23 @@ static void finish_commit(struct commit *commit, void *data)
168168
commit->buffer = NULL;
169169
}
170170

171-
static void finish_object(struct object *obj, const struct name_path *path, const char *name)
171+
static void finish_object(struct object *obj,
172+
const struct name_path *path, const char *name,
173+
void *cb_data)
172174
{
173175
if (obj->type == OBJ_BLOB && !has_sha1_file(obj->sha1))
174176
die("missing blob object '%s'", sha1_to_hex(obj->sha1));
175177
}
176178

177-
static void show_object(struct object *obj, const struct name_path *path, const char *component)
179+
static void show_object(struct object *obj,
180+
const struct name_path *path, const char *component,
181+
void *cb_data)
178182
{
179-
finish_object(obj, path, component);
183+
struct rev_info *info = cb_data;
184+
185+
finish_object(obj, path, component, cb_data);
186+
if (info->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
187+
parse_object(obj->sha1);
180188
show_object_with_name(stdout, obj, path, component);
181189
}
182190

list-objects.c

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ static void process_blob(struct rev_info *revs,
1212
struct blob *blob,
1313
show_object_fn show,
1414
struct name_path *path,
15-
const char *name)
15+
const char *name,
16+
void *cb_data)
1617
{
1718
struct object *obj = &blob->object;
1819

@@ -23,7 +24,7 @@ static void process_blob(struct rev_info *revs,
2324
if (obj->flags & (UNINTERESTING | SEEN))
2425
return;
2526
obj->flags |= SEEN;
26-
show(obj, path, name);
27+
show(obj, path, name, cb_data);
2728
}
2829

2930
/*
@@ -52,7 +53,8 @@ static void process_gitlink(struct rev_info *revs,
5253
const unsigned char *sha1,
5354
show_object_fn show,
5455
struct name_path *path,
55-
const char *name)
56+
const char *name,
57+
void *cb_data)
5658
{
5759
/* Nothing to do */
5860
}
@@ -62,7 +64,8 @@ static void process_tree(struct rev_info *revs,
6264
show_object_fn show,
6365
struct name_path *path,
6466
struct strbuf *base,
65-
const char *name)
67+
const char *name,
68+
void *cb_data)
6669
{
6770
struct object *obj = &tree->object;
6871
struct tree_desc desc;
@@ -80,7 +83,7 @@ static void process_tree(struct rev_info *revs,
8083
if (parse_tree(tree) < 0)
8184
die("bad tree object %s", sha1_to_hex(obj->sha1));
8285
obj->flags |= SEEN;
83-
show(obj, path, name);
86+
show(obj, path, name, cb_data);
8487
me.up = path;
8588
me.elem = name;
8689
me.elem_len = strlen(name);
@@ -106,14 +109,17 @@ static void process_tree(struct rev_info *revs,
106109
if (S_ISDIR(entry.mode))
107110
process_tree(revs,
108111
lookup_tree(entry.sha1),
109-
show, &me, base, entry.path);
112+
show, &me, base, entry.path,
113+
cb_data);
110114
else if (S_ISGITLINK(entry.mode))
111115
process_gitlink(revs, entry.sha1,
112-
show, &me, entry.path);
116+
show, &me, entry.path,
117+
cb_data);
113118
else
114119
process_blob(revs,
115120
lookup_blob(entry.sha1),
116-
show, &me, entry.path);
121+
show, &me, entry.path,
122+
cb_data);
117123
}
118124
strbuf_setlen(base, baselen);
119125
free(tree->buffer);
@@ -185,17 +191,17 @@ void traverse_commit_list(struct rev_info *revs,
185191
continue;
186192
if (obj->type == OBJ_TAG) {
187193
obj->flags |= SEEN;
188-
show_object(obj, NULL, name);
194+
show_object(obj, NULL, name, data);
189195
continue;
190196
}
191197
if (obj->type == OBJ_TREE) {
192198
process_tree(revs, (struct tree *)obj, show_object,
193-
NULL, &base, name);
199+
NULL, &base, name, data);
194200
continue;
195201
}
196202
if (obj->type == OBJ_BLOB) {
197203
process_blob(revs, (struct blob *)obj, show_object,
198-
NULL, name);
204+
NULL, name, data);
199205
continue;
200206
}
201207
die("unknown pending object %s (%s)",

list-objects.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
#define LIST_OBJECTS_H
33

44
typedef void (*show_commit_fn)(struct commit *, void *);
5-
typedef void (*show_object_fn)(struct object *, const struct name_path *, const char *);
6-
typedef void (*show_edge_fn)(struct commit *);
7-
5+
typedef void (*show_object_fn)(struct object *, const struct name_path *, const char *, void *);
86
void traverse_commit_list(struct rev_info *, show_commit_fn, show_object_fn, void *);
97

8+
typedef void (*show_edge_fn)(struct commit *);
109
void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn);
1110

1211
#endif

revision.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,6 +1416,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
14161416
revs->tree_objects = 1;
14171417
revs->blob_objects = 1;
14181418
revs->edge_hint = 1;
1419+
} else if (!strcmp(arg, "--verify-objects")) {
1420+
revs->tag_objects = 1;
1421+
revs->tree_objects = 1;
1422+
revs->blob_objects = 1;
1423+
revs->verify_objects = 1;
14191424
} else if (!strcmp(arg, "--unpacked")) {
14201425
revs->unpacked = 1;
14211426
} else if (!prefixcmp(arg, "--unpacked=")) {

revision.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ struct rev_info {
7373
tag_objects:1,
7474
tree_objects:1,
7575
blob_objects:1,
76+
verify_objects:1,
7677
edge_hint:1,
7778
limited:1,
7879
unpacked:1,

t/t5504-fetch-receive-strict.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ test_expect_success 'fetch without strict' '
2222
cd dst &&
2323
git config fetch.fsckobjects false &&
2424
git config transfer.fsckobjects false &&
25-
git fetch ../.git master
25+
test_must_fail git fetch ../.git master
2626
)
2727
'
2828

@@ -33,7 +33,7 @@ test_expect_success 'fetch with !fetch.fsckobjects' '
3333
cd dst &&
3434
git config fetch.fsckobjects false &&
3535
git config transfer.fsckobjects true &&
36-
git fetch ../.git master
36+
test_must_fail git fetch ../.git master
3737
)
3838
'
3939

upload-pack.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ static void show_commit(struct commit *commit, void *data)
8484
commit->buffer = NULL;
8585
}
8686

87-
static void show_object(struct object *obj, const struct name_path *path, const char *component)
87+
static void show_object(struct object *obj,
88+
const struct name_path *path, const char *component,
89+
void *cb_data)
8890
{
8991
show_object_with_name(pack_pipe, obj, path, component);
9092
}

0 commit comments

Comments
 (0)