Mục lục

Hỏi đáp Git: Giải quyết 99% tình huống thực tế

Tổng hợp các câu hỏi thực tế khi làm việc nhóm: Đang code dở muốn pull code mới, commit nhầm nhánh, lỡ tay xóa file... kèm giải pháp từng bước.

Trong quá trình làm việc team, sẽ có những lúc bạn rơi vào những tình huống "éo le". Dưới đây là tổng hợp các câu hỏi thực tế nhất và cách xử lý chuẩn Senior.

1. Tôi đang code dở bài của mình, nhưng đồng nghiệp vừa push code mới lên nhánh develop. Tôi muốn lấy code đó về nhánh mình thì làm thế nào?

Đây là tình huống gặp hàng ngày. Bạn có 2 cách tùy vào việc bạn muốn giữ lịch sử như thế nào.

Cách 1: Dùng Stash (Sạch sẽ nhất)

  1. git stash: Cất tạm code đang làm dở đi.
  2. git pull origin develop: Kéo code mới về.
  3. git stash pop: Lấy lại code đang làm dở và tiếp tục.

Cách 2: Dùng Commit tạm

  1. git add .git commit -m "temp": Commit tạm để "đánh dấu".
  2. git pull origin develop --rebase: Kéo code về và đặt các commit của bạn lên trên đầu.
  3. Làm tiếp và cuối cùng dùng git commit --amend để gộp vào commit tạm đó.

2. Tôi lỡ tay commit vào thẳng nhánh main (hoặc develop) thay vì tạo nhánh mới. Giờ làm sao để chuyển commit đó sang nhánh khác?

Đừng hoảng loạn, miễn là bạn chưa push.

Giải pháp:

  1. git branch feature/moi: Tạo nhánh mới tại vị trí commit vừa rồi (nhánh này giờ đã có code đó).
  2. git reset --hard HEAD~1: Quay ngược nhánh main về 1 commit trước đó (xóa commit lỡ tay khỏi main).
  3. git checkout feature/moi: Chuyển sang nhánh mới và làm tiếp.

3. Tôi vừa commit xong thì phát hiện mình gõ sai Message hoặc quên chưa add một file nhỏ. Có cách nào sửa mà không tạo commit mới không?

Có, dùng lệnh amend (sửa đổi).

Giải pháp:

  1. git add <file-quen-chua-add> (nếu có).
  2. git commit --amend -m "Message mới chuẩn hơn". Lưu ý: Chỉ dùng lệnh này khi chưa push commit đó lên server.

4. Tôi lỡ tay xóa một file quan trọng và đã lỡ commit luôn cái hành động xóa đó rồi. Làm sao để lấy lại file?

Git không bao giờ quên.

Giải pháp:

  1. Tìm commit cuối cùng mà file đó vẫn còn: git log -- <duong-dan-file>.
  2. Lấy lại file từ commit đó: git checkout <hash-commit> -- <duong-dan-file>.
  3. Commit lại file đó là xong.

5. Nhánh feature-A của tôi đã merge vào develop, nhưng giờ sếp bảo tính năng đó lỗi quá, phải bỏ toàn bộ Feature A ra khỏi develop để release các tính năng khác.

Đây là lúc dùng revert.

Giải pháp:

  1. Tìm cái "Merge Commit" (thường có nội dung: Merge branch 'feature-A' into develop).
  2. git revert -m 1 <hash-merge-commit>. Lệnh này sẽ tạo một commit mới đảo ngược lại toàn bộ những gì Feature A đã làm mà không làm mất lịch sử.

6. Làm sao để xem "Ai là người đã sửa dòng code này?" để tôi còn đi hỏi?

Dùng lệnh Blame (không phải để đổ lỗi, mà để tìm người chịu trách nhiệm).

Giải pháp:

  • Trên Terminal: git blame <duong-dan-file>.
  • Trên VS Code: Cài extension GitLens, nó sẽ hiện tên và thời gian sửa ngay cuối mỗi dòng code.

7. Tôi muốn gộp 5 commit vụn vặt của mình thành 1 commit duy nhất cho đẹp trước khi mở PR.

Sử dụng Squash.

Giải pháp:

  1. git rebase -i HEAD~5.
  2. Trong file hiện ra, giữ chữ pick cho commit đầu tiên, đổi pick thành s (squash) cho 4 commit còn lại.
  3. Lưu lại và gõ message tổng quát cho commit duy nhất đó.

8. Tôi muốn tìm một từ khóa (vd: mã API, tên biến) trong toàn bộ lịch sử commit từ trước đến nay.

Git cho phép bạn "lục tung" lịch sử để tìm dấu vết.

Giải pháp:

  • Tìm trong nội dung code: git log -S "từ-khóa".
  • Tìm trong tin nhắn commit: git log --grep="từ-khóa".

9. Tôi lỡ tay xóa một nhánh (branch) ở máy local. Làm sao để cứu lại?

Nếu nhánh đó đã từng được commit, bạn vẫn có thể cứu được.

Giải pháp:

  1. git reflog: Tìm mã Hash của commit cuối cùng trên nhánh đã bị xóa.
  2. git checkout -b <ten-nhanh-cu> <hash-commit>: Hồi sinh lại nhánh từ đống tro tàn.

10. Tôi đã thêm file vào .gitignore nhưng Git vẫn tiếp tục theo dõi (track) nó. Tại sao?

.gitignore chỉ có tác dụng với các file chưa từng được track. Nếu file đã nằm trong một commit cũ, bạn cần "đuổi" nó ra khỏi Index.

Giải pháp:

  1. git rm --cached <duong-dan-file>: Xóa khỏi Index nhưng vẫn giữ lại file thật trên máy.
  2. git add .git commit: Lưu lại thay đổi. Hiện tại .gitignore đã bắt đầu có tác dụng.

11. Tôi muốn làm việc trên 2 nhánh khác nhau cùng một lúc (vd: đang fix bug nhánh A nhưng muốn tham khảo code nhánh B) mà không muốn clone dự án ra 2 thư mục riêng.

Dùng tính năng Git Worktree (Cực kỳ hữu ích cho dự án lớn).

Giải pháp:

  1. git worktree add ../du-an-nhanh-B nhanh-B: Git sẽ tạo một thư mục mới ở cùng cấp, chứa toàn bộ code của nhánh B.
  2. Bạn có thể mở 2 cửa sổ VS Code làm việc song song trên 2 nhánh của cùng 1 repo.
  3. Xong việc: git worktree remove ../du-an-nhanh-B.

12. Thư mục .git của tôi nặng quá (vài GB), làm sao để dọn dẹp cho nhẹ máy?

Git lưu trữ mọi thứ, đôi khi là cả những thứ rác rưởi hoặc file nặng đã xóa.

Giải pháp:

  • git gc --prune=now --aggressive: Chạy chương trình dọn rác và tối ưu hóa bộ nhớ.

13. Tôi lỡ git push nhầm lên một nhánh không được phép (vd: push thẳng lên production).

Nếu bạn có quyền Force Push, hãy sửa lỗi ngay lập tức.

Giải pháp:

  1. Quay về trạng thái đúng ở máy local: git reset --hard <hash-ngon>.
  2. Ghi đè lên server: git push origin <ten-nhanh> --force. Cảnh báo: Hãy thông báo cho team ngay lập tức vì việc này sẽ làm hỏng lịch sử máy của họ.

14. Tôi muốn lấy duy nhất 1 file từ bên nhánh khác về nhánh hiện tại mà không muốn merge cả nhánh đó.

Sử dụng checkout cho file cụ thể.

Giải pháp:

  • git checkout <ten-nhanh-khac> -- <duong-dan-file>.

15. Tôi có 2 tài khoản (Công ty & Cá nhân). Làm sao để Git dùng đúng Email cho từng Repo?

Đừng dùng cấu hình --global. Hãy cấu hình riêng cho từng dự án.

Giải pháp:

  1. Vào thư mục dự án.
  2. git config user.name "Your Name".
  3. git config user.email "your-email@company.com". Git sẽ ưu tiên cấu hình trong folder trước rồi mới đến cấu hình global.

16. Tôi muốn đổi tên một nhánh (branch) vì tên cũ bị sai hoặc không đúng chuẩn.

Việc đổi tên nhanh nhất là thực hiện ở local rồi cập nhật lên remote.

Giải pháp:

  1. Đang ở nhánh đó: git branch -m <ten-moi>.
  2. Nếu muốn đẩy lên server: git push origin -u <ten-moi>.
  3. Xóa nhánh tên cũ trên server: git push origin --delete <ten-cu>.

17. Tôi lỡ tay Merge nhánh A vào nhánh B, nhưng chưa Push. Làm sao để "hủy" cái lệnh Merge đó?

Nếu chưa push, việc quay lại cực kỳ đơn giản.

Giải pháp:

  • git reset --hard HEAD~1: Quay lại 1 bước trước khi merge (chỉ dùng nếu đó là merge commit).
  • Hoặc dùng git reset --hard ORIG_HEAD: Git luôn lưu lại vị trí trước khi bạn làm các thao tác "nguy hiểm" như merge vào biến ORIG_HEAD.

18. Tôi chỉ muốn commit một vài dòng trong một file, còn những dòng khác trong cùng file đó tôi muốn để lại sau.

Sử dụng chế độ Interactive Patch.

Giải pháp:

  1. git add -p <file>: Git sẽ chia file thành các "hunk" (đoạn nhỏ).
  2. y để đồng ý commit đoạn đó, hoặc n để bỏ qua.
  3. Sau đó git commit như bình thường.

19. Dự án của chúng tôi quá lớn (hàng chục GB), mỗi lần clone mất cả tiếng. Có cách nào chỉ lấy code hiện tại mà không lấy toàn bộ lịch sử không?

Sử dụng Shallow Clone.

Giải pháp:

  • git clone --depth 1 <url>: Chỉ lấy snapshot mới nhất. Dung lượng sẽ cực nhẹ và cực nhanh.

20. Làm sao để xóa sạch các nhánh local đã được merge vào main để danh sách nhánh đỡ rác?

Dùng một câu lệnh bash kết hợp.

Giải pháp:

  • git branch --merged | grep -v "\*" | grep -v "main" | xargs -n 1 git branch -d. (Lệnh này sẽ tìm các nhánh đã merge, loại trừ nhánh hiện tại và nhánh main, sau đó xóa chúng).

21. Tôi muốn lưu lại trạng thái hiện tại của code để sang máy khác làm tiếp, nhưng code chưa xong nên không muốn tạo commit chính thức.

Bạn có thể tạo một nhánh "temp" hoặc dùng Git Patch.

Giải pháp:

  1. Tạo file patch: git diff > my_work.patch.
  2. Gửi file my_work.patch sang máy kia.
  3. Áp dụng patch: git apply my_work.patch.

22. Git báo lỗi "Filename too long" trên Windows.

Đây là giới hạn mặc định của Git trên Windows (260 ký tự).

Giải pháp:

  • Chạy quyền Admin: git config --global core.longpaths true.

23. Tôi muốn bỏ qua việc chạy Git Hooks (vd: bỏ qua việc check Eslint/Test khi cần commit gấp).

Dùng cờ --no-verify.

Giải pháp:

  • git commit -m "msg" --no-verify.
  • git push origin main --no-verify. Cảnh báo: Chỉ dùng khi bạn thực sự biết mình đang làm gì!

24. Tôi lỡ git revert một commit, giờ tôi lại muốn... lấy lại cái commit đó (hủy lệnh revert).

Đơn giản là bạn revert cái commit revert.

Giải pháp:

  1. Tìm mã hash của commit revert vừa rồi.
  2. git revert <hash-cua-lenh-revert>. Lịch sử Git sẽ nhìn như một cuộc đối đầu, nhưng code của bạn sẽ quay trở lại.

25. Làm sao để gắn nhãn (Tag) cho một phiên bản release (vd: v1.0.0)?

Tag giúp bạn đánh dấu các cột mốc quan trọng.

Giải pháp:

  1. Tạo tag: git tag -a v1.0.0 -m "Phiên bản chính thức đầu tiên".
  2. Đẩy tag lên server: git push origin v1.0.0 (hoặc git push --tags).

26. Dự án của tôi có sử dụng Submodule (repo nằm trong repo), làm sao để tải toàn bộ code về một cách đầy đủ?

Dùng lệnh clone thông thường sẽ khiến thư mục submodule bị trống.

Giải pháp:

  • Khi clone: git clone --recursive <url>.
  • Nếu đã lỡ clone rồi: git submodule update --init --recursive.

27. Làm sao để xóa các thông tin về các nhánh đã bị xóa trên Server (Remote) khỏi danh sách nhánh local?

Đôi khi bạn chạy git branch -a vẫn thấy hiện các nhánh mà đồng nghiệp đã xóa từ lâu.

Giải pháp:

  • git fetch --prune hoặc git remote prune origin.
  • Mẹo: Cấu hình tự động dọn dẹp: git config --global fetch.prune true.

28. Tôi gặp phải một conflict lặp đi lặp lại nhiều lần mỗi khi merge hoặc rebase. Có cách nào để Git "nhớ" cách tôi đã sửa lần trước không?

Dùng tính năng Rerere (Reuse Recorded Resolution).

Giải pháp:

  • Bật tính năng: git config --global rerere.enabled true.
  • Từ giờ, mỗi khi bạn sửa conflict, Git sẽ ghi lại. Lần sau nếu gặp đúng đoạn conflict đó, Git sẽ tự động áp dụng cách sửa cũ cho bạn.

29. Tôi muốn clone một repo cực lớn nhưng chỉ quan tâm đến một vài thư mục cụ thể bên trong.

Sử dụng Sparse Checkout (đã có từ bản 2.25).

Giải pháp:

  1. git clone --filter=blob:none --sparse <url>.
  2. git sparse-checkout set <duong-dan-thu-muc>. Git sẽ chỉ tải về dữ liệu của đúng thư mục bạn yêu cầu.

30. Tôi lỡ gõ git add . nhưng nhận ra có vài file nhạy cảm không nên đưa vào. Làm sao để "unstage" chúng trước khi commit?

Bạn muốn đưa file từ Staging Area quay lại Working Directory.

Giải pháp:

  • git reset <duong-dan-file> (cho từng file).
  • git reset (cho toàn bộ file đã add).

31. Đồng nghiệp dùng Windows, tôi dùng Mac/Linux, dẫn đến lỗi "Line Ending" (CRLF vs LF) làm Git báo thay đổi hàng loạt dù code không đổi.

Đây là vấn đề kinh điển về ký tự xuống dòng.

Giải pháp:

  • Tạo file .gitattributes ở gốc dự án với nội dung: * text=auto.
  • Hoặc cấu hình: git config --global core.autocrlf input (trên Mac/Linux) hoặc true (trên Windows).

32. Làm sao để so sánh nhanh sự khác biệt giữa 2 nhánh mà không cần checkout qua lại?

Sử dụng toán tử ...

Giải pháp:

  • git diff branch1..branch2: Xem các thay đổi có trong branch2 nhưng không có trong branch1.
  • git diff branch1..branch2 --stat: Chỉ xem danh sách file thay đổi và số dòng.

33. Làm sao để tìm xem "2 nhánh này bắt đầu tách nhau ra từ commit nào?"

Hữu ích khi bạn muốn biết gốc rễ của sự khác biệt.

Giải pháp:

  • git merge-base branch1 branch2. Git sẽ trả về mã Hash của commit chung gần nhất.

34. Tôi muốn xem danh sách các file đã thay đổi trong một commit cụ thể.

Xem nhanh mà không cần xem toàn bộ nội dung code.

Giải pháp:

  • git show --name-only <hash-commit>.

35. Tôi muốn Git tự động Stash code khi tôi chuyển sang nhánh khác (checkout) và tự động Pop code khi quay lại.

Mặc dù Git chưa có lệnh trực tiếp, bạn có thể tạo Alias hoặc dùng script, nhưng cách an toàn là dùng tính năng autostash khi rebase.

Giải pháp:

  • git config --global rebase.autostash true. Khi bạn chạy git pull --rebase, Git sẽ tự động stash code dở và trả lại sau khi xong.

Kết luận

Với 35 câu hỏi thực tế này, bạn đã trang bị đầy đủ "vũ khí" để xử lý hầu hết các tình huống dở khóc dở cười với Git. Hãy nhớ: Git không làm mất code, chỉ có con người làm mất mã hash. Luôn bình tĩnh và kiểm tra lịch sử trước khi thực hiện các lệnh có tính chất xóa bỏ.

Quảng cáo
mdhorizontal