summaryrefslogtreecommitdiff
path: root/transport-helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'transport-helper.c')
-rw-r--r--transport-helper.c165
1 files changed, 127 insertions, 38 deletions
diff --git a/transport-helper.c b/transport-helper.c
index 6f227e253b..63cabc37e3 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -9,6 +9,9 @@
#include "remote.h"
#include "string-list.h"
#include "thread-utils.h"
+#include "sigchain.h"
+#include "argv-array.h"
+#include "refs.h"
static int debug;
@@ -18,10 +21,12 @@ struct helper_data {
FILE *out;
unsigned fetch : 1,
import : 1,
+ bidi_import : 1,
export : 1,
option : 1,
push : 1,
connect : 1,
+ signed_tags : 1,
no_disconnect_req : 1;
char *export_marks;
char *import_marks;
@@ -43,7 +48,7 @@ static void sendline(struct helper_data *helper, struct strbuf *buffer)
die_errno("Full write to remote helper failed");
}
-static int recvline_fh(FILE *helper, struct strbuf *buffer)
+static int recvline_fh(FILE *helper, struct strbuf *buffer, const char *name)
{
strbuf_reset(buffer);
if (debug)
@@ -61,7 +66,7 @@ static int recvline_fh(FILE *helper, struct strbuf *buffer)
static int recvline(struct helper_data *helper, struct strbuf *buffer)
{
- return recvline_fh(helper->out, buffer);
+ return recvline_fh(helper->out, buffer, helper->name);
}
static void xchgline(struct helper_data *helper, struct strbuf *buffer)
@@ -100,6 +105,7 @@ static void do_take_over(struct transport *transport)
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;
@@ -121,11 +127,10 @@ static struct child_process *get_helper(struct transport *transport)
helper->in = -1;
helper->out = -1;
helper->err = 0;
- helper->argv = xcalloc(4, sizeof(*helper->argv));
- strbuf_addf(&buf, "git-remote-%s", data->name);
- helper->argv[0] = strbuf_detach(&buf, NULL);
- helper->argv[1] = transport->remote->name;
- helper->argv[2] = remove_ext_force(transport->url);
+ 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);
helper->git_cmd = 0;
helper->silent_exec_failure = 1;
@@ -177,6 +182,8 @@ static struct child_process *get_helper(struct transport *transport)
data->push = 1;
else if (!strcmp(capname, "import"))
data->import = 1;
+ else if (!strcmp(capname, "bidi-import"))
+ data->bidi_import = 1;
else if (!strcmp(capname, "export"))
data->export = 1;
else if (!data->refspecs && !prefixcmp(capname, "refspec ")) {
@@ -186,6 +193,8 @@ static struct child_process *get_helper(struct transport *transport)
refspecs[refspec_nr++] = xstrdup(capname + strlen("refspec "));
} else if (!strcmp(capname, "connect")) {
data->connect = 1;
+ } else if (!strcmp(capname, "signed-tags")) {
+ data->signed_tags = 1;
} else if (!prefixcmp(capname, "export-marks ")) {
struct strbuf arg = STRBUF_INIT;
strbuf_addstr(&arg, "--export-marks=");
@@ -198,7 +207,7 @@ static struct child_process *get_helper(struct transport *transport)
data->import_marks = strbuf_detach(&arg, NULL);
} else if (mandatory) {
die("Unknown mandatory capability %s. This remote "
- "helper probably needs newer version of Git.\n",
+ "helper probably needs newer version of Git.",
capname);
}
}
@@ -206,10 +215,11 @@ static struct child_process *get_helper(struct transport *transport)
int i;
data->refspec_nr = refspec_nr;
data->refspecs = parse_fetch_refspec(refspec_nr, refspecs);
- for (i = 0; i < refspec_nr; i++) {
+ for (i = 0; i < refspec_nr; i++)
free((char *)refspecs[i]);
- }
free(refspecs);
+ } else if (data->import || data->bidi_import || data->export) {
+ warning("This remote helper should implement refspec capability.");
}
strbuf_release(&buf);
if (debug)
@@ -220,22 +230,27 @@ static struct child_process *get_helper(struct transport *transport)
static int disconnect_helper(struct transport *transport)
{
struct helper_data *data = transport->data;
- struct strbuf buf = STRBUF_INIT;
int res = 0;
if (data->helper) {
if (debug)
fprintf(stderr, "Debug: Disconnecting.\n");
if (!data->no_disconnect_req) {
- strbuf_addf(&buf, "\n");
- sendline(data, &buf);
+ /*
+ * Ignore write errors; there's nothing we can do,
+ * since we're about to close the pipe anyway. And the
+ * most likely error is EPIPE due to the helper dying
+ * to report an error itself.
+ */
+ sigchain_push(SIGPIPE, SIG_IGN);
+ xwrite(data->helper->in, "\n", 1);
+ sigchain_pop(SIGPIPE);
}
close(data->helper->in);
close(data->helper->out);
fclose(data->out);
res = finish_command(data->helper);
- free((char *)data->helper->argv[0]);
- free(data->helper->argv);
+ argv_array_free_detached(data->helper->argv);
free(data->helper);
data->helper = NULL;
}
@@ -369,14 +384,23 @@ static int fetch_with_fetch(struct transport *transport,
static int get_importer(struct transport *transport, struct child_process *fastimport)
{
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;
- fastimport->argv = xcalloc(5, sizeof(*fastimport->argv));
- fastimport->argv[0] = "fast-import";
- fastimport->argv[1] = "--quiet";
+ argv_array_push(&argv, "fast-import");
+ argv_array_push(&argv, debug ? "--stats" : "--quiet");
+ if (data->bidi_import) {
+ cat_blob_fd = xdup(helper->in);
+ argv_array_pushf(&argv, "--cat-blob-fd=%d", cat_blob_fd);
+ }
+ fastimport->argv = argv.argv;
fastimport->git_cmd = 1;
- return start_command(fastimport);
+
+ code = start_command(fastimport);
+ return code;
}
static int get_exporter(struct transport *transport,
@@ -391,9 +415,11 @@ static int get_exporter(struct transport *transport,
/* 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(5 + revlist_args->nr, sizeof(*fastexport->argv));
+ 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)
fastexport->argv[argc++] = data->export_marks;
if (data->import_marks)
@@ -431,12 +457,33 @@ static int fetch_with_import(struct transport *transport,
}
write_constant(data->helper->in, "\n");
+ /*
+ * remote-helpers that advertise the bidi-import capability are required to
+ * buffer the complete batch of import commands until this newline before
+ * sending data to fast-import.
+ * These helpers read back data from fast-import on their stdin, which could
+ * be mixed with import commands, otherwise.
+ */
if (finish_command(&fastimport))
die("Error while running fast-import");
- free(fastimport.argv);
- fastimport.argv = NULL;
+ argv_array_free_detached(fastimport.argv);
+ /*
+ * The fast-import stream of a remote helper that advertises
+ * the "refspec" capability writes to the refs named after the
+ * right hand side of the first refspec matching each ref we
+ * were fetching.
+ *
+ * (If no "refspec" capability was specified, for historical
+ * reasons we default to the equivalent of *:*.)
+ *
+ * Store the result in to_fetch[i].old_sha1. Callers such
+ * as "git fetch" can use the value to write feedback to the
+ * terminal, populate FETCH_HEAD, and determine what new value
+ * should be written to peer_ref if the update is a
+ * fast-forward or this is a forced update.
+ */
for (i = 0; i < nr_heads; i++) {
char *private;
posn = to_fetch[i];
@@ -496,7 +543,7 @@ static int process_connect_service(struct transport *transport,
goto exit;
sendline(data, &cmdbuf);
- recvline_fh(input, &cmdbuf);
+ recvline_fh(input, &cmdbuf, name);
if (!strcmp(cmdbuf.buf, "")) {
data->no_disconnect_req = 1;
if (debug)
@@ -578,7 +625,7 @@ static int fetch(struct transport *transport,
return -1;
}
-static void push_update_ref_status(struct strbuf *buf,
+static int push_update_ref_status(struct strbuf *buf,
struct ref **ref,
struct ref *remote_refs)
{
@@ -592,7 +639,7 @@ static void push_update_ref_status(struct strbuf *buf,
status = REF_STATUS_REMOTE_REJECT;
refname = buf->buf + 6;
} else
- die("expected ok/error, helper said '%s'\n", buf->buf);
+ die("expected ok/error, helper said '%s'", buf->buf);
msg = strchr(refname, ' ');
if (msg) {
@@ -621,6 +668,21 @@ static void push_update_ref_status(struct strbuf *buf,
free(msg);
msg = NULL;
}
+ else if (!strcmp(msg, "already exists")) {
+ status = REF_STATUS_REJECT_ALREADY_EXISTS;
+ free(msg);
+ msg = NULL;
+ }
+ else if (!strcmp(msg, "fetch first")) {
+ status = REF_STATUS_REJECT_FETCH_FIRST;
+ free(msg);
+ msg = NULL;
+ }
+ else if (!strcmp(msg, "needs force")) {
+ status = REF_STATUS_REJECT_NEEDS_FORCE;
+ free(msg);
+ msg = NULL;
+ }
}
if (*ref)
@@ -629,7 +691,7 @@ static void push_update_ref_status(struct strbuf *buf,
*ref = find_ref_by_name(remote_refs, refname);
if (!*ref) {
warning("helper reported unexpected status of %s", refname);
- return;
+ return 1;
}
if ((*ref)->status != REF_STATUS_NONE) {
@@ -638,11 +700,12 @@ static void push_update_ref_status(struct strbuf *buf,
* status reported by the remote helper if the latter is 'no match'.
*/
if (status == REF_STATUS_NONE)
- return;
+ return 1;
}
(*ref)->status = status;
(*ref)->remote_status = msg;
+ return !(status == REF_STATUS_OK);
}
static void push_update_refs_status(struct helper_data *data,
@@ -651,11 +714,24 @@ static void push_update_refs_status(struct helper_data *data,
struct strbuf buf = STRBUF_INIT;
struct ref *ref = remote_refs;
for (;;) {
+ char *private;
+
recvline(data, &buf);
if (!buf.len)
break;
- push_update_ref_status(&buf, &ref, remote_refs);
+ if (push_update_ref_status(&buf, &ref, remote_refs))
+ continue;
+
+ if (!data->refspecs)
+ continue;
+
+ /* propagate back the update to the remote namespace */
+ private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
+ if (!private)
+ continue;
+ update_ref("update by helper", private, ref->new_sha1, NULL, 0, 0);
+ free(private);
}
strbuf_release(&buf);
}
@@ -680,6 +756,7 @@ static int push_refs_with_push(struct transport *transport,
/* Check for statuses set by set_ref_status_for_push() */
switch (ref->status) {
case REF_STATUS_REJECT_NONFASTFORWARD:
+ case REF_STATUS_REJECT_ALREADY_EXISTS:
case REF_STATUS_UPTODATE:
continue;
default:
@@ -729,6 +806,14 @@ static int push_refs_with_export(struct transport *transport,
struct string_list revlist_args = STRING_LIST_INIT_NODUP;
struct strbuf buf = STRBUF_INIT;
+ if (!data->refspecs)
+ die("remote-helper doesn't support push; refspec needed");
+
+ 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);
+ }
+
helper = get_helper(transport);
write_constant(helper->in, "export\n");
@@ -739,22 +824,25 @@ static int push_refs_with_export(struct transport *transport,
char *private;
unsigned char sha1[20];
- if (!data->refspecs)
- continue;
+ 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);
string_list_append(&revlist_args, strbuf_detach(&buf, NULL));
+ hashcpy(ref->old_sha1, sha1);
}
free(private);
- if (ref->deletion) {
+ if (ref->deletion)
die("remote-helpers do not support ref deletion");
- }
- if (ref->peer_ref)
+ 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 (get_exporter(transport, &exporter, &revlist_args))
@@ -894,6 +982,7 @@ int transport_helper_init(struct transport *transport, const char *name)
#define PBUFFERSIZE 8192
/* Print bidirectional transfer loop debug message. */
+__attribute__((format (printf, 1, 2)))
static void transfer_debug(const char *fmt, ...)
{
va_list args;
@@ -935,7 +1024,7 @@ struct unidirectional_transfer {
int src_is_sock;
/* Is destination socket? */
int dest_is_sock;
- /* Transfer state (TRANSFERING/FLUSHING/FINISHED) */
+ /* Transfer state (TRANSFERRING/FLUSHING/FINISHED) */
int state;
/* Buffer. */
char buf[BUFFERSIZE];
@@ -979,7 +1068,7 @@ static int udt_do_read(struct unidirectional_transfer *t)
return -1;
} else if (bytes == 0) {
transfer_debug("%s EOF (with %i bytes in buffer)",
- t->src_name, t->bufuse);
+ t->src_name, (int)t->bufuse);
t->state = SSTATE_FLUSHING;
} else if (bytes > 0) {
t->bufuse += bytes;
@@ -1043,7 +1132,7 @@ static void *udt_copy_task_routine(void *udt)
#ifndef NO_PTHREADS
/*
- * Join thread, with apporiate errors on failure. Name is name for the
+ * Join thread, with appropriate errors on failure. Name is name for the
* thread (for error messages). Returns 0 on success, 1 on failure.
*/
static int tloop_join(pthread_t thread, const char *name)
@@ -1109,7 +1198,7 @@ static void udt_kill_transfer(struct unidirectional_transfer *t)
}
/*
- * Join process, with apporiate errors on failure. Name is name for the
+ * Join process, with appropriate errors on failure. Name is name for the
* process (for error messages). Returns 0 on success, 1 on failure.
*/
static int tloop_join(pid_t pid, const char *name)