Bỏ qua để đến nội dung
Knowledge Hub

Branching strategy, merge/rebase/squash và force push an toàn Trung cấp

Thời gian đọc ước tính: 5 phút

Trang này trả lời: team nên dùng mô hình branch nào, tích hợp PR bằng merge, rebase hay squash, và force push thế nào cho không mất commit của đồng đội.

Trunk-based (branch ngắn) GitHub flow GitFlow
Branch lâu dài Chỉ main (trunk) Chỉ main main + develop, thêm release/*, hotfix/*
Branch tính năng Sống rất ngắn, merge vào trunk ít nhất mỗi ngày Branch → PR → review → merge → xoá branch feature/* từ develop, có thể sống lâu
Tính năng dở dang Feature flag, branch by abstraction Feature flag hoặc giữ PR mở Giữ trên feature branch
Release Từ trunk, hoặc cắt release branch “just in time” Deploy từ main Qua release/*, merge về main và develop
Hợp với Web service, continuous delivery Team nhỏ-vừa, deploy liên tục Phần mềm có nhiều version song song (app cài đặt, SDK, firmware)

Cơ sở cho lựa chọn:

  • trunkbaseddevelopment.com: dev cộng tác trên một nhánh “trunk”, tránh branch dài hạn. Branch tính năng phải ngắn, chỉ để review và chạy CI; mỗi dev tích hợp vào trunk ít nhất mỗi 24 giờ.
  • Tác giả GitFlow, Vincent Driessen, thêm “note of reflection” (5/3/2020): web app thường continuous delivery, không phải hỗ trợ nhiều version. Với team như vậy ông khuyên workflow đơn giản hơn như GitHub flow.

Mặc định cho backend service: trunk-based / GitHub flow, PR nhỏ, feature flag cho việc dài. Chỉ dùng GitFlow khi thật sự phải vá nhiều version đang chạy ngoài kia.

Branch feature có 3 commit (A, B, C), tách từ main tại M1; trong lúc đó main có thêm M2.

Trước khi tích hợp:
main: M1 ── M2
\
feature: A ── B ── C
1) Merge commit (git merge --no-ff / "Create a merge commit")
main: M1 ── M2 ─────────── X X có 2 parent: M2 và C
\ /
A ──── B ──── C ─ giữ nguyên A, B, C và hash cũ
2) Rebase rồi fast-forward (git rebase main / "Rebase and merge")
main: M1 ── M2 ── A' ── B' ── C' A', B', C' là commit MỚI (hash mới)
3) Squash merge ("Squash and merge")
main: M1 ── M2 ── S S = A+B+C gộp làm một commit
Merge commit Rebase + fast-forward Squash merge
Lịch sử main Có nhánh, đầy đủ Thẳng, giữ từng commit Thẳng, 1 commit/PR
Hash commit gốc Giữ Đổi Mất (commit gốc chỉ còn trên branch)
Revert cả PR git revert -m 1 X Revert từng commit git revert S
Hợp khi Commit trong PR đều có chủ đích, cần giữ Commit đã được dọn sạch, atomic PR nhỏ, commit trung gian là “wip”

GitHub docs lưu ý: squash merge làm mất SHA gốc. Nếu tiếp tục mở PR mới từ cùng branch đã squash, commit cũ có thể xuất hiện lại và gây conflict lặp. Sau squash merge: xoá branch, tạo branch mới từ main.

Pro Git: “Do not rebase commits that exist outside your repository and that people may have based work on.”

Rebase không “di chuyển” commit. Nó tạo commit mới (A') và bỏ commit cũ (A). Ai đã build trên A phải merge lại, và lịch sử có hai bản A (xem demo trong Lỗi hay gặp).

Branch Rebase lên origin/main?
Branch của riêng bạn, kể cả đã push để mở PR Được — miễn chưa ai base công việc lên nó
main, develop, branch nhiều người cùng push Không bao giờ — dùng git merge origin/main

Rebase branch đã push thì bắt buộc force push. Khác biệt:

Lệnh Hành vi
git push --force Tắt mọi kiểm tra; ghi đè remote dù trên đó có commit bạn chưa thấy. Docs: “It can cause the remote repository to lose commits”
git push --force-with-lease Chỉ ghi đè nếu ref trên remote vẫn bằng remote-tracking ref của bạn (origin/feature)
git push --force-with-lease --force-if-includes Thêm kiểm tra: tip của remote-tracking ref phải reachable từ một entry trong reflog của branch local — chống trường hợp IDE/cron tự git fetch ngầm làm lease mất tác dụng

Đang tải diagram…

Alias nên có trong ~/.gitconfig:

Terminal window
git config --global alias.pushf "push --force-with-lease --force-if-includes"

Reviewer chỉ ra lỗi ở commit refactor (không phải commit cuối). Đừng tạo commit “fix review” lẻ loi.

Terminal window
git add src/order/pricing.ts
git commit --fixup=3f2a9c1 # tạo commit "fixup! refactor(order): tách tính giá..."
git rebase -i --autosquash origin/main
# todo list tự đặt "fixup 7b1e..." ngay sau 3f2a9c1 — lưu và thoát
git push --force-with-lease

Theo docs:

  • --autosquash nhận diện message bắt đầu bằng fixup! , squash! , amend! và chuyển commit đó về ngay sau commit đích.
  • --fixup=amend:<commit> thay luôn message; --fixup=reword:<commit> chỉ đổi message.
  • Bật mặc định: git config --global rebase.autoSquash true.

Mẹo: muốn reviewer xem riêng phần sửa, push commit fixup! trước, đợi approve rồi mới autosquash.

Tiếp theo: Cứu sự cố.