Repository navigation
How Changes and Commits Work
The Changes sidebar lists what changed in every repository of the workspace (Conflicts, Staged and Changes), where you stage, unstage, discard and commit. The user side is in Changes and Commits. Repository row buttons are in How Repository Actions Work, the Commit arrow and gear in How Commit Options Work.
This is the daily loop of any git client, so it must be quick and predictable. It is grouped by repository, so one window manages many projects.
Two rules shape the code:
-
Every write goes through the git CLI (
git/cli.rs), so hooks, signing and config behave as in the terminal. Reads use git2. -
Every action names its repository: every mutation takes
repoRootfirst.
repoStore.statuses holds one RepoStatus per repository, loaded by get_status, which calls status::read (git2, untracked folders recursed, renames detected between HEAD and the index). buildSections in sections.ts turns them into one section per repository:
flowchart TD
F["FileStatus"] --> C{"conflicted?"}
C -->|yes| G1["Conflicts group"]
C -->|no| S{"staged kind?"}
S -->|yes| G2["Staged group"]
S --> U{"unstaged kind?"}
U -->|yes| G3["Changes group"]
G2 --> N["A partly staged file<br/>appears in both groups"]
G3 --> N
groupFiles and buildSection cache their results in a WeakMap keyed by the status object; a refresh replaces only the changed repository's status, so other sections are reused. splitSections moves clean repositories to CleanRepoList.svelte.
The selected row is a FileSelection (repoRoot, path, area) in changesSelection; picking one opens its diff (see How diffs work). After every refresh, resolveSelection keeps it valid: the same row, else the same file in the other area, else the nearest row.
Every action in mutations.ts, and commitRepo in repoActions.ts that the commit box calls, runs through repoStore.run, which sets the busy label, shows errors and refreshes that repository.
sequenceDiagram
participant Row as RepoSection or commitRepo
participant Store as repoStore.run
participant Api as api.ts
participant Cmd as commands/status.rs
participant Git as git CLI
Row->>Store: run(label, work, repoPath = repoRoot)
Store->>Api: stageFiles / unstageFiles / discardFiles / commit
Api->>Cmd: stage_files, unstage_files, discard_files, commit
Cmd->>Git: add -A, restore --staged, restore --worktree, commit -F -
Git-->>Cmd: output or error
Cmd-->>Store: result
Store->>Store: toast, refreshRepo(repoRoot)
-
stage_filesrunsgit add -A -- <paths>, so deletions are staged too. -
unstage_filesrunsgit restore --staged, orgit rm --cached -r -qin a repository with no commits yet.unstagePathsadds the old path of a staged rename. -
discard()asks first, thendiscard_filesrestores tracked files (git restore --worktree) and deletes untracked ones throughsafe_join. -
commitpasses the message on stdin withgit commit -F -, so multi-line messages arrive exactly as typed; amend with an empty message uses--no-edit.run_commitadds the Commit Options arguments, and--allforcommit_all. Turning on Amend fills an empty box fromget_head_message; turning it off clears that text again if unchanged.
There is one commit box. changesLayout.commitTargetRoot follows the repository you last worked in (focusRepo), and resolveCommitTarget falls back to the active repository, then the first. Each repository keeps its message and Amend flag in commitDraft.for(repoRoot), built on DraftBook.
RepoSection.svelte draws the repository header (name, submodule or worktree tag, change count, operation badge) with RepoActions.svelte on its right; with one repository, ChangesView.svelte shows RepoActions in the panel title instead. Stage All, Unstage All and Discard All live in the ... menu. Right-clicking the header opens that menu (repoMenuFor) plus Resolve Conflicts, Set as Active Repository (only when it is not active) and Copy Repository Path.
File row menus add Add to .gitignore (ignoreMenu, see How Ignoring Files Works) and Shelve Changes.... FileRow.svelte takes an optional badge (LFS, submodule) and note (a submodule's change).
Arrow keys move across all expanded sections, Space or Enter stages or unstages the selected row, and Delete discards an unstaged row; the list keeps the focus. Cmd+Enter in the message box commits.
| File | What it does |
|---|---|
src/lib/views/ChangesView.svelte |
The sidebar panel, keyboard handling, empty states |
src/lib/views/changes/RepoSection.svelte |
One repository: header, badges, groups, row menus |
src/lib/views/changes/RepoActions.svelte |
The repository buttons, see How Repository Actions Work |
src/lib/views/changes/FileRow.svelte |
One file row |
src/lib/views/changes/CleanRepoList.svelte |
Repositories with no changes |
src/lib/views/changes/CommitBox.svelte |
Amend, target picker, Commit and its arrow, Sync Changes |
src/lib/views/changes/CommitMessageField.svelte |
The message: history, templates, commit.template, subject note |
src/lib/views/changes/sections.ts |
Pure grouping, rows, selection and labels |
src/lib/views/changes/selection.svelte.ts, layout.svelte.ts
|
Selected change; collapse state and commit target |
src/lib/views/changes/mutations.ts |
Stage, unstage, discard |
src/lib/views/changes/commitDraft.svelte.ts, drafts.ts
|
Per-repository drafts |
src/lib/views/changes/fileStatus.ts |
Letters, unstagePaths, discardPaths, rowElementId
|
src-tauri/src/commands/status.rs |
Status, stage, unstage, discard, commit, commit_all
|
src-tauri/src/git/status.rs |
status::read and head_info
|
One commit box for all repositories. A box per section would make the layout jump on every stage, and text fields inside the list would clash with its arrow and Space keys.
Selecting a file does not change the active repository. Switching reloads branches and history, too heavy for every click. Set as active, opening a conflict and Resolve do switch it.
Repository actions on the row, not on hover. Hover-only buttons were hard to find and to reach from the keyboard. An always visible row shrinks through container queries instead.
Commit messages go through stdin. -F - keeps the text exact, with no quoting or length limits, and GIT_EDITOR=true in cli.rs means git never waits for an editor.
Confirm every discard. It cannot be undone, so the dialog names or counts the files, untracked ones included.
Staged renames showed the old name.
-
The issue: after
git mv old new, the list showed the old name and never the new one. -
Why it happened: git2's
StatusEntry::pathnames the old side of a rename. -
The fix and why we chose it: for a staged rename,
status::readtakespathfrom the new side andorig_pathfrom the old side ofhead_to_index(), which matches how the UI readspath.
Space on a header button also staged the selected row.
- The issue: pressing Space on a focused section button such as Stage All also staged or unstaged the selected file.
- Why it happened: the list's keyboard handler saw the same key press.
-
The fix and why we chose it: for Space, Enter and Delete,
ChangesView.svelteignores keys whose target is a button, select, input or textarea, so the focused control keeps its behavior.
Enter did not stage the selected file.
- The issue: after clicking a file in the Changes list, Enter did nothing. Only Space and double-click staged or unstaged it.
- Why it happened: clicking focuses the list, but Enter was only handled on the row itself, which never has the focus.
-
The fix and why we chose it:
ChangesView.sveltehandles Enter like Space, with the same rule that a focused button keeps its own behavior. Rows no longer take the focus on click, andaria-activedescendantpoints screen readers at the selected row. One keyboard owner means no key runs twice.
-
src-tauri/src/commands/tests.rs:stage_files_stages_modifications_and_deletions,unstage_files_in_unborn_and_normal_repo,discard_files_restores_tracked_and_deletes_untracked,commit_with_multiline_message_and_amendandget_status_and_file_diff_commands. -
src-tauri/src/git/tests.rs:status_reports_staged_unstaged_and_untracked_changesand thestatus_head_*tests. -
src/lib/views/changes/sections.test.ts(grouping, cache reuse, selection fallback, commit target),drafts.test.ts(DraftBook) andfileStatus.test.ts(rowElementId).
Add a Rust test with TestRepo for every new git command, and keep grouping or selection rules in sections.ts, tested. See Testing.
- Update this page when a stage, discard or commit command, grouping, selection, keys, row menus or the commit target change.
- Update Changes and Commits for visible changes.
- Retake
changes-sidebar.pngandcommit-box.png. See Docs and Screenshots.
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