The plan is to keep the original docs and schema folders unchanged for the
duration of a release; we'll only continuously update the -master copies. Right
before a new release we will copy them over.
When pressing '.' (next page) or ',' (previous page), the selection now
stays at the bottom or top of the viewport respectively, instead of
being centered which caused items to scroll off. (If the selection is
not already on the last/first line of the view, '.'/',' moves it there
without scrolling.)
This implements a special case for page navigation as suggested by the
maintainer in issue #5017, keeping the cursor position consistent with
user expectations for page-based navigation.
Fixes#5017
### PR Description
This allows for having extra slashes in git URLs. Especially in
submodules. Those may be used to avoid warnings with older bitbake
fetcher implementations in yocto. Which warns about relative urls when
not having a slash in git urls.
- git@bitbucket.org:project/repo.git -> generates a warning in bitbake,
which is annoying.
- git@bitbucket.org:/project/repo.git -> does not generate a warning in
bitbake.
But when trying to open a PR from lazygit with an url like
git@bitbucket.org:/project/repo.git leads to
https://bitbucket.org//project/repo being opened and one has to stare at
a blank page unless one removes the extra / manualy.
This PR updates the regex used for matching git url fragments in a way,
that the extra `/` is ignored.
The "Cycle pagers" command wasn't rendered correctly, because it's bound
to '|' by default, but this was taked as a table column delimiter. Fix
this by escaping the pipe character.
A similar thing could happen for the description or tooltip (and did in
fact, for a tooltip in the russion translation), so escape those too
just to be sure.
The "Cycle pagers" command wasn't rendered correctly, because it's bound to '|'
by default, but this was taked as a table column delimiter. Fix this by escaping
the pipe character.
A similar thing could happen for the description or tooltip (and did in fact,
for a tooltip in the russion translation), so escape those too just to be sure.
### PR Description
The existing diff parser incorrectly treated subsequent lines beginning
with `---` as filename headers while processing hunks. This caused
corruption when dashed lines appeared within diffs themselves.
Restrict filename detection to only occur between hunks.
This repository https://github.com/abobov/lazygit-fixup-issue
demonstrates the issue:
- clone repository
- run `git reset --soft HEAD~`
- try `<c-f>` Find base commit for fixup
- `fatal: no such path ...` error
This will put whatever git's default merge variant is as the first menu
item, and add a second item which is the opposite (no-ff if the default
is ff, and vice versa). Which one is the default depends on whether a
fast-forward merge is possible, and whether users have set git's
`merge.ff` config or lazygit's `git.merging.args` config.
If users prefer to always have the same option first no matter whether
it's applicable, they can make ff always appear first by setting git's
`merge.ff` config to "true" or "only", or by setting lazygit's
`git.merging.args` config to "--ff" or "--ff-only"; if they want no-ff
to appear first, they can do that by setting git's `merge.ff` config to
"false", or by setting lazygit's `git.merging.args` config to "--no-ff".
Which of these they choose depends on whether they want the config to
also apply to other git clients including the cli, or only to lazygit.
Closes#4091.
This will put whatever git's default merge variant is as the first menu item,
and add a second item which is the opposite (no-ff if the default is ff, and
vice versa).
If users prefer to always have the same option first no matter whether it's
applicable, they can make ff always appear first by setting git's "merge.ff"
config to "true" or "only", or by setting lazygit's "git.merging.args" config to
"--ff" or "--ff-only"; if they want no-ff to appear first, they can do that by
setting git's "merge.ff" config to "false", or by setting lazygit's
"git.merging.args" config to "--no-ff". Which of these they choose depends on
whether they want the config to also apply to other git clients including the
cli, or only to lazygit.
- Squash and FastForwardOnly are mutually exclusive, and instead of asserting
this at runtime, model the API so that they can't be passed together.
- FastForwardOnly is unused, so remove it; however, we are going to need --ff
and --no-ff in the next commit, so add those instead.
- Instead of putting the enum into the MergeOpts struct, replace the struct by
the enum. We can reintroduce the struct when we add more arguments, but for
now it's an unnecessary indirection.
I found that PR status becomes green even if no label added when we use
`Update branch` on GitHub PR page.
So I added `synchronize` event to the hooks of `Check Required Labels`.
Stashing doesn't affect submodules, so if you have a working copy that
has out-of-date submodules but no other changes, and then you revert or
paste a commit (or invoke one of the many other lazygit commands that
auto-stash, e.g. undo), lazygit would previously try to stash changes
(which did nothing, but also didn't return an error), perform the
operation, and then pop the stash again. If no stashes existed before,
then this would only cause a confusing error popup ("error:
refs/stash@{0} is not a valid reference"), but if there were stashes,
this would try to pop the newest one of these, which is very undesirable
and confusing.
Stashing doesn't affect submodules, so if you have a working copy that has
out-of-date submodules but no other changes, and then you revert or paste a
commit (or invoke one of the many other lazygit commands that auto-stash, e.g.
undo), lazygit would previously try to stash changes (which did nothing, but
also didn't return an error), perform the operation, and then pop the stash
again. If no stashes existed before, then this would only cause a confusing
error popup ("error: refs/stash@{0} is not a valid reference"), but if there
were stashes, this would try to pop the newest one of these, which is very
undesirable and confusing.
This happened to a few users after updating macOS to version 15.7.1, and
apparently a reboot fixes it, so suggest this in the error message.
Closes#4924
Adds a config option that disables the confirmation for switching to a
worktree when trying to check out a branch that is checked out in that
other worktree.
Fixes#4938.
Lazygit would become unresponsive when cherry-picking a commit that
becomes empty at the destination. There are two ways this can happen:
one where a cherry-picked commit simply becomes empty "by itself",
because the change is already present on the destination branch (this
was only a problem with git versions older than 2.45), and another where
the cherry-pick stops with conflicts, and the user resolves them such
that no changes are left, and then continues the cherry-pick. This would
still fail even with git 2.45 or later. The fix is the same for both
cases though.
Fixes#4763.
I can't do my usual "add the tests first, with EXPECTED/ACTUAL sections that
document the bug" method here, because the tests would hang without the bug
being fixed.
We need two different tests here: one where a cherry-picked commit simply
becomes empty "by itself", because the change is already present on the
destination branch (this was only a problem with git versions older than 2.45),
and the other where the cherry-pick stops with conflicts, and the user resolves
them such that no changes are left, and then continues the cherry-pick. This
would still fail even with git 2.45 or later. The fix is the same for both cases
though.
The tests show that the selection behavior after skipping an empty cherry-picked
commit is not ideal, but since that's only a minor cosmetic problem we don't
bother fixing it here.
Whenever git returns the error "The previous cherry-pick is now empty", we would
previously continue the rebase; this works for rebase because it behaves the
same as "git rebase --skip" in this case. That's not true for cherry-pick
though; if you continue a cherry-pick where the current commit is empty, it will
return the same error again, causing lazygit to be stuck in an endless loop.
Fix this by skipping instead of continuing; this shouldn't make a difference for
rebase, but works for cherry-pick.
Theoretically we could have a similar problem for revert (if you are trying to
revert a commit that has already been undone through some other means); this
should then be fixed in the same way with this change. However, the change is
not relevant for revert because git returns a different error in this case.
When focusing the main view, going into full screen mode by pressing '+'
twice, and then opening the search prompt ('/') or a menu (e.g. '?' or
':'), the full screen display would switch to the focused side panel.
Fix this by always excluding popups from the window arrangement logic.
No popup should ever have any influence on how the views beneath it are
laid out.
Addresses [this detail
aspect](https://github.com/jesseduffield/lazygit/issues/1113#issuecomment-3206883422)
of #1113.
When focusing the main view, going into full screen mode by pressing '+' twice,
and then opening the search prompt ('/') or a menu (e.g. '?' or ':'), the full
screen display would switch to the focused side panel.
Fix this by always excluding popups from the window arrangement logic. No popup
should ever have any influence on how the views beneath it are laid out.
Allow configuring an array of pagers, and cycling through them with the
`|` key (mnemonic: we are piping the output through something). I find
this useful for switching between delta (which I prefer most of the
time) and difftastic (which occasionally produces better output for
certain kinds of changes). It could also be used to switch between delta
in inline mode and in side-by-side mode.
This is an object that is owned by Gui, is accessible through GuiCommon.State(),
and also passed down to GitCommand, where it is mostly needed. Right now it
simply wraps access to the Git.Paging config, which isn't very exciting, but
we'll extend it in the next commit to handle a slice of pagers (and maintain the
currently selected pager index), and doing this refactoring up front allows us
to make that change without having to touch clients.
This is likely to do bad things; for example, if the prompt is the shell
command prompt, then we would run into what looks like a deadlock bug in
tcell. In other cases, the characters in the following lines might be
treated as random keybindings after the prompt is confirmed; this is
very similar to #4234.
In this case, it seems the best we can do is to simply swallow the line
feeds. The entire pasted text will then appear as a single long line in
the prompt, and hopefully the user knows that they can use ctrl-u to
delete it again. If not, they will probably just hit esc to close the
prompt.
Fixes#4954.
This is likely to do bad things; for example, if the prompt is the shell command
prompt, then we would run into what looks like a deadlock bug in tcell. In other
cases, the characters in the following lines might be treated as random commands
after the prompt is confirmed.
We want to switch to have paragraphs consistently on one line, and auto-wrap
them automatically when generating Config.md.
The changes to Config.md in this commit are temporary.
Our logic to decide if a file needs to be checked out from the previous
commit or deleted because it didn't exist in the previous commit didn't
work for submodules. We were using `git cat-file -e` to ask whether the
file existed, but this returns an error for submodules, so we were
always deleting those instead of reverting them back to their previous
state.
Switch to using `git ls-tree -- file` instead, which works for both
files and submodules.
Fixes#4932.
Our logic to decide if a file needs to be checked out from the previous commit
or deleted because it didn't exist in the previous commit didn't work for
submodules. We were using `git cat-file -e` to ask whether the file existed, but
this returns an error for submodules, so we were always deleting those instead
of reverting them back to their previous state.
Switch to using `git ls-tree -- file` instead, which works for both files and
submodules.
It works for submodules too.
Also, pass file name and file content explicitly; the existing tests don't care
about these, but when writing tests that do, it makes them easier to understand.
Doesn't make a difference currently, since the title is either StatusTitle when
the dashboard is showing, or LogTitle when one of the branch logs is showing.
This is going to change in the next commit, though.
When cycling to the last branch log command, and then editing the config to
remove one or more log commands, lazygit would crash with an out of bounds panic
when returning to it. Fix this by resetting the log index to 0 when it is out of
bounds. (I think resetting to 0 is better than clamping, although it doesn't
matter much.)
I know that uint was chosen to document that it can't be negative (not sure why
the "8" was chosen, though). That's pointless in languages that don't enforce
this though: you can subtract 1 from uint8(0) and you'll get something that
doesn't make sense; that's not any better than getting -1. I'm not a fan of
using unsigned types in general (at least in languages like go or C++), and they
usually just make the code more cumbersome without real benefits.
For some of the git commands that lazygit uses, it needs to force the
language to English because it parses git's output and needs to react to
certain things. It used to do this by setting the locale to
`en_US.UTF-8`; some users reported that this locale isn't available on
their systems. Use the "C" locale instead, which achieves the same
result and is guaranteed to be available everywhere.
Fixes#4731.
Some users reported that en_US.UTF-8 is not available on their systems, leading
to warnings in the command log. "C" also forces the language to English, and is
guaranteed to be available everywhere.
Custom pagers are not supported on Windows, because using a pager in git
requires the git command to run in a PTY, but the PTY package we are
using doesn't support Windows.
However, there's a workaround to use a custom pager by using a
Powershell script configured as an external diff command. Document this
in the `Custom_Pagers.md` document, and export a `LAZYGIT_COLUMNS`
environment variable to allow such scripts to work better.
This can be useful if you want to do some operation on the filtered
file, e.g. check it out using `c`, or enter the file to make a custom
patch from it.
Addresses #4935.
When lazygit detects that all conflicts have been resolved, it puts up a
popup asking whether the user wants to continue the merge or rebase.
However, it didn't rerender the main view of the conflicted file, so it
would still show the conflict markers, which is confusing. Add some
logic that updates the main view in this situation, behind the popup.
The logic in postRefreshUpdate would only rerender the main view if the context
being updated is the current view. This is not the case when a popup is showing;
but we still want to render the main view in that case, behind the popup. This
happens for example when we refresh the Files scope, we determine that all
conflicts have been resolved and show a popup asking to continue the merge or
rebase, but the postRefreshUpdate of the Files context only happens when the
popup is already showing, so we would still see the conflict markers behind the
popup, which is rather confusing.
Implements https://github.com/jesseduffield/lazygit/issues/2026. I also
tried to address issues mentioned in the
https://github.com/jesseduffield/lazygit/pull/3477 PR.
Previously, pressing `M` opened an external merge tool. Now it opens a
merge options menu that allows selecting all conflicts in chosen files
as **ours** (HEAD), **theirs** (incoming), or **union** (both), while
still providing access to the external merge tool.
This uses [git-merge-file](https://git-scm.com/docs/git-merge-file) for
a 3-way merge with the `--ours`, `--theirs`, and `--union` flags. This
approach avoids the issue mentioned in
https://github.com/jesseduffield/lazygit/discussions/1608#discussioncomment-13002595,
and correctly applies the chosen conflict resolutions while preserving
changes from other branches. The command is executed with `--object-id`,
which requires object IDs obtained via `rev-parse`, instead of relying
on the standard version that works with full saved files.
The `normalisedSelectedNodes` function in `files_controller.go` had a
bug where it didn't work correctly for the root item (`/`). This PR
fixes that.
We don't have any tests for this bug. Apparently, the functions that use
this (staging and discarding files) also work correctly when they work
on a directory and then on the contained file again, that's why nobody
has noticed yet. I briefly looked into adding unit tests for the
function, but it was more work than I was willing to put into this right
now.
The root item's path is ".", and the path of a file at top level is "./file".
When using GetPath, this gives us "." and "file", respectively, and
isDescendentOfSelectedNodes would return false for these.
Working with the internal paths (i.e. without stripping the leading "./") fixes
this.
## PR Description
This PR adds Nix flake support to provide a reproducible development
environment and build process for `lazygit` contributors, devs, and
users.
Features:
- Nix Flake Configuration: Complete flake setup supporting multiple
architectures
- Development Shell: Pre-configured environment with Go toolchain,
linting tools, and development utilities
- Multiple Entry Points: Support for `nix develop`, `nix run`, and `nix
build`
Closes#3474.
### PR Description
This PR includes 2 changes:
1. Bump golangci-lint version to `v2.4.0` from `v.2.2.1`
1. Bump Go version to `1.25.0`:
- Bump Go version to `1.25` in `go.mod`
- Bump Go version to `1.25` in `ci.yml`
- Bump base image to `golang:1.25` in `Dockerfile`
### Reference
- [Go 1.25 release note](https://tip.golang.org/doc/go1.25)
I find the command "Checkout previous branch" quite useful, and I use it
a lot. However, I'm unhappy with the visual feedback, so this PR
improves that a bit.
Previously, the feedback you got when pressing "-" was just a "Checking
out..." status in the bottom line. This was both easy to miss if you are
used to looking for an inline status in the branches panel, and it
didn't provide information about which branch was being checked out,
which can be annoying in very large repos where checking out takes a
while, and you only see at the end if you are now on the right branch.
Improve this by trying to figure out which branch was the previously
checked out one, and then checking it out normally so that you get an
inline status next to it (as if you had pressed space on it). There are
cases where this fails, e.g. when the previously checked out ref was a
detached head, in which case we fall back to the previous behavior.
Previously, the feedback you got when pressing "-" was just a "Checking out..."
status in the bottom line. This was both easy to miss if you are used to looking
for an inline status in the branches panel, and it didn't provide information
about which branch was being checked out, which can be annoying in very large
repos where checking out takes a while, and you only see at the end if you are
now on the right branch.
Improve this by trying to figure out which branch was the previously checked out
one, and then checking it out normally so that you get an inline status next to
it (as if you had pressed space on it). There are cases where this fails, e.g.
when the previously checked out ref was a detached head, in which case we fall
back to the previous behavior.
When you delete a branch, we call this function to determine whether we
need to ask for confirmation of not. We don't want to log this, because
it's not a command that a user would normally use as part of this
operation.
When you delete a branch, we call this function to determine whether we need to
ask for confirmation of not. We don't want to log this, because it's not a
command that a user would normally use as part of this operation.
In all other menus besides the keybindings menu it makes sense to hide
keybindings that match the confirmMenu binding. This is important to
make it clear which action will be triggered when you press the key.
In the keybindings menu this is different; the main purpose of that menu
is not to allow triggering commands by their key while the menu is open,
but to serve as a reference for what the keybindings are when it is not
open. Because of this, it is more important to show all bindings in this
menu, even if they conflict with the confirmMenu key.
This fixes a regression introduced in b3a3410a1a.
Fixes#4879.
In all other menus besides the keybindings menu it makes sense to hide
keybindings that match the confirmMenu binding. This is important to make it
clear which action will be triggered when you press the key.
In the keybindings menu this is different; the main purpose of that menu is not
to allow triggering commands by their key while the menu is open, but to serve
as a reference for what the keybindings are when it is not open. Because of
this, it is more important to show all bindings in this menu, even if they
conflict with the confirmMenu key.
This fixes a regression introduced in b3a3410a1a.
### PR Description
Remapping `keybinding.universal.confirm` from `<enter>` to something
like `y` is currently impossible because the same keybinding is also
used to confirm prompts (e.g. "New branch") and the search prompt. Fix
this by hard-coding enter for those; it doesn't really make sense to use
any other key for prompts.
While at it, add separate bindings for `confirmMenu` and
`confirmSuggestion` for those who would like to have different keys for
these. Of these, `confirmMenu` could be a little tricky because menus
are sometimes used purely as a choice (e.g. in "Amend commit attribute"
or the global keybindings menu), in which case you might want to use
`<enter>`, but other times as a substitute for a confirmation (e.g. for
"Delete branch"), in which case you might want to remap to `y`. I don't
have a great idea what to do about that, to be honest. Feedback welcome.
In this PR we only take care of Confirm, which many people seem to be
concerned about. We might consider doing something similar for Esc, but
it seems less urgent, and I'm out of time now. 😄
This seemingly simple change required some serious refactoring under the
hood, so thorough testing would be good to ensure we didn't break
anything.
Closes#2611Closes#2767Closes#3471
Related: #2768
This is needed when remapping the confirmMenu key to, say, "y", and there's a
menu that has an item with a "y" binding. This already worked correctly (confirm
takes precedence, as desired), but it's still confusing to see the item binding.
It seems useful to have the flexibility to remap "enter" in confirmations to
"y", but keep "enter" for menus and suggestions (even though we sometimes use
menus as confirmations, but it's still good to give users the choice).
This one doesn't make a difference in practice because we don't remap the key in
tests, but if we would, then this would no longer work correctly. It's just more
correct this way.
The universal.confirm keybinding is the wrong one to use for this, we want
universal.goInto instead. They are both bound to "enter" by default, but when
remapping confirm to "y" we don't want to use that for entering worktrees.