From: Junio C Hamano Date: Tue, 24 Jul 2018 21:50:44 +0000 (-0700) Subject: Merge branch 'jt/connectivity-check-after-unshallow' X-Git-Tag: v2.19.0-rc0~137 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/88df0fa6597fd32b0394c544fb37d6655bd34a13?hp=-c Merge branch 'jt/connectivity-check-after-unshallow' "git fetch" failed to correctly validate the set of objects it received when making a shallow history deeper, which has been corrected. * jt/connectivity-check-after-unshallow: fetch-pack: write shallow, then check connectivity fetch-pack: implement ref-in-want fetch-pack: put shallow info in output parameter fetch: refactor to make function args narrower fetch: refactor fetch_refs into two functions fetch: refactor the population of peer ref OIDs upload-pack: test negotiation with changing repository upload-pack: implement ref-in-want test-pkt-line: add unpack-sideband subcommand --- 88df0fa6597fd32b0394c544fb37d6655bd34a13 diff --combined Documentation/config.txt index a32172a43c,fb1dd7428a..43b2de7b5f --- a/Documentation/config.txt +++ b/Documentation/config.txt @@@ -354,7 -354,7 +354,7 @@@ advice.*: Advice on what to do when you've accidentally added one git repo inside of another. ignoredHook:: - Advice shown if an hook is ignored because the hook is not + Advice shown if a hook is ignored because the hook is not set as executable. waitingForEditor:: Print a message to the terminal whenever Git is waiting for @@@ -390,19 -390,16 +390,19 @@@ core.hideDotFiles: default mode is 'dotGitOnly'. core.ignoreCase:: - If true, this option enables various workarounds to enable + Internal variable which enables various workarounds to enable Git to work better on filesystems that are not case sensitive, - like FAT. For example, if a directory listing finds - "makefile" when Git expects "Makefile", Git will assume + like APFS, HFS+, FAT, NTFS, etc. For example, if a directory listing + finds "makefile" when Git expects "Makefile", Git will assume it is really the same file, and continue to remember it as "Makefile". + The default is false, except linkgit:git-clone[1] or linkgit:git-init[1] will probe and set core.ignoreCase true if appropriate when the repository is created. ++ +Git relies on the proper configuration of this variable for your operating +and file system. Modifying this value may result in unexpected behavior. core.precomposeUnicode:: This option is only used by Mac OS implementation of Git. @@@ -1165,8 -1162,7 +1165,8 @@@ color.diff.: color.decorate.:: Use customized color for 'git log --decorate' output. `` is one of `branch`, `remoteBranch`, `tag`, `stash` or `HEAD` for local - branches, remote-tracking branches, tags, stash and HEAD, respectively. + branches, remote-tracking branches, tags, stash and HEAD, respectively + and `grafted` for grafted commits. color.grep:: When set to `always`, always highlight matches. When `false` (or @@@ -1185,10 -1181,8 +1185,10 @@@ color.grep.: filename prefix (when not using `-h`) `function`;; function name lines (when using `-p`) -`linenumber`;; +`lineNumber`;; line number prefix (when using `-n`) +`column`;; + column number prefix (when using `--column`) `match`;; matching text (same as setting `matchContext` and `matchSelected`) `matchContext`;; @@@ -1803,9 -1797,6 +1803,9 @@@ gitweb.snapshot: grep.lineNumber:: If set to true, enable `-n` option by default. +grep.column:: + If set to true, enable the `--column` option by default. + grep.patternType:: Set the default matching behavior. Using a value of 'basic', 'extended', 'fixed', or 'perl' will enable the `--basic-regexp`, `--extended-regexp`, @@@ -3336,13 -3327,12 +3336,13 @@@ submodule..ignore: submodule..active:: Boolean value indicating if the submodule is of interest to git commands. This config option takes precedence over the - submodule.active config option. + submodule.active config option. See linkgit:gitsubmodules[7] for + details. submodule.active:: A repeated field which contains a pathspec used to match against a submodule's path to determine if the submodule is of interest to git - commands. + commands. See linkgit:gitsubmodules[7] for details. submodule.recurse:: Specifies if commands recurse into submodules by default. This @@@ -3489,6 -3479,13 +3489,13 @@@ Note that this configuration variable i repository-level config (this is a safety measure against fetching from untrusted repositories). + uploadpack.allowRefInWant:: + If this option is set, `upload-pack` will support the `ref-in-want` + feature of the protocol version 2 `fetch` command. This feature + is intended for the benefit of load-balanced servers which may + not have the same view of what OIDs their refs point to due to + replication delay. + url..insteadOf:: Any URL that starts with this value will be rewritten to start, instead, with . In cases where some site serves a diff --combined Documentation/technical/protocol-v2.txt index f58f24b1ef,1da71d0ddb..09e4e0273f --- a/Documentation/technical/protocol-v2.txt +++ b/Documentation/technical/protocol-v2.txt @@@ -64,8 -64,9 +64,8 @@@ When using the http:// or https:// tran info/refs request as described in `http-protocol.txt` and requests that v2 be used by supplying "version=2" in the `Git-Protocol` header. - C: Git-Protocol: version=2 - C: C: GET $GIT_URL/info/refs?service=git-upload-pack HTTP/1.0 + C: Git-Protocol: version=2 A v2 server would reply: @@@ -298,12 -299,21 +298,21 @@@ included in the client's request for use with partial clone and partial fetch operations. See `rev-list` for possible "filter-spec" values. + If the 'ref-in-want' feature is advertised, the following argument can + be included in the client's request as well as the potential addition of + the 'wanted-refs' section in the server's response as explained below. + + want-ref + Indicates to the server that the client wants to retrieve a + particular ref, where is the full name of a ref on the + server. + The response of `fetch` is broken into a number of sections separated by delimiter packets (0001), with each section beginning with its section header. output = *section - section = (acknowledgments | shallow-info | packfile) + section = (acknowledgments | shallow-info | wanted-refs | packfile) (flush-pkt | delim-pkt) acknowledgments = PKT-LINE("acknowledgments" LF) @@@ -318,6 -328,10 +327,10 @@@ shallow = "shallow" SP obj-id unshallow = "unshallow" SP obj-id + wanted-refs = PKT-LINE("wanted-refs" LF) + *PKT-LINE(wanted-ref LF) + wanted-ref = obj-id SP refname + packfile = PKT-LINE("packfile" LF) *PKT-LINE(%x01-03 *%x00-ff) @@@ -378,6 -392,19 +391,19 @@@ * This section is only included if a packfile section is also included in the response. + wanted-refs section + * This section is only included if the client has requested a + ref using a 'want-ref' line and if a packfile section is also + included in the response. + + * Always begins with the section header "wanted-refs". + + * The server will send a ref listing (" ") for + each reference requested using 'want-ref' lines. + + * The server MUST NOT send any refs which were not requested + using 'want-ref' lines. + packfile section * This section is only included if the client has sent 'want' lines in its request and either requested that no more diff --combined builtin/clone.c index 1d939af9d8,8f86d99c51..2f84717e11 --- a/builtin/clone.c +++ b/builtin/clone.c @@@ -15,7 -15,6 +15,7 @@@ #include "fetch-pack.h" #include "refs.h" #include "refspec.h" +#include "object-store.h" #include "tree.h" #include "tree-walk.h" #include "unpack-trees.h" @@@ -1078,7 -1077,7 +1078,7 @@@ int cmd_clone(int argc, const char **ar if (option_required_reference.nr || option_optional_reference.nr) setup_reference(); - refspec_item_init(&refspec, value.buf, REFSPEC_FETCH); + refspec_item_init_or_die(&refspec, value.buf, REFSPEC_FETCH); strbuf_reset(&value); @@@ -1156,7 -1155,7 +1156,7 @@@ } if (!is_local && !complete_refs_before_fetch) - transport_fetch_refs(transport, mapped_refs); + transport_fetch_refs(transport, mapped_refs, NULL); remote_head = find_ref_by_name(refs, "HEAD"); remote_head_points_at = @@@ -1198,7 -1197,7 +1198,7 @@@ if (is_local) clone_local(path, git_dir); else if (refs && complete_refs_before_fetch) - transport_fetch_refs(transport, mapped_refs); + transport_fetch_refs(transport, mapped_refs, NULL); update_remote_refs(refs, mapped_refs, remote_head_points_at, branch_top.buf, reflog_msg.buf, transport, diff --combined builtin/fetch.c index d9bb72bf49,d4b2767d48..ac06f6a576 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@@ -6,7 -6,6 +6,7 @@@ #include "repository.h" #include "refs.h" #include "refspec.h" +#include "object-store.h" #include "commit.h" #include "builtin.h" #include "string-list.h" @@@ -94,6 -93,19 +94,6 @@@ static int git_fetch_config(const char return git_default_config(k, v, cb); } -static int gitmodules_fetch_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "submodule.fetchjobs")) { - max_children = parse_submodule_fetchjobs(var, value); - return 0; - } else if (!strcmp(var, "fetch.recursesubmodules")) { - recurse_submodules = parse_fetch_recurse_submodules_arg(var, value); - return 0; - } - - return 0; -} - static int parse_refmap_arg(const struct option *opt, const char *arg, int unset) { /* @@@ -242,9 -254,9 +242,9 @@@ static int will_fetch(struct ref **head return 0; } - static void find_non_local_tags(struct transport *transport, - struct ref **head, - struct ref ***tail) + static void find_non_local_tags(const struct ref *refs, + struct ref **head, + struct ref ***tail) { struct string_list existing_refs = STRING_LIST_INIT_DUP; struct string_list remote_refs = STRING_LIST_INIT_NODUP; @@@ -252,7 -264,7 +252,7 @@@ struct string_list_item *item = NULL; for_each_ref(add_existing, &existing_refs); - for (ref = transport_get_remote_refs(transport, NULL); ref; ref = ref->next) { + for (ref = refs; ref; ref = ref->next) { if (!starts_with(ref->name, "refs/tags/")) continue; @@@ -326,7 -338,8 +326,8 @@@ string_list_clear(&remote_refs, 0); } - static struct ref *get_ref_map(struct transport *transport, + static struct ref *get_ref_map(struct remote *remote, + const struct ref *remote_refs, struct refspec *rs, int tags, int *autotags) { @@@ -334,26 -347,11 +335,11 @@@ struct ref *rm; struct ref *ref_map = NULL; struct ref **tail = &ref_map; - struct argv_array ref_prefixes = ARGV_ARRAY_INIT; /* opportunistically-updated references: */ struct ref *orefs = NULL, **oref_tail = &orefs; - const struct ref *remote_refs; - - if (rs->nr) - refspec_ref_prefixes(rs, &ref_prefixes); - else if (transport->remote && transport->remote->fetch.nr) - refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes); - - if (ref_prefixes.argc && - (tags == TAGS_SET || (tags == TAGS_DEFAULT && !rs->nr))) { - argv_array_push(&ref_prefixes, "refs/tags/"); - } - - remote_refs = transport_get_remote_refs(transport, &ref_prefixes); - - argv_array_clear(&ref_prefixes); + struct string_list existing_refs = STRING_LIST_INIT_DUP; if (rs->nr) { struct refspec *fetch_refspec; @@@ -390,7 -388,7 +376,7 @@@ if (refmap.nr) fetch_refspec = &refmap; else - fetch_refspec = &transport->remote->fetch; + fetch_refspec = &remote->fetch; for (i = 0; i < fetch_refspec->nr; i++) get_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1); @@@ -398,7 -396,6 +384,6 @@@ die("--refmap option is only meaningful with command-line refspec(s)."); } else { /* Use the defaults */ - struct remote *remote = transport->remote; struct branch *branch = branch_get(NULL); int has_merge = branch_has_merge_config(branch); if (remote && @@@ -437,7 -434,7 +422,7 @@@ /* also fetch all tags */ get_fetch_map(remote_refs, tag_refspec, &tail, 0); else if (tags == TAGS_DEFAULT && *autotags) - find_non_local_tags(transport, &ref_map, &tail); + find_non_local_tags(remote_refs, &ref_map, &tail); /* Now append any refs to be updated opportunistically: */ *tail = orefs; @@@ -446,7 -443,23 +431,23 @@@ tail = &rm->next; } - return ref_remove_duplicates(ref_map); + ref_map = ref_remove_duplicates(ref_map); + + for_each_ref(add_existing, &existing_refs); + for (rm = ref_map; rm; rm = rm->next) { + if (rm->peer_ref) { + struct string_list_item *peer_item = + string_list_lookup(&existing_refs, + rm->peer_ref->name); + if (peer_item) { + struct object_id *old_oid = peer_item->util; + oidcpy(&rm->peer_ref->old_oid, old_oid); + } + } + } + string_list_clear(&existing_refs, 1); + + return ref_map; } #define STORE_REF_ERROR_OTHER 1 @@@ -756,7 -769,7 +757,7 @@@ static int iterate_ref_map(void *cb_dat } static int store_updated_refs(const char *raw_url, const char *remote_name, - struct ref *ref_map) + int connectivity_checked, struct ref *ref_map) { FILE *fp; struct commit *commit; @@@ -765,7 -778,7 +766,7 @@@ const char *what, *kind; struct ref *rm; char *url; - const char *filename = dry_run ? "/dev/null" : git_path_fetch_head(); + const char *filename = dry_run ? "/dev/null" : git_path_fetch_head(the_repository); int want_status; int summary_width = transport_summary_width(ref_map); @@@ -778,10 -791,12 +779,12 @@@ else url = xstrdup("foreign"); - rm = ref_map; - if (check_connected(iterate_ref_map, &rm, NULL)) { - rc = error(_("%s did not send all necessary objects\n"), url); - goto abort; + if (!connectivity_checked) { + rm = ref_map; + if (check_connected(iterate_ref_map, &rm, NULL)) { + rc = error(_("%s did not send all necessary objects\n"), url); + goto abort; + } } prepare_format_display(ref_map); @@@ -933,15 -948,32 +936,32 @@@ static int quickfetch(struct ref *ref_m return check_connected(iterate_ref_map, &rm, &opt); } - static int fetch_refs(struct transport *transport, struct ref *ref_map) + static int fetch_refs(struct transport *transport, struct ref *ref_map, + struct ref **updated_remote_refs) { int ret = quickfetch(ref_map); if (ret) - ret = transport_fetch_refs(transport, ref_map); + ret = transport_fetch_refs(transport, ref_map, + updated_remote_refs); if (!ret) - ret |= store_updated_refs(transport->url, - transport->remote->name, - ref_map); + /* + * Keep the new pack's ".keep" file around to allow the caller + * time to update refs to reference the new objects. + */ + return 0; + transport_unlock_pack(transport); + return ret; + } + + /* Update local refs based on the ref values fetched from a remote */ + static int consume_refs(struct transport *transport, struct ref *ref_map) + { + int connectivity_checked = transport->smart_options + ? transport->smart_options->connectivity_checked : 0; + int ret = store_updated_refs(transport->url, + transport->remote->name, + connectivity_checked, + ref_map); transport_unlock_pack(transport); return ret; } @@@ -1017,7 -1049,7 +1037,7 @@@ static void check_not_current_branch(st static int truncate_fetch_head(void) { - const char *filename = git_path_fetch_head(); + const char *filename = git_path_fetch_head(the_repository); FILE *fp = fopen_for_writing(filename); if (!fp) @@@ -1087,7 -1119,8 +1107,8 @@@ static void backfill_tags(struct transp transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL); transport_set_option(transport, TRANS_OPT_DEPTH, "0"); transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL); - fetch_refs(transport, ref_map); + if (!fetch_refs(transport, ref_map, NULL)) + consume_refs(transport, ref_map); if (gsecondary) { transport_disconnect(gsecondary); @@@ -1098,13 -1131,12 +1119,12 @@@ static int do_fetch(struct transport *transport, struct refspec *rs) { - struct string_list existing_refs = STRING_LIST_INIT_DUP; struct ref *ref_map; - struct ref *rm; int autotags = (transport->remote->fetch_tags == 1); int retcode = 0; - - for_each_ref(add_existing, &existing_refs); + const struct ref *remote_refs; + struct ref *updated_remote_refs = NULL; + struct argv_array ref_prefixes = ARGV_ARRAY_INIT; if (tags == TAGS_DEFAULT) { if (transport->remote->fetch_tags == 2) @@@ -1120,22 -1152,24 +1140,24 @@@ goto cleanup; } - ref_map = get_ref_map(transport, rs, tags, &autotags); - if (!update_head_ok) - check_not_current_branch(ref_map); + if (rs->nr) + refspec_ref_prefixes(rs, &ref_prefixes); + else if (transport->remote && transport->remote->fetch.nr) + refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes); - for (rm = ref_map; rm; rm = rm->next) { - if (rm->peer_ref) { - struct string_list_item *peer_item = - string_list_lookup(&existing_refs, - rm->peer_ref->name); - if (peer_item) { - struct object_id *old_oid = peer_item->util; - oidcpy(&rm->peer_ref->old_oid, old_oid); - } - } + if (ref_prefixes.argc && + (tags == TAGS_SET || (tags == TAGS_DEFAULT && !rs->nr))) { + argv_array_push(&ref_prefixes, "refs/tags/"); } + remote_refs = transport_get_remote_refs(transport, &ref_prefixes); + argv_array_clear(&ref_prefixes); + + ref_map = get_ref_map(transport->remote, remote_refs, rs, + tags, &autotags); + if (!update_head_ok) + check_not_current_branch(ref_map); + if (tags == TAGS_DEFAULT && autotags) transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1"); if (prune) { @@@ -1152,7 -1186,24 +1174,24 @@@ transport->url); } } - if (fetch_refs(transport, ref_map)) { + + if (fetch_refs(transport, ref_map, &updated_remote_refs)) { + free_refs(ref_map); + retcode = 1; + goto cleanup; + } + if (updated_remote_refs) { + /* + * Regenerate ref_map using the updated remote refs. This is + * to account for additional information which may be provided + * by the transport (e.g. shallow info). + */ + free_refs(ref_map); + ref_map = get_ref_map(transport->remote, updated_remote_refs, rs, + tags, &autotags); + free_refs(updated_remote_refs); + } + if (consume_refs(transport, ref_map)) { free_refs(ref_map); retcode = 1; goto cleanup; @@@ -1164,14 -1215,13 +1203,13 @@@ if (tags == TAGS_DEFAULT && autotags) { struct ref **tail = &ref_map; ref_map = NULL; - find_non_local_tags(transport, &ref_map, &tail); + find_non_local_tags(remote_refs, &ref_map, &tail); if (ref_map) backfill_tags(transport, ref_map); free_refs(ref_map); } cleanup: - string_list_clear(&existing_refs, 1); return retcode; } @@@ -1421,7 -1471,7 +1459,7 @@@ int cmd_fetch(int argc, const char **ar for (i = 1; i < argc; i++) strbuf_addf(&default_rla, " %s", argv[i]); - config_from_gitmodules(gitmodules_fetch_config, NULL); + fetch_config_from_gitmodules(&max_children, &recurse_submodules); git_config(git_fetch_config, NULL); argc = parse_options(argc, argv, prefix, @@@ -1437,7 -1487,7 +1475,7 @@@ if (unshallow) { if (depth) die(_("--depth and --unshallow cannot be used together")); - else if (!is_repository_shallow()) + else if (!is_repository_shallow(the_repository)) die(_("--unshallow on a complete repository does not make sense")); else depth = xstrfmt("%d", INFINITE_DEPTH); diff --combined fetch-pack.c index dbd879ac7f,60bbffb976..7ccb9c0d45 --- a/fetch-pack.c +++ b/fetch-pack.c @@@ -19,7 -19,7 +19,8 @@@ #include "sha1-array.h" #include "oidset.h" #include "packfile.h" +#include "object-store.h" + #include "connected.h" static int transfer_unpack_limit = -1; static int fetch_unpack_limit = -1; @@@ -397,7 -397,7 +398,7 @@@ static int find_common(struct fetch_pac return 1; } - if (is_repository_shallow()) + if (is_repository_shallow(the_repository)) write_shallow_commits(&req_buf, 1, NULL); if (args->depth > 0) packet_buf_write(&req_buf, "deepen %d", args->depth); @@@ -428,7 -428,7 +429,7 @@@ if (skip_prefix(line, "shallow ", &arg)) { if (get_oid_hex(arg, &oid)) die(_("invalid shallow line: %s"), line); - register_shallow(&oid); + register_shallow(the_repository, &oid); continue; } if (skip_prefix(line, "unshallow ", &arg)) { @@@ -658,11 -658,11 +659,11 @@@ static void filter_refs(struct fetch_pa } i++; } - } - if (!keep && args->fetch_all && - (!args->deepen || !starts_with(ref->name, "refs/tags/"))) - keep = 1; + if (!keep && args->fetch_all && + (!args->deepen || !starts_with(ref->name, "refs/tags/"))) + keep = 1; + } if (keep) { *newtail = ref; @@@ -986,7 -986,7 +987,7 @@@ static struct ref *do_fetch_pack(struc sort_ref_list(&ref, ref_compare_name); QSORT(sought, nr_sought, cmp_ref_by_name); - if ((args->depth > 0 || is_repository_shallow()) && !server_supports("shallow")) + if ((args->depth > 0 || is_repository_shallow(the_repository)) && !server_supports("shallow")) die(_("Server does not support shallow clients")); if (args->depth > 0 || args->deepen_since || args->deepen_not) args->deepen = 1; @@@ -1084,7 -1084,7 +1085,7 @@@ static void add_shallow_requests(struct strbuf *req_buf, const struct fetch_pack_args *args) { - if (is_repository_shallow()) + if (is_repository_shallow(the_repository)) write_shallow_commits(req_buf, 1, NULL); if (args->depth > 0) packet_buf_write(req_buf, "deepen %d", args->depth); @@@ -1103,9 -1103,10 +1104,10 @@@ static void add_wants(const struct ref *wants, struct strbuf *req_buf) { + int use_ref_in_want = server_supports_feature("fetch", "ref-in-want", 0); + for ( ; wants ; wants = wants->next) { const struct object_id *remote = &wants->old_oid; - const char *remote_hex; struct object *o; /* @@@ -1123,8 -1124,10 +1125,10 @@@ continue; } - remote_hex = oid_to_hex(remote); - packet_buf_write(req_buf, "want %s\n", remote_hex); + if (!use_ref_in_want || wants->exact_oid) + packet_buf_write(req_buf, "want %s\n", oid_to_hex(remote)); + else + packet_buf_write(req_buf, "want-ref %s\n", wants->name); } } @@@ -1196,7 -1199,7 +1200,7 @@@ static int send_fetch_request(int fd_ou /* Add shallow-info and deepen request */ if (server_supports_feature("fetch", "shallow", 0)) add_shallow_requests(&req_buf, args); - else if (is_repository_shallow() || args->deepen) + else if (is_repository_shallow(the_repository) || args->deepen) die(_("Server does not support shallow requests")); /* Add filter */ @@@ -1309,7 -1312,7 +1313,7 @@@ static void receive_shallow_info(struc if (skip_prefix(reader->line, "shallow ", &arg)) { if (get_oid_hex(arg, &oid)) die(_("invalid shallow line: %s"), reader->line); - register_shallow(&oid); + register_shallow(the_repository, &oid); continue; } if (skip_prefix(reader->line, "unshallow ", &arg)) { @@@ -1335,6 -1338,32 +1339,32 @@@ args->deepen = 1; } + static void receive_wanted_refs(struct packet_reader *reader, struct ref *refs) + { + process_section_header(reader, "wanted-refs", 0); + while (packet_reader_read(reader) == PACKET_READ_NORMAL) { + struct object_id oid; + const char *end; + struct ref *r = NULL; + + if (parse_oid_hex(reader->line, &oid, &end) || *end++ != ' ') + die("expected wanted-ref, got '%s'", reader->line); + + for (r = refs; r; r = r->next) { + if (!strcmp(end, r->name)) { + oidcpy(&r->old_oid, &oid); + break; + } + } + + if (!r) + die("unexpected wanted-ref: '%s'", reader->line); + } + + if (reader->status != PACKET_READ_DELIM) + die("error processing wanted refs: %d", reader->status); + } + enum fetch_state { FETCH_CHECK_LOCAL = 0, FETCH_SEND_REQUEST, @@@ -1409,6 -1438,9 +1439,9 @@@ static struct ref *do_fetch_pack_v2(str if (process_section_header(&reader, "shallow-info", 1)) receive_shallow_info(args, &reader); + if (process_section_header(&reader, "wanted-refs", 1)) + receive_wanted_refs(&reader, ref); + /* get the pack */ process_section_header(&reader, "packfile", 0); if (get_pack(args, fd, pack_lockfile)) @@@ -1471,16 -1503,17 +1504,17 @@@ static int remove_duplicates_in_refs(st } static void update_shallow(struct fetch_pack_args *args, - struct ref **sought, int nr_sought, + struct ref *refs, struct shallow_info *si) { struct oid_array ref = OID_ARRAY_INIT; int *status; int i; + struct ref *r; if (args->deepen && alternate_shallow_file) { if (*alternate_shallow_file == '\0') { /* --unshallow */ - unlink_or_warn(git_path_shallow()); + unlink_or_warn(git_path_shallow(the_repository)); rollback_lock_file(&shallow_lock); } else commit_lock_file(&shallow_lock); @@@ -1518,8 -1551,8 +1552,8 @@@ remove_nonexistent_theirs_shallow(si); if (!si->nr_ours && !si->nr_theirs) return; - for (i = 0; i < nr_sought; i++) - oid_array_append(&ref, &sought[i]->old_oid); + for (r = refs; r; r = r->next) + oid_array_append(&ref, &r->old_oid); si->ref = &ref; if (args->update_shallow) { @@@ -1553,17 -1586,29 +1587,29 @@@ * remote is also shallow, check what ref is safe to update * without updating .git/shallow */ - status = xcalloc(nr_sought, sizeof(*status)); + status = xcalloc(ref.nr, sizeof(*status)); assign_shallow_commits_to_refs(si, NULL, status); if (si->nr_ours || si->nr_theirs) { - for (i = 0; i < nr_sought; i++) + for (r = refs, i = 0; r; r = r->next, i++) if (status[i]) - sought[i]->status = REF_STATUS_REJECT_SHALLOW; + r->status = REF_STATUS_REJECT_SHALLOW; } free(status); oid_array_clear(&ref); } + static int iterate_ref_map(void *cb_data, struct object_id *oid) + { + struct ref **rm = cb_data; + struct ref *ref = *rm; + + if (!ref) + return -1; /* end of the list */ + *rm = ref->next; + oidcpy(oid, &ref->old_oid); + return 0; + } + struct ref *fetch_pack(struct fetch_pack_args *args, int fd[], struct child_process *conn, const struct ref *ref, @@@ -1592,7 -1637,25 +1638,25 @@@ ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, &si, pack_lockfile); reprepare_packed_git(the_repository); - update_shallow(args, sought, nr_sought, &si); + + if (!args->cloning && args->deepen) { + struct check_connected_options opt = CHECK_CONNECTED_INIT; + struct ref *iterator = ref_cpy; + opt.shallow_file = alternate_shallow_file; + if (args->deepen) + opt.is_deepening_fetch = 1; + if (check_connected(iterate_ref_map, &iterator, &opt)) { + error(_("remote did not send all necessary objects")); + free_refs(ref_cpy); + ref_cpy = NULL; + rollback_lock_file(&shallow_lock); + goto cleanup; + } + args->connectivity_checked = 1; + } + + update_shallow(args, ref_cpy, &si); + cleanup: clear_shallow_info(&si); return ref_cpy; } diff --combined remote.c index 539285fbdf,2c2376fff5..3fd43453f4 --- a/remote.c +++ b/remote.c @@@ -3,7 -3,6 +3,7 @@@ #include "remote.h" #include "refs.h" #include "refspec.h" +#include "object-store.h" #include "commit.h" #include "diff.h" #include "revision.h" @@@ -1736,6 -1735,7 +1736,7 @@@ int get_fetch_map(const struct ref *rem if (refspec->exact_sha1) { ref_map = alloc_ref(name); get_oid_hex(name, &ref_map->old_oid); + ref_map->exact_oid = 1; } else { ref_map = get_remote_ref(remote_refs, name); } diff --combined t/t5537-fetch-shallow.sh index 943231af96,a7afb66049..7045685e2d --- a/t/t5537-fetch-shallow.sh +++ b/t/t5537-fetch-shallow.sh @@@ -175,8 -175,8 +175,8 @@@ EO test_expect_success POSIXPERM,SANITY 'shallow fetch from a read-only repo' ' cp -R .git read-only.git && - find read-only.git -print | xargs chmod -w && test_when_finished "find read-only.git -type d -print | xargs chmod +w" && + find read-only.git -print | xargs chmod -w && git clone --no-local --depth=2 read-only.git from-read-only && git --git-dir=from-read-only/.git log --format=%s >actual && cat >expect <"$HTTPD_ROOT_PATH/one-time-sed" && + test_must_fail git -C client fetch --depth=1 "$HTTPD_URL/one_time_sed/repo" \ + master:a_branch && + + # Ensure that the one-time-sed script was used. + ! test -e "$HTTPD_ROOT_PATH/one-time-sed" && + + # Ensure that the resulting repo is consistent, despite our failure to + # fetch. + git -C client fsck + ' + + stop_httpd + test_done diff --combined upload-pack.c index 936acabbdd,6dd82e5607..de6106a9dd --- a/upload-pack.c +++ b/upload-pack.c @@@ -3,7 -3,6 +3,7 @@@ #include "refs.h" #include "pkt-line.h" #include "sideband.h" +#include "object-store.h" #include "tag.h" #include "object.h" #include "commit.h" @@@ -65,6 -64,7 +65,7 @@@ static const char *pack_objects_hook static int filter_capability_requested; static int allow_filter; + static int allow_ref_in_want; static struct list_objects_filter_options filter_options; static void reset_timeout(void) @@@ -659,7 -659,7 +660,7 @@@ static void send_shallow(struct commit_ if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) { packet_write_fmt(1, "shallow %s", oid_to_hex(&object->oid)); - register_shallow(&object->oid); + register_shallow(the_repository, &object->oid); shallow_nr++; } result = result->next; @@@ -696,14 -696,14 +697,14 @@@ static void send_unshallow(const struc add_object_array(object, NULL, &extra_edge_obj); } /* make sure commit traversal conforms to client */ - register_shallow(&object->oid); + register_shallow(the_repository, &object->oid); } } static void deepen(int depth, int deepen_relative, struct object_array *shallows) { - if (depth == INFINITE_DEPTH && !is_repository_shallow()) { + if (depth == INFINITE_DEPTH && !is_repository_shallow(the_repository)) { int i; for (i = 0; i < shallows->nr; i++) { @@@ -783,8 -783,7 +784,8 @@@ static int send_shallow_list(int depth if (shallows->nr > 0) { int i; for (i = 0; i < shallows->nr; i++) - register_shallow(&shallows->objects[i].item->oid); + register_shallow(the_repository, + &shallows->objects[i].item->oid); } } @@@ -1077,6 -1076,8 +1078,8 @@@ static int upload_pack_config(const cha return git_config_string(&pack_objects_hook, var, value); } else if (!strcmp("uploadpack.allowfilter", var)) { allow_filter = git_config_bool(var, value); + } else if (!strcmp("uploadpack.allowrefinwant", var)) { + allow_ref_in_want = git_config_bool(var, value); } return parse_hide_refs_config(var, value, "uploadpack"); } @@@ -1116,6 -1117,7 +1119,7 @@@ void upload_pack(struct upload_pack_opt struct upload_pack_data { struct object_array wants; + struct string_list wanted_refs; struct oid_array haves; struct object_array shallows; @@@ -1137,12 -1139,14 +1141,14 @@@ static void upload_pack_data_init(struct upload_pack_data *data) { struct object_array wants = OBJECT_ARRAY_INIT; + struct string_list wanted_refs = STRING_LIST_INIT_DUP; struct oid_array haves = OID_ARRAY_INIT; struct object_array shallows = OBJECT_ARRAY_INIT; struct string_list deepen_not = STRING_LIST_INIT_DUP; memset(data, 0, sizeof(*data)); data->wants = wants; + data->wanted_refs = wanted_refs; data->haves = haves; data->shallows = shallows; data->deepen_not = deepen_not; @@@ -1151,6 -1155,7 +1157,7 @@@ static void upload_pack_data_clear(struct upload_pack_data *data) { object_array_clear(&data->wants); + string_list_clear(&data->wanted_refs, 1); oid_array_clear(&data->haves); object_array_clear(&data->shallows); string_list_clear(&data->deepen_not, 0); @@@ -1187,6 -1192,34 +1194,34 @@@ static int parse_want(const char *line return 0; } + static int parse_want_ref(const char *line, struct string_list *wanted_refs) + { + const char *arg; + if (skip_prefix(line, "want-ref ", &arg)) { + struct object_id oid; + struct string_list_item *item; + struct object *o; + + if (read_ref(arg, &oid)) { + packet_write_fmt(1, "ERR unknown ref %s", arg); + die("unknown ref %s", arg); + } + + item = string_list_append(wanted_refs, arg); + item->util = oiddup(&oid); + + o = parse_object_or_die(&oid, arg); + if (!(o->flags & WANTED)) { + o->flags |= WANTED; + add_object_array(o, NULL, &want_obj); + } + + return 1; + } + + return 0; + } + static int parse_have(const char *line, struct oid_array *haves) { const char *arg; @@@ -1212,6 -1245,8 +1247,8 @@@ static void process_args(struct packet_ /* process want */ if (parse_want(arg)) continue; + if (allow_ref_in_want && parse_want_ref(arg, &data->wanted_refs)) + continue; /* process have line */ if (parse_have(arg, &data->haves)) continue; @@@ -1354,19 -1389,36 +1391,37 @@@ static int process_haves_and_send_acks( return ret; } + static void send_wanted_ref_info(struct upload_pack_data *data) + { + const struct string_list_item *item; + + if (!data->wanted_refs.nr) + return; + + packet_write_fmt(1, "wanted-refs\n"); + + for_each_string_list_item(item, &data->wanted_refs) { + packet_write_fmt(1, "%s %s\n", + oid_to_hex(item->util), + item->string); + } + + packet_delim(1); + } + static void send_shallow_info(struct upload_pack_data *data) { /* No shallow info needs to be sent */ if (!data->depth && !data->deepen_rev_list && !data->shallows.nr && - !is_repository_shallow()) + !is_repository_shallow(the_repository)) return; packet_write_fmt(1, "shallow-info\n"); if (!send_shallow_list(data->depth, data->deepen_rev_list, data->deepen_since, &data->deepen_not, - &data->shallows) && is_repository_shallow()) + &data->shallows) && + is_repository_shallow(the_repository)) deepen(INFINITE_DEPTH, data->deepen_relative, &data->shallows); packet_delim(1); @@@ -1421,6 -1473,7 +1476,7 @@@ int upload_pack_v2(struct repository *r state = FETCH_DONE; break; case FETCH_SEND_PACK: + send_wanted_ref_info(&data); send_shallow_info(&data); packet_write_fmt(1, "packfile\n"); @@@ -1441,12 -1494,22 +1497,22 @@@ int upload_pack_advertise(struct reposi { if (value) { int allow_filter_value; + int allow_ref_in_want; + strbuf_addstr(value, "shallow"); + if (!repo_config_get_bool(the_repository, "uploadpack.allowfilter", &allow_filter_value) && allow_filter_value) strbuf_addstr(value, " filter"); + + if (!repo_config_get_bool(the_repository, + "uploadpack.allowrefinwant", + &allow_ref_in_want) && + allow_ref_in_want) + strbuf_addstr(value, " ref-in-want"); } + return 1; }