зеркало из https://github.com/microsoft/git.git
clone: respect remote unborn HEAD
Teach Git to use the "unborn" feature introduced in a previous patch as follows: Git will always send the "unborn" argument if it is supported by the server. During "git clone", if cloning an empty repository, Git will use the new information to determine the local branch to create. In all other cases, Git will ignore it. Signed-off-by: Jonathan Tan <jonathantanmy@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Родитель
39835409d1
Коммит
4f37d45706
|
@ -4,4 +4,4 @@ init.templateDir::
|
|||
|
||||
init.defaultBranch::
|
||||
Allows overriding the default branch name e.g. when initializing
|
||||
a new repository or when cloning an empty repository.
|
||||
a new repository.
|
||||
|
|
|
@ -1330,8 +1330,19 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
|||
remote_head = NULL;
|
||||
option_no_checkout = 1;
|
||||
if (!option_bare) {
|
||||
const char *branch = git_default_branch_name();
|
||||
char *ref = xstrfmt("refs/heads/%s", branch);
|
||||
const char *branch;
|
||||
char *ref;
|
||||
|
||||
if (transport_ls_refs_options.unborn_head_target &&
|
||||
skip_prefix(transport_ls_refs_options.unborn_head_target,
|
||||
"refs/heads/", &branch)) {
|
||||
ref = transport_ls_refs_options.unborn_head_target;
|
||||
transport_ls_refs_options.unborn_head_target = NULL;
|
||||
create_symref("HEAD", ref, reflog_msg.buf);
|
||||
} else {
|
||||
branch = git_default_branch_name();
|
||||
ref = xstrfmt("refs/heads/%s", branch);
|
||||
}
|
||||
|
||||
install_branch_config(0, branch, remote_name, ref);
|
||||
free(ref);
|
||||
|
@ -1385,5 +1396,6 @@ cleanup:
|
|||
junk_mode = JUNK_LEAVE_ALL;
|
||||
|
||||
strvec_clear(&transport_ls_refs_options.ref_prefixes);
|
||||
free(transport_ls_refs_options.unborn_head_target);
|
||||
return err;
|
||||
}
|
||||
|
|
28
connect.c
28
connect.c
|
@ -376,7 +376,8 @@ struct ref **get_remote_heads(struct packet_reader *reader,
|
|||
}
|
||||
|
||||
/* Returns 1 when a valid ref has been added to `list`, 0 otherwise */
|
||||
static int process_ref_v2(struct packet_reader *reader, struct ref ***list)
|
||||
static int process_ref_v2(struct packet_reader *reader, struct ref ***list,
|
||||
char **unborn_head_target)
|
||||
{
|
||||
int ret = 1;
|
||||
int i = 0;
|
||||
|
@ -397,6 +398,25 @@ static int process_ref_v2(struct packet_reader *reader, struct ref ***list)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (!strcmp("unborn", line_sections.items[i].string)) {
|
||||
i++;
|
||||
if (unborn_head_target &&
|
||||
!strcmp("HEAD", line_sections.items[i++].string)) {
|
||||
/*
|
||||
* Look for the symref target (if any). If found,
|
||||
* return it to the caller.
|
||||
*/
|
||||
for (; i < line_sections.nr; i++) {
|
||||
const char *arg = line_sections.items[i].string;
|
||||
|
||||
if (skip_prefix(arg, "symref-target:", &arg)) {
|
||||
*unborn_head_target = xstrdup(arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
if (parse_oid_hex_algop(line_sections.items[i++].string, &old_oid, &end, reader->hash_algo) ||
|
||||
*end) {
|
||||
ret = 0;
|
||||
|
@ -461,6 +481,8 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
|
|||
const char *hash_name;
|
||||
struct strvec *ref_prefixes = transport_options ?
|
||||
&transport_options->ref_prefixes : NULL;
|
||||
char **unborn_head_target = transport_options ?
|
||||
&transport_options->unborn_head_target : NULL;
|
||||
*list = NULL;
|
||||
|
||||
if (server_supports_v2("ls-refs", 1))
|
||||
|
@ -490,6 +512,8 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
|
|||
if (!for_push)
|
||||
packet_write_fmt(fd_out, "peel\n");
|
||||
packet_write_fmt(fd_out, "symrefs\n");
|
||||
if (server_supports_feature("ls-refs", "unborn", 0))
|
||||
packet_write_fmt(fd_out, "unborn\n");
|
||||
for (i = 0; ref_prefixes && i < ref_prefixes->nr; i++) {
|
||||
packet_write_fmt(fd_out, "ref-prefix %s\n",
|
||||
ref_prefixes->v[i]);
|
||||
|
@ -498,7 +522,7 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
|
|||
|
||||
/* Process response from server */
|
||||
while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
|
||||
if (!process_ref_v2(reader, &list))
|
||||
if (!process_ref_v2(reader, &list, unborn_head_target))
|
||||
die(_("invalid ls-refs response: %s"), reader->line);
|
||||
}
|
||||
|
||||
|
|
|
@ -102,11 +102,13 @@ test_expect_success 'redirected clone -v does show progress' '
|
|||
'
|
||||
|
||||
test_expect_success 'chooses correct default initial branch name' '
|
||||
git init --bare empty &&
|
||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
|
||||
git -c init.defaultBranch=foo init --bare empty &&
|
||||
test_config -C empty lsrefs.unborn advertise &&
|
||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
|
||||
git -c init.defaultBranch=up clone empty whats-up &&
|
||||
test refs/heads/up = $(git -C whats-up symbolic-ref HEAD) &&
|
||||
test refs/heads/up = $(git -C whats-up config branch.up.merge)
|
||||
test refs/heads/foo = $(git -C whats-up symbolic-ref HEAD) &&
|
||||
test refs/heads/foo = $(git -C whats-up config branch.foo.merge)
|
||||
'
|
||||
|
||||
test_expect_success 'guesses initial branch name correctly' '
|
||||
|
|
|
@ -209,6 +209,31 @@ test_expect_success 'clone with file:// using protocol v2' '
|
|||
grep "ref-prefix refs/tags/" log
|
||||
'
|
||||
|
||||
test_expect_success 'clone of empty repo propagates name of default branch' '
|
||||
test_when_finished "rm -rf file_empty_parent file_empty_child" &&
|
||||
|
||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
|
||||
git -c init.defaultBranch=mydefaultbranch init file_empty_parent &&
|
||||
|
||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
|
||||
git -c init.defaultBranch=main -c protocol.version=2 \
|
||||
clone "file://$(pwd)/file_empty_parent" file_empty_child &&
|
||||
grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
|
||||
'
|
||||
|
||||
test_expect_success '...but not if explicitly forbidden by config' '
|
||||
test_when_finished "rm -rf file_empty_parent file_empty_child" &&
|
||||
|
||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
|
||||
git -c init.defaultBranch=mydefaultbranch init file_empty_parent &&
|
||||
test_config -C file_empty_parent lsrefs.unborn ignore &&
|
||||
|
||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
|
||||
git -c init.defaultBranch=main -c protocol.version=2 \
|
||||
clone "file://$(pwd)/file_empty_parent" file_empty_child &&
|
||||
! grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
|
||||
'
|
||||
|
||||
test_expect_success 'fetch with file:// using protocol v2' '
|
||||
test_when_finished "rm -f log" &&
|
||||
|
||||
|
|
|
@ -243,6 +243,14 @@ struct transport_ls_refs_options {
|
|||
* provided ref_prefixes.
|
||||
*/
|
||||
struct strvec ref_prefixes;
|
||||
|
||||
/*
|
||||
* If unborn_head_target is not NULL, and the remote reports HEAD as
|
||||
* pointing to an unborn branch, transport_get_remote_refs() stores the
|
||||
* unborn branch in unborn_head_target. It should be freed by the
|
||||
* caller.
|
||||
*/
|
||||
char *unborn_head_target;
|
||||
};
|
||||
#define TRANSPORT_LS_REFS_OPTIONS_INIT { STRVEC_INIT }
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче