Repository navigation
How Git LFS Works
Nibtree detects Git LFS use per repository, tags LFS files in the Changes sidebar and the Files panel, shows sizes instead of pointer text in diffs, and runs git lfs commands through the git CLI. For the user side, see Git LFS.
With LFS, git stores a pointer file for each large file:
version https://git-lfs.github.com/spec/v1
oid sha256:4d7a2146...
size 12345
A diff of two pointers is three lines of noise, and when git-lfs is not installed the work tree holds only pointers, which looks like broken files. The app must say what is going on, and give the few LFS commands people need, without slowing down repositories that do not use LFS.
git/lfs.rs builds an LfsStatus { version, used, patterns, files }:
-
used: some attributes file routes paths through LFS (filter=lfs). It reads the root.gitattributes,.git/info/attributesand every tracked*/.gitattributesfrom the index, with plain file reads. -
patterns: the LFS patterns of the root.gitattributes, which is what Track and Untrack change. -
version: the output ofgit lfs version, orNonewhengit-lfsis missing or was not checked yet. The answer is kept in memory (InstallCache) for the app run, and it is only asked for when the repository uses LFS, so a repository without LFS costs no git process. WithcheckInstall(requireLfs, before an LFS action) it asks again whilegit-lfsis not known to be installed, since you may have installed it since. -
files:git lfs ls-files --name-only, only when it is installed and used.
sequenceDiagram
participant RS as RepoSection / FileExplorer
participant LS as lfsStore
participant CMD as lfs_status
participant Git as git CLI
RS->>LS: follow(repoRoot, status object)
alt same status object as last time
LS-->>RS: nothing to do
else status changed
LS->>CMD: lfsStatus(repoRoot)
CMD->>CMD: read attributes files
opt used or checkInstall, and not known yet
CMD->>Git: git lfs version (kept for the app run)
end
opt installed and used
CMD->>Git: git lfs ls-files --name-only
end
CMD-->>LS: LfsStatus
LS->>LS: statuses[repoRoot] = status
opt used and not installed
LS->>LS: offerInstall (once ever)
end
end
lfsStore.follow is called from the effects of each on-screen repository section and of the Files panel. It reads again only when the repository's status object was replaced (one read per refresh), runs at most one read per repository at a time and queues one more, and retain forgets repositories that left the workspace. lfsFileSet caches the file set per status object in a WeakMap, so the badges do not rebuild a Set per row.
git/diff.rs checks both sides with lfs::parse_pointer: at most 1024 bytes, a version line with the LFS spec URL (or the old hawser one), an oid sha256:<64 hex> and a size. When either side is a pointer, the FileDiff gets lfs: { originalSize, modifiedSize, originalOid, modifiedOid }; a side that is not a pointer (the real file in the work tree) gives its byte length. DiffView.svelte then shows "Stored in Git LFS" and lfsSizeText: 1.5 KB -> 2 MB, Added: 2 MB, Deleted: 1.5 KB, or Size: 10 B, plus ", same content" when both object ids match.
| Command | git |
|---|---|
lfs_status |
as above |
lfs_track(pattern) |
lfs track <pattern> |
lfs_untrack(pattern) |
lfs untrack <pattern> |
lfs_transfer(pull) |
lfs pull or lfs fetch, progress as "git-progress" |
lfs_prune |
lfs prune |
lfs_install |
lfs install --local |
Patterns are trimmed and refused when empty, starting with - or containing a line break.
lfsActions.ts holds the actions for Git > LFS and the LFS submenu that repoExtras.ts adds to a repository row's ... menu. Every action first calls requireLfs, which refreshes the status and, without git-lfs, shows "Git LFS Is Not Installed" with Get Git LFS (opens https://git-lfs.com) instead of running. Track uses a prompt validated by validateLfsPattern; Untrack picks from patterns; Prune asks with a danger confirm.
The one-time notice is separate: when a status says used but not installed, offerInstall waits until no other dialog is open, shows "Git LFS Is Not Installed" with Get Git LFS and Not Now, and remembers it in localStorage (nibtree.lfs.installNoticeShown), so it shows once ever per install.
stateDiagram-v2
[*] --> Unknown
Unknown --> NotUsed: used false
Unknown --> Ready: used, version set
Unknown --> Missing: used, no version
Missing --> Notice: notice not shown yet
Notice --> Missing: Get Git LFS or Not Now
Missing --> Ready: git-lfs installed, next LFS action
| File | What it does |
|---|---|
src-tauri/src/git/lfs.rs |
Pointer parser, attribute patterns, LfsStatus, track, untrack, transfer, prune, install |
src-tauri/src/commands/lfs.rs |
The six lfs_* commands |
src-tauri/src/git/diff.rs |
lfs_diff on FileDiff
|
src/lib/views/git/lfs/lfsStore.svelte.ts |
Per-repository status, follow, retain, the install notice |
src/lib/views/git/lfs/lfsActions.ts |
Menu items and actions |
src/lib/views/git/lfs/lfsModel.ts |
Sizes, diff text, pattern check, file set, needsLfsInstall
|
src/lib/diff/DiffView.svelte |
The "Stored in Git LFS" message |
src/lib/views/changes/RepoSection.svelte, views/files/explorerLook.ts
|
The LFS badges |
src/lib/menu/menuSpec.ts, menuActions.ts
|
Git > LFS |
Use git-lfs, do not reimplement it. The pointer parser is only for display. Every transfer goes through the real tool, so the LFS server, credentials and hooks behave like the terminal.
Read per refresh, not per file. One lfs_status per status refresh of an on-screen repository keeps the badges right without a process per row. It reads plain files; ls-files only runs when the repository uses LFS, and git lfs version at most once per app run.
Sizes instead of pointer text. The size change is the useful information; the object id says whether content changed.
Ask once. Many people open a repository that uses LFS without needing the large files. A single notice is helpful; a notice per repository or per launch would be noise.
install --local. Install Hooks changes only this repository, never the user's global git config.
-
src-tauri/src/git/lfs.rs:detects_lfs_pointers(valid, extended and legacy pointers; text, bad sizes, bad ids and big files refused);reads_lfs_patterns_from_attributes;status_finds_lfs_use_in_nested_attributes_without_git_lfs(no install check without LFS, one with it);install_check_runs_once_and_again_only_on_request_while_missing;tracks_and_untracks_patterns_with_git_lfs(skips whengit-lfsis not installed);diffs_of_pointer_files_carry_the_lfs_sizes. -
src/lib/views/git/lfs/lfsModel.test.ts: sizes, the diff text, pattern checks, file lookups andneedsLfsInstall.
- Update this page when
git/lfs.rs,commands/lfs.rsorsrc/lib/views/git/lfs/change. - Update Git LFS for any change to the menus, the badges or the diff message, and retake
lfs-diff.pngandlfs-not-installed.png. - The commands are listed in Commands and Events; diffs in How Diffs Work.
git lfs version ran for every repository on every refresh.
-
The issue: each status refresh started a
git lfs versionprocess per repository on screen, even without LFS. -
Why it happened:
lfs::statusalways asked for the version, though only repositories that use LFS need it. -
The fix and why we chose it: the version is asked for only when the repository uses LFS or an LFS action is about to run, and kept in memory for the app run. A missing
git-lfsis asked again before an action, so installing it while the app runs still works without a restart.
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