Merge branch 'pt/xdg-config-path' into maint
[gitweb.git] / transport-helper.c
index b468e4f88e730f88a1d231a574585ace56e7066a..5d99a6bc2e7a10d40f6d8213e2f3cad254dbdb04 100644 (file)
@@ -5,7 +5,6 @@
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
-#include "quote.h"
 #include "remote.h"
 #include "string-list.h"
 #include "thread-utils.h"
@@ -98,10 +97,11 @@ static void do_take_over(struct transport *transport)
        free(data);
 }
 
+static void standard_options(struct transport *t);
+
 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;
@@ -109,29 +109,23 @@ static struct child_process *get_helper(struct transport *transport)
        int refspec_alloc = 0;
        int duped;
        int code;
-       char git_dir_buf[sizeof(GIT_DIR_ENVIRONMENT) + PATH_MAX + 1];
-       const char *helper_env[] = {
-               git_dir_buf,
-               NULL
-       };
-
 
        if (data->helper)
                return data->helper;
 
-       helper = xcalloc(1, sizeof(*helper));
+       helper = xmalloc(sizeof(*helper));
+       child_process_init(helper);
        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;
 
-       snprintf(git_dir_buf, sizeof(git_dir_buf), "%s=%s", GIT_DIR_ENVIRONMENT, get_git_dir());
-       helper->env = helper_env;
+       argv_array_pushf(&helper->env_array, "%s=%s", GIT_DIR_ENVIRONMENT,
+                        get_git_dir());
 
        code = start_command(helper);
        if (code < 0 && errno == ENOENT)
@@ -155,7 +149,7 @@ static struct child_process *get_helper(struct transport *transport)
        write_constant(helper->in, "capabilities\n");
 
        while (1) {
-               const char *capname;
+               const char *capname, *arg;
                int mandatory = 0;
                if (recvline(data, &buf))
                        exit(128);
@@ -185,19 +179,19 @@ static struct child_process *get_helper(struct transport *transport)
                        data->export = 1;
                else if (!strcmp(capname, "check-connectivity"))
                        data->check_connectivity = 1;
-               else if (!data->refspecs && starts_with(capname, "refspec ")) {
+               else if (!data->refspecs && skip_prefix(capname, "refspec ", &arg)) {
                        ALLOC_GROW(refspecs,
                                   refspec_nr + 1,
                                   refspec_alloc);
-                       refspecs[refspec_nr++] = xstrdup(capname + strlen("refspec "));
+                       refspecs[refspec_nr++] = xstrdup(arg);
                } else if (!strcmp(capname, "connect")) {
                        data->connect = 1;
                } else if (!strcmp(capname, "signed-tags")) {
                        data->signed_tags = 1;
-               } else if (starts_with(capname, "export-marks ")) {
-                       data->export_marks = xstrdup(capname + strlen("export-marks "));
-               } else if (starts_with(capname, "import-marks")) {
-                       data->import_marks = xstrdup(capname + strlen("import-marks "));
+               } else if (skip_prefix(capname, "export-marks ", &arg)) {
+                       data->export_marks = xstrdup(arg);
+               } else if (skip_prefix(capname, "import-marks ", &arg)) {
+                       data->import_marks = xstrdup(arg);
                } else if (starts_with(capname, "no-private-update")) {
                        data->no_private_update = 1;
                } else if (mandatory) {
@@ -219,6 +213,7 @@ static struct child_process *get_helper(struct transport *transport)
        strbuf_release(&buf);
        if (debug)
                fprintf(stderr, "Debug: Capabilities complete.\n");
+       standard_options(transport);
        return data->helper;
 }
 
@@ -245,7 +240,6 @@ static int disconnect_helper(struct transport *transport)
                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;
        }
@@ -262,7 +256,8 @@ static const char *unsupported_options[] = {
 static const char *boolean_options[] = {
        TRANS_OPT_THIN,
        TRANS_OPT_KEEP,
-       TRANS_OPT_FOLLOWTAGS
+       TRANS_OPT_FOLLOWTAGS,
+       TRANS_OPT_PUSH_CERT
        };
 
 static int set_helper_option(struct transport *transport,
@@ -346,24 +341,14 @@ static int fetch_with_fetch(struct transport *transport,
        int i;
        struct strbuf buf = STRBUF_INIT;
 
-       standard_options(transport);
-       if (data->check_connectivity &&
-           data->transport_options.check_self_contained_and_connected)
-               set_helper_option(transport, "check-connectivity", "true");
-
-       if (transport->cloning)
-               set_helper_option(transport, "cloning", "true");
-
-       if (data->transport_options.update_shallow)
-               set_helper_option(transport, "update-shallow", "true");
-
        for (i = 0; i < nr_heads; i++) {
                const struct ref *posn = to_fetch[i];
                if (posn->status & REF_STATUS_UPTODATE)
                        continue;
 
                strbuf_addf(&buf, "fetch %s %s\n",
-                           sha1_to_hex(posn->old_sha1), posn->name);
+                           sha1_to_hex(posn->old_sha1),
+                           posn->symref ? posn->symref : posn->name);
        }
 
        strbuf_addch(&buf, '\n');
@@ -397,18 +382,16 @@ static int get_importer(struct transport *transport, struct child_process *fasti
 {
        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));
+       child_process_init(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);
@@ -421,30 +404,24 @@ static int get_exporter(struct transport *transport,
 {
        struct helper_data *data = transport->data;
        struct child_process *helper = get_helper(transport);
-       int argc = 0, i;
-       struct strbuf tmp = STRBUF_INIT;
+       int i;
 
-       memset(fastexport, 0, sizeof(*fastexport));
+       child_process_init(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";
-       if (data->export_marks) {
-               strbuf_addf(&tmp, "--export-marks=%s.tmp", data->export_marks);
-               fastexport->argv[argc++] = strbuf_detach(&tmp, NULL);
-       }
-       if (data->import_marks) {
-               strbuf_addf(&tmp, "--import-marks=%s", data->import_marks);
-               fastexport->argv[argc++] = strbuf_detach(&tmp, NULL);
-       }
+       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)
+               argv_array_pushf(&fastexport->args, "--export-marks=%s.tmp", data->export_marks);
+       if (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);
@@ -469,7 +446,8 @@ static int fetch_with_import(struct transport *transport,
                if (posn->status & REF_STATUS_UPTODATE)
                        continue;
 
-               strbuf_addf(&buf, "import %s\n", posn->name);
+               strbuf_addf(&buf, "import %s\n",
+                           posn->symref ? posn->symref : posn->name);
                sendline(data, &buf);
                strbuf_reset(&buf);
        }
@@ -485,7 +463,6 @@ static int fetch_with_import(struct transport *transport,
 
        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
@@ -503,14 +480,15 @@ static int fetch_with_import(struct transport *transport,
         * fast-forward or this is a forced update.
         */
        for (i = 0; i < nr_heads; i++) {
-               char *private;
+               char *private, *name;
                posn = to_fetch[i];
                if (posn->status & REF_STATUS_UPTODATE)
                        continue;
+               name = posn->symref ? posn->symref : posn->name;
                if (data->refspecs)
-                       private = apply_refspecs(data->refspecs, data->refspec_nr, posn->name);
+                       private = apply_refspecs(data->refspecs, data->refspec_nr, name);
                else
-                       private = xstrdup(posn->name);
+                       private = xstrdup(name);
                if (private) {
                        read_ref(private, posn->old_sha1);
                        free(private);
@@ -636,6 +614,16 @@ static int fetch(struct transport *transport,
        if (!count)
                return 0;
 
+       if (data->check_connectivity &&
+           data->transport_options.check_self_contained_and_connected)
+               set_helper_option(transport, "check-connectivity", "true");
+
+       if (transport->cloning)
+               set_helper_option(transport, "cloning", "true");
+
+       if (data->transport_options.update_shallow)
+               set_helper_option(transport, "update-shallow", "true");
+
        if (data->fetch)
                return fetch_with_fetch(transport, nr_heads, to_fetch);
 
@@ -840,13 +828,15 @@ static int push_refs_with_push(struct transport *transport,
                return 0;
        }
 
-       standard_options(transport);
        for_each_string_list_item(cas_option, &cas_options)
                set_helper_option(transport, "cas", cas_option->string);
 
        if (flags & TRANSPORT_PUSH_DRY_RUN) {
                if (set_helper_option(transport, "dry-run", "true") != 0)
                        die("helper %s does not support dry-run", data->name);
+       } else if (flags & TRANSPORT_PUSH_CERT) {
+               if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "true") != 0)
+                       die("helper %s does not support --signed", data->name);
        }
 
        strbuf_addch(&buf, '\n');
@@ -862,7 +852,7 @@ 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)
@@ -871,6 +861,9 @@ static int push_refs_with_export(struct transport *transport,
        if (flags & TRANSPORT_PUSH_DRY_RUN) {
                if (set_helper_option(transport, "dry-run", "true") != 0)
                        die("helper %s does not support dry-run", data->name);
+       } else if (flags & TRANSPORT_PUSH_CERT) {
+               if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "true") != 0)
+                       die("helper %s does not support --signed", data->name);
        }
 
        if (flags & TRANSPORT_PUSH_FORCE) {
@@ -882,15 +875,10 @@ static int push_refs_with_export(struct transport *transport,
 
        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);
@@ -900,15 +888,37 @@ static int push_refs_with_export(struct transport *transport,
                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,
+                                                RESOLVE_REF_READING,
+                                                sha1, &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");
        if (push_update_refs_status(data, remote_refs, flags))
@@ -1026,7 +1036,7 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
 
 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"))