Discarding a nested repository deletes its directory like that of any
untracked file. If the repository keeps its git dir in its directory,
this deletes all of its history, and nothing said so. The directory of
a removed submodule keeps its git dir elsewhere, but it may have
changes that aren't committed.
Check for both before discarding. If anything would be lost, list it
and ask for confirmation.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If nothing is staged, committing offers to stage all files first, or
stages them without asking if gui.skipNoStagedFilesWarning is set. Like
staging all files in the files panel, this adds a nested repository as
an embedded repository.
Show why the nested repository can't be staged instead, as staging all
files in the files panel does.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If you staged the directory of a removed submodule, a nested repository
or a worktree inside the repo, git added it as an embedded repository: a
gitlink without an entry in .gitmodules. For a removed submodule, this
brings it back in a broken state. git warns about it, but lazygit didn't
show the warning. Staging all files did the same to every such
directory.
Disable staging a selection that contains one of these, and disable
staging all files if that would stage one. The disabled reason tells you
how to get rid of it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Selecting one of these in the files panel showed the error "Could not
access 'modules/sub/null'". The main view showed the entry's diff
against /dev/null with --no-index, as for an untracked file. When git
compares a file with a directory that way, it looks for a file of the
same name inside the directory.
Show what the directory is and what to do about it instead. Below that,
show the status of the repository that it is, or the files in it if it
is a plain directory. The status tells you whether discarding the
directory would lose anything.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If a linked worktree lives inside the repo's working tree, it shows up
in the files panel. Discarding it there deleted its directory like that
of any untracked file. git still had the worktree on record, so the
worktrees panel listed it as missing, and its branch couldn't be checked
out anywhere else until the worktree was pruned.
Show the same menu for removing the worktree as the worktrees panel
does. Like a submodule, a worktree can't be discarded together with
other entries, because it has a menu of its own.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The files panel shows such a directory like an untracked file. Nothing
tells you that it is a directory, or that a submodule left it behind
when a commit that you checked out removed the submodule. This is the
case for the directory of a removed submodule, a nested repository, a
linked worktree inside the repo, and any untracked directory if
status.showUntrackedFiles is "normal".
Tell these apart when loading the files, by looking at the directory's
.git. Label removed submodules, nested repositories and worktrees, as we
already do for submodules, and put a slash after the name of a plain
directory, as git status does. With file icons on, a removed submodule
gets the submodule icon, and a nested repository or a plain directory
gets the directory icon.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
git status doesn't descend into a nested repository, such as the
directory that a removed submodule leaves behind. It reports the
directory as a whole, with a trailing slash. The file tree splits paths
at slashes, so it showed a directory with a file without a name in it.
Discarding the directory removed it, but then the cleanup of emptied
directories took the parent of "modules/sub/" to be "modules/sub"
itself, and failed to read it.
Take the slash off when loading the files. This also covers any
untracked directory if status.showUntrackedFiles is "normal", and linked
worktrees inside the repo. Their slash was taken off when they were
marked as worktrees; this is no longer needed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If a commit that you check out or pull removes a submodule, git leaves
the submodule's directory behind, and git status reports it as an
untracked directory. The files panel shows that directory with a file
below it that has no name. Discarding the directory shows an error,
even though the directory is gone afterwards.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The test enter_and_double_click_focus_file_diff fails occasionally on
CI. After it double-clicks a file, the files panel still has the focus
instead of the main view.
The test double-clicks by calling Click twice. Click waits for lazygit
to become idle after the press and again after the release, and the
first wait includes the git diff that the click starts for the main
view. gocui only counts the second click as a double click if it handles
it within 500ms of the first one. On a busy CI runner, where many
integration tests run in parallel, the waits can take longer than that,
and the second click counts as a single click.
Add DoubleClick to the test driver. It replays both clicks without
waiting in between, as a terminal reports a double click, and waits for
lazygit to become idle only after the second one.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If the main view is scrolled more than about 5000 lines down a diff and
another commit with a diff of more than 5000 lines is selected, the
main view keeps showing the previous diff.
A render reads its command's output into an off-screen buffer, and
shows it only once it has read enough lines to fill the view or has
reached the end of the output. It then reads on as far as an accurate
scrollbar needs, and waits for the user to scroll further. Both counts
include the lines above the view, so that a re-render of the diff the
user is scrolled into fills the view where it is. But the 5000-line cap
that is meant for the scrollbar applies to the lines above the view as
well. So if the view is scrolled further down than that, the render
stops reading before it has filled the view, and never shows anything.
The view also counts as loading until the next render starts, so the
layout doesn't keep it from scrolling past the end of the diff in the
meantime.
This has been a problem since 9e23111172 made renders read into an
off-screen buffer. Before that, the lines of a render that stopped early
showed up anyway.
Apply the cap to the lines for the scrollbar only. Also never ask for
fewer lines than fill the view. A view no more than four lines tall
needs fewer lines than that for its scrollbar, even at the top of a
diff.
A render of another diff shows it from the top, so it doesn't need the
lines above the view at all. A later commit stops it from reading them.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With gui.scrollPastBottom set to false, picking the last file of a long
diff from the jump-to-file menu of the commits panel scrolls that file
to the top of the view. This leaves blank space below the end of the
diff.
A jump in a pane that isn't focused puts the file at the top of the view
and leaves it to the layout to stop at the end of the diff. But the
layout doesn't do this while the view is loading. Picking an item from
the menu closes the menu first, and the commits panel starts a re-render
of the diff as it gets the focus back. So the view is loading when the
jump happens, and the layout corrects the position only in a later pass
after the re-render has finished. For a long diff the re-render never
finishes; a later commit fixes this. Clicking a file in the diffstat
doesn't close a menu, so it doesn't have the problem.
Stop the jump at the last screenful of the diff, as a jump in the
focused pane does. Like that one, it stops there whatever
gui.scrollPastBottom is set to.
The diff in click_a_file_in_a_diff_stat_that_comes_first fits on
screen, so the jump doesn't scroll it any more. Make it longer than the
view.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If the main view is scrolled more than about 5000 lines down a diff and
another commit is selected, the main view keeps showing the previous
diff, as long as the new one is longer than 5000 lines as well.
Similarly, if the jump-to-file menu of the commits panel is used to go
to the last file of such a diff, the file's header ends up at the top
of the view with blank space below the end of the diff. This happens
even with gui.scrollPastBottom set to false.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If you press `e` on a file of a commit, and a later commit deleted that
file, the editor is opened on a path that doesn't exist. Most editors
then create it as a new, empty file. This is confusing. The same
happens with `o`, and with any other file that the worktree it is
opened from doesn't have. An example is a file that is added in a branch
that isn't checked out anywhere, since it is opened from the current
worktree.
Show an error instead of opening the editor. For a range selection in
the commit files panel, show it only if none of the selected files
exist. Otherwise open the existing ones and leave out the others
silently; an error shown only after returning from the editor would
come too late to be useful.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If the commit files panel was entered from a branch that another
worktree has checked out, "Copy absolute path" in its copy menu gives
the file's path in the current worktree. Make it give the path in the
worktree that has the branch checked out. This is the file that `e` and
`o` open, and the one users most likely want the path of.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Do the same for `o` in the commit files panel as the previous commits
do for `e`. If the panel was entered from a branch that another worktree
has checked out, open the file from that worktree.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Pressing `o` on a file in the commit files panel has the same problem as
`e` had before the previous commit. If the commit is of a branch that
another worktree has checked out, the file is still opened from the
current worktree. The absolute path in the panel's copy menu points into
the current worktree too.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If you enter a branch in the branches panel and press `e` on a line of
a commit's diff, or on a file in the commit files panel entered from
there, lazygit opens the file in the current worktree. If another
worktree has the branch checked out, this is the wrong copy of the
file. The current worktree may have it in a different state, or not
have it at all. Clicking a lazygit-edit:// hyperlink of a diff renderer
in such a diff has the same problem.
Open the file from the worktree that has the branch checked out
instead, and carry the line number forward to the file as that worktree
has it.
If no other worktree has the branch checked out, the file is still
opened from the current worktree.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If you enter a branch in the branches panel, and that branch is checked
out in another worktree, pressing `e` on a line of a commit's diff opens
the file in the current worktree. The same happens for `e` on a file in
the commit files panel entered from there. The current worktree may not
have the file at all, as in this test, or have it in a different state
than the branch has it. The line number is carried forward to the
current worktree's version of the file too, and this gives line 0 here.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If a view doesn't wrap, a search match can be past its right edge, or
left of it once the view is scrolled sideways. Going to such a match
with /, n or N only scrolls the view vertically, so the match stays out
of sight. This matters most for a diff with gui.wrapLinesInDiffView set
to false, where a change is often at the end of a long line.
Scroll the view sideways if the match isn't on screen. Put the match a
third of the way across the view, so that some of the text before it
shows too. If the view can show the match without scrolling, go back to
the left edge instead. This applies to every view that doesn't wrap,
including the side panels.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If gui.wrapLinesInDiffView is false, long lines of a diff run off the
right edge of the main view, and there is no way to see the rest of
them. This makes it hard to see what has changed in such a line. The
old staging and patch-building views scrolled sideways with H and L, but
the focused main view that replaced them has no binding for these keys.
Bind them in the focused main view, as in the side panels. Leaving the
view scrolls it back to the left edge, as it did for the staging view.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
When we stage, discard or edit lines of the working tree's diff, we
read the file's diff again and look for the selected lines in it. If
none of them are there, nothing happens. The same goes for adding
lines of a commit's diff to a custom patch, or taking lines out of
it. This can happen if the file changed since its diff was shown, or
if we read the line numbers out of the diff on screen wrongly.
Show an error that names the file instead. As with a file that can't
be found, don't act on any file if the lines of one are missing.
Unlike the file check, this one happens after a new custom patch has
been started. If the error leaves that patch without any lines, reset
it, as we do when the last line is taken out of a patch.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If the path in the diff header of a selected line doesn't name a file
of the files panel, staging or discarding the line does nothing. The
command log shows "Apply patch", but nothing else happens, so the user
has nothing to go on when reporting it. Adding a line of a commit's
diff to a custom patch fails in the same silent way if the path
doesn't name a file of that diff. A diff renderer that names files
differently can cause this, and so can a bug in how we read the path,
like the one we had with diff.mnemonicPrefix.
Show an error that names the path instead, and do nothing else. If a
selection spans several files, don't act on the files we did find,
because that would do only part of what the user asked for. Do the
same for opening a line of a diff in its pull request.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If diff.mnemonicPrefix is set, pressing space on a line in the focused
main view does nothing. The command log shows "Apply patch", but the
line is not staged. Other commands that read a path out of the working
tree's diff go wrong as well, such as jumping to a file. Building a
custom patch from a commit's diff still works, because git uses
mnemonic prefixes only when one side of the diff is the index or the
working tree.
With this config, the paths in the working tree's diff start with i/
and w/ instead of a/ and b/. We only strip a/ and b/, so the path we
read out of the diff header is not a file of the files panel, and we
skip the line without a message.
Users who set the config want to see these prefixes, so keep them.
Strip the first component of the path instead, whatever it is. `git
apply` does the same by default, and ParseableDiffPrefixes makes sure
there is such a component.
A section without +++ and --- lines needs more than that, because we
find the new path on its "diff --git" line by searching for " b/".
Take the path from the "rename to" or "copy to" line instead where
there is one. Otherwise the two paths on the "diff --git" line are the
same. Split the line where the two halves match after stripping their
prefixes, like `git apply` does.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If diff.mnemonicPrefix is set, pressing space on a line in the focused
main view does nothing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If diff.srcPrefix and diff.dstPrefix are set (git 2.45 and later),
pressing space on a line in the focused main view does nothing. These
configs change the diff of a commit too, so the other commands that
read a path out of a diff go wrong as well.
The configs replace the a/ and b/ in front of the paths of a diff with
any string. We only know how to strip a/ and b/, so the paths we read
out of the diff header don't name a file.
We can't tell from the diff where such a prefix ends, so override the
two configs along with diff.noprefix.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If diff.srcPrefix and diff.dstPrefix are set, pressing space on a line
in the focused main view does nothing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If diff.noprefix is set, pressing space on a line of a file under a
top-level directory named a or b does nothing. In a stash entry's diff,
the menu for jumping to a file shows such a file without its directory.
We read the path of a file out of the diff header and strip the a/ or
b/ in front of it. Most diff commands override diff.noprefix so that
this prefix is there, but the diff of the working tree and the diff of
a stash entry don't. With the config set, we strip the file's own
directory instead.
Use ParseableDiffPrefixes for these two commands too.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If diff.noprefix is set, the menu for jumping to a file of a stash
entry's diff leaves out the directory of a file under a top-level
directory named a or b.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If diff.noprefix is set, pressing space on a line of a file under a
top-level directory named a or b does nothing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The 'auto' style doesn't recognize the terminal that vhs records, so it
would draw the classic graph. Now that the recordings have a font for
the branch drawing symbols, use the detailed graph in all demos.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The recording of this demo stops with the confirmation popup still on
screen. The explosion animation and the emptied Files panel that the demo
exists to show never happen at all. Nuking the working tree has asked for
confirmation since 238fdd573c, and the demo only selects the menu item, so
the nuke never runs. The demo asserted nothing about the outcome, so it
stayed green all the while.
Answer the confirmation, and assert that the Files panel ends up empty.
That assertion also holds the recording open until the animation has
played out and the panel has been refreshed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
How a demo behaves depends on how much of a list fits on the screen, and
`just e2e` ran demos on a 150x100 screen while we record them at 120x35.
So a demo could pass the test suite and still fail partway through a
recording, and there was no way to find out short of recording it.
Give a demo the recording size when it doesn't ask for a size of its
own.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
NavigateToLine looks for its target among the lines the view has
rendered. When a list is scrolled, the view holds only the part of it
that is on screen, so the target may not be among them. For that case
the helper jumps to the top of the list and walks down instead.
That walk presses a key before it looks, so it never sees the item it
starts on, which after jumping is the first item of the list. A target
sitting there is reported as missing. The shorter the terminal, the more
of a list is scrolled out of view and the more often that walk is
needed, so this surfaced once the demos began running in a 35 line
terminal.
Check the line the walk starts on before moving off it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
When running the demos at a hight of 35, as the new recording mechanism
will, this demo failed because the commits list was too small to show
both commits at the same time, and NavigateToLine has a bug that
prevents it from finding it by going to the top and pressing down until
it matches. We are going to fix that bug in the next commit for similar
future situations, but we also solve the problem here by setting the
side panels to accordion mode so that more commits are visible; this
looks better for this demo anyway.
If you copy commits with shift-C and then switch to another worktree,
the copied commits are gone, so you can't paste them there with
shift-V. This is a common workflow: copy commits from the branch that
is checked out in one worktree, maybe drop them there right away, then
switch to the worktree of another branch and paste them.
The copied commits live in the cherry-picking mode, and each repo state
creates its own. We keep a separate repo state per worktree, so every
worktree starts with an empty clipboard.
Create the cherry-picking mode once and share it between all repo
states. This also lets you paste in a different clone of the same repo,
as long as that clone has the copied commits (for example because you
fetched them there). If the current repo doesn't have them, git
cherry-pick fails with "bad object", and we show this as an error.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Emptying a pane of the main view clears it right away, but leaves the
view's tasks alone. If a diff was asked for before, and its task is
only created after the layout, that task fills the pane again. A diff's
task that is still reading can also write into the emptied view.
Give the pane an empty render as well. The render takes its place among
the view's tasks, so the earlier diff's task isn't created, and it stops
a task that is still reading. Once that task has stopped, the render
empties the view again.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If the lower pane of the main view is asked to show a diff and is then
emptied before the next layout, it ends up holding the diff. Selecting
a file with staged changes and moving back to one without any in rapid
succession does this. The pane is hidden then, but when it is shown
again, it shows the other file's diff until its next render replaces
it.
Emptying a pane clears the view right away, but leaves the view's tasks
alone. So the diff's task, which is only created after the layout,
fills the pane again. A task that is still reading a diff into the pane
isn't stopped either, so it can write into the emptied view.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If a diff and then a message are asked for in the main view before the
next layout, the main view shows the diff. One way this happens is
switching tabs twice in rapid succession. Another is discarding the
changes of the only changed file. Closing the menu asks for the file's
diff, and if the files are refreshed before the layout, "No changed
files" is asked for next. The main view then stays empty, because by
the time the diff runs, the file has no changes. This made the
hide_selection_when_changes_vanish test fail now and then.
A diff's task is only created after the layout, since the layout settles
the width that the diff is laid out to. A diff renderer's task has been
created there since 8b8343b8a9, and the task for git's own diff since
0afb94e97b. A view shows the task that was created last, so the diff's
task replaced the message's.
Reserve the diff's place among the view's tasks when the diff is asked
for, and give its task that place when it is created after the layout.
If another task has been asked for in the meantime, don't create the
diff's task at all.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
If two tab switches are handled before the next layout, and the first
tab shows a diff in the main view while the second one shows a message,
the main view ends up showing the diff.
A diff's task is only created after the layout, since the layout
settles the width that the diff is laid out to. A message's task is
created right away. So the diff's task is created after the message's,
and replaces it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A diff opens with a diffstat naming every file in it, right above the
diff of each of them. Selecting a commit puts that list in front of you,
naming the same files the menu of the diff's files offers, and the menu
is still the only way to any of them.
Make each of those names a link that goes to where that file's diff
begins, as picking it from the menu does. The panel keeps the focus, so
a file of the commit being read is a click away and the selection stays
on the commit.
The names are found in the output as it is written to the pane, for next
to nothing. The diffstat is git's own text whichever renderer the diff
goes through — delta, diff-so-fancy and difftastic all pass it on
untouched — and it comes first, so the scan for it ends with it and
nothing below is looked at.
The link states the name as the diffstat does, and which file that names
is worked out on the click, against the files the diff turned out to
hold. That is the point at which a name the diffstat cut off behind
"..." or compacted to the "{old => new}" form of a rename can be
recognized at all.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
n and N step through the files of a multi-file diff one at a time, which
is a long way to the far end of a commit touching a hundred files.
Bind ctrl+g to a menu of the diff's files, in the order the diff shows
them and by the paths the repo knows them by; picking one goes to where
its diff begins, exactly where stepping to it with n would have left
you. The menu filters as you type, so the file you have in mind is a few
characters away however many the commit touches.
The key works in the panel the diff belongs to as well as in the diff
itself. A commit is read from the commits panel, so being able to jump
from there saves focusing the diff and leaving it again for the next
commit; the diff scrolls to the file and the focus stays in the panel.
It is ctrl+g rather than f because a key that works in every panel has
to be free in all of them, and f is fetch in the files panel and fixup
in the commits panel.
The command applies only while the main view is showing a diff, and is
left out of the keybindings menu where it isn't: over a branch's commit
log, or while a conflicted file has given the main section over to the
merge conflicts view.
The diff is read to the end before the menu is built, as searching it
does: a file below the part that has been read is in neither the list
nor the view. Each item names its file rather than the row that file
begins at, so that a diff re-rendered while the menu is up is jumped
into at the row the file begins at now.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reading a change in lazygit and saying something about it on GitHub
means finding the line again in the browser: open the pull request, find
the commit, find the file, scroll to the line. The line is already under
the cursor here.
Bind G in the focused main view, the key the commits panel opens the
pull request with, to open it at the line the selection is on. The URL
names the commits whose diff is on screen, so that the line numbers of
the diff are the ones the page shows, the file by the SHA-256 of its
repo-relative path, and the line by the side of the diff it is on: R for
the new version of the file, L for the old one, where a deleted line is.
GitHub documents none of that; the form was read off the URLs its own
pages carry.
One commit is named by its hash. A range of them is named by the commit
the range starts after and the commit it ends at, the form the chooser
above a pull request's files uses. The commit a range starts after is
the parent of its oldest commit; where the range starts where the pull
request itself does, that parent is none of the pull request's own
commits, and the keyword BASE stands for it.
Which branch's pull request that is depends on the panel beneath. The
commits panel lists the commits of the checked-out branch, the
sub-commits panel those of the branch drilled into, and the commit files
panel shows the files of a commit from either. In a stack of branches,
each with a pull request of its own, those lists include the commits of
the branches below, and each of those commits is in the pull request of
its own branch. So the command looks upwards from the commit for the
nearest head of a branch with a pull request, and takes the listed
branch if it finds none. Panels showing a diff that no pull request has
a view of don't answer, and the command isn't offered over their diffs
at all.
Neither is it offered over a diff that is not the commit's own, where
the line numbers on screen are not the ones the page shows: a diff
against another ref in diffing mode, and the custom patch, whose lines
sit at the numbers the patch gives them.
A pull request holds only the commits of its branch that are pushed, and
its pages say they can't find any other commit. So the command refuses
where a commit of the diff is not one of the pull request's. Amend a
commit in the middle of the branch, and the diffs of the commits below
it still open; the ones above it sit on hashes the remote doesn't have.
A commit from before the branch, in a main branch already, is refused
too, and so is a range of commits that reaches across the head of a
branch in a stack, since its commits are in two pull requests. Whether a
commit is pushed is known only for the upstream of the listed branch.
For a branch lower in a stack, that is right as long as the branches of
the stack are pushed together.
Only GitHub pull requests are known, since that is where the pull
request data comes from. The whole path can't be exercised headlessly:
no pull request reaches the model without a GitHub token, so the test
covers where the command is offered and the three reasons it refuses.
The URL is unit-tested instead, both the anchor of a line and the way
the commits are named, and so is the choice of a branch in a stack.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Clickable renderer gutters are small and unavailable on some diff rows.
Make the whole row an editor target without changing focus or selection,
including while a popup is focused.
Use both Alt and Shift because terminal mouse protocols do not deliver
either modifier consistently across Ghostty, iTerm2, and VS Code.
Generic range assertions and migrated commit tests should describe the views
they still exercise. Drop stale explorer wording so failures and test intent
no longer point contributors at UI that does not exist.
Co-Authored-By: GitHub Copilot <copilot@github.com>
Working in a diff used to mean the staging view, and this option said
whether the long lines there were wrapped. The main view does that work
now, and the option has had nothing to govern since the staging view went
away. Both panes wrap whatever they are given.
Make the option their wrap setting instead.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Hunk defaults and wrapping no longer belong to a staging panel. Rename both
public keys with automatic migration, and remove panel-era English strings so
config, UI, schema, and generated docs describe the surviving interface.
Co-Authored-By: GitHub Copilot <copilot@github.com>
The focused diff addresses patch lines by identity, so changing how much
context is rendered cannot invalidate an active patch. Remove the
explorer era refusal and prove that another line can still be toggled
correctly after the rerender.
Co-Authored-By: GitHub Copilot <copilot@github.com>
Nothing routes to the explorer contexts after line actions and file entry
moved into the normal main pair. Delete their contexts, views, main-pair
wiring, discovery API, test drivers, and selection-state package so the
surviving diff view is the only implementation.
Their names go from the contexts a custom command may bind to as well, so
that a config still naming one is reported as the config error it now is
rather than taken as a context we simply failed to find.
Co-Authored-By: GitHub Copilot <copilot@github.com>
Enter and double-click should take users to the diff where line actions now
live, whether the file belongs to the working tree or a commit. Share the
existing focus, raw-fallback, and selection setup while retaining directory,
submodule, and conflict-specific behavior.
Co-Authored-By: GitHub Copilot <copilot@github.com>