megit-app
v0.12.2
Published
Git repository viewer in the browser: commit graph with branch lanes, diffs, stashes and WIP
Downloads
869
Maintainers
Readme
megit
A git repository viewer that runs in your browser. Commit graph with real branch lanes, diffs including your uncommitted work, stashes, staging, search, and a shell — pointed at as many local repos as you like, each in its own tab.
It writes, too: stage/unstage/discard, commit and amend, branch and tag create/delete, stash push/pop/drop, checkout (with auto-stash when the worktree is dirty), plus revert, reset, cherry-pick, merge, rebase, pull and push.

Getting started
Requires Node ≥ 22. The package is 1.8 MB, with ws as its only runtime dependency and no install scripts.
npx megit-appThat starts the server on port 3411 — set PORT to change that — and opens your browser at it. CtrlC stops it.
To keep it running after you close the terminal:
npx megit-app start
npx megit-app stopstart restarts a server that is already running, so a fresh version takes effect.
Or install it once with npm i -g megit-app and run megit, megit start and megit stop from anywhere; upgrade later with npm i -g megit-app@latest.
The package is megit-app; megit on npm is unrelated, so keep the -app. The installed command is still megit.
Features
Commit graph with lanes
Branches get their own colour and lane. The checked-out branch's path is drawn thicker so you can follow it at a glance, and merges bulge around the lanes they cross instead of cutting through them. Commits page in 200 at a time and load more on demand, so opening a repository never waits on the full history.
Ref chips sit in their own resizable column to the left — local branches, remotes, and tags — and the graph column and message column can be dragged to whatever widths suit the repo.
Light and dark themes
Toggle with the switch in the toolbar or ⌘⇧0. The choice persists.

Uncommitted work is part of the graph
A sticky WIP row sits at the top of the list whenever the worktree is dirty, connected into HEAD like any other node. Click it to stage, unstage, or discard individual files, write a message, and commit — staged and unstaged changes are separate collapsible sections with counts.

Stashes appear as their own rows, attached to the commit they were taken from with a dotted connector, and can be popped, deleted, or retitled in place.
Diffs
Click a commit to see its changed files, then a file to see the diff. Syntax highlighting, word-level intra-line highlighting, collapsed context you can expand a hunk at a time, and a per-file "Viewed" checkbox.
Unified:

Split, side by side:

Merge commits diff against their first parent. Images diff visually rather than as binary noise. Untracked files diff too, via git diff --no-index.
Commit detail
Author and committer are shown separately when they differ — including the dates, which is the bit most tools hide. The changed-file list toggles between a flat path list and a directory tree.

Search
⌘F opens a find bar that filters the rows already loaded, as you type — matching commit message, author name, email, hash prefix, or ref name. That costs no request and can't go stale when the graph refreshes underneath you. ↵ and ⇧↵ walk the matches, wrapping at the ends the way a find bar should.

If what you want is further back than the rows you've loaded, the globe button re-runs the same query as a full-history git log search. Because git ANDs its commit-limiting options, "message OR author OR hash" is three searches unioned into one date-ordered list, capped at 500 results — the counter shows 12 of 340 · all, and 340+ when the cap truncated it. A match below the loaded window pulls the graph down to it.
A real shell, in the repo
⌘J opens a full PTY already cd'd into the active repository — your shell, your prompt, your aliases. It survives panel hides and tab switches, and ⌘K clears it. xterm.js is lazy-loaded, so it costs nothing until you open it.

Git operations
megit started as a viewer, but the common operations are here:
- Toolbar — pull (fast-forward only), push, create branch, stash all, pop latest stash, undo last commit (soft reset, keeps changes staged)
- Ref chips — checkout, create branch here, rename, delete, set upstream, merge, rebase, delete tag, copy name, copy GitHub link
- Commit rows — checkout, cherry-pick, revert, reset (soft / mixed / hard), copy hash, copy GitHub link
- Files — stage, unstage, discard, commit, amend the last commit (message, staged changes, or both)
Checkout auto-stashes a dirty worktree first. Destructive items are marked as such and are hidden where they'd be meaningless.
Auto-refresh
The server watches each open repository (fs.watch, filtered and debounced) and pushes changes to the browser over SSE. Commit in your terminal and the graph updates within about a second.
Auto-refresh only ever reads local git, so commits pushed by someone else stay invisible until something fetches. r and the ⟳ button therefore fetch from the remote first, then refresh — that is the one path that surfaces new upstream commits and refreshes the Pull/Push badges. A fetch that fails (offline, no remote) is ignored and the local refresh still happens.
Settings
The cog at the right end of the tab bar opens font (bundled Ubuntu Mono, your system monospace, or your system UI face), text size in four steps from 80% to 140%, the theme, the default diff view, how many commits each graph request loads (100–1000, default 150), and a toggle for author avatars — off stops the GitHub and Gravatar lookups megit otherwise makes per commit author. The same dialog lists every keyboard shortcut below, and the two mouse gestures: double-click a branch chip to check it out, right-click a row for its actions.
Keyboard
| Key | Action | | -------------------------------------------------------- | ---------------------------------- | | ↑ ↓ Home End | move through rows | | ⌘F | search commits | | ↵ / ⇧↵ | next / previous match | | r | fetch from remote, then refresh | | ⌘J | toggle terminal | | ⌘K | clear terminal | | ⌘D | split terminal pane | | ⌘⇧0 | toggle theme | | ⌘↵ | commit | | Esc | close search or menu / cancel edit |
Platform support
| Platform | Status | | -------------------- | -------------------------------------------------- | | macOS (arm64, x64) | full | | Windows (arm64, x64) | untested on real hardware; auto-refresh unverified | | Linux | everything except the built-in terminal |
The terminal needs node-pty, which ships prebuilt binaries for macOS and Windows only. It is an optionalDependency: on Linux the install either compiles it from source (needs python3 and a C++ toolchain) or skips it, and megit hides the terminal button. Nothing else is affected.
On Windows, the watcher integration tests crash the test worker outright, so they are skipped there and auto-refresh is not exercised by CI. Everything else in the suite runs. If you use megit on Windows, please report whether the graph updates on its own after a commit — that is the part we cannot currently verify.
Configuration
The list of open repositories lives in ~/.config/megit/config.json. Repositories are only reachable through the API if they are registered there, so pointing megit at a repo is always an explicit act.
Development
Requires Node ≥ 24 and pnpm — a development-only floor, since the server runs its TypeScript unbuilt (the published package ships compiled JS and only needs Node ≥ 22).
pnpm install
pnpm dev # API on :4500 + Vite dev server on :4000Production build:
pnpm build # vite → dist/
pnpm build:server # tsc → dist-server/ (only needed for publishing)
pnpm start # serves dist/ + API on http://127.0.0.1:4500Ports come from PORT (API, default 3411) and UI_PORT (Vite dev server, default 5173); the dev/start scripts pin 4500/4000.
In development the server runs its TypeScript directly via Node's native type-stripping — no build step. That does not work for a published package, because Node refuses to strip types under node_modules, so pnpm build:server compiles server/ to dist-server/ at publish time.
scripts/make-test-repo.sh generates test-repo/ — a throwaway fixture with interleaved branches, merges, stashes and a dirty worktree, used for manual testing and for the screenshots above.
docs/architecture.md explains how the two halves fit together and why the graph layout lives in a pure module. CONTRIBUTING.md has the setup and the house rules; SECURITY.md has the threat model.
pnpm test # vitest — parsers, lane layout, watcher, menus
npx tsc --noEmit # typecheckLicense
MIT
