Skip to content

Commit 1a27b78

Browse files
committed
Merge branch 'es/local-atomic-push-failure-with-http' into maint
"git push --atomic" that goes over the transport-helper (namely, the smart http transport) failed to prevent refs to be pushed when it can locally tell that one of the ref update will fail without having to consult the other end, which has been corrected. * es/local-atomic-push-failure-with-http: transport-helper: avoid var decl in for () loop control transport-helper: enforce atomic in push_refs_with_push
2 parents 0c47e8d + 2581ea3 commit 1a27b78

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

t/t5541-http-push-smart.sh

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,55 @@ test_expect_success 'push (chunked)' '
177177
test $HEAD = $(git rev-parse --verify HEAD))
178178
'
179179

180+
test_expect_success 'push --atomic also prevents branch creation, reports collateral' '
181+
# Setup upstream repo - empty for now
182+
d=$HTTPD_DOCUMENT_ROOT_PATH/atomic-branches.git &&
183+
git init --bare "$d" &&
184+
test_config -C "$d" http.receivepack true &&
185+
up="$HTTPD_URL"/smart/atomic-branches.git &&
186+
187+
# Tell "$up" about two branches for now
188+
test_commit atomic1 &&
189+
test_commit atomic2 &&
190+
git branch collateral &&
191+
git push "$up" master collateral &&
192+
193+
# collateral is a valid push, but should be failed by atomic push
194+
git checkout collateral &&
195+
test_commit collateral1 &&
196+
197+
# Make master incompatible with upstream to provoke atomic
198+
git checkout master &&
199+
git reset --hard HEAD^ &&
200+
201+
# Add a new branch which should be failed by atomic push. This is a
202+
# regression case.
203+
git branch atomic &&
204+
205+
# --atomic should cause entire push to be rejected
206+
test_must_fail git push --atomic "$up" master atomic collateral 2>output &&
207+
208+
# the new branch should not have been created upstream
209+
test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
210+
211+
# upstream should still reflect atomic2, the last thing we pushed
212+
# successfully
213+
git rev-parse atomic2 >expected &&
214+
# on master...
215+
git -C "$d" rev-parse refs/heads/master >actual &&
216+
test_cmp expected actual &&
217+
# ...and collateral.
218+
git -C "$d" rev-parse refs/heads/collateral >actual &&
219+
test_cmp expected actual &&
220+
221+
# the failed refs should be indicated to the user
222+
grep "^ ! .*rejected.* master -> master" output &&
223+
224+
# the collateral failure refs should be indicated to the user
225+
grep "^ ! .*rejected.* atomic -> atomic .*atomic push failed" output &&
226+
grep "^ ! .*rejected.* collateral -> collateral .*atomic push failed" output
227+
'
228+
180229
test_expect_success 'push --all can push to empty repo' '
181230
d=$HTTPD_DOCUMENT_ROOT_PATH/empty-all.git &&
182231
git init --bare "$d" &&

transport-helper.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,7 @@ static int push_refs_with_push(struct transport *transport,
853853
{
854854
int force_all = flags & TRANSPORT_PUSH_FORCE;
855855
int mirror = flags & TRANSPORT_PUSH_MIRROR;
856+
int atomic = flags & TRANSPORT_PUSH_ATOMIC;
856857
struct helper_data *data = transport->data;
857858
struct strbuf buf = STRBUF_INIT;
858859
struct ref *ref;
@@ -872,6 +873,11 @@ static int push_refs_with_push(struct transport *transport,
872873
case REF_STATUS_REJECT_NONFASTFORWARD:
873874
case REF_STATUS_REJECT_STALE:
874875
case REF_STATUS_REJECT_ALREADY_EXISTS:
876+
if (atomic) {
877+
string_list_clear(&cas_options, 0);
878+
return 0;
879+
} else
880+
continue;
875881
case REF_STATUS_UPTODATE:
876882
continue;
877883
default:

transport.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,20 @@ int transport_push(struct repository *r,
12261226
err = push_had_errors(remote_refs);
12271227
ret = push_ret | err;
12281228

1229+
if ((flags & TRANSPORT_PUSH_ATOMIC) && err) {
1230+
struct ref *it;
1231+
for (it = remote_refs; it; it = it->next)
1232+
switch (it->status) {
1233+
case REF_STATUS_NONE:
1234+
case REF_STATUS_UPTODATE:
1235+
case REF_STATUS_OK:
1236+
it->status = REF_STATUS_ATOMIC_PUSH_FAILED;
1237+
break;
1238+
default:
1239+
break;
1240+
}
1241+
}
1242+
12291243
if (!quiet || err)
12301244
transport_print_push_status(transport->url, remote_refs,
12311245
verbose | porcelain, porcelain,

0 commit comments

Comments
 (0)