chore: 新增并完善发布 Git 分支 skill (#63)

* chore: add publishing git branches skill

* chore: 完善发布 Git 分支 skill
This commit is contained in:
YBF
2026-09-17 09:57:45 +08:00
committed by GitHub
parent 92bc7958e7
commit 601bacaecc
4 changed files with 130 additions and 162 deletions
@@ -0,0 +1,102 @@
---
name: publishing-git-branches
description: Use when preparing the current Git feature branch for a pull request to origin/main, especially when its name, remote state, or base may be stale.
---
# Publishing Git Branches
Prepare the current branch for review without bypassing the PR path. Keep `main`
unchanged locally and remotely; only the PR may carry the branch's commits into
`main`.
## Hard Stop: Rebase Conflict
**Any rebase conflict means stop immediately and ask the user. There are no
exceptions for a one-line, mechanical, generated, obvious, or inferred
conflict.** Do not modify a conflicted file, stage it, continue, abort, or run
another Git command intended to move the rebase forward. Report the conflict
paths and wait for an explicit next instruction.
## Workflow
1. Inspect the current branch name and its commits/diff relative to `origin/main`.
If the name is clearly about a different change, rename the local branch to a
concise name that reflects the actual changes before pushing. If the old name
already exists remotely, do not delete it unless the user separately asks.
2. Run `git fetch origin main`. Do not use `git pull` to update the branch.
3. Rebase the current branch onto the fetched `origin/main` with `git rebase
origin/main`.
4. If the rebase has any conflict, apply the hard stop above.
5. Push the current branch to `origin`. If rebasing rewrote a branch already on
the remote, update it only with `git push --force-with-lease origin HEAD`.
Never use plain `--force`.
6. Create a PR with the current branch as head and remote `main` as base. Use
the repository's configured PR tooling, write its title and body in Chinese,
and report its URL.
## Naming And PR Language
When a branch needs a new name, use `<type>/<short-kebab-topic>`. Choose the
type for the actual change:
| Type | Use for |
| --- | --- |
| `feat` | 新功能或用户可见能力 |
| `fix` | 缺陷修复 |
| `chore` | 维护、工具或流程变更 |
| `docs` | 文档变更 |
| `refactor` | 不改变预期行为的重构 |
| `test` | 测试覆盖或测试工具变更 |
| `ci` | 持续集成或发布自动化变更 |
Do not rename an existing branch solely to normalize its style; this convention
applies when creating a branch or correcting an obvious semantic mismatch.
PR title and body must be Chinese. The title must begin with the conventional
type prefix that matches the change, for example `fix: 修复 Chrome 消息回调兼容性`;
its descriptive text must be Chinese. The body must describe the change,
validation, and any risk or follow-up in Chinese.
## Invariants
- Do not fast-forward, merge, or push the current branch into local or remote
`main`; changes reach `main` only through the PR.
- Branch renaming is required only for an obvious semantic mismatch, not a
stylistic preference. State the evidence used to judge the mismatch.
- PR title must use the type prefix that matches the change, such as `feat:` or
`fix:`; the remaining title text and body use Chinese.
- Do not assume permissions for unrelated cleanup, conflict resolution, merging
the PR, deleting a branch, or force-pushing any branch other than the current
rebased branch.
## Command Shape
Use the repository's existing validation steps before push when they are known
or requested. The Git and PR sequence should have this shape:
```bash
git branch --show-current
git log --oneline origin/main..HEAD
git diff --stat origin/main...HEAD
# rename with `git branch -m <type>/<short-kebab-topic>` if the name clearly mismatches
git fetch origin main
git rebase origin/main
git push origin HEAD
# if the rebased branch already exists on origin:
git push --force-with-lease origin HEAD
# PR 标题和正文使用中文;标题必须以 `feat:`、`fix:` 或 `chore:` 等类型前缀开头
gh pr create --base main --head "$(git branch --show-current)" \
--title "<type>: <中文标题>" \
--body "<中文正文:改动、验证、风险或后续事项>"
```
Do not run the force-with-lease command after a successful ordinary push.
## Red Flags
- “The conflict is trivial” or “I can infer the intended resolution” means
stop and ask; it is still a rebase conflict.
- “It is only a typo/urgent fix” does not excuse retaining a clearly mismatched
branch name or bypassing the PR route.
- “A merge is quicker” does not replace the required rebase.
- English PR titles or bodies do not meet this project's PR writing convention.
@@ -0,0 +1,3 @@
interface:
display_name: "Publishing Git Branches"
short_description: "Safely rebase a branch and open a main PR"
+23 -160
View File
@@ -1,173 +1,36 @@
# Git 分支管理与主干合并
# Git 分支约束
> 规范规定以 `main` 为集成基线、以个人开发分支(`<dev-branch>`)承载变更时的同步、变基和合并方式
> 文只定义本项目的分支角色与不可突破的安全边界;不规定提交、同步、变基、推送或创建 PR 的操作步骤
## 1. Scope / Trigger
## Scope
以下场景必须应用本规范:
涉及 `main`、开发分支、共享分支或远程历史改写时,先确认本约束。具体发布流程由适用的发布 skill 或仓库托管规则决定,不在本文重复。
-`main` 创建或继续维护开发分支;
- `main` 在开发分支上次 `rebase` 后继续产生提交;
- 准备把 `<dev-branch>` 等开发分支合入 `main`
- 开发分支已经推送到远程,需要推送变基后的历史。
## 分支角色
本规范维护的核心不变量是:合入前,开发分支必须基于最新的 `main``main` 只接受可验证的快进合并,避免无意产生额外的 merge commit
- `main` 是集成与发布候选分支;不得直接在其上开发功能
- 开发分支应承载单一、明确且可追踪的工作项。
- 共享开发分支的协作方式由该分支的团队约定决定;个人不得单方面改写其历史。
## 2. Signatures / Commands
## 历史与权限边界
以下命令中的 `<dev-branch>` 是占位符,执行时替换为实际的开发分支名称:
- 只有分支所有者或获得明确授权的人,才能改写已推送的个人开发分支历史。
- 改写个人开发分支的远程历史时,只能使用带 lease 的保护性机制;不得以无条件强制推送绕过远程状态检查。
- 禁止改写 `main` 的远程历史,包括任何形式的强制推送。
- 发现远程分支出现未预期的新提交、保护规则拒绝操作或身份/权限不明确时,停止会改变远程历史的操作并请求确认。
```bash
# 同步远程 main 并快进本地 main
git fetch origin main
git switch main
git merge --ff-only origin/main
## 集成约束
# 把开发分支变基到最新 main
git switch <dev-branch>
git rebase main
- 进入 `main` 的变更必须保持基于当前集成基线,并满足仓库配置的保护规则与验证门槛。
- 不得以普通合并、跳过验证或绕过分支保护来掩盖基线过期、历史分叉或检查失败。
- 具体采用 PR、快进或其他受仓库保护规则允许的集成机制,以发布 skill 和托管平台的当前规则为准。
# rebase 改写了已推送的开发分支时使用
git push --force-with-lease origin <dev-branch>
## 非目标
# 合入前重新确认 main,并执行快进合并
git switch main
git fetch origin main
git merge --ff-only origin/main
git merge --ff-only <dev-branch>
git push origin main
```
本文不定义下列执行细节:
`git pull --ff-only origin main` 可以替代 `fetch``merge --ff-only origin/main`,但不能省略 `--ff-only`
- 如何同步远程 `main`、何时执行 rebase,或 rebase 冲突如何处置;
- 推送、分支改名、创建/合并 PR 的命令和顺序;
- 面向受影响代码的测试、类型检查、lint 与构建命令。
## 3. Contracts
### 3.1 分支角色
- `main` 是集成基线和发布候选分支;不在其上直接开发功能。
- `<dev-branch>` 是承载单项工作的开发分支,应对应一个明确、可追踪的工作项。
- 只有分支所有者或明确授权者才可以对已推送的个人开发分支执行改写历史的推送。
- 共享开发分支不得由个人单方面 `rebase`;需要保留共享历史时,使用普通合并或团队约定的 PR 流程。
### 3.2 同步与变基
- 变基目标必须是已经通过 `origin/main` 快进更新的本地 `main`,不能依赖未更新的旧 `origin/main`
- `main` 在上次变基后新增提交时,必须再次执行 `git rebase main`;这不是异常,而是合入前保持基线最新的正常流程。
- 变基过程中出现冲突,必须解决并完成变基、取消变基,或停下来请求处理;不能把未完成的变基状态当成可合入状态。
- 在变基完成后到最终合并之间,如果 `main` 再次前进,必须回到变基步骤重新处理。
### 3.3 历史改写与合并
- `rebase` 会为开发分支上的提交生成新的 commit hash;远程开发分支已存在时,推送必须使用 `--force-with-lease`
- `--force-with-lease` 只允许在远程分支仍处于预期状态时覆盖它;被拒绝时必须先检查远程新增提交,不能直接改用 `--force`
- 主干同步优先使用 `git fetch origin main`,避免在强制推送前无目的地刷新 `origin/<dev-branch>`;如必须先全量 fetch,应先记录预期旧 SHA,并使用显式 `--force-with-lease=refs/heads/<dev-branch>:<expected-sha>`
- `main` 的最终合并使用 `git merge --ff-only <dev-branch>`。失败表示开发分支不是当前 `main` 的直接后继,应先更新 `main` 并重新变基。
- 禁止对 `main` 使用 `push --force``push --force-with-lease`
- 如果仓库启用了 `main` 保护或强制 PR,推送开发分支后通过 PR 合入;PR 的目标分支仍必须是最新 `main`,并通过相同的检查门槛。
## 4. Validation & Error Matrix
| 条件 | 必须处理 | 禁止的应对 |
| --- | --- | --- |
| 工作区有未提交改动 | 先提交、明确保存或清理改动,再切换分支和变基 | 直接在不清楚范围的状态下 `rebase` |
| `git merge --ff-only origin/main` 失败 | 检查本地 `main` 是否有未发布提交或历史分叉,确认后再决定 | 改用普通 `merge` 掩盖 `main` 分叉 |
| `rebase` 产生冲突 | 解决后 `git add``git rebase --continue`;无法处理时 `git rebase --abort` | 跳过冲突或强行推送半成品 |
| `--force-with-lease` 被拒绝 | 查看远程开发分支新增的提交,确认是否需要整合他人改动 | 直接改用 `--force` 覆盖远程历史 |
| `git merge --ff-only <dev-branch>` 失败 | 说明 `main` 在变基后继续更新,或开发分支未基于当前 `main`;重新同步并变基 | 用普通 `merge` 生成隐藏分叉 |
| 目标代码的测试、类型检查、构建或 lint 失败 | 修复失败项后再合入 | 以分支图形“看起来正确”代替代码验证 |
| 推送 `main` 被拒绝 | 检查保护规则或远程是否又有更新,按 PR 流程处理 | 对 `main` 强制推送 |
## 5. Good / Base / Bad Cases
- **Good**`main` 更新后,先快进本地 `main`,再让 `<dev-branch>` `rebase main`;验证通过后,必要时用 `--force-with-lease` 更新远程开发分支,最后再次确认 `main` 没有前进,再用 `merge --ff-only` 合入。
- **Base**`main` 在变基后没有新提交,`git merge --ff-only <dev-branch>` 成功,历史保持一条直线。
- **Bad**`<dev-branch>` 已经基于旧 `main`,直接执行普通 `git merge <dev-branch>`;虽然可能成功,但会把旧基线和额外 merge commit 带入 `main`,隐藏真正的同步关系。
## 6. Tests Required
Git 操作本身不替代代码验证。合入前至少确认:
```bash
# 工作区和当前分支状态可解释
git status --short --branch
# main 是 dev 的祖先;返回码 0 才允许执行 ff-only 合并
git merge-base --is-ancestor main <dev-branch>
# 检查待合入差异中的空白错误
git diff --check main...<dev-branch>
```
此外,按照受影响包的规范执行目标单元测试、类型检查、lint 和构建;断言点是所有检查成功、没有未解决冲突,且 `git merge --ff-only <dev-branch>` 能成功完成。
## 7. Wrong vs Correct
### Wrong
```bash
git switch main
git merge <dev-branch>
git push origin main
# 开发分支推送失败后直接覆盖远程
git switch <dev-branch>
git push --force origin <dev-branch>
```
### Correct
```bash
git switch main
git fetch origin main
git merge --ff-only origin/main
git switch <dev-branch>
git rebase main
# 运行受影响包的测试、类型检查、lint 和构建
git push --force-with-lease origin <dev-branch>
# 在真正合入前,以此处的 main 作为最后基线
git switch main
git fetch origin main
git merge --ff-only origin/main
git switch <dev-branch>
# 如果 main 在前一步有更新,这就是第二次 rebase;没有更新时也是安全的 no-op
git rebase main
# main 有更新时需要重新运行上面的验证;必要时再次 force-with-lease 推送开发分支
git switch main
git merge --ff-only <dev-branch>
git push origin main
```
### Design Decision: Rebase Development Branches, Fast-Forward Main
**Context**:开发分支可能在不同时间点同步 `main`,而普通合并会在主干留下额外拓扑和难以解释的 merge commit。
**Options Considered**
1. 每次把 `main` 普通合并进开发分支,再普通合并回 `main`;保留所有拓扑,但主干噪声较多。
2. 开发分支变基到最新 `main`,主干只做快进合并;历史线性,但开发分支 commit hash 会改变。
**Decision**:采用选项 2。历史改写只限于个人开发分支,并用 `--force-with-lease` 防止覆盖他人新提交;`main` 使用 `--ff-only` 作为结构性门禁。
## Common Mistakes
### Common Mistake: 只在第一次开发时 rebase
**Symptom**:开发分支第一次已经基于 `main`,但 `main` 后续继续更新,最终合并仍然使用旧基线。
**Cause**:把“已经 rebase 过”误认为“永远与 main 同步”。
**Fix**:在最终合并前再次更新本地 `main`;只要 `main` 有新提交,就重新执行 `git rebase main`
**Prevention**:把“合并前再次确认 `main`”作为固定流程,并用 `git merge --ff-only` 让过期基线显式失败。
### Common Mistake: 把 `--force-with-lease` 当成无条件安全
**Symptom**:误以为任何情况下都可以安全覆盖远程开发分支。
**Cause**:忽略它仍然是覆盖远程引用的操作,只是增加了远程状态检查。
**Fix**:被拒绝时先查看远程新增提交,确认是否需要整合;不使用 `--force` 绕过保护。不要在推送前无目的地全量 `fetch` 后,就假设默认 lease 仍然知道 fetch 前的远程状态;必要时使用显式 expected SHA。
**Prevention**:仅对个人开发分支使用,并在团队文档中明确共享分支禁止历史改写。
这些步骤属于发布工作流,不应在项目级约束中形成第二套操作规范。
+2 -2
View File
@@ -15,7 +15,7 @@
| [缺失值与默认值](./missing-values.md) | 缺失值、默认值、`BUILD_HASH` 和补偿逻辑的边界契约 | 已建立约束 |
| [结构化值等价性](./structured-value-equality.md) | 结构化快照、身份、集合与序列的等价边界 | 已建立约束 |
| [数据库查询组合](./database-query-composition.md) | SQL JOIN 禁止默认、受限查询与内存组合、例外证据门槛 | 已建立约束 |
| [Git 分支管理](./git-branch-management.md) | `main` 同步、开发分支 rebase、`--force-with-lease` 与快进合并 | 已建立约束 |
| [Git 分支约束](./git-branch-management.md) | 分支角色、历史改写权限、`main` 保护与集成边界 | 已建立约束 |
## 开始前检查
@@ -26,4 +26,4 @@
5. 涉及配置、环境变量、协议字段、构建标识或默认值时,必须阅读 [缺失值与默认值](./missing-values.md)。
6. 涉及去重、幂等、持久化回读、回执核验、缓存命中或结构化 payload 的等价判断时,必须阅读 [结构化值等价性](./structured-value-equality.md)。
7. 涉及 SQL/ORM 查询、关系数据读取或投影组合时,必须阅读 [数据库查询组合](./database-query-composition.md)。
8. 任务涉及多个 Git 分支、更新 `main` 或合入主干时,先阅读 [Git 分支管理](./git-branch-management.md)。
8. 任务涉及多个 Git 分支、更新 `main` 或合入主干时,先阅读 [Git 分支约束](./git-branch-management.md)。