@fern-api/replay 0.16.0 → 0.16.1
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 +2340 -2296
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +73 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +73 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -214,8 +214,19 @@ declare class LockfileNotFoundError extends Error {
|
|
|
214
214
|
declare class LockfileManager {
|
|
215
215
|
private outputDir;
|
|
216
216
|
private lock;
|
|
217
|
+
private fernignorePatterns;
|
|
217
218
|
readonly warnings: string[];
|
|
218
219
|
constructor(outputDir: string);
|
|
220
|
+
/**
|
|
221
|
+
* Inform the lockfile of the customer's `.fernignore` patterns so that
|
|
222
|
+
* `save()` can suppress its "Patch X removed from lockfile" warning when
|
|
223
|
+
* every file in the stripped patch is already `.fernignore`-protected.
|
|
224
|
+
* The strip itself still happens (credentials never persist); only the
|
|
225
|
+
* customer-visible warning is suppressed in that case, because the
|
|
226
|
+
* protected files survive the generator wipe regardless of whether the
|
|
227
|
+
* lockfile tracks them, so there's nothing for the customer to action.
|
|
228
|
+
*/
|
|
229
|
+
setFernignorePatterns(patterns: string[]): void;
|
|
219
230
|
get lockfilePath(): string;
|
|
220
231
|
get customizationsPath(): string;
|
|
221
232
|
exists(): boolean;
|
|
@@ -874,7 +885,8 @@ declare class ReplayService {
|
|
|
874
885
|
*/
|
|
875
886
|
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
876
887
|
cleanupStaleConflictMarkers(): Promise<void>;
|
|
877
|
-
|
|
888
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
889
|
+
readFernignorePatterns(): string[];
|
|
878
890
|
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
879
891
|
buildReport(flow: "first-generation" | "no-patches" | "normal-regeneration" | "skip-application", patches: StoredPatch[], results: ReplayResult[], options?: ReplayOptions, warnings?: string[], rebaseCounts?: {
|
|
880
892
|
absorbed: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -214,8 +214,19 @@ declare class LockfileNotFoundError extends Error {
|
|
|
214
214
|
declare class LockfileManager {
|
|
215
215
|
private outputDir;
|
|
216
216
|
private lock;
|
|
217
|
+
private fernignorePatterns;
|
|
217
218
|
readonly warnings: string[];
|
|
218
219
|
constructor(outputDir: string);
|
|
220
|
+
/**
|
|
221
|
+
* Inform the lockfile of the customer's `.fernignore` patterns so that
|
|
222
|
+
* `save()` can suppress its "Patch X removed from lockfile" warning when
|
|
223
|
+
* every file in the stripped patch is already `.fernignore`-protected.
|
|
224
|
+
* The strip itself still happens (credentials never persist); only the
|
|
225
|
+
* customer-visible warning is suppressed in that case, because the
|
|
226
|
+
* protected files survive the generator wipe regardless of whether the
|
|
227
|
+
* lockfile tracks them, so there's nothing for the customer to action.
|
|
228
|
+
*/
|
|
229
|
+
setFernignorePatterns(patterns: string[]): void;
|
|
219
230
|
get lockfilePath(): string;
|
|
220
231
|
get customizationsPath(): string;
|
|
221
232
|
exists(): boolean;
|
|
@@ -874,7 +885,8 @@ declare class ReplayService {
|
|
|
874
885
|
*/
|
|
875
886
|
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
876
887
|
cleanupStaleConflictMarkers(): Promise<void>;
|
|
877
|
-
|
|
888
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
889
|
+
readFernignorePatterns(): string[];
|
|
878
890
|
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
879
891
|
buildReport(flow: "first-generation" | "no-patches" | "normal-regeneration" | "skip-application", patches: StoredPatch[], results: ReplayResult[], options?: ReplayOptions, warnings?: string[], rebaseCounts?: {
|
|
880
892
|
absorbed: number;
|
package/dist/index.js
CHANGED
|
@@ -533,6 +533,7 @@ function parseRevertedMessage(subject) {
|
|
|
533
533
|
// src/LockfileManager.ts
|
|
534
534
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync } from "fs";
|
|
535
535
|
import { join, dirname } from "path";
|
|
536
|
+
import { minimatch } from "minimatch";
|
|
536
537
|
import { stringify, parse } from "yaml";
|
|
537
538
|
var LOCKFILE_HEADER = "# DO NOT EDIT MANUALLY - Managed by Fern Replay\n";
|
|
538
539
|
var LockfileNotFoundError = class extends Error {
|
|
@@ -544,10 +545,23 @@ var LockfileNotFoundError = class extends Error {
|
|
|
544
545
|
var LockfileManager = class {
|
|
545
546
|
outputDir;
|
|
546
547
|
lock = null;
|
|
548
|
+
fernignorePatterns = [];
|
|
547
549
|
warnings = [];
|
|
548
550
|
constructor(outputDir) {
|
|
549
551
|
this.outputDir = outputDir;
|
|
550
552
|
}
|
|
553
|
+
/**
|
|
554
|
+
* Inform the lockfile of the customer's `.fernignore` patterns so that
|
|
555
|
+
* `save()` can suppress its "Patch X removed from lockfile" warning when
|
|
556
|
+
* every file in the stripped patch is already `.fernignore`-protected.
|
|
557
|
+
* The strip itself still happens (credentials never persist); only the
|
|
558
|
+
* customer-visible warning is suppressed in that case, because the
|
|
559
|
+
* protected files survive the generator wipe regardless of whether the
|
|
560
|
+
* lockfile tracks them, so there's nothing for the customer to action.
|
|
561
|
+
*/
|
|
562
|
+
setFernignorePatterns(patterns) {
|
|
563
|
+
this.fernignorePatterns = patterns;
|
|
564
|
+
}
|
|
551
565
|
get lockfilePath() {
|
|
552
566
|
return join(this.outputDir, ".fern", "replay.lock");
|
|
553
567
|
}
|
|
@@ -616,9 +630,14 @@ var LockfileManager = class {
|
|
|
616
630
|
if (sensitiveFiles.length > 0) {
|
|
617
631
|
reasons.push(`sensitive files: ${sensitiveFiles.join(", ")}`);
|
|
618
632
|
}
|
|
619
|
-
|
|
620
|
-
|
|
633
|
+
const allProtected = patch.files.length > 0 && patch.files.every(
|
|
634
|
+
(f) => this.fernignorePatterns.some((p) => f === p || minimatch(f, p))
|
|
621
635
|
);
|
|
636
|
+
if (!allProtected) {
|
|
637
|
+
this.warnings.push(
|
|
638
|
+
`Patch ${patch.id} removed from lockfile: ${reasons.join("; ")}. This prevents credential exposure in committed lockfiles.`
|
|
639
|
+
);
|
|
640
|
+
}
|
|
622
641
|
} else {
|
|
623
642
|
cleanPatches.push(patch);
|
|
624
643
|
}
|
|
@@ -1501,7 +1520,7 @@ function applyBothPatches(baseLines, oursPatches, theirsPatches) {
|
|
|
1501
1520
|
import { mkdir as mkdir2, mkdtemp, readFile as readFile2, rm, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
1502
1521
|
import { tmpdir } from "os";
|
|
1503
1522
|
import { dirname as dirname3, join as join3 } from "path";
|
|
1504
|
-
import { minimatch } from "minimatch";
|
|
1523
|
+
import { minimatch as minimatch2 } from "minimatch";
|
|
1505
1524
|
|
|
1506
1525
|
// src/accumulator/MergeAccumulator.ts
|
|
1507
1526
|
var MergeAccumulator = class {
|
|
@@ -2217,13 +2236,13 @@ var ReplayApplicator = class {
|
|
|
2217
2236
|
isExcluded(patch) {
|
|
2218
2237
|
const config = this.lockManager.getCustomizationsConfig();
|
|
2219
2238
|
if (!config.exclude) return false;
|
|
2220
|
-
return patch.files.some((file) => config.exclude.some((pattern) =>
|
|
2239
|
+
return patch.files.some((file) => config.exclude.some((pattern) => minimatch2(file, pattern)));
|
|
2221
2240
|
}
|
|
2222
2241
|
async resolveFilePath(filePath, baseTreeHash, currentTreeHash) {
|
|
2223
2242
|
const config = this.lockManager.getCustomizationsConfig();
|
|
2224
2243
|
if (config.moves) {
|
|
2225
2244
|
for (const move of config.moves) {
|
|
2226
|
-
if (
|
|
2245
|
+
if (minimatch2(filePath, move.from) || filePath === move.from) {
|
|
2227
2246
|
if (filePath === move.from) {
|
|
2228
2247
|
return move.to;
|
|
2229
2248
|
}
|
|
@@ -2514,7 +2533,7 @@ Patches absorbed by generator (${buckets.absorbed.length}):`;
|
|
|
2514
2533
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2515
2534
|
import { readFile as readFileAsync } from "fs/promises";
|
|
2516
2535
|
import { join as join6 } from "path";
|
|
2517
|
-
import { minimatch as
|
|
2536
|
+
import { minimatch as minimatch4 } from "minimatch";
|
|
2518
2537
|
init_GitClient();
|
|
2519
2538
|
|
|
2520
2539
|
// src/PatchRegionDiff.ts
|
|
@@ -2625,12 +2644,23 @@ async function computePerPatchDiff(patch, getCurrentGenContent, getWorkingTreeCo
|
|
|
2625
2644
|
unlocatableFiles.push(file);
|
|
2626
2645
|
continue;
|
|
2627
2646
|
}
|
|
2628
|
-
const locInCurrentGen =
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2647
|
+
const locInCurrentGen = [];
|
|
2648
|
+
const locInWorkingTree = [];
|
|
2649
|
+
let spanResolutionFailed = false;
|
|
2650
|
+
for (let i = 0; i < locInCurrentGenRaw.length; i++) {
|
|
2651
|
+
const cg = resolveSpan(locInCurrentGenRaw[i], currentGenLines, "old");
|
|
2652
|
+
const wt = resolveSpan(locInWorkingTreeRaw[i], workingTreeLines, "new");
|
|
2653
|
+
if (cg === null || wt === null) {
|
|
2654
|
+
spanResolutionFailed = true;
|
|
2655
|
+
break;
|
|
2656
|
+
}
|
|
2657
|
+
locInCurrentGen.push(cg);
|
|
2658
|
+
locInWorkingTree.push(wt);
|
|
2659
|
+
}
|
|
2660
|
+
if (spanResolutionFailed) {
|
|
2661
|
+
unlocatableFiles.push(file);
|
|
2662
|
+
continue;
|
|
2663
|
+
}
|
|
2634
2664
|
const overlay = overlayRegions(
|
|
2635
2665
|
currentGenLines,
|
|
2636
2666
|
locInCurrentGen,
|
|
@@ -2692,11 +2722,11 @@ function resolveSpan(loc, oursLines, prefer) {
|
|
|
2692
2722
|
if (prefer === "old") {
|
|
2693
2723
|
if (oldFits) return { ...loc, oursSpan: hunk.oldCount };
|
|
2694
2724
|
if (newFits) return { ...loc, oursSpan: hunk.newCount };
|
|
2695
|
-
|
|
2696
|
-
if (newFits) return { ...loc, oursSpan: hunk.newCount };
|
|
2697
|
-
if (oldFits) return { ...loc, oursSpan: hunk.oldCount };
|
|
2725
|
+
return loc;
|
|
2698
2726
|
}
|
|
2699
|
-
return loc;
|
|
2727
|
+
if (newFits) return { ...loc, oursSpan: hunk.newCount };
|
|
2728
|
+
if (oldFits) return { ...loc, oursSpan: hunk.oldCount };
|
|
2729
|
+
return null;
|
|
2700
2730
|
}
|
|
2701
2731
|
function sequenceMatches(needle, haystack, offset) {
|
|
2702
2732
|
if (offset + needle.length > haystack.length) return false;
|
|
@@ -2810,7 +2840,7 @@ function synthesizeDeleteFileDiff(file, content) {
|
|
|
2810
2840
|
}
|
|
2811
2841
|
|
|
2812
2842
|
// src/classifier/FileOwnership.ts
|
|
2813
|
-
import { minimatch as
|
|
2843
|
+
import { minimatch as minimatch3 } from "minimatch";
|
|
2814
2844
|
var FileOwnership = class {
|
|
2815
2845
|
constructor(git) {
|
|
2816
2846
|
this.git = git;
|
|
@@ -2834,7 +2864,7 @@ var FileOwnership = class {
|
|
|
2834
2864
|
const { file, originalFileName, patch, currentGenSha, fernignorePatterns, warnedGens, warnings } = ctx;
|
|
2835
2865
|
if (patch.user_owned) return true;
|
|
2836
2866
|
if (file === ".fernignore") return true;
|
|
2837
|
-
if (fernignorePatterns.some((p) => file === p ||
|
|
2867
|
+
if (fernignorePatterns.some((p) => file === p || minimatch3(file, p))) return true;
|
|
2838
2868
|
if (fileIsCreationInPatchContent(patch.patch_content, originalFileName ?? file)) {
|
|
2839
2869
|
return true;
|
|
2840
2870
|
}
|
|
@@ -2913,6 +2943,7 @@ var FirstGenerationFlow = class {
|
|
|
2913
2943
|
};
|
|
2914
2944
|
}
|
|
2915
2945
|
async apply(_prep) {
|
|
2946
|
+
this.service.lockManager.setFernignorePatterns(this.service.readFernignorePatterns());
|
|
2916
2947
|
this.service.lockManager.save();
|
|
2917
2948
|
return firstGenerationReport();
|
|
2918
2949
|
}
|
|
@@ -2971,6 +3002,7 @@ var SkipApplicationFlow = class {
|
|
|
2971
3002
|
};
|
|
2972
3003
|
}
|
|
2973
3004
|
async apply(_prep, options) {
|
|
3005
|
+
this.service.lockManager.setFernignorePatterns(this.service.readFernignorePatterns());
|
|
2974
3006
|
this.service.lockManager.save();
|
|
2975
3007
|
const credentialWarnings = [...this.service.lockManager.warnings];
|
|
2976
3008
|
if (!options?.stageOnly) {
|
|
@@ -3094,6 +3126,7 @@ var NoPatchesFlow = class {
|
|
|
3094
3126
|
this.service.lockManager.addPatch(patch);
|
|
3095
3127
|
}
|
|
3096
3128
|
}
|
|
3129
|
+
this.service.lockManager.setFernignorePatterns(this.service.readFernignorePatterns());
|
|
3097
3130
|
this.service.lockManager.save();
|
|
3098
3131
|
this.service.warnings.push(...this.service.lockManager.warnings);
|
|
3099
3132
|
if (newPatches.length > 0) {
|
|
@@ -3256,6 +3289,7 @@ var NormalRegenerationFlow = class {
|
|
|
3256
3289
|
}
|
|
3257
3290
|
}
|
|
3258
3291
|
}
|
|
3292
|
+
this.service.lockManager.setFernignorePatterns(this.service.readFernignorePatterns());
|
|
3259
3293
|
this.service.lockManager.save();
|
|
3260
3294
|
this.service.warnings.push(...this.service.lockManager.warnings);
|
|
3261
3295
|
if (options?.stageOnly) {
|
|
@@ -3489,7 +3523,7 @@ var ReplayService = class {
|
|
|
3489
3523
|
)
|
|
3490
3524
|
);
|
|
3491
3525
|
const hasProtectedFiles = patch.files.some(
|
|
3492
|
-
(file) => file === ".fernignore" || fernignorePatterns.some((p) => file === p ||
|
|
3526
|
+
(file) => file === ".fernignore" || fernignorePatterns.some((p) => file === p || minimatch4(file, p))
|
|
3493
3527
|
);
|
|
3494
3528
|
const allUserOwned = isUserOwnedPerFile.every(Boolean);
|
|
3495
3529
|
if (allUserOwned && patch.files.length > 0) {
|
|
@@ -3929,7 +3963,7 @@ var ReplayService = class {
|
|
|
3929
3963
|
);
|
|
3930
3964
|
if (snapHasStaleMarkers) continue;
|
|
3931
3965
|
const isProtectedSnap = patch.files.some(
|
|
3932
|
-
(file) => file === ".fernignore" || fernignorePatterns.some((p) => file === p ||
|
|
3966
|
+
(file) => file === ".fernignore" || fernignorePatterns.some((p) => file === p || minimatch4(file, p))
|
|
3933
3967
|
);
|
|
3934
3968
|
if (isProtectedSnap) continue;
|
|
3935
3969
|
const snapHash = this.detector.computeContentHash(snapshotDiff);
|
|
@@ -3949,6 +3983,11 @@ var ReplayService = class {
|
|
|
3949
3983
|
(files) => this.git.exec(["diff", currentGen, "HEAD", "--", ...files]).catch(() => null)
|
|
3950
3984
|
);
|
|
3951
3985
|
if (ppResult === null) continue;
|
|
3986
|
+
if (ppResult.hadFallback) {
|
|
3987
|
+
this.warnings.push(
|
|
3988
|
+
`Patch ${patch.id}: couldn't fully isolate ${ppResult.fallbackFiles.join(", ")} \u2014 customization preserved, attribution may have merged with another patch.`
|
|
3989
|
+
);
|
|
3990
|
+
}
|
|
3952
3991
|
const diff = ppResult.diff;
|
|
3953
3992
|
if (!diff.trim()) {
|
|
3954
3993
|
if (await allPatchFilesUserOwned(patch)) continue;
|
|
@@ -3964,7 +4003,7 @@ var ReplayService = class {
|
|
|
3964
4003
|
continue;
|
|
3965
4004
|
}
|
|
3966
4005
|
const isProtected = patch.files.some(
|
|
3967
|
-
(file) => file === ".fernignore" || fernignorePatterns.some((p) => file === p ||
|
|
4006
|
+
(file) => file === ".fernignore" || fernignorePatterns.some((p) => file === p || minimatch4(file, p))
|
|
3968
4007
|
);
|
|
3969
4008
|
if (isProtected) {
|
|
3970
4009
|
continue;
|
|
@@ -4036,6 +4075,11 @@ var ReplayService = class {
|
|
|
4036
4075
|
(files) => this.git.exec(["diff", currentGen, "HEAD", "--", ...files]).catch(() => null)
|
|
4037
4076
|
);
|
|
4038
4077
|
if (ppResult === null) continue;
|
|
4078
|
+
if (ppResult.hadFallback) {
|
|
4079
|
+
this.warnings.push(
|
|
4080
|
+
`Patch ${patch.id}: couldn't fully isolate ${ppResult.fallbackFiles.join(", ")} \u2014 customization preserved, attribution may have merged with another patch.`
|
|
4081
|
+
);
|
|
4082
|
+
}
|
|
4039
4083
|
const diff = ppResult.diff;
|
|
4040
4084
|
if (!diff.trim()) {
|
|
4041
4085
|
if (await allPatchFilesUserOwned(patch)) {
|
|
@@ -4123,13 +4167,14 @@ If you want to keep these lines, restore them in a follow-up commit and re-run r
|
|
|
4123
4167
|
if (files.length === 0) return;
|
|
4124
4168
|
const fernignorePatterns = this.readFernignorePatterns();
|
|
4125
4169
|
for (const file of files) {
|
|
4126
|
-
if (fernignorePatterns.some((pattern) =>
|
|
4170
|
+
if (fernignorePatterns.some((pattern) => minimatch4(file, pattern))) continue;
|
|
4127
4171
|
try {
|
|
4128
4172
|
await this.git.exec(["checkout", "HEAD", "--", file]);
|
|
4129
4173
|
} catch {
|
|
4130
4174
|
}
|
|
4131
4175
|
}
|
|
4132
4176
|
}
|
|
4177
|
+
/** @internal Used by Flow implementations in `src/flows/`. */
|
|
4133
4178
|
readFernignorePatterns() {
|
|
4134
4179
|
const fernignorePath = join6(this.outputDir, ".fernignore");
|
|
4135
4180
|
if (!existsSync2(fernignorePath)) return [];
|
|
@@ -4212,7 +4257,7 @@ function computeCommitBuckets(results, absorbedPatchIds, patchesPassedToCommit)
|
|
|
4212
4257
|
import { createHash as createHash2 } from "crypto";
|
|
4213
4258
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
4214
4259
|
import { dirname as dirname5, join as join7 } from "path";
|
|
4215
|
-
import { minimatch as
|
|
4260
|
+
import { minimatch as minimatch5 } from "minimatch";
|
|
4216
4261
|
import { parse as parse2, stringify as stringify2 } from "yaml";
|
|
4217
4262
|
var FernignoreMigrator = class {
|
|
4218
4263
|
git;
|
|
@@ -4244,7 +4289,7 @@ var FernignoreMigrator = class {
|
|
|
4244
4289
|
const commitsOnly = [];
|
|
4245
4290
|
const syntheticPatches = [];
|
|
4246
4291
|
for (const pattern of patterns) {
|
|
4247
|
-
const matchingPatch = patches.find((p) => p.files.some((f) =>
|
|
4292
|
+
const matchingPatch = patches.find((p) => p.files.some((f) => minimatch5(f, pattern) || f === pattern));
|
|
4248
4293
|
if (matchingPatch) {
|
|
4249
4294
|
trackedByBoth.push({
|
|
4250
4295
|
file: pattern,
|
|
@@ -4317,7 +4362,7 @@ var FernignoreMigrator = class {
|
|
|
4317
4362
|
fernignoreOnly.push(...remainingFernignoreOnly);
|
|
4318
4363
|
}
|
|
4319
4364
|
for (const patch of patches) {
|
|
4320
|
-
const hasUnprotectedFiles = patch.files.some((f) => !patterns.some((p) =>
|
|
4365
|
+
const hasUnprotectedFiles = patch.files.some((f) => !patterns.some((p) => minimatch5(f, p) || f === p));
|
|
4321
4366
|
if (hasUnprotectedFiles) {
|
|
4322
4367
|
commitsOnly.push(patch);
|
|
4323
4368
|
}
|
|
@@ -4329,7 +4374,7 @@ var FernignoreMigrator = class {
|
|
|
4329
4374
|
const results = [];
|
|
4330
4375
|
for (const pattern of patterns) {
|
|
4331
4376
|
const matching = allFiles.filter(
|
|
4332
|
-
(f) =>
|
|
4377
|
+
(f) => minimatch5(f, pattern) || f === pattern || f.startsWith(pattern + "/")
|
|
4333
4378
|
);
|
|
4334
4379
|
results.push({ pattern, files: matching.length > 0 ? matching : [pattern] });
|
|
4335
4380
|
}
|
|
@@ -4642,7 +4687,7 @@ function computeContentHash(patchContent) {
|
|
|
4642
4687
|
}
|
|
4643
4688
|
|
|
4644
4689
|
// src/commands/forget.ts
|
|
4645
|
-
import { minimatch as
|
|
4690
|
+
import { minimatch as minimatch6 } from "minimatch";
|
|
4646
4691
|
function parseDiffStat(patchContent) {
|
|
4647
4692
|
let additions = 0;
|
|
4648
4693
|
let deletions = 0;
|
|
@@ -4672,7 +4717,7 @@ function toMatchedPatch(patch) {
|
|
|
4672
4717
|
}
|
|
4673
4718
|
function matchesPatch(patch, pattern) {
|
|
4674
4719
|
const fileMatch = patch.files.some(
|
|
4675
|
-
(file) => file === pattern ||
|
|
4720
|
+
(file) => file === pattern || minimatch6(file, pattern)
|
|
4676
4721
|
);
|
|
4677
4722
|
if (fileMatch) return true;
|
|
4678
4723
|
return patch.original_message.toLowerCase().includes(pattern.toLowerCase());
|