reslop 0.2.3 → 0.2.5
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 +8 -4
- package/bin/reslop.js +4 -2
- package/lib/api.js +2 -1
- package/lib/capabilities.js +0 -4
- package/lib/cli.js +19 -10
- package/lib/clipboard.js +45 -4
- package/lib/deps-diff.js +61 -118
- package/lib/deps.js +166 -235
- 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 +68 -2
- package/lib/diff/patch.js +2 -1
- package/lib/diff/unit.js +15 -30
- package/lib/editor.js +91 -13
- package/lib/files.js +54 -27
- package/lib/git-branches.js +107 -154
- package/lib/git-deps.js +78 -155
- package/lib/git-worktree.js +508 -0
- package/lib/git.js +23 -580
- package/lib/github.js +9 -8
- package/lib/gitlab.js +5 -4
- package/lib/highlight/bash.js +2 -1
- package/lib/highlight/core.js +11 -0
- package/lib/highlight/css.js +2 -1
- package/lib/highlight/csv.js +2 -1
- package/lib/highlight/dot.js +3 -12
- package/lib/highlight/html.js +2 -1
- package/lib/highlight/json.js +2 -1
- package/lib/highlight/log.js +5 -11
- package/lib/highlight/txt.js +3 -12
- package/lib/highlight.js +2 -1
- package/lib/keys.js +22 -12
- package/lib/manifest.js +4 -6
- package/lib/npm-commands.js +82 -24
- package/lib/remote.js +21 -20
- package/lib/render/branches.js +9 -27
- package/lib/render/chrome.js +109 -35
- package/lib/render/commits.js +216 -216
- package/lib/render/diff.js +160 -47
- package/lib/render/files.js +9 -26
- package/lib/render/frame.js +97 -118
- package/lib/render/notes.js +74 -26
- package/lib/render/npm.js +104 -30
- package/lib/render/present.js +2 -1
- package/lib/render/primitives.js +128 -52
- package/lib/render/render.js +2 -1
- package/lib/render/unit.js +17 -11
- package/lib/report-model.js +6 -4
- package/lib/report-parse.js +8 -14
- package/lib/report-render.js +38 -21
- package/lib/report-run.js +12 -11
- package/lib/report-test.js +45 -51
- package/lib/report-tools.js +44 -25
- package/lib/review-merge.js +102 -131
- package/lib/review.js +69 -78
- package/lib/select.js +52 -0
- package/lib/session/actions.js +72 -87
- package/lib/session/branches.js +18 -52
- package/lib/session/change-actions.js +175 -147
- package/lib/session/commit.js +65 -61
- package/lib/session/compose.js +152 -83
- package/lib/session/dispatch.js +53 -46
- package/lib/session/edit-click.js +57 -0
- package/lib/session/files-pane.js +30 -51
- package/lib/session/items.js +73 -90
- package/lib/session/lifecycle.js +37 -98
- package/lib/session/load.js +52 -57
- package/lib/session/navigation.js +105 -95
- package/lib/session/npm.js +205 -71
- package/lib/session/ops.js +200 -45
- package/lib/session/pointer.js +33 -8
- package/lib/session/progress.js +9 -6
- package/lib/session/review.js +35 -53
- package/lib/session/tasks.js +292 -0
- package/lib/session/terminal.js +17 -19
- package/lib/session/unit.js +3 -4
- package/lib/session/update.js +76 -96
- package/lib/session/view.js +45 -16
- package/lib/session/watch.js +47 -27
- package/lib/session.js +66 -97
- package/lib/source.js +6 -3
- package/lib/update.js +5 -13
- package/lib/utilities.js +25 -3
- package/lib/wrap.js +18 -1
- package/package.json +1 -1
- package/lib/session/todos.js +0 -293
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
3
6
|
const files = require('../files.js');
|
|
4
|
-
const { listPath,
|
|
7
|
+
const { listPath, isTaskItem, isTasksEntry, isReadOnlyOrigin } = files;
|
|
5
8
|
const items = require('./items.js');
|
|
6
9
|
const { needsReload, npmBusyKind, sameBlock } = items;
|
|
7
|
-
const
|
|
10
|
+
const diff = require('../diff/diff.js');
|
|
11
|
+
const { hunkKey } = diff;
|
|
8
12
|
|
|
9
13
|
const nextUnstageOrigin = (item) => {
|
|
10
14
|
if (item.file.isNew === true) return 'untracked';
|
|
@@ -23,13 +27,63 @@ const COMMANDS = {
|
|
|
23
27
|
revert: {
|
|
24
28
|
match: () => true,
|
|
25
29
|
},
|
|
30
|
+
ignore: {
|
|
31
|
+
match: () => true,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const IGNORE_FILES = ['.gitignore', '.npmignore'];
|
|
36
|
+
|
|
37
|
+
const ignorePatternListed = (text, pattern) => {
|
|
38
|
+
for (const line of `${text ?? ''}`.split('\n')) {
|
|
39
|
+
if (line.trim() === pattern) return true;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const appendIgnorePattern = (text, pattern) => {
|
|
45
|
+
if (ignorePatternListed(text, pattern)) return text;
|
|
46
|
+
const base = text.length && !text.endsWith('\n') ? `${text}\n` : text;
|
|
47
|
+
return `${base}${pattern}\n`;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const appendIgnoreFile = (top, name, pattern) => {
|
|
51
|
+
const abs = path.join(top, name);
|
|
52
|
+
if (name === '.npmignore' && !fs.existsSync(abs)) return false;
|
|
53
|
+
const text = fs.existsSync(abs) ? fs.readFileSync(abs, 'utf8') : '';
|
|
54
|
+
const next = appendIgnorePattern(text, pattern);
|
|
55
|
+
if (next === text) return false;
|
|
56
|
+
fs.writeFileSync(abs, next);
|
|
57
|
+
return true;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const ignoreRelPath = (ui, rel) => {
|
|
61
|
+
const pattern = `${rel ?? ''}`.replace(/^\//, '');
|
|
62
|
+
if (!pattern) {
|
|
63
|
+
ui.status = 'not a diff block';
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
let changed = false;
|
|
67
|
+
for (const name of IGNORE_FILES) {
|
|
68
|
+
if (appendIgnoreFile(ui.top, name, pattern)) changed = true;
|
|
69
|
+
}
|
|
70
|
+
if (!changed) {
|
|
71
|
+
ui.status = 'already ignored';
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
ui.ignoreWatch();
|
|
75
|
+
ui.nav.clearSelection();
|
|
76
|
+
ui.reloadAfterChange();
|
|
77
|
+
ui.status = 'ignored';
|
|
26
78
|
};
|
|
27
79
|
|
|
80
|
+
const sameEntry = (entry) => entry;
|
|
81
|
+
|
|
28
82
|
const keepOrigin = (ui, item, origin) => {
|
|
29
83
|
ui.collection.keepOrigin(item, origin);
|
|
30
84
|
const idx = ui.collection.findRestoredIndex({ ...item, origin });
|
|
31
85
|
if (idx >= 0) ui.nav.index = idx;
|
|
32
|
-
|
|
86
|
+
ui.ignoreWatch();
|
|
33
87
|
ui.nav.clearSelection();
|
|
34
88
|
ui.syncReviewPath();
|
|
35
89
|
ui.syncFileCursor();
|
|
@@ -42,11 +96,11 @@ const fileCursorEntry = (ui) => {
|
|
|
42
96
|
|
|
43
97
|
const fileActionItems = (ui) => {
|
|
44
98
|
const entry = fileCursorEntry(ui);
|
|
45
|
-
if (!entry ||
|
|
99
|
+
if (!entry || isTasksEntry(entry)) return [];
|
|
46
100
|
const rel = entry.path;
|
|
47
101
|
const selected = [];
|
|
48
102
|
for (const item of ui.items) {
|
|
49
|
-
if (
|
|
103
|
+
if (isTaskItem(item)) continue;
|
|
50
104
|
if (listPath(item) !== rel) continue;
|
|
51
105
|
selected.push(item);
|
|
52
106
|
}
|
|
@@ -56,7 +110,7 @@ const fileActionItems = (ui) => {
|
|
|
56
110
|
const activeDiffItem = (ui) => {
|
|
57
111
|
const item = ui.current();
|
|
58
112
|
if (!item) return null;
|
|
59
|
-
if (
|
|
113
|
+
if (isTaskItem(item)) {
|
|
60
114
|
ui.status = 'not a diff block';
|
|
61
115
|
return null;
|
|
62
116
|
}
|
|
@@ -139,13 +193,60 @@ const applyBlockAdd = (ui, item) => {
|
|
|
139
193
|
keepOrigin(ui, item, 'staged');
|
|
140
194
|
};
|
|
141
195
|
|
|
196
|
+
const enqueueOrigins = (ui, targets, spec) => {
|
|
197
|
+
const writes = new Array(targets.length);
|
|
198
|
+
for (let i = 0; i < targets.length; i++) {
|
|
199
|
+
const current = liveItem(ui, targets[i]);
|
|
200
|
+
writes[i] = current;
|
|
201
|
+
keepOrigin(ui, current, spec.originOf(current));
|
|
202
|
+
}
|
|
203
|
+
if (spec.afterUi) spec.afterUi();
|
|
204
|
+
if (!spec.busy) ui.status = spec.done;
|
|
205
|
+
ui.ops.enqueueLazy({
|
|
206
|
+
kind: spec.busy || spec.done,
|
|
207
|
+
done: spec.done,
|
|
208
|
+
reload: spec.reload === true,
|
|
209
|
+
write: async (top) => {
|
|
210
|
+
for (const item of writes) await spec.write(top, item);
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const stageLazy = (ui, targets, extra = {}) => {
|
|
216
|
+
const busy = extra.busy || '';
|
|
217
|
+
const repo = ui.repo;
|
|
218
|
+
enqueueOrigins(ui, targets, {
|
|
219
|
+
done: 'staged',
|
|
220
|
+
busy,
|
|
221
|
+
reload: extra.reload === true,
|
|
222
|
+
originOf: () => 'staged',
|
|
223
|
+
afterUi: extra.afterUi,
|
|
224
|
+
write: (top, item) => {
|
|
225
|
+
if (busy && repo.addAsync) return repo.addAsync(top, item);
|
|
226
|
+
return repo.add(top, item);
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const unstageLazy = (ui, targets, extra = {}) => {
|
|
232
|
+
const repo = ui.repo;
|
|
233
|
+
enqueueOrigins(ui, targets, {
|
|
234
|
+
done: 'unstaged',
|
|
235
|
+
busy: '',
|
|
236
|
+
originOf: nextUnstageOrigin,
|
|
237
|
+
afterUi: extra.afterUi,
|
|
238
|
+
write: (top, item) => repo.unstage(top, item),
|
|
239
|
+
});
|
|
240
|
+
};
|
|
241
|
+
|
|
142
242
|
const onBlockAdd = (ui, item) => {
|
|
143
243
|
const busy = npmBusyKind(item);
|
|
144
244
|
const repo = ui.repo;
|
|
145
|
-
if (ui.uiOpen
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
245
|
+
if (ui.uiOpen) {
|
|
246
|
+
return void stageLazy(ui, [item], {
|
|
247
|
+
busy,
|
|
248
|
+
reload: needsReload(item),
|
|
249
|
+
});
|
|
149
250
|
}
|
|
150
251
|
applyGit(
|
|
151
252
|
ui,
|
|
@@ -159,6 +260,7 @@ const onBlockAdd = (ui, item) => {
|
|
|
159
260
|
};
|
|
160
261
|
|
|
161
262
|
const onBlockUnstage = (ui, item) => {
|
|
263
|
+
if (ui.uiOpen) return void unstageLazy(ui, [item]);
|
|
162
264
|
const origin = nextUnstageOrigin(item);
|
|
163
265
|
const repo = ui.repo;
|
|
164
266
|
applyGit(
|
|
@@ -170,6 +272,12 @@ const onBlockUnstage = (ui, item) => {
|
|
|
170
272
|
);
|
|
171
273
|
};
|
|
172
274
|
|
|
275
|
+
const dismissReloadable = (ui, targets) => {
|
|
276
|
+
for (const item of targets) {
|
|
277
|
+
if (needsReload(item)) ui.collection.dismiss(item);
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
173
281
|
const onBlockRevert = (ui, item) => {
|
|
174
282
|
const repo = ui.repo;
|
|
175
283
|
applyGit(
|
|
@@ -178,7 +286,7 @@ const onBlockRevert = (ui, item) => {
|
|
|
178
286
|
'dropped',
|
|
179
287
|
(top, current) => repo.revert(top, current),
|
|
180
288
|
() => {
|
|
181
|
-
|
|
289
|
+
dismissReloadable(ui, [item]);
|
|
182
290
|
ui.reloadAfterChange();
|
|
183
291
|
},
|
|
184
292
|
);
|
|
@@ -194,6 +302,7 @@ const onBlockCommand = (ui, name) => {
|
|
|
194
302
|
const command = COMMANDS[name];
|
|
195
303
|
const item = activeDiffItem(ui);
|
|
196
304
|
if (!guardWritable(ui, item)) return;
|
|
305
|
+
if (name === 'ignore') return void ignoreRelPath(ui, listPath(item));
|
|
197
306
|
if (!command.match(item)) {
|
|
198
307
|
ui.status = command.skipStatus;
|
|
199
308
|
return;
|
|
@@ -201,109 +310,83 @@ const onBlockCommand = (ui, name) => {
|
|
|
201
310
|
BLOCK[name](ui, item);
|
|
202
311
|
};
|
|
203
312
|
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
keepOrigin(ui, current, 'staged');
|
|
313
|
+
const stageTargets = (ui, targets, after = null) => {
|
|
314
|
+
const reload = targets.some(needsReload);
|
|
315
|
+
const busy = targets.map(npmBusyKind).find((kind) => kind) || '';
|
|
316
|
+
if (ui.uiOpen) {
|
|
317
|
+
return void stageLazy(ui, targets, { busy, reload, afterUi: after });
|
|
210
318
|
}
|
|
319
|
+
const keepStaged = (entry) => keepOrigin(ui, entry, 'staged');
|
|
320
|
+
const result = applyFileGit(ui, targets, 'staged', ui.repo.add, keepStaged);
|
|
321
|
+
if (reconcilePartial(ui, result)) return;
|
|
322
|
+
if (reload) ui.reloadAfterChange();
|
|
323
|
+
if (after) after();
|
|
211
324
|
};
|
|
212
325
|
|
|
213
|
-
const
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
selectNextFileAfter(ui, rel);
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
const onFileAdd = (ui) => {
|
|
221
|
-
const selected = fileActionItems(ui);
|
|
222
|
-
if (!selected.length) {
|
|
223
|
-
ui.status = 'not a diff block';
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
if (!guardWritable(ui, selected[0])) return;
|
|
227
|
-
const rel = listPath(selected[0]);
|
|
228
|
-
const targets = selected.filter(COMMANDS.add.match);
|
|
229
|
-
if (!targets.length) {
|
|
230
|
-
ui.status = 'already staged';
|
|
231
|
-
selectNextFileAfter(ui, rel);
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
const proposed = targets.some(needsReload);
|
|
235
|
-
const busy = targets.map(npmBusyKind).find((kind) => kind);
|
|
236
|
-
const repo = ui.repo;
|
|
237
|
-
if (ui.uiOpen && busy && repo.addAsync) {
|
|
238
|
-
const write = writeFileAddAsync(ui, targets);
|
|
239
|
-
const after = afterFileAdd(ui, rel, proposed);
|
|
240
|
-
return void ui.ops.runBusy(busy, 'staged', write, after);
|
|
241
|
-
}
|
|
326
|
+
const unstageTargets = (ui, targets, after = null) => {
|
|
327
|
+
if (ui.uiOpen) return void unstageLazy(ui, targets, { afterUi: after });
|
|
328
|
+
const keepUnstaged = (entry) =>
|
|
329
|
+
keepOrigin(ui, entry, nextUnstageOrigin(entry));
|
|
242
330
|
const result = applyFileGit(
|
|
243
331
|
ui,
|
|
244
332
|
targets,
|
|
245
|
-
'
|
|
246
|
-
|
|
247
|
-
|
|
333
|
+
'unstaged',
|
|
334
|
+
ui.repo.unstage,
|
|
335
|
+
keepUnstaged,
|
|
248
336
|
);
|
|
249
337
|
if (reconcilePartial(ui, result)) return;
|
|
250
|
-
if (
|
|
251
|
-
selectNextFileAfter(ui, rel);
|
|
338
|
+
if (after) after();
|
|
252
339
|
};
|
|
253
340
|
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
if (!selected.length) {
|
|
257
|
-
ui.status = 'not a diff block';
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
if (!guardWritable(ui, selected[0])) return;
|
|
261
|
-
const rel = listPath(selected[0]);
|
|
262
|
-
const targets = selected.filter(COMMANDS.unstage.match);
|
|
263
|
-
if (!targets.length) {
|
|
264
|
-
ui.status = 'not staged';
|
|
265
|
-
selectNextFileAfter(ui, rel);
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
const repo = ui.repo;
|
|
341
|
+
const revertTargets = (ui, targets) => {
|
|
342
|
+
dismissReloadable(ui, targets);
|
|
269
343
|
const result = applyFileGit(
|
|
270
344
|
ui,
|
|
271
345
|
targets,
|
|
272
|
-
'
|
|
273
|
-
|
|
274
|
-
|
|
346
|
+
'dropped',
|
|
347
|
+
ui.repo.revert,
|
|
348
|
+
sameEntry,
|
|
275
349
|
);
|
|
276
|
-
if (
|
|
277
|
-
|
|
350
|
+
if (!result.ok && !result.completed.length) return;
|
|
351
|
+
ui.reloadAfterChange();
|
|
278
352
|
};
|
|
279
353
|
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
if (!selected.length) {
|
|
283
|
-
ui.status = 'not a diff block';
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
if (!guardWritable(ui, selected[0])) return;
|
|
287
|
-
const rel = listPath(selected[0]);
|
|
288
|
-
for (const item of selected) {
|
|
289
|
-
if (needsReload(item)) ui.collection.dismiss(item);
|
|
290
|
-
}
|
|
291
|
-
let ok = false;
|
|
354
|
+
const revertFile = (ui, rel, selected, after) => {
|
|
355
|
+
dismissReloadable(ui, selected);
|
|
292
356
|
try {
|
|
293
357
|
ui.repo.revertFile(ui.top, rel, selected);
|
|
294
358
|
ui.status = 'dropped';
|
|
295
|
-
ok = true;
|
|
296
359
|
} catch (error) {
|
|
297
360
|
ui.status = error.message;
|
|
361
|
+
return void ui.reloadAfterChange();
|
|
298
362
|
}
|
|
299
|
-
const after = ok ? () => selectNextFileAfter(ui, rel) : null;
|
|
300
363
|
ui.reloadAfterChange(after);
|
|
301
364
|
};
|
|
302
365
|
|
|
303
|
-
const
|
|
304
|
-
add:
|
|
305
|
-
unstage:
|
|
306
|
-
revert:
|
|
366
|
+
const TARGETS = {
|
|
367
|
+
add: stageTargets,
|
|
368
|
+
unstage: unstageTargets,
|
|
369
|
+
revert: revertTargets,
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const onFileCommand = (ui, name) => {
|
|
373
|
+
const selected = fileActionItems(ui);
|
|
374
|
+
if (!selected.length) {
|
|
375
|
+
ui.status = 'not a diff block';
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (!guardWritable(ui, selected[0])) return;
|
|
379
|
+
const rel = listPath(selected[0]);
|
|
380
|
+
const next = () => selectNextFileAfter(ui, rel);
|
|
381
|
+
if (name === 'ignore') return void ignoreRelPath(ui, rel);
|
|
382
|
+
if (name === 'revert') return void revertFile(ui, rel, selected, next);
|
|
383
|
+
const command = COMMANDS[name];
|
|
384
|
+
const targets = selected.filter(command.match);
|
|
385
|
+
if (!targets.length) {
|
|
386
|
+
ui.status = command.skipStatus;
|
|
387
|
+
return void next();
|
|
388
|
+
}
|
|
389
|
+
TARGETS[name](ui, targets, next);
|
|
307
390
|
};
|
|
308
391
|
|
|
309
392
|
const hunkActionItems = (ui, item) => {
|
|
@@ -317,62 +400,6 @@ const hunkActionItems = (ui, item) => {
|
|
|
317
400
|
return selected;
|
|
318
401
|
};
|
|
319
402
|
|
|
320
|
-
const onUnitAdd = (ui, targets) => {
|
|
321
|
-
const proposed = targets.some(needsReload);
|
|
322
|
-
const busy = targets.map(npmBusyKind).find((kind) => kind);
|
|
323
|
-
const repo = ui.repo;
|
|
324
|
-
if (ui.uiOpen && busy && repo.addAsync) {
|
|
325
|
-
const write = writeFileAddAsync(ui, targets);
|
|
326
|
-
const after = () => {
|
|
327
|
-
if (proposed) ui.reloadAfterChange();
|
|
328
|
-
};
|
|
329
|
-
return void ui.ops.runBusy(busy, 'staged', write, after);
|
|
330
|
-
}
|
|
331
|
-
const result = applyFileGit(
|
|
332
|
-
ui,
|
|
333
|
-
targets,
|
|
334
|
-
'staged',
|
|
335
|
-
(top, current) => repo.add(top, current),
|
|
336
|
-
(entry) => keepOrigin(ui, entry, 'staged'),
|
|
337
|
-
);
|
|
338
|
-
if (reconcilePartial(ui, result)) return;
|
|
339
|
-
if (proposed) ui.reloadAfterChange();
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
const onUnitUnstage = (ui, targets) => {
|
|
343
|
-
const repo = ui.repo;
|
|
344
|
-
const result = applyFileGit(
|
|
345
|
-
ui,
|
|
346
|
-
targets,
|
|
347
|
-
'unstaged',
|
|
348
|
-
(top, current) => repo.unstage(top, current),
|
|
349
|
-
(entry) => keepOrigin(ui, entry, nextUnstageOrigin(entry)),
|
|
350
|
-
);
|
|
351
|
-
reconcilePartial(ui, result);
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
const onUnitRevert = (ui, targets) => {
|
|
355
|
-
for (const item of targets) {
|
|
356
|
-
if (needsReload(item)) ui.collection.dismiss(item);
|
|
357
|
-
}
|
|
358
|
-
const repo = ui.repo;
|
|
359
|
-
const result = applyFileGit(
|
|
360
|
-
ui,
|
|
361
|
-
targets,
|
|
362
|
-
'dropped',
|
|
363
|
-
(top, current) => repo.revert(top, current),
|
|
364
|
-
(entry) => entry,
|
|
365
|
-
);
|
|
366
|
-
if (!result.ok && !result.completed.length) return;
|
|
367
|
-
ui.reloadAfterChange();
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
const UNIT = {
|
|
371
|
-
add: onUnitAdd,
|
|
372
|
-
unstage: onUnitUnstage,
|
|
373
|
-
revert: onUnitRevert,
|
|
374
|
-
};
|
|
375
|
-
|
|
376
403
|
const onUnitCommand = (ui, name) => {
|
|
377
404
|
const command = COMMANDS[name];
|
|
378
405
|
const item = activeDiffItem(ui);
|
|
@@ -383,11 +410,11 @@ const onUnitCommand = (ui, name) => {
|
|
|
383
410
|
ui.status = command.skipStatus ?? 'not a diff block';
|
|
384
411
|
return;
|
|
385
412
|
}
|
|
386
|
-
|
|
413
|
+
TARGETS[name](ui, targets);
|
|
387
414
|
};
|
|
388
415
|
|
|
389
416
|
const onCommand = (ui, name) => {
|
|
390
|
-
if (ui.nav.pane === 'files') return void
|
|
417
|
+
if (ui.nav.pane === 'files') return void onFileCommand(ui, name);
|
|
391
418
|
if (ui.nav.pane === 'unit') return void onUnitCommand(ui, name);
|
|
392
419
|
onBlockCommand(ui, name);
|
|
393
420
|
};
|
|
@@ -397,6 +424,7 @@ const createChangeActions = (ui) => ({
|
|
|
397
424
|
onAdd: () => onCommand(ui, 'add'),
|
|
398
425
|
onUnstage: () => onCommand(ui, 'unstage'),
|
|
399
426
|
onRevert: () => onCommand(ui, 'revert'),
|
|
427
|
+
onIgnore: () => onCommand(ui, 'ignore'),
|
|
400
428
|
});
|
|
401
429
|
|
|
402
430
|
module.exports = { createChangeActions };
|
package/lib/session/commit.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
const accessors = require('./accessors.js');
|
|
4
|
+
const { stateView } = accessors;
|
|
5
|
+
|
|
6
|
+
const files = require('../files.js');
|
|
7
|
+
const { isReadOnlyOrigin } = files;
|
|
8
|
+
const actions = require('./actions.js');
|
|
9
|
+
const { isFixupCommit } = actions;
|
|
10
|
+
const ops = require('./ops.js');
|
|
11
|
+
const { pickRepoWrite } = ops;
|
|
12
|
+
const commits = require('../render/commits.js');
|
|
13
|
+
const { commitLine, commitText, splitMessage } = commits;
|
|
13
14
|
|
|
14
15
|
const COMMIT_STATUS = {
|
|
15
16
|
commit: 'committed',
|
|
@@ -42,6 +43,13 @@ const briefCommit = (ui) => ui.commits.commitView !== 'full';
|
|
|
42
43
|
|
|
43
44
|
const joinMessage = (line, rest) => (rest ? `${line}\n${rest}` : line);
|
|
44
45
|
|
|
46
|
+
const tidyCommitMessage = (message) => {
|
|
47
|
+
const lines = `${message ?? ''}`.split('\n').map((line) => line.trim());
|
|
48
|
+
while (lines.length && lines[0] === '') lines.shift();
|
|
49
|
+
while (lines.length && lines[lines.length - 1] === '') lines.pop();
|
|
50
|
+
return lines.join('\n');
|
|
51
|
+
};
|
|
52
|
+
|
|
45
53
|
const keepBriefBody = (ui, text) => {
|
|
46
54
|
if (!briefCommit(ui)) return text;
|
|
47
55
|
const parts = splitMessage(text);
|
|
@@ -90,6 +98,24 @@ const clampCommitCursor = (api) => {
|
|
|
90
98
|
if (nav.commitCursor > last) nav.commitCursor = Math.max(0, last);
|
|
91
99
|
};
|
|
92
100
|
|
|
101
|
+
const commitIndexForView = (api) => {
|
|
102
|
+
const ui = api.ui;
|
|
103
|
+
if (hasStagedChanges(ui)) return 0;
|
|
104
|
+
const list = listedCommits(api.state.commits);
|
|
105
|
+
const want = `${ui.rev ?? ''}`;
|
|
106
|
+
const short = `${ui.revShort ?? ''}`;
|
|
107
|
+
if (!want && !short) return 0;
|
|
108
|
+
for (let i = 0; i < list.length; i++) {
|
|
109
|
+
const entry = list[i];
|
|
110
|
+
if (!entry || entry.pending) continue;
|
|
111
|
+
const sha = `${entry.sha ?? ''}`;
|
|
112
|
+
const brief = `${entry.shortSha ?? ''}`;
|
|
113
|
+
if (want && sha === want) return i;
|
|
114
|
+
if (short && (brief === short || sha.startsWith(short))) return i;
|
|
115
|
+
}
|
|
116
|
+
return 0;
|
|
117
|
+
};
|
|
118
|
+
|
|
93
119
|
const refreshCommitList = (api) => {
|
|
94
120
|
const repo = api.ui.repo;
|
|
95
121
|
if (!repo.listCommits) return;
|
|
@@ -120,10 +146,12 @@ const showCommits = (api) => {
|
|
|
120
146
|
api.ui.status = error.message;
|
|
121
147
|
return;
|
|
122
148
|
}
|
|
123
|
-
api.ui.nav
|
|
124
|
-
|
|
149
|
+
const nav = api.ui.nav;
|
|
150
|
+
nav.commitCursor = commitIndexForView(api);
|
|
151
|
+
nav.pane = 'commits';
|
|
152
|
+
nav.resetListScroll('commits');
|
|
125
153
|
api.ui.status = '';
|
|
126
|
-
|
|
154
|
+
nav.clearSelection();
|
|
127
155
|
};
|
|
128
156
|
|
|
129
157
|
const onCommitMove = (api, delta) => {
|
|
@@ -181,11 +209,23 @@ const onCommit = (api) => {
|
|
|
181
209
|
startGitCommit(api, 'commit');
|
|
182
210
|
};
|
|
183
211
|
|
|
212
|
+
const openLoadedDiff = (ui) => {
|
|
213
|
+
ui.nav.tasksOpen = false;
|
|
214
|
+
ui.nav.scroll = 0;
|
|
215
|
+
ui.nav.index = 0;
|
|
216
|
+
ui.nav.pane = ui.items.length ? 'diff' : 'files';
|
|
217
|
+
ui.syncReviewPath();
|
|
218
|
+
ui.syncFileCursor();
|
|
219
|
+
ui.status = '';
|
|
220
|
+
};
|
|
221
|
+
|
|
184
222
|
const showSelectedDiff = (api) => {
|
|
185
223
|
if (api.ui.mode === 'compose') return;
|
|
186
224
|
const entry = selectedCommit(api);
|
|
187
225
|
if (!entry) return;
|
|
188
226
|
const ui = api.ui;
|
|
227
|
+
const viewingCommit = Boolean(ui.rev || ui.revShort);
|
|
228
|
+
if (isPending(entry) && !viewingCommit) return void onCommit(api);
|
|
189
229
|
if (isPending(entry) || !entry.sha) {
|
|
190
230
|
ui.rev = '';
|
|
191
231
|
ui.revShort = '';
|
|
@@ -193,16 +233,10 @@ const showSelectedDiff = (api) => {
|
|
|
193
233
|
ui.rev = entry.sha;
|
|
194
234
|
ui.revShort = entry.shortSha || '';
|
|
195
235
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
ui.nav.pane = ui.items.length ? 'diff' : 'files';
|
|
201
|
-
ui.syncReviewPath();
|
|
202
|
-
ui.syncFileCursor();
|
|
203
|
-
ui.status = '';
|
|
204
|
-
};
|
|
205
|
-
ui.refreshFromRepo({ keepEmpty: true, afterLoad: openDiff });
|
|
236
|
+
ui.refreshFromRepo({
|
|
237
|
+
keepEmpty: true,
|
|
238
|
+
afterLoad: () => openLoadedDiff(ui),
|
|
239
|
+
});
|
|
206
240
|
};
|
|
207
241
|
|
|
208
242
|
const onAmend = (api) => {
|
|
@@ -226,18 +260,9 @@ const applyFixupNow = (api) => {
|
|
|
226
260
|
api.ui.status = 'read only';
|
|
227
261
|
return;
|
|
228
262
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const after = () => refreshAfterWrite(api, { doneStatus: done });
|
|
233
|
-
return void api.ui.ops.runBusy('applying', done, run, after);
|
|
234
|
-
}
|
|
235
|
-
const ok = api.ui.ops.runRepo(done, (top) => {
|
|
236
|
-
repo.applyFixup(top, entry.sha);
|
|
237
|
-
});
|
|
238
|
-
if (!ok) return;
|
|
239
|
-
refreshAfterWrite(api);
|
|
240
|
-
if (!api.ui.status) api.ui.status = done;
|
|
263
|
+
api.ui.ops.runWrite('applyFixup', [entry.sha], 'applying', done, (extra) =>
|
|
264
|
+
refreshAfterWrite(api, extra),
|
|
265
|
+
);
|
|
241
266
|
};
|
|
242
267
|
|
|
243
268
|
const onApply = (api) => {
|
|
@@ -254,12 +279,6 @@ const onFixup = (api) => {
|
|
|
254
279
|
startGitCommit(api, 'fixup');
|
|
255
280
|
};
|
|
256
281
|
|
|
257
|
-
const cancelCommit = (api) => {
|
|
258
|
-
api.ui.mode = 'review';
|
|
259
|
-
api.ui.composer.state.commitKind = null;
|
|
260
|
-
api.ui.status = '';
|
|
261
|
-
};
|
|
262
|
-
|
|
263
282
|
const finishAsyncCommit = (api, kind, message, done, write) => {
|
|
264
283
|
const ui = api.ui;
|
|
265
284
|
ui.composer.closeCompose();
|
|
@@ -323,6 +342,7 @@ const finishGitCommit = (api) => {
|
|
|
323
342
|
if (isInPlaceCommit(kind) && briefCommit(ui)) {
|
|
324
343
|
message = joinMessage(message, ui.composer.state.commitBody);
|
|
325
344
|
}
|
|
345
|
+
message = tidyCommitMessage(message);
|
|
326
346
|
if (!message.trim()) {
|
|
327
347
|
ui.status = 'empty commit message';
|
|
328
348
|
return;
|
|
@@ -360,12 +380,6 @@ const cancelDropCommit = (api) => {
|
|
|
360
380
|
api.ui.status = '';
|
|
361
381
|
};
|
|
362
382
|
|
|
363
|
-
const dropAsync = (api, sha, done, write) => {
|
|
364
|
-
const run = (top) => write(top, sha);
|
|
365
|
-
const after = () => refreshAfterWrite(api, { doneStatus: done });
|
|
366
|
-
return void api.ui.ops.runBusy('dropping', done, run, after);
|
|
367
|
-
};
|
|
368
|
-
|
|
369
383
|
const confirmDropCommit = (api) => {
|
|
370
384
|
const sha = api.state.dropSha;
|
|
371
385
|
const name = api.state.dropName;
|
|
@@ -379,17 +393,9 @@ const confirmDropCommit = (api) => {
|
|
|
379
393
|
api.ui.status = 'read only';
|
|
380
394
|
return;
|
|
381
395
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
return void dropAsync(api, sha, done, picked.write);
|
|
386
|
-
}
|
|
387
|
-
const ok = api.ui.ops.runRepo(done, (top) => {
|
|
388
|
-
repo.dropCommit(top, sha);
|
|
389
|
-
});
|
|
390
|
-
if (!ok) return;
|
|
391
|
-
refreshAfterWrite(api);
|
|
392
|
-
if (!api.ui.status) api.ui.status = done;
|
|
396
|
+
api.ui.ops.runWrite('dropCommit', [sha], 'dropping', done, (extra) =>
|
|
397
|
+
refreshAfterWrite(api, extra),
|
|
398
|
+
);
|
|
393
399
|
};
|
|
394
400
|
|
|
395
401
|
const onView = (api) => {
|
|
@@ -428,8 +434,6 @@ const createCommitController = (ui) => {
|
|
|
428
434
|
onCommitMove: (delta) => onCommitMove(api, delta),
|
|
429
435
|
onShowDiff: () => showSelectedDiff(api),
|
|
430
436
|
onView: () => onView(api),
|
|
431
|
-
cancelCommit: () => cancelCommit(api),
|
|
432
|
-
startGitCommit: (kind) => startGitCommit(api, kind),
|
|
433
437
|
finishGitCommit: () => finishGitCommit(api),
|
|
434
438
|
refresh: () => refreshCommitList(api),
|
|
435
439
|
onDropCommit: () => onDropCommit(api),
|
|
@@ -441,7 +445,7 @@ const createCommitController = (ui) => {
|
|
|
441
445
|
};
|
|
442
446
|
|
|
443
447
|
module.exports = {
|
|
444
|
-
|
|
448
|
+
hasStagedChanges,
|
|
445
449
|
createCommitController,
|
|
446
450
|
listedCommits,
|
|
447
451
|
};
|