feat(git): per-block conflict view with mine/theirs choices (#45)
Opening a conflicted file in the Changes panel now shows each conflict block as its two sides, labelled from git's markers and tinted apart, with Keep mine / Take theirs / Keep both per block (undoable, ^/v to jump). Save and mark resolved writes the chosen text through the whole-file editor's PUT /git/file (baseSha stale check) then runs resolve done. The Merge conflicts group's buttons are text, and a conflicted file is marked with a red ! in the Files tree. Fix: ls-files --cached lists an unmerged path once per stage, so the tree showed a conflicted file three times; lsFiles now dedupes.
This commit is contained in:
@@ -16,6 +16,7 @@ const { execFileSync } = require("node:child_process");
|
||||
|
||||
const { runGitAction } = require("../lib/git-ops");
|
||||
const { gitFacts } = require("../lib/worktree");
|
||||
const { listTree, listPaths } = require("../lib/git-diff");
|
||||
|
||||
const ROOT = fs.mkdtempSync(path.join(os.tmpdir(), "agenthub-merge-"));
|
||||
const REPO = path.join(ROOT, "repo");
|
||||
@@ -87,6 +88,15 @@ describe("merge", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("listing files mid-merge", () => {
|
||||
it("lists a conflicted file once, not once per merge stage", async () => {
|
||||
await merge();
|
||||
const root = await listTree(REPO, {});
|
||||
assert.equal(root.entries.filter((e) => e.name === "a.txt").length, 1);
|
||||
assert.deepEqual((await listPaths(REPO, { q: "a.txt" })).paths, ["a.txt"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolve", () => {
|
||||
it("takes our side or theirs, and only for a conflicted path", async () => {
|
||||
await merge();
|
||||
|
||||
@@ -760,7 +760,9 @@ async function lsFiles(dir, args) {
|
||||
const all = out.split("\0");
|
||||
// A complete listing ends in NUL (last token ""); a cut one ends mid-path.
|
||||
const cut = all.pop() !== "";
|
||||
return { all: all.filter(Boolean), cut };
|
||||
// Mid-merge, `ls-files --cached` prints a conflicted path once per stage
|
||||
// (base, ours, theirs): one path is one file.
|
||||
return { all: [...new Set(all.filter(Boolean))], cut };
|
||||
}
|
||||
|
||||
/** What the explorer lists: tracked files plus untracked ones git does not
|
||||
|
||||
Reference in New Issue
Block a user