reslop 0.2.1 → 0.2.4
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 +27 -1
- package/README.md +19 -4
- package/bin/reslop.js +4 -2
- package/lib/ansi.js +120 -4
- package/lib/api.js +2 -1
- package/lib/cli.js +21 -9
- package/lib/clipboard.js +6 -3
- package/lib/deps-diff.js +61 -115
- package/lib/deps.js +57 -54
- package/lib/detect.js +4 -2
- package/lib/diff/align.js +2 -1
- package/lib/diff/diff.js +6 -3
- package/lib/diff/display.js +2 -1
- package/lib/diff/inline.js +2 -1
- package/lib/diff/patch.js +2 -1
- package/lib/diff/unit.js +4 -2
- package/lib/editor.js +22 -4
- package/lib/files.js +38 -12
- package/lib/git-branches.js +150 -165
- package/lib/git-deps.js +20 -60
- package/lib/git-worktree.js +508 -0
- package/lib/git.js +23 -580
- package/lib/github.js +2 -1
- package/lib/gitlab.js +2 -1
- package/lib/highlight/bash.js +2 -1
- package/lib/highlight/css.js +2 -1
- package/lib/highlight/csv.js +2 -1
- package/lib/highlight/dot.js +2 -1
- package/lib/highlight/html.js +2 -1
- package/lib/highlight/json.js +2 -1
- package/lib/highlight/log.js +2 -1
- package/lib/highlight/txt.js +2 -1
- package/lib/highlight.js +2 -1
- package/lib/keys.js +12 -6
- package/lib/manifest.js +4 -2
- package/lib/npm-commands.js +592 -0
- package/lib/remote.js +20 -13
- package/lib/render/branches.js +25 -41
- package/lib/render/chrome.js +163 -128
- package/lib/render/commits.js +727 -65
- package/lib/render/diff.js +130 -47
- package/lib/render/files.js +4 -12
- package/lib/render/frame.js +41 -13
- package/lib/render/notes.js +4 -11
- package/lib/render/npm.js +216 -0
- package/lib/render/present.js +2 -1
- package/lib/render/primitives.js +34 -50
- package/lib/render/render.js +2 -1
- package/lib/render/unit.js +14 -11
- package/lib/report-model.js +129 -0
- package/lib/report-parse.js +89 -0
- package/lib/report-render.js +104 -0
- package/lib/report-run.js +166 -0
- package/lib/report-test.js +507 -0
- package/lib/report-tools.js +337 -0
- package/lib/review-merge.js +2 -1
- package/lib/review.js +6 -2
- package/lib/session/actions.js +77 -44
- package/lib/session/branches.js +13 -49
- package/lib/session/change-actions.js +2 -1
- package/lib/session/commit.js +113 -53
- package/lib/session/compose.js +83 -14
- package/lib/session/dispatch.js +42 -22
- package/lib/session/files-pane.js +16 -21
- package/lib/session/items.js +6 -3
- package/lib/session/lifecycle.js +12 -5
- package/lib/session/load.js +4 -2
- package/lib/session/navigation.js +2 -0
- package/lib/session/npm.js +388 -0
- package/lib/session/ops.js +15 -1
- package/lib/session/pointer.js +30 -3
- package/lib/session/review.js +2 -1
- package/lib/session/terminal.js +4 -2
- package/lib/session/update.js +2 -1
- package/lib/session/view.js +41 -4
- package/lib/session.js +44 -22
- package/lib/source.js +6 -3
- package/lib/update.js +5 -9
- package/lib/utilities.js +4 -2
- package/lib/wrap.js +3 -1
- package/package.json +6 -6
package/lib/git-branches.js
CHANGED
|
@@ -4,7 +4,10 @@ const fs = require('node:fs');
|
|
|
4
4
|
const os = require('node:os');
|
|
5
5
|
const path = require('node:path');
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const files = require('./files.js');
|
|
8
|
+
const gitWorktree = require('./git-worktree.js');
|
|
9
|
+
const { shortAge } = files;
|
|
10
|
+
const { oneLine, requireOk, runGit, runGitAsync } = gitWorktree;
|
|
8
11
|
|
|
9
12
|
const BRANCH_FORMAT = [
|
|
10
13
|
'%(HEAD)',
|
|
@@ -33,36 +36,95 @@ const commitMessage = (top, sha) => {
|
|
|
33
36
|
|
|
34
37
|
const lastMessage = (top) => commitMessage(top, 'HEAD');
|
|
35
38
|
|
|
36
|
-
const
|
|
39
|
+
const COMMIT_FIELDS = ['%H', '%h', '%an', '%ae', '%ar', '%ci', '%D', '%s'];
|
|
40
|
+
const COMMIT_FORMAT = `${COMMIT_FIELDS.join('%x1f')}%x1e%B`;
|
|
37
41
|
const COMMIT_LIMIT = 256;
|
|
38
42
|
const UNIT = '\x1f';
|
|
43
|
+
const RECORD = '\x1e';
|
|
44
|
+
const TAG_REF = /^tag: /;
|
|
45
|
+
const REMOTE_HEAD = /\/HEAD(?: -> |$)/;
|
|
46
|
+
|
|
47
|
+
const formatCommitRefs = (raw) => {
|
|
48
|
+
const text = `${raw ?? ''}`.trim();
|
|
49
|
+
if (!text) return '';
|
|
50
|
+
const names = [];
|
|
51
|
+
for (const part of text.split(', ')) {
|
|
52
|
+
const name = part.trim();
|
|
53
|
+
if (!name || TAG_REF.test(name)) continue;
|
|
54
|
+
if (name !== 'HEAD' && REMOTE_HEAD.test(name)) continue;
|
|
55
|
+
names.push(name);
|
|
56
|
+
}
|
|
57
|
+
return names.join(', ');
|
|
58
|
+
};
|
|
39
59
|
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
60
|
+
const parseCommitRecord = (record, index) => {
|
|
61
|
+
const cut = record.indexOf(RECORD);
|
|
62
|
+
if (cut < 0) return null;
|
|
63
|
+
const parts = record.slice(0, cut).split(UNIT);
|
|
64
|
+
if (parts.length < COMMIT_FIELDS.length) return null;
|
|
43
65
|
const sha = (parts[0] ?? '').trim();
|
|
44
66
|
if (!sha) return null;
|
|
67
|
+
const body = record.slice(cut + 1).replace(/\s+$/, '');
|
|
68
|
+
const read = (spec) => {
|
|
69
|
+
const at = COMMIT_FIELDS.indexOf(spec);
|
|
70
|
+
return (parts[at] ?? '').trim();
|
|
71
|
+
};
|
|
72
|
+
const subject = read('%s') || body.split('\n')[0].trim();
|
|
45
73
|
return {
|
|
46
74
|
sha,
|
|
47
|
-
shortSha: (
|
|
48
|
-
author: (
|
|
49
|
-
|
|
50
|
-
|
|
75
|
+
shortSha: read('%h') || sha.slice(0, 7),
|
|
76
|
+
author: read('%an'),
|
|
77
|
+
email: read('%ae'),
|
|
78
|
+
date: shortAge(read('%ar')),
|
|
79
|
+
when: read('%ci'),
|
|
80
|
+
refs: formatCommitRefs(read('%D')),
|
|
81
|
+
subject,
|
|
82
|
+
body,
|
|
83
|
+
added: 0,
|
|
84
|
+
removed: 0,
|
|
51
85
|
head: index === 0,
|
|
52
86
|
};
|
|
53
87
|
};
|
|
54
88
|
|
|
89
|
+
const STAT_LEAD = /^\n* *\d+ files? changed[^\n]*\n?/;
|
|
90
|
+
|
|
91
|
+
const takeStat = (chunk) => {
|
|
92
|
+
const match = STAT_LEAD.exec(chunk);
|
|
93
|
+
if (!match) return { stat: '', rest: chunk };
|
|
94
|
+
return { stat: match[0], rest: chunk.slice(match[0].length) };
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const countIn = (line, word) => {
|
|
98
|
+
const match = new RegExp(`(\\d+) ${word}`).exec(line);
|
|
99
|
+
if (!match) return 0;
|
|
100
|
+
return parseInt(match[1], 10);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const parseShortstat = (line) => ({
|
|
104
|
+
added: countIn(line, 'insertion'),
|
|
105
|
+
removed: countIn(line, 'deletion'),
|
|
106
|
+
});
|
|
107
|
+
|
|
55
108
|
const listCommits = (top) => {
|
|
56
109
|
const args = [
|
|
57
110
|
'log',
|
|
111
|
+
'-z',
|
|
112
|
+
'--shortstat',
|
|
58
113
|
`--format=${COMMIT_FORMAT}`,
|
|
59
114
|
`--max-count=${COMMIT_LIMIT}`,
|
|
60
115
|
];
|
|
61
116
|
const result = requireOk(runGit(args, top), 'log failed');
|
|
62
117
|
const commits = [];
|
|
63
|
-
for (const
|
|
64
|
-
|
|
65
|
-
|
|
118
|
+
for (const chunk of result.stdout.split('\0')) {
|
|
119
|
+
const taken = takeStat(chunk);
|
|
120
|
+
if (taken.stat && commits.length) {
|
|
121
|
+
const stat = parseShortstat(taken.stat);
|
|
122
|
+
const last = commits[commits.length - 1];
|
|
123
|
+
last.added = stat.added;
|
|
124
|
+
last.removed = stat.removed;
|
|
125
|
+
}
|
|
126
|
+
if (!taken.rest) continue;
|
|
127
|
+
const entry = parseCommitRecord(taken.rest, commits.length);
|
|
66
128
|
if (entry) commits.push(entry);
|
|
67
129
|
}
|
|
68
130
|
return commits;
|
|
@@ -83,48 +145,40 @@ const dropHeadCommit = (top) => {
|
|
|
83
145
|
requireOk(runGit(['reset', '--soft', 'HEAD~1'], top), 'drop failed');
|
|
84
146
|
};
|
|
85
147
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const dropOlderCommit = (top, sha) => {
|
|
92
|
-
const onto = `${sha}^`;
|
|
93
|
-
const result = runGit(['rebase', '--onto', onto, sha], top);
|
|
94
|
-
const failed = rebaseFailed(result);
|
|
95
|
-
if (!failed) return;
|
|
148
|
+
const runRebase = (top, args, extra = {}) => {
|
|
149
|
+
const result = runGit(args, top, undefined, extra);
|
|
150
|
+
if (result.status === 0) return;
|
|
96
151
|
runGit(['rebase', '--abort'], top);
|
|
97
|
-
|
|
152
|
+
requireOk(result, 'rebase failed');
|
|
98
153
|
};
|
|
99
154
|
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
const failed = rebaseFailed(result);
|
|
104
|
-
if (!failed) return;
|
|
155
|
+
const runRebaseAsync = async (top, args, extra = {}) => {
|
|
156
|
+
const result = await runGitAsync(args, top, extra);
|
|
157
|
+
if (result.status === 0) return;
|
|
105
158
|
await runGitAsync(['rebase', '--abort'], top);
|
|
106
|
-
|
|
159
|
+
requireOk(result, 'rebase failed');
|
|
107
160
|
};
|
|
108
161
|
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
const target = resolveCommit(top, spec);
|
|
162
|
+
const dropTarget = (top, sha) => {
|
|
163
|
+
const target = resolveCommit(top, namedCommit(sha));
|
|
112
164
|
const parent = runGit(['rev-parse', '--verify', `${target}^`], top);
|
|
113
165
|
if (parent.status !== 0) throw new Error('cannot drop root commit');
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
166
|
+
return target;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const dropCommit = (top, sha) => {
|
|
170
|
+
const target = dropTarget(top, sha);
|
|
171
|
+
if (target === resolveCommit(top, 'HEAD')) return void dropHeadCommit(top);
|
|
172
|
+
runRebase(top, ['rebase', '--onto', `${target}^`, target]);
|
|
117
173
|
};
|
|
118
174
|
|
|
119
175
|
const dropCommitAsync = async (top, sha) => {
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (target === head) return void dropHeadCommit(top);
|
|
127
|
-
await dropOlderCommitAsync(top, target);
|
|
176
|
+
const target = dropTarget(top, sha);
|
|
177
|
+
if (target === resolveCommit(top, 'HEAD')) {
|
|
178
|
+
requireOk(await runGitAsync(['reset', '--soft', 'HEAD~1'], top));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
await runRebaseAsync(top, ['rebase', '--onto', `${target}^`, target]);
|
|
128
182
|
};
|
|
129
183
|
|
|
130
184
|
const FIXUP_MARK = 'fixup!';
|
|
@@ -154,72 +208,46 @@ const commitSubject = (top, sha) => {
|
|
|
154
208
|
};
|
|
155
209
|
|
|
156
210
|
const ancestorWithSubject = (top, sha, want) => {
|
|
157
|
-
const args = ['log',
|
|
211
|
+
const args = ['log', '--format=%H%x1f%s', `${sha}^`];
|
|
158
212
|
const result = runGit(args, top);
|
|
159
213
|
if (result.status !== 0) return '';
|
|
160
214
|
for (const line of result.stdout.split('\n')) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
215
|
+
const cut = line.indexOf(UNIT);
|
|
216
|
+
if (cut < 0) continue;
|
|
217
|
+
const subject = line.slice(cut + 1).trim();
|
|
218
|
+
if (subject !== want) continue;
|
|
219
|
+
return line.slice(0, cut).trim();
|
|
164
220
|
}
|
|
165
221
|
return '';
|
|
166
222
|
};
|
|
167
223
|
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
if (!failed) return;
|
|
172
|
-
runGit(['rebase', '--abort'], top);
|
|
173
|
-
throw new Error(oneLine(failed));
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const applyAutosquashAsync = async (top, ontoArgs) => {
|
|
177
|
-
const result = await runGitAsync([...AUTOSQUASH_ARGS, ...ontoArgs], top);
|
|
178
|
-
const failed = rebaseFailed(result);
|
|
179
|
-
if (!failed) return;
|
|
180
|
-
await runGitAsync(['rebase', '--abort'], top);
|
|
181
|
-
throw new Error(oneLine(failed));
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
const applyFixupOnto = (top, original, applyRange) => {
|
|
185
|
-
const parent = runGit(['rev-parse', '--verify', `${original}^`], top);
|
|
186
|
-
if (parent.status !== 0) return applyRange(top, ['--root']);
|
|
187
|
-
return applyRange(top, [(parent.stdout ?? '').trim()]);
|
|
224
|
+
const rebaseRange = (top, sha) => {
|
|
225
|
+
const parent = runGit(['rev-parse', '--verify', `${sha}^`], top);
|
|
226
|
+
return parent.status === 0 ? [parent.stdout.trim()] : ['--root'];
|
|
188
227
|
};
|
|
189
228
|
|
|
190
|
-
const
|
|
191
|
-
const
|
|
192
|
-
const commit = resolveCommit(top, spec);
|
|
229
|
+
const fixupArgs = (top, sha) => {
|
|
230
|
+
const commit = resolveCommit(top, namedCommit(sha));
|
|
193
231
|
const subject = commitSubject(top, commit);
|
|
194
232
|
if (!isFixupSubject(subject)) throw new Error('not a fixup commit');
|
|
195
233
|
const want = fixupRest(subject);
|
|
196
234
|
if (!want) throw new Error('no fixup target');
|
|
197
235
|
const original = ancestorWithSubject(top, commit, want);
|
|
198
236
|
if (!original) throw new Error('no fixup target');
|
|
199
|
-
|
|
237
|
+
return [...AUTOSQUASH_ARGS, ...rebaseRange(top, original)];
|
|
200
238
|
};
|
|
201
239
|
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if (!isFixupSubject(subject)) throw new Error('not a fixup commit');
|
|
207
|
-
const want = fixupRest(subject);
|
|
208
|
-
if (!want) throw new Error('no fixup target');
|
|
209
|
-
const original = ancestorWithSubject(top, commit, want);
|
|
210
|
-
if (!original) throw new Error('no fixup target');
|
|
211
|
-
await applyFixupOnto(top, original, applyAutosquashAsync);
|
|
212
|
-
};
|
|
240
|
+
const applyFixup = (top, sha) => runRebase(top, fixupArgs(top, sha));
|
|
241
|
+
|
|
242
|
+
const applyFixupAsync = async (top, sha) =>
|
|
243
|
+
runRebaseAsync(top, fixupArgs(top, sha));
|
|
213
244
|
|
|
214
245
|
const messageBody = (message) => {
|
|
215
246
|
const text = `${message ?? ''}`;
|
|
216
247
|
return text.endsWith('\n') ? text : `${text}\n`;
|
|
217
248
|
};
|
|
218
249
|
|
|
219
|
-
const
|
|
220
|
-
const args = ['commit', '--amend', '--only', '-F', '-'];
|
|
221
|
-
requireOk(runGit(args, top, messageBody(message)));
|
|
222
|
-
};
|
|
250
|
+
const REWORD_HEAD_ARGS = ['commit', '--amend', '--only', '-F', '-'];
|
|
223
251
|
|
|
224
252
|
const REWORD_TODO = path.join(__dirname, 'git-reword-todo.js');
|
|
225
253
|
|
|
@@ -238,32 +266,11 @@ const prepareRewordEditor = (message) => {
|
|
|
238
266
|
};
|
|
239
267
|
};
|
|
240
268
|
|
|
241
|
-
const rewordRange = (top, sha, env) => {
|
|
242
|
-
const parent = runGit(['rev-parse', '--verify', `${sha}^`], top);
|
|
243
|
-
const onto =
|
|
244
|
-
parent.status !== 0 ? ['--root'] : [(parent.stdout ?? '').trim()];
|
|
245
|
-
return runGit(['rebase', '--interactive', ...onto], top, undefined, { env });
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
const rewordRangeAsync = async (top, sha, env) => {
|
|
249
|
-
const parent = runGit(['rev-parse', '--verify', `${sha}^`], top);
|
|
250
|
-
const onto =
|
|
251
|
-
parent.status !== 0 ? ['--root'] : [(parent.stdout ?? '').trim()];
|
|
252
|
-
return runGitAsync(['rebase', '--interactive', ...onto], top, { env });
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
const finishRewordRebase = (top, result) => {
|
|
256
|
-
const failed = rebaseFailed(result);
|
|
257
|
-
if (!failed) return;
|
|
258
|
-
runGit(['rebase', '--abort'], top);
|
|
259
|
-
throw new Error(oneLine(failed));
|
|
260
|
-
};
|
|
261
|
-
|
|
262
269
|
const rewordOlder = (top, sha, message) => {
|
|
263
270
|
const prepared = prepareRewordEditor(message);
|
|
264
271
|
try {
|
|
265
|
-
const
|
|
266
|
-
|
|
272
|
+
const args = ['rebase', '--interactive', ...rebaseRange(top, sha)];
|
|
273
|
+
runRebase(top, args, { env: prepared.env });
|
|
267
274
|
} finally {
|
|
268
275
|
fs.rmSync(prepared.dir, { recursive: true, force: true });
|
|
269
276
|
}
|
|
@@ -272,29 +279,29 @@ const rewordOlder = (top, sha, message) => {
|
|
|
272
279
|
const rewordOlderAsync = async (top, sha, message) => {
|
|
273
280
|
const prepared = prepareRewordEditor(message);
|
|
274
281
|
try {
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
if (!failed) return;
|
|
278
|
-
await runGitAsync(['rebase', '--abort'], top);
|
|
279
|
-
throw new Error(oneLine(failed));
|
|
282
|
+
const args = ['rebase', '--interactive', ...rebaseRange(top, sha)];
|
|
283
|
+
await runRebaseAsync(top, args, { env: prepared.env });
|
|
280
284
|
} finally {
|
|
281
285
|
fs.rmSync(prepared.dir, { recursive: true, force: true });
|
|
282
286
|
}
|
|
283
287
|
};
|
|
284
288
|
|
|
285
289
|
const rewordCommit = (top, sha, message) => {
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
+
const commit = resolveCommit(top, namedCommit(sha));
|
|
291
|
+
if (commit === resolveCommit(top, 'HEAD')) {
|
|
292
|
+
requireOk(runGit(REWORD_HEAD_ARGS, top, messageBody(message)));
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
290
295
|
rewordOlder(top, commit, message);
|
|
291
296
|
};
|
|
292
297
|
|
|
293
298
|
const rewordCommitAsync = async (top, sha, message) => {
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
299
|
+
const commit = resolveCommit(top, namedCommit(sha));
|
|
300
|
+
if (commit === resolveCommit(top, 'HEAD')) {
|
|
301
|
+
const input = messageBody(message);
|
|
302
|
+
requireOk(await runGitAsync(REWORD_HEAD_ARGS, top, { input }));
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
298
305
|
await rewordOlderAsync(top, commit, message);
|
|
299
306
|
};
|
|
300
307
|
|
|
@@ -316,7 +323,7 @@ const parseBranchLine = (line) => {
|
|
|
316
323
|
if (!name) return null;
|
|
317
324
|
const mark = parts[0] ?? '';
|
|
318
325
|
const sha = (parts[2] ?? '').trim();
|
|
319
|
-
const date = (parts[3] ?? '')
|
|
326
|
+
const date = shortAge(parts[3] ?? '');
|
|
320
327
|
const track = parseTrack(parts[4]);
|
|
321
328
|
const subject = parts.slice(5).join('\t').trim();
|
|
322
329
|
return {
|
|
@@ -361,13 +368,10 @@ const listBranches = (top) => {
|
|
|
361
368
|
const names = new Set();
|
|
362
369
|
for (const entry of branches) names.add(entry.name);
|
|
363
370
|
const primary = defaultBranch(top, names);
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
listed[i] = { ...entry, isDefault: entry.name === primary };
|
|
369
|
-
}
|
|
370
|
-
return listed;
|
|
371
|
+
return branches.map((entry) => ({
|
|
372
|
+
...entry,
|
|
373
|
+
isDefault: entry.name === primary,
|
|
374
|
+
}));
|
|
371
375
|
};
|
|
372
376
|
|
|
373
377
|
const namedBranch = (name) => {
|
|
@@ -398,23 +402,11 @@ const createBranchAsync = async (top, name) => {
|
|
|
398
402
|
requireOk(await runGitAsync(args, top), 'checkout failed');
|
|
399
403
|
};
|
|
400
404
|
|
|
401
|
-
const rebaseBranch = (top, onto) =>
|
|
402
|
-
|
|
403
|
-
const result = runGit(['rebase', branch], top);
|
|
404
|
-
const failed = rebaseFailed(result);
|
|
405
|
-
if (!failed) return;
|
|
406
|
-
runGit(['rebase', '--abort'], top);
|
|
407
|
-
throw new Error(oneLine(failed));
|
|
408
|
-
};
|
|
405
|
+
const rebaseBranch = (top, onto) =>
|
|
406
|
+
runRebase(top, ['rebase', namedBranch(onto)]);
|
|
409
407
|
|
|
410
|
-
const rebaseBranchAsync = async (top, onto) =>
|
|
411
|
-
|
|
412
|
-
const result = await runGitAsync(['rebase', branch], top);
|
|
413
|
-
const failed = rebaseFailed(result);
|
|
414
|
-
if (!failed) return;
|
|
415
|
-
await runGitAsync(['rebase', '--abort'], top);
|
|
416
|
-
throw new Error(oneLine(failed));
|
|
417
|
-
};
|
|
408
|
+
const rebaseBranchAsync = async (top, onto) =>
|
|
409
|
+
runRebaseAsync(top, ['rebase', namedBranch(onto)]);
|
|
418
410
|
|
|
419
411
|
const dropBranch = (top, name) => {
|
|
420
412
|
const branch = namedBranch(name);
|
|
@@ -478,36 +470,31 @@ const pushCmd = (force, branch) => {
|
|
|
478
470
|
const noUpstream = (msg) =>
|
|
479
471
|
`${msg ?? ''}`.toLowerCase().includes('no upstream');
|
|
480
472
|
|
|
481
|
-
const
|
|
473
|
+
const requirePushOk = (result) => {
|
|
482
474
|
if (result.status === 0) return;
|
|
483
|
-
|
|
484
|
-
if (!noUpstream(msg)) throw pushError(msg);
|
|
485
|
-
const next = run(pushCmd(force, branch));
|
|
486
|
-
if (next.status !== 0) {
|
|
487
|
-
throw pushError(next.stderr || next.stdout || 'push failed');
|
|
488
|
-
}
|
|
475
|
+
throw pushError(result.stderr || result.stdout || 'push failed');
|
|
489
476
|
};
|
|
490
477
|
|
|
491
478
|
const pushChanges = (top, force) => {
|
|
492
479
|
const useForce = force === true;
|
|
493
480
|
const branch = pushBranch(top);
|
|
494
481
|
const tracked = hasUpstream(top) ? '' : branch;
|
|
495
|
-
|
|
496
|
-
|
|
482
|
+
let result = runGit(pushCmd(useForce, tracked), top);
|
|
483
|
+
if (result.status !== 0 && noUpstream(result.stderr || result.stdout)) {
|
|
484
|
+
result = runGit(pushCmd(useForce, branch), top);
|
|
485
|
+
}
|
|
486
|
+
requirePushOk(result);
|
|
497
487
|
};
|
|
498
488
|
|
|
499
489
|
const pushChangesAsync = async (top, force) => {
|
|
500
490
|
const useForce = force === true;
|
|
501
491
|
const branch = pushBranch(top);
|
|
502
492
|
const tracked = hasUpstream(top) ? '' : branch;
|
|
503
|
-
|
|
504
|
-
if (result.status
|
|
505
|
-
|
|
506
|
-
if (!noUpstream(msg)) throw pushError(msg);
|
|
507
|
-
const next = await runGitAsync(pushCmd(useForce, branch), top);
|
|
508
|
-
if (next.status !== 0) {
|
|
509
|
-
throw pushError(next.stderr || next.stdout || 'push failed');
|
|
493
|
+
let result = await runGitAsync(pushCmd(useForce, tracked), top);
|
|
494
|
+
if (result.status !== 0 && noUpstream(result.stderr || result.stdout)) {
|
|
495
|
+
result = await runGitAsync(pushCmd(useForce, branch), top);
|
|
510
496
|
}
|
|
497
|
+
requirePushOk(result);
|
|
511
498
|
};
|
|
512
499
|
|
|
513
500
|
const hasStaged = (top) => {
|
|
@@ -519,9 +506,7 @@ const commitPlan = (kind, message) => {
|
|
|
519
506
|
const args = ['commit'];
|
|
520
507
|
if (kind === 'amend') args.push('--amend');
|
|
521
508
|
args.push('-F', '-');
|
|
522
|
-
|
|
523
|
-
const body = text.endsWith('\n') ? text : `${text}\n`;
|
|
524
|
-
return { args, body };
|
|
509
|
+
return { args, body: messageBody(message) };
|
|
525
510
|
};
|
|
526
511
|
|
|
527
512
|
const commitChanges = (top, kind, message) => {
|
package/lib/git-deps.js
CHANGED
|
@@ -8,12 +8,14 @@ const { foldDepItems, mergeDepFile, depFileMeta } = deps;
|
|
|
8
8
|
const { parseAuditReport, parseOutdatedReport } = deps;
|
|
9
9
|
const { mergeProposedItems, MANIFEST, LOCKFILE } = deps;
|
|
10
10
|
const { collectUsedNames, collectExportMeta } = deps;
|
|
11
|
-
const
|
|
11
|
+
const files = require('./files.js');
|
|
12
|
+
const { itemPath, isPathInScope } = files;
|
|
12
13
|
const proc = require('./utilities.js');
|
|
13
14
|
const { abortError, npmBin, npmOpts, runProc, runProcSync } = proc;
|
|
14
|
-
const git = require('./git.js');
|
|
15
|
+
const git = require('./git-worktree.js');
|
|
15
16
|
const { oneLine, requireOk, runGit } = git;
|
|
16
17
|
const { gitText, worktreeText, readDepSides, revertOnePath } = git;
|
|
18
|
+
const { writeFile, writeIndex } = git;
|
|
17
19
|
|
|
18
20
|
const AUDIT_MS = 15000;
|
|
19
21
|
const INSTALL_MS = 120000;
|
|
@@ -30,12 +32,12 @@ const npmProcOptions = (cwd, extra = {}) => ({
|
|
|
30
32
|
const runNpm = (cwd, args) =>
|
|
31
33
|
runProcSync(npmBin(), args, npmProcOptions(cwd, { timeout: INSTALL_MS }));
|
|
32
34
|
|
|
33
|
-
const runNpmAsync = (cwd, args, extra = {}) =>
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
const runNpmAsync = (cwd, args, extra = {}) => {
|
|
36
|
+
const bin = npmBin();
|
|
37
|
+
const signal = extra.signal;
|
|
38
|
+
const options = npmProcOptions(cwd, { timeout: INSTALL_MS, signal });
|
|
39
|
+
return runProc(bin, args, options);
|
|
40
|
+
};
|
|
39
41
|
|
|
40
42
|
const runNpmJson = (top, args, timeout) => {
|
|
41
43
|
const result = runProcSync(npmBin(), args, npmProcOptions(top, { timeout }));
|
|
@@ -104,18 +106,6 @@ const hasProposed = (item) => {
|
|
|
104
106
|
return !!(change && change.propose);
|
|
105
107
|
};
|
|
106
108
|
|
|
107
|
-
const relatedFiles = (items) => {
|
|
108
|
-
let size = 0;
|
|
109
|
-
for (const item of items) size += depPaths(item).length;
|
|
110
|
-
const files = new Array(size);
|
|
111
|
-
let i = 0;
|
|
112
|
-
for (const item of items) {
|
|
113
|
-
const paths = depPaths(item);
|
|
114
|
-
for (let j = 0; j < paths.length; j++) files[i++] = paths[j];
|
|
115
|
-
}
|
|
116
|
-
return files;
|
|
117
|
-
};
|
|
118
|
-
|
|
119
109
|
const itemsForPath = (rel, items) => {
|
|
120
110
|
const group = [];
|
|
121
111
|
for (const item of items) {
|
|
@@ -287,12 +277,6 @@ const loadExtras = async (snapshot, extra = {}) => {
|
|
|
287
277
|
};
|
|
288
278
|
};
|
|
289
279
|
|
|
290
|
-
const writeWorktreeFile = (top, rel, text) => {
|
|
291
|
-
const abs = path.join(top, rel);
|
|
292
|
-
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
293
|
-
fs.writeFileSync(abs, text);
|
|
294
|
-
};
|
|
295
|
-
|
|
296
280
|
const captureFile = (top, rel) => {
|
|
297
281
|
const abs = path.join(top, rel);
|
|
298
282
|
try {
|
|
@@ -327,16 +311,6 @@ const restoreFiles = (top, owned) => {
|
|
|
327
311
|
for (const saved of owned) restoreFile(top, saved);
|
|
328
312
|
};
|
|
329
313
|
|
|
330
|
-
const writeIndexFile = (top, rel, text) => {
|
|
331
|
-
const saved = captureFile(top, rel);
|
|
332
|
-
writeWorktreeFile(top, rel, text);
|
|
333
|
-
try {
|
|
334
|
-
requireOk(runGit(['add', '--', rel], top));
|
|
335
|
-
} finally {
|
|
336
|
-
restoreFile(top, saved);
|
|
337
|
-
}
|
|
338
|
-
};
|
|
339
|
-
|
|
340
314
|
const mergeDepLive = (top, rel, change, origin, base, side, kind) => {
|
|
341
315
|
const sides = readDepSides(top, null, origin, rel);
|
|
342
316
|
return mergeDepFile(base, sides.oldText, sides.newText, change, side, kind);
|
|
@@ -347,7 +321,7 @@ const applyDepIndex = (top, rel, change, origin, side) => {
|
|
|
347
321
|
if (!meta) return;
|
|
348
322
|
const indexed = gitText(top, `:${rel}`) || gitText(top, `HEAD:${rel}`);
|
|
349
323
|
const text = mergeDepLive(top, rel, change, origin, indexed, side, meta.kind);
|
|
350
|
-
|
|
324
|
+
writeIndex(top, rel, text);
|
|
351
325
|
};
|
|
352
326
|
|
|
353
327
|
const applyDepWorktree = (top, rel, change, origin) => {
|
|
@@ -363,7 +337,7 @@ const applyDepWorktree = (top, rel, change, origin) => {
|
|
|
363
337
|
'old',
|
|
364
338
|
meta.kind,
|
|
365
339
|
);
|
|
366
|
-
|
|
340
|
+
writeFile(top, rel, text);
|
|
367
341
|
};
|
|
368
342
|
|
|
369
343
|
const ownedRelsOf = (plan) => {
|
|
@@ -399,7 +373,7 @@ const applyPreparation = (top, item, plan) => {
|
|
|
399
373
|
'new',
|
|
400
374
|
'manifest',
|
|
401
375
|
);
|
|
402
|
-
|
|
376
|
+
writeFile(top, plan.manifest, text);
|
|
403
377
|
};
|
|
404
378
|
|
|
405
379
|
const stagePlan = (top, plan) => {
|
|
@@ -418,26 +392,14 @@ const finishPlan = (top, plan, result, stage) => {
|
|
|
418
392
|
stageFn(top, plan);
|
|
419
393
|
};
|
|
420
394
|
|
|
421
|
-
const syncInstaller = (plan, extra) => {
|
|
422
|
-
if (extra.run) return extra.run;
|
|
423
|
-
if (plan.runner) return plan.runner;
|
|
424
|
-
return (dir) => runNpm(dir, plan.npmArgs);
|
|
425
|
-
};
|
|
426
|
-
|
|
427
|
-
const asyncInstaller = (plan, extra) => {
|
|
428
|
-
if (extra.run) return extra.run;
|
|
429
|
-
if (plan.runner) return (dir) => Promise.resolve(plan.runner(dir));
|
|
430
|
-
return (dir) => runNpmAsync(dir, plan.npmArgs);
|
|
431
|
-
};
|
|
432
|
-
|
|
433
395
|
const applyProposedUpdate = (top, item, extra = {}) => {
|
|
434
396
|
const plan = proposedNpmPlan(top, item);
|
|
435
397
|
if (!plan) return;
|
|
436
398
|
const owned = captureFiles(top, ownedRelsOf(plan));
|
|
437
399
|
try {
|
|
438
400
|
applyPreparation(top, item, plan);
|
|
439
|
-
const install =
|
|
440
|
-
const result = install(plan.cwd);
|
|
401
|
+
const install = extra.run ?? plan.runner ?? runNpm;
|
|
402
|
+
const result = install(plan.cwd, plan.npmArgs);
|
|
441
403
|
finishPlan(top, plan, result, extra.stage);
|
|
442
404
|
} catch (error) {
|
|
443
405
|
restoreFiles(top, owned);
|
|
@@ -451,8 +413,8 @@ const applyProposedUpdateAsync = async (top, item, extra = {}) => {
|
|
|
451
413
|
const owned = captureFiles(top, ownedRelsOf(plan));
|
|
452
414
|
try {
|
|
453
415
|
applyPreparation(top, item, plan);
|
|
454
|
-
const run =
|
|
455
|
-
const result = await run(plan.cwd);
|
|
416
|
+
const run = extra.run ?? plan.runner ?? runNpmAsync;
|
|
417
|
+
const result = await run(plan.cwd, plan.npmArgs);
|
|
456
418
|
finishPlan(top, plan, result, extra.stage);
|
|
457
419
|
} catch (error) {
|
|
458
420
|
restoreFiles(top, owned);
|
|
@@ -525,7 +487,9 @@ const revertDepItem = (top, item) => {
|
|
|
525
487
|
|
|
526
488
|
const revertFile = (top, rel, items) => {
|
|
527
489
|
const paths = new Set([rel]);
|
|
528
|
-
for (const
|
|
490
|
+
for (const item of items) {
|
|
491
|
+
for (const file of depPaths(item)) paths.add(file);
|
|
492
|
+
}
|
|
529
493
|
for (const file of paths) {
|
|
530
494
|
const group = itemsForPath(file, items);
|
|
531
495
|
const targets = group.length ? group : items;
|
|
@@ -533,12 +497,8 @@ const revertFile = (top, rel, items) => {
|
|
|
533
497
|
}
|
|
534
498
|
};
|
|
535
499
|
|
|
536
|
-
const handles = (item) => !!(item && item.dep);
|
|
537
|
-
|
|
538
500
|
module.exports = {
|
|
539
501
|
hasProposed,
|
|
540
|
-
handles,
|
|
541
|
-
relatedFiles,
|
|
542
502
|
itemsForPath,
|
|
543
503
|
finishLoad,
|
|
544
504
|
loadExtras,
|