reslop 0.2.5 → 0.2.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/CHANGELOG.md +22 -1
- package/LICENSE +1 -1
- package/README.md +11 -11
- package/bin/reslop.js +3 -8
- package/lib/ansi.js +72 -19
- package/lib/api.js +1 -1
- package/lib/capabilities.js +8 -8
- package/lib/cli.js +61 -75
- package/lib/clipboard.js +39 -47
- package/lib/dashboard/git.js +128 -0
- package/lib/dashboard/model.js +357 -0
- package/lib/dashboard/npm.js +290 -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 +79 -91
- package/lib/find.js +61 -0
- package/lib/git-branches.js +254 -416
- package/lib/git-deps.js +148 -272
- package/lib/git-reword-todo.js +5 -13
- package/lib/git-worktree.js +161 -214
- 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 +75 -83
- 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 +412 -347
- package/lib/render/commits.js +160 -361
- package/lib/render/dash-activity.js +212 -0
- package/lib/render/dash-blocks.js +496 -0
- package/lib/render/dash-table.js +295 -0
- package/lib/render/dashboard.js +62 -0
- package/lib/render/diff.js +123 -258
- package/lib/render/files.js +60 -86
- package/lib/render/frame.js +82 -117
- package/lib/render/notes.js +150 -210
- package/lib/render/npm.js +213 -177
- package/lib/render/packages.js +93 -0
- package/lib/render/present.js +4 -11
- package/lib/render/primitives.js +77 -172
- package/lib/render/render.js +5 -14
- package/lib/render/report.js +43 -0
- package/lib/render/repos.js +226 -0
- package/lib/render/search.js +25 -0
- package/lib/render/tiles.js +355 -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 +69 -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 +189 -65
- package/lib/session/branches.js +164 -222
- package/lib/session/change-actions.js +49 -119
- package/lib/session/commit.js +298 -352
- package/lib/session/compose.js +572 -715
- package/lib/session/dashboard.js +530 -0
- package/lib/session/dispatch.js +197 -171
- package/lib/session/files-pane.js +92 -45
- package/lib/session/find.js +94 -0
- package/lib/session/items.js +6 -17
- package/lib/session/lifecycle.js +420 -466
- package/lib/session/load.js +1 -14
- package/lib/session/navigation.js +4 -2
- package/lib/session/npm.js +403 -420
- package/lib/session/ops.js +5 -13
- package/lib/session/packages.js +143 -0
- package/lib/session/pointer.js +30 -14
- package/lib/session/progress.js +39 -30
- package/lib/session/review.js +1 -3
- package/lib/session/tasks.js +277 -236
- package/lib/session/templates.js +51 -59
- package/lib/session/terminal.js +85 -132
- package/lib/session/view.js +59 -80
- package/lib/session/watch.js +201 -212
- package/lib/session/workspace.js +455 -0
- package/lib/session.js +95 -161
- package/lib/update.js +44 -111
- package/lib/utilities.js +46 -88
- package/lib/workspace.js +166 -0
- 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, lineDelta, 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,38 @@ 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
|
-
|
|
97
|
-
|
|
98
|
-
return first === REVIEW_DIR;
|
|
99
|
-
};
|
|
120
|
+
const isReviewFile = (rel) => rel.split(/[/\\]/)[0] === REVIEW_DIR;
|
|
121
|
+
|
|
122
|
+
const isDirEntry = (rel) => rel.endsWith('/');
|
|
100
123
|
|
|
101
124
|
const dropReviewItems = (items) =>
|
|
102
125
|
items.filter((item) => !isReviewFile(itemPath(item)));
|
|
103
126
|
|
|
104
|
-
const isBinaryBuffer = (buf) =>
|
|
105
|
-
if (buf.length > BINARY_SIZE) return true;
|
|
106
|
-
return buf.includes(0);
|
|
107
|
-
};
|
|
108
|
-
|
|
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
|
-
};
|
|
127
|
+
const isBinaryBuffer = (buf) => buf.length > BINARY_SIZE || buf.includes(0);
|
|
133
128
|
|
|
134
|
-
const
|
|
135
|
-
const args = ['ls-files', '-co', '--exclude-standard', '-z'];
|
|
136
|
-
if (paths.length) args.push('--', ...paths);
|
|
137
|
-
return args;
|
|
138
|
-
};
|
|
129
|
+
const namesFromLs = (stdout) => stdout.split('\0').filter(Boolean);
|
|
139
130
|
|
|
140
131
|
const listScopeFiles = (top, paths = [], options = {}) => {
|
|
141
132
|
const rev = options.commit;
|
|
142
|
-
const args = rev
|
|
133
|
+
const args = rev
|
|
134
|
+
? ['ls-tree', '-r', '--name-only', '-z', rev, ...pathArgs(paths)]
|
|
135
|
+
: ['ls-files', '-co', '--exclude-standard', '-z', ...pathArgs(paths)];
|
|
143
136
|
const result = requireOk(runGit(args, top), 'ls-files failed');
|
|
144
|
-
const names =
|
|
137
|
+
const names = namesFromLs(result.stdout).filter(
|
|
138
|
+
(rel) => !isReviewFile(rel) && !isDirEntry(rel),
|
|
139
|
+
);
|
|
145
140
|
names.sort((left, right) => left.localeCompare(right, 'en'));
|
|
146
141
|
return names;
|
|
147
142
|
};
|
|
@@ -159,44 +154,47 @@ const binaryNewFile = (rel) => ({
|
|
|
159
154
|
const untrackedItemsFrom = (top, names) => {
|
|
160
155
|
const files = [];
|
|
161
156
|
for (const rel of names) {
|
|
162
|
-
if (isReviewFile(rel)) continue;
|
|
163
|
-
const full = path.join(top, rel);
|
|
157
|
+
if (isReviewFile(rel) || isDirEntry(rel)) continue;
|
|
164
158
|
let buf;
|
|
165
159
|
try {
|
|
166
|
-
buf = fs.readFileSync(
|
|
160
|
+
buf = fs.readFileSync(path.join(top, rel));
|
|
167
161
|
} catch (error) {
|
|
168
|
-
if (error.code === 'ENOENT') continue;
|
|
162
|
+
if (error.code === 'ENOENT' || error.code === 'EISDIR') continue;
|
|
169
163
|
throw error;
|
|
170
164
|
}
|
|
171
|
-
if (isBinaryBuffer(buf))
|
|
172
|
-
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
files.push(synthesizeNewFile(rel, buf.toString('utf8')));
|
|
165
|
+
if (isBinaryBuffer(buf)) files.push(binaryNewFile(rel));
|
|
166
|
+
else files.push(synthesizeNewFile(rel, buf.toString('utf8')));
|
|
176
167
|
}
|
|
177
168
|
return itemsFromFiles(files, 'untracked');
|
|
178
169
|
};
|
|
179
170
|
|
|
180
|
-
const
|
|
171
|
+
const countUntrackedAdded = async (top) => {
|
|
172
|
+
try {
|
|
173
|
+
const result = await runGitAsync(untrackedArgs([]), top);
|
|
174
|
+
if (result.status !== 0) return 0;
|
|
175
|
+
const items = untrackedItemsFrom(top, namesFromLs(result.stdout));
|
|
176
|
+
let added = 0;
|
|
177
|
+
for (const item of items) added += lineDelta(item).added;
|
|
178
|
+
return added;
|
|
179
|
+
} catch {
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const groupByPath = (items) => {
|
|
181
185
|
const byPath = new Map();
|
|
182
|
-
for (const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
list.push(item);
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
byPath.set(rel, [item]);
|
|
191
|
-
}
|
|
186
|
+
for (const item of items) {
|
|
187
|
+
const rel = itemPath(item);
|
|
188
|
+
const list = byPath.get(rel);
|
|
189
|
+
if (list) list.push(item);
|
|
190
|
+
else byPath.set(rel, [item]);
|
|
192
191
|
}
|
|
193
|
-
return
|
|
192
|
+
return byPath;
|
|
194
193
|
};
|
|
195
194
|
|
|
196
195
|
const gitText = (top, spec) => {
|
|
197
196
|
const result = runGit(['show', spec], top);
|
|
198
|
-
|
|
199
|
-
return result.stdout ?? '';
|
|
197
|
+
return result.status === 0 ? result.stdout : '';
|
|
200
198
|
};
|
|
201
199
|
|
|
202
200
|
const worktreeText = (top, rel) => {
|
|
@@ -215,24 +213,19 @@ const fileText = (top, rel, rev) => {
|
|
|
215
213
|
|
|
216
214
|
const readDepSides = (top, rev, origin, rel) => {
|
|
217
215
|
if (origin === 'untracked') {
|
|
218
|
-
|
|
219
|
-
return { oldText: '', newText };
|
|
216
|
+
return { oldText: '', newText: worktreeText(top, rel) };
|
|
220
217
|
}
|
|
221
218
|
if (origin === 'staged') {
|
|
222
219
|
const oldText = gitText(top, `HEAD:${rel}`);
|
|
223
|
-
|
|
224
|
-
return { oldText, newText };
|
|
220
|
+
return { oldText, newText: gitText(top, `:${rel}`) };
|
|
225
221
|
}
|
|
226
222
|
if (origin === 'unstaged') {
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
const newText = worktreeText(top, rel);
|
|
230
|
-
return { oldText, newText };
|
|
223
|
+
const oldText = gitText(top, `:${rel}`) || gitText(top, `HEAD:${rel}`);
|
|
224
|
+
return { oldText, newText: worktreeText(top, rel) };
|
|
231
225
|
}
|
|
232
226
|
if (origin === 'commit' && rev) {
|
|
233
227
|
const oldText = gitText(top, `${rev}^:${rel}`);
|
|
234
|
-
|
|
235
|
-
return { oldText, newText };
|
|
228
|
+
return { oldText, newText: gitText(top, `${rev}:${rel}`) };
|
|
236
229
|
}
|
|
237
230
|
return { oldText: '', newText: '' };
|
|
238
231
|
};
|
|
@@ -245,106 +238,77 @@ const toplevel = (cwd) => {
|
|
|
245
238
|
|
|
246
239
|
const resolveRev = (cwd, spec) => {
|
|
247
240
|
if (!spec) return null;
|
|
248
|
-
const top = toplevel(cwd);
|
|
249
241
|
const args = ['rev-parse', '--verify', `${spec}^{commit}`];
|
|
250
|
-
const result = runGit(args,
|
|
251
|
-
|
|
252
|
-
return result.stdout.trim();
|
|
242
|
+
const result = runGit(args, toplevel(cwd));
|
|
243
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
253
244
|
};
|
|
254
245
|
|
|
255
246
|
const shortRev = (top, commit) => {
|
|
256
247
|
const result = runGit(['rev-parse', '--short', commit], top);
|
|
257
|
-
|
|
258
|
-
return result.stdout.trim();
|
|
248
|
+
return result.status === 0 ? result.stdout.trim() : commit.slice(0, 7);
|
|
259
249
|
};
|
|
260
250
|
|
|
261
251
|
const currentBranch = (top) => {
|
|
262
|
-
const
|
|
263
|
-
const result = runGit(args, top);
|
|
252
|
+
const result = runGit(['rev-parse', '--abbrev-ref', 'HEAD'], top);
|
|
264
253
|
if (result.status !== 0) return '';
|
|
265
254
|
const name = result.stdout.trim();
|
|
266
|
-
|
|
267
|
-
return name;
|
|
255
|
+
return name === 'HEAD' ? '' : name;
|
|
268
256
|
};
|
|
269
257
|
|
|
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 };
|
|
258
|
+
const snapshotWorktree = (top, results) => {
|
|
259
|
+
const outputs = results.map((result) => requireOk(result).stdout);
|
|
260
|
+
const staged = itemsFromFiles(parseDiff(outputs[0]), 'staged');
|
|
261
|
+
const unstaged = itemsFromFiles(parseDiff(outputs[1]), 'unstaged');
|
|
262
|
+
const untracked = untrackedItemsFrom(top, namesFromLs(outputs[2]));
|
|
263
|
+
const grouped = groupByPath([...staged, ...unstaged, ...untracked]);
|
|
264
|
+
const parsed = dropReviewItems([...grouped.values()].flat());
|
|
265
|
+
return { top, parsed, branch: currentBranch(top) };
|
|
280
266
|
};
|
|
281
267
|
|
|
282
268
|
const snapshotCommit = (top, commit, files) => {
|
|
283
|
-
const
|
|
284
|
-
const parsed = dropReviewItems(commitItems);
|
|
269
|
+
const parsed = dropReviewItems(itemsFromFiles(files, 'commit'));
|
|
285
270
|
const revShort = shortRev(top, commit);
|
|
286
|
-
|
|
287
|
-
return { top, parsed, rev: commit, revShort, branch };
|
|
271
|
+
return { top, parsed, rev: commit, revShort, branch: currentBranch(top) };
|
|
288
272
|
};
|
|
289
273
|
|
|
290
|
-
|
|
291
|
-
const parent =
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
};
|
|
274
|
+
function* commitDiff(commit, paths) {
|
|
275
|
+
const parent = yield command(['rev-parse', '--verify', `${commit}^`]);
|
|
276
|
+
const result = yield command(commitDiffArgs(commit, parent, paths));
|
|
277
|
+
return parseDiff(requireOk(result).stdout);
|
|
278
|
+
}
|
|
296
279
|
|
|
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
|
-
};
|
|
280
|
+
const worktreeCommands = (paths) => [
|
|
281
|
+
diffArgs(['--cached'], paths),
|
|
282
|
+
diffArgs([], paths),
|
|
283
|
+
untrackedArgs(paths),
|
|
284
|
+
];
|
|
307
285
|
|
|
308
286
|
const readSnapshot = (cwd, paths, options) => {
|
|
309
287
|
const top = toplevel(cwd);
|
|
310
|
-
const extra = pathArgsOf(paths);
|
|
311
288
|
const rev = options.commit;
|
|
312
|
-
if (rev)
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
return snapshotWorktree(top,
|
|
289
|
+
if (rev) {
|
|
290
|
+
return snapshotCommit(top, rev, runSteps(commitDiff(rev, paths), top));
|
|
291
|
+
}
|
|
292
|
+
const results = worktreeCommands(paths).map((args) => runGit(args, top));
|
|
293
|
+
return snapshotWorktree(top, results);
|
|
317
294
|
};
|
|
318
295
|
|
|
319
296
|
const readSnapshotAsync = async (cwd, paths, options) => {
|
|
320
297
|
const top = toplevel(cwd);
|
|
321
|
-
const
|
|
322
|
-
const rev = options.commit;
|
|
323
|
-
const signal = options.signal;
|
|
298
|
+
const { commit: rev, signal } = options;
|
|
324
299
|
if (rev) {
|
|
325
|
-
const files = await
|
|
300
|
+
const files = await runStepsAsync(commitDiff(rev, paths), top, signal);
|
|
326
301
|
return snapshotCommit(top, rev, files);
|
|
327
302
|
}
|
|
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);
|
|
303
|
+
const running = worktreeCommands(paths).map((args) =>
|
|
304
|
+
runGitAsync(args, top, { signal }),
|
|
305
|
+
);
|
|
306
|
+
return snapshotWorktree(top, await Promise.all(running));
|
|
342
307
|
};
|
|
343
308
|
|
|
344
309
|
const applyPatch = (cwd, patch, flags) => {
|
|
345
310
|
const args = ['apply', ...flags, '--whitespace=nowarn', '-'];
|
|
346
|
-
|
|
347
|
-
requireOk(result, 'apply failed');
|
|
311
|
+
requireOk(runGit(args, cwd, { input: patch }), 'apply failed');
|
|
348
312
|
};
|
|
349
313
|
|
|
350
314
|
const addItem = (top, item) => {
|
|
@@ -357,35 +321,26 @@ const addItem = (top, item) => {
|
|
|
357
321
|
|
|
358
322
|
const unstageItem = (top, item) => {
|
|
359
323
|
if (item.origin !== 'staged') return;
|
|
360
|
-
const rel = itemPath(item);
|
|
361
324
|
if (item.file.isBinary) {
|
|
362
|
-
|
|
325
|
+
const args = ['restore', '--staged', '--', itemPath(item)];
|
|
326
|
+
return void requireOk(runGit(args, top));
|
|
363
327
|
}
|
|
364
328
|
applyPatch(top, item.patchAdd, ['--reverse', '--cached']);
|
|
365
329
|
};
|
|
366
330
|
|
|
367
331
|
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));
|
|
332
|
+
const origins = new Set(items.map((item) => item.origin));
|
|
333
|
+
const isNew = items.some((item) => item.file?.isNew);
|
|
334
|
+
if (isNew && origins.has('staged')) {
|
|
335
|
+
return void requireOk(runGit(['rm', '-f', '--', rel], top));
|
|
382
336
|
}
|
|
337
|
+
const onlyUntracked = origins.size === 1 && origins.has('untracked');
|
|
338
|
+
if (isNew || onlyUntracked) return void fs.unlinkSync(path.join(top, rel));
|
|
383
339
|
const args = ['restore', '-s', 'HEAD', '--worktree', '--staged', '--', rel];
|
|
384
340
|
requireOk(runGit(args, top));
|
|
385
341
|
};
|
|
386
342
|
|
|
387
343
|
const revertItem = (top, item) => {
|
|
388
|
-
if (item.origin === 'commit') return;
|
|
389
344
|
const rel = itemPath(item);
|
|
390
345
|
if (item.origin === 'untracked') {
|
|
391
346
|
return void fs.unlinkSync(path.join(top, rel));
|
|
@@ -393,8 +348,7 @@ const revertItem = (top, item) => {
|
|
|
393
348
|
if (item.file.isBinary) {
|
|
394
349
|
const args = ['restore', '-s', 'HEAD', '--worktree'];
|
|
395
350
|
if (item.origin === 'staged') args.push('--staged');
|
|
396
|
-
args
|
|
397
|
-
return void requireOk(runGit(args, top));
|
|
351
|
+
return void requireOk(runGit([...args, '--', rel], top));
|
|
398
352
|
}
|
|
399
353
|
if (item.origin === 'unstaged') {
|
|
400
354
|
return void applyPatch(top, item.patchRevert, ['--reverse']);
|
|
@@ -407,12 +361,6 @@ const revertItem = (top, item) => {
|
|
|
407
361
|
}
|
|
408
362
|
};
|
|
409
363
|
|
|
410
|
-
const indexText = (top, rel) => {
|
|
411
|
-
const result = runGit(['show', `:${rel}`], top);
|
|
412
|
-
requireOk(result, 'show failed');
|
|
413
|
-
return result.stdout ?? '';
|
|
414
|
-
};
|
|
415
|
-
|
|
416
364
|
const indexMode = (top, rel) => {
|
|
417
365
|
const result = requireOk(runGit(['ls-files', '--stage', '--', rel], top));
|
|
418
366
|
const line = result.stdout.trim();
|
|
@@ -428,17 +376,17 @@ const indexMode = (top, rel) => {
|
|
|
428
376
|
|
|
429
377
|
const writeIndex = (top, rel, text) => {
|
|
430
378
|
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();
|
|
379
|
+
const hashed = runGit(hashArgs, top, { input: text });
|
|
380
|
+
const sha = requireOk(hashed, 'hash-object failed').stdout.trim();
|
|
434
381
|
const mode = indexMode(top, rel);
|
|
435
382
|
const args = ['update-index', '--add', '--cacheinfo', mode, sha, rel];
|
|
436
383
|
requireOk(runGit(args, top), 'update-index failed');
|
|
437
384
|
};
|
|
438
385
|
|
|
439
|
-
const editStaged = (top, rel, abs,
|
|
440
|
-
const
|
|
441
|
-
const
|
|
386
|
+
const editStaged = (top, rel, abs, item, text) => {
|
|
387
|
+
const shown = requireOk(runGit(['show', `:${rel}`], top), 'show failed');
|
|
388
|
+
const source = shown.stdout;
|
|
389
|
+
const next = replaceBlockAdds(source, item.hunk, item.blockId, text);
|
|
442
390
|
writeIndex(top, rel, next);
|
|
443
391
|
let work = null;
|
|
444
392
|
try {
|
|
@@ -446,28 +394,24 @@ const editStaged = (top, rel, abs, hunk, blockId, text) => {
|
|
|
446
394
|
} catch (error) {
|
|
447
395
|
if (error.code !== 'ENOENT') throw error;
|
|
448
396
|
}
|
|
449
|
-
if (work === null)
|
|
450
|
-
return void fs.writeFileSync(abs, next);
|
|
451
|
-
}
|
|
452
|
-
if (work === source) fs.writeFileSync(abs, next);
|
|
397
|
+
if (work === null || work === source) fs.writeFileSync(abs, next);
|
|
453
398
|
};
|
|
454
399
|
|
|
455
|
-
const editWorktree = (abs,
|
|
400
|
+
const editWorktree = (abs, item, text) => {
|
|
456
401
|
const source = fs.readFileSync(abs, 'utf8');
|
|
457
|
-
const next = replaceBlockAdds(source, hunk, blockId, text);
|
|
458
|
-
if (next
|
|
459
|
-
fs.writeFileSync(abs, next);
|
|
402
|
+
const next = replaceBlockAdds(source, item.hunk, item.blockId, text);
|
|
403
|
+
if (next !== source) fs.writeFileSync(abs, next);
|
|
460
404
|
};
|
|
461
405
|
|
|
462
406
|
const editItem = (top, item, text) => {
|
|
463
|
-
if (item.origin
|
|
407
|
+
if (isReadOnlyOrigin(item.origin)) return;
|
|
464
408
|
const rel = itemPath(item);
|
|
465
409
|
if (!rel || !item.hunk) return;
|
|
466
410
|
const abs = path.join(top, rel);
|
|
467
411
|
if (item.origin === 'staged') {
|
|
468
|
-
return void editStaged(top, rel, abs, item
|
|
412
|
+
return void editStaged(top, rel, abs, item, text);
|
|
469
413
|
}
|
|
470
|
-
editWorktree(abs, item
|
|
414
|
+
editWorktree(abs, item, text);
|
|
471
415
|
};
|
|
472
416
|
|
|
473
417
|
const writeFile = (top, rel, text) => {
|
|
@@ -483,10 +427,12 @@ const stagePath = (top, rel) => {
|
|
|
483
427
|
};
|
|
484
428
|
|
|
485
429
|
module.exports = {
|
|
486
|
-
oneLine,
|
|
487
430
|
requireOk,
|
|
488
431
|
runGit,
|
|
489
432
|
runGitAsync,
|
|
433
|
+
command,
|
|
434
|
+
gitOp,
|
|
435
|
+
groupByPath,
|
|
490
436
|
gitText,
|
|
491
437
|
worktreeText,
|
|
492
438
|
readDepSides,
|
|
@@ -495,6 +441,7 @@ module.exports = {
|
|
|
495
441
|
resolveRev,
|
|
496
442
|
currentBranch,
|
|
497
443
|
fileText,
|
|
444
|
+
countUntrackedAdded,
|
|
498
445
|
listScopeFiles,
|
|
499
446
|
readSnapshot,
|
|
500
447
|
readSnapshotAsync,
|