summaryrefslogtreecommitdiff
path: root/send-pack.c
diff options
context:
space:
mode:
Diffstat (limited to 'send-pack.c')
-rw-r--r--send-pack.c217
1 files changed, 134 insertions, 83 deletions
diff --git a/send-pack.c b/send-pack.c
index 0407841ae8..eb4a44270b 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -12,9 +12,10 @@
#include "quote.h"
#include "transport.h"
#include "version.h"
-#include "sha1-array.h"
+#include "oid-array.h"
#include "gpg-interface.h"
#include "cache.h"
+#include "shallow.h"
int option_parse_push_signed(const struct option *opt,
const char *arg, int unset)
@@ -67,20 +68,20 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struc
int i;
int rc;
- argv_array_push(&po.args, "pack-objects");
- argv_array_push(&po.args, "--all-progress-implied");
- argv_array_push(&po.args, "--revs");
- argv_array_push(&po.args, "--stdout");
+ strvec_push(&po.args, "pack-objects");
+ strvec_push(&po.args, "--all-progress-implied");
+ strvec_push(&po.args, "--revs");
+ strvec_push(&po.args, "--stdout");
if (args->use_thin_pack)
- argv_array_push(&po.args, "--thin");
+ strvec_push(&po.args, "--thin");
if (args->use_ofs_delta)
- argv_array_push(&po.args, "--delta-base-offset");
+ strvec_push(&po.args, "--delta-base-offset");
if (args->quiet || !args->progress)
- argv_array_push(&po.args, "-q");
+ strvec_push(&po.args, "-q");
if (args->progress)
- argv_array_push(&po.args, "--progress");
+ strvec_push(&po.args, "--progress");
if (is_repository_shallow(the_repository))
- argv_array_push(&po.args, "--shallow");
+ strvec_push(&po.args, "--shallow");
po.in = -1;
po.out = args->stateless_rpc ? -1 : fd;
po.git_cmd = 1;
@@ -153,25 +154,79 @@ static int receive_status(struct packet_reader *reader, struct ref *refs)
{
struct ref *hint;
int ret;
+ struct ref_push_report *report = NULL;
+ int new_report = 0;
+ int once = 0;
hint = NULL;
ret = receive_unpack_status(reader);
while (1) {
+ struct object_id old_oid, new_oid;
+ const char *head;
const char *refname;
- char *msg;
+ char *p;
if (packet_reader_read(reader) != PACKET_READ_NORMAL)
break;
- if (!starts_with(reader->line, "ok ") && !starts_with(reader->line, "ng ")) {
- error("invalid ref status from remote: %s", reader->line);
+ head = reader->line;
+ p = strchr(head, ' ');
+ if (!p) {
+ error("invalid status line from remote: %s", reader->line);
ret = -1;
break;
}
+ *p++ = '\0';
- refname = reader->line + 3;
- msg = strchr(refname, ' ');
- if (msg)
- *msg++ = '\0';
+ if (!strcmp(head, "option")) {
+ const char *key, *val;
+ if (!hint || !(report || new_report)) {
+ if (!once++)
+ error("'option' without a matching 'ok/ng' directive");
+ ret = -1;
+ continue;
+ }
+ if (new_report) {
+ if (!hint->report) {
+ hint->report = xcalloc(1, sizeof(struct ref_push_report));
+ report = hint->report;
+ } else {
+ report = hint->report;
+ while (report->next)
+ report = report->next;
+ report->next = xcalloc(1, sizeof(struct ref_push_report));
+ report = report->next;
+ }
+ new_report = 0;
+ }
+ key = p;
+ p = strchr(key, ' ');
+ if (p)
+ *p++ = '\0';
+ val = p;
+ if (!strcmp(key, "refname"))
+ report->ref_name = xstrdup_or_null(val);
+ else if (!strcmp(key, "old-oid") && val &&
+ !parse_oid_hex(val, &old_oid, &val))
+ report->old_oid = oiddup(&old_oid);
+ else if (!strcmp(key, "new-oid") && val &&
+ !parse_oid_hex(val, &new_oid, &val))
+ report->new_oid = oiddup(&new_oid);
+ else if (!strcmp(key, "forced-update"))
+ report->forced_update = 1;
+ continue;
+ }
+
+ report = NULL;
+ new_report = 0;
+ if (strcmp(head, "ok") && strcmp(head, "ng")) {
+ error("invalid ref status from remote: %s", head);
+ ret = -1;
+ break;
+ }
+ refname = p;
+ p = strchr(refname, ' ');
+ if (p)
+ *p++ = '\0';
/* first try searching at our hint, falling back to all refs */
if (hint)
hint = find_ref_by_name(hint, refname);
@@ -179,24 +234,27 @@ static int receive_status(struct packet_reader *reader, struct ref *refs)
hint = find_ref_by_name(refs, refname);
if (!hint) {
warning("remote reported status on unknown ref: %s",
- refname);
+ refname);
continue;
}
- if (hint->status != REF_STATUS_EXPECTING_REPORT) {
+ if (hint->status != REF_STATUS_EXPECTING_REPORT &&
+ hint->status != REF_STATUS_OK &&
+ hint->status != REF_STATUS_REMOTE_REJECT) {
warning("remote reported status on unexpected ref: %s",
- refname);
+ refname);
continue;
}
-
- if (reader->line[0] == 'o' && reader->line[1] == 'k')
- hint->status = REF_STATUS_OK;
- else {
+ if (!strcmp(head, "ng")) {
hint->status = REF_STATUS_REMOTE_REJECT;
- ret = -1;
+ if (p)
+ hint->remote_status = xstrdup(p);
+ else
+ hint->remote_status = "failed";
+ } else {
+ hint->status = REF_STATUS_OK;
+ hint->remote_status = xstrdup_or_null(p);
+ new_report = 1;
}
- hint->remote_status = xstrdup_or_null(msg);
- /* start our next search from the next ref */
- hint = hint->next;
}
return ret;
}
@@ -241,11 +299,17 @@ static int check_to_send_update(const struct ref *ref, const struct send_pack_ar
case REF_STATUS_REJECT_FETCH_FIRST:
case REF_STATUS_REJECT_NEEDS_FORCE:
case REF_STATUS_REJECT_STALE:
+ case REF_STATUS_REJECT_REMOTE_UPDATED:
case REF_STATUS_REJECT_NODELETE:
return CHECK_REF_STATUS_REJECTED;
case REF_STATUS_UPTODATE:
return CHECK_REF_UPTODATE;
+
default:
+ case REF_STATUS_EXPECTING_REPORT:
+ /* already passed checks on the local side */
+ case REF_STATUS_OK:
+ /* of course this is OK */
return 0;
}
}
@@ -322,29 +386,6 @@ free_return:
return update_seen;
}
-
-static int atomic_push_failure(struct send_pack_args *args,
- struct ref *remote_refs,
- struct ref *failing_ref)
-{
- struct ref *ref;
- /* Mark other refs as failed */
- for (ref = remote_refs; ref; ref = ref->next) {
- if (!ref->peer_ref && !args->send_mirror)
- continue;
-
- switch (ref->status) {
- case REF_STATUS_EXPECTING_REPORT:
- ref->status = REF_STATUS_ATOMIC_PUSH_FAILED;
- continue;
- default:
- break; /* do nothing */
- }
- }
- return error("atomic push failed for ref %s. status: %d\n",
- failing_ref->name, failing_ref->status);
-}
-
#define NONCE_LEN_LIMIT 256
static void reject_invalid_nonce(const char *nonce, int len)
@@ -387,6 +428,7 @@ int send_pack(struct send_pack_args *args,
int atomic_supported = 0;
int use_push_options = 0;
int push_options_supported = 0;
+ int object_format_supported = 0;
unsigned cmds_sent = 0;
int ret;
struct async demux;
@@ -394,7 +436,9 @@ int send_pack(struct send_pack_args *args,
struct packet_reader reader;
/* Does the other end support the reporting? */
- if (server_supports("report-status"))
+ if (server_supports("report-status-v2"))
+ status_report = 2;
+ else if (server_supports("report-status"))
status_report = 1;
if (server_supports("delete-refs"))
allow_deleting_refs = 1;
@@ -413,6 +457,9 @@ int send_pack(struct send_pack_args *args,
if (server_supports("push-options"))
push_options_supported = 1;
+ if (!server_supports_hash(the_hash_algo->name, &object_format_supported))
+ die(_("the receiving end does not support this repository's hash algorithm"));
+
if (args->push_cert != SEND_PACK_PUSH_CERT_NEVER) {
int len;
push_cert_nonce = server_feature_value("push-cert", &len);
@@ -430,7 +477,7 @@ int send_pack(struct send_pack_args *args,
if (!remote_refs) {
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
- "Perhaps you should specify a branch such as 'master'.\n");
+ "Perhaps you should specify a branch.\n");
return 0;
}
if (args->atomic && !atomic_supported)
@@ -443,8 +490,10 @@ int send_pack(struct send_pack_args *args,
use_push_options = push_options_supported && args->push_options;
- if (status_report)
+ if (status_report == 1)
strbuf_addstr(&cap_buf, " report-status");
+ else if (status_report == 2)
+ strbuf_addstr(&cap_buf, " report-status-v2");
if (use_sideband)
strbuf_addstr(&cap_buf, " side-band-64k");
if (quiet_supported && (args->quiet || !args->progress))
@@ -453,6 +502,8 @@ int send_pack(struct send_pack_args *args,
strbuf_addstr(&cap_buf, " atomic");
if (use_push_options)
strbuf_addstr(&cap_buf, " push-options");
+ if (object_format_supported)
+ strbuf_addf(&cap_buf, " object-format=%s", the_hash_algo->name);
if (agent_supported)
strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized());
@@ -465,13 +516,6 @@ int send_pack(struct send_pack_args *args,
if (ref->deletion && !allow_deleting_refs)
ref->status = REF_STATUS_REJECT_NODELETE;
- if (!args->dry_run)
- advertise_shallow_grafts_buf(&req_buf);
-
- if (!args->dry_run && push_cert_nonce)
- cmds_sent = generate_push_cert(&req_buf, remote_refs, args,
- cap_buf.buf, push_cert_nonce);
-
/*
* Clear the status for each ref and see if we need to send
* the pack data.
@@ -489,7 +533,10 @@ int send_pack(struct send_pack_args *args,
if (use_atomic) {
strbuf_release(&req_buf);
strbuf_release(&cap_buf);
- return atomic_push_failure(args, remote_refs, ref);
+ reject_atomic_push(remote_refs, args->send_mirror);
+ error("atomic push failed for ref %s. status: %d\n",
+ ref->name, ref->status);
+ return args->porcelain ? 0 : -1;
}
/* else fallthrough */
default:
@@ -504,31 +551,35 @@ int send_pack(struct send_pack_args *args,
ref->status = REF_STATUS_EXPECTING_REPORT;
}
+ if (!args->dry_run)
+ advertise_shallow_grafts_buf(&req_buf);
+
/*
* Finally, tell the other end!
*/
- for (ref = remote_refs; ref; ref = ref->next) {
- char *old_hex, *new_hex;
-
- if (args->dry_run || push_cert_nonce)
- continue;
-
- if (check_to_send_update(ref, args) < 0)
- continue;
-
- old_hex = oid_to_hex(&ref->old_oid);
- new_hex = oid_to_hex(&ref->new_oid);
- if (!cmds_sent) {
- packet_buf_write(&req_buf,
- "%s %s %s%c%s",
- old_hex, new_hex, ref->name, 0,
- cap_buf.buf);
- cmds_sent = 1;
- } else {
- packet_buf_write(&req_buf, "%s %s %s",
- old_hex, new_hex, ref->name);
+ if (!args->dry_run && push_cert_nonce)
+ cmds_sent = generate_push_cert(&req_buf, remote_refs, args,
+ cap_buf.buf, push_cert_nonce);
+ else if (!args->dry_run)
+ for (ref = remote_refs; ref; ref = ref->next) {
+ char *old_hex, *new_hex;
+
+ if (check_to_send_update(ref, args) < 0)
+ continue;
+
+ old_hex = oid_to_hex(&ref->old_oid);
+ new_hex = oid_to_hex(&ref->new_oid);
+ if (!cmds_sent) {
+ packet_buf_write(&req_buf,
+ "%s %s %s%c%s",
+ old_hex, new_hex, ref->name, 0,
+ cap_buf.buf);
+ cmds_sent = 1;
+ } else {
+ packet_buf_write(&req_buf, "%s %s %s",
+ old_hex, new_hex, ref->name);
+ }
}
- }
if (use_push_options) {
struct string_list_item *item;