迟早,每位 Git 用户都会遇到同样的问题:应该使用 git fetch 还是 git pull 来获取远程仓库的最新更改?这两个命令看起来相似,且都与远程仓库通信,但它们对工作分支的影响截然不同。

本指南将解释 git fetch 和 git pull 之间的区别、每个命令的工作原理,以及何时选择其中一个而非另一个。

快速参考

如需可打印的快速参考,请查看 Git 速查表。

如果你想…使用
下载远程更改而不影响你的分支git fetch
下载并立即集成远程更改git pull
在合并前预览传入的提交git fetch + git log HEAD..origin/main --oneline
更新你的分支但拒绝合并提交git pull --ff-only
将本地提交变基到远程更改之上git pull --rebase

git fetch 的作用

git fetch 从远程仓库下载提交、分支和标签,并将它们存储在本地远程跟踪引用(如 origin/main 或 origin/feature-x)下。它不会触及你的工作目录、当前分支或索引。

终端
git fetch origin
输出
remote: Enumerating objects: 16, done.
remote: Counting objects: 100% (15/15), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 11 (delta 2), reused 0 (delta 0), pack-reused 0 (from 0)
From github.com:example/project
   9dfa932..560d418  main       -> origin/main
   261da74..b52ebc7  feature-x  -> origin/feature-x

输出显示了哪些远程分支被更新了。fetch 之后,本地分支 main 仍然指向之前的提交。只有远程跟踪分支 origin/main 发生了移动。

然后你可以在执行任何操作之前检查变更内容:

终端
git log main..origin/main

这是在不进行合并或变基的情况下查看新工作的安全方式。它为你提供了远程仓库中有哪些内容是你的分支所没有的预览。

git pull 的作用

git pull 是一个便捷命令。在底层,它运行 git fetch,然后根据选项和配置集成获取到的更改。要显式使用合并,请运行:

终端
git pull --no-rebase origin main

这大致等同于:

终端
git fetch origin
git merge origin/main

在 git pull 之后,你的当前分支已向前移动,你的工作目录可能包含新文件、更新的文件或需要解决的合并冲突。这种变化是即时的,并会影响你检出的分支。

并列比较

这两个命令的区别归结为它们在仓库中改变了什么。

方面git fetchgit pull
从远程下载新提交是是
更新远程跟踪分支 (origin/*)是是
更新你的当前本地分支否是
修改工作目录否是
可以创建合并冲突否是
随时运行都安全是仅在准备集成时

从你的分支角度来看,git fetch 是只读的。git pull 会主动改变你的分支。

何时使用 git fetch

当你想查看远程上发生了什么变化而暂时不集成任何内容时,请使用 git fetch。这在开始新工作之前、变基功能分支之前,或者当你想安全地检查队友的分支时非常有用。

例如,在 fetch 之后,你可以像这样比较你的分支与远程分支:

终端
git log HEAD..origin/main --oneline

这会显示存在于远程但不在你当前分支中的提交。经常运行 git fetch 成本很低,且对你的工作没有副作用,这就是为什么许多开发者每天都会定期执行它。

要列出 fetch 下载的远程跟踪分支,请运行 git branch -r。git branch 命令指南涵盖了其他列表选项。

获取单个分支

在通常的 fetch 配置下,git fetch origin 会获取所有远程分支。单分支克隆仅获取其配置的分支。当你只关心一个分支时,在远程名称后传递该分支名:

终端
git fetch origin main
输出
From github.com:example/project
 * branch            main       -> FETCH_HEAD
   560d418..f1e5856  main       -> origin/main

Git 下载 main 分支并将其尖端记录在 FETCH_HEAD 中。在通常的 fetch 配置下,它也会更新 origin/main。在配置为跟踪不同分支的单分支克隆中,origin/main 不会被更新,但你仍然可以通过以下命令审查获取到的提交:

终端
git log HEAD..FETCH_HEAD --oneline

这会显示来自获取的 main 分支中不在你当前分支中的提交。其他远程跟踪分支不会被更新,你的本地 main 分支保持原样。

检查你的分支是否落后

git status 命令告诉你你的分支与其上游的比较情况,但它不会联系远程。它将你的分支与 origin/main 的本地副本进行比较,后者仅与你最后一次 fetch 一样新鲜。这就是为什么即使同事在一小时前推送了新提交,git status 仍可能说你的分支是最新的。

先 fetch,然后检查状态:

终端
git fetch origin
git status
输出
On branch main
Your branch is behind 'origin/main' by 3 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)

你的分支没有自己的提交,因此 Git 可以在不进行合并的情况下将其向前移动。普通的 git pull 或 git pull --ff-only 将会起作用。

如果你有本地提交,并且远程也有新提交,状态看起来会有所不同:

输出
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" if you want to integrate the remote branch with yours)

分叉的分支无法快进。你需要合并或变基,如果未配置默认值,Git 会要求你进行选择(参见下面的故障排除部分)。

何时使用 git pull

当你准备好将远程更改引入当前分支并在其基础上继续工作时,请使用 git pull。常见流程如下所示:

终端
git checkout main
git pull origin main

当你知道集成过程会很直接时(例如在共享的 main 分支上,你很少会有本地提交),这是同步分支与其上游的最快方法。git pull 总是更新你当前检出的分支,所以在运行它之前值得确认一下你在哪里。

如果你想要更清晰的历史记录而没有合并提交,请配置 pull 以进行变基:

终端
git config --global pull.rebase true

从那时起,git pull 运行 git fetch 后跟 git rebase 而不是 git merge。你的本地提交会在获取到的更改之上重放,产生线性历史。

避免合并意外

在日常工作中首选 git fetch 而非 git pull 的最大原因是控制力。根据你的配置,在你有本地提交的分支上运行 git pull 可能会创建合并提交、引入冲突或在变基期间重写历史,所有这些都在一步完成。如果没有配置 pull 策略,较新的 Git 版本会拒绝拉取分叉的分支并要求你先选择一个策略。

更安全的方法是:

终端
git fetch origin
git log HEAD..origin/main --oneline
git merge origin/main

你先 fetch,审查即将进来的内容,然后决定是合并、变基还是推迟集成。对于活跃的功能分支,此工作流程避免了大多数“我的分支刚才发生了什么”的时刻。

如果你已经运行了 git pull 并且需要检查发生了什么,请从以下命令开始:

终端
git log --oneline --decorate -n 5

这有助于你确认 Git 是创建了合并提交、快进了分支,还是变基了你的本地工作。

如果 pull 创建了一个你不想要的合并提交,ORIG_HEAD 通常指向 pull 之前你的分支所在的提交。

Warninggit reset --hard 会丢弃未提交的更改。仅当你确定不需要当前工作树的状态时才使用它。

在这种情况下,你可以重置回该状态:

Terminal
git reset --hard ORIG_HEAD

实用标志

一些标志可以使这两个命令在真实工作流中更具可预测性。

  • git fetch --all - 从所有配置的远程仓库获取数据,而不仅仅是 origin。
  • git fetch --prune - 删除远程不再存在的远程跟踪分支。
  • git fetch --tags - 从远程获取所有标签,而不仅仅是指向已获取提交的标签。
  • git pull --rebase - 将本地提交变基到获取的更改之上,而不是合并。
  • git pull --ff-only - 仅在 Git 可以快进更新分支时进行更新;防止意外的合并提交。

--ff-only 在共享分支上特别有用。如果你的分支不能通过简单移动指针来向前推进,pull 操作将以 fatal: Not possible to fast-forward, aborting. 停止,然后由你决定下一步操作。

故障排除

fatal: Need to specify how to reconcile divergent branches
你的本地分支和远程分支都有对方没有的提交,且 Git 不知道应该合并还是变基。为单次 pull 指定策略,使用 git pull --no-rebase(合并)或 git pull --rebase,或者一次性设置默认值:

Terminal
git config --global pull.rebase false

使用 pull.rebase true 以默认变基,或使用 pull.ff only 以仅允许快进 pull。

fatal: Not possible to fast-forward, aborting
你运行了 git pull --ff-only(或设置了 pull.ff only),并且分支已经分叉。使用 git log HEAD..origin/main --oneline 查看传入的提交,然后手动合并或变基。

error: Your local changes to the following files would be overwritten by merge
传入的提交涉及你已修改但未提交的文件。提交你的更改,或使用 git stash 将它们暂存起来,然后再次 pull 并运行 git stash pop 以恢复它们。

常见问题

git fetch 会修改任何本地文件吗?
是的,在 Git 仓库内部:它会下载对象并更新远程跟踪引用、获取的标签以及 .git/FETCH_HEAD。你的工作目录文件和暂存区保持不变。

git pull 等同于 git fetch 加上 git merge 吗?
大体上是。当你的分支可以快进时,git pull 的行为类似于先执行 git fetch 再执行 git merge。当分支发生分叉时,Git 会停止并要求你在合并和变基之间做出选择,除非配置了 pull.rebase 或 pull.ff。如果将 pull.rebase 设置为 true(或在命令行中使用 --rebase),它将先执行 git fetch 再执行 git rebase。

日常应该使用哪一个?
为了可见性,首选 git fetch;对于集成步骤,首选 git pull --ff-only(或 git pull --rebase)。裸用 git pull 也可以,但它将两个操作隐藏在一条命令之后。

如何查看 git fetch 下载了什么?
使用 git log main..origin/main --oneline 查看本地分支上没有但远程分支有的提交,或使用 git diff HEAD origin/main 查看具体差异。

git fetch 会导致冲突吗?
不会。fetch 只下载数据而不修改工作区。冲突发生在后续的 merge 或 rebase 操作中。