Skip to content

Commit 3f763dd

Browse files
ferdinandybgitster
authored andcommitted
fetch: set remote/HEAD if it does not exist
When cloning a repository remote/HEAD is created, but when the user creates a repository with git init, and later adds a remote, remote/HEAD is only created if the user explicitly runs a variant of "remote set-head". Attempt to set remote/HEAD during fetch, if the user does not have it already set. Silently ignore any errors. Signed-off-by: Bence Ferdinandy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 9963746 commit 3f763dd

12 files changed

+203
-17
lines changed

builtin/fetch.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,6 +1577,66 @@ static int backfill_tags(struct display_state *display_state,
15771577
return retcode;
15781578
}
15791579

1580+
static const char *strip_refshead(const char *name){
1581+
skip_prefix(name, "refs/heads/", &name);
1582+
return name;
1583+
}
1584+
1585+
static int set_head(const struct ref *remote_refs)
1586+
{
1587+
int result = 0;
1588+
struct strbuf b_head = STRBUF_INIT, b_remote_head = STRBUF_INIT;
1589+
const char *remote = gtransport->remote->name;
1590+
char *head_name = NULL;
1591+
struct ref *ref, *matches;
1592+
struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
1593+
struct refspec_item refspec = {
1594+
.force = 0,
1595+
.pattern = 1,
1596+
.src = (char *) "refs/heads/*",
1597+
.dst = (char *) "refs/heads/*",
1598+
};
1599+
struct string_list heads = STRING_LIST_INIT_DUP;
1600+
struct ref_store *refs = get_main_ref_store(the_repository);
1601+
1602+
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
1603+
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
1604+
fetch_map, 1);
1605+
for (ref = matches; ref; ref = ref->next) {
1606+
string_list_append(&heads, strip_refshead(ref->name));
1607+
}
1608+
1609+
1610+
if (!heads.nr)
1611+
result = 1;
1612+
else if (heads.nr > 1)
1613+
result = 1;
1614+
else
1615+
head_name = xstrdup(heads.items[0].string);
1616+
1617+
if (!head_name)
1618+
goto cleanup;
1619+
strbuf_addf(&b_head, "refs/remotes/%s/HEAD", remote);
1620+
strbuf_addf(&b_remote_head, "refs/remotes/%s/%s", remote, head_name);
1621+
/* make sure it's valid */
1622+
if (!refs_ref_exists(refs, b_remote_head.buf)) {
1623+
result = 1;
1624+
goto cleanup;
1625+
}
1626+
if (refs_update_symref_extended(refs, b_head.buf, b_remote_head.buf,
1627+
"fetch", NULL, 1))
1628+
result = 1;
1629+
1630+
cleanup:
1631+
free(head_name);
1632+
free_refs(fetch_map);
1633+
free_refs(matches);
1634+
string_list_clear(&heads, 0);
1635+
strbuf_release(&b_head);
1636+
strbuf_release(&b_remote_head);
1637+
return result;
1638+
}
1639+
15801640
static int do_fetch(struct transport *transport,
15811641
struct refspec *rs,
15821642
const struct fetch_config *config)
@@ -1646,6 +1706,8 @@ static int do_fetch(struct transport *transport,
16461706
"refs/tags/");
16471707
}
16481708

1709+
strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
1710+
16491711
if (must_list_refs) {
16501712
trace2_region_enter("fetch", "remote_refs", the_repository);
16511713
remote_refs = transport_get_remote_refs(transport,
@@ -1790,6 +1852,12 @@ static int do_fetch(struct transport *transport,
17901852
"you need to specify exactly one branch with the --set-upstream option"));
17911853
}
17921854
}
1855+
if (set_head(remote_refs))
1856+
;
1857+
/*
1858+
* Way too many cases where this can go wrong
1859+
* so let's just fail silently for now.
1860+
*/
17931861

17941862
cleanup:
17951863
if (retcode) {

t/t4207-log-decoration-colors.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ ${c_reset}${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \
5959
${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B
6060
${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
6161
${c_tag}tag: ${c_reset}${c_tag}A1${c_reset}${c_commit}, \
62-
${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit})${c_reset} A1
62+
${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit}, \
63+
${c_reset}${c_remoteBranch}other/HEAD${c_reset}${c_commit})${c_reset} A1
6364
${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
6465
${c_stash}refs/stash${c_reset}${c_commit})${c_reset} On main: Changes to A.t
6566
${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\

t/t5505-remote.sh

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ test_expect_success 'add another remote' '
7474
cd test &&
7575
git remote add -f second ../two &&
7676
tokens_match "origin second" "$(git remote)" &&
77-
check_tracking_branch second main side another &&
77+
check_tracking_branch second main side another HEAD &&
7878
git for-each-ref "--format=%(refname)" refs/remotes |
7979
sed -e "/^refs\/remotes\/origin\//d" \
8080
-e "/^refs\/remotes\/second\//d" >actual &&
@@ -500,7 +500,7 @@ test_expect_success 'set-head --auto has no problem w/multiple HEADs' '
500500
cd test &&
501501
git fetch two "refs/heads/*:refs/remotes/two/*" &&
502502
git remote set-head --auto two >output 2>&1 &&
503-
echo "${SQ}two/HEAD${SQ} is now created and points to ${SQ}main${SQ}" >expect &&
503+
echo "${SQ}two/HEAD${SQ} is unchanged and points to ${SQ}main${SQ}" >expect &&
504504
test_cmp expect output
505505
)
506506
'
@@ -788,8 +788,10 @@ test_expect_success 'reject --no-no-tags' '
788788
'
789789

790790
cat >one/expect <<\EOF
791+
apis/HEAD -> apis/main
791792
apis/main
792793
apis/side
794+
drosophila/HEAD -> drosophila/main
793795
drosophila/another
794796
drosophila/main
795797
drosophila/side
@@ -807,19 +809,22 @@ test_expect_success 'update' '
807809
'
808810

809811
cat >one/expect <<\EOF
812+
drosophila/HEAD -> drosophila/main
810813
drosophila/another
811814
drosophila/main
812815
drosophila/side
816+
manduca/HEAD -> manduca/main
813817
manduca/main
814818
manduca/side
819+
megaloprepus/HEAD -> megaloprepus/main
815820
megaloprepus/main
816821
megaloprepus/side
817822
EOF
818823

819824
test_expect_success 'update with arguments' '
820825
(
821826
cd one &&
822-
for b in $(git branch -r)
827+
for b in $(git branch -r | grep -v HEAD)
823828
do
824829
git branch -r -d $b || exit 1
825830
done &&
@@ -851,18 +856,21 @@ test_expect_success 'update --prune' '
851856
'
852857

853858
cat >one/expect <<-\EOF
859+
apis/HEAD -> apis/main
854860
apis/main
855861
apis/side
862+
manduca/HEAD -> manduca/main
856863
manduca/main
857864
manduca/side
865+
megaloprepus/HEAD -> megaloprepus/main
858866
megaloprepus/main
859867
megaloprepus/side
860868
EOF
861869

862870
test_expect_success 'update default' '
863871
(
864872
cd one &&
865-
for b in $(git branch -r)
873+
for b in $(git branch -r | grep -v HEAD)
866874
do
867875
git branch -r -d $b || exit 1
868876
done &&
@@ -874,6 +882,7 @@ test_expect_success 'update default' '
874882
'
875883

876884
cat >one/expect <<\EOF
885+
drosophila/HEAD -> drosophila/main
877886
drosophila/another
878887
drosophila/main
879888
drosophila/side
@@ -882,7 +891,7 @@ EOF
882891
test_expect_success 'update default (overridden, with funny whitespace)' '
883892
(
884893
cd one &&
885-
for b in $(git branch -r)
894+
for b in $(git branch -r | grep -v HEAD)
886895
do
887896
git branch -r -d $b || exit 1
888897
done &&
@@ -896,7 +905,7 @@ test_expect_success 'update default (overridden, with funny whitespace)' '
896905
test_expect_success 'update (with remotes.default defined)' '
897906
(
898907
cd one &&
899-
for b in $(git branch -r)
908+
for b in $(git branch -r | grep -v HEAD)
900909
do
901910
git branch -r -d $b || exit 1
902911
done &&

t/t5510-fetch.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,30 @@ test_expect_success "fetch test for-merge" '
7575
cut -f -2 .git/FETCH_HEAD >actual &&
7676
test_cmp expected actual'
7777

78+
test_expect_success "fetch test remote HEAD" '
79+
cd "$D" &&
80+
cd two &&
81+
git fetch &&
82+
git rev-parse --verify refs/remotes/origin/HEAD &&
83+
git rev-parse --verify refs/remotes/origin/main &&
84+
head=$(git rev-parse refs/remotes/origin/HEAD) &&
85+
branch=$(git rev-parse refs/remotes/origin/main) &&
86+
test "z$head" = "z$branch"'
87+
88+
test_expect_success "fetch test remote HEAD change" '
89+
cd "$D" &&
90+
cd two &&
91+
git switch -c other &&
92+
git push -u origin other &&
93+
git rev-parse --verify refs/remotes/origin/HEAD &&
94+
git rev-parse --verify refs/remotes/origin/main &&
95+
git rev-parse --verify refs/remotes/origin/other &&
96+
git remote set-head origin other &&
97+
git fetch &&
98+
head=$(git rev-parse refs/remotes/origin/HEAD) &&
99+
branch=$(git rev-parse refs/remotes/origin/other) &&
100+
test "z$head" = "z$branch"'
101+
78102
test_expect_success 'fetch --prune on its own works as expected' '
79103
cd "$D" &&
80104
git clone . prune &&

t/t5512-ls-remote.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@ test_expect_success 'ls-remote with filtered symref (refname)' '
293293
cat >expect <<-EOF &&
294294
ref: refs/heads/main HEAD
295295
$rev HEAD
296+
ref: refs/remotes/origin/main refs/remotes/origin/HEAD
297+
$rev refs/remotes/origin/HEAD
296298
EOF
297299
git ls-remote --symref . HEAD >actual &&
298300
test_cmp expect actual

t/t5514-fetch-multiple.sh

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,17 @@ test_expect_success setup '
4545
'
4646

4747
cat > test/expect << EOF
48+
one/HEAD -> one/main
4849
one/main
4950
one/side
5051
origin/HEAD -> origin/main
5152
origin/main
5253
origin/side
54+
three/HEAD -> three/main
5355
three/another
5456
three/main
5557
three/side
58+
two/HEAD -> two/main
5659
two/another
5760
two/main
5861
two/side
@@ -97,6 +100,7 @@ cat > expect << EOF
97100
origin/HEAD -> origin/main
98101
origin/main
99102
origin/side
103+
three/HEAD -> three/main
100104
three/another
101105
three/main
102106
three/side
@@ -112,8 +116,10 @@ test_expect_success 'git fetch --multiple (but only one remote)' '
112116
'
113117

114118
cat > expect << EOF
119+
one/HEAD -> one/main
115120
one/main
116121
one/side
122+
two/HEAD -> two/main
117123
two/another
118124
two/main
119125
two/side
@@ -141,7 +147,7 @@ test_expect_success 'git fetch --multiple (bad remote names)' '
141147

142148
test_expect_success 'git fetch --all (skipFetchAll)' '
143149
(cd test4 &&
144-
for b in $(git branch -r)
150+
for b in $(git branch -r | grep -v HEAD)
145151
do
146152
git branch -r -d $b || exit 1
147153
done &&
@@ -153,19 +159,22 @@ test_expect_success 'git fetch --all (skipFetchAll)' '
153159
'
154160

155161
cat > expect << EOF
162+
one/HEAD -> one/main
156163
one/main
157164
one/side
165+
three/HEAD -> three/main
158166
three/another
159167
three/main
160168
three/side
169+
two/HEAD -> two/main
161170
two/another
162171
two/main
163172
two/side
164173
EOF
165174

166175
test_expect_success 'git fetch --multiple (ignoring skipFetchAll)' '
167176
(cd test4 &&
168-
for b in $(git branch -r)
177+
for b in $(git branch -r | grep -v HEAD)
169178
do
170179
git branch -r -d $b || exit 1
171180
done &&
@@ -221,14 +230,17 @@ test_expect_success 'git fetch --multiple --jobs=0 picks a default' '
221230

222231
create_fetch_all_expect () {
223232
cat >expect <<-\EOF
233+
one/HEAD -> one/main
224234
one/main
225235
one/side
226236
origin/HEAD -> origin/main
227237
origin/main
228238
origin/side
239+
three/HEAD -> three/main
229240
three/another
230241
three/main
231242
three/side
243+
two/HEAD -> two/main
232244
two/another
233245
two/main
234246
two/side
@@ -265,6 +277,7 @@ test_expect_success 'git fetch (fetch all remotes with fetch.all = true)' '
265277

266278
create_fetch_one_expect () {
267279
cat >expect <<-\EOF
280+
one/HEAD -> one/main
268281
one/main
269282
one/side
270283
origin/HEAD -> origin/main

t/t5516-fetch-push.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1395,7 +1395,8 @@ test_expect_success 'fetch follows tags by default' '
13951395
git tag -m "annotated" tag &&
13961396
git for-each-ref >tmp1 &&
13971397
sed -n "p; s|refs/heads/main$|refs/remotes/origin/main|p" tmp1 |
1398-
sort -k 3 >../expect
1398+
sed -n "p; s|refs/heads/main$|refs/remotes/origin/HEAD|p" |
1399+
sort -k 4 >../expect
13991400
) &&
14001401
test_when_finished "rm -rf dst" &&
14011402
git init dst &&

t/t5527-fetch-odd-refs.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ test_expect_success LONG_REF 'fetch handles extremely long refname' '
5252
long
5353
main
5454
EOF
55-
git for-each-ref --format="%(subject)" refs/remotes/long >actual &&
55+
git for-each-ref --format="%(subject)" refs/remotes/long \
56+
--exclude=refs/remotes/long/HEAD >actual &&
5657
test_cmp expect actual
5758
'
5859

t/t7900-maintenance.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,8 @@ test_expect_success 'incremental-repack task' '
328328
329329
# Delete refs that have not been repacked in these packs.
330330
git for-each-ref --format="delete %(refname)" \
331-
refs/prefetch refs/tags refs/remotes >refs &&
331+
refs/prefetch refs/tags refs/remotes \
332+
--exclude=refs/remotes/*/HEAD >refs &&
332333
git update-ref --stdin <refs &&
333334
334335
# Replace the object directory with this pack layout.

t/t9210-scalar.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ test_expect_success 'scalar clone' '
150150
"$(pwd)" &&
151151
152152
git for-each-ref --format="%(refname)" refs/remotes/origin/ >actual &&
153-
echo "refs/remotes/origin/parallel" >expect &&
153+
echo "refs/remotes/origin/HEAD" >>expect &&
154+
echo "refs/remotes/origin/parallel" >>expect &&
154155
test_cmp expect actual &&
155156
156157
test_path_is_missing 1/2 &&
@@ -219,7 +220,7 @@ test_expect_success 'scalar reconfigure --all with includeIf.onbranch' '
219220
done
220221
'
221222

222-
test_expect_success 'scalar reconfigure --all with detached HEADs' '
223+
test_expect_success 'scalar reconfigure --all with detached HEADs' '
223224
repos="two three four" &&
224225
for num in $repos
225226
do

0 commit comments

Comments
 (0)