Skip to content

Commit 63de35d

Browse files
committed
Merge branch 'jc/allow-lazy-cas' into pu
Because "git push --force-with-lease[=<ref>]" that relies on the stability of remote-tracking branches is unsafe when something fetches into the repository behind user's back, it is now disabled by default. A new configuration variable can be used to enable it by users who know what they are doing. This would pave the way to possibly turn `--force` into `--force-with-lease`. * jc/allow-lazy-cas: push: disable lazy --force-with-lease by default
2 parents ff6351e + f2f60a5 commit 63de35d

File tree

9 files changed

+56
-5
lines changed

9 files changed

+56
-5
lines changed

Documentation/config.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2623,6 +2623,11 @@ new default).
26232623

26242624
--
26252625

2626+
push.allowLazyForceWithLease::
2627+
If set to true, allow the `--force-with-lease` option
2628+
without the expected object name (i.e. expecting the objects
2629+
at the tip of corresponding remote-tracking branches).
2630+
26262631
push.followTags::
26272632
If set to true enable `--follow-tags` option by default. You
26282633
may override this configuration at time of push by specifying

Documentation/git-push.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ Note that all forms other than `--force-with-lease=<refname>:<expect>`
214214
that specifies the expected current value of the ref explicitly are
215215
still experimental and their semantics may change as we gain experience
216216
with this feature.
217+
These experimental forms are disabled by default due to safety concerns.
218+
Read the note on safety below, and if you still think you are not affected,
219+
enable it with the `push.allowLazyForceWithLease` configuration variable
220+
(cf. linkgit:git-config[1]).
217221
+
218222
"--no-force-with-lease" will cancel all the previous --force-with-lease on the
219223
command line.

builtin/send-pack.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ static void print_helper_status(struct ref *ref)
6868
msg = "stale info";
6969
break;
7070

71+
case REF_STATUS_REJECT_LAZY_CAS:
72+
res = "error";
73+
msg = "lazy force-with-error";
74+
break;
75+
7176
case REF_STATUS_REJECT_ALREADY_EXISTS:
7277
res = "error";
7378
msg = "already exists";

remote.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,9 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
15371537
int force_update)
15381538
{
15391539
struct ref *ref;
1540+
int allow_lazy_cas = 0;
15401541

1542+
git_config_get_bool("push.allowLazyForceWithLease", &allow_lazy_cas);
15411543
for (ref = remote_refs; ref; ref = ref->next) {
15421544
int force_ref_update = ref->force || force_update;
15431545
int reject_reason = 0;
@@ -1564,7 +1566,9 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
15641566
* branch.
15651567
*/
15661568
if (ref->expect_old_sha1) {
1567-
if (oidcmp(&ref->old_oid, &ref->old_oid_expect))
1569+
if (!allow_lazy_cas && ref->lazy_cas)
1570+
reject_reason = REF_STATUS_REJECT_LAZY_CAS;
1571+
else if (oidcmp(&ref->old_oid, &ref->old_oid_expect))
15681572
reject_reason = REF_STATUS_REJECT_STALE;
15691573
else
15701574
/* If the ref isn't stale then force the update. */
@@ -2361,10 +2365,13 @@ static void apply_cas(struct push_cas_option *cas,
23612365
if (!refname_match(entry->refname, ref->name))
23622366
continue;
23632367
ref->expect_old_sha1 = 1;
2364-
if (!entry->use_tracking)
2368+
if (!entry->use_tracking) {
23652369
oidcpy(&ref->old_oid_expect, &entry->expect);
2366-
else if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
2367-
oidclr(&ref->old_oid_expect);
2370+
} else {
2371+
ref->lazy_cas = 1;
2372+
if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
2373+
oidclr(&ref->old_oid_expect);
2374+
}
23682375
return;
23692376
}
23702377

@@ -2373,6 +2380,7 @@ static void apply_cas(struct push_cas_option *cas,
23732380
return;
23742381

23752382
ref->expect_old_sha1 = 1;
2383+
ref->lazy_cas = 1;
23762384
if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
23772385
oidclr(&ref->old_oid_expect);
23782386
}

remote.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ struct ref {
8989
force:1,
9090
forced_update:1,
9191
expect_old_sha1:1,
92+
lazy_cas:1,
9293
deletion:1;
9394

9495
enum {
@@ -118,6 +119,7 @@ struct ref {
118119
REF_STATUS_REJECT_FETCH_FIRST,
119120
REF_STATUS_REJECT_NEEDS_FORCE,
120121
REF_STATUS_REJECT_STALE,
122+
REF_STATUS_REJECT_LAZY_CAS,
121123
REF_STATUS_REJECT_SHALLOW,
122124
REF_STATUS_UPTODATE,
123125
REF_STATUS_REMOTE_REJECT,

send-pack.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ static int check_to_send_update(const struct ref *ref, const struct send_pack_ar
248248
case REF_STATUS_REJECT_FETCH_FIRST:
249249
case REF_STATUS_REJECT_NEEDS_FORCE:
250250
case REF_STATUS_REJECT_STALE:
251+
case REF_STATUS_REJECT_LAZY_CAS:
251252
case REF_STATUS_REJECT_NODELETE:
252253
return CHECK_REF_STATUS_REJECTED;
253254
case REF_STATUS_UPTODATE:

t/t5533-push-cas.sh

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ test_expect_success setup '
1717
# create template repository
1818
test_commit A &&
1919
test_commit B &&
20-
test_commit C
20+
test_commit C &&
21+
git config --global push.allowLazyForceWithLease true
2122
'
2223

2324
test_expect_success 'push to update (protected)' '
@@ -91,6 +92,7 @@ test_expect_success 'push to update (allowed)' '
9192
(
9293
cd dst &&
9394
test_commit D &&
95+
git config push.allowLazyForceWithLease false &&
9496
git push --force-with-lease=master:master^ origin master
9597
) &&
9698
git ls-remote dst refs/heads/master >expect &&
@@ -103,6 +105,10 @@ test_expect_success 'push to update (allowed, tracking)' '
103105
(
104106
cd dst &&
105107
test_commit D &&
108+
git config push.allowLazyForceWithLease false &&
109+
test_must_fail git push --force-with-lease=master origin master 2>err &&
110+
grep "lazy force-with-lease" err &&
111+
git config --unset push.allowLazyForceWithLease &&
106112
git push --force-with-lease=master origin master 2>err &&
107113
! grep "forced update" err
108114
) &&
@@ -151,6 +157,10 @@ test_expect_success 'push to delete (allowed)' '
151157
setup_srcdst_basic &&
152158
(
153159
cd dst &&
160+
git config push.allowLazyForceWithLease false &&
161+
test_must_fail git push --force-with-lease=master origin :master 2>err &&
162+
grep "lazy force-with-lease" err &&
163+
git config --unset push.allowLazyForceWithLease &&
154164
git push --force-with-lease=master origin :master 2>err &&
155165
grep deleted err
156166
) &&
@@ -183,6 +193,9 @@ test_expect_success 'cover everything with default force-with-lease (allowed)' '
183193
(
184194
cd dst &&
185195
git fetch &&
196+
git config push.allowLazyForceWithLease false &&
197+
test_must_fail git push --force-with-lease origin master master:naster &&
198+
git config --unset push.allowLazyForceWithLease &&
186199
git push --force-with-lease origin master master:naster
187200
) &&
188201
git ls-remote dst refs/heads/master |
@@ -196,6 +209,9 @@ test_expect_success 'new branch covered by force-with-lease' '
196209
(
197210
cd dst &&
198211
git branch branch master &&
212+
git config push.allowLazyForceWithLease false &&
213+
test_must_fail git push --force-with-lease=branch origin branch &&
214+
git config --unset push.allowLazyForceWithLease &&
199215
git push --force-with-lease=branch origin branch
200216
) &&
201217
git ls-remote dst refs/heads/branch >expect &&

transport-helper.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,10 @@ static int push_update_ref_status(struct strbuf *buf,
736736
status = REF_STATUS_REJECT_STALE;
737737
FREE_AND_NULL(msg);
738738
}
739+
else if (!strcmp(msg, "lazy force-with-error")) {
740+
status = REF_STATUS_REJECT_LAZY_CAS;
741+
FREE_AND_NULL(msg);
742+
}
739743
else if (!strcmp(msg, "forced update")) {
740744
forced = 1;
741745
FREE_AND_NULL(msg);
@@ -847,6 +851,7 @@ static int push_refs_with_push(struct transport *transport,
847851
switch (ref->status) {
848852
case REF_STATUS_REJECT_NONFASTFORWARD:
849853
case REF_STATUS_REJECT_STALE:
854+
case REF_STATUS_REJECT_LAZY_CAS:
850855
case REF_STATUS_REJECT_ALREADY_EXISTS:
851856
case REF_STATUS_UPTODATE:
852857
continue;

transport.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count,
418418
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
419419
"stale info", porcelain, summary_width);
420420
break;
421+
case REF_STATUS_REJECT_LAZY_CAS:
422+
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
423+
"lazy force-with-lease", porcelain, summary_width);
424+
break;
421425
case REF_STATUS_REJECT_SHALLOW:
422426
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
423427
"new shallow roots not allowed",
@@ -934,6 +938,7 @@ static int run_pre_push_hook(struct transport *transport,
934938
if (!r->peer_ref) continue;
935939
if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue;
936940
if (r->status == REF_STATUS_REJECT_STALE) continue;
941+
if (r->status == REF_STATUS_REJECT_LAZY_CAS) continue;
937942
if (r->status == REF_STATUS_UPTODATE) continue;
938943

939944
strbuf_reset(&buf);

0 commit comments

Comments
 (0)