Rebase là một thao tác trong Git di chuyển commit từ nhánh này lên đỉnh của nhánh khác, tạo ra lịch sử tuyến tính mà không có commit merge không cần thiết. Không giống như hợp nhất, rebase viết lại lịch sử: mỗi commit được di chuyển nhận được một hash mới vì cha của nó thay đổi. Theo tài liệu Git (2026), rebase được sử dụng để đồng bộ nhánh tính năng với trạng thái main hiện tại trước khi tạo pull request. Lệnh git rebase là một trong những công cụ chính để duy trì lịch sử sạch trong các dự án sử dụng Git Flow.
Điểm Chính
Rebase là một lệnh Git giúp đặt lại nhánh hiện tại lên một nhánh chỉ định: nó lấy tất cả commit từ nhánh hiện tại, tạm thời lưu chúng, di chuyển con trỏ nhánh đến commit đích và áp dụng tuần tự các commit đã lưu lên trên nó. Kết quả — lịch sử trông như thể nhà phát triển đã làm việc trực tiếp từ commit cuối cùng của nhánh đích.
Cú pháp cơ bản: git rebase main — khi đang ở nhánh tính năng, lệnh này di chuyển tất cả commit tính năng lên đỉnh của main. Git sử dụng chiến lược hợp nhất ba chiều cho từng commit riêng lẻ. Nếu commit A đã có trong nhánh đích (được xác định bằng hash), Git tự động bỏ qua nó, tránh các thay đổi trùng lặp.
Rebase cũng hỗ trợ chế độ onto để di chuyển một tập con của commit: git rebase --onto target start end — dạng này cho phép trích xuất một phạm vi commit từ một nhánh và áp dụng chúng lên một nhánh khác. Ví dụ, git rebase --onto main feature~3 feature di chuyển ba commit cuối cùng của nhánh feature lên main.
# Switch to feature branch
git checkout feature
# Rebase feature onto main
git rebase main
# After successful rebase — history is linear
git log --oneline --graph
# Move last 3 commits to main
git rebase --onto main HEAD~3 HEAD
Rebase và merge giải quyết cùng một nhiệm vụ — kết hợp các thay đổi từ các nhánh khác nhau — nhưng theo những cách hoàn toàn khác nhau. Merge bảo toàn lịch sử hợp nhất đầy đủ bằng cách tạo một commit merge với hai cha. Rebase viết lại lịch sử, làm cho nó tuyến tính. Việc lựa chọn giữa chúng phụ thuộc vào quy trình làm việc của nhóm và các quy tắc quản lý kho lưu trữ.
Sự khác biệt chính là cách ghi nhận sự kiện hợp nhất. Merge ghi lại: “tại điểm này chúng tôi đã hợp nhất feature vào main” — điều này có ích cho lịch sử dự án nhưng làm lộn xộn nhật ký khi hợp nhất thường xuyên. Rebase hiển thị: “các commit tính năng được tạo tuần tự từ trạng thái main mới nhất” — điều này sạch nhưng che giấu thực tế là việc phát triển được thực hiện song song.
Sự khác biệt thứ hai là xử lý xung đột. Với merge, xung đột được giải quyết một lần và giải pháp được ghi lại trong commit merge. Với rebase, xung đột có thể xảy ra cho mỗi commit được di chuyển, mỗi cái yêu cầu giải quyết riêng. Điều này tốn công hơn nhưng cho phép kiểm soát chính xác hơn những thay đổi nào được đưa vào phiên bản cuối cùng.
| Tiêu chí | Rebase | Merge |
|---|---|---|
| Lịch sử | Tuyến tính, không có commit merge | Phi tuyến, có commit merge |
| Hash commit | Được viết lại (mới) | Giữ nguyên bản gốc |
| Xung đột | Cho từng commit riêng lẻ | Một lần trong commit merge |
| Nhánh công khai | Bị cấm | Được phép |
| Lệnh hoàn tác | git rebase --abort | git merge --abort |
Rebase tương tác (git rebase -i) là chế độ mà Git mở trình soạn thảo với danh sách các commit và các hành động khả dụng cho từng commit. Nhà phát triển có thể viết lại lịch sử trước khi đẩy lên kho lưu trữ từ xa. Đây là công cụ chính để duy trì các commit sạch trong nhánh tính năng.
Các lệnh khả dụng trong chế độ tương tác: pick (giữ commit nguyên trạng), reword (thay đổi thông điệp commit), edit (dừng lại để thay đổi), squash (kết hợp với commit trước, giữ cả hai thông điệp), fixup (kết hợp, bỏ thông điệp), drop (xóa commit). Mỗi lệnh được đặt trước hash commit trong trình soạn thảo đã mở.
Squash và fixup là các lệnh được sử dụng thường xuyên nhất để kết hợp commit. Nếu nhà phát triển đã tạo 5 commit sửa lỗi nhỏ trong quá trình làm việc, squash hợp nhất chúng thành một commit logic duy nhất với một thông điệp có ý nghĩa. Fixup hữu ích để sửa lỗi chính tả: các thay đổi đi vào commit trước mà không giữ thông điệp riêng của chúng.
# Open editor for last 4 commits
git rebase -i HEAD~4
# Editor will show:
pick a1b2c3d Add authentication
pick e4f5g6h Fix typo in login
squash i6j7k8l Additional login fixes
pick m9n0o1p Add auth tests
# After saving — Git performs rebase
# and opens editor for squashed commit message
# Auto-squash without opening editor
git rebase -i HEAD~4 --autosquash
Cờ --autosquash tự động sắp xếp fixup/squash cho các commit có thông điệp bắt đầu bằng fixup! hoặc squash!. Điều này tăng tốc công việc nếu nhà phát triển đánh dấu trước các commit để kết hợp sau này. Cờ --committer-date-is-author-date giữ nguyên ngày commit gốc khi rebase — hữu ích để duy trì thứ tự thời gian trong lịch sử.
Xung đột khi rebase xảy ra khi Git không thể tự động áp dụng một commit được di chuyển do mâu thuẫn với các thay đổi trong nhánh đích. Không giống merge, nơi xung đột được giải quyết một lần, với rebase mỗi commit có thể gây ra xung đột và phải được giải quyết tuần tự cho từng commit từ cũ nhất đến mới nhất.
Khi xung đột xảy ra, Git tạm dừng rebase và báo cáo commit nào đã gây ra sự cố. Nhà phát triển mở tập tin xung đột (Git đánh dấu các khu vực xung đột bằng các dấu <<<<<<<, =======, >>>>>>>), chỉnh sửa nó, thêm vào chỉ mục (git add), và tiếp tục rebase bằng lệnh git rebase --continue. Nếu không tìm thấy giải pháp — git rebase --abort hủy bỏ hoàn toàn rebase.
Mẹo: với nhiều xung đột, sử dụng git mergetool hiệu quả hơn, công cụ này mở trình soạn thảo trực quan để giải quyết xung đột. Bạn cũng có thể bỏ qua commit có vấn đề (git rebase --skip), nhưng điều này sẽ loại bỏ các thay đổi của nó khỏi lịch sử cuối cùng, đây hiếm khi là quyết định đúng đắn.
# Start rebase with conflict
git rebase main
# Auto-merging file.txt
# CONFLICT (content): Merge conflict in file.txt
# Check status
git status
# both modified: file.txt
# Edit conflicted sections → git add → continue
git add file.txt
git rebase --continue
# If uncertain — abort
git rebase --abort
Quy tắc vàng của rebase: không bao giờ rebase các commit đã được đẩy lên kho lưu trữ từ xa và có sẵn cho các nhà phát triển khác. Vì rebase viết lại hash commit, đồng nghiệp sẽ gặp xung đột khi cố gắng đồng bộ — lịch sử cục bộ của họ sẽ khác với lịch sử từ xa đã được viết lại.
Tình huống mà rebase bị cấm tuyệt đối: nếu ai đó đã tạo một nhánh dựa trên commit của bạn (ví dụ, đồng nghiệp đã tạo nhánh từ nhánh tính năng của bạn), việc viết lại lịch sử sẽ phá vỡ công việc của họ. Trong những trường hợp này, hãy sử dụng merge. Cũng không nên rebase ngay trước hạn chót — lỗi khi giải quyết xung đột có thể mất nhiều thời gian hơn dự kiến và chặn bản phát hành.
Ngoại lệ: nếu nhánh chỉ được sử dụng bởi một nhà phát triển (nhánh tính năng cá nhân, chưa được xuất bản hoặc được xuất bản ở chế độ nháp), rebase trước khi push là thực hành tiêu chuẩn. Sau khi xuất bản và bắt đầu làm việc cộng tác — chỉ merge. GitHub và GitLab theo mặc định cung cấp squash merge như một sự thỏa hiệp: nó kết hợp các commit thành một nhưng không viết lại lịch sử của nhánh đích.
Các nhóm hiện đại thường sử dụng quy trình làm việc định hướng rebase kết hợp với GitHub Flow. Quá trình diễn ra như sau: nhà phát triển tạo một nhánh tính năng từ main, làm việc trong đó, đồng bộ định kỳ qua git rebase main và trước khi tạo pull request thực hiện rebase tương tác để dọn lịch sử.
Sau khi tạo PR (nếu cần kéo các thay đổi mới từ main), git pull --rebase main được sử dụng thay vì git pull thông thường. Điều này kéo các thay đổi mà không tạo ra commit merge không cần thiết. Git pull với cờ --rebase tương đương với git fetch + git rebase — Git đầu tiên tải các commit mới, sau đó rebase các thay đổi cục bộ lên trên chúng.
Git cho phép cấu hình rebase là hành vi mặc định cho pull: git config --global pull.rebase true. Sau cài đặt này, git pull luôn thực hiện rebase thay vì merge. Nếu cần pull thông thường — sử dụng git pull --no-rebase. Nhiều nhóm cũng bật autostash: git config --global rebase.autoStash true — điều này tự động ẩn các thay đổi chưa commit trước khi rebase và khôi phục chúng sau đó.
Câu hỏi Thường gặp
Rebase có nghĩa là thực hiện git rebase: di chuyển các commit từ nhánh hiện tại lên đỉnh của nhánh khác. Kết quả là lịch sử trở nên tuyến tính, mỗi commit nhận được một hash mới và các commit merge không được tạo ra. Lệnh này được sử dụng để đồng bộ các nhánh mà không có các điểm hợp nhất không cần thiết trong nhật ký.
Merge tạo một commit merge với hai cha, bảo toàn lịch sử song song và các hash gốc. Rebase viết lại lịch sử — các commit nhận hash mới và lịch sử trở nên tuyến tính. Merge an toàn hơn cho các nhánh công khai, rebase cung cấp nhật ký sạch hơn.
Lệnh git rebase -i HEAD~N mở trình soạn thảo với N commit cuối cùng. Cho mỗi commit, bạn có thể chọn một hành động: pick (giữ), reword (đổi tên), edit (sửa), squash (kết hợp với commit trước), fixup (kết hợp không có thông điệp), drop (xóa). Sau khi lưu, Git áp dụng các thay đổi đã chọn.
Rebase viết lại hash commit, làm cho lịch sử không tương thích với các bản sao của cùng commit trên máy của các nhà phát triển khác. Nếu đồng nghiệp đã nhận được commit của bạn qua git pull và sau đó bạn rebase chúng, git push của họ sẽ bị từ chối và git pull sẽ tạo các commit trùng lặp và xung đột.
Trước khi hoàn thành — git rebase --abort hủy bỏ hoàn toàn. Sau khi hoàn thành, có thể khôi phục trạng thái trước đó qua git reflog — tìm hash commit trước khi rebase và thực hiện git reset --hard về nó. Reflog lưu trữ lịch sử di chuyển của HEAD trong 30 ngày theo mặc định.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm