스택 브랜치
스택은 각 브랜치가 아래 브랜치 위에 쌓이는 사슬이에요. main → api → ui 처럼요.
작은 PR 세 개를 리뷰하는 편이 거대한 PR 하나를 리뷰하는 것보다 나아요.

Gitcito는 이것을 경로로 그립니다. 맨 위에 시작 브랜치, 그 아래로 레벨마다 정류장이 하나씩. 각 정류장의 PR은 바로 위 정류장을 대상으로 하고, 첫 정류장은 시작 브랜치에 착지합니다. 정류장에는 자기 커밋 수, restack이 필요한지, 그리고 제출한 뒤에는 PR 번호가 표시됩니다.
경로 편집하기
적용을 누르기 전까지는 아무것도 실행되지 않습니다. 브랜치를 고르고, 정류장을 옮기고, 경로에서 빼는 일은 모두 화면 위의 목록을 고치는 것뿐입니다. 실제 작업은 브랜치를 rebase하고 체크아웃하는 일이라, 한번 눌러보는 클릭이 할 일이 아닙니다. 경로가 제대로 읽히면 경로 적용이 되돌릴 수 있는 한 단계로 실행하고, 버리기는 저장소가 실제로 말하는 상태로 그림을 되돌립니다.
경로는 병합 순서대로 그려집니다. 위 브랜치가 아래 브랜치에 병합되고, 맨 아래는 스택이 착지하는 브랜치입니다.
| 컨트롤 | 하는 일 |
|---|---|
| 시작 필드 | 스택이 착지하는 곳입니다. 바꾸면 사슬 전체가 새 브랜치에 다시 연결되고 재생됩니다. |
| 정류장 필드 | 그 자리를 차지하는 브랜치를 바꿉니다. 경로에서 빠진 브랜치는 추적만 해제되고 삭제되지 않습니다. |
| ↑ / ↓ | 정류장을 한 칸 옮깁니다. |
| ✕ | 정류장을 경로에서 뺍니다. 앞뒤 정류장이 이어집니다. |
| 정류장 추가 | 이미 있는 브랜치를 고르면 경로 맨 위에 합류하고, 아직 없는 이름을 입력하면 마지막 정류장의 끝에서 만들어져 체크아웃됩니다. |
| 화살표 버튼 | 그 정류장을 체크아웃합니다. |
모든 필드는 입력하며 걸러내기를 지원합니다. 입력하면 걸러지고, ↑/↓와 Enter로 고르며,
목록에 없는 것을 입력해도 그대로 쓰입니다 — 그래서 origin/main 같은 원격 추적 참조도
시작 브랜치가 됩니다.
속을 보면 이 편집들은 모두 같은 작업입니다. 경로 전체를 한 번에 넘기는 것이죠. 그래서 한 동작이 되돌리기 하나(⌘Z)이지, 절반만 적용된 링크의 흔적이 아닙니다.
경로 편집의 대가
순서를 바꾸는 것은 무엇이든 — 교환, 이동, 다른 시작점 — 사슬을 다시 재생합니다. 각 정류장의 커밋이 새 베이스 위로 rebase되기 때문입니다. 그러니 restack과 똑같이 충돌할 수 있습니다. 같은 줄을 건드리는 두 정류장은 사람 없이는 자리를 바꿀 수 없습니다. 그럴 때는 아무 일도 일어나지 않습니다. 편집 전체가 되돌려지고(끝점도, 부모 링크도, 하다 만 rebase도), 부딪힌 두 정류장의 이름을 알려줍니다. 살짝 건드린 드롭다운 때문에 rebase 한복판에 남겨져서는 안 되니까요.
Restack은 그 거래의 나머지 절반입니다. 이름을 대고 요청한 rebase이므로 충돌에서 멈추고 충돌 화면을 넘겨줍니다 — 그것이 Gitcito가 거절한 순서 바꾸기를 이루는 길이기도 합니다. 거기서 해결한 뒤 정류장을 옮기세요.
되돌리기는 이전 경로를 다시 재생합니다. 옛 커밋을 되살리지는 않습니다 — 새 커밋은 부모만 다른 같은 작업이니까요.
전체 푸시
전체 푸시는 각 레벨을 --force-with-lease로 푸시하고 거기서 멈춥니다 — 아무것도 열지
않는 gh stack push입니다. 아래의 스택을 PR로 제출은 같은 푸시에 PR 작업까지 합니다.
브랜치는 원격에 올리되 아직 리뷰는 원하지 않을 때 전체 푸시를 쓰세요.
스택을 연쇄 PR로 제출하기
제출은 먼저 묻습니다. 풀 리퀘스트를 몇 개 열고 몇 개의 대상을 바꾸는지, 어느 원격에서인지,
그리고 각각의 브랜치 → 베이스 줄을. PR을 여는 일은 공개적이고 되돌리기 번거롭기 때문입니다.각 본문에 쓰이는 내비게이션 절이야말로
스택 개념이 없는 GitHub에서 사슬을 보이게 하는 것입니다.
제출은 전용 화면에서 이루어집니다. 먼저 계획, 그다음 단계별 진행 상황, 마지막에 각 풀 리퀘스트와 스택 자체의 링크입니다.

스택을 PR로 제출은 스택 도구들이 돈을 받고 파는 일을 클릭 한 번으로 해내요.
- 모든 단계를
--force-with-lease로 푸시해요(새 브랜치는 그냥 받아들이고, 재적재된 브랜치에는 이게 꼭 필요해요). - PR이 없는 모든 단계에 PR을 열어요 — 각각
main이 아니라 부모 브랜치를 기반으로 하기 때문에, 모든 리뷰에는 자기 커밋만 보여요. 제목과 설명은 그 단계 자신의 커밋에서 만들어져요. - 기반이 어긋난 기존 PR의 대상을 바로잡아요.
- 모든 PR 본문에 스택 내비게이션 섹션을 적어 넣어서, 어느 단계의 리뷰어든 사슬 전체와 이 PR이 그 안 어디에 있는지 볼 수 있어요.
이 동작은 멱등이에요. 재적재하거나 단계를 새로 얹거나 PR이 병합될 때마다 눌러도 결과는 수렴해요 — 아무것도 중복되지 않고, 어긋난 것만 손대요.
맨 아래 PR이 병합되면 같은 버튼이 뒷정리까지 해 줘요. 병합된 단계의 자식은 트렁크 위로 부모가 바뀌고, 그 단계는 추적이 해제되고, 로컬 브랜치는 삭제돼요 (안전해요 — 트렁크가 그걸 담고 있다는 게 증명되니까요). 그리고 사슬이 재적재되고 남은 PR의 대상이 모두 바로잡혀요. 아래에서 위로 병합하고, 제출을 누르고, 반복하세요.
GitHub에서는 진짜 스택이 되기도 합니다
베이스를 이어 붙이는 방식은 어느 호스트에서나 통하고, GitLab·Bitbucket·Azure DevOps에서는 그것이 전부입니다. GitHub에는 그 이상이 있습니다. stacked pull requests 프리뷰 이후로 스택은 서버 쪽 객체입니다. 풀 리퀘스트가 만들어지면 Gitcito가 아래에서 위로 그것들을 스택으로 등록하고, PR 화면의 스택 지도, 서버에서 도는 연쇄 rebase, 맨 위 PR을 머지하면 아래의 병합되지 않은 레벨까지 함께 착지하는 동작을 얻습니다.
저장소가 그 프리뷰에 없거나 토큰이 스택을 다룰 수 없으면 이 호출은 조용히 건너뜁니다. 체인과 내비게이션 절만으로도 충분하며, 다른 호스트에서와 똑같이 동작합니다.
재적재
아래쪽 브랜치가 바뀌면 — 예를 들어 api 의 리뷰 의견을 반영했다면 — 그 위의 모든
브랜치는 이제 잘못된 기반 위에 놓여 있어요. 재적재는 rebase --onto 로 사슬
전체를 연쇄적으로 리베이스해서, 부모를 다시 쓴 결과가 자식들에 커밋을 중복시키지
않도록 해요. 재적재한 뒤에는 제출을 다시 누르세요. 다시 쓴 단계들이 강제
푸시되고 PR들이 제자리에서 갱신돼요.
한계
- 제출은 아직 GitHub 전용이에요(생성은 네 호스트 모두에서 되지만, 대상 변경과 본문 갱신에는 GitHub API가 필요해요).
- 맨 아래 병합 뒤처리는 merge와 rebase 병합은 조상 관계로 알아보고, 스쿼시(squash) 병합은 그 브랜치의 PR이 병합됐는지 GitHub에 물어봐서 알아봐요 — 그래서 GitHub 토큰만 있으면 어떤 병합 방식이든 정리돼요. 다른 호스트에서, 또는 토큰이 없으면 스쿼시로 병합된 단계는 여전히 직접 추적을 해제해야 해요. 그리고 먼저 페치하세요 — 조상 관계 확인은 마지막 페치 시점의 트렁크를 읽어요.
- PR 본문의 스택 섹션은 숨은 표식 사이에서 관리돼요 — 그 위에 적은 여러분의 설명은 그대로 보존돼요.
- 순서 바꾸기와 트렁크 교체는 건드리는 모든 레벨에서 히스토리를 다시 씁니다. 브랜치는 여러분 것이고 아직 푸시하지 않은 레벨은 대가가 없지만, 이미 리뷰 중인 레벨은 다음 제출에서 강제 푸시를 받습니다.
- 레벨은 한 번에 한 칸씩만 움직입니다. 두 번의 교환은 두 번의 rebase이고, 중간에 멈춘 상태도 읽을 수 있습니다. 세 칸 건너에 떨어지는 드래그는 그렇지 않습니다.
- 정류장은 rebase됩니다. 그래서 스택이 착지하는 브랜치가 동시에 정류장이 되는 일은
없고, 보호된 브랜치도 마찬가지입니다(목록을 바꾸지 않았다면
main과master). 둘 다 공유된 히스토리를 조용히 다시 쓰는 대신 거부됩니다. - 제출은 무언가를 열기 전에 원격에 어떤 브랜치가 실제로 도착했는지 묻고, 도착하지 않은 것을 이름으로 알려줍니다. head가 없을 때 GitHub이 주는 답은 맨숭맨숭한 "Validation Failed"뿐이고, 그건 누구에게도 도움이 안 됩니다. 스택이 착지하는 브랜치도 확인합니다. 로컬에만 있으면 제출이 푸시하고 계속할지 물어봅니다.
연결 정보가 있는 곳
부모 연결은 git config 에 저장돼요. 그래서 저장소와 함께 이동하고 다시 클론해도 살아남아요. 어떤 서비스에도 아무것도 남지 않아요.
함께 보기: 대화형 리베이스 · 호스팅과 풀 리퀘스트