Git Interactive Rebase: Cách mình “tẩy trắng” lịch sử commit rác trước khi lên Production

Git tutorial - IT technology blog
Git tutorial - IT technology blog

Cơn ác mộng lúc 2 giờ sáng và những dòng commit vô nghĩa

Đã bao giờ bạn nhìn lại git log của mình và cảm thấy… xấu hổ chưa? 2 giờ sáng, mình ngồi dán mắt vào màn hình terminal, chuẩn bị merge feature quan trọng vào branch main. Gõ lệnh git log --oneline, mình bỗng khựng lại. Trước mắt là một mớ hỗn độn:

a7b2c3d fix typo
9e8f7g6 update
5h4i3j2 fix bug
1k2l3m4 fix bug again
0n9o8p7 em thề là lần này chạy được thật
6q5r4s3 thêm logic thanh toán

Để nguyên mớ này mà tạo Pull Request (PR) thì sáng mai chắc chắn bị Lead “sấy”. Ai lại để 4 cái commit chỉ để sửa đúng một lỗi typo bao giờ? Nó khiến lịch sử Git trông cực kỳ thiếu chuyên nghiệp. Sau này, nếu cần dùng git bisect để truy vết bug, đống commit “rác” này sẽ là một trở ngại lớn. Đây chính là lúc Git interactive rebase xuất hiện như một vị cứu tinh.

Git interactive rebase thực chất là gì?

Về cơ bản, git rebase là việc đặt các commit của bạn lên trên một base (gốc) mới. Khi thêm flag -i (interactive), Git sẽ mở ra một giao diện soạn thảo. Tại đây, bạn có quyền can thiệp trực tiếp vào từng commit trong quá khứ.

Công cụ này cho phép bạn thực hiện những quyền năng như:

  • Squash: Gộp 10 commit nhỏ lẻ, vụn vặt thành một commit lớn duy nhất.
  • Reword: Sửa lại những message kiểu “asdfgh” thành nội dung rõ nghĩa.
  • Drop: Xóa bỏ hoàn toàn những commit chứa code test bẩn.
  • Reorder: Thay đổi thứ tự trước sau của các commit cho logic hơn.

Nó giống như việc bạn biên tập lại một cuốn phim. Bạn cắt bỏ cảnh thừa, sắp xếp lại phân đoạn để người xem dễ hiểu nhất.

Thực hành: Bắt đầu dọn rác

Đầu tiên, hãy xác định bạn muốn quay lại bao nhiêu commit. Giả sử mình cần dọn dẹp 5 commit gần nhất:

git rebase -i HEAD~5

Ngay lập tức, Git sẽ mở editor mặc định (Vim hoặc Nano) với danh sách commit. Một điểm cần lưu ý: Thứ tự ở đây ngược với git log. Commit cũ nhất nằm ở trên cùng, commit mới nhất nằm dưới cùng.

pick 6q5r4s3 thêm logic thanh toán
pick 0n9o8p7 em thề là lần này chạy được thật
pick 1k2l3m4 fix bug again
pick 5h4i3j2 fix bug
pick 9e8f7g6 update
pick a7b2c3d fix typo

1. Gộp commit (Squash & Fixup)

Đây là tính năng mình dùng nhiều nhất. Thay vì để 4 cái commit fix bug lẻ tẻ, mình sẽ gộp chúng vào commit chính. Hãy đổi chữ pick thành f (fixup) cho các commit con. fixup sẽ xóa luôn message của commit đó và nhập nội dung vào commit phía trước.

pick 6q5r4s3 thêm logic thanh toán
f 0n9o8p7 em thề là lần này chạy được thật
f 1k2l3m4 fix bug again
f 5h4i3j2 fix bug
f 9e8f7g6 update
f a7b2c3d fix typo

2. Sửa message (Reword)

Bạn lỡ tay đặt tên commit là “fix bug” nhưng muốn đổi thành “Fix: Xử lý lỗi tràn bộ nhớ khi upload ảnh”? Hãy đổi pick thành reword (hoặc r). Sau khi lưu file, Git sẽ mở thêm một cửa sổ để bạn nhập message mới tử tế hơn.

3. Xóa commit (Drop)

Nếu thấy một commit chứa code test hoặc file config cá nhân, hãy đổi pick thành drop (hoặc d). Bạn cũng có thể đơn giản là xóa dòng đó đi trong file rebase. Commit đó sẽ biến mất như chưa từng tồn tại.

Giải quyết xung đột và hoàn tất

Sau khi chỉnh sửa, hãy lưu file và thoát (trong Vim là :wq). Nếu mọi thứ suôn sẻ, bạn sẽ thấy thông báo thành công. Tuy nhiên, thực tế thường rắc rối hơn. Đôi khi bạn sẽ gặp xung đột (conflict).

Đừng hoảng loạn. Git sẽ dừng lại để bạn sửa code. Sau khi sửa xong, bạn chỉ cần gõ:

git add .
git rebase --continue

Nếu cảm thấy mọi thứ đang rối tung lên, hãy dùng lệnh “thoát hiểm” để quay về trạng thái cũ:

git rebase --abort

Bài học xương máu: Đừng bao giờ đùa với Force Push

Rebase xong ở local, lịch sử commit của bạn đã khác hoàn toàn với remote server. Lúc này, git push thông thường sẽ bị từ chối. Bạn buộc phải dùng --force.

Mình từng mất trắng công sức một buổi sáng vì force push nhầm branch. Thay vì push lên branch cá nhân, mình gõ nhầm sang branch develop của team. Kết quả là đè nát code của 3 đồng nghiệp khác. Mình đã phải toát mồ hôi hột dùng git reflog suốt 2 tiếng để khôi phục.

Lời khuyên của mình: Hãy dùng --force-with-lease. Nó sẽ kiểm tra xem có ai khác đã push code lên remote trong lúc bạn đang rebase hay không. Nếu có, nó sẽ chặn lại để bạn không ghi đè lên công sức của người khác.

git push origin feature/payment --force-with-lease

Khi nào nên tránh xa Interactive Rebase?

Dù cực kỳ mạnh mẽ, nhưng có một quy tắc vàng: Không bao giờ rebase những commit đã được push lên branch chung.

Rebase thực chất là tạo ra các commit ID mới. Nếu bạn thay đổi lịch sử của branch main, các đồng nghiệp khác sẽ ăn trọn “hành” khi git pull. Hãy chỉ dùng nó trên branch cá nhân của bạn trước khi tạo Merge Request.

Lời kết

Git interactive rebase là cách bạn thể hiện sự chuyên nghiệp với người review code. Một lịch sử commit sạch sẽ giúp team hiểu nhanh bạn đã làm gì và tại sao làm thế. Việc bảo trì code sau này cũng nhờ đó mà nhẹ nhàng hơn rất nhiều.

Lần tới, trước khi đưa code lên production, hãy dành 5 phút để dọn dẹp lại. Tin mình đi, đồng nghiệp sẽ thầm cảm ơn bạn đấy!

Share: