This guide covers source builds, everyday use, optional configuration, and development.
For supported platforms and installation with Homebrew or mise, see the README.
Install Zig 0.16.0 and Git 2.45.1 or newer on Linux or macOS. Download Source code (tar.gz) or Source code (zip) from the latest release, extract the archive, and open a terminal in the extracted directory.
Choose one of the following builds.
zig build -Doptimize=ReleaseSafeTree-sitter syntax highlighting is enabled by default. Zig downloads and builds the required dependencies automatically; a separate Tree-sitter installation is not needed.
zig build -Doptimize=ReleaseSafe -Dsyntax-provider=noneChoose this build for a smaller binary and shorter compilation times, especially when building from scratch. It omits Tree-sitter and its language parsers. Syntax highlighting in diffs and source views is disabled; added and removed lines still use their diff colors.
Both builds produce zig-out/bin/gitframe. Check it and install it into your
local binary directory:
./zig-out/bin/gitframe --version
mkdir -p "$HOME/.local/bin"
install -m 755 zig-out/bin/gitframe "$HOME/.local/bin/gitframe"If $HOME/.local/bin is not on your PATH, add this line to your shell's
startup file, such as ~/.zshrc or ~/.bashrc, and open a new terminal:
export PATH="$HOME/.local/bin:$PATH"After installing, follow the README's installation check.
cd /path/to/your/repository
gitframeGitFrame opens Changes with staged and unstaged changes. It uses built-in defaults, and Changes refreshes automatically every three seconds.
Press ? for the current view's controls, or q to quit from a main view.
Prompts and dialogs show their own controls; Esc closes or cancels the
current interaction.
- Select a file with
j/kor the arrow keys to inspect its diff. - Press
uto switch between unified and side-by-side diffs. UseTabto move focus between the file tree and the diff. - With the file tree focused, press
Spaceto stage or unstage a file. With the diff focused,Spaceacts on the current hunk. - Press
cto open the commit panel. Enter a subject and, optionally, useTabto move to the message body. PressCtrl+sorCtrl+Enterto commit the staged changes. PlainEnteredits the message; it does not submit. - Press
Pto push. Check the branch and remote shown in the confirmation before proceeding.
Stage and unstage change the index, which holds the content for your next commit. They do not discard your working files. See Changes for other Git operations.
Use f to find a file and / to search the displayed diff or source. In
History, / searches the diff, not commit messages. With a text search
active, n moves to the next match and N to the previous one.
With the diff or source pane focused, press V to begin a line selection,
extend it with j / k or the up/down arrows, and press y to copy it.
Press Esc to clear the selection. You can also drag across text with the
mouse, release the button, and use y to copy the completed selection.
In Repository, Y copies selected code together with its repository, file,
and line information. In History and Compare, this context copy is also
available for source selections in side-by-side mode and includes the
before/after commit IDs and the selected side. Unified diff selections use
diff text. Without a selection, Y in a diff pane copies the current hunk.
Press W to toggle line wrapping in the Changes, History, or Compare diff
view, or in Repository source text. Diff wrapping works in unified and
side-by-side layouts, including the unified layout used in narrow terminals.
Help (?) shows the effective key if you remap toggle_line_wrap.
Wrapping starts off. Each page keeps its own choice while its viewer is retained, including file changes, reloads, and switching away and back. A newly created viewer starts with wrapping off; Repository also resets it when switching repositories. The choice is not saved across restarts. With wrapping off, long lines are clipped and you can scroll horizontally with the left/right arrows.
With wrapping on, continuation rows repeat neither line numbers nor diff
markers. Use the mouse wheel, Page Up/Down, or Ctrl+u/d/b/f to read long
wrapped lines. j / k still move between original source rows. Diff search
moves by original row and reveals its first fragment; scroll down to read a
match farther along that row. Repository search retains each matching
occurrence, including multiple matches on one line, and reveals the matching
fragment. Selection and copying use the original text,
without inserting newlines at wrap boundaries.
Tabs retain their spacing across wrap boundaries. If a character cannot fit even on an empty row, a replacement glyph is shown so scrolling can continue; copying still preserves the original character. A pane with no room for text shows no fragments and restores them when widened.
Press R from any working view to open the repository picker. Select a
recent repository and press Enter, or press p to enter a directory path.
Use / to filter the list. In list mode, d removes an entry from recent
history; it does not delete the repository.
You can also open a directory containing multiple repositories. GitFrame looks for repositories in its immediate child directories and lets you choose one. This also works when starting GitFrame in that parent directory.
Press b to choose an existing local branch. GitFrame checks out the selected
branch, or opens its existing worktree if it is checked out elsewhere. This
changes the active working context. To compare against a branch without
checking it out, use Compare.
Run gitframe --help for all options. For manual refresh or no transition
animations:
gitframe --no-watch
gitframe --no-transitionPress r to refresh the current view manually. You can also open a patch file
or a commit-range diff:
gitframe ./change.patch
gitframe --range main...HEADThese open in Changes as read-only diff sources: stage, unstage, and discard are unavailable. Patch files can be opened outside a repository; commit ranges require a Git worktree. Specify one source per invocation.
| Key | View | What you are viewing |
|---|---|---|
1 |
Changes | Current working-tree and index changes |
2 |
Repository | Full source files in the current worktree |
3 |
History | A commit or a selected range of commits |
4 |
Compare | Committed changes from a Base branch's merge base to HEAD |
Changes is where you prepare your next commit. Stage or unstage individual
files and hunks using the basic workflow.
Space on a directory in the file tree acts on that directory's eligible
files. Binary files and some file states do not support hunk operations;
use the file-level action when available.
In the diff pane, J / K moves between hunks and Enter folds or unfolds
the current hunk. [ / ] moves between files. Press B to show or hide
the file tree, and < / > to adjust its width.
Press v to toggle a file's reviewed mark and H to hide or show reviewed
files. These marks track your review during the current session; they do
not stage files or create commits. F cycles the file filter. If files seem
to be missing, check both the filter and whether reviewed files are hidden.
Other operations are available from Changes. Push and pull are also available from Repository, using the same keys, confirmation, cancellation, and retry flow:
| Key | Operation | Behavior |
|---|---|---|
A |
Amend | Edit the last commit's message and include the current index; submission opens a confirmation because this rewrites history |
D |
Discard | Discard unstaged tracked changes in the selected file after confirmation; directories, untracked files, and conflicts are unsupported |
s |
Create stash | Choose all changes or staged changes and an optional message |
S |
Stashes | Inspect saved stashes and apply or drop a selected entry after confirmation |
P |
Push | Show the destination and confirm before pushing the current branch |
U |
Pull | Fetch the upstream remote, then fast-forward the current branch when it is behind |
Applying a stash keeps the saved entry and does not restore its original staged/unstaged classification. Dropping a stash removes that saved entry; it does not discard the current worktree's changes.
Pull requires an upstream and supports fast-forward only. Local changes, including staged changes and untracked files, are allowed when Git can preserve them. Git refuses updates that would overwrite local changes; GitFrame does not automatically stash them. If there are no incoming commits, Pull succeeds without changing the branch, even if it is ahead. Resolve divergence with Git before retrying. Remote operations use your existing Git authentication setup; see Troubleshooting if one fails.
Press P to push or U to pull the current repository's branch while browsing
either the file tree or source. These operate on the repository, not the selected
file, and do not require opening Changes first. Custom push/pull key bindings
apply on both pages. Search and selection modes retain their input ownership.
Confirmation, progress, and errors stay on the page where the action started. On both Changes and Repository, page switching is blocked while a confirmation is open, a push/pull is running (including cancellation), or an error dialog is open. Close the dialog and wait for the action to finish before switching pages. After push, branch synchronization information refreshes; after pull, the file tree, source content, and Changes data refresh when their page is displayed.
Repository shows full source files, including files with no changes. Switch
from Changes with 2 to open the selected file's current source when that
path is available. Switching back with 1 selects the corresponding file
in Changes when it is present there.
Use Tab to focus the tree or source, Enter to expand or collapse a
directory, and f to find a file. F switches between all files and changed
files. In the source pane, use / to search, [ / ] to move between files,
and L to toggle line numbers. Type :42 and press Enter to go to source line 42.
Press e to open the selected file in an external editor. GitFrame returns
to the view when that editor command exits. Editor discovery checks VISUAL,
then EDITOR, then nvim, vim, and vi on your PATH. See
Editor to choose an explicit command.
History shows commits, details for the selected commit, and its changed
files. Press Enter on a commit to open its diff within History. From the
diff, press m to select commits again.
To inspect a range, press Space on a commit to set an anchor, move to the
other endpoint, and press Enter. The highlighted span identifies the
selected commits. These ranges stay in History; they do not replace the
live Changes view.
Use Tab / Shift+Tab to move between panes. With commit details focused,
y copies the commit or range details. Diff navigation, selection, and
copying work as described in Basic usage.
Compare shows the difference from the merge base of the selected Base branch
and the current HEAD to HEAD, using the same comparison basis as
git diff BASE...HEAD.
Press m to choose the Base branch. This changes the comparison without
checking out that branch. Working-tree and index changes belong in Changes
and are not included here.
Use f to find a changed file, Tab to focus the file tree or diff, and u
to switch diff layouts. As in Changes, v marks a file reviewed and H
hides or shows reviewed files. Press r to refresh the comparison.
GitFrame works without a configuration file. To customize it, create
~/.config/gitframe/config.toml, or
$XDG_CONFIG_HOME/gitframe/config.toml if XDG_CONFIG_HOME is set. This path
is used on both Linux and macOS. Add only the sections you need and restart
GitFrame after editing.
These are the defaults:
[reload]
auto = true
interval_seconds = 3The interval accepts whole seconds from 1 to 60. Set auto = false for
manual refresh with r. The command-line options --watch and --no-watch
override auto for that invocation.
To open Neovim at the selected line:
[editor]
argv = ["nvim", "+{line}", "{path}"]For VS Code, use this section instead:
[editor]
argv = ["code", "--wait", "--goto", "{path}:{line}:{column}"]An explicit argv takes precedence over VISUAL and EDITOR. Arguments are
passed directly to the program, without a shell. {path} is required;
{line}, {column}, and {repo_root} are also supported. The editor runs
from the repository root. A GUI editor should wait until editing finishes
before its command exits, as --wait does in the VS Code example.
Recent repositories are stored separately in
~/.local/state/gitframe/state.json, or
$XDG_STATE_HOME/gitframe/state.json if XDG_STATE_HOME is set. This file is
managed by GitFrame; use the repository picker to remove recent entries.
Reviewed marks are not saved across application restarts.
See the installation requirements for supported platforms and the required Git version.
GitFrame checks Git before opening the TUI. An older or unavailable Git, or a
failed version check, produces a terminal message and exits with status 1.
gitframe --help and gitframe --version work without Git.
The OS and Git requirements are separate: Ubuntu 24.04's standard Git 2.43
needs an update. Follow the official Git instructions for Ubuntu,
which provide the ppa:git-core/ppa repository. On macOS with Homebrew, run
brew update and brew upgrade git. Check git --version again afterward;
if it still reports an older version, put the updated Git first on your PATH.
Run command -v gitframe to check which executable your shell can find.
For a source installation, check the PATH setup.
For mise, check that mise is activated in the current shell. For Homebrew,
follow its shell setup instructions. Open a new terminal after changing
your shell's startup file.
The startup error names the configuration file. Check the TOML syntax, setting names, and values against the examples above. Invalid configuration stops startup. To return to defaults, rename the file temporarily and launch GitFrame again.
Check that the editor executable is on your PATH, and that VISUAL,
EDITOR, or [editor].argv names the intended command. Use the explicit
argv form for arguments that contain spaces. Select an existing regular
file: directories, deleted files, and symbolic links cannot be opened by
this action.
Read the footer message or error dialog for the reason. Stage and discard
need live working-tree changes, and some file states support only
file-level actions. Press r if GitFrame reports that its view is stale.
For commit failures, check Git's reported error, including author identity,
hooks, or signing setup. For push and pull failures, check the remote,
upstream, and your Git credential helper or SSH agent. If a push error dialog
offers i for an interactive retry, use it to run the operation in the
foreground. Pull requires an upstream and supports fast-forward only.
Local changes are allowed when Git can preserve them. If Git refuses an
update because it would overwrite local changes or cannot fast-forward,
resolve the reported problem with Git before retrying.
For an unresolved problem, open a
GitHub issue with the output of
gitframe --version, your OS and terminal, the error message, and steps to
reproduce it.
Install Zig 0.16.0 and Git 2.45.1 or newer, then clone the repository:
git clone https://github.com/hiroaqii/gitframe.git
cd gitframe
zig build
zig build runZig fetches the dependencies pinned in build.zig.zon; no sibling checkouts
are required. The first build needs network access. The executable is
written to zig-out/bin/gitframe.
Pass application arguments after --:
zig build run -- --no-watchThe application starts in the command's working directory. Use R to open
another repository. For a smaller build without syntax highlighting, add
-Dsyntax-provider=none as described in
Without Tree-sitter.
Run tests and check formatting:
zig build test
zig fmt --check build.zig build.zig.zon srcTo test without the syntax provider, or focus on a matching test name:
zig build test -Dsyntax-provider=none
zig build test -Dsyntax-provider=none -Dtest-filter=parseArgs-Dtest-filter applies to the aggregate Zig test artifacts; filtered runs skip
the startup CLI checks. zig build check-tests compiles the aggregate test
artifacts without running them.
Additional dependency checks are available separately:
zig build test-chasen-ui-consumer
zig build check-flow-syntax -Dflow-syntax-check=truetest-syntax-provider runs the same package-root test artifact as
test-package-root, with syntax highlighting required. Use the aggregate
test step for the full application test suite.
Use zig build --help for additional build steps and options. Release
maintainers can find publishing instructions in
GitHub Releases.