2026-07-29
今日主题
- Git 历史重写与安全推送
- Git 分布式协作模型
- Git 引用与分支跟踪模型
- gitignore 语法衍生的配置文件
新增认知
Git 历史重写与安全推送
-
重写与推送脉络:将个人提交重新落到社区主线,会改变提交的父节点和 SHA,因此远端更新不再是 fast-forward,必须强制推送。
安全性问题由此从“如何整理提交线”延伸为“如何在重写远端历史时避免覆盖并发提交”。 -
分支职责隔离:代码修复与个人研究文档应分别从同一个社区基线派生为独立分支,而不是都堆在长期同步分支上。这样每条提交线只表达一种交付意图,
后续同步、审查和回滚不会互相牵连;前提是两类改动确实不存在依赖关系。 -
等价不等于同 SHA:rebase 或 cherry-pick 会因父提交变化而生成新 SHA,不能用提交 ID 判断内容是否保持不变。
代码补丁可用稳定 patch-id 比较,单文件最终内容可比较 blob ID,再结合相对社区基线的提交数和文件范围验证重写结果。 -
Lease 是服务端 CAS:force-with-lease 不是提前锁住分支,而是客户端携带预期旧 SHA,
由服务端在更新 ref 时原子比较当前值;相等才写入新 SHA,不相等就拒绝。因此它能阻止普通 force 覆盖检查后出现的并发提交。
日常可用 git push --force-with-lease,显式指定 ref 和旧 SHA 时边界更严格。
Git 分布式协作模型
-
术语辨析脉络:Git 的分布式容易与分布式数据库或服务集群混淆,因为两者都有多份状态和同步问题,但关注对象不同。
运行时分布式系统协调多台机器共同维护一个在线系统;Git 则让多份完整仓库独立演进,之后再交换和整合历史。 -
分叉本身合法:Git 不要求所有仓库实时收敛到唯一状态,也不依靠 Raft 或 Paxos 决定唯一合法提交。不同仓库可以长期保留分叉历史,
等产生协作需求时,再由人通过 merge、rebase 或 cherry-pick 选择如何协调;前提是接受一致性由显式操作而非后台协议推动。 -
机制与权威分层:Git 的数据模型是分布式的,每个 clone 都能独立提交、查询历史并成为拉取来源;
GitHub 上围绕 origin 或 main 的中心化体验,是团队赋予某个仓库权威地位的协作规则,而不是 Git 机制要求只能存在一个中心。 -
Pull 的历史视角:Pull Request 从维护者视角命名,原意是贡献者请求上游从自己的仓库拉取提交。
git request-pull 至今仍用于生成包含起点、仓库地址、提交列表和 diffstat 的请求文本,但它不发送消息也不创建在线 PR;
GitHub PR 是对这种邮件式协作、审查和合并流程的产品化。
Git 引用与分支跟踪模型
-
引用模型脉络:Git 对象通常不可变,但协作需要稳定名称指向不断变化的历史位置,因此引入 ref 作为 refname 到对象 ID 的命名引用。
branch、tag 和 remote-tracking branch 不是三套底层机制,而是在统一 ref 机制之上通过命名空间和更新规则形成的不同语义。 -
指针不只是别名:ref 可以看作助记名称,但更准确的是持久化的可移动指针,因为它会影响对象可达性、历史位置和命令行为。
当前分支在 commit 后自动前移,远端跟踪 ref 由 fetch 更新,而 tag 通常保持不变;这些更新规则超出了普通别名的含义。 -
命名空间赋义:refs/heads、refs/tags 和 refs/remotes 分别承载本地分支、标签与远端跟踪状态。
轻量 tag 与 branch 都可直接引用对象,但约定的可变性不同;annotated tag 还会先指向携带名称、说明、时间和签名的 tag object,
再由该对象指向目标。 -
远端关系分三层:服务器上的 refs/heads/main 是真实远端分支,
本地 refs/remotes/origin/main 只是最近一次 fetch 得到的远端状态快照;
本地 branch 与其 upstream 的关联则单独维护在 .git/config 的 branch..remote 和 branch. .merge 中。
因此本地分支名无需与远端分支名相同,关联也不写进 commit 或 branch ref。 -
Refspec 负责映射:remote.
.fetch 中的 refspec 定义服务器 ref 如何映射成本地远端跟踪 ref,
例如将 refs/heads/ 映射为 refs/remotes/origin/。upstream 配置再把本地分支连接到其中一个映射结果;理解这两步,
才能区分 fetch 的同步范围与 branch 的跟踪关系。 -
映射关系脉络:从 git branch 看不到 refs/heads 前缀,是因为高层命令会按当前展示范围缩写完整 ref;进一步观察跨仓库同步可知,
所谓本地与远端不是 ref 的固有属性,而是站在哪个仓库观察,以及 refspec 如何把另一仓库的引用映射进来。 -
Heads 相对本地:refs/heads/ 表示当前仓库自己拥有的 branch heads,而不是绝对意义上的客户端分支。
GitHub 服务器仓库的正式分支也位于它自己的 refs/heads/;在本地执行 git branch 时,这一公共前缀被省略,
因此 master 实际代表 refs/heads/master。 -
Remote 是快照映射:当仓库 A 成为仓库 B 的 remote 后,
A 的 refs/heads/ 不会变成 B 的 refs/remotes/;B 只是在 fetch 时按 refspec 更新一组独立的本地快照。
两边暂时可以指向同一 commit,但 A 后续移动 branch 后,B 的 remote-tracking ref 会保持旧值,直到再次 fetch。 -
Remote 与 Upstream 分层:remote 是仓库级连接配置,描述另一个仓库的 URL 与 ref 映射;upstream 是分支级关系,
由 remote 名和该 remote 上的 branch 共同组成,用于比较、pull 和默认操作。
名为 upstream 的 remote 只是 fork 工作流中的命名惯例,不等于 upstream branch 这个概念。 -
Fetch Push 可分离:fetch 与 push 是两个独立方向,可以使用不同 URL、remote 和 refspec,
只是在简单工作流中默认复用。branch.remote 与 branch.merge 定义拉取和比较基准,
branch.pushRemote 或 remote.pushDefault 可以另选发布仓库,push.default 再决定目标分支;
这使从社区仓库拉取、向个人 fork 推送的三角工作流成为可能。
gitignore 语法衍生的配置文件
-
两者语法同源但归属不同:
.worktreeinclude 和 .gitattributes 都复用 .gitignore 的路径匹配语法(pattern 规则、ignore 库解析),
但前者是 Claude Code 私有扩展、只在建 worktree 时生效,
后者是 Git 原生功能、影响所有 clone/checkout/diff/archive 行为。判断一个 gitignore 风格文件的作用域,
先看它是被谁读取(Git 本身 vs 某个工具),而不是看语法本身。 -
.worktreeinclude 是反向抢救机制:
作用是把已被 .gitignore 排除、但 worktree 运行必需的文件(如 .env)在建 worktree 时复制过去,
因为 worktree 是全新检出,untracked 文件不会自动带过去。生效前提是双重交集:既匹配 .worktreeinclude 里的 pattern,
又确实被 gitignore 忽略;已跟踪文件永远不会被这个机制重复处理。
源码位置 harness/claudecode/src/utils/worktree.ts 的 copyWorktreeIncludeFiles。 -
.gitattributes 管跟踪文件的处理方式:.gitignore 决定要不要跟踪,.gitattributes 决定已跟踪文件被