Compare commits

...

1231 Commits

Author SHA1 Message Date
Stefan Haller
cbd23107ef WIP Make it searchable
Doesn't work very well yet, but it gives you a taste of what it could look like.
2024-10-15 16:59:50 +02:00
Stefan Haller
50426cda3a Select line that is in the middle of the screen
TODO: doesn't work the very first time
2024-10-15 16:36:19 +02:00
Stefan Haller
e031f437e9 Add navigation keybindings 2024-10-15 15:35:52 +02:00
Stefan Haller
9bd212aab1 DROPME: change window title for testing 2024-10-15 15:35:52 +02:00
Stefan Haller
9df81067e9 Make Files diff focusable 2024-10-15 15:35:52 +02:00
Stefan Haller
f31037864e Extract code to DiffHelper
TODO: handle the WithDiffModeCheck thing properly
2024-10-15 15:35:52 +02:00
Stefan Haller
03e060d82c Extract helper method mainViews 2024-10-15 15:35:52 +02:00
Stefan Haller
f88b1942ae Remove utils.Clamp, use lo.Clamp instead 2024-10-15 15:35:51 +02:00
Stefan Haller
2e05ea57dc [gocui] Make highlight overwrite the background color
It seems that highlighting has so far only been used in cases where there wasn't
a background color. Or'ing the bits of the color with the existing background
color doesn't make sense.
2024-10-15 15:06:11 +02:00
Stefan Haller
052974be65 Allow pasting commits multiple times (#3983)
- **PR Description**

After pasting commits, hide the cherry-pick status (i.e. remove the "x
commits copied" status in the lower right corner, and hide the blue
selection of the copied commits). However, keep the copied commits
around so that it's possible to paste them again. This can be useful
e.g. to backport a bugfix to multiple major version release branches.

Discussed in #3198.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be
hot-reloaded (see
[here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-10-13 16:59:04 +02:00
Stefan Haller
85523402d6 Allow pasting commits more than once
After pasting commits once, we hide the cherry-picking status (as if it had been
reset), and no longer paint the copied commits with blue hashes; however, we
still allow pasting them again. This can be useful e.g. to backport a bugfix to
multiple major version release branches.
2024-10-13 16:55:54 +02:00
Stefan Haller
f473d23d65 Wrap an overly long line 2024-10-13 16:55:54 +02:00
Stefan Haller
59a937ee7a Get rid of error return value of PostRefreshUpdate and a few related ones
I missed these in https://github.com/jesseduffield/lazygit/pull/3890.
2024-10-13 16:55:54 +02:00
Stefan Haller
53f8249ee1 Fix file icons (#3975)
- **PR Description**

Some file icons weren't drawn correctly, e.g. the ones for
`tailwind.config.ts` or `nuxt.config.ts`, but also many others.

Fixes #3747.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be
hot-reloaded (see
[here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-10-13 16:55:18 +02:00
Stefan Haller
f71274b601 Add test to ensure that file icons are one rune
This should prevent errors like that from happening again.
2024-10-13 16:51:27 +02:00
Stefan Haller
f2fd435c05 Fix many file icons
The string literal "\uf0868" does *not* create a single rune with the code point
f0868, as was intended; instead, it creates two runes, one with the code point
f086, followed by the character '8'.
2024-10-13 16:51:27 +02:00
Stefan Haller
4e361e1a87 Fix merge conflict resolution when file doesn't end with a LF (#3976)
- **PR Description**

When resolving conflicts using lazygit's merge conflicts view in a file
that doesn't end with a trailing line feed, the last line would be lost.

Fixes #3444.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be
hot-reloaded (see
[here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-10-13 16:50:53 +02:00
Stefan Haller
696e78fcc8 Fix ForEachLineInFile to not lose the last line if it doesn't end with a LF 2024-10-09 15:37:08 +02:00
Stefan Haller
b71aa5e23b Add regression test for resolving conflicts in a file without a trailing LF
The test shows that the last line of the file is lost.
2024-10-09 15:36:02 +02:00
Stefan Haller
72cf3efb1d Add test demonstrating problem with ForEachLineInFile
The function drops the last line if it doesn't end with a line feed.
2024-10-09 15:36:02 +02:00
Stefan Haller
ae610dcbb7 Extract helper function for easier testing 2024-10-09 15:08:01 +02:00
Stefan Haller
d11e11d179 Auto-render hyperlinks (#3914)
- **PR Description**

Add a facility to gocui.View to enable auto-rendering of https
hyperlinks. Then, use it for the Command Log panel (as an alternative
approach to #3911), and also in the status view and in confirmation
popups to get rid of some code that used to do this manually.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be
hot-reloaded (see
[here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-09-28 12:07:28 +02:00
Stefan Haller
825f5c0a91 Use AutoRenderHyperLinks in confirmation view
This allows us to get rid of the underlineLinks function.
2024-09-28 12:04:51 +02:00
Stefan Haller
26e3a93fc3 Use AutoRenderHyperLinks in main views
This allows clicking on links in commit messages, for examples. It also affects
the status view, so we can get rid of the manual hyperlinking there.
2024-09-28 12:04:51 +02:00
Stefan Haller
1ceb5a6b37 Turn on AutoRenderHyperLinks in the Command Log panel
Some commands output hyperlinks, and it's useful to be able to click them.
2024-09-28 12:04:51 +02:00
Stefan Haller
65b731f484 Bump gocui 2024-09-28 12:04:51 +02:00
Stefan Haller
c6a7722066 Add a menu item to delete both local and remote branch at once (#3916)
- **PR Description**

Add a menu item to delete both local and remote branch at once.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be
hot-reloaded (see
[here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-09-28 11:26:20 +02:00
Stefan Haller
1ab70ec645 Add a menu item to delete both local and remote branch at once 2024-09-28 11:23:21 +02:00
Stefan Haller
e181de1180 Better branch delete confirmation (#3915)
- **PR Description**

When deleting a local branch, put up the "This branch is not fully
merged, do you want to force delete it" confirmation only when the
branch is not merged into any of the main branches.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be
hot-reloaded (see
[here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-09-28 11:22:54 +02:00
Stefan Haller
c712b1d0fe Better local branch delete confirmation
Currently we try to delete a branch normally, and if git returns an error and
its output contains the text "branch -D", then we prompt the user to force
delete, and try again using -D. Besides just being ugly, this has the
disadvantage that git's logic to decide whether a branch is merged is not very
good; it only considers a branch merged if it is either reachable from the
current head, or from its own upstream. In many cases I want to delete a branch
that has been merged to master, but I don't have master checked out, so the
current branch is really irrelevant, and it should rather (or in addition) check
whether the branch is reachable from one of the main branches. The problem is
that git doesn't know what those are.

But lazygit does, so make the check on our side, prompt the user if necessary,
and always use -D. This is both cleaner, and works better.

See this mailing list discussion for more:
https://lore.kernel.org/git/bf6308ce-3914-4b85-a04b-4a9716bac538@haller-berlin.de/
2024-09-28 11:19:32 +02:00
Stefan Haller
c4e5995cb9 Fix bug with deleting remote branch whose name doesn't match local branch 2024-09-28 11:19:32 +02:00
Stefan Haller
be3683ccc8 Add test that demonstrates bug with deleting remote branch with different name
It's maybe not very common, but it's totally possible for a remote branch to
have a different name than the local branch. This test shows that we don't
support this properly when deleting the remote branch.
2024-09-28 11:19:32 +02:00
Stefan Haller
7e7309f97e Add question marks to questions 2024-09-28 11:19:32 +02:00
Stefan Haller
d2b6f93858 Remove unused texts 2024-09-28 11:19:32 +02:00
Stefan Haller
a91fe517d3 Remove obsolete TODO comment
Looks perfectly internationalized to me.
2024-09-28 11:19:32 +02:00
Stefan Haller
8a328a553a Fix copying commit author to clipboard (#3936)
- **PR Description**

This included single quotes in strange places in the copied text.

Fixes #3933.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be
hot-reloaded (see
[here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-09-28 11:15:03 +02:00
Stefan Haller
3d56357294 Fix copying commit author to clipboard
This was a regression introduced with the GitCommandBuilder in 25f8b0337.
2024-09-23 09:47:14 +02:00
Stefan Haller
9b2a0c4538 Add test for copying a commit author to the clipboard
The test shows that we are including single quotes in strange places.
2024-09-23 09:45:03 +02:00
Jesse Duffield
a04ad24a60 Add performance improvements section to release notes (#3922)
- **PR Description**

- **Please check if the PR fulfills these requirements**

* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
* [ ] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be
hot-reloaded (see
[here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [ ] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-09-18 21:26:47 +10:00
Jesse Duffield
0d633896ae Add performance improvements section to release notes 2024-09-18 21:06:02 +10:00
Stefan Haller
611fabde11 Fix crash when viewing the divergence of a branch which is up to date with its upstream (#3918)
This was introduced by #3838, specifically by commit e675025411.

Fixes #3900 and #3917.
2024-09-18 09:27:11 +02:00
Stefan Haller
f8073c7188 Fix crash when viewing the divergence of a branch which is up to date with its upstream
This was introduced by #3838, specifically by commit e675025411.

Add a regression test that would have crashed without the fix.
2024-09-18 09:24:10 +02:00
Stefan Haller
4dadcd2ace Improve performance with large numbers of untracked or modified files (#3919)
- **PR Description**
**BuildTreeFromFiles** used a linear complexity lookup to find if the
children has already been added. Use maps to get constant time lookup
for children.

For the test scenario in #3798 (90000 untracked files) this reduces the
time of BuildTreeFromFiles from something like 10s down to about 30ms on
my machine.

Fixes #3798.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be
hot-reloaded (see
[here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-09-18 09:23:21 +02:00
partho.kunda
b18f12ca0f Use map to quickly find children in BuildTreeFromFiles 2024-09-18 09:16:58 +02:00
Stefan Haller
c67979abbb Add options for disabling switching to the Files panel after popping or applying a stash (#3913)
- **PR Description**

In v0.44.0 we added a small QoL improvement to auto-switch to the Files
panel after popping or applying a stash. While this should be an
improvement for most people, it turns out to be in the way of some
people's workflows, so make it configurable. See
[here](https://github.com/jesseduffield/lazygit/pull/3888#issuecomment-2350853602)
for more discussion.
2024-09-15 14:19:08 +02:00
Stefan Haller
0e489bb5cc Add options for disabling switching to the Files panel after popping or applying a stash 2024-09-15 11:59:59 +02:00
Stefan Haller
647f533e71 With stacked branches, create fixup commit at the end of the branch it belongs to (#3892)
- **PR Description**

When working with stacked branches, and creating a fixup commit for a
commit in one of the lower branches of the stack, the fixup was created
at the top of the stack and the user needed to move it down to the right
branch manually. This is unnecessary extra work; create it at the end of
the right branch automatically.
2024-09-15 11:21:34 +02:00
Stefan Haller
b22149d832 Create fixup commit at end of its branch when there's a stack of branches 2024-09-15 11:19:39 +02:00
Stefan Haller
396215a5c9 Extract helper function for getting the hash of the last commit made 2024-09-15 11:19:39 +02:00
Stefan Haller
42c157a5e6 Add changeToFixup field to MoveFixupCommitDown 2024-09-15 11:19:39 +02:00
Stefan Haller
a793f709b6 Update language files from Crowdin (#3898)
- **PR Description**

Pull down the latest translations from Crowdin. Unfortunately I forgot
to do this in time for the 0.44 release, bummer.

I'm not sure how much testing/proof-reading/peer review we want to do on
these.
2024-09-15 09:43:15 +02:00
Stefan Haller
f74551e464 Generate keybindings 2024-09-08 15:20:52 +02:00
Stefan Haller
e06b1cef60 Update language files from Crowdin 2024-09-08 15:19:26 +02:00
Stefan Haller
2d0c7cb0fc Switch to Files panel after popping a stash (#3888)
#### PR Description

I find myself always switching to the Files panel after popping a stash,
100% of the time, so it makes sense that lazygit does this for me. Do it
for apply as well, for consistency.

#### Please check if the PR fulfills these requirements

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be
hot-reloaded (see
[here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-09-06 13:21:49 +02:00
Stefan Haller
f5b8619ded Switch to Files panel after popping a stash 2024-09-06 13:15:37 +02:00
Stefan Haller
4c6c915a77 Get rid of a lot of error return values (#3890)
- **PR Description**

Change many functions in the gui package (and some in gocui) to no
longer return errors.

There might be more that could be changed in this way, but I feel these
are the main ones.

Fixes #3887.
2024-09-06 08:48:44 +02:00
Stefan Haller
064fae41e7 Remove return value of OpenCommitMessagePanel
Similar to the previous commit: in 100% of the call sites we now need an extra
`return nil`. Nevertheless, I still prefer it this way.
2024-09-06 08:45:48 +02:00
Stefan Haller
d4ef8e53d5 Remove return value of Alert/Confirm/Prompt
This might seem controversial; in many cases the client code gets longer,
because it needs an extra line for an explicit `return nil`. I still prefer
this, because it makes it clearer which calls can return errors.
2024-09-06 08:45:48 +02:00
Stefan Haller
b15a1c7ae7 Remove return value of CreatePopupPanel 2024-09-06 08:45:48 +02:00
Stefan Haller
6f0182f11c Remove return value of RefreshPatchBuildingPanel 2024-09-06 08:45:48 +02:00
Stefan Haller
371998e635 Remove return value of IContextMgr.Push/Pop et. al. 2024-09-06 08:45:48 +02:00
Stefan Haller
072b465fa6 Fix a lock that is held too long
I can only guess, but I think this was a typo (or a copy-paste-o) when this code
was written. It was introduced in 55af07a1bb, and I think the defer was kept by
accident; if it had been on purpose, then the statement would have been put
right after the Lock call.
2024-09-06 08:45:48 +02:00
Stefan Haller
8302575078 Remove return value of Focus-related functions 2024-09-06 08:45:48 +02:00
Stefan Haller
8edcd71234 Remove return value of IPatchExplorerContext.Render, RenderAndFocus, and NavigateTo 2024-09-06 08:45:48 +02:00
Stefan Haller
5446683881 Remove return value of RenderToMainViews and some related functions 2024-09-06 08:45:48 +02:00
Stefan Haller
b91beb68e1 Remove return value of HandleRender 2024-09-06 08:45:48 +02:00
Stefan Haller
5659f1f3e9 Bump gocui
And adapt client code.
2024-09-06 08:45:48 +02:00
Stefan Haller
753b16b697 Add Zed editor support to editorPreset (#3886)
- **PR Description**

- **Please check if the PR fulfills these requirements**

* [X] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [X] Docs have been updated if necessary
* [X] You've read through your own file changes for silly mistakes etc
2024-09-03 19:27:29 +02:00
Dmytro Suvorov
da7a28c117 Add Zed to user_config.go and schema 2024-09-03 19:22:56 +02:00
Dmytro Suvorov
c35743d7ad Add Zed to docs/Config.md 2024-09-03 19:22:56 +02:00
Dmytro Suvorov
f0eafabd6d Add Zed support to editor_presets.go 2024-09-03 17:30:56 +03:00
Stefan Haller
fc4cf5d196 Offer autostash option when creating new branch (#3871)
- **PR Description**

Resolves https://github.com/jesseduffield/lazygit/issues/3866

My attempt at creating a shared abstraction felt like it was more
indirection than it was worth so I ended up going with the code
duplication approach.
2024-09-03 09:08:56 +02:00
Brandon
4e880e56c4 Add integration tests for checkout/new branch with autostash 2024-09-03 09:05:53 +02:00
Brandon
e8e39f5ce2 Offer autostash option when creating new branch 2024-09-03 09:05:53 +02:00
Brandon
370ab2d19c Simplify CheckoutRef 2024-09-03 09:05:53 +02:00
Stefan Haller
fa8cd47227 Don't allow opening a menu while the search or filter prompt is open (#3878)
- **PR Description**

This solves several problems that arise from opening a menu while the
prompt is open. We might try to solve these in a different way, e.g. by
dismissing the search prompt before opening a menu, but restricting what
you can do while the prompt is open seems like the more robust fix.

To achieve this, we
- call resetKeyBindings both when opening and when closing the
search/filter prompt
- change the keybindings to only contain the ones for the search prompt
when that context is active.

Fixes #3875.
2024-09-02 18:34:28 +02:00
Stefan Haller
9ec77bba91 Don't allow opening a menu while the search or filter prompt is open
This solves several problems that arise from opening a menu while the prompt is
open. We might try to solve these in a different way, e.g. by dismissing the
search prompt before opening a menu, but restricting what you can do while the
prompt is open seems like the more robust fix.

To achieve this, we
- call resetKeyBindings both when opening and when closing the search/filter
  prompt
- change the keybindings to only contain the ones for the search prompt when
  that context is active.
2024-09-02 18:31:30 +02:00
Stefan Haller
4ec9262ff6 Ask to auto-stage unstaged files when continuing a rebase after resolving conflicts (#3879)
- **PR Description**

When lazygit sees that all conflicts were resolved, it auto-stages all
previously conflicted files and asks to continue the rebase. (There is
[a PR](https://github.com/jesseduffield/lazygit/pull/3870) open to make
this optional.)

It is a common situation for this popup to be opened in the background,
while the user is still busy fixing build errors in their editor. In
this case, coming back to lazygit and confirming the continue prompt
would result in an error because not all files are staged.

Improve this by opening another popup in this case, asking to stage the
newly modified files too and continue.

See
https://github.com/jesseduffield/lazygit/issues/3111#issuecomment-1801751982
and the following discussion further down in that issue.
2024-09-02 18:27:08 +02:00
Stefan Haller
ba21d4e651 Ask to auto-stage unstaged files when continuing a rebase after resolving conflicts 2024-09-02 18:24:36 +02:00
Stefan Haller
3cffed9412 Make auto-staging resolved conflicts optional (#3870)
- **PR Description**

Add user config `git.autoStageResolvedConflicts` (default true). When
set to false, users need to stage their conflicted files manually after
resolving conflicts, and also continue a merge/rebase manually when all
conflicted files are resolved.

Fixes #3111.
2024-09-02 18:20:20 +02:00
Stefan Haller
90b8fd242d Add config git.autoStageResolvedConflicts 2024-09-02 18:12:47 +02:00
Stefan Haller
1191aca60f Actually look for conflict markers in GetHasInlineMergeConflicts
So far, lazygit has always auto-staged files as soon as the conflict markers
disappeared from them, which means that we could rely on any file that still had
a status of "UU" to still contain conflict markers.

We are going to make the auto-staging optional in the next commit, and in that
case the user will want to manually stage "UU" files; so we must now check
whether the file contains conflict markers, and disallow the staging in that
case.
2024-09-02 18:12:47 +02:00
Stefan Haller
2f01af49e8 Non-sticky range selection diff (#3869)
- **PR Description**

When selecting a range of commits, show the combined diff for them.

Along the way, fix a few minor issues with the current implementation of
diffing mode; see the individual commit messages for details.

Fixes #3862.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be
hot-reloaded (see
[here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-08-31 08:16:32 +02:00
Stefan Haller
32fef9aadb Add a simple integration test for non-sticky range diff 2024-08-28 19:51:15 +02:00
Stefan Haller
717cb40f05 Cleanup: remove now unused ListControllerTrait from SwitchToDiffFilesController 2024-08-28 19:51:15 +02:00
Stefan Haller
ef7d1a8602 Use non-sticky range diff when entering commit files panel
We make the name of the GetSelectedRefRangeForDiffFiles very specific on purpose
to make it clear that this is only for switching to diff files, so the
implementations can make assumptions about that (unlike GetSelectedRef, which is
used for different purposes and needs to stay more generic).
2024-08-28 19:51:15 +02:00
Stefan Haller
a6656e307c Extract a method CommitFilesContext.ReInit
Right now it doesn't do very much yet, but it's still worth it even in this
state, I'd say. The function is going to become a bit longer in the next commit,
though.
2024-08-28 19:51:15 +02:00
Stefan Haller
442592a149 Show diff for range selection in commits and sub-commits panel
In other views that show lists of commits (reflog and stash) it doesn't make
sense to show a range diff of selected entries because they don't form a linear
sequence, so we keep the previous behavior of showing the diff for the free end
of the selection range in those view.

The same applies to the commits view if the selection range includes rebasing
todos; these can have an arbitrary order, and a range diff doesn't make sense
for those.
2024-08-28 19:35:23 +02:00
Stefan Haller
ac335907ae Add ShortRefName to Ref interface 2024-08-28 18:27:52 +02:00
Stefan Haller
b07ce19b9a Add --stat -p to diff args in diffing mode
This is consistent with what we do for showing single commits (with git show),
and I find it very useful.
2024-08-28 18:27:52 +02:00
Stefan Haller
079c5a9905 Add prefix to main view diff when in diffing mode
Hopefully this will help alleviate the problem that diffing mode is sticky, and
you often forget that it's still on.

Make it magenta like the mode text in the information view.
2024-08-28 18:27:52 +02:00
Stefan Haller
a2c4fad410 Bugfix: properly set title of subcommits panel when refreshing
This fixes two problems:
- Set the title ref to the commit description (like
  SwitchToDiffFilesController.enter does), rather than just the hash
- SetTitleRef only sets the title of the DynamicTitleBuilder, but doesn't set it
  on the view; this only happens in ContextMgr.Activate, so you'd have to switch
  to a different side panel and then back to see the new title.
2024-08-28 18:27:52 +02:00
Stefan Haller
567e898e22 Bugfix: don't allow dropping patches from a custom patch that was made in diffing mode
The three nested `if` statements may looks strange, and you might wonder why we
don't have single one with &&. The answer is that later in this branch we will
add an `else` block to the middle one.
2024-08-28 18:27:52 +02:00
Stefan Haller
14a29d6d6f Bugfix: more comprehensive check whether custom patch must be reset
We need to compare more than just the "To" ref. The NewPatchRequired function
existed already for this purpose, it just wasn't used.
2024-08-28 18:27:52 +02:00
Stefan Haller
8e2ed8c538 Extract helper function currentFromToReverseForPatchBuilding
In the next commit it will be reused for checking whether we need to reset the
patch; and extracting it makes it easier to extend it for non-sticky diff ranges
later in the branch.
2024-08-28 18:27:52 +02:00
Stefan Haller
6ad4ffea3b Cleanup: remove unnecessary viewFiles indirection
viewFiles is only called from enter; it doesn't make much sense to fill in a
SwitchToCommitFilesContextOpts struct to pass it to viewFiles for this one call.
Simply inline viewFiles into enter and get rid of all that.
2024-08-28 18:27:52 +02:00
Stefan Haller
770d51634c Cleanup: remove diffFilesContext field of SwitchToDiffFilesController
I found this indirection confusing when reading the code. It looks like
SwitchToDiffFilesController is instantiated with different such contexts, but
it's always Contexts.CommitFiles, so just use that directly.
2024-08-28 18:27:52 +02:00
Stefan Haller
c51e13941c Cleanup: remove unused translated text 2024-08-28 16:54:38 +02:00
Stefan Haller
05ae0800c3 Add codespell support (config, workflow to detect/not fix) and make it fix few typos (#3751)
More about codespell: https://github.com/codespell-project/codespell .

I personally introduced it to dozens if not hundreds of projects already
and so far only positive feedback.

CI workflow has 'permissions' set only to 'read' so also should be safe.
2024-08-27 18:05:45 +02:00
Yaroslav Halchenko
4bbfe7b3cc [DATALAD RUNCMD] run codespell throughout fixing typos automagically
=== Do not change lines below ===
{
 "chain": [],
 "cmd": "codespell -w",
 "exit": 0,
 "extra_inputs": [],
 "inputs": [],
 "outputs": [],
 "pwd": "."
}
^^^ Do not change lines above ^^^

Signed-off-by: Yaroslav Halchenko <debian@onerussian.com>
2024-08-27 18:03:00 +02:00
Yaroslav Halchenko
00530477c9 [DATALAD RUNCMD] Do interactive fixing of some ambigous typos
=== Do not change lines below ===
{
 "chain": [],
 "cmd": "codespell -w -i 3 -C 2 ./pkg/commands/oscommands/cmd_obj_runner.go ./pkg/integration/tests/branch/rebase_and_drop.go",
 "exit": 0,
 "extra_inputs": [],
 "inputs": [],
 "outputs": [],
 "pwd": "."
}
^^^ Do not change lines above ^^^

Signed-off-by: Yaroslav Halchenko <debian@onerussian.com>
2024-08-27 18:03:00 +02:00
Yaroslav Halchenko
90c1334535 Skip also for \nd
Signed-off-by: Yaroslav Halchenko <debian@onerussian.com>
2024-08-27 18:03:00 +02:00
Yaroslav Halchenko
83ef031922 [DATALAD RUNCMD] Do interactive fixing of some ambigous typos
=== Do not change lines below ===
{
 "chain": [],
 "cmd": "codespell -w -i 3 -C 2",
 "exit": 0,
 "extra_inputs": [],
 "inputs": [],
 "outputs": [],
 "pwd": "."
}
^^^ Do not change lines above ^^^

Signed-off-by: Yaroslav Halchenko <debian@onerussian.com>
2024-08-27 18:03:00 +02:00
Yaroslav Halchenko
820c2bc0fd custom skips for codespell
Signed-off-by: Yaroslav Halchenko <debian@onerussian.com>
2024-08-27 18:03:00 +02:00
Yaroslav Halchenko
a484954d74 Add rudimentary codespell config
Signed-off-by: Yaroslav Halchenko <debian@onerussian.com>
2024-08-27 18:03:00 +02:00
Yaroslav Halchenko
56be1a4950 Add github action to codespell master on push and PRs
Signed-off-by: Yaroslav Halchenko <debian@onerussian.com>
2024-08-27 18:03:00 +02:00
Yaroslav Halchenko
1e8eb72b2f Do not git ignore .codespellrc
Signed-off-by: Yaroslav Halchenko <debian@onerussian.com>
2024-08-27 18:03:00 +02:00
Stefan Haller
18935bf7f0 Fix linter warnings (#3854)
- **PR Description**

Fix some linter warnings that I keep getting locally because I'm running
a newer version of golangci-lint than the pinned version we use on CI.

Also, update the pinned version in CI to 1.60 so that we are in sync
again.

Finally, upgrade the golangci-lint-action version to the latest,
although I'm not sure what difference this makes. The linting step runs
faster now, maybe because of better caching -- I noticed that
previously, loading and saving the cache took a very long time, which
often made the lint step the slowest of all.
2024-08-27 13:12:40 +02:00
Stefan Haller
457c4c248d Upgrade golangci-lint-action to latest version 2024-08-27 10:33:06 +02:00
Stefan Haller
8c553dcde9 Upgrade golang-ci to 1.60 2024-08-27 10:33:06 +02:00
Stefan Haller
a709caf138 Remove error return value from functions that always return nil 2024-08-27 10:33:06 +02:00
Stefan Haller
8dea2dab88 Ignore return values of fmt.Scanln 2024-08-27 10:33:06 +02:00
Stefan Haller
63aa32c521 Remove "double" formatting 2024-08-27 10:33:06 +02:00
Stefan Haller
24841f22f1 Use print instead of printf when there are no arguments 2024-08-27 10:33:06 +02:00
Stefan Haller
d712c2f199 Use format arguments instead of concatenating strings
That's what they are for.
2024-08-27 10:33:06 +02:00
Stefan Haller
4525216d26 Fix cancelled autostash resulting in stuck inline status (#3860)
- **PR Description**

When switching branches, there is a "Checking out" inline status
displayed next to the branch (but only sometimes? I think if the action
completes too quickly there is no status). If it does get displayed and
the checkout results in an autostash confirmation prompt, pressing
escape to cancel the action will cancel the checkout and close the
prompt. However, the inline status will still be displayed next to the
branch and doesn't go away by itself. Performing a manual UI refresh
(`R`) fixes the state.

If the prompt was confirmed instead, then this issue would not happen.

Reproduction:
```bash
git init
echo -e "a\n\nb" > file
git add .
git commit -m "add file"
echo -e "a\n\nc" > file
git add .
git commit -m "edit last line"
git checkout -b dev HEAD~
echo -e "b\n\nb" > file
lazygit
```

Switch to the other branch in the branches panel and press escape on the
prompt. The "Checking out" inline status should be stuck.
2024-08-27 10:30:31 +02:00
Brandon
ae61da7485 Fix cancelled autostash resulting in stuck inline status 2024-08-27 10:28:22 +02:00
Stefan Haller
fb9f6153fc Add missing closing quote in pager docs (#3864)
- **PR Description**

Add missing closing quote in pager docs.
2024-08-27 10:22:47 +02:00
Stefan Haller
a0c808842b Add missing closing quote 2024-08-27 10:16:53 +02:00
Stefan Haller
8a8490d97d Underline hyperlinks only on mouse hover (#3856)
- **PR Description**

Followup to #3825: we decided there that we don't want to underline
links in delta diffs by default, but only on mouse hover. This PR does
that; it makes it possible to decide per view whether links should be
underlined always, or only on hover. We set this to only on hover for
the main views, so that links in diffs are not underlined (also affects
the status view though), but all other links we want to underline always
for better discoverability.
2024-08-24 17:49:10 +02:00
Stefan Haller
68c7f9840a Set main views to underline hyperlinks only on mouse hover
Note that this doesn't only affect the diff views, which are the ones where we
want this, but also the status view, where the plan was to keep them underlined
always for better discoverability. We could make this configurable dynamically
(by adding another flag to ViewUpdateOpts), but actually I think it's fine this
way.
2024-08-24 17:45:54 +02:00
Stefan Haller
8d37f48744 Bump gocui 2024-08-24 17:45:51 +02:00
Stefan Haller
37f32755b5 Fix rendering regression introduced in #3839 (#3855)
- **PR Description**

Fix broken rendering introduced in #3839: the status view would often render only partially.
2024-08-24 11:51:53 +02:00
Stefan Haller
5380fe2483 Fix rendering regression introduced in #3839 2024-08-24 11:46:53 +02:00
Stefan Haller
8e71df3e53 Fix loading customCommands from per-repo config file (#3835)
- **PR Description**

Any newly loaded custom command coming from the per-repo config file should add
to the global ones (or override an existing one in the global one), rather than
replace all global ones.
2024-08-24 11:04:56 +02:00
Stefan Haller
30f43a245b Fix loading customCommands from per-repo config file
Any newly loaded custom command coming from the per-repo config file should add
to the global ones (or override an existing one in the global one), rather than
replace all global ones.

We can achieve this by simply prepending the newly loaded commands to the
existing ones. We don't have to take care of removing duplicate key assignments;
it is already possible to add two custom commands with the same key to the
global config file, the first one wins.
2024-08-24 11:01:25 +02:00
Stefan Haller
283ed29f10 Add a test that shows how per-repo config file replaces customCommands
We want to add to the global customCommands instead of replacing them.
2024-08-24 11:01:25 +02:00
Stefan Haller
ccd39bb8ae Fix wrong test assertion text
If a `t.FileSystem().FileContent("file.txt", Equals("bla"))` assertion fails
because the file doesn't exist, the error would say

   Expected path 'file.txt' to not exist, but it does

which is very confusing.
2024-08-24 11:01:25 +02:00
Stefan Haller
db40653202 Allow using </> and ,/. in sticky range select mode in patch explorer (#3837)
- **PR Description**

Don't cancel sticky range select when pressing `<`/`>`, `,`/`.` in the
patch explorer view. This was already working correctly in list views.

Fixes #3823.
2024-08-24 10:59:48 +02:00
Stefan Haller
d3940729eb Allow using </> and ,/. in sticky range select mode in patch explorer
They still cancel hunk selection mode, setting it to line selection mode, but if
range selection mode is on, we keep it on.
2024-08-24 10:56:20 +02:00
Stefan Haller
56a6ee6afb Cleanup: move SetLineSelectMode into AdjustSelectedLineIdx 2024-08-24 10:56:20 +02:00
Stefan Haller
a37a3fc4a1 Fix crash when filtering commits (#3838)
- **PR Description**

First we fix the crash reported in #3812 (see there for reproduction
recipe), and then we add a more general fix for avoiding crashes in
similar situations that we don't know about yet.

I'm not really happy with the brittleness of all this (the
interdependency between rendering and updating search results, that is),
but I haven't found a good way to untangle this yet.

Fixes #3812.
2024-08-24 10:55:12 +02:00
Stefan Haller
af87cd1dd6 Don't return model search results for commits when we don't have columnPositions
We fixed one specific scenario where this happened ealier in this branch, but in
case there are more that we don't know about yet, at least make sure we don't
crash.
2024-08-24 10:51:25 +02:00
Stefan Haller
e675025411 Return nil columnPositions when not rendering anything
... instead of returning a slice with a single [0] element. This makes it easier
to check whether we have columnPositions.
2024-08-24 10:51:25 +02:00
Stefan Haller
c3d5798c6c Fix early exit condition
I don't know what this condition is supposed to guard against, or whether we
really need it (it was added in 06ca71e955, and the commit message of that
commit only says "fix bug"). But if we do need it, then it seems that `>=` is
more correct than `>`.
2024-08-24 10:51:25 +02:00
Stefan Haller
15d17e16dd Call ReApplySearch after layout
This fixes a possible crash when exiting filter mode in the commits panel.
2024-08-24 10:51:25 +02:00
Stefan Haller
926061557b Make searching available in the filtered commits list
It is already possible to search a filtered list by searching first, and then
enabling a filter, so I found it inconsistent to not allow searching when you
are already filtering. One reason for not allowing this might be that the search
status (on the left) hides the filter status (on the right), but if we think
that's enough reason to not allow both at the same time, then we should cancel a
search when we enter filtering.
2024-08-24 10:51:25 +02:00
Stefan Haller
8522337f32 Scroll views up if needed to show all their content (#3839)
- **PR Description**

There are many situations where this can arise. Some examples are:
- the terminal window is small, and you are showing a view that shows more
content than fits into the view port, and the view is scrolled all the way
down; now you resize the terminal window to a taller size. Previously, the
scroll position of the view would stay the same, so it would add blank space
at the bottom; now it will scroll to fill that blank space with content
- expandFocusedSidePanel is on, you go to the bottom of a list view, now switch
to a different panel, then scroll that (now unfocused) panel all the way down
with the scroll wheel; now you focus that panel again. It becomes larger
because of the accordion behavior, but would show blank space at the
bottom.

And probably others that I can't remember right now. I only remember that I
always found it confusing to look at a view that had blank space at the bottom
even though it had more content to scroll into view.
2024-08-24 10:50:50 +02:00
Stefan Haller
6114f69ee5 Scroll views up if needed to show all their content
There are many situations where this can arise. Some examples are:
- the terminal window is small, and you are showing a view that shows more
  content than fits into the view port, and the view is scrolled all the way
  down; now you resize the terminal window to a taller size. Previously, the
  scroll position of the view would stay the same, so it would add blank space
  at the bottom; now it will scroll to fill that blank space with content
- expandFocusedSidePanel is on, you go to the bottom of a list view, now switch
  to a different panel, then scroll that (now unfocused) panel all the way down
  with the scroll wheel; now you focus that panel again. It becomes larger
  because of the accordion behavior, but would show blank space at the bottom.

And probably others that I can't remember right now. I only remember that I
always found it confusing to look at a view that had blank space at the bottom
even though it had more content to scroll into view.
2024-08-24 10:47:27 +02:00
Stefan Haller
0aa351443f Bump gocui 2024-08-24 10:47:25 +02:00
Stefan Haller
c28ecabfd8 Support hyperlinks from pagers (#3825)
- **PR Description**

Allows to use `delta --hyperlinks` as a pager, which turns line numbers
in the diff into clickable links that take you to the respective file.
For VS Code users, I recommend to combine this with
`--hyperlinks-file-link-format="vscode://file/{path}:{line}"`
so that it jumps to the right line.

In addition, I added a few commits that replaces our old, manual ad-hoc
handling of links in various places (status view, confirmation panels,
information view) with the new hyperlinks feature, which cleans up the
code a bit.

Fixes #3817.
2024-08-24 10:39:41 +02:00
Stefan Haller
bbd779b437 Use our new hyperlink support in the information view 2024-08-24 10:36:01 +02:00
Stefan Haller
b411897a5a Fix Decolorise to also strip hyperlinks
This is needed so that the information view is correctly aligned when we add
hyperlinks to it.
2024-08-24 10:36:01 +02:00
Stefan Haller
fb97c30080 Remove now unused function handleGenericClick 2024-08-24 10:36:01 +02:00
Stefan Haller
61b59837bb Use our new hyperlink support in confirmations 2024-08-24 10:36:01 +02:00
Stefan Haller
e65c0a9d5e Use our new hyperlink support in the status panel 2024-08-24 10:36:01 +02:00
Stefan Haller
3f7674a2e9 Add function to render a hyperlink
It might seem cleaner to integrate this into the text style system, so that you
could say `ts := ts.Url("some link")` and then `ts.Sprint("my text")`. However,
this would require adding a new field to TextStyle, which I didn't want to do.
2024-08-24 10:36:01 +02:00
Stefan Haller
fb81fc6057 Add documentation about delta --hyperlinks 2024-08-24 10:36:01 +02:00
Stefan Haller
e1acb6a547 Set an openHyperlink function on gocui 2024-08-24 10:36:01 +02:00
Stefan Haller
250eb14de1 Bump gocui 2024-08-24 10:35:59 +02:00
Stefan Haller
61ae5e16ae Improve mouse support for commit message panel (#3836)
#### PR Description

* Fix some minor problems related to cursor movement in an auto-wrapped
  commit message
* Allow clicking in an editable view to set the cursor (most useful in
  longer commit descriptions)
* Allow switching between commit message and description by clicking
2024-08-24 10:29:44 +02:00
Stefan Haller
e1efbfc842 Make it possible to scroll the commit description with the mouse wheel
It is just unexpected and confusing when it isn't.

There's something weird about the cursor position when scrolling it out of view;
it will be shown clamped to the visible area of the view (as if it had moved in
the opposite direction than the scroll direction), but then when you type
something again, or just move the cursor with the arrow keys, the view will jump
back to where the cursor really was. This looks confusing, and it might be
reason enough not to allow scrolling the view at all.
2024-08-24 10:21:30 +02:00
Stefan Haller
7d486cabeb Allow switching between commit message and description by clicking
It is annoying to have to tab to the description first before you can set the
cursor there by clicking.
2024-08-24 10:21:30 +02:00
Stefan Haller
c2e953a09f Cleanup: use the right context for CommitDescriptionController.Context()
It doesn't seem to be used by anything, it looks like we only need to implement
the method so that the IController interface is satisfied. But still, it doesn't
hurt to be correct here, and might avoid confusion in the future when somebody
does try to use the method.
2024-08-24 10:21:30 +02:00
Stefan Haller
59450c7d12 Bump gocui 2024-08-24 10:21:25 +02:00
Jesse Duffield
ca9e006cca Fix range select -> stage failure when deleted file is already staged (#3631)
- **PR Description**

Fix range select -> stage failure when deleted file is already staged.

Fixes https://github.com/jesseduffield/lazygit/issues/3603

- **Please check if the PR fulfills these requirements**

* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-08-24 10:31:46 +10:00
Brandon
387bdb1b84 Don't stage already staged deleted items 2024-08-24 09:36:44 +10:00
Jesse Duffield
5fb98655b3 Improve fixup commits script (#3853)
This script is failing currently on
https://github.com/jesseduffield/lazygit/pull/3631 because that fork's
master branch is 300 commits behind our own, but the feature branch is
up to date.

The thing is, we don't actually need to involve the master branch. All
we care about is the feature branch's own commits, so this commit simply
fetches those commits and checks them.

- **PR Description**

- **Please check if the PR fulfills these requirements**

* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
* [ ] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] If a new UserConfig entry was added, make sure it can be
hot-reloaded (see
[here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-08-24 09:36:30 +10:00
Jesse Duffield
addfa2f961 Improve fixup commits script
This script is failing currently on
https://github.com/jesseduffield/lazygit/pull/3631 because that fork's
master branch is 300 commits behind our own, but the feature branch is
up to date.

The thing is, we don't actually need to involve the master branch. All
we care about is the feature branch's own commits, so this commit simply
fetches those commits and checks them.
2024-08-24 09:27:02 +10:00
Stefan Haller
7679b109cb Specifying branch name source from refs/heads for fast forwarding (#3807)
- **PR Description**
This fixes #2625
2024-08-18 10:45:42 +02:00
Neko Box Coder
6a418c65ca Specifying branch name source from refs/heads for fast forwarding 2024-08-18 10:42:57 +02:00
Stefan Haller
aa55995924 Per-repo config files (and reloading of edited config files) (#3787)
- **PR Description**

Support per-repo user config files. For now we only support
`.git/lazygit.yml`; in the future we would also like to support
`./.lazygit.yml`, but that one will need a trust prompt as it could be
versioned, which adds quite a bit of complexity, so we leave that for
later.

We do, however, support config files in parent directories (all the way
up to the root directory). This makes it possible to add a config file
that applies to multiple repos at once. Useful if you want to set
different options for all your work repos vs. all your open-source
repos, for instance.

In addition, we support re-loading edited config files. This makes it
much easier to experiment with config settings, especially the ones that
affect the layout or color scheme, because you see the effect
immediately without having to restart lazygit.
2024-08-18 10:28:33 +02:00
Stefan Haller
5431c269d3 Add checkbox to PR template to make sure UserConfig entries can be hot-reloaded 2024-08-18 10:24:53 +02:00
Stefan Haller
73696629d9 Add dev documentation about using UserConfig 2024-08-18 10:24:53 +02:00
Stefan Haller
38b1255119 Add information about per-repo config files to Config.md 2024-08-18 10:24:53 +02:00
Stefan Haller
57de11b709 Show a confirmation when changing a config that can't be auto-reloaded 2024-08-18 10:24:53 +02:00
Stefan Haller
ce50533689 Reload changed user config files on terminal focus 2024-08-18 10:24:53 +02:00
Stefan Haller
18ad975573 Make custom commands reload when switching repos
Since onNewRepo calls resetKeybindings, which reinitializes the keybindings for
custom commands, all we have to do for this is store a pointer to a config
instead of storing the customCommands, so we get the up-to-date ones every time.
2024-08-18 10:24:52 +02:00
Stefan Haller
fd8e480363 Re-determine existing main branches if mainBranches config changed 2024-08-18 10:24:52 +02:00
Stefan Haller
3d6d677453 Store Common instead of just the list of configured main branches in MainBranches
This will make it possible to change the configured main branches at runtime.
We'll support this in the next commit.
2024-08-18 10:24:52 +02:00
Stefan Haller
aef8e71b82 Make gui.commitLength hot-reloadable 2024-08-18 10:24:52 +02:00
Stefan Haller
5872779faa Make gui.statusPanelView config hot-reloadable
It still doesn't update the view immediately when reloading the config, only the
next time the status panel is focused. But at least it does it then; writing the
code to update the panel when reloading the config is probably not justifiable.
2024-08-18 10:24:52 +02:00
Stefan Haller
04d7907864 Move initialization of global gocui properties to onUserConfigLoaded 2024-08-18 10:24:52 +02:00
Stefan Haller
65a2eccfdb Set custom author and branch colors and nerd font version after loading user config 2024-08-18 10:24:52 +02:00
Stefan Haller
4a272afc67 Reinitialize gui.ShowExtrasWindow after loading user config 2024-08-18 10:24:52 +02:00
Stefan Haller
2499a6c8a3 Initialize translation set after reading user config
This allows having per-repo config files with different languages per repo. Now
granted, this is not an important use case that we need to support; however, the
goal is to eventually make all configs hot-reloadable (as opposed to loading
them only once at startup), so this is one step in that direction.
2024-08-18 10:24:52 +02:00
Stefan Haller
74ed1ac584 Support per-repo config files
For now we only support .git/lazygit.yml; in the future we would also like to
support ./.lazygit.yml, but that one will need a trust prompt as it could be
versioned, which adds quite a bit of complexity, so we leave that for later.

We do, however, support config files in parent directories (all the way up to
the root directory). This makes it possible to add a config file that applies to
multiple repos at once. Useful if you want to set different options for all your
work repos vs. all your open-source repos, for instance.
2024-08-18 10:24:52 +02:00
Stefan Haller
d6d48f2866 Make common.UserConfig an atomic.Pointer for safe concurrent access
Currently, userConfig is only read once at startup and then never changes. Later
in this branch, we will add the possibility to reload the user config; this can
happen either when switching repositories, or when the user has edited the
config file. In both cases, reloading happens on the main thread, but the user
config could be accessed concurrently from background threads, so we need to
make this safe.
2024-08-18 10:24:52 +02:00
Stefan Haller
f98b57aa5e Change direct access to Common.UserConfig to a getter
This will allow us to turn the field into an atomic.Value for safe concurrent
access.
2024-08-18 10:24:52 +02:00
Stefan Haller
f114321322 Save changed user config back to disk in integration tests
At the moment, the user config is only read once at startup, so there's no point
in writing it back to disk. However, later in this branch we will add code that
reloads the user config when switching repos, which does happen quite a bit in
integration tests; this would undo the changes that a test made in its
SetupConfig function, so write those changes to disk to prevent that from
happening.
2024-08-18 10:24:52 +02:00
Stefan Haller
940700dc56 Introduce ConfigFile struct
This makes it more explicit how to deal with the different types of config
files: a user-supplied config file (via the LG_CONFIG_FILE env var) is required
to exist, whereas the default config file will be created if it is missing.

We will later extend this with repo-specific config files, which will be skipped
if missing.
2024-08-18 10:24:52 +02:00
Stefan Haller
be0fa98d11 Split createAllViews
Split it so createAllViews instanciates the views, and sets those properties
that are independent of the user config, and configureViewProperties which sets
those things that do depend on the user config. For now we call the second right
after the first, but later we'll call configureViewProperties after reloading
the user config.
2024-08-18 10:24:52 +02:00
Stefan Haller
e71fc43952 Remove return value of Gui.setColorScheme
It always returns nil.
2024-08-18 10:24:52 +02:00
Stefan Haller
cd0f72d66a Remove pointless reloading of UserConfig
It was added in 043cb2ea44, and the commit message was "reload config whenever
returning to gui". I don't understand what this means; Run() is called exactly
once after startup, so it would just reload the config again for no reason.

We will add a real way of reloading the config whenever it has changed later in
this branch.
2024-08-18 10:24:52 +02:00
Stefan Haller
48bb3a9dce Make fields of AppConfig private
We are going to make a few changes to the fields in this branch, and we can make
them with more peace of mind when we can be sure they are not accessed from
outside this package.
2024-08-18 10:24:52 +02:00
Stefan Haller
55d8e801f1 Use getters for AppState and UserConfig instead of accessing the fields directly
This will allow us to make them private.
2024-08-18 10:24:52 +02:00
Stefan Haller
54765d2236 Cleanup: remove unused method AppConfig.ConfigFilename 2024-08-18 10:24:52 +02:00
Stefan Haller
dbf6716b9b Cleanup: remove unused field IsNewRepo 2024-08-18 10:24:52 +02:00
Stefan Haller
a19bb0bbfe Cleanup: remove unused field DeafultConfFiles
It was also embarrassingly misspelled, so it's good that it's gone. :-)
2024-08-18 10:24:52 +02:00
Stefan Haller
75a95865ff Use filepath instead of path for file path operations
In practice, using path seems to work too, since Windows seems to be capable of
dealing with a path like C:/x/y instead of C:\x\y; but it's cleaner to do this
properly.
2024-08-18 10:24:52 +02:00
Stefan Haller
07dd8a2b07 Bump gocui 2024-08-18 10:24:52 +02:00
Stefan Haller
8bcfa3660a Fix pressing escape after clicking in diff view (#3828)
- **PR Description**

When clicking in a single-file diff view to enter staging (or custom
patch editing, when coming from the commit files panel), you needed to
press escape twice to exit, where the first press would seemingly do
nothing.
2024-08-17 11:34:10 +02:00
Stefan Haller
0e4d266a52 Fix pressing escape after clicking in diff view
When clicking in a single-file diff view to enter staging (or custom patch
editing, when coming from the commit files panel), you needed to press escape
twice to exit, where the first press would seemingly do nothing.

The reason for this was that after clicking in the diff we end up in non-sticky
range select mode, but only with a single line selected, which is basically
indistinguishable from line select mode.
2024-08-17 11:30:38 +02:00
Stefan Haller
7676572358 Improve template placeholders for custom commands (#3809)
- **PR Description**

Improve the template placeholders that are available for custom
commands:
- `SelectedCommit` replaces `SelectedLocalCommit`,
`SelectedReflogCommit`, and `SelectedSubCommit`
- `SelectedPath` is set to `SelectedCommitFilePath` when the CommitFiles
context is active

It still slightly bothers me that we don't make a similar unification
for `SelectedLocalBranch` and `SelectedRemoteBranch` (and others), but
it would be a bigger change to do that, and we decided in #3663 not to.

Fixes #3663.
2024-08-17 11:29:49 +02:00
Stefan Haller
7fb758cc1d Set SelectedPath to SelectedCommitFilePath in CommitFiles context 2024-08-17 11:26:31 +02:00
Stefan Haller
22f0d9cdd3 Expose SelectedCommit to custom commands, deprecate Selected{Local,Reflog,Sub}Commit
SelectedCommit is context-dependent and points to SelectedLocalCommit,
SelectedReflogCommit, or SelectedSubCommit depending on which panel is active.

If none of these panels is active, it returns the selected local commit, which
is probably the most useful default (e.g. when defining custom commands for the
Files panel).
2024-08-17 11:26:31 +02:00
Stefan Haller
1cb29cea15 Some cleanups for APIs related to contexts (#3808)
- **PR Description**

Some cleanups for APIs related to contexts. Most of these were triggered
by a TODO comment in the code.
2024-08-17 11:24:15 +02:00
Stefan Haller
1eb5d89f1d Remove bool return value of GetParentContext()
The comments that I'm deleting here explain why we need the bool; however, in
our case that's a theoretical issue. It would only arise if we ever were to pass
a nil context to SetParentContext, which we never do.
2024-08-17 11:14:51 +02:00
Stefan Haller
d570552206 Replace ActivateContext() with Context().Activate() 2024-08-17 11:14:51 +02:00
Stefan Haller
41f41ee4ee Rename ActivateContext/deactivateContext to Activate/deactivate
"Context" is redundant in the method names here.
2024-08-17 11:14:51 +02:00
Stefan Haller
94d6f4dae7 Replace IsCurrentContext() with Context().IsCurrent() 2024-08-17 11:14:51 +02:00
Stefan Haller
f30387e7f5 Replace CurrentPopupContexts() with Context().CurrentPopup() 2024-08-17 11:14:51 +02:00
Stefan Haller
3a8b97841f Rename PopupContexts() to CurrentPopup()
... for consistency with CurrentSide().
2024-08-17 11:14:51 +02:00
Stefan Haller
df3afb1b89 Replace CurrentSideContext() with Context().CurrentSide() 2024-08-17 11:14:51 +02:00
Stefan Haller
7f935c1ea8 Replace CurrentStaticContext() with Context().CurrentStatic() 2024-08-17 11:14:51 +02:00
Stefan Haller
7ed94c0410 Replace CurrentContext() with Context().Current() 2024-08-17 11:14:51 +02:00
Stefan Haller
8e15451117 Remove unused method RemoveContexts() 2024-08-17 11:14:51 +02:00
Stefan Haller
111d10fe5c Replace ReplaceContext() with Context().Replace() 2024-08-17 11:14:50 +02:00
Stefan Haller
98335361fd Replace PopContext() with Context().Pop() 2024-08-17 11:14:50 +02:00
Stefan Haller
bd36b8a95e Replace PushContext() with Context().Push() 2024-08-17 11:14:50 +02:00
Stefan Haller
d89dc967b8 Rename "Custom Command" to "Shell Command" (#3800)
- **PR Description**

The double use of the term "Custom Command" for both shell commands and
user-configured keybindings was confusing.
2024-08-17 10:59:23 +02:00
Stefan Haller
dbca9306de Rename "Custom Command" to "Shell Command"
The double use of the term "Custom Command" for both shell commands and
user-configured keybindings was confusing.
2024-08-17 10:56:03 +02:00
Stefan Haller
0cbe08b105 Add new integration tests folder "shell_commands"
The folder custom_commands contained tests for both custom commands (the ones
you configure in config.yml) and shell commands (the ones you execute at the ":"
prompt). I always found this confusing, so separate these into two different
folders.
2024-08-17 10:56:03 +02:00
Stefan Haller
af5b19a9da Fix line coloring when using the delta pager (#3820)
- **PR Description**

The green/red bars for added/deleted lines in delta wouldn't extend to
the right edge of the view, but end after the last printable character.
This was broken before, and fixed in #1386 two years ago, but then it
broke again in v0.42 because of #3687.
2024-08-17 10:53:47 +02:00
Stefan Haller
da8e4e44b7 Bump gocui 2024-08-17 10:50:32 +02:00
Stefan Haller
69666d1089 Switch tabs with panel jump keys (#3794)
- **PR Description**

When using the panel jump keybindings (`1` through `5` by default), and
the target panel is already the active one, go to the next tab instead.
2024-08-17 10:39:57 +02:00
Stefan Haller
b37d6dcd1c When using the panel jump keys and the target panel is already active, switch tabs 2024-08-17 10:37:00 +02:00
Stefan Haller
c72be6cbf3 Allow using shell aliases in interactive custom commands (#3793)
- **PR Description**

When executing an interactive custom command, use the user's shell
rather than "bash", and pass the -i flag. This makes it possible to use
shell aliases or shell functions which are not available in
non-interactive shells.

In previous attempts to solve this, concerns were brought up: [this
one](https://github.com/jesseduffield/lazygit/pull/2096#issuecomment-1257072541)
is addressed by using the interactive shell only for custom commands but
not anything else. [This
one](https://github.com/jesseduffield/lazygit/pull/2096#issuecomment-1343341795)
is a little dubious and unconfirmed, so I'm not very worried about it.

Supersedes #2096 and #3299. Fixes #770, #899, and #1642.
2024-08-17 10:34:40 +02:00
Stefan Haller
5a3049485c Use an interactive shell for running custom commands
Also, use the user's shell (from the SHELL env variable) instead of bash. Both
of these together allow users to use their shell aliases or shell functions in
the interactive command prompt.
2024-08-17 10:32:18 +02:00
Stefan Haller
39e77d1823 Extract helper function quotedCommandString
Will be reused in the next commit.
2024-08-17 10:32:18 +02:00
Stefan Haller
da94ee7a9a Fix redraw bug (stale content) in commits view (#3783)
- **PR Description**

When switching from a branch with fewer commits than fit in the commits
panel to another branch with even fewer commits, the last few commits of
the old branch were still drawn at the bottom.

Fixes #3778.
2024-08-17 10:29:39 +02:00
Stefan Haller
62ca873ddd Bump gocui 2024-08-17 10:25:16 +02:00
Stefan Haller
9404c2309c Allow GPG reword for last commit (#3815)
- **PR Description**
This PR fixes #3806, which is only for the last commit.
Would be good if this could be extended to commits older than head.
2024-08-16 13:11:17 +02:00
Neko Box Coder
4a2508e960 Allow rewording for last commit using GPG 2024-08-16 13:08:31 +02:00
Neko Box Coder
ce6388bdfa Adding guard to not do reword under git_commands when using gpg 2024-08-15 08:54:23 +02:00
Stefan Haller
58d7467180 Fix lack of icon assignation when extension don't match capitalization (lowercase) (#3810)
- **PR Description**
The extension icon map contain all extensions on lowercase, when a file
don't have extension on lowercase the string don't match and icon is not
assigned.
2024-08-09 12:19:46 +02:00
hasecilu
cb53e377a8 Fix lack of icon assignation when extension don't match capitalization 2024-08-08 15:18:02 -06:00
Stefan Haller
a3560eb451 Don't exit app when GetRepoPaths call fails during startup (#3779)
- **PR Description**
Fixes #3740 

As explained in the issue, 7a67096 moved some code around that caused a
call to `GetRepoPaths` to occur before `setupApp`, which usually handles
the scenario where we are not in a git directory. `GetRepoPaths` returns
an error if the path isn't a git repository, which caused the app to
exit before we reached `setupApp`.

When starting up lazygit, we ignore (and log) the error returned by
`GetRepoPaths`, and continue instead of exiting early. This allows us to
reach the step where we follow the user's `notARepository` config entry.
2024-08-03 18:37:53 +02:00
ppoum
ef4fd70f9c Ignore GetRepoPaths error when launching 2024-08-03 10:02:47 -04:00
Stefan Haller
74fe069da9 feat(custom command): support multiple contexts within one command (#3784)
- **PR Description**

For some custom commands, they can be used in multiple contexts. But for
now, if we want to do this, we should copy and paste the same config
times and times with just a different **context**.

Related issue: #3759 

This PR makes it possible to use multiple contexts in the `context` field of 
`customCommand`, separated by comma.
2024-08-02 11:59:39 +02:00
Yam Liu
542030f190 Support multiple contexts within one command, add tests, update doc 2024-08-02 11:55:29 +02:00
Yam Liu
206b2c6f0b Add a unit test case for global context 2024-08-02 11:55:29 +02:00
Stefan Haller
dc87592876 Add a readme file for the JSON files in pkg/i18n/translations (#3781) 2024-07-26 11:04:18 +02:00
Stefan Haller
37f35da436 Add a readme file for the JSON files in pkg/i18n/translations
People have started sending PRs that change these files.
2024-07-26 11:00:10 +02:00
Stefan Haller
f598da0df2 Reapply "Check for fixup commits on CI" (#3745)
Re-enable the check for fixup commits (see #3742), make it work for
forks, and extend it to "amend!" and "WIP" commits (and a few others).
2024-07-13 15:17:50 +02:00
Stefan Haller
20ccb03a45 Extend check for fixups
Also check for squash! and amend! (these are all anchored to the beginning of
the subject), and WIP and DROPME for good measure (but only if they occur in the
first line, otherwise it wouldn't let me merge this very commit :)
2024-07-13 15:15:01 +02:00
Stefan Haller
891362dfb2 Use extended regex rather than perl regex in the git call
My local git is not compiled with PCRE support, so using -E makes it easier for
me to test the script locally. And -E is good enough for the simple matching we
want to do here.
2024-07-13 15:02:34 +02:00
Stefan Haller
463cf35e64 Make checkout action work with forks 2024-07-13 14:39:28 +02:00
Stefan Haller
1919a2d2d6 Reapply "Check for fixup commits on CI"
This reverts commit f2db9fa3f9.
2024-07-13 14:08:44 +02:00
Stefan Haller
71ad3fac63 Fix language auto detection (#3744)
- **PR Description**

Fix a regression (introduced with #3649) that broke language
auto-detection. When starting lazygit with the `gui.language` config set
to "auto" (which is the default), lazygit would fail to start if the
LANG environment is set to one of our supported languages.

For example:
```
$ export LANG=nl_NL
$ lazygit
2024/07/13 11:43:03 open translations/nl-NL.json: file does not exist
```

Fixes #3743
2024-07-13 12:14:39 +02:00
Stefan Haller
ae4a579153 Fix language auto-detection
Starting lazygit with an environment containing LANG=ko_KO or LANG=nl_NL would
result in an error at startup.
2024-07-13 12:07:59 +02:00
Stefan Haller
da86096e19 Add test that demonstrates bug with language auto-detection 2024-07-13 12:07:59 +02:00
Jesse Duffield
e1d973d62a README.md: Update Sponsors (#3732)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action
2024-07-13 15:04:32 +10:00
github-actions[bot]
0489b11c0c README.md: Update Sponsors 2024-07-13 05:03:33 +00:00
Jesse Duffield
c7a9b9ca07 Add support for setting the similarity threshold for detecting renames (closes #2904) (#3025)
- **PR Description**
Implement the feature as discussed in #2904.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go run scripts/cheatsheet/main.go
generate`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

---

_Edit: Previously I have left the "Text is internationalised" checkbox
empty, as I thought that it would also have to include translations, but
as I understand currently, that would be "localization" instead, so I
ticked it._

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-07-13 15:03:21 +10:00
Jesse Duffield
f2db9fa3f9 Revert "Check for fixup commits on CI"
This reverts commit 7652d579f5.

Not working on forks, and I don't have time to fix right now
2024-07-13 14:54:20 +10:00
István Donkó
b9107d5fc8 Support setting the similarity threshold for detecting renames 2024-07-13 14:24:26 +10:00
Jesse Duffield
bfe2dd4ed8 Check for fixup commits on CI (#3742)
I keep merging PRs that still have fixup commits on them! This will make
it impossible to do so

- **PR Description**

- **Please check if the PR fulfills these requirements**

* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
* [ ] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-07-13 13:51:10 +10:00
Jesse Duffield
7652d579f5 Check for fixup commits on CI
I keep merging PRs that still have fixup commits on them! This will make
it impossible to do so
2024-07-13 13:41:47 +10:00
Jesse Duffield
73bcbe4ee2 Extend icon coverage on remotes and file extensions (#3484)
- **PR Description**

Initially I opened #3426 issue, this PR is targeted to fix that.

- **Please check if the PR fulfills these requirements**

* [X] Cheatsheets are up-to-date (run `go generate ./...`)
* [X] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [X] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [X] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [X] You've read through your own file changes for silly mistakes etc

This is the result from the remote icons that I posted on the issue.
Note: The FreeBSD only is triggered if the remote is "git.FreeBSD.org",
note the capitalization

Edit: btw, there are also icons for gitea -> `nf-linux-gitea`, \uf339, 
and forgejo -> `nf-linux-forgejo`, \uf335,  if someone is interested on
some instances that use that sofware.


![image](https://github.com/jesseduffield/lazygit/assets/53124818/d68aa9a1-5a3d-446a-af38-650754056f06)

Would be nice if in the future the user can make overrides via config
file.
```yml
icon_overrides:
  filenames:
    Cargo.toml: 🦀
    Cargo.lock: 🦀
  extensions:
    rs: 🦀
    nix: ❄
```

Fix #3426

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-07-13 13:32:26 +10:00
hasecilu
cd01e4e7c2 fixup! Add icons for some file names 2024-07-13 13:23:12 +10:00
hasecilu
c5de9cfd8e fixup! Add icons for some file extensions 2024-07-13 13:23:12 +10:00
hasecilu
1129e0e4a0 Add icons for some git remotes 2024-07-13 13:23:12 +10:00
hasecilu
981f1fa7aa Add icons for some file extensions 2024-07-13 13:23:12 +10:00
hasecilu
cad4581d05 Add icons for some file names 2024-07-13 13:23:12 +10:00
hasecilu
f0af42270e Update link from unmaintained exa to maintained eza 2024-07-13 13:23:12 +10:00
Stefan Haller
e0377f2bce Only add commit prefix if branch name matches regex pattern (#3703)
- **PR Description**
Currently if a branch name does not match a regex pattern defined in the
config.yaml (commitPrefix/es) the commit message box is populated with
the branch name as is - this does not match expectations. A prefix
should only be added if there is a match on the regex pattern.

This PR seeks to change that by checking for a match before calling
ReplaceAllString - see Issue #3695

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [-] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [-] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-07-10 09:13:25 +02:00
Luke Swan
968060a5ec Ensure branch name matches pattern before replace
Amend test for non-matching branch name
2024-07-10 09:05:41 +02:00
Luke Swan
07fe828f60 Add initial test for non-matching branch name 2024-07-10 09:05:33 +02:00
Jesse Duffield
b004b2e275 Change RepoPaths to be acquired via RepoPathCache (#3284)
### **PR Description**
In order to optimize the number of git calls made this change implements
a RepoPathCache as the API by which RepoPaths instances are acquired.
This primarily affects app startup and worktree enumeration.

This introduces a new dependency on
[go-memoize](https://github.com/kofalt/go-memoize), which is a
lightweight wrapper around go-cache and singleflight, in order to ensure
that the cache is concurrency safe. (As compared to a simple map, e.g.)
See the go-memoize README for details.

Fixes #3227.

### **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-07-07 15:43:15 +10:00
John Whitley
7a670964cd Optimize number of early calls to GetRepoPaths
This change reduces the number of calls during application startup to
one, calling GetRepoPaths() earlier than previously and plumbing the
repoPaths struct around to achieve this end.
2024-07-06 12:09:48 -07:00
Jesse Duffield
a138a31c72 README.md: Update Sponsors (#3580)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action
2024-07-06 23:00:22 +10:00
github-actions[bot]
31456a8caa README.md: Update Sponsors 2024-07-06 12:08:18 +00:00
Jesse Duffield
22764cd207 Switch between multiple log views (#3354)
- **PR Description**

* Fixes jesseduffield/lazygit#1363
* Allow switching between up two three log views

Seeing as the last activity related to this issue was over a year ago, I
decided to take a stab at this.
The implementation should be fully backwards compatible. Simply add
`allBranchesLogCmdAlt1` and/or `allBranchesLogCmdAlt2` to your config
file to use them when cycling between log commands using 'a'.
You can even use `allBranchesLogCmdAlt2` together with
`allBranchesLogCmd` (skipping `allBranchesLogCmdAlt1`) if you want, it
should not affect usability.

This is my first contribution to LazyGit, but I have experience with Go.

Changes:

- Introduced two new optional user config commands,
allBranchesLogCmdAlt1+2
- When pressing 'a' in the Status view, cycle between non-empty,
non-identical log commands
- There will always be at least one command to run, since
allBranhesLogCmd has a default

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-07-06 22:08:05 +10:00
Martin Kock
be21328c69 Allow cycling between multiple log commands
- Introduced a new optional user config command, allBranchesLogCmds
- When pressing 'a' in the Status view, cycle between non-empty, non-identical log commands
- There will always be at least one command to run, since allBranhesLogCmd has a default
- Update documentation & write an integration test
- Update translation string
2024-07-06 22:02:47 +10:00
Jesse Duffield
3d14893c65 Add Token credential request handling (#3647)
- **PR Description**

Asking for 2FA Token prompt when an additional authentication is
configured for git over SSH

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-07-06 21:47:40 +10:00
Aleksei Larkov
8813587961 Add Token credential request handling
Asking for 2FA Token prompt when an additional authentication is configured for git over SSH
2024-07-06 21:44:10 +10:00
Jesse Duffield
13bd4b964f Add flox install (#3656)
- **PR Description**

`lazygit` is available in Flox today, and seems to be working well.
Since Flox is related to Nix, I decided a good place would be right
below the Nix install section. 😉

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-07-06 21:35:19 +10:00
Bryan Honof
eae76a97e9 docs: Add flox install 2024-07-06 21:27:08 +10:00
Jesse Duffield
34214af41f Fix multi selection stage/discard not working for files with substrings (#3599)
- **PR Description**

I found an issue with multi selection stage/discard. Here is a minimal
repro:
```bash
git init
touch a
touch aa
lazygit
```

Select both files using shift and hit space. Only `a` is staged.

- **Please check if the PR fulfills these requirements**

* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-07-06 21:26:32 +10:00
Brandon
38aa5b89ab Simplify integration test 2024-07-06 21:22:04 +10:00
Brandon
2e5b570bb6 Add integration test 2024-07-06 21:22:04 +10:00
Brandon
a5eec48b4b Fix multi selection stage/discard not working for files with substrings 2024-07-06 21:22:04 +10:00
Jesse Duffield
94cf2cc7df Bump actions/checkout, actions/setup-go, actions/cache/restore, actions/cache/save (#3594) 2024-07-06 21:21:32 +10:00
kyu08
ac30aee1b8 Bump actions/checkout, actions/setup-go, actions/cache/restore, actions/cache/save 2024-07-06 21:17:29 +10:00
Jesse Duffield
d3780fd57d Allow setting a default name when creating new branches (#3487)
- **PR Description**

I commonly prefix my branch names with my first initial and last name,
such as this one (`ecubit/branch-prefixes`). It can be a bit annoying to
type out.

This PR adds a config option to set a default value for the name in the
branch creation modal.

If there would have previously been a branch name autofilled (I do not
know all such cases), this change has no effect.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
- `CONTRIBUTING.md` says I may submit without doing localization (and I
am unable)
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-07-06 21:16:31 +10:00
Elliot Cubit
5959f7bc8e Allow setting a default name when creating new branches 2024-07-06 21:06:28 +10:00
Jesse Duffield
436240bbeb Add nerdfont icons for .bicep & .bicepparam files (#3053)
- **PR Description**
As the title says, I've added the v3 nerd font icons for
[Bicep](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep).

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-07-06 20:31:04 +10:00
Scott McKendry
2317dac730 fix formatting 2024-07-06 20:27:03 +10:00
Scott McKendry
08bd36ea78 Add bicep & bicepparam icons 2024-07-06 20:27:03 +10:00
Jesse Duffield
ab31991e13 Upgrade to Alpine Linux v3.19 (#3541)
- **PR Description**

Alpine v3.15 is out-of-date since 2023-11-01 and is not getting any
security updates anymore: https://alpinelinux.org/releases/

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-07-06 20:25:05 +10:00
fossdd
4df8f87715 Upgrade to Alpine Linux v3.19
Alpine v3.15 is out-of-date since 2023-11-01 and is not getting any security updates anymore: https://alpinelinux.org/releases/
2024-07-06 20:21:40 +10:00
Stefan Haller
62a018c1d4 Update tracking behaviour for branches created from remote branches (#3712)
### Overview

| Current Behaviour | New Behaviour |
|--|--|
| Local branches will **always** track the remote branches they were
created from. | Local branches will track the remote branches they were
created from **only if their names match**. |

### Description

The current behaviour when creating a new branch off of a remote branch
is to always track the branch it was created from.

For example, if a branch 'my_branch' is created off of the remote branch
'fix_crash_13', then 'my_branch' will be tracking the remote
'fix_crash_13' branch.
It is common practice to have both the local and remote branches named
the same when the local is tracking the remote one. Therefore, it is
reasonable to expect that 'my_branch' should not track the remote
'fix_crash_13' branch.

The new behaviour when creating a new branch off of a remote branch is
to track the branch it was created from only if the branch names match.
If the branch names DO NOT match then the newly created branch will not
track the remote branch it was created from.

For example, if a user creates a new branch 'fix_crash_13' off of the
remote branch 'fix_crash_13', then the local 'fix_crash_13' branch will
track the remote 'fix_crash_13' branch.
However, if the user creates a new branch called 'other_branch_name' off
of the remote branch 'fix_crash_13', then the local 'other_branch_name'
branch will NOT track the remote 'fix_crash_13' branch.


- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-07-04 22:37:31 +02:00
T.
b26ff43d9e Update tracking behaviour for branches created from remote branches
The current behaviour when creating a new branch off of a remote branch
is to always track the branch it was created from.

For example, if a branch 'my_branch' is created off of the remote branch
'fix_crash_13', then 'my_branch' will be tracking the remote
'fix_crash_13' branch.

It is common practice to have both the local and remote branches named
the same when the local is tracking the remote one. Therefore, it is
reasonable to expect that 'my_branch' should not track the remote
'fix_crash_13' branch.

The new behaviour when creating a new branch off of a remote branch is
to track the branch it was created from only if the branch names match.
If the branch names DO NOT match then the newly created branch will not
track the remote branch it was created from.

For example, if a user creates a new branch 'fix_crash_13' off of the
remote branch 'fix_crash_13', then the local 'fix_crash_13' branch will
track the remote 'fix_crash_13' branch.
However, if the user creates a new branch called 'other_branch_name' off
of the remote branch 'fix_crash_13', then the local 'other_branch_name'
branch will NOT track the remote 'fix_crash_13' branch.
2024-07-04 22:34:36 +02:00
Stefan Haller
a047fba9f7 Update translations from Crowdin (#3707)
Add a very simple script to semi-automate the process of updating the
non-English translation files from Crowdin.
2024-07-01 08:43:35 +02:00
Stefan Haller
974016cfba Update translations from Crowdin
No content changes yet, because nobody has edited anything in Crowdin so far.

However, this changes a few `\u003` to `<` (pretty sure that was an artefact of
how we manually generated the json files in #3649), and it removes all the
translations that are identical to the English version, which I guess is a good
thing (but doesn't make a difference in practice).
2024-07-01 08:40:13 +02:00
Stefan Haller
5b6dbe57b1 Add script to update translation files from Crowdin 2024-07-01 08:40:13 +02:00
Stefan Haller
92d4073b2a Remove unused TranslationSet entries
Found these by looking for empty strings in en.json.
2024-07-01 08:40:13 +02:00
Stefan Haller
c27a7e0816 Add missing english text
I was looking for empty strings in en.json, that's how I found this one. It
resulted in an empty log entry when adding a co-author to an existing commit.
2024-07-01 08:40:13 +02:00
Stefan Haller
f3c9443ec8 Fix go generate on windows (#3706)
Fix two problems with running `go generate ./...` on Windows:
- the command would error out with `panic: Default config starting
comment not found` (depending on the setup of the local git client)
- after running the command, all files would show up as modified in git,
but without a diff. Staging the files would make them disappear again,
except for those that actually had changes.

Fix both of these by configuring git to check out the generated text
files with Unix line endings.
2024-07-01 08:39:39 +02:00
Stefan Haller
71a62b7573 Re-add a vendor .gitattributes file
This was previously ignored by our top-level .gitignore file; now that we no
longer do that, running `go mod vendor` brings it back.
2024-07-01 08:36:57 +02:00
stk
61313e5dfa Add .gitattributes file
This tells git to checkout all .md and .json files with Unix line feeds,
even on Windows. This shouldn't be a problem for working with these
files on Windows, as all modern text editors and IDEs should be capable
of editing Unix files transparently; but it makes it possible to run
`go generate ./...` on Windows, which assumes Unix line feeds in a few
places.
2024-07-01 08:36:57 +02:00
stk
94a1c27916 Normalize line endings of docs/README.md
For some reason this was checked in with CRLF line endings; change it to
just LF, like all other text files.
2024-07-01 08:36:57 +02:00
Stefan Haller
6da4f9a787 Fix running lazygit with a language other than English on Windows (#3705)
On Windows, trying to use lazygit with a language other than English
would result in the error message `open translations\ja.json: file does
not exist`.
2024-07-01 08:36:38 +02:00
stk
398ceb1dd9 Fix loading translation set json files on Windows
It seems that the embed.FS always uses foreward slashes, even on
Windows.

This not only affected generating the cheatsheets, but also loading a
translation file in production.
2024-06-30 10:41:48 +02:00
Stefan Haller
6788825ed3 Make opening git difftool more consistent (#3691)
The default shortcut to open git difftool (`ctrl+t`) is not available on
the "Local Branches" window. It is available when selecting a commit
from a local branch, a remote branch, or a tag from the "Local Branches"
window.

This is inconsistent since branches or tags are also commits, the
shortcut should also work on them directly.

This commit remedies this inconsistency by allowing the use of the
shortcut directly on a branch or a tag. The shortcut works both in the
"standard" mode and the "diffing" mode.
2024-06-30 10:32:51 +02:00
T.
2335772db6 Make opening git difftool more consistent
The default shortcut to open git difftool (ctrl+t) is not available on
the "Local Branches" window. It is available when selecting a commit
from a local branch, a remote branch, or a tag from the "Local Branches"
window.

This is inconsistent since branches or tags are also commits, the
shortcut should also work on them directly.

This commit remedies this inconsistency by allowing the use of the
shortcut directly on a branch or a tag. The shortcut works both in the
"standard" mode and the "diffing" mode.
2024-06-30 10:27:28 +02:00
Jesse Duffield
1285554cb2 Add Squash merge (#3566)
- **PR Description**

Hello,

This PR add merge --squash. A PR already exist
https://github.com/jesseduffield/lazygit/pull/3130, but the author
abandoned it, so I remake it.
I modified to fit most of the comment made except this one
https://github.com/jesseduffield/lazygit/pull/3130/files#r1404808121. I
didn't find an existing example and thus didn't know to modify the code
to fit it to the new way of doing things.

There's still the choice box to commit or not to do as discussed
https://github.com/jesseduffield/lazygit/pull/3130#issuecomment-2112324990.
I'll do it when I have time, I first need to read the code to see how it
really works.

Also only english has been made for now. 


![image](https://github.com/jesseduffield/lazygit/assets/94681915/f648ca13-3d16-4703-a074-a83fe9a1eb0f)


- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-06-30 11:05:09 +10:00
Noah
232be05785 feat: squash merge 2024-06-30 11:01:03 +10:00
Stefan Haller
8990026358 Make commit author length configurable, take 2 (#3688)
- **PR Description**

This reverts #3625, and instead adds two new config keys
`commitAuthorShortLength` and `commitAuthorLongLength` for achieving the
same thing, but with more flexibility.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-29 11:31:52 +02:00
Stefan Haller
bd782f16dd Provide two config keys for configuring the author length in commits view
One is for the normal view, the other for the expanded view.
2024-06-29 11:28:44 +02:00
Stefan Haller
7be82d4713 Revert "Add user config gui.commitAuthorFormat (#3625)"
This reverts commit 3af545daf7, reversing
changes made to 629b7ba1b8.

We changed our mind about this and want to provide different options for
achieving the same thing, but with more flexibility.
2024-06-29 11:28:00 +02:00
Stefan Haller
5dbdbd8425 Turn off the highlight of the suggestions panel when it loses focus (#3696)
- **PR Description**

The highlight is normally turned off in HandleFocusLost, but that's not
called when using ReplaceContext (and changing this would be a lot of
work, it seems), so turn it off manually for now.

For a moment I considered whether we want to show the new inactive
highlight when switching from suggestions to the prompt (actually this
did happen on master), but I decided against it for several reasons:
- it's not quite the right concept (the suggestions view is not the
"parent" context of the prompt),
- there's no benefit from seeing one of the suggestions selected (and
the selection would change arbitrarily when changing the filter string)
- there would be visual problems when the suggestions become empty, in
which case we would still highlight the first empty row (which you can
only see if you set the gui.theme.inactiveViewSelectedLineBgColor config
to some color). Now this could be considered a bug in the focus
management of the suggestions panel, but it doesn't seem worth fixing
this; the problem goes away by turning off the highlight.
2024-06-29 11:27:26 +02:00
Stefan Haller
9b73b68f95 Turn off the highlight of the suggestions panel when it loses focus
The highlight is normally turned off in HandleFocusLost, but that's not called
when using ReplaceContext (and changing this would be a lot of work, it seems),
so turn it off manually here for now.
2024-06-29 11:24:08 +02:00
Stefan Haller
c3715d0f86 Extract helper function SuggestionsController.switchToConfirmation
This fixes the minor issue that the subtitle of the suggestions view wasn't
emptied when hitting "e" to edit a custom command.
2024-06-29 11:24:08 +02:00
Stefan Haller
696b8ba457 Always reapply filters on filtered views when model changes, even inactive ones (#3697)
- **PR Description**

A filtered view would show stale data when the model changes but the
view is inactive. I think this could only happen when changing some git
state outside of lazygit; for example, when creating a new branch while
the branches panel has a filter set, but doesn't have the focus. (See
the added test for an example.)
2024-06-29 11:23:38 +02:00
Stefan Haller
e205c6ba7f Always reapply filters on filtered views, even inactive ones 2024-06-28 08:23:27 +02:00
Stefan Haller
c6df856079 Add test demonstrating a problem with updating the filter when the model changes
We only update the filter when the filtered view has the focus.
2024-06-28 08:23:27 +02:00
Stefan Haller
26c3e0d333 Stagger popup panels (#3694)
- **PR Description**

Sometimes we open a popup panel on top of another one; an example is the
`<c-o>` menu in the commit message editor, or the "add co-author" prompt
that appears on top of the commit message panel. We just added a new one
in #3676, which is the confirmation that appears when pasting a commit
message over an existing one.

Currently, these panels are sized and positioned independently of each
other, which looks ugly and makes it hard to see what's going on:
<img width="720" alt="image"
src="https://github.com/jesseduffield/lazygit/assets/1225667/6d84fc85-fadb-4f03-a973-868bc29d79f5">

Improve this by offsetting the new panel a bit down and to the right
from the one below it:
<img width="750" alt="image"
src="https://github.com/jesseduffield/lazygit/assets/1225667/c0c39f88-356b-4add-b335-4db2e54496ed">

Along the way we clean up the code a bit and fix a few minor issues; see
the individual commit messages for details.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-28 08:17:36 +02:00
Stefan Haller
4b6479b25f Stagger popup panels
When opening a popup panel on top of another one, offset the new one a little
bit down and to the right.
2024-06-28 08:14:07 +02:00
Stefan Haller
1ab1fb3599 Resize all open popup panels in layout, not just the topmost one
Probably not the most import feature in the world, but when resizing the
terminal window while multiple popup panels were open at the same time, we would
only resize the topmost one.

The main reason for changing this is because it makes the next commit easier to
implement.
2024-06-28 08:14:07 +02:00
Stefan Haller
1d502d3245 Remove return value from ResizeCurrentPopupPanel
It always returned nil, so there's no point in returning an error.
2024-06-28 08:14:07 +02:00
Stefan Haller
bb01648521 Remove redundant calls to resize editable panels at creating time
The only purpose of this was to scroll the editable text correctly (see
https://github.com/jesseduffield/lazygit/pull/2146); now that gocui takes care
of that, we no longer need to do this.
2024-06-28 08:14:07 +02:00
Stefan Haller
32cfe7a5c3 Bump gocui 2024-06-28 08:14:05 +02:00
Stefan Haller
b795d91fa8 Remove redundant resizeConfirmationPanel() call at panel creating time
We resize the panel in layout, so there's no need to do that after creation.
2024-06-27 10:18:12 +02:00
Stefan Haller
ccc620e5fc Remove duplicate function
ResizeConfirmationPanel and resizeConfirmationPanel were identical, get rid of
one of them.
2024-06-27 10:14:00 +02:00
Stefan Haller
34d7afc0e9 Remove unused functions 2024-06-27 10:14:00 +02:00
Stefan Haller
22dc7fece9 Have only one of commit message and description on the context stack at a time
This is how we do it for confirmation with suggestions too, so be consistent. It
will make things easier later in this branch if we only have one context per
"panel" on the stack, even if the panel consists of two views.

Concretely this means:
- only push the message context onto the stack when opening the panel (this
  requires making the description view visible manually; we do the same for
  suggestions)
- when switching between message and description, use ReplaceContext rather than
  PushContext
2024-06-27 10:14:00 +02:00
Stefan Haller
c3d1a79a89 Fix clicking outside of the commit description panel or suggestions panel
We forgot to handle the "suggestions" and "commitDescription" view names.

Instead of listing all the names of views that can appear in popups though,
let's use the context kind for this, which feels more robust.

This is a change in behavior: previously, clicking outside of the search or
filter prompt would close the prompt, now it no longer does (because search has
a persistent popup kind, but it wasn't listed in the list of view names before).
2024-06-27 09:36:50 +02:00
Stefan Haller
63a523c2fc Add command to paste commit message from clipboard (#3676)
- **PR Description**

Resolves #3672

Added an entry to the commit message menu called "Paste commit message from clipboard",
which splits the clipboard into subject and description and pastes both into their respective fields.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-27 08:36:52 +02:00
WaterLemons2k
d146d834c2 Add command to paste commit message from clipboard
Resolves #3672
2024-06-26 22:20:54 +02:00
Stefan Haller
68edfa20b4 Add function os.PasteFromClipboard
And a user config to override it with a custom command.
2024-06-26 22:20:54 +02:00
Stefan Haller
bfe9f233ac Convert TranslationSets to json (#3649)
- **PR Description**

This lays the ground for translating lazygit's texts using a translation
system such as Crowdin, Weblate, or Poeditor.

See #3070.
2024-06-23 14:51:03 +02:00
Stefan Haller
741d9a26b6 Change script to write only the English translation set to JSON
We will have to do this regularly in order to upload it to Crowdin (or Weblate
or whatever translation system we are going to use).

Unlike the non-English JSON files, the en.json file is not committed to the git
repo.
2024-06-23 14:47:50 +02:00
Stefan Haller
98e5fd70fb Remove non-English translation sets, read them from JSON instead 2024-06-23 14:47:50 +02:00
Stefan Haller
b34de70c99 Remove all empty strings
I guess I could have coded this into the export script, but I was too lazy and
just did it manually in VS Code, which was easy enough.
2024-06-23 14:47:50 +02:00
Stefan Haller
1e123e2124 Convert the non-English translation sets to JSON files
We write a hacky, one-off script to do that. We need this script only once, so
we don't bother polishing it much. We'll re-purpose it later in the branch to
convert the English translation set to JSON; that is an operation that we need
to do regularly in the future.
2024-06-23 14:47:50 +02:00
Stefan Haller
02aeb6101c Remove unused struct 2024-06-23 14:47:50 +02:00
Stefan Haller
2ccd9980e3 Fix wrong highlight in staging panel when entering file with only staged changes (#3667)
Reproduction recipe:
1. stage all changes in a file by pressing space on it in the files panel
2. enter the staged changes panel by pressing enter
3. unstage one of the changes

This makes the unstaged changes panel visible, but keeps the focus in
the staged changes panel. However, the highlight in the unstaged changes
view becomes visible, as if it were focused.

Fixes #3664
2024-06-23 14:46:36 +02:00
Stefan Haller
db0a1586d9 Highlight inactive selection in bold
An inactive selection is one where the view is part of the context stack, but
not the active view. For example, the files view when you enter the staging
panel, or any view when you open a panel.
2024-06-23 14:43:13 +02:00
Stefan Haller
4e441127f3 Clear highlight in HandleFocusLost
Remove the old mechanism of clearing the highlight in Layout.

This fixes a problem with a wrong highlight showing up in the staging panel when
entering a file with only staged changes.

Reproduction recipe:
1. stage all changes in a file by pressing space on it in the files panel
2. enter the staged changes panel by pressing enter
3. unstage one of the changes
This makes the unstaged changes panel visible, but keeps the focus in the staged
changes panel. However, the highlight in the unstaged changes view becomes
visible, as if it were focused.

To explain why this happens, you need to know how the selection highlighting of
a view is turned on or off. It is turned on when it gains the focus, i.e. when
ActivateFocus is called on it, which in turn happens when PushContext is called.
It is turned off in Layout when gocui sees that the current view is no longer
the same as last time, in which case it calls onViewFocusLost on the previous
current view.

This mechanism only works reliably when there is at most one PushContext call
per event handler. If there is more than one, then the first one gets its
highlight turned on, then the second one, but since gocui has never seen the
first one as the active view in Layout, it doesn't get the highlight turned off
again even though it should.

And this happens in the above scenario. When pressing enter on a file with only
staged changes, we first push the staging context (in
FilesController.EnterFile), and then later we push the stagingSecondary context
when we realize we only have staged changes. This leaves the highlight of the
staging context on.
2024-06-23 13:21:49 +02:00
Stefan Haller
cf40a5b077 Improve render performance (#3686)
- **PR Description**

Fix a performance regression that I introduced with v0.41: when entering
or leaving staging mode for a file, or when switching from a file that
has only unstaged changes to one that has both staged and unstaged
changes, there was a noticeable lag of about 500ms on my machine. With
the improvements in this PR we get this back down to about 20ms.
2024-06-23 13:13:58 +02:00
Stefan Haller
26132cf5bd Use utils.StringWidth to optimize rendering performance
runewidth.StringWidth is an expensive call, even if the input string is pure
ASCII. Improve this by providing a wrapper that short-circuits the call to len
if the input is ASCII.

Benchmark results show that for non-ASCII strings it makes no noticable
difference, but for ASCII strings it provides a more than 200x speedup.

BenchmarkStringWidthAsciiOriginal-10            718135       1637 ns/op
BenchmarkStringWidthAsciiOptimized-10        159197538          7.545 ns/op
BenchmarkStringWidthNonAsciiOriginal-10         486290       2391 ns/op
BenchmarkStringWidthNonAsciiOptimized-10        502286       2383 ns/op
2024-06-23 13:10:48 +02:00
Stefan Haller
a67eda39a5 Rerender fewer views when their width changes
In d5b4f7bb3e and 58a83b0862 we introduced a combined mechanism for rerendering
views when either their width changes (needed for the branches view which
truncates long branch names), or the screen mode (needed for those views that
display more information in half or full screen mode, e.g. the commits view).

This was a bad idea, because it unnecessarily rerenders too many views when just
their width changes, which causes a noticable lag. This is a problem, for
example, when selecting a file in the files panel that has only unstaged
changes, and then going to one that has both staged and unstaged changes; this
splits the main view, causing the side panels to become a bit narrower, and
rerendering all those views took almost 500ms on my machine. Another similar
example is entering or leaving staging mode.

Fix this by being more specific about which views need rerendering under what
conditions; this improves the time it takes to rerender in the above scenarios
from 450-500s down to about 20ms.

This reintroduces the code that was removed in 58a83b0862, but in a slightly
different way.
2024-06-23 13:10:48 +02:00
Stefan Haller
8e1464f720 Don't redraw remote branches view when its width changes
The rendering of remote branches is in no way dependent on the width of the view
(or the screen mode). Unlike in the local branches view, we don't truncate long
branch names here (because there's no more information after them).

This is an error introduced in d5b4f7bb3e.
2024-06-23 13:08:01 +02:00
Stefan Haller
a62a5089d6 Show current value in menus (#3628)
- **PR Description**

Some of our menus let you pick a value for some option (e.g. the sort
order menus for branches and commits). It's nice to see which one is the
current value when opening such a menu, so this PR implements that.

For menus that also have key bindings, the radio button goes between the
key and the label.

As an alternative, I considered selecting the current value when the
menu is opened; however, we currently have no menus where we select a
different entry than the first one, and it might be confusing for people
who are used to opening a menu and then pressing down-arrow a certain
number of times to get to a particular value.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-23 12:56:08 +02:00
Stefan Haller
4967e5136e Show radio buttons in the show log graph and commit sort order menus 2024-06-23 12:53:15 +02:00
Stefan Haller
68c966567c Show radio buttons in the sort order menu for branches 2024-06-23 12:53:15 +02:00
Stefan Haller
20a4aeab6e Support showing checkboxes or radio buttons in menus
For checkboxes it probably doesn't really make sense to use them yet, because
we'd have to find a way how you can toggle them without closing the dialog; but
we already provide rendering for them to lay the ground.

But radio buttons can be used already, because for those it is ok to close the
dialog when choosing a different option (as long as there is only one grounp of
radio buttons in the panel, that is).
2024-06-23 12:53:15 +02:00
Stefan Haller
a5620ebe3a Always show the "Discard unchanged changes" menu item (#3683)
Always show the "Discard unchanged changes" menu item in the Discard
menu, just strike it through if not applicable. This will hopefully help
with confusion about the meaning of "all" in the "Discard all changes"
entry; some people misunderstand this to mean all changes in the working
copy. Seeing the "Discard unstaged changes" item next to it hopefully
makes it clearer that "all" is meant in contrast to that.
2024-06-23 12:46:59 +02:00
Stefan Haller
1b245ef5f6 Always show the "Discard unchanged changes" menu item
Strike it through if not applicable. This will hopefully help with confusion
about the meaning of "all" in the "Discard all changes" entry; some people
misunderstand this to mean all changes in the working copy. Seeing the "Discard
unstaged changes" item next to it hopefully makes it clearer that "all" is meant
in contrast to that.
2024-06-23 12:43:34 +02:00
Stefan Haller
a08c86c182 Fix custom patch operations for added files (#3684)
- **PR Description**

Several custom patch commands on parts of an added file would fail with the
confusing error message "error: new file XXX depends on old contents".
These were dropping the custom patch from the original commit, moving the
patch to a new commit, moving it to a later commit, or moving it to the index.

We fix this by converting the patch header from an added file to a diff against
an empty file. We do this not just for the purpose of applying the patch, but
also for rendering it and copying it to the clip board. I'm not sure it matters
much in these cases, but it does feel more correct for a filtered patch to be
presented this way.

Fixes #3679.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-23 12:43:09 +02:00
Stefan Haller
4cd15a36e3 Fix custom patch operations on added files
Several custom patch commands on parts of an added file would fail with the
confusing error message "error: new file XXX depends on old contents". These
were dropping the custom patch from the original commit, moving the patch to a
new commit, moving it to a later commit, or moving it to the index.

We fix this by converting the patch header from an added file to a diff against
an empty file. We do this not just for the purpose of applying the patch, but
also for rendering it and copying it to the clip board. I'm not sure it matters
much in these cases, but it does feel more correct for a filtered patch to be
presented this way.
2024-06-23 12:40:31 +02:00
Stefan Haller
13a35408e6 Introduce options struct for RenderPatchForFile
We're going to add another argument in the next commit, and that's getting a bit
much, especially when most of the arguments are bool and you only see true and
false at the call sites without knowing what they mean.
2024-06-23 12:40:31 +02:00
Stefan Haller
1a76a7da09 Add test for moving a patch from an added file to an earlier commit
This currently works (albeit with a bit of manual work, as the user needs to
resolve conflicts), and we add this test just to make sure that we don't break
it with the following change.
2024-06-23 12:40:31 +02:00
Stefan Haller
8a16f24ecb Add test for moving a patch from a deleted file to a new commit
This currently works, we add it as a regression test to make sure we don't break
it. It is an interesting test because it turns the deletion of the file in the
moved-from commit into a modification.
2024-06-23 12:40:31 +02:00
Stefan Haller
e2b4d9cff3 Remove unneccesary test actions
Pressing enter in the patch building view does nothing.
2024-06-23 12:40:31 +02:00
Stefan Haller
b2c457366a Fix PTY layout problems (#3658)
- **PR Description**

This fixes two layout problems with pagers that draw a horizontal line
across the entire width of the view (e.g. delta):
- sometimes the width of that line was one character too long or too
short in the staged changes view
- when changing from a file or directory that has only staged or only
unstaged changes to one that has both, the length of the horizontal line
was totally off and only redraw correctly at the next refresh
2024-06-23 12:39:41 +02:00
Stefan Haller
8b8343b8a9 Run PTY tasks after layout so that they get the correct view size
This is important when using a pager that draws a horizontal line across the
entire width of the view; when changing from a file or directory that has only
unstaged (or only staged) changes to one that has both, the main view is split
in half, but the PTY task would be run on the view in its old state, so the
horizonal line would be too long and wrap around.
2024-06-23 12:36:40 +02:00
Stefan Haller
f98da780de Fix possible off-by-one error wrt PTY size
All PTYs were created with the size of the main view, on the assumption that
main and secondary always have the same size. That's not true though; in
horizontal split mode, the width of the two views can differ by one because of
rounding, and when using a pager that draws a horizontal line across the width
of the view, this is visible and looks very ugly.
2024-06-23 12:36:40 +02:00
Stefan Haller
c401f34530 Add prompt to the remote branch checkout menu (#3652)
- **PR Description**

As a followup to [this
discussion](https://github.com/jesseduffield/lazygit/pull/3388#issuecomment-2002308045),
this PR adds a way to add a prompt text to menus. It is shown above the
menu items, separated by a blank line. We use it to add a prompt to the
remote branch checkout menu.
2024-06-23 12:35:50 +02:00
Stefan Haller
ddd6323aa5 Add prompt to the remote branch checkout menu 2024-06-23 12:33:16 +02:00
Stefan Haller
7e92dbfd3d Add menu prompt
This makes it possible to add a prompt to a menu. It will be shown above the
menu items, separated from them by a blank line.
2024-06-23 12:33:16 +02:00
Stefan Haller
dbc21af3b1 Extract function wrapMessageToWidth
This steals even more code from `gocui.lineWrap`.

We'll make use of this in the next commit.
2024-06-23 12:33:15 +02:00
Stefan Haller
6f8244e3fe Fix duplicate keybinding suggestions in status bar after switching repos (#3660)
- **PR Description**

When switching to a repo that was open before, all keybinding
suggestions in the status bar would show twice.

Fixes #3612.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-23 12:31:42 +02:00
Stefan Haller
cf27fd827b Clear keybinding functions in resetHelpersAndControllers
When switching to a repo that was open before, the context tree is reused, so
before adding keybinding functions to those contexts again, we need to clear the
old ones.
2024-06-23 12:28:42 +02:00
Stefan Haller
a7c97400c6 Add a test demonstrating the bug
After switching to another repo and then back to the original one, all
keybinding suggestions in the status bar are shown twice.
2024-06-23 12:28:42 +02:00
Stefan Haller
5e9fe2be80 Fix truncation of branch names containing non-ASCII characters (#3685)
Fix truncating long branch names containing non-ASCII characters.
2024-06-23 12:28:05 +02:00
Stefan Haller
d406ec06af Fix truncation of long branch names containing non-ASCII characters 2024-06-23 12:25:28 +02:00
Stefan Haller
7ec784e2a0 Add test demonstrating wrong truncation of branch names containing non-ASCII characters 2024-06-23 12:25:28 +02:00
Stefan Haller
93af0016f7 Use actual ellipsis character instead of ... to truncate strings
Space is scarce in lazygit's UI, and using ... wastes a lot of it.
2024-06-23 12:25:28 +02:00
Stefan Haller
a171ec4294 Reduce memory consumption when loading large number of commits (#3687)
(Github decided to auto-close #2533, and I don't see any way to reopen
it, so opening a new one here. Please see there for discussion and
review.)

When pressing `>` in the commits panel to trigger loading all the
remaining commits past the initial 300, memory consumption is a pretty
big problem for larger repositories.

The two main causes seem to be
1. the cell memory from rendering the entire list of commits into the
gocui view
2. the pipe sets when git.log.showGraph is on

This PR addresses only the first of these problems, by not rendering the
entire view, but only the visible portion of it. Since we already
re-render the visible portion of the view on every layout call, this was
relatively easy to do.

Below are some measurements for our repository at work (261.985
commits):

|               | master | this PR |
| ------------- | ------ | ------- |
| without Graph | 855 MB | 360 MB  |
| with Graph    | 3.1 GB | 770 MB  |

And for the linux kernel repo (1.170.387 commits):

|               | master                                    | this PR |
| ------------- | ----------------------------------------- | ------- |
| without Graph | 5.8 GB                                    | 1.2G    |
| with Graph    | Killed by the OS after it reached 86.9 GB | 39.9 GB |

The measurements were taken after scrolling all the way down in the list
of commits. They have to be taken with a grain of salt, as memory
consumption fluctuates quite a bit in ways that I find hard to make
sense of.

As you can see, there's more work to do to reduce the memory usage for
the graph, but for our repo at work this PR makes it usable already,
which it wasn't really before.
2024-06-23 12:09:16 +02:00
Stefan Haller
deee5fa957 Render the view when scrolling with the wheel 2024-06-23 11:54:21 +02:00
Stefan Haller
44160ef844 Only render visible portion of the screen for commits view 2024-06-23 11:54:21 +02:00
Stefan Haller
dd2bffc278 Simplify ListContextTrait.FocusLine
When refreshViewportOnChange is true, we would refresh the viewport once at the
end of FocusLine, and then we would check at the end of AfterLayout if the
origin has changed, and refresh again if so. That's unnecessarily complicated,
let's just unconditionally refresh at the end of AfterLayout only.
2024-06-23 11:54:21 +02:00
Stefan Haller
9eb9b369ff Bump gocui 2024-06-23 11:54:01 +02:00
Stefan Haller
fafa4280f5 Log memory usage every 10s 2024-06-23 11:48:40 +02:00
Stefan Haller
5a5cd849d1 Search the model instead of the view in the commits panel (#3642)
- **PR Description**

This makes it possible to search the model data instead of the view when
pressing `/`, and uses this for the commits view.

This is mainly a preparation for #2533 which requires it, but it is also
useful on its own, because it makes it possible to search for full
commit hashes. It will highlight the abbreviated hash in that case.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-23 11:48:00 +02:00
Stefan Haller
6a6316cfb6 Use model searching in commits (and sub-commits) view 2024-06-23 11:43:12 +02:00
Stefan Haller
779e6f95a3 Assert that the search status view is visible
Just to be really sure that it not only contains the expected status text, but
also actually shows it.
2024-06-23 11:43:12 +02:00
Stefan Haller
44ad36bb39 Add type assertions for all searchable contexts
We want to add an additional method to ISearchableContext later in this branch,
and this will make sure that we don't forget to implement it in any concrete
context.
2024-06-23 11:43:12 +02:00
Stefan Haller
08a8b067a5 Cleanup: remove outdated comment
We do show the graph in the left/right view, as of b7673577a2.
2024-06-23 11:43:12 +02:00
Stefan Haller
15b25b5afb Fix searching in the divergence (left/right) view
Searching in the "Divergence from upstream" view would select the wrong lines.
The OnSearchSelect function gets passed a view index, and uses it to select a
model line. In most views these are the same, but not in the divergence view
(because of the Remote/Local section headers).
2024-06-23 11:43:12 +02:00
Stefan Haller
27ad75de16 Cleanup: reduce some code duplication
ListContextTrait.OnSearchSelect was introduced in 138be04e65, but it was never
called. I can only guess that a planned refactoring wasn't finished here.
2024-06-23 11:43:12 +02:00
Stefan Haller
3af545daf7 Add user config gui.commitAuthorFormat (#3625)
- **PR Description**
Adds configuration option defining whether to show full author names or
their shortened form in the commit graph.

Closes [#3624](https://github.com/jesseduffield/lazygit/issues/3624).

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-15 16:17:48 +02:00
anikiforov
57f9493770 Add user config gui.commitAuthorFormat 2024-06-15 10:39:50 +04:00
Stefan Haller
629b7ba1b8 Fix reporting of unexpected selections in integration tests (#3662)
Expected and actual selection were swapped in the error message.
2024-06-14 13:36:45 +02:00
Stefan Haller
43e04febee Fix reporting of unexpected selections in integration tests
Expected and actual selection were swapped in the error message.
2024-06-14 13:26:14 +02:00
Stefan Haller
c08a5fe4e7 Show "exec" todos in the list of rebase todos (#3654)
- **PR Description**

It is sometimes useful to perform a `git rebase -x "make test"
origin/main` in order to verify that all commits of the current branch
are green. However, if the rebase stops in the middle because one of
those tests fail, it's easy to accidentally press `m <enter>` in lazygit
after amending some fix, not realizing that the rest of the tests will
now run invisibly inside lazygit.

This PR makes two changes to improve this situation:
- It shows exec todos in the commits panel: 
<img width="482" alt="image"
src="https://github.com/jesseduffield/lazygit/assets/1225667/608b24e8-9f3d-4a5f-9bb5-e16268c86e83">
- when continuing a rebase and there are exec todos, it suspends itself
to the background so that you can see the output of the test runs in the
terminal.

We can improve this further in the future; for example, it would often
be useful to be able to delete some of the exec commands, this is not
currently possible. But it's still better than before.
2024-06-12 14:38:44 +02:00
Stefan Haller
92dd80c3e3 Suspend lazygit when continuing a rebase with exec todos
It's likely that the exec todos are some kind of lengthy build task whose output
the user will want to see in the terminal.
2024-06-12 12:45:00 +02:00
Stefan Haller
899e25b208 Show "exec" todos in the list of rebase todos
Unfortunately it isn't possible to delete them. This would often be useful, but
our todo rewriting mechanisms rely on being able to find todos by some
identifier (hash for pick, ref for update-ref), and exec todos don't have a
unique identifier.
2024-06-12 12:44:33 +02:00
Stefan Haller
906d21f3ce Improve "Find base commit for fixup" command when there are changes for master commits (#3645)
- **PR Description**

If exactly one candidate from inside the current branch is found, we
return that one even if there are also hunks belonging to master
commits; we disregard those in this case.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-10 12:03:22 +02:00
Stefan Haller
7780f1264a Disregard master commits when finding base commit for fixup
If exactly one candidate from inside the current branch is found, we return that
one even if there are also hunks belonging to master commits; we disregard those
in this case.
2024-06-10 12:00:24 +02:00
Stefan Haller
f3718ddfb0 Don't reference Model().Commits multiple times
Copy the slice into a variable and use that throughout the whole operation; this
makes us a little more robust against the model refreshing concurrently.
2024-06-10 12:00:24 +02:00
Stefan Haller
f9ba2dac9d Add test demonstrating the desired behavior
It has two modified hunks, one for a master commit and one for a branch commit.
Currently we get an error mentioning those two commits, but we would like to
silently select the branch commit.
2024-06-10 12:00:24 +02:00
Stefan Haller
6cb2ac6fcc Support range select for amending commit attributes (#3587)
- **PR Description**

This PR makes it possible for users to select a range of commits from
the commits view to amend their attributes (set/reset author, add
co-author), the same way it's already possible to do for a single
commit.

It closes #3273. 

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-07 23:28:54 +02:00
AzraelSec
6b9cf72e79 feat: support range selection for commit attributes amend 2024-06-07 23:09:52 +02:00
Stefan Haller
92f13fc56e Fix secondary window resize (#3637)
- **PR Description**
This PR fixes the behavior the staging secondary panel (staged lines)
currently has in relation to changing its view mode. In particular,
these changes allows the users to expand the secondary panels the same
way all the others do.

This closes #3629.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-07 22:54:25 +02:00
AzraelSec
06496ccd17 feat: let the staging secondary panel change view mode 2024-06-07 22:51:16 +02:00
AzraelSec
03a075c223 fix: typo in IsVisible assertion string 2024-06-07 22:51:16 +02:00
Stefan Haller
eef70dbdf6 Include demos when running integration tests on CI (#3640)
- **PR Description**

This includes the demos when using `make integration-test-all`, and
speeds them up a little bit when run in this way by disabling Wait calls
when running in headless mode.

This will guard against demos breaking when we make behavior changes, as
happened several times in the past (most recently in #3636.
2024-06-05 15:00:25 +02:00
Stefan Haller
f5329440fc Include demos when running integration tests with go test 2024-06-05 14:44:02 +02:00
Stefan Haller
4f6d3fb592 Don't wait in integration tests when running in headless mode
There's no point in spending time waiting in this case, as nobody can see it.
2024-06-05 14:44:02 +02:00
Stefan Haller
187a2f0cc2 Update rebase_onto demo test to match new the rebase menu title (#3636)
- **PR Description**

This PR updates the `rebase_onto` demo integration test, which is
currently failing on master due to [this
change](https://github.com/jesseduffield/lazygit/pull/3615/files#diff-3eb5426752dae525c92f1ecce2f7215d15bfdec91bd459e08f78574876583910R1259).
Our CI does not throw the issue, but it's still annoying to cope with it
for local development.

@jesseduffield @stefanhaller, I'm unaware of a particular workflow for
updating demo tests, but let me know if this needs some tweaks.

- **Please check if the PR fulfills these requirements**

* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
* [ ] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs have been updated if necessary
* [ ] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-06-05 14:43:44 +02:00
AzraelSec
f6457c4def fix: update rebase_onto demo test to match new rebase menu title 2024-06-05 01:38:08 +02:00
Stefan Haller
66a04e4273 Make profiling easier (#3634)
Add a `-profile` command line flag, and some documentation about how to
collect and view profile data.
2024-06-04 15:41:25 +02:00
Stefan Haller
ffedd84e92 Add some developer documentation about profiling 2024-06-04 15:37:59 +02:00
Stefan Haller
ab0b0da850 Add -profile command line flag
When on, start a built-in web server that takes requests on port 6060 to gather
profiling data.
2024-06-03 19:07:24 +02:00
Stefan Haller
b85687797d Add command to rebase onto base branch (#3615)
- **PR Description**

In the rebase menu, add a command "Rebase onto base branch". This makes
it more convenient to rebase onto master (or main), because
- you don't need to bring your local version of the base branch up to
date first
- you don't have to remember which of your main branches (e.g. "main",
"devel", or "1.0-hotfixes") your current branch is based on.

This is sitting on top of #3614.

Closes #3546.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-03 14:09:24 +02:00
Stefan Haller
a8921a13cb Add command "Rebase onto base branch" to rebase menu 2024-06-03 14:06:11 +02:00
Stefan Haller
837f7456ab Remove target branch from title of rebase menu
Put it into the individual menu items instead.

Again, this is necessary because we are going to add another entry to the menu
that is independent of the selected branch.
2024-06-03 14:06:11 +02:00
Stefan Haller
ddf5e24499 Always show rebase menu, even when rebasing is not possible
Instead, disable the individual entries in the menu.

This is necessary because we are going to add another entry to the menu that is
independent of the selected branch.
2024-06-03 14:06:11 +02:00
Stefan Haller
ca6d88080c Make "Rebase" show up with "..." in the keybindings menu 2024-06-03 14:06:11 +02:00
Stefan Haller
1b7ded6df3 Fix typo 2024-06-03 14:06:11 +02:00
Stefan Haller
36a4696573 Add command to show divergence from base branch as a left-right log (#3614)
- **PR Description**

Add a command similar to the existing "Show divergence from upstream",
but for the base branch instead. Useful to see what you would rebase
onto if you were to rebase onto the base branch now.

It could be considered somewhat questionable that we display both the
Remote and Local sections of the log here; the Local section isn't
really interesting because it's always identical to what you see in the
Commits view. I chose to still show it since it makes the divergence
view look more familiar, and I think overall it makes it clearer what
you're looking at.

This is sitting on top of #3613 and uses some of the code that was added
there.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-03 14:05:39 +02:00
Stefan Haller
343db7b3f1 Add command "View divergence from base branch" 2024-06-03 14:02:07 +02:00
Stefan Haller
4ac77f4575 Divergence from base branch display (#3613)
- **PR Description**

Add a new config option `showDivergenceFromBaseBranch`; if not "none",
it indicates in the branches view for each branch if it has fallen
behind its base branch, and optionally by how much. If set to
"onlyArrow", it will append `↓` after the branch status; if set to
"arrowAndNumber", it appends `↓17`, where the count indicates how many
commits it is behind the base branch. These are colored in blue, and go
after the existing yellow `↓3↑7` indication of divergence from the
upstream.

The option is off by default, since we are afraid that people may find
this too noisy. We may reconsider this choice in the future if the
response is positive.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-03 14:01:43 +02:00
Stefan Haller
373b1970ca Show divergence from base branch in branches list 2024-06-03 13:59:43 +02:00
Stefan Haller
5b613f5bc7 Remove ColoredBranchStatus and branchStatusColor
Previously the entire status was colored in a single color, so the API made
sense. This is going to change in the next commit, so now we must include the
color in the string returned from BranchStatus(), which means that callers who
need to do hit detection or measure the length need to decolorize it.

While we're at it, switch the order of ↑3↓7 to ↓7↑3. For some reason that I
can't really explain I find it more logical this way. The software out there is
pretty undecided about it, it seems: VS Code puts ↓7 first, and so does the
shell prompt that comes with git; git status and git branch -v put "ahead" first
though. Shrug.
2024-06-03 13:02:46 +02:00
Stefan Haller
8c385731f7 Add GetBaseBranch function 2024-06-03 13:02:46 +02:00
Stefan Haller
e79b4259e4 Make GetMergeBase a method of ExistingMainBranches 2024-06-03 13:02:46 +02:00
Stefan Haller
f4d922bc80 Factor out CommitLoader.mainBranches into its own class, and store it in Model 2024-06-03 13:02:46 +02:00
Stefan Haller
b2011dca35 Remove the cache invalidation logic from getMergeBase
It is a valid case for a branch to share no history with any of the main
branches, in which case git merge-base returns an error (and an empty string).
Since we can't distinguish this from one of the main branches having been
deleted, we shouldn't invalidate the cache in that case.
2024-06-03 13:02:46 +02:00
Stefan Haller
19d0048cc4 More explicit test of status panel content
Use Equals instead of Contains for asserting the status view content. This
solves the problem that we might assert Contains("↓2 repo"), but what it really
shows is "↑1↓2 repo", and the test still succeeds. At best this is confusing.

Also, this way we don't have to use the awkward DoesNotContain to check that it
really doesn't show a checkmark.

To do this, we need to fix two whitespace problems:
- there was always a space at the end for no reason. Simply remove it. It was
  added in efb51eee96, but from looking at that diff it seems it was added
  accidentally.
- there was a space at the beginning if the branch status was empty. This is
  actually a cosmetic problem, for branches without a status the text was
  indented by once space. Change this so that the space is added conditionally.
  It's a bit awkward that we have to use Decolorise here, but this will go away
  again later in this branch.
2024-06-03 13:02:46 +02:00
Stefan Haller
7c51ec21bf (#3618) Fix pushing a branch to remote with a different name causing error (#3630)
- **PR Description**

This fixes an error where, given the user wants push a branch to a
remote branch with a different name, the following error would be
presented:

```
Error
error: src refspec <desired remote branch name> does not match any
error: failed to push some refs to <remote .git URI>
```


- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-02 09:05:20 +02:00
Jordan
39ea5d9ab1 Add HEAD: when referencing upstream branch
Update unit tests
2024-06-02 09:02:33 +02:00
Stefan Haller
205357a44f Improve the "Find base commit for fixup" command (#3602)
- **PR Description**

Improve the `ctrl-f` command so that it also works when there are no
hunks with deleted lines in the diff. This is very useful, for example,
when a fixup commit adds a comment to a function that was added in the
PR.

Since the exact behavior of the command is getting very complex and hard
to understand, I added a design document that describes what it does,
and also how it differs from git-absorb which does a very similar thing.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-01 19:56:50 +02:00
Stefan Haller
c9c556beba Update user doc 2024-06-01 08:31:18 +02:00
Stefan Haller
b82c72b63d Add design document for "Find base commit for fixup"
This document explains why we made certain decisions about the behavior of the
command. This is too detailed for users, but could be useful in the future if we
want to discuss further improvements.
2024-06-01 08:31:18 +02:00
Stefan Haller
dbdabb34f3 Make "Find base commit for fixup" work with hunks with only added lines
To understand what this does and why, read the design document that I'm about to
add in the next commit.
2024-06-01 08:31:18 +02:00
Stefan Haller
c1a65546ad Extract a function findCommit
It's not much code, but it turns three lines of code into one, and since we need
to do this a few more times in the next commit, it's worth it.
2024-06-01 08:31:18 +02:00
Stefan Haller
2c9bca8b57 Return errors from blameDeletedLines instead of just logging them
I guess when I originally wrote this code I just didn't know how to do that...
2024-06-01 08:31:18 +02:00
Stefan Haller
880528b2e4 Also return hunks with only added lines from parseDiff
We aren't using them, yet, except for deciding whether to show the warning about
hunks with only added lines.

Add a bit of test coverage for parseDiff while we're at it.
2024-06-01 08:31:18 +02:00
Stefan Haller
e1b4d625c7 Make parseDiff a non-member function so that we can test it more easily 2024-06-01 08:31:18 +02:00
Stefan Haller
a957c5542f Rename deletedLineInfo to hunk
We'll use it with a more general meaning later in this branch.
2024-06-01 08:31:18 +02:00
Stefan Haller
f085d10c46 Add user config gui.expandedSidePanelWeight (#3623)
- **PR Description**

Add a user config `gui.expandedSidePanelWeight` which lets you change
the default weight of 2 that gets assigned to the expanded side panel if
`expandFocusedSidePanel` is true.

Closes #3621.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-06-01 08:25:30 +02:00
Stefan Haller
1269938ee6 Add user config expandedSidePanelWeight 2024-06-01 08:21:23 +02:00
Stefan Haller
557dfc5a6d Add test for ExpandFocusedSidePanel config 2024-06-01 08:16:06 +02:00
Stefan Haller
c5baa5da3a Fix pushing to branch when upstream not stored locally (#3619)
- **PR Description**

This fixes three different problems; two were recent regressions, one
has never worked.

1. For branches whose remote is not stored locally, don't ask to force
push right away. Try a normal push first. This broke with #3528.
2. For branches whose remote is not stored locally (but only for those),
if the server rejects the update, ask to force push. This broke with
#3387.
3. When force-pushing a branch whose remote is not stored locally, use
`--force` instead of `--force-with-lease`; otherwise the push would
always fail with a "stale info" error. This has never worked.

Fixes #3588.
2024-06-01 08:15:35 +02:00
Stefan Haller
3284d69886 Add integration test for pushing when the remote is not stored locally
The test needs all three fixes from this branch to succeed.
2024-06-01 08:12:45 +02:00
Stefan Haller
116c18e957 Use --force instead of --force-with-lease when remote is not stored locally
--force-with-lease simply doesn't work in this case, it will always return a
"stale info" error.
2024-06-01 08:12:45 +02:00
Stefan Haller
e93617b1de Rename Force to ForceWithLease
This describes better what it is, and we're going to add the regular --force in
the next commit.

No change in behavior in this commit.
2024-06-01 08:12:45 +02:00
Stefan Haller
aac2535104 Ask to force push if server rejected the update, if remote not stored locally
This broke with 81b497d186 (#3387). In that PR I claimed that we never want to
ask for force-pushing if the server rejected the update, on the assumption that
this can only happen because the remote tracking branch is not up to date, and
users should just fetch in this case. However, I didn't realize it's even
possible to have a branch whose upstream branch is not stored locally; in this
case we can't tell ahead of time whether a force push is going to be necessary,
so we _have_ to rely on the server response to find out. But we only want to do
that in this specific case, so this is not quite an exact revert of 81b497d186.
2024-06-01 08:12:45 +02:00
Stefan Haller
993d66a8ff Don't force-push if the remote branch is not stored locally
This broke with #3528.

If the remote branch is not stored locally, we only see question marks in the
branch status. In this case we can't tell whether we need to force-push, so it's
best to assume that we don't, and see if the server rejects the push, and react
to that by asking to force push. This second part is also broken right now,
we'll fix this in the next commit.
2024-06-01 08:12:45 +02:00
Stefan Haller
ab797fe986 Fix boolean config keys not appearing in the generated Config.md (#3622)
- **PR Description**

All boolean config keys were missing from the generated Config.md.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-05-31 21:29:17 +02:00
Stefan Haller
4bfda1a78c Fix boolean config keys not appearing in the generated Config.md
The reason why they didn't appear is that they didn't get a default value in the
generated schema; this commit fixes that.
2024-05-31 20:07:58 +02:00
Stefan Haller
91cb33db85 Remove an outdated comment
Lazygit doesn't touch this, the comment is just not true.

I wonder if we need the config at all, actually; I'd be in favor of removing it.
2024-05-31 20:05:44 +02:00
Stefan Haller
7492521829 Improve branch and reflog loading when sorting branches by date (#3609)
- **PR Description**

When branches are sorted by recency we have this logic that first loads the
branches so that they can be rendered quickly; in parallel, it starts loading
the reflog in the background, and when that's done, it loads the branches again
so that they get their recency values. This means that branches are loaded twice
at startup.

We don't need this logic when branches are not sorted by recency, so we can
simply load branches and reflog in parallel like everything else.

This shouldn't change any user observable behavior, it just avoids doing
unnecessary work at startup.
2024-05-29 13:50:02 +02:00
Stefan Haller
35af886f55 Refresh branches and reflog independently when sorting branches by date
When branches are sorted by recency we have this logic that first loads the
branches so that they can be rendered quickly; in parallel, it starts loading
the reflog in the background, and when that's done, it loads the branches again
so that they get their recency values. This means that branches are loaded twice
at startup.

We don't need this logic when branches are not sorted by recency, so we can
simply load branches and reflog in parallel like everything else.

This shouldn't change any user observable behavior, it just avoids doing
unnecessary work at startup.
2024-05-29 13:46:53 +02:00
Stefan Haller
f45ecbc19a Fix out-of-date comment
The behavior described in the comment is no longer what we do, it was changed in
ae66f720f5; we now always reuse the state.
2024-05-29 13:46:53 +02:00
Stefan Haller
51eb6d7c7d Pin golangci version to 1.58 (#3611)
Pin golangci version to 1.58.

It is annoying when CI builds suddenly start to fail because the linter
was updated and finds new things to complain about.

Updating the linter and fixing the code accordingly should be a
dedicated activity.
2024-05-29 13:46:19 +02:00
Stefan Haller
38ec4e646f Pin golangci version to 1.58
It is annoying when CI builds suddenly start to fail because the linter was
updated and finds new things to complain about.

Updating the linter and fixing the code accordingly should be a dedicated
activity.
2024-05-29 13:42:11 +02:00
Stefan Haller
ef1daf314a Fix tooltip for fixup command (#3601)
### PR Description

- Fix calculation of tooltip height; for tooltips that are just one or
two characters longer than the available width, the last word would be
cut off. On my screen this happened for the tooltip for the fixup
command.
- Fix tooltip of fixup command.

Fixes #3600.
2024-05-27 07:53:16 +02:00
Stefan Haller
ab643749e8 Fix tooltip of fixup command 2024-05-26 19:30:38 +02:00
Stefan Haller
2d0c96466a Fix calculation of tooltip height
For tooltips that are just one or two characters longer than the available
width, the last word would be cut off. On my screen this happened for the
tooltip for the fixup command.
2024-05-26 19:26:27 +02:00
Jesse Duffield
f6ace8380f Update readme 2024-05-26 14:35:38 +10:00
Stefan Haller
b4aa68d0b3 Delete the TODO comment about enabling goconst in the future from .golangci.yml (#3596)
## Context:

https://github.com/jesseduffield/lazygit/pull/3592#issuecomment-2129535231
2024-05-24 15:40:41 +02:00
kyu08
4a97fb80f3 Delete the TODO comment about enabling goconst in the future from .golangci.yml 2024-05-24 22:29:44 +09:00
Stefan Haller
8f1a04d095 Add lint to make target (#3593)
I feel this is necessary when I worked on
https://github.com/jesseduffield/lazygit/pull/3592.
2024-05-24 12:57:47 +02:00
kyu08
e5f4de8d34 Add lint to make target 2024-05-24 12:54:45 +02:00
Stefan Haller
ce3fe37787 Add copyloopvar to enabled linters (#3586)
Resolves #3573 
It looks like the update to go1.22 is done by
https://github.com/jesseduffield/lazygit/pull/3574. So it should be OK
to close #3573 when this PR is merged.

## PR Description
Add [`copyloopvar`](https://github.com/karamaru-alpha/copyloopvar) to
enabled linters to detect unnecessary assignments in `for` loops.

## Context
@jesseduffield and I were talking about necessity of this linter here.
https://github.com/jesseduffield/lazygit/issues/3573#issuecomment-2123732829
2024-05-24 11:03:01 +02:00
kyu08
6cf6941a77 Add copyloopvar to enabled linters 2024-05-22 21:57:34 +09:00
Stefan Haller
0fbed71f15 Add property outputTitle to CustomCommand (#3579)
- **PR Description**

Add property `outputTitle` to CustomCommand. It can optionally be used
to set the title of the panel that shows the output of a command (when
`showOutput` is true). If left unset, the command string is used as the
title.

Closes #3576.
2024-05-20 21:05:58 +02:00
Stefan Haller
22a38c9f50 Add property outputTitle to CustomCommand
It can optionally be used to set the title of the panel that shows the output of
a command (when showOutput is true). If left unset, the command string is used
as the title.
2024-05-20 21:02:49 +02:00
Stefan Haller
6343fb57d6 Focus on local commits view after moving code into new commit (#3577)
- **PR Description**

This PR forces lazygit to focus to the `Commits` view after moving a
custom (partial) patch in a new commit.

This closes #3200.
2024-05-20 20:58:31 +02:00
AzraelSec
5af0ea85fc feat: focus on local commits view after moving code into new commit 2024-05-20 19:12:38 +02:00
Stefan Haller
a4e9181a6b Remove hint about Config.md from PR template (#3578)
It used to be a common thing to have to update `Config.md` in a PR (and
we often forgot despite the template). As of #3565 this is no longer
necessary, so remove this from the template.

Updating docs in general is still a good thing to think about, so we
leave this in.
2024-05-20 07:01:30 +02:00
Stefan Haller
f5d57f7cc7 Remove hint about Config.md from PR template
It used to be a common thing to have to update Config.md in a PR (and we often
forgot despite the template). As of #3565 this is no longer necessary, so remove
this from the template.

Updating docs in general is still a good thing to think about, so we leave this
in.
2024-05-20 06:59:22 +02:00
Jesse Duffield
fc7b10f557 Update README.md
Updated the company sponsors with the new name of my company
2024-05-20 12:21:07 +10:00
Stefan Haller
b75c177c31 Add default lazygit config generation in Config.md from JSON schema (#3565)
- **PR Description**
This uses the JSON schema generated in
https://github.com/jesseduffield/lazygit/pull/3039 to generate and
replace the default lazygit config in Config.md when running `go
generate ./...`

Relevant issue: https://github.com/jesseduffield/lazygit/issues/3441

The generated config contains all the entries that have default values
set in `user_config.go`
2024-05-19 14:11:23 +02:00
Karim Khaleel
9b152d7619 Make Keybindings definition in UserConfig struct last
This makes the generated default config in Config.md match the original
order.
2024-05-19 14:08:27 +02:00
Karim Khaleel
b98ae1c773 Add default lazygit config generator for Config.md from JSON schema 2024-05-19 14:08:05 +02:00
Karim Khaleel
7d787afb2c Set default value for WindowSize config to pass validation 2024-05-19 14:07:51 +02:00
Karim Khaleel
af842e40d4 Remove unnecesary schema validations in user config 2024-05-19 14:07:40 +02:00
Stefan Haller
6fcb7eb8bb Correctly request force-pushing in a triangular workflow (#3528)
- **PR Description**

Some people push to a different branch (or even remote) than they pull
from. One example is described in #3437. Our logic of when to request a
force push is not appropriate for these workflows: we check the
configured upstream branch for divergence, but that's the one you pull
from. We should instead check the push-to branch for divergence.

Fixes #3437.
2024-05-19 10:00:32 +02:00
Stefan Haller
c5cf1b2428 Correctly request force-pushing in triangular workflows
To determine whether we need to ask for force pushing, we need to query the push
branch rather than the upstream branch, in case they are not the same.
2024-05-19 09:44:38 +02:00
Stefan Haller
d890c68cd0 Add ahead/behind information for @{push}
In a triangular workflow the branch that you're pulling from is not the same as
the one that you are pushing to. For example, some people find it useful to set
the upstream branch to origin/master so that pulling effectively rebases onto
master, and set the push.default git config to "current" so that "feature"
pushes to origin/feature.

Another example is a fork-based workflow where "feature" has upstream set to
upstream/main, and the repo has remote.pushDefault set to "origin", so pushing
on "feature" pushes to origin/feature.

This commit adds new fields to models.Branch that store the ahead/behind
information against the push branch; for the "normal" workflow where you pull
and push from/to the upstream branch, AheadForPush/BehindForPush will be the
same as AheadForPull/BehindForPull.
2024-05-19 09:44:38 +02:00
Stefan Haller
0aba686f97 Rename Pushables/Pullables to AheadForPull/BehindForPull
In preparation for adding AheadForPush/BehindForPush in the next commit.
2024-05-19 09:44:38 +02:00
Stefan Haller
b91b40ba4d Add test demonstrating the problem with force-pushing in a triangular workflow
Our code doesn't realize that we need to prompt the user to force push, when the
branch is up-to-date with its upstream but not with the branch that we're
pushing to.
2024-05-19 09:44:38 +02:00
Stefan Haller
c4927e21c5 Rename PushBranch to PushBranchAndSetUpstream
It is unexpected that a function called PushBranch also sets the upstream
branch; also, we want to add a PushBranch function in the next commit that
doesn't.
2024-05-19 09:44:38 +02:00
Stefan Haller
6afcc5bda8 Create shims for all model classes in SessionStateLoader
This guards against accidentally renaming a model field and thereby breaking
user's custom commands. With this change we'll get a build failure when we do
that.
2024-05-19 09:44:38 +02:00
Jesse Duffield
9fc7a5177b Bump go version to 1.22 (#3574)
- **PR Description**

Bumps go from 1.21 to 1.22, and removes newly redundant loop variable
re-declarations now that in 1.22 the variables are automatically
redeclared in each iteration for us.

- **Please check if the PR fulfills these requirements**

* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
* [ ] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [ ] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-05-19 17:22:42 +10:00
Jesse Duffield
fdff2dec79 Remove redundant variable dedeclarations
In go 1.22, loop variables are redeclared with each iteration of the
loop, rather than simple updated on each iteration. This means that we
no longer need to manually redeclare variables when they're closed over
by a function.
2024-05-19 16:38:21 +10:00
Jesse Duffield
9124d8dbaa Bump go version to 1.22 2024-05-19 16:38:21 +10:00
Jesse Duffield
4404aacdee README.md: Update Sponsors (#3503)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action
2024-05-19 16:15:32 +10:00
github-actions[bot]
8f5ce14dc0 README.md: Update Sponsors 2024-05-19 05:09:25 +00:00
Stefan Haller
866e80529b Delete and edit custom commands history items (#3534)
- **PR Description**

Allow deleting and editing custom command history items. Deleting is
done by hitting `d` on a suggestion; editing is done by hitting `e`,
which fills the selected item into the command prompt for further
editing.

Closes #2528.
2024-05-19 07:09:13 +02:00
Stefan Haller
010b0ae923 Show delete/edit keybindings in suggestions subtitle if available 2024-05-19 07:06:18 +02:00
Stefan Haller
a7041cf492 Allow editing a custom command from the suggestions list by pressing 'e'
For custom commands it is useful to select an earlier command and have it copied
to the prompt for further editing. This can be done by hitting 'e' now.

For other types of suggestion panels we don't enable this behavior, as you can't
create arbitrary new items there that don't already exist as a suggestion.
2024-05-19 07:06:18 +02:00
Stefan Haller
da3e0f7147 Support deleting items from the custom commands history
In the custom commands panel you can now tab to the suggestions and hit 'd' to
delete items from there. Useful if you mistyped a command and don't want it to
appear in your history any more.
2024-05-19 07:06:18 +02:00
Jesse Duffield
269d01233f Improve nvim-remote mode (#3508)
- If _not_ inside a neovim session, treat as a normal nvim invocation
and suspend lazygit.

- If inside a neovim session:
  - Do not try to suspend lazygit.
  - Send `q` keystroke to neovim session to quit lazygit.
  - Send filename/line/etc. to neovim session.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-05-19 14:42:47 +10:00
Charlie Moog
615ff09afa improve nvim-remote mode
- If _not_ inside a neovim session, treat as
  a normal nvim session and suspend lazygit.

- If inside a neovim session:
  - Do not try to suspend lazygit.
  - Send `q` keystroke to neovim session to quit lazygit.
  - Send filename/line/etc. to neovim session.
2024-05-19 14:39:02 +10:00
Jesse Duffield
772388294a Attempt #2 at preventing codacy coverage step from running on forks (#3572)
- **PR Description**

First attempt didn't work. Let's try this one, based on
https://github.com/orgs/community/discussions/25217

<img width="910" alt="image"
src="https://github.com/jesseduffield/lazygit/assets/8456633/fa2e82b0-db34-486b-8bea-ec3807d10a33">

- **Please check if the PR fulfills these requirements**

* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
* [ ] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [ ] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-05-19 14:38:48 +10:00
Jesse Duffield
e09b38153c Attempt #2 at preventing codacy coverage step from running on forks 2024-05-19 14:36:25 +10:00
Jesse Duffield
cd62c719f1 Only run code coverage report on non-fork branches (#3571)
- **PR Description**

Codacy's coverage report feature requires the use of a secret key, which
is only available on the main repo and is not available on forks. So,
the step has been always failing on any forks. This commit ensures that
we only run it on non-forks.

This greatly diminishes the value of the coverage reports. I've talked
to one of the Codacy people and advised that they should just have an
API key for coverage reports which is not a secret, like what bugsnag
does.

I've disabled the gate in codacy meaning if the coverage ever drops
beneath some percentage, the job won't fail. It wouldn't make sense to
fail the job if some other PR from a fork was responsible for reducing
the coverage percentage beneath some threshold.

- **Please check if the PR fulfills these requirements**

* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
* [ ] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [ ] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-05-19 14:10:16 +10:00
Jesse Duffield
36e7524146 Only run code coverage report on non-fork branches
Codacy's coverage report feature requires the use of a secret key, which
is only available on the main repo and is not available on forks. So,
the step has been always failing on any forks. This commit ensures that
we only run it on non-forks.

This greatly diminishes the value of the coverage reports. I've talked
to one of the Codacy people and advised that they should just have an
API key for coverage reports which is not a secret, like what bugsnag
does.
2024-05-19 14:05:30 +10:00
Stefan Haller
189f39de2b Fix Stashing partial files for git version >= 2.35.0 (#3569)
- **PR Description**
Use git's `--staged` flag to stash staged changes if available (requires
git 2.35.0 or later), and fall back to our previous method if not. This
is a lot faster than our previous method, and it fixes two bugs, see
linked issues.

Fixes #3333 and #3563.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-05-18 10:07:24 +02:00
dsolerhww
5b80c0c792 Fix stashing partialy staged files for git version >= 2.35.0
Use `git stash push --staged` git feature available on git version >
2.35.0.
2024-05-18 09:59:00 +02:00
Stefan Haller
d8b3c0e568 Fix loading commits with very long subjects (#3533)
- **PR Description**

This PR fixes a problem with lazygit locking up completely when there's
a commit whose subject line is longer than approximately 65500
characters.

Fixes #3529.
2024-05-15 13:29:28 +02:00
Stefan Haller
f69eb6dc48 Use ScanLinesAndTruncateWhenLongerThanBuffer instead of bufio.ScanLines 2024-05-15 13:27:01 +02:00
Stefan Haller
e0a2d97f0f Put subject last in git log's prettyFormat
We are going to truncate overly long lines returned from git log, and the most
likely field that is going to make the line too long is the subject; so we must
put it last, otherwise we'd end up with not enough fields to split when it's too
long.

It might not be obvious from the diff what's happening to the mock command
output in the test: it didn't have the divergence field (">") at all, which was
kind of a bug. It didn't matter for these tests though, because we are not
testing the divergence here, and our production code happens to be resilient
against it missing. But now we must add the ">" field before the subject.
2024-05-15 13:27:01 +02:00
Stefan Haller
66d0ce841c Implement ScanLinesAndTruncateWhenLongerThanBuffer 2024-05-15 13:27:01 +02:00
Stefan Haller
6bb8c180b2 Handle scanner error in RunAndProcessLines
Scanners can return errors (e.g. ErrTooLong), and if we don't handle it, the
cmd.Wait() call below will block forever because nobody drains the command's
output.

This happens for CommitLoader.GetCommits when there's a commit whose subject
line is longer than approx. 65500 characters; in that case, lazygit would lock
up completely. With this fix it remains usable, but the commit list is truncated
before the bad commit, which is not good enough. We'll improve that in the
remaining commits of this branch.
2024-05-15 13:27:01 +02:00
Stefan Haller
5558d873af Fix clicking in status side panel (#3547)
- **PR Description**

Clicking in the status side panel should activate it; also, clicking on
the repo name should bring up the recent repositories menu, and clicking
on the "(rebasing)" text should bring up the rebase options menu. All of
this was broken for a long time, since somewhere around the big
refactoring of March 2023. (The exact commit where it broke is hard to
bisect, since many of the commits in that area either don't compile or
crash at startup...)

I'm fixing this not because I think it's super important functionality
(nobody seems to have missed it for over a year), but because I have to
touch this code in another PR, and noticed that it wasn't working.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-05-15 13:24:07 +02:00
Stefan Haller
380855d4e8 Add tests for clicking in status side panel
To prevent this from breaking again. All three tests would fail without the fix
from the previous commit.
2024-05-15 13:21:17 +02:00
Stefan Haller
88e7c44552 Fix clicking in status side panel
Seems to have been broken since that big refactoring in March 23.
2024-05-15 13:21:17 +02:00
Stefan Haller
ac0524f7cb Fix deadlock reporting (#3550)
Deadlock reporting broke in e1ceb6892a (last September); since then, it
was *off* when running debug builds normally, but *on* when debugging an
integration test. Both of which are exactly opposite of what we want.
2024-05-15 13:19:41 +02:00
Stefan Haller
10e29ce7dd Fix deadlock reporting
Deadlock reporting broke in e1ceb6892a; since then, it was *off* when running
debug builds normally, but *on* when debugging an integration test. Both of
which are exactly opposite of what we want.
2024-05-05 15:23:58 +02:00
Stefan Haller
618fe533f8 Add commitPrefix for defining a prefix for any project (#3291)
- **PR Description**

Adds a new option `git.comitPrefix` to act similarly to
`git.commitPrefixes`, except, if defined, it applies to any repo that
doesn't have an entry in `git.commitPrefixes`.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-05-01 19:12:53 +02:00
Jonathan Duck
01ff18dd92 Add commitPrefix for defining a prefix for any project 2024-05-01 19:03:12 +02:00
Stefan Haller
af0897f18f Enable the commit graph in the divergence view (#3537)
- **PR Description**

In the "View divergence from upstream" view we have so far disabled the
commit graph because it was too difficult to implement properly. I
really miss it though, so here's a PR that enables it there, too.

For feature branches it is not essential, because these usually don't
contain merges and the graph is a trivial line. However, for the master
branch against its upstream it is useful too see how many PRs were
merged since you last fetched it, and the graph helps a lot with that.
Also, when we implement #3536 it will be very useful there, too.
2024-04-30 13:53:35 +02:00
Stefan Haller
b7673577a2 Enable the commit graph in the divergence view 2024-04-30 13:50:46 +02:00
Stefan Haller
00c55d5711 chore: fix some comments and typos (#3535)
- **PR Description**
fix some comments and typos
2024-04-28 10:02:59 +02:00
knowmost
0677a58e9f chore: fix some comments and typos
Signed-off-by: knowmost <knowmost@outlook.com>
2024-04-28 09:44:59 +02:00
Stefan Haller
b3a60ce407 Add config options for length of commit hash displayed in commits view (#3505)
- **PR Description**

Add a new config option `gui.commitHashLength` to change the length of
the commit hash displayed in commits view.

default:
<img width="472" alt="image"
src="https://github.com/jesseduffield/lazygit/assets/98684296/36dced1e-0c74-4dbd-8670-98e17a75d83a">

With config:
```yaml
gui:
  commitHashLength: 3
```
<img width="463" alt="image"
src="https://github.com/jesseduffield/lazygit/assets/98684296/e8023cd8-f138-4af8-ae0e-3661f80206ca">


- Changes
- Added the user config option to to `pkg/config/user_config.go` and
`schema/config.json`
  - documented in `docs/Config.md`
- Changed the code that displays the hash in
`pkg/gui/presentation/commits.go`
  
---

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-04-28 08:22:48 +02:00
Stefan Haller
b63321a302 Refactor pkg/gui/presentation/commits.go slightly to be consistent
Change `func displayCommit()` so all the individual strings are built first,
then the whole thing `cols` is put together. Before, most strings were built
prior to constructing `cols`, but a few were built inside the `cols`
construction.
2024-04-27 11:31:19 +02:00
Olivia Bahr
a4354ccdfb Add config option for length of commit hash displayed in commits view
- Add config option `commitHashLength` to to pkg/config/user_config.go
- Changed the hash display in pkg/gui/presentation/commits.go
2024-04-27 11:30:49 +02:00
Stefan Haller
aa81e191e2 Support externalDiffCommand in diffing mode (#3519)
- **PR Description**

Support `git.paging.externalDiffCommand` config in diffing mode (i.e.
when showing the diff between two commits using `W`).

Fixes #3518.
2024-04-25 08:53:14 +02:00
Stefan Haller
496308e023 Support external diff command in diffing mode 2024-04-25 08:50:30 +02:00
Stefan Haller
98c569749f Use git.paging.colorArg in diffing mode diff 2024-04-25 08:50:30 +02:00
Stefan Haller
047380f311 Cleanup: use separate Arg calls for unrelated arguments 2024-04-25 08:50:30 +02:00
Stefan Haller
b2ff09ec0c Use errors.New instead of fmt.Errorf with no parameters (#3523) 2024-04-24 10:53:39 +02:00
ChengenH
dd6bfa1680 chore: use errors.New to replace fmt.Errorf with no parameters. 2024-04-24 16:21:34 +08:00
Stefan Haller
0a5e9b2d60 Drop update-ref todos pointing to head (#3456)
- **PR Description**

The rebase.updateRefs feature of git is very useful to rebase a stack of
branches and keep everything nicely stacked; however, it is usually in
the way when you make a copy of a branch and want to rebase it "away"
from the original branch in some way or other. For example, the original
branch might sit on main, and you want to rebase the copy onto devel to
see if things still compile there. Or you want to do some heavy history
rewriting experiments on the copy, but keep the original branch in case
the experiments fail. Or you want to split a branch in two because it
contains two unrelated sets of changes; so you make a copy, and drop
half of the commits from the copy, then check out the original branch
and drop the other half of the commits from it.

In all these cases, git's updateRefs feature insists on moving the
original branch along with the copy in the first rebase that you make on
the copy. I think this is a bug in git, it should create update-ref
todos only for branches that point into the middle of your branch
(because only then do they form a stack), not when they point at the
head (because then it's a copy). I had a long discussion about this on
the git mailing list [1], but people either don't agree or don't care
enough.

So we fix this on our side: whenever we start a rebase for whatever
reason, be it interactive, non-interactive, or behind-the-scenes, we
drop any update-ref todos that are at the very top of the todo list,
which fixes all the above-mentioned scenarios nicely.

I will admit that there's one scenario where git's behavior is the
desired one, and the fix in this PR makes it worse: when you create a
new branch off of an existing one, with the intention of creating a
stack of branches, but before you make the first commit on the new
branch you realize some problem with the first branch (e.g. a commit
that needs to be reworded or dropped). It this case you do want both
branches to be affected by the change. In my experience this scenario is
much rarer than the other ones that I described above, and it's also
much easier to recover from: just check out the other branch again and
hard-reset it to the rebased one.

[1]
https://public-inbox.org/git/354f9fed-567f-42c8-9da9-148a5e223022@haller-berlin.de/
2024-04-23 08:32:17 +02:00
Stefan Haller
8b99a3c949 Drop update-ref commands at the top of the rebase-todo file
The rebase.updateRefs feature of git is very useful to rebase a stack of
branches and keep everything nicely stacked; however, it is usually in the way
when you make a copy of a branch and want to rebase it "away" from the original
branch in some way or other. For example, the original branch might sit on main,
and you want to rebase the copy onto devel to see if things still compile there.
Or you want to do some heavy history rewriting experiments on the copy, but keep
the original branch in case the experiments fail. Or you want to split a branch
in two because it contains two unrelated sets of changes; so you make a copy,
and drop half of the commits from the copy, then check out the original branch
and drop the other half of the commits from it.

In all these cases, git's updateRefs feature insists on moving the original
branch along with the copy in the first rebase that you make on the copy. I
think this is a bug in git, it should create update-ref todos only for branches
that point into the middle of your branch (because only then do they form a
stack), not when they point at the head (because then it's a copy). I had a long
discussion about this on the git mailing list [1], but people either don't agree
or don't care enough.

So we fix this on our side: whenever we start a rebase for whatever reason, be
it interactive, non-interactive, or behind-the-scenes, we drop any update-ref
todos that are at the very top of the todo list, which fixes all the
above-mentioned scenarios nicely.

I will admit that there's one scenario where git's behavior is the desired one,
and the fix in this PR makes it worse: when you create a new branch off of an
existing one, with the intention of creating a stack of branches, but before you
make the first commit on the new branch you realize some problem with the first
branch (e.g. a commit that needs to be reworded or dropped). It this case you do
want both branches to be affected by the change. In my experience this scenario
is much rarer than the other ones that I described above, and it's also much
easier to recover from: just check out the other branch again and hard-reset it
to the rebased one.

[1]
https://public-inbox.org/git/354f9fed-567f-42c8-9da9-148a5e223022@haller-berlin.de/
2024-04-22 20:59:15 +02:00
Stefan Haller
af6d072cc6 Add tests demonstrating undesired behavior with update-ref todos for copied branches
These tests succeed here, but have comments explaining which bits are undesired.
See next commit for a more detailed explanation why.
2024-04-22 20:59:15 +02:00
Stefan Haller
7270dea48d Switch git-todo-parser from fsmiamoto original repo to stefanhaller's fork
Sometimes it takes a while to get PRs accepted upstream, and this blocks our
progress. Since I'm pretty much the only one making changes there anyway, it
makes sense to point to my fork directly.
2024-04-22 20:59:15 +02:00
Stefan Haller
69153acfdb Remove TODO.* from .gitignore
It was added in 2018 (700f8c7e79), but I don't know for what purpose. It just
took me 15 minutes to figure out why my new file todo.go wasn't added, so I'm
removing this entry as I find it more harmful than helpful.
2024-04-22 20:55:03 +02:00
Stefan Haller
b4fbfd20cb Fix amend to operation not working with non-HEAD merge commit (#3510)
- **PR Description**

Resolves https://github.com/jesseduffield/lazygit/issues/3421

The error is "Expected exactly one original SHA, found 0" but the merge
commit hash (`d6a7a04c626e40071133de26ebe8fdd225caa5c0`) is present in
the rebase TODO file.


![image](https://github.com/jesseduffield/lazygit/assets/13722457/2e6d5fdb-af9f-4eae-9972-8e51a77ba614)

![image](https://github.com/jesseduffield/lazygit/assets/13722457/65dd4b1b-b080-47b0-9079-71c5e0d76cd2)

However, the commit is missed during search because the filter is only
looking for pick commits:
580818e935/pkg/utils/rebase_todo.go (L238)

Checking for merge commits as well fixes the issue.

I believe only pick and merge should be valid here. If already in an
interactive rebase, lazygit only allows amending to the current HEAD
commit. When that happens, this whole interactive rebase logic is
bypassed and lazygit just performs `git commit --amend`:
580818e935/pkg/gui/controllers/local_commits_controller.go (L668)

This is the reason why amending to a HEAD merge commit currently works
whereas non-HEAD does not.
2024-04-22 18:58:55 +02:00
Brandon
ef99e47d09 Fix amend to operation not working with non-HEAD merge commit 2024-04-22 08:48:59 -07:00
Stefan Haller
580818e935 pkg: fix some typos (#3364) 2024-04-20 13:49:43 +02:00
thirdkeyword
1c098ff82a pkg: fix some typos
Signed-off-by: thirdkeyword <fliterdashen@gmail.com>
2024-04-20 13:47:39 +02:00
Stefan Haller
34f8f7293c Simplify error handling (#3502)
- **PR Description**

Simplify and canonicalize error handling across the code base.

Previously it was important to make sure that errors don't bubble up
into gocui, because it would panic there; so we had to show errors in
error panels in the right places (in controller code, usually). This is
error-prone because it's easy to forget (see #3490 for an example);
also, it's not always obvious where in the call chain the error panel
needs to be shown. Most of the time it's in controller code, but we had
plenty of calls to `Error()` in helpers, and I remember that I found
this very confusing when I was new to the code base.

Change this so that you can simply return errors everywhere. The
functions to show an error message manually have been removed for
clarity.

I tried to structure the commits so that you can review them one by one.

Closes #3491.
2024-04-18 10:14:51 +02:00
Stefan Haller
caad553502 Remove ErrorMsg
There is no reason any more for application code to show error messages in a
panel. Just return an error instead.
2024-04-18 10:10:30 +02:00
Stefan Haller
723b92916d Rename Error() to ErrorHandler()
It is now only used as the error handler that is passed to gocui.Gui on
construction; it's not a client-facing API any more. Also, it doesn't have to
handle gocui.ErrQuit, as gocui takes care of that.
2024-04-18 10:10:30 +02:00
Stefan Haller
653994845e Return error from RefreshOptions.Then 2024-04-18 10:10:30 +02:00
Stefan Haller
bbad3a70ce Log errors from refresh instead of showing them in a panel
We are already doing this in other cases in this file.
2024-04-18 10:10:30 +02:00
Stefan Haller
1869fda800 Make OnWorker callback return an error
This lets us get rid of a few more calls to Error(), and it simplifies things
for clients of OnWorker: they can simply return an error from their callback
like we do everywhere else.
2024-04-18 10:10:30 +02:00
Stefan Haller
5396a70661 Clean up error handling of WithWaitingStatus and WithWaitingStatusSync 2024-04-18 10:10:30 +02:00
Stefan Haller
82a3d33ce3 Remove calls to Error()
Now that we have an error handler set, we can simply let them bubble up all the
way to gocui.
2024-04-18 10:10:30 +02:00
Stefan Haller
325800a72e Set ErrorHandler 2024-04-18 10:10:30 +02:00
Stefan Haller
9f8ae76189 Bump gocui
In Gui.onWorker we only make the minimum possible change to get things to
compile after the API-breaking change of the gocui update; we'll make this
cleaner later in this branch.
2024-04-18 10:10:30 +02:00
Stefan Haller
8a77e51576 Remove PopupHandler index and mutex
It doesn't seem to be used.
2024-04-16 10:03:35 +02:00
Jesse Duffield
145795c0b0 README.md: Update Sponsors (#3498)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action
2024-04-16 09:25:11 +10:00
github-actions[bot]
cbe6dd7b2f README.md: Update Sponsors 2024-04-15 23:24:36 +00:00
Jesse Duffield
b6ca9090f4 exclude sponsors PRs from release notes (#3499)
- **PR Description**

- **Please check if the PR fulfills these requirements**

* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
* [ ] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [ ] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-04-16 09:24:21 +10:00
Jesse Duffield
5d13e3cf76 exclude sponsors PRs from release notes 2024-04-16 09:23:37 +10:00
Jesse Duffield
9e5ceaa1ec sponsors.yml: Create PR instead of trying to push to a protected branch (#3493)
- **PR Description**
Uses [this popular-ish
action](https://github.com/marketplace/actions/create-pull-request) to
create a PR.

> How the action behaves:
> 
> If there are changes (i.e. a diff exists with the checked-out base
branch), the changes will be pushed to a new branch and a pull request
created.
> If there are no changes (i.e. no diff exists with the checked-out base
branch), no pull request will be created and the action exits silently.
> If a pull request already exists it will be updated if necessary.
Local changes in the Actions workspace, or changes on the base branch,
can cause an update. If no update is required the action exits silently.
> If a pull request exists and new changes on the base branch make the
pull request unnecessary (i.e. there is no longer a diff between the
pull request branch and the base), the pull request is automatically
closed. Additionally, if
[delete-branch](https://github.com/marketplace/actions/create-pull-request#delete-branch)
is set to true the branch will be deleted.

Demo Action Run:
https://github.com/SachinVin/lazygit/actions/runs/8675283475
Demo PR: https://github.com/SachinVin/lazygit/pull/2

You also might want to consider changing this action to trigger as a
cron job instead on pushes to master.

- **Please check if the PR fulfills these requirements**

* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
* [ ] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-04-16 09:09:06 +10:00
SachinVin
ba0c00b5d1 sponsors.yml: Create PR instead of trying to push to a protected branch 2024-04-13 22:59:55 +05:30
Stefan Haller
145fb6191c standardize commit hash commit sha (#3398)
Standardise on use of 'commit hash' rather than 'commit SHA'
Close: https://github.com/jesseduffield/lazygit/issues/3208
2024-04-12 08:37:30 +02:00
Stefan Haller
7e14d88dc9 Support both Sha and Hash on commits in custom commands
We achieve this by wrapping the model Commit in a custom struct that provides
both.
2024-04-12 08:33:47 +02:00
Stefan Haller
e6a07b3f03 Add integration test that accesses commit properties in a custom command
Useful as a regression test to check that the following commit doesn't break it.
2024-04-12 08:33:47 +02:00
Shin-JaeHeon
28923fc9d0 improve korean translation 2024-04-12 08:33:47 +02:00
pikomonde
19bef17042 rename sha to hash 10, last remaining sha (hopefully) 2024-04-12 08:33:47 +02:00
pikomonde
170c4ecb8c rename sha to hash 9, case: Sha 2024-04-12 08:33:47 +02:00
pikomonde
de1c495704 rename sha to hash 8, update some log and comment 2024-04-12 08:33:47 +02:00
pikomonde
fccfbf1f63 rename sha to hash 7, language translate 2024-04-12 08:33:47 +02:00
pikomonde
05fb12b1d5 rename sha to hash 6, update short hash 2024-04-12 08:33:47 +02:00
pikomonde
9cf1ca10a2 rename sha to hash 5 2024-04-12 08:33:47 +02:00
pikomonde
dc6863b83d rename sha to hash 4 2024-04-12 08:33:47 +02:00
pikomonde
13af335b37 rename sha to hash 3 2024-04-12 08:33:47 +02:00
pikomonde
92aab21d3a rename sha to hash 2 2024-04-12 08:33:47 +02:00
pikomonde
e6ef1642fa rename sha to hash 2024-04-12 08:33:47 +02:00
pikomonde
84333eebc3 renaming variable to CommitHash 2024-04-12 08:33:47 +02:00
pikomonde
7f6eea2a55 standardize 'Commit Sha' to 'Commit Hash' 2024-04-12 08:31:40 +02:00
Stefan Haller
06624e85d6 Add StatusPanelView config (#3309)
Adds a new config `statusPanelView ` which allows you to select the
default view of the main window: `dashboard` (default),
`allBranchesLog`.
2024-04-10 17:43:36 +02:00
oakio
5616d6a9bc Dynamic copyright year 2024-04-10 17:38:57 +02:00
oakio
5c3aacb4cb UserConfig validation 2024-04-10 17:38:57 +02:00
oakio
2b5c814080 Add StatusPanelView config 2024-04-10 17:38:57 +02:00
Stefan Haller
4ba85608c8 Fix stderr redirection (#3479)
- **PR Description**

Seems that there's a problem in the Stdout/Stderr/Stdin stream vars
assignments,
probably copy-paste issue.

If this is intentional, I suggest adding an explanation on why `Stderr`
-> `Stdout` intentionally to avoid other PRs and confused people around
:)

Thanks!
2024-04-09 09:10:35 +02:00
Emanuele "Lele" Calo
a63c660f28 Fix stderr redirection
Seems that there's a problem in the Stdout/Stderr/Stdin vars
assignments, probably copy-paste issue.
2024-04-09 09:08:14 +02:00
Stefan Haller
fab7ca2b58 Fix issue #3308 (#3478)
- **PR Description**

It seems that setting TERM=dumb breaks compatibility with curses
subprocess in Go, especially with `pinentry-curses`

This fixes #3308
2024-04-09 09:07:44 +02:00
Emanuele "Lele" Calo
7b0e06d885 TERM: remove TERM variable hard-coded value set 2024-04-09 04:15:03 +02:00
Jesse Duffield
426375b7cd Replace min/max helpers with built-in min/max (#3482)
- **PR Description**

We upgraded our minimum Go version to 1.21 in commit
57ac9c2189. We can now replace our
`utils.Min` and `utils.Max` functions with the built-in `min` and `max`.

Reference: https://go.dev/ref/spec#Min_and_max

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-04-08 09:31:14 +10:00
Eng Zer Jun
f933a2f7ec Replace min/max helpers with built-in min/max
We upgraded our minimum Go version to 1.21 in commit
57ac9c2189. We can now replace our
`utils.Min` and `utils.Max` functions with the built-in `min` and `max`.

Reference: https://go.dev/ref/spec#Min_and_max
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2024-04-07 23:24:10 +08:00
Stefan Haller
ed61b9a7f2 pkg: fix some comment (#3481) 2024-04-07 11:06:42 +02:00
hongkuang
d8c1eb879b pkg: fix some comment
Signed-off-by: hongkuang <liurenhong@outlook.com>
2024-04-07 16:09:26 +08:00
Stefan Haller
c02408bba0 Add spinner config options (#3463)
- **PR Description**

Add `spinner` config section to the user config. The section has 2
options:
- `frames` - list of strings that are iterated in the spinner animation
- `rate` - spinner's rate in milliseconds

Closes #3435
2024-04-06 13:49:51 +02:00
Artem Belyakov
f3dba743f0 Add SpinnerConfig
This new config section allows to customize frames and rate of
thespinner
2024-04-06 13:46:15 +02:00
Stefan Haller
53f0c4aeff Migrate null keybindings to <disabled>, and remove our yaml fork (#3459)
- **PR Description**

Migrate users' config files to change all `null` keybindings to
`"<disabled>"`. Then remove our yaml fork, since that was the only
reason we were using it.

Fixes #3458.
2024-04-01 11:15:02 +02:00
Stefan Haller
84e82ea8b6 fixup! Introduce a yaml_utils.Walk function 2024-03-30 13:05:33 +01:00
Stefan Haller
8487bc397d Remove our yaml fork
Switch back to the official go-yaml package.
2024-03-29 17:55:06 +01:00
Stefan Haller
93aee0dca0 Migrate null keybindings to "<disabled>"
Unfortunately the migration code requires yaml v3, but our yaml fork is based on
v2, so we need to import both in app_config.go in this commit, which is ugly. We
can clean this up in the next commit.
2024-03-29 17:55:06 +01:00
Stefan Haller
4d8b8b647a Introduce a yaml_utils.Walk function 2024-03-29 17:55:06 +01:00
Stefan Haller
2385c1d111 Make URLs in confirmation panels clickable, and underline them (#3446)
- **PR Description**

This is especially helpful for the breaking changes popup, which has a
link to the release notes, but it could also be useful for other panels
that display some warning or error with a link to more information.
2024-03-29 10:58:55 +01:00
Stefan Haller
5d509efe19 Underline links in confirmation panels 2024-03-29 10:55:33 +01:00
Stefan Haller
b9a75ee0ed Make links clickable in confirmation panels
This is not opt-in, we do it always. I can't imagine a situation where we
wouldn't want it.
2024-03-29 10:55:33 +01:00
Stefan Haller
d102f12304 Make HandleGenericClick a little smarter
Make it recognize URLs wrapped in angle brackets, and followed by punktuation.
We don't need this for the status panel, but we will need it for confirmation
panels.
2024-03-29 10:55:33 +01:00
Stefan Haller
6396d1ce03 Extract a function HandleGenericClick 2024-03-29 10:55:33 +01:00
Stefan Haller
c59e6b6451 Cleanup: don't mess with globals in tests without resetting them
Changing globals in the init() function of a test file is a bad idea, as it
affects all other tests that run after it. Do it explicitly in each test
function that needs it, and take care of restoring the previous value
afterwards.
2024-03-29 10:55:33 +01:00
Stefan Haller
1cedfa463b Fix schema link in Config.md (#3451)
The schema at https://json.schemastore.org/lazygit.json is broken and
doesn't work. It should actually be removed.
2024-03-28 13:31:28 +01:00
Stefan Haller
9b035aed67 Fix schema link in Config.md 2024-03-28 13:24:54 +01:00
Stefan Haller
ce9cdd8d78 Fix rewording signed commits when the log.showsignature git config is true (#3431)
- **PR Description**

For people who have the log.showsignature git config set to true, trying
to reword a signed commit would put the signature verification into the
subject field and the commit subject into the description field of the
commit message panel. Amending commits, adding co-authors to a commit,
and copying a commit message to the clipboard would all be broken in a
similar way.

Slightly related is #1911, but back then it was more about performance
than wrong behavior, it seems.

Fixes #3425.
2024-03-28 13:19:11 +01:00
Stefan Haller
f774b7eb5c Fix rewording signed commits when the log.showsignature git config is true
For people who have the log.showsignature git config set to true, trying to
reword a signed commit would put the signature verification into the subject
field and the commit subject into the description field of the commit message
panel. Amending commits, adding co-authors to a commit, and copying a commit
message to the clipboard would all be broken in a similar way.
2024-03-28 13:16:10 +01:00
Stefan Haller
e4b4b6d5f4 Fix excluding files in submodules or worktrees (#3432)
- **PR Description**

Make the "Add to .git/info/exclude" command work correctly in a worktree
or in a submodule. Previously it would result in an error message.

Fixes #3427.
2024-03-28 13:13:52 +01:00
Stefan Haller
93251db67e Fix the "Add to .git/info/exclude" command in submodules or worktrees 2024-03-28 13:11:08 +01:00
Stefan Haller
de52a68b53 Add a test that demonstrates the problem
Using the "Add to .git/info/exclude" in a worktree results in an error message,
as the test shows. The same would happen in a submodule, but I'm not adding an
extra test for that, as the circumstances are the same.
2024-03-28 13:11:08 +01:00
Stefan Haller
42ebf1947a Cleanup: simplify return statements 2024-03-28 13:11:08 +01:00
Stefan Haller
c995e7ef2e Cleanup: remove pointless condition and error message
The file .git/info/exclude can't possibly show up in the files panel.
2024-03-28 13:11:08 +01:00
Stefan Haller
04fcb78c0c Keep the same commit selected when exiting filtering mode (#3416)
- **PR Description**

When exiting filtering mode, we currently keep the selection index the
same in the commits panel. This doesn't make sense at all, since the
index in the filtered view has no relation to the index in the
unfiltered view.

I often use filtering mode (either by path or by author) to find a given
commit faster than I would otherwise be able to. When exiting filtering
mode, it's useful to keep the same commit selected, so that I can look
at the surrounding commits, see which branch it was a part of, etc. So
reselect the commit again after exiting filtering mode.

Sometimes this is not possible, most likely when the commit is so long
ago that it's outside of the initial 300 range. In that case, at least
select the commit again that was selected before I entered filtering;
this is still better than arbitrarily keeping the same selection index.
2024-03-28 12:27:02 +01:00
Stefan Haller
bd8518355e Keep the same commit selected when exiting filtering mode
When exiting filtering mode, we currently keep the selection index the same in
the commits panel. This doesn't make sense at all, since the index in the
filtered view has no relation to the index in the unfiltered view.

I often use filtering mode (either by path or by author) to find a given commit
faster than I would otherwise be able to. When exiting filtering mode, it's
useful to keep the same commit selected, so that I can look at the surrounding
commits, see which branch it was a part of, etc. So reselect the commit again
after exiting filtering mode.

Sometimes this is not possible, most likely when the commit is so long ago that
it's outside of the initial 300 range. In that case, at least select the commit
again that was selected before I entered filtering; this is still better than
arbitrarily keeping the same selection index.
2024-03-28 12:23:46 +01:00
Stefan Haller
f93cb54b5d Fix crash when filtering the keybindings menu (#3450)
- **PR Description**

It would crash when some keybindings are set to null, and the filter
string is such that only those keybindings remain visible.

Fixes #3449.
2024-03-28 12:19:06 +01:00
Stefan Haller
0b70dfbf46 Fix crash when filtering the keybindings menu
It would crash when some keybindings are set to null, and the filter string is
such that only those keybindings remain visible.

The reason for the crash is that when inserting non-model items (menu section
headers in this case) you specify a column to align them to. This works on the
assumption that the number of columns is always the same. It can cope with the
case that columns are removed because they are empty for all items; but it can't
cope with the case that the getDisplayStrings function returns a lower number of
columns.

And this is what happened here: MenuViewModel.GetDisplayStrings would omit the
keybinding column when none of the entries have a keybinding. This logic is
unnecessary, the generic list rendering mechanism takes care of this, so
removing this logic fixes the crash.

We do have to make sure though that the column is really empty when there's no
keybinding, so change the logic to use FgCyan only when there's a keybinding.
2024-03-28 09:58:45 +01:00
Stefan Haller
38876ba141 Update Busy.md (#3433)
- **PR Description**
enqueing -> enqueueing
2024-03-26 23:12:49 +01:00
Ikko Eltociear Ashimine
57786fbb1f Update Busy.md
enqueing -> enqueueing
2024-03-26 23:08:51 +01:00
Stefan Haller
e295ccefab Fix deleting update ref todos (#3439)
- **PR Description**

Deleting an update-ref todo in an interactive rebase now behaves as
expected (i.e. it leaves the branch referenced by the update-ref
untouched). Previously it would have deleted the branch referenced by
the update-ref todo when the rebase was continued. See #3418 for more
details.

Fixes #3418.
2024-03-26 22:43:46 +01:00
Stefan Haller
1fdcc29277 Fix deleting update-ref todos
It is a bad idea to read a git-rebase-todo file, remove some update-ref todos,
and write it back out behind git's back. This will cause git to actually remove
the branches referenced by those update-ref todos when the rebase is continued.

The reason is that git remembers the refs affected by update-ref todos at the
beginning of the rebase, and remembers information about them in the file
.git/rebase-merge/update-refs. Then, whenever the user performs a "git rebase
--edit-todo" command, it updates that file based on whether update-ref todos
were added or removed by that edit. If we rewrite the git-rebase-todo file
behind git's back, this updating doesn't happen.

Fix this by not updating the git-rebase-todo file directly in this case, but
performing a "git rebase --edit-todo" command where we set ourselves as the
editor and change the file in there. This makes git update the bookkeeping
information properly.

Ideally we would use this method for all cases where we change the
git-rebase-todo file (e.g. moving todos up/down, or changing the type of a
todo); this would be cleaner because we wouldn't mess with git's private
implementation details. I tried this, but unfortunately it isn't fast enough.
Right now, moving a todo up or down takes between 1 and 2ms on my machine;
changing it to do a "git rebase --edit-todo" slows it down to over 100ms, which
is unacceptable.
2024-03-26 22:29:56 +01:00
Stefan Haller
6da9d55e47 Cleanup: update outdated comment
We used to pass the todo string, but this has changed to instructions a while
ago.
2024-03-26 22:25:43 +01:00
Stefan Haller
ba85f93fb3 Extend delete_update_ref_todo test to actually test what it was supposed to
In the test we simply removed the update-ref todo but didn't make any other
changes to the todos. This should really have kept everything the way it was,
including the other branch head. The fact that the star was gone was really
because of the bug that we are going to fix later in the branch.

Change the test so that it also makes a change before the update-ref todo; this
way we test that the star is gone because we deleted the update-ref, not because
of the bug.

To guard against the bug, we add another assertion for the branches view to test
that both branches are still there. This currently fails.
2024-03-26 22:23:50 +01:00
Stefan Haller
cdbec3997d Cleanup: fix typo in test comment 2024-03-26 22:23:50 +01:00
Stefan Haller
fb675b79f8 Set the TERM env variable (#3420)
Resolves #3419

I have tested this change with all the pagers shown in [the
docs](https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md).
Are there others that people frequently use and I should test?

A nice side effect of setting `TERM=dumb` is that `less` now correctly
complains (e.g. when forgetting to set `--paging=never` for delta:


![image](https://github.com/jesseduffield/lazygit/assets/4602612/33e9c048-6ab0-4196-95f6-86ee8effc873)


## Pagers Tested

### none

![image](https://github.com/jesseduffield/lazygit/assets/4602612/4e408fe6-5f19-4142-9183-de56fd738962)

### Delta

![image](https://github.com/jesseduffield/lazygit/assets/4602612/90dd12fa-ed58-4d49-9f71-da5e57f63a74)

```yaml
git:
  paging:
    colorArg: always
    pager: delta --paging=never
```

#### diff-so-fancy

![image](https://github.com/jesseduffield/lazygit/assets/4602612/930e6b93-904e-49a2-bfc2-9b5d9639f514)

```yaml
git:
  paging:
    colorArg: always
    pager: diff-so-fancy
```

#### ydiff

![image](https://github.com/jesseduffield/lazygit/assets/4602612/c2464f0c-f34f-4ebc-9624-8bc0350d92a7)

```yaml
git:
  paging:
    colorArg: always
    pager: ydiff -p cat
```

#### difft

![image](https://github.com/jesseduffield/lazygit/assets/4602612/599dee7a-6568-40e4-9213-76ba643d651f)

```yaml
git:
  paging:
    externalDiffCommand: difft --color=always
```
2024-03-23 23:49:22 +01:00
Tau
f30be824b3 Set the TERM env variable
This communicates to pagers that we're in a very simple
terminal that they should not expect to have much capabilities.

See #3419
2024-03-23 23:46:37 +01:00
Jesse Duffield
e1c3ef6629 Update README.md 2024-03-23 21:01:19 +11:00
Jesse Duffield
a70bb0a7aa Update README.md 2024-03-23 20:59:53 +11:00
Jesse Duffield
7718cb009b Update README.md
Updating for top-level sponsorship stuff
2024-03-23 20:58:02 +11:00
Jesse Duffield
1337a943fe Update interactive rebase demo (#3392)
**PR Description**

This is in anticipation of the new release which supports range-select.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-03-23 20:48:54 +11:00
Stefan Haller
471ea39758 Fix inline status removal when recording demos 2024-03-23 20:45:51 +11:00
Jesse Duffield
c3f0b5cb4a Update interactive rebase demo
I'm adding an explicit delay between moving the commits and selecting
the next items because otherwise it happens too fast
2024-03-23 20:45:51 +11:00
Stefan Haller
3675570a39 Fix container detection (#3412)
- **PR Description**

Running WSL without a container would be treated as native linux,
causing problems at it would then attempt to use `xdg-open`. This was
caused by `isContainer()` always returning true due to some dubious
conditionals. These have been removed.

The env-var check seems to not be used by lazygit, nor any common
containers, and therefore appears to only exist to manually tell lazygit
to behave as if it were inside of a container. This functionality has
been kept, but the env-var has been changed to be all uppercaps as to
comply with the POSIX standard.

Fixes #2757
Bug introduced in 4d78d76
2024-03-22 14:43:31 +01:00
aritmos
68495ea0ee Fix container detection
Running WSL without a container would be treated as native linux, causing problems at it would then attempt to use `xdg-open`.
This was caused by `isContainer()` always returning true due to some dubious conditionals. These have been removed.

The env-var check seems to not be used by lazygit, nor any common containers, and therefore appears to only exist to manually tell lazygit to behave as if it were inside of a container.
This functionality has been kept, but the env-var has been changed to be all uppercaps as to comply with the POSIX standard.

Fixes #2757
Bug introduced in 4d78d76
2024-03-22 13:42:15 +01:00
Stefan Haller
53363b761c Add config to truncate commit hashes when copying them to the clipboard (#3402)
- **PR Description**

I often copy hashes in the commits panel in order to paste them into
Github comments (or other places), and I can't stand it when they have
the full length.

I picked a default of 12 for this; I find this to be a good middle
ground between being reliable in large repos (12 still works in the
linux kernel repo today, but it might not be enough in really huge
repos) and not being too ugly (many smaller repos can probably get away
with less).
2024-03-22 10:01:43 +01:00
Stefan Haller
96c5cbe34e Change CopiedToClipboard text to start lower-case
It is used in the context "'xyz' copied to clipboard", so it looks weird when it
start uppercase.
2024-03-22 09:59:46 +01:00
Stefan Haller
b8a0473c68 Change toast for copying sha to clipboard to include what was copied
This makes it easier to see that "y, enter" copies the full sha, whereas ctrl-o
copies an abbreviated sha.
2024-03-22 09:58:54 +01:00
Stefan Haller
571141ea7c Change log message for copying sha to the clipboard to include "full"
To make it even clearer that this is different from copying a sha with ctrl-o.
2024-03-22 09:58:54 +01:00
Stefan Haller
3b29705a78 Add config to truncate commit hashes when copying them to the clipboard
I often copy hashes in the commits panel in order to paste them into Github
comments (or other places), and I can't stand it when they have the full length.

I picked a default of 12 for this; I find this to be a good middle ground
between being reliable in large repos (12 still works in the linux kernel repo
today, but it might not be enough in really huge repos) and not being too ugly
(many smaller repos can probably get away with less).

We deliberately don't change this for the "Copy to clipboard" menu, since this
gives users a way to copy the unabbreviated sha if they need this occasionally.
2024-03-22 09:58:54 +01:00
Stefan Haller
28dd7f9467 Make it easy to create "amend!" commits (#3409)
- **PR Description**

To support this, we turn the confirmation prompt of the "Create fixup
commit" command into a menu; creating a fixup commit is the first entry,
so that "shift-F, enter" behaves the same as before. But there are
additional entries for creating "amend!" commits, either with or without
file changes. These make it easy to reword commit messages of existing
commits.
2024-03-22 08:32:16 +01:00
Stefan Haller
150cc70698 Make it easy to create "amend!" commits
To support this, we turn the confirmation prompt of the "Create fixup commit"
command into a menu; creating a fixup commit is the first entry, so that
"shift-F, enter" behaves the same as before. But there are additional entries
for creating "amend!" commits, either with or without file changes. These make
it easy to reword commit messages of existing commits.
2024-03-22 08:27:45 +01:00
Stefan Haller
c92e9d9bdc Remove CreateFixupCommitDescription as it's identical to CreateFixupCommit 2024-03-22 08:27:45 +01:00
Stefan Haller
e23337f8f5 Support editing multiple files at once using range selection (#3407)
- **PR Description**

This makes it possible to select a range of files (either in the files
panel, or in the commit-files panel), and hit `e` to open them all at
once in the editor.

We pass all of them to a single editor command, hoping that the editor
will be able to handle multiple files (VS Code and vim do).

We ignore directories that happen to be in the selection range; this
makes it easier to edit multiple files in different folders in tree
view. We show an error if only directories are selected, though.
2024-03-22 08:22:46 +01:00
Stefan Haller
73019574a8 Support editing multiple files at once using range selection
We pass all of them to a single editor command, hoping that the editor will be
able to handle multiple files (VS Code and vim do).

We ignore directories that happen to be in the selection range; this makes it
easier to edit multiple files in different folders in tree view. We show an
error if only directories are selected, though.
2024-03-22 08:20:16 +01:00
Stefan Haller
9b5269b490 When adding a new remote, select it and fetch it (#3401)
- **PR Description**

I'm doing these two things every time I add a new remote, in 100% of the
cases, so do them automatically for me.
2024-03-22 08:19:45 +01:00
Stefan Haller
e8db27b0b3 When creating a new remote, select it and fetch it
I'm doing these two things every time I add a new remote, in 100% of the cases,
so do them automatically for me.
2024-03-22 08:17:12 +01:00
Stefan Haller
337a0dbc9b Make the links in the status panel point to the current version rather than master (#3397)
We've seen a lot of issues recently where people complain that lazygit
doesn't behave as documented, but that was only because they were
running the latest release but were looking at the documentation of
master. Make the documentation links in the status panel point to the
release that they are using in the hope that this will help a little bit
with this problem.
2024-03-22 08:16:20 +01:00
Stefan Haller
5870133924 Make links in status view clickable, and underline them 2024-03-22 08:13:59 +01:00
Stefan Haller
fa79c92748 Make the links in the status panel point to the current version rather than master
We've seen a lot of issues recently where people complain that lazygit doesn't
behave as documented, but that was only because they were running the latest
release but were looking at the documentation of master. Make the documentation
links in the status panel point to the release that they are using in the hope
that this will help a little bit with this problem.
2024-03-22 08:13:59 +01:00
Stefan Haller
251bfb6a24 Always prompt to return from subprocess if there was an error (#3410)
- **PR Description**

When lazygit suspends itself to the background to run an external
command, and the command returns a non-zero exit code, always show the
prompt for pressing enter to return to lazygit, even if the
`promptToReturnFromSubprocess` is set to `false`. The rationale is that
if the process returned an error, it likely also printed some error
message to the console that users will always want to read.

I was considering turning the `promptToReturnFromSubprocess` config into
an enum with values `never`, `onlyOnError`, `always`, but then I felt
that `onlyOnError` is really the only sensible choice for people who
have it set to `false` now, so I just hard-coded that.
2024-03-22 08:13:15 +01:00
stk
377eced31a Always prompt to return from subprocess if there was an error
Except when we are running integration tests, in which case we never want to
prompt because there wouldn't be a way to confirm the prompt.
2024-03-21 14:17:10 +01:00
Stefan Haller
fadc2893d2 Add NixOs installation instructions to README.md (#3249)
Extend README.md with instructions on how to try and install the lazygit
on NixOs.
2024-03-19 12:38:20 +01:00
Radosław Śnieżek
1e59de2768 Add NixOs installation instructions README.md
Extend README.md with instructions on how to try and install
the lazygit on NixOs.
2024-03-19 12:32:13 +01:00
Stefan Haller
1d4448d691 Fixed translations for zh_TW (#3393)
- **PR Description**

Improve translations for Traditional Chinese.
2024-03-19 07:59:59 +01:00
Stefan Haller
2c33f0ce1f Fix indentation 2024-03-19 07:55:23 +01:00
Stefan Haller
14b3e0574c Improve translations for zh_TW
Authored-by: Oliver Tzeng <olivertzeng@proton.me>
2024-03-19 07:55:23 +01:00
Stefan Haller
318a399bfb Fix enabling/disabling of ctrl-o in commit message panel (#3395)
- **PR Description**

This fixes two minor regressions introduced in #3097 related to commands
that open a commit message panel but don't set an onSwitchToEditor
function (an example is the commit message panel that appears when
moving a custom patch to a new commit):
- the "Press <c-o> to open menu" hint was hidden. That's wrong, it is
still possible to open the menu in this case. (And it still worked, we
just wouldn't show the hint.)
- invoking the "open in editor" menu item would silently do nothing. Now
we set a DisabledReason for the item in this case.
2024-03-19 07:13:20 +01:00
Stefan Haller
7764e6d1cb Fix disabling the switch-to-editor menu item if unavailable
Some operations don't support switching to the editor from the commit message
panel; an example is the commit message panel that appears when moving a custom
patch into a new commit. Disable the "open in editor" menu entry in this case,
instead of silently doing nothing.
2024-03-18 21:10:58 +01:00
Stefan Haller
1a9ce9db94 Always show the "Press <c-o> to open menu" help text in the commit panel
Previously we would hide it if no onSwitchToEditor function was set; that was
from a time when <c-o> was bound directly to the switch-to-editor command. Now
it is bound to showing a menu, and that menu is always available even if no
onSwitchToEditor function is set. (We rather need to disable the switch to
editor item _within_ that menu, see next commit.)
2024-03-18 20:44:03 +01:00
Stefan Haller
9a3db0dfbd Update translations for polish language (#3389)
- **PR Description**

Hi, I'm happy using `lazygit` from quite a while. I've decided to give
back to community fraction of my time. I'm native polish speaker, and
seen that polish translation have some gaps.

I've added missing translation for polish language and improved existing
one. I've followed technical jargon used on
https://git-scm.com/book/pl/v2/
 
For easier diff comparison and feature maintenance, I've ordered keys to
be in same order as those in `english.go`
2024-03-17 18:53:13 +01:00
undg
d65b21d6b8 Add missing translations for polish 2024-03-17 18:46:19 +01:00
Stefan Haller
435a835c51 Use substring filtering instead of fuzzy filtering by default (#3376)
By default we now search for substrings; you can search for multiple
substrings by separating them with spaces. Add a config option
`gui.filterMode` that can be set to 'fuzzy' to switch back to the
previous behavior.

Addresses #3373.
2024-03-17 12:28:13 +01:00
Stefan Haller
4f2bebe453 Get rid of the retain-sort-order-when-filtering logic again
For die-hard fuzzy-searching fans it's probably in the way, so taking it out
makes fuzzy filtering work better. For substring filtering it always retains the
sort order anyway.
2024-03-17 12:23:07 +01:00
Stefan Haller
7d2163d632 Rename FuzzySearchFunc to FilterFunc
It isn't necessarily fuzzy any more.
2024-03-17 11:56:29 +01:00
Stefan Haller
561afa9901 Rename FuzzySearch to FilterStrings
It isn't necessarily fuzzy any more.
2024-03-17 11:55:30 +01:00
Stefan Haller
a8797c7261 Default to substring filtering, add option to go back to fuzzy filtering
By default we now search for substrings; you can search for multiple substrings
by separating them with spaces. Add a config option gui.filterMode that can be
set to 'fuzzy' to switch back to the previous behavior.
2024-03-17 11:55:09 +01:00
Stefan Haller
a82e26d11e Don't sort the results of fuzzy.Find
It sorts them already, so it's unnecessary. In the next commit we use this same
code for substring searching too, and in that case we don't want to sort because
sorting is by Score, but we don't even fill in the score for substring
searching.
2024-03-17 07:57:19 +01:00
Stefan Haller
2cf8e7bc5c When checking out a remote branch by name, ask the user how (#3388)
- **PR Description**

When checking out a remote branch by name, ask the user how they want to
check it out; the choices are to create a new local branch that tracks
the remote, or a detached head.

This is an alternative to #3371, and fixes #2312.
2024-03-17 07:56:12 +01:00
Stefan Haller
6932e04708 Refresh after creating local branch, before checking it out
This way we see the local branch immediately when switching to the branches
view, and we see an inline waiting status on it when checking it out.
2024-03-17 07:53:38 +01:00
Stefan Haller
30db7234d9 Show inline waiting status when checking out a local branch 2024-03-17 07:53:38 +01:00
Stefan Haller
0d5c748fe8 Show the same menu when pressing space on a remote branch
The old behavior of showing a prompt to choose a name for the new local branch
is still available via the 'n' keybinding.
2024-03-17 07:53:38 +01:00
Stefan Haller
e42cbf95ae When checking out a remote branch by name, ask the user how
The choices are to create a new local branch that tracks the remote, or a
detached head.
2024-03-17 07:53:38 +01:00
Stefan Haller
0360b82aab Don't ask to force-push if the remote rejected updates (#3387)
Lazygit has two ways to decide whether it needs to ask the user to
force-push:
1. if it knows ahead of time that the push will fail because the branch
has diverged, by looking at the incoming/outgoing information that it
shows as ↑3↓7.
2. by examining the error that comes back when the push has failed.

The second situation should happen only rarely, because lazygit fetches
every minute by default, so the ↑3↓7 information is usually up to date.
It might not be if the user turned off auto-fetch (or increased the
auto-fetch interval). However, in this case it's almost always harmful
to prompt the user to force-push, because we know that the reason for
diverging is that something was pushed to the remote, and we would wipe
it out by force-pushing. In such a situation, the more likely user
action is to pull the remote changes and then push normally again.

So just remove the second prompt, and replace it by a better error
message when we detect that updates were rejected remotely.

A little bit of history archeology reveals that the second prompt was
added at a time where we didn't have the first one yet, so at that time
it made sense to have it; but when the first prompt was added, we should
have removed the second.
2024-03-17 07:42:34 +01:00
Stefan Haller
81b497d186 Don't ask to force-push if the remote rejected updates
Lazygit has two ways to decide whether it needs to ask the user to force-push:
1. if it knows ahead of time that the push will fail because the branch has
diverged, by looking at the incoming/outgoing information that it shows as ↑3↓7.
2. by examining the error that comes back when the push has failed.

The second situation should happen only rarely, because lazygit fetches every
minute by default, so the ↑3↓7 information is usually up to date. It might not
be if the user turned off auto-fetch (or increased the auto-fetch interval).
However, in this case it's almost always harmful to prompt the user to
force-push, because we know that the reason for diverging is that something was
pushed to the remote, and we would wipe it out by force-pushing. In such a
situation, the more likely user action is to pull the remote changes and then
push normally again.

So just remove the second prompt, and replace it by a better error message when
we detect that updates were rejected remotely.

A little bit of history archeology reveals that the second prompt was added at a
time where we didn't have the first one yet, so at that time it made sense to
have it; but when the first prompt was added, we should have removed the second.
2024-03-17 07:38:58 +01:00
Stefan Haller
e9d050c5d5 Allow moving and deleting update-ref todos (#3391)
- **PR Description**

Previously it wasn't possible to move an update-ref entry up or down
using ctrl-j and ctrl-k, or to delete an update-ref entry. For moving, a
work-around was to move the surrounding commits instead, but it's still
nice to be able to do it directly. Deleting is very much necessary
though, since there are situations where git adds the update-ref entries
but they are not wanted; one example is if you want to make a copy of a
branch and rebase to a different place, without the original branch
following it. (There's a long discussion about this
[here](https://public-inbox.org/git/adb7f680-5bfa-6fa5-6d8a-61323fee7f53@haller-berlin.de/).)

Update-ref todos can't be set to "drop" like other todos, because they
have no sha associated with them, so we need to delete them immediately.
We show a confirmation before doing that, because you'd have to abort
the rebase if you do it accidentally.

We allow range selecting normal todos and update-ref todos at the same
time; in that case, we delete all the update-ref todos and set all the
other ones to "drop". Not that this is an absolutely necessary feature,
but it wasn't hard to do.
2024-03-17 07:38:25 +01:00
Stefan Haller
5c56bc7015 Set mode to none when calling SetSelectionRangeAndMode with empty non-sticky range
So far, the only situation where we called SetSelectionRangeAndMode was one
where the range could only get larger (in startInteractiveRebaseWithEdit, in
which case update-ref todos can be inserted by the rebase). However, in the last
commit we introduced a new call site where the range can get smaller, including
being reduced to a single item. Since this is indistinguishable from a single
selection, set the mode to none in this case; without this, hitting escape would
seemingly do nothing because it collapses the empty range selection.
2024-03-16 22:01:13 +01:00
Stefan Haller
0608fc6471 Allow deleting update-ref todos 2024-03-16 22:01:13 +01:00
Stefan Haller
64a1a455d6 Extract a findTodo helper function
We will reuse it in the next commit.
2024-03-16 22:01:03 +01:00
Stefan Haller
bd975a8dcb Allow moving update-ref todos up/down 2024-03-16 22:01:03 +01:00
Stefan Haller
e5fa9e1c4a Store full ref in Name field of update-ref commits
Strip the prefix at presentation time instead. This makes it easier to find
update-ref todos in order to move them up/down, or delete them.
2024-03-16 15:48:34 +01:00
Stefan Haller
e8d84a1f2c Refactor: pass Todo to moveTodoUp/Down instead of Sha and Action
We need this because we want to enable moving update-ref todos, which don't have
a sha.
2024-03-16 15:21:17 +01:00
Stefan Haller
fd18db6ba2 Show "breaking changes" message at startup (#3377)
- **PR Description**

Remember which version of lazygit the user was last running, and show a
list of breaking changes since that version (if any) if the user
upgraded to a newer version.

It's a little unobvious how to test this manually, because we don't show
the popup for developer builds. You'll have to build with something like
`go build -ldflags="-X 'main.version=0.41.0'"` in order to test it.

This is an extremely stripped down version of #3261.
2024-03-12 13:30:19 +01:00
Stefan Haller
36fa25fa6d Handle mouse-wheel scrolling in confirmation panel
This can easily happen for the breaking changes panel when there are many.
2024-03-12 13:27:14 +01:00
Stefan Haller
2f4437591e Show popup message with breaking changes on startup 2024-03-12 13:27:14 +01:00
Stefan Haller
d12ceeb1ec Add Co-Author support to new commits (#3097)
- Adds Co-Author support to commit menu (`<C-o>` by default) 
  - `e` Opens up the commit message in your editor
  - `c` Lets you add a co author to your commit
- Cleans up and amend commit attribute menu related code
2024-03-12 08:40:47 +01:00
Stefan Haller
1ec87364fe Add integration test 2024-03-11 09:19:11 +01:00
Abhishek Keshri
7c687938a5 Add commit menu entry "Add co-author" 2024-03-11 09:18:55 +01:00
Abhishek Keshri
744519de60 Add a commit menu to the commit message panel
And move the "switch to editor" command into this menu. So far this is the only
entry, but we'll add another one in the next commit.
2024-03-11 09:18:40 +01:00
Stefan Haller
b8f4cd0ef6 Extract functions AddCoAuthorToMessage and AddCoAuthorToDescription
In this commit we only need the former; the latter will be reused later in this
branch.
2024-03-11 09:18:40 +01:00
Stefan Haller
287195b5b2 Make test assertion more specific
It's safe to rely on git padding the log messages with exactly four spaces (I
think). This makes the diff of the following commit slightly clearer.
2024-03-11 09:18:40 +01:00
Abhishek Keshri
e1b341e174 Make keybindings for the "Amend attribute" menu configurable 2024-03-11 09:18:40 +01:00
Abhishek Keshri
b1523c3f07 Internationalize the tooltips of the "Amend commit attributes" menu 2024-03-11 09:18:40 +01:00
Abhishek Keshri
c3c5753a35 Add forgotten keybindings to Config.md 2024-03-11 09:18:40 +01:00
Jesse Duffield
272e41929c Update sponsors in readme 2024-03-09 20:48:33 +11:00
Stefan Haller
ac4767bb2a Auto-wrap commit message while typing (#3173)
- **PR Description**

Add new config settings `git.commit.autoWrapCommitMessage` (default
true) and `git.commit.autoWrapWidth` (default 72), which allow automatic
as-you-type wrapping of the commit message body to the specified width.

There are occasional situations where this wrapping is in the way, for
example when you need to have longer lines in the message for some
reason (perhaps because you have a very wide ASCII art picture or
table), and you'll have to resort to switching to the editor in that
case. However, in my experience these cases are quite rare.
2024-03-09 10:06:29 +01:00
Stefan Haller
d1f8c45099 Add integration test 2024-03-09 10:00:44 +01:00
Stefan Haller
41a68f7c4a Remove hard line breaks when rewording commits
... and when recalling a commit message from an old commit by pressing up-arrow.

This is necessary because committing turns our soft line breaks into real ones,
but when rewording we want to turn them back into soft ones again, so that it's
possible to insert words at the beginning of a paragraph and have everything
rewrap nicely.

This is only a best effort; the algorithm only removes those hard line breaks
that can be removed without changing the way the message looks. This works well
when the previous commit message was wrapped at the same width, which for most
users should be the most common case; but if it wasn't, the result is not great.
Specifically, if the old wrap width was smaller, some hard line breaks just
won't be removed; if it was wider though, you'll get an unpleasant comb effect
with alternating long and short lines. In such a case it's best to switch to the
editor and use whatever wrapping features you have there (e.g. alt-Q).
2024-03-09 10:00:44 +01:00
Stefan Haller
944d82028f Replace DOS linefeeds with Unix line feeds when loading a commit message
I have seen some commit messages that contain CRLF instead of just LF; I'm not
sure if these were created by a broken git client, but they exist, so we need to
deal with them. Editing them when rewording a commit sort of works, but is a
little strange; the \r characters are invisble, so you need an extra arrow key
press to skip over them.

In the next commit we are going to add more logic related to line breaks, and it
is getting confused by the \r, so it is becoming more important to fix this. The
easiest fix is to normalize the line endings right after loading.
2024-03-09 10:00:44 +01:00
Stefan Haller
379a6f1922 Save and restore the unwrapped description
When preserving the commit message (when cancelling a commit), and later
restoring it, use the unwrapped description.
2024-03-09 10:00:44 +01:00
Stefan Haller
cede021400 Add config for soft-wrapping the commit message body 2024-03-09 10:00:44 +01:00
Stefan Haller
99ad6005e8 Bump gocui 2024-03-09 10:00:07 +01:00
Stefan Haller
dc9ee186f4 Adjust selection after squashing fixups (#3338)
- **PR Description**

Keep the same commit selected after squashing fixup commits, and after
creating fixup commits.

Edge case: it is now possible to have a range of commits selected when
squashing fixups (by using the "in current branch" version of the
command), and in that case we don't bother preserving the range. It
would be possible, but would require more code, and I don't think it's
worth it, so I'm simply collapsing the range in that case.
2024-03-09 07:58:14 +01:00
Stefan Haller
bb26979420 Keep the same line selected after squashing fixup commits
This uses a bit of a heuristic that is hopefully correct most of the time.
2024-03-09 07:55:22 +01:00
Stefan Haller
c6d20c876e Extract common code to a helper method
This should arguably have been done in b133318b40 already; it's becoming more
important now because we're going to extend the common code with more logic in
the next commit.
2024-03-09 07:55:22 +01:00
Stefan Haller
3e3b902228 Move selection down by one after creating a fixup commit 2024-03-09 07:55:22 +01:00
Stefan Haller
dfb45ba893 Extend squash_fixups_in_current_branch test to check the selection
This shows a problem with the wrong commit being selected after squashing.
2024-03-09 07:55:22 +01:00
Stefan Haller
314efe2539 Add test for creating a fixup commit and squashing fixups
We have such a test already (squash_fixups_above_first_commit.go), but it can't
be used for what we want to check here, because it uses the first commit, and we
can't move down from there. So create a new one that basically does the same
thing, but for a commit in the middle. The focus of this new test is to check
how the selection behaves; as you can see, there is a problem both when creating
a fixup and when squashing fixups. We'll address these separately in the next
commits.
2024-03-09 07:55:22 +01:00
Stefan Haller
bbc680266b Support setting a range of commits to "edit" outside of a rebase (#3370)
- **PR Description**

It is now possible to select a range of commits (while not in a rebase),
and hit "e" to edit them all. This starts a rebase on the bottom-most
commit of the range, and sets all the selected commits to "edit"
(skipping merge commits, because they can't be edited).
2024-03-09 07:46:34 +01:00
Stefan Haller
40232440b7 Support setting a range of commits to "edit" outside of a rebase
It starts a rebase on the bottom-most commit of the range, and sets all the
selected commits to "edit" (skipping merge commits, because they can't be
edited).
2024-03-09 07:43:48 +01:00
Stefan Haller
44f553b609 Show all submodules recursively (#3341)
- **PR Description**

Extend the submodules tab to show not only the top-level submodules, but
also their nested submodules, recursively.

Fixes #3306.
2024-03-07 20:20:42 +01:00
Stefan Haller
3b723282cb Show all submodules recursively 2024-03-07 20:16:28 +01:00
Stefan Haller
db4f12929e Pass entire submodule to UpdateUrl instead of name and path separately
This will make the next commit slightly simpler.
2024-03-07 20:16:28 +01:00
Stefan Haller
ea87912a74 Fix deleting submodule where name and path are different 2024-03-07 20:16:28 +01:00
Stefan Haller
ddcd916301 Add test to check that the git dir for a deleted submodule was removed
The test shows that it actually doesn't work when the submodule name is
different from its path. We'll fix this in the next commit.
2024-03-07 20:16:28 +01:00
Stefan Haller
85a6a42bff Extend submodule tests to use a submodule where name and path are different
In most real-world scenarios, name and path are usually the same. They don't
have to be though, and it's important to make sure we use the right one when
passing arguments to git commands, so change the tests to have different name
and path.
2024-03-07 20:16:28 +01:00
Stefan Haller
ad017459d2 Don't strike out reserved keys in menus (#3365)
It seems to cause more confusion than it helps.

Fixes #2819.
2024-03-07 07:30:08 +01:00
Stefan Haller
cfaf4b29d0 Don't strike out reserved keys in menus
It seems to cause more confusion than it helps.
2024-03-06 18:41:27 +01:00
Stefan Haller
8d9e5d1258 Bump gocui (#3356)
This resolves the regression from the last gocui bump that strikethrough
no longer worked.

Fixes #3355.
2024-03-03 18:46:50 +01:00
Stefan Haller
212ec7e8e5 Bump gocui
This resolves the regression from the last gocui bump that strikethrough no
longer worked.
2024-03-03 18:41:03 +01:00
Stefan Haller
0f14065245 Remove support for old style non-interactive rebase (#3348)
When doing a non-interactive rebase using a version of git earlier than
2.26, or by explicitly calling `git -c rebase.backend=apply rebase`,
lazygit can display the pending todos by parsing the numbered patch
files in `.git/rebase-apply/`. Unfortunately, support for this has been
broken for more than three years because of the change in 682db77401
(the string literal "normal" should have been changed to
REBASE_MODE_NORMAL instead of REBASE_MODE_MERGING).

It's not an important bug since you can only get into this situation by
doing a rebase outside of lazygit, and then only with a pretty old git
version or by using very uncommon git options. So instead of fixing the
bug, just remove the code.
2024-03-02 10:28:37 +01:00
Stefan Haller
eb0f7e3d02 Remove support for old-style non-interactive rebases
When doing a non-interactive rebase using a version of git earlier than 2.26, or
by explicitly calling `git -c rebase.backend=apply rebase`, lazygit can display
the pending todos by parsing the numbered patch files in `.git/rebase-apply/`.
Unfortunately, support for this has been broken for more than three years
because of the change in 682db77401 (the string literal "normal" should have
been changed to REBASE_MODE_NORMAL instead of REBASE_MODE_MERGING).

It's not an important bug since you can only get into this situation by doing a
rebase outside of lazygit, and then only with a pretty old git version or by
using very uncommon git options. So instead of fixing the bug, just remove the
code.
2024-03-02 10:26:15 +01:00
Stefan Haller
e9b04b8cfc Allow more than one argument in git.merging.args config (#3336)
- **PR Description**

Fix a bug where merging would fail with an error message when you try to
put more than one argument in the `git.merging.args` config. This broke
with 25f8b0337e.

Fixes #3334.
2024-03-02 10:24:36 +01:00
Stefan Haller
253a0096f9 Break git.merging.args config into separate arguments on whitespace
This makes it possible again to pass multiple arguments, for example
"--ff-only --autostash". This won't work correctly if you want to use
an argument that contains a space, but it's very unlikely that people
will want to do that, so I think this is good enough.
2024-03-02 10:22:01 +01:00
Stefan Haller
aa81c456bb Add a test that demonstrates a bug with multiple args in git.merging.args config
We are currently passing the whole string as a single argument, which doesn't
work of course.
2024-03-02 10:22:01 +01:00
Stefan Haller
ee5533f9bf Don't show branch head on rebase todos if the rebase.updateRefs config is on (#3340)
- **PR Description**

When doing an interactive rebase of a stack of branches with the
`rebase.updateRefs` git config set to true, you get `update-ref` todos
between the "pick" items. In this situation we would still show the
branch head icon for each pick item that used to be the head of a
branch. This is confusing, especially when you want to move a new commit
to the head of a branch in the middle of the stack. Consider the
following scenario:
<img width="410" alt="image"
src="https://github.com/jesseduffield/lazygit/assets/1225667/438b7157-e51e-48fb-95a9-b67039a7ad30">
Here we have made a new commit at the top of the stack, entered
interactive rebase, and moved the commit down to the head of the second
branch. Now it sits between the commit that shows the branch icon of the
second branch, and the update-ref item for the second branch. This is
super confusing, and it's simply better to not show branch icons for
pick entries. That's what this PR does.

We do show the icons if the `rebase.updateRefs` config is off, because
otherwise there would be no indication of where the branches start and
end. Of course, changing anything in this case will destroy the stack,
so maybe it would be better to hide the icons in this case too to make
this more obvious. I'm unsure about that.
2024-03-02 10:20:23 +01:00
Stefan Haller
416a40b0e6 Don't show branch head on rebase todos if the rebase.updateRefs config is on
The additional branch head icon is more confusing than useful in this situation.
The update-ref entries show very clearly where the branch heads will go when
continuing the rebase; the information where the branch heads used to be before
the rebase is not really needed here, and just makes the display more confusing.

I'm not adding more tests here because the changes to the existing tests
demonstrate the change clearly enough.
2024-03-02 10:17:58 +01:00
Stefan Haller
418b316fab Rename showBranchMarkerForHeadCommit parameter to hasRebaseUpdateRefsConfig
Name it after what it is rather than what it is used for. In the next commit we
will use it for another condition.
2024-03-02 10:17:58 +01:00
Stefan Haller
fd8ce7d779 Add test demonstrating the current (undesired) behavior 2024-03-02 10:17:58 +01:00
Stefan Haller
ed34ddc04d Provide two helix presets, one for "helix" and one for "hx" (#3346)
The helix binary seems to be called "helix" on some distributions (e.g.
Arch), but "hx" on others (e.g. Fedora). Provide presets for both, so
that auto-detection from $EDITOR works.

See
https://github.com/jesseduffield/lazygit/issues/2624#issuecomment-1966509979.
2024-03-02 10:16:58 +01:00
Stefan Haller
581161ac34 Provide two helix presets, one for "helix" and one for "hx"
The helix binary seems to be called "helix" on some distributions (e.g. Arch),
but "hx" on others (e.g. Fedora). Provide presets for both, so that
auto-detection from $EDITOR works.
2024-03-02 10:15:03 +01:00
Stefan Haller
69c9477783 Make ctrl-f available in staging view (#3349)
- **PR Description**

A common workflow for me is to create a fixup commit from only some of
my current changes; to do that, I enter a file, stage a few hunks, and
then want to invoke ctrl-f to find the base commit for these changes.
Currently I need to esc back to the files panel in order to do that;
it's more convenient to be able to do this right from the staging panel.

Labelled as "ignore-for-release" because the ctrl-f feature has only
been added after the last release, so no release notes needed for this
change.
2024-03-02 10:13:55 +01:00
Stefan Haller
98d6504d1d Make ctrl-f available in staging view
A common workflow for me is to create a fixup commit from only some of my
current changes; to do that, I enter a file, stage a few hunks, and then want to
invoke ctrl-f to find the base commit for these changes. Currently I need to esc
back to the files panel in order to do that; it's more convenient to be able to
do this right from the staging panel.
2024-03-02 10:12:03 +01:00
Stefan Haller
afb9980ad9 Fix linter warnings (#3351)
These started to pop up in my editor after I installed an update of
gopls, I think.
2024-03-02 10:11:44 +01:00
Stefan Haller
e3809f3111 Fix linter warnings
These started to pop up in my editor after I installed an update of gopls, I
think. It's unfortunate that we don't see them on CI.
2024-03-02 10:09:32 +01:00
Stefan Haller
fe7d1847b7 Fix display of Chinese characters on Windows (#3352)
Bump our gocui dependency, which in turn bumps tcell to v2.7.1, which
should fix problems with multibyte characters on Windows.

Fixes #2741.
2024-03-02 10:08:55 +01:00
Stefan Haller
ad0394aebe Bump gocui
The main change here is to bump tcell to v2.7.1, which should fix problems with
multibyte characters on Windows.
2024-03-01 14:07:19 +01:00
Stefan Haller
af56065dd6 Add author filtering to commit view (#3302)
- **PR Description**

This PR introduces a new feature to the commit view, allowing users to
filter commits based on the author's name or email address. Similar to
the existing path filtering functionality, accessible through `<c-s>`,
this feature allows users to filter the commit history by the currently
selected commit's author if the commit view is focused, or by typing in
the author's name or email address.

This feature adds an entry to the filtering menu, to provide users with
a familiar and intuitive experience


![filter-by-author](https://github.com/jesseduffield/lazygit/assets/3098462/5b00a716-e432-4204-8568-0e93b1411bc7)

- **Please check if the PR fulfils these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc
2024-02-21 10:00:49 +01:00
Tristan Déplantes
503422a72e Add author filtering to commit view
This commit introduces a new feature to the commit view, allowing users
to filter commits based on the author's name or email address. Similar
to the existing path filtering functionality, accessible through <c-s>,
this feature allows users to filter the commit history by the currently
selected commit's author if the commit view is focused, or by typing in
the author's name or email address.

This feature adds an entry to the filtering menu, to provide users with
a familiar and intuitive experience
2024-02-21 09:58:09 +01:00
Stefan Haller
329b434915 Change "git reset" default to --mixed (#3264)
Calling "git reset" on the command line (without further arguments)
defaults to --mixed, which is reason enough to make it the default for
us, too.

But I also find myself using --mixed more often than --soft. The main
use case for me is that I made a bunch of WIP commits, and want to turn
them into real commits when I'm done hacking. I select the last commit
before the WIP commits and reset to it, leaving all changes of all those
commits in the working directory. Since I want to start staging things
from there, I prefer those modifications to be unstaged at that point,
which is what --mixed does.
2024-02-21 09:57:31 +01:00
Stefan Haller
9362aede8f Add tooltips for reset menu items 2024-02-21 09:55:04 +01:00
Stefan Haller
dfabe8db70 Change "git reset" default to --mixed
Calling "git reset" on the command line (without further arguments) defaults to
--mixed, which is reason enough to make it the default for us, too.

But I also find myself using --mixed more often than --soft. The main use case
for me is that I made a bunch of WIP commits, and want to turn them into real
commits when I'm done hacking. I select the last commit before the WIP commits
and reset to it, leaving all changes of all those commits in the working
directory. Since I want to start staging things from there, I prefer those
modifications to be unstaged at that point, which is what --mixed does.
2024-02-21 09:55:04 +01:00
Stefan Haller
b0f3bb7a9a Use $XDG_STATE_HOME for state.yml (#2936)
- **PR Description**

fixes #2856, #2794, #818
2024-02-18 15:54:55 +01:00
Stefan Haller
c7c8776371 Update Config.md 2024-02-18 15:52:06 +01:00
Ching Pei Yang
7cef4f4e33 Change log path to state dir 2024-02-18 15:36:56 +01:00
Ching Pei Yang
2c0520bc4b Use $XDG_STATE_DIR for state.yml 2024-02-18 15:36:56 +01:00
Ching Pei Yang
2118ecdf8e Switch to github.com/adrg/xdg 2024-02-18 15:36:56 +01:00
Stefan Haller
4302018437 Fix some problems with patches if git diff was customized with config (e.g. external or noprefix). (#3222)
- **PR Description**

I encountered the problem that I couldn't extract changes into a new
commit because I had difftastic as an external git tool configured.

Add `diff.noprefix=false` config Option and also specify `--no-ext-diff`
when doing the `git diff` after applying a patch.
This fixes #3107.
Though, there might be other config options that can cause problems, but
fixing these common cases should be an improvement nevertheless.
2024-02-18 15:34:09 +01:00
Stefan Haller
236f42879c Add integration test
The test would fail without the fixes in the previous commits; even if
only one of the configs is set.
2024-02-18 15:24:09 +01:00
Stefan Haller
d329c92554 Set diff.noprefix=false for all other diff commands too
This fixes problems with being able to stage things in a custom patch correctly.
2024-02-18 15:22:43 +01:00
Matthias Richerzhagen
afbaf82395 Fix problems with patches if git diff was customized with config. 2024-02-18 15:22:43 +01:00
Stefan Haller
a2ff2e6dd9 Keep sort order when filtering lists sorted by date (#3282)
- **PR Description**

Keep the sort order stable when filtering lists of things sorted by date
(branches, stashes, reflog entries).
2024-02-16 13:57:36 +01:00
Stefan Haller
9f0b4d0000 Don't omit section headers when filtering the keybindings menu 2024-02-16 13:51:15 +01:00
Stefan Haller
649048c336 Optionally keep sort order stable when filtering lists
For some lists it is useful to keep the same sort order when filtering (rather
than sorting by best match like we usually do). Add an optional function to
FilteredList to make this possible, and use it whenever we show lists of things
sorted by date (branches, stashes, reflog entries), as well as menu items
because this allows us to keep the section headers in the keybindings menu,
which is useful for understanding what you are looking at when filtering.
2024-02-16 13:51:15 +01:00
Stefan Haller
57ac9c2189 Bump required go version to 1.21
We'll need this to use the slices.Sort function in the next commit. It would
also be possible to use sort.Ints instead, but it's slower.
2024-02-16 13:51:15 +01:00
Stefan Haller
c4eedec9d5 Fix initial scroll bar size again (#3285)
After #3283 we need to read more lines initially so that the scrollbar
goes to its minimal height of 1 for long diffs. Without this, it would
start with a height of 2 and then become smaller after you scroll down
half the window height.

It does mean that we need to read twice the number of lines initially
(up to the limit of 5000). I think it's worth it, I find the incorrect
initial size confusing.
2024-02-16 13:50:50 +01:00
Stefan Haller
2b9cb3a640 Fix number of lines to read from a task initially for the right scroll bar size
After #3283 we need to read more lines initially so that the scrollbar goes to
its minimal height of 1 for long diffs. Without this, it would start with a
height of 2 and then become smaller after you scroll down half the window
height.
2024-02-16 13:48:27 +01:00
Stefan Haller
1081c45397 Fix order of custom commands history (#3286)
- **PR Description**

Fix order problems when saving custom commands history

This fixes two problems:
- each time the custom commands panel was opened, the history of commands would
  be shown in reversed order compared to last time. (The reason is that
  lo.Reverse modifies the slice in place rather than just returning a new,
  reversed slice.)
- when executing a previous command again (either by typing it in again, or by
  picking it from the history), it should move to the beginning of the history,
  but didn't.

We fix this by storing the history in reversed order (as the user sees it in
the panel), this makes the logic simpler. We just have to prepend rather
than append newly added commands now.

While this is theoretically a breaking change, it's not worth bothering because
the order was wrong for existing users in 50% of the cases anyway.
2024-02-16 13:46:04 +01:00
Stefan Haller
6c6201ab04 Fix order problems when saving custom commands history
This fixes two problems:
- each time the custom commands panel was opened, the history of commands would
  be shown in reversed order compared to last time. (The reason is that
  lo.Reverse modifies the slice in place rather than just returning a new,
  reversed slice.)
- when executing a previous command again (either by typing it in again, or by
  picking it from the history), it should move to the beginning of the history,
  but didn't.

We fix this by storing the history in reversed order (as the user sees it in
the panel), this makes the logic simpler. We just have to prepend rather
than append newly added commands now.

While this is theoretically a breaking change, it's not worth bothering because
the order was wrong for existing users in 50% of the cases anyway.
2024-02-16 13:31:37 +01:00
Stefan Haller
ea42275f06 Simplify saving app state 2024-02-16 13:31:37 +01:00
Stefan Haller
5b567f3774 Migrate git.log.showGraph and git.log.order to app state (#3197) 2024-02-16 13:29:24 +01:00
Stefan Haller
7f4a05debf Redraw commits view when showGraph setting changes 2024-02-16 13:23:35 +01:00
Alex March
e354a9bb48 Deprecate git.log.showGraph and git.log.order config
Added identical properties to AppState that should eventually have their defaults set.
2024-02-16 13:23:35 +01:00
Stefan Haller
b01bad7fad Fix two problems related to update-ref rebase todo items (#3290)
- **PR Description**

This fixes two loosely related problems with `update-ref` todos:
1. Panic when hitting enter on an `update-ref` item
2. When selecting an `update-ref` item and then triggering a refresh,
there was a bogus error message `fatal: ambiguous argument '': unknown
revision or path not in the working tree.`
2024-02-16 13:10:05 +01:00
Stefan Haller
3b7f32db95 Avoid crash when hitting enter on an update-ref todo 2024-02-16 13:06:24 +01:00
Stefan Haller
d3804d313c Fix a problem with refreshing while an update-ref todo is selected
Scenario:
- show the files of a commit, escape out of it again
- start an interactive rebase of a stack of branches, with the rebase.updateRefs
  git config set to true
- select one of the update-ref todos
- trigger a refresh (either manually or by bringing lazygit's terminal window to
  the front)

This results in an error message "fatal: ambiguous argument '': unknown revision
or path not in the working tree."

Fix this by putting another band-aid on the check for the commit files refresh.

This is the easiest way to fix the problem, but I don't think it's the best one.
We shouldn't be refreshing the commit files context at all if it isn't visible,
because it's pointless; there's no way to switch to it again except by calling
viewFiles again with a specific ref. But I'm too lazy too figure out how to do
that right now.
2024-02-16 13:01:04 +01:00
Jesse Duffield
8746c3d9e0 Support range select removing files from a commit (#3276)
- **PR Description**
Support adding range select for removing multiple files from a commit.
Closes #3260.

The approaches I saw were to either modify
`RebaseCommands.DiscardOldFileChanges` to accept multiple files or do
the file removals as part of the custom patch workflow. I ended up going
with the second way, but I'd be happy to re-implement with the first
approach if that's preferred.

I added a test that handles several different situations when removing
commit files, and updated the original test for removing a single file
from a commit.

I changed how the user gets informed when removing files from a commit.
The previous version had 2 prompts that I combined into 1 (because those
two situations are now possible to see at the same time), and I added
the total number of files that will be affected. This feature seems like
it could cause some real damage if used improperly, so I'm trying to let
the user know as much as possible.

There are 2 or 3 i18n strings that I removed because they're no longer
used. `RebaseCommands.DiscardOldFileChanges` isn't used anywhere any
more, but I left it in there anyway.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-02-16 21:15:32 +11:00
Aaron Hoffman
c431698dba Fix range select bug
After discarding file changes from the commit, the was still referencing
these indexes as being part of the range select. The consequence was
needing to hit escape twice to exit commit files in some situations.
Canceling the range select after discarding changes fixes that.
2024-02-13 09:10:15 -06:00
Aaron Hoffman
d138f7ce86 Clean up test case
I'm combining the delete single file case from `discard_old_file_change`
with the content of `discard_range_select` and calling that
`discard_old_file_changes`. Hopefully that cleans things up a little
bit.

This also adds a check that the custom patch is getting reset properly.
2024-02-13 09:10:15 -06:00
Aaron Hoffman
62a0c895b4 Cleanup
The waiting status shouldn't happen until after the user has responded
to the popup.

Since we're not giving a standalone prompt about clearing the patch, all
of the business in `discard` doesn't need to be in a function any more
2024-02-13 09:10:15 -06:00
Aaron Hoffman
15d5261933 Support range select removing files from a commit 2024-02-13 09:10:15 -06:00
Stefan Haller
beb730a9e6 Change default of git.log.showGraph to 'always' (#3314)
Change default of git.log.showGraph to 'always'; most people seem to
prefer it to be on.

Fixes #3312.
2024-02-13 14:41:01 +01:00
Stefan Haller
b1d05b6371 Change default of git.log.showGraph to 'always'
Most people seem to prefer it to be on.
2024-02-13 14:34:40 +01:00
Stefan Haller
0c5212ae0b Disallow cherry-picking merge commits (#3316)
Cherry-picking merge commits is currently not supported because of the
way copy/pasting is currently implemented. Disable the command with a
proper error message if the user tries to copy a merge commit, instead
of running into the confusing error message that git would otherwise
give, see #1374.

While we're at it, disable it also when trying to copy an "update-ref"
todo, which doesn't make sense.
2024-02-12 12:03:23 +01:00
Stefan Haller
bc6616d511 Disallow cherry-picking merge commits 2024-02-10 11:27:28 +01:00
Stefan Haller
2c82b3f8dd Disallow cherry-picking update-ref todos 2024-02-10 11:27:28 +01:00
Stefan Haller
dc40fb5267 Cleanup: remove unused method 2024-02-10 11:05:09 +01:00
Stefan Haller
49df908dd3 Fix cherrypick demo (#3287)
Cherrypick selections are now cleared after pasting (#3240), so the demo
needs a tiny change to reflect that.

- **PR Description**
The cherry pick demo is failing after the changes in #3240. This is just
a small update to that demo to reflect the new (and more convenient)
behavior from #3240.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-30 17:10:10 +01:00
Aaron Hoffman
fdc54b7455 Fix cherrypick demo
Cherrypick selections are now cleared after pasting (#3240), so the demo
needs a tiny change to reflect that.
2024-01-30 09:27:44 -06:00
Stefan Haller
607034a61e Clear cherry-picked commits after pasting (#3240)
It can be tedious after each cherry-pick opearation to clear the
selection by pressing escape in order for lazygit to stop displaying
info about copied commits. Also, it seems to be a rare case to
cherry-pick commits to more than one destination.

The simplest solution to address this issue is to clear the selection
upon paste, including merge conflict scenario.
Previously discussed in #3198.
2024-01-30 08:59:38 +01:00
molejnik88
ee173ff7c9 Clear cherry-picked commits after pasting
It can be tedious after each cherry-pick opearation to clear the
selection by pressing escape in order for lazygit to stop displaying
info about copied commits. Also, it seems to be a rare case to
cherry-pick commits to more than one destination.

The simplest solution to address this issue is to clear the selection
upon paste.

The only exception is a merge conflict. Initially, I wanted to clear
selected commits in this scenario too. During a discussion we found out
that it may be convenient to have the copied commits still around.
Aborting the rebase and pasting the commits in the middle of a branch
can be a valid use case.
2024-01-30 09:21:12 +11:00
Jesse Duffield
761c77f5a2 Use slimmer scrollbars (#3283)
(hopefully) fixes https://github.com/jesseduffield/lazygit/issues/1924

The previous scrollbars were too chunky and encroached too much on a
view's content.

The new ones are slim and right-aligned so they encroach into dead space
between views which is much better.

Before:

![image](https://github.com/jesseduffield/lazygit/assets/8456633/fe125804-0cb8-4f9b-be4c-5710fd73ac14)


After:

![image](https://github.com/jesseduffield/lazygit/assets/8456633/8ae18f96-e2fd-46ff-be9a-09e745da1177)

Admittedly, the original scrollbars look more like typical scrollbars
and do a better job at telling you where the start and end of the
scrollbar is. We experimented with having special characters for
scrollbar bounds in the slim version but it all looked pretty ugly, and
there's just not many ascii characters to choose from.

Based on gocui PR: https://github.com/jesseduffield/gocui/pull/45

The important changes are in 
vendor/github.com/jesseduffield/gocui/gui.go
vendor/github.com/jesseduffield/gocui/scrollbar.go

The other changes are just modules being updated.

- **PR Description**

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-30 08:49:36 +11:00
Jesse Duffield
f9e8428061 Use slimmer scrollbars
The previous scrollbars were too chunky and encroached too much on a view's content.

The new ones are slim and right-aligned so they encroach into dead space between views
which is much better
2024-01-30 08:44:03 +11:00
Stefan Haller
9d840088ac Add command to squash all fixups in the current branch (#3274)
Add command to squash all fixups in the current branch.

To do that, change the "Apply fixup commits" command to show a menu with
the two choices "in current branch" and "above the selected commit"; we
make "in current branch" the default, as it's the more useful one most
of the time, even though it is a breaking change for those who are used
to "shift-S enter" meaning "squash above selected".

Fixes #3263.
2024-01-29 10:05:40 +01:00
Stefan Haller
b133318b40 Add command to squash all fixups in the current branch
To do that, change the "Apply fixup commits" command to show a menu with the two
choices "in current branch" and "above the selected commit"; we make "in current
branch" the default, as it's the more useful one most of the time, even though
it is a breaking change for those who are used to "shift-S enter" meaning
"squash above selected".
2024-01-29 09:37:47 +01:00
Stefan Haller
24e79a057c Fix refreshing custom patch view when adding first file to custom patch (#3266)
Fixes #3258.
2024-01-28 10:01:49 +01:00
Stefan Haller
c66667c8c1 Fix main view refresh after adding the first file to a custom patch
This broke with 240948b.
2024-01-28 09:49:56 +01:00
Stefan Haller
e8e7ddea45 Fix typo 2024-01-28 09:49:56 +01:00
Jesse Duffield
6322944a5e Support selecting file range in patch builder (#3259)
- **PR Description**
Adds support for selecting a range of files and adding them to a custom
patch. Closes #3251
The behavior for node selection is the same as used in #3248 because I
copied the approach. Please let me know if there's a mismatch or if
something else is preferred.

I also copied `normalisedSelectedNodes` and
`isDescendentOfSelectedNodes` verbatim, just adapted their signature
types.

It seems like we could share those two functions between
`[]*filetree.CommitFileNode` and `[]*filetree.FileNode` by making those
functions like `func normalisedSelectedCommitNodes[T any](selectedNodes
[]*filetree.Node[T]) []*filetree.Node[T]`. That would require calling
them with a `lo.Map(...)` which returns `node.GetRaw()`, and I feel
weird about giving a different type back to the calling function.

I added a couple of test cases, and all of the existing patch tests pass
for me, but please do let me know if there are any other test cases I
should add.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-28 12:13:42 +11:00
Aaron Hoffman
510f9a1ae1 Support selecting file range in patch builder
test: add move_range_to_index

test: add toggle_range
2024-01-28 12:00:47 +11:00
Jesse Duffield
9b2a5f636a Warn users when attempting to cherry pick with old key (#3271)
I wrote this feature and even I sometimes use the wrong key so I want to
make this very clear to users so that there's no confusion.

We can get rid of this once users have had time to adjust


![image](https://github.com/jesseduffield/lazygit/assets/8456633/8a453eaf-381d-468e-8280-58516eead43a)


- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-28 12:00:03 +11:00
Jesse Duffield
e1aa68a7bd Warn users when attempting to cherry pick with old key
I wrote this feature and even I sometimes use the wrong key so I want
to make this very clear to users so that there's no confusion
2024-01-28 11:48:21 +11:00
Jesse Duffield
cf5d4d4f8e Show better keybinding suggestions (#3203)
- **PR Description**

This PR's goal is to improve discoverability of keybindings in lazygit.
I've have a couple people in real life mention to me that it wasn't
obvious what key to press for viewing rebase options, for example.


This PR:
* shows more keybindings in the options view
* shows certain keybindings prominently in certain modes e.g. 'view
rebase options: m' when mid-rebase.

Before:

![image](https://github.com/jesseduffield/lazygit/assets/8456633/483477ef-93e1-4fd1-af86-3ffa84167f62)


After:

![image](https://github.com/jesseduffield/lazygit/assets/8456633/4c93dd19-f072-45ec-afa6-810727211f66)




- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-28 09:27:01 +11:00
Jesse Duffield
bd7fabef1f Reduce the chance of race condition with list cursor
Before this commit, we had pkg/integration/tests/submodule/add.go
failing with a panic. I'm pretty sure the issue is this: we're now
calling quite a few GetDisabledReason calls on each layout() call,
and if a background thread happens to update a model slice while
we're doing this, we can end up with a selection index that's now
out of bounds because it hasn't been clamped to match the new list
length.

Specifically, here we had the selected index being -1 (the list starts
empty and somehow the value is -1 in this case) and then the list
gets a new submodule so the length is now 1, but the list cursor
doesn't know about this so remains on the old value. Then we confirm
the length is greater than zero and try to get the selected submodule
and get an out of bounds error.

This commit fixes the issue by clamping the selected index whenever
we get the length of the list so that it stays in-sync. This is not
a perfect solution because the length can change at any time, but
it seems to reliably fix the test, and using mutexes didn't seem to
make a difference.

Note that we're swapping the order of IFileTree and IListCursor in
the file tree view model to ensure that the list cursor's Len()
method is called (which performs the clamping).

Also, comment from the PR:
This 'trait' pattern we're using is convenient but can lead to awkward
situations. In this case we have both the list view model and the
(embedded) list cursor with a Len() method. The list cursor Len()
method just calls the list view model Len() method. But I wanted
to make it that the list view model now calls ClampSelection() on the
list cursor whenever it obtains the length. This will cause an
infinite loop because ClampSelection() internally calls Len()
(which calls the list view model's Len() method which in turn
calls ClampSelection() again, etc).

The only reason we were passing the list view model into the list
cursor was to supply the length method, so now we're just doing
that directly, and letting the list view model delegate the Len()
call to the list cursor, which now itself calls ClampSelection.
2024-01-28 09:20:52 +11:00
Jesse Duffield
1a38d515d7 Display more keybindings on-screen 2024-01-28 08:33:13 +11:00
Jesse Duffield
0f9d9e13d1 Show mode-specific keybinding suggestions
As part of making lazygit more discoverable, there are certain keys which you almost certainly
need to press when you're in a given mode e.g. 'v' to paste commits when cherry-picking. This
commit prominently shows these keybinding suggestions alongside the others in the option view.

I'm using the same colours for these keybindings as is associated with the mode elsewhere e.g.
yellow for rebasing and cyan for cherry-picking. The cherry-picking one is a bit weird because
we also use cyan text to show loaders and app status at the bottom left so it may be confusing,
but I haven't personally found it awkward from having tested it out myself.

Previously we would render these options whenever a new context was activated, but now that we
need to re-render options whenever a mode changes, I'm instead rendering them on each screen
re-render (i.e. in the layout function). Given how cheap it is to render this text, I think
it's fine performance-wise.
2024-01-28 08:33:13 +11:00
Jesse Duffield
c07b3fad64 Ensure file view length is never returned as -1
This was causing issues when obtaining selected items
2024-01-28 08:33:13 +11:00
Jesse Duffield
9bf3063457 Add loads of tooltips (#3269)
- **PR Description**

This is cherry-picked from
https://github.com/jesseduffield/lazygit/pull/3203. That PR both adds
tooltips and adds keybinding suggestions, and the latter part is having
some concurrency issues that I want to properly fix before merging.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-28 08:20:51 +11:00
Jesse Duffield
7bddf53223 Improve keybinding descriptions
This adds a bunch of tooltips to keybindings and updates some keybinding descriptions (i.e. labels).

It's in preparation for displaying more keybindings on-screen (in the bottom right of the screen),
and so due in part to laziness it shortens some descriptions so that we don't need to manage both
a short and long description (for on-screen vs in-menu). Nonetheless I've added a ShortDescription
field for when we do want to have both a short and long description.

You'll notice that some keybindings I deemed unworthy of the options view have longer descriptions,
because I could get away with it.
2024-01-28 08:12:01 +11:00
Jesse Duffield
0aa6109d4d Render keybinding cheatsheet as markdown table
We're going to be adding tooltips to the cheatsheet to better explain what each actions
does. As such, we're switching to a table format rather than a list.

I'm also changing how the keys are represented, using a markdown approach rather than
an html approach
2024-01-28 08:12:01 +11:00
Stefan Haller
cb1b13e95e Keep same selection range when quick-starting an interactive rebase (#3247)
This is useful if you want to move a range of commits, so you select
them, and then realize it's better to do it in an interactive rebase. Pressing 'i'
preserves the range now.
2024-01-26 11:26:00 +01:00
Stefan Haller
f9876c9742 Keep same selection range when quick-starting an interactive rebase
This is useful if you want to move a range of commits, so you select them, and
then realize it's better to do it in an interactive rebase. Pressing 'i'
preserves the range now.
2024-01-26 11:18:13 +01:00
Stefan Haller
d28a2ec059 Add tests for preserving the selection when pressing 'i'
Preserving the selection for a non-range selection already works as expected;
however, the test for a selection range shows an undesired behavior.
2024-01-26 11:18:06 +01:00
Stefan Haller
f226a277bf Rename MinMax to SortRange 2024-01-26 11:18:06 +01:00
Jesse Duffield
cce29209be Add shortcuts for filtering files by status (#3137)
- 's' for showing only staged files
- 'u' for showing only unstaged files
- 'r' for resetting the filter

- **PR Description**

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->

I've thought about adding `disabled` reasons for them but they kinda
don't make sense since if there are only `unstaged` files, disabling the
`Show only staged files` makes no sense because the `Show only unstaged
files` shows nothing different so we should disable that too, and then
what's the point of having a menu if everything except for `reset` is
disabled.
2024-01-26 20:57:12 +11:00
Luka Markušić
efb6524fa0 Add shortcuts for filtering files by status
- 's' for showing only staged files
- 'u' for showing only unstaged files
- 'r' for resetting the filter
2024-01-26 20:53:16 +11:00
Stefan Haller
a65f003ccc Inline status for fetching remotes (#3238)
When fetching a remote in the remotes tab, show the status inline (i.e.
in the list row of the remote), like we do when fast-forwarding a branch
in the branches panel.
2024-01-26 08:48:05 +01:00
Stefan Haller
b485363006 Remove unused text FetchingRemoteStatus
We just removed the last use of it.
2024-01-26 08:29:04 +01:00
Stefan Haller
7fb5266027 Use inline status for fetching remotes 2024-01-26 08:29:04 +01:00
Stefan Haller
ae89dde969 Pass absolute file paths to all editor commands (#3255)
- **PR Description**

This helps work around bugs in editors that may get confused about
relative paths (like nvim-remote, see
https://github.com/neovim/neovim/issues/18519), and shouldn't have any
negative effect on others.
2024-01-26 08:26:54 +01:00
Stefan Haller
12500be554 Pass absolute file paths to all editor commands
This helps work around bugs in editors that may get confused about relative
paths (like nvim-remote, see https://github.com/neovim/neovim/issues/18519), and
shouldn't have any negative effect on others.
2024-01-25 08:56:10 +01:00
Jesse Duffield
05b97c8c8e Support range select for staging/discarding files (#3248) 2024-01-25 18:26:24 +11:00
Jesse Duffield
0402674ee7 Fix error message for selected lines
We had the actual and expected lines swapped around erroneously
2024-01-25 11:34:59 +11:00
Jesse Duffield
269ef7f250 Support range select for staging/discarding files
As part of this, you must now press enter on a merge conflict file
to focus the merge view; you can no longer press space and if you do
it will raise an error.
2024-01-25 11:34:59 +11:00
Jesse Duffield
798225d9e1 Move file discard action into files controller
It's better just having all the keybindings in one file especially when you want to
share code
2024-01-24 20:51:00 +11:00
Stefan Haller
b7d4db2446 Use git rev-parse to obtain repository and worktree paths (#3183)
- **PR Description**
This changes GetRepoPaths() to pull information from `git rev-parse`
instead of partially reimplementing git's logic for pathfinding. This
change fixes issues with bare repos, esp. versioned homedir use cases,
by aligning lazygit's path handling to what git itself does. I believe
it also paves the way for lazygit to run from any subdirectory of a
working tree with relatively minor changes.

Addresses #1294 and #3175.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-24 08:47:49 +01:00
John Whitley
3d9f1e02e5 Refactor repo_paths.go to use git rev-parse
This changes GetRepoPaths() to pull information from `git rev-parse`
instead of effectively reimplementing git's logic for pathfinding. This
change fixes issues with bare repos, esp. versioned homedir use cases,
by aligning lazygit's path handling to what git itself does.

This change also enables lazygit to run from arbitrary subdirectories of
a repository, including correct handling of symlinks, including "deep"
symlinks into a repo, worktree, a repo's submodules, etc.

Integration tests are now resilient against unintended side effects from
the host's environment variables. Of necessity, $PATH and $TERM are the
only env vars allowed through now.
2024-01-24 08:40:01 +01:00
Stefan Haller
74d937881e Make range selections created with the mouse non-sticky (#3234)
- **PR Description**

I prefer this because I almost never use sticky range selections, but
I'm not sure everybody agrees. Also, this fixes the issue that just
clicking a line in a diff (without dragging) already creates a range
selection. It still does, technically, but it's no longer a problem
because a non-sticky one-line range selection behaves the same as a
non-range selection.

See #3233.
2024-01-24 08:26:33 +01:00
Stefan Haller
e72c759541 Make range selections created with the mouse non-sticky
I prefer this because I almost never use sticky range selections. Also, this
fixes the issue that just clicking a line in a diff (without dragging) already
creates a range selection. It still does, technically, but it's no longer a
problem because a non-sticky one-line range selection behaves the same as a
non-range selection.
2024-01-24 08:22:55 +01:00
README-bot
9cd69e46df Updated README.md 2024-01-23 22:28:37 +00:00
Jesse Duffield
5511cc170e Support range select for rebase actions (#3232)
- **PR Description**

Adds support for range select in rebase actions both mid-rebase and
outside a rebase i.e.:
* pick
* drop
* fixup
* squash
* move up
* move down

Also includes a refactor to support a withItems wrapper for keybinding
handlers that support a range of items to be selected.

TODO:
* [x] Add integration tests
* [x] Add some LogAction calls (some are currently commented out)

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-24 09:28:22 +11:00
Jesse Duffield
41d5f4dbb5 Disallow updating non-standard TODO lines when rebasing 2024-01-23 17:23:56 +11:00
Jesse Duffield
f0de880136 Support range select in rebase actions 2024-01-23 17:23:56 +11:00
Jesse Duffield
44e2542e4a Better assertion logic for line selection
Previously if we marked a line with IsSelected() we would check if it was selected, but
we would not check if other lines were unexpectedly selected. Now, if you use IsSelected(),
we ensure that _only_ the lines you marked as such are the selected lines.
2024-01-23 13:43:39 +11:00
Jesse Duffield
a5f3515ad8 Set groundwork for better disabled reasons with range select
Something dumb that we're currently doing is expecting list items
to define an ID method which returns a string. We use that when copying
items to clipboard with ctrl+o and when getting a ref name for diffing.

This commit gets us a little deeper into that hole by explicitly requiring
list items to implement that method so that we can easily use the new
helper functions in list_controller_trait.go.

In future we need to just remove the whole ID thing entirely but I'm too
lazy to do that right now.
2024-01-23 13:03:37 +11:00
Stefan Haller
a67ad44781 Add config setting to suppress showing file icons (#3216)
- **PR Description**

Add a config option `gui.showFileIcons` (default: true) which can be set
to false to suppress showing file icons.
2024-01-22 08:43:54 +01:00
Stefan Haller
36134006c5 Add config setting to suppress showing file icons 2024-01-22 08:40:03 +01:00
README-bot
3215839524 Updated README.md 2024-01-22 02:40:34 +00:00
Jesse Duffield
c2218133bc Show file names in default colour (#3081)
Fixes https://github.com/jesseduffield/lazygit/issues/3077

Show unstaged file names in default colour

Previously, we had the following rules:
* file names were in red when unstaged or partially staged
* directory names were in red if unstaged, yellow if partially staged,
and
  green if fully staged

Red text on a black background can be hard to read, so instead I'm
changing it
so that unstaged files have their names in the default text colour.
I'm also making it so that partially staged files are in yellow, just
like how
partially staged directories are yellow (same deal with the commit files
view
when adding to a custom patch).

So the new rules are:
* unstaged files/directories use the default colour
* partially staged files/directories are in yellow
* fully staged files/directories are in green

I've also done a refactor on the code clean up some dead code from when
the file tree
outline was drawn with box characters, and I've made it so that the
indentation in
each line is handled inside the function that draws the line rather than
in the recursive
parent function. This makes it easier to experiment with things like
showing the file
status characters on the left edge of the view (admittedly after
experimenting with it,
I decided I didn't like it). Apologies for having a refactor and a
functional change
in the one commit but by the time I was done, I couldn't be bothered
going back and
retroactively splitting it into two halves.
2024-01-22 13:40:19 +11:00
Jesse Duffield
7c3d8921b7 Show unstaged file names in default colour
Previously, we had the following rules:
* file names were in red when unstaged or partially staged
* directory names were in red if unstaged, yellow if partially staged, and
  green if fully staged

Red text on a black background can be hard to read, so instead I'm changing it
so that unstaged files have their names in the default text colour.
I'm also making it so that partially staged files are in yellow, just like how
partially staged directories are yellow (same deal with the commit files view
when adding to a custom patch).

So the new rules are:
* unstaged files/directories use the default colour
* partially staged files/directories are in yellow
* fully staged files/directories are in green

I've also done a refactor on the code clean up some dead code from when the file tree
outline was drawn with box characters, and I've made it so that the indentation in
each line is handled inside the function that draws the line rather than in the recursive
parent function. This makes it easier to experiment with things like showing the file
status characters on the left edge of the view (admittedly after experimenting with it,
I decided I didn't like it). Apologies for having a refactor and a functional change
in the one commit but by the time I was done, I couldn't be bothered going back and
retroactively splitting it into two halves.
2024-01-22 13:31:05 +11:00
Stefan Haller
221ebdc1fb Keep same branch selected when fetching a branch while sorted by date (#3186)
- **PR Description**

Now that branches can be sorted by date, there's a new situation that we
didn't have before: when fetching a branch, its committer date can
change, so it will move up in the list; we need to update the selection
index to follow. This is important for the case that master is behind
its upstream and you want to rebase your checked-out branch onto master:
in that case you would select master, press "f" to fetch, and then press
"r" to rebase onto it. It's very bad if master doesn't stay selected
after fetching.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [ ] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-19 09:31:21 +01:00
Stefan Haller
2c9b4770bc Keep same branch selected when refreshing branches
This wasn't necessary before, because the only available branch sorting option
was by recency, so the sort order couldn't change except by checking out
branches. Now, you can sort by committer date, so the branch order can change by
fetching; in this case it's important to keep the same branch selected. One
important use case is to rebase the checked-out branch onto master; you select
master, press "f" to fetch it (this can now change its position in the list),
and then press "r" to rebase. To make this work smoothly it's important to keep
master selected after pressing "f".
2024-01-19 09:25:07 +01:00
Stefan Haller
9867180202 Add test showing how branch should stay selected after fetching (but doesn't yet) 2024-01-19 09:23:55 +01:00
README-bot
8dec35ba67 Updated README.md 2024-01-18 23:56:30 +00:00
Jesse Duffield
74e07080d0 Add range selection ability on list contexts (#3207)
- **PR Description**

Issue: https://github.com/jesseduffield/lazygit/issues/3196

This PR adds the ability to select a range of items in list contexts. It
does this in two ways:
* Sticky range select: like what we already have in the staging view,
you press 'v' to toggle range select and then use up/down keys to extend
the range
* Non-sticky range select: rather than explicitly toggling this on/off,
you use shift+up/down to extend the range

The PR adds the ability to range select in all list contexts, but it's
up to individual actions to opt-in to supporting a range. This PR only
supports it for copying a range of commits for cherry-picking. We can
add more support iteratively so that we're not merging a single giant
PR. For all actions requiring selection of a single-item, an error will
be shown if a range is selected.

Other use cases we want to support in the near future:
* marking commits as pick/drop/fixup/squash/etc when mid-rebase
* fixup/squash/drop when outside rebase
* moving commits up/down (in or out of rebase)
* staging/unstaging multiple files
* discarding multiple files

## Updated keybindings

Because the 'v' binding is now globally dedicated to toggling range
select, I've changed the cherry-pick copy/paste keys from 'c' and 'v' to
'shift+C' and 'shift+V' respectively. I've also nullified the 'v'
keybinding on the 'view divergence from upstream' option in the upstream
options menu (conveniently it was the first option in the menu so you
can press enter on it).

## Standardised range select display

As a bonus, this PR standardises how we display a range select. We
already had range select support in the patch explorer view and merge
conflicts view, but they were directly rendering the highlighted
selection (i.e. blue background colour) in the content written to the
view, rather than tell the view which lines were selected and have the
view highlight them itself. A convenient benefit here is that now the
entire line is highlighted, including trailing space, rather than just
the content of the line. Another convenient benefit is that our
integration tests can now easily ask the view which lines are selected,
rather than depending on the specific context, because the view keeps
track of it.

I've removed the selectedRangeBgColor config option because
selectedLineBgColor should be fine. I don't see the need for two
options, but tell me if you think otherwise.

Also, another thing we're standardising on: hitting escape will cancel
the range select, which in the staging/patch-building views means if
you're selecting a range, you'll need to hit escape twice to exit out of
the view. For consistency, we're also applying this logic if you have a
hunk selected. I personally would much prefer this and have several
times accidentally exited out of the view when trying to cancel a range
select by pressing escape. In lazygit in general, 'escape' means 'exit
out of the innermost mode' and I would consider range select to be a
kind of mode.

## Sticky vs non-sticky range interaction

Here's the state machine that explains how the sticky and non-sticky
range select modes interact. Although users will typically pick one or
the other, it's important to be clear on what the logic is if you swap
between them:
```
(no range, press 'v') -> sticky range
(no range, press arrow) -> no range
(no range, press shift+arrow) -> nonsticky range
(sticky range, press 'v') -> no range
(sticky range, press arrow) -> sticky range
(sticky range, press shift+arrow) -> nonsticky range
(nonsticky range, press 'v') -> no range
(nonsticky range, press arrow) -> no range
(nonsticky range, press shift+arrow) -> nonsticky range
```

Also if you press escape in either range mode, it cancels the range
select.

## Some implementation details
* when the action involves toggling e.g. toggling cherry-pick copy or
toggling staged, we decide what to do based on the selection: for
example with staging: if there are any unstaged changes in the
selection, we'll stage everything, otherwise we unstage everything. This
is the logic we already had when staging individual directories.
* we retain range selection if a view loses focus
* where we previously set SetSelectedLineIdx all over the place (e.g.
setting selected line idx to 0 when checking out a branch) we're now
using SetSelection which also resets the range select. There are only a
couple of places where we would still want to use SetSelectedLineIdx
(e.g. when the user moves up/down a page, because they would want to
retain range select in that case)

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [ ] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-19 10:56:16 +11:00
Jesse Duffield
ab3004bcd5 Show unacknowledged toast message upon integration test failure
Often if a test fails and there's an unaknowledged toast message, that message will
explain why the test failed. Given that we don't display toast messages in
integration tests when they run (for reasons I can't recall right now), we need to
log it as part of the error message.
2024-01-19 10:50:49 +11:00
Jesse Duffield
1ea0c270df Disable range-select in menu and suggestions view
We don't need it there so no need to enable it.

I'm leaving the disabled reason checks there, even though they're now redundant,
because they're only one-liners and they communicate intent.
2024-01-19 10:50:49 +11:00
Jesse Duffield
51fb82d6bf Enforce single-item selection in various actions
We want to show an error when the user tries to invoke an action that expects only
a single item to be selected.

We're using the GetDisabledReason field to enforce this (as well as DisabledReason
on menu items).

I've created a ListControllerTrait to store some shared convenience functions for this.
2024-01-19 10:50:49 +11:00
Jesse Duffield
280b4d60f8 Support select range for cherry pick
This requires us to change the 'v' keybinding for paste to something else,
now that 'v' is used globally for toggling range select. So I'm using
'shift+v' and I'm likewise changing 'c' to 'shift+c' for copying, so
that they're consistent.

We will need to clearly communicate this change in keybindings.
2024-01-19 10:50:49 +11:00
Jesse Duffield
54bd94ad24 Add SetSelection function for list contexts and use it in most places
The only time we should call SetSelectedLineIdx is when we are happy for a
select range to be retained which means things like moving the selected line
index to top top/bottom or up/down a page as the user navigates.

But in every other case we should now call SetSelection because that will
set the selected index and cancel the range which is almost always what we
want.
2024-01-19 10:47:21 +11:00
Jesse Duffield
8840c1a2b7 Remove 'v' menu keys
We can no longer use this because 'v' is globally reserved for range select.
Conveniently it was the first item in the menu anyway for both of these.
2024-01-19 10:47:21 +11:00
Jesse Duffield
d08fafb1c4 Clear range select upon pressing 'escape'
This is the highest priority of the escape actions because it's the thing you're
most likely to want to do upon hitting escape if you have a range selected.

Applying this to the staging/patch-building views is tricky: if we want this logic
for when a range of lines is selected, we'll also need to apply it when a hunk
is selected too. I still think it's worth it though: I've often accidentally
escaped from the staging view when trying to cancel a range selection.
2024-01-19 10:47:21 +11:00
Jesse Duffield
f3eb180f75 Standardise display of range selection across views
We're not fully standardising here: different contexts can store their range state however
they like. What we are standardising on is that now the view is always responsible for
highlighting the selected lines, meaning the context/controller needs to tell the view
where the range start is.

Two convenient benefits from this change:
1) we no longer need bespoke code in integration tests for asserting on selected lines because
we can just ask the view
2) line selection in staging/patch-building/merge-conflicts views now look the same as in
list views i.e. the highlight applies to the whole line (including trailing space)

I also noticed a bug with merge conflicts not rendering the selection on focus though I suspect
it wasn't a bug with any real consequences when the view wasn't displaying the selection.

I'm going to scrap the selectedRangeBgColor config and just let it use the single line
background color. Hopefully nobody cares, but there's really no need for an extra config.
2024-01-19 10:47:21 +11:00
Jesse Duffield
c0c3aac02e Support non-sticky range selection in patch explorer views 2024-01-19 10:47:21 +11:00
Jesse Duffield
24a4302c52 Add range selection ability on list contexts
This adds range select ability in two ways:
1) Sticky: like what we already have with the staging view i.e. press v then use arrow keys
2) Non-sticky: where you just use shift+up/down to expand the range

The state machine works like this:
(no range, press 'v') -> sticky range
(no range, press arrow) -> no range
(no range, press shift+arrow) -> nonsticky range
(sticky range, press 'v') -> no range
(sticky range, press arrow) -> sticky range
(sticky range, press shift+arrow) -> nonsticky range
(nonsticky range, press 'v') -> no range
(nonsticky range, press arrow) -> no range
(nonsticky range, press shift+arrow) -> nonsticky range
2024-01-19 10:47:21 +11:00
Jesse Duffield
e887a2eb3c Stop hiding debug vscode launch tasks
No idea why these were hidden in the first place
2024-01-19 10:47:21 +11:00
README-bot
8a94663df1 Updated README.md 2024-01-16 12:24:57 +00:00
Jesse Duffield
8062f96d6b Fix CI erroneously marking failed tests as successful (#3221)
CI is erroneously saying that our integration tests are passing.

As @stefanhaller says in a comment:
> It broke with this commit:
aaecd6cc40.
Since that change, the run_integration_tests.sh script will exit with
the exit status of the the last mv command, not the test run. Should be
pretty easy to fix.

Thanks to Stefan for fixing this

(The reason I'm saying this all here in the PR description is that PR
descriptions now get included in merge commits)
2024-01-16 23:24:39 +11:00
Stefan Haller
cf59b40a1b Fix exit code of run_integration_tests.sh when capturing code coverage data 2024-01-16 18:23:53 +11:00
Stefan Haller
53acbc8f18 Fix crash with short branch names (#3219) 2024-01-15 13:28:36 +01:00
Stefan Haller
36e57d16bd Don't try to shorten branch names that are already 3 characters or less
This fixes a potential crash when the available width is very small and the
branch name is one to three characters long.
2024-01-15 13:27:49 +01:00
Jesse Duffield
080aa78266 Do not include keybindings from another view in keybindings menu (#3220)
Previously we included all navigation keybindings from all views in the
keybindings menu, meaning if you pressed enter on 'next page' in the
commits view, you'd end up triggering the action in the sub-commits
view.

- **PR Description**

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-15 20:17:33 +11:00
Jesse Duffield
fc8998e377 Do not include keybindings from another view in keybindings menu
Previously we included all navigation keybindings from all views in the keybindings menu, meaning
if you pressed enter on 'next page' in the commits view, you'd end up triggering the action
in the sub-commits view.
2024-01-15 20:08:11 +11:00
README-bot
d4a0ca35c7 Updated README.md 2024-01-15 03:45:52 +00:00
Jesse Duffield
f79305090b Show Toast instead of error panel when invoking a disabled command (#3180)
- **PR Description**

Addresses #3116.

I'm not 100% sure I like the behavior, but I put it out there so that
others can test it and form an opinion. It not only affects keybindings,
but also invoking menu items (either with enter or with their key
binding): the menu now stays open in that case, which I think is
actually better.

There's a horrible hack for keeping the integration tests working, I
don't have a good idea how to fix that for real. Suggestions welcome.
2024-01-15 14:45:39 +11:00
Stefan Haller
f133224683 Double the duration of error toasts
This gives users more time to read them.
2024-01-14 17:45:35 +01:00
Stefan Haller
83337d9fa8 Allow showing Disabled errors as error panel instead of toast 2024-01-14 17:45:35 +01:00
Stefan Haller
84e1d15079 Make DisabledReason a struct
This is a pure refactoring, no change in behavior yet. We'll add another field
to the struct in the next commit.
2024-01-14 17:45:35 +01:00
Stefan Haller
09a24ee97d Use ErrorToast instead of error panel when invoking a disabled command 2024-01-14 17:45:35 +01:00
Stefan Haller
99a3ccde71 Add ErrorToast function 2024-01-14 17:45:35 +01:00
Stefan Haller
8ca8a43968 Make it mandatory to acknowledge toasts in tests 2024-01-14 17:42:03 +01:00
Stefan Haller
9fa43394fe Make it possible to handle toasts in integration tests
Use it in two selected tests to demonstrate what it looks like.
2024-01-14 17:42:03 +01:00
Jesse Duffield
37590a495c Add 'Quick start interactive rebase' action (#3213)
- **PR Description**

A common issue I have is that I want to move a commit from the top of my
branch all the way down to the first commit on the branch. To do that, I
need to navigate down to the first commit on my branch, press 'e' to
start an interactive rebase, then navigate back up to the top of the
branch, then move my commit back down to the base. This is annoying.

Similarly annoying is moving the commit one-by-one without explicitly
starting an interactive rebase, because then each individual step is its
own rebase which takes a while in aggregate.

This PR allows you to press 'i' from the commits view to start an
interactive rebase from an 'appropriate' base. By appropriate, we mean
that we want to start from the HEAD and stop when we reach the first
merge commit or commit on the main branch. This may end up including
more commits than you need, but it doesn't make a difference.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-13 22:39:06 +11:00
Jesse Duffield
ee106f9af8 Do not perform IO work when getting disabled reason with local commits
Because we obtain disabled reasons after every action, we need to keep the code for doing so
super fast. As such, we should not be hitting the filesystem to get rebase state, instead
we should just get the cached state.

I feel like we should actually be using the cached state everywhere like we do with all
our other models if only for the sake of consistency.
2024-01-13 12:57:49 +11:00
Jesse Duffield
23a98937b4 Update README to add backticks to key
Missed a spot when I originally wrote this
2024-01-13 12:57:49 +11:00
Jesse Duffield
a0b63090e0 Update codebase guide
I'm adding a couple more terms: Keybinding and Action, to keep things standardised
2024-01-13 12:57:49 +11:00
Jesse Duffield
53a8bd2e3f Add ability to start an interactive rebase onto an appropriate base
A common issue I have is that I want to move a commit from the top of my branch
all the way down to the first commit on the branch. To do that, I need to navigate
down to the first commit on my branch, press 'e' to start an interactive rebase,
then navigate back up to the top of the branch, then move my commit back down to
the base. This is annoying.

Similarly annoying is moving the commit one-by-one without explicitly starting
an interactive rebase, because then each individual step is its own rebase which
takes a while in aggregate.

This PR allows you to press 'i' from the commits view to start an interactive
rebase from an 'appropriate' base. By appropriate, we mean that we want to start
from the HEAD and stop when we reach the first merge commit or commit on the main
branch. This may end up including more commits than you need, but it doesn't make
a difference.
2024-01-13 12:57:49 +11:00
Jesse Duffield
e9962b98a7 Set working directory in lazygit test command (#3215)
We need to fetch our list of tests both outside of our test binary and
within. We need to get the list from within so that we can run the code
that drives the test and runs assertions. To get the list of tests we
need to know where the root of the lazygit repo is, given that the tests
live in files under that root.

So far, we've used this GetLazyRootDirectory() function for that, but it
assumes that we're not in a test directory (it just looks for the first
.git dir it can find). Because we didn't want to properly fix this
before, we've been setting the working directory of the test command to
the lazygit root, and using the --path CLI arg to override it when the
test itself ran. This was a terrible hack.

Now, we're passing the lazygit root directory as an env var to the
integration test, so that we can set the working directory to the actual
path of the test repo; removing the need to use the --path arg.

- **PR Description**

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-12 23:33:26 +11:00
Jesse Duffield
a1ce6029c1 Pass -f as single arg in integration test
For some bizarre reason `pkg/integration/tests/filter_by_path/cli_arg.go` is failing as of 8c716184 like so:

```
test_lazygit

  Usage:
    test_lazygit [git-arg]

  Positional Variables:
    git-arg   Panel to focus upon opening lazygit. Accepted values (based on git terminology): status, branch, log, stash. Ignored if --filter arg is passed.
  Flags:
    -h --help               Displays help with available flag, subcommand, and positional value parameters.
    -p --path               Path of git repo. (equivalent to --work-tree=<path> --git-dir=<path>/.git/)
    -f --filter             Path to filter on in `git log -- <path>`. When in filter mode, the commits, reflog, and stash are filtered based on the given path, and some operations are restricted
    -v --version            Print the current version
    -d --debug              Run in debug mode with logging (see --logs flag below). Use the LOG_LEVEL env var to set the log level (debug/info/warn/error) (default: false)
    -l --logs               Tail lazygit logs (intended to be used when `lazygit --debug` is called in a separate terminal tab)
    -c --config             Print the default config
    -cd --print-config-dir   Print the config directory
    -ucd --use-config-dir     override default config directory with provided directory
    -w --work-tree          equivalent of the --work-tree git argument
    -g --git-dir            equivalent of the --git-dir git argument
    -ucf --use-config-file    Comma separated list to custom config file(s)

Unknown arguments supplied:  filterFile
```

where the CLI args are:
```
([]string) (len=5 cap=5) {
 (string) (len=25) "/tmp/lazygit/test_lazygit",
 (string) (len=6) "-debug",
 (string) (len=108) "--use-config-dir=/Users/jesseduffieldduffield/repos/lazygit/test/_results/filter_by_path/cli_arg/used_config",
 (string) (len=2) "-f",
 (string) (len=10) "filterFile"
}
```

This appears to be a bug in flaggy itself. I've updated to the latest version but it still breaks. Bizarrely it works fine on CI and
only fails locally. Running lazygit locally with `lg -f pkg/gui/controllers/helpers/refresh_helper.go` it works fine. So I don't
know what's going on there. At any rate, I'm just going to get the test passing by passing `-f=filterFile` as a single argument.
2024-01-12 20:19:50 +11:00
Jesse Duffield
8c716184c1 Set working directory in lazygit test command
We need to fetch our list of tests both outside of our test binary and within. We need
to get the list from within so that we can run the code that drives the test and runs
assertions. To get the list of tests we need to know where the root of the lazygit repo
is, given that the tests live in files under that root.

So far, we've used this GetLazyRootDirectory() function for that, but it assumes that
we're not in a test directory (it just looks for the first .git dir it can find). Because
we didn't want to properly fix this before, we've been setting the working directory of
the test command to the lazygit root, and using the --path CLI arg to override it when
the test itself ran. This was a terrible hack.

Now, we're passing the lazygit root directory as an env var to the integration test, so
that we can set the working directory to the actual path of the test repo; removing the
need to use the --path arg.
2024-01-12 19:59:31 +11:00
Jesse Duffield
5c888a0b47 Update codebase guide
fixes a line that used an incorrect path
2024-01-11 09:57:05 +11:00
Jesse Duffield
498092a8ec Add codebase guide (#3206)
- **PR Description**

After reading through some AI-generated lazygit docs, it occurred to me
that we should actually be documenting some of this stuff ourselves.

Contributors can feel free to add stuff to this guide if they think it
provides useful context.

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-11 09:50:51 +11:00
Jesse Duffield
caf6a3629d Add codebase guide 2024-01-11 09:43:38 +11:00
Stefan Haller
1ca96bbe5b Fix keybindings for characters involving AltGr on Windows (#3194) 2024-01-10 09:42:30 +01:00
Stefan Haller
cb5d0bca1c Bump gocui
... and switch back from stefanhaller's tcell fork to the official tcell. This
basically reverts 7ccb871a45.
2024-01-10 09:39:25 +01:00
Stefan Haller
ef3d6f4a32 Support insteadOf URL rewriting when opening URLs in browser (#3177) 2024-01-10 09:29:26 +01:00
Stefan Haller
b470442a46 Obtain remote URL by calling "ls-remote --get-url" instead of using git config
This has the advantage that it still works when the user has configured aliases
using the insteadOf feature [1].

[1] https://git-scm.com/docs/git-config/2.42.0#Documentation/git-config.txt-urlltbasegtinsteadOf)
2024-01-10 09:24:23 +01:00
Stefan Haller
bf01c0b00e Allow multiple fetch commands (or fetch and pull) to run concurrently (#3202) 2024-01-10 09:23:22 +01:00
Stefan Haller
76e39af76f Allow multiple fetch commands (or fetch and pull) to run concurrently
Git has a bug [1] whereby running multiple fetch commands at the same time
causes all of them to append their information to the .git/FETCH_HEAD file,
causing the next git pull that wants to use the information to become confused,
and show an error like "Cannot rebase onto multiple branches". This error would
occur when pressing "f" and "p" in quick succession in the files panel, but also
when pressing "p" while a background fetch happens to be running. One likely
situation for this is pressing "p" right after startup.

Since lazygit never uses the information written to .git/FETCH_HEAD, it's best
to avoid writing to it, which fixes the scenarios described above.

However, it doesn't fix the problem of repeatedly pressing "f" quickly on the
checked-out branch; since we call "git pull" in that case, the above fix doesn't
help there. We'll address this separately in another PR.

[1] See https://public-inbox.org/git/xmqqy1daffk8.fsf@gitster.g/ for more
information.
2024-01-10 09:18:38 +01:00
Stefan Haller
5b91cd0cc8 Extract a function fetchCommandBuilder 2024-01-10 09:18:38 +01:00
Stefan Haller
6255728e63 Add a method GitVersion.IsAtLeast 2024-01-10 09:18:38 +01:00
README-bot
b657fc4f0b Updated README.md 2024-01-10 08:17:42 +00:00
Stefan Haller
d294d51791 Add command to find base commit for creating a fixup (#3105) 2024-01-10 09:17:29 +01:00
Stefan Haller
b35f8776e1 Warn when there are hunks with only added lines
The algorithm works by blaming the deleted lines, so if a hunk contains only
added lines, we can only hope that it also belongs in the same commit. Warn the
user about this.

Note: the warning might be overly agressive, we'll have to see if this is
annoying. The reason is that it depends on the diff context size whether added
lines go into their own hunk or are grouped together with other added or deleted
lines into one hunk. However, our algorithm uses a diff context size of 0,
because that makes it easiest to parse the diff; this results in hunks having
only added lines more often than what the user sees. For example, moving a line
of code down by two lines will likely result in a single hunk for the user, but
in two hunks for our algorithm. On the other hand, being this strict makes the
warning consistent. We could consider using the user's diff context size in the
algorithm, but then it would depend on the current context size whether the
warning appears, which could be confusing. Plus, it would make the algorithm
quite a bit more complicated.
2024-01-10 09:11:40 +01:00
Stefan Haller
8ca78412ac Add command to find base commit for creating a fixup 2024-01-10 09:11:40 +01:00
Stefan Haller
33f933ba21 Add config setting for splitting window vertically in half screen mode (#3133) 2024-01-09 15:53:32 +01:00
Stefan Haller
d70dd5123d Add config setting for side panel location (left or top) in half screen mode 2024-01-09 15:45:26 +01:00
Stefan Haller
15da702140 Fix preserving the commit message when description contains blank lines (#3170) 2024-01-09 14:35:43 +01:00
Stefan Haller
cd50c79ae4 Preserve the commit message correctly even if the description has blank lines
There are two possible fixes for this bug, and they differ in behavior when
rewording a commit. The one I chose here always splits at the first line feed,
which means that for an improperly formatted commit message such as this one:

   This is a very long multi-line subject,
   which you shouldn't really use in git.

   And this is the body (we call it "description" in lazygit).

we split after the first line instead of after the first paragraph. This is
arguably not what the original author meant, but splitting after the first
paragraph doesn't really work well in lazygit, because we would try to put both
lines into the one-line subject field of the message panel, and you'd only see
the second and not even know that there are more.

The other potential fix would have been to join subject and description with two
line feeds instead of one in JoinCommitMessageAndDescription; this would have
fixed our bug in the same way, but would result in splitting the above message
after the second line instead of the first. I think that's worse, so I decided
for the first fix.

While we're at it, simplify the code a little bit; strings.Cut is documented to
return (s, "") when the separator is not found, so there's no need to do this on
our side.

We do have to trim spaces on the description now, to support the regular reword
case where subject and body are separated by a blank line.
2024-01-09 14:31:53 +01:00
Stefan Haller
3ebba5f32c Add test demonstrating a bug with preserving the commit message
SplitCommitMessageAndDescription splits at the first '\n\n' that it finds (if
there is one), which in this case is between the two paragraphs of the
description. This is wrong.
2024-01-09 14:31:53 +01:00
Stefan Haller
9a423c388d Remove unused function
I think this is a left-over from before we had the new commit message panel. It
no longer makes sense to add a newline to the commit subject.
2024-01-09 14:31:53 +01:00
Stefan Haller
daf9b8cfa9 Simplify GetCommitMessage
Use git log instead of git rev-list, this way we don't get a line "commit <sha>"
at the beginning that we then have to discard again.

The test TestGetCommitMsg is becoming a bit pointless now, since it just
compares that input and output are identical.
2024-01-09 14:31:53 +01:00
README-bot
b6a9220343 Updated README.md 2024-01-09 13:30:54 +00:00
Stefan Haller
aeb017e029 Add command to open git difftool (#3156) 2024-01-09 14:29:26 +01:00
Stefan Haller
a6174271aa Update cheat sheets and json schema 2024-01-09 14:27:33 +01:00
Stefan Haller
517e0f8248 Add command to open git difftool 2024-01-09 14:27:33 +01:00
Stefan Haller
c1cb95db6f Remove unused function 2024-01-09 14:24:14 +01:00
Stefan Haller
966e5f5337 Fix checking out a tag when there is a branch with the same name (#3179) 2024-01-09 14:23:08 +01:00
Stefan Haller
f244ec8251 Fix checking out a tag when a branch with the same name exists 2024-01-09 14:18:35 +01:00
Stefan Haller
2b97f0fb43 Add test demonstrating the problem
When there's a branch with the same name as the tag, the branch gets checked out
instead of the tag.
2024-01-09 14:18:35 +01:00
Stefan Haller
af5e25cfb5 Replace copy commit SHA with copy commit subject on the y s keybind in the commits view (#3188) 2024-01-09 14:17:05 +01:00
README-bot
93cae68e94 Updated README.md 2024-01-09 09:55:08 +00:00
Jesse Duffield
6e6fe6a489 Show a friendly error message when starting lazygit from a non-existent cwd (#3192)
Closes #3187

- **PR Description**

#3187 observes that lazygit crashes with a stack trace if it's run from
a non-existent current working directory. The steps to reproduce are:

```
mkdir test
cd test
rm -r ../test
lazygit
```

(Note: I can repro this on Ubuntu, but not on macOS Sonoma, where
lazygit starts regardless of whether the current working directory
exists or not.)

Here's how the repro steps look on Ubuntu with this PR applied:

```
simon@ubuntu:/Users/simon/src/3p/lazygit$ go build .
simon@ubuntu:/Users/simon/src/3p/lazygit$ mkdir deleteme
simon@ubuntu:/Users/simon/src/3p/lazygit$ cd deleteme/
simon@ubuntu:/Users/simon/src/3p/lazygit/deleteme$ rm -r ../deleteme
simon@ubuntu:/Users/simon/src/3p/lazygit/deleteme$ ../lazygit
2024/01/02 18:40:15 Error: the current working directory does not exist
```

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go generate ./...`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc

<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for
examples
-->
2024-01-09 20:54:50 +11:00
Karim Khaleel
2c2436574d Replace copy SHA with copy subject on commit 'y s' 2024-01-03 02:19:39 +03:00
Simon Whitaker
0cdca9ac2c Update error message 2024-01-02 18:43:39 +00:00
Simon Whitaker
4172be6bc8 Show a friendly error message when starting lazygit from a non-existent cwd
Closes 3187
2024-01-02 18:25:28 +00:00
Stefan Haller
d97b37a178 Add local branch sorting menu (#3182) 2023-12-27 15:30:25 +01:00
Alex March
21334fa889 Add integration test for local branch sort order 2023-12-27 15:25:29 +01:00
Alex March
36a29f225b Add a sort order menu for local branches 2023-12-27 15:25:29 +01:00
README-bot
1e85c4379f Updated README.md 2023-12-27 10:24:12 +00:00
Stefan Haller
470632b97a Add age to stash entries (#3174) 2023-12-27 11:23:57 +01:00
AzraelSec
50044dd5e0 chore: use null char as a stash entries divider during loading 2023-12-27 11:21:49 +01:00
AzraelSec
bc330b8ff3 feat: add age on stash lines 2023-12-27 11:21:49 +01:00
README-bot
7f36494eb2 Updated README.md 2023-12-22 08:16:48 +00:00
Stefan Haller
85c48ba887 Add remote branch sorting menu, saving the option to state.yml (#3171) 2023-12-22 09:16:34 +01:00
Stefan Haller
1e3935cbaf Add integration test for remote branch sort order 2023-12-22 16:30:20 +09:00
Alex March
3fe491fcb2 Implement a sort order menu for remote branches 2023-12-22 16:30:20 +09:00
README-bot
66b608b2f9 Updated README.md 2023-12-15 15:39:58 +00:00
Stefan Haller
4ee01d153b fix(config): add yaml:"options" struct tag to CustomCommandPrompt.[]Options (#3163) 2023-12-15 16:39:43 +01:00
Emre Deger
79e04fad9a fix(config): add yaml struct tag to CustomCommandPrompt.[]Options
add `yaml` struct tag for fixing uppercase issue on json schema
2023-12-15 07:29:48 +03:00
README-bot
6778bc04a3 Updated README.md 2023-12-10 15:07:02 +00:00
Stefan Haller
d548f857a4 Fall back to WithWaitingStatus if item is not visible (#3083) 2023-12-10 16:06:49 +01:00
Stefan Haller
f99c59b6d5 Fall back to WithWaitingStatus if item is scrolled out of view 2023-12-10 16:03:25 +01:00
Stefan Haller
0fd4983c66 Fall back to WithWaitingStatus if view showing the item is not visible 2023-12-10 15:57:51 +01:00
Stefan Haller
240948b882 Return only visible views from TopViewInWindow
Without this it's not reliably possible to ask whether a given view is visible
by asking

  windowHelper.TopViewInWindow(context.GetWindowName()) == context.GetView()

because there could be transient, invisible contexts after it in the Z order.

I guess it's a bit of a coincidence that this has never been a problem so far.
2023-12-10 15:57:51 +01:00
README-bot
cf82e69bbe Updated README.md 2023-12-09 14:40:36 +00:00
Stefan Haller
653e59a3d5 Make move up/down blocking (#2966) 2023-12-09 15:40:23 +01:00
Stefan Haller
e342860ef1 Add WithWaitingStatusSync for reverting commits 2023-12-09 15:28:41 +01:00
Stefan Haller
569adae6a2 Use WithWaitingStatusSync for move commit up/down 2023-12-09 15:28:40 +01:00
Stefan Haller
79fe885dcd Add WithWaitingStatusSync 2023-12-09 15:28:40 +01:00
Stefan Haller
a46f26e148 Bump gocui 2023-12-09 15:23:40 +01:00
Stefan Haller
ca4b8b25f0 Fix bottom line alignment (#3076) 2023-12-09 11:57:06 +01:00
Jesse Duffield
dad2c5fa52 Add tests for window arrangement code
The output of the GetWindowDimensions function is hard to understand just by looking at it,
so I've added a helper function in the tests to render the window layout as text, so that
in order to create a new test you just come up with some args and paste the output as the
expected output.

This has the same downsides that any snapshot-based testing has: it's more brittle than
targeted assertions. But it is much easier to make sense of these snapshots than it is
to make sense of more fine-grained assertions, and I like the fact that these tests can
serve as documentation.
2023-12-09 11:53:52 +01:00
Jesse Duffield
8a08abcd35 Refactor window arrangement helper to use pure function
This will make it easier to test the file
2023-12-09 11:18:28 +01:00
Jesse Duffield
b96befa250 Layout the bottom line view using spacer views
We are also removing the single-character padding on the left/right edges of the bottom
line because it's unnecessary

Unfortunately we need to create views for each spacer: it's not enough to just
layout the existing views with padding inbetween because gocui only renders
views meaning if there is no view in a given position, that position will just
render whatever was there previously (at least that's what I recall from talking
this through with Stefan: I could be way off).

Co-authored-by: Stefan Haller <stefan@haller-berlin.de>
2023-12-09 11:18:28 +01:00
Stefan Haller
8cc820668a Fix an incorrect comment
It sounds like at some point we only showed a slash as the search prompt, but I
dug a bit through the history and couldn't find a state of the code where that
was the case. (shrug)
2023-12-09 11:18:28 +01:00
Luka Markušić
ccb1ee04a5 fix: MacOS default path misspelling (#3148) 2023-12-07 10:07:52 +01:00
ZeroMask
d00936fb4e fix: MacOS default path misspelling 2023-12-07 11:52:39 +03:00
README-bot
e84d5acd23 Updated README.md 2023-12-07 07:35:43 +00:00
Stefan Haller
a8a4211d2b Add a copy-to-clipboard menu to the file view (with diff copy options) (#3104) 2023-12-07 08:35:28 +01:00
AzraelSec
6907816af9 chore: update jsonschema 2023-12-07 08:30:03 +01:00
AzraelSec
38db574de9 chore: update cheatsheets 2023-12-07 08:30:03 +01:00
AzraelSec
c7012528fc feat: introduce a copy menu into the file view 2023-12-07 08:30:03 +01:00
Jesse Duffield
2162e5ff64 Re-enable 'Unset upstream' option when upstream branch is missing (#3086) 2023-12-06 15:58:11 +11:00
README-bot
f40f5710f0 Updated README.md 2023-12-06 04:58:04 +00:00
Jesse Duffield
97940cae10 Updated installation instruction for Gentoo (#3113) 2023-12-06 15:57:50 +11:00
Stefan Haller
1555503493 Add UserConfig jsonschema generation script (#3039) 2023-12-02 13:27:37 +01:00
Karim Khaleel
1a035db4c8 Add UserConfig jsonschema generation script 2023-12-02 10:46:24 +01:00
Karim Khaleel
df5b3693d6 Add invopop/jsonschema fork 2023-12-02 10:46:24 +01:00
Stefan Haller
b123719107 Update cheatsheets (#3143) 2023-12-02 10:41:08 +01:00
Stefan Haller
6ec109c15a Update cheatsheets
This was forgotten in #3046.
2023-12-02 10:36:15 +01:00
Jesse Duffield
dee1ff007d fixed typo in test description (#3101) 2023-12-02 09:37:49 +11:00
README-bot
02b739743a Updated README.md 2023-12-01 22:35:12 +00:00
Jesse Duffield
f5361fdcb4 commmit - enhance docs for keybinding 'c' for local branch (#3046) 2023-12-02 09:34:58 +11:00
Jesse Duffield
b86d5bd688 Update README.md 2023-11-30 19:46:12 +11:00
README-bot
4bfffa69ff Updated README.md 2023-11-30 08:24:43 +00:00
Jesse Duffield
f9b4bcde38 Capture test code coverage stats (#3135) 2023-11-30 19:21:22 +11:00
Jesse Duffield
aaecd6cc40 Add coverage arg for integration tests
This PR captures the code coverage from our unit and integration tests. At the
moment it simply pushes the result to Codacy, a platform that assists with
improving code health. Right now the focus is just getting visibility but I want
to experiment with alerts on PRs when a PR causes a drop in code coverage.

To be clear: I'm not a dogmatist about this: I have no aspirations to get to
100% code coverage, and I don't consider lines-of-code-covered to be a perfect
metric, but it is a pretty good heuristic for how extensive your tests are.

The good news is that our coverage is actually pretty good which was a surprise
to me!

As a conflict of interest statement: I'm in Codacy's 'Pioneers' program which
provides funding and mentorship, and part of the arrangement is to use Codacy's
tooling on lazygit. This is something I'd have been happy to explore even
without being part of the program, and just like with any other static analysis
tool, we can tweak it to fit our use case and values.

## How we're capturing code coverage

This deserves its own section. Basically when you build the lazygit binary you
can specify that you want the binary to capture coverage information when it
runs. Then, if you run the binary with a GOCOVERDIR env var, it will write
coverage information to that directory before exiting.

It's a similar story with unit tests except with those you just specify the
directory inline via `-test.gocoverdir`.

We run both unit tests and integration tests separately in CI, _and_ we run them
parallel with different OS's and git versions. So I've got each step uploading
the coverage files as an artefact, and then in a separate step we combine all
the artefacts together and generate a combined coverage file, which we then
upload to codacy (but in future we can do other things with it like warn in a PR
if code coverage decreases too much).

Another caveat is that when running integration tests, not only do we want to
obtain code coverage from code executed by the test binary, we also want to
obtain code coverage from code executed by the test runner. Otherwise, for each
integration test you add, the setup code (which is run by the test runner, not
the test binary) will be considered un-covered and for a large setup step it may
appear that your PR _decreases_ coverage on net. Go doesn't easily let you
exclude directories from coverage reports so it's better to just track the
coverage from both the runner and the binary.

The binary expects a GOCOVERDIR env var but the test runner expects a
test.gocoverdir positional arg and if you pass the positional arg it will
internally overwrite GOCOVERDIR to some random temp directory and if you then
pass that to the test binary, it doesn't seem to actually write to it by the
time the test finishes. So to get around that we're using LAZYGIT_GOCOVERDIR and
then within the test runner we're mapping that to GOCOVERDIR before running the
test binary. So they both end up writing to the same directory. Coverage data
files are named to avoid conflicts, including something unique to the process,
so we don't need to worry about name collisions between the test runner and the
test binary's coverage files. We then merge the files together purely for the
sake of having fewer artefacts to upload.

## Misc

Initially I was able to have all the instances of '/tmp/code_coverage' confined
to the ci.yml which was good because it was all in one place but now it's spread
across ci.yml and scripts/run_integration_tests.sh and I don't feel great about
that but can't think of a way to make it cleaner.

I believe there's a use case for running scripts/run_integration_tests.sh
outside of CI (so that you can run tests against older git versions locally) so
I've made it that unless you pass the LAZYGIT_GOCOVERDIR env var to that script,
it skips all the code coverage stuff.

On a separate note: it seems that Go's coverage report is based on percentage of
statements executed, whereas codacy cares more about lines of code executed, so
codacy reports a higher percentage (e.g. 82%) than Go's own coverage report
(74%).
2023-11-30 12:58:41 +11:00
Jesse Duffield
7e5f25e415 Use args struct for RunTests
There were too many position arguments
2023-11-29 11:39:10 +11:00
Stefan Haller
d8059d7f7d Use a PTY when calling external diff command (#3120) 2023-11-22 12:51:02 +01:00
尼诺
0f2b79a1d4 Use a PTY when calling external diff command
This is important for communicating the view size to the external command.
e.g. The columns in difft's side-by-side mode are aligned correctly.
2023-11-22 12:08:05 +01:00
README-bot
bb87642aee Updated README.md 2023-11-19 16:10:18 +00:00
Stefan Haller
4a5b3eaa52 Fix go.mod file (#3118) 2023-11-19 17:10:03 +01:00
Stefan Haller
1b4e76797f Add "go mod tidy" check to CI
This should catch errors like this earlier.
2023-11-18 16:03:51 +01:00
Stefan Haller
58971182ca Fix go.mod file (go-difflib dependency should be indirect)
This broke with commit 7af371701d, but nobody noticed yet.
2023-11-18 16:01:03 +01:00
Hamed Benazha
9427a85805 Update README.md to contain new information to install it on gentoo
I changed the installation of method for the Gentoo distribution from a user owned overlay to a community owned one. GURU is the community owned overlay, it's safer to use and easier to maintain
2023-11-10 09:26:30 +01:00
Luka Markušić
d145e818d0 Fix unsetting upstream when it doesn't exist 2023-11-04 23:46:27 +01:00
Luka Markušić
e0fc8fe25b Introduce failing "UnsetUpstream" test 2023-11-04 23:45:21 +01:00
Luka Markušić
bb705d91a4 Rename integration test "ResetUpstream"
We are unsetting upstream in it, not resetting to upstream
2023-11-04 23:18:38 +01:00
schuebel
738fa286b2 fixed typo in test description 2023-10-30 14:17:34 +01:00
README-bot
1d1b8cc01f Updated README.md 2023-10-27 04:14:57 +00:00
Jesse Duffield
c357284e1a Add Warp link to readme
Adding warp to the readme as they are trialling out sponsoring the project.
2023-10-27 04:14:40 +00:00
README-bot
232596b83d Updated README.md 2023-10-26 07:45:25 +00:00
Jesse Duffield
1c1d558cc7 Add Codacy badge
I applied to the Codacy Pioneers program for funding and mentorship and I got accepted. Part of the terms is to show the codacy report badge in the readme. Thank God it's an A!
2023-10-26 07:45:08 +00:00
README-bot
dffb16ed96 Updated README.md 2023-10-24 15:27:07 +00:00
Stefan Haller
df8ce76bd6 Update Stacked_Branches.md (#3090) 2023-10-24 17:26:49 +02:00
Alan Potter
4d18b62c54 Update Stacked_Branches.md
Fix a typo.
2023-10-24 05:32:29 -04:00
README-bot
8193731a82 Updated README.md 2023-10-18 10:28:08 +00:00
Jesse Duffield
1a6d062192 Color file icons (#3080) 2023-10-18 21:27:53 +11:00
Jesse Duffield
1ff13cdfc6 Advise against raising pull requests from master branch 2023-10-18 21:21:50 +11:00
aashish2057
19e8cafe41 create iconProperties struct and convert iconMaps to use iconProperties 2023-10-18 21:21:36 +11:00
README-bot
cbb5fe6007 Updated README.md 2023-10-16 11:30:40 +00:00
Stefan Haller
c4fc30c243 Truncate branch names to make branch status always visible (#3075) 2023-10-16 13:30:24 +02:00
Stefan Haller
117aa1dcc6 Advise developers to use a nerd font in their editor 2023-10-16 13:15:05 +02:00
Stefan Haller
c550737a4f Truncate long branch names to make branch status visible 2023-10-16 13:15:05 +02:00
Stefan Haller
9e37ae3f5d Make the window a little wider for headless integration tests
100 was an unrealistically narrow width; make it a little wider so that we will
have to truncate things less often.
2023-10-16 09:03:07 +02:00
Stefan Haller
c89ef8b84a Make it possible to set the nerd fonts version to "off"
We don't need this for production code, but it will be needed for tests in the
next commit.
2023-10-16 09:03:07 +02:00
Stefan Haller
23befdd13a Pass "now" into utils.Loader
This makes it possible to write deterministic tests for views that use it.
2023-10-16 09:03:07 +02:00
Stefan Haller
58a83b0862 Remove special code to rerender views on screen mode change
The previous commit handles this case too.
2023-10-16 09:03:07 +02:00
Stefan Haller
d5b4f7bb3e Rerender certain views when their width changes
Situations where a view's width changes:
- changing screen modes
- enter staging or patch building
- resizing the terminal window

For the first of these we currently have special code to force a rerender, since
some views render different content depending on whether they are in full-screen
mode. We'll be able to remove that code now, since this new generic mechanism
takes care of that too.

But we will need this more general mechanism for cases where views truncate
their content to the view width; we'll add one example for that later in this
branch.
2023-10-16 09:03:07 +02:00
README-bot
3cee4de39c Updated README.md 2023-10-15 07:46:45 +00:00
Stefan Haller
f609a04671 Add 'lvim' editor preset for lunarvim (#3074) 2023-10-15 09:46:30 +02:00
zottelsheep
7ffb6ffb0f Add 'lvim' editor preset for lunarvim
Add 'lvim' as a new standardTerminalEditorPreset, since lunarvim uses an alias for nvim.
2023-10-14 15:04:23 +02:00
Stefan Haller
3691856021 Re-apply filter when model changes (#3058) 2023-10-10 08:43:48 +02:00
Stefan Haller
b5ca6a3add When refreshing models, re-apply active filter for the corresponding view 2023-10-10 08:37:30 +02:00
Stefan Haller
ecaa4846f1 Fix crash when trying to filter the list of remotes (#3059) 2023-10-10 08:36:47 +02:00
Stefan Haller
787f9966ec Fix crash when trying to filter the list of remotes
This seems to be a left-over from an earlier iteration of the code. Removing it
fixes the crash.
2023-10-10 08:33:18 +02:00
Stefan Haller
013cfc77a1 Show sync status in branches list (#3021) 2023-10-10 08:32:42 +02:00
Jesse Duffield
16f7b01fec Add disabled compat for user config (#2833) (#3060) 2023-10-10 16:49:31 +11:00
Karim Khaleel
421c6565f9 Update wording in disable keybindings test
Co-authored-by: Jesse Duffield <jessedduffield@gmail.com>
2023-10-10 08:38:15 +03:00
Karim Khaleel
d02deeefd8 Add disabled compat for user config (#2833)
Treat <disabled> setting as equivalent to "null"
in keybindings user configs.
2023-10-09 22:34:50 +03:00
Stefan Haller
235f5bb221 Avoid rendering branches view twice when refreshing
refreshWorktrees re-renders the branches view, because the branches view shows
worktrees against branches. This means that when both BRANCHES and WORKTREES are
requested to be refreshed, the branches view would be rendered twice in short
succession. This causes an ugly visual glitch when force-pushing a branch,
because when pushing is done, we would see the ↑4↓9 status come back from under
the Pushing status for a brief moment, to be replaced with a green checkmark a
moment later.

Fix this by including the worktree refresh in the branches refresh when both are
requested. This means that the two are no longer running in parallel for an
async refresh, but hopefully that's not so bad.
2023-10-08 18:45:36 +02:00
Stefan Haller
be3b4bd791 Remove sync mutex
I'm pretty convinced we don't need it. Git itself does a good job of making sure
that concurrent operations don't corrupt anything.
2023-10-08 18:45:36 +02:00
Stefan Haller
67d6447e12 Disallow pulling/pushing a branch while the branch is pushed or pulled 2023-10-08 18:45:36 +02:00
Stefan Haller
fd9d7cb7bb Disallow checking out another branch while the current one is being pulled 2023-10-08 18:45:36 +02:00
Stefan Haller
3d6965ccbb Add inline status for pushing tags and deleting remote tags 2023-10-08 18:45:36 +02:00
Stefan Haller
707fa37160 Add inline status for pushing/pulling/fast-forwarding branches
When pulling/pushing/fast-forwarding a branch, show this state in the branches
list for that branch for as long as the operation takes, to make it easier to
see when it's done (without having to stare at the status bar in the lower
left).

This will hopefully help with making these operations feel more predictable, now
that we no longer show a loader panel for them.
2023-10-08 18:45:36 +02:00
Stefan Haller
7075b66bc6 Add WithInlineStatus helper function
Very similar to WithWaitingStatus, except that the status is shown in a view
next to the affected item, rather than in the status bar.

Not used by anything yet; again, committing separately to get smaller commits.
2023-10-08 18:45:36 +02:00
Stefan Haller
9d55d71fdd Add GetItemOperation/SetItemOperation/ClearItemOperation to IStateAccessor
Not used by anything yet; committing this separately in the interest of having
smaller independent commits.
2023-10-08 18:30:57 +02:00
Stefan Haller
cc9a20c4ab Don't report errors from within a WithWaitingStatus
We can just return our error to WithWaitingStatus, it will take care of
reporting it.
2023-10-08 18:30:34 +02:00
README-bot
c39fafe6ec Updated README.md 2023-10-04 23:33:09 +00:00
Jesse Duffield
2a11725749 Remove redundant len check (#3051) 2023-10-05 10:32:53 +11:00
Eng Zer Jun
deed9eb18e Remove redundant len check
From the Go specification [1]:

  "3. If the map is nil, the number of iterations is 0."

`len` returns 0 if the map is nil [2]. Therefore, checking `len(v) > 0`
before a loop is unnecessary.

[1]: https://go.dev/ref/spec#For_range
[2]: https://pkg.go.dev/builtin#len

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2023-10-04 21:03:26 +08:00
Sebastian
50b49ebeb1 Merge branch 'jesseduffield:master' into feature/keybinding-branch-recent 2023-10-04 09:42:53 +02:00
Sebastian Mangelsen
a74c6eef40 adjust the text to received review comments 2023-10-04 09:41:00 +02:00
README-bot
a3759c65e4 Updated README.md 2023-10-03 19:20:39 +00:00
Stefan Haller
519760077b Add Micro editor preset (#3049) 2023-10-03 21:20:21 +02:00
Nikita Karamov
8390622f70 Add Micro editor preset 2023-10-03 19:42:28 +02:00
README-bot
a04fbafcfd Updated README.md 2023-10-03 09:35:18 +00:00
Stefan Haller
4fe8dee40f Band-aid fix for submodule/reset.go test failure (#3047) 2023-10-03 11:34:59 +02:00
Stefan Haller
6fd80565c7 Band-aid fix for submodule/reset.go test failure
This is not a complete fix, but it's good enough to fix the spurious test
failures of submodule/reset.go. We have some vague hope to fix this in a more
sustainable way by somehow improving our concurrency model fundamentally, but
that's a more long-term undertaking, and it's annoying that this test fails so
often, so let's fix it in this way for now.
2023-10-03 09:36:46 +02:00
Sebastian Mangelsen
5ebd8ac7fe commmit - enhance docs for keybinding 'c' for local branch
- closes #3030
- mention that it supports '-'
- fix documentation and builtin help
2023-10-02 18:06:24 +02:00
README-bot
3cda1d03d9 Updated README.md 2023-10-01 06:05:28 +00:00
Stefan Haller
d4eb02fac0 Add ability to force portrait mode (#3037) 2023-10-01 08:05:13 +02:00
Louis DeLosSantos
9c72d8a2b0 Add ability to force portrait mode
A new gui config flag 'portraitMode':<string> is added to influence when
LazyGit stacks its UI components on top of one another.

The accepted values are 'auto', 'always', 'never'.

'auto': enter portrait mode when terminal becomes narrow enough

'always': always use portrait mode unconditional of the terminal
dimensions

'never': never use portraid mode

Signed-off-by: Louis DeLosSantos <louis.delos@gmail.com>
2023-09-30 20:57:38 +02:00
Jesse Duffield
4f3127ccb8 Update PR template to use go generate command (#3041) 2023-09-30 20:06:58 +10:00
Jesse Duffield
877c27722f add gofumpt to workspace settings 2023-09-30 19:46:03 +10:00
Jesse Duffield
0dda8b58d1 Update PR template to use go generate command 2023-09-30 19:45:55 +10:00
Jesse Duffield
e997d1ae5d Add comments in user config struct (#3040) 2023-09-30 19:39:42 +10:00
Jesse Duffield
3e2ef84d56 Ignore deprecation checks in linter
This is pretty funny: the staticcheck linter gets mad if we use a field which is marked
in a comment as being deprecated. But it tripped on my own comment saying that a field
is deprecated in terms of the user config!

Obviously we have to make use of this field, otherwise we would just remove it entirely
rather than mark it as deprecated, so I'm silencing this lint.

I doubt this lint would actually come in handy in other cases (like when using a third
party package) and worst case scenario we just end up fixing the problem when we
try to upgrade the package and the deprecated field is now gone).
2023-09-30 19:24:59 +10:00
Jesse Duffield
b2eccd6fe8 Add comments in user config struct
We're going to make this user config struct a more authoritative source of truth.

Some of these fields weren't actually explained anywhere so I've added explanations.

In places where a lot of explanation is required I've linked to existing explanations in other docs.
2023-09-30 19:23:52 +10:00
Stefan Haller
cc25a3b8c5 Change Makefile to build non-optimized (#3028) 2023-09-30 10:59:22 +02:00
Stefan Haller
e93d945dba Change Makefile to build non-optimized 2023-09-30 10:50:32 +02:00
README-bot
246e08096f Updated README.md 2023-09-30 08:46:30 +00:00
Stefan Haller
860a52a736 Use go:generate for cheatsheet (#3035) 2023-09-30 10:46:16 +02:00
Stefan Haller
18d8b29461 Make test_list_generator.go print what it's doing
It's confusing if the cheatsheet generator prints output but this one doesn't.
2023-09-29 20:40:15 +02:00
Stefan Haller
7af371701d Use go:generate for generating cheatsheets
This has several benefits:
- it's less code
- we're using the same mechanism to generate all our auto-generated files, so if
  someone wants to add a new one, it's clear which pattern to follow
- we can re-generate all generated files with a single command
  ("go generate ./...", or "make generate")
- we only need a single check on CI to check that all files are up to date (see
  previous commit)
2023-09-29 20:38:29 +02:00
Stefan Haller
5ccc95b76f Generalize the CI check for the test list to all auto-generated files
At the moment, test_list.go is the only file that we generate using go:generate.
We will add another one in the next commit though, and we might add even more in
the future; it's useful to have a single check on CI that checks them all.
2023-09-29 20:20:09 +02:00
README-bot
cdb1d76484 Updated README.md 2023-09-28 08:04:37 +00:00
Stefan Haller
9e423e949c Improve debugging of integration tests (#3029)
Several improvements to make debugging integration tests work better.
2023-09-28 10:04:23 +02:00
Stefan Haller
e1ceb6892a Disable deadlock reporting when debugging an integration test 2023-09-28 10:03:53 +02:00
Stefan Haller
40b8557608 Disable the 40-second timeout for integration tests when debugging
Pausing at breakpoints and stepping through code can often take longer than 40s,
so the timeout is annoying when debugging.
2023-09-28 10:03:53 +02:00
Stefan Haller
92e107f52d Use constant for WAIT_FOR_DEBUGGER env var 2023-09-28 10:03:53 +02:00
Stefan Haller
2f6a87df98 Build lazygit without optimizations and inlining when debugging
This makes the debugging experience better.
2023-09-28 10:03:53 +02:00
Jesse Duffield
2c29577e3c Respect $GIT_WORK_TREE and $GIT_DIR env vars (fix #3010). (#3024) 2023-09-26 23:52:19 +10:00
BZ
e72559bb27 respect and env vars 2023-09-25 12:20:12 +02:00
Stefan Haller
6da1cf87b1 Support passing -race flag to integration tests (#3019) 2023-09-25 09:14:38 +02:00
Stefan Haller
508b869773 Pass MAKECMDGOALS to make integration-test-tui
We need this to be able to pass the "-race" argument, i.e.

  make integration-test-tui -- -race
2023-09-25 09:09:41 +02:00
Stefan Haller
59cc6843e6 Print race detector logs after running a test with -race 2023-09-25 09:09:41 +02:00
Stefan Haller
f108fd2236 Support -race arg when running integration tests to turn on go's race detector
For the "cli" and "tui" modes of the test runner there's a "-race" parameter to
turn it on; for running tests on CI with go test, you turn it on by setting the
environment variable LAZYGIT_RACE_DETECTOR to a non-empty value.
2023-09-25 09:09:41 +02:00
Stefan Haller
8081b59a02 Allow passing multiple flags to the cli runner
This is useful for example to pass both -slow and -debug. Since we're about to
add yet another flag in the next commit, it becomes even more important. Plus,
it makes the code a little nicer too.
2023-09-25 09:09:41 +02:00
Stefan Haller
26d180a50a Fix minor resource leak in runCmdHeadless
We still want to close the pty if the command failed.
2023-09-25 09:09:41 +02:00
Jesse Duffield
10fe872c71 Fix issue where active search inappropriately changed selected line (#3022) 2023-09-25 16:43:47 +10:00
Jesse Duffield
c74448f00d Don't select current search result when showing search status
Previously there was no way to render a view's search status without also moving the cursor
to the current search match. This caused issues where we wanted to display the status
after leaving the view and coming back, or when beginning a new search from within the
view.

This commit separates the two use cases so we only move the cursor when we're actually
selecting the next search match
2023-09-25 16:37:59 +10:00
Jesse Duffield
41ab7c44a0 Use upstream branch when opening pull requests (#2693)
- **PR Description**

Should probably solve #2691

- **Please check if the PR fulfills these requirements**

* [x] Cheatsheets are up-to-date (run `go run scripts/cheatsheet/main.go
generate`)
* [x] Code has been formatted (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [x] Tests have been added/updated (see
[here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md)
for the integration test guide)
* [x] Text is internationalised (see
[here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [x] Docs (specifically `docs/Config.md`) have been updated if
necessary
* [x] You've read through your own file changes for silly mistakes etc
2023-09-25 11:03:15 +10:00
README-bot
3589a43682 Updated README.md 2023-09-25 00:24:22 +00:00
Jesse Duffield
ad70341139 Add menu to rebase onto selected branch remote upstream (#3020) 2023-09-25 10:24:06 +10:00
AzraelSec
ea93df571d feat: add a menu to rebase current branch to a target branch upstream 2023-09-24 20:13:59 +02:00
AzraelSec
bc21921001 chore: rename branch upstream view options method 2023-09-22 12:19:38 +02:00
Stefan Haller
07cbb62510 Replace loader panels with waiting status (pull/push/fetch) (#2973) 2023-09-20 14:07:25 +02:00
Stefan Haller
fc868cdda5 Shorten the waiting status for fast-forwarding a branch
Now that we no longer show it in a loader panel, but in the app status view,
it's awkwardly long (the loading animation is much further to the right than for
other waiting status texts). Hopefully seeing just "Fast-forwarding <branch>" is
enough to be able to tell what's happening.
2023-09-20 13:56:06 +02:00
Stefan Haller
864a9ada57 Remove unused WithLoaderPanel code 2023-09-20 13:30:49 +02:00
Stefan Haller
f3e9d50d94 Use WithWaitingStatus instead of WithLoaderPanel for pull/push/fetch 2023-09-20 13:30:49 +02:00
Stefan Haller
cdad0b998e Add constant for LoaderAnimationInterval
Since Loader and renderAppStatus need to agree on it, it helps for it to be a
constant in case we want to change it.
2023-09-20 13:30:49 +02:00
Stefan Haller
64012d67a9 Remove unused class FakePopupHandler 2023-09-20 13:30:49 +02:00
Stefan Haller
33d9d4dbf1 Hide waiting status during credentials prompt (#3016) 2023-09-20 13:30:15 +02:00
Stefan Haller
4c5e250ed8 Add integration test for deleting a remote branch with credentials prompt
This test is interesting because it shows a credentials prompt inside a
WithWaitingStatus. Prior to this branch this test would have hung forever.
2023-09-20 13:29:30 +02:00
Stefan Haller
1359fa14c1 When pausing a task during a waiting status, hide the status while paused
We do this for two reasons:
- when popping up a credentials prompt, it looks distracting if the waiting
  status keeps spinning while the user is typing the password
- the task that updates the waiting status periodically would keep the program
  busy, so integration tests would wait forever for the program to become idle
  again
2023-09-20 13:29:30 +02:00
Stefan Haller
c222a618a8 Extract WaitingStatusHandle 2023-09-20 13:29:11 +02:00
Stefan Haller
05c32e292e Extract StatusManager.addStatus method
Avoids a bit of code duplication.
2023-09-20 11:27:50 +02:00
README-bot
fb4f742416 Updated README.md 2023-09-20 06:10:06 +00:00
Jesse Duffield
276438b601 Add history for search view (#2877) 2023-09-20 16:09:53 +10:00
Karim Khaleel
edec116ceb Add search history
Add search history for filterable and searchable views.
2023-09-20 08:35:41 +03:00
Stefan Haller
1aae1772ce Allow cherry-picking commits during a rebase (#3013) 2023-09-19 07:15:10 +02:00
Stefan Haller
a642395e9f Allow cherry-picking commits during a rebase
This can be useful when you know that a cherry-picked commit would conflict at
the tip of your branch, but doesn't at the beginning of the branch (or
somewhere in the middle). In that case you want to be able to edit the commit
before where you want to insert the cherry-picked commits, and then paste to
insert them into the todo list at that point.
2023-09-18 10:50:19 +02:00
Stefan Haller
70bfeddc90 Add StatusCommands.IsInNormalRebase and IsInInteractiveRebase
... and implement RebaseMode in terms of these.
2023-09-18 10:50:19 +02:00
Stefan Haller
e2a966443b Add a DisabledReason mechanism for menu items and keybindings (#2992) 2023-09-18 10:26:11 +02:00
Stefan Haller
0b13c3ca87 Disabled paste when there are no copied commits 2023-09-18 10:20:23 +02:00
Stefan Haller
8b6766de79 Use DisabledReason for commits panel commands 2023-09-18 10:20:23 +02:00
Stefan Haller
c9371f812f Use DisabledReason for rebasing a branch onto itself 2023-09-18 10:20:23 +02:00
Stefan Haller
e592d81b60 Add Enabled func to Binding 2023-09-18 10:20:23 +02:00
Stefan Haller
f2f50ccf75 Use DisabledReason for upstream options items 2023-09-18 10:15:11 +02:00
Stefan Haller
75aed98c35 Use DisabledReason when deleting branches is not possible
Two cases: trying to delete the currently checked-out branch, or deleting the
upstream of a branch that doesn't have one.
2023-09-18 10:15:11 +02:00
Stefan Haller
7f9818cfa2 Add DisabledReason field to MenuItem
This is useful to disable items that are not applicable right now because of
some condition (e.g. the "delete branch" menu item when the currently
checked-out branch is selected).

When a DisabledReason is set on a menu item, we
- show it in a tooltip (below the regular tooltip of the item, if it has one)
- strike through the item's key, if it has one
- show an error message with the DisabledReason if the user tries to invoke the
  command
2023-09-18 10:15:11 +02:00
README-bot
679148449a Updated README.md 2023-09-18 08:14:59 +00:00
Stefan Haller
bc00aeb10d Rename test/results to test/_results (#3012) 2023-09-18 10:14:43 +02:00
Luka Markušić
4d258bd981 Use UpstreamBranch for opening pull requests 2023-09-18 13:40:52 +10:00
Stefan Haller
c465b0f2ff Rename test/results to test/_results
This prevents commands like "go test ./..." from looking into it, and it
prevents VS Code's Problems panel from showing errors about the go files in that
folder.
2023-09-15 18:04:20 +02:00
README-bot
25160b671e Updated README.md 2023-09-13 07:17:12 +00:00
Jesse Duffield
20bfa42792 Use Error method to handle commits url copy from unknown service (#3007) 2023-09-13 17:16:53 +10:00
AzraelSec
5a740e34c7 fix: use Error method to handle the commit url copy from unknown service 2023-09-12 19:06:08 +02:00
Stefan Haller
0aad599a9b Various debugging improvements (#3000) 2023-09-11 08:27:03 +02:00
Stefan Haller
b6c892a08a Provide a simple way to debug an integration test 2023-09-11 08:17:58 +02:00
Stefan Haller
28d12e4e5d Add debug configuration to attach to a running lazygit process
I often find it more convenient to start a lazygit process in a terminal window
and then attach to it, rather than have VS Code launch one for me.

Note that this doesn't work with "go run main.go". It does work with "make run",
however.

Make sure there's only one lazygit process running, otherwise VS Code will open
a chooser with all the running processes to pick one from, but it's pretty much
impossible to tell which is which.
2023-09-10 11:47:25 +02:00
Stefan Haller
5d5e24a48e Change "make run" to do a build and then launch lazygit
As far as I can tell, there's not much of a difference in behavior between the
two. The advantage of doing it this way is that you can attach a debugger to the
running lazygit process; see next commit.
2023-09-10 11:47:25 +02:00
Stefan Haller
de598e55a6 Hide system goroutines in callstack
I have never found a reason to see them, and they just pollute the stack window
unnecessarily.
2023-09-10 11:47:25 +02:00
Stefan Haller
0fd8392067 Format launch.json with Prettier 2023-09-10 11:47:25 +02:00
Jesse Duffield
67ac2c5d9b Change the default of the "gui.borders" config to "rounded" (#2998) 2023-09-10 12:23:25 +10:00
Jesse Duffield
7626fd7df3 Add co-author to commits (#2912) 2023-09-10 11:38:16 +10:00
Orlando Maussa
db409fa69f Add coauthor (#2)
Add co-author to commits

Add addCoAuthor command for commits

- Implement the `addCoAuthor` command to add co-authors to commits.
- Utilize suggestions helpers to populate author names from the suggestions list.
- Added command to gui at `LocalCommitsController`.

This commit introduces the `addCoAuthor` command, which allows users to easily add co-authors to their commits. The co-author names are populated from the suggestions list, minimizing the chances of user input errors. The co-authors are added using the Co-authored-by metadata format recognized by GitHub and GitLab.
2023-09-09 07:18:47 -05:00
Stefan Haller
5b8a8d356c Change the default of the "gui.borders" config to "rounded"
Most people seem to agree that it looks better than the sharp edges of "single".
2023-09-09 10:42:24 +02:00
Stefan Haller
d7e2ca3f10 Add jump-to-panel label config setting (#2993) 2023-09-09 09:55:52 +02:00
Maria José Solano
387fbf6ab6 feat: add jump-to-panel label setting 2023-09-09 09:45:08 +02:00
Stefan Haller
917eb88617 Bump gocui 2023-09-09 09:44:50 +02:00
README-bot
005827395d Updated README.md 2023-09-09 07:35:14 +00:00
Jesse Duffield
7f9fa64074 Replace whitespace with '-' when renaming a branch (#2990) 2023-09-09 17:34:57 +10:00
README-bot
ab06a1c85c Updated README.md 2023-09-06 06:45:15 +00:00
Stefan Haller
37048911ca Support to reset the current branch to a selected branch upstream (#2940) 2023-09-06 08:45:00 +02:00
AzraelSec
2b7b6f71ee feat: add a menu to reset current branch to a target branch upstream 2023-09-06 08:40:07 +02:00
AzraelSec
47d422bb8a chore: rename "Set/Unset upstream" menu to "Upstream Options"
This should already have been done when adding the "View divergence from
upstream" command, but now we're going to add yet another item to the menu that
is unrelated to setting or unsetting the upstream.
2023-09-06 00:23:35 +02:00
Cal Courtney
c3ca77d6bf Replace whitespace with '-' when renaming a branch 2023-09-05 14:57:18 +01:00
README-bot
a3cd1e93be Updated README.md 2023-09-05 12:01:12 +00:00
Stefan Haller
4cf8d81155 Save diff context size in state.yml instead of config.yml (#2969) 2023-09-05 14:00:55 +02:00
Stefan Haller
7774fe0ab3 Move diff context size from UserConfig to AppState 2023-09-05 13:55:30 +02:00
Stefan Haller
f7db17f7d0 Load defaults for AppState before reading from yaml
So far this hasn't been necessary because all defaults were zero values. We're
about to add the first non-zero value though, and it's important that it is
initialized correctly for users who have a state.yml that doesn't have it yet.
2023-09-04 17:50:49 +02:00
Stefan Haller
1106981827 Extract a SaveAppStateAndLogError function
It seems that most actions that change a state option and resave the state want
to just log the error.
2023-09-04 17:50:49 +02:00
Stefan Haller
1dac4158e9 Don't pass ignoreWhitespace to git commands
Now that AppState is available via common.Common, they can take it from there.
2023-09-04 17:50:49 +02:00
Stefan Haller
18c5780485 Add AppState to common.Common 2023-09-04 17:48:39 +02:00
README-bot
e895052437 Updated README.md 2023-09-04 14:58:07 +00:00
Stefan Haller
ade8d78c63 Add support for external diff commands (e.g. difftastic) (#2868) 2023-09-04 16:57:49 +02:00
Stefan Haller
2e74b7177e Fix keybinding for editing config file 2023-09-04 16:52:30 +02:00
Stefan Haller
6266e19623 Add support for external diff commands (e.g. difftastic) 2023-09-04 16:52:30 +02:00
Stefan Haller
c67aa49856 Add explicit --no-ext-diff arg to CommitCommands.ShowCmdObj
We do this for ShowFileDiffCmdObj and WorktreeFileDiffCmdObj too, so why not
here.
2023-09-04 13:16:00 +02:00
README-bot
3a54089859 Updated README.md 2023-09-04 07:45:52 +00:00
Stefan Haller
e2c447b8d0 Improve prompts when amending commits (#2980) 2023-09-04 09:45:34 +02:00
Stefan Haller
843e12286f Improve prompts when amending commits
This fixes two minor problems with the prompts:

1. When pressing shift-A in the local commits view, it would first prompt
   whether to stage all files, and then it would prompt whether to amend the
   commit at all. This doesn't make sense, it needs to be the other way round.

2. When pressing shift-A on the head commit in an interactive rebase, we would
   ask whether they want to amend the last commit, like when pressing shift-A in
   the files view. While this is technically correct, the fact that we're
   amending the head commit in this case is just an implementation detail, and
   from the user's point of view it's better to use the same prompt as we do for
   any other commit.

To fix these, we remove the confirmation panel from AmendHelper.AmendHead() and
instead add it at the two call sites, so that we have more control over this.
2023-09-01 18:55:16 +02:00
README-bot
e60936e964 Updated README.md 2023-08-31 14:13:26 +00:00
Stefan Haller
b9cfd89b80 Fix escape not cancelling filter mode, but closing the menu instead (#2977) 2023-08-31 16:13:06 +02:00
Stefan Haller
de4224bbe4 Fix escape not cancelling filter mode, but closing the menu instead
When filtering is on in a menu, pressing esc should only cancel the filter, but
not close the menu.
2023-08-30 22:37:13 +02:00
README-bot
ce91de76e4 Updated README.md 2023-08-30 09:22:09 +00:00
Stefan Haller
acfce07aa0 Don't show toasts when running integration tests (#2975) 2023-08-30 11:21:52 +02:00
Stefan Haller
81216189e4 Don't show toasts when running integration tests
It's pointless because you can't check for them in tests anyway, so they
unnecessarily slow down the test run by two seconds for no reason.
2023-08-30 10:54:32 +02:00
Stefan Haller
2b26b380b6 Check for staged files for "Amend commit" and "Create fixup commit" (#2970) 2023-08-29 09:23:45 +02:00
Stefan Haller
1ba8d3060b Ensure committable files for "amend to" and "create fixup commit"
These would previously fail with confusing error messages when no files were
staged.

The diff is best viewed with "ignore whitespace" turned on.
2023-08-29 09:10:59 +02:00
Stefan Haller
f561007fb7 Extract a WithEnsureCommitableFiles function
This encapsulates the logic to make sure we have something to commit; which is
to
- auto-stage all files if no files are staged and the SkipNoStagedFilesWarning
config is on
- otherwise, prompt the user whether they want to stage all files
- error out if we don't have any files at all

Of these, the first one was only done when committing with the built-in commit
message panel; there's no reason why it shouldn't also be done when committing
with the editor, or when amending, and now it is.
2023-08-29 09:10:59 +02:00
Stefan Haller
a63d5891e2 Add command to show divergence from upstream (#2871) 2023-08-29 08:22:44 +02:00
Stefan Haller
df38e954f4 Add integration test for the new divergence log 2023-08-29 08:16:40 +02:00
Stefan Haller
ea532b4729 Add DoesNotContainAnyOf matcher 2023-08-29 08:16:40 +02:00
Stefan Haller
572d1b5920 Add "Show divergence from upstream" entry to Upstream menu in branches panel 2023-08-29 08:16:40 +02:00
Stefan Haller
e8fac6ca73 Extract a SubCommitsHelper from SwitchToSubCommitsController
We want to use it from BranchesController too.
2023-08-29 08:16:40 +02:00
Stefan Haller
4de3fadb00 Remove sub_commits_context's Title method
It implemented this because it wants to do custom truncation of the ref name;
however, we can achieve the same thing by passing the truncated ref name to our
DynamicTitleBuilder, which was previously unused.
2023-08-29 08:16:40 +02:00
Stefan Haller
12f24aa8b4 Add option RefToShowDivergenceFrom to GetCommitsOptions
Not used yet.
2023-08-29 08:16:40 +02:00
Stefan Haller
911aa7774b Don't return commits from setCommitMergedStatuses
Since the slice stores pointers to objects, and we're only modifying the objects
but not the slice itself, there's no need to return it and assign it back. This
will allow us to call the function for subslices of commits.

Also, move the condition that checks for an empty string inside the function;
we're going to call it from more than one place, so this makes it easier.
2023-08-29 08:16:40 +02:00
Stefan Haller
1fb0e1e151 Section headers in keybindings menu (#2911) 2023-08-29 08:14:30 +02:00
Stefan Haller
94bf279b5a Remove the magenta color on menu items that open a menu
It's distracting. But keep the ellipsis.
2023-08-29 08:04:47 +02:00
Stefan Haller
37381b610d Add sections (local, global) to the keybindings menu 2023-08-29 08:04:47 +02:00
Stefan Haller
6d4df57393 Add option to add sections to menus 2023-08-29 08:04:47 +02:00
Stefan Haller
3df01aaff0 Add a mechanism to insert non-model items into list contexts
Not used by anything yet.
2023-08-29 08:04:47 +02:00
Stefan Haller
4ee4f6f34b Make columnPositions include entries for removed columns
We will pass these positions back to clients for rendering non-model items, and
it's important that clients can consistently rely on them no matter which
columns were removed.
2023-08-28 14:21:06 +02:00
Stefan Haller
7953f7fa86 Make RenderDisplayStrings return the column positions
Not used by anything yet, but we'll need it later in this branch.
2023-08-28 14:21:06 +02:00
Stefan Haller
7a8df7795c Take removed columns into account when applying column alignments 2023-08-28 14:21:06 +02:00
Stefan Haller
aa493d3a9e Add failing test demonstrating bug with column alignments and removed columns
When columns to the left of a column with an alignment are removed, the
alignment applies to the wrong column. We'll fix this in the next commit.
2023-08-28 14:21:06 +02:00
Stefan Haller
72731f2c16 Change RenderDisplayStrings to return a slice of strings
We'll join them with newlines afterwards. This will make it easier to insert
other (non-model) items.
2023-08-28 14:21:06 +02:00
Stefan Haller
f680b6e82e Cleanup: use slices.Delete to delete elements from a slice
I find this much easier to read.
2023-08-28 14:21:06 +02:00
Stefan Haller
ec22570eac Add tests for renderLines
These are very simple yet, but we'll extend them in the next commits.
2023-08-28 14:21:06 +02:00
Stefan Haller
198ead7c14 Extract a ListRenderer struct
I'm doing this not so much because it's a great abstraction, but just because it
will make it much easier to write tests for it.
2023-08-28 14:21:06 +02:00
Stefan Haller
297a020abf Call getDisplayStrings with a valid range of model indices
It's nicer if clients can rely on the indices being valid, and don't have to
clamp themselves.
2023-08-28 14:21:06 +02:00
Stefan Haller
473d989cde Extract a renderLines function
We'll make some changes to how the display strings are rendered, so it helps to
have this code only once. This also fixes the problem that contexts using
refreshViewportOnChange weren't able to use column alignments so far. We didn't
need this yet, but it's just nice if everything works. :)
2023-08-28 14:21:06 +02:00
Stefan Haller
061bfce835 Change length parameter of getDisplayStrings to endIdx
It's more natural to work with this way, as we will see later in this branch.
2023-08-28 14:21:06 +02:00
Jesse Duffield
996e30e5d9 Add icons for files with .mdx and .svelte file extensions (#2889) 2023-08-28 20:25:03 +10:00
Jesse Duffield
ecd2a14a15 Add install instructions for openSUSE (#2727) 2023-08-28 20:10:49 +10:00
README-bot
8dfa8dc48c Updated README.md 2023-08-28 09:52:42 +00:00
Jesse Duffield
f7c183a429 Add instruction in PR template to start PRs with an imperative (#2967) 2023-08-28 19:52:25 +10:00
Jesse Duffield
473f86fcaa Add instruction in PR template to start PRs with an imperative
This spares me effort when it comes to making release notes.

Yes, sometimes it may be easier to start a message without an imperative e.g. 'When X happens, do Y'
but I don't want to overwhelm the contributor with details.
2023-08-28 19:46:45 +10:00
Jesse Duffield
28353da61b Allow adding a port to webDomain part of services config (#2908) 2023-08-28 19:38:45 +10:00
Jesse Duffield
efd49f07fd Support custom keybindings for confirm discard (#2960) 2023-08-28 19:36:39 +10:00
Jesse Duffield
b8e183acc4 Add installation guide using winget CLI (#2963) 2023-08-28 14:58:56 +10:00
Erick Garcia Godoy
821c4b05fe Add Winget to installation guide 2023-08-27 05:50:12 -03:00
Erick Garcia Godoy
4272d84fa2 Add Winget to installation guide 2023-08-27 05:45:43 -03:00
Mark Skelton
e95505453e Update docs 2023-08-25 08:52:52 -05:00
Mark Skelton
6dc34d19ea Support custom keybindings for confirm discard 2023-08-25 08:50:05 -05:00
Stefan Haller
a670fbb33c Select same commit again after pressing "e" to edit a commit (#2954) 2023-08-24 10:48:07 +02:00
Stefan Haller
98e6c119f5 Select same commit again after pressing "e" to edit a commit
When editing a commit, the index of the current commit can change; for example,
when merge commits are involved, or when working with stacked branches where
"update-ref" commands may be added above the selected commit.

Reselect the current commit after pressing "e"; this requires doing the refresh
blocking on the main thread. (Another option that I considered was to use a
SYNC refresh, and then select the new line with an OnUIThread inside the Then
function. This also works, but results in a very visible lag.)
2023-08-22 14:08:12 +02:00
Stefan Haller
c718a73d0a Call Then function only after everything is done
I'm actually surprised how this could even have worked before.
2023-08-22 14:06:31 +02:00
Stefan Haller
d74c817fd8 Panic when trying to use RefreshOptions.Then with mode ASYNC
This doesn't work, and since it took me a while of debugging to figure this out,
alert other developers earlier when they try to do it.
2023-08-22 14:06:31 +02:00
Stefan Haller
93d19db158 Add assertion to show the problem 2023-08-22 14:06:29 +02:00
Raido Oras
d7b611aa05 Allow port in webDomain for services config values 2023-08-21 14:33:58 +03:00
Jesse Duffield
fc6008fdff fix GitHub Actions warnings (#2950) 2023-08-21 19:27:26 +10:00
kyu08
a65d3119c0 upgrade golangci/golangci-lint-action to v3.7.0 2023-08-21 18:11:33 +09:00
kyu08
51ecf4089b upgrade actions/setup-go to v4 and remove actions/cache for go cache 2023-08-21 18:10:52 +09:00
kyu08
0c0fe8997b upgrade goreleaser/goreleaser-action to v4 2023-08-21 18:09:59 +09:00
kyu08
f86309dd03 upgrade JamesIves/github-sponsors-readme-action to v1.2.2 2023-08-21 18:09:33 +09:00
Stefan Haller
c31fcb7134 Switch to editor from commit message panel (#2881) 2023-08-21 10:12:26 +02:00
Stefan Haller
91ec42f3f8 Add integration test 2023-08-21 10:03:34 +02:00
Stefan Haller
8f628296ad Mention ctrl+o binding in commit message sub title
Only do this when an onSwitchToEditor function is actually provided. For the
"Move patch into new commit" command we don't, because it isn't totally
straightforward in that case.
2023-08-21 10:03:34 +02:00
Stefan Haller
61bd3e8dd2 Add key binding for switching from the commit message panel to an editor
This is useful for when you begin to type the message in lazygit's commit panel,
and then realize that you'd rather use your editor's more powerful editing
capabilities. Pressing <c-o> will take you right there.
2023-08-21 10:03:34 +02:00
Stefan Haller
7263630967 Remove obsolete comment 2023-08-21 10:03:34 +02:00
Jesse Duffield
16711c6f1a Added termux installation [README.md] (#2892) 2023-08-21 18:02:59 +10:00
Jesse Duffield
b2023f10b6 Fix: Update 'zh' to 'zh-CN' in lazygit Language Configuration (#2842) 2023-08-21 17:56:45 +10:00
Stefan Haller
e915488a83 Add gui.scrollOffBehavior config for scrolling list views by half-pages (#2939) 2023-08-21 09:11:03 +02:00
Stefan Haller
b2d629b50a Add scrollOffEnabled config 2023-08-21 09:03:45 +02:00
Stefan Haller
125d4fa9dc Pass UserConfig to checkScrollUp/Down instead of just the scrollOffMargin
This will allow us to add a scrollOffEnabled config and have the functions
respect it without changes to clients.
2023-08-21 08:10:28 +02:00
Stefan Haller
527a1596f3 Add tests for scroll-off margin of zero 2023-08-21 08:10:28 +02:00
Stefan Haller
51d9f70f9e Fix section levels
These appeared as subsections of "Platform Defaults", which doesn't make sense.
2023-08-21 08:10:28 +02:00
Jesse Duffield
83727d48b5 Feature add git flow instructions (#2785) 2023-08-21 15:51:51 +10:00
Bart
8efc175340 Add git flow description in readme 2023-08-21 15:50:54 +10:00
Jesse Duffield
6a6cb25d7e Handle trailing slash in worktree path (#2947) 2023-08-21 13:26:58 +10:00
README-bot
fd782ff568 Updated README.md 2023-08-21 01:26:29 +00:00
Jesse Duffield
dbfb469bad Add Makefile (#2937) 2023-08-21 11:26:15 +10:00
Stefan Haller
888a976fc0 Fix the commit graph display after selection jumps in commits view (#2943) 2023-08-20 08:51:29 +02:00
Stefan Haller
2073730186 Fix the commit graph display after selection jumps in commits view
When navigating in the commits view to a line that is out of view (e.g. by
pressing , or . to scroll by page, or < or > to scroll to the top or bottom),
the commit graph was not correctly highlighted. Fix this by rerendering the
viewport in this case.
2023-08-20 08:46:04 +02:00
Cristian Betivu
7a4a0c85c4 Fix test 2023-08-19 19:12:36 +03:00
Cristian Betivu
382ecb6bfe Add unit test 2023-08-19 19:10:35 +03:00
Cristian Betivu
03694f7502 Fix arg order to asserts 2023-08-19 19:10:25 +03:00
Cristian Betivu
ee308a4994 Clean before convertion? 2023-08-19 18:36:57 +03:00
Cristian Betivu
a2b2336173 Stylistic changes 2023-08-19 18:36:57 +03:00
Cristian Betivu
d7b1deb465 Clean path 2023-08-19 18:36:57 +03:00
Cristian Betivu
dd01639f57 Improve error message 2023-08-19 18:36:57 +03:00
Stefan Haller
525932fbf2 Fix sha colors when rebasing (#2946) 2023-08-19 12:38:00 +02:00
Stefan Haller
b1314349d7 Fix yellow/red coloring while rebasing
It determines the yellow/red status by getting the merge-base between the
current branch and its upstream; while we're rebasing, the current branch is
HEAD, so it tried to get the merge-base between HEAD and HEAD{u}, which doesn't
work. Fix this by passing the name of the checked-out branch separately.
2023-08-19 09:26:27 +02:00
Stefan Haller
9671f549a1 Fix the blue sha color of todo commits while rebasing
This broke with 5d8a85f7e7.
2023-08-19 09:24:00 +02:00
kyu08
a1738a77ae Add Makefile 2023-08-19 00:13:03 +09:00
Jesse Duffield
689deb72bd Add emacs-keybinds for word navigation (#2935) 2023-08-17 09:41:14 +10:00
Ching Pei Yang
84372cfad9 Add emacs-keybinds for word navigation 2023-08-15 12:22:17 +02:00
README-bot
e429415ed4 Updated README.md 2023-08-15 09:49:34 +00:00
Stefan Haller
7402be98b6 Jump to middle of the view when selection leaves the visible area (#2915) 2023-08-15 11:49:20 +02:00
Stefan Haller
341b9725d4 Add ScrollOffMargin user config
When set to a non-zero value, views will scroll when the selection gets this
close to the top or bottom of the view.
2023-08-15 11:40:40 +02:00
Stefan Haller
8f164f7bc5 Stop cycling hunks when reaching the end
Previously, when pressing right-arrow when the cursor is already in the last
hunk, it would jump back to the beginning of that hunk. This can be confusing if
the hunk is long, maybe the start of the hunk is already scrolled off the top of
the window, and then pressing right-arrow actually scrolls *backwards*, which is
counter-intuitive. It's better to do nothing in this case.

Same for left-arrow when the cursor is already in the first hunk, although here
the problem is not so severe (unless diff context was increased by a huge
amount, and the start of the first hunk is scrolled off the bottom of the
window).
2023-08-15 11:40:40 +02:00
Stefan Haller
79c11a0458 If selected line is outside, move it to the middle of the view
Previously, the current line was only moved as much as necessary so that it's in
view again. This had the problem that when jumping downwards from hunk to hunk
with the right-arrow key, only the first line of the new hunk was shown at the
bottom of the window. I prefer to put the selected line in the middle of the
view in this case, so that I can see more of the newly selected hunk.

This has the consequence that when scrolling through the view line by line using
down-arrow, the view jumps by half a screen whenever I reach the bottom. I can
see how some users might be opposed to this change, but I happen to like it too,
because it allows me to see more context of what's ahead.
2023-08-15 11:40:40 +02:00
Stefan Haller
4a4afc4639 Fix typo in comment 2023-08-15 11:40:40 +02:00
Stefan Haller
ebdfd8046a Bump gocui 2023-08-15 11:40:40 +02:00
Jesse Duffield
d99e983236 Add compare-commits demo (#2929) 2023-08-12 17:32:15 +10:00
Jesse Duffield
1c6cfafba7 Add features to table of contents 2023-08-12 17:27:25 +10:00
Jesse Duffield
83d642b74f Add demo for diffing two commits 2023-08-12 17:24:05 +10:00
Jesse Duffield
b1bc437d1b Factor out common config setup functions in demo package 2023-08-12 16:52:40 +10:00
Jesse Duffield
26989ce0ee Show commit mark before showing extra info (#2928) 2023-08-12 16:44:27 +10:00
Jesse Duffield
e6356ce10c Show commit mark before showing extra info
The 'YOU ARE HERE' marking should be shown before we show things like the current tag, otherwise it could be missed
2023-08-12 16:34:04 +10:00
README-bot
b92da0dc54 Updated README.md 2023-08-12 06:19:15 +00:00
Jesse Duffield
70e668bfcf Add more demos (#2927) 2023-08-12 16:18:59 +10:00
Jesse Duffield
8dd517870d Add commit graph demo 2023-08-12 16:16:03 +10:00
Jesse Duffield
f1753f36c8 Add rebase from marked base commit test
This also fixes a bug where after the rebase each commit in the commits view had a tick against it because we hadn't
refreshed the view since the base commit was no longer marked
2023-08-12 16:16:03 +10:00
Jesse Duffield
3ea81d4a6f Add undo demo 2023-08-12 16:15:50 +10:00
Federico
0df5cb1286 Allow deleting remote tags/branches from local tag/branch views (#2738) 2023-08-10 17:39:26 +10:00
Jesse Duffield
c43830b027 Support editing files in existing neovim instance (#2916) 2023-08-10 17:23:58 +10:00
Stefan Haller
08624b8ef7 Fix jumping to the correct line from the staging view (#2919) 2023-08-10 07:27:11 +02:00
Stefan Haller
73b68927af Fix bug in LineNumberOfLine
This fixes a regression that was introduced in 73c7dc9c5d.
2023-08-10 07:22:42 +02:00
Stefan Haller
bf699d3a79 Add test case for LineNumberOfLine()
There's a bug in LineNumberOfLine, but the existing test coverage doesn't catch
it, as the only test case for this was one where oldStart and newStart were the
same for all hunks. Add a test case where newStart is different for one of the
hunks; this demonstrates a bug, where all expected results from index 12 on are
off by one.
2023-08-10 07:22:42 +02:00
README-bot
f2e8d549ba Updated README.md 2023-08-09 22:36:48 +00:00
Jesse Duffield
4ffb6c0fea Show dialogue when attempting to open info link fails (#2899) 2023-08-10 08:36:32 +10:00
Simon Whitaker
54776052a1 If OpenLink errors, show a dialog instead
If the command used by OSCommand.OpenLink fails, lazygit crashes. With this change, if the OpenLink command fails, lazygit just shows a dialog inviting the user to visit the relevant URL.

Fixes #2882
2023-08-09 13:12:40 +01:00
Jesse Duffield
9c5eedf748 use 'suspend' instead of 'editInTerminal' internally
'suspend' is a more appropriate name, especially now that you can choose not to suspend despite
still being in a terminal
2023-08-09 22:03:58 +10:00
Jesse Duffield
ca08956f77 Use soft wrapping in config doc
Looking online I can't find any consensus about whether soft or hard wrap is better.
This post goes into the pros/cons: https://martin-ueding.de/posts/hard-vs-soft-line-wrap/

I find that editing hard-wrapped text is a pain in the ass, and it's hard to enforce
consistency. So I'm switching to soft-wrapping for this doc.
2023-08-09 21:33:12 +10:00
Jesse Duffield
aa74239f05 Add nvim-remote editor preset
This allows us to jump back to the parent neovim process when we want to edit a file, rather than opening a new neovim
process within lazygit.

Arguably this should be the default, but I'm not familiar with the various ways people use lazygit with neovim.
2023-08-09 21:32:53 +10:00
Jesse Duffield
9e7018db8a Honour editInTerminal value when opening a worktree folder
There was no good reason not to do this in the first place.
2023-08-09 21:00:27 +10:00
Jesse Duffield
cdea5b4873 Fix issue where explosion effect was out-of-view (#2909) 2023-08-08 23:07:46 +10:00
Jesse Duffield
4b0432423d Reset origin when clearing view 2023-08-08 22:01:43 +10:00
Jesse Duffield
2607580a09 Add Click() to GuiDriver (#2898) 2023-08-08 17:05:32 +10:00
Jesse Duffield
ebd56bd8d5 Mention JSON schema (#2893) 2023-08-08 08:19:05 +10:00
Simon Whitaker
ed1547e0cb Add a Click() primitive to the integration test library 2023-08-07 15:10:28 +01:00
微笑
82b5c20050 Add zh-TW to docs/Config.md
Co-authored-by: Bill ZHANG <36790218+Lutra-Fs@users.noreply.github.com>
2023-08-07 19:52:28 +08:00
EmilySeville7cfg
f32fe84c9b feat(doc): better JSON schema usage explanation 2023-08-07 18:27:37 +10:00
EmilySeville7cfg
fb05ee369f feat(doc): mention JSON schema 2023-08-07 03:38:30 +10:00
Jothi Prasath
213da4fddd added termux installation 2023-08-06 20:12:11 +05:30
Harshit Tomar
e78a09ebab added svelte and mdx 2023-08-06 20:26:07 +10:00
微笑
1431ffcebf Corrected 'zh' to 'zh-CN' in lazygit docs for proper localization 2023-07-29 12:07:40 +08:00
Pavel Dostál
1a2a301aa2 Add instructions for openSUSE 2023-06-12 08:21:34 +02:00
1316 changed files with 67953 additions and 37178 deletions

7
.codespellrc Normal file
View File

@@ -0,0 +1,7 @@
[codespell]
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
skip = .git*,go.sum,*.lock,.codespellrc,vendor,translations,Keybindings_*.md
check-hidden = true
# camel-cased
ignore-regex = (\b[A-Za-z][a-z]*[A-Z]\S+\b|\.edn\b|\S+…|\\nd\b)
ignore-words-list = fomrat,inbetween

4
.editorconfig Normal file
View File

@@ -0,0 +1,4 @@
root = true
[*.go]
indent_style = tab

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
*.go text
*.md text eol=lf
*.json text eol=lf

View File

@@ -2,9 +2,15 @@
- **Please check if the PR fulfills these requirements**
* [ ] Cheatsheets are up-to-date (run `go run scripts/cheatsheet/main.go generate`)
* [ ] Cheatsheets are up-to-date (run `go generate ./...`)
* [ ] Code has been formatted (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md) for the integration test guide)
* [ ] Text is internationalised (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if necessary
* [ ] If a new UserConfig entry was added, make sure it can be hot-reloaded (see [here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig))
* [ ] Docs have been updated if necessary
* [ ] You've read through your own file changes for silly mistakes etc
<!--
Be sure to name your PR with an imperative e.g. 'Add worktrees view'
see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for examples
-->

3
.github/release.yml vendored
View File

@@ -21,6 +21,9 @@ changelog:
- title: I18n 🌎
labels:
- i18n
- title: Performance Improvements 📊
labels:
- performance
- title: Other Changes
labels:
- "*"

View File

@@ -3,22 +3,22 @@ name: Continuous Delivery
on:
push:
tags:
- 'v*'
- "v*"
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Unshallow repo
run: git fetch --prune --unshallow
- name: Setup Go
uses: actions/setup-go@v1
uses: actions/setup-go@v5
with:
go-version: 1.20.x
go-version: 1.22.x
- name: Run goreleaser
uses: goreleaser/goreleaser-action@v1
uses: goreleaser/goreleaser-action@v4
with:
distribution: goreleaser
version: v1.17.2

View File

@@ -1,7 +1,7 @@
name: Continuous Integration
env:
GO_VERSION: 1.20
GO_VERSION: 1.22
on:
push:
@@ -28,24 +28,22 @@ jobs:
GOFLAGS: -mod=vendor
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v1
uses: actions/setup-go@v5
with:
go-version: 1.20.x
- name: Cache build
uses: actions/cache@v3
with:
path: |
${{matrix.cache_path}}
~/go/pkg/mod
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
restore-keys: |
${{runner.os}}-go-
go-version: 1.22.x
- name: Test code
# we're passing -short so that we skip the integration tests, which will be run in parallel below
run: |
go test ./... -short
mkdir -p /tmp/code_coverage
go test ./... -short -cover -args "-test.gocoverdir=/tmp/code_coverage"
- name: Upload code coverage artifacts
uses: actions/upload-artifact@v3
with:
name: coverage-unit-${{ matrix.os }}-${{ github.run_id }}
path: /tmp/code_coverage
integration-tests:
strategy:
fail-fast: false
@@ -63,11 +61,11 @@ jobs:
GOFLAGS: -mod=vendor
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Restore Git cache
if: matrix.git-version != 'latest'
id: cache-git-restore
uses: actions/cache/restore@v3
uses: actions/cache/restore@v4
with:
path: ~/git-${{matrix.git-version}}
key: ${{runner.os}}-git-${{matrix.git-version}}
@@ -84,28 +82,28 @@ jobs:
run: sudo make -C "$HOME/git-${{matrix.git-version}}" -j install
- name: Save Git cache
if: steps.cache-git-restore.outputs.cache-hit != 'true' && matrix.git-version != 'latest'
uses: actions/cache/save@v3
uses: actions/cache/save@v4
with:
path: ~/git-${{matrix.git-version}}
key: ${{runner.os}}-git-${{matrix.git-version}}
- name: Setup Go
uses: actions/setup-go@v1
uses: actions/setup-go@v5
with:
go-version: 1.20.x
- name: Cache build
uses: actions/cache@v1
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
restore-keys: |
${{runner.os}}-go-
go-version: 1.22.x
- name: Print git version
run: git --version
- name: Test code
env:
# See https://go.dev/blog/integration-test-coverage
LAZYGIT_GOCOVERDIR: /tmp/code_coverage
run: |
mkdir -p /tmp/code_coverage
./scripts/run_integration_tests.sh
- name: Upload code coverage artifacts
uses: actions/upload-artifact@v3
with:
name: coverage-integration-${{ matrix.git-version }}-${{ github.run_id }}
path: /tmp/code_coverage
build:
runs-on: ubuntu-latest
env:
@@ -113,20 +111,11 @@ jobs:
GOARCH: amd64
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v1
uses: actions/setup-go@v5
with:
go-version: 1.20.x
- name: Cache build
uses: actions/cache@v1
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-build
restore-keys: |
${{runner.os}}-go-
go-version: 1.22.x
- name: Build linux binary
run: |
GOOS=linux go build
@@ -149,31 +138,23 @@ jobs:
GOARCH: amd64
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v1
uses: actions/setup-go@v5
with:
go-version: 1.20.x
- name: Cache build
uses: actions/cache@v1
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-build
restore-keys: |
${{runner.os}}-go-
- name: Check Cheatsheet
run: |
go run scripts/cheatsheet/main.go check
go-version: 1.22.x
- name: Check Vendor Directory
# ensure our vendor directory matches up with our go modules
run: |
go mod vendor && git diff --exit-code || (echo "Unexpected change to vendor directory. Run 'go mod vendor' locally and commit the changes" && exit 1)
- name: Check Integration Test List
# ensure our integration test list is up to date
- name: Check go.mod file
# ensure our go.mod file is clean
run: |
go generate pkg/integration/tests/tests.go && git diff --exit-code || (echo "Integration test list not up to date. Run 'go generate pkg/integration/tests/tests.go' locally and commit the changes" && exit 1)
go mod tidy && git diff --exit-code || (echo "go.mod file is not clean. Run 'go mod tidy' locally and commit the changes" && exit 1)
- name: Check All Auto-Generated Files
# ensure all our auto-generated files are up to date
run: |
go generate ./... && git diff --quiet || (git status -s; echo "Auto-generated files not up to date. Run 'go generate ./...' locally and commit the changes" && exit 1)
shell: bash # needed so that we get "-o pipefail"
- name: Check Filenames
run: scripts/check_filenames.sh
@@ -183,24 +164,15 @@ jobs:
GOFLAGS: -mod=vendor
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v1
uses: actions/setup-go@v5
with:
go-version: 1.20.x
- name: Cache build
uses: actions/cache@v1
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
restore-keys: |
${{runner.os}}-go-
go-version: 1.22.x
- name: Lint
uses: golangci/golangci-lint-action@v3.1.0
uses: golangci/golangci-lint-action@v6.1.0
with:
version: latest
version: v1.60
- name: errors
run: golangci-lint run
if: ${{ failure() }}
@@ -212,4 +184,57 @@ jobs:
with:
mode: exactly
count: 1
labels: "ignore-for-release, feature, enhancement, bug, maintenance, docs, i18n"
labels: "ignore-for-release, feature, enhancement, bug, maintenance, docs, i18n, performance"
upload-coverage:
# List all jobs that produce coverage files
needs: [unit-tests, integration-tests]
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.22.x
- name: Download all coverage artifacts
uses: actions/download-artifact@v3
with:
path: /tmp/code_coverage
- name: Combine coverage files
run: |
# Find all directories in /tmp/code_coverage and create a comma-separated list
COVERAGE_DIRS=$(find /tmp/code_coverage -mindepth 1 -maxdepth 1 -type d -printf '/tmp/code_coverage/%f,' | sed 's/,$//')
echo "Coverage directories: $COVERAGE_DIRS"
# Run the combine command with the generated list
go tool covdata textfmt -i=$COVERAGE_DIRS -o coverage.out
echo "Combined coverage:"
go tool cover -func coverage.out | tail -1 | awk '{print $3}'
- name: Upload to Codacy
run: |
CODACY_PROJECT_TOKEN=${{ secrets.CODACY_PROJECT_TOKEN }} \
bash <(curl -Ls https://coverage.codacy.com/get.sh) report \
--force-coverage-parser go -r coverage.out
check-for-fixups:
runs-on: ubuntu-latest
if: github.ref != 'refs/heads/master'
steps:
# See https://github.com/actions/checkout/issues/552#issuecomment-1167086216
- name: "PR commits"
run: echo "PR_FETCH_DEPTH=$(( ${{ github.event.pull_request.commits }} ))" >> "${GITHUB_ENV}"
- name: "Checkout PR branch and all PR commits"
uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: ${{ env.PR_FETCH_DEPTH }}
- name: Check for fixups
run: |
./scripts/check_for_fixups.sh ${{ github.event.pull_request.base.ref }}

25
.github/workflows/codespell.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
# Codespell configuration is within .codespellrc
---
name: Codespell
on:
push:
branches: [master]
pull_request:
branches: [master]
permissions:
contents: read
jobs:
codespell:
name: Check for spelling errors
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Annotate locations with typos
uses: codespell-project/codespell-problem-matcher@v1
- name: Codespell
uses: codespell-project/actions-codespell@v2

View File

@@ -9,20 +9,20 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Generate Sponsors 💖
uses: JamesIves/github-sponsors-readme-action@v1.0.8
uses: JamesIves/github-sponsors-readme-action@v1.2.2
with:
token: ${{ secrets.SPONSORS_TOKEN }}
file: 'README.md'
file: "README.md"
if: ${{ github.repository == 'jesseduffield/lazygit' }}
- name: Commit and push if changed
run: |-
git diff
git config --global user.email "actions@users.noreply.github.com"
git config --global user.name "README-bot"
git add README.md
git commit -m "Updated README.md" || exit 0
git push
- name: Create Pull Request 🚀
uses: peter-evans/create-pull-request@v6
with:
commit-message: "README.md: Update Sponsors"
title: "README.md: Update Sponsors"
author: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
labels: "ignore-for-release"
delete-branch: true

9
.gitignore vendored
View File

@@ -5,9 +5,7 @@
# Hidden
.*
# TODO
TODO.*
!.codespellrc
# Notes
*.notes
@@ -22,6 +20,7 @@ lazygit.exe
# Exceptions
!.gitignore
!.gitattributes
!.goreleaser.yml
!.golangci.yml
!.circleci/
@@ -35,10 +34,12 @@ lazygit.exe
test/git_server/data
test/results/**
test/_results/**
oryxBuildBinary
__debug_bin
.worktrees
demo/output/*
coverage.out

View File

@@ -1,30 +1,36 @@
linters:
disable:
- structcheck # gives false positives
enable:
- gofumpt
- thelper
- goimports
- tparallel
- wastedassign
- exportloopref
- unparam
- prealloc
- unconvert
- exhaustive
- makezero
- nakedret
# - goconst # TODO: enable and fix issues
- copyloopvar
fast: false
linters-settings:
copyloopvar:
# Check all assigning the loop variable to another variable.
# Default: false
# If true, an assignment like `a := x` will be detected as an error.
check-alias: true
exhaustive:
default-signifies-exhaustive: true
staticcheck:
# SA1019 is for checking that we're not using fields marked as deprecated
# in a comment. It decides this in a loose way so I'm silencing it. Also because
# it's tripping on our own structs.
checks: ["all", "-SA1019"]
nakedret:
# the gods will judge me but I just don't like naked returns at all
max-func-lines: 0
run:
go: '1.20'
go: '1.22'
timeout: 10m

45
.vscode/launch.json vendored
View File

@@ -7,11 +7,12 @@
"request": "launch",
"mode": "auto",
"program": "main.go",
"args": ["--debug", "--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"],
"args": [
"--debug",
"--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"
],
"hideSystemGoroutines": true,
"console": "integratedTerminal",
"presentation": {
"hidden": true
}
},
{
"name": "Tail Lazygit logs",
@@ -19,17 +20,41 @@
"request": "launch",
"mode": "auto",
"program": "main.go",
"args": ["--logs", "--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"],
"args": [
"--logs",
"--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"
],
"console": "integratedTerminal",
"presentation": {
"hidden": true
}
}
},
{
"name": "Attach to a running Lazygit",
"type": "go",
"request": "attach",
"mode": "local",
"processId": "lazygit",
"hideSystemGoroutines": true,
"console": "integratedTerminal",
},
{
// To use this, first start an integration test with the "cli" runner and
// use the -debug option; e.g.
// $ make integration-test-cli -- -debug tag/reset.go
"name": "Attach to integration test runner",
"type": "go",
"request": "attach",
"mode": "local",
"processId": "test_lazygit",
"hideSystemGoroutines": true,
"console": "integratedTerminal",
},
],
"compounds": [
{
"name": "Run with logs",
"configurations": ["Tail Lazygit logs", "Debug Lazygit"],
"configurations": [
"Tail Lazygit logs",
"Debug Lazygit"
],
"stopAll": true
}
]

5
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"gopls": {
"formatting.gofumpt": true,
},
}

View File

@@ -10,6 +10,15 @@ before making a change.
[This video](https://www.youtube.com/watch?v=kNavnhzZHtk) walks through the process of adding a small feature to lazygit. If you have no idea where to start, watching that video is a good first step.
## Codebase guide
[This doc](./docs/dev/Codebase_Guide.md) explains:
* what the different packages in the codebase are for
* where important files live
* important concepts in the code
* how the event loop works
* other useful information
## All code changes happen through Pull Requests
Pull requests are the best way to propose changes to the codebase. We actively
@@ -21,6 +30,8 @@ welcome your pull requests:
4. Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
5. Issue that pull request!
Please do not raise pull request from your fork's master branch: make a feature branch instead. Lazygit maintainers will sometimes push changes to your branch when reviewing a PR and we often can't do this if you use your master branch.
If you've never written Go in your life, then join the club! Lazygit was the maintainer's first Go program, and most contributors have never used Go before. Go is widely considered an easy-to-learn language, so if you're looking for an open source project to gain dev experience, you've come to the right place.
## Running in a VSCode dev container
@@ -97,6 +108,10 @@ To run gofumpt from your terminal go:
go install mvdan.cc/gofumpt@latest && gofumpt -l -w .
```
## Programming Font
Lazygit supports [Nerd Fonts](https://www.nerdfonts.com) to render certain icons. Sometimes we use some of these icons verbatim in string literals in the code (mainly in tests), so you need to set your development environment to use a nerd font to see these.
## 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!).
@@ -139,31 +154,7 @@ If you want to trigger a debug session from VSCode, you can use the following sn
## Profiling
If you want to investigate what's contributing to CPU usage you can add the following to the top of the `main()` function in `main.go`
```go
import "runtime/pprof"
func main() {
f, err := os.Create("cpu.prof")
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
defer f.Close()
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
...
```
Then run lazygit, and afterwards, from your terminal, run:
```sh
go tool pprof --web cpu.prof
```
That should open an application which allows you to view the breakdown of CPU usage.
If you want to investigate what's contributing to CPU or memory usage, see [this separate document](docs/dev/Profiling.md).
## Testing

View File

@@ -2,14 +2,14 @@
# docker build -t lazygit .
# docker run -it lazygit:latest /bin/sh
FROM golang:1.20 as build
FROM golang:1.22 as build
WORKDIR /go/src/github.com/jesseduffield/lazygit/
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build
FROM alpine:3.15
FROM alpine:3.19
RUN apk add --no-cache -U git xdg-utils
WORKDIR /go/src/github.com/jesseduffield/lazygit/
COPY --from=build /go/src/github.com/jesseduffield/lazygit ./

72
Makefile Normal file
View File

@@ -0,0 +1,72 @@
.PHONY: all
all: build
.PHONY: build
build:
go build -gcflags='all=-N -l'
.PHONY: install
install:
go install
.PHONY: run
run: build
./lazygit
# Run `make run-debug` in one terminal tab and `make print-log` in another to view the program and its log output side by side
.PHONY: run-debug
run-debug:
go run main.go -debug
.PHONY: print-log
print-log:
go run main.go --logs
.PHONY: unit-test
unit-test:
go test ./... -short
.PHONY: test
test: unit-test integration-test-all
# Generate all our auto-generated files (test list, cheatsheets, maybe other things in the future)
.PHONY: generate
generate:
go generate ./...
.PHONY: format
format:
gofumpt -l -w .
.PHONY: lint
lint:
golangci-lint run
# For more details about integration test, see https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md.
.PHONY: integration-test-tui
integration-test-tui:
go run cmd/integration_test/main.go tui $(filter-out $@,$(MAKECMDGOALS))
.PHONY: integration-test-cli
integration-test-cli:
go run cmd/integration_test/main.go cli $(filter-out $@,$(MAKECMDGOALS))
.PHONY: integration-test-all
integration-test-all:
go test pkg/integration/clients/*.go
.PHONY: bump-gocui
bump-gocui:
scripts/bump_gocui.sh
.PHONY: bump-lazycore
bump-lazycore:
scripts/bump_lazycore.sh
.PHONY: record-demo
record-demo:
demo/record_demo.sh $(filter-out $@,$(MAKECMDGOALS))
.PHONY: vendor
vendor:
go mod vendor && go mod tidy

170
README.md

File diff suppressed because one or more lines are too long

26
cmd/i18n/main.go Normal file
View File

@@ -0,0 +1,26 @@
package main
import (
"encoding/json"
"log"
"os"
"github.com/jesseduffield/lazygit/pkg/i18n"
)
func saveLanguageFileToJson(tr *i18n.TranslationSet, filepath string) error {
jsonData, err := json.MarshalIndent(tr, "", " ")
if err != nil {
return err
}
jsonData = append(jsonData, '\n')
return os.WriteFile(filepath, jsonData, 0o644)
}
func main() {
err := saveLanguageFileToJson(i18n.EnglishTranslationSet(), "en.json")
if err != nil {
log.Fatal(err)
}
}

View File

@@ -16,7 +16,7 @@ Usage:
> go run cmd/integration_test/main.go cli [--slow] [--sandbox] <test1> <test2> ...
If you pass no test names, it runs all tests
Accepted environment variables:
KEY_PRESS_DELAY (e.g. 200): the number of milliseconds to wait between keypresses
INPUT_DELAY (e.g. 200): the number of milliseconds to wait between keypresses or mouse clicks
TUI mode:
> go run cmd/integration_test/main.go tui
@@ -26,6 +26,29 @@ Usage:
> go run cmd/integration_test/main.go help
`
type flagInfo struct {
name string // name of the flag; can be used with "-" or "--"
flag *bool // a pointer to the variable that should be set to true when this flag is passed
}
// Takes the args that you want to parse (excluding the program name and any
// subcommands), and returns the remaining args with the flags removed
func parseFlags(args []string, flags []flagInfo) []string {
outer:
for len(args) > 0 {
for _, f := range flags {
if args[0] == "-"+f.name || args[0] == "--"+f.name {
*f.flag = true
args = args[1:]
continue outer
}
}
break
}
return args
}
func main() {
if len(os.Args) < 2 {
log.Fatal(usage)
@@ -35,23 +58,26 @@ func main() {
case "help":
fmt.Println(usage)
case "cli":
testNames := os.Args[2:]
slow := false
sandbox := false
// get the next arg if it's --slow
if len(os.Args) > 2 {
if os.Args[2] == "--slow" || os.Args[2] == "-slow" {
testNames = os.Args[3:]
slow = true
} else if os.Args[2] == "--sandbox" || os.Args[2] == "-sandbox" {
testNames = os.Args[3:]
sandbox = true
}
}
clients.RunCLI(testNames, slow, sandbox)
waitForDebugger := false
raceDetector := false
testNames := parseFlags(os.Args[2:], []flagInfo{
{"slow", &slow},
{"sandbox", &sandbox},
{"debug", &waitForDebugger},
{"race", &raceDetector},
})
clients.RunCLI(testNames, slow, sandbox, waitForDebugger, raceDetector)
case "tui":
clients.RunTUI()
raceDetector := false
remainingArgs := parseFlags(os.Args[2:], []flagInfo{
{"race", &raceDetector},
})
if len(remainingArgs) > 0 {
log.Fatal("tui only supports the -race argument.")
}
clients.RunTUI(raceDetector)
default:
log.Fatal(usage)
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ You can add custom command keybindings in your config.yml (accessible by pressin
customCommands:
- key: '<c-r>'
context: 'commits'
command: 'hub browse -- "commit/{{.SelectedLocalCommit.Sha}}"'
command: 'hub browse -- "commit/{{.SelectedLocalCommit.Hash}}"'
- key: 'a'
context: 'files'
command: "git {{if .SelectedFile.HasUnstagedChanges}} add {{else}} reset {{end}} {{.SelectedFile.Name | quote}}"
@@ -59,6 +59,7 @@ For a given custom command, here are the allowed fields:
| description | Label for the custom command when displayed in the keybindings menu | no |
| stream | Whether you want to stream the command's output to the Command Log panel | no |
| showOutput | Whether you want to show the command's output in a popup within Lazygit | no |
| outputTitle | The title to display in the popup panel if showOutput is true. If left unset, the command will be used as the title. | no |
| after | Actions to take after the command has completed | no |
Here are the options for the `after` key:
@@ -86,6 +87,11 @@ The permitted contexts are:
| stash | The 'Stash' tab |
| global | This keybinding will take affect everywhere |
> **Bonus**
>
> You can use a comma-separated string, such as `context: 'commits, subCommits'`, to make it effective in multiple contexts.
## Prompts
### Common fields
@@ -290,9 +296,7 @@ Here's an example using a command but not specifying anything else: so each line
Your commands can contain placeholder strings using Go's [template syntax](https://jan.newmarch.name/golang/template/chapter-template.html). The template syntax is pretty powerful, letting you do things like conditionals if you want, but for the most part you'll simply want to be accessing the fields on the following objects:
```
SelectedLocalCommit
SelectedReflogCommit
SelectedSubCommit
SelectedCommit
SelectedFile
SelectedPath
SelectedLocalBranch
@@ -305,7 +309,10 @@ SelectedWorktree
CheckedOutBranch
```
To see what fields are available on e.g. the `SelectedFile`, see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/commands/models/file.go) (all the modelling lives in the same directory). Note that the custom commands feature does not guarantee backwards compatibility (until we hit Lazygit version 1.0 of course) which means a field you're accessing on an object may no longer be available from one release to the next. Typically however, all you'll need is `{{.SelectedFile.Name}}`, `{{.SelectedLocalCommit.Sha}}` and `{{.SelectedLocalBranch.Name}}`. In the future we will likely introduce a tighter interface that exposes a limited set of fields for each model.
(For legacy reasons, `SelectedLocalCommit`, `SelectedReflogCommit`, and `SelectedSubCommit` are also available, but they are deprecated.)
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).
## Keybinding collisions

View File

@@ -1,6 +1,6 @@
# Custom Pagers
Lazygit supports custom pagers, [configured](/docs/Config.md) in the config.yml file (which can be opened by pressing `o` in the Status panel).
Lazygit supports custom pagers, [configured](/docs/Config.md) in the config.yml file (which can be opened by pressing `e` in the Status panel).
Support does not extend to Windows users, because we're making use of a package which doesn't have Windows support.
@@ -26,6 +26,8 @@ git:
![](https://i.imgur.com/QJpQkF3.png)
A cool feature of delta is --hyperlinks, which renders clickable links for the line numbers in the left margin, and lazygit supports these. To use them, set the `pager:` config to `delta --dark --paging=never --line-numbers --hyperlinks --hyperlinks-file-link-format="lazygit-edit://{path}:{line}"`; this allows you to click on an underlined line number in the diff to jump right to that same line in your editor.
## Diff-so-fancy
```yaml
@@ -62,3 +64,25 @@ git:
```
If you set `useConfig: true`, lazygit will use whatever pager is specified in `$GIT_PAGER`, `$PAGER`, or your *git config*. If the pager ends with something like ` | less` we will strip that part out, because less doesn't play nice with our rendering approach. If the custom pager uses less under the hood, that will also break rendering (hence the `--paging=never` flag for the `delta` pager).
## Using external diff commands
Some diff tools can't work as a simple pager like the ones above do, because they need access to the entire diff, so just post-processing git's diff is not enough for them. The most notable example is probably [difftastic](https://difftastic.wilfred.me.uk).
These can be used in lazygit by using the `externalDiffCommand` config; in the case of difftastic, that could be
```yaml
git:
paging:
externalDiffCommand: difft --color=always
```
The `colorArg`, `pager`, and `useConfig` options are not used in this case.
You can add whatever extra arguments you prefer for your difftool; for instance
```yaml
git:
paging:
externalDiffCommand: difft --color=always --display=inline --syntax-highlight=off
```

65
docs/Fixup_Commits.md Normal file
View File

@@ -0,0 +1,65 @@
# Fixup Commits
## Background
There's this common scenario that you have a PR in review, the reviewer is
requesting some changes, and you make those changes and would normally simply
squash them into the original commit that they came from. If you do that,
however, there's no way for the reviewer to see what you changed. You could just
make a separate commit with those changes at the end of the branch, but this is
not ideal because it results in a git history that is not very clean.
To help with this, git has a concept of fixup commits: you do make a separate
commit, but the subject of this commit is the string "fixup! " followed by the
original commit subject. This both tells the reviewer what's going on (you are
making a change that you later will squash into the designated commit), and it
provides an easy way to actually perform this squash operation when you are
ready to do that (before merging).
## Creating fixup commits
You could of course create fixup commits manually by typing in the commit
message with the prefix yourself. But lazygit has an easier way to do that:
in the Commits view, select the commit that you want to create a fixup for, and
press shift-F (for "Create fixup commit for this commit"). This automatically
creates a commit with the appropriate subject line.
Don't confuse this with the lowercase "f" command ("Fixup commit"); that one
squashes the selected commit into its parent, this is not what we want here.
## Creating amend commits
There's a special type of fixup commit that uses "amend!" instead of "fixup!" in
the commit message subject; in addition to fixing up the original commit with
changes it allows you to also (or only) change the commit message of the
original commit. The menu that appears when pressing shift-F has options for
both of these; they bring up a commit message panel similar to when you reword a
commit, but then create the "amend!" commit containing the new message. Note
that in that panel you only type the new message as you want it to be
eventually; lazygit then takes care of formatting the "amend!" commit
appropriately for you (with the subject of your new message moving into the body
of the "amend!" commit).
## Squashing fixup commits
When you're ready to merge the branch and want to squash all these fixup commits
that you created, that's very easy to do: select the first commit of your branch
and hit shift-S (for "Squash all 'fixup!' commits above selected commit
(autosquash)"). Boom, done.
## Finding the commit to create a fixup for
When you are making changes to code that you changed earlier in a long branch,
it can be tedious to find the commit to squash it into. Lazygit has a command to
help you with this, too: in the Files view, press ctrl-f to select the right
base commit in the Commits view automatically. From there, you can either press
shift-F to create a fixup commit for it, or shift-A to amend your changes into
the commit if you haven't published your branch yet.
If you have many modifications in your working copy, it is a good idea to stage
related changes that are meant to go into the same fixup commit; if no changes
are staged, ctrl-f works on all unstaged modifications, and then it might show
an error if it finds multiple different base commits. If you are interested in
what the command does to do its magic, and how you can help it work better, you
may want to read the [design document](dev/Find_Base_Commit_For_Fixup_Design.md)
that describes this.

View File

@@ -1,10 +1,11 @@
# Documentation Overview
* [Configuration](./Config.md).
* [Custom Commands](./Custom_Command_Keybindings.md)
* [Custom Pagers](./Custom_Pagers.md)
* [Keybindings](./keybindings)
* [Undo/Redo](./Undoing.md)
* [Searching/Filtering](./Searching.md)
* [Stacked Branches](./Stacked_Branches.md)
* [Dev docs](./dev)
# Documentation Overview
* [Configuration](./Config.md).
* [Custom Commands](./Custom_Command_Keybindings.md)
* [Custom Pagers](./Custom_Pagers.md)
* [Dev docs](./dev)
* [Keybindings](./keybindings)
* [Undo/Redo](./Undoing.md)
* [Range Select](./Range_Select.md)
* [Searching/Filtering](./Searching.md)
* [Stacked Branches](./Stacked_Branches.md)

14
docs/Range_Select.md Normal file
View File

@@ -0,0 +1,14 @@
# Range Select
Some actions can be performed on a range of contiguous items. For example:
* staging multiple files at once
* squashing multiple commits at once
* copying (for cherry-pick) multiple commits at once
There are two ways to select a range of items:
1. Sticky range select: Press 'v' to toggle range select, then expand the selection using the up/down arrow key. To reset the selection, press 'v' again.
2. Non-sticky range select: Press shift+up or shift+down to expand the selection. To reset the selection, press up/down without shift.
The sticky option will be more familiar to vim users, and the second option will feel more natural to users who aren't used to doing things in a modal way.
In order to perform an action on a range of items, simply press the normal key for that action. If the action only works on individual items, it will raise an error. This is a new feature and the plan is to incrementally support range select for more and more actions. If there is an action you would like to support range select which currently does not, please raise an issue in the repo.

View File

@@ -7,12 +7,12 @@ refactorings, one for backend changes, and one for frontend changes. Those
branches would then all be stacked onto each other.
Git has support for rebasing such a stack as a whole; you can enable it by
setting the git config `rebase.updateRfs` to true. If you then rebase the
setting the git config `rebase.updateRefs` to true. If you then rebase the
topmost branch of the stack, the other ones in the stack will follow. This
includes interactive rebases, so for example amending a commit in the first
branch of the stack will "just work" in the sense that it keeps the other
branches properly stacked onto it.
Lazygit visualizes the invidual branch heads in the stack by marking them with a
Lazygit visualizes the individual branch heads in the stack by marking them with a
cyan asterisk (or a cyan branch symbol if you are using [nerd
fonts](Config.md#display-nerd-fonts-icons)).

View File

@@ -1,9 +1,9 @@
# Undo/Redo in lazygit
![Gif](../../assets/undo2.gif)
You can undo the last action by pressing 'z' and redo with `ctrl+z`. Here we drop a couple of commits and then undo the actions.
Undo uses the reflog which is specific to commits and branches so we can't undo changes to the working tree or stash.
## Keybindings:
'z' to undo, 'ctrl+z' to redo
![undo](../../assets/demo/undo-compressed.gif)
## How it works

View File

@@ -57,7 +57,7 @@ go utils.Safe(f)
Where `utils.Safe` is a helper function that ensures we clean up the gui if the goroutine panics.
### Programmatically enqueing a UI event
### Programmatically enqueueing a UI event
This is invoked with `self.c.OnUIThread(f)`. Internally, it creates a task before enqueuing the function as an event (including the task in the event struct) and once that event is processed by the event queue (and any other pending events are processed) the task is removed from the map by calling `task.Done()`.

100
docs/dev/Codebase_Guide.md Normal file
View File

@@ -0,0 +1,100 @@
# Lazygit Codebase Guide
## Packages
* `pkg/app`: Contains startup code, initialises a bunch of stuff like logging, the user config, etc, before starting the gui. Catches and handles some errors that the gui raises.
* `pkg/app/daemon`: Contains code relating to the lazygit daemon. This could be better named: it's is not a daemon in the sense that it's a long-running background process; rather it's a short-lived background process that we pass to git for certain tasks, like GIT_EDITOR for when we want to set the TODO file for an interactive rebase.
* `pkg/cheatsheet`: Generates the keybinding cheatsheets in `docs/keybindings`.
* `pkg/commands/git_commands`: All communication to the git binary happens here. So for example there's a `Checkout` method which calls `git checkout`.
* `pkg/commands/oscommands`: Contains code for talking to the OS, and for invoking commands in general
* `pkg/commands/git_config`: Reading of the git config all happens here.
* `pkg/commands/hosting_service`: Contains code that is specific to git hosting services (aka forges).
* `pkg/commands/models`: Contains model structs that represent commits, branches, files, etc.
* `pkg/commands/patch`: Contains code for parsing and working with git patches
* `pkg/common`: Contains the `Common` struct which holds common dependencies like the logger, i18n, and the user config. Most structs in the code will have a field named `c` which holds a common struct (or a derivative of the common struct).
* `pkg/config`: Contains code relating to the Lazygit user config. Specifically `pkg/config/user_config/go` defines the user config struct and its default values. See [below](#using-userconfig) for some important information about using it.
* `pkg/constants`: Contains some constant strings (e.g. links to docs)
* `pkg/env`: Contains code relating to setting/getting environment variables
* `pkg/i18n`: Contains internationalised strings
* `pkg/integration`: Contains end-to-end tests
* `pkg/jsonschema`: Contains generator for user config JSON schema.
* `pkg/logs`: Contains code for instantiating the logger and for tailing the logs via `lazygit --logs`
* `pkg/tasks`: Contains code for running asynchronous tasks: mostly related to efficiently rendering command output to the main window.
* `pkg/theme`: Contains code related to colour themes.
* `pkg/updates`: Contains code related to Lazygit updates (checking for update, download and installing the update)
* `pkg/utils`: Contains lots of low-level helper functions
* `pkg/gui`: Contains code related to the gui. We've still got a God Struct in the form of our Gui struct, but over time code has been moved out into contexts, controllers, and helpers, and we intend to continue moving more code out over time.
* `pkg/gui/context`: Contains code relating to contexts. There is a context for each view e.g. a branches context, a tags context, etc. Contexts manage state related to the view and receive keypresses.
* `pkg/gui/controllers`: Contains code relating to controllers. Controllers define a list of keybindings and their associated handlers. One controller can be assigned to multiple contexts, and one context can contain multiple controllers.
* `pkg/gui/controllers/helpers`: Contains code that is shared between multiple controllers.
* `pkg/gui/filetree`: Contains code relating to the representation of filetrees.
* `pkg/gui/keybindings`: Contains code for mapping between keybindings and their labels
* `pkg/gui/mergeconflicts`: Contains code relating to the handling of merge conflicts
* `pkg/gui/modes`: Contains code relating to the state of different modes e.g. cherry picking mode, rebase mode.
* `pkg/gui/patch_exploring`: Contains code relating to the state of patch-oriented views like the staging view.
* `pkg/gui/popup`: Contains code that lets you easily raise popups
* `pkg/gui/presentation`: Contains presentation code i.e. code concerned with rendering content inside views
* `pkg/gui/services/custom_commands`: Contains code related to user-defined custom commands.
* `pkg/gui/status`: Contains code for invoking loaders and toasts
* `pkg/gui/style`: Contains code for specifying text styles (colour, bold, etc)
* `pkg/gui/types`: Contains various gui-specific types and interfaces. Lots of code lives here to avoid circular dependencies
* `vendor/github.com/jesseduffield/gocui`: Gocui is the underlying library used for handling the gui event loop, handling keypresses, and rendering the UI. It defines the View struct which our own context structs build upon.
## Important files
* `pkg/config/user_config.go`: defines the user config and default values
* `pkg/gui/keybindings.go`: defines keybindings which have not yet been moved into a controller (originally all keybindings were defined here)
* `pkg/gui/controllers.go`: links up controllers with contexts
* `pkg/gui/controllers/helpers/helpers.go`: defines all the different helper structs
* `pkg/commands/git.go`: defines all the different git command structs
* `pkg/gui/gui.go`: defines the top-level gui state and gui initialisation/run code
* `pkg/gui/layout.go`: defines what happens on each render
* `pkg/gui/controllers/helpers/window_arrangement_helper.go`: defines the layout of the UI and the size/position of each window
* `pkg/gui/context/context.go`: defines the different contexts
* `pkg/gui/context/setup.go`: defines initialisation code for all contexts
* `pkg/gui/context.go`: manages the lifecycle of contexts, the context stack, and focus changes.
* `pkg/gui/types/views.go`: defines views
* `pkg/gui/views.go`: defines the ordering of views (front to back) and their initialisation code
* `pkg/gui/gui_common.go`: defines gui-specific methods that all controllers and helpers have access to
* `pkg/i18n/english.go`: defines the set of i18n strings and their English values
* `pkg/gui/controllers/helpers/refresh_helper.go`: manages refreshing of models. The refresh helper is typically invoked at the end of an action to re-load affected models from git (e.g. re-load branches after doing a git pull)
* `pkg/gui/controllers/quit_actions.go`: contains code that runs when you hit 'escape' on a view (assuming the view doesn't define its own escape handler)
* `vendor/github.com/jesseduffield/gocui/gui.go`: defines the gocui gui struct
* `vendor/github.com/jesseduffield/gocui/view.go`: defines the gocui view struct
## Concepts
* **View**: Views are defined in the gocui package, and they maintain an internal buffer of content which is rendered each time the screen is drawn.
* **Context**: A context is tied to a view and contains some additional state and logic specific to that view e.g. the branches context has code relating specifically to branches, and writes the list of branches to the branches view. Views and contexts share some responsibilities for historical reasons.
* **Controller**: A controller defined keybindings with associated handlers. One controller can be assigned to multiple contexts and one context can have multiple controllers. For example the list controller handles keybindings relating to navigating a list, and is assigned to all list contexts (e.g. the branches context).
* **Helper**: A helper defines shared code used by controllers, or used by some other parts of the application. Often a controller will have a method that ends up needing to be used by another controller, so in that case we move the method out into a helper so that both controllers can use it. We need to do this because controllers cannot refer to other controllers' methods.
In terms of dependencies, controllers sit at the highest level, so they can refer to helpers, contexts, and views (although it's preferable for view-specific code to live in contexts). Helpers can refer to contexts and views, and contexts can only refer to views. Views can't refer to contexts, controllers, or helpers.
* **Window**: A window is a section of the screen which will render a view. Windows are named after the default view that appears there, so for example there is a 'stash' window that is so named because by default the stash view appears there. But if you press enter on a stash entry, the stash entry's files will be shown in a different view, but in the same window.
* **Panel**: The term 'panel' is still used in a few places to refer to either a view or a window, and it's a term that is now deprecated in favour of 'view' and 'window'.
* **Tab**: Each tab in a window (e.g. Files, Worktrees, Submodules) actually has a corresponding view which we bring to the front upon changing tabs.
* **Model**: Representation of a git object e.g. commits, branches, files.
* **ViewModel**: Used by a context to maintain state related to the view.
* **Keybinding**: A keybinding associates a _key_ with an _action_. For example if you press the 'down' arrow, the action performed will be your cursor moving down a list by one.
* **Action**: An action is the thing that happens when you press a key. Often an action will invoke a git command, but not always: for example, navigation actions don't involve git.
* **Common structs**: Most structs have a field named `c` which contains a 'common' struct: a struct containing a bag of dependencies that most structs of the same layer require. For example if you want to access a helper from a controller you can do so with `self.c.Helpers.MyHelper`.
## Event loop and threads
The event loop is managed in the `MainLoop` function of `vendor/github.com/jesseduffield/gocui/gui.go`. Any time there is an event like a key press or a window resize, the event will be processed and then the screen will be redrawn. This involves calling the `layout` function defined in `pkg/gui/layout.go`, which lays out the windows and invokes some on-render hooks.
Often, as part of handling a keypress, we'll want to run some code asynchronously so that it doesn't block the UI thread. For this we'll typically run `self.c.OnWorker(myFunc)`. If the worker wants to then do something on the UI thread again it can call `self.c.OnUIThread(myOtherFunc)`.
## 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.
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).
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.

View File

@@ -0,0 +1,229 @@
# About the mechanics of lazygit's "Find base commit for fixup" command
## Background
Lazygit has a command called "Find base commit for fixup" that helps with
creating fixup commits. (It is bound to "ctrl-f" by default, and I'll call it
simply "the ctrl-f command" throughout the rest of this text for brevity.)
It's a heuristic that needs to make a few assumptions; it tends to work well in
practice if users are aware of its limitations. The user-facing side of the
topic is explained [here](../Fixup_Commits.md). In this document we describe how
it works internally, and the design decisions behind it.
It is also interesting to compare it to the standalone tool
[git-absorb](https://github.com/tummychow/git-absorb) which does a very similar
thing, but made different decisions in some cases. We'll explore these
differences in this document.
## Design goals
I'll start with git-absorb's design goals (my interpretation, since I can't
speak for git-absorb's maintainer of course): its main goal seems to be minimum
user interaction required. The idea is that you have a PR in review, the
reviewer requested a bunch of changes, you make all these changes, so you have a
working copy with lots of modified files, and then you fire up git-absorb and it
creates all the necessary fixup commits automatically with no further user
intervention.
While this sounds attractive, it conflicts with ctrl-f's main design goal, which
is to support creating high-quality fixups. My philosophy is that fixup commits
should have the same high quality standards as normal commits; in particular:
- they should be atomic. This means that multiple diff hunks that belong
together to form one logical change should be in the same fixup commit. (Not
always possible if the logical change needs to be fixed up into several
different base commits.)
- they should be minimal. Every fixup commit should ideally contain only one
logical change, not several unrelated ones.
Why is this important? Because fixup commits are mainly a tool for reviewing (if
they weren't, you might as well squash the changes into their base commits right
away). And reviewing fixup commits is easier if they are well-structured, just
like normal commits.
The only way to achieve this with git-absorb is to set the `oneFixupPerCommit`
config option (for the first goal), and then manually stage the changes that
belong together (for the second). This is close to what you have to do with
ctrl-f, with one exception that we'll get to below.
But ctrl-f enforces this by refusing to do the job if the staged hunks belong to
more than one base commit. Git-absorb will happily create multiple fixup commits
in this case; ctrl-f doesn't, to enforce that you pay attention to how you group
the changes. There's another reason for this behavior: ctrl-f doesn't create
fixup commits itself (unlike git-absorb), instead it just selects the found base
commit so that the user can decide whether to amend the changes right in, or
create a fixup commit from there (both are single-key commands in lazygit). And
lazygit doesn't support non-contiguous multiselections of commits, but even if
it did, it wouldn't help much in this case.
## The mechanics
### General approach
Git-absorb uses a relatively simple approach, and the benefit is of course that
it is easy to understand: it looks at every diff hunk separately, and for every
hunk it looks at all commits (starting from the newest one backwards) to find
the earliest commit that the change can be amended to without conflicts.
It is important to realize that "diff hunk" doesn't necessarily mean what you
see in the diff view. Git-absorb and ctrl-f both use a context of 0 when diffing
your code, so they often see more and smaller hunks than users do. For example,
moving a line of code down by one line is a single hunk for users, but it's two
separate hunks for git-absorb and ctrl-f; one for deleting the line at the old
place, and another one for adding the line at the new place, even if it's only
one line further down.
From this, it follows that there's one big problem with git-absorb's approach:
when moving code, it doesn't realize that the two related hunks of deleting the
code from the old place and inserting it at the new place belong together, and
often it will manage to create a fixup commit for the first hunk, but leave the
other hunk in your working copy as "don't know what to do with this". As an
example, suppose your PR is adding a line of code to an existing function, maybe
one that declares a new variable, and a reviewer suggests to move this line down
a bit, closer to where some other related variables are declared. Moving the
line down results in two diff hunks (from the perspective of git-absorb and
ctrl-f, as they both use a context of 0 when diffing), and when looking at the
second diff hunk in isolation there's no way to find a base commit in your PR
for it, because the surrounding code is already on main.
To solve this, the ctrl-f command makes a distinction between hunks that have
deleted lines and hunks that have only added lines. If the whole diff contains
any hunks that have deleted lines, it uses only those hunks to determine the
base commit, and then assumes that all the hunks that have only added lines
belong into the same commit. This nicely solves the above example of moving
code, but also other examples such as the following:
<details>
<summary>Click to show example</summary>
Suppose you have a PR in which you added the following function:
```go
func findCommit(hash string) (*models.Commit, int, bool) {
for i, commit := range self.c.Model().Commits {
if commit.Hash == hash {
return commit, i, true
}
}
return nil, -1, false
}
```
A reviewer suggests to replace the manual `for` loop with a call to
`lo.FindIndexOf` since that's less code and more idiomatic. So your modification
is this:
```diff
--- a/my_file.go
+++ b/my_file.go
@@ -12,2 +12,3 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
+ "github.com/samber/lo"
"golang.org/x/sync/errgroup"
@@ -308,9 +309,5 @@ func (self *FixupHelper) blameAddedLines(addedLineHunks []*hunk) ([]string, error
func findCommit(hash string) (*models.Commit, int, bool) {
- for i, commit := range self.c.Model().Commits {
- if commit.Hash == hash {
- return commit, i, true
- }
- }
-
- return nil, -1, false
+ return lo.FindIndexOf(self.c.Model().Commits, func(commit *models.Commit) bool {
+ return commit.Hash == hash
+ })
}
```
If we were to look at these two hunks separately, we'd easily find the base
commit for the second one, but we wouldn't find the one for the first hunk
because the imports around the added import have been on main for a long time.
In fact, git-absorb leaves this hunk in the working copy because it doesn't know
what to do with it.
</details>
Only if there are no hunks with deleted lines does ctrl-f look at the hunks with
only added lines and determines the base commit for them. This solves cases like
adding a comment above a function that you added in your PR.
The downside of this more complicated approach is that it relies on the user
staging related hunks correctly. However, in my experience this is easy to do
and not very error-prone, as long as users are aware of this behavior. Lazygit
tries to help making them aware of it by showing a warning whenever there are
hunks with only added lines in addition to hunks with deleted lines.
### Finding the base commit for a given hunk
As explained above, git-absorb finds the base commit by walking the commits
backwards until it finds one that conflicts with the hunk, and then the found
base commit is the one just before that one. This works reliably, but it is
slow.
Ctrl-f uses a different approach that is usually much faster, but should always
yield the same result. Again, it makes a distinction between hunks with deleted
lines and hunks with only added lines. For hunks with deleted lines it performs
a line range blame for all the deleted lines (e.g. `git blame -L42,+3 --
filename`), and if the result is the same for all deleted lines, then that's the
base commit; otherwise it returns an error.
For hunks with only added lines, it gets a little more complicated. We blame the
single lines just before and just after the hunk (I'll ignore the edge cases of
either of those not existing because the hunk is at the beginning or end of the
file; read the code to see how we handle these cases). If the blame result is
the same for both, then that's the base commit. This is the case of adding a
line in the middle of a block of code that was added in the PR. Otherwise, the
base commit is the more recent of the two (and in this case it doesn't matter if
the other one is an earlier commit in the current branch, or a possibly very old
commit that's already on main). This covers the common case of adding a comment
to a function that was added in the PR, but also adding another line at the end
of a block of code that was added in the base commit.
It's interesting to discuss what "more recent" means here. You could say if
commit A is an ancestor of commit B (or in other words, A is reachable from B)
then B is the more recent one. And if none of the two commits is reachable from
the other, you have an error case because it's unclear which of the two should
be considered the base commit. The scenario in which this happens is a commit
history like this:
```
C---D
/ \
A---B---E---F---G
```
where, for instance, D and E are the two blame results.
Unfortunately, determining the ancestry relationship between two commits using
git commands is a bit expensive and not totally straightforward. Fortunately,
it's not necessary in lazygit because lazygit has the most recent 300 commits
cached in memory, and can simply search its linear list of commits to see which
one is closer to the beginning of the list. If only one of the two commits is
found within those 300 commits, then that's the more recent one; if neither is
found, we assume that both commits are on main and error out. In the merge
scenario pictured above, we arbitrarily return one of the two commits (this will
depend on the log order), but that's probably fine as this scenario should be
extremely rare in practice; in most cases, feature branches are simply linear.
### Knowing where to stop searching
Git-absorb needs to know when to stop walking backwards searching for commits,
since it doesn't make sense to create fixups for commits that are already on
main. However, it doesn't know where the current branch ends and main starts, so
it needs to rely on user input for this. By default it searches the most recent
10 commits, but this can be overridden with a config setting. In longer branches
this is often not enough for finding the base commit; but setting it to a higher
value causes the command to take longer to complete when the base commit can't
be found.
Lazygit doesn't have this problem. For a given blame result it needs to
determine whether that commit is already on main, and if it can find the commit
in its cached list of the first 300 commits it can get that information from
there, because lazygit knows what the user's configured main branches are
(`master` and `main` by default, but it could also include branches like `devel`
or `1.0-hotfixes`), and so it can tell for each commit whether it's contained in
one of those main branches. And if it can't find it among the first 300 commits,
it assumes the commit already on main, on the assumption that no feature branch
has more than 300 commits.

69
docs/dev/Profiling.md Normal file
View File

@@ -0,0 +1,69 @@
# Profiling Lazygit
If you want to investigate what's contributing to CPU or memory usage, start
lazygit with the `-profile` command line flag. This tells it to start an
integrated web server that listens for profiling requests.
## Save profile data
### CPU
While lazygit is running with the `-profile` flag, perform a CPU profile and
save it to a file by running this command in another terminal window:
```sh
curl -o cpu.out http://127.0.0.1:6060/debug/pprof/profile
```
By default, it profiles for 30 seconds. To change the duration, use
```sh
curl -o cpu.out 'http://127.0.0.1:6060/debug/pprof/profile?seconds=60'
```
### Memory
To save a heap profile (containing information about all memory allocated so
far since startup), use
```sh
curl -o mem.out http://127.0.0.1:6060/debug/pprof/heap
```
Sometimes it can be useful to get a delta log, i.e. to see how memory usage
developed from one point in time to another. For that, use
```sh
curl -o mem.out 'http://127.0.0.1:6060/debug/pprof/heap?seconds=20'
```
This will log the memory usage difference between now and 20 seconds later, so
it gives you 20 seconds to perform the action in lazygit that you are interested
in measuring.
## View profile data
To display the profile data, you can either use speedscope.app, or the pprof
tool that comes with go. I prefer the former because it has a nicer UI and is a
little more powerful; however, I have seen cases where it wasn't able to load a
profile for some reason, in which case it's good to have the pprof tool as a
fallback.
### Speedscope.app
Go to https://www.speedscope.app/ in your browser, and drag the saved profile
onto the browser window. Refer to [the
documentation](https://github.com/jlfwong/speedscope?tab=readme-ov-file#usage)
for how to navigate the data.
### Pprof tool
To view a profile that you saved as `cpu.out`, use
```sh
go tool pprof -http=:8080 cpu.out
```
By default this shows the graph view, which I don't find very useful myself.
Choose "Flame Graph" from the View menu to show a much more useful
representation of the data.

View File

@@ -1,5 +1,8 @@
# Dev Documentation Overview
* [Busy/Idle tracking](./Busy.md).
* [Codebase Guide](./Codebase_Guide.md)
* [Busy/Idle Tracking](./Busy.md)
* [Integration Tests](../../pkg/integration/README.md)
* [Demo Recordings](./Demo_Recordings.md)
* [Find base commit for fixup design](Find_Base_Commit_For_Fixup_Design.md)
* [Profiling](Profiling.md)

View File

@@ -1,4 +1,4 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
# Lazygit Keybindings
@@ -6,348 +6,364 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
## Global keybindings
<pre>
<kbd>&lt;c-r&gt;</kbd>: Switch to a recent repo
<kbd>&lt;pgup&gt;</kbd>: Scroll up main panel (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: Scroll down main panel (fn+down/shift+j)
<kbd>@</kbd>: Open command log menu
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
<kbd>:</kbd>: Execute custom command
<kbd>&lt;c-p&gt;</kbd>: View custom patch options
<kbd>m</kbd>: View merge/rebase options
<kbd>R</kbd>: Refresh
<kbd>+</kbd>: Next screen mode (normal/half/fullscreen)
<kbd>_</kbd>: Prev screen mode
<kbd>?</kbd>: Open menu
<kbd>&lt;c-s&gt;</kbd>: View filter-by-path options
<kbd>W</kbd>: Open diff menu
<kbd>&lt;c-e&gt;</kbd>: Open diff menu
<kbd>&lt;c-w&gt;</kbd>: Toggle whether or not whitespace changes are shown in the diff view
<kbd>z</kbd>: Undo
<kbd>&lt;c-z&gt;</kbd>: Redo
<kbd>P</kbd>: Push
<kbd>p</kbd>: Pull
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | Switch to a recent repo | |
| `` <pgup> (fn+up/shift+k) `` | Scroll up main window | |
| `` <pgdown> (fn+down/shift+j) `` | Scroll down main window | |
| `` @ `` | View command log options | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | Push | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | Pull | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | Increase diff context size | Increase the amount of the context shown around changes in the diff view. |
| `` { `` | Decrease diff context size | Decrease the amount of the context shown around changes in the diff view. |
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
| `` <c-p> `` | View custom patch options | |
| `` m `` | View merge/rebase options | View options to abort/continue/skip the current merge/rebase. |
| `` R `` | Refresh | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | Next screen mode (normal/half/fullscreen) | |
| `` _ `` | Prev screen mode | |
| `` ? `` | Open keybindings menu | |
| `` <c-s> `` | View filter options | View options for filtering the commit log, so that only commits matching the filter are shown. |
| `` W `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` <c-e> `` | View diffing options | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` q `` | Quit | |
| `` <esc> `` | Cancel | |
| `` <c-w> `` | Toggle whitespace | Toggle whether or not whitespace changes are shown in the diff view. |
| `` z `` | Undo | The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
| `` <c-z> `` | Redo | The reflog will be used to determine what git command to run to redo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
## List panel navigation
<pre>
<kbd>,</kbd>: Previous page
<kbd>.</kbd>: Next page
<kbd>&lt;</kbd>: Scroll to top
<kbd>&gt;</kbd>: Scroll to bottom
<kbd>/</kbd>: Search the current view by text
<kbd>H</kbd>: Scroll left
<kbd>L</kbd>: Scroll right
<kbd>]</kbd>: Next tab
<kbd>[</kbd>: Previous tab
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | Previous page | |
| `` . `` | Next page | |
| `` < `` | Scroll to top | |
| `` > `` | Scroll to bottom | |
| `` v `` | Toggle range select | |
| `` <s-down> `` | Range select down | |
| `` <s-up> `` | Range select up | |
| `` / `` | Search the current view by text | |
| `` H `` | Scroll left | |
| `` L `` | Scroll right | |
| `` ] `` | Next tab | |
| `` [ `` | Previous tab | |
## Commit files
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy the committed file name to the clipboard
<kbd>c</kbd>: Checkout file
<kbd>d</kbd>: Discard this commit's changes to this file
<kbd>o</kbd>: Open file
<kbd>e</kbd>: Edit file
<kbd>&lt;space&gt;</kbd>: Toggle file included in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>`</kbd>: Toggle file tree view
<kbd>/</kbd>: Search the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy path to clipboard | |
| `` c `` | Checkout | Checkout file. This replaces the file in your working tree with the version from the selected commit. |
| `` d `` | Remove | Discard this commit's changes to this file. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes this file. |
| `` o `` | Open file | Open file in default application. |
| `` e `` | Edit | Open file in external editor. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <space> `` | Toggle file included in patch | Toggle whether the file is included in the custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` a `` | Toggle all files | Add/remove all commit's files to custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` <enter> `` | Enter file / Toggle directory collapsed | If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory. |
| `` ` `` | Toggle file tree view | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` / `` | Search the current view by text | |
## Commit summary
<pre>
<kbd>&lt;enter&gt;</kbd>: Confirm
<kbd>&lt;esc&gt;</kbd>: Close
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Confirm | |
| `` <esc> `` | Close | |
## Commits
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>b</kbd>: View bisect options
<kbd>s</kbd>: Squash down
<kbd>f</kbd>: Fixup commit
<kbd>r</kbd>: Reword commit
<kbd>R</kbd>: Reword commit with editor
<kbd>d</kbd>: Delete commit
<kbd>e</kbd>: Edit commit
<kbd>p</kbd>: Pick commit (when mid-rebase)
<kbd>F</kbd>: Create fixup commit for this commit
<kbd>S</kbd>: Squash all 'fixup!' commits above selected commit (autosquash)
<kbd>&lt;c-j&gt;</kbd>: Move commit down one
<kbd>&lt;c-k&gt;</kbd>: Move commit up one
<kbd>v</kbd>: Paste commits (cherry-pick)
<kbd>B</kbd>: Mark commit as base commit for rebase
<kbd>A</kbd>: Amend commit with staged changes
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: Revert commit
<kbd>T</kbd>: Tag commit
<kbd>&lt;c-l&gt;</kbd>: Open log menu
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Create new branch off of commit
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: Copy commit (cherry-pick)
<kbd>C</kbd>: Copy commit range (cherry-pick)
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: Search the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy commit hash to clipboard | |
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
| `` b `` | View bisect options | |
| `` s `` | Squash | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
| `` f `` | Fixup | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
| `` r `` | Reword | Reword the selected commit's message. |
| `` R `` | Reword with editor | |
| `` d `` | Drop | Drop the selected commit. This will remove the commit from the branch via a rebase. If the commit makes changes that later commits depend on, you may need to resolve merge conflicts. |
| `` e `` | Edit (start interactive rebase) | Edit the selected commit. Use this to start an interactive rebase from the selected commit. When already mid-rebase, this will mark the selected commit for editing, which means that upon continuing the rebase, the rebase will pause at the selected commit to allow you to make changes. |
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Mark the selected commit to be picked (when mid-rebase). This means that the commit will be retained upon continuing the rebase. |
| `` F `` | Create fixup commit | Create 'fixup!' commit for the selected commit. Later on, you can press `S` on this same commit to apply all above fixup commits. |
| `` S `` | Apply fixup commits | Squash all 'fixup!' commits, either above the selected commit, or all in current branch (autosquash). |
| `` <c-j> `` | Move commit down one | |
| `` <c-k> `` | Move commit up one | |
| `` V `` | Paste (cherry-pick) | |
| `` B `` | Mark as base commit for rebase | Select a base commit for the next rebase. When you rebase onto a branch, only commits above the base commit will be brought across. This uses the `git rebase --onto` command. |
| `` A `` | Amend | Amend commit with staged changes. If the selected commit is the HEAD commit, this will perform `git commit --amend`. Otherwise the commit will be amended via a rebase. |
| `` a `` | Amend commit attribute | Set/Reset commit author or set co-author. |
| `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
| `` T `` | Tag commit | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
| `` <c-l> `` | View log options | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
| `` <space> `` | Checkout | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Create new branch off of commit | |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Copy (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | View files | |
| `` w `` | View worktree options | |
| `` / `` | Search the current view by text | |
## Confirmation panel
<pre>
<kbd>&lt;enter&gt;</kbd>: Confirm
<kbd>&lt;esc&gt;</kbd>: Close/Cancel
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Confirm | |
| `` <esc> `` | Close/Cancel | |
## Files
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy the file name to the clipboard
<kbd>d</kbd>: View 'discard changes' options
<kbd>&lt;space&gt;</kbd>: Toggle staged
<kbd>&lt;c-b&gt;</kbd>: Filter files by status
<kbd>c</kbd>: Commit changes
<kbd>w</kbd>: Commit changes without pre-commit hook
<kbd>A</kbd>: Amend last commit
<kbd>C</kbd>: Commit changes using git editor
<kbd>e</kbd>: Edit file
<kbd>o</kbd>: Open file
<kbd>i</kbd>: Ignore or exclude file
<kbd>r</kbd>: Refresh files
<kbd>s</kbd>: Stash all changes
<kbd>S</kbd>: View stash options
<kbd>a</kbd>: Stage/unstage all
<kbd>&lt;enter&gt;</kbd>: Stage individual hunks/lines for file, or collapse/expand for directory
<kbd>g</kbd>: View upstream reset options
<kbd>D</kbd>: View reset options
<kbd>`</kbd>: Toggle file tree view
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>f</kbd>: Fetch
<kbd>/</kbd>: Search the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy path to clipboard | |
| `` <space> `` | Stage | Toggle staged for selected file. |
| `` <c-b> `` | Filter files by status | |
| `` y `` | Copy to clipboard | |
| `` c `` | Commit | Commit staged changes. |
| `` w `` | Commit changes without pre-commit hook | |
| `` A `` | Amend last commit | |
| `` C `` | Commit changes using git editor | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | Edit | Open file in external editor. |
| `` o `` | Open file | Open file in default application. |
| `` i `` | Ignore or exclude file | |
| `` r `` | Refresh files | |
| `` s `` | Stash | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
| `` S `` | View stash options | View stash options (e.g. stash all, stash staged, stash unstaged). |
| `` a `` | Stage all | Toggle staged/unstaged for all files in working tree. |
| `` <enter> `` | Stage lines / Collapse directory | If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it. |
| `` d `` | Discard | View options for discarding changes to the selected file. |
| `` g `` | View upstream reset options | |
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
| `` ` `` | Toggle file tree view | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` M `` | Open external merge tool | Run `git mergetool`. |
| `` f `` | Fetch | Fetch changes from remote. |
| `` / `` | Search the current view by text | |
## Local branches
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy branch name to clipboard
<kbd>i</kbd>: Show git-flow options
<kbd>&lt;space&gt;</kbd>: Checkout
<kbd>n</kbd>: New branch
<kbd>o</kbd>: Create pull request
<kbd>O</kbd>: Create pull request options
<kbd>&lt;c-y&gt;</kbd>: Copy pull request URL to clipboard
<kbd>c</kbd>: Checkout by name
<kbd>F</kbd>: Force checkout
<kbd>d</kbd>: Delete branch
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>M</kbd>: Merge into currently checked out branch
<kbd>f</kbd>: Fast-forward this branch from its upstream
<kbd>T</kbd>: Create tag
<kbd>g</kbd>: View reset options
<kbd>R</kbd>: Rename branch
<kbd>u</kbd>: Set/Unset upstream
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy branch name to clipboard | |
| `` i `` | Show git-flow options | |
| `` <space> `` | Checkout | Checkout selected item. |
| `` n `` | New branch | |
| `` o `` | Create pull request | |
| `` O `` | View create pull request options | |
| `` <c-y> `` | Copy pull request URL to clipboard | |
| `` c `` | Checkout by name | Checkout by name. In the input box you can enter '-' to switch to the last branch. |
| `` F `` | Force checkout | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | Rebase | Rebase the checked-out branch onto the selected branch. |
| `` M `` | Merge | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` f `` | Fast-forward | Fast-forward selected branch from its upstream. |
| `` T `` | New tag | |
| `` s `` | Sort order | |
| `` g `` | Reset | |
| `` R `` | Rename branch | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Main panel (merging)
<pre>
<kbd>e</kbd>: Edit file
<kbd>o</kbd>: Open file
<kbd>&lt;left&gt;</kbd>: Select previous conflict
<kbd>&lt;right&gt;</kbd>: Select next conflict
<kbd>&lt;up&gt;</kbd>: Select previous hunk
<kbd>&lt;down&gt;</kbd>: Select next hunk
<kbd>z</kbd>: Undo
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>&lt;space&gt;</kbd>: Pick hunk
<kbd>b</kbd>: Pick all hunks
<kbd>&lt;esc&gt;</kbd>: Return to files panel
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Pick hunk | |
| `` b `` | Pick all hunks | |
| `` <up> `` | Previous hunk | |
| `` <down> `` | Next hunk | |
| `` <left> `` | Previous conflict | |
| `` <right> `` | Next conflict | |
| `` z `` | Undo | Undo last merge conflict resolution. |
| `` e `` | Edit file | Open file in external editor. |
| `` o `` | Open file | Open file in default application. |
| `` M `` | Open external merge tool | Run `git mergetool`. |
| `` <esc> `` | Return to files panel | |
## Main panel (normal)
<pre>
<kbd>mouse wheel down</kbd>: Scroll down (fn+up)
<kbd>mouse wheel up</kbd>: Scroll up (fn+down)
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | Scroll down | |
| `` mouse wheel up (fn+down) `` | Scroll up | |
## Main panel (patch building)
<pre>
<kbd>&lt;left&gt;</kbd>: Select previous hunk
<kbd>&lt;right&gt;</kbd>: Select next hunk
<kbd>v</kbd>: Toggle drag select
<kbd>V</kbd>: Toggle drag select
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Open file
<kbd>e</kbd>: Edit file
<kbd>&lt;space&gt;</kbd>: Add/Remove line(s) to patch
<kbd>&lt;esc&gt;</kbd>: Exit custom patch builder
<kbd>/</kbd>: Search the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Go to previous hunk | |
| `` <right> `` | Go to next hunk | |
| `` v `` | Toggle range select | |
| `` a `` | Select hunk | Toggle hunk selection mode. |
| `` <c-o> `` | Copy selected text to clipboard | |
| `` o `` | Open file | Open file in default application. |
| `` e `` | Edit file | Open file in external editor. |
| `` <space> `` | Toggle lines in patch | |
| `` <esc> `` | Exit custom patch builder | |
| `` / `` | Search the current view by text | |
## Main panel (staging)
<pre>
<kbd>&lt;left&gt;</kbd>: Select previous hunk
<kbd>&lt;right&gt;</kbd>: Select next hunk
<kbd>v</kbd>: Toggle drag select
<kbd>V</kbd>: Toggle drag select
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Open file
<kbd>e</kbd>: Edit file
<kbd>&lt;esc&gt;</kbd>: Return to files panel
<kbd>&lt;tab&gt;</kbd>: Switch to other panel (staged/unstaged changes)
<kbd>&lt;space&gt;</kbd>: Toggle line staged / unstaged
<kbd>d</kbd>: Discard change (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: Commit changes
<kbd>w</kbd>: Commit changes without pre-commit hook
<kbd>C</kbd>: Commit changes using git editor
<kbd>/</kbd>: Search the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Go to previous hunk | |
| `` <right> `` | Go to next hunk | |
| `` v `` | Toggle range select | |
| `` a `` | Select hunk | Toggle hunk selection mode. |
| `` <c-o> `` | Copy selected text to clipboard | |
| `` <space> `` | Stage | Toggle selection staged / unstaged. |
| `` d `` | Discard | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
| `` o `` | Open file | Open file in default application. |
| `` e `` | Edit file | Open file in external editor. |
| `` <esc> `` | Return to files panel | |
| `` <tab> `` | Switch view | Switch to other view (staged/unstaged changes). |
| `` E `` | Edit hunk | Edit selected hunk in external editor. |
| `` c `` | Commit | Commit staged changes. |
| `` w `` | Commit changes without pre-commit hook | |
| `` C `` | Commit changes using git editor | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | Search the current view by text | |
## Menu
<pre>
<kbd>&lt;enter&gt;</kbd>: Execute
<kbd>&lt;esc&gt;</kbd>: Close
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Execute | |
| `` <esc> `` | Close | |
| `` / `` | Filter the current view by text | |
## Reflog
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Create new branch off of commit
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: Copy commit (cherry-pick)
<kbd>C</kbd>: Copy commit range (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy commit hash to clipboard | |
| `` <space> `` | Checkout | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Create new branch off of commit | |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Copy (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Remote branches
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy branch name to clipboard
<kbd>&lt;space&gt;</kbd>: Checkout
<kbd>n</kbd>: New branch
<kbd>M</kbd>: Merge into currently checked out branch
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>d</kbd>: Delete branch
<kbd>u</kbd>: Set as upstream of checked-out branch
<kbd>g</kbd>: View reset options
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy branch name to clipboard | |
| `` <space> `` | Checkout | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` n `` | New branch | |
| `` M `` | Merge | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` r `` | Rebase | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | Set the selected remote branch as the upstream of the checked-out branch. |
| `` s `` | Sort order | |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Remotes
<pre>
<kbd>f</kbd>: Fetch remote
<kbd>n</kbd>: Add new remote
<kbd>d</kbd>: Remove remote
<kbd>e</kbd>: Edit remote
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | View branches | |
| `` n `` | New remote | |
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
| `` e `` | Edit | Edit the selected remote's name or URL. |
| `` f `` | Fetch | Fetch updates from the remote repository. This retrieves new commits and branches without merging them into your local branches. |
| `` / `` | Filter the current view by text | |
## Stash
<pre>
<kbd>&lt;space&gt;</kbd>: Apply
<kbd>g</kbd>: Pop
<kbd>d</kbd>: Drop
<kbd>n</kbd>: New branch
<kbd>r</kbd>: Rename stash
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Apply | Apply the stash entry to your working directory. |
| `` g `` | Pop | Apply the stash entry to your working directory and remove the stash entry. |
| `` d `` | Drop | Remove the stash entry from the stash list. |
| `` n `` | New branch | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
| `` r `` | Rename stash | |
| `` <enter> `` | View files | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Status
<pre>
<kbd>o</kbd>: Open config file
<kbd>e</kbd>: Edit config file
<kbd>u</kbd>: Check for update
<kbd>&lt;enter&gt;</kbd>: Switch to a recent repo
<kbd>a</kbd>: Show all branch logs
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | Open config file | Open file in default application. |
| `` e `` | Edit config file | Open file in external editor. |
| `` u `` | Check for update | |
| `` <enter> `` | Switch to a recent repo | |
| `` a `` | Show/cycle all branch logs | |
## Sub-commits
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Create new branch off of commit
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: Copy commit (cherry-pick)
<kbd>C</kbd>: Copy commit range (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: Search the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy commit hash to clipboard | |
| `` <space> `` | Checkout | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Create new branch off of commit | |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Copy (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | View files | |
| `` w `` | View worktree options | |
| `` / `` | Search the current view by text | |
## Submodules
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy submodule name to clipboard
<kbd>&lt;enter&gt;</kbd>: Enter submodule
<kbd>&lt;space&gt;</kbd>: Enter submodule
<kbd>d</kbd>: Remove submodule
<kbd>u</kbd>: Update submodule
<kbd>n</kbd>: Add new submodule
<kbd>e</kbd>: Update submodule URL
<kbd>i</kbd>: Initialize submodule
<kbd>b</kbd>: View bulk submodule options
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Copy submodule name to clipboard | |
| `` <enter> `` | Enter | Enter submodule. After entering the submodule, you can press `<esc>` to escape back to the parent repo. |
| `` d `` | Remove | Remove the selected submodule and its corresponding directory. |
| `` u `` | Update | Update selected submodule. |
| `` n `` | New submodule | |
| `` e `` | Update submodule URL | |
| `` i `` | Initialize | Initialize the selected submodule to prepare for fetching. You probably want to follow this up by invoking the 'update' action to fetch the submodule. |
| `` b `` | View bulk submodule options | |
| `` / `` | Filter the current view by text | |
## Tags
<pre>
<kbd>&lt;space&gt;</kbd>: Checkout
<kbd>d</kbd>: Delete tag
<kbd>P</kbd>: Push tag
<kbd>n</kbd>: Create tag
<kbd>g</kbd>: View reset options
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Checkout | Checkout the selected tag tag as a detached HEAD. |
| `` n `` | New tag | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | Push tag | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | View commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Worktrees
<pre>
<kbd>n</kbd>: Create worktree
<kbd>&lt;space&gt;</kbd>: Switch to worktree
<kbd>&lt;enter&gt;</kbd>: Switch to worktree
<kbd>o</kbd>: Open in editor
<kbd>d</kbd>: Remove worktree
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | New worktree | |
| `` <space> `` | Switch | Switch to the selected worktree. |
| `` o `` | Open in editor | |
| `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
| `` / `` | Filter the current view by text | |

View File

@@ -1,4 +1,4 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
# Lazygit キーバインド
@@ -6,348 +6,364 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
## グローバルキーバインド
<pre>
<kbd>&lt;c-r&gt;</kbd>: 最近使用したリポジトリに切り替え
<kbd>&lt;pgup&gt;</kbd>: メインパネルを上にスクロール (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: メインパネルをにスクロール (fn+down/shift+j)
<kbd>@</kbd>: コマンドログメニューを開く
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
<kbd>:</kbd>: カスタムコマンドを実行
<kbd>&lt;c-p&gt;</kbd>: View custom patch options
<kbd>m</kbd>: View merge/rebase options
<kbd>R</kbd>: リフレッシュ
<kbd>+</kbd>: 次のスクリーンモード (normal/half/fullscreen)
<kbd>_</kbd>: 前のスクリーンモード
<kbd>?</kbd>: メニューを開く
<kbd>&lt;c-s&gt;</kbd>: View filter-by-path options
<kbd>W</kbd>: 差分メニューを開く
<kbd>&lt;c-e&gt;</kbd>: 差分メニューを開く
<kbd>&lt;c-w&gt;</kbd>: 空白文字の差分の表示有無を切り替え
<kbd>z</kbd>: アンドゥ (via reflog) (experimental)
<kbd>&lt;c-z&gt;</kbd>: リドゥ (via reflog) (experimental)
<kbd>P</kbd>: Push
<kbd>p</kbd>: Pull
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | 最近使用したリポジトリに切り替え | |
| `` <pgup> (fn+up/shift+k) `` | メインパネルをにスクロール | |
| `` <pgdown> (fn+down/shift+j) `` | メインパネルを下にスクロール | |
| `` @ `` | コマンドログメニューを開く | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | Push | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | Pull | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | Increase diff context size | Increase the amount of the context shown around changes in the diff view. |
| `` { `` | Decrease diff context size | Decrease the amount of the context shown around changes in the diff view. |
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
| `` <c-p> `` | View custom patch options | |
| `` m `` | View merge/rebase options | View options to abort/continue/skip the current merge/rebase. |
| `` R `` | リフレッシュ | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | 次のスクリーンモード (normal/half/fullscreen) | |
| `` _ `` | 前のスクリーンモード | |
| `` ? `` | メニューを開く | |
| `` <c-s> `` | View filter options | View options for filtering the commit log, so that only commits matching the filter are shown. |
| `` W `` | 差分メニューを開く | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` <c-e> `` | 差分メニューを開く | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` q `` | 終了 | |
| `` <esc> `` | キャンセル | |
| `` <c-w> `` | 空白文字の差分の表示有無を切り替え | Toggle whether or not whitespace changes are shown in the diff view. |
| `` z `` | アンドゥ (via reflog) (experimental) | The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
| `` <c-z> `` | リドゥ (via reflog) (experimental) | The reflog will be used to determine what git command to run to redo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
## 一覧パネルの操作
<pre>
<kbd>,</kbd>: 前のページ
<kbd>.</kbd>: 次のページ
<kbd>&lt;</kbd>: 最上部までスクロール
<kbd>&gt;</kbd>:部までスクロール
<kbd>/</kbd>: 検索を開始
<kbd>H</kbd>: 左スクロール
<kbd>L</kbd>: 右スクロール
<kbd>]</kbd>: 次のタブ
<kbd>[</kbd>: 前のタブ
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | 前のページ | |
| `` . `` | 次のページ | |
| `` < `` |部までスクロール | |
| `` > `` | 最下部までスクロール | |
| `` v `` | 範囲選択を切り替え | |
| `` <s-down> `` | Range select down | |
| `` <s-up> `` | Range select up | |
| `` / `` | 検索を開始 | |
| `` H `` | 左スクロール | |
| `` L `` | 右スクロール | |
| `` ] `` | 次のタブ | |
| `` [ `` | 前のタブ | |
## Stash
<pre>
<kbd>&lt;space&gt;</kbd>: 適用
<kbd>g</kbd>: Pop
<kbd>d</kbd>: Drop
<kbd>n</kbd>: 新しいブランチを作成
<kbd>r</kbd>: Stashを変更
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 適用 | Apply the stash entry to your working directory. |
| `` g `` | Pop | Apply the stash entry to your working directory and remove the stash entry. |
| `` d `` | Drop | Remove the stash entry from the stash list. |
| `` n `` | 新しいブランチを作成 | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
| `` r `` | Stashを変更 | |
| `` <enter> `` | View files | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Sub-commits
<pre>
<kbd>&lt;c-o&gt;</kbd>: コミットのSHAをクリップボードにコピー
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: コミットをチェックアウト
<kbd>y</kbd>: コミットの情報をコピー
<kbd>o</kbd>: ブラウザでコミットを開く
<kbd>n</kbd>: コミットにブランチを作成
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: コミットをコピー (cherry-pick)
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: 検索を開始
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | コミットのhashをクリップボードにコピー | |
| `` <space> `` | チェックアウト | Checkout the selected commit as a detached HEAD. |
| `` y `` | コミットの情報をコピー | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | ブラウザでコミットを開く | |
| `` n `` | コミットにブランチを作成 | |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | コミットをコピー (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | View files | |
| `` w `` | View worktree options | |
| `` / `` | 検索を開始 | |
## Worktrees
<pre>
<kbd>n</kbd>: Create worktree
<kbd>&lt;space&gt;</kbd>: Switch to worktree
<kbd>&lt;enter&gt;</kbd>: Switch to worktree
<kbd>o</kbd>: Open in editor
<kbd>d</kbd>: Remove worktree
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | New worktree | |
| `` <space> `` | Switch | Switch to the selected worktree. |
| `` o `` | Open in editor | |
| `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
| `` / `` | Filter the current view by text | |
## コミット
<pre>
<kbd>&lt;c-o&gt;</kbd>: コミットのSHAをクリップボードにコピー
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>b</kbd>: View bisect options
<kbd>s</kbd>: Squash down
<kbd>f</kbd>: Fixup commit
<kbd>r</kbd>: コミットメッセージを変更
<kbd>R</kbd>: エディタでコミットメッセージを編集
<kbd>d</kbd>: コミットを削除
<kbd>e</kbd>: コミットを編集
<kbd>p</kbd>: Pick commit (when mid-rebase)
<kbd>F</kbd>: このコミットに対するfixupコミットを作成
<kbd>S</kbd>: Squash all 'fixup!' commits above selected commit (autosquash)
<kbd>&lt;c-j&gt;</kbd>: コミットを1つ下に移動
<kbd>&lt;c-k&gt;</kbd>: コミットを1つ上に移動
<kbd>v</kbd>: コミットを貼り付け (cherry-pick)
<kbd>B</kbd>: Mark commit as base commit for rebase
<kbd>A</kbd>: ステージされた変更でamendコミット
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: コミットをrevert
<kbd>T</kbd>: タグを作成
<kbd>&lt;c-l&gt;</kbd>: ログメニューを開く
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: コミットをチェックアウト
<kbd>y</kbd>: コミットの情報をコピー
<kbd>o</kbd>: ブラウザでコミットを開く
<kbd>n</kbd>: コミットにブランチを作成
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: コミットをコピー (cherry-pick)
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: 検索を開始
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | コミットのhashをクリップボードにコピー | |
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
| `` b `` | View bisect options | |
| `` s `` | Squash | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
| `` f `` | Fixup | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
| `` r `` | コミットメッセージを変更 | Reword the selected commit's message. |
| `` R `` | エディタでコミットメッセージを編集 | |
| `` d `` | コミットを削除 | Drop the selected commit. This will remove the commit from the branch via a rebase. If the commit makes changes that later commits depend on, you may need to resolve merge conflicts. |
| `` e `` | Edit (start interactive rebase) | コミットを編集 |
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Mark the selected commit to be picked (when mid-rebase). This means that the commit will be retained upon continuing the rebase. |
| `` F `` | Fixupコミットを作成 | このコミットに対するfixupコミットを作成 |
| `` S `` | Apply fixup commits | Squash all 'fixup!' commits, either above the selected commit, or all in current branch (autosquash). |
| `` <c-j> `` | コミットを1つ下に移動 | |
| `` <c-k> `` | コミットを1つ上に移動 | |
| `` V `` | コミットを貼り付け (cherry-pick) | |
| `` B `` | Mark as base commit for rebase | Select a base commit for the next rebase. When you rebase onto a branch, only commits above the base commit will be brought across. This uses the `git rebase --onto` command. |
| `` A `` | Amend | ステージされた変更でamendコミット |
| `` a `` | Amend commit attribute | Set/Reset commit author or set co-author. |
| `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
| `` T `` | タグを作成 | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
| `` <c-l> `` | ログメニューを開く | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
| `` <space> `` | チェックアウト | Checkout the selected commit as a detached HEAD. |
| `` y `` | コミットの情報をコピー | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | ブラウザでコミットを開く | |
| `` n `` | コミットにブランチを作成 | |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | コミットをコピー (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | View files | |
| `` w `` | View worktree options | |
| `` / `` | 検索を開始 | |
## コミットファイル
<pre>
<kbd>&lt;c-o&gt;</kbd>: コミットされたファイル名をクリップボードにコピー
<kbd>c</kbd>: Checkout file
<kbd>d</kbd>: Discard this commit's changes to this file
<kbd>o</kbd>: ファイルを開く
<kbd>e</kbd>: ファイルを編集
<kbd>&lt;space&gt;</kbd>: Toggle file included in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>`</kbd>: ファイルツリーの表示を切り替え
<kbd>/</kbd>: 検索を開始
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | ファイル名をクリップボードにコピー | |
| `` c `` | チェックアウト | Checkout file. This replaces the file in your working tree with the version from the selected commit. |
| `` d `` | Remove | Discard this commit's changes to this file. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes this file. |
| `` o `` | ファイルを開く | Open file in default application. |
| `` e `` | Edit | Open file in external editor. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <space> `` | Toggle file included in patch | Toggle whether the file is included in the custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` a `` | Toggle all files | Add/remove all commit's files to custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` <enter> `` | Enter file / Toggle directory collapsed | If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory. |
| `` ` `` | ファイルツリーの表示を切り替え | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` / `` | 検索を開始 | |
## コミットメッセージ
<pre>
<kbd>&lt;enter&gt;</kbd>: 確認
<kbd>&lt;esc&gt;</kbd>: 閉じる
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 確認 | |
| `` <esc> `` | 閉じる | |
## サブモジュール
<pre>
<kbd>&lt;c-o&gt;</kbd>: サブモジュール名をクリップボードにコピー
<kbd>&lt;enter&gt;</kbd>: サブモジュールを開く
<kbd>&lt;space&gt;</kbd>: サブモジュールを開く
<kbd>d</kbd>: サブモジュールを削除
<kbd>u</kbd>: サブモジュールを更新
<kbd>n</kbd>: サブモジュールを新規追加
<kbd>e</kbd>: サブモジュールのURLを更新
<kbd>i</kbd>: サブモジュールを初期化
<kbd>b</kbd>: View bulk submodule options
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | サブモジュール名をクリップボードにコピー | |
| `` <enter> `` | Enter | サブモジュールを開く |
| `` d `` | Remove | Remove the selected submodule and its corresponding directory. |
| `` u `` | Update | サブモジュールを更新 |
| `` n `` | サブモジュールを新規追加 | |
| `` e `` | サブモジュールのURLを更新 | |
| `` i `` | Initialize | サブモジュールを初期化 |
| `` b `` | View bulk submodule options | |
| `` / `` | Filter the current view by text | |
## ステータス
<pre>
<kbd>o</kbd>: 設定ファイルを開く
<kbd>e</kbd>: 設定ファイルを編集
<kbd>u</kbd>: 更新を確認
<kbd>&lt;enter&gt;</kbd>: 最近使用したリポジトリに切り替え
<kbd>a</kbd>: すべてのブランチログを表示
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | 設定ファイルを開く | Open file in default application. |
| `` e `` | 設定ファイルを編集 | Open file in external editor. |
| `` u `` | 更新を確認 | |
| `` <enter> `` | 最近使用したリポジトリに切り替え | |
| `` a `` | すべてのブランチログを表示 | |
## タグ
<pre>
<kbd>&lt;space&gt;</kbd>: チェックアウト
<kbd>d</kbd>: タグを削除
<kbd>P</kbd>: タグをpush
<kbd>n</kbd>: タグを作成
<kbd>g</kbd>: View reset options
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: コミットを閲覧
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | チェックアウト | Checkout the selected tag tag as a detached HEAD. |
| `` n `` | タグを作成 | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | タグをpush | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | コミットを閲覧 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## ファイル
<pre>
<kbd>&lt;c-o&gt;</kbd>: ファイル名をクリップボードにコピー
<kbd>d</kbd>: View 'discard changes' options
<kbd>&lt;space&gt;</kbd>: ステージ/アンステージ
<kbd>&lt;c-b&gt;</kbd>: ファイルをフィルタ (ステージ/アンステージ)
<kbd>c</kbd>: 変更をコミット
<kbd>w</kbd>: pre-commitフックを実行せずに変更をコミット
<kbd>A</kbd>: 最新のコミットにamend
<kbd>C</kbd>: gitエディタを使用して変更をコミット
<kbd>e</kbd>: ファイルを編集
<kbd>o</kbd>: ファイルを開く
<kbd>i</kbd>: ファイルをignore
<kbd>r</kbd>: ファイルをリフレッシュ
<kbd>s</kbd>: 変更をstash
<kbd>S</kbd>: View stash options
<kbd>a</kbd>: すべての変更をステージ/アンステージ
<kbd>&lt;enter&gt;</kbd>: Stage individual hunks/lines for file, or collapse/expand for directory
<kbd>g</kbd>: View upstream reset options
<kbd>D</kbd>: View reset options
<kbd>`</kbd>: ファイルツリーの表示を切り替え
<kbd>M</kbd>: Git mergetoolを開く
<kbd>f</kbd>: Fetch
<kbd>/</kbd>: 検索を開始
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | ファイル名をクリップボードにコピー | |
| `` <space> `` | ステージ/アンステージ | Toggle staged for selected file. |
| `` <c-b> `` | ファイルをフィルタ (ステージ/アンステージ) | |
| `` y `` | Copy to clipboard | |
| `` c `` | 変更をコミット | Commit staged changes. |
| `` w `` | pre-commitフックを実行せずに変更をコミット | |
| `` A `` | 最新のコミットにamend | |
| `` C `` | gitエディタを使用して変更をコミット | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | Edit | Open file in external editor. |
| `` o `` | ファイルを開く | Open file in default application. |
| `` i `` | ファイルをignore | |
| `` r `` | ファイルをリフレッシュ | |
| `` s `` | Stash | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
| `` S `` | View stash options | View stash options (e.g. stash all, stash staged, stash unstaged). |
| `` a `` | すべての変更をステージ/アンステージ | Toggle staged/unstaged for all files in working tree. |
| `` <enter> `` | Stage lines / Collapse directory | If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it. |
| `` d `` | Discard | View options for discarding changes to the selected file. |
| `` g `` | View upstream reset options | |
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
| `` ` `` | ファイルツリーの表示を切り替え | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` M `` | Git mergetoolを開く | Run `git mergetool`. |
| `` f `` | Fetch | Fetch changes from remote. |
| `` / `` | 検索を開始 | |
## ブランチ
<pre>
<kbd>&lt;c-o&gt;</kbd>: ブランチ名をクリップボードにコピー
<kbd>i</kbd>: Show git-flow options
<kbd>&lt;space&gt;</kbd>: チェックアウト
<kbd>n</kbd>: 新しいブランチを作成
<kbd>o</kbd>: Pull Requestを作成
<kbd>O</kbd>: Create pull request options
<kbd>&lt;c-y&gt;</kbd>: Pull RequestのURLをクリップボードにコピー
<kbd>c</kbd>: Checkout by name
<kbd>F</kbd>: Force checkout
<kbd>d</kbd>: ブランチを削除
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>M</kbd>: 現在のブランチにマージ
<kbd>f</kbd>: Fast-forward this branch from its upstream
<kbd>T</kbd>: タグを作成
<kbd>g</kbd>: View reset options
<kbd>R</kbd>: ブランチ名を変更
<kbd>u</kbd>: Set/Unset upstream
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: コミットを閲覧
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | ブランチ名をクリップボードにコピー | |
| `` i `` | Show git-flow options | |
| `` <space> `` | チェックアウト | Checkout selected item. |
| `` n `` | 新しいブランチを作成 | |
| `` o `` | Pull Requestを作成 | |
| `` O `` | View create pull request options | |
| `` <c-y> `` | Pull RequestのURLをクリップボードにコピー | |
| `` c `` | Checkout by name | Checkout by name. In the input box you can enter '-' to switch to the last branch. |
| `` F `` | Force checkout | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | Rebase | Rebase the checked-out branch onto the selected branch. |
| `` M `` | 現在のブランチにマージ | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` f `` | Fast-forward | Fast-forward selected branch from its upstream. |
| `` T `` | タグを作成 | |
| `` s `` | 並び替え | |
| `` g `` | Reset | |
| `` R `` | ブランチ名を変更 | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | コミットを閲覧 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## メインパネル (Merging)
<pre>
<kbd>e</kbd>: ファイルを編集
<kbd>o</kbd>: ファイルを開く
<kbd>&lt;left&gt;</kbd>: 前のコンフリクトを選択
<kbd>&lt;right&gt;</kbd>: 次のコンフリクトを選択
<kbd>&lt;up&gt;</kbd>: 前のhunkを選択
<kbd>&lt;down&gt;</kbd>: 次のhunkを選択
<kbd>z</kbd>: アンドゥ
<kbd>M</kbd>: Git mergetoolを開く
<kbd>&lt;space&gt;</kbd>: Pick hunk
<kbd>b</kbd>: Pick all hunks
<kbd>&lt;esc&gt;</kbd>: ファイル一覧に戻る
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Pick hunk | |
| `` b `` | Pick all hunks | |
| `` <up> `` | 前のhunkを選択 | |
| `` <down> `` | 次のhunkを選択 | |
| `` <left> `` | 前のコンフリクトを選択 | |
| `` <right> `` | 次のコンフリクトを選択 | |
| `` z `` | アンドゥ | Undo last merge conflict resolution. |
| `` e `` | ファイルを編集 | Open file in external editor. |
| `` o `` | ファイルを開く | Open file in default application. |
| `` M `` | Git mergetoolを開く | Run `git mergetool`. |
| `` <esc> `` | ファイル一覧に戻る | |
## メインパネル (Normal)
<pre>
<kbd>mouse wheel down</kbd>: 下にスクロール (fn+up)
<kbd>mouse wheel up</kbd>: 上にスクロール (fn+down)
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | 下にスクロール | |
| `` mouse wheel up (fn+down) `` | 上にスクロール | |
## メインパネル (Patch Building)
<pre>
<kbd>&lt;left&gt;</kbd>: 前のhunkを選択
<kbd>&lt;right&gt;</kbd>: 次のhunkを選択
<kbd>v</kbd>: 範囲選択を切り替え
<kbd>V</kbd>: 範囲選択を切り替え
<kbd>a</kbd>: Hunk選択を切り替え
<kbd>&lt;c-o&gt;</kbd>: 選択されたテキストをクリップボードにコピー
<kbd>o</kbd>: ファイルを開く
<kbd>e</kbd>: ファイルを編集
<kbd>&lt;space&gt;</kbd>: 行をパッチに追加/削除
<kbd>&lt;esc&gt;</kbd>: Exit custom patch builder
<kbd>/</kbd>: 検索を開始
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 前のhunkを選択 | |
| `` <right> `` | 次のhunkを選択 | |
| `` v `` | 範囲選択を切り替え | |
| `` a `` | Hunk選択を切り替え | Toggle hunk selection mode. |
| `` <c-o> `` | 選択されたテキストをクリップボードにコピー | |
| `` o `` | ファイルを開く | Open file in default application. |
| `` e `` | ファイルを編集 | Open file in external editor. |
| `` <space> `` | 行をパッチに追加/削除 | |
| `` <esc> `` | Exit custom patch builder | |
| `` / `` | 検索を開始 | |
## メインパネル (Staging)
<pre>
<kbd>&lt;left&gt;</kbd>: 前のhunkを選択
<kbd>&lt;right&gt;</kbd>: 次のhunkを選択
<kbd>v</kbd>: 範囲選択を切り替え
<kbd>V</kbd>: 範囲選択を切り替え
<kbd>a</kbd>: Hunk選択を切り替え
<kbd>&lt;c-o&gt;</kbd>: 選択されたテキストをクリップボードにコピー
<kbd>o</kbd>: ファイルを開く
<kbd>e</kbd>: ファイルを編集
<kbd>&lt;esc&gt;</kbd>: ファイル一覧に戻る
<kbd>&lt;tab&gt;</kbd>: パネルを切り替え
<kbd>&lt;space&gt;</kbd>: 選択行をステージ/アンステージ
<kbd>d</kbd>: 変更を削除 (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: 変更をコミット
<kbd>w</kbd>: pre-commitフックを実行せずに変更をコミット
<kbd>C</kbd>: gitエディタを使用して変更をコミット
<kbd>/</kbd>: 検索を開始
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 前のhunkを選択 | |
| `` <right> `` | 次のhunkを選択 | |
| `` v `` | 範囲選択を切り替え | |
| `` a `` | Hunk選択を切り替え | Toggle hunk selection mode. |
| `` <c-o> `` | 選択されたテキストをクリップボードにコピー | |
| `` <space> `` | ステージ/アンステージ | 選択行をステージ/アンステージ |
| `` d `` | 変更を削除 (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
| `` o `` | ファイルを開く | Open file in default application. |
| `` e `` | ファイルを編集 | Open file in external editor. |
| `` <esc> `` | ファイル一覧に戻る | |
| `` <tab> `` | パネルを切り替え | Switch to other view (staged/unstaged changes). |
| `` E `` | Edit hunk | Edit selected hunk in external editor. |
| `` c `` | 変更をコミット | Commit staged changes. |
| `` w `` | pre-commitフックを実行せずに変更をコミット | |
| `` C `` | gitエディタを使用して変更をコミット | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | 検索を開始 | |
## メニュー
<pre>
<kbd>&lt;enter&gt;</kbd>: 実行
<kbd>&lt;esc&gt;</kbd>: 閉じる
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 実行 | |
| `` <esc> `` | 閉じる | |
| `` / `` | Filter the current view by text | |
## リモート
<pre>
<kbd>f</kbd>: リモートをfetch
<kbd>n</kbd>: リモートを新規追加
<kbd>d</kbd>: リモートを削除
<kbd>e</kbd>: リモートを編集
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | View branches | |
| `` n `` | リモートを新規追加 | |
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
| `` e `` | Edit | リモートを編集 |
| `` f `` | Fetch | リモートをfetch |
| `` / `` | Filter the current view by text | |
## リモートブランチ
<pre>
<kbd>&lt;c-o&gt;</kbd>: ブランチ名をクリップボードにコピー
<kbd>&lt;space&gt;</kbd>: チェックアウト
<kbd>n</kbd>: 新しいブランチを作成
<kbd>M</kbd>: 現在のブランチにマージ
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>d</kbd>: ブランチを削除
<kbd>u</kbd>: Set as upstream of checked-out branch
<kbd>g</kbd>: View reset options
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: コミットを閲覧
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | ブランチ名をクリップボードにコピー | |
| `` <space> `` | チェックアウト | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` n `` | 新しいブランチを作成 | |
| `` M `` | 現在のブランチにマージ | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` r `` | Rebase | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | Set the selected remote branch as the upstream of the checked-out branch. |
| `` s `` | 並び替え | |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | コミットを閲覧 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## 参照ログ
<pre>
<kbd>&lt;c-o&gt;</kbd>: コミットのSHAをクリップボードにコピー
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: コミットをチェックアウト
<kbd>y</kbd>: コミットの情報をコピー
<kbd>o</kbd>: ブラウザでコミットを開く
<kbd>n</kbd>: コミットにブランチを作成
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: コミットをコピー (cherry-pick)
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: コミットを閲覧
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | コミットのhashをクリップボードにコピー | |
| `` <space> `` | チェックアウト | Checkout the selected commit as a detached HEAD. |
| `` y `` | コミットの情報をコピー | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | ブラウザでコミットを開く | |
| `` n `` | コミットにブランチを作成 | |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | コミットをコピー (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Reset copied (cherry-picked) commits selection | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | コミットを閲覧 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## 確認パネル
<pre>
<kbd>&lt;enter&gt;</kbd>: 確認
<kbd>&lt;esc&gt;</kbd>: 閉じる/キャンセル
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 確認 | |
| `` <esc> `` | 閉じる/キャンセル | |

View File

@@ -1,4 +1,4 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
# Lazygit 키 바인딩
@@ -6,348 +6,364 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
## 글로벌 키 바인딩
<pre>
<kbd>&lt;c-r&gt;</kbd>: 최근에 사용한 저장소로 전환
<kbd>&lt;pgup&gt;</kbd>: 메인 패널을 위로 스크롤 (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: 메인 패널을 아래로로 스크롤 (fn+down/shift+j)
<kbd>@</kbd>: 명령어 로그 메뉴 열기
<kbd>}</kbd>: Diff 보기의 변경 사항 주위에 표시되는 컨텍스트의 크기를 늘리기
<kbd>{</kbd>: Diff 보기의 변경 사항 주위에 표시되는 컨텍스트 크기 줄이기
<kbd>:</kbd>: Execute custom command
<kbd>&lt;c-p&gt;</kbd>: 커스텀 Patch 옵션 보기
<kbd>m</kbd>: View merge/rebase options
<kbd>R</kbd>: 새로고침
<kbd>+</kbd>: 다음 스크린 모드 (normal/half/fullscreen)
<kbd>_</kbd>: 이전 스크린 모드
<kbd>?</kbd>: 매뉴 열기
<kbd>&lt;c-s&gt;</kbd>: View filter-by-path options
<kbd>W</kbd>: Diff 메뉴 열기
<kbd>&lt;c-e&gt;</kbd>: Diff 메뉴 열기
<kbd>&lt;c-w&gt;</kbd>: 공백문자를 Diff 뷰에서 표시 여부 전환
<kbd>z</kbd>: 되돌리기 (reflog) (실험적)
<kbd>&lt;c-z&gt;</kbd>: 다시 실행 (reflog) (실험적)
<kbd>P</kbd>: 푸시
<kbd>p</kbd>: 업데이트
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | 최근에 사용한 저장소로 전환 | |
| `` <pgup> (fn+up/shift+k) `` | 메인 패널을 로 스크롤 | |
| `` <pgdown> (fn+down/shift+j) `` | 메인 패널을 아래로로 스크롤 | |
| `` @ `` | 명령어 로그 메뉴 열기 | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | 푸시 | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | 업데이트 | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | Diff 보기의 변경 사항 주위에 표시되는 컨텍스트의 크기를 늘리기 | Increase the amount of the context shown around changes in the diff view. |
| `` { `` | Diff 보기의 변경 사항 주위에 표시되는 컨텍스트 크기 줄이기 | Decrease the amount of the context shown around changes in the diff view. |
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
| `` <c-p> `` | 커스텀 Patch 옵션 보기 | |
| `` m `` | View merge/rebase options | View options to abort/continue/skip the current merge/rebase. |
| `` R `` | 새로고침 | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | 다음 스크린 모드 (normal/half/fullscreen) | |
| `` _ `` | 이전 스크린 모드 | |
| `` ? `` | 매뉴 열기 | |
| `` <c-s> `` | View filter-by-path options | View options for filtering the commit log, so that only commits matching the filter are shown. |
| `` W `` | Diff 메뉴 열기 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` <c-e> `` | Diff 메뉴 열기 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` q `` | 종료 | |
| `` <esc> `` | 취소 | |
| `` <c-w> `` | 공백문자를 Diff 뷰에서 표시 여부 전환 | Toggle whether or not whitespace changes are shown in the diff view. |
| `` z `` | 되돌리기 (reflog) (실험적) | The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
| `` <c-z> `` | 다시 실행 (reflog) (실험적) | The reflog will be used to determine what git command to run to redo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
## List panel navigation
<pre>
<kbd>,</kbd>: 이전 페이지
<kbd>.</kbd>: 다음 페이지
<kbd>&lt;</kbd>: 맨 위로 스크롤
<kbd>&gt;</kbd>:아래로 스크롤
<kbd>/</kbd>: 검색 시작
<kbd>H</kbd>: 우 스크롤
<kbd>L</kbd>: 좌 스크롤
<kbd>]</kbd>: 이전 탭
<kbd>[</kbd>: 다음 탭
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | 이전 페이지 | |
| `` . `` | 다음 페이지 | |
| `` < `` |로 스크롤 | |
| `` > `` | 맨 아래로 스크롤 | |
| `` v `` | 드래그 선택 전환 | |
| `` <s-down> `` | Range select down | |
| `` <s-up> `` | Range select up | |
| `` / `` | 검색 시작 | |
| `` H `` | 우 스크롤 | |
| `` L `` | 좌 스크롤 | |
| `` ] `` | 이전 탭 | |
| `` [ `` | 다음 탭 | |
## Reflog
<pre>
<kbd>&lt;c-o&gt;</kbd>: 커밋 SHA를 클립보드에 복사
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: 커밋을 체크아웃
<kbd>y</kbd>: 커밋 attribute 복사
<kbd>o</kbd>: 브라우저에서 커밋 열기
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: 커밋 보기
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
| `` <space> `` | 체크아웃 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 커밋 attribute 복사 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 브라우저에서 커밋 열기 | |
| `` n `` | 커밋에서 새 브랜치를 만듭니다. | |
| `` g `` | View reset options | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | 커밋을 복사 (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Reset cherry-picked (copied) commits selection | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 커밋 보기 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Stash
<pre>
<kbd>&lt;space&gt;</kbd>: 적용
<kbd>g</kbd>: Pop
<kbd>d</kbd>: Drop
<kbd>n</kbd>: 새 브랜치 생성
<kbd>r</kbd>: Rename stash
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 적용 | Apply the stash entry to your working directory. |
| `` g `` | Pop | Apply the stash entry to your working directory and remove the stash entry. |
| `` d `` | Drop | Remove the stash entry from the stash list. |
| `` n `` | 새 브랜치 생성 | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
| `` r `` | Rename stash | |
| `` <enter> `` | View selected item's files | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Sub-commits
<pre>
<kbd>&lt;c-o&gt;</kbd>: 커밋 SHA를 클립보드에 복사
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: 커밋을 체크아웃
<kbd>y</kbd>: 커밋 attribute 복사
<kbd>o</kbd>: 브라우저에서 커밋 열기
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: 검색 시작
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
| `` <space> `` | 체크아웃 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 커밋 attribute 복사 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 브라우저에서 커밋 열기 | |
| `` n `` | 커밋에서 새 브랜치를 만듭니다. | |
| `` g `` | View reset options | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | 커밋을 복사 (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Reset cherry-picked (copied) commits selection | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | View selected item's files | |
| `` w `` | View worktree options | |
| `` / `` | 검색 시작 | |
## Worktrees
<pre>
<kbd>n</kbd>: Create worktree
<kbd>&lt;space&gt;</kbd>: Switch to worktree
<kbd>&lt;enter&gt;</kbd>: Switch to worktree
<kbd>o</kbd>: Open in editor
<kbd>d</kbd>: Remove worktree
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | New worktree | |
| `` <space> `` | Switch | Switch to the selected worktree. |
| `` o `` | Open in editor | |
| `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
| `` / `` | Filter the current view by text | |
## 메뉴
<pre>
<kbd>&lt;enter&gt;</kbd>: 실행
<kbd>&lt;esc&gt;</kbd>: 닫기
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 실행 | |
| `` <esc> `` | 닫기 | |
| `` / `` | Filter the current view by text | |
## 메인 패널 (Merging)
<pre>
<kbd>e</kbd>: 파일 편집
<kbd>o</kbd>: 파일 닫기
<kbd>&lt;left&gt;</kbd>: 이전 충돌을 선택
<kbd>&lt;right&gt;</kbd>: 다음 충돌을 선택
<kbd>&lt;up&gt;</kbd>: 이전 hunk를 선택
<kbd>&lt;down&gt;</kbd>: 다음 hunk를 선택
<kbd>z</kbd>: 되돌리기
<kbd>M</kbd>: Git mergetool를 열기
<kbd>&lt;space&gt;</kbd>: Pick hunk
<kbd>b</kbd>: Pick all hunks
<kbd>&lt;esc&gt;</kbd>: 파일 목록으로 돌아가기
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Pick hunk | |
| `` b `` | Pick all hunks | |
| `` <up> `` | 이전 hunk를 선택 | |
| `` <down> `` | 다음 hunk를 선택 | |
| `` <left> `` | 이전 충돌을 선택 | |
| `` <right> `` | 다음 충돌을 선택 | |
| `` z `` | 되돌리기 | Undo last merge conflict resolution. |
| `` e `` | 파일 편집 | Open file in external editor. |
| `` o `` | 파일 닫기 | Open file in default application. |
| `` M `` | Git mergetool를 열기 | Run `git mergetool`. |
| `` <esc> `` | 파일 목록으로 돌아가기 | |
## 메인 패널 (Normal)
<pre>
<kbd>mouse wheel down</kbd>: 아래로 스크롤 (fn+up)
<kbd>mouse wheel up</kbd>: 위로 스크롤 (fn+down)
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | 아래로 스크롤 | |
| `` mouse wheel up (fn+down) `` | 위로 스크롤 | |
## 메인 패널 (Patch Building)
<pre>
<kbd>&lt;left&gt;</kbd>: 이전 hunk를 선택
<kbd>&lt;right&gt;</kbd>: 다음 hunk를 선택
<kbd>v</kbd>: 드래그 선택 전환
<kbd>V</kbd>: 드래그 선택 전환
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: 선택한 텍스트를 클립보드에 복사
<kbd>o</kbd>: 파일 닫기
<kbd>e</kbd>: 파일 편집
<kbd>&lt;space&gt;</kbd>: Line(s)을 패치에 추가/삭제
<kbd>&lt;esc&gt;</kbd>: Exit custom patch builder
<kbd>/</kbd>: 검색 시작
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 이전 hunk를 선택 | |
| `` <right> `` | 다음 hunk를 선택 | |
| `` v `` | 드래그 선택 전환 | |
| `` a `` | Toggle select hunk | Toggle hunk selection mode. |
| `` <c-o> `` | 선택한 텍스트를 클립보드에 복사 | |
| `` o `` | 파일 닫기 | Open file in default application. |
| `` e `` | 파일 편집 | Open file in external editor. |
| `` <space> `` | Line(s)을 패치에 추가/삭제 | |
| `` <esc> `` | Exit custom patch builder | |
| `` / `` | 검색 시작 | |
## 메인 패널 (Staging)
<pre>
<kbd>&lt;left&gt;</kbd>: 이전 hunk를 선택
<kbd>&lt;right&gt;</kbd>: 다음 hunk를 선택
<kbd>v</kbd>: 드래그 선택 전환
<kbd>V</kbd>: 드래그 선택 전환
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: 선택한 텍스트를 클립보드에 복사
<kbd>o</kbd>: 파일 닫기
<kbd>e</kbd>: 파일 편집
<kbd>&lt;esc&gt;</kbd>: 파일 목록으로 돌아가기
<kbd>&lt;tab&gt;</kbd>: 패널 전환
<kbd>&lt;space&gt;</kbd>: 선택한 행을 staged / unstaged
<kbd>d</kbd>: 변경을 삭제 (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: 커밋 변경내용
<kbd>w</kbd>: Commit changes without pre-commit hook
<kbd>C</kbd>: Git 편집기를 사용하여 변경 내용을 커밋합니다.
<kbd>/</kbd>: 검색 시작
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 이전 hunk를 선택 | |
| `` <right> `` | 다음 hunk를 선택 | |
| `` v `` | 드래그 선택 전환 | |
| `` a `` | Toggle select hunk | Toggle hunk selection mode. |
| `` <c-o> `` | 선택한 텍스트를 클립보드에 복사 | |
| `` <space> `` | Staged 전환 | 선택한 행을 staged / unstaged |
| `` d `` | 변경을 삭제 (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
| `` o `` | 파일 닫기 | Open file in default application. |
| `` e `` | 파일 편집 | Open file in external editor. |
| `` <esc> `` | 파일 목록으로 돌아가기 | |
| `` <tab> `` | 패널 전환 | Switch to other view (staged/unstaged changes). |
| `` E `` | Edit hunk | Edit selected hunk in external editor. |
| `` c `` | 커밋 변경내용 | Commit staged changes. |
| `` w `` | Commit changes without pre-commit hook | |
| `` C `` | Git 편집기를 사용하여 변경 내용을 커밋합니다. | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | 검색 시작 | |
## 브랜치
<pre>
<kbd>&lt;c-o&gt;</kbd>: 브랜치명을 클립보드에 복사
<kbd>i</kbd>: Git-flow 옵션 보기
<kbd>&lt;space&gt;</kbd>: 체크아웃
<kbd>n</kbd>: 새 브랜치 생성
<kbd>o</kbd>: 풀 리퀘스트 생성
<kbd>O</kbd>: 풀 리퀘스트 생성 옵션
<kbd>&lt;c-y&gt;</kbd>: 풀 리퀘스트 URL을 클립보드에 복사
<kbd>c</kbd>: 이름으로 체크아웃
<kbd>F</kbd>: 강제 체크아웃
<kbd>d</kbd>: 브랜치 삭제
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
<kbd>M</kbd>: 현재 브랜치에 병합
<kbd>f</kbd>: Fast-forward this branch from its upstream
<kbd>T</kbd>: 태그를 생성
<kbd>g</kbd>: View reset options
<kbd>R</kbd>: 브랜치 이름 변경
<kbd>u</kbd>: Set/Unset upstream
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: 커밋 보기
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 브랜치명을 클립보드에 복사 | |
| `` i `` | Git-flow 옵션 보기 | |
| `` <space> `` | 체크아웃 | Checkout selected item. |
| `` n `` | 새 브랜치 생성 | |
| `` o `` | 풀 리퀘스트 생성 | |
| `` O `` | 풀 리퀘스트 생성 옵션 | |
| `` <c-y> `` | 풀 리퀘스트 URL을 클립보드에 복사 | |
| `` c `` | 이름으로 체크아웃 | Checkout by name. In the input box you can enter '-' to switch to the last branch. |
| `` F `` | 강제 체크아웃 | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | 체크아웃된 브랜치를 이 브랜치에 리베이스 | Rebase the checked-out branch onto the selected branch. |
| `` M `` | 현재 브랜치에 병합 | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` f `` | Fast-forward this branch from its upstream | Fast-forward selected branch from its upstream. |
| `` T `` | 태그를 생성 | |
| `` s `` | Sort order | |
| `` g `` | View reset options | |
| `` R `` | 브랜치 이름 변경 | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 커밋 보기 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## 상태
<pre>
<kbd>o</kbd>: 설정 파일 열기
<kbd>e</kbd>: 설정 파일 수정
<kbd>u</kbd>: 업데이트 확인
<kbd>&lt;enter&gt;</kbd>: 최근에 사용한 저장소로 전환
<kbd>a</kbd>: 모든 브랜치 로그 표시
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | 설정 파일 열기 | Open file in default application. |
| `` e `` | 설정 파일 수정 | Open file in external editor. |
| `` u `` | 업데이트 확인 | |
| `` <enter> `` | 최근에 사용한 저장소로 전환 | |
| `` a `` | 모든 브랜치 로그 표시 | |
## 서브모듈
<pre>
<kbd>&lt;c-o&gt;</kbd>: 서브모듈 이름을 클립보드에 복사
<kbd>&lt;enter&gt;</kbd>: 서브모듈 열기
<kbd>&lt;space&gt;</kbd>: 서브모듈 열기
<kbd>d</kbd>: 서브모듈 삭제
<kbd>u</kbd>: 서브모듈 업데이트
<kbd>n</kbd>: 새로운 서브모듈 추가
<kbd>e</kbd>: 서브모듈의 URL을 수정
<kbd>i</kbd>: 서브모듈 초기화
<kbd>b</kbd>: View bulk submodule options
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 서브모듈 이름을 클립보드에 복사 | |
| `` <enter> `` | Enter | 서브모듈 열기 |
| `` d `` | Remove | Remove the selected submodule and its corresponding directory. |
| `` u `` | Update | 서브모듈 업데이트 |
| `` n `` | 새로운 서브모듈 추가 | |
| `` e `` | 서브모듈의 URL을 수정 | |
| `` i `` | Initialize | 서브모듈 초기화 |
| `` b `` | View bulk submodule options | |
| `` / `` | Filter the current view by text | |
## 원격
<pre>
<kbd>f</kbd>: 원격을 업데이트
<kbd>n</kbd>: 새로운 Remote 추가
<kbd>d</kbd>: Remote를 삭제
<kbd>e</kbd>: Remote를 수정
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | View branches | |
| `` n `` | 새로운 Remote 추가 | |
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
| `` e `` | Edit | Remote를 수정 |
| `` f `` | Fetch | 원격을 업데이트 |
| `` / `` | Filter the current view by text | |
## 원격 브랜치
<pre>
<kbd>&lt;c-o&gt;</kbd>: 브랜치명을 클립보드에 복사
<kbd>&lt;space&gt;</kbd>: 체크아웃
<kbd>n</kbd>: 새 브랜치 생성
<kbd>M</kbd>: 현재 브랜치에 병합
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
<kbd>d</kbd>: 브랜치 삭제
<kbd>u</kbd>: Set as upstream of checked-out branch
<kbd>g</kbd>: View reset options
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: 커밋 보기
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 브랜치명을 클립보드에 복사 | |
| `` <space> `` | 체크아웃 | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` n `` | 새 브랜치 생성 | |
| `` M `` | 현재 브랜치에 병합 | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` r `` | 체크아웃된 브랜치를 이 브랜치에 리베이스 | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | Set the selected remote branch as the upstream of the checked-out branch. |
| `` s `` | Sort order | |
| `` g `` | View reset options | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 커밋 보기 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## 커밋
<pre>
<kbd>&lt;c-o&gt;</kbd>: 커밋 SHA를 클립보드에 복사
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>b</kbd>: Bisect 옵션 보기
<kbd>s</kbd>: Squash down
<kbd>f</kbd>: Fixup commit
<kbd>r</kbd>: 커밋메시지 변경
<kbd>R</kbd>: 에디터에서 커밋메시지 수정
<kbd>d</kbd>: 커밋 삭제
<kbd>e</kbd>: 커밋을 편집
<kbd>p</kbd>: Pick commit (when mid-rebase)
<kbd>F</kbd>: Create fixup commit for this commit
<kbd>S</kbd>: Squash all 'fixup!' commits above selected commit (autosquash)
<kbd>&lt;c-j&gt;</kbd>: 커밋을 1개 아래로 이동
<kbd>&lt;c-k&gt;</kbd>: 커밋을 1개 위로 이동
<kbd>v</kbd>: 커밋을 붙여넣기 (cherry-pick)
<kbd>B</kbd>: Mark commit as base commit for rebase
<kbd>A</kbd>: Amend commit with staged changes
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: 커밋 되돌리기
<kbd>T</kbd>: Tag commit
<kbd>&lt;c-l&gt;</kbd>: 로그 메뉴 열기
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: 커밋을 체크아웃
<kbd>y</kbd>: 커밋 attribute 복사
<kbd>o</kbd>: 브라우저에서 커밋 열기
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
<kbd>&lt;enter&gt;</kbd>: View selected item's files
<kbd>/</kbd>: 검색 시작
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 커밋 해시를 클립보드에 복사 | |
| `` <c-r> `` | Reset cherry-picked (copied) commits selection | |
| `` b `` | Bisect 옵션 보기 | |
| `` s `` | Squash | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
| `` f `` | Fixup | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
| `` r `` | 커밋메시지 변경 | Reword the selected commit's message. |
| `` R `` | 에디터에서 커밋메시지 수정 | |
| `` d `` | 커밋 삭제 | Drop the selected commit. This will remove the commit from the branch via a rebase. If the commit makes changes that later commits depend on, you may need to resolve merge conflicts. |
| `` e `` | Edit (start interactive rebase) | 커밋을 편집 |
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Pick commit (when mid-rebase) |
| `` F `` | Create fixup commit | Create fixup commit for this commit |
| `` S `` | Apply fixup commits | Squash all 'fixup!' commits above selected commit (autosquash) |
| `` <c-j> `` | 커밋을 1개 아래로 이동 | |
| `` <c-k> `` | 커밋을 1개 위로 이동 | |
| `` V `` | 커밋을 붙여넣기 (cherry-pick) | |
| `` B `` | Mark as base commit for rebase | Select a base commit for the next rebase. When you rebase onto a branch, only commits above the base commit will be brought across. This uses the `git rebase --onto` command. |
| `` A `` | Amend | Amend commit with staged changes |
| `` a `` | Amend commit attribute | Set/Reset commit author or set co-author. |
| `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
| `` T `` | Tag commit | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
| `` <c-l> `` | 로그 메뉴 열기 | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
| `` <space> `` | 체크아웃 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 커밋 attribute 복사 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 브라우저에서 커밋 열기 | |
| `` n `` | 커밋에서 새 브랜치를 만듭니다. | |
| `` g `` | View reset options | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | 커밋을 복사 (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | View selected item's files | |
| `` w `` | View worktree options | |
| `` / `` | 검색 시작 | |
## 커밋 파일
<pre>
<kbd>&lt;c-o&gt;</kbd>: 커밋한 파일명을 클립보드에 복사
<kbd>c</kbd>: Checkout file
<kbd>d</kbd>: Discard this commit's changes to this file
<kbd>o</kbd>: 파일 닫기
<kbd>e</kbd>: 파일 편집
<kbd>&lt;space&gt;</kbd>: Toggle file included in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>`</kbd>: 파일 트리뷰로 전환
<kbd>/</kbd>: 검색 시작
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 파일명을 클립보드에 복사 | |
| `` c `` | 체크아웃 | Checkout file |
| `` d `` | Remove | Discard this commit's changes to this file |
| `` o `` | 파일 닫기 | Open file in default application. |
| `` e `` | Edit | Open file in external editor. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <space> `` | Toggle file included in patch | Toggle whether the file is included in the custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` a `` | Toggle all files included in patch | Add/remove all commit's files to custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` <enter> `` | Enter file to add selected lines to the patch (or toggle directory collapsed) | If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory. |
| `` ` `` | 파일 트리뷰로 전환 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` / `` | 검색 시작 | |
## 커밋메시지
<pre>
<kbd>&lt;enter&gt;</kbd>: 확인
<kbd>&lt;esc&gt;</kbd>: 닫기
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 확인 | |
| `` <esc> `` | 닫기 | |
## 태그
<pre>
<kbd>&lt;space&gt;</kbd>: 체크아웃
<kbd>d</kbd>: 태그 삭제
<kbd>P</kbd>: 태그를 push
<kbd>n</kbd>: 태그를 생성
<kbd>g</kbd>: View reset options
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: 커밋 보기
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 체크아웃 | Checkout the selected tag tag as a detached HEAD. |
| `` n `` | 태그를 생성 | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | 태그를 push | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | 커밋 보기 | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## 파일
<pre>
<kbd>&lt;c-o&gt;</kbd>: 파일명을 클립보드에 복사
<kbd>d</kbd>: View 'discard changes' options
<kbd>&lt;space&gt;</kbd>: Staged 전환
<kbd>&lt;c-b&gt;</kbd>: 파일을 필터하기 (Staged/unstaged)
<kbd>c</kbd>: 커밋 변경내용
<kbd>w</kbd>: Commit changes without pre-commit hook
<kbd>A</kbd>: 마지맛 커밋 수정
<kbd>C</kbd>: Git 편집기를 사용하여 변경 내용을 커밋합니다.
<kbd>e</kbd>: 파일 편집
<kbd>o</kbd>: 파일 닫기
<kbd>i</kbd>: Ignore file
<kbd>r</kbd>: 파일 새로고침
<kbd>s</kbd>: 변경사항을 Stash
<kbd>S</kbd>: Stash 옵션 보기
<kbd>a</kbd>: 모든 변경을 Staged/unstaged으로 전환
<kbd>&lt;enter&gt;</kbd>: Stage individual hunks/lines for file, or collapse/expand for directory
<kbd>g</kbd>: View upstream reset options
<kbd>D</kbd>: View reset options
<kbd>`</kbd>: 파일 트리뷰로 전환
<kbd>M</kbd>: Git mergetool를 열기
<kbd>f</kbd>: Fetch
<kbd>/</kbd>: 검색 시작
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 파일명을 클립보드에 복사 | |
| `` <space> `` | Staged 전환 | Toggle staged for selected file. |
| `` <c-b> `` | 파일을 필터하기 (Staged/unstaged) | |
| `` y `` | Copy to clipboard | |
| `` c `` | 커밋 변경내용 | Commit staged changes. |
| `` w `` | Commit changes without pre-commit hook | |
| `` A `` | 마지맛 커밋 수정 | |
| `` C `` | Git 편집기를 사용하여 변경 내용을 커밋합니다. | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | Edit | Open file in external editor. |
| `` o `` | 파일 닫기 | Open file in default application. |
| `` i `` | Ignore file | |
| `` r `` | 파일 새로고침 | |
| `` s `` | Stash | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
| `` S `` | Stash 옵션 보기 | View stash options (e.g. stash all, stash staged, stash unstaged). |
| `` a `` | 모든 변경을 Staged/unstaged으로 전환 | Toggle staged/unstaged for all files in working tree. |
| `` <enter> `` | Stage individual hunks/lines for file, or collapse/expand for directory | If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it. |
| `` d `` | View 'discard changes' options | View options for discarding changes to the selected file. |
| `` g `` | View upstream reset options | |
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
| `` ` `` | 파일 트리뷰로 전환 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` M `` | Git mergetool를 열기 | Run `git mergetool`. |
| `` f `` | Fetch | Fetch changes from remote. |
| `` / `` | 검색 시작 | |
## 확인 패널
<pre>
<kbd>&lt;enter&gt;</kbd>: 확인
<kbd>&lt;esc&gt;</kbd>: 닫기/취소
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 확인 | |
| `` <esc> `` | 닫기/취소 | |

View File

@@ -1,4 +1,4 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
# Lazygit Sneltoetsen
@@ -6,348 +6,364 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
## Globale sneltoetsen
<pre>
<kbd>&lt;c-r&gt;</kbd>: Wissel naar een recente repo
<kbd>&lt;pgup&gt;</kbd>: Scroll naar beneden vanaf hoofdpaneel (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: Scroll naar beneden vanaf hoofdpaneel (fn+down/shift+j)
<kbd>@</kbd>: Open command log menu
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
<kbd>:</kbd>: Voer aangepaste commando uit
<kbd>&lt;c-p&gt;</kbd>: Bekijk aangepaste patch opties
<kbd>m</kbd>: Bekijk merge/rebase opties
<kbd>R</kbd>: Verversen
<kbd>+</kbd>: Volgende scherm modus (normaal/half/groot)
<kbd>_</kbd>: Vorige scherm modus
<kbd>?</kbd>: Open menu
<kbd>&lt;c-s&gt;</kbd>: Bekijk scoping opties
<kbd>W</kbd>: Open diff menu
<kbd>&lt;c-e&gt;</kbd>: Open diff menu
<kbd>&lt;c-w&gt;</kbd>: Toggle whether or not whitespace changes are shown in the diff view
<kbd>z</kbd>: Ongedaan maken (via reflog) (experimenteel)
<kbd>&lt;c-z&gt;</kbd>: Redo (via reflog) (experimenteel)
<kbd>P</kbd>: Push
<kbd>p</kbd>: Pull
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | Wissel naar een recente repo | |
| `` <pgup> (fn+up/shift+k) `` | Scroll naar beneden vanaf hoofdpaneel | |
| `` <pgdown> (fn+down/shift+j) `` | Scroll naar beneden vanaf hoofdpaneel | |
| `` @ `` | View command log options | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | Push | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | Pull | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | Increase diff context size | Increase the amount of the context shown around changes in the diff view. |
| `` { `` | Decrease diff context size | Decrease the amount of the context shown around changes in the diff view. |
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
| `` <c-p> `` | Bekijk aangepaste patch opties | |
| `` m `` | Bekijk merge/rebase opties | View options to abort/continue/skip the current merge/rebase. |
| `` R `` | Verversen | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | Volgende scherm modus (normaal/half/groot) | |
| `` _ `` | Vorige scherm modus | |
| `` ? `` | Open menu | |
| `` <c-s> `` | Bekijk scoping opties | View options for filtering the commit log, so that only commits matching the filter are shown. |
| `` W `` | Open diff menu | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` <c-e> `` | Open diff menu | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` q `` | Quit | |
| `` <esc> `` | Annuleren | |
| `` <c-w> `` | Toggle whitespace | Toggle whether or not whitespace changes are shown in the diff view. |
| `` z `` | Ongedaan maken (via reflog) (experimenteel) | The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
| `` <c-z> `` | Redo (via reflog) (experimenteel) | The reflog will be used to determine what git command to run to redo the last git command. This does not include changes to the working tree; only commits are taken into consideration. |
## Lijstpaneel navigatie
<pre>
<kbd>,</kbd>: Vorige pagina
<kbd>.</kbd>: Volgende pagina
<kbd>&lt;</kbd>: Scroll naar boven
<kbd>&gt;</kbd>: Scroll naar beneden
<kbd>/</kbd>: Start met zoeken
<kbd>H</kbd>: Scroll left
<kbd>L</kbd>: Scroll right
<kbd>]</kbd>: Volgende tabblad
<kbd>[</kbd>: Vorige tabblad
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | Vorige pagina | |
| `` . `` | Volgende pagina | |
| `` < `` | Scroll naar boven | |
| `` > `` | Scroll naar beneden | |
| `` v `` | Toggle drag selecteer | |
| `` <s-down> `` | Range select down | |
| `` <s-up> `` | Range select up | |
| `` / `` | Start met zoeken | |
| `` H `` | Scroll left | |
| `` L `` | Scroll right | |
| `` ] `` | Volgende tabblad | |
| `` [ `` | Vorige tabblad | |
## Bestanden
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer de bestandsnaam naar het klembord
<kbd>d</kbd>: Bekijk 'veranderingen ongedaan maken' opties
<kbd>&lt;space&gt;</kbd>: Toggle staged
<kbd>&lt;c-b&gt;</kbd>: Filter files by status
<kbd>c</kbd>: Commit veranderingen
<kbd>w</kbd>: Commit veranderingen zonder pre-commit hook
<kbd>A</kbd>: Wijzig laatste commit
<kbd>C</kbd>: Commit veranderingen met de git editor
<kbd>e</kbd>: Verander bestand
<kbd>o</kbd>: Open bestand
<kbd>i</kbd>: Ignore or exclude file
<kbd>r</kbd>: Refresh bestanden
<kbd>s</kbd>: Stash-bestanden
<kbd>S</kbd>: Bekijk stash opties
<kbd>a</kbd>: Toggle staged alle
<kbd>&lt;enter&gt;</kbd>: Stage individuele hunks/lijnen
<kbd>g</kbd>: Bekijk upstream reset opties
<kbd>D</kbd>: Bekijk reset opties
<kbd>`</kbd>: Toggle bestandsboom weergave
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>f</kbd>: Fetch
<kbd>/</kbd>: Start met zoeken
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer de bestandsnaam naar het klembord | |
| `` <space> `` | Toggle staged | Toggle staged for selected file. |
| `` <c-b> `` | Filter files by status | |
| `` y `` | Copy to clipboard | |
| `` c `` | Commit veranderingen | Commit staged changes. |
| `` w `` | Commit veranderingen zonder pre-commit hook | |
| `` A `` | Wijzig laatste commit | |
| `` C `` | Commit veranderingen met de git editor | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | Edit | Open file in external editor. |
| `` o `` | Open bestand | Open file in default application. |
| `` i `` | Ignore or exclude file | |
| `` r `` | Refresh bestanden | |
| `` s `` | Stash | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
| `` S `` | Bekijk stash opties | View stash options (e.g. stash all, stash staged, stash unstaged). |
| `` a `` | Toggle staged alle | Toggle staged/unstaged for all files in working tree. |
| `` <enter> `` | Stage individuele hunks/lijnen | If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it. |
| `` d `` | Bekijk 'veranderingen ongedaan maken' opties | View options for discarding changes to the selected file. |
| `` g `` | Bekijk upstream reset opties | |
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
| `` ` `` | Toggle bestandsboom weergave | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` M `` | Open external merge tool | Run `git mergetool`. |
| `` f `` | Fetch | Fetch changes from remote. |
| `` / `` | Start met zoeken | |
## Bevestigingspaneel
<pre>
<kbd>&lt;enter&gt;</kbd>: Bevestig
<kbd>&lt;esc&gt;</kbd>: Sluiten
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Bevestig | |
| `` <esc> `` | Sluiten | |
## Branches
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer branch name naar klembord
<kbd>i</kbd>: Laat git-flow opties zien
<kbd>&lt;space&gt;</kbd>: Uitchecken
<kbd>n</kbd>: Nieuwe branch
<kbd>o</kbd>: Maak een pull-request
<kbd>O</kbd>: Bekijk opties voor pull-aanvraag
<kbd>&lt;c-y&gt;</kbd>: Kopieer de URL van het pull-verzoek naar het klembord
<kbd>c</kbd>: Uitchecken bij naam
<kbd>F</kbd>: Forceer checkout
<kbd>d</kbd>: Verwijder branch
<kbd>r</kbd>: Rebase branch
<kbd>M</kbd>: Merge in met huidige checked out branch
<kbd>f</kbd>: Fast-forward deze branch vanaf zijn upstream
<kbd>T</kbd>: Creëer tag
<kbd>g</kbd>: Bekijk reset opties
<kbd>R</kbd>: Hernoem branch
<kbd>u</kbd>: Set/Unset upstream
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: Bekijk commits
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer branch name naar klembord | |
| `` i `` | Laat git-flow opties zien | |
| `` <space> `` | Uitchecken | Checkout selected item. |
| `` n `` | Nieuwe branch | |
| `` o `` | Maak een pull-request | |
| `` O `` | Bekijk opties voor pull-aanvraag | |
| `` <c-y> `` | Kopieer de URL van het pull-verzoek naar het klembord | |
| `` c `` | Uitchecken bij naam | Checkout by name. In the input box you can enter '-' to switch to the last branch. |
| `` F `` | Forceer checkout | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | Rebase branch | Rebase the checked-out branch onto the selected branch. |
| `` M `` | Merge in met huidige checked out branch | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` f `` | Fast-forward deze branch vanaf zijn upstream | Fast-forward selected branch from its upstream. |
| `` T `` | Creëer tag | |
| `` s `` | Sort order | |
| `` g `` | Bekijk reset opties | |
| `` R `` | Hernoem branch | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Bekijk commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Commit bericht
<pre>
<kbd>&lt;enter&gt;</kbd>: Bevestig
<kbd>&lt;esc&gt;</kbd>: Sluiten
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Bevestig | |
| `` <esc> `` | Sluiten | |
## Commit bestanden
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer de vastgelegde bestandsnaam naar het klembord
<kbd>c</kbd>: Bestand uitchecken
<kbd>d</kbd>: Uitsluit deze commit zijn veranderingen aan dit bestand
<kbd>o</kbd>: Open bestand
<kbd>e</kbd>: Verander bestand
<kbd>&lt;space&gt;</kbd>: Toggle bestand inbegrepen in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter bestand om geselecteerde regels toe te voegen aan de patch
<kbd>`</kbd>: Toggle bestandsboom weergave
<kbd>/</kbd>: Start met zoeken
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer de bestandsnaam naar het klembord | |
| `` c `` | Uitchecken | Bestand uitchecken |
| `` d `` | Remove | Uitsluit deze commit zijn veranderingen aan dit bestand |
| `` o `` | Open bestand | Open file in default application. |
| `` e `` | Edit | Open file in external editor. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <space> `` | Toggle bestand inbegrepen in patch | Toggle whether the file is included in the custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` a `` | Toggle all files | Add/remove all commit's files to custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` <enter> `` | Enter bestand om geselecteerde regels toe te voegen aan de patch | If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory. |
| `` ` `` | Toggle bestandsboom weergave | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` / `` | Start met zoeken | |
## Commits
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer commit SHA naar klembord
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (gekopieerde) commits selectie
<kbd>b</kbd>: View bisect options
<kbd>s</kbd>: Squash beneden
<kbd>f</kbd>: Fixup commit
<kbd>r</kbd>: Hernoem commit
<kbd>R</kbd>: Hernoem commit met editor
<kbd>d</kbd>: Verwijder commit
<kbd>e</kbd>: Wijzig commit
<kbd>p</kbd>: Kies commit (wanneer midden in rebase)
<kbd>F</kbd>: Creëer fixup commit
<kbd>S</kbd>: Squash bovenstaande commits
<kbd>&lt;c-j&gt;</kbd>: Verplaats commit 1 naar beneden
<kbd>&lt;c-k&gt;</kbd>: Verplaats commit 1 naar boven
<kbd>v</kbd>: Plak commits (cherry-pick)
<kbd>B</kbd>: Mark commit as base commit for rebase
<kbd>A</kbd>: Wijzig commit met staged veranderingen
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: Commit ongedaan maken
<kbd>T</kbd>: Tag commit
<kbd>&lt;c-l&gt;</kbd>: Open log menu
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Creëer nieuwe branch van commit
<kbd>g</kbd>: Bekijk reset opties
<kbd>c</kbd>: Kopieer commit (cherry-pick)
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
<kbd>&lt;enter&gt;</kbd>: Bekijk gecommite bestanden
<kbd>/</kbd>: Start met zoeken
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer commit hash naar klembord | |
| `` <c-r> `` | Reset cherry-picked (gekopieerde) commits selectie | |
| `` b `` | View bisect options | |
| `` s `` | Squash | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
| `` f `` | Fixup | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
| `` r `` | Hernoem commit | Reword the selected commit's message. |
| `` R `` | Hernoem commit met editor | |
| `` d `` | Verwijder commit | Drop the selected commit. This will remove the commit from the branch via a rebase. If the commit makes changes that later commits depend on, you may need to resolve merge conflicts. |
| `` e `` | Edit (start interactive rebase) | Wijzig commit |
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Kies commit (wanneer midden in rebase) |
| `` F `` | Creëer fixup commit | Creëer fixup commit |
| `` S `` | Apply fixup commits | Squash bovenstaande commits |
| `` <c-j> `` | Verplaats commit 1 naar beneden | |
| `` <c-k> `` | Verplaats commit 1 naar boven | |
| `` V `` | Plak commits (cherry-pick) | |
| `` B `` | Mark as base commit for rebase | Select a base commit for the next rebase. When you rebase onto a branch, only commits above the base commit will be brought across. This uses the `git rebase --onto` command. |
| `` A `` | Amend | Wijzig commit met staged veranderingen |
| `` a `` | Amend commit attribute | Set/Reset commit author or set co-author. |
| `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
| `` T `` | Tag commit | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
| `` <c-l> `` | View log options | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
| `` <space> `` | Uitchecken | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Creëer nieuwe branch van commit | |
| `` g `` | Bekijk reset opties | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Kopieer commit (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Bekijk gecommite bestanden | |
| `` w `` | View worktree options | |
| `` / `` | Start met zoeken | |
## Menu
<pre>
<kbd>&lt;enter&gt;</kbd>: Uitvoeren
<kbd>&lt;esc&gt;</kbd>: Sluiten
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Uitvoeren | |
| `` <esc> `` | Sluiten | |
| `` / `` | Filter the current view by text | |
## Mergen
<pre>
<kbd>e</kbd>: Verander bestand
<kbd>o</kbd>: Open bestand
<kbd>&lt;left&gt;</kbd>: Selecteer voorgaand conflict
<kbd>&lt;right&gt;</kbd>: Selecteer volgende conflict
<kbd>&lt;up&gt;</kbd>: Selecteer bovenste hunk
<kbd>&lt;down&gt;</kbd>: Selecteer onderste hunk
<kbd>z</kbd>: Ongedaan maken
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>&lt;space&gt;</kbd>: Kies stuk
<kbd>b</kbd>: Kies beide stukken
<kbd>&lt;esc&gt;</kbd>: Ga terug naar het bestanden paneel
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Kies stuk | |
| `` b `` | Kies beide stukken | |
| `` <up> `` | Selecteer bovenste hunk | |
| `` <down> `` | Selecteer onderste hunk | |
| `` <left> `` | Selecteer voorgaand conflict | |
| `` <right> `` | Selecteer volgende conflict | |
| `` z `` | Ongedaan maken | Undo last merge conflict resolution. |
| `` e `` | Verander bestand | Open file in external editor. |
| `` o `` | Open bestand | Open file in default application. |
| `` M `` | Open external merge tool | Run `git mergetool`. |
| `` <esc> `` | Ga terug naar het bestanden paneel | |
## Normaal
<pre>
<kbd>mouse wheel down</kbd>: Scroll omlaag (fn+up)
<kbd>mouse wheel up</kbd>: Scroll omhoog (fn+down)
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | Scroll omlaag | |
| `` mouse wheel up (fn+down) `` | Scroll omhoog | |
## Patch bouwen
<pre>
<kbd>&lt;left&gt;</kbd>: Selecteer de vorige hunk
<kbd>&lt;right&gt;</kbd>: Selecteer de volgende hunk
<kbd>v</kbd>: Toggle drag selecteer
<kbd>V</kbd>: Toggle drag selecteer
<kbd>a</kbd>: Toggle selecteer hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Open bestand
<kbd>e</kbd>: Verander bestand
<kbd>&lt;space&gt;</kbd>: Voeg toe/verwijder lijn(en) in patch
<kbd>&lt;esc&gt;</kbd>: Sluit lijn-bij-lijn modus
<kbd>/</kbd>: Start met zoeken
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Selecteer de vorige hunk | |
| `` <right> `` | Selecteer de volgende hunk | |
| `` v `` | Toggle drag selecteer | |
| `` a `` | Toggle selecteer hunk | Toggle hunk selection mode. |
| `` <c-o> `` | Copy selected text to clipboard | |
| `` o `` | Open bestand | Open file in default application. |
| `` e `` | Verander bestand | Open file in external editor. |
| `` <space> `` | Voeg toe/verwijder lijn(en) in patch | |
| `` <esc> `` | Sluit lijn-bij-lijn modus | |
| `` / `` | Start met zoeken | |
## Reflog
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer commit SHA naar klembord
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Creëer nieuwe branch van commit
<kbd>g</kbd>: Bekijk reset opties
<kbd>c</kbd>: Kopieer commit (cherry-pick)
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (gekopieerde) commits selectie
<kbd>&lt;enter&gt;</kbd>: Bekijk commits
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer commit hash naar klembord | |
| `` <space> `` | Uitchecken | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Creëer nieuwe branch van commit | |
| `` g `` | Bekijk reset opties | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Kopieer commit (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Reset cherry-picked (gekopieerde) commits selectie | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Bekijk commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Remote branches
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer branch name naar klembord
<kbd>&lt;space&gt;</kbd>: Uitchecken
<kbd>n</kbd>: Nieuwe branch
<kbd>M</kbd>: Merge in met huidige checked out branch
<kbd>r</kbd>: Rebase branch
<kbd>d</kbd>: Verwijder branch
<kbd>u</kbd>: Stel in als upstream van uitgecheckte branch
<kbd>g</kbd>: Bekijk reset opties
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: Bekijk commits
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer branch name naar klembord | |
| `` <space> `` | Uitchecken | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` n `` | Nieuwe branch | |
| `` M `` | Merge in met huidige checked out branch | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` r `` | Rebase branch | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | Stel in als upstream van uitgecheckte branch |
| `` s `` | Sort order | |
| `` g `` | Bekijk reset opties | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Bekijk commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Remotes
<pre>
<kbd>f</kbd>: Fetch remote
<kbd>n</kbd>: Voeg een nieuwe remote toe
<kbd>d</kbd>: Verwijder remote
<kbd>e</kbd>: Wijzig remote
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | View branches | |
| `` n `` | Voeg een nieuwe remote toe | |
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
| `` e `` | Edit | Wijzig remote |
| `` f `` | Fetch | Fetch remote |
| `` / `` | Filter the current view by text | |
## Staging
<pre>
<kbd>&lt;left&gt;</kbd>: Selecteer de vorige hunk
<kbd>&lt;right&gt;</kbd>: Selecteer de volgende hunk
<kbd>v</kbd>: Toggle drag selecteer
<kbd>V</kbd>: Toggle drag selecteer
<kbd>a</kbd>: Toggle selecteer hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Open bestand
<kbd>e</kbd>: Verander bestand
<kbd>&lt;esc&gt;</kbd>: Ga terug naar het bestanden paneel
<kbd>&lt;tab&gt;</kbd>: Ga naar een ander paneel
<kbd>&lt;space&gt;</kbd>: Toggle lijnen staged / unstaged
<kbd>d</kbd>: Verwijdert change (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: Commit veranderingen
<kbd>w</kbd>: Commit veranderingen zonder pre-commit hook
<kbd>C</kbd>: Commit veranderingen met de git editor
<kbd>/</kbd>: Start met zoeken
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Selecteer de vorige hunk | |
| `` <right> `` | Selecteer de volgende hunk | |
| `` v `` | Toggle drag selecteer | |
| `` a `` | Toggle selecteer hunk | Toggle hunk selection mode. |
| `` <c-o> `` | Copy selected text to clipboard | |
| `` <space> `` | Toggle staged | Toggle lijnen staged / unstaged |
| `` d `` | Verwijdert change (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
| `` o `` | Open bestand | Open file in default application. |
| `` e `` | Verander bestand | Open file in external editor. |
| `` <esc> `` | Ga terug naar het bestanden paneel | |
| `` <tab> `` | Ga naar een ander paneel | Switch to other view (staged/unstaged changes). |
| `` E `` | Edit hunk | Edit selected hunk in external editor. |
| `` c `` | Commit veranderingen | Commit staged changes. |
| `` w `` | Commit veranderingen zonder pre-commit hook | |
| `` C `` | Commit veranderingen met de git editor | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | Start met zoeken | |
## Stash
<pre>
<kbd>&lt;space&gt;</kbd>: Toepassen
<kbd>g</kbd>: Pop
<kbd>d</kbd>: Laten vallen
<kbd>n</kbd>: Nieuwe branch
<kbd>r</kbd>: Rename stash
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: Bekijk gecommite bestanden
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Toepassen | Apply the stash entry to your working directory. |
| `` g `` | Pop | Apply the stash entry to your working directory and remove the stash entry. |
| `` d `` | Laten vallen | Remove the stash entry from the stash list. |
| `` n `` | Nieuwe branch | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
| `` r `` | Rename stash | |
| `` <enter> `` | Bekijk gecommite bestanden | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Status
<pre>
<kbd>o</kbd>: Open config bestand
<kbd>e</kbd>: Verander config bestand
<kbd>u</kbd>: Check voor updates
<kbd>&lt;enter&gt;</kbd>: Wissel naar een recente repo
<kbd>a</kbd>: Alle logs van de branch laten zien
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | Open config bestand | Open file in default application. |
| `` e `` | Verander config bestand | Open file in external editor. |
| `` u `` | Check voor updates | |
| `` <enter> `` | Wissel naar een recente repo | |
| `` a `` | Alle logs van de branch laten zien | |
## Sub-commits
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer commit SHA naar klembord
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Creëer nieuwe branch van commit
<kbd>g</kbd>: Bekijk reset opties
<kbd>c</kbd>: Kopieer commit (cherry-pick)
<kbd>C</kbd>: Kopieer commit reeks (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (gekopieerde) commits selectie
<kbd>&lt;enter&gt;</kbd>: Bekijk gecommite bestanden
<kbd>/</kbd>: Start met zoeken
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer commit hash naar klembord | |
| `` <space> `` | Uitchecken | Checkout the selected commit as a detached HEAD. |
| `` y `` | Copy commit attribute to clipboard | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Open commit in browser | |
| `` n `` | Creëer nieuwe branch van commit | |
| `` g `` | Bekijk reset opties | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Kopieer commit (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Reset cherry-picked (gekopieerde) commits selectie | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Bekijk gecommite bestanden | |
| `` w `` | View worktree options | |
| `` / `` | Start met zoeken | |
## Submodules
<pre>
<kbd>&lt;c-o&gt;</kbd>: Kopieer submodule naam naar klembord
<kbd>&lt;enter&gt;</kbd>: Enter submodule
<kbd>&lt;space&gt;</kbd>: Enter submodule
<kbd>d</kbd>: Remove submodule
<kbd>u</kbd>: Update submodule
<kbd>n</kbd>: Voeg nieuwe submodule toe
<kbd>e</kbd>: Update submodule URL
<kbd>i</kbd>: Initialiseer submodule
<kbd>b</kbd>: Bekijk bulk submodule opties
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopieer submodule naam naar klembord | |
| `` <enter> `` | Enter | Enter submodule |
| `` d `` | Remove | Remove the selected submodule and its corresponding directory. |
| `` u `` | Update | Update selected submodule. |
| `` n `` | Voeg nieuwe submodule toe | |
| `` e `` | Update submodule URL | |
| `` i `` | Initialize | Initialiseer submodule |
| `` b `` | Bekijk bulk submodule opties | |
| `` / `` | Filter the current view by text | |
## Tags
<pre>
<kbd>&lt;space&gt;</kbd>: Uitchecken
<kbd>d</kbd>: Verwijder tag
<kbd>P</kbd>: Push tag
<kbd>n</kbd>: Creëer tag
<kbd>g</kbd>: Bekijk reset opties
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: Bekijk commits
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Uitchecken | Checkout the selected tag tag as a detached HEAD. |
| `` n `` | Creëer tag | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | Push tag | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Bekijk commits | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Worktrees
<pre>
<kbd>n</kbd>: Create worktree
<kbd>&lt;space&gt;</kbd>: Switch to worktree
<kbd>&lt;enter&gt;</kbd>: Switch to worktree
<kbd>o</kbd>: Open in editor
<kbd>d</kbd>: Remove worktree
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | New worktree | |
| `` <space> `` | Switch | Switch to the selected worktree. |
| `` o `` | Open in editor | |
| `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
| `` / `` | Filter the current view by text | |

View File

@@ -1,353 +1,369 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
# Lazygit Keybindings
# Lazygit Skróty klawiszowe
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
_Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
## Globalne
## Globalne skróty klawiszowe
<pre>
<kbd>&lt;c-r&gt;</kbd>: Switch to a recent repo
<kbd>&lt;pgup&gt;</kbd>: Scroll up main panel (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: Scroll down main panel (fn+down/shift+j)
<kbd>@</kbd>: Open command log menu
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
<kbd>:</kbd>: Wykonaj własną komendę
<kbd>&lt;c-p&gt;</kbd>: View custom patch options
<kbd>m</kbd>: Widok scalenia/opcje zmiany bazy
<kbd>R</kbd>: Odśwież
<kbd>+</kbd>: Next screen mode (normal/half/fullscreen)
<kbd>_</kbd>: Prev screen mode
<kbd>?</kbd>: Open menu
<kbd>&lt;c-s&gt;</kbd>: View filter-by-path options
<kbd>W</kbd>: Open diff menu
<kbd>&lt;c-e&gt;</kbd>: Open diff menu
<kbd>&lt;c-w&gt;</kbd>: Toggle whether or not whitespace changes are shown in the diff view
<kbd>z</kbd>: Undo
<kbd>&lt;c-z&gt;</kbd>: Redo
<kbd>P</kbd>: Push
<kbd>p</kbd>: Pull
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | Przełącz na ostatnie repozytorium | |
| `` <pgup> (fn+up/shift+k) `` | Przewiń główne okno w górę | |
| `` <pgdown> (fn+down/shift+j) `` | Przewiń główne okno w dół | |
| `` @ `` | Pokaż opcje dziennika poleceń | Pokaż opcje dla dziennika poleceń, np. pokazywanie/ukrywanie dziennika poleceń i skupienie na dzienniku poleceń. |
| `` P `` | Wypchnij | Wypchnij bieżącą gałąź do jej gałęzi nadrzędnej. Jeśli nie skonfigurowano gałęzi nadrzędnej, zostaniesz poproszony o skonfigurowanie gałęzi nadrzędnej. |
| `` p `` | Pociągnij | Pociągnij zmiany z zdalnego dla bieżącej gałęzi. Jeśli nie skonfigurowano gałęzi nadrzędnej, zostaniesz poproszony o skonfigurowanie gałęzi nadrzędnej. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | Zwiększ rozmiar kontekstu w widoku różnic | Zwiększ ilość kontekstu pokazywanego wokół zmian w widoku różnic. |
| `` { `` | Zmniejsz rozmiar kontekstu w widoku różnic | Zmniejsz ilość kontekstu pokazywanego wokół zmian w widoku różnic. |
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
| `` <c-p> `` | Wyświetl opcje niestandardowej łatki | |
| `` m `` | Pokaż opcje scalania/rebase | Pokaż opcje do przerwania/kontynuowania/pominięcia bieżącego scalania/rebase. |
| `` R `` | Odśwież | Odśwież stan git (tj. uruchom `git status`, `git branch`, itp. w tle, aby zaktualizować zawartość paneli). To nie uruchamia `git fetch`. |
| `` + `` | Następny tryb ekranu (normalny/półpełny/pełnoekranowy) | |
| `` _ `` | Poprzedni tryb ekranu | |
| `` ? `` | Otwórz menu przypisań klawiszy | |
| `` <c-s> `` | Pokaż opcje filtrowania | Pokaż opcje filtrowania dziennika commitów, tak aby pokazywane były tylko commity pasujące do filtra. |
| `` W `` | Pokaż opcje różnicowania | Pokaż opcje dotyczące różnicowania dwóch refów, np. różnicowanie względem wybranego refa, wprowadzanie refa do różnicowania i odwracanie kierunku różnic. |
| `` <c-e> `` | Pokaż opcje różnicowania | Pokaż opcje dotyczące różnicowania dwóch refów, np. różnicowanie względem wybranego refa, wprowadzanie refa do różnicowania i odwracanie kierunku różnic. |
| `` q `` | Wyjdź | |
| `` <esc> `` | Anuluj | |
| `` <c-w> `` | Przełącz białe znaki | Przełącz czy zmiany białych znaków są pokazywane w widoku różnic. |
| `` z `` | Cofnij | Dziennik reflog zostanie użyty do określenia, jakie polecenie git należy uruchomić, aby cofnąć ostatnie polecenie git. Nie obejmuje to zmian w drzewie roboczym; brane są pod uwagę tylko commity. |
| `` <c-z> `` | Ponów | Dziennik reflog zostanie użyty do określenia, jakie polecenie git należy uruchomić, aby ponowić ostatnie polecenie git. Nie obejmuje to zmian w drzewie roboczym; brane są pod uwagę tylko commity. |
## List panel navigation
## Nawigacja panelu listy
<pre>
<kbd>,</kbd>: Previous page
<kbd>.</kbd>: Next page
<kbd>&lt;</kbd>: Scroll to top
<kbd>&gt;</kbd>: Scroll to bottom
<kbd>/</kbd>: Search the current view by text
<kbd>H</kbd>: Scroll left
<kbd>L</kbd>: Scroll right
<kbd>]</kbd>: Next tab
<kbd>[</kbd>: Previous tab
</pre>
## Commit summary
<pre>
<kbd>&lt;enter&gt;</kbd>: Potwierdź
<kbd>&lt;esc&gt;</kbd>: Zamknij
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | Poprzednia strona | |
| `` . `` | Następna strona | |
| `` < `` | Przewiń do góry | |
| `` > `` | Przewiń do dołu | |
| `` v `` | Przełącz zaznaczenie zakresu | |
| `` <s-down> `` | Zaznacz zakres w dół | |
| `` <s-up> `` | Zaznacz zakres w górę | |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
| `` H `` | Przewiń w lewo | |
| `` L `` | Przewiń w prawo | |
| `` ] `` | Następna zakładka | |
| `` [ `` | Poprzednia zakładka | |
## Commity
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>b</kbd>: View bisect options
<kbd>s</kbd>: Ściśnij
<kbd>f</kbd>: Napraw commit
<kbd>r</kbd>: Zmień nazwę commita
<kbd>R</kbd>: Zmień nazwę commita w edytorze
<kbd>d</kbd>: Usuń commit
<kbd>e</kbd>: Edytuj commit
<kbd>p</kbd>: Wybierz commit (podczas zmiany bazy)
<kbd>F</kbd>: Utwórz commit naprawczy dla tego commita
<kbd>S</kbd>: Spłaszcz wszystkie commity naprawcze powyżej zaznaczonych commitów (autosquash)
<kbd>&lt;c-j&gt;</kbd>: Przenieś commit 1 w dół
<kbd>&lt;c-k&gt;</kbd>: Przenieś commit 1 w górę
<kbd>v</kbd>: Wklej commity (przebieranie)
<kbd>B</kbd>: Mark commit as base commit for rebase
<kbd>A</kbd>: Popraw commit zmianami z poczekalni
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: Odwróć commit
<kbd>T</kbd>: Tag commit
<kbd>&lt;c-l&gt;</kbd>: Open log menu
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Create new branch off of commit
<kbd>g</kbd>: Wyświetl opcje resetu
<kbd>c</kbd>: Kopiuj commit (przebieranie)
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
<kbd>&lt;enter&gt;</kbd>: Przeglądaj pliki commita
<kbd>/</kbd>: Search the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj hash commita do schowka | |
| `` <c-r> `` | Resetuj wybrane (cherry-picked) commity | |
| `` b `` | Zobacz opcje bisect | |
| `` s `` | Scal | Scal wybrany commit z commitami poniżej. Wiadomość wybranego commita zostanie dołączona do commita poniżej. |
| `` f `` | Poprawka | Włącz wybrany commit do commita poniżej. Podobnie do fixup, ale wiadomość wybranego commita zostanie odrzucona. |
| `` r `` | Przeformułuj | Przeformułuj wiadomość wybranego commita. |
| `` R `` | Przeformułuj za pomocą edytora | |
| `` d `` | Usuń | Usuń wybrany commit. To usunie commit z gałęzi za pomocą rebazowania. Jeśli commit wprowadza zmiany, od których zależą późniejsze commity, być może będziesz musiał rozwiązać konflikty scalania. |
| `` e `` | Edytuj (rozpocznij interaktywne rebazowanie) | Edytuj wybrany commit. Użyj tego, aby rozpocząć interaktywne rebazowanie od wybranego commita. Podczas trwania rebazowania, to oznaczy wybrany commit do edycji, co oznacza, że po kontynuacji rebazowania, rebazowanie zostanie wstrzymane na wybranym commicie, aby umożliwić wprowadzenie zmian. |
| `` i `` | Rozpocznij interaktywny rebase | Rozpocznij interaktywny rebase dla commitów na twoim branchu. To będzie zawierać wszystkie commity od HEAD do pierwszego commita scalenia lub commita głównego brancha.
Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita, naciśnij `e`. |
| `` p `` | Wybierz | Oznacz wybrany commit do wybrania (podczas rebazowania). Oznacza to, że commit zostanie zachowany po kontynuacji rebazowania. |
| `` F `` | Utwórz commit fixup | Utwórz commit 'fixup!' dla wybranego commita. Później możesz nacisnąć `S` na tym samym commicie, aby zastosować wszystkie powyższe commity fixup. |
| `` S `` | Zastosuj commity fixup | Scal wszystkie commity 'fixup!', albo powyżej wybranego commita, albo wszystkie w bieżącej gałęzi (autosquash). |
| `` <c-j> `` | Przesuń commit w dół | |
| `` <c-k> `` | Przesuń commit w górę | |
| `` V `` | Wklej (cherry-pick) | |
| `` B `` | Oznacz jako bazowy commit dla rebase | Wybierz bazowy commit dla następnego rebase. Kiedy robisz rebase na branch, tylko commity powyżej bazowego commita zostaną przeniesione. Używa to polecenia `git rebase --onto`. |
| `` A `` | Popraw | Popraw commit ze zmianami zatwierdzonymi. Jeśli wybrany commit jest commit HEAD, to wykona `git commit --amend`. W przeciwnym razie commit zostanie poprawiony za pomocą rebazowania. |
| `` a `` | Popraw atrybut commita | Ustaw/Resetuj autora commita lub ustaw współautora. |
| `` t `` | Cofnij | Utwórz commit cofający dla wybranego commita, który stosuje zmiany wybranego commita w odwrotnej kolejności. |
| `` T `` | Otaguj commit | Utwórz nowy tag wskazujący na wybrany commit. Zostaniesz poproszony o wprowadzenie nazwy tagu i opcjonalnego opisu. |
| `` <c-l> `` | Zobacz opcje logów | Zobacz opcje dla logów commitów, np. zmiana kolejności sortowania, ukrywanie grafu gita, pokazywanie całego grafu gita. |
| `` <space> `` | Przełącz | Przełącz wybrany commit jako odłączoną HEAD. |
| `` y `` | Kopiuj atrybut commita do schowka | Kopiuj atrybut commita do schowka (np. hash, URL, różnice, wiadomość, autor). |
| `` o `` | Otwórz commit w przeglądarce | |
| `` n `` | Utwórz nową gałąź z commita | |
| `` g `` | Reset | Wyświetl opcje resetu (miękki/mieszany/twardy) do wybranego elementu. |
| `` C `` | Kopiuj (cherry-pick) | Oznacz commit jako skopiowany. Następnie, w widoku lokalnych commitów, możesz nacisnąć `V`, aby wkleić (cherry-pick) skopiowane commity do sprawdzonej gałęzi. W dowolnym momencie możesz nacisnąć `<esc>`, aby anulować zaznaczenie. |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` <enter> `` | Wyświetl pliki | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
## Confirmation panel
## Drzewa pracy
<pre>
<kbd>&lt;enter&gt;</kbd>: Potwierdź
<kbd>&lt;esc&gt;</kbd>: Zamknij
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | Nowe drzewo pracy | |
| `` <space> `` | Przełącz | Przełącz do wybranego drzewa pracy. |
| `` o `` | Otwórz w edytorze | |
| `` d `` | Usuń | Usuń wybrane drzewo pracy. To usunie zarówno katalog drzewa pracy, jak i metadane o drzewie pracy w katalogu .git. |
| `` / `` | Filtruj bieżący widok po tekście | |
## Local branches
## Główny panel (budowanie łatki)
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy branch name to clipboard
<kbd>i</kbd>: Show git-flow options
<kbd>&lt;space&gt;</kbd>: Przełącz
<kbd>n</kbd>: Nowa gałąź
<kbd>o</kbd>: Utwórz żądanie pobrania
<kbd>O</kbd>: Utwórz opcje żądania ściągnięcia
<kbd>&lt;c-y&gt;</kbd>: Skopiuj adres URL żądania pobrania do schowka
<kbd>c</kbd>: Przełącz używając nazwy
<kbd>F</kbd>: Wymuś przełączenie
<kbd>d</kbd>: Usuń gałąź
<kbd>r</kbd>: Zmiana bazy gałęzi
<kbd>M</kbd>: Scal do obecnej gałęzi
<kbd>f</kbd>: Fast-forward this branch from its upstream
<kbd>T</kbd>: Create tag
<kbd>g</kbd>: Wyświetl opcje resetu
<kbd>R</kbd>: Rename branch
<kbd>u</kbd>: Set/Unset upstream
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Idź do poprzedniego fragmentu | |
| `` <right> `` | Idź do następnego fragmentu | |
| `` v `` | Przełącz zaznaczenie zakresu | |
| `` a `` | Zaznacz fragment | Przełącz tryb zaznaczania fragmentu. |
| `` <c-o> `` | Kopiuj zaznaczony tekst do schowka | |
| `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. |
| `` e `` | Edytuj plik | Otwórz plik w zewnętrznym edytorze. |
| `` <space> `` | Przełącz linie w łatce | |
| `` <esc> `` | Wyjdź z budowniczego niestandardowej łatki | |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
## Main panel (patch building)
## Lokalne gałęzie
<pre>
<kbd>&lt;left&gt;</kbd>: Poprzedni kawałek
<kbd>&lt;right&gt;</kbd>: Następny kawałek
<kbd>v</kbd>: Toggle drag select
<kbd>V</kbd>: Toggle drag select
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Otwórz plik
<kbd>e</kbd>: Edytuj plik
<kbd>&lt;space&gt;</kbd>: Add/Remove line(s) to patch
<kbd>&lt;esc&gt;</kbd>: Wyście z trybu "linia po linii"
<kbd>/</kbd>: Search the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj nazwę gałęzi do schowka | |
| `` i `` | Pokaż opcje git-flow | |
| `` <space> `` | Przełącz | Przełącz wybrany element. |
| `` n `` | Nowa gałąź | |
| `` o `` | Utwórz żądanie ściągnięcia | |
| `` O `` | Zobacz opcje tworzenia pull requesta | |
| `` <c-y> `` | Kopiuj adres URL żądania ściągnięcia do schowka | |
| `` c `` | Przełącz według nazwy | Przełącz według nazwy. W polu wprowadzania możesz wpisać '-' aby przełączyć się na ostatnią gałąź. |
| `` F `` | Wymuś przełączenie | Wymuś przełączenie wybranej gałęzi. To spowoduje odrzucenie wszystkich lokalnych zmian w drzewie roboczym przed przełączeniem na wybraną gałąź. |
| `` d `` | Usuń | Wyświetl opcje usuwania lokalnej/odległej gałęzi. |
| `` r `` | Przebazuj | Przebazuj przełączoną gałąź na wybraną gałąź. |
| `` M `` | Scal | Scal wybraną gałąź z aktualnie sprawdzoną gałęzią. |
| `` f `` | Szybkie przewijanie | Szybkie przewijanie wybranej gałęzi z jej źródła. |
| `` T `` | Nowy tag | |
| `` s `` | Kolejność sortowania | |
| `` g `` | Reset | |
| `` R `` | Zmień nazwę gałęzi | |
| `` u `` | Pokaż opcje upstream | Pokaż opcje dotyczące upstream gałęzi, np. ustawianie/usuwanie upstream i resetowanie do upstream. |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` <enter> `` | Pokaż commity | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |
## Menu
<pre>
<kbd>&lt;enter&gt;</kbd>: Wykonaj
<kbd>&lt;esc&gt;</kbd>: Zamknij
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Wykonaj | |
| `` <esc> `` | Zamknij | |
| `` / `` | Filtruj bieżący widok po tekście | |
## Panel główny (normalny)
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | Przewiń w dół | |
| `` mouse wheel up (fn+down) `` | Przewiń w górę | |
## Panel główny (scalanie)
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Wybierz fragment | |
| `` b `` | Wybierz wszystkie fragmenty | |
| `` <up> `` | Poprzedni fragment | |
| `` <down> `` | Następny fragment | |
| `` <left> `` | Poprzedni konflikt | |
| `` <right> `` | Następny konflikt | |
| `` z `` | Cofnij | Cofnij ostatnie rozwiązanie konfliktu scalania. |
| `` e `` | Edytuj plik | Otwórz plik w zewnętrznym edytorze. |
| `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. |
| `` M `` | Otwórz zewnętrzne narzędzie scalania | Uruchom `git mergetool`. |
| `` <esc> `` | Wróć do panelu plików | |
## Panel główny (zatwierdzanie)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Idź do poprzedniego fragmentu | |
| `` <right> `` | Idź do następnego fragmentu | |
| `` v `` | Przełącz zaznaczenie zakresu | |
| `` a `` | Zaznacz fragment | Przełącz tryb zaznaczania fragmentu. |
| `` <c-o> `` | Kopiuj zaznaczony tekst do schowka | |
| `` <space> `` | Zatwierdź | Przełącz zaznaczenie zatwierdzone/niezatwierdzone. |
| `` d `` | Odrzuć | Gdy zaznaczona jest niezatwierdzona zmiana, odrzuć ją używając `git reset`. Gdy zaznaczona jest zatwierdzona zmiana, cofnij zatwierdzenie. |
| `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. |
| `` e `` | Edytuj plik | Otwórz plik w zewnętrznym edytorze. |
| `` <esc> `` | Wróć do panelu plików | |
| `` <tab> `` | Przełącz widok | Przełącz na inny widok (zatwierdzone/niezatwierdzone zmiany). |
| `` E `` | Edytuj fragment | Edytuj wybrany fragment w zewnętrznym edytorze. |
| `` c `` | Commit | Zatwierdź zmiany zatwierdzone. |
| `` w `` | Zatwierdź zmiany bez hooka pre-commit | |
| `` C `` | Zatwierdź zmiany używając edytora git | |
| `` <c-f> `` | Znajdź bazowy commit do poprawki | Znajdź commit, na którym opierają się Twoje obecne zmiany, w celu poprawienia/zmiany commita. To pozwala Ci uniknąć przeglądania commitów w Twojej gałęzi jeden po drugim, aby zobaczyć, który commit powinien być poprawiony/zmieniony. Zobacz dokumentację: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
## Panel potwierdzenia
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Potwierdź | |
| `` <esc> `` | Zamknij/Anuluj | |
## Pliki
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy the file name to the clipboard
<kbd>d</kbd>: Pokaż opcje porzucania zmian
<kbd>&lt;space&gt;</kbd>: Przełącz stan poczekalni
<kbd>&lt;c-b&gt;</kbd>: Filter files by status
<kbd>c</kbd>: Zatwierdź zmiany
<kbd>w</kbd>: Zatwierdź zmiany bez skryptu pre-commit
<kbd>A</kbd>: Zmień ostatni commit
<kbd>C</kbd>: Zatwierdź zmiany używając edytora
<kbd>e</kbd>: Edytuj plik
<kbd>o</kbd>: Otwórz plik
<kbd>i</kbd>: Ignore or exclude file
<kbd>r</kbd>: Odśwież pliki
<kbd>s</kbd>: Przechowaj zmiany
<kbd>S</kbd>: Wyświetl opcje schowka
<kbd>a</kbd>: Przełącz stan poczekalni wszystkich
<kbd>&lt;enter&gt;</kbd>: Zatwierdź pojedyncze linie
<kbd>g</kbd>: View upstream reset options
<kbd>D</kbd>: Wyświetl opcje resetu
<kbd>`</kbd>: Toggle file tree view
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>f</kbd>: Pobierz
<kbd>/</kbd>: Search the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj ścieżkę do schowka | |
| `` <space> `` | Zatwierdź | Przełącz zatwierdzenie dla wybranego pliku. |
| `` <c-b> `` | Filtruj pliki według statusu | |
| `` y `` | Kopiuj do schowka | |
| `` c `` | Commit | Zatwierdź zmiany zatwierdzone. |
| `` w `` | Zatwierdź zmiany bez hooka pre-commit | |
| `` A `` | Popraw ostatni commit | |
| `` C `` | Zatwierdź zmiany używając edytora git | |
| `` <c-f> `` | Znajdź bazowy commit do poprawki | Znajdź commit, na którym opierają się Twoje obecne zmiany, w celu poprawienia/zmiany commita. To pozwala Ci uniknąć przeglądania commitów w Twojej gałęzi jeden po drugim, aby zobaczyć, który commit powinien być poprawiony/zmieniony. Zobacz dokumentację: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | Edytuj | Otwórz plik w zewnętrznym edytorze. |
| `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. |
| `` i `` | Ignoruj lub wyklucz plik | |
| `` r `` | Odśwież pliki | |
| `` s `` | Schowaj | Schowaj wszystkie zmiany. Dla innych wariantów schowania, użyj klawisza wyświetlania opcji schowka. |
| `` S `` | Wyświetl opcje schowka | Wyświetl opcje schowka (np. schowaj wszystko, schowaj zatwierdzone, schowaj niezatwierdzone). |
| `` a `` | Zatwierdź wszystko | Przełącz zatwierdzenie/odznaczenie dla wszystkich plików w drzewie roboczym. |
| `` <enter> `` | Zatwierdź linie / Zwiń katalog | Jeśli wybrany element jest plikiem, skup się na widoku zatwierdzania, aby móc zatwierdzać poszczególne fragmenty/linie. Jeśli wybrany element jest katalogiem, zwiń/rozwiń go. |
| `` d `` | Odrzuć | Wyświetl opcje odrzucania zmian w wybranym pliku. |
| `` g `` | Pokaż opcje resetowania do upstream | |
| `` D `` | Reset | Wyświetl opcje resetu dla drzewa roboczego (np. zniszczenie drzewa roboczego). |
| `` ` `` | Przełącz widok drzewa plików | Przełącz widok plików między płaskim a drzewem. Płaski układ pokazuje wszystkie ścieżki plików na jednej liście, układ drzewa grupuje pliki według katalogów. |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` M `` | Otwórz zewnętrzne narzędzie scalania | Uruchom `git mergetool`. |
| `` f `` | Pobierz | Pobierz zmiany ze zdalnego serwera. |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
## Pliki commita
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy the committed file name to the clipboard
<kbd>c</kbd>: Plik wybierania
<kbd>d</kbd>: Porzuć zmiany commita dla tego pliku
<kbd>o</kbd>: Otwórz plik
<kbd>e</kbd>: Edytuj plik
<kbd>&lt;space&gt;</kbd>: Toggle file included in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>`</kbd>: Toggle file tree view
<kbd>/</kbd>: Search the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj ścieżkę do schowka | |
| `` c `` | Przełącz | Przełącz plik. Zastępuje plik w twoim drzewie roboczym wersją z wybranego commita. |
| `` d `` | Usuń | Odrzuć zmiany w tym pliku z tego commita. Uruchamia interaktywny rebase w tle, więc możesz otrzymać konflikt scalania, jeśli późniejszy commit również zmienia ten plik. |
| `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. |
| `` e `` | Edytuj | Otwórz plik w zewnętrznym edytorze. |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` <space> `` | Przełącz plik włączony w łatkę | Przełącz, czy plik jest włączony w niestandardową łatkę. Zobacz https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` a `` | Przełącz wszystkie pliki | Dodaj/usuń wszystkie pliki commita do niestandardowej łatki. Zobacz https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` <enter> `` | Wejdź do pliku / Przełącz zwiń katalog | Jeśli plik jest wybrany, wejdź do pliku, aby móc dodawać/usuwać poszczególne linie do niestandardowej łatki. Jeśli wybrany jest katalog, przełącz katalog. |
| `` ` `` | Przełącz widok drzewa plików | Przełącz widok plików między płaskim a drzewem. Płaski układ pokazuje wszystkie ścieżki plików na jednej liście, układ drzewa grupuje pliki według katalogów. |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
## Poczekalnia
## Podsumowanie commita
<pre>
<kbd>&lt;left&gt;</kbd>: Poprzedni kawałek
<kbd>&lt;right&gt;</kbd>: Następny kawałek
<kbd>v</kbd>: Toggle drag select
<kbd>V</kbd>: Toggle drag select
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Otwórz plik
<kbd>e</kbd>: Edytuj plik
<kbd>&lt;esc&gt;</kbd>: Wróć do panelu plików
<kbd>&lt;tab&gt;</kbd>: Switch to other panel (staged/unstaged changes)
<kbd>&lt;space&gt;</kbd>: Toggle line staged / unstaged
<kbd>d</kbd>: Discard change (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: Zatwierdź zmiany
<kbd>w</kbd>: Zatwierdź zmiany bez skryptu pre-commit
<kbd>C</kbd>: Zatwierdź zmiany używając edytora
<kbd>/</kbd>: Search the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Potwierdź | |
| `` <esc> `` | Zamknij | |
## Reflog
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Create new branch off of commit
<kbd>g</kbd>: Wyświetl opcje resetu
<kbd>c</kbd>: Kopiuj commit (przebieranie)
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
## Remote branches
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy branch name to clipboard
<kbd>&lt;space&gt;</kbd>: Przełącz
<kbd>n</kbd>: Nowa gałąź
<kbd>M</kbd>: Scal do obecnej gałęzi
<kbd>r</kbd>: Zmiana bazy gałęzi
<kbd>d</kbd>: Usuń gałąź
<kbd>u</kbd>: Set as upstream of checked-out branch
<kbd>g</kbd>: Wyświetl opcje resetu
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
## Remotes
<pre>
<kbd>f</kbd>: Fetch remote
<kbd>n</kbd>: Add new remote
<kbd>d</kbd>: Remove remote
<kbd>e</kbd>: Edit remote
<kbd>/</kbd>: Filter the current view by text
</pre>
## Scalanie
<pre>
<kbd>e</kbd>: Edytuj plik
<kbd>o</kbd>: Otwórz plik
<kbd>&lt;left&gt;</kbd>: Poprzedni konflikt
<kbd>&lt;right&gt;</kbd>: Następny konflikt
<kbd>&lt;up&gt;</kbd>: Wybierz poprzedni kawałek
<kbd>&lt;down&gt;</kbd>: Wybierz następny kawałek
<kbd>z</kbd>: Cofnij
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>&lt;space&gt;</kbd>: Wybierz kawałek
<kbd>b</kbd>: Wybierz oba kawałki
<kbd>&lt;esc&gt;</kbd>: Wróć do panelu plików
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj hash commita do schowka | |
| `` <space> `` | Przełącz | Przełącz wybrany commit jako odłączoną HEAD. |
| `` y `` | Kopiuj atrybut commita do schowka | Kopiuj atrybut commita do schowka (np. hash, URL, różnice, wiadomość, autor). |
| `` o `` | Otwórz commit w przeglądarce | |
| `` n `` | Utwórz nową gałąź z commita | |
| `` g `` | Reset | Wyświetl opcje resetu (miękki/mieszany/twardy) do wybranego elementu. |
| `` C `` | Kopiuj (cherry-pick) | Oznacz commit jako skopiowany. Następnie, w widoku lokalnych commitów, możesz nacisnąć `V`, aby wkleić (cherry-pick) skopiowane commity do sprawdzonej gałęzi. W dowolnym momencie możesz nacisnąć `<esc>`, aby anulować zaznaczenie. |
| `` <c-r> `` | Resetuj wybrane (cherry-picked) commity | |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` <enter> `` | Pokaż commity | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |
## Schowek
<pre>
<kbd>&lt;space&gt;</kbd>: Zastosuj
<kbd>g</kbd>: Wyciągnij
<kbd>d</kbd>: Porzuć
<kbd>n</kbd>: Nowa gałąź
<kbd>r</kbd>: Rename stash
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: Przeglądaj pliki commita
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Zastosuj | Zastosuj wpis schowka do katalogu roboczego. |
| `` g `` | Wyciągnij | Zastosuj wpis schowka do katalogu roboczego i usuń wpis schowka. |
| `` d `` | Usuń | Usuń wpis schowka z listy schowka. |
| `` n `` | Nowa gałąź | Utwórz nową gałąź z wybranego wpisu schowka. Działa poprzez przełączenie git na commit, na którym wpis schowka został utworzony, tworzenie nowej gałęzi z tego commita, a następnie zastosowanie wpisu schowka do nowej gałęzi jako dodatkowego commita. |
| `` r `` | Zmień nazwę schowka | |
| `` <enter> `` | Wyświetl pliki | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |
## Status
<pre>
<kbd>o</kbd>: Otwórz konfigurację
<kbd>e</kbd>: Edytuj konfigurację
<kbd>u</kbd>: Sprawdź aktualizacje
<kbd>&lt;enter&gt;</kbd>: Switch to a recent repo
<kbd>a</kbd>: Pokaż wszystkie logi gałęzi
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | Otwórz plik konfiguracyjny | Otwórz plik w domyślnej aplikacji. |
| `` e `` | Edytuj plik konfiguracyjny | Otwórz plik w zewnętrznym edytorze. |
| `` u `` | Sprawdź aktualizacje | |
| `` <enter> `` | Przełącz na ostatnie repozytorium | |
| `` a `` | Pokaż wszystkie gałęzie w logach | |
## Sub-commits
## Sub-commity
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: Checkout commit
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: Open commit in browser
<kbd>n</kbd>: Create new branch off of commit
<kbd>g</kbd>: Wyświetl opcje resetu
<kbd>c</kbd>: Kopiuj commit (przebieranie)
<kbd>C</kbd>: Kopiuj zakres commitów (przebieranie)
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: Przeglądaj pliki commita
<kbd>/</kbd>: Search the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj hash commita do schowka | |
| `` <space> `` | Przełącz | Przełącz wybrany commit jako odłączoną HEAD. |
| `` y `` | Kopiuj atrybut commita do schowka | Kopiuj atrybut commita do schowka (np. hash, URL, różnice, wiadomość, autor). |
| `` o `` | Otwórz commit w przeglądarce | |
| `` n `` | Utwórz nową gałąź z commita | |
| `` g `` | Reset | Wyświetl opcje resetu (miękki/mieszany/twardy) do wybranego elementu. |
| `` C `` | Kopiuj (cherry-pick) | Oznacz commit jako skopiowany. Następnie, w widoku lokalnych commitów, możesz nacisnąć `V`, aby wkleić (cherry-pick) skopiowane commity do sprawdzonej gałęzi. W dowolnym momencie możesz nacisnąć `<esc>`, aby anulować zaznaczenie. |
| `` <c-r> `` | Resetuj wybrane (cherry-picked) commity | |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` <enter> `` | Wyświetl pliki | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Szukaj w bieżącym widoku po tekście | |
## Submodules
## Submoduły
<pre>
<kbd>&lt;c-o&gt;</kbd>: Copy submodule name to clipboard
<kbd>&lt;enter&gt;</kbd>: Enter submodule
<kbd>&lt;space&gt;</kbd>: Enter submodule
<kbd>d</kbd>: Remove submodule
<kbd>u</kbd>: Update submodule
<kbd>n</kbd>: Add new submodule
<kbd>e</kbd>: Update submodule URL
<kbd>i</kbd>: Initialize submodule
<kbd>b</kbd>: View bulk submodule options
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj nazwę submodułu do schowka | |
| `` <enter> `` | Wejdź | Wejdź do submodułu. Po wejściu do submodułu możesz nacisnąć `<esc>`, aby wrócić do repozytorium nadrzędnego. |
| `` d `` | Usuń | Usuń wybrany submoduł i odpowiadający mu katalog. |
| `` u `` | Aktualizuj | Aktualizuj wybrany submoduł. |
| `` n `` | Nowy submoduł | |
| `` e `` | Zaktualizuj URL submodułu | |
| `` i `` | Zainicjuj | Zainicjuj wybrany submoduł, aby przygotować do pobrania. Prawdopodobnie chcesz to kontynuować, wywołując akcję 'update', aby pobrać submoduł. |
| `` b `` | Pokaż opcje masowych operacji na submodułach | |
| `` / `` | Filtruj bieżący widok po tekście | |
## Tags
## Tagi
<pre>
<kbd>&lt;space&gt;</kbd>: Przełącz
<kbd>d</kbd>: Delete tag
<kbd>P</kbd>: Push tag
<kbd>n</kbd>: Create tag
<kbd>g</kbd>: Wyświetl opcje resetu
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: View commits
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Przełącz | Przełącz wybrany tag jako odłączoną głowę (detached HEAD). |
| `` n `` | Nowy tag | Utwórz nowy tag z bieżącego commita. Zostaniesz poproszony o wprowadzenie nazwy tagu i opcjonalnego opisu. |
| `` d `` | Usuń | Wyświetl opcje usuwania lokalnego/odległego tagu. |
| `` P `` | Wyślij tag | Wyślij wybrany tag do zdalnego. Zostaniesz poproszony o wybranie zdalnego. |
| `` g `` | Reset | Wyświetl opcje resetu (miękki/mieszany/twardy) do wybranego elementu. |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` <enter> `` | Pokaż commity | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |
## Worktrees
## Zdalne
<pre>
<kbd>n</kbd>: Create worktree
<kbd>&lt;space&gt;</kbd>: Switch to worktree
<kbd>&lt;enter&gt;</kbd>: Switch to worktree
<kbd>o</kbd>: Open in editor
<kbd>d</kbd>: Remove worktree
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Wyświetl gałęzie | |
| `` n `` | Nowy zdalny | |
| `` d `` | Usuń | Usuń wybrany zdalny. Wszelkie lokalne gałęzie śledzące gałąź zdalną z tego zdalnego nie zostaną dotknięte. |
| `` e `` | Edytuj | Edytuj nazwę lub URL wybranego zdalnego. |
| `` f `` | Pobierz | Pobierz aktualizacje z zdalnego repozytorium. Pobiera nowe commity i gałęzie bez scalania ich z lokalnymi gałęziami. |
| `` / `` | Filtruj bieżący widok po tekście | |
## Zwykłe
## Zdalne gałęzie
<pre>
<kbd>mouse wheel down</kbd>: Przewiń w dół (fn+up)
<kbd>mouse wheel up</kbd>: Przewiń w górę (fn+down)
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Kopiuj nazwę gałęzi do schowka | |
| `` <space> `` | Przełącz | Przełącz na nową lokalną gałąź na podstawie wybranej gałęzi zdalnej. Nowa gałąź będzie śledzić gałąź zdalną. |
| `` n `` | Nowa gałąź | |
| `` M `` | Scal | Scal wybraną gałąź z aktualnie sprawdzoną gałęzią. |
| `` r `` | Przebazuj | Przebazuj przełączoną gałąź na wybraną gałąź. |
| `` d `` | Usuń | Usuń gałąź zdalną ze zdalnego. |
| `` u `` | Ustaw jako upstream | Ustaw wybraną gałąź zdalną jako upstream sprawdzonej gałęzi. |
| `` s `` | Kolejność sortowania | |
| `` g `` | Reset | Wyświetl opcje resetu (miękki/mieszany/twardy) do wybranego elementu. |
| `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) | |
| `` <enter> `` | Pokaż commity | |
| `` w `` | Zobacz opcje drzewa pracy | |
| `` / `` | Filtruj bieżący widok po tekście | |

View File

@@ -1,4 +1,4 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
# Lazygit Связки клавиш
@@ -6,348 +6,364 @@ _Связки клавиш_
## Глобальные сочетания клавиш
<pre>
<kbd>&lt;c-r&gt;</kbd>: Переключиться на последний репозиторий
<kbd>&lt;pgup&gt;</kbd>: Прокрутить вверх главную панель (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: Прокрутить вниз главную панель (fn+down/shift+j)
<kbd>@</kbd>: Открыть меню журнала команд
<kbd>}</kbd>: Увеличить размер контекста, отображаемого вокруг изменений в просмотрщике сравнении
<kbd>{</kbd>: Уменьшите размер контекста, отображаемого вокруг изменений в просмотрщике сравнении
<kbd>:</kbd>: Выполнить пользовательскую команду
<kbd>&lt;c-p&gt;</kbd>: Просмотреть пользовательские параметры патча
<kbd>m</kbd>: Просмотреть параметры слияния/перебазирования
<kbd>R</kbd>: Обновить
<kbd>+</kbd>: Следующий режим экрана (нормальный/полуэкранный/полноэкранный)
<kbd>_</kbd>: Предыдущий режим экрана
<kbd>?</kbd>: Открыть меню
<kbd>&lt;c-s&gt;</kbd>: Просмотреть параметры фильтрации по пути
<kbd>W</kbd>: Открыть меню сравнении
<kbd>&lt;c-e&gt;</kbd>: Открыть меню сравнении
<kbd>&lt;c-w&gt;</kbd>: Переключить отображение изменении пробелов в просмотрщике сравнении
<kbd>z</kbd>: Отменить (через reflog) (экспериментальный)
<kbd>&lt;c-z&gt;</kbd>: Повторить (через reflog) (экспериментальный)
<kbd>P</kbd>: Отправить изменения
<kbd>p</kbd>: Получить и слить изменения
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | Переключиться на последний репозиторий | |
| `` <pgup> (fn+up/shift+k) `` | Прокрутить вверх главную панель | |
| `` <pgdown> (fn+down/shift+j) `` | Прокрутить вниз главную панель | |
| `` @ `` | Открыть меню журнала команд | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | Отправить изменения | Push the current branch to its upstream branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` p `` | Получить и слить изменения | Pull changes from the remote for the current branch. If no upstream is configured, you will be prompted to configure an upstream branch. |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | Увеличить размер контекста, отображаемого вокруг изменений в просмотрщике сравнении | Increase the amount of the context shown around changes in the diff view. |
| `` { `` | Уменьшите размер контекста, отображаемого вокруг изменений в просмотрщике сравнении | Decrease the amount of the context shown around changes in the diff view. |
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
| `` <c-p> `` | Просмотреть пользовательские параметры патча | |
| `` m `` | Просмотреть параметры слияния/перебазирования | View options to abort/continue/skip the current merge/rebase. |
| `` R `` | Обновить | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | Следующий режим экрана (нормальный/полуэкранный/полноэкранный) | |
| `` _ `` | Предыдущий режим экрана | |
| `` ? `` | Открыть меню | |
| `` <c-s> `` | Просмотреть параметры фильтрации по пути | View options for filtering the commit log, so that only commits matching the filter are shown. |
| `` W `` | Открыть меню сравнении | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` <c-e> `` | Открыть меню сравнении | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` q `` | Выйти | |
| `` <esc> `` | Отменить | |
| `` <c-w> `` | Переключить отображение изменении пробелов в просмотрщике сравнении | Toggle whether or not whitespace changes are shown in the diff view. |
| `` z `` | Отменить (через reflog) (экспериментальный) | Журнал ссылок (reflog) будет использоваться для определения того, какую команду git запустить, чтобы отменить последнюю команду git. Сюда не входят изменения в рабочем дереве; учитываются только коммиты. |
| `` <c-z> `` | Повторить (через reflog) (экспериментальный) | Журнал ссылок (reflog) будет использоваться для определения того, какую команду git нужно запустить, чтобы повторить последнюю команду git. Сюда не входят изменения в рабочем дереве; учитываются только коммиты. |
## Навигация по панели списка
<pre>
<kbd>,</kbd>: Предыдущая страница
<kbd>.</kbd>: Следующая страница
<kbd>&lt;</kbd>: Пролистать наверх
<kbd>&gt;</kbd>: Прокрутить вниз
<kbd>/</kbd>: Найти
<kbd>H</kbd>: Прокрутить влево
<kbd>L</kbd>: Прокрутить вправо
<kbd>]</kbd>: Следующая вкладка
<kbd>[</kbd>: Предыдущая вкладка
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | Предыдущая страница | |
| `` . `` | Следующая страница | |
| `` < `` | Пролистать наверх | |
| `` > `` | Прокрутить вниз | |
| `` v `` | Переключить выборку перетаскивания | |
| `` <s-down> `` | Range select down | |
| `` <s-up> `` | Range select up | |
| `` / `` | Найти | |
| `` H `` | Прокрутить влево | |
| `` L `` | Прокрутить вправо | |
| `` ] `` | Следующая вкладка | |
| `` [ `` | Предыдущая вкладка | |
## Worktrees
<pre>
<kbd>n</kbd>: Create worktree
<kbd>&lt;space&gt;</kbd>: Switch to worktree
<kbd>&lt;enter&gt;</kbd>: Switch to worktree
<kbd>o</kbd>: Open in editor
<kbd>d</kbd>: Remove worktree
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | New worktree | |
| `` <space> `` | Switch | Switch to the selected worktree. |
| `` o `` | Open in editor | |
| `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
| `` / `` | Filter the current view by text | |
## Главная панель (Индексирование)
<pre>
<kbd>&lt;left&gt;</kbd>: Выбрать предыдущую часть
<kbd>&lt;right&gt;</kbd>: Выбрать следующую часть
<kbd>v</kbd>: Переключить выборку перетаскивания
<kbd>V</kbd>: Переключить выборку перетаскивания
<kbd>a</kbd>: Переключить выборку частей
<kbd>&lt;c-o&gt;</kbd>: Скопировать выделенный текст в буфер обмена
<kbd>o</kbd>: Открыть файл
<kbd>e</kbd>: Редактировать файл
<kbd>&lt;esc&gt;</kbd>: Вернуться к панели файлов
<kbd>&lt;tab&gt;</kbd>: Переключиться на другую панель (проиндексированные/непроиндексированные изменения)
<kbd>&lt;space&gt;</kbd>: Переключить строку в проиндексированные / непроиндексированные
<kbd>d</kbd>: Отменить изменение (git reset)
<kbd>E</kbd>: Изменить эту часть
<kbd>c</kbd>: Сохранить изменения
<kbd>w</kbd>: Закоммитить изменения без предварительного хука коммита
<kbd>C</kbd>: Сохранить изменения с помощью редактора git
<kbd>/</kbd>: Найти
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Выбрать предыдущую часть | |
| `` <right> `` | Выбрать следующую часть | |
| `` v `` | Переключить выборку перетаскивания | |
| `` a `` | Переключить выборку частей | Toggle hunk selection mode. |
| `` <c-o> `` | Скопировать выделенный текст в буфер обмена | |
| `` <space> `` | Переключить индекс | Переключить строку в проиндексированные / непроиндексированные |
| `` d `` | Отменить изменение (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
| `` o `` | Открыть файл | Open file in default application. |
| `` e `` | Редактировать файл | Open file in external editor. |
| `` <esc> `` | Вернуться к панели файлов | |
| `` <tab> `` | Переключиться на другую панель (проиндексированные/непроиндексированные изменения) | Switch to other view (staged/unstaged changes). |
| `` E `` | Изменить эту часть | Edit selected hunk in external editor. |
| `` c `` | Сохранить изменения | Commit staged changes. |
| `` w `` | Закоммитить изменения без предварительного хука коммита | |
| `` C `` | Сохранить изменения с помощью редактора git | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | Найти | |
## Главная панель (Обычный)
<pre>
<kbd>mouse wheel down</kbd>: Прокрутить вниз (fn+up)
<kbd>mouse wheel up</kbd>: Прокрутить вверх (fn+down)
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | Прокрутить вниз | |
| `` mouse wheel up (fn+down) `` | Прокрутить вверх | |
## Главная панель (Слияние)
<pre>
<kbd>e</kbd>: Редактировать файл
<kbd>o</kbd>: Открыть файл
<kbd>&lt;left&gt;</kbd>: Выбрать предыдущий конфликт
<kbd>&lt;right&gt;</kbd>: Выбрать следующий конфликт
<kbd>&lt;up&gt;</kbd>: Выбрать предыдущую часть
<kbd>&lt;down&gt;</kbd>: Выбрать следующую часть
<kbd>z</kbd>: Отменить
<kbd>M</kbd>: Открыть внешний инструмент слияния (git mergetool)
<kbd>&lt;space&gt;</kbd>: Выбрать эту часть
<kbd>b</kbd>: Выбрать все части
<kbd>&lt;esc&gt;</kbd>: Вернуться к панели файлов
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Выбрать эту часть | |
| `` b `` | Выбрать все части | |
| `` <up> `` | Выбрать предыдущую часть | |
| `` <down> `` | Выбрать следующую часть | |
| `` <left> `` | Выбрать предыдущий конфликт | |
| `` <right> `` | Выбрать следующий конфликт | |
| `` z `` | Отменить | Undo last merge conflict resolution. |
| `` e `` | Редактировать файл | Open file in external editor. |
| `` o `` | Открыть файл | Open file in default application. |
| `` M `` | Открыть внешний инструмент слияния (git mergetool) | Run `git mergetool`. |
| `` <esc> `` | Вернуться к панели файлов | |
## Главная панель (сборка патчей)
<pre>
<kbd>&lt;left&gt;</kbd>: Выбрать предыдущую часть
<kbd>&lt;right&gt;</kbd>: Выбрать следующую часть
<kbd>v</kbd>: Переключить выборку перетаскивания
<kbd>V</kbd>: Переключить выборку перетаскивания
<kbd>a</kbd>: Переключить выборку частей
<kbd>&lt;c-o&gt;</kbd>: Скопировать выделенный текст в буфер обмена
<kbd>o</kbd>: Открыть файл
<kbd>e</kbd>: Редактировать файл
<kbd>&lt;space&gt;</kbd>: Добавить/удалить строку(и) для патча
<kbd>&lt;esc&gt;</kbd>: Выйти из сборщика пользовательских патчей
<kbd>/</kbd>: Найти
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | Выбрать предыдущую часть | |
| `` <right> `` | Выбрать следующую часть | |
| `` v `` | Переключить выборку перетаскивания | |
| `` a `` | Переключить выборку частей | Toggle hunk selection mode. |
| `` <c-o> `` | Скопировать выделенный текст в буфер обмена | |
| `` o `` | Открыть файл | Open file in default application. |
| `` e `` | Редактировать файл | Open file in external editor. |
| `` <space> `` | Добавить/удалить строку(и) для патча | |
| `` <esc> `` | Выйти из сборщика пользовательских патчей | |
| `` / `` | Найти | |
## Журнал ссылок (Reflog)
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать SHA коммита в буфер обмена
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: Переключить коммит
<kbd>y</kbd>: Скопировать атрибут коммита
<kbd>o</kbd>: Открыть коммит в браузере
<kbd>n</kbd>: Создать новую ветку с этого коммита
<kbd>g</kbd>: Просмотреть параметры сброса
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Сбросить отобранную (скопированную | cherry-picked) выборку коммитов
<kbd>&lt;enter&gt;</kbd>: Просмотреть коммиты
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
| `` <space> `` | Переключить | Checkout the selected commit as a detached HEAD. |
| `` y `` | Скопировать атрибут коммита | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Открыть коммит в браузере | |
| `` n `` | Создать новую ветку с этого коммита | |
| `` g `` | Просмотреть параметры сброса | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Скопировать отобранные коммит (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Сбросить отобранную (скопированную | cherry-picked) выборку коммитов | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Просмотреть коммиты | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Коммиты
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать SHA коммита в буфер обмена
<kbd>&lt;c-r&gt;</kbd>: Сбросить отобранную (скопированную | cherry-picked) выборку коммитов
<kbd>b</kbd>: Просмотреть параметры бинарного поиска
<kbd>s</kbd>: Объединить несколько коммитов в один нижний
<kbd>f</kbd>: Объединить несколько коммитов в один отбросив сообщение коммита
<kbd>r</kbd>: Перефразировать коммит
<kbd>R</kbd>: Переписать коммит с помощью редактора
<kbd>d</kbd>: Удалить коммит
<kbd>e</kbd>: Изменить коммит
<kbd>p</kbd>: Выбрать коммит (в середине перебазирования)
<kbd>F</kbd>: Создать fixup коммит для этого коммита
<kbd>S</kbd>: Объединить все 'fixup!' коммиты выше в выбранный коммит (автосохранение)
<kbd>&lt;c-j&gt;</kbd>: Переместить коммит вниз на один
<kbd>&lt;c-k&gt;</kbd>: Переместить коммит вверх на один
<kbd>v</kbd>: Вставить отобранные коммиты (cherry-pick)
<kbd>B</kbd>: Mark commit as base commit for rebase
<kbd>A</kbd>: Править последний коммит с проиндексированными изменениями
<kbd>a</kbd>: Установить/убрать автора коммита
<kbd>t</kbd>: Отменить коммит
<kbd>T</kbd>: Пометить коммит тегом
<kbd>&lt;c-l&gt;</kbd>: Открыть меню журнала
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: Переключить коммит
<kbd>y</kbd>: Скопировать атрибут коммита
<kbd>o</kbd>: Открыть коммит в браузере
<kbd>n</kbd>: Создать новую ветку с этого коммита
<kbd>g</kbd>: Просмотреть параметры сброса
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
<kbd>&lt;enter&gt;</kbd>: Просмотреть файлы выбранного элемента
<kbd>/</kbd>: Найти
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
| `` <c-r> `` | Сбросить отобранную (скопированную | cherry-picked) выборку коммитов | |
| `` b `` | Просмотреть параметры бинарного поиска | |
| `` s `` | Объединить коммиты (Squash) | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
| `` f `` | Объединить несколько коммитов в один отбросив сообщение коммита (Fixup) | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
| `` r `` | Перефразировать коммит | Reword the selected commit's message. |
| `` R `` | Переписать коммит с помощью редактора | |
| `` d `` | Удалить коммит | Drop the selected commit. This will remove the commit from the branch via a rebase. If the commit makes changes that later commits depend on, you may need to resolve merge conflicts. |
| `` e `` | Edit (start interactive rebase) | Изменить коммит |
| `` i `` | Start interactive rebase | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Выбрать коммит (в середине перебазирования) |
| `` F `` | Создать fixup коммит | Создать fixup коммит для этого коммита |
| `` S `` | Apply fixup commits | Объединить все 'fixup!' коммиты выше в выбранный коммит (автосохранение) |
| `` <c-j> `` | Переместить коммит вниз на один | |
| `` <c-k> `` | Переместить коммит вверх на один | |
| `` V `` | Вставить отобранные коммиты (cherry-pick) | |
| `` B `` | Mark as base commit for rebase | Select a base commit for the next rebase. When you rebase onto a branch, only commits above the base commit will be brought across. This uses the `git rebase --onto` command. |
| `` A `` | Amend | Править последний коммит с проиндексированными изменениями |
| `` a `` | Установить/убрать автора коммита | Set/Reset commit author or set co-author. |
| `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
| `` T `` | Пометить коммит тегом | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
| `` <c-l> `` | Открыть меню журнала | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
| `` <space> `` | Переключить | Checkout the selected commit as a detached HEAD. |
| `` y `` | Скопировать атрибут коммита | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Открыть коммит в браузере | |
| `` n `` | Создать новую ветку с этого коммита | |
| `` g `` | Просмотреть параметры сброса | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Скопировать отобранные коммит (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Просмотреть файлы выбранного элемента | |
| `` w `` | View worktree options | |
| `` / `` | Найти | |
## Локальные Ветки
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать название ветки в буфер обмена
<kbd>i</kbd>: Показать параметры git-flow
<kbd>&lt;space&gt;</kbd>: Переключить
<kbd>n</kbd>: Новая ветка
<kbd>o</kbd>: Создать запрос на принятие изменений
<kbd>O</kbd>: Создать параметры запроса принятие изменений
<kbd>&lt;c-y&gt;</kbd>: Скопировать URL запроса на принятие изменений в буфер обмена
<kbd>c</kbd>: Переключить по названию
<kbd>F</kbd>: Принудительное переключение
<kbd>d</kbd>: Удалить ветку
<kbd>r</kbd>: Перебазировать переключённую ветку на эту ветку
<kbd>M</kbd>: Слияние с текущей переключённой веткой
<kbd>f</kbd>: Перемотать эту ветку вперёд из её upstream-ветки
<kbd>T</kbd>: Создать тег
<kbd>g</kbd>: Просмотреть параметры сброса
<kbd>R</kbd>: Переименовать ветку
<kbd>u</kbd>: Установить/убрать upstream-ветку
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: Просмотреть коммиты
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать название ветки в буфер обмена | |
| `` i `` | Показать параметры git-flow | |
| `` <space> `` | Переключить | Checkout selected item. |
| `` n `` | Новая ветка | |
| `` o `` | Создать запрос на принятие изменений | |
| `` O `` | Создать параметры запроса принятие изменений | |
| `` <c-y> `` | Скопировать URL запроса на принятие изменений в буфер обмена | |
| `` c `` | Переключить по названию | Checkout by name. In the input box you can enter '-' to switch to the last branch. |
| `` F `` | Принудительное переключение | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | Delete | View delete options for local/remote branch. |
| `` r `` | Перебазировать переключённую ветку на эту ветку | Rebase the checked-out branch onto the selected branch. |
| `` M `` | Слияние с текущей переключённой веткой | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` f `` | Перемотать эту ветку вперёд из её upstream-ветки | Fast-forward selected branch from its upstream. |
| `` T `` | Создать тег | |
| `` s `` | Порядок сортировки | |
| `` g `` | Просмотреть параметры сброса | |
| `` R `` | Переименовать ветку | |
| `` u `` | View upstream options | View options relating to the branch's upstream e.g. setting/unsetting the upstream and resetting to the upstream. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Просмотреть коммиты | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Меню
<pre>
<kbd>&lt;enter&gt;</kbd>: Выполнить
<kbd>&lt;esc&gt;</kbd>: Закрыть
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Выполнить | |
| `` <esc> `` | Закрыть | |
| `` / `` | Filter the current view by text | |
## Панель Подтверждения
<pre>
<kbd>&lt;enter&gt;</kbd>: Подтвердить
<kbd>&lt;esc&gt;</kbd>: Закрыть/отменить
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Подтвердить | |
| `` <esc> `` | Закрыть/отменить | |
## Подкоммиты
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать SHA коммита в буфер обмена
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: Переключить коммит
<kbd>y</kbd>: Скопировать атрибут коммита
<kbd>o</kbd>: Открыть коммит в браузере
<kbd>n</kbd>: Создать новую ветку с этого коммита
<kbd>g</kbd>: Просмотреть параметры сброса
<kbd>c</kbd>: Скопировать отобранные коммит (cherry-pick)
<kbd>C</kbd>: Скопировать несколько отобранных коммитов (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: Сбросить отобранную (скопированную | cherry-picked) выборку коммитов
<kbd>&lt;enter&gt;</kbd>: Просмотреть файлы выбранного элемента
<kbd>/</kbd>: Найти
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать hash коммита в буфер обмена | |
| `` <space> `` | Переключить | Checkout the selected commit as a detached HEAD. |
| `` y `` | Скопировать атрибут коммита | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | Открыть коммит в браузере | |
| `` n `` | Создать новую ветку с этого коммита | |
| `` g `` | Просмотреть параметры сброса | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | Скопировать отобранные коммит (cherry-pick) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | Сбросить отобранную (скопированную | cherry-picked) выборку коммитов | |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Просмотреть файлы выбранного элемента | |
| `` w `` | View worktree options | |
| `` / `` | Найти | |
## Подмодули
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать название подмодуля в буфер обмена
<kbd>&lt;enter&gt;</kbd>: Ввести подмодуль
<kbd>&lt;space&gt;</kbd>: Ввести подмодуль
<kbd>d</kbd>: Удалить подмодуль
<kbd>u</kbd>: Обновить подмодуль
<kbd>n</kbd>: Добавить новый подмодуль
<kbd>e</kbd>: Обновить URL подмодуля
<kbd>i</kbd>: Инициализировать подмодуль
<kbd>b</kbd>: Просмотреть параметры массового подмодуля
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать название подмодуля в буфер обмена | |
| `` <enter> `` | Enter | Ввести подмодуль |
| `` d `` | Remove | Remove the selected submodule and its corresponding directory. |
| `` u `` | Update | Обновить подмодуль |
| `` n `` | Добавить новый подмодуль | |
| `` e `` | Обновить URL подмодуля | |
| `` i `` | Initialize | Инициализировать подмодуль |
| `` b `` | Просмотреть параметры массового подмодуля | |
| `` / `` | Filter the current view by text | |
## Сводка коммита
<pre>
<kbd>&lt;enter&gt;</kbd>: Подтвердить
<kbd>&lt;esc&gt;</kbd>: Закрыть
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | Подтвердить | |
| `` <esc> `` | Закрыть | |
## Сохранить Изменения Файлов
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать закомиченное имя файла в буфер обмена
<kbd>c</kbd>: Переключить файл
<kbd>d</kbd>: Отменить изменения коммита в этом файле
<kbd>o</kbd>: Открыть файл
<kbd>e</kbd>: Редактировать файл
<kbd>&lt;space&gt;</kbd>: Переключить файлы включённые в патч
<kbd>a</kbd>: Переключить все файлы, включённые в патч
<kbd>&lt;enter&gt;</kbd>: Введите файл, чтобы добавить выбранные строки в патч (или свернуть каталог переключения)
<kbd>`</kbd>: Переключить вид дерева файлов
<kbd>/</kbd>: Найти
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать название файла в буфер обмена | |
| `` c `` | Переключить | Переключить файл |
| `` d `` | Remove | Отменить изменения коммита в этом файле |
| `` o `` | Открыть файл | Open file in default application. |
| `` e `` | Edit | Open file in external editor. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <space> `` | Переключить файлы включённые в патч | Toggle whether the file is included in the custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` a `` | Переключить все файлы, включённые в патч | Add/remove all commit's files to custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` <enter> `` | Введите файл, чтобы добавить выбранные строки в патч (или свернуть каталог переключения) | If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory. |
| `` ` `` | Переключить вид дерева файлов | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` / `` | Найти | |
## Статус
<pre>
<kbd>o</kbd>: Открыть файл конфигурации
<kbd>e</kbd>: Редактировать файл конфигурации
<kbd>u</kbd>: Проверить обновления
<kbd>&lt;enter&gt;</kbd>: Переключиться на последний репозиторий
<kbd>a</kbd>: Показать все логи ветки
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | Открыть файл конфигурации | Open file in default application. |
| `` e `` | Редактировать файл конфигурации | Open file in external editor. |
| `` u `` | Проверить обновления | |
| `` <enter> `` | Переключиться на последний репозиторий | |
| `` a `` | Показать все логи ветки | |
## Теги
<pre>
<kbd>&lt;space&gt;</kbd>: Переключить
<kbd>d</kbd>: Удалить тег
<kbd>P</kbd>: Отправить тег
<kbd>n</kbd>: Создать тег
<kbd>g</kbd>: Просмотреть параметры сброса
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: Просмотреть коммиты
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Переключить | Checkout the selected tag tag as a detached HEAD. |
| `` n `` | Создать тег | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
| `` d `` | Delete | View delete options for local/remote tag. |
| `` P `` | Отправить тег | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | Reset | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Просмотреть коммиты | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Удалённые ветки
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать название ветки в буфер обмена
<kbd>&lt;space&gt;</kbd>: Переключить
<kbd>n</kbd>: Новая ветка
<kbd>M</kbd>: Слияние с текущей переключённой веткой
<kbd>r</kbd>: Перебазировать переключённую ветку на эту ветку
<kbd>d</kbd>: Удалить ветку
<kbd>u</kbd>: Установить как upstream-ветку переключённую ветку
<kbd>g</kbd>: Просмотреть параметры сброса
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: Просмотреть коммиты
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать название ветки в буфер обмена | |
| `` <space> `` | Переключить | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` n `` | Новая ветка | |
| `` M `` | Слияние с текущей переключённой веткой | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` r `` | Перебазировать переключённую ветку на эту ветку | Rebase the checked-out branch onto the selected branch. |
| `` d `` | Delete | Delete the remote branch from the remote. |
| `` u `` | Set as upstream | Установить как upstream-ветку переключённую ветку |
| `` s `` | Порядок сортировки | |
| `` g `` | Просмотреть параметры сброса | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` <enter> `` | Просмотреть коммиты | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |
## Удалённые репозитории
<pre>
<kbd>f</kbd>: Получение изменения из удалённого репозитория
<kbd>n</kbd>: Добавить новую удалённую ветку
<kbd>d</kbd>: Удалить удалённую ветку
<kbd>e</kbd>: Редактировать удалённый репозитории
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | View branches | |
| `` n `` | Добавить новую удалённую ветку | |
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
| `` e `` | Edit | Редактировать удалённый репозитории |
| `` f `` | Получить изменения | Получение изменения из удалённого репозитория |
| `` / `` | Filter the current view by text | |
## Файлы
<pre>
<kbd>&lt;c-o&gt;</kbd>: Скопировать название файла в буфер обмена
<kbd>d</kbd>: Просмотреть параметры «отмены изменении»
<kbd>&lt;space&gt;</kbd>: Переключить индекс
<kbd>&lt;c-b&gt;</kbd>: Фильтровать файлы (проиндексированные/непроиндексированные)
<kbd>c</kbd>: Сохранить изменения
<kbd>w</kbd>: Закоммитить изменения без предварительного хука коммита
<kbd>A</kbd>: Правка последнего коммита
<kbd>C</kbd>: Сохранить изменения с помощью редактора git
<kbd>e</kbd>: Редактировать файл
<kbd>o</kbd>: Открыть файл
<kbd>i</kbd>: Игнорировать или исключить файл
<kbd>r</kbd>: Обновить файлы
<kbd>s</kbd>: Припрятать все изменения
<kbd>S</kbd>: Просмотреть параметры хранилища
<kbd>a</kbd>: Все проиндексированные/непроиндексированные
<kbd>&lt;enter&gt;</kbd>: Проиндексировать отдельные части/строки для файла или свернуть/развернуть для каталога
<kbd>g</kbd>: Просмотреть параметры сброса upstream-ветки
<kbd>D</kbd>: Просмотреть параметры сброса
<kbd>`</kbd>: Переключить вид дерева файлов
<kbd>M</kbd>: Открыть внешний инструмент слияния (git mergetool)
<kbd>f</kbd>: Получить изменения
<kbd>/</kbd>: Найти
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | Скопировать название файла в буфер обмена | |
| `` <space> `` | Переключить индекс | Toggle staged for selected file. |
| `` <c-b> `` | Фильтровать файлы (проиндексированные/непроиндексированные) | |
| `` y `` | Copy to clipboard | |
| `` c `` | Сохранить изменения | Commit staged changes. |
| `` w `` | Закоммитить изменения без предварительного хука коммита | |
| `` A `` | Правка последнего коммита | |
| `` C `` | Сохранить изменения с помощью редактора git | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | Edit | Open file in external editor. |
| `` o `` | Открыть файл | Open file in default application. |
| `` i `` | Игнорировать или исключить файл | |
| `` r `` | Обновить файлы | |
| `` s `` | Stash | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
| `` S `` | Просмотреть параметры хранилища | View stash options (e.g. stash all, stash staged, stash unstaged). |
| `` a `` | Все проиндексированные/непроиндексированные | Toggle staged/unstaged for all files in working tree. |
| `` <enter> `` | Проиндексировать отдельные части/строки для файла или свернуть/развернуть для каталога | If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it. |
| `` d `` | Просмотреть параметры «отмены изменении» | View options for discarding changes to the selected file. |
| `` g `` | Просмотреть параметры сброса upstream-ветки | |
| `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). |
| `` ` `` | Переключить вид дерева файлов | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` <c-t> `` | Open external diff tool (git difftool) | |
| `` M `` | Открыть внешний инструмент слияния (git mergetool) | Run `git mergetool`. |
| `` f `` | Получить изменения | Fetch changes from remote. |
| `` / `` | Найти | |
## Хранилище
<pre>
<kbd>&lt;space&gt;</kbd>: Применить припрятанные изменения
<kbd>g</kbd>: Применить припрятанные изменения и тут же удалить их из хранилища
<kbd>d</kbd>: Удалить припрятанные изменения из хранилища
<kbd>n</kbd>: Новая ветка
<kbd>r</kbd>: Переименовать хранилище
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: Просмотреть файлы выбранного элемента
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | Применить припрятанные изменения | Apply the stash entry to your working directory. |
| `` g `` | Применить припрятанные изменения и тут же удалить их из хранилища | Apply the stash entry to your working directory and remove the stash entry. |
| `` d `` | Удалить припрятанные изменения из хранилища | Remove the stash entry from the stash list. |
| `` n `` | Новая ветка | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
| `` r `` | Переименовать хранилище | |
| `` <enter> `` | Просмотреть файлы выбранного элемента | |
| `` w `` | View worktree options | |
| `` / `` | Filter the current view by text | |

View File

@@ -1,353 +1,369 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
# Lazygit 按键绑定
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
_图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
## 全局键绑定
<pre>
<kbd>&lt;c-r&gt;</kbd>: 切换到最近的仓库
<kbd>&lt;pgup&gt;</kbd>: 向上滚动主面板 (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: 向下滚动主面板 (fn+down/shift+j)
<kbd>@</kbd>: 打开命令日志菜单
<kbd>}</kbd>: 扩大差异视图中显示的上下文范围
<kbd>{</kbd>: 缩小差异视图中显示的上下文范围
<kbd>:</kbd>: 执行自定义命令
<kbd>&lt;c-p&gt;</kbd>: 查看自定义补丁选项
<kbd>m</kbd>: 查看 合并/变基 选项
<kbd>R</kbd>: 刷新
<kbd>+</kbd>: 下一屏模式(正常/半屏/全屏)
<kbd>_</kbd>: 上一屏模式
<kbd>?</kbd>: 打开菜单
<kbd>&lt;c-s&gt;</kbd>: 查看按路径过滤选项
<kbd>W</kbd>: 打开 diff 菜单
<kbd>&lt;c-e&gt;</kbd>: 打开 diff 菜单
<kbd>&lt;c-w&gt;</kbd>: 切换是否在差异视图中显示空白字符差异
<kbd>z</kbd>: (通过 reflog撤销「实验功能」
<kbd>&lt;c-z&gt;</kbd>: (通过 reflog重做「实验功能」
<kbd>P</kbd>: 推送
<kbd>p</kbd>: 拉取
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | 切换到最近的仓库 | |
| `` <pgup> (fn+up/shift+k) `` | 向上滚动主面板 | |
| `` <pgdown> (fn+down/shift+j) `` | 向下滚动主面板 | |
| `` @ `` | 打开命令日志菜单 | 查看命令日志的选项,例如显示/隐藏命令日志以及聚焦命令日志 |
| `` P `` | 推送 | 推送当前分支到它的上游。如果上游为配置,你可以在弹窗中配置上游分支。 |
| `` p `` | 拉取 | 从当前分支的远程分支获取改动。如果上游为配置,你可以在弹窗中配置上游分支。 |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | 扩大差异视图中显示的上下文范围 | 增加diff视图中围绕更改显示的上下文数量 |
| `` { `` | 缩小差异视图中显示的上下文范围 | 减少diff视图中围绕更改显示的上下文数量 |
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
| `` <c-p> `` | 查看自定义补丁选项 | |
| `` m `` | 查看 合并/变基 选项 | 查看当前合并或变基的中止、继续、跳过选项 |
| `` R `` | 刷新 | 刷新git状态(即在后台上运行`git status`,`git branch`等命令以更新面板内容) 不会运行`git fetch` |
| `` + `` | 下一屏模式(正常/半屏/全屏) | |
| `` _ `` | 上一屏模式 | |
| `` ? `` | 打开菜单 | |
| `` <c-s> `` | 查看按路径过滤选项 | 查看用于过滤提交日志的选项,以便仅显示与过滤器匹配的提交。 |
| `` W `` | 打开 diff 菜单 | 查看与比较两个引用相关的选项,例如与选定的 ref 进行比较,输入要比较的 ref然后反转比较方向。 |
| `` <c-e> `` | 打开 diff 菜单 | 查看与比较两个引用相关的选项,例如与选定的 ref 进行比较,输入要比较的 ref然后反转比较方向。 |
| `` q `` | 退出 | |
| `` <esc> `` | 取消 | |
| `` <c-w> `` | 切换是否在差异视图中显示空白字符差异 | 切换是否在diff视图中显示空白更改 |
| `` z `` | (通过 reflog)撤销「实验功能」 | Reflog将用于确定运行哪个git命令来撤消最后一个git命令。这并不包括对工作树的更改只考虑提交。 |
| `` <c-z> `` | (通过 reflog)重做「实验功能」 | Reflog将用于确定运行哪个git命令来重做上一个git命令。这并不包括对工作树的更改只考虑提交。 |
## 列表面板导航
<pre>
<kbd>,</kbd>: 上一页
<kbd>.</kbd>: 下一页
<kbd>&lt;</kbd>: 滚动到顶部
<kbd>&gt;</kbd>: 滚动到底部
<kbd>/</kbd>: 开始搜索
<kbd>H</kbd>: 向左滚动
<kbd>L</kbd>: 向右滚动
<kbd>]</kbd>: 下一个标签
<kbd>[</kbd>: 上一个标签
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | 上一页 | |
| `` . `` | 下一页 | |
| `` < `` | 滚动到顶部 | |
| `` > `` | 滚动到底部 | |
| `` v `` | 切换拖动选择 | |
| `` <s-down> `` | 向下扩展选择范围 | |
| `` <s-up> `` | 向上扩展选择范围 | |
| `` / `` | 开始搜索 | |
| `` H `` | 向左滚动 | |
| `` L `` | 向右滚动 | |
| `` ] `` | 下一个标签 | |
| `` [ `` | 上一个标签 | |
## Reflog 页面
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将提交的 SHA 复制到剪贴板
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: 检出提交
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: 在浏览器中打开提交
<kbd>n</kbd>: 从提交创建新分支
<kbd>g</kbd>: 查看重置选项
<kbd>c</kbd>: 复制提交(拣选)
<kbd>C</kbd>: 复制提交范围(拣选)
<kbd>&lt;c-r&gt;</kbd>: 重置已拣选(复制)的提交
<kbd>&lt;enter&gt;</kbd>: 查看提交
<kbd>/</kbd>: Filter the current view by text
</pre>
## Worktrees
<pre>
<kbd>n</kbd>: Create worktree
<kbd>&lt;space&gt;</kbd>: Switch to worktree
<kbd>&lt;enter&gt;</kbd>: Switch to worktree
<kbd>o</kbd>: Open in editor
<kbd>d</kbd>: Remove worktree
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(例如hash、URL、diff、消息、作者)。 |
| `` o `` | 在浏览器中打开提交 | |
| `` n `` | 从提交创建新分支 | |
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
| `` C `` | 复制提交(拣选) | 标记提交为已复制。然后,在本地提交视图中,你可以按 `V` (Cherry-Pick) 将已复制提交粘贴到已检出的分支中。任何时候都可以按 `<esc>` 来取消选择。 |
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` <enter> `` | 查看提交 | |
| `` w `` | 查看工作区选项 | |
| `` / `` | 通过文本过滤当前视图 | |
## 分支页面
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将分支名称复制到剪贴板
<kbd>i</kbd>: 显示 git-flow 选项
<kbd>&lt;space&gt;</kbd>: 检出
<kbd>n</kbd>: 新分支
<kbd>o</kbd>: 创建抓取请求
<kbd>O</kbd>: 创建抓取请求选项
<kbd>&lt;c-y&gt;</kbd>: 将抓取请求 URL 复制到剪贴板
<kbd>c</kbd>: 按名称检出
<kbd>F</kbd>: 强制检出
<kbd>d</kbd>: 删除分支
<kbd>r</kbd>: 将已检出的分支变基到该分支
<kbd>M</kbd>: 合并到当前检出的分支
<kbd>f</kbd>: 从上游快进此分支
<kbd>T</kbd>: 创建标签
<kbd>g</kbd>: 查看重置选项
<kbd>R</kbd>: 重命名分支
<kbd>u</kbd>: Set/Unset upstream
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: 查看提交
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 将分支名称复制到剪贴板 | |
| `` i `` | 显示 git-flow 选项 | |
| `` <space> `` | 检出 | 检出选中的项目 |
| `` n `` | 新分支 | |
| `` o `` | 创建抓取请求 | |
| `` O `` | 创建抓取请求选项 | |
| `` <c-y> `` | 将抓取请求 URL 复制到剪贴板 | |
| `` c `` | 按名称检出 | 按名称检出。在输入框中,您可以输入'-' 来切换到最后一个分支。 |
| `` F `` | 强制检出 | 强制检出所选分支。这将在检出所选分支之前放弃工作目录中的所有本地更改。 |
| `` d `` | 删除 | 查看本地/远程分支的删除选项 |
| `` r `` | 将已检出的分支变基到该分支 | 将检出的分支变基到所选的分支上。 |
| `` M `` | 合并到当前检出的分支 | Merge selected branch into currently checked out branch. |
| `` f `` | 从上游快进此分支 | 将当前分支直接移动到远程追踪分支的最新提交 |
| `` T `` | 创建标签 | |
| `` s `` | 排序 | |
| `` g `` | 查看重置选项 | |
| `` R `` | 重命名分支 | |
| `` u `` | 查看上游选项 | 查看与分支上游相关的选项,例如设置/取消设置上游和重置为上游。 |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` <enter> `` | 查看提交 | |
| `` w `` | 查看工作区选项 | |
| `` / `` | 通过文本过滤当前视图 | |
## 子提交
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将提交的 SHA 复制到剪贴板
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: 检出提交
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: 在浏览器中打开提交
<kbd>n</kbd>: 从提交创建新分支
<kbd>g</kbd>: 查看重置选项
<kbd>c</kbd>: 复制提交(拣选)
<kbd>C</kbd>: 复制提交范围(拣选)
<kbd>&lt;c-r&gt;</kbd>: 重置已拣选(复制)的提交
<kbd>&lt;enter&gt;</kbd>: 查看提交的文件
<kbd>/</kbd>: 开始搜索
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(例如hash、URL、diff、消息、作者)。 |
| `` o `` | 在浏览器中打开提交 | |
| `` n `` | 从提交创建新分支 | |
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
| `` C `` | 复制提交(拣选) | 标记提交为已复制。然后,在本地提交视图中,你可以按 `V` (Cherry-Pick) 将已复制提交粘贴到已检出的分支中。任何时候都可以按 `<esc>` 来取消选择。 |
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` <enter> `` | 查看提交的文件 | |
| `` w `` | 查看工作区选项 | |
| `` / `` | 开始搜索 | |
## 子模块
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将子模块名称复制到剪贴板
<kbd>&lt;enter&gt;</kbd>: 输入子模块
<kbd>&lt;space&gt;</kbd>: 输入子模块
<kbd>d</kbd>: 删除子模块
<kbd>u</kbd>: 更新子模块
<kbd>n</kbd>: 添加新的子模块
<kbd>e</kbd>: 更新子模块 URL
<kbd>i</kbd>: 初始化子模块
<kbd>b</kbd>: 查看批量子模块选项
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 将子模块名称复制到剪贴板 | |
| `` <enter> `` | 进入 | 输入子模块 |
| `` d `` | 删除 | 删除选定的子模块及其相应的目录 |
| `` u `` | 更新 | 更新子模块 |
| `` n `` | 添加新的子模块 | |
| `` e `` | 更新子模块 URL | |
| `` i `` | 初始化 | 初始化子模块 |
| `` b `` | 查看批量子模块选项 | |
| `` / `` | 通过文本过滤当前视图 | |
## 工作区
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | 新建工作树 | |
| `` <space> `` | 切换 | 切换到选中的工作树 |
| `` o `` | 在编辑器中编写 | |
| `` d `` | 删除 | 删除选定的工作树。这将删除工作树的目录以及 .git 目录中有关工作树的元数据。 |
| `` / `` | 通过文本过滤当前视图 | |
## 提交
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将提交的 SHA 复制到剪贴板
<kbd>&lt;c-r&gt;</kbd>: 重置已拣选(复制)的提交
<kbd>b</kbd>: 查看二分查找选项
<kbd>s</kbd>: 向下压缩
<kbd>f</kbd>: 修正提交fixup
<kbd>r</kbd>: 改写提交
<kbd>R</kbd>: 使用编辑器重命名提交
<kbd>d</kbd>: 删除提交
<kbd>e</kbd>: 编辑提交
<kbd>p</kbd>: 选择提交(变基过程中)
<kbd>F</kbd>: 创建修正提交
<kbd>S</kbd>: 压缩在所选提交之上的所有“fixup!”提交(自动压缩)
<kbd>&lt;c-j&gt;</kbd>: 下移提交
<kbd>&lt;c-k&gt;</kbd>: 上移提交
<kbd>v</kbd>: 粘贴提交(拣选)
<kbd>B</kbd>: Mark commit as base commit for rebase
<kbd>A</kbd>: 用已暂存的更改来修补提交
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: 还原提交
<kbd>T</kbd>: 标签提交
<kbd>&lt;c-l&gt;</kbd>: 打开日志菜单
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: 检出提交
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: 在浏览器中打开提交
<kbd>n</kbd>: 从提交创建新分支
<kbd>g</kbd>: 查看重置选项
<kbd>c</kbd>: 复制提交(拣选)
<kbd>C</kbd>: 复制提交范围(拣选)
<kbd>&lt;enter&gt;</kbd>: 查看提交的文件
<kbd>/</kbd>: 开始搜索
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 将提交的 hash 复制到剪贴板 | |
| `` <c-r> `` | 重置已拣选(复制)的提交 | |
| `` b `` | 查看二分查找选项 | |
| `` s `` | 压缩(Squash) | 将已选提交压缩到该提交之下。这些选定的提交的消息会附加到该提交的消息之下。 |
| `` f `` | 修正(fixup) | 将选定的提交合并到其下面的提交中。与压缩类似,但所选提交的消息将被丢弃。 |
| `` r `` | 改写提交 | 重写所选提交的消息。 |
| `` R `` | 使用编辑器重命名提交 | |
| `` d `` | 删除提交 | 删除选中的提交。这将通过变基从分支中删除该提交,如果该提交修改的内容依赖于后续的提交,则需要解决合并冲突。 |
| `` e `` | 编辑(开始交互式变基) | 编辑提交 |
| `` i `` | 开始交互式变基 | 为分支上的提交启动交互式变基。这将包括从 HEAD 提交到第一个合并提交或主分支提交的所有提交。
如果您想从所选提交启动交互式变基,请按 `e`。 |
| `` p `` | 拣选(Pick) | 选择提交(变基过程中) |
| `` F `` | 为此提交创建修正 | 创建修正提交 |
| `` S `` | 应用该修复提交 | 压缩在所选提交之上的所有“fixup!”提交(自动压缩) |
| `` <c-j> `` | 下移提交 | |
| `` <c-k> `` | 上移提交 | |
| `` V `` | 粘贴提交(拣选) | |
| `` B `` | 标记一个主提交用于变基 | 选择下一次变基的主提交。当您变基到一个分支时只有高于主提交的提交才会被引入。这使用“git rebase --onto”命令。 |
| `` A `` | 修补(Amend) | 用已暂存的变更来修补提交 |
| `` a `` | 修补提交属性 | 设置或重置提交的作者,或添加其他作者。 |
| `` t `` | 撤销(Revert) | 为所选提交创建还原提交,这会反向应用所选提交的更改。 |
| `` T `` | 标签提交 | 创建一个新标签指向所选提交。你可以在弹窗中输入标签名称和描述(可选)。 |
| `` <c-l> `` | 打开日志菜单 | 查看提交日志的选项,例如更改排序顺序、隐藏 git graph、显示整个 git graph。 |
| `` <space> `` | 检出 | 检出所选择的提交作为分离HEAD。 |
| `` y `` | 复制提交属性到剪贴板 | 复制提交属性到剪贴板(例如hash、URL、diff、消息、作者)。 |
| `` o `` | 在浏览器中打开提交 | |
| `` n `` | 从提交创建新分支 | |
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
| `` C `` | 复制提交(拣选) | 标记提交为已复制。然后,在本地提交视图中,你可以按 `V` (Cherry-Pick) 将已复制的提交粘贴到已检出的分支中。任何时候都可以按 `<esc>` 来取消选择。 |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` <enter> `` | 查看提交的文件 | |
| `` w `` | 查看工作区选项 | |
| `` / `` | 开始搜索 | |
## 提交信息
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 确认 | |
| `` <esc> `` | 关闭 | |
## 提交文件
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将提交的文件名复制到剪贴板
<kbd>c</kbd>: 检出文件
<kbd>d</kbd>: 放弃对此文件的提交更改
<kbd>o</kbd>: 打开文件
<kbd>e</kbd>: 编辑文件
<kbd>&lt;space&gt;</kbd>: 补丁中包含的切换文件
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: 输入文件以将所选行添加到补丁中(或切换目录折叠)
<kbd>`</kbd>: 切换文件树视图
<kbd>/</kbd>: 开始搜索
</pre>
## 提交讯息
<pre>
<kbd>&lt;enter&gt;</kbd>: 确认
<kbd>&lt;esc&gt;</kbd>: 关闭
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 将文件名复制到剪贴板 | |
| `` c `` | 检出 | 检出文件 |
| `` d `` | 删除 | 放弃对此文件的提交变更 |
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
| `` e `` | 编辑 | 使用外部编辑器打开文件 |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` <space> `` | 补丁中包含的切换文件 | 切换文件是否包含在自定义补丁中。请参阅 https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches。 |
| `` a `` | 操作所有文件 | 添加或删除所有提交中的文件到自定义的补丁中。请参阅 https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches。 |
| `` <enter> `` | 输入文件以将所选行添加到补丁中(或切换目录折叠) | 如果已选择一个文件则Enter进入该文件以便您可以向自定义补丁添加/删除单独的行。如果选择了目录,则切换目录。 |
| `` ` `` | 切换文件树视图 | 在平铺部署与树布局之间切换文件视图。平铺布局在一个列表中展示所有文件路径,树布局则根据目录分组展示。 |
| `` / `` | 开始搜索 | |
## 文件
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将文件名复制到剪贴板
<kbd>d</kbd>: 查看'放弃更改'选项
<kbd>&lt;space&gt;</kbd>: 切换暂存状态
<kbd>&lt;c-b&gt;</kbd>: Filter files by status
<kbd>c</kbd>: 提交更改
<kbd>w</kbd>: 提交更改而无需预先提交钩子
<kbd>A</kbd>: 修补最后一次提交
<kbd>C</kbd>: 提交更改(使用编辑器编辑提交信息)
<kbd>e</kbd>: 编辑文件
<kbd>o</kbd>: 打开文件
<kbd>i</kbd>: 忽略文件
<kbd>r</kbd>: 刷新文件
<kbd>s</kbd>: 将所有更改加入贮藏
<kbd>S</kbd>: 查看贮藏选项
<kbd>a</kbd>: 切换所有文件的暂存状态
<kbd>&lt;enter&gt;</kbd>: 暂存单个 块/行 用于文件, 或 折叠/展开 目录
<kbd>g</kbd>: 查看上游重置选项
<kbd>D</kbd>: 查看重置选项
<kbd>`</kbd>: 切换文件树视图
<kbd>M</kbd>: 打开外部合并工具 (git mergetool)
<kbd>f</kbd>: 抓取
<kbd>/</kbd>: 开始搜索
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 将文件名复制到剪贴板 | |
| `` <space> `` | 切换暂存状态 | 为选定的文件切换暂存状态 |
| `` <c-b> `` | 通过状态过滤文件 | |
| `` y `` | 复制到剪贴板 | |
| `` c `` | 提交变更 | 提交暂存文件 |
| `` w `` | 提交变更而无需预先提交钩子 | |
| `` A `` | 修补最后一次提交 | |
| `` C `` | 提交变更(使用编辑器编辑提交信息) | |
| `` <c-f> `` | 找到用于修复的基准提交 | 找到您当前变更所基于的提交,以便于修正/改进该提交。这样做可以省去您逐一查看分支提交来确定应该修正/改进哪个提交的麻烦。请参阅文档: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | 编辑 | 使用外部编辑器打开文件 |
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
| `` i `` | 忽略文件 | |
| `` r `` | 刷新文件 | |
| `` s `` | 贮藏 | 贮藏所有变更.若要使用其他贮藏变体,请使用查看贮藏选项快捷键 |
| `` S `` | 查看贮藏选项 | 查看贮藏选项(例如:贮藏所有、贮藏已暂存变更、贮藏未暂存变更) |
| `` a `` | 切换所有文件的暂存状态 | 切换工作区中所有文件的已暂存/未暂存状态 |
| `` <enter> `` | 暂存单个 块/行 用于文件, 或 折叠/展开 目录 | 如果选中的是一个文件,则会进入到暂存视图,以便可以暂存单个代码块/行。如果选中的是一个目录,则会折叠/展开这个目录 |
| `` d `` | 查看'放弃变更'选项 | 查看选中文件的放弃变更选项 |
| `` g `` | 查看上游重置选项 | |
| `` D `` | 重置 | 查看工作树的重置选项(例如:清除工作树)。 |
| `` ` `` | 切换文件树视图 | 在平铺部署与树布局之间切换文件视图。平铺布局在一个列表中展示所有文件路径,树布局则根据目录分组展示。 |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` M `` | 打开外部合并工具(git mergetool) | 执行 `git mergetool`. |
| `` f `` | 抓取 | 从远程获取变更 |
| `` / `` | 开始搜索 | |
## 构建补丁中
<pre>
<kbd>&lt;left&gt;</kbd>: 选择上一个区块
<kbd>&lt;right&gt;</kbd>: 选择一个区块
<kbd>v</kbd>: 切换拖动选择
<kbd>V</kbd>: 切换拖动选择
<kbd>a</kbd>: 切换选择区块
<kbd>&lt;c-o&gt;</kbd>: 将选中文本复制到剪贴板
<kbd>o</kbd>: 打开文件
<kbd>e</kbd>: 编辑文件
<kbd>&lt;space&gt;</kbd>: 添加/移除 行到补丁
<kbd>&lt;esc&gt;</kbd>: 退出逐行模式
<kbd>/</kbd>: 开始搜索
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 选择一个区块 | |
| `` <right> `` | 选择下一个区块 | |
| `` v `` | 切换拖动选择 | |
| `` a `` | 切换选择代码块 | 切换代码块选择模式 |
| `` <c-o> `` | 将选中文本复制到剪贴板 | |
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
| `` e `` | 编辑文件 | 使用外部编辑器打开文件 |
| `` <space> `` | 添加/移除 行到补丁 | |
| `` <esc> `` | 退出逐行模式 | |
| `` / `` | 开始搜索 | |
## 标签页面
<pre>
<kbd>&lt;space&gt;</kbd>: 检出
<kbd>d</kbd>: 删除标签
<kbd>P</kbd>: 推送标签
<kbd>n</kbd>: 创建标签
<kbd>g</kbd>: 查看重置选项
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: 查看提交
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 检出 | 检出选择的标签作为分离的HEAD |
| `` n `` | 创建标签 | 基于当前提交创建一个新标签。你将在弹窗中输入标签名称和描述(可选)。 |
| `` d `` | 删除 | 查看本地/远程标签的删除选项 |
| `` P `` | 推送标签 | 推送选择的标签到远端。你将在弹窗中选择一个远端。 |
| `` g `` | 重置 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` <enter> `` | 查看提交 | |
| `` w `` | 查看工作区选项 | |
| `` / `` | 通过文本过滤当前视图 | |
## 正在合并
<pre>
<kbd>e</kbd>: 编辑文件
<kbd>o</kbd>: 打开文件
<kbd>&lt;left&gt;</kbd>: 选择上一个冲突
<kbd>&lt;right&gt;</kbd>: 选择下一个冲突
<kbd>&lt;up&gt;</kbd>: 选择部块
<kbd>&lt;down&gt;</kbd>: 选择底部块
<kbd>z</kbd>: 撤销
<kbd>M</kbd>: 打开外部合并工具 (git mergetool)
<kbd>&lt;space&gt;</kbd>: 选中区块
<kbd>b</kbd>: 选中所有区块
<kbd>&lt;esc&gt;</kbd>: 返回文件面板
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 选中区块 | |
| `` b `` | 选中所有区块 | |
| `` <up> `` | 选择顶部块 | |
| `` <down> `` | 选择部块 | |
| `` <left> `` | 选择上一个冲突 | |
| `` <right> `` | 选择下一个冲突 | |
| `` z `` | 撤销 | 撤消上次合并冲突解决 |
| `` e `` | 编辑文件 | 使用外部编辑器打开文件 |
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
| `` M `` | 打开外部合并工具(git mergetool) | 执行 `git mergetool`. |
| `` <esc> `` | 返回文件面板 | |
## 正在暂存
<pre>
<kbd>&lt;left&gt;</kbd>: 选择上一个区块
<kbd>&lt;right&gt;</kbd>: 选择一个区块
<kbd>v</kbd>: 切换拖动选择
<kbd>V</kbd>: 切换拖动选择
<kbd>a</kbd>: 切换选择区块
<kbd>&lt;c-o&gt;</kbd>: 将选中文本复制到剪贴板
<kbd>o</kbd>: 打开文件
<kbd>e</kbd>: 编辑文件
<kbd>&lt;esc&gt;</kbd>: 返回文件面板
<kbd>&lt;tab&gt;</kbd>: 切换到其他面板
<kbd>&lt;space&gt;</kbd>: 切换行暂存状态
<kbd>d</kbd>: 取消变更 (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: 提交更改
<kbd>w</kbd>: 提交更而无需预先提交钩子
<kbd>C</kbd>: 提交更改(使用编辑器编辑提交信息
<kbd>/</kbd>: 开始搜索
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 选择一个区块 | |
| `` <right> `` | 选择下一个区块 | |
| `` v `` | 切换拖动选择 | |
| `` a `` | 切换选择代码块 | 切换代码块选择模式 |
| `` <c-o> `` | 将选中文本复制到剪贴板 | |
| `` <space> `` | 切换暂存状态 | 切换行暂存状态 |
| `` d `` | 取消变更(git reset) | 当选择未暂存的变更时使用git reset丢弃该变更。当选择已暂存的变更时取消暂存该变更 |
| `` o `` | 打开文件 | 使用默认程序打开该文件 |
| `` e `` | 编辑文件 | 使用外部编辑器打开文件 |
| `` <esc> `` | 返回文件面板 | |
| `` <tab> `` | 切换到其他面板 | 切换到其他视图(已暂存/未暂存的变更) |
| `` E `` | 编辑代码块 | 在外部编辑器中编辑选中的代码块 |
| `` c `` | 提交变更 | 提交暂存文件 |
| `` w `` | 提交更而无需预先提交钩子 | |
| `` C `` | 提交变更(使用编辑器编辑提交信息) | |
| `` <c-f> `` | 找到用于修复的基准提交 | 找到您当前变更所基于的提交,以便于修正/改进该提交。这样做可以省去您逐一查看分支提交来确定应该修正/改进哪个提交的麻烦。请参阅文档: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | 开始搜索 | |
## 正常
<pre>
<kbd>mouse wheel down</kbd>: 向下滚动 (fn+up)
<kbd>mouse wheel up</kbd>:滚动 (fn+down)
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` |滚动 | |
| `` mouse wheel up (fn+down) `` | 向上滚动 | |
## 状态
<pre>
<kbd>o</kbd>: 打开配置文件
<kbd>e</kbd>: 编辑配置文件
<kbd>u</kbd>: 检查更新
<kbd>&lt;enter&gt;</kbd>: 切换到最近的仓库
<kbd>a</kbd>: 显示所有分支的日志
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | 打开配置文件 | 使用默认程序打开该文件 |
| `` e `` | 编辑配置文件 | 使用外部编辑器打开文件 |
| `` u `` | 检查更新 | |
| `` <enter> `` | 切换到最近的仓库 | |
| `` a `` | 显示所有分支的日志 | |
## 确认面板
<pre>
<kbd>&lt;enter&gt;</kbd>: 确认
<kbd>&lt;esc&gt;</kbd>: 关闭
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 确认 | |
| `` <esc> `` | 关闭 | |
## 菜单
<pre>
<kbd>&lt;enter&gt;</kbd>: 执行
<kbd>&lt;esc&gt;</kbd>: 关闭
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 执行 | |
| `` <esc> `` | 关闭 | |
| `` / `` | 通过文本过滤当前视图 | |
## 贮藏
<pre>
<kbd>&lt;space&gt;</kbd>: 应用
<kbd>g</kbd>: 应用并删除
<kbd>d</kbd>: 删除
<kbd>n</kbd>: 新分支
<kbd>r</kbd>: Rename stash
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: 查看提交的文件
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 应用 | 将贮藏项应用到您的工作目录。 |
| `` g `` | 应用并删除 | 将存储项应用到工作目录并删除存储项。 |
| `` d `` | 删除 | 从贮藏列表中删除该贮藏项 |
| `` n `` | 新分支 | 从选定的贮藏项创建一个新分支。这是通过 git 检查创建贮藏项的提交,从该提交创建一个新分支,然后将贮藏项作为附加提交应用到新分支来实现的。 |
| `` r `` | 重命名贮藏 | |
| `` <enter> `` | 查看提交的文件 | |
| `` w `` | 查看工作区选项 | |
| `` / `` | 通过文本过滤当前视图 | |
## 远程分支
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将分支名称复制到剪贴板
<kbd>&lt;space&gt;</kbd>: 检出
<kbd>n</kbd>: 新分支
<kbd>M</kbd>: 合并到当前检出的分支
<kbd>r</kbd>: 将已检出的分支变基到该分支
<kbd>d</kbd>: 删除分支
<kbd>u</kbd>: 设置为检出分支的上游
<kbd>g</kbd>: 查看重置选项
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: 查看提交
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 将分支名称复制到剪贴板 | |
| `` <space> `` | 检出 | 基于当前选中的远程分支检出一个新的本地分支或者将远程分支作分离的HEAD。 |
| `` n `` | 新分支 | |
| `` M `` | 合并到当前检出的分支 | Merge selected branch into currently checked out branch. |
| `` r `` | 将已检出的分支变基到该分支 | 将检出的分支变基到所选的分支上。 |
| `` d `` | 删除 | 从远程删除远程分支。 |
| `` u `` | 设置为上游 | 设置为检出分支的上游 |
| `` s `` | 排序 | |
| `` g `` | 查看重置选项 | 查看重置选项 (soft/mixed/hard) 用于重置到选择项 |
| `` <c-t> `` | 使用外部差异比较工具(git difftool) | |
| `` <enter> `` | 查看提交 | |
| `` w `` | 查看工作区选项 | |
| `` / `` | 通过文本过滤当前视图 | |
## 远程页面
<pre>
<kbd>f</kbd>: 抓取远程仓库
<kbd>n</kbd>: 添加新的远程仓库
<kbd>d</kbd>: 删除远程
<kbd>e</kbd>: 编辑远程仓库
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 查看分支 | |
| `` n `` | 添加新的远程仓库 | |
| `` d `` | 删除 | 删除选中的远程。从远程跟踪远程分支的任何本地分支都不会受到影响。 |
| `` e `` | 编辑 | 编辑远程仓库 |
| `` f `` | 抓取 | 抓取远程仓库 |
| `` / `` | 通过文本过滤当前视图 | |

View File

@@ -1,353 +1,369 @@
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go run scripts/cheatsheet/main.go generate` from the project root._
_This file is auto-generated. To update, make the changes in the pkg/i18n directory and then run `go generate ./...` from the project root._
# Lazygit 鍵盤快捷鍵
_說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B`B`表示 Shift+B_
_說明:`<c-b>` 表示 CtrlB、`<a-b>` 表示 AltB`B`表示 ShiftB_
## 全快捷鍵
## 全快捷鍵
<pre>
<kbd>&lt;c-r&gt;</kbd>: 切換到最近使用的版本庫
<kbd>&lt;pgup&gt;</kbd>: 向上捲動主面板 (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: 向下捲動主面板 (fn+down/shift+j)
<kbd>@</kbd>: 開啟命令記錄選單
<kbd>}</kbd>: 增加差異檢視中顯示變更周圍上下文的大小
<kbd>{</kbd>: 減小差異檢視中顯示變更周圍上下文的大小
<kbd>:</kbd>: 執行自訂命令
<kbd>&lt;c-p&gt;</kbd>: 檢視自訂補丁選項
<kbd>m</kbd>: 查看合併/變基選項
<kbd>R</kbd>: 重新整理
<kbd>+</kbd>: 下一個螢幕模式(常規/半螢幕/全螢幕)
<kbd>_</kbd>: 上一個螢幕模式
<kbd>?</kbd>: 開啟選單
<kbd>&lt;c-s&gt;</kbd>: 檢視篩選路徑選項
<kbd>W</kbd>: 開啟差異比較選單
<kbd>&lt;c-e&gt;</kbd>: 開啟差異比較選單
<kbd>&lt;c-w&gt;</kbd>: 切換是否在差異檢視中顯示空格變更
<kbd>z</kbd>: 復原
<kbd>&lt;c-z&gt;</kbd>: 取消復原
<kbd>P</kbd>: 推送
<kbd>p</kbd>: 拉取
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-r> `` | 切換到最近使用的版本庫 | |
| `` <pgup> (fn+up/shift+k) `` | 向上捲動主面板 | |
| `` <pgdown> (fn+down/shift+j) `` | 向下捲動主面板 | |
| `` @ `` | 開啟命令記錄選單 | View options for the command log e.g. show/hide the command log and focus the command log. |
| `` P `` | 推送 | 推送到遠端。如果沒有設定遠端,會開啟設定視窗。 |
| `` p `` | 拉取 | 從遠端同步當前分支。如果沒有設定遠端,會開啟設定視窗。 |
| `` ) `` | Increase rename similarity threshold | Increase the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` ( `` | Decrease rename similarity threshold | Decrease the similarity threshold for a deletion and addition pair to be treated as a rename. |
| `` } `` | 增加差異檢視中顯示變更周圍上下文的大小 | Increase the amount of the context shown around changes in the diff view. |
| `` { `` | 減小差異檢視中顯示變更周圍上下文的大小 | Decrease the amount of the context shown around changes in the diff view. |
| `` : `` | Execute shell command | Bring up a prompt where you can enter a shell command to execute. |
| `` <c-p> `` | 檢視自訂補丁選項 | |
| `` m `` | 查看合併/變基選項 | View options to abort/continue/skip the current merge/rebase. |
| `` R `` | 重新整理 | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | 下一個螢幕模式(常規/半螢幕/全螢幕) | |
| `` _ `` | 上一個螢幕模式 | |
| `` ? `` | 開啟選單 | |
| `` <c-s> `` | 檢視篩選路徑選項 | View options for filtering the commit log, so that only commits matching the filter are shown. |
| `` W `` | 開啟差異比較選單 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` <c-e> `` | 開啟差異比較選單 | View options relating to diffing two refs e.g. diffing against selected ref, entering ref to diff against, and reversing the diff direction. |
| `` q `` | 結束 | |
| `` <esc> `` | 取消 | |
| `` <c-w> `` | 切換是否在差異檢視中顯示空格變更 | Toggle whether or not whitespace changes are shown in the diff view. |
| `` z `` | 復原 | 將使用 reflog 確任 git 指令以復原。這不包括工作區更改;只考慮提交。 |
| `` <c-z> `` | 取消復原 | 將使用 reflog 確任 git 指令以重作。這不包括工作區更改;只考慮提交。 |
## 列表面板導航
## 移動
<pre>
<kbd>,</kbd>: 上一頁
<kbd>.</kbd>: 下一頁
<kbd>&lt;</kbd>: 捲動到頂部
<kbd>&gt;</kbd>: 捲動到底部
<kbd>/</kbd>: 開始搜尋
<kbd>H</kbd>: 向左捲動
<kbd>L</kbd>: 向右捲動
<kbd>]</kbd>: 下一個索引標籤
<kbd>[</kbd>: 上一個索引標籤
</pre>
## Reflog
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製提交 SHA 到剪貼簿
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: 檢出提交
<kbd>y</kbd>: 複製提交屬性
<kbd>o</kbd>: 在瀏覽器中開啟提交
<kbd>n</kbd>: 從提交建立新分支
<kbd>g</kbd>: 檢視重設選項
<kbd>c</kbd>: 複製提交 (揀選)
<kbd>C</kbd>: 複製提交範圍 (揀選)
<kbd>&lt;c-r&gt;</kbd>: 重設選定的揀選 (複製) 提交
<kbd>&lt;enter&gt;</kbd>: 檢視提交
<kbd>/</kbd>: Filter the current view by text
</pre>
## Worktrees
<pre>
<kbd>n</kbd>: Create worktree
<kbd>&lt;space&gt;</kbd>: Switch to worktree
<kbd>&lt;enter&gt;</kbd>: Switch to worktree
<kbd>o</kbd>: Open in editor
<kbd>d</kbd>: Remove worktree
<kbd>/</kbd>: Filter the current view by text
</pre>
## 主視窗 (一般)
<pre>
<kbd>mouse wheel down</kbd>: 向下捲動 (fn+up)
<kbd>mouse wheel up</kbd>: 向上捲動 (fn+down)
</pre>
## 主視窗 (合併中)
<pre>
<kbd>e</kbd>: 編輯檔案
<kbd>o</kbd>: 開啟檔案
<kbd>&lt;left&gt;</kbd>: 選擇上一個衝突
<kbd>&lt;right&gt;</kbd>: 選擇下一個衝突
<kbd>&lt;up&gt;</kbd>: 選擇上一段
<kbd>&lt;down&gt;</kbd>: 選擇下一段
<kbd>z</kbd>: 復原
<kbd>M</kbd>: 開啟外部合併工具 (git mergetool)
<kbd>&lt;space&gt;</kbd>: 挑選程式碼片段
<kbd>b</kbd>: 挑選所有程式碼片段
<kbd>&lt;esc&gt;</kbd>: 返回檔案面板
</pre>
## 主視窗 (預存中)
<pre>
<kbd>&lt;left&gt;</kbd>: 選擇上一段
<kbd>&lt;right&gt;</kbd>: 選擇下一段
<kbd>v</kbd>: 切換拖曳選擇
<kbd>V</kbd>: 切換拖曳選擇
<kbd>a</kbd>: 切換選擇程式碼塊
<kbd>&lt;c-o&gt;</kbd>: 複製所選文本至剪貼簿
<kbd>o</kbd>: 開啟檔案
<kbd>e</kbd>: 編輯檔案
<kbd>&lt;esc&gt;</kbd>: 返回檔案面板
<kbd>&lt;tab&gt;</kbd>: 切換至另一個面板 (已預存/未預存更改)
<kbd>&lt;space&gt;</kbd>: 切換現有行的狀態 (已預存/未預存)
<kbd>d</kbd>: 刪除變更 (git reset)
<kbd>E</kbd>: 編輯程式碼塊
<kbd>c</kbd>: 提交變更
<kbd>w</kbd>: 沒有預提交 hook 就提交更改
<kbd>C</kbd>: 使用 git 編輯器提交變更
<kbd>/</kbd>: 開始搜尋
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` , `` | 上一頁 | |
| `` . `` | 下一頁 | |
| `` < `` | 捲動到頂部 | |
| `` > `` | 捲動到底部 | |
| `` v `` | 切換拖曳選擇 | |
| `` <s-down> `` | Range select down | |
| `` <s-up> `` | Range select up | |
| `` / `` | 搜尋 | |
| `` H `` | 向左捲動 | |
| `` L `` | 向右捲動 | |
| `` ] `` | 下一個索引標籤 | |
| `` [ `` | 上一個索引標籤 | |
## 主面板 (補丁生成)
<pre>
<kbd>&lt;left&gt;</kbd>: 選擇上一段
<kbd>&lt;right&gt;</kbd>: 選擇一段
<kbd>v</kbd>: 切換拖曳選擇
<kbd>V</kbd>: 切換拖曳選擇
<kbd>a</kbd>: 切換選擇程式碼塊
<kbd>&lt;c-o&gt;</kbd>: 複製所選文本至剪貼簿
<kbd>o</kbd>: 開啟檔案
<kbd>e</kbd>: 編輯檔案
<kbd>&lt;space&gt;</kbd>: 向 (或從) 補丁中添加/刪除行
<kbd>&lt;esc&gt;</kbd>: 退出自訂補丁建立器
<kbd>/</kbd>: 開始搜尋
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 選擇一段 | |
| `` <right> `` | 選擇下一段 | |
| `` v `` | 切換拖曳選擇 | |
| `` a `` | 切換選擇程式碼塊 | Toggle hunk selection mode. |
| `` <c-o> `` | 複製所選文本至剪貼簿 | |
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
| `` e `` | 編輯檔案 | 使用外部編輯器開啟 |
| `` <space> `` | 向 (或從) 補丁中添加/刪除行 | |
| `` <esc> `` | 退出自訂補丁建立器 | |
| `` / `` | 搜尋 | |
## 主面板(一般)
| Key | Action | Info |
|-----|--------|-------------|
| `` mouse wheel down (fn+up) `` | 向下捲動 | |
| `` mouse wheel up (fn+down) `` | 向上捲動 | |
## 主面板(合併)
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 挑選程式碼片段 | |
| `` b `` | 挑選所有程式碼片段 | |
| `` <up> `` | 選擇上一段 | |
| `` <down> `` | 選擇下一段 | |
| `` <left> `` | 選擇上一個衝突 | |
| `` <right> `` | 選擇下一個衝突 | |
| `` z `` | 復原 | Undo last merge conflict resolution. |
| `` e `` | 編輯檔案 | 使用外部編輯器開啟 |
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
| `` M `` | 開啟外部合併工具 | 執行 `git mergetool`。 |
| `` <esc> `` | 返回檔案面板 | |
## 主面板(預存)
| Key | Action | Info |
|-----|--------|-------------|
| `` <left> `` | 選擇上一段 | |
| `` <right> `` | 選擇下一段 | |
| `` v `` | 切換拖曳選擇 | |
| `` a `` | 切換選擇程式碼塊 | Toggle hunk selection mode. |
| `` <c-o> `` | 複製所選文本至剪貼簿 | |
| `` <space> `` | 切換預存 | 切換現有行的狀態 (已預存/未預存) |
| `` d `` | 刪除變更 (git reset) | When unstaged change is selected, discard the change using `git reset`. When staged change is selected, unstage the change. |
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
| `` e `` | 編輯檔案 | 使用外部編輯器開啟 |
| `` <esc> `` | 返回檔案面板 | |
| `` <tab> `` | 切換至另一個面板 (已預存/未預存更改) | Switch to other view (staged/unstaged changes). |
| `` E `` | 編輯程式碼塊 | Edit selected hunk in external editor. |
| `` c `` | 提交變更 | 提交暫存區變更 |
| `` w `` | 沒有預提交 hook 就提交更改 | |
| `` C `` | 使用 git 編輯器提交變更 | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` / `` | 搜尋 | |
## 功能表
<pre>
<kbd>&lt;enter&gt;</kbd>: 執行
<kbd>&lt;esc&gt;</kbd>: 關閉
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 執行 | |
| `` <esc> `` | 關閉 | |
| `` / `` | 搜尋 | |
## 子提交
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製提交 SHA 到剪貼簿
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: 檢出提交
<kbd>y</kbd>: 複製提交屬性
<kbd>o</kbd>: 在瀏覽器中開啟提交
<kbd>n</kbd>: 從提交建立新分支
<kbd>g</kbd>: 檢視重設選項
<kbd>c</kbd>: 複製提交 (揀選)
<kbd>C</kbd>: 複製提交範圍 (揀選)
<kbd>&lt;c-r&gt;</kbd>: 重設選定的揀選 (複製) 提交
<kbd>&lt;enter&gt;</kbd>: 檢視所選項目的檔案
<kbd>/</kbd>: 開始搜尋
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製提交 hash 到剪貼簿 | |
| `` <space> `` | 檢出 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 複製提交屬性 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 在瀏覽器中開啟提交 | |
| `` n `` | 從提交建立新分支 | |
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | 複製提交 (揀選) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | 重設選定的揀選 (複製) 提交 | |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` <enter> `` | 檢視所選項目的檔案 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
## 子模組
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製子模組名稱到剪貼簿
<kbd>&lt;enter&gt;</kbd>: 進入子模組
<kbd>&lt;space&gt;</kbd>: 進入子模組
<kbd>d</kbd>: 移除子模組
<kbd>u</kbd>: 更新子模組
<kbd>n</kbd>: 新增子模組
<kbd>e</kbd>: 更新子模組 URL
<kbd>i</kbd>: 初始化子模組
<kbd>b</kbd>: 查看批量子模組選項
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製子模組名稱到剪貼簿 | |
| `` <enter> `` | Enter | 進入子模組 |
| `` d `` | Remove | Remove the selected submodule and its corresponding directory. |
| `` u `` | Update | 更新子模組 |
| `` n `` | 新增子模組 | |
| `` e `` | 更新子模組 URL | |
| `` i `` | Initialize | 初始化子模組 |
| `` b `` | 查看批量子模組選項 | |
| `` / `` | 搜尋 | |
## 工作目錄
| Key | Action | Info |
|-----|--------|-------------|
| `` n `` | New worktree | |
| `` <space> `` | Switch | Switch to the selected worktree. |
| `` o `` | 在編輯器中開啟 | |
| `` d `` | Remove | Remove the selected worktree. This will both delete the worktree's directory, as well as metadata about the worktree in the .git directory. |
| `` / `` | 搜尋 | |
## 提交
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製提交 SHA 到剪貼簿
<kbd>&lt;c-r&gt;</kbd>: 重設選定的揀選 (複製) 提交
<kbd>b</kbd>: 查看二分選項
<kbd>s</kbd>: 向下壓縮
<kbd>f</kbd>: 修復提交 (Fixup)
<kbd>r</kbd>: 改寫提交
<kbd>R</kbd>: 使用編輯器改寫提交
<kbd>d</kbd>: 刪除提交
<kbd>e</kbd>: 編輯提交
<kbd>p</kbd>: 挑選提交 (於變基過程中)
<kbd>F</kbd>: 為此提交建立修復提交
<kbd>S</kbd>: 壓縮上方所有的“fixup!”提交 (自動壓縮)
<kbd>&lt;c-j&gt;</kbd>: 向下移動提交
<kbd>&lt;c-k&gt;</kbd>: 向上移動提交
<kbd>v</kbd>: 貼上提交 (揀選)
<kbd>B</kbd>: Mark commit as base commit for rebase
<kbd>A</kbd>: 使用已預存的更改修正提交
<kbd>a</kbd>: 設置/重設提交作者
<kbd>t</kbd>: 還原提交
<kbd>T</kbd>: 打標籤到提交
<kbd>&lt;c-l&gt;</kbd>: 開啟記錄選單
<kbd>w</kbd>: View worktree options
<kbd>&lt;space&gt;</kbd>: 檢出提交
<kbd>y</kbd>: 複製提交屬性
<kbd>o</kbd>: 在瀏覽器中開啟提交
<kbd>n</kbd>: 從提交建立新分支
<kbd>g</kbd>: 檢視重設選項
<kbd>c</kbd>: 複製提交 (揀選)
<kbd>C</kbd>: 複製提交範圍 (揀選)
<kbd>&lt;enter&gt;</kbd>: 檢視所選項目的檔案
<kbd>/</kbd>: 開始搜尋
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製提交 hash 到剪貼簿 | |
| `` <c-r> `` | 重設選定的揀選 (複製) 提交 | |
| `` b `` | 查看二分選項 | |
| `` s `` | 壓縮 (Squash) | Squash the selected commit into the commit below it. The selected commit's message will be appended to the commit below it. |
| `` f `` | 修復 (Fixup) | Meld the selected commit into the commit below it. Similar to squash, but the selected commit's message will be discarded. |
| `` r `` | 改寫提交 | 改寫選中的提交訊息 |
| `` R `` | 使用編輯器改寫提交 | |
| `` d `` | 刪除提交 | Drop the selected commit. This will remove the commit from the branch via a rebase. If the commit makes changes that later commits depend on, you may need to resolve merge conflicts. |
| `` e `` | 編輯(開始互動變基) | 編輯提交 |
| `` i `` | 開始互動變基 | Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | 挑選 | 挑選提交 (於變基過程中) |
| `` F `` | 建立修復提交 | 為此提交建立修復提交 |
| `` S `` | 壓縮上方所有「fixup」提交自動壓縮 | 是否壓縮上方 {{.commit}} 所有「fixup」提交 |
| `` <c-j> `` | 向下移動提交 | |
| `` <c-k> `` | 向上移動提交 | |
| `` V `` | 貼上提交 (揀選) | |
| `` B `` | 為了變基已標注提交為基準提交 | 請為了下一次變基選擇一項基準提交;此將執行 `git rebase --onto`。 |
| `` A `` | 修改 | 使用已預存的更改修正提交 |
| `` a `` | 設定/重設提交作者 | Set/Reset commit author or set co-author. |
| `` t `` | 還原 | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. |
| `` T `` | 打標籤到提交 | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. |
| `` <c-l> `` | 開啟記錄選單 | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
| `` <space> `` | 檢出 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 複製提交屬性 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 在瀏覽器中開啟提交 | |
| `` n `` | 從提交建立新分支 | |
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | 複製提交 (揀選) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` <enter> `` | 檢視所選項目的檔案 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
## 提交摘要
<pre>
<kbd>&lt;enter&gt;</kbd>: 確認
<kbd>&lt;esc&gt;</kbd>: 關閉
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 確認 | |
| `` <esc> `` | 關閉 | |
## 提交檔案
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製提交的檔案名稱到剪貼簿
<kbd>c</kbd>: 檢出檔案
<kbd>d</kbd>: 捨棄此提交對此檔案的更改
<kbd>o</kbd>: 開啟檔案
<kbd>e</kbd>: 編輯檔案
<kbd>&lt;space&gt;</kbd>: 切換檔案是否包含在補丁中
<kbd>a</kbd>: 切換所有檔案是否包含在補丁中
<kbd>&lt;enter&gt;</kbd>: 輸入檔案以將選定的行添加至補丁(或切換目錄折疊)
<kbd>`</kbd>: 切換檔案樹狀視圖
<kbd>/</kbd>: 開始搜尋
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製檔案名稱到剪貼簿 | |
| `` c `` | 檢出 | 檢出檔案 |
| `` d `` | Remove | Discard this commit's changes to this file. This runs an interactive rebase in the background, so you may get a merge conflict if a later commit also changes this file. |
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
| `` e `` | 編輯 | 使用外部編輯器開啟 |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` <space> `` | 切換檔案是否包含在補丁中 | Toggle whether the file is included in the custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` a `` | 切換所有檔案是否包含在補丁中 | Add/remove all commit's files to custom patch. See https://github.com/jesseduffield/lazygit#rebase-magic-custom-patches. |
| `` <enter> `` | 輸入檔案以將選定的行添加至補丁(或切換目錄折疊) | If a file is selected, enter the file so that you can add/remove individual lines to the custom patch. If a directory is selected, toggle the directory. |
| `` ` `` | 顯示檔案樹狀視圖 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` / `` | 搜尋 | |
## 收藏 (Stash)
<pre>
<kbd>&lt;space&gt;</kbd>: 套用
<kbd>g</kbd>: 還原
<kbd>d</kbd>: 捨棄
<kbd>n</kbd>: 新分支
<kbd>r</kbd>: 重新命名收藏
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: 檢視所選項目的檔案
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 套用 | Apply the stash entry to your working directory. |
| `` g `` | 還原 | Apply the stash entry to your working directory and remove the stash entry. |
| `` d `` | 捨棄 | Remove the stash entry from the stash list. |
| `` n `` | 新分支 | Create a new branch from the selected stash entry. This works by git checking out the commit that the stash entry was created from, creating a new branch from that commit, then applying the stash entry to the new branch as an additional commit. |
| `` r `` | 重新命名收藏 | |
| `` <enter> `` | 檢視所選項目的檔案 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
## 日誌
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製提交 hash 到剪貼簿 | |
| `` <space> `` | 檢出 | Checkout the selected commit as a detached HEAD. |
| `` y `` | 複製提交屬性 | Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author). |
| `` o `` | 在瀏覽器中開啟提交 | |
| `` n `` | 從提交建立新分支 | |
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` C `` | 複製提交 (揀選) | Mark commit as copied. Then, within the local commits view, you can press `V` to paste (cherry-pick) the copied commit(s) into your checked out branch. At any time you can press `<esc>` to cancel the selection. |
| `` <c-r> `` | 重設選定的揀選 (複製) 提交 | |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` <enter> `` | 檢視提交 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
## 本地分支
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製分支名稱到剪貼簿
<kbd>i</kbd>: 顯示 git-flow 選項
<kbd>&lt;space&gt;</kbd>: 檢出
<kbd>n</kbd>: 新分支
<kbd>o</kbd>: 建立拉取請求
<kbd>O</kbd>: 建立拉取請求選項
<kbd>&lt;c-y&gt;</kbd>: 複製拉取請求的 URL 到剪貼板
<kbd>c</kbd>: 根據名稱檢出
<kbd>F</kbd>: 強制檢出
<kbd>d</kbd>: 刪除分支
<kbd>r</kbd>: 將已檢出的分支變基至此分支
<kbd>M</kbd>: 合併到當前檢出的分支
<kbd>f</kbd>: 從上游快進此分支
<kbd>T</kbd>: 建立標籤
<kbd>g</kbd>: 檢視重設選項
<kbd>R</kbd>: 重新命名分支
<kbd>u</kbd>: 設定/取消設定上游
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: 檢視提交
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製分支名稱到剪貼簿 | |
| `` i `` | 顯示 git-flow 選項 | |
| `` <space> `` | 檢出 | 檢出選定的項目。 |
| `` n `` | 新分支 | |
| `` o `` | 建立拉取請求 | |
| `` O `` | 建立拉取請求選項 | |
| `` <c-y> `` | 複製拉取請求的 URL 到剪貼板 | |
| `` c `` | 根據名稱檢出 | Checkout by name. In the input box you can enter '-' to switch to the last branch. |
| `` F `` | 強制檢出 | Force checkout selected branch. This will discard all local changes in your working directory before checking out the selected branch. |
| `` d `` | 刪除 | View delete options for local/remote branch. |
| `` r `` | 將已檢出的分支變基至此分支 | Rebase the checked-out branch onto the selected branch. |
| `` M `` | 合併到當前檢出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` f `` | 從上游快進此分支 | 從遠端快進所選的分支 |
| `` T `` | 建立標籤 | |
| `` s `` | 排序規則 | |
| `` g `` | 檢視重設選項 | |
| `` R `` | 重新命名分支 | |
| `` u `` | 檢視遠端設定 | 檢視有關遠端分支的設定(例如重設至遠端) |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` <enter> `` | 檢視提交 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
## 標籤
<pre>
<kbd>&lt;space&gt;</kbd>: 檢出
<kbd>d</kbd>: 刪除標籤
<kbd>P</kbd>: 推送標籤
<kbd>n</kbd>: 建立標籤
<kbd>g</kbd>: 檢視重設選項
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: 檢視提交
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <space> `` | 檢出 | Checkout the selected tag tag as a detached HEAD. |
| `` n `` | 建立標籤 | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. |
| `` d `` | 刪除 | View delete options for local/remote tag. |
| `` P `` | 推送標籤 | Push the selected tag to a remote. You'll be prompted to select a remote. |
| `` g `` | 重設 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` <enter> `` | 檢視提交 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |
## 檔案
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製檔案名稱到剪貼簿
<kbd>d</kbd>: 檢視“捨棄更改”的選項
<kbd>&lt;space&gt;</kbd>: 切換預存
<kbd>&lt;c-b&gt;</kbd>: 篩選檔案 (預存/未預存)
<kbd>c</kbd>: 提交變更
<kbd>w</kbd>: 沒有預提交 hook 就提交更改
<kbd>A</kbd>: 修正上次提交
<kbd>C</kbd>: 使用 git 編輯器提交變更
<kbd>e</kbd>: 編輯檔案
<kbd>o</kbd>: 開啟檔案
<kbd>i</kbd>: 忽略或排除檔案
<kbd>r</kbd>: 重新整理檔案
<kbd>s</kbd>: 收藏所有變更
<kbd>S</kbd>: 檢視收藏選項
<kbd>a</kbd>: 全部預存/取消預存
<kbd>&lt;enter&gt;</kbd>: 選擇檔案中的單個程式碼塊/行,或展開/折疊目錄
<kbd>g</kbd>: 檢視上游重設選項
<kbd>D</kbd>: 檢視重設選項
<kbd>`</kbd>: 切換檔案樹狀視圖
<kbd>M</kbd>: 開啟外部合併工具 (git mergetool)
<kbd>f</kbd>: 擷取
<kbd>/</kbd>: 開始搜尋
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製檔案名稱到剪貼簿 | |
| `` <space> `` | 切換預存 | Toggle staged for selected file. |
| `` <c-b> `` | 篩選檔案 (預存/未預存) | |
| `` y `` | 複製到剪貼簿 | |
| `` c `` | 提交變更 | 提交暫存區變更 |
| `` w `` | 沒有預提交 hook 就提交更改 | |
| `` A `` | 修改上次提交 | |
| `` C `` | 使用 git 編輯器提交變更 | |
| `` <c-f> `` | Find base commit for fixup | Find the commit that your current changes are building upon, for the sake of amending/fixing up the commit. This spares you from having to look through your branch's commits one-by-one to see which commit should be amended/fixed up. See docs: <https://github.com/jesseduffield/lazygit/tree/master/docs/Fixup_Commits.md> |
| `` e `` | 編輯 | 使用外部編輯器開啟 |
| `` o `` | 開啟檔案 | 使用預設軟體開啟 |
| `` i `` | 忽略或排除檔案 | |
| `` r `` | 重新整理檔案 | |
| `` s `` | 收藏 | Stash all changes. For other variations of stashing, use the view stash options keybinding. |
| `` S `` | 檢視收藏選項 | View stash options (e.g. stash all, stash staged, stash unstaged). |
| `` a `` | 全部預存/取消預存 | Toggle staged/unstaged for all files in working tree. |
| `` <enter> `` | 選擇檔案中的單個程式碼塊/行,或展開/折疊目錄 | If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it. |
| `` d `` | 捨棄 | 檢視選中變動進行捨棄復原 |
| `` g `` | 檢視遠端重設選項 | |
| `` D `` | 重設 | View reset options for working tree (e.g. nuking the working tree). |
| `` ` `` | 顯示檔案樹狀視圖 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory. |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` M `` | 開啟外部合併工具 | 執行 `git mergetool`。 |
| `` f `` | 擷取 | 同步遠端異動 |
| `` / `` | 搜尋 | |
## 狀態
<pre>
<kbd>o</kbd>: 開啟設定檔案
<kbd>e</kbd>: 編輯設定檔案
<kbd>u</kbd>: 檢查更新
<kbd>&lt;enter&gt;</kbd>: 切換到最近使用的版本庫
<kbd>a</kbd>: 顯示所有分支日誌
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` o `` | 開啟設定檔案 | 使用預設軟體開啟 |
| `` e `` | 編輯設定檔案 | 使用外部編輯器開啟 |
| `` u `` | 檢查更新 | |
| `` <enter> `` | 切換到最近使用的版本庫 | |
| `` a `` | 顯示所有分支日誌 | |
## 確認面板
<pre>
<kbd>&lt;enter&gt;</kbd>: 確認
<kbd>&lt;esc&gt;</kbd>: 關閉/取消
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | 確認 | |
| `` <esc> `` | 關閉/取消 | |
## 遠端
<pre>
<kbd>f</kbd>: 擷取遠端
<kbd>n</kbd>: 新增遠端
<kbd>d</kbd>: 移除遠端
<kbd>e</kbd>: 編輯遠端
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <enter> `` | View branches | |
| `` n `` | 新增遠端 | |
| `` d `` | Remove | Remove the selected remote. Any local branches tracking a remote branch from the remote will be unaffected. |
| `` e `` | 編輯 | 編輯遠端 |
| `` f `` | 擷取 | 擷取遠端 |
| `` / `` | 搜尋 | |
## 遠端分支
<pre>
<kbd>&lt;c-o&gt;</kbd>: 複製分支名稱到剪貼簿
<kbd>&lt;space&gt;</kbd>: 檢出
<kbd>n</kbd>: 新分支
<kbd>M</kbd>: 合併到當前檢出的分支
<kbd>r</kbd>: 將已檢出的分支變基至此分支
<kbd>d</kbd>: 刪除分支
<kbd>u</kbd>: 將此分支設為當前分支之上游
<kbd>g</kbd>: 檢視重設選項
<kbd>w</kbd>: View worktree options
<kbd>&lt;enter&gt;</kbd>: 檢視提交
<kbd>/</kbd>: Filter the current view by text
</pre>
| Key | Action | Info |
|-----|--------|-------------|
| `` <c-o> `` | 複製分支名稱到剪貼簿 | |
| `` <space> `` | 檢出 | Checkout a new local branch based on the selected remote branch, or the remote branch as a detached head. |
| `` n `` | 新分支 | |
| `` M `` | 合併到當前檢出的分支 | View options for merging the selected item into the current branch (regular merge, squash merge) |
| `` r `` | 將已檢出的分支變基至此分支 | Rebase the checked-out branch onto the selected branch. |
| `` d `` | 刪除 | Delete the remote branch from the remote. |
| `` u `` | 設置為遠端 | 將此分支設為當前分支之遠端 |
| `` s `` | 排序規則 | |
| `` g `` | 檢視重設選項 | View reset options (soft/mixed/hard) for resetting onto selected item. |
| `` <c-t> `` | 開啟外部差異工具 (git difftool) | |
| `` <enter> `` | 檢視提交 | |
| `` w `` | 檢視工作目錄選項 | |
| `` / `` | 搜尋 | |

38
go.mod
View File

@@ -1,31 +1,32 @@
module github.com/jesseduffield/lazygit
go 1.20
go 1.22
require (
github.com/OpenPeeDeeP/xdg v1.0.0
github.com/adrg/xdg v0.4.0
github.com/atotto/clipboard v0.1.4
github.com/aybabtme/humanlog v0.4.1
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
github.com/creack/pty v1.1.11
github.com/fsmiamoto/git-todo-parser v0.0.5
github.com/go-errors/errors v1.4.2
github.com/gdamore/tcell/v2 v2.7.4
github.com/go-errors/errors v1.5.1
github.com/gookit/color v1.4.2
github.com/iancoleman/orderedmap v0.3.0
github.com/imdario/mergo v0.3.11
github.com/integrii/flaggy v1.4.0
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
github.com/jesseduffield/gocui v0.3.1-0.20230806095321-ac7b03108825
github.com/jesseduffield/gocui v0.3.1-0.20240928100326-393cf89a5d3f
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
github.com/jesseduffield/yaml v2.1.0+incompatible
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3
github.com/kyokomi/emoji/v2 v2.2.8
github.com/lucasb-eyer/go-colorful v1.2.0
github.com/mattn/go-runewidth v0.0.15
github.com/mattn/go-runewidth v0.0.16
github.com/mgutz/str v1.2.0
github.com/pmezard/go-difflib v1.0.0
github.com/mitchellh/go-ps v1.0.0
github.com/sahilm/fuzzy v0.1.0
github.com/samber/lo v1.31.0
github.com/sanity-io/litter v1.5.2
@@ -33,42 +34,49 @@ require (
github.com/sirupsen/logrus v1.4.2
github.com/spf13/afero v1.9.5
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
github.com/stefanhaller/tcell/v2 v2.6.2-0.20230806061358-2dfa11eddb68
github.com/stretchr/testify v1.8.0
github.com/stefanhaller/git-todo-parser v0.0.7-0.20240406123903-fd957137b6e2
github.com/stretchr/testify v1.8.1
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8
golang.org/x/sync v0.8.0
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
github.com/fatih/color v1.9.0 // indirect
github.com/gdamore/encoding v1.0.0 // indirect
github.com/gdamore/encoding v1.0.1 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.0.0 // indirect
github.com/go-logfmt/logfmt v0.5.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/invopop/jsonschema v0.10.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.11 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/onsi/ginkgo v1.10.3 // indirect
github.com/onsi/gomega v1.7.1 // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/xanzy/ssh-agent v0.2.1 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/term v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/term v0.24.0 // indirect
golang.org/x/text v0.18.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)

79
go.sum
View File

@@ -38,8 +38,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OpenPeeDeeP/xdg v1.0.0 h1:UDLmNjCGFZZCaVMB74DqYEtXkHxnTxcr4FeJVF9uCn8=
github.com/OpenPeeDeeP/xdg v1.0.0/go.mod h1:tMoSueLQlMf0TCldjrJLNIjAc5qAOIcHt5REi88/Ygo=
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
@@ -51,6 +51,10 @@ github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn
github.com/aybabtme/humanlog v0.4.1 h1:D8d9um55rrthJsP8IGSHBcti9lTb/XknmDAX6Zy8tek=
github.com/aybabtme/humanlog v0.4.1/go.mod h1:B0bnQX4FTSU3oftPMTTPvENCy8LqixLDvYJA9TUCAGo=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
@@ -80,16 +84,17 @@ github.com/fatih/color v1.7.1-0.20180516100307-2d684516a886/go.mod h1:Zm6kSWBoL9
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsmiamoto/git-todo-parser v0.0.5 h1:Bhzd/vz/6Qm3udfkd6NO9fWfD3TpwR9ucp3N75/J5I8=
github.com/fsmiamoto/git-todo-parser v0.0.5/go.mod h1:B+AgTbNE2BARvJqzXygThzqxLIaEWvwr2sxKYYb0Fas=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU=
github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
@@ -166,6 +171,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
@@ -173,27 +180,30 @@ github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/integrii/flaggy v1.4.0 h1:A1x7SYx4jqu5NSrY14z8Z+0UyX2S5ygfJJrfolWR3zM=
github.com/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI=
github.com/invopop/jsonschema v0.10.0 h1:c1ktzNLBun3LyQQhyty5WE3lulbOdIIyOVlkmDLehcE=
github.com/invopop/jsonschema v0.10.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8TIcC6Y4RI+1ZbJDOHfGJ570tPeYVCqo7/tws=
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
github.com/jesseduffield/gocui v0.3.1-0.20230806095321-ac7b03108825 h1:4Ea8qV/BbZAGcXd8MAufDsbwwfz2pbRZdqIodC/XHZs=
github.com/jesseduffield/gocui v0.3.1-0.20230806095321-ac7b03108825/go.mod h1:trXE7RRGL2hTsv+Ntk+SHLtRobg9JE138n3Ug/X2Cf4=
github.com/jesseduffield/gocui v0.3.1-0.20240928100326-393cf89a5d3f h1:ZzsAUDwPFLPITKLcJpMSqt/3rERdI8YRZKr2l0plrls=
github.com/jesseduffield/gocui v0.3.1-0.20240928100326-393cf89a5d3f/go.mod h1:XtEbqCbn45keRXEu+OMZkjN5gw6AEob59afsgHjokZ8=
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5/go.mod h1:qxN4mHOAyeIDLP7IK7defgPClM/z1Kze8VVQiaEjzsQ=
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I=
github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE=
github.com/jesseduffield/yaml v2.1.0+incompatible/go.mod h1:w0xGhOSIJCGYYW+hnFPTutCy5aACpkcwbmORt5axGqk=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3 h1:s995u+gNQADMaixtNOs+jilRC/Q78q0UXSI7+4T0cDE=
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3/go.mod h1:MCbEh21gjOzxc31udr3u4QM9DAdf8TFJCZz3u5hYIxA=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
@@ -214,6 +224,8 @@ github.com/kyokomi/emoji/v2 v2.2.8 h1:jcofPxjHWEkJtkIbcLHvZhxKgCPl6C7MyjTrD4KDqU
github.com/kyokomi/emoji/v2 v2.2.8/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
@@ -223,13 +235,15 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -249,8 +263,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
@@ -268,12 +282,12 @@ github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stefanhaller/tcell/v2 v2.6.2-0.20230806061358-2dfa11eddb68 h1:NSTj9xAKUu85d6pAdNFyblL84BfiOB1rVnzxQO/cYUk=
github.com/stefanhaller/tcell/v2 v2.6.2-0.20230806061358-2dfa11eddb68/go.mod h1:PuJ7T6QKbsU7iVOHlhRoV3D/ipIAJsyiV4dbwcVaYj8=
github.com/stefanhaller/git-todo-parser v0.0.7-0.20240406123903-fd957137b6e2 h1:RTNWemd/9z9A5L/AggEP3OdhRz5dXETB/wdAlYF0SuM=
github.com/stefanhaller/git-todo-parser v0.0.7-0.20240406123903-fd957137b6e2/go.mod h1:HFt9hGqMzgQ+gVxMKcvTvGaFz4Y0yYycqqAp2V3wcJY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -281,10 +295,14 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/urfave/cli v1.20.1-0.20180226030253-8e01ec4cd3e2/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
@@ -347,6 +365,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -381,6 +400,7 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -403,6 +423,9 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20170407050850-f3918c30c5c2/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -447,17 +470,19 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -467,8 +492,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -520,6 +546,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -14,7 +14,6 @@ import (
"github.com/spf13/afero"
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
@@ -23,6 +22,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/env"
"github.com/jesseduffield/lazygit/pkg/gui"
"github.com/jesseduffield/lazygit/pkg/i18n"
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
"github.com/jesseduffield/lazygit/pkg/logs"
"github.com/jesseduffield/lazygit/pkg/updates"
)
@@ -42,7 +42,7 @@ func Run(
common *common.Common,
startArgs appTypes.StartArgs,
) {
app, err := NewApp(config, common)
app, err := NewApp(config, startArgs.IntegrationTest, common)
if err == nil {
err = app.Run(startArgs)
@@ -62,21 +62,21 @@ func Run(
func NewCommon(config config.AppConfigurer) (*common.Common, error) {
userConfig := config.GetUserConfig()
var err error
appState := config.GetAppState()
log := newLogger(config)
tr, err := i18n.NewTranslationSetFromConfig(log, userConfig.Gui.Language)
if err != nil {
return nil, err
}
// Initialize with English for the time being; the real translation set for
// the configured language will be read after reading the user config
tr := i18n.EnglishTranslationSet()
return &common.Common{
Log: log,
Tr: tr,
UserConfig: userConfig,
Debug: config.GetDebug(),
Fs: afero.NewOsFs(),
}, nil
cmn := &common.Common{
Log: log,
Tr: tr,
AppState: appState,
Debug: config.GetDebug(),
Fs: afero.NewOsFs(),
}
cmn.SetUserConfig(userConfig)
return cmn, nil
}
func newLogger(cfg config.AppConfigurer) *logrus.Entry {
@@ -92,7 +92,7 @@ func newLogger(cfg config.AppConfigurer) *logrus.Entry {
}
// NewApp bootstrap a new application
func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
func NewApp(config config.AppConfigurer, test integrationTypes.IntegrationTest, common *common.Common) (*App, error) {
app := &App{
closers: []io.Closer{},
Config: config,
@@ -116,7 +116,14 @@ func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
return app, err
}
showRecentRepos, err := app.setupRepo()
// If we're not in a repo, GetRepoPaths will return an error. The error is moot for us
// at this stage, since we'll try to init a new repo in setupRepo(), below
repoPaths, err := git_commands.GetRepoPaths(app.OSCommand.Cmd, gitVersion)
if err != nil {
common.Log.Infof("Error getting repo paths: %v", err)
}
showRecentRepos, err := app.setupRepo(repoPaths)
if err != nil {
return app, err
}
@@ -126,7 +133,7 @@ func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
showRecentRepos = true
}
app.Gui, err = gui.NewGui(common, config, gitVersion, updater, showRecentRepos, dirName)
app.Gui, err = gui.NewGui(common, config, gitVersion, updater, showRecentRepos, dirName, test)
if err != nil {
return app, err
}
@@ -165,14 +172,16 @@ func openRecentRepo(app *App) bool {
return false
}
func (app *App) setupRepo() (bool, error) {
func (app *App) setupRepo(
repoPaths *git_commands.RepoPaths,
) (bool, error) {
if env.GetGitDirEnv() != "" {
// we've been given the git dir directly. We'll verify this dir when initializing our Git object
// we've been given the git dir directly. Skip setup
return false, nil
}
// if we are not in a git repo, we ask if we want to `git init`
if err := commands.VerifyInGitRepo(app.OSCommand); err != nil {
if repoPaths == nil {
cwd, err := os.Getwd()
if err != nil {
return false, err
@@ -184,7 +193,7 @@ func (app *App) setupRepo() (bool, error) {
var shouldInitRepo bool
initialBranchArg := ""
switch app.UserConfig.NotARepository {
switch app.UserConfig().NotARepository {
case "prompt":
// Offer to initialize a new repository in current directory.
fmt.Print(app.Tr.CreateRepo)
@@ -218,6 +227,7 @@ func (app *App) setupRepo() (bool, error) {
if err := app.OSCommand.Cmd.New(args).Run(); err != nil {
return false, err
}
return false, nil
}
@@ -235,10 +245,7 @@ func (app *App) setupRepo() (bool, error) {
}
// Run this afterward so that the previous repo creation steps can run without this interfering
if isBare, err := git_commands.IsBareRepo(app.OSCommand); isBare {
if err != nil {
return false, err
}
if repoPaths.IsBareRepo() {
fmt.Print(app.Tr.BareRepo)

View File

@@ -8,11 +8,11 @@ import (
"os/exec"
"strconv"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/stefanhaller/git-todo-parser/todo"
)
// Sometimes lazygit will be invoked in daemon mode from a parent lazygit process.
@@ -33,12 +33,14 @@ const (
DaemonKindUnknown DaemonKind = iota
DaemonKindExitImmediately
DaemonKindRemoveUpdateRefsForCopiedBranch
DaemonKindCherryPick
DaemonKindMoveTodoUp
DaemonKindMoveTodoDown
DaemonKindMoveTodosUp
DaemonKindMoveTodosDown
DaemonKindInsertBreak
DaemonKindChangeTodoActions
DaemonKindMoveFixupCommitDown
DaemonKindWriteRebaseTodo
)
const (
@@ -52,13 +54,15 @@ func getInstruction() Instruction {
jsonData := os.Getenv(DaemonInstructionEnvKey)
mapping := map[DaemonKind]func(string) Instruction{
DaemonKindExitImmediately: deserializeInstruction[*ExitImmediatelyInstruction],
DaemonKindCherryPick: deserializeInstruction[*CherryPickCommitsInstruction],
DaemonKindChangeTodoActions: deserializeInstruction[*ChangeTodoActionsInstruction],
DaemonKindMoveFixupCommitDown: deserializeInstruction[*MoveFixupCommitDownInstruction],
DaemonKindMoveTodoUp: deserializeInstruction[*MoveTodoUpInstruction],
DaemonKindMoveTodoDown: deserializeInstruction[*MoveTodoDownInstruction],
DaemonKindInsertBreak: deserializeInstruction[*InsertBreakInstruction],
DaemonKindExitImmediately: deserializeInstruction[*ExitImmediatelyInstruction],
DaemonKindRemoveUpdateRefsForCopiedBranch: deserializeInstruction[*RemoveUpdateRefsForCopiedBranchInstruction],
DaemonKindCherryPick: deserializeInstruction[*CherryPickCommitsInstruction],
DaemonKindChangeTodoActions: deserializeInstruction[*ChangeTodoActionsInstruction],
DaemonKindMoveFixupCommitDown: deserializeInstruction[*MoveFixupCommitDownInstruction],
DaemonKindMoveTodosUp: deserializeInstruction[*MoveTodosUpInstruction],
DaemonKindMoveTodosDown: deserializeInstruction[*MoveTodosDownInstruction],
DaemonKindInsertBreak: deserializeInstruction[*InsertBreakInstruction],
DaemonKindWriteRebaseTodo: deserializeInstruction[*WriteRebaseTodoInstruction],
}
return mapping[getDaemonKind()](jsonData)
@@ -155,6 +159,26 @@ func NewExitImmediatelyInstruction() Instruction {
return &ExitImmediatelyInstruction{}
}
type RemoveUpdateRefsForCopiedBranchInstruction struct{}
func (self *RemoveUpdateRefsForCopiedBranchInstruction) Kind() DaemonKind {
return DaemonKindRemoveUpdateRefsForCopiedBranch
}
func (self *RemoveUpdateRefsForCopiedBranchInstruction) SerializedInstructions() string {
return serializeInstruction(self)
}
func (self *RemoveUpdateRefsForCopiedBranchInstruction) run(common *common.Common) error {
return handleInteractiveRebase(common, func(path string) error {
return nil
})
}
func NewRemoveUpdateRefsForCopiedBranchInstruction() Instruction {
return &RemoveUpdateRefsForCopiedBranchInstruction{}
}
type CherryPickCommitsInstruction struct {
Todo string
}
@@ -208,28 +232,32 @@ func (self *ChangeTodoActionsInstruction) SerializedInstructions() string {
func (self *ChangeTodoActionsInstruction) run(common *common.Common) error {
return handleInteractiveRebase(common, func(path string) error {
for _, c := range self.Changes {
if err := utils.EditRebaseTodo(path, c.Sha, todo.Pick, c.NewAction, getCommentChar()); err != nil {
return err
changes := lo.Map(self.Changes, func(c ChangeTodoAction, _ int) utils.TodoChange {
return utils.TodoChange{
Hash: c.Hash,
OldAction: todo.Pick,
NewAction: c.NewAction,
}
}
})
return nil
return utils.EditRebaseTodo(path, changes, getCommentChar())
})
}
// Takes the sha of some commit, and the sha of a fixup commit that was created
// Takes the hash of some commit, and the hash of a fixup commit that was created
// at the end of the branch, then moves the fixup commit down to right after the
// original commit, changing its type to "fixup"
// original commit, changing its type to "fixup" (only if ChangeToFixup is true)
type MoveFixupCommitDownInstruction struct {
OriginalSha string
FixupSha string
OriginalHash string
FixupHash string
ChangeToFixup bool
}
func NewMoveFixupCommitDownInstruction(originalSha string, fixupSha string) Instruction {
func NewMoveFixupCommitDownInstruction(originalHash string, fixupHash string, changeToFixup bool) Instruction {
return &MoveFixupCommitDownInstruction{
OriginalSha: originalSha,
FixupSha: fixupSha,
OriginalHash: originalHash,
FixupHash: fixupHash,
ChangeToFixup: changeToFixup,
}
}
@@ -243,55 +271,69 @@ func (self *MoveFixupCommitDownInstruction) SerializedInstructions() string {
func (self *MoveFixupCommitDownInstruction) run(common *common.Common) error {
return handleInteractiveRebase(common, func(path string) error {
return utils.MoveFixupCommitDown(path, self.OriginalSha, self.FixupSha, getCommentChar())
return utils.MoveFixupCommitDown(path, self.OriginalHash, self.FixupHash, self.ChangeToFixup, getCommentChar())
})
}
type MoveTodoUpInstruction struct {
Sha string
type MoveTodosUpInstruction struct {
Hashes []string
}
func NewMoveTodoUpInstruction(sha string) Instruction {
return &MoveTodoUpInstruction{
Sha: sha,
func NewMoveTodosUpInstruction(hashes []string) Instruction {
return &MoveTodosUpInstruction{
Hashes: hashes,
}
}
func (self *MoveTodoUpInstruction) Kind() DaemonKind {
return DaemonKindMoveTodoUp
func (self *MoveTodosUpInstruction) Kind() DaemonKind {
return DaemonKindMoveTodosUp
}
func (self *MoveTodoUpInstruction) SerializedInstructions() string {
func (self *MoveTodosUpInstruction) SerializedInstructions() string {
return serializeInstruction(self)
}
func (self *MoveTodoUpInstruction) run(common *common.Common) error {
func (self *MoveTodosUpInstruction) run(common *common.Common) error {
todosToMove := lo.Map(self.Hashes, func(hash string, _ int) utils.Todo {
return utils.Todo{
Hash: hash,
Action: todo.Pick,
}
})
return handleInteractiveRebase(common, func(path string) error {
return utils.MoveTodoUp(path, self.Sha, todo.Pick, getCommentChar())
return utils.MoveTodosUp(path, todosToMove, getCommentChar())
})
}
type MoveTodoDownInstruction struct {
Sha string
type MoveTodosDownInstruction struct {
Hashes []string
}
func NewMoveTodoDownInstruction(sha string) Instruction {
return &MoveTodoDownInstruction{
Sha: sha,
func NewMoveTodosDownInstruction(hashes []string) Instruction {
return &MoveTodosDownInstruction{
Hashes: hashes,
}
}
func (self *MoveTodoDownInstruction) Kind() DaemonKind {
return DaemonKindMoveTodoDown
func (self *MoveTodosDownInstruction) Kind() DaemonKind {
return DaemonKindMoveTodosDown
}
func (self *MoveTodoDownInstruction) SerializedInstructions() string {
func (self *MoveTodosDownInstruction) SerializedInstructions() string {
return serializeInstruction(self)
}
func (self *MoveTodoDownInstruction) run(common *common.Common) error {
func (self *MoveTodosDownInstruction) run(common *common.Common) error {
todosToMove := lo.Map(self.Hashes, func(hash string, _ int) utils.Todo {
return utils.Todo{
Hash: hash,
Action: todo.Pick,
}
})
return handleInteractiveRebase(common, func(path string) error {
return utils.MoveTodoDown(path, self.Sha, todo.Pick, getCommentChar())
return utils.MoveTodosDown(path, todosToMove, getCommentChar())
})
}
@@ -314,3 +356,27 @@ func (self *InsertBreakInstruction) run(common *common.Common) error {
return utils.PrependStrToTodoFile(path, []byte("break\n"))
})
}
type WriteRebaseTodoInstruction struct {
TodosFileContent []byte
}
func NewWriteRebaseTodoInstruction(todosFileContent []byte) Instruction {
return &WriteRebaseTodoInstruction{
TodosFileContent: todosFileContent,
}
}
func (self *WriteRebaseTodoInstruction) Kind() DaemonKind {
return DaemonKindWriteRebaseTodo
}
func (self *WriteRebaseTodoInstruction) SerializedInstructions() string {
return serializeInstruction(self)
}
func (self *WriteRebaseTodoInstruction) run(common *common.Common) error {
return handleInteractiveRebase(common, func(path string) error {
return os.WriteFile(path, self.TodosFileContent, 0o644)
})
}

View File

@@ -5,11 +5,12 @@ import (
"path/filepath"
"strings"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/env"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/stefanhaller/git-todo-parser/todo"
)
type TodoLine struct {
@@ -21,7 +22,7 @@ func (self *TodoLine) ToString() string {
if self.Action == "break" {
return self.Action + "\n"
} else {
return self.Action + " " + self.Commit.Sha + " " + self.Commit.Name + "\n"
return self.Action + " " + self.Commit.Hash + " " + self.Commit.Name + "\n"
}
}
@@ -34,7 +35,7 @@ func TodoLinesToString(todoLines []TodoLine) string {
}
type ChangeTodoAction struct {
Sha string
Hash string
NewAction todo.TodoCommand
}
@@ -44,6 +45,10 @@ func handleInteractiveRebase(common *common.Common, f func(path string) error) e
path := os.Args[1]
if strings.HasSuffix(path, "git-rebase-todo") {
err := utils.RemoveUpdateRefsForCopiedBranch(path, getCommentChar())
if err != nil {
return err
}
return f(path)
} else if strings.HasSuffix(path, filepath.Join(gitDir(), "COMMIT_EDITMSG")) { // TODO: test
// if we are rebasing and squashing, we'll see a COMMIT_EDITMSG

View File

@@ -4,6 +4,8 @@ import (
"bytes"
"fmt"
"log"
"net/http"
_ "net/http/pprof"
"os"
"os/exec"
"path/filepath"
@@ -30,6 +32,7 @@ type cliArgs struct {
PrintVersionInfo bool
Debug bool
TailLogs bool
Profile bool
PrintDefaultConfig bool
PrintConfigDir bool
UseConfigDir string
@@ -133,6 +136,12 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
if integrationTest != nil {
integrationTest.SetupConfig(appConfig)
// Preserve the changes that the test setup just made to the config, so
// they don't get lost when we reload the config while running the test
// (which happens when switching between repos, going in and out of
// submodules, etc).
appConfig.SaveGlobalUserConfig()
}
common, err := NewCommon(appConfig)
@@ -145,6 +154,14 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
return
}
if cliArgs.Profile {
go func() {
if err := http.ListenAndServe("localhost:6060", nil); err != nil {
log.Fatal(err)
}
}()
}
parsedGitArg := parseGitArg(cliArgs.GitArg)
Run(appConfig, common, appTypes.NewStartArgs(cliArgs.FilterPath, parsedGitArg, integrationTest))
@@ -171,6 +188,9 @@ func parseCliArgsAndEnvVars() *cliArgs {
tailLogs := false
flaggy.Bool(&tailLogs, "l", "logs", "Tail lazygit logs (intended to be used when `lazygit --debug` is called in a separate terminal tab)")
profile := false
flaggy.Bool(&profile, "", "profile", "Start the profiler and serve it on http port 6060. See CONTRIBUTING.md for more info.")
printDefaultConfig := false
flaggy.Bool(&printDefaultConfig, "c", "config", "Print the default config")
@@ -180,10 +200,10 @@ func parseCliArgsAndEnvVars() *cliArgs {
useConfigDir := ""
flaggy.String(&useConfigDir, "ucd", "use-config-dir", "override default config directory with provided directory")
workTree := ""
workTree := os.Getenv("GIT_WORK_TREE")
flaggy.String(&workTree, "w", "work-tree", "equivalent of the --work-tree git argument")
gitDir := ""
gitDir := os.Getenv("GIT_DIR")
flaggy.String(&gitDir, "g", "git-dir", "equivalent of the --git-dir git argument")
customConfigFile := ""
@@ -202,6 +222,7 @@ func parseCliArgsAndEnvVars() *cliArgs {
PrintVersionInfo: printVersionInfo,
Debug: debug,
TailLogs: tailLogs,
Profile: profile,
PrintDefaultConfig: printDefaultConfig,
PrintConfigDir: printConfigDir,
UseConfigDir: useConfigDir,
@@ -263,7 +284,7 @@ func mergeBuildInfo(buildInfo *BuildInfo) {
buildInfo.Commit = revision.Value
// if lazygit was built from source we'll show the version as the
// abbreviated commit hash
buildInfo.Version = utils.ShortSha(revision.Value)
buildInfo.Version = utils.ShortHash(revision.Value)
}
// if version hasn't been set we assume that neither has the date

View File

@@ -27,6 +27,10 @@ func knownError(tr *i18n.TranslationSet, err error) (string, bool) {
originalError: "fatal: not a git repository",
newError: tr.NotARepository,
},
{
originalError: "getwd: no such file or directory",
newError: tr.WorkingDirectoryDoesNotExist,
},
}
if mapping, ok := lo.Find(mappings, func(mapping errorMapping) bool {

View File

@@ -1,77 +0,0 @@
package cheatsheet
import (
"fmt"
"io/fs"
"log"
"os"
"path/filepath"
"regexp"
"github.com/pmezard/go-difflib/difflib"
)
func Check() {
dir := GetKeybindingsDir()
tmpDir := filepath.Join(os.TempDir(), "lazygit_cheatsheet")
err := os.RemoveAll(tmpDir)
if err != nil {
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
}
err = os.Mkdir(tmpDir, 0o700)
if err != nil {
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
}
generateAtDir(tmpDir)
defer os.RemoveAll(tmpDir)
actualContent := obtainContent(dir)
expectedContent := obtainContent(tmpDir)
if expectedContent == "" {
log.Fatal("empty expected content")
}
if actualContent != expectedContent {
err := difflib.WriteUnifiedDiff(os.Stdout, difflib.UnifiedDiff{
A: difflib.SplitLines(expectedContent),
B: difflib.SplitLines(actualContent),
FromFile: "Expected",
FromDate: "",
ToFile: "Actual",
ToDate: "",
Context: 1,
})
if err != nil {
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
}
fmt.Printf("\nCheatsheets are out of date. Please run `%s` at the project root and commit the changes. If you run the script and no keybindings files are updated as a result, try rebasing onto master and trying again.\n", CommandToRun())
os.Exit(1)
}
fmt.Println("\nCheatsheets are up to date")
}
func obtainContent(dir string) string {
re := regexp.MustCompile(`Keybindings_\w+\.md$`)
content := ""
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
if re.MatchString(path) {
bytes, err := os.ReadFile(path)
if err != nil {
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
}
content += fmt.Sprintf("\n%s\n\n", filepath.Base(path))
content += string(bytes)
}
return nil
})
if err != nil {
log.Fatalf("Error occurred while checking if cheatsheets are up to date: %v", err)
}
return content
}

View File

@@ -1,10 +1,12 @@
// This "script" generates a file called Keybindings_{{.LANG}}.md
// in current working directory.
//go:generate go run generator.go
// This "script" generates files called Keybindings_{{.LANG}}.md
// in the docs/keybindings directory.
//
// The content of this generated file is a keybindings cheatsheet.
// The content of these generated files is a keybindings cheatsheet.
//
// To generate cheatsheet in english run:
// go run scripts/generate_cheatsheet.go
// To generate the cheatsheets, run:
// go generate pkg/cheatsheet/generate.go
package cheatsheet
@@ -12,7 +14,6 @@ import (
"fmt"
"log"
"os"
"strings"
"github.com/jesseduffield/generics/maps"
"github.com/jesseduffield/lazycore/pkg/utils"
@@ -42,7 +43,7 @@ type headerWithBindings struct {
}
func CommandToRun() string {
return "go run scripts/cheatsheet/main.go generate"
return "go generate ./..."
}
func GetKeybindingsDir() string {
@@ -50,7 +51,10 @@ func GetKeybindingsDir() string {
}
func generateAtDir(cheatsheetDir string) {
translationSetsByLang := i18n.GetTranslationSets()
translationSetsByLang, err := i18n.GetTranslationSets()
if err != nil {
log.Fatal(err)
}
mConfig := config.NewDummyAppConfig()
for lang := range translationSetsByLang {
@@ -59,7 +63,12 @@ func generateAtDir(cheatsheetDir string) {
if err != nil {
log.Fatal(err)
}
mApp, _ := app.NewApp(mConfig, common)
tr, err := i18n.NewTranslationSetFromConfig(common.Log, lang)
if err != nil {
log.Fatal(err)
}
common.Tr = tr
mApp, _ := app.NewApp(mConfig, nil, common)
path := cheatsheetDir + "/Keybindings_" + lang + ".md"
file, err := os.Create(path)
if err != nil {
@@ -189,11 +198,11 @@ func formatSections(tr *i18n.TranslationSet, bindingSections []*bindingSection)
for _, section := range bindingSections {
content += formatTitle(section.title)
content += "<pre>\n"
content += "| Key | Action | Info |\n"
content += "|-----|--------|-------------|\n"
for _, binding := range section.bindings {
content += formatBinding(binding)
}
content += "</pre>\n"
}
return content
@@ -204,19 +213,15 @@ func formatTitle(title string) string {
}
func formatBinding(binding *types.Binding) string {
result := fmt.Sprintf(" <kbd>%s</kbd>: %s", escapeAngleBrackets(keybindings.LabelFromKey(binding.Key)), binding.Description)
action := keybindings.LabelFromKey(binding.Key)
description := binding.Description
if binding.Alternative != "" {
result += fmt.Sprintf(" (%s)", binding.Alternative)
action += fmt.Sprintf(" (%s)", binding.Alternative)
}
result += "\n"
return result
}
func escapeAngleBrackets(str string) string {
result := strings.ReplaceAll(str, ">", "&gt;")
result = strings.ReplaceAll(result, "<", "&lt;")
return result
// Use backticks for keyboard keys. Two backticks are needed with an inner space
// to escape a key that is itself a backtick.
return fmt.Sprintf("| `` %s `` | %s | %s |\n", action, description, binding.Tooltip)
}
func italicize(str string) string {

View File

@@ -262,7 +262,7 @@ func TestGetBindingSections(t *testing.T) {
for _, test := range tests {
t.Run(test.testName, func(t *testing.T) {
actual := getBindingSections(test.bindings, &tr)
actual := getBindingSections(test.bindings, tr)
assert.EqualValues(t, test.expected, actual)
})
}

View File

@@ -0,0 +1,14 @@
//go:build ignore
package main
import (
"fmt"
"github.com/jesseduffield/lazygit/pkg/cheatsheet"
)
func main() {
fmt.Printf("Generating cheatsheets in %s...\n", cheatsheet.GetKeybindingsDir())
cheatsheet.Generate()
}

View File

@@ -2,13 +2,9 @@ package commands
import (
"os"
"path"
"path/filepath"
"strings"
"github.com/go-errors/errors"
"github.com/sasha-s/go-deadlock"
"github.com/spf13/afero"
gogit "github.com/jesseduffield/go-git/v5"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
@@ -16,12 +12,12 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/commands/patch"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/env"
"github.com/jesseduffield/lazygit/pkg/utils"
)
// GitCommand is our main git interface
type GitCommand struct {
Blame *git_commands.BlameCommands
Branch *git_commands.BranchCommands
Commit *git_commands.CommitCommands
Config *git_commands.ConfigCommands
@@ -63,43 +59,17 @@ func NewGitCommand(
version *git_commands.GitVersion,
osCommand *oscommands.OSCommand,
gitConfig git_config.IGitConfig,
syncMutex *deadlock.Mutex,
) (*GitCommand, error) {
currentPath, err := os.Getwd()
if err != nil {
return nil, utils.WrapError(err)
}
// converting to forward slashes for the sake of windows (which uses backwards slashes). We want everything
// to have forward slashes internally
currentPath = filepath.ToSlash(currentPath)
gitDir := env.GetGitDirEnv()
if gitDir != "" {
// we've been given the git directory explicitly so no need to navigate to it
_, err := cmn.Fs.Stat(gitDir)
if err != nil {
return nil, utils.WrapError(err)
}
} else {
// we haven't been given the git dir explicitly so we assume it's in the current working directory as `.git/` (or an ancestor directory)
rootDirectory, err := findWorktreeRoot(cmn.Fs, currentPath)
if err != nil {
return nil, utils.WrapError(err)
}
currentPath = rootDirectory
err = os.Chdir(rootDirectory)
if err != nil {
return nil, utils.WrapError(err)
}
}
repoPaths, err := git_commands.GetRepoPaths(cmn.Fs, currentPath)
repoPaths, err := git_commands.GetRepoPaths(osCommand.Cmd, version)
if err != nil {
return nil, errors.Errorf("Error getting repo paths: %v", err)
}
err = os.Chdir(repoPaths.WorktreePath())
if err != nil {
return nil, utils.WrapError(err)
}
repository, err := gogit.PlainOpenWithOptions(
repoPaths.WorktreeGitDirPath(),
&gogit.PlainOpenOptions{DetectDotGit: false, EnableDotGitCommonDir: true},
@@ -118,7 +88,6 @@ func NewGitCommand(
gitConfig,
repoPaths,
repository,
syncMutex,
), nil
}
@@ -129,7 +98,6 @@ func NewGitCommandAux(
gitConfig git_config.IGitConfig,
repoPaths *git_commands.RepoPaths,
repo *gogit.Repository,
syncMutex *deadlock.Mutex,
) *GitCommand {
cmd := NewGitCmdObjBuilder(cmn.Log, osCommand.Cmd)
@@ -140,7 +108,7 @@ func NewGitCommandAux(
// common ones are: cmn, osCommand, dotGitDir, configCommands
configCommands := git_commands.NewConfigCommands(cmn, gitConfig, repo)
gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, repo, configCommands, syncMutex)
gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, repo, configCommands)
fileLoader := git_commands.NewFileLoader(gitCommon, cmd, configCommands)
statusCommands := git_commands.NewStatusCommands(gitCommon)
@@ -159,15 +127,14 @@ func NewGitCommandAux(
stashCommands := git_commands.NewStashCommands(gitCommon, fileLoader, workingTreeCommands)
patchBuilder := patch.NewPatchBuilder(cmn.Log,
func(from string, to string, reverse bool, filename string, plain bool) (string, error) {
// TODO: make patch builder take Gui.IgnoreWhitespaceInDiffView into
// account. For now we just pass false.
return workingTreeCommands.ShowFileDiff(from, to, reverse, filename, plain, false)
return workingTreeCommands.ShowFileDiff(from, to, reverse, filename, plain)
})
patchCommands := git_commands.NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder)
bisectCommands := git_commands.NewBisectCommands(gitCommon)
worktreeCommands := git_commands.NewWorktreeCommands(gitCommon)
blameCommands := git_commands.NewBlameCommands(gitCommon)
branchLoader := git_commands.NewBranchLoader(cmn, cmd, branchCommands.CurrentBranchInfo, configCommands)
branchLoader := git_commands.NewBranchLoader(cmn, gitCommon, cmd, branchCommands.CurrentBranchInfo, configCommands)
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
commitLoader := git_commands.NewCommitLoader(cmn, cmd, statusCommands.RebaseMode, gitCommon)
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)
@@ -177,6 +144,7 @@ func NewGitCommandAux(
tagLoader := git_commands.NewTagLoader(cmn, cmd)
return &GitCommand{
Blame: blameCommands,
Branch: branchCommands,
Commit: commitCommands,
Config: configCommands,
@@ -211,32 +179,6 @@ func NewGitCommandAux(
}
}
// this returns the root of the current worktree. So if you start lazygit from within
// a subdirectory of the worktree, it will start in the context of the root of that worktree
func findWorktreeRoot(fs afero.Fs, currentPath string) (string, error) {
for {
// we don't care if .git is a directory or a file: either is okay.
_, err := fs.Stat(path.Join(currentPath, ".git"))
if err == nil {
return currentPath, nil
}
if !os.IsNotExist(err) {
return "", utils.WrapError(err)
}
currentPath = path.Dir(currentPath)
atRoot := currentPath == path.Dir(currentPath)
if atRoot {
// we should never really land here: the code that creates GitCommand should
// verify we're in a git directory
return "", errors.New("Must open lazygit in a git repository")
}
}
}
func VerifyInGitRepo(osCommand *oscommands.OSCommand) error {
return osCommand.Cmd.New(git_commands.NewGitCmd("rev-parse").Arg("--git-dir").ToArgv()).DontLog().Run()
}

View File

@@ -38,6 +38,10 @@ func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj {
return self.innerBuilder.NewShell(cmdStr).AddEnvVars(defaultEnvVar)
}
func (self *gitCmdObjBuilder) NewInteractiveShell(cmdStr string) oscommands.ICmdObj {
return self.innerBuilder.NewInteractiveShell(cmdStr).AddEnvVars(defaultEnvVar)
}
func (self *gitCmdObjBuilder) Quote(str string) string {
return self.innerBuilder.Quote(str)
}

View File

@@ -76,7 +76,7 @@ func (self *BisectCommands) GetInfoForGitDir(gitDir string) *BisectInfo {
return info
}
sha := strings.TrimSpace(string(fileContent))
hash := strings.TrimSpace(string(fileContent))
if name == info.newTerm {
status = BisectStatusNew
@@ -86,7 +86,7 @@ func (self *BisectCommands) GetInfoForGitDir(gitDir string) *BisectInfo {
status = BisectStatusSkipped
}
info.statusMap[sha] = status
info.statusMap[hash] = status
}
currentContent, err := os.ReadFile(filepath.Join(gitDir, "BISECT_EXPECTED_REV"))
@@ -94,8 +94,8 @@ func (self *BisectCommands) GetInfoForGitDir(gitDir string) *BisectInfo {
self.Log.Infof("error getting git bisect info: %s", err.Error())
return info
}
currentSha := strings.TrimSpace(string(currentContent))
info.current = currentSha
currentHash := strings.TrimSpace(string(currentContent))
info.current = currentHash
return info
}
@@ -135,7 +135,7 @@ func (self *BisectCommands) StartWithTerms(oldTerm string, newTerm string) error
}
// tells us whether we've found our problem commit(s). We return a string slice of
// commit sha's if we're done, and that slice may have more that one item if
// commit hashes if we're done, and that slice may have more that one item if
// skipped commits are involved.
func (self *BisectCommands) IsDone() (bool, []string, error) {
info := self.GetInfo()
@@ -143,8 +143,8 @@ func (self *BisectCommands) IsDone() (bool, []string, error) {
return false, nil, nil
}
newSha := info.GetNewSha()
if newSha == "" {
newHash := info.GetNewHash()
if newHash == "" {
return false, nil, nil
}
@@ -153,14 +153,14 @@ func (self *BisectCommands) IsDone() (bool, []string, error) {
done := false
candidates := []string{}
cmdArgs := NewGitCmd("rev-list").Arg(newSha).ToArgv()
cmdArgs := NewGitCmd("rev-list").Arg(newHash).ToArgv()
err := self.cmd.New(cmdArgs).RunAndProcessLines(func(line string) (bool, error) {
sha := strings.TrimSpace(line)
hash := strings.TrimSpace(line)
if status, ok := info.statusMap[sha]; ok {
if status, ok := info.statusMap[hash]; ok {
switch status {
case BisectStatusSkipped, BisectStatusNew:
candidates = append(candidates, sha)
candidates = append(candidates, hash)
return false, nil
case BisectStatusOld:
done = true
@@ -185,7 +185,7 @@ func (self *BisectCommands) IsDone() (bool, []string, error) {
// render the commits from the bad commit.
func (self *BisectCommands) ReachableFromStart(bisectInfo *BisectInfo) bool {
cmdArgs := NewGitCmd("merge-base").
Arg("--is-ancestor", bisectInfo.GetNewSha(), bisectInfo.GetStartSha()).
Arg("--is-ancestor", bisectInfo.GetNewHash(), bisectInfo.GetStartHash()).
ToArgv()
err := self.cmd.New(cmdArgs).DontLog().Run()

View File

@@ -29,10 +29,10 @@ type BisectInfo struct {
newTerm string // 'bad' by default
oldTerm string // 'good' by default
// map of commit sha's to their status
// map of commit hashes to their status
statusMap map[string]BisectStatus
// the sha of the commit that's under test
// the hash of the commit that's under test
current string
}
@@ -49,26 +49,26 @@ func NewNullBisectInfo() *BisectInfo {
return &BisectInfo{started: false}
}
func (self *BisectInfo) GetNewSha() string {
for sha, status := range self.statusMap {
func (self *BisectInfo) GetNewHash() string {
for hash, status := range self.statusMap {
if status == BisectStatusNew {
return sha
return hash
}
}
return ""
}
func (self *BisectInfo) GetCurrentSha() string {
func (self *BisectInfo) GetCurrentHash() string {
return self.current
}
func (self *BisectInfo) GetStartSha() string {
func (self *BisectInfo) GetStartHash() string {
return self.start
}
func (self *BisectInfo) Status(commitSha string) (BisectStatus, bool) {
status, ok := self.statusMap[commitSha]
func (self *BisectInfo) Status(commitHash string) (BisectStatus, bool) {
status, ok := self.statusMap[commitHash]
return status, ok
}
@@ -93,7 +93,7 @@ func (self *BisectInfo) Bisecting() bool {
return false
}
if self.GetNewSha() == "" {
if self.GetNewHash() == "" {
return false
}

View File

@@ -0,0 +1,33 @@
package git_commands
import (
"fmt"
)
type BlameCommands struct {
*GitCommon
}
func NewBlameCommands(gitCommon *GitCommon) *BlameCommands {
return &BlameCommands{
GitCommon: gitCommon,
}
}
// Blame a range of lines. For each line, output the hash of the commit where
// the line last changed, then a space, then a description of the commit (author
// and date), another space, and then the line. For example:
//
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 11) func NewBlameCommands(gitCommon *GitCommon) *BlameCommands {
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 12) return &BlameCommands{
// ac90ebac688fe8bc2ffd922157a9d2c54681d2aa (Stefan Haller 2023-08-01 14:54:56 +0200 13) GitCommon: gitCommon,
func (self *BlameCommands) BlameLineRange(filename string, commit string, firstLine int, numLines int) (string, error) {
cmdArgs := NewGitCmd("blame").
Arg("-l").
Arg(fmt.Sprintf("-L%d,+%d", firstLine, numLines)).
Arg(commit).
Arg("--").
Arg(filename)
return self.cmd.New(cmdArgs.ToArgv()).RunWithOutput()
}

View File

@@ -4,13 +4,16 @@ import (
"fmt"
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/mgutz/str"
"github.com/samber/lo"
)
type BranchCommands struct {
*GitCommon
allBranchesLogCmdIndex uint8 // keeps track of current all branches log command
}
func NewBranchCommands(gitCommon *GitCommon) *BranchCommands {
@@ -28,6 +31,26 @@ func (self *BranchCommands) New(name string, base string) error {
return self.cmd.New(cmdArgs).Run()
}
func (self *BranchCommands) NewWithoutTracking(name string, base string) error {
cmdArgs := NewGitCmd("checkout").
Arg("-b", name, base).
Arg("--no-track").
ToArgv()
return self.cmd.New(cmdArgs).Run()
}
// CreateWithUpstream creates a new branch with a given upstream, but without
// checking it out
func (self *BranchCommands) CreateWithUpstream(name string, upstream string) error {
cmdArgs := NewGitCmd("branch").
Arg("--track").
Arg(name, upstream).
ToArgv()
return self.cmd.New(cmdArgs).Run()
}
// CurrentBranchInfo get the current branch information.
func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
branchName, err := self.cmd.New(
@@ -54,10 +77,10 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
for _, line := range utils.SplitLines(output) {
split := strings.Split(strings.TrimRight(line, "\r\n"), "\x00")
if len(split) == 3 && split[0] == "*" {
sha := split[1]
hash := split[1]
displayName := split[2]
return BranchInfo{
RefName: sha,
RefName: hash,
DisplayName: displayName,
DetachedHead: true,
}, nil
@@ -85,8 +108,8 @@ func (self *BranchCommands) CurrentBranchName() (string, error) {
return "", err
}
// Delete delete branch
func (self *BranchCommands) Delete(branch string, force bool) error {
// LocalDelete delete branch locally
func (self *BranchCommands) LocalDelete(branch string, force bool) error {
cmdArgs := NewGitCmd("branch").
ArgIfElse(force, "-D", "-d").
Arg(branch).
@@ -123,7 +146,7 @@ func (self *BranchCommands) GetGraph(branchName string) (string, error) {
}
func (self *BranchCommands) GetGraphCmdObj(branchName string) oscommands.ICmdObj {
branchLogCmdTemplate := self.UserConfig.Git.BranchLogCmd
branchLogCmdTemplate := self.UserConfig().Git.BranchLogCmd
templateValues := map[string]string{
"branchName": self.cmd.Quote(branchName),
}
@@ -205,13 +228,18 @@ func (self *BranchCommands) Rename(oldName string, newName string) error {
type MergeOpts struct {
FastForwardOnly bool
Squash bool
}
func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
if opts.Squash && opts.FastForwardOnly {
panic("Squash and FastForwardOnly can't both be true")
}
cmdArgs := NewGitCmd("merge").
Arg("--no-edit").
ArgIf(self.UserConfig.Git.Merging.Args != "", self.UserConfig.Git.Merging.Args).
Arg(strings.Fields(self.UserConfig().Git.Merging.Args)...).
ArgIf(opts.FastForwardOnly, "--ff-only").
ArgIf(opts.Squash, "--squash", "--ff").
Arg(branchName).
ToArgv()
@@ -219,5 +247,40 @@ func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
}
func (self *BranchCommands) AllBranchesLogCmdObj() oscommands.ICmdObj {
return self.cmd.New(str.ToArgv(self.UserConfig.Git.AllBranchesLogCmd)).DontLog()
// Only choose between non-empty, non-identical commands
candidates := lo.Uniq(lo.WithoutEmpty(append([]string{
self.UserConfig().Git.AllBranchesLogCmd,
},
self.UserConfig().Git.AllBranchesLogCmds...,
)))
n := len(candidates)
i := self.allBranchesLogCmdIndex
self.allBranchesLogCmdIndex = uint8((int(i) + 1) % n)
return self.cmd.New(str.ToArgv(candidates[i])).DontLog()
}
func (self *BranchCommands) IsBranchMerged(branch *models.Branch, mainBranches *MainBranches) (bool, error) {
branchesToCheckAgainst := []string{"HEAD"}
if branch.RemoteBranchStoredLocally() {
branchesToCheckAgainst = append(branchesToCheckAgainst, fmt.Sprintf("%s@{upstream}", branch.Name))
}
branchesToCheckAgainst = append(branchesToCheckAgainst, mainBranches.Get()...)
cmdArgs := NewGitCmd("rev-list").
Arg("--max-count=1").
Arg(branch.Name).
Arg(lo.Map(branchesToCheckAgainst, func(branch string, _ int) string {
return fmt.Sprintf("^%s", branch)
})...).
ToArgv()
stdout, _, err := self.cmd.New(cmdArgs).RunWithOutputs()
if err != nil {
return false, err
}
return stdout == "", nil
}

View File

@@ -3,7 +3,9 @@ package git_commands
import (
"fmt"
"regexp"
"strconv"
"strings"
"time"
"github.com/jesseduffield/generics/set"
"github.com/jesseduffield/go-git/v5/config"
@@ -13,6 +15,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"golang.org/x/exp/slices"
"golang.org/x/sync/errgroup"
)
// context:
@@ -39,6 +42,7 @@ type BranchInfo struct {
// BranchLoader returns a list of Branch objects for the current repo
type BranchLoader struct {
*common.Common
*GitCommon
cmd oscommands.ICmdObjBuilder
getCurrentBranchInfo func() (BranchInfo, error)
config BranchLoaderConfigCommands
@@ -46,12 +50,14 @@ type BranchLoader struct {
func NewBranchLoader(
cmn *common.Common,
gitCommon *GitCommon,
cmd oscommands.ICmdObjBuilder,
getCurrentBranchInfo func() (BranchInfo, error),
config BranchLoaderConfigCommands,
) *BranchLoader {
return &BranchLoader{
Common: cmn,
GitCommon: gitCommon,
cmd: cmd,
getCurrentBranchInfo: getCurrentBranchInfo,
config: config,
@@ -59,36 +65,43 @@ func NewBranchLoader(
}
// Load the list of branches for the current repo
func (self *BranchLoader) Load(reflogCommits []*models.Commit) ([]*models.Branch, error) {
branches := self.obtainBranches()
func (self *BranchLoader) Load(reflogCommits []*models.Commit,
mainBranches *MainBranches,
oldBranches []*models.Branch,
loadBehindCounts bool,
onWorker func(func() error),
renderFunc func(),
) ([]*models.Branch, error) {
branches := self.obtainBranches(self.version.IsAtLeast(2, 22, 0))
reflogBranches := self.obtainReflogBranches(reflogCommits)
// loop through reflog branches. If there is a match, merge them, then remove it from the branches and keep it in the reflog branches
branchesWithRecency := make([]*models.Branch, 0)
outer:
for _, reflogBranch := range reflogBranches {
for j, branch := range branches {
if branch.Head {
continue
}
if strings.EqualFold(reflogBranch.Name, branch.Name) {
branch.Recency = reflogBranch.Recency
branchesWithRecency = append(branchesWithRecency, branch)
branches = utils.Remove(branches, j)
continue outer
if self.AppState.LocalBranchSortOrder == "recency" {
reflogBranches := self.obtainReflogBranches(reflogCommits)
// loop through reflog branches. If there is a match, merge them, then remove it from the branches and keep it in the reflog branches
branchesWithRecency := make([]*models.Branch, 0)
outer:
for _, reflogBranch := range reflogBranches {
for j, branch := range branches {
if branch.Head {
continue
}
if strings.EqualFold(reflogBranch.Name, branch.Name) {
branch.Recency = reflogBranch.Recency
branchesWithRecency = append(branchesWithRecency, branch)
branches = utils.Remove(branches, j)
continue outer
}
}
}
// Sort branches that don't have a recency value alphabetically
// (we're really doing this for the sake of deterministic behaviour across git versions)
slices.SortFunc(branches, func(a *models.Branch, b *models.Branch) bool {
return a.Name < b.Name
})
branches = utils.Prepend(branches, branchesWithRecency...)
}
// Sort branches that don't have a recency value alphabetically
// (we're really doing this for the sake of deterministic behaviour across git versions)
slices.SortFunc(branches, func(a *models.Branch, b *models.Branch) bool {
return a.Name < b.Name
})
branches = utils.Prepend(branches, branchesWithRecency...)
foundHead := false
for i, branch := range branches {
if branch.Head {
@@ -117,12 +130,109 @@ outer:
branch.UpstreamRemote = match.Remote
branch.UpstreamBranch = match.Merge.Short()
}
// If the branch already existed, take over its BehindBaseBranch value
// to reduce flicker
if oldBranch, found := lo.Find(oldBranches, func(b *models.Branch) bool {
return b.Name == branch.Name
}); found {
branch.BehindBaseBranch.Store(oldBranch.BehindBaseBranch.Load())
}
}
if loadBehindCounts && self.UserConfig().Gui.ShowDivergenceFromBaseBranch != "none" {
onWorker(func() error {
return self.GetBehindBaseBranchValuesForAllBranches(branches, mainBranches, renderFunc)
})
}
return branches, nil
}
func (self *BranchLoader) obtainBranches() []*models.Branch {
func (self *BranchLoader) GetBehindBaseBranchValuesForAllBranches(
branches []*models.Branch,
mainBranches *MainBranches,
renderFunc func(),
) error {
mainBranchRefs := mainBranches.Get()
if len(mainBranchRefs) == 0 {
return nil
}
t := time.Now()
errg := errgroup.Group{}
for _, branch := range branches {
errg.Go(func() error {
baseBranch, err := self.GetBaseBranch(branch, mainBranches)
if err != nil {
return err
}
behind := 0 // prime it in case something below fails
if baseBranch != "" {
output, err := self.cmd.New(
NewGitCmd("rev-list").
Arg("--left-right").
Arg("--count").
Arg(fmt.Sprintf("%s...%s", branch.FullRefName(), baseBranch)).
ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
return err
}
// The format of the output is "<ahead>\t<behind>"
aheadBehindStr := strings.Split(strings.TrimSpace(output), "\t")
if len(aheadBehindStr) == 2 {
if value, err := strconv.Atoi(aheadBehindStr[1]); err == nil {
behind = value
}
}
}
branch.BehindBaseBranch.Store(int32(behind))
return nil
})
}
err := errg.Wait()
self.Log.Debugf("time to get behind base branch values for all branches: %s", time.Since(t))
renderFunc()
return err
}
// Find the base branch for the given branch (i.e. the main branch that the
// given branch was forked off of)
//
// Note that this function may return an empty string even if the returned error
// is nil, e.g. when none of the configured main branches exist. This is not
// considered an error condition, so callers need to check both the returned
// error and whether the returned base branch is empty (and possibly react
// differently in both cases).
func (self *BranchLoader) GetBaseBranch(branch *models.Branch, mainBranches *MainBranches) (string, error) {
mergeBase := mainBranches.GetMergeBase(branch.FullRefName())
if mergeBase == "" {
return "", nil
}
output, err := self.cmd.New(
NewGitCmd("for-each-ref").
Arg("--contains").
Arg(mergeBase).
Arg("--format=%(refname)").
Arg(mainBranches.Get()...).
ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
return "", err
}
trimmedOutput := strings.TrimSpace(output)
split := strings.Split(trimmedOutput, "\n")
if len(split) == 0 || split[0] == "" {
return "", nil
}
return split[0], nil
}
func (self *BranchLoader) obtainBranches(canUsePushTrack bool) []*models.Branch {
output, err := self.getRawBranches()
if err != nil {
panic(err)
@@ -144,7 +254,8 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
return nil, false
}
return obtainBranch(split), true
storeCommitDateAsRecency := self.AppState.LocalBranchSortOrder != "recency"
return obtainBranch(split, storeCommitDateAsRecency, canUsePushTrack), true
})
}
@@ -156,8 +267,18 @@ func (self *BranchLoader) getRawBranches() (string, error) {
"%00",
)
var sortOrder string
switch strings.ToLower(self.AppState.LocalBranchSortOrder) {
case "recency", "date":
sortOrder = "-committerdate"
case "alphabetical":
sortOrder = "refname"
default:
sortOrder = "refname"
}
cmdArgs := NewGitCmd("for-each-ref").
Arg("--sort=-committerdate").
Arg(fmt.Sprintf("--sort=%s", sortOrder)).
Arg(fmt.Sprintf("--format=%s", format)).
Arg("refs/heads").
ToArgv()
@@ -170,30 +291,50 @@ var branchFields = []string{
"refname:short",
"upstream:short",
"upstream:track",
"push:track",
"subject",
"objectname",
"committerdate:unix",
}
// Obtain branch information from parsed line output of getRawBranches()
func obtainBranch(split []string) *models.Branch {
func obtainBranch(split []string, storeCommitDateAsRecency bool, canUsePushTrack bool) *models.Branch {
headMarker := split[0]
fullName := split[1]
upstreamName := split[2]
track := split[3]
subject := split[4]
commitHash := split[5]
pushTrack := split[4]
subject := split[5]
commitHash := split[6]
commitDate := split[7]
name := strings.TrimPrefix(fullName, "heads/")
pushables, pullables, gone := parseUpstreamInfo(upstreamName, track)
aheadForPull, behindForPull, gone := parseUpstreamInfo(upstreamName, track)
var aheadForPush, behindForPush string
if canUsePushTrack {
aheadForPush, behindForPush, _ = parseUpstreamInfo(upstreamName, pushTrack)
} else {
aheadForPush, behindForPush = aheadForPull, behindForPull
}
recency := ""
if storeCommitDateAsRecency {
if unixTimestamp, err := strconv.ParseInt(commitDate, 10, 64); err == nil {
recency = utils.UnixToTimeAgo(unixTimestamp)
}
}
return &models.Branch{
Name: name,
Pushables: pushables,
Pullables: pullables,
UpstreamGone: gone,
Head: headMarker == "*",
Subject: subject,
CommitHash: commitHash,
Name: name,
Recency: recency,
AheadForPull: aheadForPull,
BehindForPull: behindForPull,
AheadForPush: aheadForPush,
BehindForPush: behindForPush,
UpstreamGone: gone,
Head: headMarker == "*",
Subject: subject,
CommitHash: commitHash,
}
}
@@ -209,10 +350,10 @@ func parseUpstreamInfo(upstreamName string, track string) (string, string, bool)
return "?", "?", true
}
pushables := parseDifference(track, `ahead (\d+)`)
pullables := parseDifference(track, `behind (\d+)`)
ahead := parseDifference(track, `ahead (\d+)`)
behind := parseDifference(track, `behind (\d+)`)
return pushables, pullables, false
return ahead, behind, false
}
func parseDifference(track string, regexStr string) string {

View File

@@ -2,7 +2,9 @@ package git_commands
// "*|feat/detect-purge|origin/feat/detect-purge|[ahead 1]"
import (
"strconv"
"testing"
"time"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/stretchr/testify/assert"
@@ -10,78 +12,114 @@ import (
func TestObtainBranch(t *testing.T) {
type scenario struct {
testName string
input []string
expectedBranch *models.Branch
testName string
input []string
storeCommitDateAsRecency bool
expectedBranch *models.Branch
}
// Use a time stamp of 2 1/2 hours ago, resulting in a recency string of "2h"
now := time.Now().Unix()
timeStamp := strconv.Itoa(int(now - 2.5*60*60))
scenarios := []scenario{
{
testName: "TrimHeads",
input: []string{"", "heads/a_branch", "", "", "subject", "123"},
testName: "TrimHeads",
input: []string{"", "heads/a_branch", "", "", "", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
expectedBranch: &models.Branch{
Name: "a_branch",
Pushables: "?",
Pullables: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
Name: "a_branch",
AheadForPull: "?",
BehindForPull: "?",
AheadForPush: "?",
BehindForPush: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "NoUpstream",
input: []string{"", "a_branch", "", "", "subject", "123"},
testName: "NoUpstream",
input: []string{"", "a_branch", "", "", "", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
expectedBranch: &models.Branch{
Name: "a_branch",
Pushables: "?",
Pullables: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
Name: "a_branch",
AheadForPull: "?",
BehindForPull: "?",
AheadForPush: "?",
BehindForPush: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "IsHead",
input: []string{"*", "a_branch", "", "", "subject", "123"},
testName: "IsHead",
input: []string{"*", "a_branch", "", "", "", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
expectedBranch: &models.Branch{
Name: "a_branch",
Pushables: "?",
Pullables: "?",
Head: true,
Subject: "subject",
CommitHash: "123",
Name: "a_branch",
AheadForPull: "?",
BehindForPull: "?",
AheadForPush: "?",
BehindForPush: "?",
Head: true,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "IsBehindAndAhead",
input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]", "subject", "123"},
testName: "IsBehindAndAhead",
input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]", "[behind 2, ahead 3]", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
expectedBranch: &models.Branch{
Name: "a_branch",
Pushables: "3",
Pullables: "2",
Head: false,
Subject: "subject",
CommitHash: "123",
Name: "a_branch",
AheadForPull: "3",
BehindForPull: "2",
AheadForPush: "3",
BehindForPush: "2",
Head: false,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "RemoteBranchIsGone",
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]", "subject", "123"},
testName: "RemoteBranchIsGone",
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]", "[gone]", "subject", "123", timeStamp},
storeCommitDateAsRecency: false,
expectedBranch: &models.Branch{
Name: "a_branch",
UpstreamGone: true,
Pushables: "?",
Pullables: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
Name: "a_branch",
UpstreamGone: true,
AheadForPull: "?",
BehindForPull: "?",
AheadForPush: "?",
BehindForPush: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
},
},
{
testName: "WithCommitDateAsRecency",
input: []string{"", "a_branch", "", "", "", "subject", "123", timeStamp},
storeCommitDateAsRecency: true,
expectedBranch: &models.Branch{
Name: "a_branch",
Recency: "2h",
AheadForPull: "?",
BehindForPull: "?",
AheadForPush: "?",
BehindForPush: "?",
Head: false,
Subject: "subject",
CommitHash: "123",
},
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
branch := obtainBranch(s.input)
branch := obtainBranch(s.input, s.storeCommitDateAsRecency, true)
assert.EqualValues(t, s.expectedBranch, branch)
})
}

View File

@@ -41,7 +41,6 @@ func TestBranchGetCommitDifferences(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildBranchCommands(commonDeps{runner: s.runner})
pushables, pullables := instance.GetCommitDifferences("HEAD", "@{u}")
@@ -89,11 +88,10 @@ func TestBranchDeleteBranch(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildBranchCommands(commonDeps{runner: s.runner})
s.test(instance.Delete("test", s.force))
s.test(instance.LocalDelete("test", s.force))
s.runner.CheckForMissingCalls()
})
}
@@ -127,6 +125,19 @@ func TestBranchMerge(t *testing.T) {
branchName: "mybranch",
expected: []string{"merge", "--no-edit", "--merging-args", "mybranch"},
},
{
testName: "multiple merging args",
userConfig: &config.UserConfig{
Git: config.GitConfig{
Merging: config.MergingConfig{
Args: "--arg1 --arg2", // it's up to the user what they put here
},
},
},
opts: MergeOpts{},
branchName: "mybranch",
expected: []string{"merge", "--no-edit", "--arg1", "--arg2", "mybranch"},
},
{
testName: "fast forward only",
userConfig: &config.UserConfig{},
@@ -137,7 +148,6 @@ func TestBranchMerge(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
ExpectGitArgs(s.expected, "", nil)
@@ -177,7 +187,6 @@ func TestBranchCheckout(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildBranchCommands(commonDeps{runner: s.runner})
s.test(instance.Checkout("test", CheckoutOptions{Force: s.force}))
@@ -266,7 +275,6 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildBranchCommands(commonDeps{runner: s.runner})
s.test(instance.CurrentBranchInfo())

View File

@@ -38,9 +38,44 @@ func (self *CommitCommands) SetAuthor(value string) error {
return self.cmd.New(cmdArgs).Run()
}
// Add a commit's coauthor using Github/Gitlab Co-authored-by metadata. Value is expected to be of the form 'Name <Email>'
func (self *CommitCommands) AddCoAuthor(hash string, author string) error {
message, err := self.GetCommitMessage(hash)
if err != nil {
return err
}
message = AddCoAuthorToMessage(message, author)
cmdArgs := NewGitCmd("commit").
Arg("--allow-empty", "--amend", "--only", "-m", message).
ToArgv()
return self.cmd.New(cmdArgs).Run()
}
func AddCoAuthorToMessage(message string, author string) string {
subject, body, _ := strings.Cut(message, "\n")
return strings.TrimSpace(subject) + "\n\n" + AddCoAuthorToDescription(strings.TrimSpace(body), author)
}
func AddCoAuthorToDescription(description string, author string) string {
if description != "" {
lines := strings.Split(description, "\n")
if strings.HasPrefix(lines[len(lines)-1], "Co-authored-by:") {
description += "\n"
} else {
description += "\n\n"
}
}
return description + fmt.Sprintf("Co-authored-by: %s", author)
}
// ResetToCommit reset to commit
func (self *CommitCommands) ResetToCommit(sha string, strength string, envVars []string) error {
cmdArgs := NewGitCmd("reset").Arg("--"+strength, sha).ToArgv()
func (self *CommitCommands) ResetToCommit(hash string, strength string, envVars []string) error {
cmdArgs := NewGitCmd("reset").Arg("--"+strength, hash).ToArgv()
return self.cmd.New(cmdArgs).
// prevents git from prompting us for input which would freeze the program
@@ -53,7 +88,7 @@ func (self *CommitCommands) ResetToCommit(sha string, strength string, envVars [
func (self *CommitCommands) CommitCmdObj(summary string, description string) oscommands.ICmdObj {
messageArgs := self.commitMessageArgs(summary, description)
skipHookPrefix := self.UserConfig.Git.SkipHookPrefix
skipHookPrefix := self.UserConfig().Git.SkipHookPrefix
cmdArgs := NewGitCmd("commit").
ArgIf(skipHookPrefix != "" && strings.HasPrefix(summary, skipHookPrefix), "--no-verify").
@@ -68,8 +103,21 @@ func (self *CommitCommands) RewordLastCommitInEditorCmdObj() oscommands.ICmdObj
return self.cmd.New(NewGitCmd("commit").Arg("--allow-empty", "--amend", "--only").ToArgv())
}
func (self *CommitCommands) RewordLastCommitInEditorWithMessageFileCmdObj(tmpMessageFile string) oscommands.ICmdObj {
return self.cmd.New(NewGitCmd("commit").
Arg("--allow-empty", "--amend", "--only", "--edit", "--file="+tmpMessageFile).ToArgv())
}
func (self *CommitCommands) CommitInEditorWithMessageFileCmdObj(tmpMessageFile string) oscommands.ICmdObj {
return self.cmd.New(NewGitCmd("commit").
Arg("--edit").
Arg("--file="+tmpMessageFile).
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
ToArgv())
}
// RewordLastCommit rewords the topmost commit with the given message
func (self *CommitCommands) RewordLastCommit(summary string, description string) error {
func (self *CommitCommands) RewordLastCommit(summary string, description string) oscommands.ICmdObj {
messageArgs := self.commitMessageArgs(summary, description)
cmdArgs := NewGitCmd("commit").
@@ -77,7 +125,7 @@ func (self *CommitCommands) RewordLastCommit(summary string, description string)
Arg(messageArgs...).
ToArgv()
return self.cmd.New(cmdArgs).Run()
return self.cmd.New(cmdArgs)
}
func (self *CommitCommands) commitMessageArgs(summary string, description string) []string {
@@ -100,25 +148,35 @@ func (self *CommitCommands) CommitEditorCmdObj() oscommands.ICmdObj {
}
func (self *CommitCommands) signoffFlag() string {
if self.UserConfig.Git.Commit.SignOff {
if self.UserConfig().Git.Commit.SignOff {
return "--signoff"
} else {
return ""
}
}
func (self *CommitCommands) GetCommitMessage(commitSha string) (string, error) {
cmdArgs := NewGitCmd("rev-list").
Arg("--format=%B", "--max-count=1", commitSha).
func (self *CommitCommands) GetCommitMessage(commitHash string) (string, error) {
cmdArgs := NewGitCmd("log").
Arg("--format=%B", "--max-count=1", commitHash).
Config("log.showsignature=false").
ToArgv()
messageWithHeader, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
message := strings.Join(strings.SplitAfter(messageWithHeader, "\n")[1:], "")
return strings.TrimSpace(message), err
message, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return strings.ReplaceAll(strings.TrimSpace(message), "\r\n", "\n"), err
}
func (self *CommitCommands) GetCommitDiff(commitSha string) (string, error) {
cmdArgs := NewGitCmd("show").Arg("--no-color", commitSha).ToArgv()
func (self *CommitCommands) GetCommitSubject(commitHash string) (string, error) {
cmdArgs := NewGitCmd("log").
Arg("--format=%s", "--max-count=1", commitHash).
Config("log.showsignature=false").
ToArgv()
subject, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return strings.TrimSpace(subject), err
}
func (self *CommitCommands) GetCommitDiff(commitHash string) (string, error) {
cmdArgs := NewGitCmd("show").Arg("--no-color", commitHash).ToArgv()
diff, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return diff, err
@@ -129,9 +187,9 @@ type Author struct {
Email string
}
func (self *CommitCommands) GetCommitAuthor(commitSha string) (Author, error) {
func (self *CommitCommands) GetCommitAuthor(commitHash string) (Author, error) {
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--pretty=format:'%an%x00%ae'", commitSha).
Arg("--no-patch", "--pretty=format:%an%x00%ae", commitHash).
ToArgv()
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
@@ -148,23 +206,37 @@ func (self *CommitCommands) GetCommitAuthor(commitSha string) (Author, error) {
return author, err
}
func (self *CommitCommands) GetCommitMessageFirstLine(sha string) (string, error) {
return self.GetCommitMessagesFirstLine([]string{sha})
func (self *CommitCommands) GetCommitMessageFirstLine(hash string) (string, error) {
return self.GetCommitMessagesFirstLine([]string{hash})
}
func (self *CommitCommands) GetCommitMessagesFirstLine(shas []string) (string, error) {
func (self *CommitCommands) GetCommitMessagesFirstLine(hashes []string) (string, error) {
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--pretty=format:%s").
Arg(shas...).
Arg(hashes...).
ToArgv()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
}
func (self *CommitCommands) GetCommitsOneline(shas []string) (string, error) {
// Example output:
//
// cd50c79ae Preserve the commit message correctly even if the description has blank lines
// 3ebba5f32 Add test demonstrating a bug with preserving the commit message
// 9a423c388 Remove unused function
func (self *CommitCommands) GetHashesAndCommitMessagesFirstLine(hashes []string) (string, error) {
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--pretty=format:%h %s").
Arg(hashes...).
ToArgv()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
}
func (self *CommitCommands) GetCommitsOneline(hashes []string) (string, error) {
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--oneline").
Arg(shas...).
Arg(hashes...).
ToArgv()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
@@ -183,41 +255,62 @@ func (self *CommitCommands) AmendHeadCmdObj() oscommands.ICmdObj {
return self.cmd.New(cmdArgs)
}
func (self *CommitCommands) ShowCmdObj(sha string, filterPath string, ignoreWhitespace bool) oscommands.ICmdObj {
contextSize := self.UserConfig.Git.DiffContextSize
func (self *CommitCommands) ShowCmdObj(hash string, filterPath string) oscommands.ICmdObj {
contextSize := self.AppState.DiffContextSize
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
cmdArgs := NewGitCmd("show").
Config("diff.noprefix=false").
ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd).
ArgIfElse(extDiffCmd != "", "--ext-diff", "--no-ext-diff").
Arg("--submodule").
Arg("--color="+self.UserConfig.Git.Paging.ColorArg).
Arg("--color="+self.UserConfig().Git.Paging.ColorArg).
Arg(fmt.Sprintf("--unified=%d", contextSize)).
Arg("--stat").
Arg("--decorate").
Arg("-p").
Arg(sha).
ArgIf(ignoreWhitespace, "--ignore-all-space").
Arg(hash).
ArgIf(self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
Arg(fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold)).
ArgIf(filterPath != "", "--", filterPath).
Dir(self.repoPaths.worktreePath).
ToArgv()
return self.cmd.New(cmdArgs).DontLog()
}
// Revert reverts the selected commit by sha
func (self *CommitCommands) Revert(sha string) error {
cmdArgs := NewGitCmd("revert").Arg(sha).ToArgv()
// Revert reverts the selected commit by hash
func (self *CommitCommands) Revert(hash string) error {
cmdArgs := NewGitCmd("revert").Arg(hash).ToArgv()
return self.cmd.New(cmdArgs).Run()
}
func (self *CommitCommands) RevertMerge(sha string, parentNumber int) error {
cmdArgs := NewGitCmd("revert").Arg(sha, "-m", fmt.Sprintf("%d", parentNumber)).
func (self *CommitCommands) RevertMerge(hash string, parentNumber int) error {
cmdArgs := NewGitCmd("revert").Arg(hash, "-m", fmt.Sprintf("%d", parentNumber)).
ToArgv()
return self.cmd.New(cmdArgs).Run()
}
// CreateFixupCommit creates a commit that fixes up a previous commit
func (self *CommitCommands) CreateFixupCommit(sha string) error {
cmdArgs := NewGitCmd("commit").Arg("--fixup=" + sha).ToArgv()
func (self *CommitCommands) CreateFixupCommit(hash string) error {
cmdArgs := NewGitCmd("commit").Arg("--fixup=" + hash).ToArgv()
return self.cmd.New(cmdArgs).Run()
}
// CreateAmendCommit creates a commit that changes the commit message of a previous commit
func (self *CommitCommands) CreateAmendCommit(originalSubject, newSubject, newDescription string, includeFileChanges bool) error {
description := newSubject
if newDescription != "" {
description += "\n\n" + newDescription
}
cmdArgs := NewGitCmd("commit").
Arg("-m", "amend! "+originalSubject).
Arg("-m", description).
ArgIf(!includeFileChanges, "--only", "--allow-empty").
ToArgv()
return self.cmd.New(cmdArgs).Run()
}

View File

@@ -24,6 +24,7 @@ func NewCommitFileLoader(common *common.Common, cmd oscommands.ICmdObjBuilder) *
// GetFilesInDiff get the specified commit files
func (self *CommitFileLoader) GetFilesInDiff(from string, to string, reverse bool) ([]*models.CommitFile, error) {
cmdArgs := NewGitCmd("diff").
Config("diff.noprefix=false").
Arg("--submodule").
Arg("--no-ext-diff").
Arg("--name-status").

View File

@@ -6,17 +6,18 @@ import (
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"sync"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/stefanhaller/git-todo-parser/todo"
)
// context:
@@ -34,11 +35,6 @@ type CommitLoader struct {
readFile func(filename string) ([]byte, error)
walkFiles func(root string, fn filepath.WalkFunc) error
dotGitDir string
// List of main branches that exist in the repo.
// We use these to obtain the merge base of the branch.
// When nil, we're yet to obtain the list of existing main branches.
// When an empty slice, we've obtained the list and it's empty.
mainBranches []string
*GitCommon
}
@@ -55,7 +51,6 @@ func NewCommitLoader(
getRebaseMode: getRebaseMode,
readFile: os.ReadFile,
walkFiles: filepath.Walk,
mainBranches: nil,
GitCommon: gitCommon,
}
}
@@ -63,10 +58,15 @@ func NewCommitLoader(
type GetCommitsOptions struct {
Limit bool
FilterPath string
FilterAuthor string
IncludeRebaseCommits bool
RefName string // e.g. "HEAD" or "my_branch"
RefForPushedStatus string // the ref to use for determining pushed/unpushed status
// determines if we show the whole git graph i.e. pass the '--all' flag
All bool
// If non-empty, show divergence from this ref (left-right log)
RefToShowDivergenceFrom string
MainBranches *MainBranches
}
// GetCommits obtains the commits of the current branch
@@ -92,22 +92,26 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
defer wg.Done()
logErr = self.getLogCmd(opts).RunAndProcessLines(func(line string) (bool, error) {
commit := self.extractCommitFromLine(line)
commit := self.extractCommitFromLine(line, opts.RefToShowDivergenceFrom != "")
commits = append(commits, commit)
return false, nil
})
})
var ancestor string
var remoteAncestor string
go utils.Safe(func() {
defer wg.Done()
ancestor = self.getMergeBase(opts.RefName)
ancestor = opts.MainBranches.GetMergeBase(opts.RefName)
if opts.RefToShowDivergenceFrom != "" {
remoteAncestor = opts.MainBranches.GetMergeBase(opts.RefToShowDivergenceFrom)
}
})
passedFirstPushedCommit := false
// I can get this before
firstPushedCommit, err := self.getFirstPushedCommit(opts.RefName)
firstPushedCommit, err := self.getFirstPushedCommit(opts.RefForPushedStatus)
if err != nil {
// must have no upstream branch so we'll consider everything as pushed
passedFirstPushedCommit = true
@@ -120,13 +124,15 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
}
for _, commit := range commits {
if commit.Sha == firstPushedCommit {
if commit.Hash == firstPushedCommit {
passedFirstPushedCommit = true
}
if passedFirstPushedCommit {
commit.Status = models.StatusPushed
} else {
commit.Status = models.StatusUnpushed
if commit.Status != models.StatusRebasing {
if passedFirstPushedCommit {
commit.Status = models.StatusPushed
} else {
commit.Status = models.StatusUnpushed
}
}
}
@@ -134,8 +140,23 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
return commits, nil
}
if ancestor != "" {
commits = setCommitMergedStatuses(ancestor, commits)
if opts.RefToShowDivergenceFrom != "" {
sort.SliceStable(commits, func(i, j int) bool {
// In the divergence view we want incoming commits to come first
return commits[i].Divergence > commits[j].Divergence
})
_, localSectionStart, found := lo.FindIndexOf(commits, func(commit *models.Commit) bool {
return commit.Divergence == models.DivergenceLeft
})
if !found {
localSectionStart = len(commits)
}
setCommitMergedStatuses(remoteAncestor, commits[:localSectionStart])
setCommitMergedStatuses(ancestor, commits[localSectionStart:])
} else {
setCommitMergedStatuses(ancestor, commits)
}
return commits, nil
@@ -172,20 +193,24 @@ func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*mod
return result, nil
}
// extractCommitFromLine takes a line from a git log and extracts the sha, message, date, and tag if present
// extractCommitFromLine takes a line from a git log and extracts the hash, message, date, and tag if present
// then puts them into a commit object
// example input:
// 8ad01fe32fcc20f07bc6693f87aa4977c327f1e1|10 hours ago|Jesse Duffield| (HEAD -> master, tag: v0.15.2)|refresh commits when adding a tag
func (self *CommitLoader) extractCommitFromLine(line string) *models.Commit {
split := strings.SplitN(line, "\x00", 7)
func (self *CommitLoader) extractCommitFromLine(line string, showDivergence bool) *models.Commit {
split := strings.SplitN(line, "\x00", 8)
sha := split[0]
hash := split[0]
unixTimestamp := split[1]
authorName := split[2]
authorEmail := split[3]
extraInfo := strings.TrimSpace(split[4])
parentHashes := split[5]
message := split[6]
divergence := models.DivergenceNone
if showDivergence {
divergence = lo.Ternary(split[6] == "<", models.DivergenceLeft, models.DivergenceRight)
}
message := split[7]
tags := []string{}
@@ -211,7 +236,7 @@ func (self *CommitLoader) extractCommitFromLine(line string) *models.Commit {
}
return &models.Commit{
Sha: sha,
Hash: hash,
Name: message,
Tags: tags,
ExtraInfo: extraInfo,
@@ -219,21 +244,19 @@ func (self *CommitLoader) extractCommitFromLine(line string) *models.Commit {
AuthorName: authorName,
AuthorEmail: authorEmail,
Parents: parents,
Divergence: divergence,
}
}
func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode) ([]*models.Commit, error) {
commits, err := self.getRebasingCommits(rebaseMode)
if err != nil {
return nil, err
}
commits := self.getRebasingCommits(rebaseMode)
if len(commits) == 0 {
return nil, nil
}
commitShas := lo.FilterMap(commits, func(commit *models.Commit, _ int) (string, bool) {
return commit.Sha, commit.Sha != ""
commitHashes := lo.FilterMap(commits, func(commit *models.Commit, _ int) (string, bool) {
return commit.Hash, commit.Hash != ""
})
// note that we're not filtering these as we do non-rebasing commits just because
@@ -242,14 +265,14 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
NewGitCmd("show").
Config("log.showSignature=false").
Arg("--no-patch", "--oneline", "--abbrev=20", prettyFormat).
Arg(commitShas...).
Arg(commitHashes...).
ToArgv(),
).DontLog()
fullCommits := map[string]*models.Commit{}
err = cmdObj.RunAndProcessLines(func(line string) (bool, error) {
commit := self.extractCommitFromLine(line)
fullCommits[commit.Sha] = commit
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
commit := self.extractCommitFromLine(line, false)
fullCommits[commit.Hash] = commit
return false, nil
})
if err != nil {
@@ -257,23 +280,23 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
}
findFullCommit := lo.Ternary(self.version.IsOlderThan(2, 25, 2),
func(sha string) *models.Commit {
func(hash string) *models.Commit {
for s, c := range fullCommits {
if strings.HasPrefix(s, sha) {
if strings.HasPrefix(s, hash) {
return c
}
}
return nil
},
func(sha string) *models.Commit {
return fullCommits[sha]
func(hash string) *models.Commit {
return fullCommits[hash]
})
hydratedCommits := make([]*models.Commit, 0, len(commits))
for _, rebasingCommit := range commits {
if rebasingCommit.Sha == "" {
if rebasingCommit.Hash == "" {
hydratedCommits = append(hydratedCommits, rebasingCommit)
} else if commit := findFullCommit(rebasingCommit.Sha); commit != nil {
} else if commit := findFullCommit(rebasingCommit.Hash); commit != nil {
commit.Action = rebasingCommit.Action
commit.Status = rebasingCommit.Status
hydratedCommits = append(hydratedCommits, commit)
@@ -283,73 +306,20 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
}
// getRebasingCommits obtains the commits that we're in the process of rebasing
func (self *CommitLoader) getRebasingCommits(rebaseMode enums.RebaseMode) ([]*models.Commit, error) {
switch rebaseMode {
case enums.REBASE_MODE_MERGING:
return self.getNormalRebasingCommits()
case enums.REBASE_MODE_INTERACTIVE:
return self.getInteractiveRebasingCommits()
default:
return nil, nil
}
}
func (self *CommitLoader) getNormalRebasingCommits() ([]*models.Commit, error) {
rewrittenCount := 0
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply/rewritten"))
if err == nil {
content := string(bytesContent)
rewrittenCount = len(strings.Split(content, "\n"))
}
// we know we're rebasing, so lets get all the files whose names have numbers
commits := []*models.Commit{}
err = self.walkFiles(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply"), func(path string, f os.FileInfo, err error) error {
if rewrittenCount > 0 {
rewrittenCount--
return nil
}
if err != nil {
return err
}
re := regexp.MustCompile(`^\d+$`)
if !re.MatchString(f.Name()) {
return nil
}
bytesContent, err := self.readFile(path)
if err != nil {
return err
}
content := string(bytesContent)
commit := self.commitFromPatch(content)
commits = append([]*models.Commit{commit}, commits...)
return nil
})
if err != nil {
return nil, err
}
return commits, nil
}
// git-rebase-todo example:
// pick ac446ae94ee560bdb8d1d057278657b251aaef17 ac446ae
// pick afb893148791a2fbd8091aeb81deba4930c73031 afb8931
func (self *CommitLoader) getRebasingCommits(rebaseMode enums.RebaseMode) []*models.Commit {
if rebaseMode != enums.REBASE_MODE_INTERACTIVE {
return nil
}
// git-rebase-todo.backup example:
// pick 49cbba374296938ea86bbd4bf4fee2f6ba5cccf6 third commit on master
// pick ac446ae94ee560bdb8d1d057278657b251aaef17 blah commit on master
// pick afb893148791a2fbd8091aeb81deba4930c73031 fourth commit on master
// getInteractiveRebasingCommits takes our git-rebase-todo and our git-rebase-todo.backup files
// and extracts out the sha and names of commits that we still have to go
// in the rebase:
func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, error) {
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"))
if err != nil {
self.Log.Error(fmt.Sprintf("error occurred reading git-rebase-todo: %s", err.Error()))
// we assume an error means the file doesn't exist so we just return
return nil, nil
return nil
}
commits := []*models.Commit{}
@@ -357,14 +327,14 @@ func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, err
todos, err := todo.Parse(bytes.NewBuffer(bytesContent), self.config.GetCoreCommentChar())
if err != nil {
self.Log.Error(fmt.Sprintf("error occurred while parsing git-rebase-todo file: %s", err.Error()))
return nil, nil
return nil
}
// See if the current commit couldn't be applied because it conflicted; if
// so, add a fake entry for it
if conflictedCommitSha := self.getConflictedCommit(todos); conflictedCommitSha != "" {
if conflictedCommitHash := self.getConflictedCommit(todos); conflictedCommitHash != "" {
commits = append(commits, &models.Commit{
Sha: conflictedCommitSha,
Hash: conflictedCommitHash,
Name: "",
Status: models.StatusRebasing,
Action: models.ActionConflict,
@@ -373,20 +343,22 @@ func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, err
for _, t := range todos {
if t.Command == todo.UpdateRef {
t.Msg = strings.TrimPrefix(t.Ref, "refs/heads/")
t.Msg = t.Ref
} else if t.Command == todo.Exec {
t.Msg = t.ExecCommand
} else if t.Commit == "" {
// Command does not have a commit associated, skip
continue
}
commits = utils.Prepend(commits, &models.Commit{
Sha: t.Commit,
Hash: t.Commit,
Name: t.Msg,
Status: models.StatusRebasing,
Action: t.Command,
})
}
return commits, nil
return commits
}
func (self *CommitLoader) getConflictedCommit(todos []todo.Todo) string {
@@ -476,27 +448,15 @@ func (self *CommitLoader) getConflictedCommitImpl(todos []todo.Todo, doneTodos [
return lastTodo.Commit
}
// assuming the file starts like this:
// From e93d4193e6dd45ca9cf3a5a273d7ba6cd8b8fb20 Mon Sep 17 00:00:00 2001
// From: Lazygit Tester <test@example.com>
// Date: Wed, 5 Dec 2018 21:03:23 +1100
// Subject: second commit on master
func (self *CommitLoader) commitFromPatch(content string) *models.Commit {
lines := strings.Split(content, "\n")
sha := strings.Split(lines[0], " ")[1]
name := strings.TrimPrefix(lines[3], "Subject: ")
return &models.Commit{
Sha: sha,
Name: name,
Status: models.StatusRebasing,
func setCommitMergedStatuses(ancestor string, commits []*models.Commit) {
if ancestor == "" {
return
}
}
func setCommitMergedStatuses(ancestor string, commits []*models.Commit) []*models.Commit {
passedAncestor := false
for i, commit := range commits {
// some commits aren't really commits and don't have sha's, such as the update-ref todo
if commit.Sha != "" && strings.HasPrefix(ancestor, commit.Sha) {
// some commits aren't really commits and don't have hashes, such as the update-ref todo
if commit.Hash != "" && strings.HasPrefix(ancestor, commit.Hash) {
passedAncestor = true
}
if commit.Status != models.StatusPushed && commit.Status != models.StatusUnpushed {
@@ -506,85 +466,6 @@ func setCommitMergedStatuses(ancestor string, commits []*models.Commit) []*model
commits[i].Status = models.StatusMerged
}
}
return commits
}
func (self *CommitLoader) getMergeBase(refName string) string {
if self.mainBranches == nil {
self.mainBranches = self.getExistingMainBranches()
}
if len(self.mainBranches) == 0 {
return ""
}
// We pass all configured main branches to the merge-base call; git will
// return the base commit for the closest one.
output, err := self.cmd.New(
NewGitCmd("merge-base").Arg(refName).Arg(self.mainBranches...).
ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
// If there's an error, it must be because one of the main branches that
// used to exist when we called getExistingMainBranches() was deleted
// meanwhile. To fix this for next time, throw away our cache.
self.mainBranches = nil
}
return ignoringWarnings(output)
}
func (self *CommitLoader) getExistingMainBranches() []string {
var existingBranches []string
var wg sync.WaitGroup
mainBranches := self.UserConfig.Git.MainBranches
existingBranches = make([]string, len(mainBranches))
for i, branchName := range mainBranches {
wg.Add(1)
i := i
branchName := branchName
go utils.Safe(func() {
defer wg.Done()
// Try to determine upstream of local main branch
if ref, err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--symbolic-full-name", branchName+"@{u}").ToArgv(),
).DontLog().RunWithOutput(); err == nil {
existingBranches[i] = strings.TrimSpace(ref)
return
}
// If this failed, a local branch for this main branch doesn't exist or it
// has no upstream configured. Try looking for one in the "origin" remote.
ref := "refs/remotes/origin/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err == nil {
existingBranches[i] = ref
return
}
// If this failed as well, try if we have the main branch as a local
// branch. This covers the case where somebody is using git locally
// for something, but never pushing anywhere.
ref = "refs/heads/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err == nil {
existingBranches[i] = ref
}
})
}
wg.Wait()
existingBranches = lo.Filter(existingBranches, func(branch string, _ int) bool {
return branch != ""
})
return existingBranches
}
func ignoringWarnings(commandOutput string) string {
@@ -597,7 +478,7 @@ func ignoringWarnings(commandOutput string) string {
return lastLine
}
// getFirstPushedCommit returns the first commit SHA which has been pushed to the ref's upstream.
// getFirstPushedCommit returns the first commit hash which has been pushed to the ref's upstream.
// all commits above this are deemed unpushed and marked as such.
func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
output, err := self.cmd.New(
@@ -617,18 +498,25 @@ func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
// getLog gets the git log.
func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
config := self.UserConfig.Git.Log
gitLogOrder := self.AppState.GitLogOrder
refSpec := opts.RefName
if opts.RefToShowDivergenceFrom != "" {
refSpec += "..." + opts.RefToShowDivergenceFrom
}
cmdArgs := NewGitCmd("log").
Arg(opts.RefName).
ArgIf(config.Order != "default", "--"+config.Order).
Arg(refSpec).
ArgIf(gitLogOrder != "default", "--"+gitLogOrder).
ArgIf(opts.All, "--all").
Arg("--oneline").
Arg(prettyFormat).
Arg("--abbrev=40").
ArgIf(opts.FilterAuthor != "", "--author="+opts.FilterAuthor).
ArgIf(opts.Limit, "-300").
ArgIf(opts.FilterPath != "", "--follow").
Arg("--no-show-signature").
ArgIf(opts.RefToShowDivergenceFrom != "", "--left-right").
Arg("--").
ArgIf(opts.FilterPath != "", opts.FilterPath).
ToArgv()
@@ -636,4 +524,4 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
return self.cmd.New(cmdArgs).DontLog()
}
const prettyFormat = `--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s`
const prettyFormat = `--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%x00%s`

View File

@@ -5,25 +5,26 @@ import (
"strings"
"testing"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/stefanhaller/git-todo-parser/todo"
"github.com/stretchr/testify/assert"
)
var commitsOutput = strings.Replace(`0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|HEAD -> better-tests|b21997d6b4cbdf84b149|better typing for rebase mode
b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164|1640824515|Jesse Duffield|jessedduffield@gmail.com|origin/better-tests|e94e8fc5b6fab4cb755f|fix logging
e94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c|1640823749|Jesse Duffield|jessedduffield@gmail.com|tag: 123, tag: 456|d8084cd558925eb7c9c3|refactor
d8084cd558925eb7c9c38afeed5725c21653ab90|1640821426|Jesse Duffield|jessedduffield@gmail.com||65f910ebd85283b5cce9|WIP
65f910ebd85283b5cce9bf67d03d3f1a9ea3813a|1640821275|Jesse Duffield|jessedduffield@gmail.com||26c07b1ab33860a1a759|WIP
26c07b1ab33860a1a7591a0638f9925ccf497ffa|1640750752|Jesse Duffield|jessedduffield@gmail.com||3d4470a6c072208722e5|WIP
3d4470a6c072208722e5ae9a54bcb9634959a1c5|1640748818|Jesse Duffield|jessedduffield@gmail.com||053a66a7be3da43aacdc|WIP
053a66a7be3da43aacdc7aa78e1fe757b82c4dd2|1640739815|Jesse Duffield|jessedduffield@gmail.com||985fe482e806b172aea4|refactoring the config struct`, "|", "\x00", -1)
var commitsOutput = strings.Replace(`0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|HEAD -> better-tests|b21997d6b4cbdf84b149|>|better typing for rebase mode
b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164|1640824515|Jesse Duffield|jessedduffield@gmail.com|origin/better-tests|e94e8fc5b6fab4cb755f|>|fix logging
e94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c|1640823749|Jesse Duffield|jessedduffield@gmail.com|tag: 123, tag: 456|d8084cd558925eb7c9c3|>|refactor
d8084cd558925eb7c9c38afeed5725c21653ab90|1640821426|Jesse Duffield|jessedduffield@gmail.com||65f910ebd85283b5cce9|>|WIP
65f910ebd85283b5cce9bf67d03d3f1a9ea3813a|1640821275|Jesse Duffield|jessedduffield@gmail.com||26c07b1ab33860a1a759|>|WIP
26c07b1ab33860a1a7591a0638f9925ccf497ffa|1640750752|Jesse Duffield|jessedduffield@gmail.com||3d4470a6c072208722e5|>|WIP
3d4470a6c072208722e5ae9a54bcb9634959a1c5|1640748818|Jesse Duffield|jessedduffield@gmail.com||053a66a7be3da43aacdc|>|WIP
053a66a7be3da43aacdc7aa78e1fe757b82c4dd2|1640739815|Jesse Duffield|jessedduffield@gmail.com||985fe482e806b172aea4|>|refactoring the config struct`, "|", "\x00", -1)
var singleCommitOutput = strings.Replace(`0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|HEAD -> better-tests|b21997d6b4cbdf84b149|better typing for rebase mode`, "|", "\x00", -1)
var singleCommitOutput = strings.Replace(`0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|HEAD -> better-tests|b21997d6b4cbdf84b149|>|better typing for rebase mode`, "|", "\x00", -1)
func TestGetCommits(t *testing.T) {
type scenario struct {
@@ -42,10 +43,10 @@ func TestGetCommits(t *testing.T) {
testName: "should return no commits if there are none",
logOrder: "topo-order",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
expectedCommits: []*models.Commit{},
expectedError: nil,
@@ -54,10 +55,10 @@ func TestGetCommits(t *testing.T) {
testName: "should use proper upstream name for branch",
logOrder: "topo-order",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", IncludeRebaseCommits: false},
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", RefForPushedStatus: "refs/heads/mybranch", IncludeRebaseCommits: false},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"merge-base", "refs/heads/mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "refs/heads/mybranch", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
ExpectGitArgs([]string{"log", "refs/heads/mybranch", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
expectedCommits: []*models.Commit{},
expectedError: nil,
@@ -66,13 +67,13 @@ func TestGetCommits(t *testing.T) {
testName: "should return commits if they are present",
logOrder: "topo-order",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
mainBranches: []string{"master", "main", "develop"},
runner: oscommands.NewFakeRunner(t).
// here it's seeing which commits are yet to be pushed
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
// here it's actually getting all the commits in a formatted form, one per line
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, commitsOutput, nil).
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, commitsOutput, nil).
// here it's testing which of the configured main branches have an upstream
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil). // this one does
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")). // this one doesn't, so it checks origin instead
@@ -85,7 +86,7 @@ func TestGetCommits(t *testing.T) {
expectedCommits: []*models.Commit{
{
Sha: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Hash: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Name: "better typing for rebase mode",
Status: models.StatusUnpushed,
Action: models.ActionNone,
@@ -99,7 +100,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Sha: "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164",
Hash: "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164",
Name: "fix logging",
Status: models.StatusPushed,
Action: models.ActionNone,
@@ -113,7 +114,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Sha: "e94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c",
Hash: "e94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c",
Name: "refactor",
Status: models.StatusPushed,
Action: models.ActionNone,
@@ -127,7 +128,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Sha: "d8084cd558925eb7c9c38afeed5725c21653ab90",
Hash: "d8084cd558925eb7c9c38afeed5725c21653ab90",
Name: "WIP",
Status: models.StatusPushed,
Action: models.ActionNone,
@@ -141,7 +142,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Sha: "65f910ebd85283b5cce9bf67d03d3f1a9ea3813a",
Hash: "65f910ebd85283b5cce9bf67d03d3f1a9ea3813a",
Name: "WIP",
Status: models.StatusPushed,
Action: models.ActionNone,
@@ -155,7 +156,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Sha: "26c07b1ab33860a1a7591a0638f9925ccf497ffa",
Hash: "26c07b1ab33860a1a7591a0638f9925ccf497ffa",
Name: "WIP",
Status: models.StatusMerged,
Action: models.ActionNone,
@@ -169,7 +170,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Sha: "3d4470a6c072208722e5ae9a54bcb9634959a1c5",
Hash: "3d4470a6c072208722e5ae9a54bcb9634959a1c5",
Name: "WIP",
Status: models.StatusMerged,
Action: models.ActionNone,
@@ -183,7 +184,7 @@ func TestGetCommits(t *testing.T) {
},
},
{
Sha: "053a66a7be3da43aacdc7aa78e1fe757b82c4dd2",
Hash: "053a66a7be3da43aacdc7aa78e1fe757b82c4dd2",
Name: "refactoring the config struct",
Status: models.StatusMerged,
Action: models.ActionNone,
@@ -203,13 +204,13 @@ func TestGetCommits(t *testing.T) {
testName: "should not call merge-base for mainBranches if none exist",
logOrder: "topo-order",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
mainBranches: []string{"master", "main"},
runner: oscommands.NewFakeRunner(t).
// here it's seeing which commits are yet to be pushed
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
// here it's actually getting all the commits in a formatted form, one per line
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
// here it's testing which of the configured main branches exist; neither does
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "", errors.New("error")).
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/master"}, "", errors.New("error")).
@@ -220,7 +221,7 @@ func TestGetCommits(t *testing.T) {
expectedCommits: []*models.Commit{
{
Sha: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Hash: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Name: "better typing for rebase mode",
Status: models.StatusUnpushed,
Action: models.ActionNone,
@@ -240,13 +241,13 @@ func TestGetCommits(t *testing.T) {
testName: "should call merge-base for all main branches that exist",
logOrder: "topo-order",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
mainBranches: []string{"master", "main", "develop", "1.0-hotfixes"},
runner: oscommands.NewFakeRunner(t).
// here it's seeing which commits are yet to be pushed
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
// here it's actually getting all the commits in a formatted form, one per line
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
// here it's testing which of the configured main branches exist
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil).
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")).
@@ -259,7 +260,7 @@ func TestGetCommits(t *testing.T) {
expectedCommits: []*models.Commit{
{
Sha: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Hash: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
Name: "better typing for rebase mode",
Status: models.StatusUnpushed,
Action: models.ActionNone,
@@ -279,10 +280,10 @@ func TestGetCommits(t *testing.T) {
testName: "should not specify order if `log.order` is `default`",
logOrder: "default",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
expectedCommits: []*models.Commit{},
expectedError: nil,
@@ -291,10 +292,10 @@ func TestGetCommits(t *testing.T) {
testName: "should set filter path",
logOrder: "default",
rebaseMode: enums.REBASE_MODE_NONE,
opts: GetCommitsOptions{RefName: "HEAD", FilterPath: "src"},
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", FilterPath: "src"},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s", "--abbrev=40", "--follow", "--no-show-signature", "--", "src"}, "", nil),
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%m%x00%s", "--abbrev=40", "--follow", "--no-show-signature", "--", "src"}, "", nil),
expectedCommits: []*models.Commit{},
expectedError: nil,
@@ -302,14 +303,15 @@ func TestGetCommits(t *testing.T) {
}
for _, scenario := range scenarios {
scenario := scenario
t.Run(scenario.testName, func(t *testing.T) {
common := utils.NewDummyCommon()
common.UserConfig.Git.Log.Order = scenario.logOrder
common.AppState = &config.AppState{}
common.AppState.GitLogOrder = scenario.logOrder
cmd := oscommands.NewDummyCmdObjBuilder(scenario.runner)
builder := &CommitLoader{
Common: common,
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
cmd: cmd,
getRebaseMode: func() (enums.RebaseMode, error) { return scenario.rebaseMode, nil },
dotGitDir: ".git",
readFile: func(filename string) ([]byte, error) {
@@ -320,8 +322,10 @@ func TestGetCommits(t *testing.T) {
},
}
common.UserConfig.Git.MainBranches = scenario.mainBranches
commits, err := builder.GetCommits(scenario.opts)
common.UserConfig().Git.MainBranches = scenario.mainBranches
opts := scenario.opts
opts.MainBranches = NewMainBranches(common, cmd)
commits, err := builder.GetCommits(opts)
assert.Equal(t, scenario.expectedCommits, commits)
assert.Equal(t, scenario.expectedError, err)
@@ -337,14 +341,14 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
todos []todo.Todo
doneTodos []todo.Todo
amendFileExists bool
expectedSha string
expectedHash string
}{
{
testName: "no done todos",
todos: []todo.Todo{},
doneTodos: []todo.Todo{},
amendFileExists: false,
expectedSha: "",
expectedHash: "",
},
{
testName: "common case (conflict)",
@@ -360,7 +364,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedSha: "fa1afe1",
expectedHash: "fa1afe1",
},
{
testName: "last command was 'break'",
@@ -369,7 +373,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
{Command: todo.Break},
},
amendFileExists: false,
expectedSha: "",
expectedHash: "",
},
{
testName: "last command was 'exec'",
@@ -381,7 +385,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedSha: "",
expectedHash: "",
},
{
testName: "last command was 'reword'",
@@ -390,7 +394,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
{Command: todo.Reword},
},
amendFileExists: false,
expectedSha: "",
expectedHash: "",
},
{
testName: "'pick' was rescheduled",
@@ -407,7 +411,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedSha: "",
expectedHash: "",
},
{
testName: "'pick' was rescheduled, buggy git version",
@@ -432,7 +436,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedSha: "",
expectedHash: "",
},
{
testName: "conflicting 'pick' after 'exec'",
@@ -457,7 +461,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedSha: "fa1afe1",
expectedHash: "fa1afe1",
},
{
testName: "'edit' with amend file",
@@ -469,7 +473,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: true,
expectedSha: "",
expectedHash: "",
},
{
testName: "'edit' without amend file",
@@ -481,7 +485,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
},
amendFileExists: false,
expectedSha: "fa1afe1",
expectedHash: "fa1afe1",
},
}
for _, scenario := range scenarios {
@@ -501,8 +505,8 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
},
}
sha := builder.getConflictedCommitImpl(scenario.todos, scenario.doneTodos, scenario.amendFileExists)
assert.Equal(t, scenario.expectedSha, sha)
hash := builder.getConflictedCommitImpl(scenario.todos, scenario.doneTodos, scenario.amendFileExists)
assert.Equal(t, scenario.expectedHash, hash)
})
}
}
@@ -519,36 +523,37 @@ func TestCommitLoader_setCommitMergedStatuses(t *testing.T) {
{
testName: "basic",
commits: []*models.Commit{
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Sha: "67890", Name: "2", Action: models.ActionNone, Status: models.StatusPushed},
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
{Hash: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Hash: "67890", Name: "2", Action: models.ActionNone, Status: models.StatusPushed},
{Hash: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
},
ancestor: "67890",
expectedCommits: []*models.Commit{
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Sha: "67890", Name: "2", Action: models.ActionNone, Status: models.StatusMerged},
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusMerged},
{Hash: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Hash: "67890", Name: "2", Action: models.ActionNone, Status: models.StatusMerged},
{Hash: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusMerged},
},
},
{
testName: "with update-ref",
commits: []*models.Commit{
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Sha: "", Name: "", Action: todo.UpdateRef, Status: models.StatusNone},
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
{Hash: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Hash: "", Name: "", Action: todo.UpdateRef, Status: models.StatusNone},
{Hash: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
},
ancestor: "deadbeef",
expectedCommits: []*models.Commit{
{Sha: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Sha: "", Name: "", Action: todo.UpdateRef, Status: models.StatusNone},
{Sha: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
{Hash: "12345", Name: "1", Action: models.ActionNone, Status: models.StatusUnpushed},
{Hash: "", Name: "", Action: todo.UpdateRef, Status: models.StatusNone},
{Hash: "abcde", Name: "3", Action: models.ActionNone, Status: models.StatusPushed},
},
},
}
for _, scenario := range scenarios {
t.Run(scenario.testName, func(t *testing.T) {
expectedCommits := setCommitMergedStatuses(scenario.ancestor, scenario.commits)
expectedCommits := scenario.commits
setCommitMergedStatuses(scenario.ancestor, expectedCommits)
assert.Equal(t, scenario.expectedCommits, expectedCommits)
})
}

View File

@@ -30,11 +30,10 @@ func TestCommitRewordCommit(t *testing.T) {
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{runner: s.runner})
assert.NoError(t, instance.RewordLastCommit(s.summary, s.description))
assert.NoError(t, instance.RewordLastCommit(s.summary, s.description).Run())
s.runner.CheckForMissingCalls()
})
}
@@ -100,7 +99,6 @@ func TestCommitCommitCmdObj(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
userConfig := config.GetDefaultConfig()
userConfig.Git.Commit.SignOff = s.configSignoff
@@ -136,7 +134,6 @@ func TestCommitCommitEditorCmdObj(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
userConfig := config.GetDefaultConfig()
userConfig.Git.Commit.SignOff = s.configSignoff
@@ -153,7 +150,7 @@ func TestCommitCommitEditorCmdObj(t *testing.T) {
func TestCommitCreateFixupCommit(t *testing.T) {
type scenario struct {
testName string
sha string
hash string
runner *oscommands.FakeCmdObjRunner
test func(error)
}
@@ -161,7 +158,7 @@ func TestCommitCreateFixupCommit(t *testing.T) {
scenarios := []scenario{
{
testName: "valid case",
sha: "12345",
hash: "12345",
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "--fixup=12345"}, "", nil),
test: func(err error) {
@@ -171,10 +168,59 @@ func TestCommitCreateFixupCommit(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{runner: s.runner})
s.test(instance.CreateFixupCommit(s.sha))
s.test(instance.CreateFixupCommit(s.hash))
s.runner.CheckForMissingCalls()
})
}
}
func TestCommitCreateAmendCommit(t *testing.T) {
type scenario struct {
testName string
originalSubject string
newSubject string
newDescription string
includeFileChanges bool
runner *oscommands.FakeCmdObjRunner
}
scenarios := []scenario{
{
testName: "subject only",
originalSubject: "original subject",
newSubject: "new subject",
newDescription: "",
includeFileChanges: true,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "-m", "amend! original subject", "-m", "new subject"}, "", nil),
},
{
testName: "subject and description",
originalSubject: "original subject",
newSubject: "new subject",
newDescription: "new description",
includeFileChanges: true,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "-m", "amend! original subject", "-m", "new subject\n\nnew description"}, "", nil),
},
{
testName: "without file changes",
originalSubject: "original subject",
newSubject: "new subject",
newDescription: "",
includeFileChanges: false,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"commit", "-m", "amend! original subject", "-m", "new subject", "--only", "--allow-empty"}, "", nil),
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{runner: s.runner})
err := instance.CreateAmendCommit(s.originalSubject, s.newSubject, s.newDescription, s.includeFileChanges)
assert.NoError(t, err)
s.runner.CheckForMissingCalls()
})
}
@@ -182,54 +228,88 @@ func TestCommitCreateFixupCommit(t *testing.T) {
func TestCommitShowCmdObj(t *testing.T) {
type scenario struct {
testName string
filterPath string
contextSize int
ignoreWhitespace bool
expected []string
testName string
filterPath string
contextSize int
similarityThreshold int
ignoreWhitespace bool
extDiffCmd string
expected []string
}
scenarios := []scenario{
{
testName: "Default case without filter path",
filterPath: "",
contextSize: 3,
ignoreWhitespace: false,
expected: []string{"show", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
testName: "Default case without filter path",
filterPath: "",
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: false,
extDiffCmd: "",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%"},
},
{
testName: "Default case with filter path",
filterPath: "file.txt",
contextSize: 3,
ignoreWhitespace: false,
expected: []string{"show", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--", "file.txt"},
testName: "Default case with filter path",
filterPath: "file.txt",
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: false,
extDiffCmd: "",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--", "file.txt"},
},
{
testName: "Show diff with custom context size",
filterPath: "",
contextSize: 77,
ignoreWhitespace: false,
expected: []string{"show", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890"},
testName: "Show diff with custom context size",
filterPath: "",
contextSize: 77,
similarityThreshold: 50,
ignoreWhitespace: false,
extDiffCmd: "",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%"},
},
{
testName: "Show diff, ignoring whitespace",
filterPath: "",
contextSize: 77,
ignoreWhitespace: true,
expected: []string{"show", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space"},
testName: "Show diff with custom similarity threshold",
filterPath: "",
contextSize: 3,
similarityThreshold: 33,
ignoreWhitespace: false,
extDiffCmd: "",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=33%"},
},
{
testName: "Show diff, ignoring whitespace",
filterPath: "",
contextSize: 77,
similarityThreshold: 50,
ignoreWhitespace: true,
extDiffCmd: "",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space", "--find-renames=50%"},
},
{
testName: "Show diff with external diff command",
filterPath: "",
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: false,
extDiffCmd: "difft --color=always",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%"},
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
userConfig := config.GetDefaultConfig()
userConfig.Git.DiffContextSize = s.contextSize
userConfig.Git.Paging.ExternalDiffCommand = s.extDiffCmd
appState := &config.AppState{}
appState.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
appState.DiffContextSize = s.contextSize
appState.RenameSimilarityThreshold = s.similarityThreshold
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil)
instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner})
repoPaths := RepoPaths{
worktreePath: "/path/to/worktree",
}
instance := buildCommitCommands(commonDeps{userConfig: userConfig, appState: appState, runner: runner, repoPaths: &repoPaths})
assert.NoError(t, instance.ShowCmdObj("1234567890", s.filterPath, s.ignoreWhitespace).Run())
assert.NoError(t, instance.ShowCmdObj("1234567890", s.filterPath).Run())
runner.CheckForMissingCalls()
})
}
@@ -244,19 +324,17 @@ func TestGetCommitMsg(t *testing.T) {
scenarios := []scenario{
{
"empty",
` commit deadbeef`,
``,
``,
},
{
"no line breaks (single line)",
`commit deadbeef
use generics to DRY up context code`,
`use generics to DRY up context code`,
`use generics to DRY up context code`,
},
{
"with line breaks",
`commit deadbeef
Merge pull request #1750 from mark2185/fix-issue-template
`Merge pull request #1750 from mark2185/fix-issue-template
'git-rev parse' should be 'git rev-parse'`,
`Merge pull request #1750 from mark2185/fix-issue-template
@@ -266,10 +344,9 @@ Merge pull request #1750 from mark2185/fix-issue-template
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{
runner: oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1", "deadbeef"}, s.input, nil),
runner: oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"-c", "log.showsignature=false", "log", "--format=%B", "--max-count=1", "deadbeef"}, s.input, nil),
})
output, err := instance.GetCommitMessage("deadbeef")
@@ -290,15 +367,14 @@ func TestGetCommitMessageFromHistory(t *testing.T) {
scenarios := []scenario{
{
"Empty message",
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "", nil).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1"}, "", nil),
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "", nil).ExpectGitArgs([]string{"-c", "log.showsignature=false", "log", "--format=%B", "--max-count=1"}, "", nil),
func(output string, err error) {
assert.Error(t, err)
},
},
{
"Default case to retrieve a commit in history",
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "sha3 \n", nil).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1", "sha3"}, `commit sha3
use generics to DRY up context code`, nil),
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "hash3 \n", nil).ExpectGitArgs([]string{"-c", "log.showsignature=false", "log", "--format=%B", "--max-count=1", "hash3"}, `use generics to DRY up context code`, nil),
func(output string, err error) {
assert.NoError(t, err)
assert.Equal(t, "use generics to DRY up context code", output)
@@ -307,7 +383,6 @@ func TestGetCommitMessageFromHistory(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{runner: s.runner})
@@ -317,3 +392,70 @@ func TestGetCommitMessageFromHistory(t *testing.T) {
})
}
}
func TestAddCoAuthorToMessage(t *testing.T) {
scenarios := []struct {
name string
message string
expectedResult string
}{
{
// This never happens, I think it isn't possible to create a commit
// with an empty message. Just including it for completeness.
name: "Empty message",
message: "",
expectedResult: "\n\nCo-authored-by: John Doe <john@doe.com>",
},
{
name: "Just a subject, no body",
message: "Subject",
expectedResult: "Subject\n\nCo-authored-by: John Doe <john@doe.com>",
},
{
name: "Subject and body",
message: "Subject\n\nBody",
expectedResult: "Subject\n\nBody\n\nCo-authored-by: John Doe <john@doe.com>",
},
{
name: "Body already ending with a Co-authored-by line",
message: "Subject\n\nBody\n\nCo-authored-by: Jane Smith <jane@smith.com>",
expectedResult: "Subject\n\nBody\n\nCo-authored-by: Jane Smith <jane@smith.com>\nCo-authored-by: John Doe <john@doe.com>",
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
result := AddCoAuthorToMessage(s.message, "John Doe <john@doe.com>")
assert.Equal(t, s.expectedResult, result)
})
}
}
func TestAddCoAuthorToDescription(t *testing.T) {
scenarios := []struct {
name string
description string
expectedResult string
}{
{
name: "Empty description",
description: "",
expectedResult: "Co-authored-by: John Doe <john@doe.com>",
},
{
name: "Non-empty description",
description: "Body",
expectedResult: "Body\n\nCo-authored-by: John Doe <john@doe.com>",
},
{
name: "Description already ending with a Co-authored-by line",
description: "Body\n\nCo-authored-by: Jane Smith <jane@smith.com>",
expectedResult: "Body\n\nCo-authored-by: Jane Smith <jane@smith.com>\nCo-authored-by: John Doe <john@doe.com>",
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
result := AddCoAuthorToDescription(s.description, "John Doe <john@doe.com>")
assert.Equal(t, s.expectedResult, result)
})
}
}

View File

@@ -4,7 +4,6 @@ import (
gogit "github.com/jesseduffield/go-git/v5"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/sasha-s/go-deadlock"
)
type GitCommon struct {
@@ -15,8 +14,6 @@ type GitCommon struct {
repoPaths *RepoPaths
repo *gogit.Repository
config *ConfigCommands
// mutex for doing things like push/pull/fetch
syncMutex *deadlock.Mutex
}
func NewGitCommon(
@@ -27,7 +24,6 @@ func NewGitCommon(
repoPaths *RepoPaths,
repo *gogit.Repository,
config *ConfigCommands,
syncMutex *deadlock.Mutex,
) *GitCommon {
return &GitCommon{
Common: cmn,
@@ -37,6 +33,5 @@ func NewGitCommon(
repoPaths: repoPaths,
repo: repo,
config: config,
syncMutex: syncMutex,
}
}

View File

@@ -43,7 +43,7 @@ func (self *ConfigCommands) ConfiguredPager() string {
}
func (self *ConfigCommands) GetPager(width int) string {
useConfig := self.UserConfig.Git.Paging.UseConfig
useConfig := self.UserConfig().Git.Paging.UseConfig
if useConfig {
pager := self.ConfiguredPager()
return strings.Split(pager, "| less")[0]
@@ -53,14 +53,14 @@ func (self *ConfigCommands) GetPager(width int) string {
"columnWidth": strconv.Itoa(width/2 - 6),
}
pagerTemplate := self.UserConfig.Git.Paging.Pager
pagerTemplate := string(self.UserConfig().Git.Paging.Pager)
return utils.ResolvePlaceholderString(pagerTemplate, templateValues)
}
// UsingGpg tells us whether the user has gpg enabled so that we can know
// whether we need to run a subprocess to allow them to enter their password
func (self *ConfigCommands) UsingGpg() bool {
overrideGpg := self.UserConfig.Git.OverrideGpg
overrideGpg := self.UserConfig().Git.OverrideGpg
if overrideGpg {
return false
}

View File

@@ -17,6 +17,7 @@ import (
type commonDeps struct {
runner *oscommands.FakeCmdObjRunner
userConfig *config.UserConfig
appState *config.AppState
gitVersion *GitVersion
gitConfig *git_config.FakeGitConfig
getenv func(string) string
@@ -32,7 +33,7 @@ func buildGitCommon(deps commonDeps) *GitCommon {
gitCommon.Common = deps.common
if gitCommon.Common == nil {
gitCommon.Common = utils.NewDummyCommonWithUserConfig(deps.userConfig)
gitCommon.Common = utils.NewDummyCommonWithUserConfigAndAppState(deps.userConfig, deps.appState)
}
if deps.fs != nil {
@@ -57,9 +58,9 @@ func buildGitCommon(deps commonDeps) *GitCommon {
}
gitCommon.cmd = cmd
gitCommon.Common.UserConfig = deps.userConfig
if gitCommon.Common.UserConfig == nil {
gitCommon.Common.UserConfig = config.GetDefaultConfig()
gitCommon.Common.SetUserConfig(deps.userConfig)
if gitCommon.Common.UserConfig() == nil {
gitCommon.Common.SetUserConfig(config.GetDefaultConfig())
}
gitCommon.version = deps.gitVersion

View File

@@ -1,6 +1,10 @@
package git_commands
import "github.com/jesseduffield/lazygit/pkg/commands/oscommands"
import (
"fmt"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
)
type DiffCommands struct {
*GitCommon
@@ -13,7 +17,90 @@ func NewDiffCommands(gitCommon *GitCommon) *DiffCommands {
}
func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
useExtDiff := extDiffCmd != ""
return self.cmd.New(
NewGitCmd("diff").Arg("--submodule", "--no-ext-diff", "--color").Arg(diffArgs...).ToArgv(),
NewGitCmd("diff").
Config("diff.noprefix=false").
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff").
Arg("--submodule").
Arg(fmt.Sprintf("--color=%s", self.UserConfig().Git.Paging.ColorArg)).
Arg(diffArgs...).
Dir(self.repoPaths.worktreePath).
ToArgv(),
)
}
func (self *DiffCommands) internalDiffCmdObj(diffArgs ...string) *GitCommandBuilder {
return NewGitCmd("diff").
Config("diff.noprefix=false").
Arg("--no-ext-diff", "--no-color").
Arg(diffArgs...).
Dir(self.repoPaths.worktreePath)
}
func (self *DiffCommands) GetPathDiff(path string, staged bool) (string, error) {
return self.cmd.New(
self.internalDiffCmdObj().
ArgIf(staged, "--staged").
Arg(path).
ToArgv(),
).RunWithOutput()
}
func (self *DiffCommands) GetAllDiff(staged bool) (string, error) {
return self.cmd.New(
self.internalDiffCmdObj().
ArgIf(staged, "--staged").
ToArgv(),
).RunWithOutput()
}
type DiffToolCmdOptions struct {
// The path to show a diff for. Pass "." for the entire repo.
Filepath string
// The commit against which to show the diff. Leave empty to show a diff of
// the working copy.
FromCommit string
// The commit to diff against FromCommit. Leave empty to diff the working
// copy against FromCommit. Leave both FromCommit and ToCommit empty to show
// the diff of the unstaged working copy changes against the index if Staged
// is false, or the staged changes against HEAD if Staged is true.
ToCommit string
// Whether to reverse the left and right sides of the diff.
Reverse bool
// Whether the given Filepath is a directory. We'll pass --dir-diff to
// git-difftool in that case.
IsDirectory bool
// Whether to show the staged or the unstaged changes. Must be false if both
// FromCommit and ToCommit are non-empty.
Staged bool
}
func (self *DiffCommands) OpenDiffToolCmdObj(opts DiffToolCmdOptions) oscommands.ICmdObj {
return self.cmd.New(NewGitCmd("difftool").
Arg("--no-prompt").
ArgIf(opts.IsDirectory, "--dir-diff").
ArgIf(opts.Staged, "--cached").
ArgIf(opts.FromCommit != "", opts.FromCommit).
ArgIf(opts.ToCommit != "", opts.ToCommit).
ArgIf(opts.Reverse, "-R").
Arg("--", opts.Filepath).
ToArgv())
}
func (self *DiffCommands) DiffIndexCmdObj(diffArgs ...string) oscommands.ICmdObj {
return self.cmd.New(
NewGitCmd("diff-index").
Config("diff.noprefix=false").
Arg("--submodule", "--no-ext-diff", "--no-color", "--patch").
Arg(diffArgs...).ToArgv(),
)
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
)
type FileCommands struct {
@@ -30,7 +31,7 @@ func (self *FileCommands) Cat(fileName string) (string, error) {
}
func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (string, error) {
editor := self.UserConfig.OS.EditCommand
editor := self.UserConfig().OS.EditCommand
if editor == "" {
editor = self.config.GetCoreEditor()
@@ -59,7 +60,7 @@ func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (
"line": strconv.Itoa(lineNumber),
}
editCmdTemplate := self.UserConfig.OS.EditCommandTemplate
editCmdTemplate := self.UserConfig().OS.EditCommandTemplate
if len(editCmdTemplate) == 0 {
switch editor {
case "emacs", "nano", "vi", "vim", "nvim":
@@ -75,33 +76,36 @@ func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (
return utils.ResolvePlaceholderString(editCmdTemplate, templateValues), nil
}
func (self *FileCommands) GetEditCmdStr(filename string) (string, bool) {
func (self *FileCommands) GetEditCmdStr(filenames []string) (string, bool) {
// Legacy support for old config; to be removed at some point
if self.UserConfig.OS.Edit == "" && self.UserConfig.OS.EditCommandTemplate != "" {
if cmdStr, err := self.GetEditCmdStrLegacy(filename, 1); err == nil {
if self.UserConfig().OS.Edit == "" && self.UserConfig().OS.EditCommandTemplate != "" {
// If multiple files are selected, we'll simply edit just the first one.
// It's not worth fixing this for the legacy support.
if cmdStr, err := self.GetEditCmdStrLegacy(filenames[0], 1); err == nil {
return cmdStr, true
}
}
template, editInTerminal := config.GetEditTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
template, suspend := config.GetEditTemplate(&self.UserConfig().OS, self.guessDefaultEditor)
quotedFilenames := lo.Map(filenames, func(filename string, _ int) string { return self.cmd.Quote(filename) })
templateValues := map[string]string{
"filename": self.cmd.Quote(filename),
"filename": strings.Join(quotedFilenames, " "),
}
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
return cmdStr, editInTerminal
return cmdStr, suspend
}
func (self *FileCommands) GetEditAtLineCmdStr(filename string, lineNumber int) (string, bool) {
// Legacy support for old config; to be removed at some point
if self.UserConfig.OS.EditAtLine == "" && self.UserConfig.OS.EditCommandTemplate != "" {
if self.UserConfig().OS.EditAtLine == "" && self.UserConfig().OS.EditCommandTemplate != "" {
if cmdStr, err := self.GetEditCmdStrLegacy(filename, lineNumber); err == nil {
return cmdStr, true
}
}
template, editInTerminal := config.GetEditAtLineTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
template, suspend := config.GetEditAtLineTemplate(&self.UserConfig().OS, self.guessDefaultEditor)
templateValues := map[string]string{
"filename": self.cmd.Quote(filename),
@@ -109,18 +113,18 @@ func (self *FileCommands) GetEditAtLineCmdStr(filename string, lineNumber int) (
}
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
return cmdStr, editInTerminal
return cmdStr, suspend
}
func (self *FileCommands) GetEditAtLineAndWaitCmdStr(filename string, lineNumber int) string {
// Legacy support for old config; to be removed at some point
if self.UserConfig.OS.EditAtLineAndWait == "" && self.UserConfig.OS.EditCommandTemplate != "" {
if self.UserConfig().OS.EditAtLineAndWait == "" && self.UserConfig().OS.EditCommandTemplate != "" {
if cmdStr, err := self.GetEditCmdStrLegacy(filename, lineNumber); err == nil {
return cmdStr
}
}
template := config.GetEditAtLineAndWaitTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
template := config.GetEditAtLineAndWaitTemplate(&self.UserConfig().OS, self.guessDefaultEditor)
templateValues := map[string]string{
"filename": self.cmd.Quote(filename),
@@ -131,15 +135,15 @@ func (self *FileCommands) GetEditAtLineAndWaitCmdStr(filename string, lineNumber
return cmdStr
}
func (self *FileCommands) GetOpenDirInEditorCmdStr(path string) string {
template := config.GetOpenDirInEditorTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
func (self *FileCommands) GetOpenDirInEditorCmdStr(path string) (string, bool) {
template, suspend := config.GetOpenDirInEditorTemplate(&self.UserConfig().OS, self.guessDefaultEditor)
templateValues := map[string]string{
"dir": self.cmd.Quote(path),
}
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
return cmdStr
return cmdStr, suspend
}
func (self *FileCommands) guessDefaultEditor() string {

View File

@@ -100,15 +100,19 @@ type FileStatus struct {
PreviousName string
}
func (c *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
func (self *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
cmdArgs := NewGitCmd("status").
Arg(opts.UntrackedFilesArg).
Arg("--porcelain").
Arg("-z").
ArgIf(opts.NoRenames, "--no-renames").
ArgIfElse(
opts.NoRenames,
"--no-renames",
fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold),
).
ToArgv()
statusLines, _, err := c.cmd.New(cmdArgs).DontLog().RunWithOutputs()
statusLines, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
if err != nil {
return []FileStatus{}, err
}

View File

@@ -5,27 +5,31 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/stretchr/testify/assert"
)
func TestFileGetStatusFiles(t *testing.T) {
type scenario struct {
testName string
runner oscommands.ICmdObjRunner
expectedFiles []*models.File
testName string
similarityThreshold int
runner oscommands.ICmdObjRunner
expectedFiles []*models.File
}
scenarios := []scenario{
{
"No files found",
50,
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"}, "", nil),
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "", nil),
[]*models.File{},
},
{
"Several files found",
50,
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
"MM file1.txt\x00A file3.txt\x00AM file2.txt\x00?? file4.txt\x00UU file5.txt",
nil,
),
@@ -94,8 +98,9 @@ func TestFileGetStatusFiles(t *testing.T) {
},
{
"File with new line char",
50,
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"}, "MM a\nb.txt", nil),
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "MM a\nb.txt", nil),
[]*models.File{
{
Name: "a\nb.txt",
@@ -113,8 +118,9 @@ func TestFileGetStatusFiles(t *testing.T) {
},
{
"Renamed files",
50,
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
"R after1.txt\x00before1.txt\x00RM after2.txt\x00before2.txt",
nil,
),
@@ -149,8 +155,9 @@ func TestFileGetStatusFiles(t *testing.T) {
},
{
"File with arrow in name",
50,
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
`?? a -> b.txt`,
nil,
),
@@ -172,12 +179,14 @@ func TestFileGetStatusFiles(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
cmd := oscommands.NewDummyCmdObjBuilder(s.runner)
appState := &config.AppState{}
appState.RenameSimilarityThreshold = s.similarityThreshold
loader := &FileLoader{
GitCommon: buildGitCommon(commonDeps{}),
GitCommon: buildGitCommon(commonDeps{appState: appState}),
cmd: cmd,
config: &FakeFileLoaderConfig{showUntrackedFiles: "yes"},
getFileType: func(string) string { return "file" },

View File

@@ -177,36 +177,44 @@ func TestEditFileCmdStrLegacy(t *testing.T) {
}
}
func TestEditFileCmd(t *testing.T) {
func TestEditFilesCmd(t *testing.T) {
type scenario struct {
filename string
osConfig config.OSConfig
expectedCmdStr string
expectedEditInTerminal bool
filenames []string
osConfig config.OSConfig
expectedCmdStr string
suspend bool
}
scenarios := []scenario{
{
filename: "test",
osConfig: config.OSConfig{},
expectedCmdStr: `vim -- "test"`,
expectedEditInTerminal: true,
filenames: []string{"test"},
osConfig: config.OSConfig{},
expectedCmdStr: `vim -- "test"`,
suspend: true,
},
{
filename: "test",
filenames: []string{"test"},
osConfig: config.OSConfig{
Edit: "nano {{filename}}",
},
expectedCmdStr: `nano "test"`,
expectedEditInTerminal: true,
expectedCmdStr: `nano "test"`,
suspend: true,
},
{
filename: "file/with space",
filenames: []string{"file/with space"},
osConfig: config.OSConfig{
EditPreset: "sublime",
},
expectedCmdStr: `subl -- "file/with space"`,
expectedEditInTerminal: false,
expectedCmdStr: `subl -- "file/with space"`,
suspend: false,
},
{
filenames: []string{"multiple", "files"},
osConfig: config.OSConfig{
EditPreset: "sublime",
},
expectedCmdStr: `subl -- "multiple" "files"`,
suspend: false,
},
}
@@ -218,28 +226,28 @@ func TestEditFileCmd(t *testing.T) {
userConfig: userConfig,
})
cmdStr, editInTerminal := instance.GetEditCmdStr(s.filename)
cmdStr, suspend := instance.GetEditCmdStr(s.filenames)
assert.Equal(t, s.expectedCmdStr, cmdStr)
assert.Equal(t, s.expectedEditInTerminal, editInTerminal)
assert.Equal(t, s.suspend, suspend)
}
}
func TestEditFileAtLineCmd(t *testing.T) {
type scenario struct {
filename string
lineNumber int
osConfig config.OSConfig
expectedCmdStr string
expectedEditInTerminal bool
filename string
lineNumber int
osConfig config.OSConfig
expectedCmdStr string
suspend bool
}
scenarios := []scenario{
{
filename: "test",
lineNumber: 42,
osConfig: config.OSConfig{},
expectedCmdStr: `vim +42 -- "test"`,
expectedEditInTerminal: true,
filename: "test",
lineNumber: 42,
osConfig: config.OSConfig{},
expectedCmdStr: `vim +42 -- "test"`,
suspend: true,
},
{
filename: "test",
@@ -247,8 +255,8 @@ func TestEditFileAtLineCmd(t *testing.T) {
osConfig: config.OSConfig{
EditAtLine: "nano +{{line}} {{filename}}",
},
expectedCmdStr: `nano +35 "test"`,
expectedEditInTerminal: true,
expectedCmdStr: `nano +35 "test"`,
suspend: true,
},
{
filename: "file/with space",
@@ -256,8 +264,8 @@ func TestEditFileAtLineCmd(t *testing.T) {
osConfig: config.OSConfig{
EditPreset: "sublime",
},
expectedCmdStr: `subl -- "file/with space":12`,
expectedEditInTerminal: false,
expectedCmdStr: `subl -- "file/with space":12`,
suspend: false,
},
}
@@ -269,9 +277,9 @@ func TestEditFileAtLineCmd(t *testing.T) {
userConfig: userConfig,
})
cmdStr, editInTerminal := instance.GetEditAtLineCmdStr(s.filename, s.lineNumber)
cmdStr, suspend := instance.GetEditAtLineCmdStr(s.filename, s.lineNumber)
assert.Equal(t, s.expectedCmdStr, cmdStr)
assert.Equal(t, s.expectedEditInTerminal, editInTerminal)
assert.Equal(t, s.suspend, suspend)
}
}

View File

@@ -23,7 +23,6 @@ func TestStartCmdObj(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildFlowCommands(commonDeps{})
@@ -69,7 +68,6 @@ func TestFinishCmdObj(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildFlowCommands(commonDeps{
gitConfig: git_config.NewFakeGitConfig(s.gitConfigMockResponses),

View File

@@ -44,6 +44,14 @@ func (self *GitCommandBuilder) Config(value string) *GitCommandBuilder {
return self
}
func (self *GitCommandBuilder) ConfigIf(condition bool, ifTrue string) *GitCommandBuilder {
if condition {
self.Config(ifTrue)
}
return self
}
// the -C arg will make git do a `cd` to the directory before doing anything else
func (self *GitCommandBuilder) Dir(path string) *GitCommandBuilder {
// repo path comes before the command
@@ -52,6 +60,14 @@ func (self *GitCommandBuilder) Dir(path string) *GitCommandBuilder {
return self
}
func (self *GitCommandBuilder) DirIf(condition bool, path string) *GitCommandBuilder {
if condition {
return self.Dir(path)
}
return self
}
// Note, you may prefer to use the Dir method instead of this one
func (self *GitCommandBuilder) Worktree(path string) *GitCommandBuilder {
// worktree arg comes before the command

View File

@@ -0,0 +1,127 @@
package git_commands
import (
"strings"
"sync"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/sasha-s/go-deadlock"
)
type MainBranches struct {
c *common.Common
// Which of the configured main branches actually exist in the repository. Full
// ref names, and it could be either "refs/heads/..." or "refs/remotes/origin/..."
// depending on which one exists for a given bare name.
existingMainBranches []string
previousMainBranches []string
cmd oscommands.ICmdObjBuilder
mutex *deadlock.Mutex
}
func NewMainBranches(
cmn *common.Common,
cmd oscommands.ICmdObjBuilder,
) *MainBranches {
return &MainBranches{
c: cmn,
existingMainBranches: nil,
cmd: cmd,
mutex: &deadlock.Mutex{},
}
}
// Get the list of main branches that exist in the repository. This is a list of
// full ref names.
func (self *MainBranches) Get() []string {
self.mutex.Lock()
defer self.mutex.Unlock()
configuredMainBranches := self.c.UserConfig().Git.MainBranches
if self.existingMainBranches == nil || !utils.EqualSlices(self.previousMainBranches, configuredMainBranches) {
self.existingMainBranches = self.determineMainBranches(configuredMainBranches)
self.previousMainBranches = configuredMainBranches
}
return self.existingMainBranches
}
// Return the merge base of the given refName with the closest main branch.
func (self *MainBranches) GetMergeBase(refName string) string {
mainBranches := self.Get()
if len(mainBranches) == 0 {
return ""
}
// We pass all existing main branches to the merge-base call; git will
// return the base commit for the closest one.
// We ignore errors from this call, since we can't distinguish whether the
// error is because one of the main branches has been deleted since the last
// call to determineMainBranches, or because the refName has no common
// history with any of the main branches. Since the former should happen
// very rarely, users must quit and restart lazygit to fix it; the latter is
// also not very common, but can totally happen and is not an error.
output, _ := self.cmd.New(
NewGitCmd("merge-base").Arg(refName).Arg(mainBranches...).
ToArgv(),
).DontLog().RunWithOutput()
return ignoringWarnings(output)
}
func (self *MainBranches) determineMainBranches(configuredMainBranches []string) []string {
var existingBranches []string
var wg sync.WaitGroup
existingBranches = make([]string, len(configuredMainBranches))
for i, branchName := range configuredMainBranches {
wg.Add(1)
go utils.Safe(func() {
defer wg.Done()
// Try to determine upstream of local main branch
if ref, err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--symbolic-full-name", branchName+"@{u}").ToArgv(),
).DontLog().RunWithOutput(); err == nil {
existingBranches[i] = strings.TrimSpace(ref)
return
}
// If this failed, a local branch for this main branch doesn't exist or it
// has no upstream configured. Try looking for one in the "origin" remote.
ref := "refs/remotes/origin/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err == nil {
existingBranches[i] = ref
return
}
// If this failed as well, try if we have the main branch as a local
// branch. This covers the case where somebody is using git locally
// for something, but never pushing anywhere.
ref = "refs/heads/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err == nil {
existingBranches[i] = ref
}
})
}
wg.Wait()
existingBranches = lo.Filter(existingBranches, func(branch string, _ int) bool {
return branch != ""
})
return existingBranches
}

View File

@@ -4,12 +4,12 @@ import (
"path/filepath"
"time"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/app/daemon"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/patch"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/stefanhaller/git-todo-parser/todo"
)
type PatchCommands struct {
@@ -47,8 +47,8 @@ type ApplyPatchOpts struct {
Reverse bool
}
func (self *PatchCommands) ApplyCustomPatch(reverse bool) error {
patch := self.PatchBuilder.PatchToApply(reverse)
func (self *PatchCommands) ApplyCustomPatch(reverse bool, turnAddedFilesIntoDiffAgainstEmptyFile bool) error {
patch := self.PatchBuilder.PatchToApply(reverse, turnAddedFilesIntoDiffAgainstEmptyFile)
return self.ApplyPatch(patch, ApplyPatchOpts{
Index: true,
@@ -94,7 +94,7 @@ func (self *PatchCommands) DeletePatchesFromCommit(commits []*models.Commit, com
}
// apply each patch in reverse
if err := self.ApplyCustomPatch(true); err != nil {
if err := self.ApplyCustomPatch(true, true); err != nil {
_ = self.rebase.AbortRebase()
return err
}
@@ -123,7 +123,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
}
// apply each patch forward
if err := self.ApplyCustomPatch(false); err != nil {
if err := self.ApplyCustomPatch(false, false); err != nil {
// Don't abort the rebase here; this might cause conflicts, so give
// the user a chance to resolve them
return err
@@ -157,13 +157,13 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
baseIndex := sourceCommitIdx + 1
changes := []daemon.ChangeTodoAction{
{Sha: commits[sourceCommitIdx].Sha, NewAction: todo.Edit},
{Sha: commits[destinationCommitIdx].Sha, NewAction: todo.Edit},
{Hash: commits[sourceCommitIdx].Hash, NewAction: todo.Edit},
{Hash: commits[destinationCommitIdx].Hash, NewAction: todo.Edit},
}
self.os.LogCommand(logTodoChanges(changes), false)
err := self.rebase.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: commits[baseIndex].Sha,
baseHashOrRoot: commits[baseIndex].Hash,
overrideEditor: true,
instruction: daemon.NewChangeTodoActionsInstruction(changes),
}).Run()
@@ -172,7 +172,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
}
// apply each patch in reverse
if err := self.ApplyCustomPatch(true); err != nil {
if err := self.ApplyCustomPatch(true, true); err != nil {
_ = self.rebase.AbortRebase()
return err
}
@@ -219,7 +219,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitIdx int, stash bool) error {
if stash {
if err := self.stash.Push(self.Tr.StashPrefix + commits[commitIdx].Sha); err != nil {
if err := self.stash.Push(self.Tr.StashPrefix + commits[commitIdx].Hash); err != nil {
return err
}
}
@@ -228,7 +228,7 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId
return err
}
if err := self.ApplyCustomPatch(true); err != nil {
if err := self.ApplyCustomPatch(true, true); err != nil {
if self.status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
_ = self.rebase.AbortRebase()
}
@@ -282,7 +282,7 @@ func (self *PatchCommands) PullPatchIntoNewCommit(
return err
}
if err := self.ApplyCustomPatch(true); err != nil {
if err := self.ApplyCustomPatch(true, true); err != nil {
_ = self.rebase.AbortRebase()
return err
}
@@ -321,7 +321,11 @@ func (self *PatchCommands) PullPatchIntoNewCommit(
// only some lines of a range of adjacent added lines. To solve this, we
// get the diff of HEAD and the original commit and then apply that.
func (self *PatchCommands) diffHeadAgainstCommit(commit *models.Commit) (string, error) {
cmdArgs := NewGitCmd("diff").Arg("HEAD.." + commit.Sha).ToArgv()
cmdArgs := NewGitCmd("diff").
Config("diff.noprefix=false").
Arg("--no-ext-diff").
Arg("HEAD.." + commit.Hash).
ToArgv()
return self.cmd.New(cmdArgs).RunWithOutput()
}

View File

@@ -5,13 +5,13 @@ import (
"path/filepath"
"strings"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/app/daemon"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/stefanhaller/git-todo-parser/todo"
)
type RebaseCommands struct {
@@ -35,9 +35,8 @@ func NewRebaseCommands(
}
func (self *RebaseCommands) RewordCommit(commits []*models.Commit, index int, summary string, description string) error {
if models.IsHeadCommit(commits, index) {
// we've selected the top commit so no rebase is required
return self.commit.RewordLastCommit(summary, description)
if self.config.UsingGpg() {
return errors.New(self.Tr.DisabledForGPG)
}
err := self.BeginInteractiveRebaseForCommit(commits, index, false)
@@ -46,7 +45,7 @@ func (self *RebaseCommands) RewordCommit(commits []*models.Commit, index int, su
}
// now the selected commit should be our head so we'll amend it with the new message
err = self.commit.RewordLastCommit(summary, description)
err = self.commit.RewordLastCommit(summary, description).Run()
if err != nil {
return err
}
@@ -56,105 +55,107 @@ func (self *RebaseCommands) RewordCommit(commits []*models.Commit, index int, su
func (self *RebaseCommands) RewordCommitInEditor(commits []*models.Commit, index int) (oscommands.ICmdObj, error) {
changes := []daemon.ChangeTodoAction{{
Sha: commits[index].Sha,
Hash: commits[index].Hash,
NewAction: todo.Reword,
}}
self.os.LogCommand(logTodoChanges(changes), false)
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: getBaseShaOrRoot(commits, index+1),
instruction: daemon.NewChangeTodoActionsInstruction(changes),
baseHashOrRoot: getBaseHashOrRoot(commits, index+1),
instruction: daemon.NewChangeTodoActionsInstruction(changes),
}), nil
}
func (self *RebaseCommands) ResetCommitAuthor(commits []*models.Commit, index int) error {
return self.GenericAmend(commits, index, func() error {
func (self *RebaseCommands) ResetCommitAuthor(commits []*models.Commit, start, end int) error {
return self.GenericAmend(commits, start, end, func(_ *models.Commit) error {
return self.commit.ResetAuthor()
})
}
func (self *RebaseCommands) SetCommitAuthor(commits []*models.Commit, index int, value string) error {
return self.GenericAmend(commits, index, func() error {
func (self *RebaseCommands) SetCommitAuthor(commits []*models.Commit, start, end int, value string) error {
return self.GenericAmend(commits, start, end, func(_ *models.Commit) error {
return self.commit.SetAuthor(value)
})
}
func (self *RebaseCommands) GenericAmend(commits []*models.Commit, index int, f func() error) error {
if models.IsHeadCommit(commits, index) {
func (self *RebaseCommands) AddCommitCoAuthor(commits []*models.Commit, start, end int, value string) error {
return self.GenericAmend(commits, start, end, func(commit *models.Commit) error {
return self.commit.AddCoAuthor(commit.Hash, value)
})
}
func (self *RebaseCommands) GenericAmend(commits []*models.Commit, start, end int, f func(commit *models.Commit) error) error {
if start == end && models.IsHeadCommit(commits, start) {
// we've selected the top commit so no rebase is required
return f()
return f(commits[start])
}
err := self.BeginInteractiveRebaseForCommit(commits, index, false)
err := self.BeginInteractiveRebaseForCommitRange(commits, start, end, false)
if err != nil {
return err
}
// now the selected commit should be our head so we'll amend it
err = f()
if err != nil {
return err
for commitIndex := end; commitIndex >= start; commitIndex-- {
err = f(commits[commitIndex])
if err != nil {
return err
}
if err := self.ContinueRebase(); err != nil {
return err
}
}
return self.ContinueRebase()
return nil
}
func (self *RebaseCommands) MoveCommitDown(commits []*models.Commit, index int) error {
baseShaOrRoot := getBaseShaOrRoot(commits, index+2)
func (self *RebaseCommands) MoveCommitsDown(commits []*models.Commit, startIdx int, endIdx int) error {
baseHashOrRoot := getBaseHashOrRoot(commits, endIdx+2)
sha := commits[index].Sha
msg := utils.ResolvePlaceholderString(
self.Tr.Log.MoveCommitDown,
map[string]string{
"shortSha": utils.ShortSha(sha),
},
)
self.os.LogCommand(msg, false)
hashes := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) string {
return commit.Hash
})
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: baseShaOrRoot,
instruction: daemon.NewMoveTodoDownInstruction(sha),
baseHashOrRoot: baseHashOrRoot,
instruction: daemon.NewMoveTodosDownInstruction(hashes),
overrideEditor: true,
}).Run()
}
func (self *RebaseCommands) MoveCommitUp(commits []*models.Commit, index int) error {
baseShaOrRoot := getBaseShaOrRoot(commits, index+1)
func (self *RebaseCommands) MoveCommitsUp(commits []*models.Commit, startIdx int, endIdx int) error {
baseHashOrRoot := getBaseHashOrRoot(commits, endIdx+1)
sha := commits[index].Sha
msg := utils.ResolvePlaceholderString(
self.Tr.Log.MoveCommitUp,
map[string]string{
"shortSha": utils.ShortSha(sha),
},
)
self.os.LogCommand(msg, false)
hashes := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) string {
return commit.Hash
})
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: baseShaOrRoot,
instruction: daemon.NewMoveTodoUpInstruction(sha),
baseHashOrRoot: baseHashOrRoot,
instruction: daemon.NewMoveTodosUpInstruction(hashes),
overrideEditor: true,
}).Run()
}
func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, index int, action todo.TodoCommand) error {
baseIndex := index + 1
func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, startIdx int, endIdx int, action todo.TodoCommand) error {
baseIndex := endIdx + 1
if action == todo.Squash || action == todo.Fixup {
baseIndex++
}
baseShaOrRoot := getBaseShaOrRoot(commits, baseIndex)
baseHashOrRoot := getBaseHashOrRoot(commits, baseIndex)
changes := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) daemon.ChangeTodoAction {
return daemon.ChangeTodoAction{
Hash: commit.Hash,
NewAction: action,
}
})
changes := []daemon.ChangeTodoAction{{
Sha: commits[index].Sha,
NewAction: action,
}}
self.os.LogCommand(logTodoChanges(changes), false)
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: baseShaOrRoot,
baseHashOrRoot: baseHashOrRoot,
overrideEditor: true,
instruction: daemon.NewChangeTodoActionsInstruction(changes),
}).Run()
@@ -169,8 +170,8 @@ func (self *RebaseCommands) EditRebase(branchRef string) error {
)
self.os.LogCommand(msg, false)
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: branchRef,
instruction: daemon.NewInsertBreakInstruction(),
baseHashOrRoot: branchRef,
instruction: daemon.NewInsertBreakInstruction(),
}).Run()
}
@@ -184,21 +185,21 @@ func (self *RebaseCommands) EditRebaseFromBaseCommit(targetBranchName string, ba
)
self.os.LogCommand(msg, false)
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: baseCommit,
onto: targetBranchName,
instruction: daemon.NewInsertBreakInstruction(),
baseHashOrRoot: baseCommit,
onto: targetBranchName,
instruction: daemon.NewInsertBreakInstruction(),
}).Run()
}
func logTodoChanges(changes []daemon.ChangeTodoAction) string {
changeTodoStr := strings.Join(lo.Map(changes, func(c daemon.ChangeTodoAction, _ int) string {
return fmt.Sprintf("%s:%s", c.Sha, c.NewAction)
return fmt.Sprintf("%s:%s", c.Hash, c.NewAction)
}), "\n")
return fmt.Sprintf("Changing TODO actions: %s", changeTodoStr)
return fmt.Sprintf("Changing TODO actions:\n%s", changeTodoStr)
}
type PrepareInteractiveRebaseCommandOpts struct {
baseShaOrRoot string
baseHashOrRoot string
onto string
instruction daemon.Instruction
overrideEditor bool
@@ -207,7 +208,7 @@ type PrepareInteractiveRebaseCommandOpts struct {
// PrepareInteractiveRebaseCommand returns the cmd for an interactive rebase
// we tell git to run lazygit to edit the todo list, and we pass the client
// lazygit a todo string to write to the todo file
// lazygit instructions what to do with the todo file
func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteractiveRebaseCommandOpts) oscommands.ICmdObj {
ex := oscommands.GetLazygitPath()
@@ -215,11 +216,11 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract
Arg("--interactive").
Arg("--autostash").
Arg("--keep-empty").
ArgIf(opts.keepCommitsThatBecomeEmpty && !self.version.IsOlderThan(2, 26, 0), "--empty=keep").
ArgIf(opts.keepCommitsThatBecomeEmpty && self.version.IsAtLeast(2, 26, 0), "--empty=keep").
Arg("--no-autosquash").
ArgIf(!self.version.IsOlderThan(2, 22, 0), "--rebase-merges").
ArgIf(self.version.IsAtLeast(2, 22, 0), "--rebase-merges").
ArgIf(opts.onto != "", "--onto", opts.onto).
Arg(opts.baseShaOrRoot).
Arg(opts.baseHashOrRoot).
ToArgv()
debug := "FALSE"
@@ -236,7 +237,7 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract
if opts.instruction != nil {
cmdObj.AddEnvVars(daemon.ToEnvVars(opts.instruction)...)
} else {
gitSequenceEditor = "true"
cmdObj.AddEnvVars(daemon.ToEnvVars(daemon.NewRemoveUpdateRefsForCopiedBranchInstruction())...)
}
cmdObj.AddEnvVars(
@@ -253,55 +254,143 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract
return cmdObj
}
// GitRebaseEditTodo runs "git rebase --edit-todo", saving the given todosFileContent to the file
func (self *RebaseCommands) GitRebaseEditTodo(todosFileContent []byte) error {
ex := oscommands.GetLazygitPath()
cmdArgs := NewGitCmd("rebase").
Arg("--edit-todo").
ToArgv()
debug := "FALSE"
if self.Debug {
debug = "TRUE"
}
self.Log.WithField("command", cmdArgs).Debug("RunCommand")
cmdObj := self.cmd.New(cmdArgs)
cmdObj.AddEnvVars(daemon.ToEnvVars(daemon.NewWriteRebaseTodoInstruction(todosFileContent))...)
cmdObj.AddEnvVars(
"DEBUG="+debug,
"LANG=en_US.UTF-8", // Force using EN as language
"LC_ALL=en_US.UTF-8", // Force using EN as language
"GIT_EDITOR="+ex,
"GIT_SEQUENCE_EDITOR="+ex,
)
return cmdObj.Run()
}
func (self *RebaseCommands) getHashOfLastCommitMade() (string, error) {
cmdArgs := NewGitCmd("rev-parse").Arg("--verify", "HEAD").ToArgv()
return self.cmd.New(cmdArgs).RunWithOutput()
}
// AmendTo amends the given commit with whatever files are staged
func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) error {
commit := commits[commitIndex]
if err := self.commit.CreateFixupCommit(commit.Sha); err != nil {
if err := self.commit.CreateFixupCommit(commit.Hash); err != nil {
return err
}
// Get the sha of the commit we just created
cmdArgs := NewGitCmd("rev-parse").Arg("--verify", "HEAD").ToArgv()
fixupSha, err := self.cmd.New(cmdArgs).RunWithOutput()
fixupHash, err := self.getHashOfLastCommitMade()
if err != nil {
return err
}
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: getBaseShaOrRoot(commits, commitIndex+1),
baseHashOrRoot: getBaseHashOrRoot(commits, commitIndex+1),
overrideEditor: true,
instruction: daemon.NewMoveFixupCommitDownInstruction(commit.Sha, fixupSha),
instruction: daemon.NewMoveFixupCommitDownInstruction(commit.Hash, fixupHash, true),
}).Run()
}
// EditRebaseTodo sets the action for a given rebase commit in the git-rebase-todo file
func (self *RebaseCommands) EditRebaseTodo(commit *models.Commit, action todo.TodoCommand) error {
func (self *RebaseCommands) MoveFixupCommitDown(commits []*models.Commit, targetCommitIndex int) error {
fixupHash, err := self.getHashOfLastCommitMade()
if err != nil {
return err
}
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseHashOrRoot: getBaseHashOrRoot(commits, targetCommitIndex+1),
overrideEditor: true,
instruction: daemon.NewMoveFixupCommitDownInstruction(commits[targetCommitIndex].Hash, fixupHash, false),
}).Run()
}
func todoFromCommit(commit *models.Commit) utils.Todo {
if commit.Action == todo.UpdateRef {
return utils.Todo{Ref: commit.Name, Action: commit.Action}
} else {
return utils.Todo{Hash: commit.Hash, Action: commit.Action}
}
}
// Sets the action for the given commits in the git-rebase-todo file
func (self *RebaseCommands) EditRebaseTodo(commits []*models.Commit, action todo.TodoCommand) error {
commitsWithAction := lo.Map(commits, func(commit *models.Commit, _ int) utils.TodoChange {
return utils.TodoChange{
Hash: commit.Hash,
OldAction: commit.Action,
NewAction: action,
}
})
return utils.EditRebaseTodo(
filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"), commit.Sha, commit.Action, action, self.config.GetCoreCommentChar())
filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"),
commitsWithAction,
self.config.GetCoreCommentChar(),
)
}
// MoveTodoDown moves a rebase todo item down by one position
func (self *RebaseCommands) MoveTodoDown(commit *models.Commit) error {
fileName := filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo")
return utils.MoveTodoDown(fileName, commit.Sha, commit.Action, self.config.GetCoreCommentChar())
func (self *RebaseCommands) DeleteUpdateRefTodos(commits []*models.Commit) error {
todosToDelete := lo.Map(commits, func(commit *models.Commit, _ int) utils.Todo {
return todoFromCommit(commit)
})
todosFileContent, err := utils.DeleteTodos(
filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"),
todosToDelete,
self.config.GetCoreCommentChar(),
)
if err != nil {
return err
}
return self.GitRebaseEditTodo(todosFileContent)
}
// MoveTodoDown moves a rebase todo item down by one position
func (self *RebaseCommands) MoveTodoUp(commit *models.Commit) error {
func (self *RebaseCommands) MoveTodosDown(commits []*models.Commit) error {
fileName := filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo")
return utils.MoveTodoUp(fileName, commit.Sha, commit.Action, self.config.GetCoreCommentChar())
todosToMove := lo.Map(commits, func(commit *models.Commit, _ int) utils.Todo {
return todoFromCommit(commit)
})
return utils.MoveTodosDown(fileName, todosToMove, self.config.GetCoreCommentChar())
}
func (self *RebaseCommands) MoveTodosUp(commits []*models.Commit) error {
fileName := filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo")
todosToMove := lo.Map(commits, func(commit *models.Commit, _ int) utils.Todo {
return todoFromCommit(commit)
})
return utils.MoveTodosUp(fileName, todosToMove, self.config.GetCoreCommentChar())
}
// SquashAllAboveFixupCommits squashes all fixup! commits above the given one
func (self *RebaseCommands) SquashAllAboveFixupCommits(commit *models.Commit) error {
shaOrRoot := commit.Sha + "^"
hashOrRoot := commit.Hash + "^"
if commit.IsFirstCommit() {
shaOrRoot = "--root"
hashOrRoot = "--root"
}
cmdArgs := NewGitCmd("rebase").
Arg("--interactive", "--rebase-merges", "--autostash", "--autosquash", shaOrRoot).
Arg("--interactive", "--rebase-merges", "--autostash", "--autosquash", hashOrRoot).
ToArgv()
return self.runSkipEditorCommand(self.cmd.New(cmdArgs))
@@ -312,7 +401,13 @@ func (self *RebaseCommands) SquashAllAboveFixupCommits(commit *models.Commit) er
func (self *RebaseCommands) BeginInteractiveRebaseForCommit(
commits []*models.Commit, commitIndex int, keepCommitsThatBecomeEmpty bool,
) error {
if len(commits)-1 < commitIndex {
return self.BeginInteractiveRebaseForCommitRange(commits, commitIndex, commitIndex, keepCommitsThatBecomeEmpty)
}
func (self *RebaseCommands) BeginInteractiveRebaseForCommitRange(
commits []*models.Commit, start, end int, keepCommitsThatBecomeEmpty bool,
) error {
if len(commits)-1 < end {
return errors.New("index outside of range of commits")
}
@@ -323,14 +418,17 @@ func (self *RebaseCommands) BeginInteractiveRebaseForCommit(
return errors.New(self.Tr.DisabledForGPG)
}
changes := []daemon.ChangeTodoAction{{
Sha: commits[commitIndex].Sha,
NewAction: todo.Edit,
}}
changes := make([]daemon.ChangeTodoAction, 0, end-start)
for commitIndex := end; commitIndex >= start; commitIndex-- {
changes = append(changes, daemon.ChangeTodoAction{
Hash: commits[commitIndex].Hash,
NewAction: todo.Edit,
})
}
self.os.LogCommand(logTodoChanges(changes), false)
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: getBaseShaOrRoot(commits, commitIndex+1),
baseHashOrRoot: getBaseHashOrRoot(commits, end+1),
overrideEditor: true,
keepCommitsThatBecomeEmpty: keepCommitsThatBecomeEmpty,
instruction: daemon.NewChangeTodoActionsInstruction(changes),
@@ -339,13 +437,13 @@ func (self *RebaseCommands) BeginInteractiveRebaseForCommit(
// RebaseBranch interactive rebases onto a branch
func (self *RebaseCommands) RebaseBranch(branchName string) error {
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{baseShaOrRoot: branchName}).Run()
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{baseHashOrRoot: branchName}).Run()
}
func (self *RebaseCommands) RebaseBranchFromBaseCommit(targetBranchName string, baseCommit string) error {
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: baseCommit,
onto: targetBranchName,
baseHashOrRoot: baseCommit,
onto: targetBranchName,
}).Run()
}
@@ -403,23 +501,25 @@ func (self *RebaseCommands) runSkipEditorCommand(cmdObj oscommands.ICmdObj) erro
}
// DiscardOldFileChanges discards changes to a file from an old commit
func (self *RebaseCommands) DiscardOldFileChanges(commits []*models.Commit, commitIndex int, fileName string) error {
func (self *RebaseCommands) DiscardOldFileChanges(commits []*models.Commit, commitIndex int, filePaths []string) error {
if err := self.BeginInteractiveRebaseForCommit(commits, commitIndex, false); err != nil {
return err
}
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
cmdArgs := NewGitCmd("cat-file").Arg("-e", "HEAD^:"+fileName).ToArgv()
for _, filePath := range filePaths {
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
cmdArgs := NewGitCmd("cat-file").Arg("-e", "HEAD^:"+filePath).ToArgv()
if err := self.cmd.New(cmdArgs).Run(); err != nil {
if err := self.os.Remove(fileName); err != nil {
if err := self.cmd.New(cmdArgs).Run(); err != nil {
if err := self.os.Remove(filePath); err != nil {
return err
}
if err := self.workingTree.StageFile(filePath); err != nil {
return err
}
} else if err := self.workingTree.CheckoutFile("HEAD^", filePath); err != nil {
return err
}
if err := self.workingTree.StageFile(fileName); err != nil {
return err
}
} else if err := self.workingTree.CheckoutFile("HEAD^", fileName); err != nil {
return err
}
// amend the commit
@@ -432,10 +532,10 @@ func (self *RebaseCommands) DiscardOldFileChanges(commits []*models.Commit, comm
return self.ContinueRebase()
}
// CherryPickCommits begins an interactive rebase with the given shas being cherry picked onto HEAD
// CherryPickCommits begins an interactive rebase with the given hashes being cherry picked onto HEAD
func (self *RebaseCommands) CherryPickCommits(commits []*models.Commit) error {
commitLines := lo.Map(commits, func(commit *models.Commit, _ int) string {
return fmt.Sprintf("%s %s", utils.ShortSha(commit.Sha), commit.Name)
return fmt.Sprintf("%s %s", utils.ShortHash(commit.Hash), commit.Name)
})
msg := utils.ResolvePlaceholderString(
self.Tr.Log.CherryPickCommits,
@@ -446,20 +546,34 @@ func (self *RebaseCommands) CherryPickCommits(commits []*models.Commit) error {
self.os.LogCommand(msg, false)
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: "HEAD",
instruction: daemon.NewCherryPickCommitsInstruction(commits),
baseHashOrRoot: "HEAD",
instruction: daemon.NewCherryPickCommitsInstruction(commits),
}).Run()
}
// CherryPickCommitsDuringRebase simply prepends the given commits to the existing git-rebase-todo file
func (self *RebaseCommands) CherryPickCommitsDuringRebase(commits []*models.Commit) error {
todoLines := lo.Map(commits, func(commit *models.Commit, _ int) daemon.TodoLine {
return daemon.TodoLine{
Action: "pick",
Commit: commit,
}
})
todo := daemon.TodoLinesToString(todoLines)
filePath := filepath.Join(self.repoPaths.worktreeGitDirPath, "rebase-merge/git-rebase-todo")
return utils.PrependStrToTodoFile(filePath, []byte(todo))
}
// we can't start an interactive rebase from the first commit without passing the
// '--root' arg
func getBaseShaOrRoot(commits []*models.Commit, index int) string {
func getBaseHashOrRoot(commits []*models.Commit, index int) string {
// We assume that the commits slice contains the initial commit of the repo.
// Technically this assumption could prove false, but it's unlikely you'll
// be starting a rebase from 300 commits ago (which is the original commit limit
// at time of writing)
if index < len(commits) {
return commits[index].Sha
return commits[index].Hash
} else {
return "--root"
}

View File

@@ -67,7 +67,6 @@ func TestRebaseRebaseBranch(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildRebaseCommands(commonDeps{runner: s.runner, gitVersion: s.gitVersion})
s.test(instance.RebaseBranch(s.arg))
@@ -89,7 +88,6 @@ func TestRebaseSkipEditorCommand(t *testing.T) {
`^GIT_SEQUENCE_EDITOR=.*$`,
"^" + daemon.DaemonKindEnvKey + "=" + strconv.Itoa(int(daemon.DaemonKindExitImmediately)) + "$",
} {
regexStr := regexStr
foundMatch := lo.ContainsBy(envVars, func(envVar string) bool {
return regexp.MustCompile(regexStr).MatchString(envVar)
})
@@ -111,7 +109,7 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
gitConfigMockResponses map[string]string
commits []*models.Commit
commitIndex int
fileName string
fileName []string
runner *oscommands.FakeCmdObjRunner
test func(error)
}
@@ -122,7 +120,7 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
gitConfigMockResponses: nil,
commits: []*models.Commit{},
commitIndex: 0,
fileName: "test999.txt",
fileName: []string{"test999.txt"},
runner: oscommands.NewFakeRunner(t),
test: func(err error) {
assert.Error(t, err)
@@ -131,9 +129,9 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
{
testName: "returns error when using gpg",
gitConfigMockResponses: map[string]string{"commit.gpgsign": "true"},
commits: []*models.Commit{{Name: "commit", Sha: "123456"}},
commits: []*models.Commit{{Name: "commit", Hash: "123456"}},
commitIndex: 0,
fileName: "test999.txt",
fileName: []string{"test999.txt"},
runner: oscommands.NewFakeRunner(t),
test: func(err error) {
assert.Error(t, err)
@@ -143,11 +141,11 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
testName: "checks out file if it already existed",
gitConfigMockResponses: nil,
commits: []*models.Commit{
{Name: "commit", Sha: "123456"},
{Name: "commit2", Sha: "abcdef"},
{Name: "commit", Hash: "123456"},
{Name: "commit2", Hash: "abcdef"},
},
commitIndex: 0,
fileName: "test999.txt",
fileName: []string{"test999.txt"},
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "abcdef"}, "", nil).
ExpectGitArgs([]string{"cat-file", "-e", "HEAD^:test999.txt"}, "", nil).
@@ -163,7 +161,6 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildRebaseCommands(commonDeps{
runner: s.runner,

View File

@@ -23,7 +23,7 @@ func NewReflogCommitLoader(common *common.Common, cmd oscommands.ICmdObjBuilder)
// GetReflogCommits only returns the new reflog commits since the given lastReflogCommit
// if none is passed (i.e. it's value is nil) then we get all the reflog commits
func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit, filterPath string) ([]*models.Commit, bool, error) {
func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit, filterPath string, filterAuthor string) ([]*models.Commit, bool, error) {
commits := make([]*models.Commit, 0)
cmdArgs := NewGitCmd("log").
@@ -31,6 +31,7 @@ func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit
Arg("-g").
Arg("--abbrev=40").
Arg("--format=%h%x00%ct%x00%gs%x00%p").
ArgIf(filterAuthor != "", "--author="+filterAuthor).
ArgIf(filterPath != "", "--follow", "--", filterPath).
ToArgv()
@@ -44,7 +45,7 @@ func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit
}
// note that the unix timestamp here is the timestamp of the COMMIT, not the reflog entry itself,
// so two consecutive reflog entries may have both the same SHA and therefore same timestamp.
// so two consecutive reflog entries may have both the same hash and therefore same timestamp.
// We use the reflog message to disambiguate, and fingers crossed that we never see the same of those
// twice in a row. Reason being that it would mean we'd be erroneously exiting early.
if lastReflogCommit != nil && self.sameReflogCommit(commit, lastReflogCommit) {
@@ -64,7 +65,7 @@ func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit
}
func (self *ReflogCommitLoader) sameReflogCommit(a *models.Commit, b *models.Commit) bool {
return a.Sha == b.Sha && a.UnixTimestamp == b.UnixTimestamp && a.Name == b.Name
return a.Hash == b.Hash && a.UnixTimestamp == b.UnixTimestamp && a.Name == b.Name
}
func (self *ReflogCommitLoader) parseLine(line string) (*models.Commit, bool) {
@@ -82,7 +83,7 @@ func (self *ReflogCommitLoader) parseLine(line string) (*models.Commit, bool) {
}
return &models.Commit{
Sha: fields[0],
Hash: fields[0],
Name: fields[2],
UnixTimestamp: int64(unixTimestamp),
Status: models.StatusReflog,

View File

@@ -25,6 +25,7 @@ func TestGetReflogCommits(t *testing.T) {
runner *oscommands.FakeCmdObjRunner
lastReflogCommit *models.Commit
filterPath string
filterAuthor string
expectedCommits []*models.Commit
expectedOnlyObtainedNew bool
expectedError error
@@ -49,35 +50,35 @@ func TestGetReflogCommits(t *testing.T) {
lastReflogCommit: nil,
expectedCommits: []*models.Commit{
{
Sha: "c3c4b66b64c97ffeecde",
Hash: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from A to B",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
Parents: []string{"51baa8c1"},
},
{
Sha: "c3c4b66b64c97ffeecde",
Hash: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from B to A",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
Parents: []string{"51baa8c1"},
},
{
Sha: "c3c4b66b64c97ffeecde",
Hash: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from A to B",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
Parents: []string{"51baa8c1"},
},
{
Sha: "c3c4b66b64c97ffeecde",
Hash: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from master to A",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
Parents: []string{"51baa8c1"},
},
{
Sha: "f4ddf2f0d4be4ccc7efa",
Hash: "f4ddf2f0d4be4ccc7efa",
Name: "checkout: moving from A to master",
Status: models.StatusReflog,
UnixTimestamp: 1643149435,
@@ -93,7 +94,7 @@ func TestGetReflogCommits(t *testing.T) {
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, reflogOutput, nil),
lastReflogCommit: &models.Commit{
Sha: "c3c4b66b64c97ffeecde",
Hash: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from B to A",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
@@ -101,7 +102,7 @@ func TestGetReflogCommits(t *testing.T) {
},
expectedCommits: []*models.Commit{
{
Sha: "c3c4b66b64c97ffeecde",
Hash: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from A to B",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
@@ -117,7 +118,7 @@ func TestGetReflogCommits(t *testing.T) {
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p", "--follow", "--", "path"}, reflogOutput, nil),
lastReflogCommit: &models.Commit{
Sha: "c3c4b66b64c97ffeecde",
Hash: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from B to A",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
@@ -126,7 +127,32 @@ func TestGetReflogCommits(t *testing.T) {
filterPath: "path",
expectedCommits: []*models.Commit{
{
Sha: "c3c4b66b64c97ffeecde",
Hash: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from A to B",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
Parents: []string{"51baa8c1"},
},
},
expectedOnlyObtainedNew: true,
expectedError: nil,
},
{
testName: "when passing filterAuthor",
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p", "--author=John Doe <john@doe.com>"}, reflogOutput, nil),
lastReflogCommit: &models.Commit{
Hash: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from B to A",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
Parents: []string{"51baa8c1"},
},
filterAuthor: "John Doe <john@doe.com>",
expectedCommits: []*models.Commit{
{
Hash: "c3c4b66b64c97ffeecde",
Name: "checkout: moving from A to B",
Status: models.StatusReflog,
UnixTimestamp: 1643150483,
@@ -150,14 +176,13 @@ func TestGetReflogCommits(t *testing.T) {
}
for _, scenario := range scenarios {
scenario := scenario
t.Run(scenario.testName, func(t *testing.T) {
builder := &ReflogCommitLoader{
Common: utils.NewDummyCommon(),
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
}
commits, onlyObtainednew, err := builder.GetReflogCommits(scenario.lastReflogCommit, scenario.filterPath)
commits, onlyObtainednew, err := builder.GetReflogCommits(scenario.lastReflogCommit, scenario.filterPath, scenario.filterAuthor)
assert.Equal(t, scenario.expectedOnlyObtainedNew, onlyObtainednew)
assert.Equal(t, scenario.expectedError, err)
t.Logf("actual commits: \n%s", litter.Sdump(commits))

View File

@@ -2,6 +2,7 @@ package git_commands
import (
"fmt"
"strings"
"github.com/jesseduffield/gocui"
)
@@ -53,7 +54,15 @@ func (self *RemoteCommands) DeleteRemoteBranch(task gocui.Task, remoteName strin
Arg(remoteName, "--delete", branchName).
ToArgv()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
}
func (self *RemoteCommands) DeleteRemoteTag(task gocui.Task, remoteName string, tagName string) error {
cmdArgs := NewGitCmd("push").
Arg(remoteName, "--delete", tagName).
ToArgv()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
}
// CheckRemoteBranchExists Returns remote branch
@@ -66,3 +75,14 @@ func (self *RemoteCommands) CheckRemoteBranchExists(branchName string) bool {
return err == nil
}
// Resolve what might be a aliased URL into a full URL
// SEE: `man -P 'less +/--get-url +n' git-ls-remote`
func (self *RemoteCommands) GetRemoteURL(remoteName string) (string, error) {
cmdArgs := NewGitCmd("ls-remote").
Arg("--get-url", remoteName).
ToArgv()
url, err := self.cmd.New(cmdArgs).RunWithOutput()
return strings.TrimSpace(url), err
}

View File

@@ -1,6 +1,7 @@
package git_commands
import (
"fmt"
"strings"
"sync"
@@ -83,14 +84,23 @@ func (self *RemoteLoader) GetRemotes() ([]*models.Remote, error) {
func (self *RemoteLoader) getRemoteBranchesByRemoteName() (map[string][]*models.RemoteBranch, error) {
remoteBranchesByRemoteName := make(map[string][]*models.RemoteBranch)
cmdArgs := NewGitCmd("branch").Arg("-r").ToArgv()
err := self.cmd.New(cmdArgs).DontLog().RunAndProcessLines(func(line string) (bool, error) {
// excluding lines like 'origin/HEAD -> origin/master' (there will be a separate
// line for 'origin/master')
if strings.Contains(line, "->") {
return false, nil
}
var sortOrder string
switch strings.ToLower(self.AppState.RemoteBranchSortOrder) {
case "alphabetical":
sortOrder = "refname"
case "date":
sortOrder = "-committerdate"
default:
sortOrder = "refname"
}
cmdArgs := NewGitCmd("for-each-ref").
Arg(fmt.Sprintf("--sort=%s", sortOrder)).
Arg("--format=%(refname:short)").
Arg("refs/remotes").
ToArgv()
err := self.cmd.New(cmdArgs).DontLog().RunAndProcessLines(func(line string) (bool, error) {
line = strings.TrimSpace(line)
split := strings.SplitN(line, "/", 2)

View File

@@ -1,34 +1,27 @@
package git_commands
import (
"fmt"
ioFs "io/fs"
"os"
"path"
"path/filepath"
"strings"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/env"
"github.com/samber/lo"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/spf13/afero"
)
type RepoPaths struct {
currentPath string
worktreePath string
worktreeGitDirPath string
repoPath string
repoGitDirPath string
repoName string
isBareRepo bool
}
// Current working directory of the program. Currently, this will always
// be the same as WorktreePath(), but in future we may support running
// lazygit from inside a subdirectory of the worktree.
func (self *RepoPaths) CurrentPath() string {
return self.currentPath
}
var gitPathFormatVersion GitVersion = GitVersion{2, 31, 0, ""}
// Path to the current worktree. If we're in the main worktree, this will
// be the same as RepoPath()
@@ -62,182 +55,97 @@ func (self *RepoPaths) RepoName() string {
return self.repoName
}
func (self *RepoPaths) IsBareRepo() bool {
return self.isBareRepo
}
// Returns the repo paths for a typical repo
func MockRepoPaths(currentPath string) *RepoPaths {
return &RepoPaths{
currentPath: currentPath,
worktreePath: currentPath,
worktreeGitDirPath: path.Join(currentPath, ".git"),
worktreeGitDirPath: filepath.Join(currentPath, ".git"),
repoPath: currentPath,
repoGitDirPath: path.Join(currentPath, ".git"),
repoGitDirPath: filepath.Join(currentPath, ".git"),
repoName: "lazygit",
isBareRepo: false,
}
}
func GetRepoPaths(
fs afero.Fs,
currentPath string,
cmd oscommands.ICmdObjBuilder,
version *GitVersion,
) (*RepoPaths, error) {
return getRepoPathsAux(afero.NewOsFs(), resolveSymlink, currentPath)
cwd, err := os.Getwd()
if err != nil {
return nil, err
}
return GetRepoPathsForDir(cwd, cmd, version)
}
func getRepoPathsAux(
fs afero.Fs,
resolveSymlinkFn func(string) (string, error),
currentPath string,
func GetRepoPathsForDir(
dir string,
cmd oscommands.ICmdObjBuilder,
version *GitVersion,
) (*RepoPaths, error) {
worktreePath := currentPath
repoGitDirPath, repoPath, err := getCurrentRepoGitDirPath(fs, resolveSymlinkFn, currentPath)
gitDirOutput, err := callGitRevParseWithDir(cmd, version, dir, "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree")
if err != nil {
return nil, errors.Errorf("failed to get repo git dir path: %v", err)
return nil, err
}
var worktreeGitDirPath string
if env.GetWorkTreeEnv() != "" {
// This env is set when you pass --work-tree to lazygit. In that case,
// we're not dealing with a linked work-tree, we're dealing with a 'specified'
// worktree (for lack of a better term). In this case, the worktree has no
// .git file and it just contains a bunch of files: it has no idea it's
// pointed to by a bare repo. As such it does not have its own git dir within
// the bare repo's git dir. Instead, we just use the bare repo's git dir.
worktreeGitDirPath = repoGitDirPath
} else {
var err error
worktreeGitDirPath, err = getWorktreeGitDirPath(fs, currentPath)
gitDirResults := strings.Split(utils.NormalizeLinefeeds(gitDirOutput), "\n")
worktreePath := gitDirResults[0]
worktreeGitDirPath := gitDirResults[1]
repoGitDirPath := gitDirResults[2]
if version.IsOlderThanVersion(&gitPathFormatVersion) {
repoGitDirPath, err = filepath.Abs(repoGitDirPath)
if err != nil {
return nil, errors.Errorf("failed to get worktree git dir path: %v", err)
return nil, err
}
}
isBareRepo := gitDirResults[3] == "true"
repoName := path.Base(repoPath)
// If we're in a submodule, --show-superproject-working-tree will return
// a value, meaning gitDirResults will be length 5. In that case
// return the worktree path as the repoPath. Otherwise we're in a
// normal repo or a worktree so return the parent of the git common
// dir (repoGitDirPath)
isSubmodule := len(gitDirResults) == 5
var repoPath string
if isSubmodule {
repoPath = worktreePath
} else {
repoPath = filepath.Dir(repoGitDirPath)
}
repoName := filepath.Base(repoPath)
return &RepoPaths{
currentPath: currentPath,
worktreePath: worktreePath,
worktreeGitDirPath: worktreeGitDirPath,
repoPath: repoPath,
repoGitDirPath: repoGitDirPath,
repoName: repoName,
isBareRepo: isBareRepo,
}, nil
}
// Returns the path of the git-dir for the worktree. For linked worktrees, the worktree has
// a .git file that points to the git-dir (which itself lives in the git-dir
// of the repo)
func getWorktreeGitDirPath(fs afero.Fs, worktreePath string) (string, error) {
// if .git is a file, we're in a linked worktree, otherwise we're in
// the main worktree
dotGitPath := path.Join(worktreePath, ".git")
gitFileInfo, err := fs.Stat(dotGitPath)
func callGitRevParseWithDir(
cmd oscommands.ICmdObjBuilder,
version *GitVersion,
dir string,
gitRevArgs ...string,
) (string, error) {
gitRevParse := NewGitCmd("rev-parse").ArgIf(version.IsAtLeastVersion(&gitPathFormatVersion), "--path-format=absolute").Arg(gitRevArgs...)
if dir != "" {
gitRevParse.Dir(dir)
}
gitCmd := cmd.New(gitRevParse.ToArgv()).DontLog()
res, err := gitCmd.RunWithOutput()
if err != nil {
return "", err
return "", errors.Errorf("'%s' failed: %v", gitCmd.ToString(), err)
}
if gitFileInfo.IsDir() {
return dotGitPath, nil
}
return linkedWorktreeGitDirPath(fs, worktreePath)
}
func linkedWorktreeGitDirPath(fs afero.Fs, worktreePath string) (string, error) {
dotGitPath := path.Join(worktreePath, ".git")
gitFileContents, err := afero.ReadFile(fs, dotGitPath)
if err != nil {
return "", err
}
// The file will have `gitdir: /path/to/.git/worktrees/<worktree-name>`
gitDirLine := lo.Filter(strings.Split(string(gitFileContents), "\n"), func(line string, _ int) bool {
return strings.HasPrefix(line, "gitdir: ")
})
if len(gitDirLine) == 0 {
return "", errors.New(fmt.Sprintf("%s is a file which suggests we are in a submodule or a worktree but the file's contents do not contain a gitdir pointing to the actual .git directory", dotGitPath))
}
gitDir := strings.TrimPrefix(gitDirLine[0], "gitdir: ")
// For windows support
gitDir = filepath.ToSlash(gitDir)
return gitDir, nil
}
func getCurrentRepoGitDirPath(
fs afero.Fs,
resolveSymlinkFn func(string) (string, error),
currentPath string,
) (string, string, error) {
var unresolvedGitPath string
if env.GetGitDirEnv() != "" {
unresolvedGitPath = env.GetGitDirEnv()
} else {
unresolvedGitPath = path.Join(currentPath, ".git")
}
gitPath, err := resolveSymlinkFn(unresolvedGitPath)
if err != nil {
return "", "", err
}
// check if .git is a file or a directory
gitFileInfo, err := fs.Stat(gitPath)
if err != nil {
return "", "", err
}
if gitFileInfo.IsDir() {
// must be in the main worktree
return gitPath, path.Dir(gitPath), nil
}
// either in a submodule, or worktree
worktreeGitPath, err := linkedWorktreeGitDirPath(fs, currentPath)
if err != nil {
return "", "", errors.Errorf("could not find git dir for %s: %v", currentPath, err)
}
_, err = fs.Stat(worktreeGitPath)
if err != nil {
if os.IsNotExist(err) {
// hardcoding error to get around windows-specific error message
return "", "", errors.Errorf("could not find git dir for %s. %s does not exist", currentPath, worktreeGitPath)
}
return "", "", errors.Errorf("could not find git dir for %s: %v", currentPath, err)
}
// confirm whether the next directory up is the worktrees directory
parent := path.Dir(worktreeGitPath)
if path.Base(parent) == "worktrees" {
gitDirPath := path.Dir(parent)
return gitDirPath, path.Dir(gitDirPath), nil
}
// Unlike worktrees, submodules can be nested arbitrarily deep, so we check
// if the `modules` directory is anywhere up the chain.
if strings.Contains(worktreeGitPath, "/modules/") {
// For submodules, we just return the path directly
return worktreeGitPath, currentPath, nil
}
// If this error causes issues, we could relax the constraint and just always
// return the path
return "", "", errors.Errorf("could not find git dir for %s: path is not under `worktrees` or `modules` directories", currentPath)
}
// takes a path containing a symlink and returns the true path
func resolveSymlink(path string) (string, error) {
l, err := os.Lstat(path)
if err != nil {
return "", err
}
if l.Mode()&os.ModeSymlink == 0 {
return path, nil
}
return filepath.EvalSymlinks(path)
return strings.TrimSpace(res), nil
}
// Returns the paths of linked worktrees
@@ -245,7 +153,7 @@ func linkedWortkreePaths(fs afero.Fs, repoGitDirPath string) []string {
result := []string{}
// For each directory in this path we're going to cat the `gitdir` file and append its contents to our result
// That file points us to the `.git` file in the worktree.
worktreeGitDirsPath := path.Join(repoGitDirPath, "worktrees")
worktreeGitDirsPath := filepath.Join(repoGitDirPath, "worktrees")
// ensure the directory exists
_, err := fs.Stat(worktreeGitDirsPath)
@@ -262,7 +170,7 @@ func linkedWortkreePaths(fs afero.Fs, repoGitDirPath string) []string {
return nil
}
gitDirPath := path.Join(currPath, "gitdir")
gitDirPath := filepath.Join(currPath, "gitdir")
gitDirBytes, err := afero.ReadFile(fs, gitDirPath)
if err != nil {
// ignoring error
@@ -270,7 +178,7 @@ func linkedWortkreePaths(fs afero.Fs, repoGitDirPath string) []string {
}
trimmedGitDir := strings.TrimSpace(string(gitDirBytes))
// removing the .git part
worktreeDir := path.Dir(trimmedGitDir)
worktreeDir := filepath.Dir(trimmedGitDir)
result = append(result, worktreeDir)
return nil
})

View File

@@ -1,141 +1,223 @@
package git_commands
import (
"fmt"
"runtime"
"strings"
"testing"
"github.com/go-errors/errors"
"github.com/spf13/afero"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
)
func mockResolveSymlinkFn(p string) (string, error) { return p, nil }
type (
argFn func() []string
errFn func(getRevParseArgs argFn) error
)
type Scenario struct {
Name string
BeforeFunc func(fs afero.Fs)
BeforeFunc func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn)
Path string
Expected *RepoPaths
Err error
Err errFn
}
func TestGetRepoPathsAux(t *testing.T) {
func TestGetRepoPaths(t *testing.T) {
scenarios := []Scenario{
{
Name: "typical case",
BeforeFunc: func(fs afero.Fs) {
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
// setup for main worktree
_ = fs.MkdirAll("/path/to/repo/.git", 0o755)
mockOutput := lo.Ternary(runtime.GOOS == "windows", []string{
// --show-toplevel
`C:\path\to\repo`,
// --git-dir
`C:\path\to\repo\.git`,
// --git-common-dir
`C:\path\to\repo\.git`,
// --is-bare-repository
"false",
// --show-superproject-working-tree
}, []string{
// --show-toplevel
"/path/to/repo",
// --git-dir
"/path/to/repo/.git",
// --git-common-dir
"/path/to/repo/.git",
// --is-bare-repository
"false",
// --show-superproject-working-tree
})
runner.ExpectGitArgs(
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
strings.Join(mockOutput, "\n"),
nil)
},
Path: "/path/to/repo",
Expected: &RepoPaths{
currentPath: "/path/to/repo",
Expected: lo.Ternary(runtime.GOOS == "windows", &RepoPaths{
worktreePath: `C:\path\to\repo`,
worktreeGitDirPath: `C:\path\to\repo\.git`,
repoPath: `C:\path\to\repo`,
repoGitDirPath: `C:\path\to\repo\.git`,
repoName: `repo`,
isBareRepo: false,
}, &RepoPaths{
worktreePath: "/path/to/repo",
worktreeGitDirPath: "/path/to/repo/.git",
repoPath: "/path/to/repo",
repoGitDirPath: "/path/to/repo/.git",
repoName: "repo",
},
isBareRepo: false,
}),
Err: nil,
},
{
Name: "linked worktree",
BeforeFunc: func(fs afero.Fs) {
// setup for linked worktree
_ = fs.MkdirAll("/path/to/repo/.git/worktrees/worktree1", 0o755)
_ = afero.WriteFile(fs, "/path/to/repo/worktree1/.git", []byte("gitdir: /path/to/repo/.git/worktrees/worktree1"), 0o644)
},
Path: "/path/to/repo/worktree1",
Expected: &RepoPaths{
currentPath: "/path/to/repo/worktree1",
worktreePath: "/path/to/repo/worktree1",
worktreeGitDirPath: "/path/to/repo/.git/worktrees/worktree1",
repoPath: "/path/to/repo",
repoGitDirPath: "/path/to/repo/.git",
repoName: "repo",
Name: "bare repo",
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
// setup for main worktree
mockOutput := lo.Ternary(runtime.GOOS == "windows", []string{
// --show-toplevel
`C:\path\to\repo`,
// --git-dir
`C:\path\to\bare_repo\bare.git`,
// --git-common-dir
`C:\path\to\bare_repo\bare.git`,
// --is-bare-repository
`true`,
// --show-superproject-working-tree
}, []string{
// --show-toplevel
"/path/to/repo",
// --git-dir
"/path/to/bare_repo/bare.git",
// --git-common-dir
"/path/to/bare_repo/bare.git",
// --is-bare-repository
"true",
// --show-superproject-working-tree
})
runner.ExpectGitArgs(
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
strings.Join(mockOutput, "\n"),
nil)
},
Path: "/path/to/repo",
Expected: lo.Ternary(runtime.GOOS == "windows", &RepoPaths{
worktreePath: `C:\path\to\repo`,
worktreeGitDirPath: `C:\path\to\bare_repo\bare.git`,
repoPath: `C:\path\to\bare_repo`,
repoGitDirPath: `C:\path\to\bare_repo\bare.git`,
repoName: `bare_repo`,
isBareRepo: true,
}, &RepoPaths{
worktreePath: "/path/to/repo",
worktreeGitDirPath: "/path/to/bare_repo/bare.git",
repoPath: "/path/to/bare_repo",
repoGitDirPath: "/path/to/bare_repo/bare.git",
repoName: "bare_repo",
isBareRepo: true,
}),
Err: nil,
},
{
Name: "worktree .git file missing gitdir directive",
BeforeFunc: func(fs afero.Fs) {
_ = fs.MkdirAll("/path/to/repo/.git/worktrees/worktree2", 0o755)
_ = afero.WriteFile(fs, "/path/to/repo/worktree2/.git", []byte("blah"), 0o644)
},
Path: "/path/to/repo/worktree2",
Expected: nil,
Err: errors.New("failed to get repo git dir path: could not find git dir for /path/to/repo/worktree2: /path/to/repo/worktree2/.git is a file which suggests we are in a submodule or a worktree but the file's contents do not contain a gitdir pointing to the actual .git directory"),
},
{
Name: "worktree .git file gitdir directive points to a non-existing directory",
BeforeFunc: func(fs afero.Fs) {
_ = fs.MkdirAll("/path/to/repo/.git/worktrees/worktree2", 0o755)
_ = afero.WriteFile(fs, "/path/to/repo/worktree2/.git", []byte("gitdir: /nonexistant"), 0o644)
},
Path: "/path/to/repo/worktree2",
Expected: nil,
Err: errors.New("failed to get repo git dir path: could not find git dir for /path/to/repo/worktree2. /nonexistant does not exist"),
},
{
Name: "submodule",
BeforeFunc: func(fs afero.Fs) {
_ = fs.MkdirAll("/path/to/repo/.git/modules/submodule1", 0o755)
_ = afero.WriteFile(fs, "/path/to/repo/submodule1/.git", []byte("gitdir: /path/to/repo/.git/modules/submodule1"), 0o644)
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
mockOutput := lo.Ternary(runtime.GOOS == "windows", []string{
// --show-toplevel
`C:\path\to\repo\submodule1`,
// --git-dir
`C:\path\to\repo\.git\modules\submodule1`,
// --git-common-dir
`C:\path\to\repo\.git\modules\submodule1`,
// --is-bare-repository
`false`,
// --show-superproject-working-tree
`C:\path\to\repo`,
}, []string{
// --show-toplevel
"/path/to/repo/submodule1",
// --git-dir
"/path/to/repo/.git/modules/submodule1",
// --git-common-dir
"/path/to/repo/.git/modules/submodule1",
// --is-bare-repository
"false",
// --show-superproject-working-tree
"/path/to/repo",
})
runner.ExpectGitArgs(
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
strings.Join(mockOutput, "\n"),
nil)
},
Path: "/path/to/repo/submodule1",
Expected: &RepoPaths{
currentPath: "/path/to/repo/submodule1",
Expected: lo.Ternary(runtime.GOOS == "windows", &RepoPaths{
worktreePath: `C:\path\to\repo\submodule1`,
worktreeGitDirPath: `C:\path\to\repo\.git\modules\submodule1`,
repoPath: `C:\path\to\repo\submodule1`,
repoGitDirPath: `C:\path\to\repo\.git\modules\submodule1`,
repoName: `submodule1`,
isBareRepo: false,
}, &RepoPaths{
worktreePath: "/path/to/repo/submodule1",
worktreeGitDirPath: "/path/to/repo/.git/modules/submodule1",
repoPath: "/path/to/repo/submodule1",
repoGitDirPath: "/path/to/repo/.git/modules/submodule1",
repoName: "submodule1",
},
isBareRepo: false,
}),
Err: nil,
},
{
Name: "submodule in nested directory",
BeforeFunc: func(fs afero.Fs) {
_ = fs.MkdirAll("/path/to/repo/.git/modules/my/submodule1", 0o755)
_ = afero.WriteFile(fs, "/path/to/repo/my/submodule1/.git", []byte("gitdir: /path/to/repo/.git/modules/my/submodule1"), 0o644)
Name: "git rev-parse returns an error",
BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
runner.ExpectGitArgs(
append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--is-bare-repository", "--show-superproject-working-tree"),
"",
errors.New("fatal: invalid gitfile format: /path/to/repo/worktree2/.git"))
},
Path: "/path/to/repo/my/submodule1",
Expected: &RepoPaths{
currentPath: "/path/to/repo/my/submodule1",
worktreePath: "/path/to/repo/my/submodule1",
worktreeGitDirPath: "/path/to/repo/.git/modules/my/submodule1",
repoPath: "/path/to/repo/my/submodule1",
repoGitDirPath: "/path/to/repo/.git/modules/my/submodule1",
repoName: "submodule1",
},
Err: nil,
},
{
Name: "submodule git dir not under .git/modules",
BeforeFunc: func(fs afero.Fs) {
_ = fs.MkdirAll("/random/submodule1", 0o755)
_ = afero.WriteFile(fs, "/path/to/repo/my/submodule1/.git", []byte("gitdir: /random/submodule1"), 0o644)
},
Path: "/path/to/repo/my/submodule1",
Path: "/path/to/repo/worktree2",
Expected: nil,
Err: errors.New("failed to get repo git dir path: could not find git dir for /path/to/repo/my/submodule1: path is not under `worktrees` or `modules` directories"),
Err: func(getRevParseArgs argFn) error {
args := strings.Join(getRevParseArgs(), " ")
return errors.New(
fmt.Sprintf("'git %v --show-toplevel --absolute-git-dir --git-common-dir --is-bare-repository --show-superproject-working-tree' failed: fatal: invalid gitfile format: /path/to/repo/worktree2/.git", args),
)
},
},
}
for _, s := range scenarios {
s := s
t.Run(s.Name, func(t *testing.T) {
fs := afero.NewMemMapFs()
runner := oscommands.NewFakeRunner(t)
cmd := oscommands.NewDummyCmdObjBuilder(runner)
version, err := GetGitVersion(oscommands.NewDummyOSCommand())
if err != nil {
t.Fatal(err)
}
getRevParseArgs := func() []string {
args := []string{"rev-parse"}
if version.IsAtLeast(2, 31, 0) {
args = append(args, "--path-format=absolute")
}
return args
}
// prepare the filesystem for the scenario
s.BeforeFunc(fs)
s.BeforeFunc(runner, getRevParseArgs)
// run the function with the scenario path
repoPaths, err := getRepoPathsAux(fs, mockResolveSymlinkFn, s.Path)
repoPaths, err := GetRepoPathsForDir("", cmd, version)
// check the error and the paths
if s.Err != nil {
scenarioErr := s.Err(getRevParseArgs)
assert.Error(t, err)
assert.EqualError(t, err, s.Err.Error())
assert.EqualError(t, err, scenarioErr.Error())
} else {
assert.Nil(t, err)
assert.Equal(t, s.Expected, repoPaths)

View File

@@ -60,34 +60,36 @@ func (self *StashCommands) Push(message string) error {
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) Store(sha string, message string) error {
func (self *StashCommands) Store(hash string, message string) error {
trimmedMessage := strings.Trim(message, " \t")
cmdArgs := NewGitCmd("stash").Arg("store").
ArgIf(trimmedMessage != "", "-m", trimmedMessage).
Arg(sha).
Arg(hash).
ToArgv()
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) Sha(index int) (string, error) {
func (self *StashCommands) Hash(index int) (string, error) {
cmdArgs := NewGitCmd("rev-parse").
Arg(fmt.Sprintf("refs/stash@{%d}", index)).
ToArgv()
sha, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
return strings.Trim(sha, "\r\n"), err
hash, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
return strings.Trim(hash, "\r\n"), err
}
func (self *StashCommands) ShowStashEntryCmdObj(index int, ignoreWhitespace bool) oscommands.ICmdObj {
func (self *StashCommands) ShowStashEntryCmdObj(index int) oscommands.ICmdObj {
cmdArgs := NewGitCmd("stash").Arg("show").
Arg("-p").
Arg("--stat").
Arg(fmt.Sprintf("--color=%s", self.UserConfig.Git.Paging.ColorArg)).
Arg(fmt.Sprintf("--unified=%d", self.UserConfig.Git.DiffContextSize)).
ArgIf(ignoreWhitespace, "--ignore-all-space").
Arg(fmt.Sprintf("--color=%s", self.UserConfig().Git.Paging.ColorArg)).
Arg(fmt.Sprintf("--unified=%d", self.AppState.DiffContextSize)).
ArgIf(self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
Arg(fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold)).
Arg(fmt.Sprintf("stash@{%d}", index)).
Dir(self.repoPaths.worktreePath).
ToArgv()
return self.cmd.New(cmdArgs).DontLog()
@@ -120,9 +122,22 @@ func (self *StashCommands) StashUnstagedChanges(message string) error {
return nil
}
// SaveStagedChanges stashes only the currently staged changes. This takes a few steps
// shoutouts to Joe on https://stackoverflow.com/questions/14759748/stashing-only-staged-changes-in-git-is-it-possible
// SaveStagedChanges stashes only the currently staged changes.
func (self *StashCommands) SaveStagedChanges(message string) error {
if self.version.IsAtLeast(2, 35, 0) {
return self.cmd.New(NewGitCmd("stash").Arg("push").Arg("--staged").Arg("-m", message).ToArgv()).Run()
}
// Git versions older than 2.35.0 don't support the --staged flag, so we
// need to fall back to a more complex solution.
// Shoutouts to Joe on https://stackoverflow.com/questions/14759748/stashing-only-staged-changes-in-git-is-it-possible
//
// Note that this method has a few bugs:
// - it fails when there are *only* staged changes
// - it fails when staged and unstaged changes within a single file are too close together
// We don't bother fixing these, because users can simply update git when
// they are affected by these issues.
// wrap in 'writing', which uses a mutex
if err := self.cmd.New(
NewGitCmd("stash").Arg("--keep-index").ToArgv(),
@@ -178,7 +193,7 @@ func (self *StashCommands) StashIncludeUntrackedChanges(message string) error {
}
func (self *StashCommands) Rename(index int, message string) error {
sha, err := self.Sha(index)
hash, err := self.Hash(index)
if err != nil {
return err
}
@@ -187,7 +202,7 @@ func (self *StashCommands) Rename(index int, message string) error {
return err
}
err = self.Store(sha, message)
err = self.Store(hash, message)
if err != nil {
return err
}

View File

@@ -32,14 +32,14 @@ func (self *StashLoader) GetStashEntries(filterPath string) []*models.StashEntry
return self.getUnfilteredStashEntries()
}
cmdArgs := NewGitCmd("stash").Arg("list", "--name-only").ToArgv()
cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--name-only", "--pretty=%ct|%gs").ToArgv()
rawString, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
if err != nil {
return self.getUnfilteredStashEntries()
}
stashEntries := []*models.StashEntry{}
var currentStashEntry *models.StashEntry
lines := utils.SplitLines(rawString)
lines := utils.SplitNul(rawString)
isAStash := func(line string) bool { return strings.HasPrefix(line, "stash@{") }
re := regexp.MustCompile(`stash@\{(\d+)\}`)
@@ -66,7 +66,7 @@ outer:
}
func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--pretty=%gs").ToArgv()
cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--pretty=%ct|%gs").ToArgv()
rawString, _ := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return lo.Map(utils.SplitNul(rawString), func(line string, index int) *models.StashEntry {
@@ -75,8 +75,23 @@ func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
}
func (c *StashLoader) stashEntryFromLine(line string, index int) *models.StashEntry {
return &models.StashEntry{
model := &models.StashEntry{
Name: line,
Index: index,
}
tstr, msg, ok := strings.Cut(line, "|")
if !ok {
return model
}
t, err := strconv.ParseInt(tstr, 10, 64)
if err != nil {
return model
}
model.Name = msg
model.Recency = utils.UnixToTimeAgo(t)
return model
}

View File

@@ -22,14 +22,14 @@ func TestGetStashEntries(t *testing.T) {
"No stash entries found",
"",
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%gs"}, "", nil),
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%ct|%gs"}, "", nil),
[]*models.StashEntry{},
},
{
"Several stash entries found",
"",
oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%gs"},
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%ct|%gs"},
"WIP on add-pkg-commands-test: 55c6af2 increase parallel build\x00WIP on master: bb86a3f update github template\x00",
nil,
),
@@ -47,7 +47,6 @@ func TestGetStashEntries(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
cmd := oscommands.NewDummyCmdObjBuilder(s.runner)

View File

@@ -47,7 +47,7 @@ func TestStashSave(t *testing.T) {
func TestStashStore(t *testing.T) {
type scenario struct {
testName string
sha string
hash string
message string
expected []string
}
@@ -55,89 +55,105 @@ func TestStashStore(t *testing.T) {
scenarios := []scenario{
{
testName: "Non-empty message",
sha: "0123456789abcdef",
hash: "0123456789abcdef",
message: "New stash name",
expected: []string{"stash", "store", "-m", "New stash name", "0123456789abcdef"},
},
{
testName: "Empty message",
sha: "0123456789abcdef",
hash: "0123456789abcdef",
message: "",
expected: []string{"stash", "store", "0123456789abcdef"},
},
{
testName: "Space message",
sha: "0123456789abcdef",
hash: "0123456789abcdef",
message: " ",
expected: []string{"stash", "store", "0123456789abcdef"},
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
ExpectGitArgs(s.expected, "", nil)
instance := buildStashCommands(commonDeps{runner: runner})
assert.NoError(t, instance.Store(s.sha, s.message))
assert.NoError(t, instance.Store(s.hash, s.message))
runner.CheckForMissingCalls()
})
}
}
func TestStashSha(t *testing.T) {
func TestStashHash(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"rev-parse", "refs/stash@{5}"}, "14d94495194651adfd5f070590df566c11d28243\n", nil)
instance := buildStashCommands(commonDeps{runner: runner})
sha, err := instance.Sha(5)
hash, err := instance.Hash(5)
assert.NoError(t, err)
assert.Equal(t, "14d94495194651adfd5f070590df566c11d28243", sha)
assert.Equal(t, "14d94495194651adfd5f070590df566c11d28243", hash)
runner.CheckForMissingCalls()
}
func TestStashStashEntryCmdObj(t *testing.T) {
type scenario struct {
testName string
index int
contextSize int
ignoreWhitespace bool
expected []string
testName string
index int
contextSize int
similarityThreshold int
ignoreWhitespace bool
expected []string
}
scenarios := []scenario{
{
testName: "Default case",
index: 5,
contextSize: 3,
ignoreWhitespace: false,
expected: []string{"git", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "stash@{5}"},
testName: "Default case",
index: 5,
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: false,
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--find-renames=50%", "stash@{5}"},
},
{
testName: "Show diff with custom context size",
index: 5,
contextSize: 77,
ignoreWhitespace: false,
expected: []string{"git", "stash", "show", "-p", "--stat", "--color=always", "--unified=77", "stash@{5}"},
testName: "Show diff with custom context size",
index: 5,
contextSize: 77,
similarityThreshold: 50,
ignoreWhitespace: false,
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=77", "--find-renames=50%", "stash@{5}"},
},
{
testName: "Default case",
index: 5,
contextSize: 3,
ignoreWhitespace: true,
expected: []string{"git", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--ignore-all-space", "stash@{5}"},
testName: "Show diff with custom similarity threshold",
index: 5,
contextSize: 3,
similarityThreshold: 33,
ignoreWhitespace: false,
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--find-renames=33%", "stash@{5}"},
},
{
testName: "Default case",
index: 5,
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: true,
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--ignore-all-space", "--find-renames=50%", "stash@{5}"},
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
userConfig := config.GetDefaultConfig()
userConfig.Git.DiffContextSize = s.contextSize
instance := buildStashCommands(commonDeps{userConfig: userConfig})
appState := &config.AppState{}
appState.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
appState.DiffContextSize = s.contextSize
appState.RenameSimilarityThreshold = s.similarityThreshold
repoPaths := RepoPaths{
worktreePath: "/path/to/worktree",
}
instance := buildStashCommands(commonDeps{userConfig: userConfig, appState: appState, repoPaths: &repoPaths})
cmdStr := instance.ShowStashEntryCmdObj(s.index, s.ignoreWhitespace).Args()
cmdStr := instance.ShowStashEntryCmdObj(s.index).Args()
assert.Equal(t, s.expected, cmdStr)
})
}
@@ -148,8 +164,8 @@ func TestStashRename(t *testing.T) {
testName string
index int
message string
expectedShaCmd []string
shaResult string
expectedHashCmd []string
hashResult string
expectedDropCmd []string
expectedStoreCmd []string
}
@@ -159,8 +175,8 @@ func TestStashRename(t *testing.T) {
testName: "Default case",
index: 3,
message: "New message",
expectedShaCmd: []string{"rev-parse", "refs/stash@{3}"},
shaResult: "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd\n",
expectedHashCmd: []string{"rev-parse", "refs/stash@{3}"},
hashResult: "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd\n",
expectedDropCmd: []string{"stash", "drop", "stash@{3}"},
expectedStoreCmd: []string{"stash", "store", "-m", "New message", "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd"},
},
@@ -168,18 +184,17 @@ func TestStashRename(t *testing.T) {
testName: "Empty message",
index: 4,
message: "",
expectedShaCmd: []string{"rev-parse", "refs/stash@{4}"},
shaResult: "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd\n",
expectedHashCmd: []string{"rev-parse", "refs/stash@{4}"},
hashResult: "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd\n",
expectedDropCmd: []string{"stash", "drop", "stash@{4}"},
expectedStoreCmd: []string{"stash", "store", "f0d0f20f2f61ffd6d6bfe0752deffa38845a3edd"},
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
ExpectGitArgs(s.expectedShaCmd, s.shaResult, nil).
ExpectGitArgs(s.expectedHashCmd, s.hashResult, nil).
ExpectGitArgs(s.expectedDropCmd, "", nil).
ExpectGitArgs(s.expectedStoreCmd, "", nil)
instance := buildStashCommands(commonDeps{runner: runner})

View File

@@ -3,10 +3,8 @@ package git_commands
import (
"os"
"path/filepath"
"strconv"
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
)
@@ -25,19 +23,16 @@ func NewStatusCommands(
// RebaseMode returns "" for non-rebase mode, "normal" for normal rebase
// and "interactive" for interactive rebase
func (self *StatusCommands) RebaseMode() (enums.RebaseMode, error) {
exists, err := self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply"))
if err != nil {
return enums.REBASE_MODE_NONE, err
}
if exists {
ok, err := self.IsInNormalRebase()
if err == nil && ok {
return enums.REBASE_MODE_NORMAL, nil
}
exists, err = self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge"))
if exists {
ok, err = self.IsInInteractiveRebase()
if err == nil && ok {
return enums.REBASE_MODE_INTERACTIVE, err
} else {
return enums.REBASE_MODE_NONE, err
}
return enums.REBASE_MODE_NONE, err
}
func (self *StatusCommands) WorkingTreeState() enums.RebaseMode {
@@ -52,20 +47,16 @@ func (self *StatusCommands) WorkingTreeState() enums.RebaseMode {
return enums.REBASE_MODE_NONE
}
func (self *StatusCommands) IsBareRepo() (bool, error) {
return IsBareRepo(self.os)
func (self *StatusCommands) IsBareRepo() bool {
return self.repoPaths.isBareRepo
}
func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
res, err := osCommand.Cmd.New(
NewGitCmd("rev-parse").Arg("--is-bare-repository").ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
return false, err
}
func (self *StatusCommands) IsInNormalRebase() (bool, error) {
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply"))
}
// The command returns output with a newline, so we need to strip
return strconv.ParseBool(strings.TrimSpace(res))
func (self *StatusCommands) IsInInteractiveRebase() (bool, error) {
return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge"))
}
// IsInMergeState states whether we are still mid-merge

View File

@@ -26,8 +26,12 @@ func NewSubmoduleCommands(gitCommon *GitCommon) *SubmoduleCommands {
}
}
func (self *SubmoduleCommands) GetConfigs() ([]*models.SubmoduleConfig, error) {
file, err := os.Open(".gitmodules")
func (self *SubmoduleCommands) GetConfigs(parentModule *models.SubmoduleConfig) ([]*models.SubmoduleConfig, error) {
gitModulesPath := ".gitmodules"
if parentModule != nil {
gitModulesPath = filepath.Join(parentModule.FullPath(), gitModulesPath)
}
file, err := os.Open(gitModulesPath)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
@@ -51,21 +55,27 @@ func (self *SubmoduleCommands) GetConfigs() ([]*models.SubmoduleConfig, error) {
}
configs := []*models.SubmoduleConfig{}
lastConfigIdx := -1
for scanner.Scan() {
line := scanner.Text()
if name, ok := firstMatch(line, `\[submodule "(.*)"\]`); ok {
configs = append(configs, &models.SubmoduleConfig{Name: name})
configs = append(configs, &models.SubmoduleConfig{
Name: name, ParentModule: parentModule,
})
lastConfigIdx = len(configs) - 1
continue
}
if len(configs) > 0 {
lastConfig := configs[len(configs)-1]
if lastConfigIdx != -1 {
if path, ok := firstMatch(line, `\s*path\s*=\s*(.*)\s*`); ok {
lastConfig.Path = path
configs[lastConfigIdx].Path = path
nestedConfigs, err := self.GetConfigs(configs[lastConfigIdx])
if err == nil {
configs = append(configs, nestedConfigs...)
}
} else if url, ok := firstMatch(line, `\s*url\s*=\s*(.*)\s*`); ok {
lastConfig.Url = url
configs[lastConfigIdx].Url = url
}
}
}
@@ -77,12 +87,12 @@ func (self *SubmoduleCommands) Stash(submodule *models.SubmoduleConfig) error {
// if the path does not exist then it hasn't yet been initialized so we'll swallow the error
// because the intention here is to have no dirty worktree state
if _, err := os.Stat(submodule.Path); os.IsNotExist(err) {
self.Log.Infof("submodule path %s does not exist, returning", submodule.Path)
self.Log.Infof("submodule path %s does not exist, returning", submodule.FullPath())
return nil
}
cmdArgs := NewGitCmd("stash").
Dir(submodule.Path).
Dir(submodule.FullPath()).
Arg("--include-untracked").
ToArgv()
@@ -90,8 +100,13 @@ func (self *SubmoduleCommands) Stash(submodule *models.SubmoduleConfig) error {
}
func (self *SubmoduleCommands) Reset(submodule *models.SubmoduleConfig) error {
parentDir := ""
if submodule.ParentModule != nil {
parentDir = submodule.ParentModule.FullPath()
}
cmdArgs := NewGitCmd("submodule").
Arg("update", "--init", "--force", "--", submodule.Path).
DirIf(parentDir != "", parentDir).
ToArgv()
return self.cmd.New(cmdArgs).Run()
@@ -107,6 +122,20 @@ func (self *SubmoduleCommands) UpdateAll() error {
func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
// based on https://gist.github.com/myusuf3/7f645819ded92bda6677
if submodule.ParentModule != nil {
wd, err := os.Getwd()
if err != nil {
return err
}
err = os.Chdir(submodule.ParentModule.FullPath())
if err != nil {
return err
}
defer func() { _ = os.Chdir(wd) }()
}
if err := self.cmd.New(
NewGitCmd("submodule").
Arg("deinit", "--force", "--", submodule.Path).ToArgv(),
@@ -141,7 +170,7 @@ func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
// We may in fact want to use the repo's git dir path but git docs say not to
// mix submodules and worktrees anyway.
return os.RemoveAll(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "modules", submodule.Path))
return os.RemoveAll(submodule.GitDirPath(self.repoPaths.repoGitDirPath))
}
func (self *SubmoduleCommands) Add(name string, path string, url string) error {
@@ -158,10 +187,24 @@ func (self *SubmoduleCommands) Add(name string, path string, url string) error {
return self.cmd.New(cmdArgs).Run()
}
func (self *SubmoduleCommands) UpdateUrl(name string, path string, newUrl string) error {
func (self *SubmoduleCommands) UpdateUrl(submodule *models.SubmoduleConfig, newUrl string) error {
if submodule.ParentModule != nil {
wd, err := os.Getwd()
if err != nil {
return err
}
err = os.Chdir(submodule.ParentModule.FullPath())
if err != nil {
return err
}
defer func() { _ = os.Chdir(wd) }()
}
setUrlCmdStr := NewGitCmd("config").
Arg(
"--file", ".gitmodules", "submodule."+name+".url", newUrl,
"--file", ".gitmodules", "submodule."+submodule.Name+".url", newUrl,
).
ToArgv()
@@ -170,7 +213,7 @@ func (self *SubmoduleCommands) UpdateUrl(name string, path string, newUrl string
return err
}
syncCmdStr := NewGitCmd("submodule").Arg("sync", "--", path).
syncCmdStr := NewGitCmd("submodule").Arg("sync", "--", submodule.Path).
ToArgv()
if err := self.cmd.New(syncCmdStr).Run(); err != nil {

View File

@@ -19,6 +19,7 @@ func NewSyncCommands(gitCommon *GitCommon) *SyncCommands {
// Push pushes to a branch
type PushOpts struct {
Force bool
ForceWithLease bool
UpstreamRemote string
UpstreamBranch string
SetUpstream bool
@@ -30,13 +31,14 @@ func (self *SyncCommands) PushCmdObj(task gocui.Task, opts PushOpts) (oscommands
}
cmdArgs := NewGitCmd("push").
ArgIf(opts.Force, "--force-with-lease").
ArgIf(opts.Force, "--force").
ArgIf(opts.ForceWithLease, "--force-with-lease").
ArgIf(opts.SetUpstream, "--set-upstream").
ArgIf(opts.UpstreamRemote != "", opts.UpstreamRemote).
ArgIf(opts.UpstreamBranch != "", opts.UpstreamBranch).
ArgIf(opts.UpstreamBranch != "", "HEAD:"+opts.UpstreamBranch).
ToArgv()
cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex)
cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest(task)
return cmdObj, nil
}
@@ -49,10 +51,16 @@ func (self *SyncCommands) Push(task gocui.Task, opts PushOpts) error {
return cmdObj.Run()
}
func (self *SyncCommands) fetchCommandBuilder(fetchAll bool) *GitCommandBuilder {
return NewGitCmd("fetch").
ArgIf(fetchAll, "--all").
// avoid writing to .git/FETCH_HEAD; this allows running a pull
// concurrently without getting errors
ArgIf(self.version.IsAtLeast(2, 29, 0), "--no-write-fetch-head")
}
func (self *SyncCommands) FetchCmdObj(task gocui.Task) oscommands.ICmdObj {
cmdArgs := NewGitCmd("fetch").
ArgIf(self.UserConfig.Git.FetchAll, "--all").
ToArgv()
cmdArgs := self.fetchCommandBuilder(self.UserConfig().Git.FetchAll).ToArgv()
cmdObj := self.cmd.New(cmdArgs)
cmdObj.PromptOnCredentialRequest(task)
@@ -64,13 +72,10 @@ func (self *SyncCommands) Fetch(task gocui.Task) error {
}
func (self *SyncCommands) FetchBackgroundCmdObj() oscommands.ICmdObj {
cmdArgs := NewGitCmd("fetch").
ArgIf(self.UserConfig.Git.FetchAll, "--all").
ToArgv()
cmdArgs := self.fetchCommandBuilder(self.UserConfig().Git.FetchAll).ToArgv()
cmdObj := self.cmd.New(cmdArgs)
cmdObj.DontLog().FailOnCredentialRequest()
cmdObj.WithMutex(self.syncMutex)
return cmdObj
}
@@ -90,13 +95,13 @@ func (self *SyncCommands) Pull(task gocui.Task, opts PullOptions) error {
Arg("--no-edit").
ArgIf(opts.FastForwardOnly, "--ff-only").
ArgIf(opts.RemoteName != "", opts.RemoteName).
ArgIf(opts.BranchName != "", opts.BranchName).
ArgIf(opts.BranchName != "", "refs/heads/"+opts.BranchName).
GitDirIf(opts.WorktreeGitDir != "", opts.WorktreeGitDir).
ToArgv()
// setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user
// has 'pull.rebase = interactive' configured.
return self.cmd.New(cmdArgs).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest(task).Run()
}
func (self *SyncCommands) FastForward(
@@ -105,18 +110,18 @@ func (self *SyncCommands) FastForward(
remoteName string,
remoteBranchName string,
) error {
cmdArgs := NewGitCmd("fetch").
cmdArgs := self.fetchCommandBuilder(false).
Arg(remoteName).
Arg(remoteBranchName + ":" + branchName).
Arg("refs/heads/" + remoteBranchName + ":" + branchName).
ToArgv()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
}
func (self *SyncCommands) FetchRemote(task gocui.Task, remoteName string) error {
cmdArgs := NewGitCmd("fetch").
cmdArgs := self.fetchCommandBuilder(false).
Arg(remoteName).
ToArgv()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
}

View File

@@ -18,62 +18,70 @@ func TestSyncPush(t *testing.T) {
scenarios := []scenario{
{
testName: "Push with force disabled",
opts: PushOpts{Force: false},
opts: PushOpts{ForceWithLease: false},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push"})
assert.NoError(t, err)
},
},
{
testName: "Push with force-with-lease enabled",
opts: PushOpts{ForceWithLease: true},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease"})
assert.NoError(t, err)
},
},
{
testName: "Push with force enabled",
opts: PushOpts{Force: true},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease"})
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force"})
assert.NoError(t, err)
},
},
{
testName: "Push with force disabled, upstream supplied",
opts: PushOpts{
Force: false,
ForceWithLease: false,
UpstreamRemote: "origin",
UpstreamBranch: "master",
},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "origin", "master"})
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "origin", "HEAD:master"})
assert.NoError(t, err)
},
},
{
testName: "Push with force disabled, setting upstream",
opts: PushOpts{
Force: false,
ForceWithLease: false,
UpstreamRemote: "origin",
UpstreamBranch: "master",
SetUpstream: true,
},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--set-upstream", "origin", "master"})
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--set-upstream", "origin", "HEAD:master"})
assert.NoError(t, err)
},
},
{
testName: "Push with force enabled, setting upstream",
testName: "Push with force-with-lease enabled, setting upstream",
opts: PushOpts{
Force: true,
ForceWithLease: true,
UpstreamRemote: "origin",
UpstreamBranch: "master",
SetUpstream: true,
},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease", "--set-upstream", "origin", "master"})
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease", "--set-upstream", "origin", "HEAD:master"})
assert.NoError(t, err)
},
},
{
testName: "Push with remote branch but no origin",
opts: PushOpts{
Force: true,
ForceWithLease: true,
UpstreamRemote: "",
UpstreamBranch: "master",
SetUpstream: true,
@@ -86,7 +94,6 @@ func TestSyncPush(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildSyncCommands(commonDeps{})
task := gocui.NewFakeTask()
@@ -124,10 +131,9 @@ func TestSyncFetch(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildSyncCommands(commonDeps{})
instance.UserConfig.Git.FetchAll = s.fetchAllConfig
instance.UserConfig().Git.FetchAll = s.fetchAllConfig
task := gocui.NewFakeTask()
s.test(instance.FetchCmdObj(task))
})
@@ -163,10 +169,9 @@ func TestSyncFetchBackground(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildSyncCommands(commonDeps{})
instance.UserConfig.Git.FetchAll = s.fetchAllConfig
instance.UserConfig().Git.FetchAll = s.fetchAllConfig
s.test(instance.FetchBackgroundCmdObj())
})
}

View File

@@ -41,7 +41,7 @@ func (self *TagCommands) HasTag(tagName string) bool {
return self.cmd.New(cmdArgs).Run() == nil
}
func (self *TagCommands) Delete(tagName string) error {
func (self *TagCommands) LocalDelete(tagName string) error {
cmdArgs := NewGitCmd("tag").Arg("-d", tagName).
ToArgv()
@@ -52,5 +52,5 @@ func (self *TagCommands) Push(task gocui.Task, remoteName string, tagName string
cmdArgs := NewGitCmd("push").Arg(remoteName, "tag", tagName).
ToArgv()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).Run()
}

View File

@@ -44,7 +44,6 @@ func TestGetTags(t *testing.T) {
}
for _, scenario := range scenarios {
scenario := scenario
t.Run(scenario.testName, func(t *testing.T) {
loader := &TagLoader{
Common: utils.NewDummyCommon(),

View File

@@ -69,3 +69,11 @@ func (v *GitVersion) IsOlderThan(major, minor, patch int) bool {
func (v *GitVersion) IsOlderThanVersion(version *GitVersion) bool {
return v.IsOlderThan(version.Major, version.Minor, version.Patch)
}
func (v *GitVersion) IsAtLeast(major, minor, patch int) bool {
return !v.IsOlderThan(major, minor, patch)
}
func (v *GitVersion) IsAtLeastVersion(version *GitVersion) bool {
return v.IsAtLeast(version.Major, version.Minor, version.Patch)
}

View File

@@ -45,3 +45,12 @@ func TestGitVersionIsOlderThan(t *testing.T) {
assert.True(t, (&GitVersion{2, 0, 1, ""}).IsOlderThan(2, 1, 0))
assert.True(t, (&GitVersion{2, 0, 1, ""}).IsOlderThan(3, 0, 0))
}
func TestGitVersionIsAtLeast(t *testing.T) {
assert.True(t, (&GitVersion{2, 0, 0, ""}).IsAtLeast(1, 99, 99))
assert.True(t, (&GitVersion{2, 0, 0, ""}).IsAtLeast(2, 0, 0))
assert.True(t, (&GitVersion{2, 1, 0, ""}).IsAtLeast(2, 0, 9))
assert.False(t, (&GitVersion{2, 0, 1, ""}).IsAtLeast(2, 1, 0))
assert.False(t, (&GitVersion{2, 0, 1, ""}).IsAtLeast(3, 0, 0))
}

View File

@@ -3,6 +3,7 @@ package git_commands
import (
"fmt"
"os"
"path/filepath"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/commands/models"
@@ -31,10 +32,6 @@ func (self *WorkingTreeCommands) OpenMergeToolCmdObj() oscommands.ICmdObj {
return self.cmd.New(NewGitCmd("mergetool").ToArgv())
}
func (self *WorkingTreeCommands) OpenMergeTool() error {
return self.OpenMergeToolCmdObj().Run()
}
// StageFile stages a file
func (self *WorkingTreeCommands) StageFile(path string) error {
return self.StageFiles([]string{path})
@@ -61,21 +58,20 @@ func (self *WorkingTreeCommands) UnstageAll() error {
// UnStageFile unstages a file
// we accept an array of filenames for the cases where a file has been renamed i.e.
// we accept the current name and the previous name
func (self *WorkingTreeCommands) UnStageFile(fileNames []string, reset bool) error {
for _, name := range fileNames {
var cmdArgs []string
if reset {
cmdArgs = NewGitCmd("reset").Arg("HEAD", "--", name).ToArgv()
} else {
cmdArgs = NewGitCmd("rm").Arg("--cached", "--force", "--", name).ToArgv()
}
err := self.cmd.New(cmdArgs).Run()
if err != nil {
return err
}
func (self *WorkingTreeCommands) UnStageFile(paths []string, tracked bool) error {
if tracked {
return self.UnstageTrackedFiles(paths)
} else {
return self.UnstageUntrackedFiles(paths)
}
return nil
}
func (self *WorkingTreeCommands) UnstageTrackedFiles(paths []string) error {
return self.cmd.New(NewGitCmd("reset").Arg("HEAD", "--").Arg(paths...).ToArgv()).Run()
}
func (self *WorkingTreeCommands) UnstageUntrackedFiles(paths []string) error {
return self.cmd.New(NewGitCmd("rm").Arg("--cached", "--force", "--").Arg(paths...).ToArgv()).Run()
}
func (self *WorkingTreeCommands) BeforeAndAfterFileForRename(file *models.File) (*models.File, *models.File, error) {
@@ -169,6 +165,7 @@ func (self *WorkingTreeCommands) DiscardAllFileChanges(file *models.File) error
if file.Added {
return self.os.RemoveFile(file.Name)
}
return self.DiscardUnstagedFileChanges(file)
}
@@ -176,6 +173,8 @@ type IFileNode interface {
ForEachFile(cb func(*models.File) error) error
GetFilePathsMatching(test func(*models.File) bool) []string
GetPath() string
// Returns file if the node is not a directory, otherwise returns nil
GetFile() *models.File
}
func (self *WorkingTreeCommands) DiscardAllDirChanges(node IFileNode) error {
@@ -184,13 +183,24 @@ func (self *WorkingTreeCommands) DiscardAllDirChanges(node IFileNode) error {
}
func (self *WorkingTreeCommands) DiscardUnstagedDirChanges(node IFileNode) error {
if err := self.RemoveUntrackedDirFiles(node); err != nil {
return err
}
file := node.GetFile()
if file == nil {
if err := self.RemoveUntrackedDirFiles(node); err != nil {
return err
}
cmdArgs := NewGitCmd("checkout").Arg("--", node.GetPath()).ToArgv()
if err := self.cmd.New(cmdArgs).Run(); err != nil {
return err
cmdArgs := NewGitCmd("checkout").Arg("--", node.GetPath()).ToArgv()
if err := self.cmd.New(cmdArgs).Run(); err != nil {
return err
}
} else {
if file.Added && !file.HasStagedChanges {
return self.os.RemoveFile(file.Name)
}
if err := self.DiscardUnstagedFileChanges(file); err != nil {
return err
}
}
return nil
@@ -211,7 +221,6 @@ func (self *WorkingTreeCommands) RemoveUntrackedDirFiles(node IFileNode) error {
return nil
}
// DiscardUnstagedFileChanges directly
func (self *WorkingTreeCommands) DiscardUnstagedFileChanges(file *models.File) error {
cmdArgs := NewGitCmd("checkout").Arg("--", file.Name).ToArgv()
return self.cmd.New(cmdArgs).Run()
@@ -224,38 +233,44 @@ func (self *WorkingTreeCommands) Ignore(filename string) error {
// Exclude adds a file to the .git/info/exclude for the repo
func (self *WorkingTreeCommands) Exclude(filename string) error {
return self.os.AppendLineToFile(".git/info/exclude", filename)
excludeFile := filepath.Join(self.repoPaths.repoGitDirPath, "info", "exclude")
return self.os.AppendLineToFile(excludeFile, filename)
}
// WorktreeFileDiff returns the diff of a file
func (self *WorkingTreeCommands) WorktreeFileDiff(file *models.File, plain bool, cached bool, ignoreWhitespace bool) string {
func (self *WorkingTreeCommands) WorktreeFileDiff(file *models.File, plain bool, cached bool) string {
// for now we assume an error means the file was deleted
s, _ := self.WorktreeFileDiffCmdObj(file, plain, cached, ignoreWhitespace).RunWithOutput()
s, _ := self.WorktreeFileDiffCmdObj(file, plain, cached).RunWithOutput()
return s
}
func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool, ignoreWhitespace bool) oscommands.ICmdObj {
colorArg := self.UserConfig.Git.Paging.ColorArg
func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool) oscommands.ICmdObj {
colorArg := self.UserConfig().Git.Paging.ColorArg
if plain {
colorArg = "never"
}
contextSize := self.UserConfig.Git.DiffContextSize
contextSize := self.AppState.DiffContextSize
prevPath := node.GetPreviousPath()
noIndex := !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile()
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
useExtDiff := extDiffCmd != "" && !plain
cmdArgs := NewGitCmd("diff").
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff").
Arg("--submodule").
Arg("--no-ext-diff").
Arg(fmt.Sprintf("--unified=%d", contextSize)).
Arg(fmt.Sprintf("--color=%s", colorArg)).
ArgIf(ignoreWhitespace, "--ignore-all-space").
ArgIf(!plain && self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
Arg(fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold)).
ArgIf(cached, "--cached").
ArgIf(noIndex, "--no-index").
Arg("--").
ArgIf(noIndex, "/dev/null").
Arg(node.GetPath()).
ArgIf(prevPath != "", prevPath).
Dir(self.repoPaths.worktreePath).
ToArgv()
return self.cmd.New(cmdArgs).DontLog()
@@ -263,42 +278,44 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain
// ShowFileDiff get the diff of specified from and to. Typically this will be used for a single commit so it'll be 123abc^..123abc
// but when we're in diff mode it could be any 'from' to any 'to'. The reverse flag is also here thanks to diff mode.
func (self *WorkingTreeCommands) ShowFileDiff(from string, to string, reverse bool, fileName string, plain bool,
ignoreWhitespace bool,
) (string, error) {
return self.ShowFileDiffCmdObj(from, to, reverse, fileName, plain, ignoreWhitespace).RunWithOutput()
func (self *WorkingTreeCommands) ShowFileDiff(from string, to string, reverse bool, fileName string, plain bool) (string, error) {
return self.ShowFileDiffCmdObj(from, to, reverse, fileName, plain).RunWithOutput()
}
func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool,
ignoreWhitespace bool,
) oscommands.ICmdObj {
contextSize := self.UserConfig.Git.DiffContextSize
func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool) oscommands.ICmdObj {
contextSize := self.AppState.DiffContextSize
colorArg := self.UserConfig.Git.Paging.ColorArg
colorArg := self.UserConfig().Git.Paging.ColorArg
if plain {
colorArg = "never"
}
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
useExtDiff := extDiffCmd != "" && !plain
cmdArgs := NewGitCmd("diff").
Config("diff.noprefix=false").
ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff").
Arg("--submodule").
Arg("--no-ext-diff").
Arg(fmt.Sprintf("--unified=%d", contextSize)).
Arg("--no-renames").
Arg(fmt.Sprintf("--color=%s", colorArg)).
Arg(from).
Arg(to).
ArgIf(reverse, "-R").
ArgIf(ignoreWhitespace, "--ignore-all-space").
ArgIf(!plain && self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
Arg("--").
Arg(fileName).
Dir(self.repoPaths.worktreePath).
ToArgv()
return self.cmd.New(cmdArgs).DontLog()
}
// CheckoutFile checks out the file for the given commit
func (self *WorkingTreeCommands) CheckoutFile(commitSha, fileName string) error {
cmdArgs := NewGitCmd("checkout").Arg(commitSha, "--", fileName).
func (self *WorkingTreeCommands) CheckoutFile(commitHash, fileName string) error {
cmdArgs := NewGitCmd("checkout").Arg(commitHash, "--", fileName).
ToArgv()
return self.cmd.New(cmdArgs).Run()
@@ -329,7 +346,7 @@ func (self *WorkingTreeCommands) RemoveUntrackedFiles() error {
// ResetAndClean removes all unstaged changes and removes all untracked files
func (self *WorkingTreeCommands) ResetAndClean() error {
submoduleConfigs, err := self.submodule.GetConfigs()
submoduleConfigs, err := self.submodule.GetConfigs(nil)
if err != nil {
return err
}
@@ -347,7 +364,7 @@ func (self *WorkingTreeCommands) ResetAndClean() error {
return self.RemoveUntrackedFiles()
}
// ResetHardHead runs `git reset --hard`
// ResetHard runs `git reset --hard`
func (self *WorkingTreeCommands) ResetHard(ref string) error {
cmdArgs := NewGitCmd("reset").Arg("--hard", ref).
ToArgv()

View File

@@ -61,7 +61,6 @@ func TestWorkingTreeUnstageFile(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
s.test(instance.UnStageFile([]string{"test.txt"}, s.reset))
@@ -190,7 +189,6 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, removeFile: s.removeFile})
err := instance.DiscardAllFileChanges(s.file)
@@ -207,13 +205,14 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
func TestWorkingTreeDiff(t *testing.T) {
type scenario struct {
testName string
file *models.File
plain bool
cached bool
ignoreWhitespace bool
contextSize int
runner *oscommands.FakeCmdObjRunner
testName string
file *models.File
plain bool
cached bool
ignoreWhitespace bool
contextSize int
similarityThreshold int
runner *oscommands.FakeCmdObjRunner
}
const expectedResult = "pretend this is an actual git diff"
@@ -226,12 +225,13 @@ func TestWorkingTreeDiff(t *testing.T) {
HasStagedChanges: false,
Tracked: true,
},
plain: false,
cached: false,
ignoreWhitespace: false,
contextSize: 3,
plain: false,
cached: false,
ignoreWhitespace: false,
contextSize: 3,
similarityThreshold: 50,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "cached",
@@ -240,12 +240,13 @@ func TestWorkingTreeDiff(t *testing.T) {
HasStagedChanges: false,
Tracked: true,
},
plain: false,
cached: true,
ignoreWhitespace: false,
contextSize: 3,
plain: false,
cached: true,
ignoreWhitespace: false,
contextSize: 3,
similarityThreshold: 50,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--cached", "--", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=50%", "--cached", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "plain",
@@ -254,12 +255,13 @@ func TestWorkingTreeDiff(t *testing.T) {
HasStagedChanges: false,
Tracked: true,
},
plain: true,
cached: false,
ignoreWhitespace: false,
contextSize: 3,
plain: true,
cached: false,
ignoreWhitespace: false,
contextSize: 3,
similarityThreshold: 50,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=never", "--", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=never", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "File not tracked and file has no staged changes",
@@ -268,12 +270,13 @@ func TestWorkingTreeDiff(t *testing.T) {
HasStagedChanges: false,
Tracked: false,
},
plain: false,
cached: false,
ignoreWhitespace: false,
contextSize: 3,
plain: false,
cached: false,
ignoreWhitespace: false,
contextSize: 3,
similarityThreshold: 50,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=50%", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil),
},
{
testName: "Default case (ignore whitespace)",
@@ -282,12 +285,13 @@ func TestWorkingTreeDiff(t *testing.T) {
HasStagedChanges: false,
Tracked: true,
},
plain: false,
cached: false,
ignoreWhitespace: true,
contextSize: 3,
plain: false,
cached: false,
ignoreWhitespace: true,
contextSize: 3,
similarityThreshold: 50,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--ignore-all-space", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "Show diff with custom context size",
@@ -296,23 +300,44 @@ func TestWorkingTreeDiff(t *testing.T) {
HasStagedChanges: false,
Tracked: true,
},
plain: false,
cached: false,
ignoreWhitespace: false,
contextSize: 17,
plain: false,
cached: false,
ignoreWhitespace: false,
contextSize: 17,
similarityThreshold: 50,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=17", "--color=always", "--", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=17", "--color=always", "--find-renames=50%", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "Show diff with custom similarity threshold",
file: &models.File{
Name: "test.txt",
HasStagedChanges: false,
Tracked: true,
},
plain: false,
cached: false,
ignoreWhitespace: false,
contextSize: 3,
similarityThreshold: 33,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"-C", "/path/to/worktree", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--find-renames=33%", "--", "test.txt"}, expectedResult, nil),
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
userConfig := config.GetDefaultConfig()
userConfig.Git.DiffContextSize = s.contextSize
appState := &config.AppState{}
appState.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
appState.DiffContextSize = s.contextSize
appState.RenameSimilarityThreshold = s.similarityThreshold
repoPaths := RepoPaths{
worktreePath: "/path/to/worktree",
}
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, userConfig: userConfig})
result := instance.WorktreeFileDiff(s.file, s.plain, s.cached, s.ignoreWhitespace)
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, userConfig: userConfig, appState: appState, repoPaths: &repoPaths})
result := instance.WorktreeFileDiff(s.file, s.plain, s.cached)
assert.Equal(t, expectedResult, result)
s.runner.CheckForMissingCalls()
})
@@ -343,7 +368,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "Show diff with custom context size",
@@ -354,7 +379,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 123,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=123", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "diff", "--no-ext-diff", "--submodule", "--unified=123", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "Default case (ignore whitespace)",
@@ -365,19 +390,23 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
ignoreWhitespace: true,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
ExpectGitArgs([]string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "diff", "--no-ext-diff", "--submodule", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
userConfig := config.GetDefaultConfig()
userConfig.Git.DiffContextSize = s.contextSize
appState := &config.AppState{}
appState.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
appState.DiffContextSize = s.contextSize
repoPaths := RepoPaths{
worktreePath: "/path/to/worktree",
}
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, userConfig: userConfig})
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner, userConfig: userConfig, appState: appState, repoPaths: &repoPaths})
result, err := instance.ShowFileDiff(s.from, s.to, s.reverse, "test.txt", s.plain, s.ignoreWhitespace)
result, err := instance.ShowFileDiff(s.from, s.to, s.reverse, "test.txt", s.plain)
assert.NoError(t, err)
assert.Equal(t, expectedResult, result)
s.runner.CheckForMissingCalls()
@@ -387,18 +416,18 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
func TestWorkingTreeCheckoutFile(t *testing.T) {
type scenario struct {
testName string
commitSha string
fileName string
runner *oscommands.FakeCmdObjRunner
test func(error)
testName string
commitHash string
fileName string
runner *oscommands.FakeCmdObjRunner
test func(error)
}
scenarios := []scenario{
{
testName: "typical case",
commitSha: "11af912",
fileName: "test999.txt",
testName: "typical case",
commitHash: "11af912",
fileName: "test999.txt",
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"checkout", "11af912", "--", "test999.txt"}, "", nil),
test: func(err error) {
@@ -406,9 +435,9 @@ func TestWorkingTreeCheckoutFile(t *testing.T) {
},
},
{
testName: "returns error if there is one",
commitSha: "11af912",
fileName: "test999.txt",
testName: "returns error if there is one",
commitHash: "11af912",
fileName: "test999.txt",
runner: oscommands.NewFakeRunner(t).
ExpectGitArgs([]string{"checkout", "11af912", "--", "test999.txt"}, "", errors.New("error")),
test: func(err error) {
@@ -418,11 +447,10 @@ func TestWorkingTreeCheckoutFile(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
s.test(instance.CheckoutFile(s.commitSha, s.fileName))
s.test(instance.CheckoutFile(s.commitHash, s.fileName))
s.runner.CheckForMissingCalls()
})
}
@@ -449,7 +477,6 @@ func TestWorkingTreeDiscardUnstagedFileChanges(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
s.test(instance.DiscardUnstagedFileChanges(s.file))
@@ -477,7 +504,6 @@ func TestWorkingTreeDiscardAnyUnstagedFileChanges(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
s.test(instance.DiscardAnyUnstagedFileChanges())
@@ -505,7 +531,6 @@ func TestWorkingTreeRemoveUntrackedFiles(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
s.test(instance.RemoveUntrackedFiles())
@@ -535,7 +560,6 @@ func TestWorkingTreeResetHard(t *testing.T) {
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
s.test(instance.ResetHard(s.ref))

View File

@@ -4,6 +4,7 @@ import (
iofs "io/fs"
"path/filepath"
"strings"
"sync"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/commands/models"
@@ -57,18 +58,14 @@ func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
isCurrent := path == worktreePath
isPathMissing := self.pathExists(path)
var gitDir string
gitDir, err := getWorktreeGitDirPath(self.Fs, path)
if err != nil {
self.Log.Warnf("Could not find git dir for worktree %s: %v", path, err)
}
current = &models.Worktree{
IsMain: isMain,
IsCurrent: isCurrent,
IsPathMissing: isPathMissing,
Path: path,
GitDir: gitDir,
// we defer populating GitDir until a loop below so that
// we can parallelize the calls to git rev-parse
GitDir: "",
}
} else if strings.HasPrefix(splitLine, "branch ") {
branch := strings.SplitN(splitLine, " ", 2)[1]
@@ -76,6 +73,26 @@ func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
}
}
wg := sync.WaitGroup{}
wg.Add(len(worktrees))
for _, worktree := range worktrees {
go utils.Safe(func() {
defer wg.Done()
if worktree.IsPathMissing {
return
}
gitDir, err := callGitRevParseWithDir(self.cmd, self.version, worktree.Path, "--absolute-git-dir")
if err != nil {
self.Log.Warnf("Could not find git dir for worktree %s: %v", worktree.Path, err)
return
}
worktree.GitDir = gitDir
})
}
wg.Wait()
names := getUniqueNamesFromPaths(lo.Map(worktrees, func(worktree *models.Worktree, _ int) string {
return worktree.Path
}))

Some files were not shown because too many files have changed in this diff Show More