9 điểm bởi GN⁺ 8 ngày trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Tính năng mới của GitHub cho phép chia các thay đổi mã nguồn lớn thành những PR nhỏ, dễ review để quản lý theo trình tự
  • Mỗi PR được review độc lập, và toàn bộ stack có thể được merge chỉ với một cú nhấp chuột
  • Hỗ trợ tạo stack, điều hướng, rebase, merge qua GitHub UI và CLI gh stack, đồng thời trực quan hóa cấu trúc phân cấp bằng stack map
  • Thông qua tích hợp AI coding agent, có thể tự động chia diff lớn thành các đơn vị stack hoặc phát triển theo quy trình dựa trên stack
  • Mục tiêu là giảm độ phức tạp và rủi ro xung đột của các PR lớn, đồng thời tăng hiệu quả review và tốc độ phát triển của nhóm

Tính năng chính

  • Quản lý PR dạng stack

    • Tổ chức nhiều PR thành stack có thứ tự, trong đó mỗi PR dựa trên nhánh của PR ngay bên dưới
    • Hình thành cấu trúc chuỗi cuối cùng dẫn tới nhánh main
    • GitHub nhận diện toàn bộ stack và hiển thị stack map trong UI, giúp reviewer dễ dàng điều hướng qua từng tầng
    • Quy tắc bảo vệ nhánh được áp dụng cho nhánh đích cuối cùng, và kiểm thử CI được chạy cho mọi PR trong stack
  • Quản lý stack được đơn giản hóa

    • Trong GitHub UI, có thể di chuyển giữa các PR trong stack, kiểm tra trạng thái của từng tầng và chạy cascading rebase cho toàn bộ stack
    • Có thể merge toàn bộ stack hoặc chỉ merge một phần chỉ với một cú nhấp chuột
    • Sau khi merge, các PR còn lại sẽ tự động được rebase, trong đó PR chưa merge thấp nhất sẽ được điều chỉnh để nhắm tới nhánh cơ sở
  • Hỗ trợ CLI mạnh mẽ

    • Qua CLI gh stack, có thể thực hiện tạo stack, rebase, push nhánh, tạo PR, di chuyển giữa các tầng ngay trong terminal
    • Ví dụ lệnh CLI
      • gh extension install github/gh-stack : cài đặt extension
      • gh stack alias : thiết lập lệnh tắt
      • gs init <branch> : tạo nhánh đầu tiên
      • gs add <branch> : thêm tầng mới
      • gs push : push tất cả nhánh
      • gs submit : tạo PR cho toàn bộ stack
  • Tích hợp AI Agent

    • Với lệnh npx skills add github/gh-stack, có thể giúp AI coding agent học cách thực hiện tác vụ liên quan đến stack
    • Có thể tự động tách diff lớn thành các đơn vị stack, hoặc phát triển theo phương thức dựa trên stack ngay từ đầu

Vì sao cần PR dạng stack

  • PR lớn gây ra độ khó review cao hơn, chậm merge và tăng rủi ro xung đột
    • Reviewer dễ mất ngữ cảnh, chất lượng phản hồi giảm và tốc độ chung của cả nhóm bị chậm lại
  • Stacked PRs giải quyết điều này bằng cách chia thành chuỗi PR nhỏ, tập trung
    • Mỗi PR có thể được review độc lập, trong khi toàn bộ thay đổi được tích lũy tuần tự

Bắt đầu

1 bình luận

 
Ý kiến trên Hacker News
  • Thứ tôi cần không phải là "stacked PR" mà là UI để quản lý từng commit riêng lẻ

    • Tôi muốn chỉ merge một vài commit độc lập, hoặc đánh dấu một commit cụ thể là đã review xong
    • Tôi muốn làm interactive rebase/squash/edit ở cấp độ commit, nhưng UI của GitHub không làm được
    • Cần có khả năng bình luận vào message commit hay vào một commit cụ thể, và tính năng trực quan hóa thay đổi giữa các lần force push bằng diff of diff
      Git vốn đã có khái niệm commit rồi, nên tôi không hiểu vì sao lại phải chồng thêm một tầng trừu tượng gọi là “stacked PR” lên trên
    • Đây là khái niệm mang quy trình stacked diff do Phabricator tạo ra sang GitHub
      Nó giúp dễ tiếp tục làm việc mới trên phần việc còn chưa được merge, và cho phép reviewer review độc lập theo từng phần nhỏ đối với thay đổi lớn
      Điều này đặc biệt hữu ích trong monorepo quy mô lớn hoặc môi trường doanh nghiệp
    • Nhưng tôi cảm thấy việc liên tục viết lại lịch sử git là nguy hiểm
      Lặp đi lặp lại squash, rebase, force push giống như tự chĩa súng vào chân mình
      Chỉ với git merge --no-ff, git log --first-parent, git bisect --first-parent cũng đủ đạt được hiệu quả tương tự
    • SuperSmartLog(SSL) từng dùng ở Meta là triển khai xuất sắc nhất
      Nó được công khai trong tài liệu interactive smartlog và cũng có extension cho VSCode
      Hơi tiếc là nó lại không được dùng rộng rãi hơn
    • Workflow tôi thích là xem PR/MR như “thay đổi mang tính nguyên tử”
      Commit được dùng như quá trình tiến hóa để hoàn thiện PR đó
      1. Nếu PR quá lớn thì chia ra bằng commit
      2. Ghi lại quá trình phát triển của PR trong các commit (bao gồm cả lý do như “đổi foo thành bar”)
    • Điều bạn nói thực chất chính là Gerrit
  • Sau khi dùng Phabricator và Mercurial rồi quay lại GitHub, tôi có cảm giác như trở về thời kỳ đồ đá
    Thật vui khi thấy jujutsu hay tính năng lần này tái hiện lại luồng stacked diff
    Nó không chỉ hữu ích với monorepo mà còn giúp review nhỏ và nhanh trong quá trình phát triển tính năng dài hạn

    • Thật may là Git đã thắng trong cuộc chiến DVCS
      Mercurial lúc nào cũng nói “nhanh hơn git”, nhưng thực tế thì chậm hoặc hay lỗi
      Git xấu xí nhưng nhanh và đáng tin cậy
    • Tôi vẫn ghét review trên GitHub nên dùng Gerrit
      Khi review thay đổi lớn (ví dụ: cập nhật dependency vendor), trải nghiệm review file của GitHub khá tệ
    • Tôi rất nhớ UI review của Phabricator
  • Cuối cùng cũng ra rồi!
    Mô hình “PR=branch” của GitHub trước giờ thật khó hiểu
    stacked commit kiểu Phabricator/Gerrit hợp với cách tôi suy nghĩ hơn
    Giờ chắc phải cài CLI rồi

    • Hơi tiếc vì phải phụ thuộc vào GH CLI, nhưng hy vọng khi GA sẽ có hỗ trợ trên UI
    • Theo mô hình trong đầu tôi thì tôi không thấy rõ khác biệt giữa branch và stacked PR
      Branch chỉ là cắm một lá cờ vào commit, và việc giữ lại nhiều điểm chỉ có ý nghĩa khi phần trước đó có thể được merge độc lập
  • Tôi tò mò không biết họ đã giải quyết vấn đề UX của Squash & Merge hiện tại chưa
    Tôi đang xếp thủ công theo kiểu main <- PR A <- PR B <- PR C,
    và khi PR A được merge trước thì PR B trở thành địa ngục xung đột
    UI GitHub tự động đổi target branch về main rồi gây ra xung đột kỳ quặc
    Tôi chỉ cần một công cụ “cứ thế mà chạy ổn”

    • Xung đột có lẽ là do PR A đã được squash merge
      Hy vọng gh stack sync sẽ xử lý bằng rebase --onto
    • Trên thực tế CLI và server xử lý bằng git rebase --onto
      Ví dụ: PR1(main<-A,B), PR2(main<-A,B,C,D), PR3(main<-A,B,C,D,E,F)
      Nếu PR1,2 được squash merge thì main sẽ là S1(A+B), S2(C+D)
      Sau đó git rebase --onto S2 D branch3 sẽ sắp xếp lại mà không bị xung đột
    • Trong workflow của tôi, nếu A được merge thì B cũng nên đã ở trạng thái được merge rồi
      Có thể giải quyết bằng git rebase --update-refs --onto origin/main A C
      GH CLI sẽ tự động hóa quy trình này
    • Đây là giới hạn logic của git chứ không phải bug
    • Tôi cũng đồng ý chuyện này rất khó chịu và không trực quan
      Nhưng rốt cuộc giải pháp vẫn chỉ là rebase thủ công để sắp xếp lại các commit
  • Khi phát triển một mình thì stacked PR gần như không cần thiết, nhưng thói quen chia nhỏ theo đơn vị nhỏ vẫn rất quan trọng
    Vì các công cụ AI (ví dụ: Claude Code) thường tạo ra diff lớn trong một lần,
    nên sẽ rất thú vị nếu agent có thể tự chia công việc thành các đơn vị logic

    • PR vốn đã có thể gồm nhiều commit, nên nếu agent không duyệt được tốt phần đó thì stacked PR cũng vậy thôi
    • Tôi dùng Claude với jj cùng nhau để cho nó tái cấu trúc stack công việc sao cho thân thiện với review trên trunk
    • Nếu tạo ra các branch nhỏ phụ thuộc lẫn nhau, địa ngục đồng bộ hóa sẽ xuất hiện khi branch trước đó được cập nhật
    • Có thể xem hướng dẫn tích hợp agent liên quan trên trang web
  • git-spice cũng đáng để thử
    Nó tương thích với GitLab v.v., và tôi dùng hoàn toàn git-spice thay cho các lệnh git hiện có

  • Việc GitHub cuối cùng cũng đưa tính năng stack vào UI là rất tuyệt
    Nó tương tự glab stack của GitLab
    Tuy vậy quá trình merge có vẻ sẽ hơi gượng — nếu merge phần phía dưới của stack thì phần còn lại sẽ bị rebase và CI sẽ chạy lại
    Khi muốn chỉ merge hai patch phía dưới trong ba patch, bạn sẽ phải chờ test cho từng cái

    • Theo thiết kế hiện tại, nếu CI của hai PR phía dưới đều pass thì có thể merge một lần
      Sau đó stack phía trên sẽ được rebase và CI có thể chạy lại
      Chúng tôi dự định sẽ bổ sung phần này rõ hơn trong tài liệu
  • Tôi không thực sự hiểu sự cần thiết của stacked PR
    Trong git vốn đã có thể review và áp dụng từng bộ patch riêng lẻ
    Mô hình PR ngược lại còn gom tất cả thành một khối
    Stacked PR trông như một tầng trừu tượng khác để lách qua vấn đề đó

    • Đội tôi từng làm xem PR như “đơn vị tối thiểu có thể áp dụng được”
      Các commit bên trong chỉ là lịch sử phát triển, và khi merge thì squash chúng thành một
      Làm vậy trong lúc phát triển thì có thể commit thoải mái, còn khi merge thì chỉ giữ lại thay đổi sạch sẽ
    • Trong Phabricator, commit và diff là quan hệ 1:1 nên tự nhiên hơn nhiều
      Cách GitHub triển khai tạo cảm giác hơi chắp vá
    • PR vốn là đơn vị thay đổi mang tính nguyên tử, còn stacked PR cho phép làm việc dựa trên PR mới ở phía trên nó
      Nói cách khác, đó là cấu trúc xếp chồng công việc theo từng bước thành các đơn vị có thể review được
  • Đã có một startup tên Graphite tập trung vào stacked PR từ trước
    Tôi đã dùng Graphite, nên rất vui khi GitHub cũng triển khai thứ tương tự

    • Công ty tôi cũng dùng Graphite và khá hài lòng
  • Tôi thích stacked PR, nhưng cách GitHub triển khai lần này lại cho cảm giác khá kỳ lạ
    Chỉ cần để mỗi branch trỏ tới branch cha của nó là đủ
    Thứ cần hơn CLI là hỗ trợ UI

    • Nhưng sau khi branch cha được merge thì việc rebase rất đau đầu
      Sẽ tốt nếu CLI tự động hóa quy trình này
    • CLI là tùy chọn, và stacked PR cũng có thể được tạo trên UI
      Lý do đặt chuỗi branch là để diff chỉ hiển thị các thay đổi của branch đó
    • Thực ra chuyện này git gốc đã làm được từ lâu
      Chỉ là thiếu UI và trực quan hóa mà thôi
    • Hy vọng tiếp theo sẽ có cải tiến về UI