It's surprising that `git clone` and `git init && git remote add -f` don't create the same remote state. Fix this by introducing a new configuration: `fetch.updateHead` which updates the remote `HEAD` when it's not present with "missing", or always with "always". By default it's "never", which retains the current behavior. This has already been discussed before [1]. Changes since v1: 1. Make `fetch_update_head` a named enum as suggested by Ævar 2. Remove `need_update_head`: use switch case instead, per Ævar 3. Make `update_head` receive `fetch_missing` boolean, instead of enum, per Ævar 4. Make `update_head` receive an unconsted `struct remote`: worse, but simplifies the review process [1] https://lore.kernel.org/git/20201118091219.3341585-1-felipe.contreras@xxxxxxxxx/ Felipe Contreras (2): Add fetch.updateHead option fetch: add support for HEAD update on mirrors Documentation/config/fetch.txt | 4 ++ Documentation/config/remote.txt | 3 ++ builtin/fetch.c | 76 ++++++++++++++++++++++++++++++++- remote.c | 20 +++++++++ remote.h | 12 ++++++ t/t5510-fetch.sh | 49 +++++++++++++++++++++ 6 files changed, 163 insertions(+), 1 deletion(-) Range-diff against v1: 1: 1cb238c83d ! 1: 0b80baba39 Add fetch.updateHead option @@ Commit message For the next major version of Git, we might want to change this default. + Helped-by: Ævar Arnfjörð Bjarmason <avarab@xxxxxxxxx> Signed-off-by: Felipe Contreras <felipe.contreras@xxxxxxxxx> ## Documentation/config/fetch.txt ## @@ builtin/fetch.c: static int fetch_prune_tags_config = -1; /* unspecified */ static int prune_tags = -1; /* unspecified */ #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */ -+static int fetch_update_head = FETCH_UPDATE_HEAD_DEFAULT; ++static enum fetch_update_head fetch_update_head = FETCH_UPDATE_HEAD_DEFAULT; + static int all, append, dry_run, force, keep, multiple, update_head_ok; static int write_fetch_head = 1; @@ builtin/fetch.c: static int backfill_tags(struct transport *transport, return retcode; } -+static void update_head(int config, const struct ref *head, const struct remote *remote) ++static void update_head(int fetch_missing, const struct ref *head, ++ struct remote *remote) +{ + char *ref, *target; + const char *r; @@ builtin/fetch.c: static int backfill_tags(struct transport *transport, + if (!head || !head->symref || !remote) + return; + -+ ref = apply_refspecs((struct refspec *)&remote->fetch, "refs/heads/HEAD"); -+ target = apply_refspecs((struct refspec *)&remote->fetch, head->symref); ++ ref = apply_refspecs(&remote->fetch, "refs/heads/HEAD"); ++ target = apply_refspecs(&remote->fetch, head->symref); + + if (!ref || !target) { + warning(_("could not update remote head")); @@ builtin/fetch.c: static int backfill_tags(struct transport *transport, + r = resolve_ref_unsafe(ref, 0, NULL, &flags); + + if (r) { -+ if (config == FETCH_UPDATE_HEAD_MISSING) { -+ if (flags & REF_ISSYMREF) -+ /* already present */ -+ return; -+ } else if (config == FETCH_UPDATE_HEAD_ALWAYS) { ++ if (!fetch_missing) { + if (!strcmp(r, target)) + /* already up-to-date */ + return; -+ } else -+ /* should never happen */ ++ } else if (flags & REF_ISSYMREF) ++ /* already present */ + return; + } + @@ builtin/fetch.c: static int do_fetch(struct transport *transport, int must_list_refs = 1; struct fetch_head fetch_head = { 0 }; struct strbuf err = STRBUF_INIT; -+ int need_update_head = 0, update_head_config = 0; ++ enum fetch_update_head update_head_config = FETCH_UPDATE_HEAD_DEFAULT; if (tags == TAGS_DEFAULT) { if (transport->remote->fetch_tags == 2) @@ builtin/fetch.c: static int do_fetch(struct transport *transport, + else + update_head_config = fetch_update_head; + -+ need_update_head = update_head_config && update_head_config != FETCH_UPDATE_HEAD_NEVER; -+ -+ if (need_update_head) ++ switch (update_head_config) { ++ case FETCH_UPDATE_HEAD_MISSING: ++ case FETCH_UPDATE_HEAD_ALWAYS: + strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD"); ++ default: ++ break; ++ } refspec_ref_prefixes(&transport->remote->fetch, &transport_ls_refs_options.ref_prefixes); + } @@ builtin/fetch.c: static int do_fetch(struct transport *transport, commit_fetch_head(&fetch_head); -+ if (need_update_head) -+ update_head(update_head_config, find_ref_by_name(remote_refs, "HEAD"), transport->remote); ++ switch (update_head_config) { ++ case FETCH_UPDATE_HEAD_MISSING: ++ case FETCH_UPDATE_HEAD_ALWAYS: ++ update_head(update_head_config == FETCH_UPDATE_HEAD_MISSING, ++ find_ref_by_name(remote_refs, "HEAD"), ++ transport->remote); ++ default: ++ break; ++ } + if (set_upstream) { struct branch *branch = branch_get("HEAD"); @@ remote.c: static void read_branches_file(struct remote_state *remote_state, remote->fetch_tags = 1; /* always auto-follow */ } -+int parse_update_head(int *r, const char *var, const char *value) ++int parse_update_head(enum fetch_update_head *r, const char *var, ++ const char *value) +{ -+ if (!r) -+ return -1; -+ else if (!value) ++ if (!value) + return config_error_nonbool(var); + else if (!strcmp(value, "never")) + *r = FETCH_UPDATE_HEAD_NEVER; @@ remote.h: enum { REMOTE_BRANCHES }; -+enum { ++enum fetch_update_head { + FETCH_UPDATE_HEAD_DEFAULT = 0, + FETCH_UPDATE_HEAD_NEVER, + FETCH_UPDATE_HEAD_MISSING, @@ remote.h: struct remote { int prune; int prune_tags; -+ int update_head; ++ enum fetch_update_head update_head; + /** * The configured helper programs to run on the remote side, for @@ remote.h: void apply_push_cas(struct push_cas_option *, struct remote *, struct char *relative_url(const char *remote_url, const char *url, const char *up_path); -+int parse_update_head(int *r, const char *var, const char *value); ++int parse_update_head(enum fetch_update_head *r, const char *var, ++ const char *value); + #endif 2: fe6d62510b ! 2: 5c0f48b9cc fetch: add support for HEAD update on mirrors @@ Commit message Signed-off-by: Felipe Contreras <felipe.contreras@xxxxxxxxx> ## builtin/fetch.c ## -@@ builtin/fetch.c: static void update_head(int config, const struct ref *head, const struct remote +@@ builtin/fetch.c: static void update_head(int fetch_missing, const struct ref *head, if (!head || !head->symref || !remote) return; -- ref = apply_refspecs((struct refspec *)&remote->fetch, "refs/heads/HEAD"); -- target = apply_refspecs((struct refspec *)&remote->fetch, head->symref); +- ref = apply_refspecs(&remote->fetch, "refs/heads/HEAD"); +- target = apply_refspecs(&remote->fetch, head->symref); + if (!remote->mirror) { -+ ref = apply_refspecs((struct refspec *)&remote->fetch, "refs/heads/HEAD"); -+ target = apply_refspecs((struct refspec *)&remote->fetch, head->symref); ++ ref = apply_refspecs(&remote->fetch, "refs/heads/HEAD"); ++ target = apply_refspecs(&remote->fetch, head->symref); - if (!ref || !target) { - warning(_("could not update remote head")); -- 2.40.0+fc1