with one click
dt-push
// One-push release workflow: auto git add all changes, pull latest, logical-group commit, push to remote with optional tag.
// One-push release workflow: auto git add all changes, pull latest, logical-group commit, push to remote with optional tag.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | dt:push |
| description | One-push release workflow: auto git add all changes, pull latest, logical-group commit, push to remote with optional tag. |
| argument-hint | [version|--preview] e.g. /dt:push 1.2.2 or /dt:push --preview |
中文环境要求
本技能运行在中文环境下,请遵循以下约定:
- 面向用户的回复、注释、提示信息必须使用中文
- AI 内部处理过程可以使用英文
- 所有生成的文件必须使用 UTF-8 编码
一键发布工作流:自动暂存所有变更、拉取最新代码、按逻辑分组提交、推送到远程仓库。
重要:所有 git 命令均在用户当前工作目录执行。
本 skill 全程在用户当前分支上操作,任何情况下都 禁止:
git branch <new> / git checkout -b / git switch -c)唯一例外:远程尚未存在当前分支时,git push -u 会在远程创建同名分支 —— 这是当前分支的首次推送,不属于"新建分支"。
如果冲突 / rebase / push 无法继续推进,必须停下并向用户报告当前状态(git status 输出 + 建议的下一步),让用户人工决策,不得自动创建新分支来绕过。
git add / git commit,但本地提交尚未 push 到远程/dt:push - 自动暂存所有变更,按逻辑分组提交,推送到远程/dt:push 1.2.2 - 更新文档版本号到 1.2.2,提交并打 tag/dt:push --preview - 预览分组方案与 commit messages,不执行任何 git 操作| Parameter | Description |
|---|---|
| No args | 自动 git add 所有变更,按逻辑分组提交,推送到远程 |
X.Y.Z | 额外将文档中的版本号更新为指定版本,并创建 tag |
--preview | 仅预览逻辑分组方案与 commit messages,不执行任何 git 操作 |
在执行任何操作之前,验证环境是否满足要求:
# 1. 确认在 git 仓库中
git rev-parse --is-inside-work-tree
# 2. 确认 remote origin 存在
git remote get-url origin
# 3. 确认当前分支
git branch --show-current
# 4. 获取上游分支(如果存在)
git rev-parse --abbrev-ref --symbolic-full-name @{u}
# 5. 确认工作区状态(已暂存或未暂存)
git status --porcelain
# 6. 检查本地是否有未推送提交(仅在存在上游分支时执行)
git rev-list --count @{u}..HEAD
检查结果处理:
| 检查项 | 处理方式 |
|---|---|
| 非 git 仓库 | 提示用户,退出 |
| 无 remote origin | 提示用户,退出 |
| 无当前分支 | 提示用户,退出 |
| 无 upstream | 允许继续;Step 1 跳过 pull,Step 5 首次 push 时使用 git push -u |
| 工作区有变更 | 允许继续;后续会走 Step 4 自动分组提交 |
| 工作区无变更但存在未推送提交 | 允许继续;跳过 Step 4,直接在同步远程后执行 Step 5 推送已有本地提交 |
| 工作区无变更且无未推送提交 | 若未提供版本号则退出;若提供版本号则继续执行 Step 3 生成版本提交 |
| 分离 HEAD / 非预期分支状态 | 停止并提示用户,不自动创建或切换分支 |
关键原则:
dt:push 判断的是“是否存在待推送工作”,而不是仅判断“工作区是否脏”在提交之前,先拉取远程最新代码,避免推送时冲突。
执行流程:
BRANCH=$(git branch --show-current)
REMOTE=$(git remote | head -1)
UPSTREAM=$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null || true)
git stash 保存,拉取完成后 git stash pop 恢复git pull --rebase $REMOTE $BRANCH
git stash pop 恢复工作区冲突处理策略:
重要原则:
git pull --rebase 是否真的产生冲突(检查退出码 + git diff --name-only --diff-filter=U 是否有输出)git pull --rebase 成功返回且无未合并路径,静默继续下一步,不打断用户。
只要出现冲突,不要静默自动解决,也不要只给出"请手动处理后重试",更不得创建新分支规避。必须对每个冲突文件逐个提示用户,由用户明确选择:
先收集冲突文件列表:
git diff --name-only --diff-filter=U
对每个冲突文件,在询问用户之前必须展示这些信息:
ours)相关代码片段theirs)相关代码片段建议使用的 git 数据来源:
git diff --merge -- <file>
git show :2:<file> # 本地版本 / ours
git show :3:<file> # 远程版本 / theirs
建议生成规则:
| 场景 | 建议 |
|---|---|
| 远程只包含文档、锁文件、生成文件或格式化更新,本地包含真实功能变更 | 优先建议“合并”或“使用本地版本”,避免丢失功能代码 |
| 本地仅是格式化、注释、无语义微调,远程包含明确业务修复 | 优先建议“使用远程版本” |
| 双方修改不同代码块,语义可兼容 | 优先建议“合并两个版本” |
| 双方修改同一接口、同一条件分支、同一文案语义 | 默认建议“合并两个版本”,不要直接猜测覆盖 |
| 二进制文件或无法展示文本 diff | 展示文件信息并说明风险,仍然让用户在“远程 / 本地 / 合并”中选择 |
对每个文件的交互流程:
remote / local / merge# 用户选择 remote
git checkout --theirs -- <file>
git add <file>
# 用户选择 local
git checkout --ours -- <file>
git add <file>
merge:
git add <file>git rebase --continue
git rebase --continue 后进入下一轮冲突,重复上述逐文件流程,直到 rebase 完成git stash pop,也使用同样的逐文件流程;处理完成后再继续后续步骤分支硬约束:上述任一步骤失败时(例如 git rebase --continue 仍然失败、无法解析合并方案、stash pop 无法完成),停止并报告给用户当前 git status,让用户人工决策,不得创建任何新分支作为绕过方案。
从 args 中提取版本号(可选)。
如果提供了版本号,校验格式是否为 semver (X.Y.Z):
如果未提供版本号 → 跳过 Step 3,直接进入 Step 4
扫描项目中的文档文件,将版本号更新为指定版本。
重要:此步骤在 Step 4 之前执行,此步骤提交的文件不会出现在 Step 4 的文件扫描中。
需要检查并更新的文件(按优先级):
| 文件 | 更新内容 |
|---|---|
README.md | 版本号相关描述 |
README_EN.md | 英文 README 中的版本号 |
docs/PROJECT_OVERVIEW.md | 项目概览中的版本信息 |
docs/CHANGELOG.md | 在顶部插入新版本记录 |
docs/.doc-metadata.json | metadata 中的版本信息(如有) |
其他 docs/*.md | 任何包含旧版本号的文档 |
版本号检测策略:
README.md 或 docs/PROJECT_OVERVIEW.md 中查找当前版本号版本 X.Y.Z、version X.Y.Z、vX.Y.Z、版本:X.Y.Z 等带上下文的模式minSdkVersion、compileSdk、依赖库版本号等非项目版本号build.gradle、build.gradle.kts、pubspec.yaml 等构建配置文件中的依赖版本*.md 和 docs/ 目录下的文件替换后验证:
git diff 检查所有变更提交版本号变更:
git add <changed doc files>
git commit -m "chore: bump version to X.Y.Z"
自动暂存所有工作区变更,按逻辑分组提交,同一逻辑变更合并为 1 个 commit。
跳过条件:
添加所有变更:
git add .
获取变更文件列表(基于 HEAD):
git diff HEAD --name-only
先完整读取所有文件的 diff,再按以下规则(优先级从高到低)归并到分组:
| 优先级 | 分组规则 | 示例场景 |
|---|---|---|
| P0 | 相同字符串替换:多个文件的 diff 中出现相同的 - old / + new 字符串对(剔除上下文行后仍重合) | 多文件统一替换 baseUrl 域名 |
| P1 | 同一符号重命名:函数名、类名、常量名在多文件同步变动 | 函数重命名后波及的所有调用方文件 |
| P2 | 同一目录 / 功能模块:路径前缀相同且 diff 语义相关(如同属一个 feature 分支的改动) | app/api/meeting/* 内的多个文件一同修改 |
| P3 | 同主题批量新增:在同一次操作中新增的一组文件(如新 skill 的 SKILL.md + README.md) | 新增 skill 时一并提交两个新文件 |
| P4 | 独立变更:不匹配任何上述规则的文件,回退为单文件 commit | 彼此无关的零散修改 |
分组执行逻辑(伪代码):
1. 读取所有变更文件及其 diff
2. 字符串替换检测(P0):
a. 提取每个文件中所有 "-旧 / +新" 行对
b. 对 (旧, 新) 做哈希分组
c. 出现在 ≥ 2 个文件中的 (旧, 新) 对 → 这些文件归为同一 P0 分组
3. 符号重命名检测(P1):
a. 对剩余文件检测标识符(函数名/类名)的统一替换
b. 在 ≥ 2 个文件中出现同一标识符变化 → 归为 P1 分组
4. 目录聚类(P2):
a. 对剩余文件按目录前缀聚合
b. 同目录下 ≥ 2 个文件有关联 diff → 归为 P2 分组
5. 同主题新增(P3):
a. 剩余新增文件中,位于同一目录或明显成套的(如 SKILL.md + README.md)→ 归为 P3 分组
6. 其余文件 → 各自独立 P4 单文件 commit
核心原则:P0 / P1 / P2 分组不能只凭"文件里出现了主题模式"就把整个文件塞进来。一个文件只有在绝大多数 diff 行都能被该分组主题解释时,才允许留在分组内。否则这个文件的实际修改意图会被主题 commit 吞掉,事后从远程日志看不出它真正改了什么。
对 4.1 阶段被归入 P0 / P1 / P2 的每个文件,逐个计算纯度:
purity = 能被该分组主题解释的 diff 行数 / 该文件的总有效 diff 行数
各分组的"主题解释行"定义:
| 分组 | 主题解释行 |
|---|---|
| P0 | 命中该分组 (旧串, 新串) 替换对的所有 -/+ 行 |
| P1 | 命中该分组符号重命名(函数名 / 类名 / 常量名)的所有 -/+ 行 |
| P2 | 该文件 diff 中语义上能归入该目录 / 功能模块共同主题的行(需对剩余 diff 做语义判定) |
总有效 diff 行数的计算规则:
- / + 行,不算上下文行和 ---/+++ 文件头-/+ 行不计入分母(不算污染也不算主题)-旧 + 新 视为 1 个有效变更单元(2 行算 2 行;删除块/新增块按行数计)纯度门槛与处理:
| 纯度 | 处理 |
|---|---|
| ≥ 30% | 文件保留在原分组 |
| < 30% | 强制移出原分组,回退到 P4 独立 commit 流程;不询问用户、不做交互 |
边界情况:
纯度校验失败的 commit message 处理:当文件被移出 P0 / P1 / P2 进入 P4 后,其独立 commit 的 type 和描述按该文件残留 diff 的真实主题生成,不得继续使用原分组的主题词(例如不能继续叫"统一国际化...")。AI 需要根据移出文件的真实修改内容识别 type(fix / refactor / feat 等)并生成准确描述。
变更文件总数 ≤ 3 个:跳过分组分析,直接将全部文件合并为 1 个 commit,避免过度切分。
对每个分组(含单文件 P4)依次执行:
git reset HEAD # 清空暂存区
git add <该分组所有文件>
git commit -m "<分组 commit message>"
如果 commit 失败(如 pre-commit hook 拒绝)→ 停止,保留当前状态,提示用户处理后手动重新执行。
分组 commit message 基于该分组所有文件的聚合 diff 生成,而非单文件 diff:
| 变更类型 | type | 示例 |
|---|---|---|
| 新增功能/文件 | feat | feat: 新增会议模块 API 接口与对应文档 |
| Bug 修复 | fix | fix: 修复登录页面输入框焦点丢失问题 |
| 重构 | refactor | refactor: 重构网络请求拦截器结构 |
| 文档 | docs | docs: 更新 API 接口说明文档 |
| 配置/构建 | chore | chore: 更新依赖版本 |
| 样式/资源 | style | style: 更新登录页面布局样式 |
P0 字符串替换组专属描述格式:chore: 统一 <变更对象> 为 <新值> 或 refactor: 将 <旧值> 重命名为 <新值>
Commit message 要求:
Co-Authored-By 行执行 /dt:push --preview 时,Step 4 仅展示分组结果与生成的 commit messages,不执行任何 git 操作:
[preview] 检测到 N 个逻辑分组:
分组 1(P0 字符串替换,3 个文件)
文件:app/api/meeting.py, docs/api.md, scripts/sync_swagger.sh
将提交:chore: 统一测试环境域名地址为 new.example.com
分组 2(P3 同主题新增,2 个文件)
文件:skills/push/SKILL.md, skills/push/README.md
将提交:feat: 新增 push skill 文档
分组 3(P4 独立,1 个文件)
文件:README.md
将提交:docs: 更新项目简介
输入 yes 确认执行,或输入 no 退出。
如果前面没有产生新的工作区提交,但 Step 0 检测到本地分支领先 upstream,则此处直接推送已有本地 commit。
推送代码:
已有 upstream:
git push $REMOTE $BRANCH
无 upstream(首次推送当前分支):
git push -u $REMOTE $BRANCH
如果推送失败(远程有新提交),执行重试:
git pull --rebase $REMOTE $BRANCH
git push origin HEADremote / local / mergegit rebase --continue 成功后 → 再次执行 git push origin HEAD分支硬约束:push 失败 / rebase 失败时,若无法通过"逐文件三选一"解决,直接停止并向用户展示 git status,不得创建任何新分支规避冲突。
创建 Tag(仅当提供了版本号时):
首先检查 tag 是否已存在:
git tag -l "X.Y.Z"
git tag "X.Y.Z"
git push origin "X.Y.Z"
Tag 命名格式:直接使用用户提供的版本号,例如 1.2.2
执行完成后,远程仓库应包含:
X.Y.Z)styleremote / local / merge;任何情况下都不得通过创建新分支规避冲突v 前缀,例如 1.2.2/dt:push --preview 仅预览分组方案与 commit messages,不执行任何 git 操作git commit 但未 git push 的场景属于正常路径;工作区干净时不能据此直接退出