in send-pack.c [482:784]
int send_pack(struct repository *r,
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;
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");
ret = 0;
goto out;
}
repo_config_get_bool(r, "push.negotiate", &push_negotiate);
if (push_negotiate) {
trace2_region_enter("send_pack", "push_negotiate", r);
get_commons_through_negotiation(r, args->url, remote_refs, &commons);
trace2_region_leave("send_pack", "push_negotiate", r);
}
if (!repo_config_get_bool(r, "push.usebitmaps", &use_bitmaps))
args->disable_bitmaps = !use_bitmaps;
repo_config_get_bool(r, "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(r->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;
const char *nonce = server_feature_value("push-cert", &len);
if (nonce) {
reject_invalid_nonce(nonce, len);
push_cert_nonce = xmemdupz(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", r->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) {
reject_atomic_push(remote_refs, args->send_mirror);
error("atomic push failed for ref %s. status: %d",
ref->name, ref->status);
ret = ERROR_SEND_PACK_BAD_REF_STATUS;
packet_flush(out);
goto out;
}
/* 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(r, &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);
trace2_printf("Generated push certificate");
} 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(r))) {
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);
}
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(r, 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(r, &reader, remote_refs);
if (use_sideband) {
close(demux.out);
finish_async(&demux);
}
fd[1] = -1;
ret = -1;
goto out;
}
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(r, &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)
goto out;
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:
ret = ERROR_SEND_PACK_BAD_REF_STATUS;
goto out;
}
}
ret = 0;
out:
oid_array_clear(&commons);
strbuf_release(&req_buf);
strbuf_release(&cap_buf);
free(push_cert_nonce);
return ret;
}