git push
Xem thửVấn đề cần giải quyết
Bạn đã commit xong và gõ git push nhưng nhận về ! [rejected] main -> main (fetch first). Bạn lo mình làm hỏng gì. Thực ra Git đang bảo vệ công việc của đồng đội: máy chủ có commit mà bạn chưa có, và nếu nhận commit của bạn nó sẽ phải bỏ commit đó. Hiểu cơ chế này giúp bạn xử lý bình tĩnh và đúng cách.
Sau bài này, bạn có thể
- Dùng
git pushvà-uđể đẩy nhánh lên máy chủ và thiết lập upstream. - Giải thích vì sao push bị từ chối và xử lý an toàn bằng
pull --rebase. - Biết khi nào có thể dùng
--force-with-leasevà khi nào tuyệt đối không.
Tiên quyết: bài git fetch và git rebase.
Dùng cơ bản
git push origin main
git push origin feature-login
Push thành công khi máy chủ chỉ cần "đi tiếp" từ vị trí hiện tại tới commit của bạn, gọi là fast-forward.
Thử và quan sát
Hãy dự đoán: bạn có commit C4, đồng đội vừa push C5 lên cùng nhánh. Khi bạn git push, điều gì xảy ra, và cách xử lý an toàn là gì? Chạy mô phỏng, rồi thử biến thể đẩy một nhánh mới với -u.
Đ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 push origin main | Remote đã có C5 mà bạn chưa có. Nếu nhận C4, nó sẽ phải bỏ C5, nên Git từ chối để bảo vệ công việc của đồng đội. |
| 2 | git pull --rebase origin main | Lấy C5 về rồi áp lại C4 của bạn lên trên thành C4' với hash mới. Lịch sử giờ là một đường thẳng. |
| 3 | git push origin main | Remote chỉ cần đi tiếp từ C5 tới C4' nên push được chấp nhận, là fast-forward. |
Biến thể: Đẩy nhánh mới với -u
| Bước | Lệnh | Điều xảy ra |
|---|---|---|
| 1 | git switch -c feature-login && git commit -m "Form đăng nhập" | Tạo nhánh mới và commit F1 trên máy bạn. Remote chưa biết nhánh này. |
| 2 | git push -u origin feature-login | Push tạo nhánh trên remote. Cờ -u ghi lại upstream: nhánh cục bộ theo dõi origin/feature-login. |
| 3 | git commit -m "Thêm nút gửi" | Commit tiếp F2 ở máy bạn. |
| 4 | git push | Nhờ upstream đã ghi, không cần chỉ rõ đích nữa. Git biết đẩy lên origin/feature-login. |
Giải đáp. Push bị từ chối vì hai lịch sử rẽ nhánh từ C3: máy chủ không thể đi từ C5 tới C4 mà không bỏ C5. Cách an toàn là git pull --rebase origin main, xếp commit của bạn lên trên C5 thành C4' rồi git push, lúc này là fast-forward. Ở biến thể, cờ -u ghi lại upstream để các lần sau chỉ cần git push.
git push -u origin feature-login
git push
Cờ -u (--set-upstream) chỉ cần dùng một lần cho mỗi nhánh. Với tag:
git push origin v1.0.0
git push origin --tags
Khi bị từ chối
! [rejected] main -> main (fetch first)
Bị từ chối là Git bảo vệ công việc của người khác, không phải lỗi. Xử lý:
git pull --rebase origin main
git push origin main
Nguyên tắc: đồng bộ trước rồi push, đừng force push.
Force push: cẩn trọng
git push --force-with-lease origin feature
Force push ghi đè nhánh trên máy chủ bằng lịch sử cục bộ. --force-with-lease an toàn hơn --force vì nó kiểm tra máy chủ còn đúng như lần fetch cuối của bạn trước khi ghi đè.
Đang tải sơ đồ…
Cách đọc hình. Git hỏi: nhánh trên máy chủ có còn đúng như origin/feature bạn thấy ở lần fetch cuối không? Nếu đúng thì không ai push thêm, nên Git ghi đè bằng lịch sử của bạn. Nếu không thì có người đã push, và Git từ chối để bạn fetch và xem lại.
Không bao giờ force push lên nhánh chung như
main. Chỉ push lên nhánh mình sở hữu; với nhánh chung, luôn pull hoặc rebase trước khi push.
Hiểu lầm thường gặp
- "Push bị từ chối nghĩa là tôi làm sai." Nó chỉ nghĩa là máy chủ đã đi trước. Hãy đồng bộ rồi push lại.
- "Dùng --force cho nhanh." Nó có thể xóa commit của đồng đội; ít nhất hãy dùng
--force-with-lease. - "Push cũng đẩy tag." Không, tag phải được push riêng hoặc dùng
--tags.
Tự kiểm tra
- Vì sao Git từ chối push khi máy chủ có commit bạn chưa có?
- Cờ
-ulàm gì và cần dùng mấy lần cho mỗi nhánh? --force-with-leasean toàn hơn--forceở điểm nào?
Đáp án gợi ý. (1) Nhận commit của bạn sẽ buộc bỏ commit của người khác. (2) Ghi lại upstream; chỉ một lần cho mỗi nhánh. (3) Nó từ chối ghi đè nếu máy chủ đã khác với lần fetch cuối của bạn.
Điều cần nhớ
- Push thành công khi máy chủ chỉ cần fast-forward.
- Bị từ chối:
git pull --rebaserồi push lại. -ughi upstream một lần; tag phải push riêng.- Không force push lên nhánh chung; nếu buộc phải force, dùng
--force-with-lease. - Bài sau: git pull.
Nguồn tham khảo
Tài liệu git-push (git-scm.com/docs/git-push); Pro Git, chương 3.5 (git-scm.com/book).