reslop 0.2.5 → 0.2.6
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/CHANGELOG.md +12 -1
- package/LICENSE +1 -1
- package/README.md +11 -11
- package/bin/reslop.js +3 -8
- package/lib/ansi.js +32 -19
- package/lib/capabilities.js +8 -8
- package/lib/cli.js +40 -74
- package/lib/clipboard.js +39 -47
- package/lib/dashboard/git.js +128 -0
- package/lib/dashboard/model.js +332 -0
- package/lib/dashboard/npm.js +193 -0
- package/lib/dashboard/tree.js +278 -0
- package/lib/deps-diff.js +197 -402
- package/lib/deps.js +113 -240
- package/lib/detect.js +8 -18
- package/lib/diff/align.js +41 -113
- package/lib/diff/diff.js +12 -43
- package/lib/diff/display.js +25 -36
- package/lib/diff/edit.js +6 -20
- package/lib/diff/inline.js +76 -144
- package/lib/diff/items.js +33 -47
- package/lib/diff/parse.js +28 -56
- package/lib/diff/patch.js +12 -33
- package/lib/diff/unit.js +79 -178
- package/lib/editor.js +52 -69
- package/lib/files.js +71 -89
- package/lib/git-branches.js +254 -416
- package/lib/git-deps.js +147 -272
- package/lib/git-reword-todo.js +5 -13
- package/lib/git-worktree.js +141 -212
- package/lib/git.js +21 -49
- package/lib/github.js +67 -181
- package/lib/gitlab.js +40 -90
- package/lib/highlight/bash.js +55 -177
- package/lib/highlight/core.js +155 -141
- package/lib/highlight/css.js +25 -58
- package/lib/highlight/csv.js +19 -37
- package/lib/highlight/dart.js +72 -213
- package/lib/highlight/dot.js +14 -14
- package/lib/highlight/html.js +42 -63
- package/lib/highlight/js.js +177 -506
- package/lib/highlight/json.js +17 -37
- package/lib/highlight/log.js +19 -25
- package/lib/highlight/txt.js +7 -10
- package/lib/highlight.js +17 -46
- package/lib/keys.js +49 -82
- package/lib/manifest.js +67 -126
- package/lib/npm-commands.js +120 -169
- package/lib/remote.js +140 -190
- package/lib/render/branches.js +63 -61
- package/lib/render/chrome.js +220 -333
- package/lib/render/commits.js +160 -361
- package/lib/render/dash-activity.js +212 -0
- package/lib/render/dash-blocks.js +245 -0
- package/lib/render/dash-table.js +290 -0
- package/lib/render/dashboard.js +62 -0
- package/lib/render/diff.js +123 -258
- package/lib/render/files.js +40 -85
- package/lib/render/frame.js +78 -117
- package/lib/render/notes.js +139 -210
- package/lib/render/npm.js +213 -177
- package/lib/render/present.js +4 -11
- package/lib/render/primitives.js +72 -174
- package/lib/render/render.js +5 -14
- package/lib/render/report.js +43 -0
- package/lib/render/tiles.js +260 -0
- package/lib/render/unit.js +15 -31
- package/lib/report-model.js +110 -33
- package/lib/report-parse.js +56 -37
- package/lib/report-render.js +102 -59
- package/lib/report-run.js +87 -77
- package/lib/report-test.js +221 -99
- package/lib/report-tools.js +96 -154
- package/lib/review-merge.js +33 -41
- package/lib/review.js +68 -129
- package/lib/runs.js +225 -0
- package/lib/select.js +53 -91
- package/lib/session/accessors.js +14 -21
- package/lib/session/actions.js +61 -43
- package/lib/session/branches.js +166 -222
- package/lib/session/change-actions.js +49 -119
- package/lib/session/commit.js +274 -355
- package/lib/session/compose.js +556 -714
- package/lib/session/dashboard.js +385 -0
- package/lib/session/dispatch.js +162 -170
- package/lib/session/files-pane.js +76 -44
- package/lib/session/items.js +6 -17
- package/lib/session/lifecycle.js +393 -466
- package/lib/session/load.js +1 -14
- package/lib/session/navigation.js +2 -2
- package/lib/session/npm.js +403 -420
- package/lib/session/ops.js +5 -13
- package/lib/session/pointer.js +16 -14
- package/lib/session/progress.js +39 -30
- package/lib/session/review.js +1 -3
- package/lib/session/tasks.js +276 -235
- package/lib/session/templates.js +51 -59
- package/lib/session/terminal.js +85 -132
- package/lib/session/view.js +47 -81
- package/lib/session/watch.js +196 -212
- package/lib/session.js +81 -161
- package/lib/update.js +44 -111
- package/lib/utilities.js +46 -88
- package/lib/wrap.js +29 -55
- package/package.json +1 -1
package/lib/git-worktree.js
CHANGED
|
@@ -3,17 +3,15 @@
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const { runProc, runProcSync, isAbort } =
|
|
6
|
+
const utilities = require('./utilities.js');
|
|
7
|
+
const { runProc, runProcSync, isAbort, oneLine } = utilities;
|
|
8
8
|
const diff = require('./diff/diff.js');
|
|
9
9
|
const { parseDiff, synthesizeNewFile, itemsFromFiles } = diff;
|
|
10
10
|
const files = require('./files.js');
|
|
11
|
-
const { itemPath, REVIEW_DIR } = files;
|
|
11
|
+
const { itemPath, isReadOnlyOrigin, REVIEW_DIR } = files;
|
|
12
12
|
const edit = require('./diff/edit.js');
|
|
13
13
|
const { replaceBlockAdds } = edit;
|
|
14
14
|
|
|
15
|
-
const oneLine = (text, fallback = 'git failed') => proc.oneLine(text, fallback);
|
|
16
|
-
|
|
17
15
|
const GIT_CONFIG = [
|
|
18
16
|
'-c',
|
|
19
17
|
'core.quotepath=false',
|
|
@@ -21,14 +19,11 @@ const GIT_CONFIG = [
|
|
|
21
19
|
'i18n.logOutputEncoding=utf-8',
|
|
22
20
|
];
|
|
23
21
|
const DIFF_OPTS = ['--no-color', '--no-ext-diff', '--no-renames', '-U3'];
|
|
24
|
-
const
|
|
25
|
-
const GIT_MAX_BUFFER = proc.DEFAULT_MAX_BUFFER;
|
|
22
|
+
const BINARY_SIZE = 1000000;
|
|
26
23
|
|
|
27
|
-
const
|
|
24
|
+
const gitOptions = (cwd, extra) => ({
|
|
28
25
|
cwd,
|
|
29
26
|
env: { ...process.env, GIT_OPTIONAL_LOCKS: '0', ...extra.env },
|
|
30
|
-
encoding: GIT_ENCODING,
|
|
31
|
-
maxBuffer: GIT_MAX_BUFFER,
|
|
32
27
|
input: extra.input,
|
|
33
28
|
signal: extra.signal,
|
|
34
29
|
});
|
|
@@ -46,37 +41,61 @@ const requireOk = (result, fallback = 'git failed') => {
|
|
|
46
41
|
throw new Error(oneLine(msg, fallback));
|
|
47
42
|
};
|
|
48
43
|
|
|
49
|
-
const runGit = (args, cwd,
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
const runGit = (args, cwd, extra = {}) =>
|
|
45
|
+
gitResult(runProcSync('git', args, gitOptions(cwd, extra)));
|
|
46
|
+
|
|
47
|
+
const runGitAsync = (args, cwd, extra = {}) =>
|
|
48
|
+
runProc('git', args, gitOptions(cwd, extra)).then(gitResult);
|
|
49
|
+
|
|
50
|
+
const command = (args, extra = {}) => ({
|
|
51
|
+
args,
|
|
52
|
+
input: extra.input,
|
|
53
|
+
env: extra.env,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const runSteps = (steps, top) => {
|
|
57
|
+
try {
|
|
58
|
+
let step = steps.next();
|
|
59
|
+
while (!step.done) {
|
|
60
|
+
step = steps.next(runGit(step.value.args, top, step.value));
|
|
61
|
+
}
|
|
62
|
+
return step.value;
|
|
63
|
+
} finally {
|
|
64
|
+
steps.return();
|
|
65
|
+
}
|
|
52
66
|
};
|
|
53
67
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
68
|
+
const runStepsAsync = async (steps, top, signal) => {
|
|
69
|
+
try {
|
|
70
|
+
let step = steps.next();
|
|
71
|
+
while (!step.done) {
|
|
72
|
+
const extra = { ...step.value, signal };
|
|
73
|
+
step = steps.next(await runGitAsync(step.value.args, top, extra));
|
|
74
|
+
}
|
|
75
|
+
return step.value;
|
|
76
|
+
} finally {
|
|
77
|
+
steps.return();
|
|
78
|
+
}
|
|
57
79
|
};
|
|
58
80
|
|
|
59
|
-
const
|
|
81
|
+
const gitOp = (op) => ({
|
|
82
|
+
sync: (top, ...args) => runSteps(op(...args), top),
|
|
83
|
+
async: (top, ...args) => runStepsAsync(op(...args), top),
|
|
84
|
+
});
|
|
60
85
|
|
|
61
|
-
const
|
|
62
|
-
...GIT_CONFIG,
|
|
63
|
-
'diff',
|
|
64
|
-
'--cached',
|
|
65
|
-
...DIFF_OPTS,
|
|
66
|
-
...pathArgs,
|
|
67
|
-
];
|
|
86
|
+
const pathArgs = (paths) => (paths.length ? ['--', ...paths] : []);
|
|
68
87
|
|
|
69
|
-
const
|
|
88
|
+
const diffArgs = (revs, paths) => [
|
|
70
89
|
...GIT_CONFIG,
|
|
71
90
|
'diff',
|
|
72
91
|
...DIFF_OPTS,
|
|
73
|
-
...
|
|
92
|
+
...revs,
|
|
93
|
+
...pathArgs(paths),
|
|
74
94
|
];
|
|
75
95
|
|
|
76
|
-
const commitDiffArgs = (commit, parent,
|
|
96
|
+
const commitDiffArgs = (commit, parent, paths) => {
|
|
77
97
|
if (parent.status === 0) {
|
|
78
|
-
|
|
79
|
-
return [...GIT_CONFIG, 'diff', ...DIFF_OPTS, from, commit, ...pathArgs];
|
|
98
|
+
return diffArgs([parent.stdout.trim(), commit], paths);
|
|
80
99
|
}
|
|
81
100
|
return [
|
|
82
101
|
...GIT_CONFIG,
|
|
@@ -86,62 +105,34 @@ const commitDiffArgs = (commit, parent, pathArgs) => {
|
|
|
86
105
|
'-p',
|
|
87
106
|
...DIFF_OPTS,
|
|
88
107
|
commit,
|
|
89
|
-
...pathArgs,
|
|
108
|
+
...pathArgs(paths),
|
|
90
109
|
];
|
|
91
110
|
};
|
|
92
111
|
|
|
93
|
-
const
|
|
112
|
+
const untrackedArgs = (paths) => [
|
|
113
|
+
'ls-files',
|
|
114
|
+
'-o',
|
|
115
|
+
'--exclude-standard',
|
|
116
|
+
'-z',
|
|
117
|
+
...pathArgs(paths),
|
|
118
|
+
];
|
|
94
119
|
|
|
95
|
-
const isReviewFile = (rel) =>
|
|
96
|
-
if (!rel) return false;
|
|
97
|
-
const first = rel.split(/[/\\]/)[0];
|
|
98
|
-
return first === REVIEW_DIR;
|
|
99
|
-
};
|
|
120
|
+
const isReviewFile = (rel) => rel.split(/[/\\]/)[0] === REVIEW_DIR;
|
|
100
121
|
|
|
101
122
|
const dropReviewItems = (items) =>
|
|
102
123
|
items.filter((item) => !isReviewFile(itemPath(item)));
|
|
103
124
|
|
|
104
|
-
const isBinaryBuffer = (buf) =>
|
|
105
|
-
if (buf.length > BINARY_SIZE) return true;
|
|
106
|
-
return buf.includes(0);
|
|
107
|
-
};
|
|
125
|
+
const isBinaryBuffer = (buf) => buf.length > BINARY_SIZE || buf.includes(0);
|
|
108
126
|
|
|
109
|
-
const namesFromLs = (stdout) =>
|
|
110
|
-
if (!stdout) return [];
|
|
111
|
-
return stdout.split('\0').filter(Boolean);
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const untrackedLsArgs = (paths) => {
|
|
115
|
-
const args = ['ls-files', '-o', '--exclude-standard', '-z'];
|
|
116
|
-
if (paths.length) args.push('--', ...paths);
|
|
117
|
-
return args;
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const listUntracked = (top, paths) => {
|
|
121
|
-
const result = requireOk(runGit(untrackedLsArgs(paths), top));
|
|
122
|
-
return namesFromLs(result.stdout);
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
const filterListed = (names) =>
|
|
126
|
-
names.filter((rel) => rel && !isReviewFile(rel));
|
|
127
|
-
|
|
128
|
-
const listTreeArgs = (rev, paths) => {
|
|
129
|
-
const args = ['ls-tree', '-r', '--name-only', '-z', rev];
|
|
130
|
-
if (paths.length) args.push('--', ...paths);
|
|
131
|
-
return args;
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const listWorktreeArgs = (paths) => {
|
|
135
|
-
const args = ['ls-files', '-co', '--exclude-standard', '-z'];
|
|
136
|
-
if (paths.length) args.push('--', ...paths);
|
|
137
|
-
return args;
|
|
138
|
-
};
|
|
127
|
+
const namesFromLs = (stdout) => stdout.split('\0').filter(Boolean);
|
|
139
128
|
|
|
140
129
|
const listScopeFiles = (top, paths = [], options = {}) => {
|
|
141
130
|
const rev = options.commit;
|
|
142
|
-
const args = rev
|
|
131
|
+
const args = rev
|
|
132
|
+
? ['ls-tree', '-r', '--name-only', '-z', rev, ...pathArgs(paths)]
|
|
133
|
+
: ['ls-files', '-co', '--exclude-standard', '-z', ...pathArgs(paths)];
|
|
143
134
|
const result = requireOk(runGit(args, top), 'ls-files failed');
|
|
144
|
-
const names =
|
|
135
|
+
const names = namesFromLs(result.stdout).filter((rel) => !isReviewFile(rel));
|
|
145
136
|
names.sort((left, right) => left.localeCompare(right, 'en'));
|
|
146
137
|
return names;
|
|
147
138
|
};
|
|
@@ -160,43 +151,33 @@ const untrackedItemsFrom = (top, names) => {
|
|
|
160
151
|
const files = [];
|
|
161
152
|
for (const rel of names) {
|
|
162
153
|
if (isReviewFile(rel)) continue;
|
|
163
|
-
const full = path.join(top, rel);
|
|
164
154
|
let buf;
|
|
165
155
|
try {
|
|
166
|
-
buf = fs.readFileSync(
|
|
156
|
+
buf = fs.readFileSync(path.join(top, rel));
|
|
167
157
|
} catch (error) {
|
|
168
158
|
if (error.code === 'ENOENT') continue;
|
|
169
159
|
throw error;
|
|
170
160
|
}
|
|
171
|
-
if (isBinaryBuffer(buf))
|
|
172
|
-
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
files.push(synthesizeNewFile(rel, buf.toString('utf8')));
|
|
161
|
+
if (isBinaryBuffer(buf)) files.push(binaryNewFile(rel));
|
|
162
|
+
else files.push(synthesizeNewFile(rel, buf.toString('utf8')));
|
|
176
163
|
}
|
|
177
164
|
return itemsFromFiles(files, 'untracked');
|
|
178
165
|
};
|
|
179
166
|
|
|
180
|
-
const
|
|
167
|
+
const groupByPath = (items) => {
|
|
181
168
|
const byPath = new Map();
|
|
182
|
-
for (const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
list.push(item);
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
byPath.set(rel, [item]);
|
|
191
|
-
}
|
|
169
|
+
for (const item of items) {
|
|
170
|
+
const rel = itemPath(item);
|
|
171
|
+
const list = byPath.get(rel);
|
|
172
|
+
if (list) list.push(item);
|
|
173
|
+
else byPath.set(rel, [item]);
|
|
192
174
|
}
|
|
193
|
-
return
|
|
175
|
+
return byPath;
|
|
194
176
|
};
|
|
195
177
|
|
|
196
178
|
const gitText = (top, spec) => {
|
|
197
179
|
const result = runGit(['show', spec], top);
|
|
198
|
-
|
|
199
|
-
return result.stdout ?? '';
|
|
180
|
+
return result.status === 0 ? result.stdout : '';
|
|
200
181
|
};
|
|
201
182
|
|
|
202
183
|
const worktreeText = (top, rel) => {
|
|
@@ -215,24 +196,19 @@ const fileText = (top, rel, rev) => {
|
|
|
215
196
|
|
|
216
197
|
const readDepSides = (top, rev, origin, rel) => {
|
|
217
198
|
if (origin === 'untracked') {
|
|
218
|
-
|
|
219
|
-
return { oldText: '', newText };
|
|
199
|
+
return { oldText: '', newText: worktreeText(top, rel) };
|
|
220
200
|
}
|
|
221
201
|
if (origin === 'staged') {
|
|
222
202
|
const oldText = gitText(top, `HEAD:${rel}`);
|
|
223
|
-
|
|
224
|
-
return { oldText, newText };
|
|
203
|
+
return { oldText, newText: gitText(top, `:${rel}`) };
|
|
225
204
|
}
|
|
226
205
|
if (origin === 'unstaged') {
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
const newText = worktreeText(top, rel);
|
|
230
|
-
return { oldText, newText };
|
|
206
|
+
const oldText = gitText(top, `:${rel}`) || gitText(top, `HEAD:${rel}`);
|
|
207
|
+
return { oldText, newText: worktreeText(top, rel) };
|
|
231
208
|
}
|
|
232
209
|
if (origin === 'commit' && rev) {
|
|
233
210
|
const oldText = gitText(top, `${rev}^:${rel}`);
|
|
234
|
-
|
|
235
|
-
return { oldText, newText };
|
|
211
|
+
return { oldText, newText: gitText(top, `${rev}:${rel}`) };
|
|
236
212
|
}
|
|
237
213
|
return { oldText: '', newText: '' };
|
|
238
214
|
};
|
|
@@ -245,106 +221,77 @@ const toplevel = (cwd) => {
|
|
|
245
221
|
|
|
246
222
|
const resolveRev = (cwd, spec) => {
|
|
247
223
|
if (!spec) return null;
|
|
248
|
-
const top = toplevel(cwd);
|
|
249
224
|
const args = ['rev-parse', '--verify', `${spec}^{commit}`];
|
|
250
|
-
const result = runGit(args,
|
|
251
|
-
|
|
252
|
-
return result.stdout.trim();
|
|
225
|
+
const result = runGit(args, toplevel(cwd));
|
|
226
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
253
227
|
};
|
|
254
228
|
|
|
255
229
|
const shortRev = (top, commit) => {
|
|
256
230
|
const result = runGit(['rev-parse', '--short', commit], top);
|
|
257
|
-
|
|
258
|
-
return result.stdout.trim();
|
|
231
|
+
return result.status === 0 ? result.stdout.trim() : commit.slice(0, 7);
|
|
259
232
|
};
|
|
260
233
|
|
|
261
234
|
const currentBranch = (top) => {
|
|
262
|
-
const
|
|
263
|
-
const result = runGit(args, top);
|
|
235
|
+
const result = runGit(['rev-parse', '--abbrev-ref', 'HEAD'], top);
|
|
264
236
|
if (result.status !== 0) return '';
|
|
265
237
|
const name = result.stdout.trim();
|
|
266
|
-
|
|
267
|
-
return name;
|
|
238
|
+
return name === 'HEAD' ? '' : name;
|
|
268
239
|
};
|
|
269
240
|
|
|
270
|
-
const snapshotWorktree = (top,
|
|
271
|
-
const
|
|
272
|
-
const
|
|
273
|
-
const
|
|
274
|
-
const
|
|
275
|
-
const
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
const branch = currentBranch(top);
|
|
279
|
-
return { top, parsed, branch };
|
|
241
|
+
const snapshotWorktree = (top, results) => {
|
|
242
|
+
const outputs = results.map((result) => requireOk(result).stdout);
|
|
243
|
+
const staged = itemsFromFiles(parseDiff(outputs[0]), 'staged');
|
|
244
|
+
const unstaged = itemsFromFiles(parseDiff(outputs[1]), 'unstaged');
|
|
245
|
+
const untracked = untrackedItemsFrom(top, namesFromLs(outputs[2]));
|
|
246
|
+
const grouped = groupByPath([...staged, ...unstaged, ...untracked]);
|
|
247
|
+
const parsed = dropReviewItems([...grouped.values()].flat());
|
|
248
|
+
return { top, parsed, branch: currentBranch(top) };
|
|
280
249
|
};
|
|
281
250
|
|
|
282
251
|
const snapshotCommit = (top, commit, files) => {
|
|
283
|
-
const
|
|
284
|
-
const parsed = dropReviewItems(commitItems);
|
|
252
|
+
const parsed = dropReviewItems(itemsFromFiles(files, 'commit'));
|
|
285
253
|
const revShort = shortRev(top, commit);
|
|
286
|
-
|
|
287
|
-
return { top, parsed, rev: commit, revShort, branch };
|
|
254
|
+
return { top, parsed, rev: commit, revShort, branch: currentBranch(top) };
|
|
288
255
|
};
|
|
289
256
|
|
|
290
|
-
|
|
291
|
-
const parent =
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
};
|
|
257
|
+
function* commitDiff(commit, paths) {
|
|
258
|
+
const parent = yield command(['rev-parse', '--verify', `${commit}^`]);
|
|
259
|
+
const result = yield command(commitDiffArgs(commit, parent, paths));
|
|
260
|
+
return parseDiff(requireOk(result).stdout);
|
|
261
|
+
}
|
|
296
262
|
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
);
|
|
303
|
-
const args = commitDiffArgs(commit, parent, extra);
|
|
304
|
-
const result = requireOk(await runGitAsync(args, top, { signal }));
|
|
305
|
-
return parseDiff(result.stdout);
|
|
306
|
-
};
|
|
263
|
+
const worktreeCommands = (paths) => [
|
|
264
|
+
diffArgs(['--cached'], paths),
|
|
265
|
+
diffArgs([], paths),
|
|
266
|
+
untrackedArgs(paths),
|
|
267
|
+
];
|
|
307
268
|
|
|
308
269
|
const readSnapshot = (cwd, paths, options) => {
|
|
309
270
|
const top = toplevel(cwd);
|
|
310
|
-
const extra = pathArgsOf(paths);
|
|
311
271
|
const rev = options.commit;
|
|
312
|
-
if (rev)
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
return snapshotWorktree(top,
|
|
272
|
+
if (rev) {
|
|
273
|
+
return snapshotCommit(top, rev, runSteps(commitDiff(rev, paths), top));
|
|
274
|
+
}
|
|
275
|
+
const results = worktreeCommands(paths).map((args) => runGit(args, top));
|
|
276
|
+
return snapshotWorktree(top, results);
|
|
317
277
|
};
|
|
318
278
|
|
|
319
279
|
const readSnapshotAsync = async (cwd, paths, options) => {
|
|
320
280
|
const top = toplevel(cwd);
|
|
321
|
-
const
|
|
322
|
-
const rev = options.commit;
|
|
323
|
-
const signal = options.signal;
|
|
281
|
+
const { commit: rev, signal } = options;
|
|
324
282
|
if (rev) {
|
|
325
|
-
const files = await
|
|
283
|
+
const files = await runStepsAsync(commitDiff(rev, paths), top, signal);
|
|
326
284
|
return snapshotCommit(top, rev, files);
|
|
327
285
|
}
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const unstagedP = runGitAsync(unstagedArgs, top, { signal });
|
|
333
|
-
const lsP = runGitAsync(lsArgs, top, { signal });
|
|
334
|
-
const extras = await Promise.all([stagedP, unstagedP, lsP]);
|
|
335
|
-
const stagedRes = extras[0];
|
|
336
|
-
const unstagedRes = extras[1];
|
|
337
|
-
const lsRes = extras[2];
|
|
338
|
-
requireOk(stagedRes);
|
|
339
|
-
requireOk(unstagedRes);
|
|
340
|
-
const names = namesFromLs(requireOk(lsRes).stdout);
|
|
341
|
-
return snapshotWorktree(top, stagedRes.stdout, unstagedRes.stdout, names);
|
|
286
|
+
const running = worktreeCommands(paths).map((args) =>
|
|
287
|
+
runGitAsync(args, top, { signal }),
|
|
288
|
+
);
|
|
289
|
+
return snapshotWorktree(top, await Promise.all(running));
|
|
342
290
|
};
|
|
343
291
|
|
|
344
292
|
const applyPatch = (cwd, patch, flags) => {
|
|
345
293
|
const args = ['apply', ...flags, '--whitespace=nowarn', '-'];
|
|
346
|
-
|
|
347
|
-
requireOk(result, 'apply failed');
|
|
294
|
+
requireOk(runGit(args, cwd, { input: patch }), 'apply failed');
|
|
348
295
|
};
|
|
349
296
|
|
|
350
297
|
const addItem = (top, item) => {
|
|
@@ -357,35 +304,26 @@ const addItem = (top, item) => {
|
|
|
357
304
|
|
|
358
305
|
const unstageItem = (top, item) => {
|
|
359
306
|
if (item.origin !== 'staged') return;
|
|
360
|
-
const rel = itemPath(item);
|
|
361
307
|
if (item.file.isBinary) {
|
|
362
|
-
|
|
308
|
+
const args = ['restore', '--staged', '--', itemPath(item)];
|
|
309
|
+
return void requireOk(runGit(args, top));
|
|
363
310
|
}
|
|
364
311
|
applyPatch(top, item.patchAdd, ['--reverse', '--cached']);
|
|
365
312
|
};
|
|
366
313
|
|
|
367
314
|
const revertOnePath = (top, rel, items) => {
|
|
368
|
-
const origins = new Set();
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
if (item.file && item.file.isNew) isNew = true;
|
|
373
|
-
}
|
|
374
|
-
if (isNew) {
|
|
375
|
-
if (origins.has('staged')) {
|
|
376
|
-
return void requireOk(runGit(['rm', '-f', '--', rel], top));
|
|
377
|
-
}
|
|
378
|
-
return void fs.unlinkSync(path.join(top, rel));
|
|
379
|
-
}
|
|
380
|
-
if (origins.has('untracked') && origins.size === 1) {
|
|
381
|
-
return void fs.unlinkSync(path.join(top, rel));
|
|
315
|
+
const origins = new Set(items.map((item) => item.origin));
|
|
316
|
+
const isNew = items.some((item) => item.file?.isNew);
|
|
317
|
+
if (isNew && origins.has('staged')) {
|
|
318
|
+
return void requireOk(runGit(['rm', '-f', '--', rel], top));
|
|
382
319
|
}
|
|
320
|
+
const onlyUntracked = origins.size === 1 && origins.has('untracked');
|
|
321
|
+
if (isNew || onlyUntracked) return void fs.unlinkSync(path.join(top, rel));
|
|
383
322
|
const args = ['restore', '-s', 'HEAD', '--worktree', '--staged', '--', rel];
|
|
384
323
|
requireOk(runGit(args, top));
|
|
385
324
|
};
|
|
386
325
|
|
|
387
326
|
const revertItem = (top, item) => {
|
|
388
|
-
if (item.origin === 'commit') return;
|
|
389
327
|
const rel = itemPath(item);
|
|
390
328
|
if (item.origin === 'untracked') {
|
|
391
329
|
return void fs.unlinkSync(path.join(top, rel));
|
|
@@ -393,8 +331,7 @@ const revertItem = (top, item) => {
|
|
|
393
331
|
if (item.file.isBinary) {
|
|
394
332
|
const args = ['restore', '-s', 'HEAD', '--worktree'];
|
|
395
333
|
if (item.origin === 'staged') args.push('--staged');
|
|
396
|
-
args
|
|
397
|
-
return void requireOk(runGit(args, top));
|
|
334
|
+
return void requireOk(runGit([...args, '--', rel], top));
|
|
398
335
|
}
|
|
399
336
|
if (item.origin === 'unstaged') {
|
|
400
337
|
return void applyPatch(top, item.patchRevert, ['--reverse']);
|
|
@@ -407,12 +344,6 @@ const revertItem = (top, item) => {
|
|
|
407
344
|
}
|
|
408
345
|
};
|
|
409
346
|
|
|
410
|
-
const indexText = (top, rel) => {
|
|
411
|
-
const result = runGit(['show', `:${rel}`], top);
|
|
412
|
-
requireOk(result, 'show failed');
|
|
413
|
-
return result.stdout ?? '';
|
|
414
|
-
};
|
|
415
|
-
|
|
416
347
|
const indexMode = (top, rel) => {
|
|
417
348
|
const result = requireOk(runGit(['ls-files', '--stage', '--', rel], top));
|
|
418
349
|
const line = result.stdout.trim();
|
|
@@ -428,17 +359,17 @@ const indexMode = (top, rel) => {
|
|
|
428
359
|
|
|
429
360
|
const writeIndex = (top, rel, text) => {
|
|
430
361
|
const hashArgs = ['hash-object', '-w', `--path=${rel}`, '--stdin'];
|
|
431
|
-
const hashed = runGit(hashArgs, top, text);
|
|
432
|
-
requireOk(hashed, 'hash-object failed');
|
|
433
|
-
const sha = (hashed.stdout ?? '').trim();
|
|
362
|
+
const hashed = runGit(hashArgs, top, { input: text });
|
|
363
|
+
const sha = requireOk(hashed, 'hash-object failed').stdout.trim();
|
|
434
364
|
const mode = indexMode(top, rel);
|
|
435
365
|
const args = ['update-index', '--add', '--cacheinfo', mode, sha, rel];
|
|
436
366
|
requireOk(runGit(args, top), 'update-index failed');
|
|
437
367
|
};
|
|
438
368
|
|
|
439
|
-
const editStaged = (top, rel, abs,
|
|
440
|
-
const
|
|
441
|
-
const
|
|
369
|
+
const editStaged = (top, rel, abs, item, text) => {
|
|
370
|
+
const shown = requireOk(runGit(['show', `:${rel}`], top), 'show failed');
|
|
371
|
+
const source = shown.stdout;
|
|
372
|
+
const next = replaceBlockAdds(source, item.hunk, item.blockId, text);
|
|
442
373
|
writeIndex(top, rel, next);
|
|
443
374
|
let work = null;
|
|
444
375
|
try {
|
|
@@ -446,28 +377,24 @@ const editStaged = (top, rel, abs, hunk, blockId, text) => {
|
|
|
446
377
|
} catch (error) {
|
|
447
378
|
if (error.code !== 'ENOENT') throw error;
|
|
448
379
|
}
|
|
449
|
-
if (work === null)
|
|
450
|
-
return void fs.writeFileSync(abs, next);
|
|
451
|
-
}
|
|
452
|
-
if (work === source) fs.writeFileSync(abs, next);
|
|
380
|
+
if (work === null || work === source) fs.writeFileSync(abs, next);
|
|
453
381
|
};
|
|
454
382
|
|
|
455
|
-
const editWorktree = (abs,
|
|
383
|
+
const editWorktree = (abs, item, text) => {
|
|
456
384
|
const source = fs.readFileSync(abs, 'utf8');
|
|
457
|
-
const next = replaceBlockAdds(source, hunk, blockId, text);
|
|
458
|
-
if (next
|
|
459
|
-
fs.writeFileSync(abs, next);
|
|
385
|
+
const next = replaceBlockAdds(source, item.hunk, item.blockId, text);
|
|
386
|
+
if (next !== source) fs.writeFileSync(abs, next);
|
|
460
387
|
};
|
|
461
388
|
|
|
462
389
|
const editItem = (top, item, text) => {
|
|
463
|
-
if (item.origin
|
|
390
|
+
if (isReadOnlyOrigin(item.origin)) return;
|
|
464
391
|
const rel = itemPath(item);
|
|
465
392
|
if (!rel || !item.hunk) return;
|
|
466
393
|
const abs = path.join(top, rel);
|
|
467
394
|
if (item.origin === 'staged') {
|
|
468
|
-
return void editStaged(top, rel, abs, item
|
|
395
|
+
return void editStaged(top, rel, abs, item, text);
|
|
469
396
|
}
|
|
470
|
-
editWorktree(abs, item
|
|
397
|
+
editWorktree(abs, item, text);
|
|
471
398
|
};
|
|
472
399
|
|
|
473
400
|
const writeFile = (top, rel, text) => {
|
|
@@ -483,10 +410,12 @@ const stagePath = (top, rel) => {
|
|
|
483
410
|
};
|
|
484
411
|
|
|
485
412
|
module.exports = {
|
|
486
|
-
oneLine,
|
|
487
413
|
requireOk,
|
|
488
414
|
runGit,
|
|
489
415
|
runGitAsync,
|
|
416
|
+
command,
|
|
417
|
+
gitOp,
|
|
418
|
+
groupByPath,
|
|
490
419
|
gitText,
|
|
491
420
|
worktreeText,
|
|
492
421
|
readDepSides,
|
package/lib/git.js
CHANGED
|
@@ -3,47 +3,18 @@
|
|
|
3
3
|
const capabilities = require('./capabilities.js');
|
|
4
4
|
const { capabilitiesFor, attachCapabilities } = capabilities;
|
|
5
5
|
const worktree = require('./git-worktree.js');
|
|
6
|
-
const { readSnapshot, readSnapshotAsync, toplevel, resolveRev } = worktree;
|
|
7
|
-
const { listScopeFiles, fileText, editItem, writeFile, stagePath } = worktree;
|
|
8
|
-
const { addItem, unstageItem, revertItem, currentBranch } = worktree;
|
|
9
6
|
const branches = require('./git-branches.js');
|
|
10
7
|
const gitDeps = require('./git-deps.js');
|
|
11
8
|
|
|
12
9
|
const load = (cwd, paths = [], options = {}) => {
|
|
13
|
-
const snapshot = readSnapshot(cwd, paths, options);
|
|
10
|
+
const snapshot = worktree.readSnapshot(cwd, paths, options);
|
|
14
11
|
return gitDeps.finishLoad(snapshot, { ...options, paths });
|
|
15
12
|
};
|
|
16
13
|
|
|
17
14
|
const loadAsync = async (cwd, paths = [], options = {}) => {
|
|
18
|
-
const snapshot = await readSnapshotAsync(cwd, paths, options);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
paths,
|
|
22
|
-
deferExtras: options.deferExtras ?? true,
|
|
23
|
-
});
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const addGitItem = (top, item) => {
|
|
27
|
-
if (item.dep) return gitDeps.addItem(top, item);
|
|
28
|
-
return addItem(top, item);
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const addGitItemAsync = async (top, item) => {
|
|
32
|
-
if (item.dep) return gitDeps.addItemAsync(top, item);
|
|
33
|
-
return addItem(top, item);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const unstageGitItem = (top, item) => {
|
|
37
|
-
if (item.dep) return gitDeps.unstageItem(top, item);
|
|
38
|
-
return unstageItem(top, item);
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const revertGitItem = (top, item) => {
|
|
42
|
-
if (item.origin === 'commit') return;
|
|
43
|
-
if (item.dep) {
|
|
44
|
-
return void gitDeps.revertDepItem(top, item);
|
|
45
|
-
}
|
|
46
|
-
revertItem(top, item);
|
|
15
|
+
const snapshot = await worktree.readSnapshotAsync(cwd, paths, options);
|
|
16
|
+
const deferExtras = options.deferExtras ?? true;
|
|
17
|
+
return gitDeps.finishLoad(snapshot, { ...options, paths, deferExtras });
|
|
47
18
|
};
|
|
48
19
|
|
|
49
20
|
const createGitRepo = () =>
|
|
@@ -52,18 +23,18 @@ const createGitRepo = () =>
|
|
|
52
23
|
load,
|
|
53
24
|
loadAsync,
|
|
54
25
|
loadExtras: gitDeps.loadExtras,
|
|
55
|
-
toplevel,
|
|
56
|
-
resolveRev,
|
|
57
|
-
listFiles: listScopeFiles,
|
|
58
|
-
fileText,
|
|
59
|
-
add:
|
|
60
|
-
addAsync:
|
|
61
|
-
unstage:
|
|
62
|
-
revert:
|
|
26
|
+
toplevel: worktree.toplevel,
|
|
27
|
+
resolveRev: worktree.resolveRev,
|
|
28
|
+
listFiles: worktree.listScopeFiles,
|
|
29
|
+
fileText: worktree.fileText,
|
|
30
|
+
add: gitDeps.addItem,
|
|
31
|
+
addAsync: gitDeps.addItemAsync,
|
|
32
|
+
unstage: gitDeps.unstageItem,
|
|
33
|
+
revert: gitDeps.revertItem,
|
|
63
34
|
revertFile: gitDeps.revertFile,
|
|
64
|
-
edit: editItem,
|
|
65
|
-
writeFile,
|
|
66
|
-
stagePath,
|
|
35
|
+
edit: worktree.editItem,
|
|
36
|
+
writeFile: worktree.writeFile,
|
|
37
|
+
stagePath: worktree.stagePath,
|
|
67
38
|
commit: branches.commitChanges,
|
|
68
39
|
commitAsync: branches.commitChangesAsync,
|
|
69
40
|
hasStaged: branches.hasStaged,
|
|
@@ -71,7 +42,7 @@ const createGitRepo = () =>
|
|
|
71
42
|
commitMessage: branches.commitMessage,
|
|
72
43
|
listBranches: branches.listBranches,
|
|
73
44
|
listCommits: branches.listCommits,
|
|
74
|
-
currentBranch,
|
|
45
|
+
currentBranch: worktree.currentBranch,
|
|
75
46
|
checkout: branches.checkoutBranch,
|
|
76
47
|
checkoutAsync: branches.checkoutBranchAsync,
|
|
77
48
|
createBranch: branches.createBranch,
|
|
@@ -95,13 +66,14 @@ const createGitRepo = () =>
|
|
|
95
66
|
);
|
|
96
67
|
|
|
97
68
|
module.exports = {
|
|
98
|
-
...worktree,
|
|
99
69
|
...branches,
|
|
70
|
+
currentBranch: worktree.currentBranch,
|
|
71
|
+
editItem: worktree.editItem,
|
|
100
72
|
load,
|
|
101
73
|
loadAsync,
|
|
102
74
|
loadExtras: gitDeps.loadExtras,
|
|
103
|
-
addItem:
|
|
104
|
-
unstageItem:
|
|
105
|
-
revertItem:
|
|
75
|
+
addItem: gitDeps.addItem,
|
|
76
|
+
unstageItem: gitDeps.unstageItem,
|
|
77
|
+
revertItem: gitDeps.revertItem,
|
|
106
78
|
createGitRepo,
|
|
107
79
|
};
|