Working with diffs
The Main panel always shows a diff for whatever is selected in the Files panel. Everything on this page acts on that diff — the one currently on screen.
Reading a diff
Section titled “Reading a diff”Additions, deletions, hunk headers and metadata are each tinted, and the +/- gutter
stays pinned as the text scrolls sideways:
--- a/internal/svn/status.go (revision 42)
+++ b/internal/svn/status.go (working copy)
@@ -12,7 +12,9 @@ func Parse(r io.Reader) ([]Entry, error) {
dec := xml.NewDecoder(r)
var doc statusXML
- if err := dec.Decode(&doc); err != nil {
- return nil, err
+ if err := dec.Decode(&doc); err != nil {
+ return nil, fmt.Errorf("parse status: %w", err)
}
return doc.Entries(), nil
}
Scroll with J / K from any panel, or focus Main with 0 and use the movement keys. ← / → scroll a column at a time, and ^ / $ jump to the start and end of the line.
Directory diffs
Section titled “Directory diffs”Highlight a directory row and Main shows the combined diff of every change beneath it.
The root / row gives you the whole working copy as one patch.
This is on by default. Turn it off globally with
directoryDiff — a directory row then shows a
short hint instead — and press D to reveal one on demand for the current
session.
Selecting a group in the Changelists view works the same way: Main shows the combined diff of the whole list.
Reading a diff side by side
Section titled “Reading a diff side by side”s reopens the diff Main is showing as a two-pane comparison in a centered overlay.
- Each removal sits opposite the addition that replaced it; context lines appear on both sides.
- Every line is numbered in its own revision — the left pane in the base revision, the right in the working copy — and the pane labels name them.
@@hunk headers span the full width.- Both panes scroll together with the movement keys.
- A multi-file diff is paged one file at a time: [ and ] turn between files, and the bottom border names the open one along with the line range and page number.
- The layout behind dims out of the way while you read.
s or esc closes the overlay.
If Main has no diff to show — a directory row with directory diffs off, or a failed load — a toast says so rather than opening an empty overlay.
Saving a diff
Section titled “Saving a diff”w writes the diff on screen to a file. A prompt asks for the name and suggests a default; leaving it blank uses the suggestion.
| Selection | What is saved | Suggested name |
|---|---|---|
| A file | That file’s diff | path-to-file.diff |
| A directory | The combined diff beneath it | path-to-dir.diff |
The root / row |
The whole working copy | working-copy.diff |
| A changelist | The list’s combined diff | feature-x.diff, staged.diff, unstaged.diff |
The extension is added for you unless the name already ends in .diff or .patch, and
only the base name is used — a path in the prompt cannot write outside the output
directory.
Files land in diffOutputDir, which is created
if it does not exist. Empty — the default — means the working copy’s root, and a leading
~ is expanded to your home directory.
The Diffs view
Section titled “The Diffs view”] twice from the Changes view opens Diffs: the .diff and .patch
files already in diffOutputDir, newest first.
Selecting one shows its contents in Main, where / search and s side-by-side both work as usual. e opens the patch in your editor.
The view lists files, not working-copy changes — stage and revert do nothing here, and
w says the diff is already saved. d deletes the
highlighted patch file from diffOutputDir, after asking. It rescans on
R, on entering the view, after a successful save, and whenever
diffOutputDir changes.
Applying a saved patch
Section titled “Applying a saved patch”p in the Diffs view applies the highlighted patch to the source path
revision is showing, after asking for confirmation. It runs svn patch, so the changes
land as local modifications for you to review and commit as usual.
Two checks stand between the prompt and the patch, because svn patch on its own will
happily apply a patch in the wrong place — creating every target it cannot find and
rejecting the patch’s hunks into it:
- The patch must belong here. Every path in a patch is relative to the directory it was
produced in, so
revisionfirst looks for the files the patch expects to already exist. If none of them are in the source path, the patch was taken from another directory and is refused. - Some of it must land.
revisionthen runssvn patch --dry-run. If svn says nothing in the patch applies — every target missing, or every one conflicting — it is refused, since applying it would leave rejects and nothing else.
Partly applied patches
Section titled “Partly applied patches”A patch that only partly fits is still applied. svn patch takes the hunks it can and
writes the rest to a <file>.svnpatch.rej beside its target, leaving that file to be
finished by hand. The toast says so:
applied feature-x.diff to 3 files, 1 with rejects (.rej), 1 not foundRejects are per file, not per hunk: a file with any rejected hunk is counted there even
though its other hunks did go in. .rej files are svn-ignored, so they never appear
in the Changes view — the Rejects view is where you read them.
Finding rejects
Section titled “Finding rejects”] once more from the Diffs view opens Rejects: every .rej file
beneath the source path, as a tree. The search
is recursive, because a reject lands beside the file it failed to patch rather than in one
directory; .svn is skipped. Directories nest exactly as they do in the Changes view, and
enter folds one shut.
Selecting a reject shows its hunks in Main, where / search and s side-by-side both work as usual. m opens the reject against the file it was written for so you can take its hunks one at a time — see Resolving conflicts and rejects. e opens it in your editor to apply the hunks by hand instead, and d deletes it once you are done, after asking. A directory row has no file of its own, so it summarizes what sits beneath it.
The walk is deferred until you look: the view rescans on R, on entering it, after applying a patch, and after the source path changes — never while another view is on screen.
Searching inside a diff
Section titled “Searching inside a diff”Focus Main with 0, press /, and type. Matching lines are highlighted in place — nothing is hidden — and n / N jump between them. See Filtering & searching.
s, w and D are in the View section of the keybindings reference.