开发者必看的 15 个困惑的 Git 术语(以及它们的真正含义)

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:

git checkout a3c4d5e

你会看到这样的提示: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",以此类推。

git show HEAD~1

显示你当前 commit 的上一个。

HEAD^1 和 HEAD^2

这些是父指针。它们在处理 merge commit 时最有用。

一个 merge commit 有两个父节点——一个来自你所在的分支,另一个来自你合并进来的分支。

git diff HEAD^1 HEAD^2

对比合并时两边各自贡献了什么。

^1 表示"第一个父节点",^2 表示"第二个父节点"。

经验法则:

  • ~ 线性地往回走
  • ^ 处理 merge commit 的父节点

如下图演示

15 个让开发者困惑的 Git 术语(以及它们的真正含义)-CatchAdmin PHP 后台管理框架

把 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

15 个让开发者困惑的 Git 术语(以及它们的真正含义)-CatchAdmin PHP 后台管理框架

mainB 之后就没动过。

git switch main
git merge --ff-only feature

结果:


没有创建 merge commit -> Git 只是把 main 的指针往前移了。

分支已分叉 -> 无法 Fast-forward

15 个让开发者困惑的 Git 术语(以及它们的真正含义)-CatchAdmin PHP 后台管理框架

两边都在 B 之后有了新提交。

git switch main
git merge feature

现在 Git 必须创建一个 merge commit 来合并 ED

如果你坚持要 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 项目时才搞清楚区别。之前我一直以为它俩是一回事 😃

origin 是你克隆仓库时默认的远程仓库名称。

如果你 fork 了一个仓库,原始仓库叫做 upstream

git remote -v

你可能会看到:

origin   git@github.com:yourname/project.git
upstream git@github.com:org/project.git

用 upstream 从原始源拉取变更:

git fetch upstream

Remote-Tracking Branches

origin/mainmain 不是一回事。

  • main = 你的本地分支
  • origin/main = Git 对远程 main 的最后已知快照
git fetch
git diff main origin/main

这能告诉你上游有什么变化,而不用立即合并。

Fetch vs Pull

git fetch 只更新你对远程仓库的本地认知。

git pull 做了这个,然后自动合并(或 rebase)。

git fetch origin
git merge origin/main

这比盲目 git pull 更安全、更明确。

The Staging Area (Index)

暂存区是 Git 的"候车室"。

你决定哪些变更进入下一个 commit。

git add src/App.java
git status
git commit -m "Fix null check"

你可以暂存特定的代码块:

git add -p

就像在结账前往购物车里放东西。

Working Directory

你的工作目录就是你正在编辑的文件——不是仓库本身。

真正的仓库在 .git/ 里。

git status
# "working tree clean" 意思是你的文件和 HEAD 一致

Tracked vs Untracked Files

你添加过一次的文件是"已跟踪"的。

新文件在你暂存它们之前不会被跟踪。

git status
# Untracked files: (use "git add <file>")

提交前一定要检查这个——未跟踪的文件不会出现在 commit 里。

Dirty Tree

"Dirty tree" 只是说你的工作目录有未提交的变更。

git status
# modified: src/App.java

切换分支前先提交或 stash 它们,避免冲突。

Reset vs Revert

Git 里最容易被误解的两个命令。

  • reset 移动 HEAD,可能会丢弃 commit(危险)
  • revert 创建一个新 commit 来撤销之前的变更(安全)
git reset --hard HEAD~1   # 删除最后一个 commit
git revert HEAD           # 通过添加新 commit 安全地撤销

如果你在共享分支上工作,永远用 revert

总结

从本质上讲,Git 就是一个由 commit指针 组成的图。

一旦你理解了 HEAD分支快照 的工作原理,其他一切都会变得合乎逻辑。

posted @ 2025-10-29 07:25  JaguarJack  阅读(241)  评论(1)    收藏  举报