Repository navigation
How the Staging View Works
The staging view shows a file of the Changes diff in three panes: HEAD, the index and the work tree, with bands joining each side's changes to the index in the middle. It reuses the 3-pane diff of merge commits. For the user side, see Staging View.
The two-pane diff shows either the staged or the unstaged half of a file, so a file with both needs two clicks to understand. The staging view shows both halves at once around the index, and stages or unstages from the bands. We already had every piece: the 3-pane frame and bands of the merge tool and the merge commit view.
ChangesDiff.svelte (and the status bar's Changes tab, ChangesTab.svelte) shows ThreePaneDiff.svelte when Diff layout is side by side, settings.changesDiffPanes is "three" and stagingViewFits says the file is a plain modified file (not new, deleted, renamed, untracked or conflicted). StagingDiffs (views/changes/stagingDiffs.svelte.ts) loads both diffs:
flowchart LR
S[get_file_diff staged] --> L[HEAD against the index: left]
U[get_file_diff unstaged] --> W[workTreeSide: flip it]
W --> R[work tree against the index: right]
L --> T[ThreePaneDiff]
R --> T
ThreeWayEditors takes each side pane against the middle. The unstaged diff runs the other way (index to work tree), so workTreeSide swaps its texts, EOLs and hunks (flipHunks). The options { rightNewer: true, conflicts: false } keep its colors right (lines the work tree adds are green) and never call a change a conflict.
A band's arrow calls ThreeWayEditors.taken(side, index), which takeChange builds: the index text with the change's lines from that side. changesSelection.applyDiffChange writes it to the index, the same path as the two-pane arrows. The new status reloads both diffs; StagingDiffs keeps a diff whose texts did not change, and ThreePaneDiff keeps the middle pane's scroll position when it rebuilds.
For the colors, paneTones (threeWay.ts) makes the left pane older and the right newer when rightNewer is set; parentMarks and resultMarks give each mark that tone, the middle the opposite end of its side, and toneColors (mergeExtensions.ts) draws old in --diff-removed and new in --diff-added. MergeFrame's colors fades each band from its older pane to its newer one. A merge's conflicts keep their color, and the merge tool sets no tone.
Every diff draws the same DiffToolbar.svelte: the two-pane DiffView and ThreePaneDiff pass the counter, the layout (three, sideBySide or inline), the Three panes tooltip and what they offer, so a button or a fix reaches both. threeWayBlame.ts puts Blame on one pane (blame.pane: the work tree here, the merge result for a merge commit). Open File shows when the caller passes workingFile; it calls openWorkingFile (diff/openWorkingFile.ts, shared with DiffView) at ThreeWayEditors.lineOnScreen("right"), the working tree's line.
MergeFrame saves two boundaries, settings.threePaneSplit, as the panes' flex-grow shares. Each connector's outer edge has a SplitHandle (the two-pane diff's handle) in a box that leaves out the other connector, so its ratio is a share of the panes' width. moveThreeSplit (library logic/threeSplit.ts) keeps every pane at 15% or more. The merge tool shares the frame and the setting.
| File | What it does |
|---|---|
src/lib/views/changes/stagingDiffs.svelte.ts |
Which files fit, loading both diffs, the flipped work tree side |
src/lib/views/changes/ChangesDiff.svelte, views/git/ChangesTab.svelte
|
Pick the staging view or the two-pane diff |
src/lib/diff/ThreePaneDiff.svelte, threeWayBlame.ts
|
The frame for merges and staging, and its pane blame |
src/lib/diff/DiffToolbar.svelte, diffToolbar.ts
|
The one toolbar of every diff, and its counter |
src/lib/diff/threeWay.ts |
ThreeWayOptions, flipHunks, takeChange
|
- Off by default. The two-pane diff stays the Changes default; the choice is remembered once made.
-
A setting, not browser storage. The pane counts are preferences in
settings.json(changesDiffPanes,mergeDiffPanes), so Settings > Git can show them and they move with the settings file. Three columns show only while Diff layout is side by side; the toolbar's Side by side button means 2 columns (diff/diffPanes.ts). The oldlocalStorageflags are not read back, so a changed choice starts at the default once. - Plain modified files only. A new or deleted file has an empty side, and a rename has two paths; the two-pane diff shows them better.
-
src/lib/views/changes/stagingDiffs.test.ts: which files fit, the flipped work tree side and the index's line ending (indexEol). -
threeSplit.test.ts(UI library) andsettingsData.test.ts: the panes' boundaries, their minimum and the saved split. -
src/lib/diff/threeWay.test.ts: the staging colors, no conflicts, and staging and unstaging withtakeChange.
The three panes had no Open File button.
- The issue: the two-pane diff has Open File, but switching to three panes (staging or a merge commit) lost it.
-
Why it happened:
ThreePaneDiffbuilds its own toolbar, and Open File lived only inDiffView. -
The fix and why we chose it: the open code moved to
openWorkingFile.ts, which both views call, so the check for a missing file and the toast stay the same in both.
The Changes tab had no Three panes button.
- The issue: the diff in the Changes tab (status bar > N changes) showed only Side by side and Inline, so a modified file could not open in the staging view there.
-
Why it happened: only
ChangesDiff.sveltepassedonThreePanesand drewThreePaneDiff; the tab compares with HEAD in its own view. -
The fix and why we chose it: the tab uses the same
stagingViewFits,StagingDiffsandchangesDiffPanessetting, so both places look and switch alike. Its Stage and Unstage write the index pane's text withstageContent, in the index's own line ending (indexEol), one click after another.
The three-pane toolbar drifted from the two-pane one.
- The issue: three panes had no Blame, no divider after the counter and an extra change count, and the Collapse tooltip differed.
-
Why it happened:
ThreePaneDiffdrew its own toolbar, a copy ofDiffToolbarthat fixes did not reach. -
The fix and why we chose it:
DiffToolbartakes plain props instead of the two-pane editors, and both views draw it, so the buttons, order and spacing live in one place.
The three panes could not be resized.
- The issue: the two-pane diff lets you drag the line between its sides, but the three panes kept equal widths, so long lines were cut off in every pane.
-
Why it happened:
MergeFramegave each paneflex: 1and had no handle; onlyDiffFramehad aSplitHandle. -
The fix and why we chose it:
MergeFrameholds two boundaries (threePaneSplit) with aSplitHandleon each connector's outer edge, reusing the two-pane handle and its pointer math, so dragging, keys and double-click behave the same. Being in the library frame, it reaches the merge tool and merge commits too.
- Retake
diff-staging-view.pngwhen the view looks different.
These pages are generated from docs/wiki. To correct something, open a pull request against the source file; edits made here are overwritten.
Using Nibtree
- Getting Started
- Welcome Screen
- Workspaces
- Files Panel
- File Operations
- File Icons
- Rounded Panels
- Editor and Tabs
- Pin Reorder and Wrap Tabs
- Single Tab Title
- Split Editor
- New File and Unsaved Changes
- Unload Hidden Tabs
- Editing Code
- Code Appearance
- Markdown Editor
- Image and PDF Preview
- Search Everywhere
- Recent Files
- Navigation Bar
- Find and Replace
- Changes and Commits
- Sign In Prompts
- Commit Box Layout
- Repository Actions
- Commit Options
- Ignoring Files
- Diffs
- Inline Diffs
- Staging View
- Revert a Change
- Resolving Conflicts
- Merge Tool
- Blame
- History and Log
- Merge Commits
- Branches and Tags
- Branches Popup
- Remotes
- Stashes
- Shelf
- Git Menu
- Git Dialogs
- Interactive Rebase
- Worktrees
- AI Agents
- AI Tasks
- AI Usage
- Keep Computer Awake
- Submodules
- Git LFS
- GitHub
- Git Console
- Terminal
- Terminal Features
- Scripts
- Navigation
- Menus
- Settings
- Settings Search
- Color Themes
- Settings Terminal and Automation
- Settings Files
- Updates
- Status Bar and Help
- Notifications
- Memory Use
- Clear Cache
- MCP and CLI
- Git Mergetool
- Keyboard Shortcuts
- Keyboard Shortcuts Editor
- Troubleshooting
- Troubleshooting Git
Developers
- Developer Guide
- Architecture
- Project Layout
- Backend
- How Folder Watching Works
- Folder Watching Bugs We Fixed
- How Workspace Files Work
- How the Preview Scheme Works
- Backend Services
- How Terminal Features Work
- Terminal Bugs We Fixed
- Frontend
- How the Rich Markdown Editor Works
- Rich Markdown Bugs We Fixed
- Git Menu Links
- Menu Keys and Routing
- Frontend Modules
- Blame Bugs We Fixed
- Project Color And Title Bar
- One Diff View
- Diff Bugs We Fixed
- Merge Tool Layouts
- Merge Tool Bugs We Fixed
- Color Theme Bugs We Fixed
- How the Connected Diff Works
- Rounded Panels Bugs We Fixed
- Update Bugs We Fixed
- Status Bar Bugs We Fixed
- MCP and CLI Bugs We Fixed
- Repository Actions Bugs We Fixed
- How the Changes Tab Works
- CodeMirror Patterns
- How the Cursor Works
- How Editor Features Work
- How the Path Bar Works
- Commands and Events
- Settings Reference
- Commands Workspace and Changes
- Commands Branches and Remotes
- Commands History and Extras
- Commands App and Tools
- Testing
- Test Suites
- Debugging
- Debugging with MCP
- Measuring Setting Memory
- How Terminal Memory Is Measured
- How Markdown Diagrams Work
- Releases and CI
- Publishing the Wiki
- Versioning and Changelog
- Browser Version Packages
- Browser Release Checks
- Docker Image
- Platforms and Signing
- Windows Support
- Windows Bugs We Fixed
- Windows MCP and CLI
- Docs and Screenshots
- UI Library
- UI Library MCP
- UI Library Bugs
- UI Library Migration
- Contributing
How features work
- Welcome screen
- Workspaces and folders
- Files panel
- File operations
- File icons
- Editor and tabs
- Pin, reorder and wrap tabs
- Single tab title
- Split editor
- New File and unsaved changes
- Unload hidden tabs
- Editing code
- Code appearance
- Markdown editor
- Image and PDF preview
- Search Everywhere
- Recent Files
- Navigation Bar
- Find and replace
- Changes and commits
- Sign-in prompts
- Commit box layout
- Repository actions
- Commit options
- Ignoring files
- Diffs
- Inline diffs
- Staging view
- Revert a change
- Resolving conflicts
- Merge tool
- Blame
- History and log
- Merge commits
- Commit tabs
- Branches and tags
- Branches popup
- Remotes
- Stashes
- Shelf
- Git menu
- Git dialogs
- Interactive rebase
- Worktrees
- AI agents
- AI usage
- Keep computer awake
- AI tasks
- Submodules
- Git LFS
- GitHub account
- Git Console
- Back and Forward
- Integrated terminal
- Scripts tool window
- Menu bar
- Settings
- Search in Settings
- Color themes
- Rounded panels
- Updates and release channels
- Status bar and help links
- Notifications
- Memory readout and memory log
- Clear Cache
- MCP server and command line tool
- git mergetool mode