Cho đến giờ, chúng ta chỉ xem xét kịch bản lý tưởng: bạn viết mã, tạo commit, mở Pull Request. Nhưng trong thực tế, mọi chuyện thường không diễn ra như ý: bạn quên thêm tệp, gõ nhầm trong thông điệp commit hoặc nhận ra mình đã đi sai hướng. Bài viết này sẽ chỉ cách xử lý những vấn đề thường gặp nhất bằng công cụ của IDE.

Ví dụ và ảnh chụp giao diện được thực hiện trên IDE JetBrains; tên và vị trí thao tác có thể khác tùy phiên bản bạn dùng. Hãy thực hành trong một repository riêng dùng để luyện tập. Với dự án thực tế, hãy tuân theo quy tắc về branch, commit và gửi thay đổi của dự án đó.

Khi Claude Code làm việc trong dự án, lịch sử đã lưu giúp bạn khôi phục trạng thái trước đó. Nhưng Git không ghi lại từng lần nhấn phím: các chỉnh sửa chưa commit có thể mất. Trước khi hoàn tác, hãy kiểm tra branch, các tệp đã chọn và xem bản sao cần dùng có thực sự tồn tại không.

Rollback - hoàn tác thay đổi cục bộ

Tình huống: bạn đã sửa một tệp nhưng nhận ra mọi chỉnh sửa đều sai và muốn nhanh chóng đưa tệp về trạng thái ở commit gần nhất.

Hình minh họa cho mục "Rollback - hoàn tác thay đổi cục bộ"

Hãy dùng thao tác Rollback:

  1. mở tab Commit ở bảng điều khiển bên trái;
  2. trong danh sách, tìm tệp đã thay đổi mà bạn muốn hoàn tác (hoặc chọn nhiều tệp);
  3. nhấp chuột phải rồi chọn Rollback (hoặc nhấn biểu tượng mũi tên cong).

IDE sẽ cảnh báo rằng các thay đổi cục bộ đã chọn sẽ bị mất. Trước tiên, hãy xem diff và lưu lại những gì cần giữ. Chỉ xác nhận Rollback với các chỉnh sửa bạn thực sự muốn bỏ; sau đó kiểm tra kết quả. Thao tác này xóa công việc chưa commit, chứ không sao lưu nó.

Lưu ý

Trước khi dùng Rollback, hãy tách phần chỉnh sửa của agent khỏi những thay đổi có từ trước trong cùng tệp. Rollback đưa các tệp đã chọn về trạng thái của commit gần nhất (HEAD) và loại bỏ cả thay đổi đã đưa vào staging lẫn chưa đưa vào. Việc này khác với git restore không có flag: lệnh đó lấy nội dung từ index và chỉ thay đổi các tệp trong working tree.

Amend - bổ sung commit gần nhất

Tình huống: bạn vừa nhấn Commit thì nhận ra mình quên đưa vào một tệp quan trọng (chẳng hạn tệp test vừa cập nhật) hoặc gõ nhầm trong thông điệp. Nếu commit chưa được chia sẻ, bạn có thể dùng Amend. Tạo một commit sửa lỗi riêng cũng hoàn toàn ổn, nhất là khi lịch sử đã được chia sẻ.

Hình minh họa cho mục "Amend - bổ sung commit gần nhất"

Để bổ sung cho một commit chưa được chia sẻ, hãy dùng thao tác Amend (bổ sung hoặc sửa commit):

  1. trong cửa sổ Commit, sửa tệp bị bỏ sót và đánh dấu ô bên cạnh tệp đó;
  2. trong bảng Commit Message, tìm ô chọn Amend (hoặc biểu tượng bánh răng -> Amend Commit) rồi đánh dấu;
  3. IDE sẽ điền thông điệp của commit trước đó để bạn chỉnh sửa;
  4. nhấn Commit.

Amend tạo một commit mới thay cho commit cũ, với mã định danh khác. Đối tượng cũ không bị sửa trực tiếp. Nếu commit trước đã được gửi và người khác đang dùng nó, thường nên tạo một commit sửa lỗi riêng.

Tìm hiểu thêm về thao tác này trong hướng dẫn chính thức của JetBrains.

Undo Commit

Tình huống: bạn vừa tạo commit nhưng chưa gửi nó bằng Push và nhận ra mình đã vội. Bạn muốn bỏ commit khỏi lịch sử nhưng vẫn giữ các thay đổi trong editor để tiếp tục làm việc.

Hãy dùng thao tác Undo Commit:

  1. mở lịch sử bằng tab Git -> Log ở phía dưới;
  2. tìm commit mới nhất của chính branch hiện tại (HEAD), chứ không đơn giản lấy dòng trên cùng của biểu đồ chung, rồi mở menu ngữ cảnh;
  3. chọn Undo Commit.
Hình minh họa cho mục "Undo Commit"

Chọn changelist (thường là Default Changelist) rồi nhấn OK. Commit sẽ biến mất khỏi lịch sử, còn các tệp trở lại cửa sổ Commit với trạng thái đã thay đổi. Bạn vẫn giữ được phần việc của mình và có thể tiếp tục viết mã.

Reset - di chuyển branch và trạng thái tệp

Thao tác Reset Current Branch to Here... là công cụ mạnh, cho phép di chuyển con trỏ branch đến bất kỳ commit nào đã chọn. Tùy tình huống, nó có thể nhẹ nhàng hoàn tác các bước gần nhất hoặc xóa hẳn phần mã bị hỏng.

Tình huống A - hoàn tác các commit cục bộ gần nhất

Bạn đã tạo vài commit cục bộ (trước khi Push) rồi nhận ra cả nhánh phát triển này đều đi sai hướng.

  1. mở tab Git -> Log;
  2. tìm commit tốt gần nhất mà bạn muốn quay lại (commit nằm trước chuỗi commit sai);
  3. nhấp chuột phải vào commit đó rồi chọn Reset Current Branch to Here....

Tình huống B - đưa về trạng thái trên server

Bạn muốn khôi phục các tệp đang được theo dõi theo branch từ xa. Trước tiên, hãy lưu lại phần công việc cục bộ cần giữ và chạy Fetch: nhãn origin/main phản ánh lần cập nhật gần nhất bạn nhận được, chứ không liên tục đồng bộ với server. Nếu đang có thao tác bị xung đột, hãy xử lý rồi hoàn tất hoặc hủy thao tác đó trước; đừng xem Hard là cách chữa mọi vấn đề.

  1. mở Git -> Log;
  2. tìm dấu branch từ xa - thường là commit có nhãn màu tím origin/main (hoặc origin/tên_branch_của_bạn). Đây là trạng thái mã mới nhất trên server mà bạn đã biết;
  3. nhấp chuột phải vào commit đó rồi chọn Reset Current Branch to Here....
Cửa sổ Reset Current Branch

Các chế độ Reset

Trong cửa sổ Reset, hãy chọn chế độ có chủ đích. Cả ba lựa chọn đều di chuyển branch hiện tại đến commit đã chọn, nhưng index và các tệp đang làm việc sẽ được xử lý khác nhau:

  • Soft giữ nguyên tệp đang làm việc và index. Mixed giữ nguyên tệp nhưng đưa index về commit đã chọn, nên phần chênh lệch vẫn chưa được đưa vào staging. Đây là hai chế độ khác nhau, dù cả hai đều cho phép bạn tiếp tục làm việc với nội dung tệp;
  • Hard đưa index và các tệp đang được theo dõi về commit đã chọn, đồng thời xóa các chỉnh sửa chưa commit của chúng. Các tệp untracked không liên quan thường vẫn còn, nhưng những đường dẫn cản trở việc khôi phục có thể bị xóa. Commit không lập tức biến mất khỏi mọi nơi lưu trữ, nhưng bạn không nên trông chờ có thể cứu lại phần việc sau đó.

Sau khi push thì làm gì

Tình huống: bạn đã chạy push rồi mới phát hiện lỗi.

Khi commit đã lên server, nó trở thành một phần trong lịch sử chung của dự án. Cố viết lại lịch sử đó (bằng Reset hoặc Amend) có thể gây rắc rối lớn cho đồng đội - hãy hình dung họ đã tải thay đổi của bạn về và bắt đầu xây dựng công việc của họ dựa trên đó.

Cách xử lý đúng và an toàn: chỉ cần tạo một commit mới để sửa lỗi rồi gửi bằng push như bình thường. Đây là cách làm phổ biến. Lịch sử tuyến tính, trong đó commit sửa lỗi rõ ràng theo sau phần mã sai, giúp cả nhóm theo dõi tiến trình dự án.

IDE còn có thao tác Revert Commit. Nếu nhấp chuột phải vào commit sai trong nhật ký Git -> Log rồi chọn Revert Commit, IDE sẽ tạo một "anti-commit" thực hiện chính xác điều ngược lại (chẳng hạn xóa những dòng đã thêm).

Hình minh họa cho mục "Sau khi push thì làm gì"

Revert hữu ích khi bạn cần hoàn tác một thay đổi đã được chia sẻ bằng commit mới. Nó áp dụng được cho cả mã lẫn văn bản, nhưng có thể xung đột với các chỉnh sửa về sau. Sau khi xử lý xung đột, hãy kiểm tra kết quả; nếu các thay đổi phụ thuộc lẫn nhau phức tạp, đôi khi tạo một bản sửa riêng sẽ dễ hiểu hơn.

Stash / Shelve - tạm cất thay đổi

Tình huống: bạn đang viết mã cho một feature mới, các tệp đều đang hiện màu đỏ và xanh lam. Bỗng trưởng nhóm chạy đến: "dừng lại, cần sửa gấp bug trên branch main". Bạn chưa thể chuyển sang main vì còn những thay đổi dang dở gây xung đột. Có thể tạo commit cục bộ tạm thời nếu quy tắc dự án cho phép, nhưng nếu chưa tiện commit lúc này thì bạn có thể tạm gác phần việc đó lại.

Giải pháp là tạm cất công việc vào một chiếc hộp ảo. Trong Git, tính năng này gọi là Stash; còn IDE có cơ chế riêng là Shelve (cất lên kệ).

  1. mở cửa sổ Commit và chọn các tệp đã thay đổi;
  2. chọn Shelve Changes...; các bước này dành cho Shelve, còn Git Stash được gọi riêng;
  3. đặt tên dễ hiểu cho mục đã cất rồi nhấn Shelve.
Hình minh họa cho mục "Stash / Shelve - tạm cất thay đổi"

Sau khi dùng Shelve, hãy kiểm tra các tệp còn lại: mọi chỉnh sửa cần giữ đã được cất đi chưa? Giờ bạn có thể chuyển sang main, sửa lỗi và lưu kết quả theo quy tắc dự án. Sau đó quay lại branch đang làm, mở Shelf và chọn Unshelve.

Với Git Stash, hãy mở danh sách stash riêng và khôi phục mục cần dùng. Đây là nơi lưu trữ khác. Stash thông thường không bao gồm tệp untracked mới nếu không có tùy chọn bổ sung; trước khi chuyển branch, hãy chắc chắn mọi phần việc cần thiết đều đã được lưu. Quá trình khôi phục có thể gây xung đột, nên hãy kiểm tra lại các tệp sau đó.

Khai thác Git Log hiệu quả hơn

Cửa sổ Git -> Log không chỉ là danh sách commit, mà còn là công cụ mạnh để truy vết lịch sử thay đổi.

  • Commit hash - chuỗi gồm chữ cái và chữ số, chẳng hạn a1b2c3d. Đây là mã định danh duy nhất của mỗi snapshot mã nguồn.Commit hash trong nhật ký
  • bạn có thể lọc lịch sử theo branch, tác giả, ngày hoặc từ khóa trong thông điệp commit - nhờ đó nhanh chóng tìm ra ai đã làm phần nào và vào lúc nào;
  • để tìm commit đã thêm hoặc xóa một dòng mã cụ thể, cửa sổ Log có thể tìm trong cả thông điệp lẫn nội dung thay đổi;
  • nhấp chuột phải vào lề editor (nơi có số dòng) và chọn Annotate with Git Blame - IDE sẽ cho biết bên cạnh mỗi dòng ai là người sửa gần nhất và sửa trong commit nào. Sau đó mở commit và cuộc thảo luận liên quan: Blame chỉ cho biết lần sửa dòng gần nhất, chứ không giải thích lý do.

Vùng rủi ro - Force Push

Chúng ta đã thấy rằng sửa các commit đã gửi không phải ý hay. Nhưng nếu bạn đã thay đổi lịch sử cục bộ (chẳng hạn bằng Undo Commit hoặc Amend) trong khi server vẫn có phiên bản cũ thì sao? Git sẽ báo lỗi khi bạn chạy push thông thường, để bảo vệ lịch sử chung khỏi bị ghi đè.

Force push cho phép thay thế tham chiếu từ xa đã chọn ngay cả khi lịch sử mới không tiếp nối lịch sử cũ. Lệnh này không xóa toàn bộ server: hậu quả phụ thuộc vào các tham chiếu bạn gửi lên. Quy tắc bảo vệ branch cũng có thể chặn kiểu push này.

Force push có thể loại khỏi branch từ xa những commit không có trong lịch sử cục bộ của bạn. Vì vậy, viết lại lịch sử của branch chung cần có quyết định riêng được cả nhóm thống nhất. Với lỗi thông thường, hãy tạo commit mới hoặc dùng Revert.

Trường hợp thường được cân nhắc force push là branch của riêng bạn sau khi đã thống nhất viết lại lịch sử. Ngay cả lúc đó, hãy kiểm tra xem có commit mới của người khác không. Tùy chọn --force-with-lease kiểm tra trạng thái dự kiến của branch từ xa, nhưng bạn vẫn cần hiểu commit nào sẽ bị thay thế.

Trong ảnh, Force Push nằm trong menu thả xuống của nút Push. Trước khi dùng, hãy kiểm tra phiên bản IDE của bạn áp dụng chế độ force push nào.

Nút Force Push

Nếu bạn không chắc 100% mình đang làm gì, đừng dùng force push. Tạo commit mới chứa phần sửa lỗi (hoặc dùng Revert) sẽ an toàn hơn.

Claude Code liên quan thế nào

Quy tắc này áp dụng cho cả bạn lẫn Claude Code: viết lại lịch sử chung rất rủi ro. Đừng xem force push là thao tác thường ngày và đừng để agent chạy lệnh đó trên branch chung. Nếu Claude đề xuất thao tác sẽ viết lại lịch sử trên server, hãy dừng lại và xem có lựa chọn an toàn hơn không (commit mới hoặc Revert).

Cập nhật dự án

Trước khi bắt đầu công việc mới, hãy kiểm tra branch gốc và những thay đổi còn dang dở. Lấy cập nhật khi cần rồi xem lại kết quả hợp nhất. Update Project không đảm bảo sẽ không có xung đột và có thể dùng merge hoặc rebase tùy thiết lập.

Sau khi cập nhật, hãy kiểm tra branch hiện tại, danh sách commit và trạng thái các tệp. Nếu có xung đột, hãy giải quyết trước khi tiếp tục.

Menu ngữ cảnh Git Log

Khi mở tab Git -> Log rồi nhấp chuột phải vào một commit bất kỳ, bạn sẽ thấy menu ngữ cảnh khá dài. Hãy điểm qua các lệnh hữu ích nhất, được nhóm theo mục đích.

Hình minh họa cho mục "Menu ngữ cảnh Git Log"

Nhóm 1 - xem và phân tích (thao tác an toàn)

  • Copy Revision Number - sao chép commit hash duy nhất vào clipboard. Tiện khi gửi cho đồng nghiệp liên kết đến commit hoặc ghi nó vào ticket (Jira, YouTrack);
  • Compare with Local - so sánh trạng thái tệp trong commit cũ với nội dung hiện có trong editor. Đây là cách hay để xem mã đã trông như thế nào một tháng trước;
  • Show Repository at Revision - mở cấu trúc tất cả tệp dự án tại thời điểm commit đó (branch hiện tại không thay đổi).

Nhóm 2 - làm việc với branch

  • New Branch... - tạo branch mới trực tiếp từ commit đã chọn trong quá khứ. Rất hữu ích nếu bạn đã đi quá xa, nhận ra mình làm hỏng mọi thứ và muốn bắt đầu branch mới từ trạng thái còn hoạt động gần nhất;
  • Checkout Revision - chuyển dự án cục bộ sang commit này. Bạn sẽ ở trạng thái detached HEAD: có thể chạy mã cũ, nhưng commit mới sẽ không gắn với branch nào. Nếu đã tạo commit cần giữ khi ở detached HEAD, trước tiên hãy lưu chúng vào branch mới. Để quay lại, hãy Checkout một branch thông thường (ví dụ main).

Nhóm 3 - thay đổi và chuyển commit

  • Cherry-Pick - cho phép lấy một commit cụ thể từ branch khác và sao chép vào branch hiện tại. Chẳng hạn, ai đó đã sửa bug đang cản trở công việc của bạn ở branch khác - bạn Cherry-Pick commit đó sang branch mình mà không cần merge toàn bộ branch kia;
  • Edit Commit Message... - nếu commit chưa được gửi (chưa Push), bạn có thể nhanh chóng đổi thông điệp. Cách này tiện hơn Amend khi chỉ cần sửa phần chữ.

Nhóm 4 - hoàn tác thay đổi

Chúng ta đã tìm hiểu kỹ các lệnh này ở trên:

  • Reset Current Branch to Here... - di chuyển branch hiện tại đến commit này (thay đổi lịch sử có thể truy cập từ branch đó);
  • Revert Commit - tạo một "anti-commit" để đảo ngược nội dung đã thay đổi bằng commit cục bộ mới, sau đó bạn có thể gửi lên server;
  • Undo Commit... - đưa commit cục bộ trở lại cửa sổ thay đổi.

Nhóm 5 - viết lại lịch sử

Menu còn có các lệnh như Squash Commits... (gộp nhiều commit thành một), Drop Commits (xóa commit ở giữa lịch sử) hoặc Interactively Rebase from Here.... Đây là các công cụ để tinh chỉnh lịch sử cục bộ bằng Interactive Rebase.

Lưu ý

Trong lúc làm quen, hãy hạn chế dùng các lệnh này - chúng có thể viết lại commit đã chọn cùng phần lịch sử tiếp theo của branch. Trong công việc nhóm, người ta dùng chúng để chỉnh lại branch cục bộ còn nháp trước khi gửi review (mở Pull Request).

Git là lưới an toàn của bạn

Hãy chọn cách khôi phục tùy theo trạng thái công việc: tệp chưa commit, commit cục bộ và lịch sử đã chia sẻ cần những cách xử lý khác nhau. Lưu đúng phiên bản cần thiết và xem các thay đổi trước thao tác hữu ích hơn là hy vọng mọi lỗi đều có thể hoàn tác.

Kết hợp với Claude Code

Khi Claude Code làm việc trong dự án, những công cụ này là lớp bảo vệ chính của bạn. Hãy áp dụng chúng như sau:

  • tạo commit thường xuyên: mỗi commit là một điểm có thể quay lại. Nếu agent đi sai hướng, bạn có thể trở về trạng thái hoạt động gần nhất;
  • để hoàn tác chỉnh sửa chưa commit, trước tiên hãy tách những thay đổi cần giữ, rồi kiểm tra phạm vi và nguồn khôi phục trong Rollback hoặc git restore;
  • nếu đã tạo commit nhưng chưa gửi, Undo Commit sẽ đưa các thay đổi trở lại editor;
  • nếu lỗi đã lên server, tạo commit sửa lỗi mới hoặc dùng Revert sẽ an toàn hơn viết lại lịch sử.
Lưu ý

Đừng đồng ý một cách máy móc với thao tác xóa dữ liệu hoặc viết lại lịch sử (Reset hard, force push). Trước tiên, hãy xem chính xác Claude Code định làm gì. Trước một công việc lớn, hãy lưu trạng thái cần thiết và kiểm tra điểm khôi phục thực sự có chứa phần việc của bạn.

Bài viết trước: Pull request từ lúc tạo đến khi merge · Bài viết tiếp theo: Tạo repository, rebase và bí mật