一文读懂 Git fetch 和 Git pull 的终极区别(带实验结果)
Git pull 是一个 Git 命令用来同时执行 git fetch 和 git merge。本文分享了这两个命令的区别和用法。
Git 命令是非常流行的,尤其是在分布式版本控制系统中,可以对远端的仓库进行同步。开发者需要根据项目实际所需来选择合适的命令。在本文章中,我们将解释 git fetch 和 git pull 之间的区别,并且为两者不同的使用场景做一个详述。
极狐 GitLab 为 GitLab 的中文发行版,中文版本对国内用户更友好,可以一键私有化部署,也可以直接使用 SaaS,可直接申请 60 天专业版免费试用 https://dl.gitlab.cn/1nwrtlbt,导入 license 即可使用
Git fetch 和 git pull 的基本知识
Git fetch 和 git pull 都是用来从远端仓库更新信息的 Git 命令。所以,他们有什么区别呢?Git fetch 将更新从远端仓库下载到本地,但是不会对当前工作目录做任何变更。因为变更并没有被合入到本地分支,因此你可以在不打断当前工作流的情况下从远端仓库检出变更。另一方面,git pull 也能像 git fetch 一样从远端仓库拉取最新的变更,但是它却会把变更自动合入到当前分支。相比于 git fetch,git pull 直接将远端仓库的变更应用到本地工作目录中。
什么是 git fetch
git fetch 从远端仓库拉取最新的提交历史,但是不会对当前的本地工作目录产生任何影响。即使在获取(fetch)远端变更后,这些变更也不会在本地分支上有所反应。它的主要用途是:当你想要从远端仓库获取最新的状态,然后对变更被合入到本地仓库之前对变更做预览。为了将获取的变更应用到本地分支,你还需要手动执行 git merge 或者 git rebase。
什么是 git pull
git pull 命令是将 git fetch和 git merge(或 git rebase)结合在了一起。这能够让你从远端仓库拉取(fetch)变更并且将变更自动应用到当前的本地分支上。
当用 git fetch 从远端仓库获取变更时,变更并没有应用到本地分支,而执行 git pull 命令会自动将变更从远端仓库应用到本地分支上。
Git pull 特别适合将远程变更快速反映到本地分支上,但这可能会导致冲突,因此要小心使用,特别是当多人协作时。
什么时候用 git fetch
Git fetch 是一个从远端仓库拉取最新变更信息的命令。获取的信息并不直接反映在本地分支。使用 git pull 命令则会将所有的变更都反映到本地分支上,包括不正确的或者有问题的变更,统统搞到本地分支上。
当变更同时在远端和本地分支上进行时,或者当团队内的新入职员工进行操作时,使用 git fetch 是比较安全的,可以先用 git fetch 获取变更内容,然后再执行 merge 或者 rebase 命令。
什么时候用 git pull
Git pull 是一个比 git fetch 执行更多流程的命令。Git pull 可以同时执行 git fetch 和 git merge 或 git rebase 命令。因此,当你特别希望远端变更能及时反映到本地分支上的时候,就用 git pull 吧。
实验验证
在极狐GitLab 上创建一个 Repo,存储一个 README.md 和 1.txt 文件,其中 1.txt 文件内容如下:
cat 1.txt
1111
2222
3333
首先,保持本地和远端仓库的一致性。然后在远端仓库上将 2222改为 2233。这时候远端仓库和本地仓库之间的代码就存在差异了,本地仓库落后远端仓库一个 commit:
Git fetch 验证
根据前面文章的描述,执行 git fetch命令会拉取远端仓库的最新信息,但是不会将其合并到被爱分支。也就是说在本地执行 git fetch命令后,会看到变更拉取的信息,但是查看 1.txt 的内容,依旧是 2222,验证一下结论:
# fetch 变更
git fetch
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Unpacking objects: 100% (3/3), 315 bytes | 315.00 KiB/s, done.
From jhma.jihulab.net:devsecops1/ai
d143d7e..75eadda main -> origin/main
# 查看 1.txt 内容
cat 1.txt
1111
2222
3333
可以看到 1.txt 中依旧是 2222。然后手动执行一下 git merge或者 git rebase,再查看结果:
# 执行 git rebase 命令
git rebase
Successfully rebased and updated refs/heads/main.
# 查看 1.txt 内容
cat 1.txt
1111
2233
3333
Git pull 验证
再对远端代码做一下修改,将 1.txt 中的 3333改为 3322。先确定本地分支上的内容,然后执行 git pull,最后再确认本地分支上的内容:
# 本地分支内容查看
cat 1.txt
1111
2233
3333
# git pull 命令
git pull
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Unpacking objects: 100% (3/3), 321 bytes | 321.00 KiB/s, done.
From jhma.jihulab.net:devsecops1/ai
75eadda..e66df87 main -> origin/main
Updating 75eadda..e66df87
Fast-forward
1.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
# 再次查看本地分支内容
1111
2233
3322
可以看到远端分支的 3333直接变成了 3322,直接反映在了本地分支上。
进阶试验
下面来制造一个冲突,来进一步查看 git fetch 和 git pull 之间的差别。整体测试流程为:
- 在远端仓库新建一个 branch-1分支;
- 将远端仓库中 branch-1分支上的 3333改为 4444;
- 将本地 branch-1分支上的 3333改为 5555;
- 分别执行 git fetch 和 git pull 命令查看结果
首先看执行 git fetch 的结果:
# 执行 git fetch 命令
git fetch origin branch-1
From jhma.jihulab.net:devsecops1/ai
* branch branch-1 -> FETCH_HEAD
# 查看 branch-1 分支上的内容
cat 1.txt
1111
2222
5555
可以看到 1.txt 文件中的 3333 变成了 5555。这个符合预期,因为 git fetch 会拉取变更,但是不会合并到本地分支,接着执行 git merge 或者 git rebase 命令:
git merge
Auto-merging 1.txt
CONFLICT (content): Merge conflict in 1.txt
Automatic merge failed; fix conflicts and then commit the result.
提示冲突,然后打开 1.txt 文件查看:
1111
2222
<<<<<<< HEAD
5555
=======
4444
>>>>>>> refs/remotes/origin/branch-1
可以看到冲突的内容,此时解决冲突的时候,只要确保保留哪些内容即可,比如保留 4444那就把 5555删除,然后重新提交代即可:
# 提交代码
git add . && git commit -m "fix confilict"
[branch-1 77f28b1] fix confilict
# 推动代码
git push origin branch-1
Enumerating objects: 8, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 2 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 511 bytes | 511.00 KiB/s, done.
Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
remote:
remote: To create a merge request for branch-1, visit:
remote: http://jhma.jihulab.net/devsecops1/ai/-/merge_requests/new?merge_request%5Bsource_branch%5D=branch-1
remote:
To jhma.jihulab.net:devsecops1/ai.git
55ce9b9..77f28b1 branch-1 -> branch-1
前面是 git fetch 在面对冲突时候的表现。还是最开始的条件,如果直接执行 git pull 会有什么不同吗?
# 远端 branch-1 分支内容
1111
2222
5555
# 本地 branch-1 分支内容
1111
2222
4444
# git pull 命令
Auto-merging 1.txt
CONFLICT (content): Merge conflict in 1.txt
Automatic merge failed; fix conflicts and then commit the result.
# 查看冲突内容
1111
2222
<<<<<<< HEAD
5555
=======
4444
>>>>>>> a91c399bb2772f5a121830638d138264df27c9ee
剩下的冲突解决和上面的就一样了。
总结
Git fetch 和 Git pull 虽然都是从远端仓库将变更拉取到本地的命令,但是两者在作用上有所不同,简言之:Git fetch 拉取变更但是不将变更应用到本地分支;Git pull 拉取变更同时将变更应用到本地分支,可以理解为 Git pull = Git fetch + Git merge(or rebase)。