reslop 0.1.9 → 0.2.1

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.
Files changed (96) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/README.md +3 -1
  3. package/bin/reslop.js +2 -2
  4. package/lib/ansi.js +148 -5
  5. package/lib/api.js +4 -2
  6. package/lib/capabilities.js +13 -14
  7. package/lib/cli.js +38 -49
  8. package/lib/clipboard.js +1 -1
  9. package/lib/{deps/changes.js → deps-diff.js} +309 -19
  10. package/lib/deps.js +498 -29
  11. package/lib/detect.js +1 -7
  12. package/lib/diff/align.js +7 -10
  13. package/lib/diff/diff.js +53 -0
  14. package/lib/diff/display.js +5 -4
  15. package/lib/diff/inline.js +26 -21
  16. package/lib/diff/items.js +6 -4
  17. package/lib/diff/patch.js +1 -3
  18. package/lib/diff/unit.js +392 -0
  19. package/lib/files.js +5 -8
  20. package/lib/{git/branches.js → git-branches.js} +191 -11
  21. package/lib/{git/dependencies.js → git-deps.js} +111 -38
  22. package/lib/git-reword-todo.js +20 -0
  23. package/lib/git.js +606 -68
  24. package/lib/github.js +28 -197
  25. package/lib/gitlab.js +24 -195
  26. package/lib/highlight/bash.js +1 -3
  27. package/lib/highlight/css.js +1 -3
  28. package/lib/highlight/js.js +11 -14
  29. package/lib/highlight.js +6 -9
  30. package/lib/keys.js +14 -8
  31. package/lib/manifest.js +330 -0
  32. package/lib/remote.js +374 -0
  33. package/lib/render/branches.js +18 -100
  34. package/lib/render/chrome.js +71 -14
  35. package/lib/render/commits.js +69 -60
  36. package/lib/render/diff.js +20 -20
  37. package/lib/render/files.js +6 -34
  38. package/lib/render/frame.js +36 -13
  39. package/lib/render/notes.js +26 -8
  40. package/lib/render/primitives.js +99 -11
  41. package/lib/{render.js → render/render.js} +8 -8
  42. package/lib/render/unit.js +41 -0
  43. package/lib/review-merge.js +270 -0
  44. package/lib/review.js +782 -14
  45. package/lib/select.js +5 -10
  46. package/lib/session/accessors.js +9 -1
  47. package/lib/session/actions.js +61 -18
  48. package/lib/session/branches.js +116 -128
  49. package/lib/session/change-actions.js +202 -123
  50. package/lib/session/commit.js +201 -115
  51. package/lib/session/compose.js +181 -15
  52. package/lib/session/dispatch.js +110 -60
  53. package/lib/session/files-pane.js +156 -148
  54. package/lib/session/items.js +22 -30
  55. package/lib/session/lifecycle.js +151 -54
  56. package/lib/session/load.js +19 -33
  57. package/lib/session/navigation.js +12 -1
  58. package/lib/session/ops.js +28 -41
  59. package/lib/session/pointer.js +57 -57
  60. package/lib/session/review.js +23 -37
  61. package/lib/session/terminal.js +6 -12
  62. package/lib/session/todos.js +37 -13
  63. package/lib/session/unit.js +208 -0
  64. package/lib/session/update.js +33 -46
  65. package/lib/session/view.js +78 -9
  66. package/lib/session/watch.js +128 -24
  67. package/lib/session.js +22 -246
  68. package/lib/source.js +3 -3
  69. package/lib/update.js +40 -75
  70. package/lib/{git/proc.js → utilities.js} +75 -11
  71. package/lib/wrap.js +1 -3
  72. package/package.json +2 -2
  73. package/lib/deps/items.js +0 -134
  74. package/lib/deps/manifest.js +0 -152
  75. package/lib/deps/merge.js +0 -120
  76. package/lib/deps/proposals.js +0 -164
  77. package/lib/deps/reports.js +0 -180
  78. package/lib/deps/rows.js +0 -176
  79. package/lib/deps/scan.js +0 -138
  80. package/lib/deps/usage.js +0 -68
  81. package/lib/deps/versions.js +0 -119
  82. package/lib/diff.js +0 -33
  83. package/lib/git/changes.js +0 -164
  84. package/lib/git/read.js +0 -253
  85. package/lib/git/run.js +0 -100
  86. package/lib/npm/commands.js +0 -100
  87. package/lib/remote/changes.js +0 -37
  88. package/lib/remote/headers.js +0 -36
  89. package/lib/remote/http.js +0 -122
  90. package/lib/remote/notes.js +0 -94
  91. package/lib/review/format.js +0 -434
  92. package/lib/review/storage.js +0 -150
  93. package/lib/review/store.js +0 -178
  94. package/lib/review/templates.js +0 -49
  95. package/lib/session/origins.js +0 -7
  96. package/lib/sys.js +0 -84
package/lib/deps/usage.js DELETED
@@ -1,68 +0,0 @@
1
- 'use strict';
2
-
3
- const SOURCE_EXT = ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts'];
4
- const REQUIRE_SPEC = /require\(\s*['"]([^'"]+)['"]\s*\)/g;
5
- const FROM_SPEC = /\bfrom\s+['"]([^'"]+)['"]/g;
6
- const IMPORT_FILE = /import\s+['"]([^'"]+)['"]/g;
7
- const IMPORT_CALL = /import\(\s*['"]([^'"]+)['"]\s*\)/g;
8
-
9
- const toPackageName = (spec) => {
10
- if (!spec || spec.startsWith('.') || spec.startsWith('/')) return '';
11
- if (spec.startsWith('node:')) return '';
12
- if (spec.startsWith('@')) {
13
- const parts = spec.split('/');
14
- if (parts.length < 2) return '';
15
- return `${parts[0]}/${parts[1]}`;
16
- }
17
- return spec.split('/')[0];
18
- };
19
-
20
- const addSpec = (used, spec) => {
21
- const name = toPackageName(spec);
22
- if (name) used.add(name);
23
- };
24
-
25
- const addMatches = (used, text, pattern) => {
26
- const matches = text.matchAll(pattern);
27
- for (const match of matches) addSpec(used, match[1]);
28
- };
29
-
30
- const collectUsedFromText = (used, text) => {
31
- addMatches(used, text, REQUIRE_SPEC);
32
- addMatches(used, text, FROM_SPEC);
33
- addMatches(used, text, IMPORT_FILE);
34
- addMatches(used, text, IMPORT_CALL);
35
- };
36
-
37
- const collectUsedFromScripts = (used, scripts) => {
38
- if (!scripts || typeof scripts !== 'object') return;
39
- for (const script of Object.values(scripts)) {
40
- const tokens = `${script}`.split(/[\s;&|]+/);
41
- for (const token of tokens) {
42
- if (!token || token.startsWith('-')) continue;
43
- if (token === 'npx' || token === 'npm' || token === 'node') continue;
44
- addSpec(used, token);
45
- }
46
- }
47
- };
48
-
49
- const pkgHasExportEntry = (pkg) => {
50
- if (!pkg || typeof pkg !== 'object') return false;
51
- const main = pkg.main;
52
- if (typeof main === 'string' && main) return true;
53
- const esm = pkg.module;
54
- if (typeof esm === 'string' && esm) return true;
55
- const exported = pkg.exports;
56
- if (typeof exported === 'string' && exported) return true;
57
- if (!exported || typeof exported !== 'object') return false;
58
- if (Array.isArray(exported)) return exported.length > 0;
59
- return Object.keys(exported).length > 0;
60
- };
61
-
62
- module.exports = {
63
- SOURCE_EXT,
64
- toPackageName,
65
- collectUsedFromText,
66
- collectUsedFromScripts,
67
- pkgHasExportEntry,
68
- };
@@ -1,119 +0,0 @@
1
- 'use strict';
2
-
3
- const { SECTION_KEYS } = require('./manifest.js');
4
-
5
- const applyWantedRange = (declared, wanted) => {
6
- if (!wanted) return declared ?? '';
7
- if (!declared) return wanted;
8
- if (/^(file:|git[+@:]|workspace:|https?:|npm:)/.test(declared)) {
9
- return declared;
10
- }
11
- if (declared.includes(' ') || declared.includes('|')) return declared;
12
- if (declared.includes('<') || declared.includes('>')) return declared;
13
- const prefix = declared.startsWith('^') || declared.startsWith('~');
14
- const mark = prefix ? declared[0] : '';
15
- return `${mark}${wanted}`;
16
- };
17
-
18
- const findDeclared = (pkg, name, type) => {
19
- if (type && SECTION_KEYS.includes(type)) {
20
- const map = pkg[type];
21
- if (map && typeof map[name] === 'string') {
22
- return { section: type, version: map[name] };
23
- }
24
- }
25
- for (const section of SECTION_KEYS) {
26
- const map = pkg[section];
27
- if (map && typeof map[name] === 'string') {
28
- return { section, version: map[name] };
29
- }
30
- }
31
- return null;
32
- };
33
-
34
- const VER_CORE = /^(\d+)\.(\d+)\.(\d+)/;
35
-
36
- const parseVer = (text) => {
37
- const match = `${text}`.match(VER_CORE);
38
- if (!match) return null;
39
- const major = Number(match[1]);
40
- const minor = Number(match[2]);
41
- const patch = Number(match[3]);
42
- return [major, minor, patch];
43
- };
44
-
45
- const fmtVer = (parts) => `${parts[0]}.${parts[1]}.${parts[2]}`;
46
-
47
- const cmpVer = (left, right) => {
48
- if (left[0] !== right[0]) return left[0] - right[0];
49
- if (left[1] !== right[1]) return left[1] - right[1];
50
- return left[2] - right[2];
51
- };
52
-
53
- const bumpPatch = (parts) => {
54
- const major = parts[0];
55
- const minor = parts[1];
56
- const patch = parts[2] + 1;
57
- return [major, minor, patch];
58
- };
59
-
60
- const clauseUpper = (clause) => {
61
- const pattern = /(<=?)\s*(\d+\.\d+\.\d+)/g;
62
- const matches = clause.matchAll(pattern);
63
- let best = null;
64
- for (const match of matches) {
65
- const op = match[1];
66
- const parsed = parseVer(match[2]);
67
- if (!parsed) continue;
68
- if (best && cmpVer(parsed, best.ver) >= 0) continue;
69
- best = { op, ver: parsed };
70
- }
71
- return best;
72
- };
73
-
74
- const patchedFromRange = (current, range) => {
75
- if (!current || !range) return '';
76
- const now = parseVer(current);
77
- if (!now) return '';
78
- const clauses = `${range}`.split('||');
79
- for (const clause of clauses) {
80
- const upper = clauseUpper(clause);
81
- if (!upper) continue;
82
- if (now[0] !== upper.ver[0]) continue;
83
- if (cmpVer(now, upper.ver) > 0) continue;
84
- if (upper.op === '<') return fmtVer(upper.ver);
85
- return fmtVer(bumpPatch(upper.ver));
86
- }
87
- return '';
88
- };
89
-
90
- const sameMajor = (from, to) => {
91
- const left = parseVer(from);
92
- const right = parseVer(to);
93
- if (!left || !right) return false;
94
- return left[0] === right[0];
95
- };
96
-
97
- const proposedLockVersion = (current, vul) => {
98
- if (!current) return '';
99
- if (!vul) return current;
100
- const fix = vul.fix;
101
- if (fix && sameMajor(current, fix) && fix !== current) return fix;
102
- const patched = patchedFromRange(current, vul.range);
103
- if (patched && patched !== current) return patched;
104
- return current;
105
- };
106
-
107
- const proposedWanted = (out, vul) => {
108
- if (out && out.wanted && out.wanted !== out.current) return out.wanted;
109
- if (vul && vul.fix) return vul.fix;
110
- return '';
111
- };
112
-
113
- module.exports = {
114
- applyWantedRange,
115
- findDeclared,
116
- patchedFromRange,
117
- proposedLockVersion,
118
- proposedWanted,
119
- };
package/lib/diff.js DELETED
@@ -1,33 +0,0 @@
1
- 'use strict';
2
-
3
- const { diffChars } = require('./diff/inline.js');
4
- const { isCtxType, pairIndices, attachInline } = require('./diff/align.js');
5
- const { parseDiff } = require('./diff/parse.js');
6
- const patch = require('./diff/patch.js');
7
- const { splitHunk, flattenBlock, countSides, formatPatch } = patch;
8
- const display = require('./diff/display.js');
9
- const { DISPLAY_CONTEXT, displayLines, blockAddText } = display;
10
- const edit = require('./diff/edit.js');
11
- const { addBlockRange, replaceBlockAdds } = edit;
12
- const items = require('./diff/items.js');
13
- const { synthesizeNewFile, itemsFromFiles, refreshIndexPatches } = items;
14
-
15
- module.exports = {
16
- DISPLAY_CONTEXT,
17
- isCtxType,
18
- diffChars,
19
- pairIndices,
20
- attachInline,
21
- parseDiff,
22
- splitHunk,
23
- flattenBlock,
24
- countSides,
25
- formatPatch,
26
- displayLines,
27
- blockAddText,
28
- addBlockRange,
29
- replaceBlockAdds,
30
- synthesizeNewFile,
31
- itemsFromFiles,
32
- refreshIndexPatches,
33
- };
@@ -1,164 +0,0 @@
1
- 'use strict';
2
-
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
-
6
- const { itemPath } = require('../files.js');
7
- const { replaceBlockAdds } = require('../diff/edit.js');
8
- const { requireOk, runGit } = require('./run.js');
9
-
10
- const applyPatch = (cwd, patch, flags) => {
11
- const args = ['apply', ...flags, '--whitespace=nowarn', '-'];
12
- const result = runGit(args, cwd, patch);
13
- requireOk(result, 'apply failed');
14
- };
15
-
16
- const addItem = (top, item) => {
17
- if (item.origin === 'staged' || item.origin === 'commit') return;
18
- if (item.origin === 'untracked' || item.file.isBinary) {
19
- requireOk(runGit(['add', '--', item.file.newPath], top));
20
- return;
21
- }
22
- applyPatch(top, item.patchAdd, ['--cached']);
23
- };
24
-
25
- const unstageItem = (top, item) => {
26
- if (item.origin !== 'staged') return;
27
- const rel = itemPath(item);
28
- if (item.file.isBinary) {
29
- requireOk(runGit(['restore', '--staged', '--', rel], top));
30
- return;
31
- }
32
- applyPatch(top, item.patchAdd, ['--reverse', '--cached']);
33
- };
34
-
35
- const revertOnePath = (top, rel, items) => {
36
- const origins = new Set();
37
- let isNew = false;
38
- for (const item of items) {
39
- origins.add(item.origin);
40
- if (item.file && item.file.isNew) isNew = true;
41
- }
42
- if (isNew) {
43
- if (origins.has('staged')) {
44
- requireOk(runGit(['rm', '-f', '--', rel], top));
45
- return;
46
- }
47
- fs.unlinkSync(path.join(top, rel));
48
- return;
49
- }
50
- if (origins.has('untracked') && origins.size === 1) {
51
- fs.unlinkSync(path.join(top, rel));
52
- return;
53
- }
54
- const args = ['restore', '-s', 'HEAD', '--worktree', '--staged', '--', rel];
55
- requireOk(runGit(args, top));
56
- };
57
-
58
- const revertItem = (top, item) => {
59
- if (item.origin === 'commit') return;
60
- const rel = itemPath(item);
61
- if (item.origin === 'untracked') {
62
- fs.unlinkSync(path.join(top, rel));
63
- return;
64
- }
65
- if (item.file.isBinary) {
66
- const args = ['restore', '-s', 'HEAD', '--worktree'];
67
- if (item.origin === 'staged') args.push('--staged');
68
- args.push('--', rel);
69
- requireOk(runGit(args, top));
70
- return;
71
- }
72
- if (item.origin === 'unstaged') {
73
- applyPatch(top, item.patchRevert, ['--reverse']);
74
- return;
75
- }
76
- applyPatch(top, item.patchRevert, ['--reverse', '--cached']);
77
- try {
78
- applyPatch(top, item.patchRevert, ['--reverse']);
79
- } catch (error) {
80
- if (!error.message.includes('does not apply')) throw error;
81
- }
82
- };
83
-
84
- const revertFile = (top, rel, items) => {
85
- const group = [];
86
- for (const item of items) {
87
- if (itemPath(item) === rel) group.push(item);
88
- }
89
- const targets = group.length ? group : items;
90
- revertOnePath(top, rel, targets);
91
- };
92
-
93
- const indexText = (top, rel) => {
94
- const result = runGit(['show', `:${rel}`], top);
95
- requireOk(result, 'show failed');
96
- return result.stdout ?? '';
97
- };
98
-
99
- const indexMode = (top, rel) => {
100
- const result = runGit(['ls-files', '--stage', '--', rel], top);
101
- if (result.status !== 0) return '100644';
102
- const line = (result.stdout ?? '').trim();
103
- if (!line) return '100644';
104
- const mode = line.split(' ')[0];
105
- return mode || '100644';
106
- };
107
-
108
- const writeIndex = (top, rel, text) => {
109
- const hashed = runGit(['hash-object', '-w', '--stdin'], top, text);
110
- requireOk(hashed, 'hash-object failed');
111
- const sha = (hashed.stdout ?? '').trim();
112
- const mode = indexMode(top, rel);
113
- const args = ['update-index', '--cacheinfo', mode, sha, rel];
114
- requireOk(runGit(args, top), 'update-index failed');
115
- };
116
-
117
- const writeWorktree = (abs, text) => {
118
- fs.writeFileSync(abs, text);
119
- };
120
-
121
- const editStaged = (top, rel, abs, hunk, blockId, text) => {
122
- const source = indexText(top, rel);
123
- const next = replaceBlockAdds(source, hunk, blockId, text);
124
- writeIndex(top, rel, next);
125
- let work = null;
126
- try {
127
- work = fs.readFileSync(abs, 'utf8');
128
- } catch (error) {
129
- if (error.code !== 'ENOENT') throw error;
130
- }
131
- if (work === null) {
132
- writeWorktree(abs, next);
133
- return;
134
- }
135
- if (work === source) writeWorktree(abs, next);
136
- };
137
-
138
- const editWorktree = (abs, hunk, blockId, text) => {
139
- const source = fs.readFileSync(abs, 'utf8');
140
- const next = replaceBlockAdds(source, hunk, blockId, text);
141
- if (next === source) return;
142
- writeWorktree(abs, next);
143
- };
144
-
145
- const editItem = (top, item, text) => {
146
- if (item.origin === 'commit' || item.origin === 'pr') return;
147
- const rel = itemPath(item);
148
- if (!rel || !item.hunk) return;
149
- const abs = path.join(top, rel);
150
- if (item.origin === 'staged') {
151
- editStaged(top, rel, abs, item.hunk, item.blockId, text);
152
- return;
153
- }
154
- editWorktree(abs, item.hunk, item.blockId, text);
155
- };
156
-
157
- module.exports = {
158
- addItem,
159
- unstageItem,
160
- revertOnePath,
161
- revertItem,
162
- revertFile,
163
- editItem,
164
- };
package/lib/git/read.js DELETED
@@ -1,253 +0,0 @@
1
- 'use strict';
2
-
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
-
6
- const diff = require('../diff.js');
7
- const { parseDiff, synthesizeNewFile, itemsFromFiles } = diff;
8
- const { itemPath, REVIEW_DIR } = require('../files.js');
9
- const git = require('./run.js');
10
- const { requireOk, runGit, runGitAsync } = git;
11
- const { pathArgsOf, cachedDiffArgs, worktreeDiffArgs, commitDiffArgs } = git;
12
-
13
- const BINARY_SIZE = 1000000;
14
-
15
- const isReviewFile = (rel) => {
16
- if (!rel) return false;
17
- const first = rel.split(/[/\\]/)[0];
18
- return first === REVIEW_DIR;
19
- };
20
-
21
- const dropReviewItems = (items) => {
22
- const kept = [];
23
- for (const item of items) {
24
- if (isReviewFile(itemPath(item))) continue;
25
- kept.push(item);
26
- }
27
- return kept;
28
- };
29
-
30
- const isBinaryBuffer = (buf) => {
31
- if (buf.length > BINARY_SIZE) return true;
32
- return buf.includes(0);
33
- };
34
-
35
- const namesFromLs = (stdout) => {
36
- if (!stdout) return [];
37
- return stdout.split('\0').filter(Boolean);
38
- };
39
-
40
- const untrackedLsArgs = (paths) => {
41
- const args = ['ls-files', '-o', '--exclude-standard', '-z'];
42
- if (paths.length) args.push('--', ...paths);
43
- return args;
44
- };
45
-
46
- const listUntracked = (top, paths) => {
47
- const result = requireOk(runGit(untrackedLsArgs(paths), top));
48
- return namesFromLs(result.stdout);
49
- };
50
-
51
- const binaryNewFile = (rel) => ({
52
- oldPath: rel,
53
- newPath: rel,
54
- isNew: true,
55
- isDeleted: false,
56
- isBinary: true,
57
- preamble: [`diff --git a/${rel} b/${rel}`, 'new file mode 100644'],
58
- hunks: [],
59
- });
60
-
61
- const untrackedItemsFrom = (top, names) => {
62
- const files = [];
63
- for (const rel of names) {
64
- if (isReviewFile(rel)) continue;
65
- const full = path.join(top, rel);
66
- let buf;
67
- try {
68
- buf = fs.readFileSync(full);
69
- } catch (error) {
70
- if (error.code === 'ENOENT') continue;
71
- throw error;
72
- }
73
- if (isBinaryBuffer(buf)) {
74
- files.push(binaryNewFile(rel));
75
- continue;
76
- }
77
- files.push(synthesizeNewFile(rel, buf.toString('utf8')));
78
- }
79
- return itemsFromFiles(files, 'untracked');
80
- };
81
-
82
- const mergeItems = (groups) => {
83
- const byPath = new Map();
84
- for (const group of groups) {
85
- for (const item of group) {
86
- const rel = itemPath(item);
87
- const list = byPath.get(rel);
88
- if (list) {
89
- list.push(item);
90
- continue;
91
- }
92
- byPath.set(rel, [item]);
93
- }
94
- }
95
- const items = [];
96
- for (const list of byPath.values()) {
97
- for (const item of list) items.push(item);
98
- }
99
- return items;
100
- };
101
-
102
- const gitText = (top, spec) => {
103
- const result = runGit(['show', spec], top);
104
- if (result.status !== 0) return '';
105
- return result.stdout ?? '';
106
- };
107
-
108
- const worktreeText = (top, rel) => {
109
- try {
110
- return fs.readFileSync(path.join(top, rel), 'utf8');
111
- } catch {
112
- return '';
113
- }
114
- };
115
-
116
- const readDepSides = (top, rev, origin, rel) => {
117
- if (origin === 'untracked') {
118
- const newText = worktreeText(top, rel);
119
- return { oldText: '', newText };
120
- }
121
- if (origin === 'staged') {
122
- const oldText = gitText(top, `HEAD:${rel}`);
123
- const newText = gitText(top, `:${rel}`);
124
- return { oldText, newText };
125
- }
126
- if (origin === 'unstaged') {
127
- const indexed = gitText(top, `:${rel}`);
128
- const oldText = indexed || gitText(top, `HEAD:${rel}`);
129
- const newText = worktreeText(top, rel);
130
- return { oldText, newText };
131
- }
132
- if (origin === 'commit' && rev) {
133
- const oldText = gitText(top, `${rev}^:${rel}`);
134
- const newText = gitText(top, `${rev}:${rel}`);
135
- return { oldText, newText };
136
- }
137
- return { oldText: '', newText: '' };
138
- };
139
-
140
- const toplevel = (cwd) => {
141
- const result = runGit(['rev-parse', '--show-toplevel'], cwd);
142
- if (result.status !== 0) throw new Error('not a git repository');
143
- return result.stdout.trim();
144
- };
145
-
146
- const resolveRev = (cwd, spec) => {
147
- if (!spec) return null;
148
- const top = toplevel(cwd);
149
- const args = ['rev-parse', '--verify', `${spec}^{commit}`];
150
- const result = runGit(args, top);
151
- if (result.status !== 0) return null;
152
- return result.stdout.trim();
153
- };
154
-
155
- const shortRev = (top, commit) => {
156
- const result = runGit(['rev-parse', '--short', commit], top);
157
- if (result.status !== 0) return commit.slice(0, 7);
158
- return result.stdout.trim();
159
- };
160
-
161
- const currentBranch = (top) => {
162
- const args = ['rev-parse', '--abbrev-ref', 'HEAD'];
163
- const result = runGit(args, top);
164
- if (result.status !== 0) return '';
165
- const name = result.stdout.trim();
166
- if (!name || name === 'HEAD') return '';
167
- return name;
168
- };
169
-
170
- const snapshotWorktree = (top, stagedOut, unstagedOut, names) => {
171
- const stagedDiff = parseDiff(stagedOut);
172
- const unstagedDiff = parseDiff(unstagedOut);
173
- const staged = itemsFromFiles(stagedDiff, 'staged');
174
- const unstaged = itemsFromFiles(unstagedDiff, 'unstaged');
175
- const untracked = untrackedItemsFrom(top, names);
176
- const combined = mergeItems([staged, unstaged, untracked]);
177
- const parsed = dropReviewItems(combined);
178
- const branch = currentBranch(top);
179
- return { top, parsed, branch };
180
- };
181
-
182
- const snapshotCommit = (top, commit, files) => {
183
- const commitItems = itemsFromFiles(files, 'commit');
184
- const parsed = dropReviewItems(commitItems);
185
- const revShort = shortRev(top, commit);
186
- const branch = currentBranch(top);
187
- return { top, parsed, rev: commit, revShort, branch };
188
- };
189
-
190
- const loadCommit = (top, commit, extra) => {
191
- const parent = runGit(['rev-parse', '--verify', `${commit}^`], top);
192
- const args = commitDiffArgs(commit, parent, extra);
193
- const result = requireOk(runGit(args, top));
194
- return parseDiff(result.stdout);
195
- };
196
-
197
- const loadCommitAsync = async (top, commit, extra, signal) => {
198
- const parent = await runGitAsync(
199
- ['rev-parse', '--verify', `${commit}^`],
200
- top,
201
- { signal },
202
- );
203
- const args = commitDiffArgs(commit, parent, extra);
204
- const result = requireOk(await runGitAsync(args, top, { signal }));
205
- return parseDiff(result.stdout);
206
- };
207
-
208
- const readSnapshot = (cwd, paths, options) => {
209
- const top = toplevel(cwd);
210
- const extra = pathArgsOf(paths);
211
- const rev = options.commit;
212
- if (rev) return snapshotCommit(top, rev, loadCommit(top, rev, extra));
213
- const stagedRes = requireOk(runGit(cachedDiffArgs(extra), top));
214
- const unstagedRes = requireOk(runGit(worktreeDiffArgs(extra), top));
215
- const names = listUntracked(top, paths);
216
- return snapshotWorktree(top, stagedRes.stdout, unstagedRes.stdout, names);
217
- };
218
-
219
- const readSnapshotAsync = async (cwd, paths, options) => {
220
- const top = toplevel(cwd);
221
- const extra = pathArgsOf(paths);
222
- const rev = options.commit;
223
- const signal = options.signal;
224
- if (rev) {
225
- const files = await loadCommitAsync(top, rev, extra, signal);
226
- return snapshotCommit(top, rev, files);
227
- }
228
- const stagedArgs = cachedDiffArgs(extra);
229
- const unstagedArgs = worktreeDiffArgs(extra);
230
- const lsArgs = untrackedLsArgs(paths);
231
- const stagedP = runGitAsync(stagedArgs, top, { signal });
232
- const unstagedP = runGitAsync(unstagedArgs, top, { signal });
233
- const lsP = runGitAsync(lsArgs, top, { signal });
234
- const extras = await Promise.all([stagedP, unstagedP, lsP]);
235
- const stagedRes = extras[0];
236
- const unstagedRes = extras[1];
237
- const lsRes = extras[2];
238
- requireOk(stagedRes);
239
- requireOk(unstagedRes);
240
- const names = namesFromLs(requireOk(lsRes).stdout);
241
- return snapshotWorktree(top, stagedRes.stdout, unstagedRes.stdout, names);
242
- };
243
-
244
- module.exports = {
245
- toplevel,
246
- resolveRev,
247
- currentBranch,
248
- gitText,
249
- worktreeText,
250
- readDepSides,
251
- readSnapshot,
252
- readSnapshotAsync,
253
- };