@n-seiji/nuthatch 0.1.5 → 0.1.7
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/clean-n2f9z8n3.js +208 -0
- package/dist/cli-23z98dw1.js +116 -0
- package/dist/cli-2bhb9p0x.js +19 -0
- package/dist/cli-3ya15je0.js +114 -0
- package/dist/cli-71jgs14h.js +112 -0
- package/dist/cli-7jg5qqs9.js +88 -0
- package/dist/cli-dbzx9nzg.js +40 -0
- package/dist/cli-g72ra4e5.js +104 -0
- package/dist/cli-pick-w9qk71fy.js +1533 -0
- package/dist/cli-pqd2qe7v.js +35 -0
- package/dist/cli-qrxgkskr.js +69 -0
- package/dist/cli-rcw5adqd.js +124 -0
- package/dist/cli-update-7exha1em.js +891 -0
- package/dist/cli-ygt2w4e1.js +195 -0
- package/dist/cli.js +220 -5841
- package/dist/init-s0dafa83.js +65 -0
- package/dist/jump-nvdkeega.js +103 -0
- package/dist/ls-kvz4wmc9.js +34 -0
- package/dist/render-ls-6r3vv7y0.js +160 -0
- package/dist/rm-231qjgd8.js +7 -0
- package/dist/root-mmyazwc3.js +7 -0
- package/package.json +2 -3
- package/shell/init.zsh +7 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import {
|
|
2
|
+
messageOf
|
|
3
|
+
} from "./cli-qrxgkskr.js";
|
|
4
|
+
import {
|
|
5
|
+
withRepoLock
|
|
6
|
+
} from "./cli-rcw5adqd.js";
|
|
7
|
+
import {
|
|
8
|
+
loadRepoContext,
|
|
9
|
+
isWorktreeDirty,
|
|
10
|
+
resolveBranchCheckout
|
|
11
|
+
} from "./cli-7jg5qqs9.js";
|
|
12
|
+
import {
|
|
13
|
+
EXIT_SAFE_REJECTION,
|
|
14
|
+
ok,
|
|
15
|
+
fail
|
|
16
|
+
} from "./cli-2bhb9p0x.js";
|
|
17
|
+
|
|
18
|
+
// src/commands/root-holder-swap.ts
|
|
19
|
+
var holderRejection = (branch, holderPath, reason) => fail(EXIT_SAFE_REJECTION, `Branch "${branch}" is already checked out at ${holderPath}, and it ${reason}. Not swapping — cd there instead of using hop root.`);
|
|
20
|
+
var prunableHolderRejection = (branch, holderPath) => fail(EXIT_SAFE_REJECTION, `Branch "${branch}" is held by a stale worktree registration at ${holderPath} (git reports it prunable). Not swapping — if that worktree was moved, re-link it with "git worktree repair <new path>"; otherwise drop the registration first ("hop rm ${branch}" or "git worktree prune").`);
|
|
21
|
+
var multipleHoldersRejection = (branch, holders) => fail(EXIT_SAFE_REJECTION, `Branch "${branch}" is checked out at ${holders.length} worktrees at once (${holders.map((holder) => holder.path).join(", ")}). Not swapping — hop won't guess which one to detach.`);
|
|
22
|
+
var resolveHolderSwap = async ({
|
|
23
|
+
git,
|
|
24
|
+
fresh,
|
|
25
|
+
target,
|
|
26
|
+
switchOptions,
|
|
27
|
+
policy
|
|
28
|
+
}) => {
|
|
29
|
+
if (target === "-") {
|
|
30
|
+
return { detachedHolder: null };
|
|
31
|
+
}
|
|
32
|
+
const freshHolders = fresh.worktrees.filter((wt) => wt.branch === target && wt.kind !== "root");
|
|
33
|
+
if (freshHolders.length > 1) {
|
|
34
|
+
return { rejection: multipleHoldersRejection(target, freshHolders) };
|
|
35
|
+
}
|
|
36
|
+
const [freshHolder] = freshHolders;
|
|
37
|
+
if (freshHolder === undefined) {
|
|
38
|
+
return { detachedHolder: null };
|
|
39
|
+
}
|
|
40
|
+
if (policy.expectedPath !== undefined && freshHolder.path !== policy.expectedPath) {
|
|
41
|
+
return {
|
|
42
|
+
rejection: holderRejection(target, freshHolder.path, `changed from the picker selection at ${policy.expectedPath}`)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (freshHolder.kind === "external" && !policy.allowExternal) {
|
|
46
|
+
return {
|
|
47
|
+
rejection: holderRejection(target, freshHolder.path, "is external and requires confirmation before it can be put into detached HEAD")
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (switchOptions.createBranch === true) {
|
|
51
|
+
return {
|
|
52
|
+
rejection: holderRejection(target, freshHolder.path, "wasn't visible when hop checked existing local branches (e.g. it has no commits yet, or was created after that check)")
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (freshHolder.locked) {
|
|
56
|
+
return {
|
|
57
|
+
rejection: holderRejection(target, freshHolder.path, `is locked by git${freshHolder.lockReason === null ? "" : ` (${freshHolder.lockReason})`}`)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (freshHolder.prunable) {
|
|
61
|
+
return { rejection: prunableHolderRejection(target, freshHolder.path) };
|
|
62
|
+
}
|
|
63
|
+
const holderDirty = await isWorktreeDirty(git, fresh.worktrees, freshHolder.path);
|
|
64
|
+
if (holderDirty) {
|
|
65
|
+
return {
|
|
66
|
+
rejection: holderRejection(target, freshHolder.path, "has uncommitted or untracked changes")
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
await git.detachHead(freshHolder.path);
|
|
70
|
+
return { detachedHolder: { path: freshHolder.path, branch: target } };
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// src/commands/root.ts
|
|
74
|
+
var root2 = async (git, fs, options) => {
|
|
75
|
+
const context = await loadRepoContext(git, fs, options.cwd);
|
|
76
|
+
if (options.target === undefined) {
|
|
77
|
+
return ok({
|
|
78
|
+
path: context.rootPath,
|
|
79
|
+
data: {
|
|
80
|
+
branch: rootBranchOf(context),
|
|
81
|
+
switched: false,
|
|
82
|
+
detachedHolder: null
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const dirtyRejection = await dirtyRootRejection(git, context);
|
|
87
|
+
if (dirtyRejection !== null) {
|
|
88
|
+
return dirtyRejection;
|
|
89
|
+
}
|
|
90
|
+
if (options.target === "-") {
|
|
91
|
+
return switchAndReport({
|
|
92
|
+
git,
|
|
93
|
+
fs,
|
|
94
|
+
context,
|
|
95
|
+
target: "-",
|
|
96
|
+
switchOptions: {},
|
|
97
|
+
holderPolicy: { allowExternal: true }
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const checkout = await resolveBranchCheckout(git, context.rootPath, options.target, options.track);
|
|
101
|
+
if (!checkout.ok) {
|
|
102
|
+
return checkout.rejection;
|
|
103
|
+
}
|
|
104
|
+
return switchAndReport({
|
|
105
|
+
git,
|
|
106
|
+
fs,
|
|
107
|
+
context,
|
|
108
|
+
target: options.target,
|
|
109
|
+
switchOptions: checkout.options,
|
|
110
|
+
holderPolicy: {
|
|
111
|
+
allowExternal: options.allowExternalHolderSwap ?? true,
|
|
112
|
+
...options.expectedHolderPath === undefined ? {} : { expectedPath: options.expectedHolderPath }
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
var rootBranchOf = ({ worktrees }) => worktrees.find((wt) => wt.kind === "root")?.branch ?? null;
|
|
117
|
+
var dirtyRootRejection = async (git, context) => {
|
|
118
|
+
const dirty = await isWorktreeDirty(git, context.worktrees, context.rootPath);
|
|
119
|
+
if (!dirty) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
return fail(EXIT_SAFE_REJECTION, "Root clone has uncommitted or untracked changes. Commit, stash, or discard them before switching.");
|
|
123
|
+
};
|
|
124
|
+
var rollBackSwitch = async (git, { rootPath, previousBranch, detachedHolder }) => {
|
|
125
|
+
const warnings = [];
|
|
126
|
+
if (previousBranch !== null) {
|
|
127
|
+
try {
|
|
128
|
+
await git.switchBranch(rootPath, previousBranch, {});
|
|
129
|
+
} catch {
|
|
130
|
+
warnings.push(`Could not restore ${rootPath} to its original branch (${previousBranch})`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (detachedHolder !== null) {
|
|
134
|
+
try {
|
|
135
|
+
await git.switchBranch(detachedHolder.path, detachedHolder.branch, {});
|
|
136
|
+
} catch {
|
|
137
|
+
warnings.push(`Could not restore ${detachedHolder.path} to its original branch (${detachedHolder.branch}); it remains in detached HEAD`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return warnings;
|
|
141
|
+
};
|
|
142
|
+
var switchAndReport = ({
|
|
143
|
+
git,
|
|
144
|
+
fs,
|
|
145
|
+
context,
|
|
146
|
+
target,
|
|
147
|
+
switchOptions,
|
|
148
|
+
holderPolicy
|
|
149
|
+
}) => withRepoLock(context.commonDir, async () => {
|
|
150
|
+
let detachedHolder = null;
|
|
151
|
+
let previousBranch = null;
|
|
152
|
+
try {
|
|
153
|
+
const fresh = await loadRepoContext(git, fs, context.rootPath);
|
|
154
|
+
previousBranch = rootBranchOf(fresh);
|
|
155
|
+
const stillDirty = await dirtyRootRejection(git, fresh);
|
|
156
|
+
if (stillDirty !== null) {
|
|
157
|
+
return stillDirty;
|
|
158
|
+
}
|
|
159
|
+
const holderSwap = await resolveHolderSwap({
|
|
160
|
+
git,
|
|
161
|
+
fresh,
|
|
162
|
+
target,
|
|
163
|
+
switchOptions,
|
|
164
|
+
policy: holderPolicy
|
|
165
|
+
});
|
|
166
|
+
if ("rejection" in holderSwap) {
|
|
167
|
+
return holderSwap.rejection;
|
|
168
|
+
}
|
|
169
|
+
({ detachedHolder } = holderSwap);
|
|
170
|
+
await git.switchBranch(fresh.rootPath, target, switchOptions);
|
|
171
|
+
let resolvedBranch = target;
|
|
172
|
+
if (target === "-") {
|
|
173
|
+
const afterSwitch = await loadRepoContext(git, fs, fresh.rootPath);
|
|
174
|
+
resolvedBranch = rootBranchOf(afterSwitch);
|
|
175
|
+
}
|
|
176
|
+
return ok({
|
|
177
|
+
path: fresh.rootPath,
|
|
178
|
+
data: {
|
|
179
|
+
branch: resolvedBranch,
|
|
180
|
+
switched: true,
|
|
181
|
+
detachedHolder: detachedHolder?.path ?? null
|
|
182
|
+
},
|
|
183
|
+
warnings: detachedHolder === null ? [] : [`Put ${detachedHolder.path} into detached HEAD`]
|
|
184
|
+
});
|
|
185
|
+
} catch (error) {
|
|
186
|
+
const warnings = await rollBackSwitch(git, {
|
|
187
|
+
rootPath: context.rootPath,
|
|
188
|
+
previousBranch,
|
|
189
|
+
detachedHolder
|
|
190
|
+
});
|
|
191
|
+
return fail(EXIT_SAFE_REJECTION, `Failed to switch root: ${messageOf(error)}`, warnings);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
export { root2 };
|