UI Enhancements

dsh-sidebar-git-graph

AFunDog/dsh-sidebar-git-graph · ★ 2

AFunDog

DSH right-sidebar page: a read-only Git commit graph (VS Code style branch/merge lanes). Works with or without dsh-better-sidebar.

License
MIT
Added
2026-10-30
Maintenance
active
Install kind
github
Manifest status
Declared
Manifest path
package.json#dsh
Manifest checked
2026-10-30T09:38:20.232Z
Lifecycle scripts
None found

Static manifest checks report declarations only; they are not compatibility or security review.

安装

dsh plugin --profile web add 'github:AFunDog/dsh-sidebar-git-graph#4dfefb3cc465be189fa1806f0bc9a33d30006a95'

GitHub 源码安装:首次需按提示配置 allowBuilds 构建授权后重试

For install and environment setup, plus plugin development tutorials, see DSH community docs ↗

README

Source README · body: en · Read Chinese README

Table of contents
  1. Features
  2. Install
  3. With or without dsh-better-sidebar
  4. Settings
  5. Limitations
  6. Development
  7. License

ci license

A right-sidebar page for DSH (DeepSeek Harness) that draws the current session's repository as a branches-and-merges commit graph — VS Code style: colored lanes, bezier curves where branches split and merge, ref chips, per-commit details — with a working-tree Changes area above it, for every working tree of the repository.

Read-only. It never runs checkout, commit, reset, rebase, fetch, push, add, restore or worktree add/remove/prune. The only git commands it runs are rev-parse, status, for-each-ref, log, diff and worktree list.

The Git graph page: colored lanes, ref chips, commit rows, with the working-tree Changes area above

The same page in the dark theme: lane colors are derived from theme tokens

中文说明见 README.md。Contributing / internals / the full trap list live in AGENTS.md.

Features

  • Working-tree changes, VS Code style — a Changes and a Staged Changes section above the graph (plus Merge Changes during a merge), each row carrying its status letter (M/A/D/R/C/U) and its added/deleted line counts. Click a file to expand its diff inline; the sections fold and remember it.
  • Branch/merge relationships at a glance — one lane per concurrent line of history, curved connectors where a branch forks or merges back, thicker dots on merge commits, and the HEAD lane emphasized.
  • Ref chips per commit — local branch / remote branch / tag, with the current branch filled in the theme's brand color and the theme's matching on-brand foreground.
  • One workspace, several repositories — a workspace often holds more than one repository (this project's own checkout has a nested one under vendor/@zeng/). The header then grows a picker listing every repository it found; the choice is remembered per workspace. A workspace that is itself only a subdirectory of a repository works too — the enclosing repository is offered as well. Discovery never leaves the workspace: it scans downwards, bounded by depth, directory count, repository count and a time budget.
  • Every working tree of the repository — a repository can have several (git worktree add), each on its own branch with its own uncommitted changes, and the linked ones usually live outside the workspace directory entirely (in the case this feature was built for, they are sibling directories). The picker lists all of them, each labelled with its own branch, so you can switch between the main working tree and a linked one and see that working tree's graph and changes. The main working tree, detached heads and locked working trees are marked; working trees whose directory is gone are skipped and counted in a notice rather than silently dropped. Because the list comes from git worktree list, it works from any of them — standing in a linked working tree also offers you the main one.
  • Header at a glance — repository name, current branch, ↑ahead ↓behind, dirty-file count.
  • Tells you when its picture of the remote is behind — the graph is drawn from your local repository and this page never touches the network that is the read-only promise. So a merge that just happened on the remote is invisible here until someone runs git fetch. Rather than leave you wondering whether the graph is wrong, the page says how long ago the repository last fetched (past 6 hours), and which local branches are behind their upstream. A repository that has never been fetched is reported as having no remote instead of as stale.
  • Follows the session's worktree label — when @zeng/dsh-session-worktree is installed and enabled, the page opens on the working tree that session is labelled with, instead of whatever repository the working directory happens to sit in. Which repository wins: your click in the picker → the session's worktree label → the last repository remembered for this workspace → the repository of the working directory → the first one found. The page says when it is following the label, and says so more loudly when it cannot (a label pointing at a directory that is gone, is not a repository root, or is outside what this page may read) — it never silently swaps in a different repository. Uninstalling or disabling that plugin restores the previous behaviour exactly.
  • Commit search — highlights matches and steps through them (the graph stays intact; it never re-lays-out a filtered list).
  • Details on click — including which files that commit touched. Full sha, author + email, absolute time, parents, participating refs; below that, the list of changed files with their status letter (A/M/D/R/C/T), the original name for renames, and per-file +/− counts. Click a file and its patch expands in place — the same rendering and the same feel as the Changes area. A merge commit is compared against its first parent and the heading says so (otherwise you would think the files were written by the merge itself); a binary file says "binary file, no line-by-line diff" when expanded instead of showing you an empty diff.
  • Virtualized list — a 2000-commit window still renders only the visible rows; nothing is requested while the page is not the visible tab.
  • Honest error states — not a git repository / no git on PATH / no workspace / trust fence refused / git failed each get their own message instead of a blank page.
  • Zero dependencies, no build step — the host half is plain ESM, the browser half is a single hand-written client-bundle file. What you read in lib/ is what runs.

Install

dsh plugin --profile web add github:AFunDog/dsh-sidebar-git-graph

Then restart dsh web (the host half adds a route) and hard-refresh the browser. Open the right sidebar and pick Git graph — from the + menu when dsh-better-sidebar is installed, or from the sidebar's guide page on a bare DSH (the + control is not drawn while that pane already holds the guide tab). The page follows the session you are viewing: switch sessions and it re-reads that session's workspace.

# pin a tag (recommended: lockfiles record the resolved commit)
dsh plugin --profile web add github:AFunDog/dsh-sidebar-git-graph#v0.2.0

# from a local clone (absolute path only — relative paths are rejected by spec parsing)
dsh plugin --profile web add /path/to/dsh-sidebar-git-graph

With or without dsh-better-sidebar

The page works both ways, and picks automatically:

Your setup What happens
dsh-better-sidebar installed The page registers through its public ctx.betterSidebar.registerTab service: it shows up in the + menu, gets a card in Settings → Side cards (with an on/off switch and a per-plugin setting for the commit window), and appears under Git graph.
Bare DSH The page registers with DSH's own sidebar tab type (ctx.sidebarRightTabs + the sidebar.right.pane.tab slot) — same column, same + menu, minus the settings card.

Settings

Setting Where Default Meaning
Commits loaded per read Settings → Side cards → Git graph → feature settings (dsh-better-sidebar only) 400 100–2000. Bigger is more complete and slower on first read.
Repository scan depth same place 5 0–8. How deep to look for repositories to offer in the picker; 0 means "no scan — only the repository the working directory is in". Lower it if a huge workspace reports an incomplete list.
History scope page header all branches All branches or current branch only.
Commit search page header — Highlights matches and steps through them.

Limitations

  • Read-only by design. No staging, no commit, no checkout, no push. To change the working tree, use another tool.
  • Untracked files carry no +/− counts. git diff --no-index compares exactly two paths, so per-file counts would mean one process per file — hundreds of them in a repository that has not been added yet. The section header counts what it can and leaves the rest blank rather than printing a 0 that would read as "nothing changed". A file changed on both sides shows up in both sections, which is what the two sections are for.
  • A nested repository is one non-clickable row. git does not descend into a repository inside a repository (even with -uall), so the outer repository sees a single directory and there is no diff to show for it. The page says so on the row instead of doing nothing when clicked.
  • At most 3000 rows, and at most 512 KiB of patch per file; both are reported in the page rather than silently cut.
  • The window is bounded (up to 2000 commits per read, default 400); older history is reported as truncated rather than silently dropped.
  • Repository discovery is bounded, and it scans the filesystem. Depth, directory count, repository count and elapsed time are all capped; hitting a cap is reported in the page instead of being hidden, and lowering the scan depth is the fix. Directories that never hold repositories of interest (node_modules, target, .venv, build output, …) are not descended into — though each of them is still stat'd once, so a repository that happens to be named build is still found.
  • The picker only offers what is reachable from the workspace — the workspace itself, something inside it, the repository that encloses it, or a working tree of the repository the workspace is in (as reported by git worktree list). The host refuses any other path even if the browser asks for it, and re-checks with rev-parse that the path really is a repository root.
  • A deleted working directory is skipped instead of being fatal. The path held by the session/tab may be a linked working tree that has already been removed (deleting them once the work is done is the normal case). The host walks on to the first candidate that still exists and says which directory is gone and where it drew instead — rather than answering with nothing but 工作目录不存在.
  • A working tree is read on its own. git status and git log are per working tree, so each one shows its own branch, its own uncommitted changes and its own HEAD; the page never merges two working trees into one view. Working trees that are gone (prunable) and bare repositories are listed as a count in a notice rather than offered.
  • A commit's file list is capped at 2000 files (an initial commit in a large repository can touch tens of thousands); past that the page says "N in total, first M listed" rather than quietly listing fewer. A merge commit lists what changed against its first parent (the same convention VS Code uses), and an empty commit says so.
  • Search does not filter the graph — filtering would break lane continuity, which is the whole point of the page; matches are highlighted and stepped through instead.
  • Lane colors are derived from DSH theme tokens with color-mix(). On a browser without color-mix(), lanes fall back to the four theme state colors in rotation.
  • Verified on DSH 0.1.7-rc.2 with dsh-better-sidebar 0.21.1; the native fallback path is covered by the same tests but has had less real-world use.
  • The npm name dsh-git-graph belongs to a different plugin by another author (enoughpower/dsh-git-graph). This one is @zeng/dsh-sidebar-git-graph and is currently distributed from GitHub only.

Development

No build step: edit lib/, restart dsh web for host-half changes, hard-refresh for browser-half changes (a changed client bundle is picked up with a reload — its rev is re-derived from the file's mtime).

npm test        # = node scripts/test.mjs: node --check on every lib file, then every test

The architecture map, the lane-layout invariant, the theme-token checks and the implementer traps live in AGENTS.md.

License

MIT — see LICENSE.

中文说明