Merge branch 'fc/remote-helper-refmap'
authorJunio C Hamano <gitster@pobox.com>
Mon, 16 Jun 2014 19:18:15 +0000 (12:18 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 16 Jun 2014 19:18:15 +0000 (12:18 -0700)
Allow remote-helper/fast-import based transport to rename the refs
while transferring the history.

* fc/remote-helper-refmap:
transport-helper: remove unnecessary strbuf resets
transport-helper: add support to delete branches
fast-export: add support to delete refs
fast-import: add support to delete refs
transport-helper: add support to push symbolic refs
transport-helper: add support for old:new refspec
fast-export: add new --refspec option
fast-export: improve argument parsing

1  2 
Documentation/git-fast-import.txt
t/t5801-remote-helpers.sh
transport-helper.c
index 3ffa2fa2c8b38d2657b7ef21dfabbc2d816ade18,c32a9a5aeed23357be8fcd5c69bd317f9de323ba..377eeaa36db2407f76a9cd21777bb2eda4ff74bc
@@@ -231,7 -231,7 +231,7 @@@ Date Format
  ~~~~~~~~~~~~
  The following date formats are supported.  A frontend should select
  the format it will use for this import by passing the format name
 -in the \--date-format=<fmt> command line option.
 +in the \--date-format=<fmt> command-line option.
  
  `raw`::
        This is the Git native format and is `<time> SP <offutc>`.
@@@ -348,7 -348,7 +348,7 @@@ and control the current import process
  `done`::
        Marks the end of the stream. This command is optional
        unless the `done` feature was requested using the
 -      `--done` command line option or `feature done` command.
 +      `--done` command-line option or `feature done` command.
  
  `cat-blob`::
        Causes fast-import to print a blob in 'cat-file --batch'
@@@ -437,7 -437,7 +437,7 @@@ the email address from the other field
  of bytes, except `LT`, `GT` and `LF`.  `<name>` is typically UTF-8 encoded.
  
  The time of the change is specified by `<when>` using the date format
 -that was selected by the \--date-format=<fmt> command line option.
 +that was selected by the \--date-format=<fmt> command-line option.
  See ``Date Formats'' above for the set of supported formats, and
  their syntax.
  
@@@ -483,6 -483,9 +483,9 @@@ Marks must be declared (via `mark`) bef
  * Any valid Git SHA-1 expression that resolves to a commit.  See
    ``SPECIFYING REVISIONS'' in linkgit:gitrevisions[7] for details.
  
+ * The special null SHA-1 (40 zeros) specifies that the branch is to be
+   removed.
  The special case of restarting an incremental import from the
  current branch value should be written as:
  ----
@@@ -1085,7 -1088,7 +1088,7 @@@ Option commands must be the first comma
  feature commands), to give an option command after any non-option
  command is an error.
  
 -The following commandline options change import semantics and may therefore
 +The following command-line options change import semantics and may therefore
  not be passed as option:
  
  * date-format
  If the `done` feature is not in use, treated as if EOF was read.
  This can be used to tell fast-import to finish early.
  
 -If the `--done` command line option or `feature done` command is
 +If the `--done` command-line option or `feature done` command is
  in use, the `done` command is mandatory and marks the end of the
  stream.
  
index a00a660763451d57b2a9537ec1b99fcfd0c9a831,aec924b2e1d669b2554e0c0e8f6d4b88ab3cfce5..24194075468819881826c24c08b1aa97bacb9897
@@@ -87,13 -87,29 +87,29 @@@ test_expect_success 'push new branch b
        compare_refs local HEAD server refs/heads/new-name
  '
  
- test_expect_failure 'push new branch with old:new refspec' '
+ test_expect_success 'push new branch with old:new refspec' '
        (cd local &&
         git push origin new-name:new-refspec
        ) &&
        compare_refs local HEAD server refs/heads/new-refspec
  '
  
+ test_expect_success 'push new branch with HEAD:new refspec' '
+       (cd local &&
+        git checkout new-name
+        git push origin HEAD:new-refspec-2
+       ) &&
+       compare_refs local HEAD server refs/heads/new-refspec-2
+ '
+ test_expect_success 'push delete branch' '
+       (cd local &&
+        git push origin :new-name
+       ) &&
+       test_must_fail git --git-dir="server/.git" \
+        rev-parse --verify refs/heads/new-name
+ '
  test_expect_success 'forced push' '
        (cd local &&
        git checkout -b force-test &&
@@@ -212,30 -228,19 +228,30 @@@ test_expect_success 'push update refs f
        echo "update fail" >>file &&
        git commit -a -m "update fail" &&
        git rev-parse --verify testgit/origin/heads/update >expect &&
 -      GIT_REMOTE_TESTGIT_PUSH_ERROR="non-fast forward" &&
 -      export GIT_REMOTE_TESTGIT_PUSH_ERROR &&
 -      test_expect_code 1 git push origin update &&
 +      test_expect_code 1 env GIT_REMOTE_TESTGIT_FAILURE="non-fast forward" \
 +              git push origin update &&
        git rev-parse --verify testgit/origin/heads/update >actual &&
        test_cmp expect actual
        )
  '
  
 +clean_mark () {
 +      cut -f 2 -d ' ' "$1" |
 +      git cat-file --batch-check |
 +      grep commit |
 +      sort >$(basename "$1")
 +}
 +
 +cmp_marks () {
 +      test_when_finished "rm -rf git.marks testgit.marks" &&
 +      clean_mark ".git/testgit/$1/git.marks" &&
 +      clean_mark ".git/testgit/$1/testgit.marks" &&
 +      test_cmp git.marks testgit.marks
 +}
 +
  test_expect_success 'proper failure checks for fetching' '
 -      (GIT_REMOTE_TESTGIT_FAILURE=1 &&
 -      export GIT_REMOTE_TESTGIT_FAILURE &&
 -      cd local &&
 -      test_must_fail git fetch 2> error &&
 +      (cd local &&
 +      test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git fetch 2>error &&
        cat error &&
        grep -q "Error while running fast-import" error
        )
  
  test_expect_success 'proper failure checks for pushing' '
        (cd local &&
 -      test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git push --all
 +      git checkout -b crash master &&
 +      echo crash >>file &&
 +      git commit -a -m crash &&
 +      test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git push --all &&
 +      cmp_marks origin
        )
  '
  
diff --combined transport-helper.c
index 4cfb8f3a4acc36681774e165f92de7fbd948fde5,a90094d4a3b6132a37343be093ca78e41b246ffd..84c616f180bebc730d05f306906558dd0abad2a4
@@@ -58,7 -58,7 +58,7 @@@ static int recvline_fh(FILE *helper, st
        if (strbuf_getline(buffer, helper, '\n') == EOF) {
                if (debug)
                        fprintf(stderr, "Debug: Remote helper quit.\n");
 -              exit(128);
 +              return 1;
        }
  
        if (debug)
@@@ -71,6 -71,12 +71,6 @@@ static int recvline(struct helper_data 
        return recvline_fh(helper->out, buffer, helper->name);
  }
  
 -static void xchgline(struct helper_data *helper, struct strbuf *buffer)
 -{
 -      sendline(helper, buffer);
 -      recvline(helper, buffer);
 -}
 -
  static void write_constant(int fd, const char *str)
  {
        if (debug)
@@@ -101,6 -107,7 +101,6 @@@ static void do_take_over(struct transpo
  static struct child_process *get_helper(struct transport *transport)
  {
        struct helper_data *data = transport->data;
 -      struct argv_array argv = ARGV_ARRAY_INIT;
        struct strbuf buf = STRBUF_INIT;
        struct child_process *helper;
        const char **refspecs = NULL;
        helper->in = -1;
        helper->out = -1;
        helper->err = 0;
 -      argv_array_pushf(&argv, "git-remote-%s", data->name);
 -      argv_array_push(&argv, transport->remote->name);
 -      argv_array_push(&argv, remove_ext_force(transport->url));
 -      helper->argv = argv_array_detach(&argv, NULL);
 +      argv_array_pushf(&helper->args, "git-remote-%s", data->name);
 +      argv_array_push(&helper->args, transport->remote->name);
 +      argv_array_push(&helper->args, remove_ext_force(transport->url));
        helper->git_cmd = 0;
        helper->silent_exec_failure = 1;
  
        while (1) {
                const char *capname;
                int mandatory = 0;
 -              recvline(data, &buf);
 +              if (recvline(data, &buf))
 +                      exit(128);
  
                if (!*buf.buf)
                        break;
                } else if (!strcmp(capname, "signed-tags")) {
                        data->signed_tags = 1;
                } else if (starts_with(capname, "export-marks ")) {
 -                      struct strbuf arg = STRBUF_INIT;
 -                      strbuf_addstr(&arg, "--export-marks=");
 -                      strbuf_addstr(&arg, capname + strlen("export-marks "));
 -                      data->export_marks = strbuf_detach(&arg, NULL);
 +                      data->export_marks = xstrdup(capname + strlen("export-marks "));
                } else if (starts_with(capname, "import-marks")) {
 -                      struct strbuf arg = STRBUF_INIT;
 -                      strbuf_addstr(&arg, "--import-marks=");
 -                      strbuf_addstr(&arg, capname + strlen("import-marks "));
 -                      data->import_marks = strbuf_detach(&arg, NULL);
 +                      data->import_marks = xstrdup(capname + strlen("import-marks "));
                } else if (starts_with(capname, "no-private-update")) {
                        data->no_private_update = 1;
                } else if (mandatory) {
@@@ -243,6 -256,7 +243,6 @@@ static int disconnect_helper(struct tra
                close(data->helper->out);
                fclose(data->out);
                res = finish_command(data->helper);
 -              argv_array_free_detached(data->helper->argv);
                free(data->helper);
                data->helper = NULL;
        }
@@@ -293,9 -307,7 +293,9 @@@ static int set_helper_option(struct tra
                quote_c_style(value, &buf, NULL, 0);
        strbuf_addch(&buf, '\n');
  
 -      xchgline(data, &buf);
 +      sendline(data, &buf);
 +      if (recvline(data, &buf))
 +              exit(128);
  
        if (!strcmp(buf.buf, "ok"))
                ret = 0;
@@@ -367,8 -379,7 +367,8 @@@ static int fetch_with_fetch(struct tran
        sendline(data, &buf);
  
        while (1) {
 -              recvline(data, &buf);
 +              if (recvline(data, &buf))
 +                      exit(128);
  
                if (starts_with(buf.buf, "lock ")) {
                        const char *name = buf.buf + 5;
@@@ -394,16 -405,18 +394,16 @@@ static int get_importer(struct transpor
  {
        struct child_process *helper = get_helper(transport);
        struct helper_data *data = transport->data;
 -      struct argv_array argv = ARGV_ARRAY_INIT;
        int cat_blob_fd, code;
        memset(fastimport, 0, sizeof(*fastimport));
        fastimport->in = helper->out;
 -      argv_array_push(&argv, "fast-import");
 -      argv_array_push(&argv, debug ? "--stats" : "--quiet");
 +      argv_array_push(&fastimport->args, "fast-import");
 +      argv_array_push(&fastimport->args, debug ? "--stats" : "--quiet");
  
        if (data->bidi_import) {
                cat_blob_fd = xdup(helper->in);
 -              argv_array_pushf(&argv, "--cat-blob-fd=%d", cat_blob_fd);
 +              argv_array_pushf(&fastimport->args, "--cat-blob-fd=%d", cat_blob_fd);
        }
 -      fastimport->argv = argv.argv;
        fastimport->git_cmd = 1;
  
        code = start_command(fastimport);
@@@ -416,24 -429,24 +416,24 @@@ static int get_exporter(struct transpor
  {
        struct helper_data *data = transport->data;
        struct child_process *helper = get_helper(transport);
 -      int argc = 0, i;
 +      int i;
 +
        memset(fastexport, 0, sizeof(*fastexport));
  
        /* we need to duplicate helper->in because we want to use it after
         * fastexport is done with it. */
        fastexport->out = dup(helper->in);
 -      fastexport->argv = xcalloc(6 + revlist_args->nr, sizeof(*fastexport->argv));
 -      fastexport->argv[argc++] = "fast-export";
 -      fastexport->argv[argc++] = "--use-done-feature";
 -      fastexport->argv[argc++] = data->signed_tags ?
 -              "--signed-tags=verbatim" : "--signed-tags=warn-strip";
 +      argv_array_push(&fastexport->args, "fast-export");
 +      argv_array_push(&fastexport->args, "--use-done-feature");
 +      argv_array_push(&fastexport->args, data->signed_tags ?
 +              "--signed-tags=verbatim" : "--signed-tags=warn-strip");
        if (data->export_marks)
 -              fastexport->argv[argc++] = data->export_marks;
 +              argv_array_pushf(&fastexport->args, "--export-marks=%s.tmp", data->export_marks);
        if (data->import_marks)
 -              fastexport->argv[argc++] = data->import_marks;
 +              argv_array_pushf(&fastexport->args, "--import-marks=%s", data->import_marks);
  
        for (i = 0; i < revlist_args->nr; i++)
 -              fastexport->argv[argc++] = revlist_args->items[i].string;
 +              argv_array_push(&fastexport->args, revlist_args->items[i].string);
  
        fastexport->git_cmd = 1;
        return start_command(fastexport);
@@@ -474,6 -487,7 +474,6 @@@ static int fetch_with_import(struct tra
  
        if (finish_command(&fastimport))
                die("Error while running fast-import");
 -      argv_array_free_detached(fastimport.argv);
  
        /*
         * The fast-import stream of a remote helper that advertises
@@@ -549,9 -563,7 +549,9 @@@ static int process_connect_service(stru
                goto exit;
  
        sendline(data, &cmdbuf);
 -      recvline_fh(input, &cmdbuf, name);
 +      if (recvline_fh(input, &cmdbuf, name))
 +              exit(128);
 +
        if (!strcmp(cmdbuf.buf, "")) {
                data->no_disconnect_req = 1;
                if (debug)
@@@ -727,22 -739,16 +727,22 @@@ static int push_update_ref_status(struc
        return !(status == REF_STATUS_OK);
  }
  
 -static void push_update_refs_status(struct helper_data *data,
 +static int push_update_refs_status(struct helper_data *data,
                                    struct ref *remote_refs,
                                    int flags)
  {
        struct strbuf buf = STRBUF_INIT;
        struct ref *ref = remote_refs;
 +      int ret = 0;
 +
        for (;;) {
                char *private;
  
 -              recvline(data, &buf);
 +              if (recvline(data, &buf)) {
 +                      ret = 1;
 +                      break;
 +              }
 +
                if (!buf.len)
                        break;
  
                free(private);
        }
        strbuf_release(&buf);
 +      return ret;
  }
  
  static int push_refs_with_push(struct transport *transport,
        sendline(data, &buf);
        strbuf_release(&buf);
  
 -      push_update_refs_status(data, remote_refs, flags);
 -      return 0;
 +      return push_update_refs_status(data, remote_refs, flags);
  }
  
  static int push_refs_with_export(struct transport *transport,
        struct ref *ref;
        struct child_process *helper, exporter;
        struct helper_data *data = transport->data;
-       struct string_list revlist_args = STRING_LIST_INIT_NODUP;
+       struct string_list revlist_args = STRING_LIST_INIT_DUP;
        struct strbuf buf = STRBUF_INIT;
  
        if (!data->refspecs)
  
        write_constant(helper->in, "export\n");
  
-       strbuf_reset(&buf);
        for (ref = remote_refs; ref; ref = ref->next) {
                char *private;
                unsigned char sha1[20];
  
-               if (ref->deletion)
-                       die("remote-helpers do not support ref deletion");
                private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
                if (private && !get_sha1(private, sha1)) {
                        strbuf_addf(&buf, "^%s", private);
                free(private);
  
                if (ref->peer_ref) {
-                       if (strcmp(ref->peer_ref->name, ref->name))
-                               die("remote-helpers do not support old:new syntax");
-                       string_list_append(&revlist_args, ref->peer_ref->name);
+                       if (strcmp(ref->name, ref->peer_ref->name)) {
+                               if (!ref->deletion) {
+                                       const char *name;
+                                       int flag;
+                                       /* Follow symbolic refs (mainly for HEAD). */
+                                       name = resolve_ref_unsafe(ref->peer_ref->name, sha1, 1, &flag);
+                                       if (!name || !(flag & REF_ISSYMREF))
+                                               name = ref->peer_ref->name;
+                                       strbuf_addf(&buf, "%s:%s", name, ref->name);
+                               } else
+                                       strbuf_addf(&buf, ":%s", ref->name);
+                               string_list_append(&revlist_args, "--refspec");
+                               string_list_append(&revlist_args, buf.buf);
+                               strbuf_release(&buf);
+                       }
+                       if (!ref->deletion)
+                               string_list_append(&revlist_args, ref->peer_ref->name);
                }
        }
  
        if (get_exporter(transport, &exporter, &revlist_args))
                die("Couldn't run fast-export");
  
+       string_list_clear(&revlist_args, 1);
        if (finish_command(&exporter))
                die("Error while running fast-export");
 -      push_update_refs_status(data, remote_refs, flags);
 +      if (push_update_refs_status(data, remote_refs, flags))
 +              return 1;
 +
 +      if (data->export_marks) {
 +              strbuf_addf(&buf, "%s.tmp", data->export_marks);
 +              rename(buf.buf, data->export_marks);
 +              strbuf_release(&buf);
 +      }
 +
        return 0;
  }
  
@@@ -976,8 -988,7 +990,8 @@@ static struct ref *get_refs_list(struc
  
        while (1) {
                char *eov, *eon;
 -              recvline(data, &buf);
 +              if (recvline(data, &buf))
 +                      exit(128);
  
                if (!*buf.buf)
                        break;
  
  int transport_helper_init(struct transport *transport, const char *name)
  {
 -      struct helper_data *data = xcalloc(sizeof(*data), 1);
 +      struct helper_data *data = xcalloc(1, sizeof(*data));
        data->name = name;
  
        if (getenv("GIT_TRANSPORT_HELPER_DEBUG"))