Ask a parsed patch which of its lines a selection covers

Staging a selection walks the file's diff to find the lines the user
pointed at, by where each of them sits in the file. The next commit needs
the same answer for a single row, so pull the walk out of the staging path
before there are two copies of it.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller
2026-10-04 19:01:02 +02:00
co-authored by Claude Opus 5
parent c0adf81978
commit f214ba90cc
@@ -198,34 +198,7 @@ func (self *WorkingTreeDiffActions) applyDiffLines(
) (bool, error) {
parsedPatch := patch.Parse(self.c.Git().WorkingTree.WorktreeFileDiff(file, git_commands.DiffModePlain, sourceCached))
type changeLine struct {
lineNumber int
isDeletion bool
}
selected := set.New[changeLine]()
for _, info := range infos {
if info.Type == types.DiffLineDeleted {
selected.Add(changeLine{info.OldLine, true})
} else {
selected.Add(changeLine{info.NewLine, false})
}
}
var patchLineIndices []int
for idx, line := range parsedPatch.Lines() {
var key changeLine
switch {
case line.IsAddition():
key = changeLine{parsedPatch.LineNumberOfLine(idx), false}
case line.IsDeletion():
key = changeLine{parsedPatch.OldLineNumberOfLine(idx), true}
default:
continue
}
if selected.Includes(key) {
patchLineIndices = append(patchLineIndices, idx)
}
}
patchLineIndices := changeLineIndices(parsedPatch, infos)
changesLeft := len(patchLineIndices) < changeLineCount(parsedPatch)
@@ -256,6 +229,42 @@ func (self *WorkingTreeDiffActions) applyDiffLines(
return changesLeft, self.c.Git().Patch.ApplyPatch(patchToApply, opts)
}
// changeLineIndices says which lines of a parsed patch the given diff rows are, as the
// indices a patch is built in terms of. Each row is looked for by where it sits in the
// file: an addition by its place in the new version, a deletion by its place in the
// old one, which is what tells the two halves of a changed line apart.
func changeLineIndices(parsedPatch *patch.Patch, infos []types.DiffLineInfo) []int {
type changeLine struct {
lineNumber int
isDeletion bool
}
selected := set.New[changeLine]()
for _, info := range infos {
if info.Type == types.DiffLineDeleted {
selected.Add(changeLine{info.OldLine, true})
} else {
selected.Add(changeLine{info.NewLine, false})
}
}
var indices []int
for idx, line := range parsedPatch.Lines() {
var key changeLine
switch {
case line.IsAddition():
key = changeLine{parsedPatch.LineNumberOfLine(idx), false}
case line.IsDeletion():
key = changeLine{parsedPatch.OldLineNumberOfLine(idx), true}
default:
continue
}
if selected.Includes(key) {
indices = append(indices, idx)
}
}
return indices
}
// changeLineCount returns how many of a patch's lines are changes rather than context
// or header. A selection of the whole diff covers exactly that many.
func changeLineCount(p *patch.Patch) int {