@@ -1577,6 +1577,82 @@ static int backfill_tags(struct display_state *display_state,
return retcode;
}
+static void report_set_head(const char *remote, const char *head_name,
+ struct strbuf *buf_prev) {
+ struct strbuf buf_prefix = STRBUF_INIT;
+ const char *prev_head;
+
+ 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))
+ ;
+ else if (prev_head) {
+ printf("'HEAD' at '%s' has changed from '%s' to '%s'\n",
+ remote, prev_head, head_name);
+ printf("Run 'git remote set-head %s %s' to follow the change.\n",
+ remote, head_name);
+ }
+}
+
+static const char *strip_refshead(const char *name){
+ skip_prefix(name, "refs/heads/", &name);
+ return name;
+}
+
+static int set_head(const struct ref *remote_refs)
+{
+ int result = 0;
+ 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 (!heads.nr)
+ result = 1;
+ else if (heads.nr > 1) {
+ result = 1;
+ } else
+ head_name = xstrdup(heads.items[0].string);
+ if (head_name) {
+ 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 (!refs_ref_exists(refs, b_remote_head.buf))
+ result |= error(_("Not a valid ref: %s"), b_remote_head.buf);
+ else if (refs_update_symref(refs, b_head.buf, b_remote_head.buf,
+ "remote set-head", &b_local_head, true))
+ result |= error(_("Could not setup %s"), b_head.buf);
+ else {
+ report_set_head(remote, head_name, &b_local_head);
+ }
+ free(head_name);
+ }
+
+ strbuf_release(&b_head);
+ strbuf_release(&b_local_head);
+ strbuf_release(&b_remote_head);
+ return result;
+}
+
static int do_fetch(struct transport *transport,
struct refspec *rs,
const struct fetch_config *config)
@@ -1646,6 +1722,8 @@ static int do_fetch(struct transport *transport,
"refs/tags/");
}
+ 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,
@@ -1790,6 +1868,10 @@ static int do_fetch(struct transport *transport,
"you need to specify exactly one branch with the --set-upstream option"));
}
}
+ if (set_head(remote_refs))
+ printf("Ran into issues with creating \'%s/HEAD\',\n"
+ "use \'git remote set-head -a %s\' to investigate.\n",
+ gtransport->remote->name,gtransport->remote->name);
cleanup:
if (retcode) {
@@ -2020,6 +2102,7 @@ static int fetch_multiple(struct string_list *list, int max_children,
return !!result;
}
+
/*
* Fetching from the promisor remote should use the given filter-spec
* or inherit the default filter-spec from the config.
@@ -45,14 +45,17 @@ test_expect_success setup '
'
cat > test/expect << EOF
+ one/HEAD -> one/main
one/main
one/side
origin/HEAD -> origin/main
origin/main
origin/side
+ three/HEAD -> three/main
three/another
three/main
three/side
+ two/HEAD -> two/main
two/another
two/main
two/side
@@ -97,6 +100,7 @@ cat > expect << EOF
origin/HEAD -> origin/main
origin/main
origin/side
+ three/HEAD -> three/main
three/another
three/main
three/side
@@ -112,8 +116,10 @@ test_expect_success 'git fetch --multiple (but only one remote)' '
'
cat > expect << EOF
+ one/HEAD -> one/main
one/main
one/side
+ two/HEAD -> two/main
two/another
two/main
two/side
@@ -221,14 +227,17 @@ test_expect_success 'git fetch --multiple --jobs=0 picks a default' '
create_fetch_all_expect () {
cat >expect <<-\EOF
+ one/HEAD -> one/main
one/main
one/side
origin/HEAD -> origin/main
origin/main
origin/side
+ three/HEAD -> three/main
three/another
three/main
three/side
+ two/HEAD -> two/main
two/another
two/main
two/side
If the user has remote/HEAD set already and it looks like it has changed on the server, then print a message, otherwise set it if we can. Silently pass if the user already has the same remote/HEAD set as reported by the server. Signed-off-by: Bence Ferdinandy <bence@ferdinandy.com> --- Notes: v3: - does not rely on remote set-head anymore so it only authenticates once - uses the new REF_CREATE_ONLY to atomically check if the ref exists and only write it if it doesn't - in all other cases the maximum it does is print a warning v4: - instead of the discarded REF_CREATE_ONLY, it uses the existing, but updated transaction api to request a silent create only - it now uses the atomic before_target to determine reporting - refactored for legibility builtin/fetch.c | 83 +++++++++++++++++++++++++++++++++++++++ t/t5514-fetch-multiple.sh | 9 +++++ 2 files changed, 92 insertions(+)