mirror of
https://github.com/git/git.git
synced 2024-11-28 04:23:30 +08:00
eea0e59ffb
Each of these were checked with gcc -E -I. ${SOURCE_FILE} | grep ${HEADER_FILE} to ensure that removing the direct inclusion of the header actually resulted in that header no longer being included at all (i.e. that no other header pulled it in transitively). ...except for a few cases where we verified that although the header was brought in transitively, nothing from it was directly used in that source file. These cases were: * builtin/credential-cache.c * builtin/pull.c * builtin/send-pack.c Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
755 lines
20 KiB
C
755 lines
20 KiB
C
#include "git-compat-util.h"
|
|
#include "config.h"
|
|
#include "commit.h"
|
|
#include "date.h"
|
|
#include "gettext.h"
|
|
#include "hex.h"
|
|
#include "object-store-ll.h"
|
|
#include "pkt-line.h"
|
|
#include "sideband.h"
|
|
#include "run-command.h"
|
|
#include "remote.h"
|
|
#include "connect.h"
|
|
#include "send-pack.h"
|
|
#include "transport.h"
|
|
#include "version.h"
|
|
#include "oid-array.h"
|
|
#include "gpg-interface.h"
|
|
#include "shallow.h"
|
|
#include "parse-options.h"
|
|
#include "trace2.h"
|
|
#include "write-or-die.h"
|
|
|
|
int option_parse_push_signed(const struct option *opt,
|
|
const char *arg, int unset)
|
|
{
|
|
if (unset) {
|
|
*(int *)(opt->value) = SEND_PACK_PUSH_CERT_NEVER;
|
|
return 0;
|
|
}
|
|
switch (git_parse_maybe_bool(arg)) {
|
|
case 1:
|
|
*(int *)(opt->value) = SEND_PACK_PUSH_CERT_ALWAYS;
|
|
return 0;
|
|
case 0:
|
|
*(int *)(opt->value) = SEND_PACK_PUSH_CERT_NEVER;
|
|
return 0;
|
|
}
|
|
if (!strcasecmp("if-asked", arg)) {
|
|
*(int *)(opt->value) = SEND_PACK_PUSH_CERT_IF_ASKED;
|
|
return 0;
|
|
}
|
|
die("bad %s argument: %s", opt->long_name, arg);
|
|
}
|
|
|
|
static void feed_object(const struct object_id *oid, FILE *fh, int negative)
|
|
{
|
|
if (negative &&
|
|
!repo_has_object_file_with_flags(the_repository, oid,
|
|
OBJECT_INFO_SKIP_FETCH_OBJECT |
|
|
OBJECT_INFO_QUICK))
|
|
return;
|
|
|
|
if (negative)
|
|
putc('^', fh);
|
|
fputs(oid_to_hex(oid), fh);
|
|
putc('\n', fh);
|
|
}
|
|
|
|
/*
|
|
* Make a pack stream and spit it out into file descriptor fd
|
|
*/
|
|
static int pack_objects(int fd, struct ref *refs, struct oid_array *advertised,
|
|
struct oid_array *negotiated,
|
|
struct send_pack_args *args)
|
|
{
|
|
/*
|
|
* The child becomes pack-objects --revs; we feed
|
|
* the revision parameters to it via its stdin and
|
|
* let its stdout go back to the other end.
|
|
*/
|
|
struct child_process po = CHILD_PROCESS_INIT;
|
|
FILE *po_in;
|
|
int i;
|
|
int rc;
|
|
|
|
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)
|
|
strvec_push(&po.args, "--thin");
|
|
if (args->use_ofs_delta)
|
|
strvec_push(&po.args, "--delta-base-offset");
|
|
if (args->quiet || !args->progress)
|
|
strvec_push(&po.args, "-q");
|
|
if (args->progress)
|
|
strvec_push(&po.args, "--progress");
|
|
if (is_repository_shallow(the_repository))
|
|
strvec_push(&po.args, "--shallow");
|
|
if (args->disable_bitmaps)
|
|
strvec_push(&po.args, "--no-use-bitmap-index");
|
|
po.in = -1;
|
|
po.out = args->stateless_rpc ? -1 : fd;
|
|
po.git_cmd = 1;
|
|
po.clean_on_exit = 1;
|
|
if (start_command(&po))
|
|
die_errno("git pack-objects failed");
|
|
|
|
/*
|
|
* We feed the pack-objects we just spawned with revision
|
|
* parameters by writing to the pipe.
|
|
*/
|
|
po_in = xfdopen(po.in, "w");
|
|
for (i = 0; i < advertised->nr; i++)
|
|
feed_object(&advertised->oid[i], po_in, 1);
|
|
for (i = 0; i < negotiated->nr; i++)
|
|
feed_object(&negotiated->oid[i], po_in, 1);
|
|
|
|
while (refs) {
|
|
if (!is_null_oid(&refs->old_oid))
|
|
feed_object(&refs->old_oid, po_in, 1);
|
|
if (!is_null_oid(&refs->new_oid))
|
|
feed_object(&refs->new_oid, po_in, 0);
|
|
refs = refs->next;
|
|
}
|
|
|
|
fflush(po_in);
|
|
if (ferror(po_in))
|
|
die_errno("error writing to pack-objects");
|
|
fclose(po_in);
|
|
|
|
if (args->stateless_rpc) {
|
|
char *buf = xmalloc(LARGE_PACKET_MAX);
|
|
while (1) {
|
|
ssize_t n = xread(po.out, buf, LARGE_PACKET_MAX);
|
|
if (n <= 0)
|
|
break;
|
|
send_sideband(fd, -1, buf, n, LARGE_PACKET_MAX);
|
|
}
|
|
free(buf);
|
|
close(po.out);
|
|
po.out = -1;
|
|
}
|
|
|
|
rc = finish_command(&po);
|
|
if (rc) {
|
|
/*
|
|
* For a normal non-zero exit, we assume pack-objects wrote
|
|
* something useful to stderr. For death by signal, though,
|
|
* we should mention it to the user. The exception is SIGPIPE
|
|
* (141), because that's a normal occurrence if the remote end
|
|
* hangs up (and we'll report that by trying to read the unpack
|
|
* status).
|
|
*/
|
|
if (rc > 128 && rc != 141)
|
|
error("pack-objects died of signal %d", rc - 128);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int receive_unpack_status(struct packet_reader *reader)
|
|
{
|
|
if (packet_reader_read(reader) != PACKET_READ_NORMAL)
|
|
return error(_("unexpected flush packet while reading remote unpack status"));
|
|
if (!skip_prefix(reader->line, "unpack ", &reader->line))
|
|
return error(_("unable to parse remote unpack status: %s"), reader->line);
|
|
if (strcmp(reader->line, "ok"))
|
|
return error(_("remote unpack failed: %s"), reader->line);
|
|
return 0;
|
|
}
|
|
|
|
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 *p;
|
|
if (packet_reader_read(reader) != PACKET_READ_NORMAL)
|
|
break;
|
|
head = reader->line;
|
|
p = strchr(head, ' ');
|
|
if (!p) {
|
|
error("invalid status line from remote: %s", reader->line);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
*p++ = '\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) {
|
|
CALLOC_ARRAY(hint->report, 1);
|
|
report = hint->report;
|
|
} else {
|
|
report = hint->report;
|
|
while (report->next)
|
|
report = report->next;
|
|
CALLOC_ARRAY(report->next, 1);
|
|
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);
|
|
if (!hint)
|
|
hint = find_ref_by_name(refs, refname);
|
|
if (!hint) {
|
|
warning("remote reported status on unknown ref: %s",
|
|
refname);
|
|
continue;
|
|
}
|
|
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);
|
|
continue;
|
|
}
|
|
if (!strcmp(head, "ng")) {
|
|
hint->status = REF_STATUS_REMOTE_REJECT;
|
|
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;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int sideband_demux(int in UNUSED, int out, void *data)
|
|
{
|
|
int *fd = data, ret;
|
|
if (async_with_fork())
|
|
close(fd[1]);
|
|
ret = recv_sideband("send-pack", fd[0], out);
|
|
close(out);
|
|
return ret;
|
|
}
|
|
|
|
static int advertise_shallow_grafts_cb(const struct commit_graft *graft, void *cb)
|
|
{
|
|
struct strbuf *sb = cb;
|
|
if (graft->nr_parent == -1)
|
|
packet_buf_write(sb, "shallow %s\n", oid_to_hex(&graft->oid));
|
|
return 0;
|
|
}
|
|
|
|
static void advertise_shallow_grafts_buf(struct strbuf *sb)
|
|
{
|
|
if (!is_repository_shallow(the_repository))
|
|
return;
|
|
for_each_commit_graft(advertise_shallow_grafts_cb, sb);
|
|
}
|
|
|
|
#define CHECK_REF_NO_PUSH -1
|
|
#define CHECK_REF_STATUS_REJECTED -2
|
|
#define CHECK_REF_UPTODATE -3
|
|
static int check_to_send_update(const struct ref *ref, const struct send_pack_args *args)
|
|
{
|
|
if (!ref->peer_ref && !args->send_mirror)
|
|
return CHECK_REF_NO_PUSH;
|
|
|
|
/* 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_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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* the beginning of the next line, or the end of buffer.
|
|
*
|
|
* NEEDSWORK: perhaps move this to git-compat-util.h or somewhere and
|
|
* convert many similar uses found by "git grep -A4 memchr".
|
|
*/
|
|
static const char *next_line(const char *line, size_t len)
|
|
{
|
|
const char *nl = memchr(line, '\n', len);
|
|
if (!nl)
|
|
return line + len; /* incomplete line */
|
|
return nl + 1;
|
|
}
|
|
|
|
static int generate_push_cert(struct strbuf *req_buf,
|
|
const struct ref *remote_refs,
|
|
struct send_pack_args *args,
|
|
const char *cap_string,
|
|
const char *push_cert_nonce)
|
|
{
|
|
const struct ref *ref;
|
|
struct string_list_item *item;
|
|
char *signing_key_id = xstrdup(get_signing_key_id());
|
|
const char *cp, *np;
|
|
struct strbuf cert = STRBUF_INIT;
|
|
int update_seen = 0;
|
|
|
|
strbuf_addstr(&cert, "certificate version 0.1\n");
|
|
strbuf_addf(&cert, "pusher %s ", signing_key_id);
|
|
datestamp(&cert);
|
|
strbuf_addch(&cert, '\n');
|
|
if (args->url && *args->url) {
|
|
char *anon_url = transport_anonymize_url(args->url);
|
|
strbuf_addf(&cert, "pushee %s\n", anon_url);
|
|
free(anon_url);
|
|
}
|
|
if (push_cert_nonce[0])
|
|
strbuf_addf(&cert, "nonce %s\n", push_cert_nonce);
|
|
if (args->push_options)
|
|
for_each_string_list_item(item, args->push_options)
|
|
strbuf_addf(&cert, "push-option %s\n", item->string);
|
|
strbuf_addstr(&cert, "\n");
|
|
|
|
for (ref = remote_refs; ref; ref = ref->next) {
|
|
if (check_to_send_update(ref, args) < 0)
|
|
continue;
|
|
update_seen = 1;
|
|
strbuf_addf(&cert, "%s %s %s\n",
|
|
oid_to_hex(&ref->old_oid),
|
|
oid_to_hex(&ref->new_oid),
|
|
ref->name);
|
|
}
|
|
if (!update_seen)
|
|
goto free_return;
|
|
|
|
if (sign_buffer(&cert, &cert, get_signing_key()))
|
|
die(_("failed to sign the push certificate"));
|
|
|
|
packet_buf_write(req_buf, "push-cert%c%s", 0, cap_string);
|
|
for (cp = cert.buf; cp < cert.buf + cert.len; cp = np) {
|
|
np = next_line(cp, cert.buf + cert.len - cp);
|
|
packet_buf_write(req_buf,
|
|
"%.*s", (int)(np - cp), cp);
|
|
}
|
|
packet_buf_write(req_buf, "push-cert-end\n");
|
|
|
|
free_return:
|
|
free(signing_key_id);
|
|
strbuf_release(&cert);
|
|
return update_seen;
|
|
}
|
|
|
|
#define NONCE_LEN_LIMIT 256
|
|
|
|
static void reject_invalid_nonce(const char *nonce, int len)
|
|
{
|
|
int i = 0;
|
|
|
|
if (NONCE_LEN_LIMIT <= len)
|
|
die("the receiving end asked to sign an invalid nonce <%.*s>",
|
|
len, nonce);
|
|
|
|
for (i = 0; i < len; i++) {
|
|
int ch = nonce[i] & 0xFF;
|
|
if (isalnum(ch) ||
|
|
ch == '-' || ch == '.' ||
|
|
ch == '/' || ch == '+' ||
|
|
ch == '=' || ch == '_')
|
|
continue;
|
|
die("the receiving end asked to sign an invalid nonce <%.*s>",
|
|
len, nonce);
|
|
}
|
|
}
|
|
|
|
static void get_commons_through_negotiation(const char *url,
|
|
const struct ref *remote_refs,
|
|
struct oid_array *commons)
|
|
{
|
|
struct child_process child = CHILD_PROCESS_INIT;
|
|
const struct ref *ref;
|
|
int len = the_hash_algo->hexsz + 1; /* hash + NL */
|
|
|
|
child.git_cmd = 1;
|
|
child.no_stdin = 1;
|
|
child.out = -1;
|
|
strvec_pushl(&child.args, "fetch", "--negotiate-only", NULL);
|
|
for (ref = remote_refs; ref; ref = ref->next) {
|
|
if (!is_null_oid(&ref->new_oid))
|
|
strvec_pushf(&child.args, "--negotiation-tip=%s", oid_to_hex(&ref->new_oid));
|
|
}
|
|
strvec_push(&child.args, url);
|
|
|
|
if (start_command(&child))
|
|
die(_("send-pack: unable to fork off fetch subprocess"));
|
|
|
|
do {
|
|
char hex_hash[GIT_MAX_HEXSZ + 1];
|
|
int read_len = read_in_full(child.out, hex_hash, len);
|
|
struct object_id oid;
|
|
const char *end;
|
|
|
|
if (!read_len)
|
|
break;
|
|
if (read_len != len)
|
|
die("invalid length read %d", read_len);
|
|
if (parse_oid_hex(hex_hash, &oid, &end) || *end != '\n')
|
|
die("invalid hash");
|
|
oid_array_append(commons, &oid);
|
|
} while (1);
|
|
|
|
if (finish_command(&child)) {
|
|
/*
|
|
* The information that push negotiation provides is useful but
|
|
* not mandatory.
|
|
*/
|
|
warning(_("push negotiation failed; proceeding anyway with push"));
|
|
}
|
|
}
|
|
|
|
int send_pack(struct send_pack_args *args,
|
|
int fd[], struct child_process *conn,
|
|
struct ref *remote_refs,
|
|
struct oid_array *extra_have)
|
|
{
|
|
struct oid_array commons = OID_ARRAY_INIT;
|
|
int in = fd[0];
|
|
int out = fd[1];
|
|
struct strbuf req_buf = STRBUF_INIT;
|
|
struct strbuf cap_buf = STRBUF_INIT;
|
|
struct ref *ref;
|
|
int need_pack_data = 0;
|
|
int allow_deleting_refs = 0;
|
|
int status_report = 0;
|
|
int use_sideband = 0;
|
|
int quiet_supported = 0;
|
|
int agent_supported = 0;
|
|
int advertise_sid = 0;
|
|
int push_negotiate = 0;
|
|
int use_atomic = 0;
|
|
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;
|
|
const char *push_cert_nonce = NULL;
|
|
struct packet_reader reader;
|
|
int use_bitmaps;
|
|
|
|
if (!remote_refs) {
|
|
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
|
|
"Perhaps you should specify a branch.\n");
|
|
return 0;
|
|
}
|
|
|
|
git_config_get_bool("push.negotiate", &push_negotiate);
|
|
if (push_negotiate)
|
|
get_commons_through_negotiation(args->url, remote_refs, &commons);
|
|
|
|
if (!git_config_get_bool("push.usebitmaps", &use_bitmaps))
|
|
args->disable_bitmaps = !use_bitmaps;
|
|
|
|
git_config_get_bool("transfer.advertisesid", &advertise_sid);
|
|
|
|
/* Does the other end support the reporting? */
|
|
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;
|
|
if (server_supports("ofs-delta"))
|
|
args->use_ofs_delta = 1;
|
|
if (server_supports("side-band-64k"))
|
|
use_sideband = 1;
|
|
if (server_supports("quiet"))
|
|
quiet_supported = 1;
|
|
if (server_supports("agent"))
|
|
agent_supported = 1;
|
|
if (!server_supports("session-id"))
|
|
advertise_sid = 0;
|
|
if (server_supports("no-thin"))
|
|
args->use_thin_pack = 0;
|
|
if (server_supports("atomic"))
|
|
atomic_supported = 1;
|
|
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) {
|
|
size_t len;
|
|
push_cert_nonce = server_feature_value("push-cert", &len);
|
|
if (push_cert_nonce) {
|
|
reject_invalid_nonce(push_cert_nonce, len);
|
|
push_cert_nonce = xmemdupz(push_cert_nonce, len);
|
|
} else if (args->push_cert == SEND_PACK_PUSH_CERT_ALWAYS) {
|
|
die(_("the receiving end does not support --signed push"));
|
|
} else if (args->push_cert == SEND_PACK_PUSH_CERT_IF_ASKED) {
|
|
warning(_("not sending a push certificate since the"
|
|
" receiving end does not support --signed"
|
|
" push"));
|
|
}
|
|
}
|
|
|
|
if (args->atomic && !atomic_supported)
|
|
die(_("the receiving end does not support --atomic push"));
|
|
|
|
use_atomic = atomic_supported && args->atomic;
|
|
|
|
if (args->push_options && !push_options_supported)
|
|
die(_("the receiving end does not support push options"));
|
|
|
|
use_push_options = push_options_supported && args->push_options;
|
|
|
|
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))
|
|
strbuf_addstr(&cap_buf, " quiet");
|
|
if (use_atomic)
|
|
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());
|
|
if (advertise_sid)
|
|
strbuf_addf(&cap_buf, " session-id=%s", trace2_session_id());
|
|
|
|
/*
|
|
* NEEDSWORK: why does delete-refs have to be so specific to
|
|
* send-pack machinery that set_ref_status_for_push() cannot
|
|
* set this bit for us???
|
|
*/
|
|
for (ref = remote_refs; ref; ref = ref->next)
|
|
if (ref->deletion && !allow_deleting_refs)
|
|
ref->status = REF_STATUS_REJECT_NODELETE;
|
|
|
|
/*
|
|
* Clear the status for each ref and see if we need to send
|
|
* the pack data.
|
|
*/
|
|
for (ref = remote_refs; ref; ref = ref->next) {
|
|
switch (check_to_send_update(ref, args)) {
|
|
case 0: /* no error */
|
|
break;
|
|
case CHECK_REF_STATUS_REJECTED:
|
|
/*
|
|
* When we know the server would reject a ref update if
|
|
* we were to send it and we're trying to send the refs
|
|
* atomically, abort the whole operation.
|
|
*/
|
|
if (use_atomic) {
|
|
strbuf_release(&req_buf);
|
|
strbuf_release(&cap_buf);
|
|
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:
|
|
continue;
|
|
}
|
|
if (!ref->deletion)
|
|
need_pack_data = 1;
|
|
|
|
if (args->dry_run || !status_report)
|
|
ref->status = REF_STATUS_OK;
|
|
else
|
|
ref->status = REF_STATUS_EXPECTING_REPORT;
|
|
}
|
|
|
|
if (!args->dry_run)
|
|
advertise_shallow_grafts_buf(&req_buf);
|
|
|
|
/*
|
|
* Finally, tell the other end!
|
|
*/
|
|
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;
|
|
|
|
packet_buf_flush(&req_buf);
|
|
for_each_string_list_item(item, args->push_options)
|
|
packet_buf_write(&req_buf, "%s", item->string);
|
|
}
|
|
|
|
if (args->stateless_rpc) {
|
|
if (!args->dry_run && (cmds_sent || is_repository_shallow(the_repository))) {
|
|
packet_buf_flush(&req_buf);
|
|
send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
|
|
}
|
|
} else {
|
|
write_or_die(out, req_buf.buf, req_buf.len);
|
|
packet_flush(out);
|
|
}
|
|
strbuf_release(&req_buf);
|
|
strbuf_release(&cap_buf);
|
|
|
|
if (use_sideband && cmds_sent) {
|
|
memset(&demux, 0, sizeof(demux));
|
|
demux.proc = sideband_demux;
|
|
demux.data = fd;
|
|
demux.out = -1;
|
|
demux.isolate_sigpipe = 1;
|
|
if (start_async(&demux))
|
|
die("send-pack: unable to fork off sideband demultiplexer");
|
|
in = demux.out;
|
|
}
|
|
|
|
packet_reader_init(&reader, in, NULL, 0,
|
|
PACKET_READ_CHOMP_NEWLINE |
|
|
PACKET_READ_DIE_ON_ERR_PACKET);
|
|
|
|
if (need_pack_data && cmds_sent) {
|
|
if (pack_objects(out, remote_refs, extra_have, &commons, args) < 0) {
|
|
if (args->stateless_rpc)
|
|
close(out);
|
|
if (git_connection_is_socket(conn))
|
|
shutdown(fd[0], SHUT_WR);
|
|
|
|
/*
|
|
* Do not even bother with the return value; we know we
|
|
* are failing, and just want the error() side effects,
|
|
* as well as marking refs with their remote status (if
|
|
* we get one).
|
|
*/
|
|
if (status_report)
|
|
receive_status(&reader, remote_refs);
|
|
|
|
if (use_sideband) {
|
|
close(demux.out);
|
|
finish_async(&demux);
|
|
}
|
|
fd[1] = -1;
|
|
return -1;
|
|
}
|
|
if (!args->stateless_rpc)
|
|
/* Closed by pack_objects() via start_command() */
|
|
fd[1] = -1;
|
|
}
|
|
if (args->stateless_rpc && cmds_sent)
|
|
packet_flush(out);
|
|
|
|
if (status_report && cmds_sent)
|
|
ret = receive_status(&reader, remote_refs);
|
|
else
|
|
ret = 0;
|
|
if (args->stateless_rpc)
|
|
packet_flush(out);
|
|
|
|
if (use_sideband && cmds_sent) {
|
|
close(demux.out);
|
|
if (finish_async(&demux)) {
|
|
error("error in sideband demultiplexer");
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (args->porcelain)
|
|
return 0;
|
|
|
|
for (ref = remote_refs; ref; ref = ref->next) {
|
|
switch (ref->status) {
|
|
case REF_STATUS_NONE:
|
|
case REF_STATUS_UPTODATE:
|
|
case REF_STATUS_OK:
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|