简介
很多人只会 git pull,但真正有经验的开发者会大量使用 git fetch
因为fetch是“安全操作”,它是完全“只读”的同步操作。
直接使用git pull
问题:
- 自动 merge
- 可能突然产生 merge commit
- 可能在你不知情的情况下改变历史结构
更稳的习惯是:
git fetch
然后自己决定:
- rebase?
- merge?
- 不动?
很多人会养成习惯:
每天开始工作:先git fetch一下再说,保持对远程状态的感知,但不立即合并。
一个高级经验:fetch 不等于更新本地分支
很多人误以为:git fetch 会更新本地分支
不会。
它只更新:origin/所有分支,这会从 origin 远程仓库拉取所有分支的最新提交和历史,但不会自动合并或切换分支。
如果你在 main:main ≠ origin/main,它们是两个不同指针。
注意:执行 git fetch 默认会从当前分支跟踪的远程仓库拉取。
你可以使用,git branch -vv来查看下,当前分支跟踪的远程
输出示例:
* main abc1234 [origin/main]
dev def5678 [origin/dev]
1. fetch的底层原理
Git 的三层结构 + 一个概念:(必须先理解)
Git 本质是三个区域:
工作区(Working Directory)
暂存区(Index)
本地仓库(.git 里的 commit 对象 + 指针(分支))
再加一个概念:
远程引用(其实包括在本地仓库这个区域中,理解成一个特殊的分支,是远程分支在本地的镜像分支)
执行:git fetch,底层做了三件事:
1)Git 会通过协议(SSH / HTTPS)连接远程仓库。
2)所有远程分支下载“缺失的对象”:
Git 的核心是 commit 对象
假设远程比你多D和E两个commit对象:
D --- E
Git 会把 D、E 对应的对象下载到:
.git/objects/
注意:只是下载数据文件。
3)更新远程引用(ref)
关键点,更新
refs/remotes/origin/所有分支 这个指针
例如上面,会从C移送到E
2.fetch为什么安全
因为:fetch 不会动这两个东西:工作区(Working Directory) ,暂存区(Index)
只会下载并更新本地仓库(.git 里的 commit 对象),并更新远程引用(refs/remotes/origin/所有分支)
核心理解:master 和 origin/master 是两个东西
很多人没理解这一点。
master ← 你当前分支
origin/master ← 远程分支的本地镜像
它们是两个完全不同的指针。说白了,理解成两个不同分支即可,自然指针也不会相同
fetch 只动:
origin/master
不会动:
master
3.为什么 Git 要这样设计?
因为:
fetch 是“同步信息”
merge / rebase 才是“修改历史”
Git 把这两个动作严格分离。
这也是 Git 设计非常优秀的地方。而pull应该理解成一个快捷命令,是fetch + merge