Skip to content

Commit d45d771

Browse files
committed
Merge branch 'bc/smart-http-atomic-push'
The atomic push over smart HTTP transport did not work, which has been corrected. * bc/smart-http-atomic-push: remote-curl: pass on atomic capability to remote side
2 parents 22dd22d + 6f11942 commit d45d771

File tree

5 files changed

+62
-3
lines changed

5 files changed

+62
-3
lines changed

Documentation/gitremote-helpers.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,11 @@ set by Git if the remote helper has the 'option' capability.
509509
Indicate that only the objects wanted need to be fetched, not
510510
their dependents.
511511

512+
'option atomic' {'true'|'false'}::
513+
When pushing, request the remote server to update refs in a single atomic
514+
transaction. If successful, all refs will be updated, or none will. If the
515+
remote side does not support this capability, the push will fail.
516+
512517
SEE ALSO
513518
--------
514519
linkgit:git-remote[1]

remote-curl.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ struct options {
4040
push_cert : 2,
4141
deepen_relative : 1,
4242
from_promisor : 1,
43-
no_dependents : 1;
43+
no_dependents : 1,
44+
atomic : 1;
4445
};
4546
static struct options options;
4647
static struct string_list cas_options = STRING_LIST_INIT_DUP;
@@ -148,6 +149,14 @@ static int set_option(const char *name, const char *value)
148149
else
149150
return -1;
150151
return 0;
152+
} else if (!strcmp(name, "atomic")) {
153+
if (!strcmp(value, "true"))
154+
options.atomic = 1;
155+
else if (!strcmp(value, "false"))
156+
options.atomic = 0;
157+
else
158+
return -1;
159+
return 0;
151160
} else if (!strcmp(name, "push-option")) {
152161
if (*value != '"')
153162
string_list_append(&options.push_options, value);
@@ -1196,6 +1205,8 @@ static int push_git(struct discovery *heads, int nr_spec, const char **specs)
11961205
argv_array_push(&args, "--signed=yes");
11971206
else if (options.push_cert == SEND_PACK_PUSH_CERT_IF_ASKED)
11981207
argv_array_push(&args, "--signed=if-asked");
1208+
if (options.atomic)
1209+
argv_array_push(&args, "--atomic");
11991210
if (options.verbosity == 0)
12001211
argv_array_push(&args, "--quiet");
12011212
else if (options.verbosity > 1)

t/t5541-http-push-smart.sh

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,12 @@ test_expect_success 'push --atomic also prevents branch creation, reports collat
184184
test_config -C "$d" http.receivepack true &&
185185
up="$HTTPD_URL"/smart/atomic-branches.git &&
186186
187-
# Tell "$up" about two branches for now
187+
# Tell "$up" about three branches for now
188188
test_commit atomic1 &&
189189
test_commit atomic2 &&
190190
git branch collateral &&
191-
git push "$up" master collateral &&
191+
git branch other &&
192+
git push "$up" master collateral other &&
192193
193194
# collateral is a valid push, but should be failed by atomic push
194195
git checkout collateral &&
@@ -226,6 +227,41 @@ test_expect_success 'push --atomic also prevents branch creation, reports collat
226227
grep "^ ! .*rejected.* collateral -> collateral .*atomic push failed" output
227228
'
228229

230+
test_expect_success 'push --atomic fails on server-side errors' '
231+
# Use previously set up repository
232+
d=$HTTPD_DOCUMENT_ROOT_PATH/atomic-branches.git &&
233+
test_config -C "$d" http.receivepack true &&
234+
up="$HTTPD_URL"/smart/atomic-branches.git &&
235+
236+
# break ref updates for other on the remote site
237+
mkdir "$d/refs/heads/other.lock" &&
238+
239+
# add the new commit to other
240+
git branch -f other collateral &&
241+
242+
# --atomic should cause entire push to be rejected
243+
test_must_fail git push --atomic "$up" atomic other 2>output &&
244+
245+
# the new branch should not have been created upstream
246+
test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
247+
248+
# upstream should still reflect atomic2, the last thing we pushed
249+
# successfully
250+
git rev-parse atomic2 >expected &&
251+
# ...to other.
252+
git -C "$d" rev-parse refs/heads/other >actual &&
253+
test_cmp expected actual &&
254+
255+
# the new branch should not have been created upstream
256+
test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
257+
258+
# the failed refs should be indicated to the user
259+
grep "^ ! .*rejected.* other -> other .*atomic transaction failed" output &&
260+
261+
# the collateral failure refs should be indicated to the user
262+
grep "^ ! .*rejected.* atomic -> atomic .*atomic transaction failed" output
263+
'
264+
229265
test_expect_success 'push --all can push to empty repo' '
230266
d=$HTTPD_DOCUMENT_ROOT_PATH/empty-all.git &&
231267
git init --bare "$d" &&

transport-helper.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,10 @@ static void set_common_push_options(struct transport *transport,
854854
die(_("helper %s does not support --signed=if-asked"), name);
855855
}
856856

857+
if (flags & TRANSPORT_PUSH_ATOMIC)
858+
if (set_helper_option(transport, TRANS_OPT_ATOMIC, "true") != 0)
859+
die(_("helper %s does not support --atomic"), name);
860+
857861
if (flags & TRANSPORT_PUSH_OPTIONS) {
858862
struct string_list_item *item;
859863
for_each_string_list_item(item, transport->push_options)

transport.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ void transport_check_allowed(const char *type);
208208
/* Filter objects for partial clone and fetch */
209209
#define TRANS_OPT_LIST_OBJECTS_FILTER "filter"
210210

211+
/* Request atomic (all-or-nothing) updates when pushing */
212+
#define TRANS_OPT_ATOMIC "atomic"
213+
211214
/**
212215
* Returns 0 if the option was used, non-zero otherwise. Prints a
213216
* message to stderr if the option is not used.

0 commit comments

Comments
 (0)