fetch

4 阅读2分钟

简介

很多人只会 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