@fern-api/replay 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -762,17 +762,57 @@ var ReplayDetector = class {
762
762
  }
763
763
  const isAncestor = await this.git.isAncestor(lastGen.commit_sha, "HEAD");
764
764
  if (!isAncestor) {
765
+ return this.detectPatchesViaMergeBase(lastGen, lock);
766
+ }
767
+ return this.detectPatchesInRange(lastGen.commit_sha, lastGen, lock);
768
+ }
769
+ /**
770
+ * FER-10201 — Non-linear-history detection via `merge-base(prevGen, HEAD)`.
771
+ *
772
+ * After a squash-merge of a Fern-bot PR, `lastGen.commit_sha` (the previous
773
+ * `[fern-generated]`) is orphaned but still in git's object database. The
774
+ * merge-base is the commit on main from which the bot's branch was created —
775
+ * a reachable ancestor of HEAD. Walking that range and classifying each
776
+ * commit via `isGenerationCommit` mirrors the linear path and eliminates
777
+ * the bug class where pipeline-driven changes (autoversion, replay,
778
+ * generation) get bundled as customer customizations.
779
+ *
780
+ * Falls through to the legacy composite path when no common ancestor exists
781
+ * (truly disjoint history — orphan branches with no shared root).
782
+ */
783
+ async detectPatchesViaMergeBase(lastGen, lock) {
784
+ let mergeBase = "";
785
+ try {
786
+ mergeBase = (await this.git.exec(["merge-base", lastGen.commit_sha, "HEAD"])).trim();
787
+ } catch {
788
+ }
789
+ if (!mergeBase) {
790
+ this.warnings.push(
791
+ `No common ancestor between previous generation ${lastGen.commit_sha.slice(0, 7)} and HEAD. Falling back to composite tree-diff detection.`
792
+ );
765
793
  return this.detectPatchesViaTreeDiff(
766
794
  lastGen,
767
795
  /* commitKnownMissing */
768
796
  false
769
797
  );
770
798
  }
799
+ return this.detectPatchesInRange(mergeBase, lastGen, lock);
800
+ }
801
+ /**
802
+ * FER-10201 — Walk `${rangeStart}..HEAD`, classify each commit, emit one
803
+ * `StoredPatch` per customer commit. Body shared by the linear path
804
+ * (rangeStart = lastGen.commit_sha) and the merge-base path.
805
+ *
806
+ * Patch `base_generation` is always `lastGen.commit_sha` — the
807
+ * lockfile-tracked reference the applicator uses to fetch base file
808
+ * content, regardless of where the walk actually started.
809
+ */
810
+ async detectPatchesInRange(rangeStart, lastGen, lock) {
771
811
  const log = await this.git.exec([
772
812
  "log",
773
813
  "--first-parent",
774
814
  "--format=%H%x00%an%x00%ae%x00%s",
775
- `${lastGen.commit_sha}..HEAD`,
815
+ `${rangeStart}..HEAD`,
776
816
  "--",
777
817
  this.sdkOutputDir
778
818
  ]);