Skip to content

Commit cf92cb2

Browse files
committed
Merge branch 'jk/clone-unborn-confusion'
"git clone" from a repository with some ref whose HEAD is unborn did not set the HEAD in the resulting repository correctly, which has been corrected. * jk/clone-unborn-confusion: clone: move unborn head creation to update_head() clone: use remote branch if it matches default HEAD clone: propagate empty remote HEAD even with other branches clone: drop extra newline from warning message
2 parents 99c0d94 + daf7898 commit cf92cb2

File tree

3 files changed

+117
-39
lines changed

3 files changed

+117
-39
lines changed

builtin/clone.c

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ static void update_remote_refs(const struct ref *refs,
607607
}
608608

609609
static void update_head(const struct ref *our, const struct ref *remote,
610-
const char *msg)
610+
const char *unborn, const char *msg)
611611
{
612612
const char *head;
613613
if (our && skip_prefix(our->name, "refs/heads/", &head)) {
@@ -633,6 +633,15 @@ static void update_head(const struct ref *our, const struct ref *remote,
633633
*/
634634
update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
635635
UPDATE_REFS_DIE_ON_ERR);
636+
} else if (unborn && skip_prefix(unborn, "refs/heads/", &head)) {
637+
/*
638+
* Unborn head from remote; same as "our" case above except
639+
* that we have no ref to update.
640+
*/
641+
if (create_symref("HEAD", unborn, NULL) < 0)
642+
die(_("unable to update HEAD"));
643+
if (!option_bare)
644+
install_branch_config(0, head, remote_name, unborn);
636645
}
637646
}
638647

@@ -673,7 +682,7 @@ static int checkout(int submodule_progress, int filter_submodules)
673682
head = resolve_refdup("HEAD", RESOLVE_REF_READING, &oid, NULL);
674683
if (!head) {
675684
warning(_("remote HEAD refers to nonexistent ref, "
676-
"unable to checkout.\n"));
685+
"unable to checkout"));
677686
return 0;
678687
}
679688
if (!strcmp(head, "HEAD")) {
@@ -877,6 +886,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
877886
const struct ref *refs, *remote_head;
878887
struct ref *remote_head_points_at = NULL;
879888
const struct ref *our_head_points_at;
889+
char *unborn_head = NULL;
880890
struct ref *mapped_refs = NULL;
881891
const struct ref *ref;
882892
struct strbuf key = STRBUF_INIT;
@@ -1267,51 +1277,49 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
12671277
if (transport_fetch_refs(transport, mapped_refs))
12681278
die(_("remote transport reported error"));
12691279
}
1270-
1271-
remote_head = find_ref_by_name(refs, "HEAD");
1272-
remote_head_points_at =
1273-
guess_remote_head(remote_head, mapped_refs, 0);
1274-
1275-
if (option_branch) {
1276-
our_head_points_at =
1277-
find_remote_branch(mapped_refs, option_branch);
1278-
1279-
if (!our_head_points_at)
1280-
die(_("Remote branch %s not found in upstream %s"),
1281-
option_branch, remote_name);
1282-
}
1283-
else
1284-
our_head_points_at = remote_head_points_at;
12851280
}
1286-
else {
1287-
const char *branch;
1288-
const char *ref;
1289-
char *ref_free = NULL;
12901281

1291-
if (option_branch)
1292-
die(_("Remote branch %s not found in upstream %s"),
1293-
option_branch, remote_name);
1282+
remote_head = find_ref_by_name(refs, "HEAD");
1283+
remote_head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
12941284

1295-
warning(_("You appear to have cloned an empty repository."));
1285+
if (option_branch) {
1286+
our_head_points_at = find_remote_branch(mapped_refs, option_branch);
1287+
if (!our_head_points_at)
1288+
die(_("Remote branch %s not found in upstream %s"),
1289+
option_branch, remote_name);
1290+
} else if (remote_head_points_at) {
1291+
our_head_points_at = remote_head_points_at;
1292+
} else if (remote_head) {
12961293
our_head_points_at = NULL;
1297-
remote_head_points_at = NULL;
1298-
remote_head = NULL;
1299-
option_no_checkout = 1;
1294+
} else {
1295+
const char *branch;
1296+
1297+
if (!mapped_refs) {
1298+
warning(_("You appear to have cloned an empty repository."));
1299+
option_no_checkout = 1;
1300+
}
13001301

13011302
if (transport_ls_refs_options.unborn_head_target &&
13021303
skip_prefix(transport_ls_refs_options.unborn_head_target,
13031304
"refs/heads/", &branch)) {
1304-
ref = transport_ls_refs_options.unborn_head_target;
1305-
create_symref("HEAD", ref, reflog_msg.buf);
1305+
unborn_head = xstrdup(transport_ls_refs_options.unborn_head_target);
13061306
} else {
13071307
branch = git_default_branch_name(0);
1308-
ref_free = xstrfmt("refs/heads/%s", branch);
1309-
ref = ref_free;
1308+
unborn_head = xstrfmt("refs/heads/%s", branch);
13101309
}
13111310

1312-
if (!option_bare)
1313-
install_branch_config(0, branch, remote_name, ref);
1314-
free(ref_free);
1311+
/*
1312+
* We may have selected a local default branch name "foo",
1313+
* and even though the remote's HEAD does not point there,
1314+
* it may still have a "foo" branch. If so, set it up so
1315+
* that we can follow the usual checkout code later.
1316+
*
1317+
* Note that for an empty repo we'll already have set
1318+
* option_no_checkout above, which would work against us here.
1319+
* But for an empty repo, find_remote_branch() can never find
1320+
* a match.
1321+
*/
1322+
our_head_points_at = find_remote_branch(mapped_refs, branch);
13151323
}
13161324

13171325
write_refspec_config(src_ref_prefix, our_head_points_at,
@@ -1331,7 +1339,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
13311339
branch_top.buf, reflog_msg.buf, transport,
13321340
!is_local);
13331341

1334-
update_head(our_head_points_at, remote_head, reflog_msg.buf);
1342+
update_head(our_head_points_at, remote_head, unborn_head, reflog_msg.buf);
13351343

13361344
/*
13371345
* We want to show progress for recursive submodule clones iff
@@ -1358,6 +1366,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
13581366
strbuf_release(&key);
13591367
free_refs(mapped_refs);
13601368
free_refs(remote_head_points_at);
1369+
free(unborn_head);
13611370
free(dir);
13621371
free(path);
13631372
UNLEAK(repo);

t/t5605-clone-local.sh

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ test_expect_success 'preparing origin repository' '
2121
git bundle create b2.bundle main &&
2222
mkdir dir &&
2323
cp b1.bundle dir/b3 &&
24-
cp b1.bundle b4
24+
cp b1.bundle b4 &&
25+
git branch not-main main &&
26+
git bundle create b5.bundle not-main
2527
'
2628

2729
test_expect_success 'local clone without .git suffix' '
@@ -83,11 +85,19 @@ test_expect_success 'bundle clone from b4.bundle that does not exist' '
8385
test_must_fail git clone b4.bundle bb
8486
'
8587

86-
test_expect_success 'bundle clone with nonexistent HEAD' '
88+
test_expect_success 'bundle clone with nonexistent HEAD (match default)' '
8789
git clone b2.bundle b2 &&
8890
(cd b2 &&
8991
git fetch &&
90-
test_must_fail git rev-parse --verify refs/heads/main)
92+
git rev-parse --verify refs/heads/main)
93+
'
94+
95+
test_expect_success 'bundle clone with nonexistent HEAD (no match default)' '
96+
git clone b5.bundle b5 &&
97+
(cd b5 &&
98+
git fetch &&
99+
test_must_fail git rev-parse --verify refs/heads/main &&
100+
test_must_fail git rev-parse --verify refs/heads/not-main)
91101
'
92102

93103
test_expect_success 'clone empty repository' '

t/t5702-protocol-v2.sh

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,65 @@ test_expect_success 'bare clone propagates empty default branch' '
250250
grep "refs/heads/mydefaultbranch" file_empty_child.git/HEAD
251251
'
252252

253+
test_expect_success 'clone propagates unborn HEAD from non-empty repo' '
254+
test_when_finished "rm -rf file_unborn_parent file_unborn_child" &&
255+
256+
git init file_unborn_parent &&
257+
(
258+
cd file_unborn_parent &&
259+
git checkout -b branchwithstuff &&
260+
test_commit --no-tag stuff &&
261+
git symbolic-ref HEAD refs/heads/mydefaultbranch
262+
) &&
263+
264+
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
265+
git -c init.defaultBranch=main -c protocol.version=2 \
266+
clone "file://$(pwd)/file_unborn_parent" \
267+
file_unborn_child 2>stderr &&
268+
grep "refs/heads/mydefaultbranch" file_unborn_child/.git/HEAD &&
269+
grep "warning: remote HEAD refers to nonexistent ref" stderr
270+
'
271+
272+
test_expect_success 'bare clone propagates unborn HEAD from non-empty repo' '
273+
test_when_finished "rm -rf file_unborn_parent file_unborn_child.git" &&
274+
275+
git init file_unborn_parent &&
276+
(
277+
cd file_unborn_parent &&
278+
git checkout -b branchwithstuff &&
279+
test_commit --no-tag stuff &&
280+
git symbolic-ref HEAD refs/heads/mydefaultbranch
281+
) &&
282+
283+
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
284+
git -c init.defaultBranch=main -c protocol.version=2 \
285+
clone --bare "file://$(pwd)/file_unborn_parent" \
286+
file_unborn_child.git 2>stderr &&
287+
grep "refs/heads/mydefaultbranch" file_unborn_child.git/HEAD &&
288+
! grep "warning:" stderr
289+
'
290+
291+
test_expect_success 'defaulted HEAD uses remote branch if available' '
292+
test_when_finished "rm -rf file_unborn_parent file_unborn_child" &&
293+
294+
git init file_unborn_parent &&
295+
(
296+
cd file_unborn_parent &&
297+
git config lsrefs.unborn ignore &&
298+
git checkout -b branchwithstuff &&
299+
test_commit --no-tag stuff &&
300+
git symbolic-ref HEAD refs/heads/mydefaultbranch
301+
) &&
302+
303+
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
304+
git -c init.defaultBranch=branchwithstuff -c protocol.version=2 \
305+
clone "file://$(pwd)/file_unborn_parent" \
306+
file_unborn_child 2>stderr &&
307+
grep "refs/heads/branchwithstuff" file_unborn_child/.git/HEAD &&
308+
test_path_is_file file_unborn_child/stuff.t &&
309+
! grep "warning:" stderr
310+
'
311+
253312
test_expect_success 'fetch with file:// using protocol v2' '
254313
test_when_finished "rm -f log" &&
255314

0 commit comments

Comments
 (0)