diff options
-rw-r--r-- | builtin/fetch.c | 119 | ||||
-rw-r--r-- | builtin/pack-objects.c | 4 | ||||
-rw-r--r-- | builtin/rev-list.c | 14 | ||||
-rw-r--r-- | list-objects.c | 28 | ||||
-rw-r--r-- | list-objects.h | 5 | ||||
-rw-r--r-- | revision.c | 5 | ||||
-rw-r--r-- | revision.h | 1 | ||||
-rwxr-xr-x | t/t5504-fetch-receive-strict.sh | 4 | ||||
-rw-r--r-- | upload-pack.c | 4 |
9 files changed, 107 insertions, 77 deletions
diff --git a/builtin/fetch.c b/builtin/fetch.c index e422ced929..ead8c9d5c3 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -345,6 +345,64 @@ static int update_local_ref(struct ref *ref, } } +/* + * The ref_map records the tips of the refs we are fetching. If + * + * $ git rev-list --verify-objects --stdin --not --all + * + * (feeding all the refs in ref_map on its standard input) does not + * error out, that means everything reachable from these updated refs + * locally exists and is connected to some of our existing refs. + * + * Returns 0 if everything is connected, non-zero otherwise. + */ +static int check_everything_connected(struct ref *ref_map, int quiet) +{ + struct child_process rev_list; + const char *argv[] = {"rev-list", "--verify-objects", + "--stdin", "--not", "--all", NULL, NULL}; + char commit[41]; + struct ref *ref; + int err = 0; + + if (!ref_map) + return 0; + + if (quiet) + argv[5] = "--quiet"; + + memset(&rev_list, 0, sizeof(rev_list)); + rev_list.argv = argv; + rev_list.git_cmd = 1; + rev_list.in = -1; + rev_list.no_stdout = 1; + rev_list.no_stderr = quiet; + if (start_command(&rev_list)) + return error(_("Could not run 'git rev-list'")); + + sigchain_push(SIGPIPE, SIG_IGN); + + memcpy(commit + 40, "\n", 2); + for (ref = ref_map; ref; ref = ref->next) { + memcpy(commit, sha1_to_hex(ref->old_sha1), 40); + if (write_in_full(rev_list.in, commit, 41) < 0) { + if (errno != EPIPE && errno != EINVAL) + error(_("failed write to rev-list: %s"), + strerror(errno)); + err = -1; + break; + } + } + if (close(rev_list.in)) { + error(_("failed to close rev-list's stdin: %s"), strerror(errno)); + err = -1; + } + + sigchain_pop(SIGPIPE); + + return finish_command(&rev_list) || err; +} + static int store_updated_refs(const char *raw_url, const char *remote_name, struct ref *ref_map) { @@ -364,6 +422,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, url = transport_anonymize_url(raw_url); else url = xstrdup("foreign"); + + if (check_everything_connected(ref_map, 0)) + return error(_("%s did not send all necessary objects\n"), url); + for (rm = ref_map; rm; rm = rm->next) { struct ref *ref = NULL; @@ -457,24 +519,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, * We would want to bypass the object transfer altogether if * everything we are going to fetch already exists and is connected * locally. - * - * The refs we are going to fetch are in ref_map. If running - * - * $ git rev-list --objects --stdin --not --all - * - * (feeding all the refs in ref_map on its standard input) - * does not error out, that means everything reachable from the - * refs we are going to fetch exists and is connected to some of - * our existing refs. */ static int quickfetch(struct ref *ref_map) { - struct child_process revlist; - struct ref *ref; - int err; - const char *argv[] = {"rev-list", - "--quiet", "--objects", "--stdin", "--not", "--all", NULL}; - /* * If we are deepening a shallow clone we already have these * objects reachable. Running rev-list here will return with @@ -484,47 +531,7 @@ static int quickfetch(struct ref *ref_map) */ if (depth) return -1; - - if (!ref_map) - return 0; - - memset(&revlist, 0, sizeof(revlist)); - revlist.argv = argv; - revlist.git_cmd = 1; - revlist.no_stdout = 1; - revlist.no_stderr = 1; - revlist.in = -1; - - err = start_command(&revlist); - if (err) { - error(_("could not run rev-list")); - return err; - } - - /* - * If rev-list --stdin encounters an unknown commit, it terminates, - * which will cause SIGPIPE in the write loop below. - */ - sigchain_push(SIGPIPE, SIG_IGN); - - for (ref = ref_map; ref; ref = ref->next) { - if (write_in_full(revlist.in, sha1_to_hex(ref->old_sha1), 40) < 0 || - write_str_in_full(revlist.in, "\n") < 0) { - if (errno != EPIPE && errno != EINVAL) - error(_("failed write to rev-list: %s"), strerror(errno)); - err = -1; - break; - } - } - - if (close(revlist.in)) { - error(_("failed to close rev-list's stdin: %s"), strerror(errno)); - err = -1; - } - - sigchain_pop(SIGPIPE); - - return finish_command(&revlist) || err; + return check_everything_connected(ref_map, 1); } static int fetch_refs(struct transport *transport, struct ref *ref_map) diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index a9c67c18ba..2b18de5dc3 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -2073,7 +2073,9 @@ static void show_commit(struct commit *commit, void *data) commit->object.flags |= OBJECT_ADDED; } -static void show_object(struct object *obj, const struct name_path *path, const char *last) +static void show_object(struct object *obj, + const struct name_path *path, const char *last, + void *data) { char *name = path_name(path, last); diff --git a/builtin/rev-list.c b/builtin/rev-list.c index f5ce4873e3..ab3be7ca82 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -168,15 +168,23 @@ static void finish_commit(struct commit *commit, void *data) commit->buffer = NULL; } -static void finish_object(struct object *obj, const struct name_path *path, const char *name) +static void finish_object(struct object *obj, + const struct name_path *path, const char *name, + void *cb_data) { if (obj->type == OBJ_BLOB && !has_sha1_file(obj->sha1)) die("missing blob object '%s'", sha1_to_hex(obj->sha1)); } -static void show_object(struct object *obj, const struct name_path *path, const char *component) +static void show_object(struct object *obj, + const struct name_path *path, const char *component, + void *cb_data) { - finish_object(obj, path, component); + struct rev_info *info = cb_data; + + finish_object(obj, path, component, cb_data); + if (info->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT) + parse_object(obj->sha1); show_object_with_name(stdout, obj, path, component); } diff --git a/list-objects.c b/list-objects.c index 0fb44e7ed7..39d80c0175 100644 --- a/list-objects.c +++ b/list-objects.c @@ -12,7 +12,8 @@ static void process_blob(struct rev_info *revs, struct blob *blob, show_object_fn show, struct name_path *path, - const char *name) + const char *name, + void *cb_data) { struct object *obj = &blob->object; @@ -23,7 +24,7 @@ static void process_blob(struct rev_info *revs, if (obj->flags & (UNINTERESTING | SEEN)) return; obj->flags |= SEEN; - show(obj, path, name); + show(obj, path, name, cb_data); } /* @@ -52,7 +53,8 @@ static void process_gitlink(struct rev_info *revs, const unsigned char *sha1, show_object_fn show, struct name_path *path, - const char *name) + const char *name, + void *cb_data) { /* Nothing to do */ } @@ -62,7 +64,8 @@ static void process_tree(struct rev_info *revs, show_object_fn show, struct name_path *path, struct strbuf *base, - const char *name) + const char *name, + void *cb_data) { struct object *obj = &tree->object; struct tree_desc desc; @@ -80,7 +83,7 @@ static void process_tree(struct rev_info *revs, if (parse_tree(tree) < 0) die("bad tree object %s", sha1_to_hex(obj->sha1)); obj->flags |= SEEN; - show(obj, path, name); + show(obj, path, name, cb_data); me.up = path; me.elem = name; me.elem_len = strlen(name); @@ -106,14 +109,17 @@ static void process_tree(struct rev_info *revs, if (S_ISDIR(entry.mode)) process_tree(revs, lookup_tree(entry.sha1), - show, &me, base, entry.path); + show, &me, base, entry.path, + cb_data); else if (S_ISGITLINK(entry.mode)) process_gitlink(revs, entry.sha1, - show, &me, entry.path); + show, &me, entry.path, + cb_data); else process_blob(revs, lookup_blob(entry.sha1), - show, &me, entry.path); + show, &me, entry.path, + cb_data); } strbuf_setlen(base, baselen); free(tree->buffer); @@ -185,17 +191,17 @@ void traverse_commit_list(struct rev_info *revs, continue; if (obj->type == OBJ_TAG) { obj->flags |= SEEN; - show_object(obj, NULL, name); + show_object(obj, NULL, name, data); continue; } if (obj->type == OBJ_TREE) { process_tree(revs, (struct tree *)obj, show_object, - NULL, &base, name); + NULL, &base, name, data); continue; } if (obj->type == OBJ_BLOB) { process_blob(revs, (struct blob *)obj, show_object, - NULL, name); + NULL, name, data); continue; } die("unknown pending object %s (%s)", diff --git a/list-objects.h b/list-objects.h index d65dbf03e6..3db7bb6fa3 100644 --- a/list-objects.h +++ b/list-objects.h @@ -2,11 +2,10 @@ #define LIST_OBJECTS_H typedef void (*show_commit_fn)(struct commit *, void *); -typedef void (*show_object_fn)(struct object *, const struct name_path *, const char *); -typedef void (*show_edge_fn)(struct commit *); - +typedef void (*show_object_fn)(struct object *, const struct name_path *, const char *, void *); void traverse_commit_list(struct rev_info *, show_commit_fn, show_object_fn, void *); +typedef void (*show_edge_fn)(struct commit *); void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn); #endif diff --git a/revision.c b/revision.c index 19a493bf60..9bae329c15 100644 --- a/revision.c +++ b/revision.c @@ -1416,6 +1416,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->tree_objects = 1; revs->blob_objects = 1; revs->edge_hint = 1; + } else if (!strcmp(arg, "--verify-objects")) { + revs->tag_objects = 1; + revs->tree_objects = 1; + revs->blob_objects = 1; + revs->verify_objects = 1; } else if (!strcmp(arg, "--unpacked")) { revs->unpacked = 1; } else if (!prefixcmp(arg, "--unpacked=")) { diff --git a/revision.h b/revision.h index 8eeb542dfb..754f31b1cd 100644 --- a/revision.h +++ b/revision.h @@ -73,6 +73,7 @@ struct rev_info { tag_objects:1, tree_objects:1, blob_objects:1, + verify_objects:1, edge_hint:1, limited:1, unpacked:1, diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh index 66100124a9..8341fc4d15 100755 --- a/t/t5504-fetch-receive-strict.sh +++ b/t/t5504-fetch-receive-strict.sh @@ -22,7 +22,7 @@ test_expect_success 'fetch without strict' ' cd dst && git config fetch.fsckobjects false && git config transfer.fsckobjects false && - git fetch ../.git master + test_must_fail git fetch ../.git master ) ' @@ -33,7 +33,7 @@ test_expect_success 'fetch with !fetch.fsckobjects' ' cd dst && git config fetch.fsckobjects false && git config transfer.fsckobjects true && - git fetch ../.git master + test_must_fail git fetch ../.git master ) ' diff --git a/upload-pack.c b/upload-pack.c index 31686712c7..470cffd7c1 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -84,7 +84,9 @@ static void show_commit(struct commit *commit, void *data) commit->buffer = NULL; } -static void show_object(struct object *obj, const struct name_path *path, const char *component) +static void show_object(struct object *obj, + const struct name_path *path, const char *component, + void *cb_data) { show_object_with_name(pack_pipe, obj, path, component); } |