在刚接触 Git 时,最容易混淆的就是 fetch 和 pull 这两个命令。表面上它们都是同步远程代码,但实际一个只负责下载,另一个会直接改写当前分支。下面按我平时操作的顺序说明,重点是让你知道每一步之后应该看什么。
fetch 和 pull 的差别在合并这一步
很多人已经知道“fetch 只下载、pull 会合并”,但到具体操作时还是会犹豫。这里先把最关键的分界线说清楚。
git fetch 和 git pull 的本质区别在于是否自动合并。fetch 只将远程仓库的提交记录下载到本地,但不会改动你的工作目录和当前分支,相当于只更新了远程分支的镜像。而 pull 等价于 fetch 后执行 merge,它会直接把远程分支的变更合并到当前分支。如果你只是想查看远程有哪些新提交,而不想立刻影响本地代码,应该只执行 fetch。判断方法很简单:执行 git fetch origin main 后,用 git log HEAD..origin/main 查看本地落后了哪些提交;如果这些提交看起来没问题,再手动执行 git merge origin/main,效果等同于 git pull 的默认行为。
也就是说,pull 并不是多敲一下键盘的事,而是把“要不要合并”这个决定权替你拿走了。如果你不需要合并,fetch 是最安全的动作。
先把远程拉下来,再决定要不要合并
我的习惯是:除非当前分支干净得不能再干净,否则一律先 fetch。因为先 fetch 不改变任何本地状态,你可以反复执行,直到满意为止。
在不确定远程变更是否会与本地冲突时,优先使用 fetch 而不是 pull。先运行 git fetch,然后比较本地分支与远程分支的差异:git diff HEAD..origin/main 可以看到具体改动内容。如果改动集中在其他文件,或者你正处在未提交的工作区中,用 fetch 可以给你预留充分的检查时间。只有当你明确知道远程提交是安全的,或者你只是单纯想同步一个干净的分支时,才直接使用 pull。对于工作区有未提交修改的情况,pull 可能因为合并冲突而拒绝执行,但 fetch 始终不会干扰这些修改,因此更可靠。
这里的关键在于,fetch 之后,工作区里的未提交修改还在原位,远程分支的更新只出现在 .git 的引用里。你完全不用担心 fetch 会把你写到一半的代码打乱。
pull 的隐式合并会带来什么
如果你一直在用 pull,可能觉得它没风险,直到某天发现历史里多了一个自动生成的 merge commit。
pull 的主要风险在于它隐式执行了合并操作,而合并可能产生冲突或意外的 merge commit。如果你的本地分支已经落后多次提交,且远程分支也被其他人改动过,pull 会尝试合并这些历史,一旦冲突就会把你带入需要手动解决的中间状态。相比之下,fetch 只是更新远程引用,绝不改动工作目录和当前分支,所以它是无副作用的。如果你有一个非常干净的分支,并希望保持线性历史,可以始终沿用 fetch 加 merge --ff-only 的组合,而不是直接 pull,这样遇到非快进合并时会立刻停下,避免自动生成合并提交。
这里需要特别注意的是,merge --ff-only 不是默认行为,你必须主动写出来。如果你想保持线性历史,可以把这条组合设成常用习惯。
怎么检查远程到底改了什么
我通常在 fetch 完先看 status 和 log,边看边决定下一步。
要判断当前分支是否落后于远程,可以先执行 git fetch,然后用 git status 看输出中的“Your branch is behind”提示。如果想看具体的提交差异,用 git log --oneline HEAD..origin/main 列出远程有而本地没有的提交;反之,用 git log --oneline origin/main..HEAD 列出本地独有的提交。这样你就能清晰评估 pull 将会带来哪些改动。若这些改动你不关心,直接忽略即可。由于 fetch 不改变本地状态,你可以放心反复运行它来保持远程元数据最新,而不必担心破坏现有工作。
如果你只想把当前分支快进到远程最新,并且接受非快进时失败,可以直接用这个组合:
git fetch origin && git merge --ff-only origin/main这条命令比 pull 更可控,因为它不会自动产生合并提交。
什么时候可以直接 pull
直接 pull 的适用场景其实比较窄。如果你的工作区完全干净,且本地分支没有独有提交,比如刚 clone 下来想同步最新,或者你一直追着远程走,那 pull 可以少敲一条命令。这种情况下 pull 通常会走 fast-forward,不产生额外合并提交。
但如果本地和远程已经分叉,或者你在公共分支上工作,就不要直接用 pull。你无法预测它会不会因为冲突把你带进手动合并的中间状态。更稳妥的做法是先 fetch,再用 git diff 看远程改了什么,最后决定 merge 还是 rebase。
如果你发现远程分支和本地分支改动完全不同,也可以先 fetch,再手动用 git rebase origin/main 把本地提交放在远程提交之后。虽然 rebase 也是改写历史,但至少你是在看清差异之后主动做的,而不是被 pull 带进合并里。
对个人项目而言,如果历史不敏感,直接 pull 也没问题。但如果你需要整理提交信息,或者不想让远程代码自动改动工作目录,就始终用 fetch 加手动合并。
一句话边界
如果你还在犹豫“该用 fetch 还是 pull”,先选 fetch。它是无副作用的,任何时候都可以安全执行;pull 则代表你接受了自动合并。想在 CI 脚本里用 fetch 而非 pull 也同理,因为脚本环境更需要可控性。