From 4591cdb685484a4dcd37577b1e18109afdcb2945 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 20 Aug 2026 20:18:32 +0200 Subject: [PATCH] Build mixed custom patches from the focused diff The broad regression for combining a whole file, a hunk, a range, and individual lines must survive the patch-building panel. Keep the side panel for the explicit whole-file operation and drive every content selection through the commit's focused diff. Co-Authored-By: GitHub Copilot --- .../build_patch_with_mixed_selections.go | 114 +++++++++++++ .../patch_building/specific_selection.go | 159 ------------------ pkg/integration/tests/test_list.go | 2 +- 3 files changed, 115 insertions(+), 160 deletions(-) create mode 100644 pkg/integration/tests/main_view/build_patch_with_mixed_selections.go delete mode 100644 pkg/integration/tests/patch_building/specific_selection.go diff --git a/pkg/integration/tests/main_view/build_patch_with_mixed_selections.go b/pkg/integration/tests/main_view/build_patch_with_mixed_selections.go new file mode 100644 index 000000000..e1fee0c37 --- /dev/null +++ b/pkg/integration/tests/main_view/build_patch_with_mixed_selections.go @@ -0,0 +1,114 @@ +package main_view + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var BuildPatchWithMixedSelections = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Build a custom patch from a whole file, a hunk, individual lines, and a range", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) { + config.GetUserConfig().Gui.UseHunkModeInStagingView = false + }, + SetupRepo: func(shell *Shell) { + shell.CreateFileAndAdd("hunk-file", "1a\n1b\n1c\n1d\n1e\n1f\n1g\n1h\n1i\n1j\n1k\n1l\n1m\n1n\n1o\n1p\n1q\n1r\n1s\n1t\n1u\n1v\n1w\n1x\n1y\n1z\n") + shell.Commit("first commit") + + shell.UpdateFileAndAdd("hunk-file", "aa\n1b\ncc\n1d\n1e\n1f\n1g\n1h\n1i\n1j\n1k\n1l\n1m\n1n\n1o\n1p\n1q\n1r\n1s\ntt\nuu\nvv\n1w\n1x\n1y\n1z\n") + shell.CreateFileAndAdd("line-file", "2a\n2b\n2c\n2d\n2e\n2f\n2g\n2h\n2i\n2j\n2k\n2l\n2m\n2n\n2o\n2p\n2q\n2r\n2s\n2t\n2u\n2v\n2w\n2x\n2y\n2z\n") + shell.CreateFileAndAdd("direct-file", "direct file content") + shell.Commit("second commit") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("second commit").IsSelected(), + Contains("first commit"), + ). + PressEnter() + + // The side panel remains the way to put an entire file operation into a patch. + t.Views().CommitFiles(). + IsFocused(). + Lines( + Equals("▼ /").IsSelected(), + Contains("direct-file"), + Contains("hunk-file"), + Contains("line-file"), + ). + SelectNextItem(). + PressPrimaryAction() + + t.Views().Information().Content(Contains("Building patch")) + t.Views().Secondary().Content(Contains("direct file content")) + + // Add the first modification of hunk-file as one change block. + t.Views().CommitFiles(). + NavigateToLine(Contains("hunk-file")). + Press(keys.Universal.FocusMainView) + t.Views().Main(). + IsFocused(). + SelectedLines(Contains("-1a")). + Press(keys.Main.ToggleSelectHunk). + SelectedLines( + Contains("-1a"), + Contains("+aa"), + ). + PressPrimaryAction(). + PressEscape(). + PressEscape() + + // Build line-file's part from one line, a range, and another line. + t.Views().CommitFiles(). + IsFocused(). + NavigateToLine(Contains("line-file")). + Press(keys.Universal.FocusMainView) + t.Views().Main(). + IsFocused(). + SelectedLines(Contains("+2a")). + PressPrimaryAction(). + NavigateToLine(Contains("+2c")). + Press(keys.Universal.ToggleRangeSelect). + NavigateToLine(Contains("+2e")). + PressPrimaryAction(). + NavigateToLine(Contains("+2g")). + PressPrimaryAction(). + PressEscape() + + t.Views().Secondary().ContainsLines( + // direct-file patch + Contains(`diff --git a/direct-file b/direct-file`), + Contains(`index`), + Contains(`--- a/direct-file`), + Contains(`+++ b/direct-file`), + Contains(`@@ -0,0 +1 @@`), + Contains(`+direct file content`), + Contains(`\ No newline at end of file`), + // hunk-file patch + Contains(`diff --git a/hunk-file b/hunk-file`), + Contains(`index`), + Contains(`--- a/hunk-file`), + Contains(`+++ b/hunk-file`), + Contains(`@@ -1,4 +1,4 @@`), + Contains(`-1a`), + Contains(`+aa`), + Contains(` 1b`), + Contains(` 1c`), + Contains(` 1d`), + // line-file patch + Contains(`diff --git a/line-file b/line-file`), + Contains(`index`), + Contains(`--- a/line-file`), + Contains(`+++ b/line-file`), + Contains(`@@ -0,0 +1,5 @@`), + Contains(`+2a`), + Contains(`+2c`), + Contains(`+2d`), + Contains(`+2e`), + Contains(`+2g`), + ) + }, +}) diff --git a/pkg/integration/tests/patch_building/specific_selection.go b/pkg/integration/tests/patch_building/specific_selection.go deleted file mode 100644 index 723bde864..000000000 --- a/pkg/integration/tests/patch_building/specific_selection.go +++ /dev/null @@ -1,159 +0,0 @@ -package patch_building - -import ( - "github.com/jesseduffield/lazygit/pkg/config" - . "github.com/jesseduffield/lazygit/pkg/integration/components" -) - -var SpecificSelection = NewIntegrationTest(NewIntegrationTestArgs{ - Description: "Build a custom patch with a specific selection of lines, adding individual lines, as well as a range and hunk, and adding a file directly", - ExtraCmdArgs: []string{}, - Skip: false, - SetupConfig: func(config *config.AppConfig) { - config.GetUserConfig().Gui.UseHunkModeInStagingView = false - }, - SetupRepo: func(shell *Shell) { - shell.CreateFileAndAdd("hunk-file", "1a\n1b\n1c\n1d\n1e\n1f\n1g\n1h\n1i\n1j\n1k\n1l\n1m\n1n\n1o\n1p\n1q\n1r\n1s\n1t\n1u\n1v\n1w\n1x\n1y\n1z\n") - shell.Commit("first commit") - - // making changes in two separate places for the sake of having two hunks - shell.UpdateFileAndAdd("hunk-file", "aa\n1b\ncc\n1d\n1e\n1f\n1g\n1h\n1i\n1j\n1k\n1l\n1m\n1n\n1o\n1p\n1q\n1r\n1s\ntt\nuu\nvv\n1w\n1x\n1y\n1z\n") - - shell.CreateFileAndAdd("line-file", "2a\n2b\n2c\n2d\n2e\n2f\n2g\n2h\n2i\n2j\n2k\n2l\n2m\n2n\n2o\n2p\n2q\n2r\n2s\n2t\n2u\n2v\n2w\n2x\n2y\n2z\n") - shell.CreateFileAndAdd("direct-file", "direct file content") - shell.Commit("second commit") - }, - Run: func(t *TestDriver, keys config.KeybindingConfig) { - t.Views().Commits(). - Focus(). - Lines( - Contains("second commit").IsSelected(), - Contains("first commit"), - ). - PressEnter() - - t.Views().CommitFiles(). - IsFocused(). - Lines( - Equals("▼ /").IsSelected(), - Contains("direct-file"), - Contains("hunk-file"), - Contains("line-file"), - ). - SelectNextItem(). - PressPrimaryAction(). - Tap(func() { - t.Views().Information().Content(Contains("Building patch")) - - t.Views().Secondary().Content(Contains("direct file content")) - }). - NavigateToLine(Contains("hunk-file")). - PressEnter() - - t.Views().PatchBuilding(). - IsFocused(). - SelectedLines( - Contains("-1a"), - ). - Press(keys.Main.ToggleSelectHunk). - SelectedLines( - Contains(`-1a`), - Contains(`+aa`), - ). - PressPrimaryAction(). - SelectedLines( - Contains(`-1c`), - Contains(`+cc`), - ). - Tap(func() { - t.Views().Information().Content(Contains("Building patch")) - - t.Views().Secondary().Content( - // when we're inside the patch building panel, we only show the patch - // in the secondary panel that relates to the selected file - DoesNotContain("direct file content"). - Contains("@@ -1,6 +1,6 @@"). - Contains(" 1f"), - ) - }). - // Cancel hunk select - PressEscape(). - // Escape the view - PressEscape() - - t.Views().CommitFiles(). - IsFocused(). - NavigateToLine(Contains("line-file")). - PressEnter() - - t.Views().PatchBuilding(). - IsFocused(). - SelectedLines( - Contains("+2a"), - ). - PressPrimaryAction(). - SelectedLines( - Contains("+2b"), - ). - NavigateToLine(Contains("+2c")). - Press(keys.Universal.ToggleRangeSelect). - NavigateToLine(Contains("+2e")). - PressPrimaryAction(). - SelectedLines( - Contains("+2f"), - ). - NavigateToLine(Contains("+2g")). - PressPrimaryAction(). - SelectedLines( - Contains("+2h"), - ). - Tap(func() { - t.Views().Information().Content(Contains("Building patch")) - - t.Views().Secondary().ContainsLines( - Contains("+2a"), - Contains("+2c"), - Contains("+2d"), - Contains("+2e"), - Contains("+2g"), - ) - }). - PressEscape(). - Tap(func() { - t.Views().Secondary().ContainsLines( - // direct-file patch - Contains(`diff --git a/direct-file b/direct-file`), - Contains(`index`), - Contains(`--- a/direct-file`), - Contains(`+++ b/direct-file`), - Contains(`@@ -0,0 +1 @@`), - Contains(`+direct file content`), - Contains(`\ No newline at end of file`), - // hunk-file patch - Contains(`diff --git a/hunk-file b/hunk-file`), - Contains(`index`), - Contains(`--- a/hunk-file`), - Contains(`+++ b/hunk-file`), - // git works out the context of the patch's own hunks, which is why - // this is not the six lines the file's diff had - Contains(`@@ -1,4 +1,4 @@`), - Contains(`-1a`), - Contains(`+aa`), - Contains(` 1b`), - Contains(` 1c`), - Contains(` 1d`), - // line-file patch - Contains(`diff --git a/line-file b/line-file`), - Contains(`index`), - Contains(`--- a/line-file`), - Contains(`+++ b/line-file`), - Contains(`@@ -0,0 +1,5 @@`), - Contains(`+2a`), - Contains(`+2c`), - Contains(`+2d`), - Contains(`+2e`), - Contains(`+2g`), - ) - }) - }, -}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 05d491341..c1f5ac92f 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -376,6 +376,7 @@ var tests = []*components.IntegrationTest{ main_view.BuildPatchFromACommitsDiff, main_view.BuildPatchFromAReflogEntry, main_view.BuildPatchFromAWholeCommitsDiff, + main_view.BuildPatchWithMixedSelections, main_view.ClickSelectsDiffLine, main_view.CommitFromMainView, main_view.CopyRowsThatAreNoDiffLine, @@ -501,7 +502,6 @@ var tests = []*components.IntegrationTest{ patch_building.RenamedFileWhole, patch_building.SelectAllFiles, patch_building.SelectDirecoriesSharingPrefix, - patch_building.SpecificSelection, patch_building.ToggleDirectory, patch_building.ToggleRange, reflog.Checkout,