diff options
Diffstat (limited to 'builtin/fetch.c')
| -rw-r--r-- | builtin/fetch.c | 165 |
1 files changed, 154 insertions, 11 deletions
diff --git a/builtin/fetch.c b/builtin/fetch.c index 55f97134aa..872ad49834 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1,13 +1,13 @@ /* * "git fetch" */ +#define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "advice.h" #include "config.h" #include "gettext.h" #include "environment.h" #include "hex.h" -#include "repository.h" #include "refs.h" #include "refspec.h" #include "object-name.h" @@ -454,13 +454,10 @@ static void filter_prefetch_refspec(struct refspec *rs) ref_namespace[NAMESPACE_TAGS].ref))) { int j; - free(rs->items[i].src); - free(rs->items[i].dst); + refspec_item_clear(&rs->items[i]); - for (j = i + 1; j < rs->nr; j++) { + for (j = i + 1; j < rs->nr; j++) rs->items[j - 1] = rs->items[j]; - rs->raw[j - 1] = rs->raw[j]; - } rs->nr--; i--; continue; @@ -668,7 +665,7 @@ static int s_update_ref(const char *action, */ if (!transaction) { transaction = our_transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), - &err); + 0, &err); if (!transaction) { ret = STORE_REF_ERROR_OTHER; goto out; @@ -1577,6 +1574,135 @@ static int backfill_tags(struct display_state *display_state, return retcode; } +static const char *strip_refshead(const char *name){ + skip_prefix(name, "refs/heads/", &name); + return name; +} + +static void set_head_advice_msg(const char *remote, const char *head_name) +{ + const char message_advice_set_head[] = + N_("Run 'git remote set-head %s %s' to follow the change, or set\n" + "'remote.%s.followRemoteHEAD' configuration option to a different value\n" + "if you do not want to see this message. Specifically running\n" + "'git config set remote.%s.followRemoteHEAD %s' will disable the warning\n" + "until the remote changes HEAD to something else."); + + advise_if_enabled(ADVICE_FETCH_SET_HEAD_WARN, _(message_advice_set_head), + remote, head_name, remote, remote, head_name); +} + +static void report_set_head(const char *remote, const char *head_name, + struct strbuf *buf_prev, int updateres) { + struct strbuf buf_prefix = STRBUF_INIT; + const char *prev_head = NULL; + + strbuf_addf(&buf_prefix, "refs/remotes/%s/", remote); + skip_prefix(buf_prev->buf, buf_prefix.buf, &prev_head); + + if (prev_head && strcmp(prev_head, head_name)) { + printf("'HEAD' at '%s' is '%s', but we have '%s' locally.\n", + remote, head_name, prev_head); + set_head_advice_msg(remote, head_name); + } + else if (updateres && buf_prev->len) { + printf("'HEAD' at '%s' is '%s', " + "but we have a detached HEAD pointing to '%s' locally.\n", + remote, head_name, buf_prev->buf); + set_head_advice_msg(remote, head_name); + } + strbuf_release(&buf_prefix); +} + +static int set_head(const struct ref *remote_refs, int follow_remote_head, + const char *no_warn_branch) +{ + int result = 0, create_only, is_bare, was_detached; + struct strbuf b_head = STRBUF_INIT, b_remote_head = STRBUF_INIT, + b_local_head = STRBUF_INIT; + const char *remote = gtransport->remote->name; + char *head_name = NULL; + struct ref *ref, *matches; + struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map; + struct refspec_item refspec = { + .force = 0, + .pattern = 1, + .src = (char *) "refs/heads/*", + .dst = (char *) "refs/heads/*", + }; + struct string_list heads = STRING_LIST_INIT_DUP; + struct ref_store *refs = get_main_ref_store(the_repository); + + get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0); + matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"), + fetch_map, 1); + for (ref = matches; ref; ref = ref->next) { + string_list_append(&heads, strip_refshead(ref->name)); + } + + if (follow_remote_head == FOLLOW_REMOTE_NEVER) + goto cleanup; + + if (!heads.nr) + result = 1; + else if (heads.nr > 1) + result = 1; + else + head_name = xstrdup(heads.items[0].string); + + if (!head_name) + goto cleanup; + is_bare = is_bare_repository(); + create_only = follow_remote_head == FOLLOW_REMOTE_ALWAYS ? 0 : !is_bare; + if (is_bare) { + strbuf_addstr(&b_head, "HEAD"); + strbuf_addf(&b_remote_head, "refs/heads/%s", head_name); + } else { + strbuf_addf(&b_head, "refs/remotes/%s/HEAD", remote); + strbuf_addf(&b_remote_head, "refs/remotes/%s/%s", remote, head_name); + } + /* make sure it's valid */ + if (!is_bare && !refs_ref_exists(refs, b_remote_head.buf)) { + result = 1; + goto cleanup; + } + was_detached = refs_update_symref_extended(refs, b_head.buf, b_remote_head.buf, + "fetch", &b_local_head, create_only); + if (was_detached == -1) { + result = 1; + goto cleanup; + } + if (verbosity >= 0 && + follow_remote_head == FOLLOW_REMOTE_WARN && + (!no_warn_branch || strcmp(no_warn_branch, head_name))) + report_set_head(remote, head_name, &b_local_head, was_detached); + +cleanup: + free(head_name); + free_refs(fetch_map); + free_refs(matches); + string_list_clear(&heads, 0); + strbuf_release(&b_head); + strbuf_release(&b_local_head); + strbuf_release(&b_remote_head); + return result; +} + +static int uses_remote_tracking(struct transport *transport, struct refspec *rs) +{ + if (!remote_is_configured(transport->remote, 0)) + return 0; + + if (!rs->nr) + rs = &transport->remote->fetch; + + for (int i = 0; i < rs->nr; i++) + if (rs->items[i].dst) + return 1; + + return 0; +} + static int do_fetch(struct transport *transport, struct refspec *rs, const struct fetch_config *config) @@ -1646,6 +1772,11 @@ static int do_fetch(struct transport *transport, "refs/tags/"); } + if (uses_remote_tracking(transport, rs)) { + must_list_refs = 1; + strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD"); + } + if (must_list_refs) { trace2_region_enter("fetch", "remote_refs", the_repository); remote_refs = transport_get_remote_refs(transport, @@ -1670,7 +1801,7 @@ static int do_fetch(struct transport *transport, if (atomic_fetch) { transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), - &err); + 0, &err); if (!transaction) { retcode = -1; goto cleanup; @@ -1790,6 +1921,13 @@ static int do_fetch(struct transport *transport, "you need to specify exactly one branch with the --set-upstream option")); } } + if (set_head(remote_refs, transport->remote->follow_remote_head, + transport->remote->no_warn_branch)) + ; + /* + * Way too many cases where this can go wrong + * so let's just fail silently for now. + */ cleanup: if (retcode) { @@ -1980,6 +2118,8 @@ static int fetch_multiple(struct string_list *list, int max_children, strvec_pushl(&argv, "-c", "fetch.bundleURI=", "fetch", "--append", "--no-auto-gc", "--no-write-commit-graph", NULL); + for (i = 0; i < server_options.nr; i++) + strvec_pushf(&argv, "--server-option=%s", server_options.items[i].string); add_options_to_argv(&argv, config); if (max_children != 1 && list->nr != 1) { @@ -2138,7 +2278,10 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, return exit_code; } -int cmd_fetch(int argc, const char **argv, const char *prefix) +int cmd_fetch(int argc, + const char **argv, + const char *prefix, + struct repository *repo UNUSED) { struct fetch_config config = { .display_format = DISPLAY_FORMAT_FULL, @@ -2210,8 +2353,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) N_("deepen history of shallow clone")), OPT_STRING(0, "shallow-since", &deepen_since, N_("time"), N_("deepen history of shallow repository based on time")), - OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"), - N_("deepen history of shallow clone, excluding rev")), + OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("ref"), + N_("deepen history of shallow clone, excluding ref")), OPT_INTEGER(0, "deepen", &deepen_relative, N_("deepen history of shallow clone")), OPT_SET_INT_F(0, "unshallow", &unshallow, |
