Có một từ khoá thấy rất nhiều người tìm kiếm đó là "xóa commit đã push". Tình cờ thay, trước đó tôi viết một bài có tên là Tôi vừa lỡ commit sai, làm sao để sửa lại ngay lập tức?. Bài viết này lại lọt vào kết quả tìm kiếm top đầu trên Google, nhưng điều đáng nói là nội dung không nhắc đến cách giải quyết vấn đề mọi người đang gặp phải, mà chỉ đơn giản là hướng dẫn bạn xóa commit "chưa" push.
Vì thế để tránh nhầm lẫn, đồng thời giúp bạn đọc có giải pháp chính xác hơn trong vấn đề này. Bài viết ngày hôm nay chúng ta hãy cùng nhau tìm hiểu xem làm cách nào để xóa commit đã push nhé!
Trong suốt thời gian làm việc, đôi khi nghe nhiều người hay nói git khá "dễ" học, hoặc tự tin "khoe" kỹ năng sử dụng thành thạo git của họ. Thành thật mà nói đối với tôi, git là một thứ gì đó rất khó mà bản thân chỉ có thể sử dụng nó ở mức trung bình. Tức là biết cách sử dụng các lệnh cơ bản như init
, clone
, pull
, push
, branch
... và một số lệnh khác khi làm việc nhóm. Thực tế git cung cấp rất nhiều câu lệnh cùng với nhiều tuỳ chọn khác nữa, vẫn là push
nhưng kèm với nó là nhiều tuỳ chọn (options) mà chưa bao giờ dùng tới vì không biết công dụng của nó. Đấy là còn chưa kể đến githooks - một tính năng nâng cao trong git. Nhiều lệnh nâng cao của git phải nói là chưa biết đến sự tồn tại, rất khó hiểu và nhiều khả năng phù hợp cho các dự án lớn, các dự án phức tạp. Mỗi người, nhóm, tổ chức lại có một quy trình làm việc bằng git riêng, cho nên điều đó cũng góp phần tạo nên sự đa dạng trong cách sử dụng git.
Quay trở lại với vấn đề, tạm quên những thứ phức tạp ở trên đi. Về bản chất, một khi bạn đã push
commit lên remote rồi thì khả năng để xóa commit đó khá là rủi ro. Chưa cần biết đến mục đích xóa là gì. Xoá do commit vô nghĩa? Xoá do commit sai nên xóa đi cho đẹp git tree? Hay chỉ đơn giản là thích thì xoá!? Tưởng chừng đơn giản nhưng hoá ra lại rất phức tạp.
Hãy hình dung git sinh ra là dành cho quy trình làm việc nhóm, phân tán. Mỗi commit được push
lên như một cam kết thay đổi mã. Có người pull
về, họ tiếp tục viết thêm mã vào và lại đẩy lên. Giả sử là bạn có thể xóa được commit bất kì đi thì chẳng phải sẽ gây ra một sự xáo trộn mã ghê ghớm hay sao? Dòng mã đó đã tồn tại trong các commit sau đó, nếu ai đó đã chỉnh sửa vào đoạn mã mà bạn muốn xoá thì chẳng phải chúng ta sẽ trải qua một quá trình rebase
từ thời điểm xoá điện hiện tại? Quả là một cơn ác mộng.
Lúc này, bạn sẽ phải từ bỏ hy vọng xóa bất kì commit đã push nào đi, vì git không khuyến khích làm điều đó. Nhưng nếu muốn xóa các commit cuối cùng thì vẫn có khả năng làm được với rủi ro tối thiểu.
Có thể bạn sẽ thấy một khoảng "thời gian vàng" để có thể xóa các commit cuối mà không gây ảnh hưởng đến ai đó, chính là push
lên rồi nhưng chưa ai pull
về. Như vậy chúng ta hoàn toàn có thể sử dụng cờ --force
để push
lại toàn bộ commit ở dưới local lên remote. Tất nhiên cờ --force
luôn là một thứ gì đó tối kỵ mà không nên lạm dụng. Hầu hết các chương trình cung cấp cờ force đều cảnh báo hãy chắc chắn là bạn biết bạn đang làm gì. Nếu không, đừng bao giờ sử dụng. Tóm lại, có 3 trường hợp xoá commit bằng cờ --force
.
Nhánh dùng chung để ám chỉ nhánh được nhiều người tham gia phát triển, ví dụ điển hình là develop, nơi mọi người liên tục commit hoặc merge code.
Giả sử nhánh develop trên remote có commit cuối cùng là commit bạn muốn xóa, tuyệt vời, hãy tận dụng khoảng thời gian chưa ai pull về và chưa ai push bất kì thứ gì mới lên thì bạn có thể force.
# Trở về commit trước đó
$ git reset HEAD~1
# Force push lại toàn bộ commit dưới local
$ git push --force
Thế là xong, bây giờ git tree của bạn trở về y hệt như trước khi có commit cuối cùng.
Trong trường hợp commit cuối cùng không phải là của bạn nữa, lúc này chắc chắn đã có người pull
commit của bạn về rồi, nếu force push thời điểm này sẽ gây ra một sự xáo trộn mã ghê ghớm, dễ thấy nhất là khả năng mất tất cả commit từ phía sau commit bạn muốn xóa.
Nhánh tính năng mà bạn đang phát triển chưa được merge
vào develop, có một commit cuối cùng sai và bạn muốn xóa nó khỏi git thì lúc này có thể thoải mái sử dụng force push để đẩy lại code. Vì nhánh chỉ có một mình commit nên khả năng sẽ không có ai pull code về và push code lên. Cách làm thì cũng tương tự như trên.
# Trở về commit trước đó
$ git reset HEAD~1
# Force push lại toàn bộ commit trước đó
$ git push --force
Sử dụng rebase -i
để chỉnh sửa số lượng commit gần nhất. Ví dụ commit muốn xoá nằm ở vị trí thứ 10.
$ git rebase -i HEAD~10
pick abc123 Commit 1
pick def456 Commit 2
...
pick ghi789 Commit 10
Sửa lại pick
ở Commit 10 thành drop
và lưu lại. Nếu may mắn, bạn sẽ không phải giải quyết bất kỳ xung đột nào do commit vừa xoá không có đoạn mã nào liên quan đến 9 commit trước đó. Còn nếu không, tình xuống xấu nhất là cần phải giải quyết lần lượt 9 xung đột xảy ra trong 9 commit còn lại. Đó là một cơn ác mộng!
Cuối cùng, nếu commit đã được xoá, xung đột đã được giải quyết thì vẫn cần --force
để đẩy tất cả thay đổi lên remote, không quên kèm theo lời cầu nguyện cho đồng nghiệp chưa ai làm gì trên nhánh đó.
Trong git, nếu sử dụng --force
để push
, nó thay thế toàn bộ commit dưới local của bạn lên remote. Thế cho nên commit của người khác sau thời điểm bạn force thì sẽ "không cánh mà bay". Vì thế, một quy tắc vàng ở đây là: Chỉ force push trên nhánh một mình phát triển, còn đối với nhánh nhiều người tham gia thì không nên dùng force. Nếu vẫn quyết định dùng, hãy tập hợp mọi người lại và thảo luận cùng với họ.
Chưa kể nhiều nhóm có quy trình làm việc sử dụng Protected Branch, tức là tạo ra quy tắc để bảo vệ nhánh, ai có quyền pull
, push
, ai có quyền merge
... Giả sử không có quyền, đồng nghĩa không có cách nào để force push, giải pháp tốt nhất khi đó là giải trình với quản lý của bạn.
Từ đầu đến giờ cứ nhắc đến force, force, force nghe mà nặng nề. Vậy ngoài cách đó ra thì còn cách nào xóa được commit không? Theo tôi được biết thì không còn cách nào cả. Commit khi còn ở dưới local thì có thể xóa theo cách trong bài viết Tôi vừa lỡ commit sai, làm sao để sửa lại ngay lập tức?, chứ một khi đã push lên rồi thì chỉ có cách force để xóa, mà đã force hãy lưu ý đến đến khả năng làm mất commit của người khác.
Nếu không muốn dùng cờ force mà vẫn muốn huỷ bỏ commit cuối cùng thì hãy thử revert
lại commit trước đó. Lệnh revert
tạo ra một commit mới với các thay đổi nhằm khôi phục lại những gì đã đổi ở commit trước đó. Mọi thứ sẽ trở lại như cũ, chỉ có điều bạn không thể xóa được commit đó.
$ git revert HEAD
$ git push
Cuối cùng, đây là bài viết tham khảo chứ không khuyến khích bạn đọc áp dụng vào trong thực tế. Hãy sẵn sàng chịu trách nhiệm trước những gì mà bạn sắp làm. Hãy nhớ, chỉ nên dùng --force
khi biết chắc chắn rằng nó làm gì.
Git được sinh ra để giải quyết vấn đề làm việc nhóm, phân tán. Mỗi commit trong git như một cam kết và rất khó để xóa được chúng nếu đã đẩy lên remote. Tuy nhiên bạn có thể thử áp dụng cách dùng cờ --force
để xóa commit cuối cùng hoặc một giải pháp an toàn hơn là revert lại commit.
Bí mật ngăn xếp của Blog
Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!
Đăng ký nhận thông báo bài viết mới
Bình luận (0)