git pull
Xem thửVấn đề cần giải quyết
Sáng nào bạn cũng gõ git pull và đôi khi lịch sử bỗng có thêm những commit hợp nhất "Merge branch 'main' of github.com..." chẳng mang ý nghĩa gì. Lý do là git pull không phải một thao tác đơn giản: nó gồm hai bước, và bước thứ hai có hai cách làm.
Sau bài này, bạn có thể
- Mô tả
git pulllàfetchrồi tích hợp bằng merge hoặc rebase. - So sánh hình dạng lịch sử của hai cách và chọn cách phù hợp với đội.
- Xử lý xung đột khi pull theo đúng kiểu tích hợp đang chạy.
Tiên quyết: bài git fetch và git push.
Dùng cơ bản
git pull origin main
git pull
Dòng sau dùng được khi nhánh đã có upstream. Pull tải cập nhật về (fetch), rồi tích hợp chúng vào nhánh hiện tại.
Thử và quan sát
Hãy dự đoán: bạn có commit D còn máy chủ có commit E, cùng tách từ C2. git pull sẽ tạo ra hình dạng lịch sử nào, và git pull --rebase khác ra sao? Chạy mô phỏng, rồi bấm biến thể để xem pull với rebase.
Đang tải Animation…
Bảng bước (bản đọc tĩnh của mô phỏng)
| Bước | Lệnh | Điều xảy ra |
|---|---|---|
| 1 | git pull | Bước một là fetch: E về máy bạn và origin/main dời tới E. Nhánh main của bạn chưa đổi nên hai lịch sử đang rẽ nhánh. |
| 2 | (bước hai: merge) | Mặc định bước tích hợp là merge: Git tạo commit hợp nhất M có hai cha là D và E. Lịch sử giữ hai dòng rồi gộp lại. |
| 3 | git push | Đẩy commit hợp nhất lên remote; remote chỉ đi tiếp từ E tới M. |
Biến thể: Dùng git pull --rebase
| Bước | Lệnh | Điều xảy ra |
|---|---|---|
| 1 | git pull --rebase | Cũng bắt đầu bằng fetch: E về máy bạn và origin/main dời tới E. |
| 2 | (bước hai: rebase) | Thay vì merge, Git áp lại D lên trên E thành D' với hash mới. Lịch sử là đường thẳng C2, E, D' và không có commit hợp nhất. |
| 3 | git push | Remote đi tiếp từ E tới D' nên push là fast-forward. |
Giải đáp. Mặc định pull dùng merge: Git tạo commit hợp nhất M có hai cha là D và E, lịch sử giữ hai dòng rồi gộp lại. Với --rebase, D được áp lại thành D' trên E, lịch sử là đường thẳng và không có commit hợp nhất; đổi lại D' có hash mới nên chỉ dùng cho commit chưa chia sẻ.
Đặt rebase làm mặc định
git config --global pull.rebase true
Trên dự án nhóm, git pull --rebase thường cho lịch sử sạch hơn và ít commit hợp nhất vô nghĩa.
Xử lý xung đột khi pull
Nếu có xung đột, Git dừng lại. Việc đầu tiên luôn là sửa tệp xung đột rồi git add. Sau đó lệnh tiếp tục phụ thuộc vào kiểu pull đang chạy.
Đang tải sơ đồ…
Cách đọc hình. Pull dừng vì xung đột. Bạn sửa tệp và git add. Rồi xem pull đang chạy kiểu nào: nếu là rebase thì git rebase --continue; nếu là merge thì git commit. Dùng nhầm lệnh tiếp tục cho kiểu pull đang chạy sẽ báo lỗi hoặc không có tác dụng.
Tách bước để kiểm soát
git pull tiện nhưng "giấu" bước fetch. Khi muốn kiểm soát và xem trước, hãy tách:
git fetch origin
git log main..origin/main
git merge origin/main
Hiểu lầm thường gặp
- "Pull chỉ là tải về." Pull là fetch cộng tích hợp, nên có thể tạo commit hợp nhất hoặc gây xung đột.
- "Pull --rebase an toàn tuyệt đối." Nó viết lại commit cục bộ của bạn; chỉ an toàn khi các commit đó chưa chia sẻ.
- "Mặc định luôn là rebase." Mặc định là merge, trừ khi bạn cấu hình
pull.rebase.
Tự kiểm tra
- Hai bước của
git pulllà gì? - Khác biệt về hình dạng lịch sử giữa pull merge và pull rebase?
- Pull dừng vì xung đột khi bạn dùng
--rebase. Sau khi sửa và add, lệnh nào tiếp tục?
Đáp án gợi ý. (1) Fetch rồi tích hợp bằng merge hoặc rebase. (2) Merge cho lịch sử rẽ nhánh và có commit hợp nhất; rebase cho đường thẳng với commit mới. (3) git rebase --continue.
Điều cần nhớ
- Pull = fetch + tích hợp; mặc định là merge, tùy chọn là rebase.
- Rebase cho lịch sử thẳng nhưng viết lại hash commit cục bộ.
- Xung đột: sửa, add, rồi tiếp tục đúng theo kiểu pull.
- Bài sau: Pull Request, đề xuất và review thay đổi.
Nguồn tham khảo
Tài liệu git-pull (git-scm.com/docs/git-pull); Pro Git, chương 3.5 (git-scm.com/book).