Zed自定义Git命令

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选中提交的完整 SHACommit + 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 clipboardwl-copy
  • open -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 statusgit 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"]
  }
]

六、收尾:三条心法

  1. 写法优先:拿不准就整行写 command,别跟 args 拼接较劲
  2. 变量即路由:菜单放哪,由变量决定,别硬塞
  3. 标签醒目Delete branch: / Delete tag: 前缀写清楚,右键前瞄一眼就不手滑

记得刷新Git树验证命令效果,如tag添加和删除需要刷新后才能看到效果。


参考资料

  • Zed Docs — Custom Git Commands — 官方文档,涵盖 git-command 标签、Git 专属变量($ZED_GIT_SHA$ZED_GIT_REF 等)、变量过滤规则和示例配置。