@@ -428,6 +428,58 @@ namespace litecore {
428428 } else {
429429 // Compare it with the current document revision:
430430 order = VersionVecWithLegacy::compare (newVers, curVers);
431+
432+ // CBL-8277: Handle the case where revision history may appear broken.
433+ //
434+ // Concrete scenario for a single document ID:
435+ // 1. Local DB deletes the doc.
436+ // 2. Local pushes the tombstone to a remote SGW.
437+ // 3. In the target CB database, a new doc is created with the same ID.
438+ // (In CBS, a deleted doc is effectively purged, so the ID is free
439+ // for reuse by an unrelated document.)
440+ // 4. Local DB performs a pull.
441+ //
442+ // At step 4 the BLIP message carries a document that starts at a new
443+ // version with no history. In general, this conflicts with whatever
444+ // revision Local currently holds, and it is indistinguishable from a
445+ // genuinely new document authored elsewhere (i.e. one that never went
446+ // through the sequence above). The following analysis shows how we can
447+ // recognize the sequence above and treat `newVers` as a newer revision
448+ // rather than a conflict.
449+ //
450+ // Proposition:
451+ // For a given document at a given remote, the set of authors in the
452+ // Version Vector may only grow.
453+ //
454+ // Applying the proposition here:
455+ // - `remote` : the remote index that `newVers` came from.
456+ // - `rev` : the latest revision we know we have shared with remote
457+ // `remote`.
458+ // - `newVers` : the revision arriving from `remote` via BLIP.
459+ //
460+ // Because the remote has already seen `rev`, its Version Vector must
461+ // contain `rev`'s author. Our general VV comparison relies on this. If
462+ // that author is now missing from `newVers`, it must have been dropped.
463+ // From this we can draw the following conservative conclusion:
464+ //
465+ // `newVers` is "newer" than any version that shares an author with
466+ // `rev` at the same or older age (lower or equal logical clock).
467+ //
468+ // In general, we further posit that is_newwer_or_same is transitive.
469+ // From the fact that `rev` has been seen by the remote,
470+ // `newVers` is_newer_or_same `rev`
471+ // Therefore, we can deduce that `newVers` is_newer_or_same `curVers`
472+ // if `rev` is_newer_same `curVers`. Since `same` can be determined by the
473+ // general comparison, we are only concerned here with is_newer.
474+
475+ if ( order == kConflicting && remote != RemoteID::Local ) {
476+ if ( optional<Revision> rev = _doc.loadRemoteRevision (remote); rev ) {
477+ VersionVecWithLegacy landmark{rev->revID };
478+ versionOrder orderWithLandmark = VersionVecWithLegacy::compare (landmark, curVers);
479+ if ( orderWithLandmark == kSame || orderWithLandmark == kNewer ) order = kNewer ;
480+ }
481+ }
482+
431483 logPutExisting (curVers, newVers, order, remote);
432484
433485 // Check for no-op or conflict:
0 commit comments