TL;DR

git fetch tải changes từ remote về local repo nhưng không đụng đến working directory hay branch hiện tại — chỉ cập nhật remote-tracking refs (vd origin/main). git pull = git fetch + git merge, tự động apply changes vào branch đang đứng. Công thức đơn giản: git pull = git fetch + git merge. Fetch an toàn 100%. Pull có thể gây conflict hoặc merge commit rác nếu không cẩn thận.

Vấn đề: tại sao dev vẫn nhầm?

Một tweet viral từ @paypaldev tóm gọn trong 2 dòng:

Fetch: lấy changes mới từ remote, không áp dụng vào local branch.
Pull: là kết hợp của git fetchgit merge.

Nghe đơn giản nhưng hàng ngày vẫn có dev chạy git pull trên branch bẩn rồi ngồi gỡ conflict 30 phút. Hoặc tệ hơn: pull nhầm branch, overwrite commit chưa push. Hiểu đúng 2 lệnh này là bài học Git cơ bản nhất mà ai cũng nên đóng đinh lại.

git fetch làm gì chính xác?

Theo git-scm.com/docs/git-fetch, fetch tải về:

  • Commits, blobs, trees và mọi object cần để hoàn thiện history
  • Cập nhật remote-tracking branches như refs/remotes/origin/main
  • Ghi thông tin ref vào .git/FETCH_HEAD

không làm gì khác:

  • KHÔNG merge hay rebase
  • KHÔNG sửa working directory — chạy bất cứ lúc nào đều an toàn
  • KHÔNG update local branch của bạn
  • KHÔNG tạo merge commit
  • KHÔNG fast-forward branch hiện tại

Nói cách khác: fetch chỉ "xem remote đang có gì mới". Không có gì trong code của bạn thay đổi.

git pull làm gì?

Git pull là một convenience wrapper, tương đương chạy:

git fetch origin
git merge FETCH_HEAD

Nếu bạn config pull.rebase=true, nó chạy git rebase thay vì merge — replay commits local của bạn lên trên tip remote mới nhất, cho history tuyến tính, không có merge commit rác.

So sánh trực tiếp

Aspectgit fetchgit pull
Download remote refs
Cập nhật working dirKhông
Cập nhật local branchKhông
Rủi ro merge conflictKhông
ReversibleDễCần reset/revert
Use caseXem trước rồi quyết địnhSync nhanh

Khi nào dùng cái nào?

Fetch-first workflow (khuyên dùng cho team)

git fetch origin
git log origin/main..main    # commits local chưa có trên remote
git log main..origin/main    # commits remote chưa có trên local
git diff main origin/main    # xem diff thực sự
git merge origin/main        # chỉ merge khi đã review

Bước fetch giúp bạn thấy được ai push gì trước khi đụng đến branch hiện tại. Đặc biệt quan trọng khi nhiều người cùng làm một branch.

Pull để sync nhanh

git pull origin main

Ổn khi bạn làm solo hoặc branch của bạn ổn định, ít divergence với remote.

Rebase pull cho history sạch

git pull --rebase origin main
# hoặc set global:
git config --global pull.rebase true

Không còn merge commit "Merge branch 'main' of origin..." rác trong log.

Dọn remote branches đã bị xoá

git fetch --all --prune

Remove mọi origin/* ref đã không còn tồn tại trên remote.

Cạm bẫy thường gặp

  • git pull khi working tree bẩn: Git sẽ từ chối merge nếu có uncommitted changes xung đột với incoming. Commit hoặc stash trước.
  • Merge commit rác: mỗi lần pull trên branch có local commits chưa push tạo thêm merge commit. Dùng --rebase.
  • Pull nhầm branch: git pull origin wrong-branch merge branch lạ vào. Luôn check branch trước.
  • Quên fetch: làm việc với remote-tracking branches cũ. Fetch thường xuyên, tốt nhất là auto-fetch qua IDE hoặc hook.

Best practice

Khẩu quyết từ GitLab: "Fetch often, pull only when necessary."

  1. Fetch thường xuyên (hoặc bật auto-fetch trong IDE) để luôn biết remote có gì mới.
  2. Chỉ pull khi thật sự muốn apply changes vào branch hiện tại.
  3. Bật pull.rebase=true cho team repo để tránh merge commit rác.
  4. Với branch quan trọng (main, release): luôn fetch trước, review log, rồi merge/rebase thủ công.

Kết

git fetch vs git pull không phải kiến thức mới lạ — nhưng nó là một trong những nhầm lẫn phổ biến nhất của Git, kể cả với dev nhiều năm kinh nghiệm. Nhớ công thức: git pull = git fetch + git merge. Khi không chắc, fetch trước, xem log, rồi mới quyết định.

Nguồn: git-scm.com, GitLab blog, @paypaldev.