Spec Kit git 扩展 speckit.git.feature 命令详解:特性分支创建、编号策略与分支命名模板
2026/9/5 16:43:10 网站建设 项目流程

Spec Kit git 扩展 speckit.git.feature 命令详解:特性分支创建、编号策略与分支命名模板

【免费下载链接】spec-kit💫 Toolkit to help you get started with Spec-Driven Development项目地址: https://gitcode.com/GitHub_Trending/sp/spec-kit

本文围绕 Spec Kit(spec-kit)中 git 扩展的核心命令speckit.git.feature展开,系统讲解它如何在 SDD(Spec-Driven Development,规格驱动开发)工作流中自动创建并切换特性分支:包括分支编号的两种模式(顺序号/时间戳)、配置解析优先级、branch_template分支命名模板的四类占位符与校验规则,以及底层脚本对编号探测、命名长度限制与无 Git 环境的降级处理机制。读完后你可以完整掌握该命令的触发方式、全部可配置参数,并能结合仓库源码理解其实现细节。

命令定位:它只负责"建分支",不负责"建规格"

speckit.git.feature是 git 扩展命令定义 描述的一条 Agent 可执行命令,其 frontmatter 中声明的职责是:

Create a feature branch with sequential or timestamp numbering(创建顺序编号或时间戳编号的特性分支)

文档开篇即明确了职责边界:该命令只处理分支创建("This command handles branch creation only"),而规格目录与规格文件的创建由核心的 specify 工作流完成。这一点在 git 扩展清单 中也能得到印证:speckit.git.feature被注册为before_specify钩子,且optional: false——也就是说,当 git 扩展被安装后,每次执行核心 specify 命令之前它会强制先运行,为本次规格创建一条专属分支。核心 specify 模板 中同样写明:Pre-Execution Checks 阶段会读取.specify/extensions.ymlhooks.before_specify配置并等待钩子执行完毕后再继续。

git 扩展在 扩展 README 中的定位是一个"可选、自包含"的 Git 操作模块,除分支创建外还提供仓库初始化、分支校验、远端检测和自动提交等能力;speckit.git.feature是其中承担特性分支生命周期管理的一环。

安装与启停方式(来自扩展 README):

# 安装内置 git 扩展(无需网络) specify extension add git # 禁用(禁用后仍会继续创建规格,只是不建分支) specify extension disable git # 重新启用 specify extension enable git

扩展安装时会把 配置模板 复制为项目内的.specify/extensions/git/git-config.yml,后续所有分支行为都由该文件驱动。

用户输入与执行前置检查

命令定义中的User Input一节直接嵌入占位符$ARGUMENTS,并要求 Agent "MUST consider the user input before proceeding (if not empty)"——即用户随命令一起输入的文字(通常是特性描述)必须被纳入分支命名的考量。

执行前置条件只有一条,且非常具体:

git rev-parse --is-inside-work-tree 2>/dev/null

用该命令探测当前目录是否处于 Git 工作树内。如果 Git 不可用,命令不会报错中断,而是"警告用户并跳过分支创建"(见下文"优雅降级"一节)。

GIT_BRANCH_NAME 环境变量覆盖:精确指定分支名

命令定义中的Environment Variable Override一节给出了最高优先级的分支命名通道:当用户通过环境变量、参数或请求显式提供GIT_BRANCH_NAME时,Agent 必须把该值注入环境后再调用脚本。此时脚本的行为是(来自命令文档,与 Bash 脚本实现 逐条对应):

  • 使用精确值作为分支名,绕过所有前缀/后缀生成逻辑;
  • --short-name--number--timestamp三个标志全部被忽略
  • FEATURE_NUM的提取规则:若分支名的最后一个路径段以"数字或时间戳特性标记"开头(例如042-namefeat/042-namejdoe/app/042-name),则提取该前缀作为特性编号;否则FEATURE_NUM取完整分支名。

脚本内部的对应实现是extract_feature_num_from_branch:先尝试匹配^[0-9]{8}-[0-9]{6}-的时间戳前缀,再退化为匹配任意数字前缀^[0-9]+-,都匹配不到则回退为完整分支名(见 create-new-feature-branch.sh 与 Python 版同构实现)。

值得注意的是,GIT_BRANCH_NAME的精确值仍需满足 GitHub 的 244 字节分支名上限,超限会直接报错退出而不是截断——因为截断一个"用户精确指定"的名字是不被允许的(详见下文长度限制一节)。

分支编号模式:四级配置解析顺序

命令定义中的Branch Numbering Mode一节规定了编号策略(顺序号或时间戳)的解析顺序,这是该命令最容易被忽略的配置细节:

  1. 检查.specify/extensions/git/git-config.yml中的branch_numbering值;
  2. 检查.specify/init-options.json中的feature_numbering值(继承自 Spec Kit 核心);
  3. 检查.specify/init-options.json中的branch_numbering值(已弃用,仅为向后兼容,未来版本将移除);
  4. 以上都不存在时,默认为sequential

这一"扩展配置优先、核心配置兜底"的优先级设计有测试佐证:tests/test_branch_numbering.py 验证了自 v0.10.0 起specify init--branch-numbering命令行标志已被移除(传入会报No such option),分支编号完全交由 git 扩展的配置文件管理。

配置模板中两种模式的语义如下(来自 git-config.yml 的注释):

  • sequential:生成001002… 三位零填充顺序号;
  • timestamp:生成YYYYMMDD-HHMMSS时间戳前缀。

分支名模板:branch_template 与 branch_prefix

命令定义的Branch Name Template一节说明:脚本会读取git-config.yml中可选的branch_template。为空或缺失时使用默认形态{number}-{slug};若设置了模板,则必须满足两条结构约束——{slug}不得出现在{number}之前,且最终路径段必须以{number}-开头。脚本会展开以下四个占位符:

占位符含义取值来源(源码佐证)
{author}净化后的 Git 作者名git config user.name,缺失时回退user.email@前本地部分,再缺失时回退系统USER环境变量(get_author_token)
{app}净化后的 Spec Kit 初始化目录名取仓库根目录的 basename(get_app_token)
{number}顺序号或时间戳见"编号自动确定"一节
{slug}生成的短分支名--short-name指定或自动从特性描述提炼

"净化"由clean_branch_name完成:转小写、非字母数字替换为连字符、压缩连续连字符、去除首尾连字符(create-new-feature-branch.sh)。

对 monorepo 场景,文档给出模板{author}/{app}/{number}-{slug}可以生成形如jdoe/web/008-guided-tour的分支名,同时保持"每个项目独立的特性编号"。这条能力在 tests/extensions/git/test_git_extension.py 中有专门测试:test_branch_template_adds_author_and_app_namespace验证命名空间生成,test_branch_template_scopes_number_after_numeric_app_namespacetest_branch_template_scopes_existing_branch_numbers验证编号探测按模板前缀隔离——即只统计同一命名空间下的分支序号,避免 A 项目的分支抬高 B 项目的序号。

三条模板校验规则在脚本中是硬性错误(不满足直接exit 1):

  1. 必须包含{number}占位符——否则生成的分支不再是合法的特性分支;
  2. {slug}不得出现在{number}之前——{slug}只能用于最终特性段;
  3. 最终路径段必须以{number}-开头。

对应实现见 validate_branch_template(Python 版同逻辑见 create_new_feature_branch.py),测试用例test_branch_template_requires_number_tokentest_branch_template_rejects_slug_before_numbertest_branch_template_requires_feature_segment_to_start_with_number分别覆盖三类错误输入。

除模板外还有一个更简单的入口:branch_prefix。它是"纯命名空间"的简写,展开规则为<branch_prefix>/{number}-{slug}(前缀以/结尾时不重复添加分隔符),见 resolve_branch_template。例如配置branch_prefix: "features/{app}"会展开为features/{app}/{number}-{slug}(配置模板注释 中的示例)。优先级上branch_template高于branch_prefix:模板非空时直接采用,只有模板为空才回退到前缀展开。

配置示例(取自 git 扩展 README 的完整配置段):

# 分支编号策略: "sequential" 或 "timestamp" branch_numbering: sequential # 可选分支名模板。留空则使用默认 "{number}-{slug}" # 支持占位符: {author}, {app}, {number}, {slug} # {slug} 不得出现在 {number} 之前; 最终路径段必须以 {number}- 开头 # monorepo 示例: "{author}/{app}/{number}-{slug}" branch_template: "" # 可选的简写命名空间。留空则按 branch_template/默认行为 # 示例: "features/{app}" 展开为 "features/{app}/{number}-{slug}" branch_prefix: "" # git init 时使用的自定义提交信息 init_commit_message: "[Spec Kit] Initial commit"

执行:脚本调用方式与硬性规则

命令定义要求 Agent 先为分支生成一个简洁短名(2–4 个词):分析特性描述、提取最有意义的关键词、尽量使用"动词-名词"结构(如add-user-authfix-payment-bug)、保留技术术语与缩写(OAuth2、API、JWT)。随后按平台选择脚本执行(以下命令原文来自命令文档的Execution一节):

  • Bash.specify/extensions/git/scripts/bash/create-new-feature-branch.sh --json --short-name "<short-name>" "<feature description>"
  • Bash(时间戳模式).specify/extensions/git/scripts/bash/create-new-feature-branch.sh --json --timestamp --short-name "<short-name>" "<feature description>"
  • PowerShell.specify/extensions/git/scripts/powershell/create-new-feature-branch.ps1 -Json -ShortName "<short-name>" "<feature description>"
  • PowerShell(时间戳模式).specify/extensions/git/scripts/powershell/create-new-feature-branch.ps1 -Json -Timestamp -ShortName "<short-name>" "<feature description>"

文档同时给出四条IMPORTANT规则,理解它们是正确驱动该命令的关键:

  1. 不要传--number——脚本会自动确定正确的下一个编号(编号探测逻辑见下节);
  2. 始终带 JSON 标志(Bash 为--json,PowerShell 为-Json),保证输出可被可靠解析;
  3. 每个特性只能运行一次该脚本——因为顺序号是"探测最大值 +1"的副作用操作,重复运行会抬高编号;
  4. 不要手工展开branch_template——脚本自己读取扩展配置并一致地应用模板。

从脚本的--help输出(create-new-feature-branch.sh)可以看到完整参数面,供直接手工调用时参考:

参数作用
--json以 JSON 格式输出
--dry-run只计算分支名,不实际创建分支
--allow-existing-branch分支已存在时切换到它,而不是失败
--short-name <name>提供自定义短名(2–4 个词)
--number N手工指定分支编号(覆盖自动探测),必须为非负整数
--timestamp用时间戳前缀(YYYYMMDD-HHMMSS)替代顺序编号
环境变量GIT_BRANCH_NAME使用该精确分支名,绕过所有前缀/后缀生成

除 Bash 与 PowerShell 两个实现外,仓库中还带有一份 Python 移植版 create_new_feature_branch.py(文件头注明它是 Bash/PowerShell 双实现的 Python port),并有专门的 parity 测试(test_git_extension_python_parity.py)保证三者行为一致,适合在缺少 Bash 环境的平台上直接运行。

编号是如何"自动确定"的

"不要传--number"这条规则背后,是一套三路取最大值的编号探测机制(check_existing_branches):

  1. specs 目录get_highest_from_specs):扫描specs/下所有子目录名,匹配"3 位及以上数字前缀"(^[0-9]{3,}-)且排除时间戳目录(^[0-9]{8}-[0-9]{6}-)的形态,取最大序号。这样即便没有 Git 仓库,或某次分支被删除,规格目录也能保证编号不回退;
  2. 本地与远端分支get_highest_from_branches):解析git branch -a输出,剥掉当前分支标记与remotes/<name>/前缀后按同样规则提取。默认路径会先执行git fetch --all --prune同步远端再统计;--dry-run路径则改走无副作用的git ls-remote --heads(设置GIT_TERMINAL_PROMPT=0防止交互提示),避免试算时产生网络写操作;
  3. 模板命名空间隔离:当配置了branch_template时,脚本从模板中{number}之前的部分渲染出scope_prefix(如jdoe/web/),编号探测只统计该前缀下的分支——这正是 monorepo 下"per-project feature numbering"得以成立的原因,测试test_branch_template_scopes_existing_branch_numbers覆盖了该行为。

最终编号取三路最大值加 1,并以printf "%03d"格式化为至少三位零填充数字(脚本主流程)。时间戳模式下则直接取date +%Y%m%d-%H%M%S,并且若同时传了--number会打印[specify] Warning: --number is ignored when --timestamp is used后清空编号。

短名自动生成的过滤逻辑

如果 Agent 没有传--short-name,脚本会自己从特性描述提炼短名(generate_branch_name,Bash / Python 逻辑一致):

  • 小写化、非字母数字转空格后逐词过滤:剔除内置停用词表(the、to、for、add、get、need 等 40 余个常见虚词);
  • 保留长度 ≥3 的词;短词仅在原文中以全大写形式出现时保留(用于留住 API、DB、JWT 这类缩写,恰好呼应命令文档中"保留技术术语与缩写"的要求);
  • 取前 3 个有意义的词(恰好有 4 个时取 4 个)用连字符拼接。

--short-name已提供,则跳过上述提炼,直接对指定值做clean_branch_name净化。

244 字节分支名长度限制

脚本内置了 GitHub 分支名 244 字节上限的处理(Bash / Python):

  • 若分支名来自GIT_BRANCH_NAME且超限:直接报错退出,提示"must be 244 bytes or fewer in UTF-8";
  • 若分支名为自动生成且超限:逐字符截断 slug 后缀(同时去掉尾部连字符)重新渲染模板,并输出警告[specify] Warning: Branch name exceeded GitHub's 244-byte limit,同时打印原始名与截断后的字节数;
  • 若模板前缀本身就超过 244 字节(截空 slug 仍超限):报错"Branch template prefix exceeds GitHub's 244-byte branch name limit"。

优雅降级:没有 Git 时也能走通

命令定义的Graceful Degradation一节与脚本行为一致:当未安装 Git 或当前目录不是 Git 仓库时——

  • 跳过分支创建并打印警告:[specify] Warning: Git repository not detected; skipped branch creation(脚本实际输出会附带计算出的目标分支名,见 警告分支);
  • 脚本仍然输出BRANCH_NAMEFEATURE_NUM,调用方可以照常引用(此时编号退化为仅扫描specs/目录);
  • 结合 扩展 README 的降级说明:规格目录仍会创建在specs/下,分支校验同样跳过,远端检测返回空结果——整个 specify 流程不因缺少 Git 而中断。

这与extension.ymlrequires.tools: git标记为required: false的声明互为印证:Git 是该扩展的"软依赖"。

输出:JSON 契约与下游消费

命令定义的Output一节约定脚本以 JSON 输出两个字段:

  • BRANCH_NAME:分支名,例如顺序模式下003-user-auth,时间戳模式下20260319-143022-user-auth,模板模式下jdoe/web/003-user-auth
  • FEATURE_NUM:所用的数字或时间戳前缀。

脚本在 JSON 模式下优先使用jq生成(无jq时回退到手工转义拼接,见 输出段);非 JSON 模式则输出BRANCH_NAME:/FEATURE_NUM:两行纯文本,方便人工阅读。

下游消费方是核心 specify 命令:specify 模板 明确规定,before_specify钩子成功后"已创建/切换到 git 分支并输出含BRANCH_NAMEFEATURE_NUM的 JSON,记下这些值供参考,但分支名不决定规格目录名";若用户提供了GIT_BRANCH_NAME,同样要求透传给钩子。这一契约保证了"分支创建"与"规格目录创建"两个职责可以各自演进而互不耦合。

延伸阅读

  • 命令定义(本文主体):speckit.git.feature.md
  • git 扩展总览、钩子表与安装方式:extensions/git/README.md
  • 扩展清单(命令注册、钩子声明、配置默认值):extensions/git/extension.yml
  • 配置模板/运行时配置:extensions/git/config-template.yml、extensions/git/git-config.yml
  • 三个平台实现:create-new-feature-branch.sh、create-new-feature-branch.ps1、create_new_feature_branch.py
  • 行为测试:tests/extensions/git/test_git_extension.py、tests/test_branch_numbering.py
  • 核心侧钩子消费逻辑:templates/commands/specify.md

【免费下载链接】spec-kit💫 Toolkit to help you get started with Spec-Driven Development项目地址: https://gitcode.com/GitHub_Trending/sp/spec-kit

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询