worktree 여러 개를 한 줄로 병합하기

요약. worktree 를 세 개 넘게 벌려 놓으면 다음 문제가 옵니다. 이걸 어떻게 다시 합치느냐입니다. 저는 폴더마다 작업자를 한 명씩 앉히고 마지막에 스크립트 한 줄로 전부 일렬 병합합니다. 관건은 병합 방식이 아니라 실패했을 때 원자적으로 되돌아오는지입니다.

도토리 블루투스 넘패드 - 폰을 PC 의 17키 숫자패드로

제가 만든 무료 앱입니다. 많이 써 주세요.

폴더 수가 곧 동시 작업 수입니다

1편에서 worktree 는 브랜치 전환 비용을 없애는 도구였습니다. 폴더마다 독립된 빌드 트리라는 성질에는 다른 쓸모가 하나 더 있습니다. 여러 폴더를 동시에 돌려도 서로 간섭하지 않는다는 점입니다. 같은 저장소를 두 프로세스가 빌드하면 산출물 경로가 겹쳐 깨지는데, worktree 는 그 경로 자체가 다릅니다.

그래서 폴더를 작업 슬롯으로 씁니다. docs 폴더에 문서 작업자, feature-login 폴더에 기능 작업자를 두고 각자 자기 폴더에서 빌드와 테스트를 돌립니다. 사람은 master 폴더에서 리뷰만 합니다. AI 에이전트를 여러 개 병렬로 돌릴 때 특히 잘 맞습니다. 에이전트끼리 파일 락을 두고 싸울 일이 없고, 한쪽이 빌드를 깨도 다른 슬롯은 멀쩡합니다.

병렬로 벌린 것은 merge 커밋 대신 한 줄로 모읍니다

브랜치 다섯 개를 각각 master 로 merge 하면 그래프가 다이아몬드 다발이 됩니다. 나중에 히스토리를 읽으면 어떤 변경이 어떤 변경 위에서 검증됐는지 알 수 없습니다. 그래서 반대로 갑니다. 계보 순서대로 앞 브랜치의 새 tip 위에 rebase --onto 를 이어 붙이고, 다 끝나면 모든 브랜치를 그 최종 tip 으로 fast-forward 정렬합니다.

제 --gw1 플래그가 이 일을 합니다. 실행 전 계획은 이렇게 나옵니다.

merge plan — linearize 3 worktrees onto 'master', then ff-all:
  master               pull --ff-only: already up to date
  master               (base, tip ce5b697f)
  docs                 ff-advance chain to be09cdb1
  feature/login        rebase --onto <docs> ce5b697f
  then fast-forward every branch (incl. master) to the final tip
  then push master → origin/master (fast-forward, never forced)

실행 결과는 merge 커밋이 없는 직선 하나입니다. 모든 worktree 가 같은 커밋을 가리키므로, 어느 폴더에서 빌드해도 결과가 같습니다.

* 8494e3f add login page
* be09cdb write docs
* ce5b697 init

자동화의 값은 원자성에서 나옵니다

rebase 체인은 중간에서 충돌하면 절반만 재작성된 상태로 멈춥니다. 이 상태가 자동화의 최대 위험입니다. 그래서 실패하면 모든 브랜치를 진입 시점의 sha 로 되돌립니다. 충돌하는 브랜치 두 개를 만들어 실제로 돌려 보면 다음 한 줄과 exit 1 만 남고, 세 브랜치의 sha 가 실행 전과 동일했습니다.

git-worktree: rebase conflict on 'feature/login' (onto previous tip) — rolled back
to entry state (except the origin ff of 'master'). resolve 'feature/login' manually,
then retry.

되돌아온다는 보장이 있으니 dry-run 으로 결과를 추측하지 않고 그냥 실행해서 확인합니다. 3-way 충돌은 실제로 rebase 를 해봐야만 드러나므로 이쪽이 빠릅니다. 단계별로 실패를 어떻게 다룰지는 미리 정해 두었습니다.

단계실패 시
기본 브랜치 origin 동기화 (맨 처음)아무것도 건드리지 않고 전체 중단
rebase 체인 / ff 정렬모든 브랜치를 진입 상태로 롤백
마지막 origin push롤백하지 않음. 선형화는 그대로 유지

동기화를 맨 처음에 두는 이유는 낡은 base 위에 쌓으면 push 시점에 또 갈라지기 때문입니다. 반대로 마지막 push 실패에서 롤백하면, 네트워크나 권한 문제 때문에 애써 만든 선형 결과를 날립니다. 그래서 여기서는 git 의 거부 사유 원문과 재시도 명령만 남기고 종료 코드로만 실패를 알립니다. 원인을 고쳐 다시 돌리면 push 만 재시도됩니다.

정리

worktree 폴더는 병렬 작업 슬롯이고, 슬롯을 늘린 대가는 마지막 병합에서 치릅니다. 그 대가를 merge 커밋 다발이 아니라 rebase 체인 한 줄로 받으면 히스토리가 읽히고, 모든 폴더가 같은 커밋에서 다시 출발합니다. 자동화로 넘길 수 있는지는 속도가 아니라 실패 시 원자성으로 판단합니다.

다음에 브랜치 셋 이상을 동시에 굴리게 되면, 합칠 스크립트를 먼저 짜고 그 스크립트에 롤백을 넣으십시오.