diff --git a/README.md b/README.md index aafd2768d..25ab68483 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ If you're a mere mortal like me and you're tired of hearing how powerful git is - [Elevator Pitch](#elevator-pitch) - [Table of contents](#table-of-contents) - [Features](#features) - - [Stage individual lines](#stage-individual-lines) + - [Stage hunks or individual lines](#stage-hunks-or-individual-lines) - [Interactive Rebase](#interactive-rebase) - [Cherry-pick](#cherry-pick) - [Bisect](#bisect) @@ -133,11 +133,11 @@ Lazygit is not my fulltime job but it is a hefty part time job so if you want to ## Features -### Stage individual lines +### Stage hunks or individual lines -Press `` on a changed file to focus its diff in the main view. Press `` on the selected line to stage it, or press `v` to start selecting a range of lines. You can also press `a` to switch to hunk selection mode. When a file has both staged and unstaged changes, use `` to move between the two diff panes; the same actions stage or unstage the selection depending on the pane. +Press `0` on a changed file to focus its diff in the main view. The selection covers a whole hunk to begin with, so `` stages that hunk and moves on to the next one. When you want only part of a hunk, press `a` for line-by-line selection and `v` to select a range of lines. What you staged shows up in the pane below. Press `` to move between the two panes; `` unstages in the lower one. Press `c` to commit without leaving the diff. -![stage_lines](../assets/demo/stage_lines-compressed.gif) +![stage_hunks_or_lines](../assets/demo/stage_hunks_or_lines-compressed.gif) ### Interactive Rebase @@ -195,12 +195,16 @@ You can create worktrees to have multiple branches going at once without the nee You can build a custom patch from an old commit and then remove the patch from the commit, split out a new commit, apply the patch in reverse to the index, and more. -In this example we have a redundant comment that we want to remove from an old commit. We hit `` on the commit to view its files, then `` on a file to focus its diff. From there, `` adds the selected comment line to the custom patch and `ctrl+p` opens the custom patch options, where we choose to remove the patch from the original commit. +In this example an old commit contains a change that belongs in a commit of its own. We hit `0` on the commit to focus its diff. `` on the hunk we want to move adds it to the custom patch, and `ctrl+p` opens the custom patch options, where we choose to move the patch into a new commit. Learn more in the [Rebase magic Youtube tutorial](https://youtu.be/4XaToVut_hs). ![custom_patch](../assets/demo/custom_patch-compressed.gif) +If you only want to remove a hunk from an old commit, you don't need a custom patch for that. Select the hunk in the commit's diff and press `d`. Lazygit rewrites the commit without it, running an interactive rebase in the background. + +![remove_hunk_from_commit](../assets/demo/remove_hunk_from_commit-compressed.gif) + ### Rebase from marked base commit Say you're on a feature branch that was itself branched off of the develop branch, and you've decided you'd rather be branching off the master branch. You need a way to rebase only the commits from your feature branch. In this demo we check to see which was the last commit on the develop branch, then press `shift+b` to mark that commit as our base commit, then press `r` on the master branch to rebase onto it, only bringing across the commits from our feature branch. Then we push our changes with `shift+p`. diff --git a/pkg/integration/tests/demo/custom_patch.go b/pkg/integration/tests/demo/custom_patch.go index 923596397..6e2dfa93b 100644 --- a/pkg/integration/tests/demo/custom_patch.go +++ b/pkg/integration/tests/demo/custom_patch.go @@ -10,66 +10,99 @@ var usersFileContent = `package main import "fmt" func main() { - // TODO: verify that this actually works + fmt.Println(shims.Greeting()) + serve() +} + +func serve() { + fmt.Println("listening on :8080") +} +` + +var usersFileContentWithLogging = `package main + +import "fmt" + +func main() { fmt.Println("hello world") + serve() +} + +func logRequest(path string) { + fmt.Println("request:", path) +} + +func serve() { + fmt.Println("listening on :8080") } ` var CustomPatch = NewIntegrationTest(NewIntegrationTestArgs{ - Description: "Remove a line from an old commit", + Description: "Split a change out of an old commit into a new one", ExtraCmdArgs: []string{}, Skip: false, IsDemo: true, SetupConfig: func(cfg *config.AppConfig) { setDefaultDemoConfig(cfg) - cfg.GetUserConfig().Gui.UseHunkModeInDiffView = false }, SetupRepo: func(shell *Shell) { shell.CreateNCommitsWithRandomMessages(30) shell.NewBranch("feature/user-authentication") shell.EmptyCommit("Add user authentication feature") - shell.CreateFileAndAdd("src/users.go", "package main\n") + shell.CreateFileAndAdd("src/users.go", usersFileContent) shell.Commit("Fix local session storage") shell.CreateFile("src/authentication.go", "package main") shell.CreateFile("src/session.go", "package main") - shell.UpdateFileAndAdd("src/users.go", usersFileContent) - shell.EmptyCommit("Stop using shims") + shell.UpdateFileAndAdd("src/users.go", usersFileContentWithLogging) + shell.Commit("Stop using shims") shell.UpdateFileAndAdd("src/authentication.go", "package authentication") shell.UpdateFileAndAdd("src/session.go", "package session") shell.Commit("Enhance user authentication feature") }, Run: func(t *TestDriver, keys config.KeybindingConfig) { - t.SetCaptionPrefix("Remove a line from an old commit") + t.SetCaptionPrefix("Split a change out of an old commit") t.Wait(1000) t.Views().Commits(). Focus(). NavigateToLine(Contains("Stop using shims")). Wait(1000). - PressEnter(). + Press(keys.Universal.FocusMainView) + + t.Views().Main(). + IsFocused(). + Wait(1000). + Press(keys.Universal.NextItem). + SelectedLines( + Contains("+func logRequest(path string) {"), + Contains(`fmt.Println("request:", path)`), + Contains("+}"), + Equals("+"), + ). + Wait(500). + SetCaptionPrefix("Add the hunk to a custom patch"). + PressPrimaryAction(). + Wait(1000). + SetCaptionPrefix("Move the patch into a new commit"). + Press(keys.Universal.CreatePatchOptionsMenu). Tap(func() { - t.Views().CommitFiles(). - IsFocused(). - NavigateToLine(Contains("users.go")). - Wait(1000). - PressEnter(). - Tap(func() { - t.Views().Main(). - IsFocused(). - NavigateToLine(Contains("TODO")). - Wait(500). - PressPrimaryAction(). - PressEscape() - }). - Press(keys.Universal.CreatePatchOptionsMenu). - Tap(func() { - t.ExpectPopup().Menu(). - Title(Equals("Patch options")). - Select(Contains("Remove patch from original commit")). - Wait(500). - Confirm() - }). - PressEscape() + t.ExpectPopup().Menu(). + Title(Equals("Patch options")). + Select(Contains("Move patch into new commit after the original commit")). + Wait(500). + Confirm() + + t.ExpectPopup().CommitMessagePanel(). + Type("Add request logging"). + Confirm() }) + + t.Views().Commits(). + IsFocused(). + TopLines( + Contains("Enhance user authentication feature"), + Contains("Add request logging"), + Contains("Stop using shims"), + ) }, }) diff --git a/pkg/integration/tests/demo/remove_hunk_from_commit.go b/pkg/integration/tests/demo/remove_hunk_from_commit.go new file mode 100644 index 000000000..c7fad1949 --- /dev/null +++ b/pkg/integration/tests/demo/remove_hunk_from_commit.go @@ -0,0 +1,82 @@ +package demo + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var greetingFileContent = `package main + +import "fmt" + +func greet(name string) { + fmt.Println("hello", name) +} + +func farewell(name string) { + fmt.Println("bye", name) +} +` + +var greetingFileContentWithDebugLine = `package main + +import "fmt" + +func greet(name string) { + fmt.Println("hello there", name) +} + +func farewell(name string) { + fmt.Println("DEBUG: saying bye to", name) + fmt.Println("bye", name) +} +` + +var RemoveHunkFromCommit = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Remove a hunk from an old commit", + ExtraCmdArgs: []string{}, + Skip: false, + IsDemo: true, + SetupConfig: func(cfg *config.AppConfig) { + setDefaultDemoConfig(cfg) + }, + SetupRepo: func(shell *Shell) { + shell.CreateNCommitsWithRandomMessages(30) + shell.NewBranch("feature/user-authentication") + shell.CreateFileAndAdd("src/greeting.go", greetingFileContent) + shell.Commit("Greet users when they sign in") + shell.UpdateFileAndAdd("src/greeting.go", greetingFileContentWithDebugLine) + shell.Commit("Make the greeting friendlier") + shell.EmptyCommit("Expire sessions after a day") + shell.EmptyCommit("Enhance user authentication feature") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.SetCaptionPrefix("Remove a hunk from an old commit") + t.Wait(1000) + + t.Views().Commits(). + Focus(). + NavigateToLine(Contains("Make the greeting friendlier")). + Wait(1000). + Press(keys.Universal.FocusMainView) + + t.Views().Main(). + IsFocused(). + Wait(1000). + Press(keys.Universal.NextItem). + SelectedLines( + Contains(`fmt.Println("DEBUG: saying bye to", name)`), + ). + Wait(1000). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Confirmation(). + Title(Equals("Discard lines from commit")). + Content(AnyString()). + Wait(1000). + Confirm() + }). + Wait(1000). + Content(Contains(`fmt.Println("hello there", name)`).DoesNotContain("DEBUG")) + }, +}) diff --git a/pkg/integration/tests/demo/stage_lines.go b/pkg/integration/tests/demo/stage_hunks_or_lines.go similarity index 63% rename from pkg/integration/tests/demo/stage_lines.go rename to pkg/integration/tests/demo/stage_hunks_or_lines.go index 8d05749f6..5457ecfc0 100644 --- a/pkg/integration/tests/demo/stage_lines.go +++ b/pkg/integration/tests/demo/stage_hunks_or_lines.go @@ -30,11 +30,11 @@ Simple terminal UI for git Just do brew install lazygit and bada bing bada boom you have begun on the path of laziness. - +TODO: mention the other install methods ` -var StageLines = NewIntegrationTest(NewIntegrationTestArgs{ - Description: "Stage individual lines", +var StageHunksOrLines = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Stage hunks or individual lines", ExtraCmdArgs: []string{}, Skip: false, IsDemo: true, @@ -51,29 +51,43 @@ var StageLines = NewIntegrationTest(NewIntegrationTestArgs{ shell.UpdateFile("docs/README.md", updatedFile) }, Run: func(t *TestDriver, keys config.KeybindingConfig) { - t.SetCaptionPrefix("Stage individual lines") + t.SetCaptionPrefix("Stage a hunk") t.Wait(1000) t.Views().Files(). IsFocused(). - PressEnter() + Press(keys.Universal.FocusMainView) t.Views().Main(). IsFocused(). + SelectedLines( + Contains("-Simple terminal UI for git commands"), + Contains("+Simple terminal UI for git"), + Contains("+(Not too simple though)"), + ). + Wait(1000). + PressPrimaryAction(). + Wait(1000). + SetCaptionPrefix("Stage individual lines"). + Press(keys.Main.ToggleSelectHunk). + Wait(500). Press(keys.Universal.ToggleRangeSelect). PressFast(keys.Universal.NextItem). - PressFast(keys.Universal.NextItem). + SelectedLines( + Contains("+Just do brew install lazygit"), + Contains("+boom you have begun"), + ). Wait(500). PressPrimaryAction(). - Wait(500). - PressEscape() - - t.Views().Files(). - IsFocused(). + Wait(1000). + SelectedLines( + Contains("+TODO: mention the other install methods"), + ). + SetCaptionPrefix("Commit our changes"). Press(keys.Files.CommitChanges). Tap(func() { t.ExpectPopup().CommitMessagePanel(). - Type("Update tagline"). + Type("Update tagline and install instructions"). Confirm() }) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index e51589bda..459256865 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -219,7 +219,8 @@ var tests = []*components.IntegrationTest{ demo.InteractiveRebase, demo.NukeWorkingTree, demo.RebaseOnto, - demo.StageLines, + demo.RemoveHunkFromCommit, + demo.StageHunksOrLines, demo.Undo, demo.WorktreeCreateFromBranches, diff.CopyToClipboard,