Repository navigation
How Remotes Work
Fetch, Pull and Push are the git actions that talk to the network. They run the real git command line, stream its progress into the header, and refresh the repository when they finish. For the user side, see Remotes.
Syncing with a server is where GUI Git clients most often go wrong. Credentials, SSH keys, hooks and signing all live in the user's setup; doing our own networking would mean rebuilding all of it. A slow push also needs feedback, so we show git's own progress lines as they arrive.
The actions live in src/lib/views/gitActions.ts: fetchRemote, fetchAll, pull, push, publishBranch, pushTags and syncRepo. Two places call them:
-
The Git menu (
menu/menuActions.ts) calls them without a repository, so they act on the active one. Its Pull... and Push... open dialogs that callpull_with_optionsandpush_with_options; see How the Git Menu Works. -
The repository rows in Changes pass their own
repoRoot: the Sync button, and the ... > Pull, Push submenu. See How Repository Actions Work.
Each action calls repoStore.runOp(label, work, successMessage, repoRoot). runOp wraps run, which sets repoStore.busy, clears repoStore.progress, shows an error toast on failure and refreshes that repository afterwards.
sequenceDiagram
participant H as gitActions
participant RS as repoStore
participant API as api.ts
participant CMD as commands/remote.rs
participant CLI as git/cli.rs
participant GIT as git process
H->>RS: runOp("Push", work)
RS->>API: push(repoPath, force)
API->>CMD: invoke push
CMD->>CLI: run_streaming(args, on_progress)
CLI->>GIT: spawn git push --progress
loop each progress line on stderr
GIT-->>CLI: text ending in CR or LF
CLI-->>CMD: on_progress(line)
CMD-->>RS: event git-progress with repoPath and line
RS->>RS: progress shown in the header busy pill
end
GIT-->>CLI: exit status
CMD-->>RS: OpOutcome or AppError
RS->>RS: refreshRepo, toast
-
fetch_allrunsgit fetch --progress --all --prune;fetchrunsgit fetch --progress, with--prunewhen asked, so git picks the branch's remote (ororigin). -
pullrunsgit pull --progress, plus--rebasefor Pull (Rebase). Its result goes throughoutcome(), so a pull that stops on conflicts returnsOpOutcome { conflicts: true }instead of an error.runOpthen shows a "stopped with conflicts" toast, makes the repository active and opens the Conflicts dialog. A plain pull adds no--rebaseor--ffflag, so the user'spull.rebaseandpull.ffconfig decide. -
pushreads the current branch first. A detached HEAD is refused with a clear message. When the branch has no upstream,default_remotepicksorigin, or the first remote, and adds-u <remote> <branch>; that is also Publish Branch. Force Push asks first and then adds--force-with-lease, never a plain--force. -
push_tagsrunsgit push --progress --tags, naming the default remote when the branch has no upstream (git would fall back toorigin, which may not exist).
syncRepo chains them: pull when behind, count ahead again, push when ahead, then one toast such as "Pulled 2 commits and pushed 1".
flowchart TD
A["push(repoPath, force)"] --> B{"HEAD on a branch?"}
B -->|"no"| E1["Error: Cannot push a detached HEAD"]
B -->|"yes"| C{"force?"}
C -->|"yes"| D["add --force-with-lease"]
C -->|"no"| F{"Has an upstream?"}
D --> F
F -->|"yes"| G["git push --progress"]
F -->|"no"| H{"Any remote?"}
H -->|"no"| E2["Error: This repository has no remote to push to"]
H -->|"origin or first"| I["git push --progress -u remote branch"]
cli::run_streaming reads git's stderr in small chunks. Git redraws its progress line with a carriage return, so every \r or \n ends a line and calls on_progress. Only lines ending in \n are kept in the error text, so a failure is not buried under "Receiving objects" redraws. Stdout is read on its own thread so neither pipe can block git.
Each command is a thin wrapper around a run_* function (run_fetch, run_pull, run_push, run_pull_with_options...) that takes an OnProgress callback. The Tauri commands pass emitter, which turns each line into a git-progress event with repoPath and line; the MCP tools pass a quiet one (see How MCP and CLI Work). onGitProgress in api.ts listens, and repoStore keeps the line only when it belongs to the active repository. The header's busy pill shows it; for another repository it shows the label, such as "Fetch...".
Every git command, not only remote ones, is built by cli::command:
-
The git binary is the first of
/opt/homebrew/bin/git,/usr/local/bin/gitand/usr/bin/gitthat exists, elsegit. -
PATHcomes from the login shell: an app started from Finder gets a minimalPATH, which breaks hooks that need node.user_path()asks$SHELL -l -conce, with a 3 second deadline. -
GIT_TERMINAL_PROMPT=0makes git fail instead of waiting for a password. Authentication relies on the credential helper (for example the macOS keychain) or ssh-agent. -
GIT_EDITOR=trueandGIT_SEQUENCE_EDITOR=truestop git from opening an editor. -
stdin is closed unless
run_with_stdinpasses text (commit messages, hunk staging), so nothing hangs. -
Every run is recorded for the Git Console when that setting is on (
git_console::record); see How the Git Console Works.
The arrows in the header, the status bar and the Sync button come from the status call (head_info in git/status.rs) and, per branch, from refs::read. Both use git2's graph_ahead_behind against the upstream. They update after fetch because run refreshes the repository.
| File | What it does |
|---|---|
src-tauri/src/commands/remote.rs |
fetch, fetch_all, pull, push, push_tags, default_remote, the run_* functions and the git-progress event |
src-tauri/src/git/cli.rs |
command, run, run_streaming, user_path, git binary lookup |
src-tauri/src/commands/mod.rs |
OpOutcome, outcome, run_op, blocking
|
src/lib/api.ts |
fetch, fetchAll, pull, push, pushTags, onGitProgress
|
src/lib/stores/repo.svelte.ts |
run, runOp, busy, progress
|
src/lib/views/gitActions.ts |
The shared actions, the force push confirm, syncRepo
|
src/lib/views/changes/sync.ts |
Sync plans and their texts |
src/lib/views/Header.svelte |
The busy text, through the library's AppHeader
|
src/lib/ui/toast.svelte.ts, src/lib/ui/Toasts.svelte
|
The success, info and error toasts that run and runOp show |
Use the git CLI, not libgit2, for the network. git2 would need its own credential callbacks, SSH setup and hook runner, and would still miss user config.
Fail fast on prompts. A missing credential gives a clear error toast instead of a frozen app. An in-app password dialog would mean handling secrets, which we avoid.
--force-with-lease behind a confirm. A force push is its own item, asks in a danger dialog, and the lease refuses to overwrite commits you have not seen.
No network buttons in the header. The header had Fetch, Pull, Push and Stash for the active repository only. The Git menu now covers that case and each repository row covers the others, so the buttons were removed.
Conflicts are an outcome, not an error. Pull, merge and rebase can stop halfway on purpose. Returning OpOutcome lets the UI guide the user to the conflicts instead of showing a red failure toast.
None yet.
-
src-tauri/src/git/tests.rs:run_streaming_push_and_fetch_against_local_remotepushes and fetches against a local bare remote and checks that progress lines arrive;run_streaming_failure_carries_git_messagechecks that git's error reaches the user;run_reports_failure_and_stdin_is_passed;status_head_ahead_and_behind_upstream. -
src-tauri/src/commands/remote.rs:pull_merges_or_rebases_as_asked,pull_rebase_reports_conflicts,fetch_with_prune_drops_deleted_remote_branches,fetch_all_remotes_reads_every_remote,push_tags_sends_local_tags,push_tags_without_upstream_uses_the_only_remote,push_publishes_a_branch_without_upstream, plus the Pull and Push dialog tests. -
src/lib/views/changes/sync.test.ts: sync plans, tooltips and the toast text.
Remote tests use a local bare repository from test_support.rs, so no network is needed.
- Update this page when
commands/remote.rsor the environment ingit/cli.rschanges. - Update Remotes and Troubleshooting when errors or buttons change.
- Retake
git-progress.pngwhen the progress pill changes, andremotes-pull-push-menu.pngwhen the Pull, Push submenu changes. - Related: Backend, How Branches and Tags Work.
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