@@ -1471,12 +1471,23 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
1471
1471
* with the remote-tracking branch to find the value
1472
1472
* to expect, but we did not have such a tracking
1473
1473
* branch.
1474
+ *
1475
+ * If the tip of the remote-tracking ref is unreachable
1476
+ * from any reflog entry of its local ref indicating a
1477
+ * possible update since checkout; reject the push.
1474
1478
*/
1475
1479
if (ref -> expect_old_sha1 ) {
1476
1480
if (!oideq (& ref -> old_oid , & ref -> old_oid_expect ))
1477
1481
reject_reason = REF_STATUS_REJECT_STALE ;
1482
+ else if (ref -> check_reachable && ref -> unreachable )
1483
+ reject_reason =
1484
+ REF_STATUS_REJECT_REMOTE_UPDATED ;
1478
1485
else
1479
- /* If the ref isn't stale then force the update. */
1486
+ /*
1487
+ * If the ref isn't stale, and is reachable
1488
+ * from from one of the reflog entries of
1489
+ * the local branch, force the update.
1490
+ */
1480
1491
force_ref_update = 1 ;
1481
1492
}
1482
1493
@@ -2251,12 +2262,13 @@ int is_empty_cas(const struct push_cas_option *cas)
2251
2262
2252
2263
/*
2253
2264
* Look at remote.fetch refspec and see if we have a remote
2254
- * tracking branch for the refname there. Fill its current
2255
- * value in sha1[].
2265
+ * tracking branch for the refname there. Fill the name of
2266
+ * the remote-tracking branch in *dst_refname, and the name
2267
+ * of the commit object at its tip in oid[].
2256
2268
* If we cannot do so, return negative to signal an error.
2257
2269
*/
2258
2270
static int remote_tracking (struct remote * remote , const char * refname ,
2259
- struct object_id * oid )
2271
+ struct object_id * oid , char * * dst_refname )
2260
2272
{
2261
2273
char * dst ;
2262
2274
@@ -2265,9 +2277,150 @@ static int remote_tracking(struct remote *remote, const char *refname,
2265
2277
return -1 ; /* no tracking ref for refname at remote */
2266
2278
if (read_ref (dst , oid ))
2267
2279
return -1 ; /* we know what the tracking ref is but we cannot read it */
2280
+
2281
+ * dst_refname = dst ;
2268
2282
return 0 ;
2269
2283
}
2270
2284
2285
+ /*
2286
+ * The struct "reflog_commit_array" and related helper functions
2287
+ * are used for collecting commits into an array during reflog
2288
+ * traversals in "check_and_collect_until()".
2289
+ */
2290
+ struct reflog_commit_array {
2291
+ struct commit * * item ;
2292
+ size_t nr , alloc ;
2293
+ };
2294
+
2295
+ #define REFLOG_COMMIT_ARRAY_INIT { NULL, 0, 0 }
2296
+
2297
+ /* Append a commit to the array. */
2298
+ static void append_commit (struct reflog_commit_array * arr ,
2299
+ struct commit * commit )
2300
+ {
2301
+ ALLOC_GROW (arr -> item , arr -> nr + 1 , arr -> alloc );
2302
+ arr -> item [arr -> nr ++ ] = commit ;
2303
+ }
2304
+
2305
+ /* Free and reset the array. */
2306
+ static void free_commit_array (struct reflog_commit_array * arr )
2307
+ {
2308
+ FREE_AND_NULL (arr -> item );
2309
+ arr -> nr = arr -> alloc = 0 ;
2310
+ }
2311
+
2312
+ struct check_and_collect_until_cb_data {
2313
+ struct commit * remote_commit ;
2314
+ struct reflog_commit_array * local_commits ;
2315
+ timestamp_t remote_reflog_timestamp ;
2316
+ };
2317
+
2318
+ /* Get the timestamp of the latest entry. */
2319
+ static int peek_reflog (struct object_id * o_oid , struct object_id * n_oid ,
2320
+ const char * ident , timestamp_t timestamp ,
2321
+ int tz , const char * message , void * cb_data )
2322
+ {
2323
+ timestamp_t * ts = cb_data ;
2324
+ * ts = timestamp ;
2325
+ return 1 ;
2326
+ }
2327
+
2328
+ static int check_and_collect_until (struct object_id * o_oid ,
2329
+ struct object_id * n_oid ,
2330
+ const char * ident , timestamp_t timestamp ,
2331
+ int tz , const char * message , void * cb_data )
2332
+ {
2333
+ struct commit * commit ;
2334
+ struct check_and_collect_until_cb_data * cb = cb_data ;
2335
+
2336
+ /* An entry was found. */
2337
+ if (oideq (n_oid , & cb -> remote_commit -> object .oid ))
2338
+ return 1 ;
2339
+
2340
+ if ((commit = lookup_commit_reference (the_repository , n_oid )))
2341
+ append_commit (cb -> local_commits , commit );
2342
+
2343
+ /*
2344
+ * If the reflog entry timestamp is older than the remote ref's
2345
+ * latest reflog entry, there is no need to check or collect
2346
+ * entries older than this one.
2347
+ */
2348
+ if (timestamp < cb -> remote_reflog_timestamp )
2349
+ return -1 ;
2350
+
2351
+ return 0 ;
2352
+ }
2353
+
2354
+ #define MERGE_BASES_BATCH_SIZE 8
2355
+
2356
+ /*
2357
+ * Iterate through the reflog of the local ref to check if there is an entry
2358
+ * for the given remote-tracking ref; runs until the timestamp of an entry is
2359
+ * older than latest timestamp of remote-tracking ref's reflog. Any commits
2360
+ * are that seen along the way are collected into an array to check if the
2361
+ * remote-tracking ref is reachable from any of them.
2362
+ */
2363
+ static int is_reachable_in_reflog (const char * local , const struct ref * remote )
2364
+ {
2365
+ timestamp_t date ;
2366
+ struct commit * commit ;
2367
+ struct commit * * chunk ;
2368
+ struct check_and_collect_until_cb_data cb ;
2369
+ struct reflog_commit_array arr = REFLOG_COMMIT_ARRAY_INIT ;
2370
+ size_t size = 0 ;
2371
+ int ret = 0 ;
2372
+
2373
+ commit = lookup_commit_reference (the_repository , & remote -> old_oid );
2374
+ if (!commit )
2375
+ goto cleanup_return ;
2376
+
2377
+ /*
2378
+ * Get the timestamp from the latest entry
2379
+ * of the remote-tracking ref's reflog.
2380
+ */
2381
+ for_each_reflog_ent_reverse (remote -> tracking_ref , peek_reflog , & date );
2382
+
2383
+ cb .remote_commit = commit ;
2384
+ cb .local_commits = & arr ;
2385
+ cb .remote_reflog_timestamp = date ;
2386
+ ret = for_each_reflog_ent_reverse (local , check_and_collect_until , & cb );
2387
+
2388
+ /* We found an entry in the reflog. */
2389
+ if (ret > 0 )
2390
+ goto cleanup_return ;
2391
+
2392
+ /*
2393
+ * Check if the remote commit is reachable from any
2394
+ * of the commits in the collected array, in batches.
2395
+ */
2396
+ for (chunk = arr .item ; chunk < arr .item + arr .nr ; chunk += size ) {
2397
+ size = arr .item + arr .nr - chunk ;
2398
+ if (MERGE_BASES_BATCH_SIZE < size )
2399
+ size = MERGE_BASES_BATCH_SIZE ;
2400
+
2401
+ if ((ret = in_merge_bases_many (commit , size , chunk )))
2402
+ break ;
2403
+ }
2404
+
2405
+ cleanup_return :
2406
+ free_commit_array (& arr );
2407
+ return ret ;
2408
+ }
2409
+
2410
+ /*
2411
+ * Check for reachability of a remote-tracking
2412
+ * ref in the reflog entries of its local ref.
2413
+ */
2414
+ static void check_if_includes_upstream (struct ref * remote )
2415
+ {
2416
+ struct ref * local = get_local_ref (remote -> name );
2417
+ if (!local )
2418
+ return ;
2419
+
2420
+ if (is_reachable_in_reflog (local -> name , remote ) <= 0 )
2421
+ remote -> unreachable = 1 ;
2422
+ }
2423
+
2271
2424
static void apply_cas (struct push_cas_option * cas ,
2272
2425
struct remote * remote ,
2273
2426
struct ref * ref )
@@ -2282,8 +2435,12 @@ static void apply_cas(struct push_cas_option *cas,
2282
2435
ref -> expect_old_sha1 = 1 ;
2283
2436
if (!entry -> use_tracking )
2284
2437
oidcpy (& ref -> old_oid_expect , & entry -> expect );
2285
- else if (remote_tracking (remote , ref -> name , & ref -> old_oid_expect ))
2438
+ else if (remote_tracking (remote , ref -> name ,
2439
+ & ref -> old_oid_expect ,
2440
+ & ref -> tracking_ref ))
2286
2441
oidclr (& ref -> old_oid_expect );
2442
+ else
2443
+ ref -> check_reachable = cas -> use_force_if_includes ;
2287
2444
return ;
2288
2445
}
2289
2446
@@ -2292,15 +2449,28 @@ static void apply_cas(struct push_cas_option *cas,
2292
2449
return ;
2293
2450
2294
2451
ref -> expect_old_sha1 = 1 ;
2295
- if (remote_tracking (remote , ref -> name , & ref -> old_oid_expect ))
2452
+ if (remote_tracking (remote , ref -> name ,
2453
+ & ref -> old_oid_expect ,
2454
+ & ref -> tracking_ref ))
2296
2455
oidclr (& ref -> old_oid_expect );
2456
+ else
2457
+ ref -> check_reachable = cas -> use_force_if_includes ;
2297
2458
}
2298
2459
2299
2460
void apply_push_cas (struct push_cas_option * cas ,
2300
2461
struct remote * remote ,
2301
2462
struct ref * remote_refs )
2302
2463
{
2303
2464
struct ref * ref ;
2304
- for (ref = remote_refs ; ref ; ref = ref -> next )
2465
+ for (ref = remote_refs ; ref ; ref = ref -> next ) {
2305
2466
apply_cas (cas , remote , ref );
2467
+
2468
+ /*
2469
+ * If "compare-and-swap" is in "use_tracking[_for_rest]"
2470
+ * mode, and if "--force-if-includes" was specified, run
2471
+ * the check.
2472
+ */
2473
+ if (ref -> check_reachable )
2474
+ check_if_includes_upstream (ref );
2475
+ }
2306
2476
}
0 commit comments