Zed自定义Git命令
适用环境:Zed 编辑器 + Git Graph / Git Panel · 配置位置:
~/.config/zed/tasks.json
一、核心结论
用 tags: ["git-command"] 定义任务,配合 $ZED_GIT_* 变量,把「查看、派生、分支操作」三类高频 Git 操作变成右键即用的菜单命令,从此告别手敲 SHA 和分支名。
一份配置,三个收益:
- 零记忆:不用记 SHA、不用复制粘贴,右键即点即用
- 零输入:分支名 / 标签名 / 提交号全部由变量自动注入
- 零切换:不离开编辑器,git 操作全部在 Zed 的终端里完成
二、三大机制
理解这三件事,后面所有配置都是它们的排列组合。
2.1 入口:命令从哪来
- 任务定义在全局
tasks.json(工作区级.zed/tasks.json暂不支持) - 带
git-command标签的任务会出现在 Git Graph 的右键菜单里:- 右键提交行 → 提交菜单(Commit 菜单)
- 右键分支 / 远程引用 / 标签 chip → 引用菜单(Ref 菜单)
2.2 变量:能自动注入什么
| 变量 | 含义 | 可用菜单 |
|---|---|---|
$ZED_GIT_SHA | 选中提交的完整 SHA | Commit + Ref |
$ZED_GIT_SHA_SHORT | 短 SHA(7 位) | Commit + Ref |
$ZED_GIT_REPOSITORY_NAME | 仓库名 | Commit + Ref |
$ZED_GIT_REPOSITORY_PATH | 仓库绝对路径(默认 cwd) | Commit + Ref |
$ZED_GIT_REF | 点击的引用名(分支/远程/标签) | 仅 Ref 菜单 |
编辑器类变量(
$ZED_FILE、$ZED_SELECTED_TEXT等)在 Git 任务里不会提供,引用它们的任务会被自动过滤。
2.3 过滤规则:任务怎么"隐身"
- 任务里用了
$ZED_GIT_REF→ 只在右键 chip 时出现 - 任务里只用
$ZED_GIT_SHA→ 提交菜单和引用菜单都出现 - 任务不用任何变量 → 两种菜单都出现
- 任务用了不存在的变量 → 不出现
结论:用变量本身做"菜单路由",想限定场景就引用对应变量。
三、三种写法与两个大坑
3.1 三种写法
| 写法 | 适用场景 | 示例 |
|---|---|---|
A. 纯参数:command + args | 参数无空格、无特殊字符 | git branch -d $ZED_GIT_REF |
B. 整行命令:整条 shell 命令放 command | 含管道 / 重定向 / 交互输入 | git show $ZED_GIT_SHA | pbcopy |
C. tty 交互:/dev/tty + read | 需要现场输入(起名 / 改名) | 见 3.3 |
3.2 大坑一:args 会被"拆碎"
Zed 会把 command 和所有 args 用空格拼成一条命令行交给登录 shell 执行,不负责转义。所以:
"args": ["-c", "printf 'Branch name: ' > /dev/tty; ..."]
拼接后变成 zsh -c printf 'Branch name: ' > /dev/tty; ...,-c 后面只跟了 printf 一个词 → 报 zsh: printf:1: not enough arguments。
对策:含空格/引号/特殊符号的命令一律用写法 B(整行命令),让登录 shell 原样执行,不经过拼接。
3.3 大坑二:交互输入要绑 /dev/tty
任务跑在 PTY 里,标准输入不一定是终端。输入提示的标准姿势:
{
"label": "Create tag at: $ZED_GIT_SHA_SHORT",
"command": "printf 'Tag name: ' > /dev/tty; IFS= read -r name < /dev/tty; [ -n \"$name\" ] && git tag \"$name\" \"$ZED_GIT_SHA\"",
"tags": ["git-command"]
}
三段式:打印提示到 tty → 从 tty 读入 → 非空才执行。注意这里整行命令在 command 字段,天然规避了大坑一。
3.4 平台差异
pbcopy(复制到剪贴板)仅 macOS;Linux 换xclip -selection clipboard或wl-copyopen -a Fork依赖 Fork 应用,其他平台可换成 GitHub/GitLab 链接
四、命令清单
4.1 提交右键族(Commit 菜单)—— 看、取、派、查
| 命令 | 作用 | 备注 |
|---|---|---|
| Cherry-pick | 摘取提交 | 已有基础版;-x 版会记录来源 |
| Revert | 撤销提交 | 已有;-m 1 版专用于合并提交 |
| Show | 查看提交 + 文件统计 | git show --stat |
| Diff vs parent | 只看该提交的改动 | git diff sha^ sha |
| Branches containing | 哪些分支包含此提交 | 排查漏合并神器 |
| Copy patch | 整段 patch 进剪贴板 | macOS 需 pbcopy |
| Create fixup | 对旧提交打补丁标记 | 配合 rebase --autosquash |
| Create tag at | 在提交上打标签 | tty 交互输入 |
| Bisect bad / good | 二分定位回归 | 右键提交直接标记 |
4.2 分支标签右键族(Ref 菜单)—— 引用的增删改查
| 命令 | 作用 | 备注 |
|---|---|---|
| Delete branch | 删除本地分支 | -d 仅删已合并,-D 强删 |
| Delete tag | 删除本地标签 | 可选配"删远程标签"版 |
| Check out | 切换分支 | 官方示例同款 |
| Merge | 并入当前分支 | 注意当前分支被保护 |
| Rebase onto | 当前分支变基到它 | |
| Diff vs HEAD | 与当前分支的差异统计 | |
| Log | 看该引用最近 15 条 | --graph 带拓扑 |
| Push -u origin | 推送并建立上游 | 新建分支后常用 |
| Rename | 重命名分支 | tty 交互输入 |
4.3 通用族(两种菜单都会出现)—— 谨慎添加
git status、git fetch --prune 这类无变量任务两种菜单都显示,容易把菜单刷爆,按需添加。
五、完整配置(可直接粘贴)
合并进
~/.config/zed/tasks.json的数组里即可。其中Create branch at已修复为整行写法(规避大坑一)。
[
{ "label": "Cherry-pick", "command": "git", "args": ["cherry-pick", "$ZED_GIT_SHA"], "tags": ["git-command"] },
{ "label": "Cherry-pick (-x): $ZED_GIT_SHA_SHORT", "command": "git", "args": ["cherry-pick", "-x", "$ZED_GIT_SHA"], "tags": ["git-command"] },
{ "label": "Revert", "command": "git", "args": ["revert", "--no-edit", "$ZED_GIT_SHA"], "tags": ["git-command"] },
{ "label": "Revert merge: $ZED_GIT_SHA_SHORT", "command": "git", "args": ["revert", "-m", "1", "--no-edit", "$ZED_GIT_SHA"], "tags": ["git-command"] },
{ "label": "Show: $ZED_GIT_SHA_SHORT", "command": "git", "args": ["show", "--stat", "$ZED_GIT_SHA"], "tags": ["git-command"] },
{ "label": "Branches containing: $ZED_GIT_SHA_SHORT", "command": "git", "args": ["branch", "-a", "--contains", "$ZED_GIT_SHA"], "tags": ["git-command"] },
{ "label": "Diff vs parent: $ZED_GIT_SHA_SHORT", "command": "git", "args": ["diff", "$ZED_GIT_SHA^", "$ZED_GIT_SHA"], "tags": ["git-command"] },
{ "label": "Create fixup: $ZED_GIT_SHA_SHORT", "command": "git", "args": ["commit", "--fixup", "$ZED_GIT_SHA"], "tags": ["git-command"] },
{ "label": "Copy patch: $ZED_GIT_SHA_SHORT", "command": "git show $ZED_GIT_SHA | pbcopy", "tags": ["git-command"] },
{
"label": "Create tag at: $ZED_GIT_SHA_SHORT",
"command": "printf 'Tag name: ' > /dev/tty; IFS= read -r name < /dev/tty; [ -n \"$name\" ] && git tag \"$name\" \"$ZED_GIT_SHA\"",
"tags": ["git-command"]
},
{ "label": "Bisect bad: $ZED_GIT_SHA_SHORT", "command": "git", "args": ["bisect", "bad", "$ZED_GIT_SHA"], "tags": ["git-command"] },
{ "label": "Bisect good: $ZED_GIT_SHA_SHORT", "command": "git", "args": ["bisect", "good", "$ZED_GIT_SHA"], "tags": ["git-command"] },
{
"label": "Create branch at: $ZED_GIT_SHA_SHORT",
"command": "printf 'Branch name: ' > /dev/tty; IFS= read -r name < /dev/tty; [ -n \"$name\" ] && git branch \"$name\" \"$ZED_GIT_SHA\"",
"tags": ["git-command"],
"reveal": "always",
"use_new_terminal": true
},
{
"label": "Open in Fork",
"command": "zsh",
"args": ["-c", "open -a Fork . && sleep 2 && open \"fork://repo/$ZED_GIT_REPOSITORY_NAME/commit/$ZED_GIT_SHA\""],
"tags": ["git-command"]
},
{ "label": "Reset --soft to: $ZED_GIT_SHA_SHORT", "command": "git", "args": ["reset", "--soft", "$ZED_GIT_SHA"], "tags": ["git-command"] },
{ "label": "Reset --hard to: $ZED_GIT_SHA_SHORT", "command": "git", "args": ["reset", "--hard", "$ZED_GIT_SHA"], "tags": ["git-command"] },
{ "label": "Delete branch: $ZED_GIT_REF", "command": "git", "args": ["branch", "-d", "$ZED_GIT_REF"], "tags": ["git-command"] },
{ "label": "Delete tag: $ZED_GIT_REF", "command": "git", "args": ["tag", "-d", "$ZED_GIT_REF"], "tags": ["git-command"] },
{ "label": "Delete remote tag: $ZED_GIT_REF", "command": "git", "args": ["push", "origin", "--delete", "refs/tags/$ZED_GIT_REF"], "tags": ["git-command"] },
{ "label": "Check out: $ZED_GIT_REF", "command": "git", "args": ["checkout", "$ZED_GIT_REF"], "tags": ["git-command"] },
{ "label": "Merge: $ZED_GIT_REF", "command": "git", "args": ["merge", "$ZED_GIT_REF"], "tags": ["git-command"] },
{ "label": "Rebase onto: $ZED_GIT_REF", "command": "git", "args": ["rebase", "$ZED_GIT_REF"], "tags": ["git-command"] },
{ "label": "Diff vs HEAD: $ZED_GIT_REF", "command": "git", "args": ["diff", "--stat", "HEAD", "$ZED_GIT_REF"], "tags": ["git-command"] },
{ "label": "Log: $ZED_GIT_REF", "command": "git", "args": ["log", "--oneline", "--graph", "-15", "$ZED_GIT_REF"], "tags": ["git-command"] },
{ "label": "Push -u origin: $ZED_GIT_REF", "command": "git", "args": ["push", "-u", "origin", "$ZED_GIT_REF"], "tags": ["git-command"] },
{
"label": "Rename: $ZED_GIT_REF",
"command": "printf 'New name: ' > /dev/tty; IFS= read -r name < /dev/tty; [ -n \"$name\" ] && git branch -m \"$ZED_GIT_REF\" \"$name\"",
"tags": ["git-command"]
}
]
六、收尾:三条心法
- 写法优先:拿不准就整行写
command,别跟 args 拼接较劲 - 变量即路由:菜单放哪,由变量决定,别硬塞
- 标签醒目:
Delete branch:/Delete tag:前缀写清楚,右键前瞄一眼就不手滑
记得刷新Git树验证命令效果,如tag添加和删除需要刷新后才能看到效果。
参考资料
- Zed Docs — Custom Git Commands — 官方文档,涵盖
git-command标签、Git 专属变量($ZED_GIT_SHA、$ZED_GIT_REF等)、变量过滤规则和示例配置。