Batch fix merge conflict khi merge hàng loạt PR

TẤT CẢ Batch fix merge conflict khi merge hàng loạt PR
Mục lục

Chạy lệnh merge tất cả Pull Request đang mở, mình thấy 4 trong 6 PR báo đỏ "conflict". Cảm giác đầu tiên là ngán: hình dung phải mở từng file, đọc từng dòng <<<<<<<, đoán bên nào đúng. Nhưng sau vài lần va, mình nhận ra batch fix merge conflict kiểu này hầu hết không phải xung đột logic — nó là "merge race" ở file tự sinh. Phân loại đúng thì fix cả loạt nhanh hơn hẳn. Bài này mình chia sẻ lại cách làm thật, từ lúc bấm lệnh tới lúc push sạch.

Bối cảnh: một lệnh merge cả loạt PR

Mình gom thao tác merge hàng loạt vào một shortcut nội bộ (đặt tên prm). Ý tưởng đơn giản: liệt kê mọi PR đang mở, cái nào CI xanh thì squash-merge lên main, rồi để deploy tự chạy. Đây là kiểu workflow "gom một lượt" quen thuộc với ai làm blog/tool nhiều nhánh song song.

Vấn đề là khi có nhiều PR mở cùng lúc, mỗi lần một PR merge vào main sẽ làm các PR còn lại bị lệch nền (stale base). Cái này khớp với những gì mình từng ghi lại ở bài giải quyết merge conflict tự động trong CI/CD — càng nhiều PR chờ, xác suất đụng nhau càng cao.

Vì sao PR "sạch" bỗng conflict

Đây là hiểu lầm phổ biến nhất, nên mình nói kỹ. CI xanh chỉ chứng minh một điều: nhánh đó nhất quán với chính nó tại thời điểm chạy. Nó không chứng minh nhánh còn merge sạch với main ở hiện tại.

Trong lúc PR nằm chờ, main vẫn chạy tiếp: các hook/cron sinh lại file dữ liệu, PR khác merge trước và đổi cùng vùng file. Kết quả là nhánh của bạn dựa trên một bản main cũ. Đến lúc merge, git so bản nền cũ với main mới → phát sinh conflict. Bản chất là merge race, không phải code sai.

Điểm mình muốn nhấn: một PR chuyển sang trạng thái conflict sau khi đã QA xanh gần như luôn là dấu hiệu nền cũ, chứ không phải bug mới. Cách nghĩ này giúp bớt hoảng và đi thẳng vào việc phân loại. Bài kiểm tra trạng thái PR sau khi merge mình từng viết cũng xoay quanh thói quen này: đọc trạng thái trước, sửa sau.

Bước 1: Phân loại conflict trước khi đụng tay

Trước khi resolve bất cứ gì, mình luôn hỏi: file nào conflict, và file đó từ đâu ra? Kinh nghiệm cho thấy conflict rơi vào ba nhóm rất khác nhau:

  • File tự sinh (generated): các file dữ liệu JSON do script/CI build lại (index bài viết, references, scores, report…). Đặc điểm: diff chỉ khác timestamp hoặc vài entry, và nội dung thật của PR không hề đụng.
  • File nội dung (content): bài viết markdown do người viết. Đây mới là "tài sản" thật, cần giữ.
  • File template / mã nguồn: HTML template, CSS/SCSS, script. Nhóm này nguy hiểm nhất vì lấy mù một bên dễ làm vỡ build.

Trong phiên vừa rồi của mình, cả bốn PR conflict thì hầu hết chỉ đụng đúng một file index tự sinh. config, template, style đều tự merge sạch (git ghép được các hunk không chồng lấn). Nghĩa là "4 PR đỏ" trông đáng sợ nhưng thực chất là cùng một loại race nhẹ.

Batch fix merge conflict: chiến lược resolve theo từng nhóm

Sau khi phân loại, mỗi nhóm có một cách xử lý cố định. Mình dán bảng này ngay đầu việc để khỏi phân vân:

Nhóm fileChiến lượcLý do
Data JSON tự sinh (index, references, scores)Lấy bản main rồi để CI build lạiLà artifact, không phải nguồn sự thật; regenerate sẽ khớp state mới
Bài viết markdownGiữ bản PR (trừ khi bài đã có sẵn trên main)Nội dung là tài sản thật của nhánh
Template / SCSS / mã nguồnResolve từng vùng, không lấy mùLấy một bên dễ mất hunk của phía kia hoặc vỡ cú pháp

Hai cái bẫy mình luôn kiểm tra thêm, vì chúng lọt qua cả khi không còn conflict marker:

  1. Link nội bộ trỏ tới bài chưa tồn tại: nếu bản PR thêm liên kết tới một bài mà nhánh khác chưa publish, build sẽ đỏ. Cách phòng nằm ở bài git merge và xử lý conflict — verify đích của mọi link trước khi tin.
  2. Cú pháp template sai sau khi ghép: một dòng template ghép nhầm là đủ kéo sập build của mọi PR khác, đúng như case study một dòng Tera parse lỗi kéo theo 5 PR fail. Bài học: conflict-free không đồng nghĩa build-safe.

Bước 3: Quy trình thực tế mình làm

Vì máy local thường đang mở dở một nhánh khác, mình không muốn checkout qua lại làm bẩn cây làm việc. Cách gọn nhất là dùng git worktree (tài liệu chính thức) — tạo một thư mục làm việc riêng cho từng nhánh, xử lý xong thì xoá.

Các bước mình lặp cho từng PR conflict:

  1. git fetch origin main để chắc chắn đang so với main mới nhất.
  2. Tạo worktree riêng cho nhánh PR (tránh đụng cây làm việc đang mở).
  3. git merge origin/main vào nhánh — để git tự ghép phần ghép được.
  4. Với file tự sinh còn conflict: lấy bản main (git checkout --theirs <file>), git add.
  5. Với template/nội dung nếu có: resolve từng vùng, giữ đúng hai phía.
  6. Commit merge, rồi push lại lên đúng nhánh PR.

Điểm hay của worktree: mình mở song song nhiều nhánh mà không sợ lẫn. Nếu một nhánh đang bị session khác giữ worktree, mình dùng worktree ở chế độ detached tại commit remote rồi push thẳng lên ref của nhánh — không cần chiếm quyền checkout của nhánh đó.

Bước 4: Verify trước khi push (đừng bỏ qua)

Đây là phần mình từng lười và trả giá. Một PR không còn marker vẫn có thể vỡ build. Checklist tối thiểu trước khi push:

  • Quét sạch marker: không còn dòng nào bắt đầu bằng <<<<<<<, >>>>>>> hay =======.
  • Chạy checker link nội bộ — bắt link trỏ tới bài/asset không tồn tại.
  • Chạy QA scoped cho phạm vi đã đụng (không cần build toàn bộ nếu chỉ chạm một phần).
  • Để CI lo phần build đầy đủ — mình không build nặng ở local, coi CI là nguồn xác nhận chính thức.

Về việc "vì sao không build local", mình theo hướng giảm tải máy và tin CI, giống tinh thần bài checklist theo dõi deploy sau khi merge tới production: push xong thì theo dõi trạng thái, không ngồi build tay từng lần.

Bài học rút ra

Sau vài phiên "merge cả loạt", mình gói lại mấy điều tự nhắc:

  • Đừng sợ màu đỏ. Bốn PR conflict phần lớn là cùng một race ở file tự sinh — phân loại xong là thấy nhẹ.
  • Đừng tin nền cũ. PR chuyển conflict sau khi QA xanh gần như luôn là stale base, không phải bug.
  • Đừng lấy mù một bên cho template và nội dung — chỉ làm vậy với artifact tự sinh.
  • Đừng bỏ verify. Marker sạch chưa chắc build sạch; luôn quét link nội bộ và cú pháp template.

Nói ngắn gọn: batch fix merge conflict không phải trò đọc từng dòng, mà là trò phân loại rồi áp chiến lược. Khi bạn nhìn conflict qua lăng kính "file này từ đâu ra", tốc độ xử lý cả loạt PR tăng lên rõ rệt mà rủi ro vẫn thấp.

Đọc tiếp

Nếu bạn đang xây một blog/tool nhiều nhánh song song và hay gặp cảnh này, mình gợi ý đọc thêm hai hướng: tự động hoá bớt phần lặp trong giải quyết merge conflict tự động CI/CD, và giữ thói quen kiểm trạng thái ở kiểm tra trạng thái PR sau khi merge. Bạn cũng có thể xem thêm các bài cùng chuyên mục tại chuyên mục Công nghệ hoặc lướt tất cả bài viết để tìm chủ đề Git/CI/CD khác.

Liên kết bên ngoài được sử dụng trong bài viết

Bài viết hiện chưa có nguồn ngoài được khai báo riêng.

Liên kết nội bộ liên quan

Bản quyền & Ghi nguồn

Bài viết được biên tập và tổng hợp bởi Duy Nguyen/SEOMONEY. Nội dung chỉ mang tính tham khảo, không thay thế nguồn chính thức hoặc tư vấn chuyên môn.

FAQ - Câu hỏi thường gặp

Batch fix merge conflict là gì?
Là việc xử lý một loạt Pull Request cùng bị conflict với nhánh main trong một lượt, thay vì sửa lẻ từng PR. Điểm mấu chốt là phân loại conflict theo nguồn gốc file rồi áp đúng chiến lược, thay vì đọc từng dòng.
Vì sao PR đã pass CI vẫn báo conflict khi merge?
Vì CI chỉ chứng minh nhánh nhất quán với chính nó tại thời điểm chạy, không chứng minh nó merge sạch với main hiện tại. Trong lúc PR chờ, main đã đổi (thường là file tự sinh) nên phát sinh merge race — không phải lỗi logic.
Có nên lấy toàn bộ một bên khi resolve conflict không?
Chỉ nên với file tự sinh (data JSON tự build lại). Với file nội dung và template thì không nên lấy mù một bên: nội dung giữ bản PR, template phải resolve từng vùng để không mất hunk của cả hai phía.
Làm sao verify sau khi resolve mà không cần build local?
Chạy kiểm tra nhẹ: quét sạch conflict marker, chạy checker link nội bộ và QA scoped, rồi để CI lo phần build đầy đủ. Push khi cây làm việc sạch và không còn dấu <<<<<<< hay >>>>>>>.

Bình luận

Đang tải bình luận…

    Đăng nhập để tham gia thảo luận.

    Đăng nhập bằng Google để bình luận

    Chỉ dùng để bình luận. Không truy cập trình soạn thảo/CMS.