Commit Graph
8001 Commits
Author SHA1 Message Date
Stefan Haller 4598c7b548 Adapt documentation 2026-07-31 08:42:51 +02:00
Stefan Haller 8731d8a51b Rework the custom pager config (rename to diff renderer)
For a long time lazygit has used the term "custom pager" to refer to
what's really a "diff renderer". A pager is a program that allows you to
view output page by page (hence the name), e.g. less; lazygit's custom
diff renderers are not pagers. It used the term only because the feature
is implemented using git's GIT_PAGER env var, but that's an
implementation detail.

Rename the 'git.pagers' config to 'git.diffRenderers', and restructure
its elements while we're at it to make things clearer:

- Add a 'type' field to explicitly specify which type of diff renderer
  it is (the two fundamentally different ones are 'stdinFilter' and
  'extDiff').
- Add a third type, 'rawGit', which has an 'args' field that makes it
  easy to use 'git --color-words' as a custom renderer
- Unify the old 'pager' and 'externalDiffCommand' fields to a single
  'command' field for both types

Existing config files are migrated automatically.
2026-07-31 08:42:51 +02:00
Stefan Haller ac4a1f575c Fix tests to use the right field name
It doesn't matter for what this test used to test (that nothing changes
if the 'pagers' array exists), but it will be relevant once we further
migrate the 'pagers' array from there.
2026-07-31 08:42:51 +02:00
Stefan Haller 1cf632002e Make CurrentPagerName always return a name
We don't want callers to need any additional logic, so pass in the
translation set so that the function can decide what static text to
return. This allows us to get rid of the CurrentPagerUsesGitConfigDiff
method which is in the way for the refactoring we're about to do.
2026-07-31 08:42:51 +02:00
Stefan Haller 2694aa774a Extract GitCommandBuilder method AddCommonDiffArgs
We were setting the same arguments in five places; extract this to one.
2026-07-31 08:42:51 +02:00
Stefan Haller 1ebb65b775 Cleanup: add blank lines for canonical markdown
My markdown formatter in VS Code adds these when I press Command-S.
2026-07-31 08:42:51 +02:00
Stefan Haller 4479d34e37 Remove BenchmarkMigrationOnLargeConfiguration
It has served its purpose when config migration was initially
implemented, but nobody runs this benchmark nowadays, and the example
config has run out of date with reality. Some PRs have still updated it
when they made changes to the config, but others didn't, and it's
unclear what the rules are; so let's just remove it.
2026-07-31 08:42:51 +02:00
Stefan HallerandGitHub 91dcd4cbb3 Reorder commits (or rebase todos) by dragging with the mouse (#5857)
Pressing the left button on the current selection now starts a drag that
moves the selected commits, both in the normal commits view and for
todos during an interactive rebase. A press on an unselected row still
creates a range selection.

While dragging, a "drop here" indicator shows where the commits will be
inserted.

Moving commits with the mouse is useful for the case that you want to
move them a longer distance, because that's slow when doing it one by
one with the keyboard, and also you don't want to resolve conflicts at
every step. The standard workaround for that is to enter an interactive
rebase first and then continue it afterwards, but that's a bit
cumbersome; dragging solves that nicely.

Closes #5842.
2026-07-31 08:41:18 +02:00
Stefan Haller 104fdf34a9 Keep the destination visible while commits move
Moving commits runs a rebase, which can take a while. Instead of
letting the drop indicator vanish the moment the button is released,
keep it in place and turn it into a "moving commits here" spinner once
the move takes longer than a short grace period, so that quick moves
stay free of flicker. The indicator is cleared when the post-move
refresh lands.
2026-07-31 08:37:28 +02:00
Stefan Haller cefec1c5c9 Cancel commit drags with escape
While a commit drag is in progress, escape now aborts it: the drag
state and the drop indicator are discarded and the mouse capture is
released, so nothing happens when the button is eventually released.
Otherwise escape keeps its normal meaning.
2026-07-31 08:37:28 +02:00
Stefan Haller 0738d55551 Keep scrolling while commits are dragged at an edge
Reuse the drag autoscroller for commit drags. Scrolling stops once the
insertion point reaches the end of the allowed range in the scroll
direction, so during a rebase the view doesn't keep scrolling once the
last insertion position among the todos has been reached.
2026-07-31 08:37:28 +02:00
Stefan Haller eda4ad1192 Drag selected commits to a new position
Pressing the left button on the current selection now starts a drag
that moves the selected commits, both in the normal commits view and
for todos during an interactive rebase. A press anywhere else falls
through to the usual click handling, so dragging from an unselected
line still creates a range selection, and releasing without having
moved collapses the selection to the pressed commit like a plain click
would.

While dragging, the insertion point follows the pointer: rows below
the dragged block insert after the pointed-at commit, rows above it
insert before it, and during a rebase the destination is limited to
the contiguous block of movable todos around the selection. gocui
moves the view cursor along with the pointer, so each drag event moves
it back to keep the original selection highlighted.

The move happens on release. The model may have been refreshed during
the drag, so the dragged commits are located again by their identity
(hash, subject, todo action); if they no longer form a unique
contiguous block, the drop is ignored rather than guessing.
2026-07-31 08:37:28 +02:00
Stefan Haller b055d28fb1 Show commit insertion points during a drag
Render the insertion point of a commit drag as a non-model item in the
commits list. It must be inserted at the right position relative to
the section headers, because the list renderer assumes non-model items
are ordered by their model index.

Not used yet, we'll hook it up to the drag gesture in the next commit.
2026-07-31 08:37:28 +02:00
Stefan Haller e756511042 Move dragged commits in one rebase
Let the todo-move primitives take a distance instead of hardcoding a
single row, by iterating the one-row move in memory. Dropping a commit
several rows away thus rewrites the todo file once and, outside of an
interactive rebase, runs a single rebase rather than one per row.
2026-07-31 08:37:28 +02:00
Stefan Haller b85483ecc0 Unify commit movement before adding drag destinations
Merge the up/down variants of the move commands into one
direction-parameterized implementation. Dragging commits is about to
need moves over arbitrary distances, which we don't want to build twice.
2026-07-31 08:37:28 +02:00
Stefan HallerandGitHub 4085b6462d Create a range selection in list views by dragging with the mouse (#5856)
Like in the staging view, drag with the mouse in a list view to create a
non-sticky range selection (including auto-scrolling as the mouse
reaches the view edge).
2026-07-31 08:37:13 +02:00
Stefan Haller 14434015c7 Keep range drags moving at panel edges
Give the list views the same edge autoscroll during drag selection
that the staging view already has; the new mouse-release binding stops
the autoscroll when the drag ends.
2026-07-31 08:32:43 +02:00
Stefan Haller e7998356be Create a range selection by dragging with the mouse in list views
Dragging with the left button held now extends the selection from the
pressed line, exactly like moving with shift+up/down does. We use the
non-sticky flavor so that the range collapses on the next plain cursor
movement, again matching the keyboard behavior.

The binding is only registered for contexts that support range selection
in the first place; dragging in other lists continues to do nothing.
2026-07-31 08:32:43 +02:00
Stefan HallerandGitHub 10bac9dbf6 Auto-scroll when dragging to create range selection in staging view (#5855)
The staging view (and custom patch building view) already has the
capability to create a range selection by dragging with the mouse;
however, a longer range couldn't be selected this way because the
dragging stopped at the view edge. Add auto-scrolling so that the view
scrolls as the mouse reaches the view edge; slowly on the innermost edge
row, faster on the outermost row and beyond. Scrolling starts after a
short delay so that a drag merely passing near the edge doesn't scroll.
2026-07-31 08:32:25 +02:00
Stefan Haller b682fb7635 Auto-scroll when dragging to create range selection in staging view
When the pointer reaches the edge of the view during a drag (or leaves
the view entirely, which mouse capture makes possible), keep scrolling
and extending the selection: slowly on the innermost edge row, faster
on the outermost row, and very fast beyond. Scrolling starts after a
short delay so that a drag merely passing near the edge doesn't scroll.

When the view loses focus mid-drag (e.g. because a popup appeared),
cancel the autoscroll and the mouse capture.
2026-07-31 08:26:35 +02:00
Stefan Haller f5a069342b Add DragAutoScroller helper
It can be used to auto-scroll a view during drag gestures when the
cursor reaches the edge of the view.
2026-07-31 08:26:35 +02:00
Stefan Haller ba024ed6c9 Add ViewDriver methods for mouse drag gestures in integration tests
Add press/move/release primitives next to the existing Click. The test
driver remembers the last reported position so a release doesn't have
to repeat the coordinates, and RepeatMouseMove lets a test verify that
a held-button motion event within the same cell has no effect.
2026-07-31 08:26:35 +02:00
Stefan Haller 98613957ae Refactor: extract replayMouseEvent helper method 2026-07-31 08:26:35 +02:00
Stefan HallerandGitHub a199a0a680 Gocui mouse event fixes (#5854)
Some fixes to gocui mouse event handling; these don't fix current bugs
in lazygit, but they are needed for upcoming work involving drag
gestures.
2026-07-31 08:26:16 +02:00
Stefan Haller 4609985029 Route mouse events to their originating view during a drag gesture
Route all mouse events to the view that was under the pointer when the
left button was pressed, until the button is released. Previously each
event went to whatever view was under the pointer at the time, so a
drag that left the view's bounds started acting on neighboring views.

Since events can now carry positions outside the view, clamp the view
cursor to the view's bounds in that case (handlers still receive the
unclamped position), and require an actual click for tab activation so
that a captured drag crossing the tab row doesn't switch tabs.
2026-07-31 08:22:35 +02:00
Stefan Haller 44a2bbeb7c Deliver mouse release after a drag
Releasing a mouse button was delivered as a plain mouse-move (hover)
event: the release processing resets dragState to NOT_DRAGGING, after
which the event fell into the NOT_DRAGGING branch. Views therefore had
no way of telling that a drag gesture ended, which the upcoming
drag-based features (range selection, commit reordering) need.

Deliver the release as a real mouse event with the MouseRelease key
and normalize its modifiers to ModNone, so release bindings also match
modified drags. Make recordClickInfo ignore it: a release is the end of
a click, not a click of its own, and must not break double-click
detection.
2026-07-31 08:22:35 +02:00
Stefan Haller 38d2293a10 Add test for double-click detection
Add a test pinning down that a press/release/press sequence at the
same position is detected as a double click. An upcoming commit starts
delivering the release as a real mouse event to the click-recording
code, which must not mistake it for a click of its own.
2026-07-31 08:22:35 +02:00
Stefan Haller a965db2a7d Demonstrate that drag release becomes hover 2026-07-31 08:22:35 +02:00
Stefan Haller ff53a3ed8c Preserve the first mouse movement of a drag
When the left button is pressed and the pointer then moves, the event
that made the MAYBE_DRAGGING -> DRAGGING transition fell through the
switch without being assigned a key or modifier, so the first cell of
every drag arrived at handlers as a MouseRelease event without the
motion modifier and was effectively lost. Give it the same
MouseLeft/ModMotion identity as all subsequent drag events.

Held-button motion events that stay within the pressed cell carry no
information at all; swallow them instead of letting them through as
further release-shaped events (which used to clobber the double-click
state when the pointer jittered within a cell between two clicks).
2026-07-31 08:22:35 +02:00
Stefan Haller c5fe27dfa5 Additions to AGENTS.md 2026-07-31 08:22:35 +02:00
Stefan HallerandGitHub 5e0a2e97ef Some small UI polish (#5853)
Two small UI changes:
- Change list section headers to a slightly more elegant design
- Change default spinner frames and rate
2026-07-31 08:22:22 +02:00
Stefan Haller 31cbabe48f Change list section headers to a slightly more elegant design
I'm not a skilled UI designer, so I suspect there may be even better
options, but it's definitely already better than the raw ASCII "---" we
had before.

Put the line only at the beginning because it looks bad if the line
after the label is misaligned when labels don't have the same width
(e.g. "Remote" vs. "Local" in the divergence view).
2026-07-31 08:03:40 +02:00
Stefan Haller 22db808a01 Add a helper function for formatting list section headers
We want to change their design, and we want to do it only in one place.
2026-07-31 08:03:40 +02:00
Stefan Haller 67c81c5b14 Change default spinner frames and rate
I find that this looks a little nicer, and it reduces CPU load by not
spinning so fast.
2026-07-31 08:03:40 +02:00
Stefan HallerandGitHub 940d7cc80f Support absolute paths when detecting edit preset from EDITOR env var (#5876)
Fixes #5875.
2026-07-31 08:01:57 +02:00
Stefan Haller f15f713c68 Support absolute paths when detecting edit preset from EDITOR env var 2026-07-30 17:31:56 +02:00
Stefan HallerandGitHub df0943ad33 Fix multi-selection of files with common prefix not working in commit files panel (#5868)
When selecting sibling directories sharing a common prefix, trying to
add them to a custom patch or discarding them from the commit would only
add one, not both. We had the same bug in the files panel and fixed it
in #3599, but forgot to make the equivalent change in the commit files
panel.

Fixes #5866.
2026-07-28 18:39:49 +02:00
Stefan Haller 1d107721f2 Fix multi-selection of files with common prefix not working in commit files panel
Equivalent to the change that was made to isDescendentOfSelectedNodes in
files_controller.go in a5eec48b4b.
2026-07-28 18:14:58 +02:00
Stefan Haller c7acf38399 Make isDescendentOfSelectedCommitFileNodes work for the root item
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 isDescendentOfSelectedCommitFileNodes would return
false for these.

Working with the internal paths (i.e. without stripping the leading
"./") fixes this.

There is no known breakage that is caused by this, that's why I'm not
adding an integration test that demonstrates a bug.

Equivalent to the change that was made to isDescendentOfSelectedNodes in
files_controller.go in 302b621b68.
2026-07-28 18:14:58 +02:00
Stefan Haller 8fefe2b933 Cleanup: move variable assignment out of the loop
It never changes inside this function, so there's no need to recompute
it with every loop iteration.

Equivalent to the change that was made to isDescendentOfSelectedNodes in
files_controller.go in d0c6e27fee.
2026-07-28 18:04:35 +02:00
Stefan Haller 6a022241d2 Add test to demonstrate a problem with custom patches and directories sharing a prefix
We had the same bug in the files panel, and fixed it in a5eec48b4b, but
forgot to make the equivalent change to the commit files panel.
2026-07-28 18:04:35 +02:00
Stefan HallerandGitHub 292035709f Small addition to CONTRIBUTING.md (#5861) 2026-07-25 18:39:03 +02:00
Stefan Haller 9d7ca51ee7 Small addition to CONTRIBUTING.md 2026-07-25 18:36:28 +02:00
Stefan HallerandGitHub 338d017191 Fix clicking in commits panel of unfocused VS Code window (#5859)
When lazygit is running in a VS Code window that doesn't have the focus
(e.g. a split tab) and you click in the commits panel to focus it and
select a commit at the same time, it would briefly select the commit you
clicked but then flash back to the commit that was previously selected.
This PR fixes that so that the clicked commit stays selected.

This was only a problem with VS Code; in Zed's builtin terminal it
worked, apparently because it first dispatches the click and then the
focus-in event. Ghostty and iTerm2 were not affected because they don't
dispatch clicks in inactive windows or tabs at all.

Labelling as ignore-for-release because it fixes a regression that was
introduced since the last release.
2026-07-25 18:09:39 +02:00
Stefan Haller 3d9318e2a7 Preserve commit clicks during focus refreshes
This fixes the problem described in the previous commit; we no longer
capture the selection at the start of the refresh. There's no reason to
do that (we don't do it for branches either). It is enough to capture
the selection in the final bounce, before we assign the new model slice.
2026-07-24 15:32:19 +02:00
Stefan Haller 5aa003612c Demonstrate stale focus refresh overwriting a click
When clicking in the commits view of lazygit running in an unfocused VS
Code window, VS Code first sends us the focus-in event and then the
mouse-click. The focus-in refresh captures the selection when it starts,
then we handle the mouse click and you briefly see the clicked row
getting selected, but then the selection flashes back to the original
row as the refresh restores it when done.
2026-07-24 15:30:29 +02:00
Stefan HallerandGitHub a5b4477a20 Fix Windows crash when switching to fullscreen mode with a custom pager (#5838)
Fix a crash when typing `+` twice to go to full screen mode, when a
custom pager such as delta or difftastic is configured.

Fixes #5837.
2026-07-23 18:07:56 +02:00
Stefan HallerandClaude Fable 5 02c8ba3073 Clamp ConPTY sizes to the 1x1 minimum that Windows accepts
CreatePseudoConsole and ResizePseudoConsole reject zero dimensions with
E_INVALIDARG, but we legitimately request them: the pty is sized after
the main view, and that view is zero-sized while hidden, e.g. in
full-screen mode with a side panel focused. Entering that mode while a
custom pager is configured therefore made StartPty fail (degrading to
unpaged output now that the fallback works), and resizing a live pty
from onResize would fail layout. The Unix pty accepts zero sizes, so
the clamp lives in the Windows implementation only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:31:26 +02:00
Stefan HallerandClaude Fable 5 c217084c90 Add test showing StartPty fails on Windows when given a zero size
CreatePseudoConsole rejects zero dimensions with E_INVALIDARG, so
starting a pty sized after a hidden (and thus zero-sized) view fails.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:31:26 +02:00
Stefan HallerandClaude Fable 5 f000ce9f1c Never hand NewCmdTask a nil reader when a command fails to start
NewCmdTask feeds the reader returned by its start func into a
bufio.Scanner, and Scanner.Scan panics with a nil pointer dereference
when that reader is nil. Two start funcs could produce one:

- newPtyTask's fallback for a failed StartPty returned a literal nil
  reader, alongside an ExecCmd that was never started, so the intended
  "fall back to a plain cmd task" never worked. This crashed lazygit on
  Windows when using a custom pager with the main view zero-sized, e.g.
  after pressing + twice to enter full-screen mode with a side panel
  focused: ConPTY rejects zero dimensions, making StartPty fail.

- startCmdWithPipe returned nil when the pipe couldn't be created,
  which the Unix pty fallback path can trigger, since a failed pty
  start can leave the tty assigned to the command's stdout.

Make startCmdWithPipe never return a nil reader: when the pipe can't be
created, don't start the command at all and return an empty reader so
the task shuts down cleanly with the error in the log. Then route
newPtyTask's fallback through it, so a StartPty failure degrades to
running the command without a pty: the pager is lost, but the command's
output still renders.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:31:26 +02:00