From caeba3799f8bb9501ab8650526c9e604f94cbf0e Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Tue, 1 Sep 2026 19:44:59 +0200 Subject: [PATCH] Say which change line is meant in one way MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Which line of a file a diff row is, in the terms a patch is built in, is asked in two places already — staging a selection and editing a hunk — and is about to be asked in a third, of a commit's diff rather than the working tree's. The rule is the same one everywhere: an addition is where it sits in the new version of the file, a deletion where it sat in the old one. So the row is asked for its identity and the patch package for the lines with those identities, instead of walking the diff working the identities out again. Co-Authored-By: Claude Opus 5 (1M context) --- .../controllers/working_tree_diff_actions.go | 44 +++---------------- pkg/gui/types/diff_line_info.go | 12 +++++ 2 files changed, 18 insertions(+), 38 deletions(-) diff --git a/pkg/gui/controllers/working_tree_diff_actions.go b/pkg/gui/controllers/working_tree_diff_actions.go index a8b7d3d8e..ec430b3db 100644 --- a/pkg/gui/controllers/working_tree_diff_actions.go +++ b/pkg/gui/controllers/working_tree_diff_actions.go @@ -122,7 +122,8 @@ func (self *WorkingTreeDiffActions) EditHunk( } parsedPatch := patch.Parse(self.c.Git().WorkingTree.WorktreeFileDiff(file, git_commands.DiffModePlain, onStagedSide)) - lineIndices := changeLineIndices(parsedPatch, infos[:1]) + lineIndices := patch.ChangeLineIndicesForLines(parsedPatch, + []patch.LineIdentity{infos[0].PatchLineIdentity()}) if len(lineIndices) == 0 { return nil } @@ -280,7 +281,10 @@ func (self *WorkingTreeDiffActions) applyDiffLines( ) (bool, error) { parsedPatch := patch.Parse(self.c.Git().WorkingTree.WorktreeFileDiff(file, git_commands.DiffModePlain, sourceCached)) - patchLineIndices := changeLineIndices(parsedPatch, infos) + patchLineIndices := patch.ChangeLineIndicesForLines(parsedPatch, + lo.Map(infos, func(info types.DiffLineInfo, _ int) patch.LineIdentity { + return info.PatchLineIdentity() + })) changesLeft := len(patchLineIndices) < changeLineCount(parsedPatch) @@ -311,42 +315,6 @@ 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 { diff --git a/pkg/gui/types/diff_line_info.go b/pkg/gui/types/diff_line_info.go index 6326837c2..12fed2053 100644 --- a/pkg/gui/types/diff_line_info.go +++ b/pkg/gui/types/diff_line_info.go @@ -1,5 +1,7 @@ package types +import "github.com/jesseduffield/lazygit/pkg/commands/patch" + // DiffLineType classifies a row of a rendered diff. type DiffLineType int @@ -41,6 +43,16 @@ func (self DiffLineInfo) IsChange() bool { return self.Type == DiffLineAdded || self.Type == DiffLineDeleted } +// PatchLineIdentity says which change line of the file the row is, in the terms a patch +// of that file is built and read in: an addition by where it sits in the new version of +// the file, a deletion by where it sat in the old one. Only meaningful for a change row. +func (self DiffLineInfo) PatchLineIdentity() patch.LineIdentity { + if self.Type == DiffLineDeleted { + return patch.LineIdentity{LineNumber: self.OldLine, IsDeletion: true} + } + return patch.LineIdentity{LineNumber: self.NewLine} +} + // IsContent reports whether the row is a line of the file itself — a change or a // context line — as opposed to a header or a marker. Those are the rows that have a // position in the file, and so can be looked for in another rendering of the same