vde-worktree 0.0.15 → 0.0.16
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/README.ja.md +8 -3
- package/README.md +8 -3
- package/dist/index.mjs +128 -84
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -427,9 +427,14 @@ vw completion zsh --install
|
|
|
427
427
|
|
|
428
428
|
`overall` ポリシー:
|
|
429
429
|
|
|
430
|
-
- `byPR === true` -> `overall = true
|
|
431
|
-
- `
|
|
432
|
-
- `
|
|
430
|
+
- `byPR === true` -> `overall = true`(squash/rebase merge を含む)
|
|
431
|
+
- `byAncestry === false` -> `overall = false`
|
|
432
|
+
- `byAncestry === true` の場合は、分岐の証跡があるときだけ merged 扱い
|
|
433
|
+
- `.vde/worktree/state/branches/*.json` の lifecycle 記録
|
|
434
|
+
- lifecycle がない場合の `git reflog` フォールバック
|
|
435
|
+
- 分岐証跡が `baseBranch` に取り込まれていれば `overall = true`
|
|
436
|
+
- `byPR === false` または lifecycle が明示的に未取り込みなら `overall = false`
|
|
437
|
+
- それ以外は `overall = null`
|
|
433
438
|
|
|
434
439
|
`byPR` が `null` になる例:
|
|
435
440
|
|
package/README.md
CHANGED
|
@@ -427,9 +427,14 @@ Each worktree reports:
|
|
|
427
427
|
|
|
428
428
|
Overall policy:
|
|
429
429
|
|
|
430
|
-
- `byPR === true` => `overall = true`
|
|
431
|
-
- `
|
|
432
|
-
- `
|
|
430
|
+
- `byPR === true` => `overall = true` (includes squash/rebase merges)
|
|
431
|
+
- `byAncestry === false` => `overall = false`
|
|
432
|
+
- when `byAncestry === true`, require divergence evidence before treating as merged
|
|
433
|
+
- lifecycle evidence from `.vde/worktree/state/branches/*.json`
|
|
434
|
+
- reflog fallback (`git reflog`) when lifecycle evidence is missing
|
|
435
|
+
- if divergence evidence is contained in `baseBranch`, `overall = true`
|
|
436
|
+
- `byPR === false` or explicit lifecycle "not merged" evidence => `overall = false`
|
|
437
|
+
- otherwise `overall = null`
|
|
433
438
|
|
|
434
439
|
`byPR` becomes `null` when PR lookup is unavailable (for example: `gh` missing, auth missing, API error, `vde-worktree.enableGh=false`, or `--no-gh`).
|
|
435
440
|
|
package/dist/index.mjs
CHANGED
|
@@ -617,7 +617,8 @@ const hasStateDirectory = async (repoRoot) => {
|
|
|
617
617
|
const parseLifecycle = (content) => {
|
|
618
618
|
try {
|
|
619
619
|
const parsed = JSON.parse(content);
|
|
620
|
-
|
|
620
|
+
const isLastDivergedHeadValid = parsed.lastDivergedHead === null || typeof parsed.lastDivergedHead === "string" && parsed.lastDivergedHead.length > 0;
|
|
621
|
+
if (parsed.schemaVersion !== 2 || typeof parsed.branch !== "string" || typeof parsed.worktreeId !== "string" || typeof parsed.baseBranch !== "string" || typeof parsed.everDiverged !== "boolean" || isLastDivergedHeadValid !== true || typeof parsed.createdAt !== "string" || typeof parsed.updatedAt !== "string") return {
|
|
621
622
|
valid: false,
|
|
622
623
|
record: null
|
|
623
624
|
};
|
|
@@ -665,15 +666,17 @@ const readWorktreeMergeLifecycle = async ({ repoRoot, branch }) => {
|
|
|
665
666
|
};
|
|
666
667
|
}
|
|
667
668
|
};
|
|
668
|
-
const upsertWorktreeMergeLifecycle = async ({ repoRoot, branch, baseBranch,
|
|
669
|
+
const upsertWorktreeMergeLifecycle = async ({ repoRoot, branch, baseBranch, observedDivergedHead }) => {
|
|
670
|
+
const normalizedObservedHead = typeof observedDivergedHead === "string" && observedDivergedHead.length > 0 ? observedDivergedHead : null;
|
|
669
671
|
if (await hasStateDirectory(repoRoot) !== true) {
|
|
670
672
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
671
673
|
return {
|
|
672
|
-
schemaVersion:
|
|
674
|
+
schemaVersion: 2,
|
|
673
675
|
branch,
|
|
674
676
|
worktreeId: branchToWorktreeId(branch),
|
|
675
677
|
baseBranch,
|
|
676
|
-
|
|
678
|
+
everDiverged: normalizedObservedHead !== null,
|
|
679
|
+
lastDivergedHead: normalizedObservedHead,
|
|
677
680
|
createdAt: now,
|
|
678
681
|
updatedAt: now
|
|
679
682
|
};
|
|
@@ -682,14 +685,17 @@ const upsertWorktreeMergeLifecycle = async ({ repoRoot, branch, baseBranch, crea
|
|
|
682
685
|
repoRoot,
|
|
683
686
|
branch
|
|
684
687
|
});
|
|
685
|
-
if (current.valid && current.record !== null && current.record.baseBranch === baseBranch) return current.record;
|
|
688
|
+
if (current.valid && current.record !== null && current.record.baseBranch === baseBranch && normalizedObservedHead === null) return current.record;
|
|
686
689
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
690
|
+
const everDiverged = current.record?.everDiverged === true || normalizedObservedHead !== null;
|
|
691
|
+
const lastDivergedHead = normalizedObservedHead ?? current.record?.lastDivergedHead ?? null;
|
|
687
692
|
const next = {
|
|
688
|
-
schemaVersion:
|
|
693
|
+
schemaVersion: 2,
|
|
689
694
|
branch,
|
|
690
695
|
worktreeId: branchToWorktreeId(branch),
|
|
691
696
|
baseBranch,
|
|
692
|
-
|
|
697
|
+
everDiverged,
|
|
698
|
+
lastDivergedHead,
|
|
693
699
|
createdAt: current.record?.createdAt ?? now,
|
|
694
700
|
updatedAt: now
|
|
695
701
|
};
|
|
@@ -699,15 +705,17 @@ const upsertWorktreeMergeLifecycle = async ({ repoRoot, branch, baseBranch, crea
|
|
|
699
705
|
});
|
|
700
706
|
return next;
|
|
701
707
|
};
|
|
702
|
-
const moveWorktreeMergeLifecycle = async ({ repoRoot, fromBranch, toBranch, baseBranch,
|
|
708
|
+
const moveWorktreeMergeLifecycle = async ({ repoRoot, fromBranch, toBranch, baseBranch, observedDivergedHead }) => {
|
|
709
|
+
const normalizedObservedHead = typeof observedDivergedHead === "string" && observedDivergedHead.length > 0 ? observedDivergedHead : null;
|
|
703
710
|
if (await hasStateDirectory(repoRoot) !== true) {
|
|
704
711
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
705
712
|
return {
|
|
706
|
-
schemaVersion:
|
|
713
|
+
schemaVersion: 2,
|
|
707
714
|
branch: toBranch,
|
|
708
715
|
worktreeId: branchToWorktreeId(toBranch),
|
|
709
716
|
baseBranch,
|
|
710
|
-
|
|
717
|
+
everDiverged: normalizedObservedHead !== null,
|
|
718
|
+
lastDivergedHead: normalizedObservedHead,
|
|
711
719
|
createdAt: now,
|
|
712
720
|
updatedAt: now
|
|
713
721
|
};
|
|
@@ -718,12 +726,15 @@ const moveWorktreeMergeLifecycle = async ({ repoRoot, fromBranch, toBranch, base
|
|
|
718
726
|
});
|
|
719
727
|
const targetPath = lifecycleFilePath(repoRoot, toBranch);
|
|
720
728
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
729
|
+
const everDiverged = source.record?.everDiverged === true || normalizedObservedHead !== null;
|
|
730
|
+
const lastDivergedHead = normalizedObservedHead ?? source.record?.lastDivergedHead ?? null;
|
|
721
731
|
const next = {
|
|
722
|
-
schemaVersion:
|
|
732
|
+
schemaVersion: 2,
|
|
723
733
|
branch: toBranch,
|
|
724
734
|
worktreeId: branchToWorktreeId(toBranch),
|
|
725
735
|
baseBranch,
|
|
726
|
-
|
|
736
|
+
everDiverged,
|
|
737
|
+
lastDivergedHead,
|
|
727
738
|
createdAt: source.record?.createdAt ?? now,
|
|
728
739
|
updatedAt: now
|
|
729
740
|
};
|
|
@@ -1049,6 +1060,56 @@ const resolveLockState = async ({ repoRoot, branch }) => {
|
|
|
1049
1060
|
};
|
|
1050
1061
|
}
|
|
1051
1062
|
};
|
|
1063
|
+
const WORK_REFLOG_MESSAGE_PATTERN = /^(commit(?: \([^)]*\))?|cherry-pick|revert|rebase \(pick\)|merge):/;
|
|
1064
|
+
const resolveLifecycleFromReflog = async ({ repoRoot, branch, baseBranch }) => {
|
|
1065
|
+
const reflog = await runGitCommand({
|
|
1066
|
+
cwd: repoRoot,
|
|
1067
|
+
args: [
|
|
1068
|
+
"reflog",
|
|
1069
|
+
"show",
|
|
1070
|
+
"--format=%H%x09%gs",
|
|
1071
|
+
branch
|
|
1072
|
+
],
|
|
1073
|
+
reject: false
|
|
1074
|
+
});
|
|
1075
|
+
if (reflog.exitCode !== 0) return {
|
|
1076
|
+
merged: null,
|
|
1077
|
+
divergedHead: null
|
|
1078
|
+
};
|
|
1079
|
+
let latestWorkHead = null;
|
|
1080
|
+
for (const line of reflog.stdout.split("\n")) {
|
|
1081
|
+
const trimmed = line.trim();
|
|
1082
|
+
if (trimmed.length === 0) continue;
|
|
1083
|
+
const separatorIndex = trimmed.indexOf(" ");
|
|
1084
|
+
if (separatorIndex <= 0) continue;
|
|
1085
|
+
const head = trimmed.slice(0, separatorIndex).trim();
|
|
1086
|
+
const message = trimmed.slice(separatorIndex + 1).trim();
|
|
1087
|
+
if (head.length === 0 || WORK_REFLOG_MESSAGE_PATTERN.test(message) !== true) continue;
|
|
1088
|
+
if (latestWorkHead === null) latestWorkHead = head;
|
|
1089
|
+
const result = await runGitCommand({
|
|
1090
|
+
cwd: repoRoot,
|
|
1091
|
+
args: [
|
|
1092
|
+
"merge-base",
|
|
1093
|
+
"--is-ancestor",
|
|
1094
|
+
head,
|
|
1095
|
+
baseBranch
|
|
1096
|
+
],
|
|
1097
|
+
reject: false
|
|
1098
|
+
});
|
|
1099
|
+
if (result.exitCode === 0) return {
|
|
1100
|
+
merged: true,
|
|
1101
|
+
divergedHead: head
|
|
1102
|
+
};
|
|
1103
|
+
if (result.exitCode !== 1) return {
|
|
1104
|
+
merged: null,
|
|
1105
|
+
divergedHead: latestWorkHead
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
return {
|
|
1109
|
+
merged: false,
|
|
1110
|
+
divergedHead: latestWorkHead
|
|
1111
|
+
};
|
|
1112
|
+
};
|
|
1052
1113
|
const resolveMergedState = async ({ repoRoot, branch, head, baseBranch, mergedByPrByBranch }) => {
|
|
1053
1114
|
if (branch === null) return {
|
|
1054
1115
|
byAncestry: null,
|
|
@@ -1077,10 +1138,39 @@ const resolveMergedState = async ({ repoRoot, branch, head, baseBranch, mergedBy
|
|
|
1077
1138
|
repoRoot,
|
|
1078
1139
|
branch,
|
|
1079
1140
|
baseBranch,
|
|
1080
|
-
|
|
1141
|
+
observedDivergedHead: byAncestry === false ? head : null
|
|
1081
1142
|
});
|
|
1082
|
-
if (byAncestry ===
|
|
1083
|
-
else if (byAncestry ===
|
|
1143
|
+
if (byAncestry === false) byLifecycle = false;
|
|
1144
|
+
else if (byAncestry === true) if (lifecycle.everDiverged !== true || lifecycle.lastDivergedHead === null) if (byPR === true) byLifecycle = null;
|
|
1145
|
+
else {
|
|
1146
|
+
const probe = await resolveLifecycleFromReflog({
|
|
1147
|
+
repoRoot,
|
|
1148
|
+
branch,
|
|
1149
|
+
baseBranch
|
|
1150
|
+
});
|
|
1151
|
+
byLifecycle = probe.merged;
|
|
1152
|
+
if (probe.divergedHead !== null) await upsertWorktreeMergeLifecycle({
|
|
1153
|
+
repoRoot,
|
|
1154
|
+
branch,
|
|
1155
|
+
baseBranch,
|
|
1156
|
+
observedDivergedHead: probe.divergedHead
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
else {
|
|
1160
|
+
const lifecycleResult = await runGitCommand({
|
|
1161
|
+
cwd: repoRoot,
|
|
1162
|
+
args: [
|
|
1163
|
+
"merge-base",
|
|
1164
|
+
"--is-ancestor",
|
|
1165
|
+
lifecycle.lastDivergedHead,
|
|
1166
|
+
baseBranch
|
|
1167
|
+
],
|
|
1168
|
+
reject: false
|
|
1169
|
+
});
|
|
1170
|
+
if (lifecycleResult.exitCode === 0) byLifecycle = true;
|
|
1171
|
+
else if (lifecycleResult.exitCode === 1) byLifecycle = false;
|
|
1172
|
+
else byLifecycle = null;
|
|
1173
|
+
}
|
|
1084
1174
|
}
|
|
1085
1175
|
return {
|
|
1086
1176
|
byAncestry,
|
|
@@ -1897,22 +1987,6 @@ const resolveBaseBranch = async (repoRoot) => {
|
|
|
1897
1987
|
for (const candidate of ["main", "master"]) if (await doesGitRefExist(repoRoot, `refs/heads/${candidate}`)) return candidate;
|
|
1898
1988
|
throw createCliError("INVALID_ARGUMENT", { message: "Unable to resolve base branch. Configure vde-worktree.baseBranch." });
|
|
1899
1989
|
};
|
|
1900
|
-
const resolveBranchHead = async ({ repoRoot, branch }) => {
|
|
1901
|
-
const resolved = await runGitCommand({
|
|
1902
|
-
cwd: repoRoot,
|
|
1903
|
-
args: [
|
|
1904
|
-
"rev-parse",
|
|
1905
|
-
"--verify",
|
|
1906
|
-
branch
|
|
1907
|
-
],
|
|
1908
|
-
reject: false
|
|
1909
|
-
});
|
|
1910
|
-
if (resolved.exitCode !== 0 || resolved.stdout.trim().length === 0) throw createCliError("INVALID_ARGUMENT", {
|
|
1911
|
-
message: `Failed to resolve branch head: ${branch}`,
|
|
1912
|
-
details: { branch }
|
|
1913
|
-
});
|
|
1914
|
-
return resolved.stdout.trim();
|
|
1915
|
-
};
|
|
1916
1990
|
const ensureTargetPathWritable = async (targetPath) => {
|
|
1917
1991
|
try {
|
|
1918
1992
|
await access(targetPath, constants.F_OK);
|
|
@@ -3095,10 +3169,7 @@ const createCli = (options = {}) => {
|
|
|
3095
3169
|
repoRoot,
|
|
3096
3170
|
branch,
|
|
3097
3171
|
baseBranch,
|
|
3098
|
-
|
|
3099
|
-
repoRoot,
|
|
3100
|
-
branch
|
|
3101
|
-
})
|
|
3172
|
+
observedDivergedHead: null
|
|
3102
3173
|
});
|
|
3103
3174
|
await runPostHook({
|
|
3104
3175
|
name: "new",
|
|
@@ -3133,18 +3204,12 @@ const createCli = (options = {}) => {
|
|
|
3133
3204
|
const snapshot = await collectWorktreeSnapshot$1(repoRoot);
|
|
3134
3205
|
const existing = snapshot.worktrees.find((worktree) => worktree.branch === branch);
|
|
3135
3206
|
if (existing !== void 0) {
|
|
3136
|
-
if (snapshot.baseBranch !== null) {
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
repoRoot,
|
|
3143
|
-
branch,
|
|
3144
|
-
baseBranch: snapshot.baseBranch,
|
|
3145
|
-
createdHead: branchHead
|
|
3146
|
-
});
|
|
3147
|
-
}
|
|
3207
|
+
if (snapshot.baseBranch !== null) await upsertWorktreeMergeLifecycle({
|
|
3208
|
+
repoRoot,
|
|
3209
|
+
branch,
|
|
3210
|
+
baseBranch: snapshot.baseBranch,
|
|
3211
|
+
observedDivergedHead: null
|
|
3212
|
+
});
|
|
3148
3213
|
return {
|
|
3149
3214
|
status: "existing",
|
|
3150
3215
|
branch,
|
|
@@ -3190,18 +3255,12 @@ const createCli = (options = {}) => {
|
|
|
3190
3255
|
]
|
|
3191
3256
|
});
|
|
3192
3257
|
}
|
|
3193
|
-
if (lifecycleBaseBranch !== null) {
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
repoRoot,
|
|
3200
|
-
branch,
|
|
3201
|
-
baseBranch: lifecycleBaseBranch,
|
|
3202
|
-
createdHead: branchHead
|
|
3203
|
-
});
|
|
3204
|
-
}
|
|
3258
|
+
if (lifecycleBaseBranch !== null) await upsertWorktreeMergeLifecycle({
|
|
3259
|
+
repoRoot,
|
|
3260
|
+
branch,
|
|
3261
|
+
baseBranch: lifecycleBaseBranch,
|
|
3262
|
+
observedDivergedHead: null
|
|
3263
|
+
});
|
|
3205
3264
|
await runPostHook({
|
|
3206
3265
|
name: "switch",
|
|
3207
3266
|
context: hookContext
|
|
@@ -3301,19 +3360,13 @@ const createCli = (options = {}) => {
|
|
|
3301
3360
|
newPath
|
|
3302
3361
|
]
|
|
3303
3362
|
});
|
|
3304
|
-
if (snapshot.baseBranch !== null) {
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
fromBranch: oldBranch,
|
|
3312
|
-
toBranch: newBranch,
|
|
3313
|
-
baseBranch: snapshot.baseBranch,
|
|
3314
|
-
createdHead: branchHead
|
|
3315
|
-
});
|
|
3316
|
-
}
|
|
3363
|
+
if (snapshot.baseBranch !== null) await moveWorktreeMergeLifecycle({
|
|
3364
|
+
repoRoot,
|
|
3365
|
+
fromBranch: oldBranch,
|
|
3366
|
+
toBranch: newBranch,
|
|
3367
|
+
baseBranch: snapshot.baseBranch,
|
|
3368
|
+
observedDivergedHead: null
|
|
3369
|
+
});
|
|
3317
3370
|
await runPostHook({
|
|
3318
3371
|
name: "mv",
|
|
3319
3372
|
context: hookContext
|
|
@@ -3582,10 +3635,7 @@ const createCli = (options = {}) => {
|
|
|
3582
3635
|
repoRoot,
|
|
3583
3636
|
branch,
|
|
3584
3637
|
baseBranch: lifecycleBaseBranch,
|
|
3585
|
-
|
|
3586
|
-
repoRoot,
|
|
3587
|
-
branch
|
|
3588
|
-
})
|
|
3638
|
+
observedDivergedHead: null
|
|
3589
3639
|
});
|
|
3590
3640
|
await runPostHook({
|
|
3591
3641
|
name: "get",
|
|
@@ -3612,10 +3662,7 @@ const createCli = (options = {}) => {
|
|
|
3612
3662
|
repoRoot,
|
|
3613
3663
|
branch,
|
|
3614
3664
|
baseBranch: lifecycleBaseBranch,
|
|
3615
|
-
|
|
3616
|
-
repoRoot,
|
|
3617
|
-
branch
|
|
3618
|
-
})
|
|
3665
|
+
observedDivergedHead: null
|
|
3619
3666
|
});
|
|
3620
3667
|
await runPostHook({
|
|
3621
3668
|
name: "get",
|
|
@@ -3727,10 +3774,7 @@ const createCli = (options = {}) => {
|
|
|
3727
3774
|
repoRoot,
|
|
3728
3775
|
branch,
|
|
3729
3776
|
baseBranch,
|
|
3730
|
-
|
|
3731
|
-
repoRoot,
|
|
3732
|
-
branch
|
|
3733
|
-
})
|
|
3777
|
+
observedDivergedHead: null
|
|
3734
3778
|
});
|
|
3735
3779
|
if (stashOid !== null) {
|
|
3736
3780
|
if ((await runGitCommand({
|