LaoYueHanNi/dsh-git-worktree

LaoYueHanNi/dsh-git-worktree

在 Web 输入框切换分支、以真实工作区方式创建隔离的 git worktree。

Git 与代码评审

dsh-git-worktree

简体中文 | English

A dsh plugin for simple branch & worktree management in the Web UI. The composer tool row shows the current branch: pick another to switch in place, or flip the Worktree toggle to get an isolated worktree as a real workspace — as shown in the screenshot above.

[dsh]: https://github.com/cordiverse/dsh

Repo:

[!IMPORTANT]
GitHub direct installs have ended — the repository no longer carries prebuilt output. Install from npm instead:
```sh
dsh plugin --profile web add @laoyuehanni/dsh-git-worktree
`
Upgrading from a legacy `github:` install (≤ 0.3.2, package name `dsh-git-worktree`)? An in-place update fails to load — remove the old name first, then add again. Worktree folders and the plugin's settings carry over untouched.

Features

  • Commit graph: expand the branch popup in place to browse branches on the left and their real commit history and merge graph on the right. Browsing does not check out a branch. Select a commit to see its author, date, full message and file summary; bodies start with a three-line preview and expand into an internally scrolling area. History loads the next page when its list scrolls to the end, and a Load more button remains; search covers loaded titles and hashes. The author menu lists every author on the branch and keeps that list in memory for the commit; a branch update, fetch, or reload drops the cache.
  • IDEA-style branch picker: the branch menu treats / as a folder hierarchy — collapsible folders, last-segment labels, the checked-out branch's chain opens centered. Local and remote branches render as two collapsible groups (a single remote strips its prefix), with bottom search (ancestor folders kept, hits highlighted), locate-current-branch and expand/collapse tools, and green ↗N / blue ↙N divergence marks on tracking branches.
  • Remote branch checkout: pick origin/feat-x in the remote group and it checks out in place with a tracking branch directly (no confirmation); the worktree toggle sends the pick to an isolated worktree instead.
  • Row context menu: right-click a branch row for six verbs — check out (immediate, no confirmation), new branch, new branch and check out, rename branch (local rows), delete branch (local rows, the safe -d: git refuses unmerged commits and occupied branches), copy branch; a worktree row offers go-to-worktree, copy path, and remove worktree. Second-level dialogs open IN PLACE of the menu; in-place actions keep the menu open, so rows can be acted on in a run. Keyboard: arrow keys + Enter.
  • Worktree quick hop: the main checkout's blank-session menu groups branches held by live worktrees under Worktrees (hover shows the directory) — the row menu's "Go to this worktree" hops straight into that directory and starts a fresh session.
  • Branch switching: pick a branch — an in-place switch, no confirmation. Inside a linked worktree the entry scopes down: other branches stay listed but dimmed, with a hint to act from the main checkout; a started session's menu shows only its own branch (fetch and update still work).
  • Create from any branch, rename in place: right-clicking any branch row creates FROM that branch (checkouts untouched) or creates-and-checks-out (checked out on the spot); the create flyout validates as you type (git ref rules plus a duplicate check) and fires in one stroke, a failure keeping it open for a renamed retry. Local rows rename in place too.
  • Remote sync: the last toolbar tool fetches every remote and prunes stale tracking branches — the list refreshes in place, no terminal round-trip.
  • Live progress: fetch, update and push run under a floating card (like IDEA's background task) that shows the remote, git's own stage, count and speed, and a × to cancel — it never blocks the page, survives switching sessions, and a long transfer is no longer cut off after 20 or 120 seconds (only five silent minutes end it). Other git actions on that directory wait until it finishes; the fast-forward step at the end of an update can no longer be cancelled.
  • Branch push: Push on local and worktree rows opens a compact review dialog. Choose a remote only when several exist, edit the target branch (Enter applies it; the editor closes only with Enter, Esc or ×, and a click elsewhere never closes it or the dialog), and scroll first-line commit subjects independently of the fixed footer. Local commits appear while the remote is checked in the background; pushing is enabled only after verification. Optional tags support All or Follow commits. The split menu offers Force push with an explicit lease that rejects remote updates made after preview. Push acts on the selected branch without switching checkouts or committing working files; clicking Push closes the dialog and the push continues under the progress card (cancel any time); a failed push brings the dialog back with the reason and your previous remote, target and tags, explains the usual causes (remote moved on, expired lease, protected branch, authentication) and re-reads the destination for the retry. Green ↗N / blue ↙N show divergence, a green ↗ without a number marks an unpublished branch (shown only where the repository has a remote), and ! marks a missing upstream.
  • Branch update: update the CURRENT branch to its upstream (fast-forward only), from the toolbar or the current row's context menu; divergence, a missing upstream, or conflicting uncommitted changes are refused with git's own explanation — the plugin never stashes or rewrites history on your behalf.
  • Worktree isolation: in a session that has not started, right-clicking any branch row offers Create worktree (reuse or fresh directory) or New branch and worktree (cut a new branch off that row, name typed by hand); confirming isolates into /.dsh/gitworktree// inside the repository (locally ignored through .git/info/exclude — the repo's git status stays clean) and registers a real workspace, with same-branch re-creates reusing the existing worktree wherever it lives. Once the session starts the directory is fixed — the worktree verbs hide along with the Worktrees group.
  • Worktree removal: the Worktrees group's row menu (and the manager dialog) offers Remove worktree — the confirm first counts uncommitted files (red) and ahead commits, and warns the workspace's sessions will be archived; confirming removes the git worktree (the branch itself survives), then archives the sessions and drops the workspace registration. A git failure leaves the DSH side untouched and retries in place. A directory with a running session withholds the verb.
  • Worktree manager: the plugin's configuration page on Settings → Plugins opens a dialog listing every worktree across both storage locations — the per-repository .dsh/gitworktree layouts (new) and the legacy central root (old, kept for existing worktrees) — grouped by repository with a source badge, orphan directories included.
  • Legacy storage, read-only: the same page shows the historical central storage root (~/.dsh/gitworktree by default) read-only — existing worktrees stay put and keep working; new ones are created inside their repository. A legacy ~/.dsh/gitworktree/settings.json value migrates automatically on upgrade.
[!NOTE]
- The native sidebar is untouched: worktrees are ordinary registered workspaces, and the workspace tree nests the per-repository layouts under their repository automatically (existing centrally-stored worktrees keep their old location — only new ones nest).
- git clean -xdff (double -f) deletes the in-repository worktree directories (a single -f skips nested repositories). git worktree prune restores the registrations afterwards, but uncommitted work inside them is lost.
- If every workspace of a repository is deregistered, its worktrees no longer appear in the manager (or the auto-prune); re-register any directory of that repository to bring them back.

Install

dsh plugin --profile web add @laoyuehanni/dsh-git-worktree
The package declares dsh.bundle, so add wires the plugin into the profile's layer stack automatically — no config editing needed. Requires the web profile (dsh web) and a dsh 0.2.0-rc.1 (or later) host.

Running a dsh 0.1.2 alpha host? Install the dedicated compatibility build instead:

dsh plugin --profile web add @laoyuehanni/dsh-git-worktree@dsh-alpha
@dsh-alpha is a dist-tag resolving to the latest alpha-host compatibility build (currently 0.4.3-dsh-0.1.2-alpha.5) with peers pinned to the alpha line — a plain update never mixes the two channels. On 0.2.0-rc.1 or later, use the default line above.

Update

dsh plugin --profile web update @laoyuehanni/dsh-git-worktree

Remove

dsh plugin --profile web remove @laoyuehanni/dsh-git-worktree

Worktree folders are kept at both locations (the per-repository .dsh/gitworktree layouts and the legacy central root); the plugin's own settings live in the dsh settings document.

Development

Build once, install a symlink, iterate:

pnpm install
pnpm build:all
pnpm test                # vitest
node scripts/smoke.mjs  # real-git smoke over the built lib
dsh plugin --profile web add link:D:/Code/dsh-worktree

Rebuild and restart dsh web to apply changes (pnpm watch:client in the plugin directory hot-reloads the client). No prepare script by design — lib/ never enters the repo; pnpm publish builds it fresh into the tarball.

Temporary host-only mount (this launch only, no profile changes): create a cordis.yml next to the repo pointing at the built host half (Windows needs the file:/// form), then launch with it:

- insert:
    - id: git-worktree
      name: 'file:///D:/Code/dsh-worktree/lib/index.js'
dsh web --patch <plugin-dir>/cordis.yml

Only the host half mounts in this mode (the /plugin/git-worktree/* routes keep working); for UI work use the link: install above.