Skip to content

Commit 5aa4a8c

Browse files
committed
Merge branch 'bf/fetch-set-head-config' into jch
"git fetch" honors "remote.<remote>.followRemoteHEAD" settings to tweak the remote-tracking HEAD in "refs/remotes/<remote>/HEAD". * bf/fetch-set-head-config: remote set-head: set followRemoteHEAD to "warn" if "always" fetch set_head: add warn-if-not-$branch option fetch set_head: move warn advice into advise_if_enabled
2 parents e38fccf + 012bc56 commit 5aa4a8c

File tree

9 files changed

+101
-15
lines changed

9 files changed

+101
-15
lines changed

Documentation/config/remote.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,12 @@ remote.<name>.followRemoteHEAD::
106106
How linkgit:git-fetch[1] should handle updates to `remotes/<name>/HEAD`.
107107
The default value is "create", which will create `remotes/<name>/HEAD`
108108
if it exists on the remote, but not locally, but will not touch an
109-
already existing local reference. Setting to "warn" will print
109+
already existing local reference. Setting to "warn" will print
110110
a message if the remote has a different value, than the local one and
111-
in case there is no local reference, it behaves like "create". Setting
112-
to "always" will silently update it to the value on the remote.
111+
in case there is no local reference, it behaves like "create".
112+
A variant on "warn" is "warn-if-not-$branch", which behaves like
113+
"warn", but if `HEAD` on the remote is `$branch` it will be silent.
114+
Setting to "always" will silently update it to the value on the remote.
113115
Finally, setting it to "never" will never change or create the local
114116
reference.
115117
+

advice.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ static struct {
5353
[ADVICE_COMMIT_BEFORE_MERGE] = { "commitBeforeMerge" },
5454
[ADVICE_DETACHED_HEAD] = { "detachedHead" },
5555
[ADVICE_DIVERGING] = { "diverging" },
56+
[ADVICE_FETCH_SET_HEAD_WARN] = { "fetchRemoteHEADWarn" },
5657
[ADVICE_FETCH_SHOW_FORCED_UPDATES] = { "fetchShowForcedUpdates" },
5758
[ADVICE_FORCE_DELETE_BRANCH] = { "forceDeleteBranch" },
5859
[ADVICE_GRAFT_FILE_DEPRECATED] = { "graftFileDeprecated" },

advice.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ enum advice_type {
2020
ADVICE_COMMIT_BEFORE_MERGE,
2121
ADVICE_DETACHED_HEAD,
2222
ADVICE_DIVERGING,
23+
ADVICE_FETCH_SET_HEAD_WARN,
2324
ADVICE_FETCH_SHOW_FORCED_UPDATES,
2425
ADVICE_FORCE_DELETE_BRANCH,
2526
ADVICE_GRAFT_FILE_DEPRECATED,

builtin/fetch.c

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,6 +1579,19 @@ static const char *strip_refshead(const char *name){
15791579
return name;
15801580
}
15811581

1582+
static void set_head_advice_msg(const char *remote, const char *head_name)
1583+
{
1584+
const char message_advice_set_head[] =
1585+
N_("Run 'git remote set-head %s %s' to follow the change, or set\n"
1586+
"'remote.%s.followRemoteHEAD' configuration option to a different value\n"
1587+
"if you do not want to see this message. Specifically running\n"
1588+
"'git config set remote.%s.followRemoteHEAD %s' will disable the warning\n"
1589+
"until the remote changes HEAD to something else.");
1590+
1591+
advise_if_enabled(ADVICE_FETCH_SET_HEAD_WARN, _(message_advice_set_head),
1592+
remote, head_name, remote, remote, head_name);
1593+
}
1594+
15821595
static void report_set_head(const char *remote, const char *head_name,
15831596
struct strbuf *buf_prev, int updateres) {
15841597
struct strbuf buf_prefix = STRBUF_INIT;
@@ -1590,20 +1603,19 @@ static void report_set_head(const char *remote, const char *head_name,
15901603
if (prev_head && strcmp(prev_head, head_name)) {
15911604
printf("'HEAD' at '%s' is '%s', but we have '%s' locally.\n",
15921605
remote, head_name, prev_head);
1593-
printf("Run 'git remote set-head %s %s' to follow the change.\n",
1594-
remote, head_name);
1606+
set_head_advice_msg(remote, head_name);
15951607
}
15961608
else if (updateres && buf_prev->len) {
15971609
printf("'HEAD' at '%s' is '%s', "
15981610
"but we have a detached HEAD pointing to '%s' locally.\n",
15991611
remote, head_name, buf_prev->buf);
1600-
printf("Run 'git remote set-head %s %s' to follow the change.\n",
1601-
remote, head_name);
1612+
set_head_advice_msg(remote, head_name);
16021613
}
16031614
strbuf_release(&buf_prefix);
16041615
}
16051616

1606-
static int set_head(const struct ref *remote_refs, int follow_remote_head)
1617+
static int set_head(const struct ref *remote_refs, int follow_remote_head,
1618+
const char *no_warn_branch)
16071619
{
16081620
int result = 0, create_only, is_bare, was_detached;
16091621
struct strbuf b_head = STRBUF_INIT, b_remote_head = STRBUF_INIT,
@@ -1660,7 +1672,9 @@ static int set_head(const struct ref *remote_refs, int follow_remote_head)
16601672
result = 1;
16611673
goto cleanup;
16621674
}
1663-
if (follow_remote_head == FOLLOW_REMOTE_WARN && verbosity >= 0)
1675+
if (verbosity >= 0 &&
1676+
follow_remote_head == FOLLOW_REMOTE_WARN &&
1677+
(!no_warn_branch || strcmp(no_warn_branch, head_name)))
16641678
report_set_head(remote, head_name, &b_local_head, was_detached);
16651679

16661680
cleanup:
@@ -1889,7 +1903,8 @@ static int do_fetch(struct transport *transport,
18891903
"you need to specify exactly one branch with the --set-upstream option"));
18901904
}
18911905
}
1892-
if (set_head(remote_refs, transport->remote->follow_remote_head))
1906+
if (set_head(remote_refs, transport->remote->follow_remote_head,
1907+
transport->remote->no_warn_branch))
18931908
;
18941909
/*
18951910
* Way too many cases where this can go wrong

builtin/remote.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1438,6 +1438,7 @@ static int set_head(int argc, const char **argv, const char *prefix,
14381438
b_local_head = STRBUF_INIT;
14391439
char *head_name = NULL;
14401440
struct ref_store *refs = get_main_ref_store(the_repository);
1441+
struct remote *remote;
14411442

14421443
struct option options[] = {
14431444
OPT_BOOL('a', "auto", &opt_a,
@@ -1448,8 +1449,10 @@ static int set_head(int argc, const char **argv, const char *prefix,
14481449
};
14491450
argc = parse_options(argc, argv, prefix, options,
14501451
builtin_remote_sethead_usage, 0);
1451-
if (argc)
1452+
if (argc) {
14521453
strbuf_addf(&b_head, "refs/remotes/%s/HEAD", argv[0]);
1454+
remote = remote_get(argv[0]);
1455+
}
14531456

14541457
if (!opt_a && !opt_d && argc == 2) {
14551458
head_name = xstrdup(argv[1]);
@@ -1488,6 +1491,13 @@ static int set_head(int argc, const char **argv, const char *prefix,
14881491
}
14891492
if (opt_a)
14901493
report_set_head_auto(argv[0], head_name, &b_local_head, was_detached);
1494+
if (remote->follow_remote_head == FOLLOW_REMOTE_ALWAYS) {
1495+
struct strbuf config_name = STRBUF_INIT;
1496+
strbuf_addf(&config_name,
1497+
"remote.%s.followremotehead", remote->name);
1498+
git_config_set(config_name.buf, "warn");
1499+
strbuf_release(&config_name);
1500+
}
14911501

14921502
cleanup:
14931503
free(head_name);

remote.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,14 +515,23 @@ static int handle_config(const char *key, const char *value,
515515
return parse_transport_option(key, value,
516516
&remote->server_options);
517517
} else if (!strcmp(subkey, "followremotehead")) {
518+
const char *no_warn_branch;
518519
if (!strcmp(value, "never"))
519520
remote->follow_remote_head = FOLLOW_REMOTE_NEVER;
520521
else if (!strcmp(value, "create"))
521522
remote->follow_remote_head = FOLLOW_REMOTE_CREATE;
522-
else if (!strcmp(value, "warn"))
523+
else if (!strcmp(value, "warn")) {
523524
remote->follow_remote_head = FOLLOW_REMOTE_WARN;
524-
else if (!strcmp(value, "always"))
525+
remote->no_warn_branch = NULL;
526+
} else if (skip_prefix(value, "warn-if-not-", &no_warn_branch)) {
527+
remote->follow_remote_head = FOLLOW_REMOTE_WARN;
528+
remote->no_warn_branch = no_warn_branch;
529+
} else if (!strcmp(value, "always")) {
525530
remote->follow_remote_head = FOLLOW_REMOTE_ALWAYS;
531+
} else {
532+
warning(_("unrecognized followRemoteHEAD value '%s' ignored"),
533+
value);
534+
}
526535
}
527536
return 0;
528537
}

remote.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ struct remote {
116116
struct string_list server_options;
117117

118118
enum follow_remote_head_settings follow_remote_head;
119+
const char *no_warn_branch;
119120
};
120121

121122
/**

t/t5505-remote.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,17 @@ test_expect_success 'set-head --auto has no problem w/multiple HEADs' '
504504
)
505505
'
506506

507+
test_expect_success 'set-head changes followRemoteHEAD always to warn' '
508+
(
509+
cd test &&
510+
git config set remote.origin.followRemoteHEAD "always" &&
511+
git remote set-head --auto origin &&
512+
git config get remote.origin.followRemoteHEAD >actual &&
513+
echo "warn" >expect &&
514+
test_cmp expect actual
515+
)
516+
'
517+
507518
cat >test/expect <<\EOF
508519
refs/remotes/origin/side2
509520
EOF

t/t5510-fetch.sh

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ test_expect_success "fetch test followRemoteHEAD warn no change" '
123123
git fetch >output &&
124124
echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \
125125
"but we have ${SQ}other${SQ} locally." >expect &&
126-
echo "Run ${SQ}git remote set-head origin main${SQ} to follow the change." >>expect &&
127126
test_cmp expect output &&
128127
head=$(git rev-parse refs/remotes/origin/HEAD) &&
129128
branch=$(git rev-parse refs/remotes/origin/other) &&
@@ -160,7 +159,6 @@ test_expect_success "fetch test followRemoteHEAD warn detached" '
160159
echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \
161160
"but we have a detached HEAD pointing to" \
162161
"${SQ}${HEAD}${SQ} locally." >expect &&
163-
echo "Run ${SQ}git remote set-head origin main${SQ} to follow the change." >>expect &&
164162
test_cmp expect output
165163
)
166164
'
@@ -183,6 +181,44 @@ test_expect_success "fetch test followRemoteHEAD warn quiet" '
183181
)
184182
'
185183

184+
test_expect_success "fetch test followRemoteHEAD warn-if-not-branch branch is same" '
185+
test_when_finished "git config unset remote.origin.followRemoteHEAD" &&
186+
(
187+
cd "$D" &&
188+
cd two &&
189+
git rev-parse --verify refs/remotes/origin/other &&
190+
git remote set-head origin other &&
191+
git rev-parse --verify refs/remotes/origin/HEAD &&
192+
git rev-parse --verify refs/remotes/origin/main &&
193+
git config set remote.origin.followRemoteHEAD "warn-if-not-main" &&
194+
actual=$(git fetch) &&
195+
test "z" = "z$actual" &&
196+
head=$(git rev-parse refs/remotes/origin/HEAD) &&
197+
branch=$(git rev-parse refs/remotes/origin/other) &&
198+
test "z$head" = "z$branch"
199+
)
200+
'
201+
202+
test_expect_success "fetch test followRemoteHEAD warn-if-not-branch branch is different" '
203+
test_when_finished "git config unset remote.origin.followRemoteHEAD" &&
204+
(
205+
cd "$D" &&
206+
cd two &&
207+
git rev-parse --verify refs/remotes/origin/other &&
208+
git remote set-head origin other &&
209+
git rev-parse --verify refs/remotes/origin/HEAD &&
210+
git rev-parse --verify refs/remotes/origin/main &&
211+
git config set remote.origin.followRemoteHEAD "warn-if-not-some/different-branch" &&
212+
git fetch >actual &&
213+
echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \
214+
"but we have ${SQ}other${SQ} locally." >expect &&
215+
test_cmp expect actual &&
216+
head=$(git rev-parse refs/remotes/origin/HEAD) &&
217+
branch=$(git rev-parse refs/remotes/origin/other) &&
218+
test "z$head" = "z$branch"
219+
)
220+
'
221+
186222
test_expect_success "fetch test followRemoteHEAD always" '
187223
test_when_finished "git config unset remote.origin.followRemoteHEAD" &&
188224
(

0 commit comments

Comments
 (0)