1471
* with the remote-tracking branch to find the value
1472
* to expect, but we did not have such a tracking
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.
1478
*/
1479
if (ref->expect_old_sha1) {
1480
if (!oideq(&ref->old_oid, &ref->old_oid_expect))
1481
reject_reason = REF_STATUS_REJECT_STALE;
1482
+ else if (ref->check_reachable && ref->unreachable)
1483
+ reject_reason =
1484
+ REF_STATUS_REJECT_REMOTE_UPDATED;
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
+ */
1491
force_ref_update = 1;
1492
}
1493
2262
2263
/*
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[].
2268
* If we cannot do so, return negative to signal an error.
2269
*/
2270
static int remote_tracking(struct remote *remote, const char *refname,
2259
- struct object_id *oid)
2271
+ struct object_id *oid, char **dst_refname)
2272
{
2273
char *dst;
2274
2277
return -1; /* no tracking ref for refname at remote */
2278
if (read_ref(dst, oid))
2279
return -1; /* we know what the tracking ref is but we cannot read it */
2280
+
2281
+ *dst_refname = dst;
2282
return 0;
2283
}
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
+
2424
static void apply_cas(struct push_cas_option *cas,
2425
struct remote *remote,
2426
struct ref *ref)
2435
ref->expect_old_sha1 = 1;
2436
if (!entry->use_tracking)
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))
2441
oidclr(&ref->old_oid_expect);
2442
+ else
2443
+ ref->check_reachable = cas->use_force_if_includes;
2444
return;
2445
}
2446
2449
return;
2450
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))
2455
oidclr(&ref->old_oid_expect);
2456
+ else
2457
+ ref->check_reachable = cas->use_force_if_includes;
2458
}
2459
2460
void apply_push_cas(struct push_cas_option *cas,
2462
struct ref *remote_refs)
2463
{
2464
struct ref *ref;
2304
- for (ref = remote_refs; ref; ref = ref->next)
2465
+ for (ref = remote_refs; ref; ref = ref->next) {
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
+ }
2476
}