git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v4] fetch.c: defer fetch.followRemoteHEAD validation

From
Colin Hinton <colinlewishinton@gmail.com>
Date
Oct 4, 2026, 15:40 UTC
Message-ID
<CAHeTm9PK=sc4ajmf53rhurd532OST0qYfEaS-Kc5kpGZf1Zw2A@mail.gmail.com>
In-Reply-To
<xmqqa4otvbnk.fsf@gitster.g>

My rationale for this recent change came from when I was evaluating what calls get_follow_remote_head() in my patch.

With the current design, get_follow_remote_head() is only called in do_fetch() in this conditional else if (config->follow_remote_head_raw).

If followRemoteHEAD is now NULL because we set it as such in fetch_config->follow_remote_head_raw = xstrdup_or_null(v); Then this conditional is skipped, and we will never call the die(), and alert the user that their value is blank.

To fully fix based on your suggestion, I suppose the design question is, should empty string warn or die?

If empty string should warn, I likely will need to add some value in the fetch_config struct such as follow_remote_head_seen, and use this as our conditional in do_fetch() rather than the follow_remote_head_raw, to account for when followRemoteHEAD was set to anything. Then when the check in get_follow_remote_head() occurs, we know to die or warn based on NULL, or bogus.

Visually, it would look something like this.
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 2cb0bcca8b..af22f63954 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -104,6 +104,7 @@ static struct string_list negotiation_include =
STRING_LIST_INIT_NODUP;
 struct fetch_config {
        enum display_format display_format;
        char *follow_remote_head_raw;
+       int follow_remote_head_seen;
        int all;
        int prune;
        int prune_tags;
@@ -177,10 +178,8 @@ static int git_fetch_config(const char *k, const char *v,

        if (!strcmp(k, "fetch.followremotehead")) {
                free(fetch_config->follow_remote_head_raw);
-               if (!v)
-                       fetch_config->follow_remote_head_raw = xstrdup("");
-               else
-                       fetch_config->follow_remote_head_raw = xstrdup(v);
+               fetch_config->follow_remote_head_raw = xstrdup_or_null(v);
+               follow_remote_head_seen = 1;
                return 0;
        }

@@ -189,7 +188,7 @@ static int git_fetch_config(const char *k, const char *v,

 static enum follow_remote_head_settings get_follow_remote_head(const
char *setting)
 {
-       if (!setting || !*setting)
+       if (!setting) /*!*setting would return true on "" removing to
warn instead*/
                die(_("missing value for 'fetch.followRemoteHEAD'"));
        else if (!strcmp(setting, "never"))
                return FOLLOW_REMOTE_NEVER;
@@ -1960,7 +1959,7 @@ static int do_fetch(struct transport *transport,
                         */
                        if (transport->remote->follow_remote_head)
                                follow_remote_head =
transport->remote->follow_remote_head;
-                       else if (config->follow_remote_head_raw)
+                       else if (config->follow_remote_head_seen)
                                follow_remote_head =
get_follow_remote_head(config->follow_remote_head_raw);
                        else
                                follow_remote_head =
BUILTIN_FOLLOW_REMOTE_HEAD_DFLT;
@@ -2510,6 +2509,7 @@ int cmd_fetch(int argc,
        struct fetch_config config = {
                .display_format = DISPLAY_FORMAT_FULL,
                .follow_remote_head_raw = NULL,
+               .follow_remote_head_seen = 0,
                .prune = -1,
                .prune_tags = -1,
                .show_forced_updates = 1,

Let me know if this sounds right, and I will add this in for v5.
-Colin Hinton

On Sun, Oct 4, 2026 at 6:27 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Colin Hinton <colinlewishinton@gmail.com> writes:
>
> >       if (!strcmp(k, "fetch.followremotehead")) {
> > +             free(fetch_config->follow_remote_head_raw);
> >               if (!v)
> > +                     fetch_config->follow_remote_head_raw = xstrdup("");
> >               else
> > +                     fetch_config->follow_remote_head_raw = xstrdup(v);
>
> Hmph, this means that the code cannot distinguish between
>
>         [fetch] followremotehead
>
>         [fetch] followremotehead = ""
>
> It would be less code and more expressive if you lost the
> conditional, i.e.,
>
>         if (!strcmp(k, "fetch.followremotehead"))
>                 free(fetch_config->follow_remote_head_raw);
>                 fetch_config->follow_remote_head_raw = xstrdup_or_null(v);
>         }
>
> > +static enum follow_remote_head_settings get_follow_remote_head(const char *setting)
> > +{
> > +     if (!setting || !*setting)
> > +             die(_("missing value for 'fetch.followRemoteHEAD'"));
>
> Then you can differenciate
>
>         if (!setting)
>                 ... we got '[fetch] followRemoteHEAD' ...
>                 die() as before, complaining that the this is not a Bool.
>         else if (!*setting)
>                 ... we got '[fetch] followRemoteHEAD = ""' ...
>
> if we wanted to.  It probably do not need to check for an empty
> string as it will fall through the "else if" cascade below and
> eventually end up with the warning + default.
>
> > +     else if (!strcmp(setting, "never"))
> > +             return FOLLOW_REMOTE_NEVER;
> > +     else if (!strcmp(setting, "create"))
> > +             return FOLLOW_REMOTE_CREATE;
> > +     else if (!strcmp(setting, "warn"))
> > +             return FOLLOW_REMOTE_WARN;
> > +     else if (!strcmp(setting, "always"))
> > +             return FOLLOW_REMOTE_ALWAYS;
> > +     warning(_("unrecognized fetch.followRemoteHEAD value '%s' ignored"), setting);
> > +     return BUILTIN_FOLLOW_REMOTE_HEAD_DFLT;
> > +}
Previous: Junio C HamanoNext: Junio C Hamano
Message 16 of 22 in “fetch.c: defer fetch.followRemoteHEAD validation”
  1. fetch.c: defer fetch.followRemoteHEAD validationColin Hinton, Sep 22, 2026
  2. Junio C HamanoSep 22, 2026
  3. Colin HintonSep 23, 2026
  4. Matt HunterSep 24, 2026
  5. Colin HintonSep 25, 2026
  6. Matt HunterSep 24, 2026
  7. fetch.c: defer fetch.followRemoteHEAD validationColin Hinton, Sep 25, 2026
  8. Junio C HamanoSep 25, 2026
  9. Colin HintonSep 25, 2026
  10. Matt HunterSep 30, 2026
  11. Junio C HamanoSep 30, 2026
  12. Colin HintonOct 3, 2026
  13. fetch.c: defer fetch.followRemoteHEAD validationColin Hinton, Sep 25, 2026
  14. fetch.c: defer fetch.followRemoteHEAD validationColin Hinton, Oct 3, 2026
  15. Junio C HamanoOct 4, 2026
  16. Colin HintonOct 4, 2026
  17. Junio C HamanoOct 4, 2026
  18. fetch.c: defer fetch.followRemoteHEAD validationColin Hinton, Oct 4, 2026
  19. Matt HunterOct 5, 2026
  20. Junio C HamanoOct 5, 2026
  21. fetch.c: defer fetch.followRemoteHEAD validationColin Hinton, Oct 6, 2026
  22. Junio C HamanoOct 7, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.