summaryrefslogtreecommitdiff
path: root/transport-helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'transport-helper.c')
-rw-r--r--transport-helper.c267
1 files changed, 183 insertions, 84 deletions
diff --git a/transport-helper.c b/transport-helper.c
index 522d791..3d8fe7d 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -11,6 +11,7 @@
#include "thread-utils.h"
#include "sigchain.h"
#include "argv-array.h"
+#include "refs.h"
static int debug;
@@ -26,7 +27,9 @@ struct helper_data {
push : 1,
connect : 1,
signed_tags : 1,
- no_disconnect_req : 1;
+ check_connectivity : 1,
+ no_disconnect_req : 1,
+ no_private_update : 1;
char *export_marks;
char *import_marks;
/* These go from remote name (as in "list") to private name */
@@ -47,7 +50,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)
@@ -55,7 +58,7 @@ static int recvline_fh(FILE *helper, struct strbuf *buffer)
if (strbuf_getline(buffer, helper, '\n') == EOF) {
if (debug)
fprintf(stderr, "Debug: Remote helper quit.\n");
- exit(128);
+ return 1;
}
if (debug)
@@ -65,13 +68,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);
-}
-
-static void xchgline(struct helper_data *helper, struct strbuf *buffer)
-{
- sendline(helper, buffer);
- recvline(helper, buffer);
+ return recvline_fh(helper->out, buffer, helper->name);
}
static void write_constant(int fd, const char *str)
@@ -104,7 +101,6 @@ 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;
@@ -126,10 +122,9 @@ static struct child_process *get_helper(struct transport *transport)
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;
@@ -158,9 +153,10 @@ 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;
- recvline(data, &buf);
+ if (recvline(data, &buf))
+ exit(128);
if (!*buf.buf)
break;
@@ -185,25 +181,23 @@ static struct child_process *get_helper(struct transport *transport)
data->bidi_import = 1;
else if (!strcmp(capname, "export"))
data->export = 1;
- else if (!data->refspecs && !prefixcmp(capname, "refspec ")) {
+ else if (!strcmp(capname, "check-connectivity"))
+ data->check_connectivity = 1;
+ 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 (!prefixcmp(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);
- } else if (!prefixcmp(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);
+ } 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) {
die("Unknown mandatory capability %s. This remote "
"helper probably needs newer version of Git.",
@@ -217,6 +211,8 @@ static struct child_process *get_helper(struct transport *transport)
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)
@@ -247,7 +243,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;
}
@@ -260,6 +255,7 @@ static const char *unsupported_options[] = {
TRANS_OPT_THIN,
TRANS_OPT_KEEP
};
+
static const char *boolean_options[] = {
TRANS_OPT_THIN,
TRANS_OPT_KEEP,
@@ -297,11 +293,13 @@ static int set_helper_option(struct transport *transport,
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;
- else if (!prefixcmp(buf.buf, "error")) {
+ else if (starts_with(buf.buf, "error")) {
ret = -1;
} else if (!strcmp(buf.buf, "unsupported"))
ret = 1;
@@ -346,6 +344,15 @@ static int fetch_with_fetch(struct transport *transport,
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];
@@ -360,15 +367,20 @@ static int fetch_with_fetch(struct transport *transport,
sendline(data, &buf);
while (1) {
- recvline(data, &buf);
+ if (recvline(data, &buf))
+ exit(128);
- if (!prefixcmp(buf.buf, "lock ")) {
+ if (starts_with(buf.buf, "lock ")) {
const char *name = buf.buf + 5;
if (transport->pack_lockfile)
warning("%s also locked %s", data->name, name);
else
transport->pack_lockfile = xstrdup(name);
}
+ else if (data->check_connectivity &&
+ data->transport_options.check_self_contained_and_connected &&
+ !strcmp(buf.buf, "connectivity-ok"))
+ data->transport_options.self_contained_and_connected = 1;
else if (!buf.len)
break;
else
@@ -382,18 +394,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));
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);
@@ -406,24 +416,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;
+ 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);
@@ -464,7 +474,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
@@ -473,7 +482,7 @@ static int fetch_with_import(struct transport *transport,
* were fetching.
*
* (If no "refspec" capability was specified, for historical
- * reasons we default to *:*.)
+ * 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
@@ -540,7 +549,9 @@ static int process_connect_service(struct transport *transport,
goto exit;
sendline(data, &cmdbuf);
- recvline_fh(input, &cmdbuf);
+ if (recvline_fh(input, &cmdbuf, name))
+ exit(128);
+
if (!strcmp(cmdbuf.buf, "")) {
data->no_disconnect_req = 1;
if (debug)
@@ -622,17 +633,17 @@ 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)
{
char *refname, *msg;
- int status;
+ int status, forced = 0;
- if (!prefixcmp(buf->buf, "ok ")) {
+ if (starts_with(buf->buf, "ok ")) {
status = REF_STATUS_OK;
refname = buf->buf + 3;
- } else if (!prefixcmp(buf->buf, "error ")) {
+ } else if (starts_with(buf->buf, "error ")) {
status = REF_STATUS_REMOTE_REJECT;
refname = buf->buf + 6;
} else
@@ -680,6 +691,16 @@ static void push_update_ref_status(struct strbuf *buf,
free(msg);
msg = NULL;
}
+ else if (!strcmp(msg, "stale info")) {
+ status = REF_STATUS_REJECT_STALE;
+ free(msg);
+ msg = NULL;
+ }
+ else if (!strcmp(msg, "forced update")) {
+ forced = 1;
+ free(msg);
+ msg = NULL;
+ }
}
if (*ref)
@@ -688,7 +709,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) {
@@ -697,36 +718,61 @@ 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)->forced_update |= forced;
(*ref)->remote_status = msg;
+ return !(status == REF_STATUS_OK);
}
-static void push_update_refs_status(struct helper_data *data,
- struct ref *remote_refs)
+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 (;;) {
- recvline(data, &buf);
+ char *private;
+
+ if (recvline(data, &buf)) {
+ ret = 1;
+ break;
+ }
+
if (!buf.len)
break;
- push_update_ref_status(&buf, &ref, remote_refs);
+ if (push_update_ref_status(&buf, &ref, remote_refs))
+ continue;
+
+ if (flags & TRANSPORT_PUSH_DRY_RUN || !data->refspecs || data->no_private_update)
+ 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);
+ return ret;
}
static int push_refs_with_push(struct transport *transport,
- struct ref *remote_refs, int flags)
+ struct ref *remote_refs, int flags)
{
int force_all = flags & TRANSPORT_PUSH_FORCE;
int mirror = flags & TRANSPORT_PUSH_MIRROR;
struct helper_data *data = transport->data;
struct strbuf buf = STRBUF_INIT;
struct ref *ref;
+ struct string_list cas_options = STRING_LIST_INIT_DUP;
+ struct string_list_item *cas_option;
get_helper(transport);
if (!data->push)
@@ -739,6 +785,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_STALE:
case REF_STATUS_REJECT_ALREADY_EXISTS:
case REF_STATUS_UPTODATE:
continue;
@@ -761,11 +808,29 @@ static int push_refs_with_push(struct transport *transport,
strbuf_addch(&buf, ':');
strbuf_addstr(&buf, ref->name);
strbuf_addch(&buf, '\n');
+
+ /*
+ * The "--force-with-lease" options without explicit
+ * values to expect have already been expanded into
+ * the ref->old_sha1_expect[] field; we can ignore
+ * transport->smart_options->cas altogether and instead
+ * can enumerate them from the refs.
+ */
+ if (ref->expect_old_sha1) {
+ struct strbuf cas = STRBUF_INIT;
+ strbuf_addf(&cas, "%s:%s",
+ ref->name, sha1_to_hex(ref->old_sha1_expect));
+ string_list_append(&cas_options, strbuf_detach(&cas, NULL));
+ }
}
- if (buf.len == 0)
+ if (buf.len == 0) {
+ string_list_clear(&cas_options, 0);
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)
@@ -776,8 +841,7 @@ static int push_refs_with_push(struct transport *transport,
sendline(data, &buf);
strbuf_release(&buf);
- push_update_refs_status(data, remote_refs);
- return 0;
+ return push_update_refs_status(data, remote_refs, flags);
}
static int push_refs_with_export(struct transport *transport,
@@ -786,21 +850,30 @@ 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)
+ 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);
+ }
+
+ if (flags & TRANSPORT_PUSH_FORCE) {
+ if (set_helper_option(transport, "force", "true") != 0)
+ warning("helper %s does not support 'force'", data->name);
+ }
+
helper = get_helper(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 (!data->refspecs)
- continue;
private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
if (private && !get_sha1(private, sha1)) {
strbuf_addf(&buf, "^%s", private);
@@ -809,21 +882,46 @@ static int push_refs_with_export(struct transport *transport,
}
free(private);
- if (ref->deletion) {
- die("remote-helpers do not support ref deletion");
- }
+ if (ref->peer_ref) {
+ if (strcmp(ref->name, ref->peer_ref->name)) {
+ if (!ref->deletion) {
+ const char *name;
+ int flag;
- if (ref->peer_ref)
- string_list_append(&revlist_args, ref->peer_ref->name);
+ /* 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);
+ 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;
}
@@ -892,7 +990,8 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
while (1) {
char *eov, *eon;
- recvline(data, &buf);
+ if (recvline(data, &buf))
+ exit(128);
if (!*buf.buf)
break;
@@ -929,7 +1028,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"))
@@ -955,6 +1054,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;
@@ -1040,7 +1140,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;
@@ -1061,9 +1161,8 @@ static int udt_do_write(struct unidirectional_transfer *t)
return 0; /* Nothing to write. */
transfer_debug("%s is writable", t->dest_name);
- bytes = write(t->dest, t->buf, t->bufuse);
- if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN &&
- errno != EINTR) {
+ bytes = xwrite(t->dest, t->buf, t->bufuse);
+ if (bytes < 0 && errno != EWOULDBLOCK) {
error("write(%s) failed: %s", t->dest_name, strerror(errno));
return -1;
} else if (bytes > 0) {
@@ -1104,7 +1203,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)
@@ -1170,7 +1269,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)