@fern-api/replay 0.8.0 → 0.9.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/cli.cjs +496 -127
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +250 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -17
- package/dist/index.d.ts +62 -17
- package/dist/index.js +250 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -751,7 +751,7 @@ var require_dist2 = __commonJS({
|
|
|
751
751
|
function deferred2() {
|
|
752
752
|
let done;
|
|
753
753
|
let fail;
|
|
754
|
-
let
|
|
754
|
+
let status2 = "pending";
|
|
755
755
|
const promise = new Promise((_done, _fail) => {
|
|
756
756
|
done = _done;
|
|
757
757
|
fail = _fail;
|
|
@@ -759,22 +759,22 @@ var require_dist2 = __commonJS({
|
|
|
759
759
|
return {
|
|
760
760
|
promise,
|
|
761
761
|
done(result) {
|
|
762
|
-
if (
|
|
763
|
-
|
|
762
|
+
if (status2 === "pending") {
|
|
763
|
+
status2 = "resolved";
|
|
764
764
|
done(result);
|
|
765
765
|
}
|
|
766
766
|
},
|
|
767
767
|
fail(error) {
|
|
768
|
-
if (
|
|
769
|
-
|
|
768
|
+
if (status2 === "pending") {
|
|
769
|
+
status2 = "rejected";
|
|
770
770
|
fail(error);
|
|
771
771
|
}
|
|
772
772
|
},
|
|
773
773
|
get fulfilled() {
|
|
774
|
-
return
|
|
774
|
+
return status2 !== "pending";
|
|
775
775
|
},
|
|
776
776
|
get status() {
|
|
777
|
-
return
|
|
777
|
+
return status2;
|
|
778
778
|
}
|
|
779
779
|
};
|
|
780
780
|
}
|
|
@@ -1817,10 +1817,10 @@ function mergeTask(customArgs) {
|
|
|
1817
1817
|
}
|
|
1818
1818
|
};
|
|
1819
1819
|
}
|
|
1820
|
-
function pushResultPushedItem(local, remote,
|
|
1821
|
-
const deleted =
|
|
1822
|
-
const tag =
|
|
1823
|
-
const alreadyUpdated = !
|
|
1820
|
+
function pushResultPushedItem(local, remote, status2) {
|
|
1821
|
+
const deleted = status2.includes("deleted");
|
|
1822
|
+
const tag = status2.includes("tag") || /^refs\/tags/.test(local);
|
|
1823
|
+
const alreadyUpdated = !status2.includes("new");
|
|
1824
1824
|
return {
|
|
1825
1825
|
deleted,
|
|
1826
1826
|
tag,
|
|
@@ -3659,7 +3659,7 @@ var init_esm = __esm({
|
|
|
3659
3659
|
nameStatusParser = [
|
|
3660
3660
|
new LineParser(
|
|
3661
3661
|
/([ACDMRTUXB])([0-9]{0,3})\t(.[^\t]*)(\t(.[^\t]*))?$/,
|
|
3662
|
-
(result, [
|
|
3662
|
+
(result, [status2, similarity, from, _to, to]) => {
|
|
3663
3663
|
result.changed++;
|
|
3664
3664
|
result.files.push({
|
|
3665
3665
|
file: to ?? from,
|
|
@@ -3667,7 +3667,7 @@ var init_esm = __esm({
|
|
|
3667
3667
|
insertions: 0,
|
|
3668
3668
|
deletions: 0,
|
|
3669
3669
|
binary: false,
|
|
3670
|
-
status: orVoid(isDiffNameStatus(
|
|
3670
|
+
status: orVoid(isDiffNameStatus(status2) && status2),
|
|
3671
3671
|
from: orVoid(!!to && from !== to && from),
|
|
3672
3672
|
similarity: asNumber(similarity)
|
|
3673
3673
|
});
|
|
@@ -4214,7 +4214,7 @@ var init_esm = __esm({
|
|
|
4214
4214
|
]);
|
|
4215
4215
|
parseStatusSummary = function(text) {
|
|
4216
4216
|
const lines = text.split(NULL);
|
|
4217
|
-
const
|
|
4217
|
+
const status2 = new StatusSummary();
|
|
4218
4218
|
for (let i = 0, l = lines.length; i < l; ) {
|
|
4219
4219
|
let line = lines[i++].trim();
|
|
4220
4220
|
if (!line) {
|
|
@@ -4223,9 +4223,9 @@ var init_esm = __esm({
|
|
|
4223
4223
|
if (line.charAt(0) === "R") {
|
|
4224
4224
|
line += NULL + (lines[i++] || "");
|
|
4225
4225
|
}
|
|
4226
|
-
splitLine(
|
|
4226
|
+
splitLine(status2, line);
|
|
4227
4227
|
}
|
|
4228
|
-
return
|
|
4228
|
+
return status2;
|
|
4229
4229
|
};
|
|
4230
4230
|
}
|
|
4231
4231
|
});
|
|
@@ -4514,15 +4514,15 @@ var init_esm = __esm({
|
|
|
4514
4514
|
this.current = "";
|
|
4515
4515
|
this.detached = false;
|
|
4516
4516
|
}
|
|
4517
|
-
push(
|
|
4518
|
-
if (
|
|
4517
|
+
push(status2, detached, name, commit, label) {
|
|
4518
|
+
if (status2 === "*") {
|
|
4519
4519
|
this.detached = detached;
|
|
4520
4520
|
this.current = name;
|
|
4521
4521
|
}
|
|
4522
4522
|
this.all.push(name);
|
|
4523
4523
|
this.branches[name] = {
|
|
4524
|
-
current:
|
|
4525
|
-
linkedWorkTree:
|
|
4524
|
+
current: status2 === "*",
|
|
4525
|
+
linkedWorkTree: status2 === "+",
|
|
4526
4526
|
name,
|
|
4527
4527
|
commit,
|
|
4528
4528
|
label
|
|
@@ -12846,6 +12846,7 @@ var require_brace_expansion = __commonJS({
|
|
|
12846
12846
|
// src/cli.ts
|
|
12847
12847
|
var import_node_path7 = require("path");
|
|
12848
12848
|
var import_node_fs6 = require("fs");
|
|
12849
|
+
var import_node_readline = require("readline");
|
|
12849
12850
|
init_GitClient();
|
|
12850
12851
|
|
|
12851
12852
|
// src/LockfileManager.ts
|
|
@@ -12938,6 +12939,15 @@ var LockfileManager = class {
|
|
|
12938
12939
|
this.ensureLoaded();
|
|
12939
12940
|
this.lock.patches = [];
|
|
12940
12941
|
}
|
|
12942
|
+
addForgottenHash(hash) {
|
|
12943
|
+
this.ensureLoaded();
|
|
12944
|
+
if (!this.lock.forgotten_hashes) {
|
|
12945
|
+
this.lock.forgotten_hashes = [];
|
|
12946
|
+
}
|
|
12947
|
+
if (!this.lock.forgotten_hashes.includes(hash)) {
|
|
12948
|
+
this.lock.forgotten_hashes.push(hash);
|
|
12949
|
+
}
|
|
12950
|
+
}
|
|
12941
12951
|
getUnresolvedPatches() {
|
|
12942
12952
|
this.ensureLoaded();
|
|
12943
12953
|
return this.lock.patches.filter((p) => p.status === "unresolved");
|
|
@@ -13013,7 +13023,7 @@ function isReplayCommit(commit) {
|
|
|
13013
13023
|
return commit.message.startsWith("[fern-replay]");
|
|
13014
13024
|
}
|
|
13015
13025
|
function isRevertCommit(message) {
|
|
13016
|
-
return
|
|
13026
|
+
return /^Revert ".+"$/.test(message);
|
|
13017
13027
|
}
|
|
13018
13028
|
function parseRevertedSha(fullBody) {
|
|
13019
13029
|
const match2 = fullBody.match(/This reverts commit ([0-9a-f]{40})\./);
|
|
@@ -13065,6 +13075,7 @@ var ReplayDetector = class {
|
|
|
13065
13075
|
}
|
|
13066
13076
|
const commits = this.parseGitLog(log);
|
|
13067
13077
|
const newPatches = [];
|
|
13078
|
+
const forgottenHashes = new Set(lock.forgotten_hashes ?? []);
|
|
13068
13079
|
for (const commit of commits) {
|
|
13069
13080
|
if (isGenerationCommit(commit)) {
|
|
13070
13081
|
continue;
|
|
@@ -13078,7 +13089,7 @@ var ReplayDetector = class {
|
|
|
13078
13089
|
}
|
|
13079
13090
|
const patchContent = await this.git.formatPatch(commit.sha);
|
|
13080
13091
|
const contentHash = this.computeContentHash(patchContent);
|
|
13081
|
-
if (lock.patches.find((p) => p.content_hash === contentHash)) {
|
|
13092
|
+
if (lock.patches.find((p) => p.content_hash === contentHash) || forgottenHashes.has(contentHash)) {
|
|
13082
13093
|
continue;
|
|
13083
13094
|
}
|
|
13084
13095
|
const filesOutput = await this.git.exec(["diff-tree", "--no-commit-id", "--name-only", "-r", commit.sha]);
|
|
@@ -13148,6 +13159,9 @@ var ReplayDetector = class {
|
|
|
13148
13159
|
revertIndicesToRemove.add(idx);
|
|
13149
13160
|
}
|
|
13150
13161
|
}
|
|
13162
|
+
if (!matchedExisting && !matchedNew) {
|
|
13163
|
+
revertIndicesToRemove.add(i);
|
|
13164
|
+
}
|
|
13151
13165
|
}
|
|
13152
13166
|
const filteredPatches = newPatches.filter((_, i) => !revertIndicesToRemove.has(i));
|
|
13153
13167
|
return { patches: filteredPatches, revertedPatchIds: [...revertedPatchIdSet] };
|
|
@@ -13174,7 +13188,7 @@ var ReplayDetector = class {
|
|
|
13174
13188
|
if (!diff.trim()) return { patches: [], revertedPatchIds: [] };
|
|
13175
13189
|
const contentHash = this.computeContentHash(diff);
|
|
13176
13190
|
const lock = this.lockManager.read();
|
|
13177
|
-
if (lock.patches.some((p) => p.content_hash === contentHash)) {
|
|
13191
|
+
if (lock.patches.some((p) => p.content_hash === contentHash) || (lock.forgotten_hashes ?? []).includes(contentHash)) {
|
|
13178
13192
|
return { patches: [], revertedPatchIds: [] };
|
|
13179
13193
|
}
|
|
13180
13194
|
const headSha = (await this.git.exec(["rev-parse", "HEAD"])).trim();
|
|
@@ -14543,6 +14557,34 @@ var import_promises = require("fs/promises");
|
|
|
14543
14557
|
var import_node_os = require("os");
|
|
14544
14558
|
var import_node_path3 = require("path");
|
|
14545
14559
|
|
|
14560
|
+
// src/conflict-utils.ts
|
|
14561
|
+
function stripConflictMarkers(content) {
|
|
14562
|
+
const lines = content.split("\n");
|
|
14563
|
+
const result = [];
|
|
14564
|
+
let inConflict = false;
|
|
14565
|
+
let inOurs = false;
|
|
14566
|
+
for (const line of lines) {
|
|
14567
|
+
if (line.startsWith("<<<<<<< ")) {
|
|
14568
|
+
inConflict = true;
|
|
14569
|
+
inOurs = true;
|
|
14570
|
+
continue;
|
|
14571
|
+
}
|
|
14572
|
+
if (inConflict && line === "=======") {
|
|
14573
|
+
inOurs = false;
|
|
14574
|
+
continue;
|
|
14575
|
+
}
|
|
14576
|
+
if (inConflict && line.startsWith(">>>>>>> ")) {
|
|
14577
|
+
inConflict = false;
|
|
14578
|
+
inOurs = false;
|
|
14579
|
+
continue;
|
|
14580
|
+
}
|
|
14581
|
+
if (!inConflict || inOurs) {
|
|
14582
|
+
result.push(line);
|
|
14583
|
+
}
|
|
14584
|
+
}
|
|
14585
|
+
return result.join("\n");
|
|
14586
|
+
}
|
|
14587
|
+
|
|
14546
14588
|
// node_modules/node-diff3/dist/diff3.mjs
|
|
14547
14589
|
function LCS(buffer1, buffer2) {
|
|
14548
14590
|
let equivalenceClasses = {};
|
|
@@ -14866,7 +14908,8 @@ var ReplayApplicator = class {
|
|
|
14866
14908
|
async applyPatches(patches) {
|
|
14867
14909
|
this.resetAccumulator();
|
|
14868
14910
|
const results = [];
|
|
14869
|
-
for (
|
|
14911
|
+
for (let i = 0; i < patches.length; i++) {
|
|
14912
|
+
const patch = patches[i];
|
|
14870
14913
|
if (this.isExcluded(patch)) {
|
|
14871
14914
|
results.push({
|
|
14872
14915
|
patch,
|
|
@@ -14877,6 +14920,33 @@ var ReplayApplicator = class {
|
|
|
14877
14920
|
}
|
|
14878
14921
|
const result = await this.applyPatchWithFallback(patch);
|
|
14879
14922
|
results.push(result);
|
|
14923
|
+
if (result.status === "conflict" && result.fileResults) {
|
|
14924
|
+
const laterFiles = /* @__PURE__ */ new Set();
|
|
14925
|
+
for (let j = i + 1; j < patches.length; j++) {
|
|
14926
|
+
for (const f of patches[j].files) {
|
|
14927
|
+
laterFiles.add(f);
|
|
14928
|
+
}
|
|
14929
|
+
}
|
|
14930
|
+
const resolvedToOriginal = /* @__PURE__ */ new Map();
|
|
14931
|
+
if (result.resolvedFiles) {
|
|
14932
|
+
for (const [orig, resolved] of Object.entries(result.resolvedFiles)) {
|
|
14933
|
+
resolvedToOriginal.set(resolved, orig);
|
|
14934
|
+
}
|
|
14935
|
+
}
|
|
14936
|
+
for (const fileResult of result.fileResults) {
|
|
14937
|
+
if (fileResult.status !== "conflict") continue;
|
|
14938
|
+
const originalPath = resolvedToOriginal.get(fileResult.file) ?? fileResult.file;
|
|
14939
|
+
if (laterFiles.has(fileResult.file) || laterFiles.has(originalPath)) {
|
|
14940
|
+
const filePath = (0, import_node_path3.join)(this.outputDir, fileResult.file);
|
|
14941
|
+
try {
|
|
14942
|
+
const content = await (0, import_promises.readFile)(filePath, "utf-8");
|
|
14943
|
+
const stripped = stripConflictMarkers(content);
|
|
14944
|
+
await (0, import_promises.writeFile)(filePath, stripped);
|
|
14945
|
+
} catch {
|
|
14946
|
+
}
|
|
14947
|
+
}
|
|
14948
|
+
}
|
|
14949
|
+
}
|
|
14880
14950
|
}
|
|
14881
14951
|
return results;
|
|
14882
14952
|
}
|
|
@@ -14895,7 +14965,7 @@ var ReplayApplicator = class {
|
|
|
14895
14965
|
const resolvedPath = await this.resolveFilePath(filePath, baseGen.tree_hash, currentTreeHash);
|
|
14896
14966
|
const base = await this.git.showFile(baseGen.tree_hash, filePath);
|
|
14897
14967
|
const theirs = await this.applyPatchToContent(base, patch.patch_content, filePath, tempGit, tempDir);
|
|
14898
|
-
if (theirs
|
|
14968
|
+
if (theirs) {
|
|
14899
14969
|
this.fileTheirsAccumulator.set(resolvedPath, {
|
|
14900
14970
|
content: theirs,
|
|
14901
14971
|
baseGeneration: patch.base_generation
|
|
@@ -15027,7 +15097,7 @@ var ReplayApplicator = class {
|
|
|
15027
15097
|
);
|
|
15028
15098
|
let useAccumulatorAsMergeBase = false;
|
|
15029
15099
|
const accumulatorEntry = this.fileTheirsAccumulator.get(resolvedPath);
|
|
15030
|
-
if (!theirs &&
|
|
15100
|
+
if (!theirs && accumulatorEntry) {
|
|
15031
15101
|
theirs = await this.applyPatchToContent(
|
|
15032
15102
|
accumulatorEntry.content,
|
|
15033
15103
|
patch.patch_content,
|
|
@@ -15086,7 +15156,7 @@ var ReplayApplicator = class {
|
|
|
15086
15156
|
reason: "missing-content"
|
|
15087
15157
|
};
|
|
15088
15158
|
}
|
|
15089
|
-
if (!base || !ours) {
|
|
15159
|
+
if (!base && !useAccumulatorAsMergeBase || !ours) {
|
|
15090
15160
|
return {
|
|
15091
15161
|
file: resolvedPath,
|
|
15092
15162
|
status: "skipped",
|
|
@@ -15094,11 +15164,18 @@ var ReplayApplicator = class {
|
|
|
15094
15164
|
};
|
|
15095
15165
|
}
|
|
15096
15166
|
const mergeBase = useAccumulatorAsMergeBase && accumulatorEntry ? accumulatorEntry.content : base;
|
|
15167
|
+
if (mergeBase == null) {
|
|
15168
|
+
return {
|
|
15169
|
+
file: resolvedPath,
|
|
15170
|
+
status: "skipped",
|
|
15171
|
+
reason: "missing-content"
|
|
15172
|
+
};
|
|
15173
|
+
}
|
|
15097
15174
|
const merged = threeWayMerge(mergeBase, ours, effective_theirs);
|
|
15098
15175
|
const outDir = (0, import_node_path3.dirname)(oursPath);
|
|
15099
15176
|
await (0, import_promises.mkdir)(outDir, { recursive: true });
|
|
15100
15177
|
await (0, import_promises.writeFile)(oursPath, merged.content);
|
|
15101
|
-
if (effective_theirs
|
|
15178
|
+
if (effective_theirs) {
|
|
15102
15179
|
this.fileTheirsAccumulator.set(resolvedPath, {
|
|
15103
15180
|
content: effective_theirs,
|
|
15104
15181
|
baseGeneration: patch.base_generation
|
|
@@ -15336,34 +15413,6 @@ CLI Version: ${options.cliVersion}`;
|
|
|
15336
15413
|
}
|
|
15337
15414
|
};
|
|
15338
15415
|
|
|
15339
|
-
// src/conflict-utils.ts
|
|
15340
|
-
function stripConflictMarkers(content) {
|
|
15341
|
-
const lines = content.split("\n");
|
|
15342
|
-
const result = [];
|
|
15343
|
-
let inConflict = false;
|
|
15344
|
-
let inOurs = false;
|
|
15345
|
-
for (const line of lines) {
|
|
15346
|
-
if (line.startsWith("<<<<<<< ")) {
|
|
15347
|
-
inConflict = true;
|
|
15348
|
-
inOurs = true;
|
|
15349
|
-
continue;
|
|
15350
|
-
}
|
|
15351
|
-
if (inConflict && line === "=======") {
|
|
15352
|
-
inOurs = false;
|
|
15353
|
-
continue;
|
|
15354
|
-
}
|
|
15355
|
-
if (inConflict && line.startsWith(">>>>>>> ")) {
|
|
15356
|
-
inConflict = false;
|
|
15357
|
-
inOurs = false;
|
|
15358
|
-
continue;
|
|
15359
|
-
}
|
|
15360
|
-
if (!inConflict || inOurs) {
|
|
15361
|
-
result.push(line);
|
|
15362
|
-
}
|
|
15363
|
-
}
|
|
15364
|
-
return result.join("\n");
|
|
15365
|
-
}
|
|
15366
|
-
|
|
15367
15416
|
// src/ReplayService.ts
|
|
15368
15417
|
var ReplayService = class {
|
|
15369
15418
|
git;
|
|
@@ -16330,30 +16379,145 @@ function computeContentHash(patchContent) {
|
|
|
16330
16379
|
}
|
|
16331
16380
|
|
|
16332
16381
|
// src/commands/forget.ts
|
|
16333
|
-
function
|
|
16382
|
+
function parseDiffStat(patchContent) {
|
|
16383
|
+
let additions = 0;
|
|
16384
|
+
let deletions = 0;
|
|
16385
|
+
let inDiffHunk = false;
|
|
16386
|
+
for (const line of patchContent.split("\n")) {
|
|
16387
|
+
if (line.startsWith("diff --git ")) {
|
|
16388
|
+
inDiffHunk = true;
|
|
16389
|
+
continue;
|
|
16390
|
+
}
|
|
16391
|
+
if (!inDiffHunk) continue;
|
|
16392
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
16393
|
+
additions++;
|
|
16394
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
16395
|
+
deletions++;
|
|
16396
|
+
}
|
|
16397
|
+
}
|
|
16398
|
+
return { additions, deletions };
|
|
16399
|
+
}
|
|
16400
|
+
function toMatchedPatch(patch) {
|
|
16401
|
+
return {
|
|
16402
|
+
id: patch.id,
|
|
16403
|
+
message: patch.original_message,
|
|
16404
|
+
files: patch.files,
|
|
16405
|
+
diffstat: parseDiffStat(patch.patch_content),
|
|
16406
|
+
...patch.status ? { status: patch.status } : {}
|
|
16407
|
+
};
|
|
16408
|
+
}
|
|
16409
|
+
function matchesPatch(patch, pattern) {
|
|
16410
|
+
const fileMatch = patch.files.some(
|
|
16411
|
+
(file) => file === pattern || minimatch(file, pattern)
|
|
16412
|
+
);
|
|
16413
|
+
if (fileMatch) return true;
|
|
16414
|
+
return patch.original_message.toLowerCase().includes(pattern.toLowerCase());
|
|
16415
|
+
}
|
|
16416
|
+
function buildWarnings(patches) {
|
|
16417
|
+
const warnings = [];
|
|
16418
|
+
for (const patch of patches) {
|
|
16419
|
+
if (patch.status === "resolving") {
|
|
16420
|
+
warnings.push(
|
|
16421
|
+
`patch ${patch.id} has conflict markers in these files: ${patch.files.join(", ")}. Run \`git checkout -- <files>\` to restore the generated versions.`
|
|
16422
|
+
);
|
|
16423
|
+
} else if (patch.status === "unresolved") {
|
|
16424
|
+
warnings.push(
|
|
16425
|
+
`patch ${patch.id} had unresolved conflicts (files: ${patch.files.join(", ")}).`
|
|
16426
|
+
);
|
|
16427
|
+
}
|
|
16428
|
+
}
|
|
16429
|
+
return warnings;
|
|
16430
|
+
}
|
|
16431
|
+
var EMPTY_RESULT = {
|
|
16432
|
+
initialized: false,
|
|
16433
|
+
removed: [],
|
|
16434
|
+
remaining: 0,
|
|
16435
|
+
notFound: false,
|
|
16436
|
+
alreadyForgotten: [],
|
|
16437
|
+
totalPatches: 0,
|
|
16438
|
+
warnings: []
|
|
16439
|
+
};
|
|
16440
|
+
function forget(outputDir, options) {
|
|
16334
16441
|
const lockManager = new LockfileManager(outputDir);
|
|
16335
16442
|
if (!lockManager.exists()) {
|
|
16336
|
-
return {
|
|
16443
|
+
return { ...EMPTY_RESULT };
|
|
16337
16444
|
}
|
|
16338
16445
|
const lock = lockManager.read();
|
|
16339
|
-
const
|
|
16340
|
-
|
|
16341
|
-
|
|
16342
|
-
|
|
16343
|
-
|
|
16446
|
+
const totalPatches = lock.patches.length;
|
|
16447
|
+
if (options?.all) {
|
|
16448
|
+
const removed = lock.patches.map(toMatchedPatch);
|
|
16449
|
+
const warnings = buildWarnings(lock.patches);
|
|
16450
|
+
if (!options.dryRun) {
|
|
16451
|
+
for (const patch of lock.patches) {
|
|
16452
|
+
lockManager.addForgottenHash(patch.content_hash);
|
|
16453
|
+
}
|
|
16454
|
+
lockManager.clearPatches();
|
|
16455
|
+
lockManager.save();
|
|
16456
|
+
}
|
|
16457
|
+
return {
|
|
16458
|
+
initialized: true,
|
|
16459
|
+
removed,
|
|
16460
|
+
remaining: 0,
|
|
16461
|
+
notFound: false,
|
|
16462
|
+
alreadyForgotten: [],
|
|
16463
|
+
totalPatches,
|
|
16464
|
+
warnings
|
|
16465
|
+
};
|
|
16344
16466
|
}
|
|
16345
|
-
|
|
16346
|
-
|
|
16347
|
-
|
|
16348
|
-
|
|
16349
|
-
|
|
16350
|
-
|
|
16351
|
-
|
|
16352
|
-
|
|
16467
|
+
if (options?.patchIds && options.patchIds.length > 0) {
|
|
16468
|
+
const removed = [];
|
|
16469
|
+
const alreadyForgotten = [];
|
|
16470
|
+
const patchesToRemove = [];
|
|
16471
|
+
for (const id of options.patchIds) {
|
|
16472
|
+
const patch = lock.patches.find((p) => p.id === id);
|
|
16473
|
+
if (patch) {
|
|
16474
|
+
removed.push(toMatchedPatch(patch));
|
|
16475
|
+
patchesToRemove.push(patch);
|
|
16476
|
+
} else {
|
|
16477
|
+
alreadyForgotten.push(id);
|
|
16478
|
+
}
|
|
16353
16479
|
}
|
|
16354
|
-
|
|
16480
|
+
const warnings = buildWarnings(patchesToRemove);
|
|
16481
|
+
if (!options.dryRun) {
|
|
16482
|
+
for (const patch of patchesToRemove) {
|
|
16483
|
+
lockManager.addForgottenHash(patch.content_hash);
|
|
16484
|
+
lockManager.removePatch(patch.id);
|
|
16485
|
+
}
|
|
16486
|
+
lockManager.save();
|
|
16487
|
+
}
|
|
16488
|
+
return {
|
|
16489
|
+
initialized: true,
|
|
16490
|
+
removed,
|
|
16491
|
+
remaining: totalPatches - removed.length,
|
|
16492
|
+
notFound: removed.length === 0 && alreadyForgotten.length > 0,
|
|
16493
|
+
alreadyForgotten,
|
|
16494
|
+
totalPatches,
|
|
16495
|
+
warnings
|
|
16496
|
+
};
|
|
16355
16497
|
}
|
|
16356
|
-
|
|
16498
|
+
if (options?.pattern) {
|
|
16499
|
+
const matched = lock.patches.filter((p) => matchesPatch(p, options.pattern)).map(toMatchedPatch);
|
|
16500
|
+
return {
|
|
16501
|
+
initialized: true,
|
|
16502
|
+
removed: [],
|
|
16503
|
+
remaining: totalPatches,
|
|
16504
|
+
notFound: matched.length === 0,
|
|
16505
|
+
alreadyForgotten: [],
|
|
16506
|
+
totalPatches,
|
|
16507
|
+
warnings: [],
|
|
16508
|
+
matched
|
|
16509
|
+
};
|
|
16510
|
+
}
|
|
16511
|
+
return {
|
|
16512
|
+
initialized: true,
|
|
16513
|
+
removed: [],
|
|
16514
|
+
remaining: totalPatches,
|
|
16515
|
+
notFound: totalPatches === 0,
|
|
16516
|
+
alreadyForgotten: [],
|
|
16517
|
+
totalPatches,
|
|
16518
|
+
warnings: [],
|
|
16519
|
+
matched: lock.patches.map(toMatchedPatch)
|
|
16520
|
+
};
|
|
16357
16521
|
}
|
|
16358
16522
|
|
|
16359
16523
|
// src/commands/reset.ts
|
|
@@ -16471,6 +16635,55 @@ async function getChangedFiles(git, currentGen, files) {
|
|
|
16471
16635
|
return changed.length > 0 ? changed : files;
|
|
16472
16636
|
}
|
|
16473
16637
|
|
|
16638
|
+
// src/commands/status.ts
|
|
16639
|
+
function status(outputDir) {
|
|
16640
|
+
const lockManager = new LockfileManager(outputDir);
|
|
16641
|
+
if (!lockManager.exists()) {
|
|
16642
|
+
return {
|
|
16643
|
+
initialized: false,
|
|
16644
|
+
generationCount: 0,
|
|
16645
|
+
lastGeneration: void 0,
|
|
16646
|
+
patches: [],
|
|
16647
|
+
unresolvedCount: 0,
|
|
16648
|
+
excludePatterns: []
|
|
16649
|
+
};
|
|
16650
|
+
}
|
|
16651
|
+
const lock = lockManager.read();
|
|
16652
|
+
const patches = lock.patches.map((patch) => ({
|
|
16653
|
+
id: patch.id,
|
|
16654
|
+
type: patch.patch_content.includes("new file mode") ? "added" : "modified",
|
|
16655
|
+
message: patch.original_message,
|
|
16656
|
+
author: patch.original_author.split("<")[0]?.trim() || "unknown",
|
|
16657
|
+
sha: patch.original_commit.slice(0, 7),
|
|
16658
|
+
files: patch.files,
|
|
16659
|
+
fileCount: patch.files.length,
|
|
16660
|
+
...patch.status ? { status: patch.status } : {}
|
|
16661
|
+
}));
|
|
16662
|
+
const unresolvedCount = lock.patches.filter(
|
|
16663
|
+
(p) => p.status === "unresolved" || p.status === "resolving"
|
|
16664
|
+
).length;
|
|
16665
|
+
let lastGeneration;
|
|
16666
|
+
const lastGen = lock.generations.find((g) => g.commit_sha === lock.current_generation);
|
|
16667
|
+
if (lastGen) {
|
|
16668
|
+
lastGeneration = {
|
|
16669
|
+
sha: lastGen.commit_sha.slice(0, 7),
|
|
16670
|
+
timestamp: lastGen.timestamp,
|
|
16671
|
+
cliVersion: lastGen.cli_version,
|
|
16672
|
+
generatorVersions: lastGen.generator_versions
|
|
16673
|
+
};
|
|
16674
|
+
}
|
|
16675
|
+
const config = lockManager.getCustomizationsConfig();
|
|
16676
|
+
const excludePatterns = config.exclude ?? [];
|
|
16677
|
+
return {
|
|
16678
|
+
initialized: true,
|
|
16679
|
+
generationCount: lock.generations.length,
|
|
16680
|
+
lastGeneration,
|
|
16681
|
+
patches,
|
|
16682
|
+
unresolvedCount,
|
|
16683
|
+
excludePatterns
|
|
16684
|
+
};
|
|
16685
|
+
}
|
|
16686
|
+
|
|
16474
16687
|
// src/cli.ts
|
|
16475
16688
|
var COMMANDS = ["bootstrap", "status", "detect", "replay", "migrate", "forget", "reset", "resolve"];
|
|
16476
16689
|
function usage() {
|
|
@@ -16482,7 +16695,7 @@ Commands:
|
|
|
16482
16695
|
detect Detect new customization patches (dry-run)
|
|
16483
16696
|
replay Run full replay (detect + apply patches)
|
|
16484
16697
|
migrate Analyze .fernignore migration
|
|
16485
|
-
forget Remove specific patches by
|
|
16698
|
+
forget Remove specific patches (by ID, search, or --all)
|
|
16486
16699
|
reset Remove all Replay state (nuclear option)
|
|
16487
16700
|
resolve Finish replay after manually resolving conflicts
|
|
16488
16701
|
|
|
@@ -16492,6 +16705,8 @@ Options:
|
|
|
16492
16705
|
--no-check Skip conflict marker check (resolve command only)
|
|
16493
16706
|
--migrate-fernignore Move untrackable .fernignore patterns to replay.yml
|
|
16494
16707
|
--max-commits <n> Max commits to scan (default: 500)
|
|
16708
|
+
--all Remove all tracked patches (forget only)
|
|
16709
|
+
--yes, -y Skip interactive selection/confirmation (forget only)
|
|
16495
16710
|
|
|
16496
16711
|
Examples:
|
|
16497
16712
|
fern-replay bootstrap ./my-sdk
|
|
@@ -16501,8 +16716,9 @@ Examples:
|
|
|
16501
16716
|
fern-replay detect ./my-sdk
|
|
16502
16717
|
fern-replay replay ./my-sdk --dry-run
|
|
16503
16718
|
fern-replay migrate ./my-sdk
|
|
16719
|
+
fern-replay forget ./my-sdk patch-def45678
|
|
16504
16720
|
fern-replay forget ./my-sdk "src/utils/retry.ts"
|
|
16505
|
-
fern-replay forget ./my-sdk
|
|
16721
|
+
fern-replay forget ./my-sdk --all
|
|
16506
16722
|
fern-replay reset ./my-sdk
|
|
16507
16723
|
fern-replay reset ./my-sdk --dry-run`);
|
|
16508
16724
|
}
|
|
@@ -16527,7 +16743,7 @@ function parseArgs(argv) {
|
|
|
16527
16743
|
process.exit(1);
|
|
16528
16744
|
}
|
|
16529
16745
|
const flags = {};
|
|
16530
|
-
|
|
16746
|
+
const positionals = [];
|
|
16531
16747
|
for (let i = 2; i < args.length; i++) {
|
|
16532
16748
|
const arg = args[i];
|
|
16533
16749
|
if (arg === "--dry-run") {
|
|
@@ -16542,11 +16758,17 @@ function parseArgs(argv) {
|
|
|
16542
16758
|
flags.force = true;
|
|
16543
16759
|
} else if (arg === "--no-check") {
|
|
16544
16760
|
flags.noCheck = true;
|
|
16545
|
-
} else if (
|
|
16546
|
-
|
|
16761
|
+
} else if (arg === "--verbose" || arg === "-v") {
|
|
16762
|
+
flags.verbose = true;
|
|
16763
|
+
} else if (arg === "--all") {
|
|
16764
|
+
flags.all = true;
|
|
16765
|
+
} else if (arg === "--yes" || arg === "-y") {
|
|
16766
|
+
flags.yes = true;
|
|
16767
|
+
} else if (!arg.startsWith("--")) {
|
|
16768
|
+
positionals.push(arg);
|
|
16547
16769
|
}
|
|
16548
16770
|
}
|
|
16549
|
-
return { command, dir: (0, import_node_path7.resolve)(dir), flags,
|
|
16771
|
+
return { command, dir: (0, import_node_path7.resolve)(dir), flags, positionals };
|
|
16550
16772
|
}
|
|
16551
16773
|
async function runBootstrap(dir, flags) {
|
|
16552
16774
|
const dryRun = !!flags.dryRun;
|
|
@@ -16599,43 +16821,67 @@ async function runBootstrap(dir, flags) {
|
|
|
16599
16821
|
for (const w of result.warnings) console.log(` ${w}`);
|
|
16600
16822
|
}
|
|
16601
16823
|
}
|
|
16602
|
-
async function runStatus(dir) {
|
|
16603
|
-
const
|
|
16604
|
-
|
|
16605
|
-
|
|
16824
|
+
async function runStatus(dir, flags) {
|
|
16825
|
+
const result = status(dir);
|
|
16826
|
+
const verbose = !!flags.verbose;
|
|
16827
|
+
if (!result.initialized) {
|
|
16828
|
+
console.log("Replay is not initialized. Run 'fern replay init' to get started.");
|
|
16606
16829
|
return;
|
|
16607
16830
|
}
|
|
16608
|
-
|
|
16609
|
-
|
|
16610
|
-
|
|
16611
|
-
|
|
16612
|
-
|
|
16613
|
-
|
|
16614
|
-
|
|
16615
|
-
console.log(` CLI: ${lastGen.cli_version}`);
|
|
16616
|
-
if (Object.keys(lastGen.generator_versions).length > 0) {
|
|
16617
|
-
for (const [name, ver] of Object.entries(lastGen.generator_versions)) {
|
|
16831
|
+
console.log("Replay: initialized");
|
|
16832
|
+
if (result.lastGeneration) {
|
|
16833
|
+
const gen = result.lastGeneration;
|
|
16834
|
+
if (verbose) {
|
|
16835
|
+
console.log(`Last generation: ${gen.sha} (${gen.timestamp})`);
|
|
16836
|
+
console.log(` CLI: ${gen.cliVersion}`);
|
|
16837
|
+
for (const [name, ver] of Object.entries(gen.generatorVersions)) {
|
|
16618
16838
|
console.log(` ${name}: ${ver}`);
|
|
16619
16839
|
}
|
|
16840
|
+
console.log(`Generations tracked: ${result.generationCount}`);
|
|
16841
|
+
} else {
|
|
16842
|
+
const genVersions = Object.entries(gen.generatorVersions);
|
|
16843
|
+
const versionSuffix = genVersions.length > 0 ? `, ${genVersions[0][0]}@${genVersions[0][1]}` : "";
|
|
16844
|
+
console.log(`Last generation: ${gen.sha} (${gen.timestamp}${versionSuffix})`);
|
|
16620
16845
|
}
|
|
16621
16846
|
}
|
|
16847
|
+
if (result.patches.length === 0) {
|
|
16848
|
+
console.log("\nNo customizations tracked. Edit SDK files and commit \u2014 Replay will detect them on next generation.");
|
|
16849
|
+
return;
|
|
16850
|
+
}
|
|
16851
|
+
const unresolvedSuffix = result.unresolvedCount > 0 ? `, ${result.unresolvedCount} unresolved` : "";
|
|
16622
16852
|
console.log(`
|
|
16623
|
-
Patches: ${
|
|
16624
|
-
if (
|
|
16625
|
-
for (const patch of
|
|
16626
|
-
const
|
|
16627
|
-
console.log(`
|
|
16628
|
-
|
|
16853
|
+
Patches: ${result.patches.length} tracked${unresolvedSuffix}`);
|
|
16854
|
+
if (verbose) {
|
|
16855
|
+
for (const patch of result.patches) {
|
|
16856
|
+
const prefix = patch.status ? "\u26A0 " : " ";
|
|
16857
|
+
console.log(`
|
|
16858
|
+
${prefix}${patch.id} [${patch.type}] "${patch.message}"`);
|
|
16859
|
+
console.log(` by ${patch.author} (${patch.sha})`);
|
|
16629
16860
|
for (const f of patch.files) {
|
|
16630
16861
|
console.log(` ${f}`);
|
|
16631
16862
|
}
|
|
16863
|
+
if (patch.status) {
|
|
16864
|
+
console.log(" Run `fern replay resolve` to fix.");
|
|
16865
|
+
}
|
|
16866
|
+
}
|
|
16867
|
+
if (result.excludePatterns.length > 0) {
|
|
16868
|
+
console.log("\nExclude patterns (from replay.yml):");
|
|
16869
|
+
for (const p of result.excludePatterns) {
|
|
16870
|
+
console.log(` ${p}`);
|
|
16871
|
+
}
|
|
16872
|
+
}
|
|
16873
|
+
} else {
|
|
16874
|
+
console.log("");
|
|
16875
|
+
const maxDisplay = 10;
|
|
16876
|
+
const displayPatches = result.patches.slice(0, maxDisplay);
|
|
16877
|
+
for (const patch of displayPatches) {
|
|
16878
|
+
const prefix = patch.status ? "\u26A0 " : " ";
|
|
16879
|
+
const filesLabel = patch.fileCount === 1 ? "1 file" : `${patch.fileCount} files`;
|
|
16880
|
+
console.log(`${prefix}${patch.id} "${patch.message}" ${filesLabel}`);
|
|
16881
|
+
}
|
|
16882
|
+
if (result.patches.length > maxDisplay) {
|
|
16883
|
+
console.log(` ... and ${result.patches.length - maxDisplay} more`);
|
|
16632
16884
|
}
|
|
16633
|
-
}
|
|
16634
|
-
const config = lockManager.getCustomizationsConfig();
|
|
16635
|
-
if (config.exclude && config.exclude.length > 0) {
|
|
16636
|
-
console.log(`
|
|
16637
|
-
Exclude patterns: ${config.exclude.length}`);
|
|
16638
|
-
for (const p of config.exclude) console.log(` ${p}`);
|
|
16639
16885
|
}
|
|
16640
16886
|
}
|
|
16641
16887
|
async function runDetect(dir) {
|
|
@@ -16804,32 +17050,155 @@ Unprotected customizations (${analysis.commitsOnly.length}):`);
|
|
|
16804
17050
|
console.log("Run: fern-replay bootstrap <dir>");
|
|
16805
17051
|
}
|
|
16806
17052
|
}
|
|
16807
|
-
|
|
16808
|
-
|
|
16809
|
-
|
|
16810
|
-
|
|
16811
|
-
|
|
17053
|
+
function formatDiffStat(patch) {
|
|
17054
|
+
return `+${patch.diffstat.additions} -${patch.diffstat.deletions}`;
|
|
17055
|
+
}
|
|
17056
|
+
function formatPatchLine(index, patch) {
|
|
17057
|
+
const status2 = patch.status ? ` [${patch.status}]` : "";
|
|
17058
|
+
const filesStr = patch.files.join(", ");
|
|
17059
|
+
return ` [${index}] ${patch.id} "${patch.message}" ${formatDiffStat(patch)} ${filesStr}${status2}`;
|
|
17060
|
+
}
|
|
17061
|
+
function printForgetWarnings(warnings) {
|
|
17062
|
+
for (const w of warnings) {
|
|
17063
|
+
console.log(`Warning: ${w}`);
|
|
16812
17064
|
}
|
|
17065
|
+
}
|
|
17066
|
+
function promptLine(question) {
|
|
17067
|
+
const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
17068
|
+
return new Promise((resolve2) => {
|
|
17069
|
+
rl.question(question, (answer) => {
|
|
17070
|
+
rl.close();
|
|
17071
|
+
resolve2(answer.trim());
|
|
17072
|
+
});
|
|
17073
|
+
});
|
|
17074
|
+
}
|
|
17075
|
+
function parseSelection(input, max) {
|
|
17076
|
+
const trimmed2 = input.trim().toLowerCase();
|
|
17077
|
+
if (trimmed2 === "all") return "all";
|
|
17078
|
+
if (trimmed2 === "none" || trimmed2 === "") return "none";
|
|
17079
|
+
const nums = [];
|
|
17080
|
+
for (const part of trimmed2.split(",")) {
|
|
17081
|
+
const n = parseInt(part.trim(), 10);
|
|
17082
|
+
if (isNaN(n) || n < 1 || n > max) return null;
|
|
17083
|
+
if (!nums.includes(n)) nums.push(n);
|
|
17084
|
+
}
|
|
17085
|
+
return nums.length > 0 ? nums : null;
|
|
17086
|
+
}
|
|
17087
|
+
async function runForget(dir, flags, positionals) {
|
|
16813
17088
|
const dryRun = !!flags.dryRun;
|
|
16814
|
-
|
|
16815
|
-
|
|
16816
|
-
const
|
|
16817
|
-
|
|
16818
|
-
|
|
17089
|
+
const yes = !!flags.yes;
|
|
17090
|
+
const all = !!flags.all;
|
|
17091
|
+
const isPatchIdMode = positionals.length > 0 && positionals.every((a) => a.startsWith("patch-"));
|
|
17092
|
+
const pattern = !isPatchIdMode && positionals.length > 0 ? positionals[0] : void 0;
|
|
17093
|
+
const patchIds = isPatchIdMode ? positionals : void 0;
|
|
17094
|
+
if (all) {
|
|
17095
|
+
const preview = forget(dir, { all: true, dryRun: true });
|
|
17096
|
+
if (!preview.initialized) {
|
|
17097
|
+
console.log("Replay is not initialized. Run 'fern replay init' to get started.");
|
|
17098
|
+
process.exit(1);
|
|
17099
|
+
}
|
|
17100
|
+
if (preview.totalPatches === 0) {
|
|
17101
|
+
console.log("No patches tracked. Nothing to remove.");
|
|
17102
|
+
return;
|
|
17103
|
+
}
|
|
17104
|
+
if (!dryRun && !yes) {
|
|
17105
|
+
console.log(`WARNING: This will remove all ${preview.totalPatches} tracked patches.`);
|
|
17106
|
+
console.log("Affected files will be overwritten on next generation.");
|
|
17107
|
+
console.log("Replay will remain initialized and detect new customizations.\n");
|
|
17108
|
+
const confirm = await promptLine("Proceed? [y/N] ");
|
|
17109
|
+
if (confirm.toLowerCase() !== "y") {
|
|
17110
|
+
console.log("Aborted.");
|
|
17111
|
+
return;
|
|
17112
|
+
}
|
|
17113
|
+
}
|
|
17114
|
+
const finalResult = dryRun ? preview : forget(dir, { all: true });
|
|
17115
|
+
printForgetWarnings(finalResult.warnings);
|
|
17116
|
+
const verb2 = dryRun ? "Would remove" : "Removed";
|
|
17117
|
+
console.log(`${verb2} ${finalResult.removed.length} patches. ${finalResult.remaining} patches remaining.`);
|
|
16819
17118
|
return;
|
|
16820
17119
|
}
|
|
16821
|
-
|
|
16822
|
-
|
|
16823
|
-
|
|
16824
|
-
|
|
16825
|
-
|
|
16826
|
-
|
|
16827
|
-
|
|
17120
|
+
if (isPatchIdMode && patchIds) {
|
|
17121
|
+
const result2 = forget(dir, { patchIds, dryRun });
|
|
17122
|
+
if (!result2.initialized) {
|
|
17123
|
+
console.log("Replay is not initialized. Run 'fern replay init' to get started.");
|
|
17124
|
+
process.exit(1);
|
|
17125
|
+
}
|
|
17126
|
+
if (result2.alreadyForgotten.length > 0 && result2.removed.length === 0) {
|
|
17127
|
+
for (const id of result2.alreadyForgotten) {
|
|
17128
|
+
console.log(`${id} is not tracked. Nothing to remove.`);
|
|
17129
|
+
}
|
|
17130
|
+
return;
|
|
16828
17131
|
}
|
|
17132
|
+
printForgetWarnings(result2.warnings);
|
|
17133
|
+
if (result2.alreadyForgotten.length > 0) {
|
|
17134
|
+
for (const id of result2.alreadyForgotten) {
|
|
17135
|
+
console.log(`${id} is not tracked (skipped).`);
|
|
17136
|
+
}
|
|
17137
|
+
}
|
|
17138
|
+
const verb2 = dryRun ? "Would remove" : "Removed";
|
|
17139
|
+
if (result2.removed.length === 1) {
|
|
17140
|
+
const p = result2.removed[0];
|
|
17141
|
+
console.log(`${verb2} 1 patch: "${p.message}" (${p.files.length} files)`);
|
|
17142
|
+
} else {
|
|
17143
|
+
console.log(`${verb2} ${result2.removed.length} patches.`);
|
|
17144
|
+
}
|
|
17145
|
+
console.log(`${result2.remaining} patches remaining.`);
|
|
17146
|
+
return;
|
|
16829
17147
|
}
|
|
16830
|
-
|
|
16831
|
-
|
|
17148
|
+
const searchResult = pattern ? forget(dir, { pattern }) : forget(dir);
|
|
17149
|
+
if (!searchResult.initialized) {
|
|
17150
|
+
console.log("Replay is not initialized. Run 'fern replay init' to get started.");
|
|
17151
|
+
process.exit(1);
|
|
17152
|
+
}
|
|
17153
|
+
const matches = searchResult.matched ?? [];
|
|
17154
|
+
if (matches.length === 0) {
|
|
17155
|
+
if (pattern) {
|
|
17156
|
+
console.log(`No patches found matching "${pattern}". Run 'fern replay status -v' to see all tracked patches.`);
|
|
17157
|
+
} else {
|
|
17158
|
+
console.log("No patches tracked. Nothing to remove.");
|
|
17159
|
+
}
|
|
17160
|
+
return;
|
|
17161
|
+
}
|
|
17162
|
+
if (pattern) {
|
|
17163
|
+
console.log(`Found ${matches.length} patch(es) matching "${pattern}":
|
|
17164
|
+
`);
|
|
17165
|
+
} else {
|
|
17166
|
+
console.log("All tracked patches:\n");
|
|
16832
17167
|
}
|
|
17168
|
+
for (let i = 0; i < matches.length; i++) {
|
|
17169
|
+
console.log(formatPatchLine(i + 1, matches[i]));
|
|
17170
|
+
}
|
|
17171
|
+
if (!process.stdin.isTTY && !yes) {
|
|
17172
|
+
console.error("\nError: Interactive selection required. Use patch IDs directly or add --yes to remove all matches.");
|
|
17173
|
+
process.exit(1);
|
|
17174
|
+
}
|
|
17175
|
+
let selectedIds;
|
|
17176
|
+
if (yes) {
|
|
17177
|
+
selectedIds = matches.map((m) => m.id);
|
|
17178
|
+
} else {
|
|
17179
|
+
console.log();
|
|
17180
|
+
const input = await promptLine("Remove which? (comma-separated numbers, 'all', or 'none'): ");
|
|
17181
|
+
const selection = parseSelection(input, matches.length);
|
|
17182
|
+
if (selection === "none" || selection === null) {
|
|
17183
|
+
if (selection === null) console.log("Invalid selection.");
|
|
17184
|
+
console.log("Aborted.");
|
|
17185
|
+
return;
|
|
17186
|
+
}
|
|
17187
|
+
selectedIds = selection === "all" ? matches.map((m) => m.id) : selection.map((i) => matches[i - 1].id);
|
|
17188
|
+
console.log(`
|
|
17189
|
+
This will remove ${selectedIds.length} patch(es). Affected files will be overwritten on next generation.`);
|
|
17190
|
+
const confirm = await promptLine("Proceed? [y/N] ");
|
|
17191
|
+
if (confirm.toLowerCase() !== "y") {
|
|
17192
|
+
console.log("Aborted.");
|
|
17193
|
+
return;
|
|
17194
|
+
}
|
|
17195
|
+
}
|
|
17196
|
+
const result = forget(dir, { patchIds: selectedIds, dryRun });
|
|
17197
|
+
printForgetWarnings(result.warnings);
|
|
17198
|
+
const verb = dryRun ? "Would remove" : "Removed";
|
|
17199
|
+
console.log(`
|
|
17200
|
+
${verb} ${result.removed.length} patch(es).`);
|
|
17201
|
+
console.log(`${result.remaining} patches remaining.`);
|
|
16833
17202
|
}
|
|
16834
17203
|
async function runReset(dir, flags) {
|
|
16835
17204
|
const dryRun = !!flags.dryRun;
|
|
@@ -16895,7 +17264,7 @@ Resolve the conflicts in your editor, then run 'fern-replay resolve ${dir}' agai
|
|
|
16895
17264
|
console.log("Push to update the PR.");
|
|
16896
17265
|
}
|
|
16897
17266
|
async function main() {
|
|
16898
|
-
const { command, dir, flags,
|
|
17267
|
+
const { command, dir, flags, positionals } = parseArgs(process.argv);
|
|
16899
17268
|
if (!(0, import_node_fs6.existsSync)(dir)) {
|
|
16900
17269
|
console.error(`Directory not found: ${dir}`);
|
|
16901
17270
|
process.exit(1);
|
|
@@ -16905,7 +17274,7 @@ async function main() {
|
|
|
16905
17274
|
await runBootstrap(dir, flags);
|
|
16906
17275
|
break;
|
|
16907
17276
|
case "status":
|
|
16908
|
-
await runStatus(dir);
|
|
17277
|
+
await runStatus(dir, flags);
|
|
16909
17278
|
break;
|
|
16910
17279
|
case "detect":
|
|
16911
17280
|
await runDetect(dir);
|
|
@@ -16917,7 +17286,7 @@ async function main() {
|
|
|
16917
17286
|
await runMigrate(dir);
|
|
16918
17287
|
break;
|
|
16919
17288
|
case "forget":
|
|
16920
|
-
await runForget(dir, flags,
|
|
17289
|
+
await runForget(dir, flags, positionals);
|
|
16921
17290
|
break;
|
|
16922
17291
|
case "reset":
|
|
16923
17292
|
await runReset(dir, flags);
|