From fc3b7254248848d62e52401ad041d58ca6d073f7 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 20 Jul 2025 16:23:30 +0200 Subject: [PATCH 1/2] Add a test case for a deleted file to TestParseAndFormatPlain Not because it's terribly important to test here (doesn't hurt though), but because it will be useful in the next commit for a new test we're adding there. --- pkg/commands/patch/patch_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/commands/patch/patch_test.go b/pkg/commands/patch/patch_test.go index d9d330017..ba21ad158 100644 --- a/pkg/commands/patch/patch_test.go +++ b/pkg/commands/patch/patch_test.go @@ -115,6 +115,17 @@ index 0000000..4e680cc +grape ` +const deletedFile = `diff --git a/newfile b/newfile +deleted file mode 100644 +index 4e680cc1f..000000000 +--- a/newfile ++++ /dev/null +@@ -1,3 +0,0 @@ +-apple +-orange +-grape +` + const addNewlineToPreviouslyEmptyFile = `diff --git a/newfile b/newfile index e69de29..c6568ea 100644 --- a/newfile @@ -554,6 +565,10 @@ func TestParseAndFormatPlain(t *testing.T) { testName: "newFile", patchStr: newFile, }, + { + testName: "deletedFile", + patchStr: deletedFile, + }, { testName: "addNewlineToPreviouslyEmptyFile", patchStr: addNewlineToPreviouslyEmptyFile, From c5acad777d695425491dc26319260ced9e81677d Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 20 Jul 2025 16:07:24 +0200 Subject: [PATCH 2/2] Don't use hunk mode for added or deleted files When entering staging (or patch building) for an added or deleted file, it doesn't make sense to use hunk mode, because pressing space would stage/unstage the entire file, and if the user wanted to do that, they would have pressed space in the Files panel. So always use line mode for added/deleted files by default, even if the useHunkModeInStagingView user config is on. --- pkg/commands/patch/patch.go | 14 ++++++++ pkg/commands/patch/patch_test.go | 61 ++++++++++++++++++++++++++++++++ pkg/gui/patch_exploring/state.go | 2 +- 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/pkg/commands/patch/patch.go b/pkg/commands/patch/patch.go index 343eea54f..38d432ae6 100644 --- a/pkg/commands/patch/patch.go +++ b/pkg/commands/patch/patch.go @@ -186,3 +186,17 @@ func (self *Patch) AdjustLineNumber(lineNumber int) int { return adjustedLineNumber } + +func (self *Patch) IsSingleHunkForWholeFile() bool { + if len(self.hunks) != 1 { + return false + } + + // We consider a patch to be a single hunk for the whole file if it has only additions or + // deletions but not both, and no context lines. This not quite correct, because it will also + // return true for a block of added or deleted lines if the diff context size is 0, but in this + // case you wouldn't be able to stage things anyway, so it doesn't matter. + bodyLines := self.hunks[0].bodyLines + return nLinesWithKind(bodyLines, []PatchLineKind{DELETION, CONTEXT}) == 0 || + nLinesWithKind(bodyLines, []PatchLineKind{ADDITION, CONTEXT}) == 0 +} diff --git a/pkg/commands/patch/patch_test.go b/pkg/commands/patch/patch_test.go index ba21ad158..f91fc406e 100644 --- a/pkg/commands/patch/patch_test.go +++ b/pkg/commands/patch/patch_test.go @@ -710,3 +710,64 @@ func TestAdjustLineNumber(t *testing.T) { }) } } + +func TestIsSingleHunkForWholeFile(t *testing.T) { + scenarios := []struct { + testName string + patchStr string + expectedResult bool + }{ + { + testName: "simpleDiff", + patchStr: simpleDiff, + expectedResult: false, + }, + { + testName: "addNewlineToEndOfFile", + patchStr: addNewlineToEndOfFile, + expectedResult: false, + }, + { + testName: "removeNewlinefromEndOfFile", + patchStr: removeNewlinefromEndOfFile, + expectedResult: false, + }, + { + testName: "twoHunks", + patchStr: twoHunks, + expectedResult: false, + }, + { + testName: "twoChangesInOneHunk", + patchStr: twoChangesInOneHunk, + expectedResult: false, + }, + { + testName: "newFile", + patchStr: newFile, + expectedResult: true, + }, + { + testName: "deletedFile", + patchStr: deletedFile, + expectedResult: true, + }, + { + testName: "addNewlineToPreviouslyEmptyFile", + patchStr: addNewlineToPreviouslyEmptyFile, + expectedResult: true, + }, + { + testName: "exampleHunk", + patchStr: exampleHunk, + expectedResult: false, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + patch := Parse(s.patchStr) + assert.Equal(t, s.expectedResult, patch.IsSingleHunkForWholeFile()) + }) + } +} diff --git a/pkg/gui/patch_exploring/state.go b/pkg/gui/patch_exploring/state.go index 6223a9979..3852dc096 100644 --- a/pkg/gui/patch_exploring/state.go +++ b/pkg/gui/patch_exploring/state.go @@ -67,7 +67,7 @@ func NewState(diff string, selectedLineIdx int, view *gocui.View, oldState *Stat } selectMode := LINE - if useHunkModeByDefault { + if useHunkModeByDefault && !patch.IsSingleHunkForWholeFile() { selectMode = HUNK }