]> git.ipfire.org Git - thirdparty/git.git/commitdiff
clone: respect remote unborn HEAD
authorJonathan Tan <jonathantanmy@google.com>
Fri, 5 Feb 2021 20:48:49 +0000 (12:48 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 5 Feb 2021 21:49:55 +0000 (13:49 -0800)
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>
Documentation/config/init.txt
builtin/clone.c
connect.c
t/t5606-clone-options.sh
t/t5702-protocol-v2.sh
transport.h

index dc77f8c8446801fddbce2dff1ef4e678f431e202..79c79d66174ebd9f2dfb867a6a39a339529b7fc0 100644 (file)
@@ -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.
index 211d4f54b09e611879de6314c7088889fda4a208..09dcd97a2e7342dceadd71c928c11566e0e0b08b 100644 (file)
@@ -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;
 }
index 328c2792504f9c5f1a0469a2fc5463be49eec5bc..879669df9309d543242486ac70386bd911e369ee 100644 (file)
--- a/connect.c
+++ b/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);
        }
 
index 7f082fb23b6af9292b279f9db129b8eb23a6e94b..ca6339a5fbaf57b7d2ed5bebd25cf7a5ad184db1 100755 (executable)
@@ -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' '
index 7d5b17909bb811148458f0566d624722f5cb8aff..b2ead93af97575e10a42624a9442d84e85fe830a 100755 (executable)
@@ -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" &&
 
index 1f5b60e4d3156984197364a39344c4bc53e0834a..24e15799e714aeb53f725b161a14172202cbae7e 100644 (file)
@@ -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 }