@gadmin2n/cli 0.0.191 → 0.0.192
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/actions/update.action.js
CHANGED
|
@@ -535,24 +535,38 @@ function pickAdoptions(label, files) {
|
|
|
535
535
|
});
|
|
536
536
|
}
|
|
537
537
|
/**
|
|
538
|
-
* Decide which
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
*
|
|
538
|
+
* Decide which template-side-only files to bring in, then fold the chosen ones
|
|
539
|
+
* into `plan.add` so applyPlan copies them like any other add. Unchosen files
|
|
540
|
+
* are left absent — nothing is copied and no conflict is recorded.
|
|
541
|
+
*
|
|
542
|
+
* Buckets and how they're offered:
|
|
543
|
+
* - `adoptable` never tracked here → promptable (optional feature)
|
|
544
|
+
* - `deleteModifyConflict` absent, template since updated → promptable, and
|
|
545
|
+
* one-time (the next run reclassifies it as
|
|
546
|
+
* `keptDeleted` once base advances)
|
|
547
|
+
* - `keptDeleted` absent, template unchanged → NOT promptable. The
|
|
548
|
+
* absence is an old, already-final decision;
|
|
549
|
+
* re-asking on every update would be pure noise.
|
|
550
|
+
* Only an explicit `--theirs` opts back in.
|
|
543
551
|
*
|
|
544
552
|
* Resolution policy:
|
|
545
553
|
* --theirs → adopt everything; --ours / --yes → adopt nothing;
|
|
546
|
-
* interactive → batch prompt per group (default skip).
|
|
554
|
+
* interactive → batch prompt per promptable group (default skip).
|
|
547
555
|
*/
|
|
548
556
|
function resolveAdoptions(plan, opts, autoYes) {
|
|
549
557
|
return __awaiter(this, void 0, void 0, function* () {
|
|
550
558
|
const groups = [
|
|
551
|
-
{
|
|
559
|
+
{
|
|
560
|
+
bucket: 'adoptable',
|
|
561
|
+
label: 'template file(s) your instance has never had',
|
|
562
|
+
promptable: true,
|
|
563
|
+
},
|
|
552
564
|
{
|
|
553
565
|
bucket: 'deleteModifyConflict',
|
|
554
|
-
label: 'file(s)
|
|
566
|
+
label: 'file(s) absent from your instance that the template has since updated (default: keep absent)',
|
|
567
|
+
promptable: true,
|
|
555
568
|
},
|
|
569
|
+
{ bucket: 'keptDeleted', label: '', promptable: false },
|
|
556
570
|
];
|
|
557
571
|
for (const g of groups) {
|
|
558
572
|
const files = plan[g.bucket];
|
|
@@ -561,7 +575,7 @@ function resolveAdoptions(plan, opts, autoYes) {
|
|
|
561
575
|
let adopt;
|
|
562
576
|
if (opts.theirs)
|
|
563
577
|
adopt = [...files];
|
|
564
|
-
else if (opts.ours || autoYes)
|
|
578
|
+
else if (opts.ours || autoYes || !g.promptable)
|
|
565
579
|
adopt = [];
|
|
566
580
|
else
|
|
567
581
|
adopt = yield pickAdoptions(g.label, files);
|
|
@@ -640,36 +654,48 @@ function applyPlan(plan, baseRoot, oursRoot, theirsRoot, opts) {
|
|
|
640
654
|
});
|
|
641
655
|
}
|
|
642
656
|
}
|
|
643
|
-
//
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
if (opts.theirs) {
|
|
648
|
-
ensureDir(oursPath);
|
|
649
|
-
fs.copyFileSync(theirsPath, oursPath);
|
|
650
|
-
}
|
|
651
|
-
else if (!opts.ours) {
|
|
652
|
-
conflicts.push({
|
|
653
|
-
path: rel,
|
|
654
|
-
kind: 'delete/modify',
|
|
655
|
-
detail: 'instance deleted this file; template modified — file NOT restored',
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
|
-
}
|
|
657
|
+
// keptDeleted / deleteModifyConflict need no handling here: neither is a
|
|
658
|
+
// conflict (the instance has no file to corrupt), so the absence is simply
|
|
659
|
+
// kept. Restoring them is `resolveAdoptions`' job — it folds the chosen ones
|
|
660
|
+
// (all of them under --theirs) into `plan.add` before we run.
|
|
659
661
|
return conflicts;
|
|
660
662
|
}
|
|
661
663
|
/* ------------------------------------------------------------------ *
|
|
662
664
|
* Print summary
|
|
663
665
|
* ------------------------------------------------------------------ */
|
|
666
|
+
const plural = (n) => (n === 1 ? '' : 's');
|
|
667
|
+
/**
|
|
668
|
+
* The two "kept absent" notices share a prefix but must stay textually
|
|
669
|
+
* distinct: one is a one-time heads-up (the template changed a file you don't
|
|
670
|
+
* have), the other a recurring reminder (nothing changed; it stays absent).
|
|
671
|
+
*/
|
|
672
|
+
const KEPT_DELETED_UPDATED_TITLE = '🗑 Kept deleted (template updated since)';
|
|
673
|
+
const KEPT_DELETED_TITLE = '🗑 Kept deleted (template unchanged)';
|
|
674
|
+
/**
|
|
675
|
+
* Recurring reminder for files the template has but the instance doesn't, and
|
|
676
|
+
* which the template hasn't touched since the recorded base. Worded to fit both
|
|
677
|
+
* cases the classifier folds in here: a deliberate deletion (git says the file
|
|
678
|
+
* was tracked) and a non-git instance where we cannot tell deletion apart from
|
|
679
|
+
* "never had it".
|
|
680
|
+
*/
|
|
681
|
+
function printKeptDeletedSection(keptDeleted) {
|
|
682
|
+
if (keptDeleted.length === 0)
|
|
683
|
+
return;
|
|
684
|
+
console.info(chalk.cyan(`${KEPT_DELETED_TITLE} — ${keptDeleted.length} file${plural(keptDeleted.length)} absent from your instance; NOT restored from the template`));
|
|
685
|
+
for (const f of keptDeleted)
|
|
686
|
+
console.info(` ${chalk.cyan('[deleted] ')} ${f}`);
|
|
687
|
+
console.info(chalk.gray(' Informational only — the absence is kept. Restore with --theirs.'));
|
|
688
|
+
console.info();
|
|
689
|
+
}
|
|
664
690
|
/**
|
|
665
691
|
* Unified file-list printer for a `ClassifiedPlan`. Used by both pre-apply
|
|
666
692
|
* preview / dry-run output AND post-apply summary so the wording stays
|
|
667
693
|
* consistent across the three call sites.
|
|
668
694
|
*
|
|
669
695
|
* Sections that have zero files are skipped entirely (no "0 files added"
|
|
670
|
-
* noise). Structural conflicts (add/add, modify/delete
|
|
671
|
-
*
|
|
672
|
-
*
|
|
696
|
+
* noise). Structural conflicts (add/add, modify/delete) are shown together
|
|
697
|
+
* because they're all "needs decision" situations the user should see before
|
|
698
|
+
* applying.
|
|
673
699
|
*
|
|
674
700
|
* The "kept" footer (`plan.keep + plan.noop`) is shown by default; pass
|
|
675
701
|
* `{ showKept: false }` to suppress it.
|
|
@@ -677,7 +703,6 @@ function applyPlan(plan, baseRoot, oursRoot, theirsRoot, opts) {
|
|
|
677
703
|
function printPlanFileLists(plan, opts) {
|
|
678
704
|
var _a;
|
|
679
705
|
const showKept = (_a = opts === null || opts === void 0 ? void 0 : opts.showKept) !== null && _a !== void 0 ? _a : true;
|
|
680
|
-
const plural = (n) => (n === 1 ? '' : 's');
|
|
681
706
|
if (plan.update.length > 0) {
|
|
682
707
|
console.info(chalk.yellow(`📝 Updated (3-way merged) — ${plan.update.length} file${plural(plan.update.length)}`));
|
|
683
708
|
for (const f of plan.update)
|
|
@@ -706,7 +731,6 @@ function printPlanFileLists(plan, opts) {
|
|
|
706
731
|
const structural = [
|
|
707
732
|
...plan.addAddConflict.map((p) => ['add/add', p]),
|
|
708
733
|
...plan.modifyDeleteConflict.map((p) => ['modify/delete', p]),
|
|
709
|
-
...plan.deleteModifyConflict.map((p) => ['delete/modify', p]),
|
|
710
734
|
];
|
|
711
735
|
if (structural.length > 0) {
|
|
712
736
|
console.info(chalk.magenta(`⚠️ Structural conflicts — ${structural.length} file${plural(structural.length)}`));
|
|
@@ -716,6 +740,17 @@ function printPlanFileLists(plan, opts) {
|
|
|
716
740
|
}
|
|
717
741
|
console.info();
|
|
718
742
|
}
|
|
743
|
+
// delete/modify is NOT a conflict: the instance has no file to corrupt.
|
|
744
|
+
// One-time info (reclassified as keptDeleted on later runs, once an applied
|
|
745
|
+
// update advances base past the template change).
|
|
746
|
+
if (plan.deleteModifyConflict.length > 0) {
|
|
747
|
+
console.info(chalk.cyan(`${KEPT_DELETED_UPDATED_TITLE} — ${plan.deleteModifyConflict.length} file${plural(plan.deleteModifyConflict.length)} absent from your instance that the template has since updated`));
|
|
748
|
+
for (const f of plan.deleteModifyConflict)
|
|
749
|
+
console.info(` ${chalk.cyan('[delete/modify] ')} ${f}`);
|
|
750
|
+
console.info(chalk.gray(' Not conflicts — kept absent by default. Restore interactively or with --theirs.'));
|
|
751
|
+
console.info();
|
|
752
|
+
}
|
|
753
|
+
printKeptDeletedSection(plan.keptDeleted);
|
|
719
754
|
if (showKept) {
|
|
720
755
|
const keptCount = plan.keep.length + plan.noop.length;
|
|
721
756
|
if (keptCount > 0) {
|
|
@@ -940,15 +975,31 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
940
975
|
const conflicts = [
|
|
941
976
|
...plan.addAddConflict.map((p) => ['add/add', p]),
|
|
942
977
|
...plan.modifyDeleteConflict.map((p) => ['modify/delete', p]),
|
|
943
|
-
...plan.deleteModifyConflict.map((p) => ['delete/modify', p]),
|
|
944
978
|
];
|
|
945
979
|
if (conflicts.length > 0) {
|
|
946
980
|
console.info(chalk.magenta(`⚠️ Structural conflicts (${conflicts.length} files)`));
|
|
947
981
|
conflicts.forEach(([k, p]) => console.info(` [${k}] ${p}`));
|
|
948
982
|
console.info();
|
|
949
983
|
}
|
|
950
|
-
if (plan.
|
|
951
|
-
console.info(chalk.
|
|
984
|
+
if (plan.deleteModifyConflict.length > 0) {
|
|
985
|
+
console.info(chalk.cyan(`${KEPT_DELETED_UPDATED_TITLE} — absent here, template has since updated (${plan.deleteModifyConflict.length} files)`));
|
|
986
|
+
plan.deleteModifyConflict.forEach((f) => console.info(` [delete/modify] ${f}`));
|
|
987
|
+
console.info();
|
|
988
|
+
}
|
|
989
|
+
if (plan.keptDeleted.length > 0) {
|
|
990
|
+
console.info(chalk.cyan(`${KEPT_DELETED_TITLE} — absent here, NOT restored from template (${plan.keptDeleted.length} files)`));
|
|
991
|
+
plan.keptDeleted.forEach((f) => console.info(` [deleted] ${f}`));
|
|
992
|
+
console.info();
|
|
993
|
+
}
|
|
994
|
+
// `keptDeleted` is a standing state, not a pending change — it never
|
|
995
|
+
// resolves on its own, so counting it here would permanently suppress
|
|
996
|
+
// the "matches template" signal.
|
|
997
|
+
const pending = plan.add.length + plan.delete.length + plan.update.length + plan.adoptable.length +
|
|
998
|
+
plan.deleteModifyConflict.length + conflicts.length;
|
|
999
|
+
if (pending === 0) {
|
|
1000
|
+
console.info(plan.keptDeleted.length > 0
|
|
1001
|
+
? chalk.green('✅ No pending changes. Instance matches template, aside from the kept deletions above.\n')
|
|
1002
|
+
: chalk.green('✅ No differences found. Instance matches template.\n'));
|
|
952
1003
|
}
|
|
953
1004
|
}
|
|
954
1005
|
finally {
|
|
@@ -1011,7 +1062,6 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
1011
1062
|
const structural = [
|
|
1012
1063
|
...plan.addAddConflict.map((p) => ({ kind: 'add/add', path: p })),
|
|
1013
1064
|
...plan.modifyDeleteConflict.map((p) => ({ kind: 'modify/delete', path: p })),
|
|
1014
|
-
...plan.deleteModifyConflict.map((p) => ({ kind: 'delete/modify', path: p })),
|
|
1015
1065
|
];
|
|
1016
1066
|
// Adoptable files are optional (template features the instance never had),
|
|
1017
1067
|
// NOT conflicts — surfaced informationally without affecting clean state.
|
|
@@ -1019,6 +1069,18 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
1019
1069
|
console.info(chalk.cyan(`Adoptable: ${plan.adoptable.length} optional template file(s) available`) +
|
|
1020
1070
|
chalk.gray(' (run `gadmin2 update` to adopt, or `gadmin2 update diff` to list)\n'));
|
|
1021
1071
|
}
|
|
1072
|
+
// delete/modify is informational too: files absent from the instance that
|
|
1073
|
+
// the template has since updated. The absence is kept; not a conflict.
|
|
1074
|
+
if (plan.deleteModifyConflict.length > 0) {
|
|
1075
|
+
console.info(chalk.cyan(`Kept deleted: ${plan.deleteModifyConflict.length} file(s) absent here; template has since updated them`) +
|
|
1076
|
+
chalk.gray(' (run `gadmin2 update` to review, or `gadmin2 update --theirs` to restore)\n'));
|
|
1077
|
+
}
|
|
1078
|
+
// keptDeleted: recurring reminder for files absent from the instance that
|
|
1079
|
+
// the template hasn't touched since — they will NOT be restored.
|
|
1080
|
+
if (plan.keptDeleted.length > 0) {
|
|
1081
|
+
console.info(chalk.cyan(`Kept deleted: ${plan.keptDeleted.length} file(s) absent here; NOT restored from template`) +
|
|
1082
|
+
chalk.gray(' (informational only — restore with `gadmin2 update --theirs`)\n'));
|
|
1083
|
+
}
|
|
1022
1084
|
const totalConflicts = residual.length + structural.length;
|
|
1023
1085
|
if (totalConflicts === 0) {
|
|
1024
1086
|
console.info('Instance state: ' + chalk.green('✅ clean (no unresolved conflicts)\n'));
|
|
@@ -1142,13 +1204,40 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
1142
1204
|
}
|
|
1143
1205
|
const baseRoot = baseHandle.dir;
|
|
1144
1206
|
// Classify three sides. git history lets us tell "instance never had
|
|
1145
|
-
// this file" (adoptable) from "instance deleted it" (
|
|
1207
|
+
// this file" (adoptable) from "instance deleted it" (keptDeleted when
|
|
1208
|
+
// the template hasn't touched it since, deleteModifyConflict when it has).
|
|
1146
1209
|
const oursEverTracked = (0, git_history_1.buildEverTrackedChecker)(instanceDir);
|
|
1147
1210
|
const plan = (0, classify_1.classifyAll)(baseRoot, instanceDir, theirsRoot, userExcludes, oursEverTracked);
|
|
1211
|
+
// `keptDeleted` is a standing state, not a pending change: it never
|
|
1212
|
+
// resolves on its own, so counting it unconditionally would
|
|
1213
|
+
// permanently turn "up to date" into a confirm prompt plus a bogus
|
|
1214
|
+
// "files have been updated, commit them" epilogue. It only becomes
|
|
1215
|
+
// actionable under --theirs, which explicitly opts back into the
|
|
1216
|
+
// template's copy.
|
|
1148
1217
|
const totalChanges = plan.add.length + plan.delete.length + plan.update.length + plan.adoptable.length +
|
|
1149
|
-
plan.addAddConflict.length + plan.modifyDeleteConflict.length + plan.deleteModifyConflict.length
|
|
1218
|
+
plan.addAddConflict.length + plan.modifyDeleteConflict.length + plan.deleteModifyConflict.length +
|
|
1219
|
+
(mergeOpts.theirs ? plan.keptDeleted.length : 0);
|
|
1150
1220
|
if (totalChanges === 0) {
|
|
1221
|
+
printKeptDeletedSection(plan.keptDeleted);
|
|
1151
1222
|
console.info(chalk.green('✅ Instance is up to date with template.\n'));
|
|
1223
|
+
// Nothing to apply, but the instance IS reconciled with this
|
|
1224
|
+
// template version: every remaining difference is a deliberate
|
|
1225
|
+
// ours-side state (kept deletions, files that already match).
|
|
1226
|
+
// Advance the base anyway so later runs diff against the newest
|
|
1227
|
+
// template — otherwise an instance whose only difference is a kept
|
|
1228
|
+
// deletion would stay pinned to an ever-older base forever.
|
|
1229
|
+
if (meta.schematicsVersion !== schematicsVersion) {
|
|
1230
|
+
if (dryRun) {
|
|
1231
|
+
console.info(chalk.gray(` (dry-run: would advance base to schematics ${schematicsVersion})\n`));
|
|
1232
|
+
}
|
|
1233
|
+
else {
|
|
1234
|
+
(0, base_store_1.writeMeta)(instanceDir, {
|
|
1235
|
+
schematicsVersion,
|
|
1236
|
+
schemaVersion: types_1.CURRENT_META_SCHEMA_VERSION,
|
|
1237
|
+
});
|
|
1238
|
+
console.info(chalk.gray(` Base advanced to: ${templateName} @ schematics ${schematicsVersion}\n`));
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1152
1241
|
}
|
|
1153
1242
|
else if (dryRun) {
|
|
1154
1243
|
console.info(chalk.bold('═══════════════════════════════════════════════════'));
|
|
@@ -6,7 +6,7 @@ const fs = require("fs");
|
|
|
6
6
|
const path = require("path");
|
|
7
7
|
const excludes_1 = require("./excludes");
|
|
8
8
|
function classifyFile(input) {
|
|
9
|
-
var _a, _b, _c, _d, _e;
|
|
9
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
10
10
|
const { baseExists, oursExists, theirsExists } = input;
|
|
11
11
|
if (!baseExists && !oursExists && !theirsExists) {
|
|
12
12
|
throw new Error('classifyFile: impossible state — file in none of the three sides');
|
|
@@ -21,19 +21,31 @@ function classifyFile(input) {
|
|
|
21
21
|
if (baseExists && !oursExists && !theirsExists)
|
|
22
22
|
return 'noop';
|
|
23
23
|
if (baseExists && !oursExists && theirsExists) {
|
|
24
|
-
|
|
24
|
+
if (((_b = input.baseEqTheirs) === null || _b === void 0 ? void 0 : _b.call(input)) === true) {
|
|
25
|
+
// Template hasn't touched this file since the recorded base — the
|
|
26
|
+
// instance's deletion is an old, already-final decision. Still
|
|
27
|
+
// surface a recurring info notice (never a conflict): never-tracked →
|
|
28
|
+
// adoptable (optional feature); previously-tracked → keptDeleted
|
|
29
|
+
// ("absent here; we won't restore it"). No callback → we can't tell a
|
|
30
|
+
// deletion from "never had it", so stay on the side that copies
|
|
31
|
+
// nothing (keptDeleted).
|
|
32
|
+
return ((_c = input.oursEverTracked) === null || _c === void 0 ? void 0 : _c.call(input)) === false ? 'adoptable' : 'keptDeleted';
|
|
33
|
+
}
|
|
34
|
+
// Template HAS since modified the file. If the instance NEVER tracked it
|
|
25
35
|
// in git, it's a template feature the instance simply never had → optional
|
|
26
|
-
// adoption (adoptable). If it was tracked and later deleted,
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
|
|
36
|
+
// adoption (adoptable). If it was tracked and later deleted, surface a
|
|
37
|
+
// one-time informational notice (deleteModifyConflict — no longer treated
|
|
38
|
+
// as a blocking conflict; the deletion is kept by default). When we can't
|
|
39
|
+
// tell (no callback / no git), stay conservative → deleteModifyConflict
|
|
40
|
+
// (the historical behaviour).
|
|
41
|
+
return ((_d = input.oursEverTracked) === null || _d === void 0 ? void 0 : _d.call(input)) === false ? 'adoptable' : 'deleteModifyConflict';
|
|
30
42
|
}
|
|
31
43
|
if (baseExists && oursExists && !theirsExists) {
|
|
32
|
-
return ((
|
|
44
|
+
return ((_e = input.oursEqBase) === null || _e === void 0 ? void 0 : _e.call(input)) ? 'delete' : 'modifyDeleteConflict';
|
|
33
45
|
}
|
|
34
46
|
// ✓✓✓ — all three sides exist.
|
|
35
47
|
// Fast path 1: ours already equals theirs → no merge needed.
|
|
36
|
-
if ((
|
|
48
|
+
if ((_f = input.oursEqTheirs) === null || _f === void 0 ? void 0 : _f.call(input))
|
|
37
49
|
return 'keep';
|
|
38
50
|
// Fast path 2: base equals theirs → template hasn't changed for this file
|
|
39
51
|
// since the recorded base. A 3-way merge would produce ours unchanged
|
|
@@ -41,7 +53,7 @@ function classifyFile(input) {
|
|
|
41
53
|
// avoid spurious "Updated" entries on every run when the instance has
|
|
42
54
|
// legitimately diverged from the template — rendered placeholders in
|
|
43
55
|
// init.sql, regenerated yarn.lock files, user-edited .env, etc.
|
|
44
|
-
if ((
|
|
56
|
+
if ((_g = input.baseEqTheirs) === null || _g === void 0 ? void 0 : _g.call(input))
|
|
45
57
|
return 'keep';
|
|
46
58
|
return 'update';
|
|
47
59
|
}
|
|
@@ -57,7 +69,7 @@ function classifyAll(baseDir, oursDir, theirsDir, userExcludes, oursEverTracked)
|
|
|
57
69
|
const all = new Set([...baseFiles, ...oursFiles, ...theirsFiles]);
|
|
58
70
|
const plan = {
|
|
59
71
|
add: [], keep: [], noop: [],
|
|
60
|
-
delete: [], update: [], adoptable: [],
|
|
72
|
+
delete: [], update: [], adoptable: [], keptDeleted: [],
|
|
61
73
|
addAddConflict: [], modifyDeleteConflict: [], deleteModifyConflict: [],
|
|
62
74
|
};
|
|
63
75
|
for (const rel of [...all].sort()) {
|
|
@@ -6,12 +6,14 @@
|
|
|
6
6
|
* state:
|
|
7
7
|
* - never tracked → the template carries a file the instance never had
|
|
8
8
|
* (a new feature) → `adoptable` (optional adoption).
|
|
9
|
-
* - was tracked → the instance had it and deleted it on purpose
|
|
10
|
-
*
|
|
9
|
+
* - was tracked → the instance had it and deleted it on purpose → the
|
|
10
|
+
* deletion is kept: `keptDeleted` when the template hasn't
|
|
11
|
+
* touched the file since the recorded base,
|
|
12
|
+
* `deleteModifyConflict` when it has.
|
|
11
13
|
*
|
|
12
14
|
* Safety-first fallback: when the directory is NOT a git repo, or git is not on
|
|
13
15
|
* PATH, we return a checker that reports EVERYTHING as tracked (`true`). That
|
|
14
|
-
* keeps the classifier's conservative default (
|
|
16
|
+
* keeps the classifier's conservative default (never silently adopt) and makes
|
|
15
17
|
* this feature a no-op for non-git projects — zero behaviour change for them.
|
|
16
18
|
*
|
|
17
19
|
* A git repo with no commits yet resolves to an empty set → every path reports
|
|
@@ -11,12 +11,14 @@ const child_process_1 = require("child_process");
|
|
|
11
11
|
* state:
|
|
12
12
|
* - never tracked → the template carries a file the instance never had
|
|
13
13
|
* (a new feature) → `adoptable` (optional adoption).
|
|
14
|
-
* - was tracked → the instance had it and deleted it on purpose
|
|
15
|
-
*
|
|
14
|
+
* - was tracked → the instance had it and deleted it on purpose → the
|
|
15
|
+
* deletion is kept: `keptDeleted` when the template hasn't
|
|
16
|
+
* touched the file since the recorded base,
|
|
17
|
+
* `deleteModifyConflict` when it has.
|
|
16
18
|
*
|
|
17
19
|
* Safety-first fallback: when the directory is NOT a git repo, or git is not on
|
|
18
20
|
* PATH, we return a checker that reports EVERYTHING as tracked (`true`). That
|
|
19
|
-
* keeps the classifier's conservative default (
|
|
21
|
+
* keeps the classifier's conservative default (never silently adopt) and makes
|
|
20
22
|
* this feature a no-op for non-git projects — zero behaviour change for them.
|
|
21
23
|
*
|
|
22
24
|
* A git repo with no commits yet resolves to an empty set → every path reports
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** 单个文件在三方合并中的分类结果。 */
|
|
2
|
-
export declare type ClassifyResult = 'add' | 'keep' | 'noop' | 'delete' | 'update' | 'adoptable' | 'addAddConflict' | 'modifyDeleteConflict' | 'deleteModifyConflict';
|
|
2
|
+
export declare type ClassifyResult = 'add' | 'keep' | 'noop' | 'delete' | 'update' | 'adoptable' | 'keptDeleted' | 'addAddConflict' | 'modifyDeleteConflict' | 'deleteModifyConflict';
|
|
3
3
|
export interface ClassifiedPlan {
|
|
4
4
|
add: string[];
|
|
5
5
|
keep: string[];
|
|
@@ -7,11 +7,12 @@ export interface ClassifiedPlan {
|
|
|
7
7
|
delete: string[];
|
|
8
8
|
update: string[];
|
|
9
9
|
adoptable: string[];
|
|
10
|
+
keptDeleted: string[];
|
|
10
11
|
addAddConflict: string[];
|
|
11
12
|
modifyDeleteConflict: string[];
|
|
12
13
|
deleteModifyConflict: string[];
|
|
13
14
|
}
|
|
14
|
-
export declare type ConflictKind = 'content' | 'add/add' | 'modify/delete' | '
|
|
15
|
+
export declare type ConflictKind = 'content' | 'add/add' | 'modify/delete' | 'binary';
|
|
15
16
|
export interface ConflictRecord {
|
|
16
17
|
path: string;
|
|
17
18
|
kind: ConflictKind;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gadmin2n/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.192",
|
|
4
4
|
"description": "Gadmin - modern, fast, powerful node.js web framework (@cli)",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -24,8 +24,9 @@
|
|
|
24
24
|
"prepublish:npm": "npm run build",
|
|
25
25
|
"publish:npm": "npm publish --access public",
|
|
26
26
|
"updateAndPublish": "node ./updateVersion.js",
|
|
27
|
+
"pretest": "npm run build",
|
|
27
28
|
"test": "jest --config test/jest-config.json",
|
|
28
|
-
"test:dev": "npm run
|
|
29
|
+
"test:dev": "npm run build && jest --config test/jest-config.json --watchAll",
|
|
29
30
|
"prerelease": "npm run build",
|
|
30
31
|
"release": "release-it"
|
|
31
32
|
},
|