diff --git a/pkg/commands/patch/patch.go b/pkg/commands/patch/patch.go index 343eea54f16..38d432ae6bc 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 d9d330017f0..f91fc406eeb 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, @@ -695,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 6223a997920..3852dc09656 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 }