@moritzwilksch/diffle

中文文档
0.3.6发布于 2026/11/02Apache-2.0ES Module零依赖构建来源已验证

Local git diff reviewer in the browser with line comments exported as agent prompts

原始使用文档
文档目录12 节
  1. Install
  2. Pick what to review
  3. Review
  4. Send comments to GitHub
  5. Jump to definitions
  6. See the change as a graph
  7. Open from lazygit
  8. Shell completions
  9. Behind a reverse proxy
  10. On a remote machine
  11. Development
  12. Acknowledgements

diffle

CI conda-forge conda-forge-platforms npm website

Review a Git diff in the browser and comment on lines, blocks, or whole files. Then hand the comments to an agent as a prompt, or to GitHub as a pending review.

A review in diffle: a language-server hover, its references, a jump to one and a comment there, then yy copies the comments as one prompt

Install

brew install diffle                      # Homebrew
pixi global install diffle               # conda-forge
npm install -g @moritzwilksch/diffle     # npm
cargo binstall diffle                    # crates.io, the release binary
cargo install diffle                     # crates.io, compiled from source

Or grab the standalone binary. On Linux (x64, arm64, glibc) and Apple silicon macOS:

curl -fsSL https://diffle.app/install.sh | sh

It installs to ~/.local/bin. Set DIFFLE_INSTALL_DIR to change that, or DIFFLE_VERSION=v0.4.0 to pin a release. On Windows (x64, arm64), from PowerShell:

irm https://diffle.app/install.ps1 | iex

It installs to %LOCALAPPDATA%\Programs\diffle and adds that to your user PATH; set DIFFLE_NO_MODIFY_PATH to keep the PATH as it is.

To try it without installing:

npx @moritzwilksch/diffle working
pixi exec diffle working
nix run github:moritzwilksch/diffle -- working

The Nix package bundles gh; #minimal drops it, and #web, #rust, #python, and #csharp bundle language servers. Every package takes more with withLsps, and #unwrapped is the bare binary:

diffle.packages.${system}.default.withLsps [ pkgs.gopls pkgs.clang-tools ]

To build from source, pixi builds the checkout with recipe/recipe.yaml (a Rust toolchain and Node.js come from conda-forge):

pixi global install --git https://github.com/moritzwilksch/diffle diffle

Pick what to review

diffle                  # HEAD vs worktree, like a bare `git diff`
diffle working          # HEAD vs worktree: staged, unstaged, untracked
diffle develop          # what this branch added since it left develop
diffle pr 27            # GitHub PR 27, or its URL; without a number, this branch's PR
diffle show a1b2c3d     # one commit, like `git show`
diffle main..feat       # any git-diff revspec: <rev> | a..b | a...b | a b
diffle main..worktree   # "worktree" names the uncommitted tree on either side
diffle range-diff main..v1 main..v2   # how a series of commits was reworked, like `git range-diff`
diffle --help           # all commands and flags

diffle pr needs a GitHub token. A PR from another repository opens in a temporary clone.

Closing the last diffle tab stops the server and prints your open comments. Pass --keep-alive to keep it running.

Review

  • j / k: next or previous line (10j: ten lines down)
  • J / K: next or previous file
  • ] / [: next or previous hunk
  • c: comment
  • C: comment on the whole file
  • V: select a block
  • R: resolve
  • v: mark viewed
  • /: search the current file
  • g/: search all changed files
  • gf: filter files
  • F: open the full file
  • Ctrl+o: go back
  • yy: copy all comments
  • yl: copy a GitHub permalink to the current line or selection, once its commit is pushed

Press ? for the full list.

A comparison lists its commits above the threads. Pick one, or step with < and >, to see that commit alone; All changes returns to the whole range. Comments on a focused commit belong to that commit, as in diffle show.

Comments live in <git-dir>/diffle/ and never touch your worktree. They follow their text as the code changes and go stale once it leaves the diff.

Generated files start collapsed, as on GitHub: linguist-generated in .gitattributes marks them, and linguist-generated=false opts a file out. To collapse other files, add auto-viewed globs in settings or with diffle config.

Send comments to GitHub

The pull request icon adds one thread, or all open threads, to a pending review on the PR. diffle never submits it: you edit, drop, and submit the comments on GitHub yourself. Adding a thread again updates its comment instead of duplicating it.

Export works when the comparison matches the PR's committed diff, so unpushed commits and worktree changes rule it out. Stale threads are skipped.

diffle takes the token from GITHUB_TOKEN, then GH_TOKEN, then a signed-in gh. Without one, everything except GitHub still works. GITHUB_API_URL points diffle at another GitHub API, as in Actions.

For GitHub Enterprise Server, add its host to githubHosts in the config file (diffle config prints its path). Each entry maps the host in remote URLs to the host serving the web UI and API, the same one unless remotes use another, as an SSH-only host might:

{
  "githubHosts": {
    "github.work.com": "github.work.com",
    "ssh.work.com": "github.work.com"
  }
}

A run talks to one GitHub: the pull request URL's, else origin's, else the one all GitHub remotes share. The token then comes from GITHUB_ENTERPRISE_TOKEN, GH_ENTERPRISE_TOKEN, or gh auth login --hostname <host>.

Jump to definitions

diffle starts a language server for each language in the diff, if it finds one on PATH:

  • gd or Ctrl/Cmd+click: definition
  • gy: type definition
  • gA: references
  • gs / gS: file or repository symbols
  • gh: hover, including schema docs for JSON, YAML, and TOML
  • w / b / 0 / $: step through a line's symbols; the one under the cursor or the pointer is tinted wherever it recurs in the file, writes apart from reads

Results outside the diff, such as the standard library, open read-only. Large repositories can take a while to index.

diffle lsp shows which server each language gets and what to install for the rest:

python    pyrefly lsp
rust      not on PATH (tried rust-analyzer)

Override a server or turn one off for a run:

diffle working --lsp python="pyrefly lsp"        # this run only
diffle working --no-lsp                          # none at all

Or persistently, in the config file (diffle config prints its path and contents):

{ "lspCommands": { "rust": "rust-analyzer", "java": "" } }

Language servers run inside the repository and can read anything your user can. --no-lsp starts none.

See the change as a graph

Press M to swap the diff for a graph of what changed: one box per file, or per symbol with s, and an arrow wherever one uses another. d adds the unchanged code that uses the change, T the tests.

The change graph in diffle: files, then symbols, a function's links in the side panel, a peek at the lines behind them, then a pan to two linked symbols

Click a box to list what it uses and what uses it in the side panel; Peek steps through the lines behind those links without leaving the graph. A card in the panel, or a double-click on a box, frames that box with its neighbours. Enter opens the selection in the diff, and M returns to the graph as you left it.

The graph needs the language servers, the same ones Jump to definitions uses.

Open from lazygit

lazygit custom commands open diffle on what is selected. Add them to its config.yml (lazygit --print-config-dir prints the folder):

customCommands:
  - key: '<c-d>'
    context: 'files'
    command: 'diffle working'
    output: terminal
    description: 'Review uncommitted changes in diffle'
  - key: '<c-d>'
    context: 'commits'
    command: 'diffle {{.SelectedCommitRange.From}}^..{{.SelectedCommitRange.To}}'
    output: terminal
    description: 'Review the selected commits in diffle'
  - key: '<c-d>'
    context: 'localBranches'
    command: 'diffle main...{{.SelectedLocalBranch.Name}}'
    output: terminal
    description: 'Review the branch against main in diffle'

lazygit waits while you review; closing the tab returns to it with the open comments printed as a prompt. Append | pbcopy (or your clipboard tool) to a command to copy them instead.

Shell completions

diffle completion --shell bash > ~/.local/share/bash-completion/completions/diffle
diffle completion --shell zsh > ~/.local/share/zsh/site-functions/_diffle   # on fpath, before compinit
diffle completion --shell fish > ~/.config/fish/completions/diffle.fish

Rerun after upgrading to pick up new commands. Revisions don't complete.

Behind a reverse proxy

diffle works under a path prefix such as https://proxy.example/diffle/ with no extra setting. Have the proxy strip the prefix, including for WebSocket upgrades at /diffle/ws, and trust its public origin:

diffle working -H 0.0.0.0 --no-open --allowed-origin https://proxy.example

If the proxy rewrites Host and Origin to its upstream address, such as a Kubernetes service name, trust that address instead. A rejected request gets a 403 naming the header that failed.

On a remote machine

Serve on a Unix socket that only you can connect to, and forward it over SSH:

diffle working --socket /home/me/diffle.sock          # on the remote machine
ssh -N -L 4966:/home/me/diffle.sock remote            # here, then open http://localhost:4966

No browser opens on the remote machine. Closing the last tab connected through the socket stops diffle there and prints your open comments, as on your own machine. --socket needs macOS or Linux, and the forward needs OpenSSH 6.7 or later.

Development

The server is Rust (crates/diffle), the client TypeScript; you need a Rust toolchain (rust-toolchain.toml pins it) and Node.js.

pixi installs the toolchain, git, gh and lefthook from pixi.lock, and its tasks are the entry points, locally and in CI. npm builds and tests the client; cargo builds and tests the server, whose release build embeds the client.

pixi run diffle working                   # build the client, then serve the working tree
pixi run test-node && pixi run test-rust  # the client's and the server's suites
pixi run lefthook-run                     # the pre-commit hook over every file, as CI runs it
pixi run build-release                    # the release binary
pixi run diffle lsp                       # the language servers the environment resolves

The environment puts real language servers on PATH, so pixi run diffle working reviews with them. pixi run lefthook-install wires up the pre-commit hook (.lefthook.yml): it formats and lints the staged files, typechecks, and checks file-size and folder limits across the whole tree. pixi run lefthook-run runs the same hook over every file, as CI does. A debug build reads crates/diffle/client from disk: after a client change, run npm run build and reload the page. A release build (cargo build --release) embeds the client. npm run fixture -- <dir> builds the repository the tests review, covering renames, binaries, CRLF, and more. The browser suite drives Playwright's Linux Chromium in a Docker container (compose.yml, started and stopped by the suite) while the tests and the release binary run natively, so screenshots match CI on every host:

pixi run e2e                              # the browser suite, after the release build
pixi run e2e -g search                    # matching scenarios
pixi run e2e-report                       # open the last report
pixi run e2e --update-snapshots           # accept new screenshots

See AGENTS.md for more.

Acknowledgements

diffle is inspired by difit. Diffs are rendered with @pierre/diffs.

版本与发布记录

@moritzwilksch/diffle 共发布 30 个版本,首次发布于 2026/09/09;过去 12 个月发布了 30 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.3.6latest2026/11/021 天前
0.3.52026/10/294 天前
0.3.42026/10/259 天前
0.3.32026/10/2410 天前
0.3.22026/10/2311 天前
0.3.12026/10/2212 天前

关于 @moritzwilksch/diffle 的常见问题

@moritzwilksch/diffle 是什么?

浏览器中的本地git diff审阅器,将行注释导出为代理提示

如何安装 @moritzwilksch/diffle?

在项目目录运行 npm install @moritzwilksch/diffle,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 diffle,可以用 npx diffle 直接运行。

@moritzwilksch/diffle 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/moritzwilksch__diffle 是否存在。

@moritzwilksch/diffle 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@moritzwilksch/diffle 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@moritzwilksch/diffle 可以商用吗?

@moritzwilksch/diffle 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@moritzwilksch/diffle 还在维护吗?

最新版本 0.3.6 发布于 2026/11/02(1 天前);至今共发布 30 个版本,其中过去 12 个月发布了 30 个;首次发布于 2026/09/09。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。