@fern-api/replay 0.10.3 → 0.10.4

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/cli.cjs CHANGED
@@ -13340,6 +13340,7 @@ var ReplayDetector = class {
13340
13340
  }
13341
13341
  const log = await this.git.exec([
13342
13342
  "log",
13343
+ "--first-parent",
13343
13344
  "--format=%H%x00%an%x00%ae%x00%s",
13344
13345
  `${lastGen.commit_sha}..HEAD`,
13345
13346
  "--",
@@ -13355,11 +13356,20 @@ var ReplayDetector = class {
13355
13356
  if (isGenerationCommit(commit)) {
13356
13357
  continue;
13357
13358
  }
13358
- const parents = await this.git.getCommitParents(commit.sha);
13359
- if (parents.length > 1) {
13359
+ if (lock.patches.find((p) => p.original_commit === commit.sha)) {
13360
13360
  continue;
13361
13361
  }
13362
- if (lock.patches.find((p) => p.original_commit === commit.sha)) {
13362
+ const parents = await this.git.getCommitParents(commit.sha);
13363
+ if (parents.length > 1) {
13364
+ const compositePatch = await this.createMergeCompositePatch({
13365
+ mergeCommit: commit,
13366
+ firstParent: parents[0],
13367
+ lastGen,
13368
+ lock
13369
+ });
13370
+ if (compositePatch) {
13371
+ newPatches.push(compositePatch);
13372
+ }
13363
13373
  continue;
13364
13374
  }
13365
13375
  let patchContent;
@@ -13458,6 +13468,46 @@ var ReplayDetector = class {
13458
13468
  const normalized = patchContent.split("\n").filter((line) => !line.startsWith("From ") && !line.startsWith("index ") && !line.startsWith("Date: ")).join("\n");
13459
13469
  return `sha256:${(0, import_node_crypto.createHash)("sha256").update(normalized).digest("hex")}`;
13460
13470
  }
13471
+ /**
13472
+ * Create a single composite patch for a merge commit by diffing the merge
13473
+ * commit against its first parent. This captures the net change of the
13474
+ * entire merged branch as one patch, eliminating accumulator chaining and
13475
+ * zero-net-change ghost conflicts.
13476
+ */
13477
+ async createMergeCompositePatch(input) {
13478
+ const { mergeCommit, firstParent, lastGen, lock } = input;
13479
+ const forgottenHashes = new Set(lock.forgotten_hashes ?? []);
13480
+ const filesOutput = await this.git.exec([
13481
+ "diff",
13482
+ "--name-only",
13483
+ firstParent,
13484
+ mergeCommit.sha
13485
+ ]);
13486
+ const files = filesOutput.trim().split("\n").filter(Boolean).filter((f) => !INFRASTRUCTURE_FILES.has(f)).filter((f) => !f.startsWith(".fern/"));
13487
+ if (files.length === 0) return null;
13488
+ const diff = await this.git.exec([
13489
+ "diff",
13490
+ firstParent,
13491
+ mergeCommit.sha,
13492
+ "--",
13493
+ ...files
13494
+ ]);
13495
+ if (!diff.trim()) return null;
13496
+ const contentHash = this.computeContentHash(diff);
13497
+ if (lock.patches.some((p) => p.content_hash === contentHash) || forgottenHashes.has(contentHash)) {
13498
+ return null;
13499
+ }
13500
+ return {
13501
+ id: `patch-merge-${mergeCommit.sha.slice(0, 8)}`,
13502
+ content_hash: contentHash,
13503
+ original_commit: mergeCommit.sha,
13504
+ original_message: mergeCommit.message,
13505
+ original_author: `${mergeCommit.authorName} <${mergeCommit.authorEmail}>`,
13506
+ base_generation: lastGen.commit_sha,
13507
+ files,
13508
+ patch_content: diff
13509
+ };
13510
+ }
13461
13511
  /**
13462
13512
  * Detect patches via tree diff for non-linear history. Returns a composite patch.
13463
13513
  * Revert reconciliation is skipped here because tree-diff produces a single composite