分支策略实操
目标
从零开始创建一个仓库,直观确认三种集成方式(no-ff 合并、快进、cherry-pick)会在历史中留下怎样不同的结果,并通过统计这些结果编写策略对照表。
为什么重要
分支策略之争通常最终会沦为偏好之争。但真正需要回答的决策问题只有一个:发生故障时,计划如何在这个仓库中找到致因 commit。--no-ff 合并会保留“这一组变更何时被集成”的记录;快进能提供线性历史,但会抹去集成时间点;squash 合并则把二分查找的分辨率固定为 PR 大小,并且从 Git 的角度看,该分支从未被合并过。这些差异不能只靠阅读说明来掌握,必须亲自用 git rev-list --min-parents=2 统计,才能真正理解。
步骤
此镜像未配置全局 git 身份。在每个新建仓库中,必须本地配置 git config user.email / git config user.name,否则 commit 会失败。
- 在
/root/gitx1/repo创建仓库。当前分支必须是main;仓库中必须已设置本地user.email和user.name;并且至少要有 1 个包含README.md的 commit。 - 创建
feature/login分支并堆叠 2 个 commit。commit 标题必须准确为login: form、login: validation,分支中必须存在login.txt文件。以login:开头的 commit 必须恰好为 2 个。 - 切换到
main,合并feature/login,并确保保留 merge commit(即拥有两个父节点的 commit)。合并后,main中必须存在login.txt。 - 从
main创建feature/quick分支,生成一个添加quick.txt的 commit。标题必须准确为quick: typo。随后在main上执行仅快进合并。该 commit 必须只有 1 个父节点,并且main的 merge commit 数量必须仍是第 3 步创建的 1 个。 - 在
main当前位置创建附注标签v1.0.0。标签消息不得为空。 - 从
v1.0.0所在位置创建release/1.0分支,并生成一个添加hotfix.txt的 commit。标题必须准确为hotfix: null guard。然后将同一变更也应用到main。条件:两个分支中的hotfix.txt内容相同;两边都拥有hotfix: null guardcommit;但两个 commit 的哈希必须不同。 - 整理分支。删除
feature/quick,保留feature/login,再创建新的feature/wip分支,并堆叠至少 1 个main中不存在的 commit(必须仍处于尚未合并的状态)。 - 编写
/root/gitx1/strategy.md。必须包含:merge_commits=—main的 merge commit 数量branches=— 此仓库的本地分支数量tags=— 标签数量- 对 Git Flow / GitHub Flow / Trunk-Based 三种策略的说明
- 判断此仓库更接近哪种策略(必须包含“此仓库”“我们”或“当前”中的一种表述)
参考
- 身份设置:在仓库中执行
git config user.email "you@lab.local"和git config user.name "Lab"。不要使用--global。 - 默认分支名称:使用
git init -b main,或在执行git init后通过git symbolic-ref HEAD refs/heads/main修改。 - 统计数量:merge commit 使用
git rev-list --min-parents=2 --count main,分支使用git branch --format='%(refname:short)' | grep -c .,标签使用git tag | grep -c .。 - 常见错误 1:第 3 步直接使用
git merge。如果历史尚未分叉,就会发生快进,不会生成 merge commit。 - 常见错误 2:第 6 步使用
git merge release/1.0。这会多生成一个 merge commit,导致第 4 步的评分失败。请使用只取回单个 commit 的命令。 - commit 标题的大小写和空格必须完全一致。请用
git log --format='%s'检查。
创建仓库并设置身份
在 /root/gitx1/repo 创建仓库。当前分支必须是 main;仓库中必须已设置本地 user.email 和 user.name;并且至少要有 1 个包含 README.md 的 commit。
在 /root/gitx1/repo 中执行 git init,并将默认分支名称设为 main。此镜像没有全局 git 身份;如果不直接为这个仓库设置 user.email 和 user.name,commit 本身就会失败。
在功能分支上堆叠 2 个 commit
创建 feature/login 分支并堆叠 2 个 commit。commit 标题必须准确为 login: form、login: validation,分支中必须存在 login.txt 文件。以 login: 开头的 commit 必须恰好为 2 个。
创建 feature/login 分支,将对 login.txt 的修改分两次提交。commit 标题必须与指定字符串完全一致,并确保以 login: 开头的 commit 恰好有 2 个。
禁止快进地完成合并
切换到 main,合并 feature/login,并确保保留 merge commit(即拥有两个父节点的 commit)。合并后,main 中必须存在 login.txt。
切换到 main 并合并 feature/login,同时确保留下拥有两个父节点的 commit。如果直接 merge,可能会快进,从而丢失集成记录。
通过快进合入小型修正
从 main 创建 feature/quick 分支,生成一个添加 quick.txt 的 commit。标题必须准确为 quick: typo。随后在 main 上执行仅快进合并。该 commit 必须只有 1 个父节点,并且 main 的 merge commit 数量必须仍是第 3 步创建的 1 个。
创建 feature/quick,提交 quick.txt,然后在 main 上仅以快进方式合并。如果生成 merge commit,此步骤就会失败。使用 --ff-only 可在条件不满足时直接拒绝操作。
添加发布标签
在 main 当前位置创建附注标签 v1.0.0。标签消息不得为空。
在 main 上添加 v1.0.0 标签。轻量标签只是一个指针,不会保留作者和消息;请使用会创建标签对象的选项。
将发布分支的 hotfix 带到 main
从 v1.0.0 所在位置创建 release/1.0 分支,并生成一个添加 hotfix.txt 的 commit。标题必须准确为 hotfix: null guard。然后将同一变更也应用到 main。条件:两个分支中的 hotfix.txt 内容相同;两边都拥有 hotfix: null guard commit;但两个 commit 的哈希必须不同。
从 v1.0.0 创建 release/1.0 并提交 hotfix.txt,然后将同一变更也应用到 main。不要合并整个分支,只取回这一个 commit。
cherry-pick 会重新应用变更并创建新的 commit,而不是移动 commit。不过,本实验中 release/1.0 是从 main 尾部直接创建的,因此两个 commit 的父节点相同。请使用 git cherry-pick -x。这样会在消息中附加一行 (cherry picked from commit ...),从而在历史中保留其来源,并确保两个 commit 对象确实不同。这正是实际工作中将 hotfix 应用到多个分支时推荐的方式。
只清理已合并的分支
整理分支。删除 feature/quick,保留 feature/login,再创建新的 feature/wip 分支,并堆叠至少 1 个 main 中不存在的 commit(必须仍处于尚未合并的状态)。
删除已经完成合并的分支,保留尚未合并的分支。先用 git branch --merged main 确认哪些分支可以安全删除。使用 -d 而不是 -D,可以防止误删未合并分支。
统计仓库状态并编写策略对照表
编写 /root/gitx1/strategy.md。必须包含:
merge_commits=—main的 merge commit 数量branches=— 此仓库的本地分支数量tags=— 标签数量- 对 Git Flow / GitHub Flow / Trunk-Based 三种策略的说明
- 判断此仓库更接近哪种策略(必须包含“此仓库”“我们”或“当前”中的一种表述)
在 /root/gitx1/strategy.md 中,根据实际仓库统计值填写 merge_commits= / branches= / tags= 三行;比较三种分支策略,然后判断此仓库更接近哪一种。请使用 git 命令统计数值。