mirror of
https://github.com/git/git.git
synced 2025-12-12 20:36:24 +01:00
Merge branch 'sb/atomic-push'
"git push" has been taught a "--atomic" option that makes push to update more than one ref an "all-or-none" affair. * sb/atomic-push: Document receive.advertiseatomic t5543-atomic-push.sh: add basic tests for atomic pushes push.c: add an --atomic argument send-pack.c: add --atomic command line argument send-pack: rename ref_update_to_be_sent to check_to_send_update receive-pack.c: negotiate atomic push support receive-pack.c: add execute_commands_atomic function receive-pack.c: move transaction handling in a central place receive-pack.c: move iterating over all commands outside execute_commands receive-pack.c: die instead of error in case of possible future bug receive-pack.c: shorten the execute_commands loop over all commands
This commit is contained in:
65
send-pack.c
65
send-pack.c
@@ -193,10 +193,13 @@ static void advertise_shallow_grafts_buf(struct strbuf *sb)
|
||||
for_each_commit_graft(advertise_shallow_grafts_cb, sb);
|
||||
}
|
||||
|
||||
static int ref_update_to_be_sent(const struct ref *ref, const struct send_pack_args *args)
|
||||
#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 0;
|
||||
return CHECK_REF_NO_PUSH;
|
||||
|
||||
/* Check for statuses set by set_ref_status_for_push() */
|
||||
switch (ref->status) {
|
||||
@@ -206,10 +209,11 @@ static int ref_update_to_be_sent(const struct ref *ref, const struct send_pack_a
|
||||
case REF_STATUS_REJECT_NEEDS_FORCE:
|
||||
case REF_STATUS_REJECT_STALE:
|
||||
case REF_STATUS_REJECT_NODELETE:
|
||||
return CHECK_REF_STATUS_REJECTED;
|
||||
case REF_STATUS_UPTODATE:
|
||||
return 0;
|
||||
return CHECK_REF_UPTODATE;
|
||||
default:
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,7 +257,7 @@ static int generate_push_cert(struct strbuf *req_buf,
|
||||
strbuf_addstr(&cert, "\n");
|
||||
|
||||
for (ref = remote_refs; ref; ref = ref->next) {
|
||||
if (!ref_update_to_be_sent(ref, args))
|
||||
if (check_to_send_update(ref, args) < 0)
|
||||
continue;
|
||||
update_seen = 1;
|
||||
strbuf_addf(&cert, "%s %s %s\n",
|
||||
@@ -281,6 +285,29 @@ 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);
|
||||
}
|
||||
|
||||
int send_pack(struct send_pack_args *args,
|
||||
int fd[], struct child_process *conn,
|
||||
struct ref *remote_refs,
|
||||
@@ -297,6 +324,8 @@ int send_pack(struct send_pack_args *args,
|
||||
int use_sideband = 0;
|
||||
int quiet_supported = 0;
|
||||
int agent_supported = 0;
|
||||
int use_atomic = 0;
|
||||
int atomic_supported = 0;
|
||||
unsigned cmds_sent = 0;
|
||||
int ret;
|
||||
struct async demux;
|
||||
@@ -317,6 +346,8 @@ int send_pack(struct send_pack_args *args,
|
||||
agent_supported = 1;
|
||||
if (server_supports("no-thin"))
|
||||
args->use_thin_pack = 0;
|
||||
if (server_supports("atomic"))
|
||||
atomic_supported = 1;
|
||||
if (args->push_cert) {
|
||||
int len;
|
||||
|
||||
@@ -331,6 +362,10 @@ int send_pack(struct send_pack_args *args,
|
||||
"Perhaps you should specify a branch such as 'master'.\n");
|
||||
return 0;
|
||||
}
|
||||
if (args->atomic && !atomic_supported)
|
||||
die(_("server does not support --atomic push"));
|
||||
|
||||
use_atomic = atomic_supported && args->atomic;
|
||||
|
||||
if (status_report)
|
||||
strbuf_addstr(&cap_buf, " report-status");
|
||||
@@ -338,6 +373,8 @@ int send_pack(struct send_pack_args *args,
|
||||
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 (agent_supported)
|
||||
strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized());
|
||||
|
||||
@@ -362,9 +399,21 @@ int send_pack(struct send_pack_args *args,
|
||||
* the pack data.
|
||||
*/
|
||||
for (ref = remote_refs; ref; ref = ref->next) {
|
||||
if (!ref_update_to_be_sent(ref, args))
|
||||
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)
|
||||
return atomic_push_failure(args, remote_refs, ref);
|
||||
/* Fallthrough for non atomic case. */
|
||||
default:
|
||||
continue;
|
||||
|
||||
}
|
||||
if (!ref->deletion)
|
||||
need_pack_data = 1;
|
||||
|
||||
@@ -383,7 +432,7 @@ int send_pack(struct send_pack_args *args,
|
||||
if (args->dry_run || args->push_cert)
|
||||
continue;
|
||||
|
||||
if (!ref_update_to_be_sent(ref, args))
|
||||
if (check_to_send_update(ref, args) < 0)
|
||||
continue;
|
||||
|
||||
old_hex = sha1_to_hex(ref->old_sha1);
|
||||
|
||||
Reference in New Issue
Block a user