서브트리
서브트리는 다른 저장소를 내 저장소의 디렉터리 안으로 복사해 와요. 그다음부터 그
파일들은 실제로 거기 있어요. 평범한 git clone이 그것들을 가져오고,
git checkout은 다른 파일과 똑같이 그것들을 옮기며, 그 디렉터리가 다른 데서 왔다는
걸 아무도 몰라도 돼요.
바로 그 점이 서브모듈과의 차이 전부예요. 서브모듈은 포인터만
저장하고, --recurse-submodules와 자기만의 체크아웃, 그리고 헷갈리지 않게 관리해야
할 자기만의 detached HEAD를 요구하죠.
⌘K → 서브트리.

아무도 말해 주지 않는 함정
git은 서브트리에 대한 목록을 아무것도 기록하지 않아요. 서브모듈에는 모든 URL과
경로가 적힌 .gitmodules가 있어요. 서브트리에는 아무것도 없고, 가져오기를 수행한
커밋에 붙은 git-subtree-dir: 트레일러 하나뿐이에요.
그래서 어떤 저장소가 서브트리를 품고 있으면서도 그게 어디서 왔는지 알아낼 방법을 전혀 주지 않을 수 있어요. Gitcito는 할 수 있는 만큼은 해요.
- 목록은 그 트레일러들을 읽어 히스토리에서 찾아내요. 누가 어떤 도구로 추가한 서브트리든 다 나타나요.
- 출처 저장소와 ref는 Gitcito가 이 저장소의 git 설정에 기억해 둬요. 히스토리에서 발견된 서브트리는 그 항목들이 비어 있는 상태로 시작하는데, 한 번 채워 넣으면 그때부터 풀과 푸시가 동작해요.
기억된 값은 .git/config의 gitcito.subtree.* 아래에 살아서, 저장소에는 남지만
클론으로 따라가지는 않아요. 잊기는 그 값을 지우고 다른 건 건드리지 않아요.
추가하기
| 항목 | 의미 |
|---|---|
| 디렉터리 | 어디에 놓일지, 예를 들어 vendor/parser. 아직 존재하지 않아야 해요 |
| 출처 저장소 | URL 또는 디스크상의 경로 |
| 브랜치 또는 태그 | 무엇을 가져올지 |
| 스쿼시 | 히스토리 전체 대신 커밋 하나로 들여오기 |
이유가 없다면 스쿼시는 켠 채로 두세요. 끄면 그 라이브러리의 커밋 하나하나가 내
로그에 영원히 섞여 들어가고, git log는 더 이상 내 프로젝트에 관한 것이 아니게 돼요.
함께 살아가기
| 동작 | 무엇을 실행하는지 |
|---|---|
| 풀 | git subtree pull — 업스트림 변경이 내 디렉터리로 머지되어 들어와요 |
| 푸시 | git subtree push — 그 디렉터리 아래의 내 로컬 변경이 출처로 돌아가요 |
| 분리 | git subtree split -b <branch> — 그 디렉터리만의 히스토리를 브랜치로 뽑아내요. 파일은 루트에 놓인 채로요 |
분리는 알아 둘 값어치가 있어요. 벤더링된 디렉터리를 다시 독립 저장소의 히스토리로 되돌리는 일이고, 서브트리가 서브트리이길 그만두는 방법이 바로 이거예요.
알아 둘 만한 한계
- 푸시는 느려요. 매번 그 디렉터리의 히스토리를 처음부터 다시 계산하거든요. 큰 저장소에서는 즉시가 아니라 수 초에서 수 분이 걸리고, Gitcito는 기다리는 것밖에 할 수 없어요.
- 풀은 머지예요. 그래서 어떤 머지와도 마찬가지로 충돌할 수 있고, 그러면 충돌 해결기에 도착하게 돼요.
git subtree는 git 내장 명령이 아니라 contrib 스크립트예요. 최소한으로 구성된 git 설치본에는 없을 수 있어요. Gitcito는 "'subtree' is not a git command"를 그대로 전달하는 대신 그 사실을 분명히 말해 줘요.- 스쿼시된 히스토리는 나중에 되돌릴 수 없어요. 그 커밋들은 애초에 가져와지지 않았거든요.
- Gitcito는 서브모듈을 서브트리로, 또는 그 반대로 변환하지 않아요.
함께 보기: 머지와 리베이스 · UI로 하는 배관 작업