diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 02dc8dd5a16..10f85904626 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -118,7 +118,8 @@ Lazygit supports [Nerd Fonts](https://www.nerdfonts.com) to render certain icons ## Internationalisation -Boy that's a hard word to spell. Anyway, lazygit is translated into several languages within the pkg/i18n package. If you need to render text to the user, you should add a new field to the TranslationSet struct in `pkg/i18n/english.go` and add the actual content within the `EnglishTranslationSet()` method in the same file. Then you can access via `gui.Tr.YourNewText` (or `self.c.Tr.YourNewText`, etc). Although it is appreciated if you translate the text into other languages, it's not expected of you (google translate will likely do a bad job anyway!). +Boy that's a hard word to spell. Anyway, lazygit is translated into several languages within the pkg/i18n package. If you need to render text to the user, you should add a new field to the TranslationSet struct in `pkg/i18n/english.go` and add the actual content within the `EnglishTranslationSet()` method in the same file. +Then you can access via `gui.Tr.YourNewText` (or `self.c.Tr.YourNewText`, etc). Although it is appreciated if you translate the text into other languages, it's not expected of you (google translate will likely do a bad job anyway!). Note, we use 'Sentence case' for everything (so no 'Title Case' or 'whatever-it's-called-when-there's-no-capital-letters-case') diff --git a/README.md b/README.md index 87230b599a8..a3c848e623f 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,13 @@ A simple terminal UI for git commands
-[![GitHub Releases](https://img.shields.io/github/downloads/jesseduffield/lazygit/total)](https://github.com/jesseduffield/lazygit/releases) [![Go Report Card](https://goreportcard.com/badge/github.com/jesseduffield/lazygit)](https://goreportcard.com/report/github.com/jesseduffield/lazygit) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/f46416b715d74622895657935fcada21)](https://app.codacy.com/gh/jesseduffield/lazygit/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Codacy Badge](https://app.codacy.com/project/badge/Coverage/f46416b715d74622895657935fcada21)](https://app.codacy.com/gh/jesseduffield/lazygit/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage) [![GolangCI](https://golangci.com/badges/github.com/jesseduffield/lazygit.svg)](https://golangci.com) [![GitHub tag](https://img.shields.io/github/tag/jesseduffield/lazygit.svg)](https://github.com/jesseduffield/lazygit/releases/latest) [![homebrew](https://img.shields.io/homebrew/v/lazygit)](https://github.com/Homebrew/homebrew-core/blob/master/Formula/lazygit.rb) +[![GitHub Releases](https://img.shields.io/github/downloads/jesseduffield/lazygit/total)](https://github.com/jesseduffield/lazygit/releases) +[![Go Report Card](https://goreportcard.com/badge/github.com/jesseduffield/lazygit)](https://goreportcard.com/report/github.com/jesseduffield/lazygit) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/f46416b715d74622895657935fcada21)](https://app.codacy.com/gh/jesseduffield/lazygit/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) +[![Codacy Badge](https://app.codacy.com/project/badge/Coverage/f46416b715d74622895657935fcada21)](https://app.codacy.com/gh/jesseduffield/lazygit/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage) +[![GolangCI](https://golangci.com/badges/github.com/jesseduffield/lazygit.svg)](https://golangci.com) +[![GitHub tag](https://img.shields.io/github/tag/jesseduffield/lazygit.svg)](https://github.com/jesseduffield/lazygit/releases/latest) +[![homebrew](https://img.shields.io/homebrew/v/lazygit)](https://github.com/Homebrew/homebrew-core/blob/master/Formula/lazygit.rb) ![commit_and_push](../assets/demo/commit_and_push-compressed.gif) @@ -49,12 +55,113 @@ A simple terminal UI for git commands

-Mark LussierDean HerbertPeter BjorklundReilly WoodOliver GüntherPawan DhananjayBartłomiej DachCarsten GehlingCEUKHolden LucasChau TranmatejciktheAverageDev (Luca Tumedei)Piotr KowalskiNicholas CloudAliaksandr StelmachonakBurgy BenjaminJoe KlemmerTobias LütkeBen BeaumontHollyCasey BoettcherJeff ForcierMaciej T. NowakAndreas KurthBraden SteffaniakJordan GillardSebastianGeorge SpanosAndy SlezakMartin KockJesse AlamaDaniel KokottJan HeijmansKevin Nowaldsem pruijsOmar LuqEthan LiBrian MacAskillMaxiJan ZenknerVictor AremuJJFrederick MorlockMaximilian LangenfeldDavis BulsNeil LambertDavid Heinemeier HanssonMarco Aurelio Caldas MirandaEmmanuel NosakhareEthan FischerTerry TaiAdam RoesnerTim MorganMax ShypulniakKovács ÁdámtimbryandevSviatoslav AbakumovPatricio SerranoKiriBob ParsonsJohn Even BjørnevikMichael OberstStian HegglundKenth FagerlundJulien TardotAaron ArredondoEllord TayagEdgar Post-Buijssbc64Pierre SpringZac ClayThomas MüllerCarl AssmannSergey OgnevAlex GMichael HowardLasse Bloch LauritsenLarry MarburgerDavid BrockmanAlexander SlavschikAidan GaulandMaksym BieńkowskiRoman ZippJoshua WootonnJoe BuzaAnton Kesy +Mark Lussier +Dean Herbert +Peter Bjorklund +Reilly Wood +Oliver Günther +Pawan Dhananjay +Bartłomiej Dach +Carsten Gehling +CEUK + +Holden Lucas +Chau Tran +matejcik +theAverageDev (Luca Tumedei) +Piotr Kowalski + +Nicholas Cloud +Aliaksandr Stelmachonak +Burgy Benjamin +Joe Klemmer +Tobias Lütke +Ben Beaumont + +Holly +Casey Boettcher +Jeff Forcier + +Maciej T. Nowak + +Andreas Kurth +Braden Steffaniak +Jordan Gillard +Sebastian +George Spanos +Andy Slezak +Martin Kock +Jesse Alama +Daniel Kokott +Jan Heijmans +Kevin Nowald +sem pruijs +Omar Luq +Ethan Li + +Brian MacAskill +Maxi +Jan Zenkner +Victor Aremu + + +JJ +Frederick Morlock +Maximilian Langenfeld +Davis Buls + +Neil Lambert +David Heinemeier Hansson +Marco Aurelio Caldas Miranda +Emmanuel Nosakhare +Ethan Fischer +Terry Tai + +Adam Roesner +Tim Morgan +Max Shypulniak +Kovács Ádám +timbryandev +Sviatoslav Abakumov + +Patricio Serrano +Kiri +Bob Parsons + + +John Even Bjørnevik +Michael Oberst +Stian Hegglund +Kenth Fagerlund +Julien Tardot +Aaron Arredondo +Ellord Tayag +Edgar Post-Buijs +sbc64 +Pierre Spring +Zac Clay +Thomas Müller +Carl Assmann +Sergey Ognev +Alex G +Michael Howard +Lasse Bloch Lauritsen +Larry Marburger +David Brockman +Alexander Slavschik +Aidan Gauland +Maksym Bieńkowski +Roman Zipp +Joshua Wootonn +Joe Buza +Anton Kesy

## Elevator Pitch -Rant time: You've heard it before, git is _powerful_, but what good is that power when everything is so damn hard to do? Interactive rebasing requires you to edit a goddamn TODO file in your editor? _Are you kidding me?_ To stage part of a file you need to use a command line program to step through each hunk and if a hunk can't be split down any further but contains code you don't want to stage, you have to edit an arcane patch file _by hand_? _Are you KIDDING me?!_ Sometimes you get asked to stash your changes when switching branches only to realise that after you switch and unstash that there weren't even any conflicts and it would have been fine to just checkout the branch directly? _YOU HAVE GOT TO BE KIDDING ME!_ +Rant time: You've heard it before, git is _powerful_, but what good is that power when everything is so damn hard to do? Interactive rebasing requires you to edit a goddamn TODO file in your editor? _Are you kidding me?_ +To stage part of a file you need to use a command line program to step through each hunk and if a hunk can't be split down any further but contains code you don't want to stage, you have to edit an arcane patch file _by hand_? _Are you KIDDING me?!_ +Sometimes you get asked to stash your changes when switching branches only to realise that after you switch and unstash that there weren't even any conflicts and it would have been fine to just checkout the branch directly? _YOU HAVE GOT TO BE KIDDING ME!_ If you're a mere mortal like me and you're tired of hearing how powerful git is when in your daily life it's a powerful pain in your ass, lazygit might be for you. @@ -188,7 +295,8 @@ Learn more in the [Rebase magic Youtube tutorial](https://youtu.be/4XaToVut_hs). ### 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`. +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`. ![rebase_onto](../assets/demo/rebase_onto-compressed.gif) @@ -209,7 +317,8 @@ When viewing the commit graph in an enlarged window (use `+` and `_` to cycle sc ### Compare two commits -If you press `shift+w` on a commit (or branch/ref) a menu will open that allows you to mark that commit so that any other commit you select will be diffed against it. Once you've selected the second commit, you'll see the diff in the main view and if you press `` you'll see the files of the diff. You can press `shift+w` to view the diff menu again to see options like reversing the diff direction or exiting diff mode. You can also exit diff mode by pressing ``. +If you press `shift+w` on a commit (or branch/ref) a menu will open that allows you to mark that commit so that any other commit you select will be diffed against it. +Once you've selected the second commit, you'll see the diff in the main view and if you press `` you'll see the files of the diff. You can press `shift+w` to view the diff menu again to see options like reversing the diff direction or exiting diff mode. You can also exit diff mode by pressing ``. ![diff_commits](../assets/demo/diff_commits-compressed.gif) diff --git a/docs/Custom_Command_Keybindings.md b/docs/Custom_Command_Keybindings.md index 62c84d28e48..74c91f55715 100644 --- a/docs/Custom_Command_Keybindings.md +++ b/docs/Custom_Command_Keybindings.md @@ -315,7 +315,8 @@ CheckedOutBranch To see what fields are available on e.g. the `SelectedFile`, see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/gui/services/custom_commands/models.go) (all the modelling lives in the same file). -We don't support accessing all elements of a range selection yet. We might add this in the future, but as a special case you can access the range of selected commits by using `SelectedCommitRange`, which has two properties `.To` and `.From` which are the hashes of the bottom and top selected commits, respectively. This is useful for passing them to a git command that operates on a range of commits. For example, to create patches for all selected commits, you might use +We don't support accessing all elements of a range selection yet. We might add this in the future, but as a special case you can access the range of selected commits by using `SelectedCommitRange`, which has two properties `.To` and `.From` which are the hashes of the bottom and top selected commits, respectively. +This is useful for passing them to a git command that operates on a range of commits. For example, to create patches for all selected commits, you might use ```yml command: "git format-patch {{.SelectedCommitRange.From}}^..{{.SelectedCommitRange.To}}" ``` @@ -340,7 +341,8 @@ initialValue: "username/{{ runCommand "date +\"%Y/%-m\"" }}/" ## Keybinding collisions -If your custom keybinding collides with an inbuilt keybinding that is defined for the same context, only the custom keybinding will be executed. This also applies to the global context. However, one caveat is that if you have a custom keybinding defined on the global context for some key, and there is an in-built keybinding defined for the same key and for a specific context (say the 'files' context), then the in-built keybinding will take precedence. See how to change in-built keybindings [here](https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#keybindings) +If your custom keybinding collides with an inbuilt keybinding that is defined for the same context, only the custom keybinding will be executed. This also applies to the global context. +However, one caveat is that if you have a custom keybinding defined on the global context for some key, and there is an in-built keybinding defined for the same key and for a specific context (say the 'files' context), then the in-built keybinding will take precedence. See how to change in-built keybindings [here](https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#keybindings) ## Menus of custom commands diff --git a/docs/dev/Busy.md b/docs/dev/Busy.md index 6aa3b7bb59b..c89f41d87e4 100644 --- a/docs/dev/Busy.md +++ b/docs/dev/Busy.md @@ -25,7 +25,8 @@ First, it's important to distinguish three different types of goroutines: The point of distinguishing worker goroutines from background goroutines is that when any worker goroutine is running, we consider Lazygit to be 'busy', whereas this is not the case with background goroutines. It would be pointless to have background goroutines be considered 'busy' because then Lazygit would be considered busy for the entire duration of the program! -In gocui, the underlying package we use for managing the UI and events, we keep track of how many busy goroutines there are using the `Task` type. A task represents some work being done by lazygit. The gocui Gui struct holds a map of tasks and allows creating a new task (which adds it to the map), pausing/continuing a task, and marking a task as done (which removes it from the map). Lazygit is considered to be busy so long as there is at least one busy task in the map; otherwise it's considered idle. When Lazygit goes from busy to idle, it notifies the integration test. +In gocui, the underlying package we use for managing the UI and events, we keep track of how many busy goroutines there are using the `Task` type. A task represents some work being done by lazygit. +The gocui Gui struct holds a map of tasks and allows creating a new task (which adds it to the map), pausing/continuing a task, and marking a task as done (which removes it from the map). Lazygit is considered to be busy so long as there is at least one busy task in the map; otherwise it's considered idle. When Lazygit goes from busy to idle, it notifies the integration test. It's important that we play by the rules below to ensure that after the user does anything, all the processing that follows happens in a contiguous block of busy-ness with no gaps. diff --git a/docs/dev/Codebase_Guide.md b/docs/dev/Codebase_Guide.md index 1692be33ea6..3b5d19f8353 100644 --- a/docs/dev/Codebase_Guide.md +++ b/docs/dev/Codebase_Guide.md @@ -88,13 +88,16 @@ Often, as part of handling a keypress, we'll want to run some code asynchronousl ## Using UserConfig -The UserConfig struct is loaded from lazygit's global config file (and possibly repo-specific config files). It can be re-loaded while lazygit is running, e.g. when the user edits one of the config files. In this case we should make sure that any new or changed config values take effect immediately. The easiest way to achieve this is what we do in most controllers or helpers: these have a pointer to the `common.Common` struct, which contains the UserConfig, and access it from there. Since the UserConfig instance in `common.Common` is updated whenever we reload the config, the code can be sure that it always uses an up-to-date value, and there's nothing else to do. +The UserConfig struct is loaded from lazygit's global config file (and possibly repo-specific config files). It can be re-loaded while lazygit is running, e.g. when the user edits one of the config files. In this case we should make sure that any new or changed config values take effect immediately. +The easiest way to achieve this is what we do in most controllers or helpers: these have a pointer to the `common.Common` struct, which contains the UserConfig, and access it from there. Since the UserConfig instance in `common.Common` is updated whenever we reload the config, the code can be sure that it always uses an up-to-date value, and there's nothing else to do. If that's not possible for some reason, see if you can add code to `Gui.onUserConfigLoaded` to update things from the new config; there are some examples in that function to use as a guide. If that's too hard to do too, add the config to the list in `Gui.checkForChangedConfigsThatDontAutoReload` so that the user is asked to quit and restart lazygit. ## Legacy code structure -Before we had controllers and contexts, all the code lived directly in the gui package under a gui God Struct. This was fairly bloated and so we split things out to have a better separation of concerns. Nonetheless, it's a big effort to migrate all the code so we still have some logic in the gui struct that ought to live somewhere else. Likewise, we have some keybindings defined in `pkg/gui/keybindings.go` that ought to live on a controller (all keybindings used to be defined in that one file). +Before we had controllers and contexts, all the code lived directly in the gui package under a gui God Struct. This was fairly bloated and so we split things out to have a better separation of concerns. Nonetheless, it's a big effort to migrate all the code so we still have some logic in the gui struct that ought to live somewhere else. +Likewise, we have some keybindings defined in `pkg/gui/keybindings.go` that ought to live on a controller (all keybindings used to be defined in that one file). -The new structure has its own problems: we don't have a clear guide on whether code should live in a controller or helper. The current approach is to put code in a controller until it's needed by another controller, and to then extract it out into a helper. We may be better off just putting code in helpers to start with and leaving controllers super-thin, with the responsibility of just pairing keys with corresponding helper functions. But it's not clear to me if that would be better than the current approach. +The new structure has its own problems: we don't have a clear guide on whether code should live in a controller or helper. The current approach is to put code in a controller until it's needed by another controller, and to then extract it out into a helper. +We may be better off just putting code in helpers to start with and leaving controllers super-thin, with the responsibility of just pairing keys with corresponding helper functions. But it's not clear to me if that would be better than the current approach. diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index baf3e20de36..2145266ef99 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -1964,11 +1964,13 @@ func EnglishTranslationSet() *TranslationSet { SelectedItemIsNotABranch: "Selected item is not a branch", SelectedItemDoesNotHaveFiles: "Selected item does not have files to view", MultiSelectNotSupportedForSubmodules: "Multiselection not supported for submodules", - OldCherryPickKeyWarning: "The 'c' key is no longer the default key for copying commits to cherry pick. Please use `{{.copy}}` instead (and `{{.paste}}` to paste). The reason for this change is that the 'v' key for selecting a range of lines when staging is now also used for selecting a range of lines in any list view, meaning that we needed to find a new key for pasting commits, and if we're going to now use `{{.paste}}` for pasting commits, we may as well use `{{.copy}}` for copying them. If you want to configure the keybindings to get the old behaviour, set the following in your config:\n\nkeybinding:\n universal:\n toggleRangeSelect: \n commits:\n cherryPickCopy: 'c'\n pasteCommits: 'v'", - CommandDoesNotSupportOpeningInEditor: "This command doesn't support switching to the editor", - CustomCommands: "Custom commands", - NoApplicableCommandsInThisContext: "(No applicable commands in this context)", - SelectCommitsOfCurrentBranch: "Select commits of current branch", + OldCherryPickKeyWarning: "The 'c' key is no longer the default key for copying commits to cherry pick." + + " Please use `{{.copy}}` instead (and `{{.paste}}` to paste). The reason for this change is that the 'v' key for selecting a range of lines when staging is now also used for selecting a range of lines in any list view, meaning that we needed to find a new key for pasting commits, and if we're going to now use `{{.paste}}` for pasting commits, we may as well use `{{.copy}}` for copying them." + + " If you want to configure the keybindings to get the old behaviour, set the following in your config:\n\nkeybinding:\n universal:\n toggleRangeSelect: \n commits:\n cherryPickCopy: 'c'\n pasteCommits: 'v'", + CommandDoesNotSupportOpeningInEditor: "This command doesn't support switching to the editor", + CustomCommands: "Custom commands", + NoApplicableCommandsInThisContext: "(No applicable commands in this context)", + SelectCommitsOfCurrentBranch: "Select commits of current branch", Actions: Actions{ // TODO: combine this with the original keybinding descriptions (those are all in lowercase atm) @@ -2140,7 +2142,8 @@ git: autoWrapCommitMessage: true autoWrapWidth: 72 -- The 'v' key was already being used in the staging view to start a range select, but now you can use it to start a range select in any view. Unfortunately this clashes with the 'v' keybinding for pasting commits (cherry-pick), so now pasting commits is done via 'shift+V' and for the sake of consistency, copying commits is now done via 'shift+C' instead of just 'c'. Note that the 'v' keybinding is only one way to start a range-select: you can use shift+up/down arrow instead. So, if you want to configure the cherry-pick keybindings to get the old behaviour, set the following in your config: +- The 'v' key was already being used in the staging view to start a range select, but now you can use it to start a range select in any view. Unfortunately this clashes with the 'v' keybinding for pasting commits (cherry-pick), so now pasting commits is done via 'shift+V' and for the sake of consistency, copying commits is now done via 'shift+C' instead of just 'c'. + Note that the 'v' keybinding is only one way to start a range-select: you can use shift+up/down arrow instead. So, if you want to configure the cherry-pick keybindings to get the old behaviour, set the following in your config: keybinding: universal: diff --git a/pkg/integration/README.md b/pkg/integration/README.md index 0c50d8f4e05..4fac99ccaf2 100644 --- a/pkg/integration/README.md +++ b/pkg/integration/README.md @@ -70,7 +70,8 @@ Debugging an integration test is possible in two ways: 1. Use the -debug option of the integration test runner's "cli" command, e.g. `go run cmd/integration_test/main.go cli -debug tag/reset.go` 2. Select a test in the "tui" runner and hit "d" to debug it. -In both cases the test runner will print to the console that it is waiting for a debugger to attach, so now you need to tell your debugger to attach to a running process with the name "test_lazygit". If you are using Visual Studio Code, an easy way to do that is to use the "Attach to integration test runner" debug configuration. The test runner will resume automatically when it detects that a debugger was attached. Don't forget to set a breakpoint in the code that you want to step through, otherwise the test will just finish (i.e. it doesn't stop in the debugger automatically). +In both cases the test runner will print to the console that it is waiting for a debugger to attach, so now you need to tell your debugger to attach to a running process with the name "test_lazygit". +If you are using Visual Studio Code, an easy way to do that is to use the "Attach to integration test runner" debug configuration. The test runner will resume automatically when it detects that a debugger was attached. Don't forget to set a breakpoint in the code that you want to step through, otherwise the test will just finish (i.e. it doesn't stop in the debugger automatically). ### Sandbox mode