🔥 GitHub이 드디어 Stacked PR을 공식 지원한다

#github#git#stacked-prs#cli#code-review
1370자
15분

content image

솔직히 이건 좀 늦었다.

Stacked PR이라는 개념 자체는 이미 몇 년 전부터 Graphite, ghstack, stack-pr 같은 서드파티 도구들이 메워왔고, 더 거슬러 올라가면 Meta 내부에서 Phabricator와 함께 사용하던 "stacked diffs" 워크플로우가 원조다. 그런데 2026년 4월, GitHub이 드디어 자체 Stacked PRs 기능을 프리뷰로 내놓았다. 거기에 CLI 확장까지. gh-stack.

4일 만에 GitHub 공식 레포에서 v0.0.1이 릴리스됐고, Hacker News에서 458포인트에 258개의 댓글이 달렸다. 환영하는 사람, 의심하는 사람, "Phabricator 시절이 그립다"는 사람까지 반응은 다양했다. 나도 이 레포를 발견하고 README를 처음부터 끝까지 읽었다. 정리해 본다.

Stacked PR이 뭐고, 왜 필요한가

큰 기능을 하나의 PR로 올리면 리뷰어가 고통받는다. 파일 30개, 변경 라인 2,000줄짜리 PR을 본 적 있다면 안다. 제대로 읽지 않고 "LGTM" 누르고 싶은 유혹이 얼마나 강한지.

Stacked PR은 이 문제를 구조적으로 해결한다. 하나의 큰 변경을 여러 개의 작은 PR로 쪼개되, 각 PR이 이전 PR 위에 쌓이는(stacked) 구조를 만든다.

frontend      → PR #3 (base: api-endpoints) ← top
api-endpoints → PR #2 (base: auth-layer)
auth-layer    → PR #1 (base: main)          ← bottom
─────────────
main (trunk)
frontend      → PR #3 (base: api-endpoints) ← top
api-endpoints → PR #2 (base: auth-layer)
auth-layer    → PR #1 (base: main)          ← bottom
─────────────
main (trunk)

리뷰어는 각 레이어의 diff만 보면 된다. 작고 집중된 변경이니까 리뷰 품질이 올라간다. 개발자는 PR #1의 리뷰를 기다리는 동안 PR #2, #3 작업을 계속할 수 있으니 블로킹이 사라진다.

이 워크플로우의 원조는 Meta다. Meta는 내부적으로 Phabricator의 Differential을 사용해서 "stacked diffs"를 운영했고, 이걸 통해 개발자 생산성이 체감될 정도로 올라갔다고 한다. 이 경험에서 영감받은 전직 Meta 엔지니어들이 만든 게 Graphite다.

문제는 Git 자체가 이 워크플로우를 위해 설계되지 않았다는 점이다. 수동으로 하려면 브랜치를 하나씩 만들고, 리베이스하고, PR의 base 브랜치를 일일이 바꿔야 한다. 부모 PR이 머지되면 나머지 스택 전체를 다시 리베이스해야 하고, 같은 충돌을 반복해서 해결해야 할 수도 있다. 자동화 도구 없이는 현실적으로 쓰기 어렵다.

gh-stack이 하는 일

gh-stackGitHub CLI(gh)의 확장으로, Go로 작성됐다. 설치는 한 줄이다.

bash
gh extension install github/gh-stack
bash
gh extension install github/gh-stack

gh v2.0 이상이 필요하다. 다만 현재 프라이빗 프리뷰 단계라서, 레포지토리에 Stacked PRs 기능이 활성화되어 있어야 CLI가 동작한다. 대기 목록 신청은 가능하다.

스택 초기화

bash
# 인터랙티브 모드
gh stack init
 
# 브랜치를 직접 지정
gh stack init feature-auth feature-api feature-ui
 
# 기존 브랜치를 스택으로 편입
gh stack init --adopt feature-auth feature-api
 
# 접두사 + 자동 번호 매기기
gh stack init -p feat --numbered
# → feat/01 생성
bash
# 인터랙티브 모드
gh stack init
 
# 브랜치를 직접 지정
gh stack init feature-auth feature-api feature-ui
 
# 기존 브랜치를 스택으로 편입
gh stack init --adopt feature-auth feature-api
 
# 접두사 + 자동 번호 매기기
gh stack init -p feat --numbered
# → feat/01 생성

--adopt 플래그는 이미 작업 중인 브랜치들을 스택으로 묶을 수 있어서 기존 프로젝트에 도입하기 좋다. --numbered는 브랜치 이름 고민 없이 feat/01, feat/02처럼 순번을 매겨준다.

브랜치 추가와 커밋

bash
# 스택 꼭대기에 새 브랜치 추가
gh stack add api-routes
 
# 스테이징 + 커밋 + 브랜치 생성을 한 번에
gh stack add -Am "Add login endpoint"
bash
# 스택 꼭대기에 새 브랜치 추가
gh stack add api-routes
 
# 스테이징 + 커밋 + 브랜치 생성을 한 번에
gh stack add -Am "Add login endpoint"

-Am 조합이 좋다. git add -A && git commit -m "..." && git checkout -b ...를 한 명령으로 줄여준다. 현재 브랜치에 커밋이 없으면 그 브랜치에 커밋하고, 이미 커밋이 있으면 새 브랜치를 만든다. 이 동작이 직관적이다.

PR 제출

bash
# 모든 브랜치 푸시 + PR 생성 + Stack 연결
gh stack submit
 
# 자동 제목, 드래프트 모드
gh stack submit --auto --draft
bash
# 모든 브랜치 푸시 + PR 생성 + Stack 연결
gh stack submit
 
# 자동 제목, 드래프트 모드
gh stack submit --auto --draft

submit은 브랜치를 원격에 푸시하고, 각 브랜치마다 PR을 만들고, GitHub의 Stack 기능으로 PR들을 연결한다. 이전에 submit한 적이 있으면 새 PR만 스택 위에 추가된다.

동기화가 핵심이다

bash
# fetch → fast-forward trunk → cascade rebase → push → sync PRs
gh stack sync
 
# 수동 리베이스 (충돌 발생 시)
gh stack rebase
gh stack rebase --continue  # 충돌 해결 후
gh stack rebase --abort      # 되돌리기
bash
# fetch → fast-forward trunk → cascade rebase → push → sync PRs
gh stack sync
 
# 수동 리베이스 (충돌 발생 시)
gh stack rebase
gh stack rebase --continue  # 충돌 해결 후
gh stack rebase --abort      # 되돌리기

sync가 이 CLI의 진짜 가치라고 생각한다. fetch, trunk 업데이트, 캐스케이드 리베이스, 푸시, PR 상태 동기화를 한 명령에 처리한다. 충돌이 발생하면 모든 브랜치를 원래 상태로 복원하고 rebase로 수동 해결하라고 안내한다. 안전하다.

푸시할 때 --force-with-lease --atomic을 사용한다. --force-with-lease는 원격 브랜치가 내가 마지막으로 본 상태에서 변하지 않았을 때만 force push를 허용하는 안전장치다. --atomic은 모든 브랜치가 성공하거나 전부 실패하는 all-or-nothing 보장이다. 스택 푸시에서 이 조합은 필수적이다.

네비게이션

bash
gh stack up        # 트렁크에서 멀어지는 방향
gh stack down      # 트렁크 쪽으로
gh stack top       # 스택 꼭대기
gh stack bottom    # 스택 바닥
bash
gh stack up        # 트렁크에서 멀어지는 방향
gh stack down      # 트렁크 쪽으로
gh stack top       # 스택 꼭대기
gh stack bottom    # 스택 바닥

브랜치 이름을 기억할 필요 없이 스택 안에서 이동할 수 있다. 소소하지만 스택이 커지면 이게 은근히 편하다.

처음부터 끝까지 워크플로우

전체 흐름을 한번 보자.

bash
# 1. 스택 시작
gh stack init -p feat --numbered
 
# 2. 첫 번째 레이어 작업
# ... 코드 작성 ...
gh stack add -Am "Auth middleware"
# → feat/01에 커밋 (첫 커밋이니까 새 브랜치 안 만듦)
 
# 3. 두 번째 레이어
# ... 코드 작성 ...
gh stack add -Am "API routes"
# → feat/02 생성, 체크아웃, 커밋
 
# 4. 세 번째 레이어
# ... 코드 작성 ...
gh stack add -Am "Frontend components"
# → feat/03 생성
 
# 5. PR 일괄 제출
gh stack submit --auto
 
# 6. 리뷰어가 첫 번째 PR에 수정 요청
gh stack bottom
# ... 수정 후 커밋 ...
gh stack rebase   # 나머지 스택 리베이스
gh stack push     # 업데이트된 브랜치 푸시
 
# 7. 첫 번째 PR 머지 후
gh stack sync     # 전체 동기화
bash
# 1. 스택 시작
gh stack init -p feat --numbered
 
# 2. 첫 번째 레이어 작업
# ... 코드 작성 ...
gh stack add -Am "Auth middleware"
# → feat/01에 커밋 (첫 커밋이니까 새 브랜치 안 만듦)
 
# 3. 두 번째 레이어
# ... 코드 작성 ...
gh stack add -Am "API routes"
# → feat/02 생성, 체크아웃, 커밋
 
# 4. 세 번째 레이어
# ... 코드 작성 ...
gh stack add -Am "Frontend components"
# → feat/03 생성
 
# 5. PR 일괄 제출
gh stack submit --auto
 
# 6. 리뷰어가 첫 번째 PR에 수정 요청
gh stack bottom
# ... 수정 후 커밋 ...
gh stack rebase   # 나머지 스택 리베이스
gh stack push     # 업데이트된 브랜치 푸시
 
# 7. 첫 번째 PR 머지 후
gh stack sync     # 전체 동기화

git add, git commit, git checkout -b, git push를 따로 칠 필요 없다. gh stack add -Amgh stack submit으로 흐름이 끊기지 않는다. 기존 Git 개념(브랜치, 리베이스, PR)을 그대로 쓰면서 반복 작업만 자동화한다는 점이 마음에 든다. 새로운 추상화 계층을 덮어씌우지 않는다.

Graphite, ghstack과 뭐가 다른가

이미 stacked PR 도구는 여러 개 있다. GitHub이 자체 도구를 만든 이유가 뭘까.

Graphite는 가장 완성도가 높은 서드파티 도구다. CLI(gt), 웹 대시보드, VS Code 확장, 머지 큐까지 갖추고 있다. 전직 Meta 엔지니어들이 만들었고, 무료 플랜이 있지만 팀 기능은 월 $20~40/인이다.

ghstack은 Meta의 Edward Yang이 만든 오픈소스 CLI다. 하나의 브랜치에서 각 커밋을 별도 PR로 변환하는 방식이다. 별도 브랜치를 만들지 않아서 깔끔하지만, GitHub의 PR 모델과 약간 어색하게 맞물린다.

gh-stack이 이들과 다른 점은 이렇다.

GitHub 네이티브 통합. gh-stack은 GitHub의 새로운 Stack 기능과 직접 연동된다. PR 간의 관계가 GitHub UI에서 시각화되고, 각 PR의 base 브랜치가 자동 설정된다. 서드파티 도구는 이 네이티브 Stack 기능을 쓸 수 없다.

브랜치 보호 규칙 인식. 각 PR이 최종 타깃 브랜치(보통 main)를 인식하고, 해당 브랜치의 보호 규칙이 적용된다. 중간 브랜치에 별도 보호 규칙을 설정할 필요가 없다.

무료, 오픈소스. MIT 라이선스다. Graphite의 유료 기능(머지 큐, AI 리뷰)은 없지만, 핵심 스택 관리는 무료다.

git rerere 자동 활성화. gh stack init을 실행하면 git rerere(Reuse Recorded Resolution)를 자동으로 켠다. 한 번 해결한 리베이스 충돌을 Git이 기억해서 다음에 같은 충돌이 나면 자동으로 해결한다. 스택 워크플로우에서는 같은 충돌이 반복되기 쉬우므로 이게 실질적으로 크다.

스쿼시 머지 감지. 부모 PR이 스쿼시 머지되면, 리베이스가 자동으로 --onto 모드로 전환해서 커밋을 올바르게 리플레이한다. 수동으로 이걸 처리해 본 사람은 이게 얼마나 고마운 기능인지 안다.

아직 빠진 것들

v0.0.1이다 보니 빠진 것도 있다.

gh stack merge는 아직 미구현이다. 실행하면 "not yet implemented" 메시지가 나온다. 현재는 GitHub 웹에서 직접 머지해야 한다.

프라이빗 프리뷰 한정이다. 대기 목록에 등록해도 바로 쓸 수 있는 건 아니다. GitHub 로드맵에는 Q2 2026(4~6월)으로 표시되어 있다.

웹 UI 통합이 아직 제한적이다. HN 댓글에서도 지적됐듯이, CLI 중심이고 웹 UI에서의 스택 시각화나 조작은 초기 단계다.

CI/CD 비용 우려도 있다. 리베이스할 때마다 스택의 모든 브랜치에서 CI가 트리거될 수 있다. 브랜치 3개짜리 스택이면 리베이스 한 번에 CI 3번. 대규모 팀에서는 무시하기 어려운 비용이다.

AI 에이전트 연동이라는 힌트

흥미로운 부분이 하나 더 있다. gh-stack은 AI 코딩 에이전트용 스킬 파일을 제공한다.

bash
npx skills add github/gh-stack
bash
npx skills add github/gh-stack

이 명령으로 AI 에이전트(Claude Code, GitHub Copilot 등)에 gh-stack 사용법을 가르칠 수 있다. 에이전트가 대규모 작업을 수행할 때 자동으로 스택을 만들고 PR을 제출하는 시나리오를 염두에 둔 것 같다. GitHub이 AI 에이전트 시대에 맞춰 개발 도구를 설계하고 있다는 신호로 읽힌다.

그래서, 기다려볼 가치가 있나

README만 읽어도 설계가 꽤 합리적이다.

기존 Git 개념을 존중하면서 반복 작업을 자동화하고, --force-with-lease --atomic 같은 안전장치를 기본으로 깔고, git rerere까지 자동 활성화한다. 제대로 아는 사람이 만든 느낌이다.

Graphite를 이미 쓰고 있는 팀이라면 당장 갈아탈 이유는 없다. Graphite의 웹 대시보드, 머지 큐, AI 리뷰는 gh-stack에 없는 기능이다. 하지만 추가 비용 없이 GitHub 네이티브로 스택을 관리하고 싶은 팀, 또는 서드파티 도구 도입을 꺼리는 조직에서는 이게 정답이 될 수 있다.

나는 프리뷰가 열리면 사이드 프로젝트에서 바로 써볼 생각이다. 특히 gh stack init -p feat --numbered로 시작해서 add -Am으로 쌓아가는 워크플로우가 마음에 든다. 브랜치 이름 짓느라 고민하는 시간이 사라진다.

대기 목록은 gh.io/stacksbeta에서 신청할 수 있다.

참고 자료

YouTube 영상

채널 보기
마지막편, 트라이 노드를 50% 이상 줄이는 방법? 압축 트라이 성능 분석 | Trie 자료구조 이야기
BTree 노드의 구조는?
숫자 하나가 AI 모델의 운명을 바꾼다? | 선형대수학
AI를 위한 선형대수학 - 소개 | 선형대수학
AI는 왜 수백 차원의 벡터를 사용할까? 고차원 공간과 행렬 | 선형대수학
Trie 자료구조 완전 정복 - 개념부터 시각화까지 | Trie 자료구조 이야기
숫자 하나가 AI 모델의 운명을 바꾼다? | 선형대수학
트라이(Trie)를 이용한 자동 완성 알고리즘 | Trie 자료구조 이야기