Chuyện gì vừa xảy ra
Bạn chạy git branch -d feature/my-feature để dọn dẹp sau khi hoàn thành một task, và Git từ chối:
error: The branch 'feature/my-feature' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature/my-feature'.
Những commit trên feature/my-feature không thể truy cập được từ branch hiện tại của bạn — thường là main hoặc develop. Xóa ngay bây giờ và Git sẽ dọn rác chúng sau. Công sức đổ sông đổ bể.
Sau là từ then chốt ở đây. Các commit vẫn còn tồn tại ngay lúc này. Bạn có thời gian để điều tra trước khi có chuyện xấu xảy ra.
Tại sao Git chặn việc xóa
git branch -d (chữ thường) là biến thể an toàn. Nó kiểm tra xem mọi commit trên branch có thể truy cập được từ upstream hoặc từ HEAD hay không. Nếu tìm thấy một commit không thỏa điều kiện, nó sẽ từ chối.
Bốn tình huống kích hoạt lỗi này:
- Branch đã được merge qua squash merge hoặc rebase — các commit SHA gốc không tồn tại trên
main, chỉ có một phiên bản squash hoặc rebase mới. - Branch đã được merge vào một branch khác chứ không phải branch bạn đang đứng.
- Pull request đã bị đóng mà không merge và bạn sắp mất công sức thực sự.
- Ai đó đã merge từ xa và bạn chưa fetch về.
Bước 1 — Xác định những gì thực sự chưa được merge
Trước khi dùng -D, hãy dành 30 giây cho việc này:
# Các commit trên feature branch KHÔNG có trên main
git log main..feature/my-feature --oneline
Không có output nghĩa là branch thực tế trống so với main — xóa cưỡng bức vẫn an toàn. Nếu bạn thấy commit, hãy đọc các message. Chúng có trông giống công việc bạn đã thấy xuất hiện trên main với một hash khác không?
# Đối chiếu: những gì đã xuất hiện trên main gần đây?
git log --oneline main | head -20
Cũng nên kiểm tra xem remote đã dọn dẹp sau một PR đã merge chưa:
git fetch --prune
git branch -r
Tình huống A — Branch đã được merge qua squash hoặc rebase (phổ biến nhất)
Chín trong mười trường hợp, đây là thủ phạm. Nút "Squash and merge" mặc định của GitHub và tùy chọn "Squash commits" của GitLab đều viết lại lịch sử commit — PR xuất hiện trên main dưới dạng một commit mới duy nhất với SHA hoàn toàn mới, nên Git không nhận ra các commit branch gốc là đã được merge.
Kiểm tra nhanh:
# Sẽ trống nếu code đã nằm trên main
git diff main...feature/my-feature
Diff trống? Code đã an toàn trên main. Xóa cưỡng bức branch local:
git branch -D feature/my-feature
Tình huống B — Branch được merge vào một branch khác
Feature có thể đã xuất hiện trong develop trong khi bạn đang đứng trên main. Kiểm tra với đúng branch:
git log develop..feature/my-feature --oneline
Trống? Chuyển sang branch đó và dùng lệnh xóa an toàn:
git checkout develop
git branch -d feature/my-feature
Hoặc bỏ qua bước checkout khi bạn đã xác nhận công việc ở đó:
git branch -D feature/my-feature
Tình huống C — Công việc thực sự chưa được merge
Nếu git log main..feature/my-feature --oneline hiển thị các commit bạn chưa từng thấy trên main, hãy dừng lại. Chưa xóa vội. Bạn có ba lựa chọn:
Lựa chọn 1 — Merge ngay bây giờ:
git checkout main
git merge feature/my-feature
git branch -d feature/my-feature
Lựa chọn 2 — Cherry-pick chỉ những commit cần thiết:
# Lấy hash từ output của git log
git cherry-pick <commit-hash>
Lựa chọn 3 — Tạo tag trước khi xóa (mạng lưới an toàn):
git tag backup/feature-my-feature feature/my-feature
git branch -D feature/my-feature
Tag giữ commit có thể truy cập mãi mãi. Để khôi phục sau này: git checkout -b feature/my-feature backup/feature-my-feature.
Lựa chọn mạnh tay — và khi nào dùng được
git branch -D feature/my-feature
Chữ hoa -D bỏ qua toàn bộ kiểm tra merge. Dùng khi:
- Bạn đã chạy
git diff main...feature/my-featurevà không thấy gì khác biệt. - PR đã được squash-merge hoặc rebase-merge và bạn chỉ đang dọn dẹp các ref local cũ.
- Đây là một thử nghiệm tạm thời mà bạn cố tình muốn loại bỏ.
Đừng dùng bừa bãi. Lỗi này là Git đang nhắc bạn suy nghĩ hai giây trước khi xóa sạch công việc mà bạn có thể cần đến.
Dọn dẹp cả các remote tracking reference
Xóa branch local để lại remote tracking reference phía sau. Hãy dọn dẹp nó:
# Xóa remote branch nếu nó vẫn còn tồn tại trên server
git push origin --delete feature/my-feature
# Hoặc prune các remote reference cũ sau khi server đã xóa
git fetch --prune
Xác nhận đã xử lý xong
# Branch phải biến mất ở local
git branch | grep feature/my-feature
# (không có output = tốt)
# Xác nhận các commit đã nằm trên main
git log main --oneline | head -10
# Kiểm tra các remote ref còn lơ lửng
git remote prune origin --dry-run
Tham khảo nhanh
# Chẩn đoán
git log main..feature/my-feature --oneline # những gì chưa có trên main?
git diff main...feature/my-feature # có sự khác biệt nào không?
# Xóa an toàn (sau khi xác minh)
git branch -D feature/my-feature
# Mạng lưới an toàn trước khi xóa công việc chưa merge
git tag backup/feature-my-feature feature/my-feature
git branch -D feature/my-feature

