Git Rebase vs Merge: Đừng để lịch sử Commit của bạn trông như “mớ dây điện”

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

Tại sao lịch sử Git của bạn lại quan trọng?

Hãy tưởng tượng bạn đang tham gia một dự án lớn với 15 developers. Mỗi ngày có hàng chục Pull Request được merge. Nếu ai cũng dùng lệnh Merge mặc định, biểu đồ Git (Network Graph) sẽ sớm trở thành một “mớ dây điện” chằng chịt, chồng chéo lên nhau. Việc truy vết lỗi hay tìm lại một thay đổi cụ thể lúc này chẳng khác nào mò kim đáy bể.

Khi cần cập nhật code mới nhất từ nhánh main vào nhánh feature của mình, bạn thường đứng trước hai ngã rẽ: Merge hoặc Rebase. Cả hai đều giải quyết cùng một vấn đề, nhưng cách chúng để lại dấu vết trong lịch sử lại hoàn toàn khác biệt.

  • Merge: Gộp hai nhánh lại và tạo ra một “merge commit” mới. Cách này giữ nguyên vẹn lịch sử, kể cả những lần gộp nhánh lỗi hay code rác.
  • Rebase: “Viết lại” lịch sử bằng cách chuyển toàn bộ các commit của bạn lên trên đỉnh của nhánh mục tiêu. Kết quả là một đường thẳng tắp, cực kỳ dễ đọc.

Tôi từng làm việc với một Tech Lead cực kỳ khó tính. Anh ấy yêu cầu mọi commit trước khi lên production phải được dọn dẹp sạch sẽ. Ban đầu tôi thấy phiền phức, nhưng khi dự án cán mốc 1.000 commit, tôi mới hiểu giá trị của một lịch sử “sạch”. Nó giúp lệnh git bisect tìm ra commit gây lỗi chỉ trong vài phút thay vì vài giờ.

Chuẩn bị môi trường trước khi thao tác

Đừng bao giờ bắt đầu rebase khi base code local của bạn đã cũ. Việc đầu tiên là phải đồng bộ với server để tránh những xung đột không đáng có.

# Cập nhật thông tin mới nhất từ server
git fetch origin

# Chuyển sang nhánh tính năng bạn đang làm
git checkout feature-xyz

Một mẹo nhỏ để quan sát sự khác biệt: hãy cài đặt alias git adog. Lệnh này giúp bạn xem biểu đồ commit dưới dạng cây ngay trên terminal, trực quan hơn nhiều so với lệnh log mặc định.

git config --global alias.adog "log --all --decorate --oneline --graph"

So sánh chi tiết: Merge an toàn và Rebase tinh tế

1. Git Merge: Lựa chọn “giữ nguyên hiện trường”

Khi bạn gộp main vào feature bằng Merge, Git sẽ thực hiện một phép tính toán 3 chiều (3-way merge) để tạo ra một commit gộp.

git checkout feature-xyz
git merge main

Cách này cực kỳ an toàn vì nó không thay đổi các commit cũ. Tuy nhiên, nếu bạn merge liên tục để cập nhật code mỗi ngày, nhánh của bạn sẽ đầy rẫy các commit có nội dung kiểu “Merge branch ‘main’ into…”, gây nhiễu lịch sử dự án.

2. Git Rebase: Nghệ thuật kể chuyện lịch sử

Rebase hoạt động theo cơ chế khác. Nó tạm thời nhấc các commit của bạn ra, cập nhật nhánh feature lên bản mới nhất của main, rồi đặt các commit của bạn lên trên cùng.

git checkout feature-xyz
git rebase main

Lúc này, lịch sử trông như thể bạn vừa mới bắt đầu code trên nền tảng mới nhất. Mọi thứ diễn ra theo một trình tự thời gian hoàn hảo.

Sức mạnh của Interactive Rebase:

Trong quá trình code, chúng ta hay có những commit vụn vặt như “fix typo” hay “update linh tinh”. Trước khi push, hãy dùng chế độ tương tác để gộp 10 commit rác thành 1 commit có ý nghĩa duy nhất:

git rebase -i HEAD~5 # Chỉnh sửa 5 commit gần nhất

Trong bảng hiện ra, hãy đổi lệnh pick thành squash (hoặc s) cho những commit phụ. Git sẽ gộp tất cả chúng vào commit chính ngay phía trên.

Quy tắc vàng: Khi nào nên dùng lệnh nào?

Để workflow chuyên nghiệp, bạn có thể áp dụng công thức sau:

  • Dùng Rebase: Cho các nhánh cá nhân (local branch). Hãy dọn dẹp commit của mình cho thật đẹp trước khi công khai nó với thế giới.
  • Dùng Merge: Khi gộp một tính năng đã hoàn thiện vào nhánh chung (như main). Điều này giúp lưu lại cột mốc rõ ràng: “Tính năng X đã được tích hợp vào thời điểm này”.

Lưu ý “sống còn”: Tuyệt đối không rebase trên các nhánh công khai mà nhiều người đang cùng làm việc. Việc này làm thay đổi mã hash của commit, khiến đồng nghiệp của bạn gặp lỗi xung đột lịch sử nghiêm trọng khi pull code về.

Xử lý xung đột và đẩy code an toàn

Xung đột (conflict) là điều khó tránh khỏi. Khi rebase, xung đột có thể xảy ra ở từng commit thay vì chỉ một lần như merge. Đừng hoảng loạn, hãy sửa code rồi chạy:

git add . 
git rebase --continue

Nếu mọi thứ quá rối rắm, bạn luôn có đường lui với lệnh: git rebase --abort. Lệnh này sẽ đưa nhánh của bạn về trạng thái nguyên thủy như chưa có chuyện gì xảy ra.

Sau khi rebase thành công ở local, bạn sẽ không thể push theo cách thường. Hãy sử dụng lệnh an toàn sau:

git push --force-with-lease

Tại sao lại là --force-with-lease? Khác với --force mù quáng, lệnh này sẽ từ chối push nếu có ai đó khác đã đẩy code mới lên nhánh đó mà bạn chưa kịp cập nhật. Đây là chốt chặn cuối cùng giúp bạn không vô tình ghi đè và làm mất code của đồng nghiệp.

Tóm lại, hãy dùng Merge để tôn trọng sự thật lịch sử và dùng Rebase để kể lại câu chuyện đó một cách mạch lạc nhất.

Share: