PHP 开发者必看的 15 个困惑的 Git 术语(以及它们的真正含义)
做了多年开发, 自 2015 年开始使用 Git, 我审过数百个 Pull Request,收拾过无数混乱的代码仓库,也带过不少在 Git 命令里打转的新人。
老实说,我完全理解他们的困惑。Git 确实强大,但它的术语系统就像一个迷宫——很多词看着相似,实际用法却天差地别。
今天就来聊聊这些连老手都可能搞混的 Git 术语。如果你是新手,这篇文章能帮你少走很多弯路。
原文 开发者必看的 15 个困惑的 Git 术语(以及它们的真正含义)
HEAD vs head vs Detached HEAD
开发者常犯的错误:
把 HEAD 当成又一个分支名。
它的真实身份:
HEAD 是一个指针,指向你当前所在的 commit —— 通常是你所在分支的最新提交。
每次你提交代码,HEAD 就会移动到那个新 commit 上。- # 查看 HEAD 指向哪里
- git log --oneline --decorate -n 3
- # --oneline 把每个 commit 压缩成一行显示:
- # commit hash 的前 7 个字符 + commit 信息
- # --decorate
- # 在 commit 旁边显示分支和标签引用
- # 这样你就能看到 HEAD、main、origin/main 或 v1.0.0 这些指针当前在哪
- # -n 3
- # 只显示最近 3 个 commit
复制代码 你会看到类似这样的输出:- a3c4d5e (HEAD -> main, origin/main) Add user API
复制代码 如果你直接 checkout 到某个旧 commit:你会看到这样的提示:You are in 'detached HEAD' state.
这只是说你现在不在任何分支上——你在查看历史快照。没什么坏事发生。
如果你想保存在这里做的工作:- git switch -c hotfix/legacy-bug
复制代码 至于 head(小写),它不是 Git 的关键字——通常只是非正式用法或复数形式,比如"分支的 heads"。
Git 把分支的最新提交存在 .git/refs/heads/ 下,所以你可能会在内部引用中看到这个词。
比如:- .git/refs/heads/main
- .git/refs/heads/feature/login
复制代码 所以当你看到"所有 heads"时,意思是"每个分支的最新 commit",而不是那个特殊的 HEAD 指针。
后缀的含义:
Git 提供了一些方式让你从 HEAD 开始往回追溯历史。
HEAD~1
表示"HEAD 之前的一个 commit"。
HEAD~2 表示"之前的两个 commit",以此类推。显示你当前 commit 的上一个。
HEAD^1 和 HEAD^2
这些是父指针。它们在处理 merge commit 时最有用。
一个 merge commit 有两个父节点——一个来自你所在的分支,另一个来自你合并进来的分支。对比合并时两边各自贡献了什么。
^1 表示"第一个父节点",^2 表示"第二个父节点"。
经验法则:
- 用 ~ 线性地往回走
- 用 ^ 处理 merge commit 的父节点
如下图演示
把 feature 合并到 main 后,merge commit (M) 有两个父节点:
- HEAD^1 → commit C(合并前的 main)
- HEAD^2 → commit E(feature 分支的末端)
一旦你把 HEAD 想象成"你在这里",Git 的导航模型就瞬间清晰了。
Commit vs Changeset
很多开发者以为 commit 就是 diff——其实不是。
- commit = 仓库的完整快照 + 元数据 + 父节点链接
- changeset = 两个 commit 之间的差异
- git show HEAD # 显示 commit 及其 diff(changeset 视图)
- git diff HEAD~1 HEAD # 只显示两个 commit 之间的原始 diff
- git add -p # 选择性暂存变更(构建你的 changeset)
- git commit -m "Fix NPE in user lookup"
复制代码 理解这个区别能帮你打造原子化 commit——每个 commit 只包含一个逻辑变更——而不是一股脑全扔进去。
Branch ≠ Copy
创建分支不会克隆代码库。
它只是创建一个指向 commit 的新指针。- git branch feature/login
- git switch feature/login
复制代码 现在你有了一个从 main 分叉出去的轻量级指针。没有额外文件,没有复制。
Tags
Tags 标记特定的 commit——通常用于发布版本,而且不会移动。- git tag v1.0.0
- git push origin v1.0.0
复制代码 和分支不同,tags 是不可变的。一旦设置,它们永远指向同一个 commit。
Fast-Forward Merge
Fast-forward 不是什么特殊操作——它只是没有分叉的合并。
无分叉 -> 可以 Fast-forward
main 从 B 之后就没动过。- git switch main
- git merge --ff-only feature
复制代码 结果:
没有创建 merge commit -> Git 只是把 main 的指针往前移了。
分支已分叉 -> 无法 Fast-forward
两边都在 B 之后有了新提交。- git switch main
- git merge feature
复制代码 现在 Git 必须创建一个 merge commit 来合并 E 和 D。
如果你坚持要 fast-forward:- git merge --ff-only feature
- # error: Not possible to fast-forward
复制代码 规则:
如果双方在分叉后都有新 commit,就无法 fast-forward。
Squash
Squash 在合并前把多个 commit 压扁成一个——适合清理混乱的 feature 分支历史。- git merge --squash feature/login
- git commit -m "Add login feature"
复制代码 你的分支历史保持干净,同时在 PR 里不会丢失工作上下文。
origin vs upstream
这个挺有意思的……我几天前在处理一个 fork 项目时才搞清楚区别。之前我一直以为它俩是一回事
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |