一文讀懂 Git fetch 和 Git pull 的終極區別(帶實驗結果)

极狐GitLab發表於2024-09-29

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:

file

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 之間的差別。整體測試流程為:

  1. 在遠端倉庫新建一個 branch-1分支;
  2. 將遠端倉庫中 branch-1分支上的 3333改為 4444;
  3. 將本地 branch-1分支上的 3333改為 5555;
  4. 分別執行 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)

相關文章