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.
- 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
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.
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).
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.
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.
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.
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.
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.
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.
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
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).
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.
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.
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.
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
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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
ListContextTrait.OnSearchSelect was introduced in 138be04e65, but it was never
called. I can only guess that a planned refactoring wasn't finished here.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
- 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.
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.
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.
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.
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.
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/
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.
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.
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.
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.
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>
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.)
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.
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.
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.
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.
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.
... 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).
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.
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.
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.
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).
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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
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.
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".
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.
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.
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.
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
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.
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.
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.
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.
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.