* [PATCHv7 1/9] receive-pack.c: add documentation for atomic push support
2014-12-29 21:35 [PATCHv7 0/9] atomic pushes Stefan Beller
@ 2014-12-29 21:35 ` Stefan Beller
2014-12-29 21:35 ` [PATCHv7 2/9] send-pack: Rename ref_update_to_be_sent to check_to_send_update Stefan Beller
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Stefan Beller @ 2014-12-29 21:35 UTC (permalink / raw)
To: git
Cc: ronniesahlberg, mhagger, jrnieder, gitster, Ronnie Sahlberg,
Stefan Beller
From: Ronnie Sahlberg <sahlberg@google.com>
This documents the protocol option between send-pack and receive-pack to
* allow receive-pack to inform the client that it has atomic push capability
* allow send-pack to request atomic push back.
There is currently no setting in send-pack to actually request that atomic
pushes are to be used yet. This only adds protocol capability not ability
for the user to activate it. The capability is also not yet advertised
by receive-pack as git doesn't know how to handle it yet.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
---
Notes:
Changes v1 -> v2:
* Name it atomic instead of atomic-push. The name changes affects
the flags send over the wire as well as the flags in
struct send_pack_args
* Add check, which was part of the later patch here:
if (args->atomic && !atomic_supported) {
fprintf(stderr, "Server does not support atomic push.");
return -1;
}
v2 -> v3:
More concise error reporting as suggested by Junio
- fprintf(stderr, "Server does not support atomic push.");
- return -1;
+ return error("server does not support atomic push.");
skipped v4 v5
v6:
* s/one single atomic transaction./one atomic transaction./
* die(_( instead of return error(
v7:
* Don't advertise the atomic push capability yet. We cannot handle it at
this point.
* reword commit message
Documentation/technical/protocol-capabilities.txt | 13 +++++++++++--
builtin/receive-pack.c | 3 +++
send-pack.c | 10 ++++++++++
send-pack.h | 3 ++-
4 files changed, 26 insertions(+), 3 deletions(-)
diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt
index 6d5424c..4f8a7bf 100644
--- a/Documentation/technical/protocol-capabilities.txt
+++ b/Documentation/technical/protocol-capabilities.txt
@@ -18,8 +18,9 @@ was sent. Server MUST NOT ignore capabilities that client requested
and server advertised. As a consequence of these rules, server MUST
NOT advertise capabilities it does not understand.
-The 'report-status', 'delete-refs', 'quiet', and 'push-cert' capabilities
-are sent and recognized by the receive-pack (push to server) process.
+The 'atomic', 'report-status', 'delete-refs', 'quiet', and 'push-cert'
+capabilities are sent and recognized by the receive-pack (push to server)
+process.
The 'ofs-delta' and 'side-band-64k' capabilities are sent and recognized
by both upload-pack and receive-pack protocols. The 'agent' capability
@@ -244,6 +245,14 @@ respond with the 'quiet' capability to suppress server-side progress
reporting if the local progress reporting is also being suppressed
(e.g., via `push -q`, or if stderr does not go to a tty).
+atomic
+------
+
+If the server sends the 'atomic' capability it is capable of accepting
+atomic pushes. If the pushing client requests this capability, the server
+will update the refs in one atomic transaction. Either all refs are
+updated or none.
+
allow-tip-sha1-in-want
----------------------
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 32fc540..4e8eaf7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -40,6 +40,7 @@ static int transfer_unpack_limit = -1;
static int unpack_limit = 100;
static int report_status;
static int use_sideband;
+static int use_atomic;
static int quiet;
static int prefer_ofs_delta = 1;
static int auto_update_server_info;
@@ -1179,6 +1180,8 @@ static struct command *read_head_info(struct sha1_array *shallow)
use_sideband = LARGE_PACKET_MAX;
if (parse_feature_request(feature_list, "quiet"))
quiet = 1;
+ if (parse_feature_request(feature_list, "atomic"))
+ use_atomic = 1;
}
if (!strcmp(line, "push-cert")) {
diff --git a/send-pack.c b/send-pack.c
index 949cb61..6d0c159 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -294,6 +294,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;
@@ -314,6 +316,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;
@@ -328,6 +332,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");
@@ -335,6 +343,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());
diff --git a/send-pack.h b/send-pack.h
index 5635457..b664648 100644
--- a/send-pack.h
+++ b/send-pack.h
@@ -13,7 +13,8 @@ struct send_pack_args {
use_ofs_delta:1,
dry_run:1,
push_cert:1,
- stateless_rpc:1;
+ stateless_rpc:1,
+ atomic:1;
};
int send_pack(struct send_pack_args *args,
--
2.2.1.62.g3f15098
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCHv7 2/9] send-pack: Rename ref_update_to_be_sent to check_to_send_update
2014-12-29 21:35 [PATCHv7 0/9] atomic pushes Stefan Beller
2014-12-29 21:35 ` [PATCHv7 1/9] receive-pack.c: add documentation for atomic push support Stefan Beller
@ 2014-12-29 21:35 ` Stefan Beller
2014-12-29 21:35 ` [PATCHv7 3/9] send-pack.c: add --atomic command line argument Stefan Beller
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Stefan Beller @ 2014-12-29 21:35 UTC (permalink / raw)
To: git; +Cc: ronniesahlberg, mhagger, jrnieder, gitster, Stefan Beller
This renames ref_update_to_be_sent to check_to_send_update and inverts
the meaning of the return value. Having the return value inverted we
can have different values for the error codes. This is useful in a
later patch when we want to know if we hit the CHECK_REF_STATUS_REJECTED
case.
Signed-off-by: Stefan Beller <sbeller@google.com>
---
Notes:
This was introduced with the [PATCHv2] series.
Changes v2 -> v3:
* Rename to check_to_send_update
* Negative error values.
* errors values are #define'd and not raw numbers.
skipped v4 v5
v6:
* negative #define'd values
v7:
* no changes
send-pack.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/send-pack.c b/send-pack.c
index 6d0c159..b7d8e01 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -190,10 +190,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) {
@@ -203,10 +206,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;
}
}
@@ -250,7 +254,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",
@@ -369,7 +373,7 @@ 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))
+ if (check_to_send_update(ref, args) < 0)
continue;
if (!ref->deletion)
@@ -390,7 +394,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);
--
2.2.1.62.g3f15098
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCHv7 3/9] send-pack.c: add --atomic command line argument
2014-12-29 21:35 [PATCHv7 0/9] atomic pushes Stefan Beller
2014-12-29 21:35 ` [PATCHv7 1/9] receive-pack.c: add documentation for atomic push support Stefan Beller
2014-12-29 21:35 ` [PATCHv7 2/9] send-pack: Rename ref_update_to_be_sent to check_to_send_update Stefan Beller
@ 2014-12-29 21:35 ` Stefan Beller
2014-12-29 21:35 ` [PATCHv7 4/9] receive-pack.c: simplify execute_commands Stefan Beller
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Stefan Beller @ 2014-12-29 21:35 UTC (permalink / raw)
To: git
Cc: ronniesahlberg, mhagger, jrnieder, gitster, Ronnie Sahlberg,
Stefan Beller
From: Ronnie Sahlberg <sahlberg@google.com>
This adds support to send-pack to negotiate and use atomic pushes
iff the server supports it. Atomic pushes are activated by a new command
line flag --atomic.
In order to do this we also need to change the semantics for send_pack()
slightly. The existing send_pack() function actually doesn't send all the
refs back to the server when multiple refs are involved, for example
when using --all. Several of the failure modes for pushes can already be
detected locally in the send_pack client based on the information from the
initial server side list of all the refs as generated by receive-pack.
Any such refs that we thus know would fail to push are thus pruned from
the list of refs we send to the server to update.
For atomic pushes, we have to deal thus with both failures that are detected
locally as well as failures that are reported back from the server. In order
to do so we treat all local failures as push failures too.
We introduce a new status code REF_STATUS_ATOMIC_PUSH_FAILED so we can
flag all refs that we would normally have tried to push to the server
but we did not due to local failures. This is to improve the error message
back to the end user to flag that "these refs failed to update since the
atomic push operation failed."
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
---
Notes:
Notes:
Changes v1 -> v2:
* Now we only need a very small change in the existing code and have
a new static function, which cares about error reporting.
Junio wrote:
> Hmph. Is "atomic push" so special that it deserves a separate
> parameter? When we come up with yet another mode of failure, would
> we add another parameter to the callers to this function?
* error messages are worded differently (lower case!),
* use of error function instead of fprintf
* undashed the printed error message ("atomic push failed");
Changes v2 -> v3:
> We avoid assignment inside a conditional.
Ok I switched to using a switch statement.
skipped v4 v5
v6:
* realign to one error string:
+ error("atomic push failed for ref %s. status: %d\n",
+ failing_ref->name, failing_ref->status);
* Use correct value now (negative defined from previous patch)
v7:
* return error(...); instead of error(...); return -1;
Documentation/git-send-pack.txt | 7 ++++++-
builtin/send-pack.c | 6 +++++-
remote.h | 3 ++-
send-pack.c | 39 +++++++++++++++++++++++++++++++++++++--
transport.c | 4 ++++
5 files changed, 54 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt
index 2a0de42..45c7725 100644
--- a/Documentation/git-send-pack.txt
+++ b/Documentation/git-send-pack.txt
@@ -9,7 +9,7 @@ git-send-pack - Push objects over Git protocol to another repository
SYNOPSIS
--------
[verse]
-'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]
+'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] [<host>:]<directory> [<ref>...]
DESCRIPTION
-----------
@@ -62,6 +62,11 @@ be in a separate packet, and the list must end with a flush packet.
Send a "thin" pack, which records objects in deltified form based
on objects not included in the pack to reduce network traffic.
+--atomic::
+ Use an atomic transaction for updating the refs. If any of the refs
+ fails to update then the entire push will fail without changing any
+ refs.
+
<host>::
A remote host to house the repository. When this
part is specified, 'git-receive-pack' is invoked via
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index b564a77..b961e5a 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -13,7 +13,7 @@
#include "sha1-array.h"
static const char send_pack_usage[] =
-"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
+"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] [<host>:]<directory> [<ref>...]\n"
" --all and explicit <ref> specification are mutually exclusive.";
static struct send_pack_args args;
@@ -170,6 +170,10 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
args.use_thin_pack = 1;
continue;
}
+ if (!strcmp(arg, "--atomic")) {
+ args.atomic = 1;
+ continue;
+ }
if (!strcmp(arg, "--stateless-rpc")) {
args.stateless_rpc = 1;
continue;
diff --git a/remote.h b/remote.h
index 8b62efd..f346524 100644
--- a/remote.h
+++ b/remote.h
@@ -115,7 +115,8 @@ struct ref {
REF_STATUS_REJECT_SHALLOW,
REF_STATUS_UPTODATE,
REF_STATUS_REMOTE_REJECT,
- REF_STATUS_EXPECTING_REPORT
+ REF_STATUS_EXPECTING_REPORT,
+ REF_STATUS_ATOMIC_PUSH_FAILED
} status;
char *remote_status;
struct ref *peer_ref; /* when renaming */
diff --git a/send-pack.c b/send-pack.c
index b7d8e01..e8f60df 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -282,6 +282,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:
+ ; /* 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,
@@ -373,9 +396,21 @@ int send_pack(struct send_pack_args *args,
* the pack data.
*/
for (ref = remote_refs; ref; ref = ref->next) {
- if (check_to_send_update(ref, args) < 0)
+ 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;
diff --git a/transport.c b/transport.c
index 70d38e4..c67feee 100644
--- a/transport.c
+++ b/transport.c
@@ -728,6 +728,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
ref->deletion ? NULL : ref->peer_ref,
"remote failed to report status", porcelain);
break;
+ case REF_STATUS_ATOMIC_PUSH_FAILED:
+ print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+ "atomic push failed", porcelain);
+ break;
case REF_STATUS_OK:
print_ok_ref_status(ref, porcelain);
break;
--
2.2.1.62.g3f15098
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCHv7 4/9] receive-pack.c: simplify execute_commands
2014-12-29 21:35 [PATCHv7 0/9] atomic pushes Stefan Beller
` (2 preceding siblings ...)
2014-12-29 21:35 ` [PATCHv7 3/9] send-pack.c: add --atomic command line argument Stefan Beller
@ 2014-12-29 21:35 ` Stefan Beller
2014-12-29 21:35 ` [PATCHv7 5/9] receive-pack.c: move transaction handling in a central place Stefan Beller
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Stefan Beller @ 2014-12-29 21:35 UTC (permalink / raw)
To: git; +Cc: ronniesahlberg, mhagger, jrnieder, gitster, Stefan Beller
No functional changes intended.
This commit shortens execute_commands by moving some parts of the code
to extra functions.
Suggested-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
---
Notes:
v7:
new in v7 as in v7 I'd split up the previous
[PATCH 4/7] receive-pack.c: receive-pack.c: use a single ref_transaction for atomic pushes
as suggested by Eric.
This is pretty much
> patch 1: Factor out code into helper functions which will be needed by
> the upcoming atomic and non-atomic worker functions. Example helpers:
> 'cmd->error_string' and cmd->skip_update' check; and the
> 'si->shallow_ref[cmd->index]' check and handling.
builtin/receive-pack.c | 49 ++++++++++++++++++++++++++++++++-----------------
1 file changed, 32 insertions(+), 17 deletions(-)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 4e8eaf7..06eb287 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1043,11 +1043,40 @@ static void reject_updates_to_hidden(struct command *commands)
}
}
+static int should_process_cmd(struct command *cmd)
+{
+ if (cmd->error_string)
+ return 0;
+ if (cmd->skip_update)
+ return 0;
+ return 1;
+}
+
+static void check_shallow_bugs(struct command *commands,
+ struct shallow_info *si)
+{
+ struct command *cmd;
+ int checked_connectivity = 1;
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ if (!should_process_cmd(cmd))
+ continue;
+
+ if (shallow_update && si->shallow_ref[cmd->index]) {
+ error("BUG: connectivity check has not been run on ref %s",
+ cmd->ref_name);
+ checked_connectivity = 0;
+ }
+ }
+ if (shallow_update && !checked_connectivity)
+ error("BUG: run 'git fsck' for safety.\n"
+ "If there are errors, try to remove "
+ "the reported refs above");
+}
+
static void execute_commands(struct command *commands,
const char *unpacker_error,
struct shallow_info *si)
{
- int checked_connectivity;
struct command *cmd;
unsigned char sha1[20];
struct iterate_data data;
@@ -1078,27 +1107,13 @@ static void execute_commands(struct command *commands,
free(head_name_to_free);
head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL);
- checked_connectivity = 1;
for (cmd = commands; cmd; cmd = cmd->next) {
- if (cmd->error_string)
- continue;
-
- if (cmd->skip_update)
+ if (!should_process_cmd(cmd))
continue;
cmd->error_string = update(cmd, si);
- if (shallow_update && !cmd->error_string &&
- si->shallow_ref[cmd->index]) {
- error("BUG: connectivity check has not been run on ref %s",
- cmd->ref_name);
- checked_connectivity = 0;
- }
}
-
- if (shallow_update && !checked_connectivity)
- error("BUG: run 'git fsck' for safety.\n"
- "If there are errors, try to remove "
- "the reported refs above");
+ check_shallow_bugs(commands, si);
}
static struct command **queue_command(struct command **tail,
--
2.2.1.62.g3f15098
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCHv7 5/9] receive-pack.c: move transaction handling in a central place
2014-12-29 21:35 [PATCHv7 0/9] atomic pushes Stefan Beller
` (3 preceding siblings ...)
2014-12-29 21:35 ` [PATCHv7 4/9] receive-pack.c: simplify execute_commands Stefan Beller
@ 2014-12-29 21:35 ` Stefan Beller
2014-12-29 21:35 ` [PATCHv7 6/9] receive-pack.c: add execute_commands_atomic function Stefan Beller
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Stefan Beller @ 2014-12-29 21:35 UTC (permalink / raw)
To: git; +Cc: ronniesahlberg, mhagger, jrnieder, gitster, Stefan Beller
No functional changes intended.
This moves all code related to transactions into the execute_commands_loop
function which was factored out of execute_commands. This includes
beginning and committing the transaction as well as dealing with the
errors which may occur during the begin and commit phase of a transaction.
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
---
Notes:
v7:
new in v7, this is part of the previous "[PATCH 4/7]
receive-pack.c: receive-pack.c: use a single ref_transaction for atomic pushes"
This covers the suggestion of patch 2 and 3 by Eric
> patch 2: Factor out the main 'for' loop of execute_commands() into a
> new function. This new function will eventually become
> execute_commands_non_atomic(). At this point, execute_commands() is
> pretty much in its final form with the exception of the upcoming 'if
> (use_atomic)' conditional.
> patch 3: Morph the function extracted in patch 2 into
> execute_commands_non_atomic() by adding transaction handling inside
> the 'for' loop (and applying the changes from the early part of the
> patch which go along with that).
builtin/receive-pack.c | 62 ++++++++++++++++++++++++++++++++++++++------------
1 file changed, 47 insertions(+), 15 deletions(-)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 06eb287..915ad3c 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -67,6 +67,7 @@ static const char *NONCE_SLOP = "SLOP";
static const char *nonce_status;
static long nonce_stamp_slop;
static unsigned long nonce_stamp_slop_limit;
+static struct ref_transaction *transaction;
static enum deny_action parse_deny_action(const char *var, const char *value)
{
@@ -822,6 +823,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
}
if (is_null_sha1(new_sha1)) {
+ struct strbuf err = STRBUF_INIT;
if (!parse_object(old_sha1)) {
old_sha1 = NULL;
if (ref_exists(name)) {
@@ -831,35 +833,36 @@ static const char *update(struct command *cmd, struct shallow_info *si)
cmd->did_not_exist = 1;
}
}
- if (delete_ref(namespaced_name, old_sha1, 0)) {
- rp_error("failed to delete %s", name);
+ if (ref_transaction_delete(transaction,
+ namespaced_name,
+ old_sha1,
+ 0, old_sha1 != NULL,
+ "push", &err)) {
+ rp_error("%s", err.buf);
+ strbuf_release(&err);
return "failed to delete";
}
+ strbuf_release(&err);
return NULL; /* good */
}
else {
struct strbuf err = STRBUF_INIT;
- struct ref_transaction *transaction;
-
if (shallow_update && si->shallow_ref[cmd->index] &&
update_shallow_ref(cmd, si))
return "shallow error";
- transaction = ref_transaction_begin(&err);
- if (!transaction ||
- ref_transaction_update(transaction, namespaced_name,
- new_sha1, old_sha1, 0, 1, "push",
- &err) ||
- ref_transaction_commit(transaction, &err)) {
- ref_transaction_free(transaction);
-
+ if (ref_transaction_update(transaction,
+ namespaced_name,
+ new_sha1, old_sha1,
+ 0, 1, "push",
+ &err)) {
rp_error("%s", err.buf);
strbuf_release(&err);
+
return "failed to update ref";
}
-
- ref_transaction_free(transaction);
strbuf_release(&err);
+
return NULL; /* good */
}
}
@@ -1107,13 +1110,42 @@ static void execute_commands(struct command *commands,
free(head_name_to_free);
head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL);
+ execute_commands_loop(commands, si);
+
+ check_shallow_bugs(commands, si);
+}
+
+static void execute_commands_loop(struct command *commands,
+ struct shallow_info *si)
+{
+ struct command *cmd;
+ struct strbuf err = STRBUF_INIT;
+
for (cmd = commands; cmd; cmd = cmd->next) {
if (!should_process_cmd(cmd))
continue;
+ transaction = ref_transaction_begin(&err);
+ if (!transaction) {
+ rp_error("%s", err.buf);
+ strbuf_reset(&err);
+ cmd->error_string = "transaction failed to start";
+ continue;
+ }
+
cmd->error_string = update(cmd, si);
+
+ if (!cmd->error_string
+ || ref_transaction_commit(transaction, &err)) {
+ rp_error("%s", err.buf);
+ strbuf_reset(&err);
+ cmd->error_string = "failed to update ref";
+ }
+ ref_transaction_free(transaction);
}
- check_shallow_bugs(commands, si);
+
+ }
+ strbuf_release(&err);
}
static struct command **queue_command(struct command **tail,
--
2.2.1.62.g3f15098
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCHv7 6/9] receive-pack.c: add execute_commands_atomic function
2014-12-29 21:35 [PATCHv7 0/9] atomic pushes Stefan Beller
` (4 preceding siblings ...)
2014-12-29 21:35 ` [PATCHv7 5/9] receive-pack.c: move transaction handling in a central place Stefan Beller
@ 2014-12-29 21:35 ` Stefan Beller
2014-12-29 21:35 ` [PATCHv7 7/9] receive-pack.c: enable atomic push protocol support Stefan Beller
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Stefan Beller @ 2014-12-29 21:35 UTC (permalink / raw)
To: git; +Cc: ronniesahlberg, mhagger, jrnieder, gitster, Stefan Beller
Update receive-pack to use an atomic transaction iff the client negotiated
that it wanted atomic push. This leaves the default behavior to be the old
non-atomic one ref at a time update. This is to cause as little disruption
as possible to existing clients. It is unknown if there are client scripts
that depend on the old non-atomic behavior so we make it opt-in for now.
If it turns out over time that there are no client scripts that depend on the
old behavior we can change git to default to use atomic pushes and instead
offer an opt-out argument for people that do not want atomic pushes.
Inspired-by: Ronnie Sahlberg <sahlberg@google.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
---
Notes:
Changes in v7:
Eric suggested to replace "[PATCH 4/7] receive-pack.c:
receive-pack.c: use a single ref_transaction for atomic pushes"
by smaller patches
This is the last patch replacing said large commit.
Changes in v6:
This is a complete rewrite of the patch essentially.
Eric suggested to split up the flow into functions, so
it is easier to read. It's more lines of code, but indeed
it's easier to follow. Thanks Eric!
Note there is another patch following this one
moving the helper functions above execute_commands.
I just choose the order of the functions in this patch
to have a better diff (just inserting the call to the function
execute_commands_non_atomic and that function directly follows.)
The next patch of the series will move that up.
Because of the rewrite and the fixes of the previous five
versions there is not much left of Ronnies original patch,
so I'll claim authorship of this one.
Changes v1 -> v2:
* update(...) assumes to be always in a transaction
* Caring about when to begin/commit transactions is put
into execute_commands
v2->v3:
* meditated about the error flow. Now we always construct a local
strbuf err if required. Then the flow is easier to follow and
destruction of it is performed nearby.
* early return in execute_commands if transaction_begin fails.
v3->v4:
* revamp logic again. This should keep the non atomic behavior
as is (in case of error say so, in non error case just free the
transaction). In the atomic case we either do nothing (when no error),
or abort with the goto.
if (!cmd->error_string) {
if (!use_atomic
&& ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
rp_error("%s", err.buf);
strbuf_release(&err);
cmd->error_string = "failed to update ref";
}
} else if (use_atomic) {
goto atomic_failure;
} else {
ref_transaction_free(transaction);
}
* Having the goto directly there when checking for cmd->error_string,
we don't need to do it again, so the paragraph explaining the error
checking is gone as well. (Previous patch had the following, this is
put at the end of the function, where the goto jumps to and the comment
has been dropped.
+ /*
+ * update(...) may abort early (i.e. because the hook refused to
+ * update that ref) which then doesn't even record a transaction
+ * regarding that ref. Make sure all commands are without error
+ * and then commit atomically.
+ */
+ for (cmd = commands; cmd; cmd = cmd->next)
+ if (cmd->error_string)
+ break;
v4->v5:
Eric wrote:
> Repeating from my earlier review[1]: If the 'pre-receive' hook
> "declines", then this transaction is left dangling (and its resources
> leaked).
You're right. The initialization of the transaction is now
near the actual loop after the pre receive hook.
> The !use_atomic case (below), calls this error "failed to start
> transaction", not merely "transaction error".
ok, now both are "transaction failed to start".
In all cases where these generic errors are reported,
we do have a rp_error(...) with details.
> Furthermore, in the use_atomic case (also below), when a commit fails,
> you assign err.buf to cmd->error_string rather than a generic
> "transaction error" message. What differs between these cases which
> makes the generic message preferable here over the more specific
> err.buf message?
They are the same now.
> Repeating from my earlier review[1]: This is leaking 'transaction' for
> each successful commit (and only freeing it upon commit error).
Right. I thought I had it covered with the else clause. Of course not.
> At the end of this function, strbuf_release(&err) is invoked, which
> leaves all these cmd->error_strings dangling.
I removed all assignments of err.buf now.
> goto's can help simplify error-handling when multiple conditional
> branches need to perform common cleanup, however, this label
> corresponds to only a single goto statement.
moved up again.
builtin/receive-pack.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 50 insertions(+), 3 deletions(-)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 915ad3c..7e3bbe6 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1110,13 +1110,16 @@ static void execute_commands(struct command *commands,
free(head_name_to_free);
head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL);
- execute_commands_loop(commands, si);
+ if (use_atomic)
+ execute_commands_atomic(commands, si);
+ else
+ execute_commands_non_atomic(commands, si);
check_shallow_bugs(commands, si);
}
-static void execute_commands_loop(struct command *commands,
- struct shallow_info *si)
+static void execute_commands_non_atomic(struct command *commands,
+ struct shallow_info *si)
{
struct command *cmd;
struct strbuf err = STRBUF_INIT;
@@ -1148,6 +1151,50 @@ static void execute_commands_loop(struct command *commands,
strbuf_release(&err);
}
+static void execute_commands_atomic(struct command *commands,
+ struct shallow_info *si)
+{
+ struct command *cmd;
+ struct strbuf err = STRBUF_INIT;
+ const char *reported_error = "atomic push failure";
+
+ transaction = ref_transaction_begin(&err);
+ if (!transaction) {
+ rp_error("%s", err.buf);
+ strbuf_reset(&err);
+ reported_error = "transaction failed to start";
+ goto failure;
+ }
+
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ if (!should_process_cmd(cmd))
+ continue;
+
+ cmd->error_string = update(cmd, si);
+
+ if (cmd->error_string)
+ goto failure;
+ }
+
+ if (ref_transaction_commit(transaction, &err)) {
+ rp_error("%s", err.buf);
+ reported_error = "atomic transaction failed";
+ goto failure;
+ }
+
+ ref_transaction_free(transaction);
+ strbuf_release(&err);
+ return;
+
+failure:
+ for (cmd = commands; cmd; cmd = cmd->next)
+ if (!cmd->error_string)
+ cmd->error_string = reported_error;
+
+ ref_transaction_free(transaction);
+ strbuf_release(&err);
+}
+
static struct command **queue_command(struct command **tail,
const char *line,
int linelen)
--
2.2.1.62.g3f15098
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCHv7 7/9] receive-pack.c: enable atomic push protocol support
2014-12-29 21:35 [PATCHv7 0/9] atomic pushes Stefan Beller
` (5 preceding siblings ...)
2014-12-29 21:35 ` [PATCHv7 6/9] receive-pack.c: add execute_commands_atomic function Stefan Beller
@ 2014-12-29 21:35 ` Stefan Beller
2014-12-29 21:35 ` [PATCHv7 8/9] push.c: add an --atomic argument Stefan Beller
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Stefan Beller @ 2014-12-29 21:35 UTC (permalink / raw)
To: git; +Cc: ronniesahlberg, mhagger, jrnieder, gitster, Stefan Beller
This enables the atomic protocol option as implemented in the previous
patches.
Signed-off-by: Stefan Beller <sbeller@google.com>
---
Notes:
v7:
* new with v7 of the patch series.
* this was part of the first patch in the series, moved back here
for bisectability
builtin/receive-pack.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 7e3bbe6..6da6975 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -173,7 +173,8 @@ static void show_ref(const char *path, const unsigned char *sha1)
struct strbuf cap = STRBUF_INIT;
strbuf_addstr(&cap,
- "report-status delete-refs side-band-64k quiet");
+ "report-status delete-refs side-band-64k quiet "
+ "atomic");
if (prefer_ofs_delta)
strbuf_addstr(&cap, " ofs-delta");
if (push_cert_nonce)
--
2.2.1.62.g3f15098
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCHv7 8/9] push.c: add an --atomic argument
2014-12-29 21:35 [PATCHv7 0/9] atomic pushes Stefan Beller
` (6 preceding siblings ...)
2014-12-29 21:35 ` [PATCHv7 7/9] receive-pack.c: enable atomic push protocol support Stefan Beller
@ 2014-12-29 21:35 ` Stefan Beller
2014-12-29 21:35 ` [PATCHv7 9/9] t5543-atomic-push.sh: add basic tests for atomic pushes Stefan Beller
2014-12-29 21:37 ` [PATCHv7 0/9] " Stefan Beller
9 siblings, 0 replies; 11+ messages in thread
From: Stefan Beller @ 2014-12-29 21:35 UTC (permalink / raw)
To: git
Cc: ronniesahlberg, mhagger, jrnieder, gitster, Ronnie Sahlberg,
Stefan Beller
From: Ronnie Sahlberg <sahlberg@google.com>
Add a command line argument to the git push command to request atomic
pushes.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
---
Notes:
Changes v1 -> v2
It's --atomic now! (dropping the -push)
v2->v3:
* s/atomic-push/atomic/
* s/the an/an/
* no serverside, but just remote instead
* TRANSPORT_PUSH_ATOMIC instead of TRANSPORT_ATOMIC_PUSH
skipped v4 v5
v6:
- OPT_BIT(0, "atomic", &flags, N_("use an atomic transaction remote"),
+ OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"),
v7:
Use OPT_BOOL instead of OPT_BIT.
This allows for --no-atomic option on the command line.
Documentation/git-push.txt | 7 ++++++-
builtin/push.c | 5 +++++
transport.c | 1 +
transport.h | 1 +
4 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 21b3f29..4764fcf 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -9,7 +9,7 @@ git-push - Update remote refs along with associated objects
SYNOPSIS
--------
[verse]
-'git push' [--all | --mirror | --tags] [--follow-tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
+'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
[--repo=<repository>] [-f | --force] [--prune] [-v | --verbose]
[-u | --set-upstream] [--signed]
[--force-with-lease[=<refname>[:<expect>]]]
@@ -136,6 +136,11 @@ already exists on the remote side.
logged. See linkgit:git-receive-pack[1] for the details
on the receiving end.
+--[no-]atomic::
+ Use an atomic transaction on the remote side if available.
+ Either all refs are updated, or on error, no refs are updated.
+ If the server does not support atomic pushes the push will fail.
+
--receive-pack=<git-receive-pack>::
--exec=<git-receive-pack>::
Path to the 'git-receive-pack' program on the remote
diff --git a/builtin/push.c b/builtin/push.c
index a076b19..8f1d945 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -487,6 +487,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
int flags = 0;
int tags = 0;
int rc;
+ int atomic = 0;
const char *repo = NULL; /* default repository */
struct option options[] = {
OPT__VERBOSITY(&verbosity),
@@ -518,6 +519,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
OPT_BIT(0, "follow-tags", &flags, N_("push missing but relevant tags"),
TRANSPORT_PUSH_FOLLOW_TAGS),
OPT_BIT(0, "signed", &flags, N_("GPG sign the push"), TRANSPORT_PUSH_CERT),
+ OPT_BOOL(0, "atomic", &atomic, N_("request atomic transaction on remote side")),
OPT_END()
};
@@ -533,6 +535,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
if (tags)
add_refspec("refs/tags/*");
+ if (atomic)
+ flags |= TRANSPORT_PUSH_ATOMIC;
+
if (argc > 0) {
repo = argv[0];
set_refspecs(argv + 1, argc - 1, repo);
diff --git a/transport.c b/transport.c
index c67feee..1373152 100644
--- a/transport.c
+++ b/transport.c
@@ -830,6 +830,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
args.push_cert = !!(flags & TRANSPORT_PUSH_CERT);
+ args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);
args.url = transport->url;
ret = send_pack(&args, data->fd, data->conn, remote_refs,
diff --git a/transport.h b/transport.h
index 3e0091e..18d2cf8 100644
--- a/transport.h
+++ b/transport.h
@@ -125,6 +125,7 @@ struct transport {
#define TRANSPORT_PUSH_NO_HOOK 512
#define TRANSPORT_PUSH_FOLLOW_TAGS 1024
#define TRANSPORT_PUSH_CERT 2048
+#define TRANSPORT_PUSH_ATOMIC 4096
#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
#define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)
--
2.2.1.62.g3f15098
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCHv7 9/9] t5543-atomic-push.sh: add basic tests for atomic pushes
2014-12-29 21:35 [PATCHv7 0/9] atomic pushes Stefan Beller
` (7 preceding siblings ...)
2014-12-29 21:35 ` [PATCHv7 8/9] push.c: add an --atomic argument Stefan Beller
@ 2014-12-29 21:35 ` Stefan Beller
2014-12-29 21:37 ` [PATCHv7 0/9] " Stefan Beller
9 siblings, 0 replies; 11+ messages in thread
From: Stefan Beller @ 2014-12-29 21:35 UTC (permalink / raw)
To: git; +Cc: ronniesahlberg, mhagger, jrnieder, gitster, Stefan Beller
This adds tests for the atomic push option.
The first four tests check if the atomic option works in
good conditions and the last three patches check if the atomic
option prevents any change to be pushed if just one ref cannot
be updated.
Signed-off-by: Stefan Beller <sbeller@google.com>
---
Notes:
v7: no changes
v6: the same as v2, so I can resend the whole series as v6
v3 v4 v5 were skipped
Changes v1 -> v2:
> Please drop unused comments; they are distracting.
ok
> It is not wrong per-se, but haven't you already tested in
> combination with --mirror in the previous test?
I fixed the previous tests, so that there is no --mirror
and --atomic together. There is still a first --mirror push
for setup and a second with --atomic <branchnames> though
> check_branches upstream master HEAD@{2} second HEAD~
A similar function test_ref_upstream is introduced.
> What's the value of this test? Isn't it a non-fast-forward check
> you already tested in the previous one?
I messed up there. Originally I wanted to test the 2 different
stages of rejection. A non-fast-forward check is done locally and
we don't even try pushing. But I also want to test if we locally
thing all is good, but the server refuses a ref to update.
This is now done with the last test named 'atomic push obeys
update hook preventing a branch to be pushed'. And that still fails.
I'll investigate that, while still sending out the series for another
review though.
* Redone the test helper, there is test_ref_upstream now.
This tests explicitely for SHA1 values of the ref.
(It's needed in the last test for example. The git push fails,
but still modifies the ref :/ )
* checked all && chains and repaired them
* sometimes make use of git -C <workdir>
Notes v1:
Originally Ronnie had a similar patch prepared. But as I added
more tests and cleaned up the existing tests (e.g. use test_commit
instead of "echo one >file && gitadd file && git commit -a -m 'one'",
removal of dead code), the file has changed so much that I'd rather
take ownership.
t/t5543-atomic-push.sh | 178 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 178 insertions(+)
create mode 100755 t/t5543-atomic-push.sh
diff --git a/t/t5543-atomic-push.sh b/t/t5543-atomic-push.sh
new file mode 100755
index 0000000..b81a542
--- /dev/null
+++ b/t/t5543-atomic-push.sh
@@ -0,0 +1,178 @@
+#!/bin/sh
+
+test_description='pushing to a repository using the atomic push option'
+
+. ./test-lib.sh
+
+mk_repo_pair () {
+ rm -rf workbench upstream &&
+ test_create_repo upstream &&
+ test_create_repo workbench &&
+ (
+ cd upstream &&
+ git config receive.denyCurrentBranch warn
+ ) &&
+ (
+ cd workbench &&
+ git remote add up ../upstream
+ )
+}
+
+# Compare the ref ($1) in upstream with a ref value from workbench ($2)
+# i.e. test_refs second HEAD@{2}
+test_refs () {
+ test $# = 2 &&
+ git -C upstream rev-parse --verify "$1" >expect &&
+ git -C workbench rev-parse --verify "$2" >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'atomic push works for a single branch' '
+ mk_repo_pair &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ git push --atomic up master
+ ) &&
+ test_refs master master
+'
+
+test_expect_success 'atomic push works for two branches' '
+ mk_repo_pair &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git branch second &&
+ git push --mirror up &&
+ test_commit two &&
+ git checkout second &&
+ test_commit three &&
+ git push --atomic up master second
+ ) &&
+ test_refs master master &&
+ test_refs second second
+'
+
+test_expect_success 'atomic push works in combination with --mirror' '
+ mk_repo_pair &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git checkout -b second &&
+ test_commit two &&
+ git push --atomic --mirror up
+ ) &&
+ test_refs master master &&
+ test_refs second second
+'
+
+test_expect_success 'atomic push works in combination with --force' '
+ mk_repo_pair &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git branch second master &&
+ test_commit two_a &&
+ git checkout second &&
+ test_commit two_b &&
+ test_commit three_b &&
+ test_commit four &&
+ git push --mirror up &&
+ # The actual test is below
+ git checkout master &&
+ test_commit three_a &&
+ git checkout second &&
+ git reset --hard HEAD^ &&
+ git push --force --atomic up master second
+ ) &&
+ test_refs master master &&
+ test_refs second second
+'
+
+# set up two branches where master can be pushed but second can not
+# (non-fast-forward). Since second can not be pushed the whole operation
+# will fail and leave master untouched.
+test_expect_success 'atomic push fails if one branch fails' '
+ mk_repo_pair &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git checkout -b second master &&
+ test_commit two &&
+ test_commit three &&
+ test_commit four &&
+ git push --mirror up &&
+ git reset --hard HEAD~2 &&
+ test_commit five &&
+ git checkout master &&
+ test_commit six &&
+ test_must_fail git push --atomic --all up
+ ) &&
+ test_refs master HEAD@{7} &&
+ test_refs second HEAD@{4}
+'
+
+test_expect_success 'atomic push fails if one tag fails remotely' '
+ # prepare the repo
+ mk_repo_pair &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git checkout -b second master &&
+ test_commit two &&
+ git push --mirror up
+ ) &&
+ # a third party modifies the server side:
+ (
+ cd upstream &&
+ git checkout second &&
+ git tag test_tag second
+ ) &&
+ # see if we can now push both branches.
+ (
+ cd workbench &&
+ git checkout master &&
+ test_commit three &&
+ git checkout second &&
+ test_commit four &&
+ git tag test_tag &&
+ test_must_fail git push --tags --atomic up master second
+ ) &&
+ test_refs master HEAD@{3} &&
+ test_refs second HEAD@{1}
+'
+
+test_expect_success 'atomic push obeys update hook preventing a branch to be pushed' '
+ mk_repo_pair &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git checkout -b second master &&
+ test_commit two &&
+ git push --mirror up
+ ) &&
+ (
+ cd upstream &&
+ HOOKDIR="$(git rev-parse --git-dir)/hooks" &&
+ HOOK="$HOOKDIR/update" &&
+ mkdir -p "$HOOKDIR" &&
+ write_script "$HOOK" <<-\EOF
+ # only allow update to master from now on
+ test "$1" = "refs/heads/master"
+ EOF
+ ) &&
+ (
+ cd workbench &&
+ git checkout master &&
+ test_commit three &&
+ git checkout second &&
+ test_commit four &&
+ test_must_fail git push --atomic up master second
+ ) &&
+ test_refs master HEAD@{3} &&
+ test_refs second HEAD@{1}
+'
+
+test_done
--
2.2.1.62.g3f15098
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCHv7 0/9] atomic pushes
2014-12-29 21:35 [PATCHv7 0/9] atomic pushes Stefan Beller
` (8 preceding siblings ...)
2014-12-29 21:35 ` [PATCHv7 9/9] t5543-atomic-push.sh: add basic tests for atomic pushes Stefan Beller
@ 2014-12-29 21:37 ` Stefan Beller
9 siblings, 0 replies; 11+ messages in thread
From: Stefan Beller @ 2014-12-29 21:37 UTC (permalink / raw)
To: git@vger.kernel.org
Cc: ronnie sahlberg, Michael Haggerty, Jonathan Nieder,
Junio C Hamano, Stefan Beller
please ignore this series. I'm sorry for the noise.
^ permalink raw reply [flat|nested] 11+ messages in thread