This decouples StreamOutput from whether a PTY is used. In most cases we just
want to see the output in the log window, but don't have to use a PTY, e.g. for
the bisect commands.
This has the implication that custom commands that are using "stream: true" no
longer use a PTY. In most cases that's probably a good thing, but we're going to
add a separate pty config for those who really wanted this.
Hopefully, graphs will never get wider than 32768 characters. (They would get
kind of hard to navigate if they did...)
This reduces the size of the Pipe struct from 48 to 32 bytes, which makes a
significant difference when there are many millions of instances.
This saves some memory at the cost of a slight performance increase (I suppose
reallocting the slice when adding new Pipes is slightly more expensive now).
Performance of the BenchmarkRenderCommitGraph benchmark is 130μs before, 175μs
after. I'm guessing this is still acceptable.
Change the base type of some of our enums from int to uint8, and reorder fields
for better packing. This reduces the size of models.Commit from 152 to 132 bytes
on my machine.
This doesn't improve overall memory usage significantly, but why not save a
little bit of memory if it's easy.
We need to pass %P instead of %p in the format string of the git log command, so
that the parent hashes have the full length and can be shared with the real
hashes.
This in itself is not an improvement, because hashes are unique (they are shared
between real commits and rebase todos, but there are so few of those that it
doesn't matter). However, it becomes an improvement once we also store parent
hashes in the same pool; but the real motivation for this change is to also
reuse the hash pointers in Pipe objects later in the branch. This will be a big
win because in a merge-heavy git repo there are many more Pipe instances than
commits.
The "// merge commit" comment was plain wrong, this is any commit that has a
parent, merge or not. The "else if" condition was unnecessary, a plain "else"
would have been enough. But the code in the two blocks was almost identical, so
extract the one thing that was different and unify it.
And while we're at it, use IsFirstCommit() instead of counting parents.
This makes it easier to copy diff hunks and paste them into code. We only strip
the prefixes if the copied lines are either all '+' or all '-' (possibly
including context lines), otherwise we keep them. We also keep them when parts
of a hunk header is included in the selection; this is useful for copying a diff
hunk and pasting it into a github comment, for example.
A not-quite-correct edge case is when you select the '--- a/file.txt' line of a
diff header on its own; in this case we copy it as '-- a/file.txt' (same for the
'+++' line). This is probably uncommon enough that it's not worth fixing (it's
not trivial to fix because we don't know that we're in a header).
This is very old; I can only guess that this was added at a time where today's
list column handling wasn't in place yet, so the space was needed to separate
columns. This now causes a gap of two spaces between the rebase todo column and
the author column, which I'm sure wasn't intended. Funny that I never noticed.
This is very similar to what we are doing for staging or discarding hunks in the
Files panel. Git doesn't allow applying patches with a zero context size (unless
you use the --unidiff-zero option, which is discouraged).
Fish shell does not support "&&" and "||" operators like
POSIX-compatible shells. Instead, it uses a different syntax structure
based on begin/end and if/else.
This caused existing lazygit nvim-remote integration templates to break
when fish was the user's default shell.
This commit adds explicit fish shell detection using the FISH_VERSION
environment variable, and provides fish-compatible templates that
correctly handle launching Neovim or sending remote commands via $NVIM.
Fixes behavior where edits would not open in a new Neovim tab or line
navigation would fail when $NVIM was set.
Ensures smoother editing experience for users running fish shell
(supported since Nov 2012 with FISH_VERSION).
The long story: I want to call this function from RefsHelper; however, I can't
make WorkingTreeHelper a field of RefsHelper because RefsHelper is already a
field in WorkingTreeHelper, so that would be a circular dependency.
The shorter story: there's really little reason to have to instantiate a helper
object in order to call a simple function like this. Long term I would like to
get to a state where a lot more of these helper functions are free-standing, and
you pass in the data they need.
While at it, simplify the implementation of AnyStagedFiles and AnyTrackedFiles
to one-liners.
It's the same, really, except that GetCheckedOutRef() does a check if any
branches exist and returns nil if not. Since we are accessing the returned
branch unconditionally without checking for nil, it seems this check is not
needed here. (The functions we are touching here are called from handlers that
are guarded with itemSelected or singleItemSelected, so we know that at least
one branch exists.)
The goal is to get rid of the dependency to refsHelper.
And only while the task is running.
This avoids accumulating lots of blocked goroutines when scrolling a view down
more than 1024 times (the capacity of the readLines channel).
In this commit this is only possible by pressing '0' in a side panel; we'll add
mouse clicking later in the branch.
Also, you can't really do anything in the focused view except press escape to
leave it again. We'll add some more functionality in a following commit.
Previously we would render the diff for a directory to the main/secondary pair,
but a diff for a file to the staging/stagingSecondary pair. (And similar for
commit files: main/secondary for directories, but
patchBuilding/patchBuildingSecondary for files.)
I always found this confusing and couldn't really understand why we are doing
this; but now it gets in my way because I want to attach a controller to
main/secondary so that they can be focused. So change it to always use the main
context pair for everything we render from a side panel.
When rerendering a view at the end of a refresh, we call HandleFocus only if the
view has the focus. This is so that we rerender the main view for the new
selection.
What was missing here is to update the view selection from the list selection if
the view doesn't have the focus, so that the selection is painted properly.
Normally this is not relevant because you don't see the selection if another
side panel has the focus; however, you do see it as an inactive selection when
e.g. a popup is shown, in which case it does matter.
This will become more important when we introduce section headers for commits,
because in that case the view selection needs to change when the working copy
state changes from normal to rebasing or vice versa, even if the list selection
stays the same.
The changed test submodule/reset.go shows how this was wrong before: when
entering the submodule again after resetting, there is a refresh which keeps the
same branch selected as before (master); however, since the branches panel is
not focused, the view didn't notice and kept thinking that the detached head is
selected (which it isn't, you can tell by running the test in sandbox mode and
focusing the branches panel at the end: you'll see that master is selected). So
the change in this commit fixes that.
We do this because
- it's closer to what you would do on the command line
- it simplifies the code a bit
- it will allow us to support cherry-picking merge commits.
Previously we would create new Commit objects to store in the CherryPicking mode
which only contained a name and hash, all other fields were unset. I'm not sure
why we did this; it's easier to just reference the original commits. Later on
this branch we will need this because we need to determine whether a copied
commit was a merge commit (by looking at its Parents field).
We treat the .git/sequencer/todo file as read-only. Technically it seems it
would be possible to treat it as modifiable in the same way as
.git/rebase-merge/git-rebase-todo, effectively turning a cherry-pick or revert
that stops at a conflict into an interactive rebase; however, git itself doesn't
allow this (there is no "git cherry-pick --edit-todo"), so it seems safer not to
rely on it.
Theoretically it would be possible to allow modifying the rebase todos when a
cherry-pick or revert conflicts in the middle of a rebase. However, it would
introduce a bit of complexity to support this, as we would have to be able to
distinguish between rebasing todos and cherry-picking/reverting todos, which we
currently can't; it could also be a bit error-prone as far as edge cases are
concerned. And it's really a pretty uncommon situation, so it doesn't seem worth
it, and we just forbid all modifications to todos whenever we are cherry-picking
or reverting.
What happens here is that when stopping on an "edit" todo entry, we rely on the
assumption that if the .git/rebase-merge/amend file exists, the command was
successful, and if it doesn't, there was a conflict. The problem is that when
you stop on an edit command, and then run a multi-commit cherry-pick or rebase,
this will delete the amend file. You may or may not consider this a bug in git;
to work around it, we also check the existence of the rebase-merge/message file,
which will be deleted as well by the cherry-pick or revert.
This problem can't happen inside of lazygit itself right now, but this will
change in the future. It will only happen when you stopped in an interactive
rebase on an "edit" entry, and then you perform a revert or cherry-pick
consisting of more than one commit; in this situation lazygit will show a
conflict although there is none.
This is not possible with lazygit yet, as we don't support range-select for
reverting, and we don't use `git cherry-pick` for cherry-picking. Both will
change in the future, so it's good to fix this bug.
MergeRebasingCommits already merges the rebasing commits into the commits slice
that is passed in, so it doesn't make sense to append the result to commits
again. It isn't a problem, but only because commits is always empty.
It is useful to see if the conflicted commit was a "pick" or an "edit". What's
more, we're about to add support for showing cherry-picks and reverts, and
seeing that a conflicted commit was a revert is important because its diff is
backwards compared to the diff of the conflicting files in the Files panel.
This is equivalent in the current state of the code, but it will no longer be
after the next commit, because we will introduce a new status value
StatusConflicted. And in a later PR we might add yet another value
StatusCherryPicking to distinguish rebase todos from cherry-pick todos; using
commit.IsTODO is a safer way to check whether a commit is any of these.
When you are in the middle of a rebase, and you cherry-pick a commit which
conflicts, it helps to be clear on whether you are prompted to continue the
cherry-pick or the rebase.
It looks like enums.go was supposed to be file that collects a bunch of enums,
but actually there's only one in there, and since it has methods, it deserves to
be in a file of its own, named after the type.
The situation is that you perform a rebase, and one of the commits becomes empty
during the rebase, in a way that couldn't be predicted before the rebase
started. To explain: git rebase has some logic where it immediately discards
commits if it can tell that they already exist in the branch being rebased onto;
it does this by looking at the patch ids of the commits to work out which
commits already exist upstream. But for those commits that become empty even
though there isn't a corresponding commit upstream, git stops with an error, and
lazygit detects this (in CheckMergeOrRebaseWithRefreshOptions) and automatically
continues the rebase.
This all works fine; I'm adding this test because I almost broke this during
development of this branch, so I'm adding it to guard against regressions.
- Remove REBASE_MODE_NORMAL. It is not the "normal" mode anyway, rather a legacy
mode; we have removed support for it in eb0f7e3d02, so there's no point in
representing it in the enum.
- Remove distinction between REBASE_MODE_REBASING and REBASE_MODE_INTERACTIVE;
these are the same now.
- Rename StatusCommands.IsInInteractiveRebase to IsInRebase.
- Remove StatusCommands.RebaseMode; use StatusCommands.IsInRebase instead.
All test cases set it to enums.REBASE_MODE_NONE, so we can simplify things a
little bit by hard-coding that. This makes the changes in the following commits
a little easier.
When debugging an integration test that involves some behind-the-scenes
rebasing, the daemon lazygit would also wait for a debugger to attach. This is
very confusing because the test seems to hang; once you figured out what's going
on, it's inconvenient because you need to attach a debugger to the daemon every
time you debug the test.
Now, it would sometimes be useful to be able to debug the daemon itself (whether
inside an integration test, or during normal usage), and I have often wished to
be able to do that. We might introduce an additional env var (and command-line
option?) to enable this; but that's out of scope here.
For #, !, [, and ], the problem is that it doesn't ignore the file because the
special characters need to be quoted. For *, the problem is that it ignores too
much (it also hides the abc_def file because the * is treated as a glob).
It is shown either when committing with `w`, or when typing the skipHooks prefix
if there is one. This should hopefully make it clearer when the hooks are run
and when they are not.
We removed prefilling the skipHook prefix in b102646b20 with the intention of
making it clearer that using the prefix in normal commits and typing `w` to skip
hooks are now two independent features.
It turns out that some people liked it with prefilling the prefix and perceive
it as a regression, so put it back in.
But only if we don't have a preserved message; this is an important use case,
when you try to make a normal commit, the hook fails, and then you want to make
the same commit with skipping the hook, but with the same message that you
already typed.
This makes it possible to use date and time in initial values like this:
```yaml
initialValue: 'ruudk/{{ runCommand "date +\"%Y/%-m\"" }}/'
```
I want to use this to configure my BranchPrefix like this:
```yaml
git:
branchPrefix: 'ruudk/{{ runCommand "date +\"%Y/%-m\"" }}/'
```
For the less common conflict types DD, AU, UA, DU, and UD, we would previously
only show "* Unmerged path" in the main view, which isn't helpful. Also, for
some of these we would split the main view and show this text both in the
unstaged changes and staged changes views, which is a bit embarrassing.
Improve this by offering more explanation about what's going on, and what the
most likely way to resolve the situation is for each case.
We keep the same commit selected (even though its index changed because of the
added update-ref todo), which is nice; however, the main view shows the diff of
the wrong commit, which is very confusing. I'm suprised that this hasn't been
noticed yet.
The reason why this happens is that we first do the refresh, which includes
re-rendering the main view diff (with the same commit index as before, so the
wrong one), and then we restore the correct commit index but don't render the
main view again.
I can only guess here: maybe they were added to more clearly document the public
interface of the classes? If so, I don't think that works. Developers who are
not familiar with the convention will just add a new public method to the class
without updating the interface.
Apparently this was an attempt at working around go's lack of default arguments,
but it's very unidiomatic and a bit confusing. Make it a normal parameter
instead, so all clients have to pass it explicitly.
There is a section at the end with deprecated settings, and a comment saying
"The following configs are all deprecated". The clipboard-related settings were
accidentally added to that section; they are not deprecated, so move them up to
before that section.
The current rules for discarding submodule changes is that no other changed item
must be also selected. There are some bugs with the current implementation when
submodules are in folders.
For example, selecting and discarding a folder with only a nested submodule
change will currently do nothing. The submodule changes should be discarded. The
folder only contains submodule changes so it should be no different than
pressing discard on the submodule entry itself.
Also, I noticed range selecting both the folder and the submodule and then
pressing discard would be incorrectly disallowed.
Renaming a file inside the same directory shows it with its full path in the
tree view, which isn't what we want. We'll fix this in the next commit.
Also adding a few other test cases for moving files; they show that the display
of moved files in tree view isn't ideal. For example, moving file1 from top
level into dir shows it as "R file1 → file1", which isn't distinguishable from
renaming file1 inside dir. I suppose what we would like to have here is
"R ../file1 → file1" or something, but I'll leave that for the future; here I
only want to fix the regression that was introduced with the root item PR.
In 8b8343b8a9 we made a change to run newPtyTask from AfterLayout; this is
needed so that the PTY gets the new, updated view size. However, this created a
race condition for integration tests that select a line in a list view and then
expect the main view to have certain content; sometimes that content gets
rendered too late.
I'm surprised that this didn't cause more tests to fail; right now I only know
of one test that occasionally fails because of this, which is stash/rename.go.
Fix this by moving the AfterLayout to inside newPtyTask, and do it only when we
are actually using a PTY (we don't when no pager is configured, which is the
case for integration tests).
The diff is best viewed with "ignore whitespace" turned on.
The test shows a misbehavior: even though the diff shows "-one" and "+three",
meaning that "three" is the state we want to check out, we get "one". The reason
is that the checkout file command doesn't pay attention to range selections, it
only looks at the "moving end" of the range. Had we created the range by
selecting "two" and then pressed shift-up to "three", we would have gotten the
expected result.
The actual usage is case insensitive, so this doesn't actually matter.
But if fills my heart with joy.
The test is case sensitive, but the actual response to `git config
commit.gpgSign` is equivalent to `git config commit.gppsign`
It's not an ideal solution because there's no indication of what kind of objects
you can add to those maps or arrays, but at least they show up at all (with a
comment containing a link to more information), and that's already an
improvement.
Add verify flag
Add and update integration tests
Rename verify to forceSkipHooks
Adapt CommitSkipHooks integration test to actually use a hook
Remove forceSkipHooks param from OnConfirm et al
Simplify tests
Assert the entire lines using Equals instead of Contains. This makes the tests a
bit easier to read, and it makes it much easier to decide how they need to be
changed when we change the layout (like we do in the last commit of this
branch).
It is true that this requires changing all these tests for any future UI
changes, but I think this is a good price to pay; those adaptions are trivial
and can be done without thinking.
I'm pretty sure that the check for the main view was meant to be done with the
commit selected in the commits panel, not with the first file of the commit
files view selected, so it was pressing enter too early. It's pure coincidence
that the test worked.
This is useful for copying the entire content of the selected file as it was at
the selected commit.
We only add it to the commit files panel; it is not needed in the files panel,
because there you can simply press "e" to edit the file.
This allows changing git config values while lazygit is running (e.g. in a
different terminal tab, or even in lazygit's ":" shell prompt), and have them
take effect immediately, while still getting some benefit from caching them
while lazygit is in the foreground.
Before we changed the commitPrefix config to a list in #4261, we had this bug
where the defaults section in Config.md would erroneously list the default for
commitPrefix as
git:
commitPrefix:
pattern: ""
replace: ""
This was not correct, commitPrefix was a pointer, and the default for that was
nil, which is not the same.
Now, some people copied and pasted the entire defaults section into their config
files, setting the commitPrefix to an empty pattern (which is not the same as
not setting it at all). And this caused the branch name to be filled in to the
subject field for every commit; see for example
https://github.com/jesseduffield/lazygit/discussions/3632.
New users copying the defaults section into their config file in the current
version no longer have this problem because now that commitPrefix is a list, it
is no longer included in the defaults section. However, the migration that we
added in #4261 would happily carry over those empty strings into a list entry,
so people migrating from an older version still have the broken config in their
config files.
To work around the issue, ignore commit prefix settings whose pattern is an
empty string. I can't imagine a valid reason why people would actually want to
set the pattern to an empty string, so I assume this only comes from the broken
defaults problem described above.
Some operating systems 'open' implementations do not like
when some special characters are unencoded, so they will
double-enconde the branch name, which we already encode.
This particularly matters since branch names with / are common
Rewording a commit at the beginning of a long branch can take very long;
without this change, the commit message panel would stay visible with a blinking
cursor during that time, which is very confusing.
This has the slight downside that it will say "Rebasing" in the lower right
corner until the operation is done; but we already have this problem when doing
custom patch operations, or dropping changes from a commit, so it's not new, and
we can think about how to fix all these another time.
WithGpgHandling already does an async refresh when done, so there's no need to
do one here for the case of amending the head commit. On top of that,
WithGpgHandling uses WithWaitingStatus and works in the background, so the
Refresh here would come too early anyway.
All this does is clear the preserved commit message; however, we open the commit
message panel with PreserveMessage: false when rewording, so this is not
necessary.
For some reason the //nolint:golint,unused comment no longer seems to work after
I updated gopls (I think).
But I also don't understand why we mark unused stuff with linter comments
instead of just getting rid of it.
Previously we only filtered them out from the example config section in
Config.md, but they still appeared in the schema. This is not ideal, because the
schema descriptions can appear in editors on mouse hover or in auto-completions.
So filter them out earlier, so that they don't appear in the schema either.
Some people have post-checkout hooks that take a lot of time, which makes
discarding changes slow. You can argue that a post-checkout hook should only run
when you switch branches, so it doesnt't have to run when checking out single
files or directories. You can also argue that lazygit might have implemented
discarding changes by taking the current patch and applying it in reverse, which
wouldn't have run a post-checkout hook either.
So disable them for all cases where we use git checkout with a path; this
includes checking out a file from the commit files view.
This seems to be what most people use when indenting yaml files, and it seems to
make more sense than the default of 4.
We also use it the example config in Config.md.
In ff4ae4a544 we changed the order of the calls to render before selecting the
branch. This was done only to save an extra call to ReApplyFilter, which is done
by refreshView; I claimed that the order of refreshView vs. SetSelectedLineIdx
doesn't matter here. I guess I was wrong about that, it makes the integration
test custom_commands/suggestions_preset.go flaky. To fix this, put the
refreshView call back to where it was (after the SetSelectedLineIdx call), and
instead insert an extra call to ReApplyFilter where necessary to fix the bug
that ff4ae4a544 was trying to fix.