feat(lanes): stage/undo one hunk at a time, sticky review file headers
Review and the single-file view gain per-hunk Stage and Undo (stage-hunk / discard-hunk: the hunk is named by its @@ line and re-matched against a fresh diff, then git apply --cached / -R; undo needs confirm: true and two clicks). Each review file header stays pinned while its diff scrolls and carries the file's own stage/unstage/discard.
This commit is contained in:
@@ -785,6 +785,37 @@ async function listTree(dir, { subdir = "" } = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* ONE hunk of a tracked file's working-copy diff (`git diff HEAD`, the same
|
||||
* read the Changes panel shows), as a standalone patch `git apply` accepts:
|
||||
* the file's own header lines plus that hunk. The hunk is named by its `@@`
|
||||
* line and matched against a FRESH diff at the caller's context, so what gets
|
||||
* applied is always git's own text of the current tree — never a patch a
|
||||
* request carried. Null when no hunk matches (the tree moved on, or the file
|
||||
* is a secret, binary or too large to read whole).
|
||||
*/
|
||||
async function hunkPatch(dir, file, header, context) {
|
||||
if (!header || (await isSecretOnDisk(dir, file))) return null;
|
||||
const u = `-U${normalizeContext(context)}`;
|
||||
const raw = await readCapped(
|
||||
dir,
|
||||
["diff", u, "HEAD", "--no-color", "--no-ext-diff", "--", file],
|
||||
MAX_FILE_BYTES
|
||||
).catch(() => "");
|
||||
// A capped read may end mid-hunk; applying half a hunk is worse than none.
|
||||
if (!raw || raw.length >= MAX_FILE_BYTES) return null;
|
||||
const at = raw.indexOf("\n@@ ");
|
||||
if (at < 0) return null;
|
||||
const head = raw.slice(0, at + 1);
|
||||
// A content line starts with " ", "+", "-" or "\\" — never "@@" — so every
|
||||
// line that does is a hunk boundary.
|
||||
const hunk = raw
|
||||
.slice(at + 1)
|
||||
.split(/^(?=@@ )/m)
|
||||
.find((h) => h.slice(0, h.indexOf("\n")) === header);
|
||||
return hunk ? head + (hunk.endsWith("\n") ? hunk : `${hunk}\n`) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Every level of the tracked tree from ONE `git ls-files`, keyed by directory
|
||||
* path ("" is the root), each level shaped and capped exactly like
|
||||
@@ -824,6 +855,7 @@ async function listTreeAll(dir) {
|
||||
|
||||
module.exports = {
|
||||
collectChanges,
|
||||
hunkPatch,
|
||||
laneStatus,
|
||||
listCommits,
|
||||
listPaths,
|
||||
|
||||
Reference in New Issue
Block a user