본문 바로가기

책/프로 Git

Git 도구-7 - Subtree Merge

Subtree Merge

서브모듈 시스템이 무엇이고 어디에 쓰는지 배웠다. 그런데 같은 문제를 해결하는 방법이 또 하나 있다. Git은 Merge하느 시점에 무엇을 Merge할지, 어떤 전략을 사용할지 결정해야 한다. Git은 브랜치 두 개를 Merge할 때에는 Recursive 전략을 사용하고 세 개 이상의 브랜치를 Merge할 때에는 Octopus 전략을 이용한다. 이 전략은 자동으로 선택된다. Merge할 브랜치가 두개면 Recursive 전략이 선택된다. Recursive 전략은 Merge하려는 두 커밋과 공통 조상 커밋을 이용하는 3-way merge를 사용하기 때문에 단 두 개의 브랜치에만 적용할 수 있다. Octopus 전략은 브랜치가 여러개라도 Merge할 수 있지만 비교적 충돌이 쉽게 일어난다.

 

다른 전략도 있는데 그중 하나가 Subtree Merge다. 이 Merge는 하위 프로젝트 문제를 해결하는 데에도 사용한다. 위에서 사용했던 Rack 예제를 적용해 보자.

 

Subtree Merge는 마치 하위 프로젝트가 아예 합쳐진 것처럼 보일 정도로 한 프로젝트를 다른 프로젝트의 하위 디렉터리에 연결해 준다. 정말 놀라운 기능이다.

 

Rack 프로젝트를 리모트 저장소로 추가시키고 브랜치를 Checkout한다.

$ git remote add rack_remote git@github.com:schacon/rack.git
$ git fetch rack_remote
warning: no common commits
remote : Counting objects: 3184, done.
remote : Compressing objects: 100% (4165/4165), done.
remote : Total 3184 (delta 1952), reused 2770 (delta 1675)
Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done.
Receiving deltas: 100% (1952/1952), done.
From git@github.com:schacon/rack
 * [new branch]      build     -> rack_remote/build
 * [new branch]      master    -> rack_remote/master
 * [new branch]      rack-0.4  -> rack_remote/rack-0.4
 * [new branch]      rack-0.9  -> rack_remote/rack-0.9
$ git checkout -b rack_branch rack_remote/master
Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master.
Switched to a new branch "rack_branch"

 

Checkout 한 rack_branch 루트 디렉터리와 origin 프로젝트의 master 브랜치의 루트 디렉터리는 다르다. 브랜치를 바꿔가며 어떻게 다른지 확인한다.

$ ls
AUTHORS         KNOWN-ISSUES   Rakefile      contrib         lib
COPYING         README         bin           example         test
$ git checkout master
Switched to branch "master"
$ ls
README

 

여기에서 Rack 프로젝트를 master 브랜치의 하위 디렉터리에 넣으려면 git read-tree 명령어를 사용한다. 여기에서는 워킹 디렉터리와 Staging Area로 어떤 브랜치를 통째로 넣을 수 있다는 것만 알면 된다.

 

master 브랜치로 되돌아 가서 rack_branch를 rack 디렉터리에 넣는다. 

$ git read-tree --prefix=rack/ -u rack_branch

 

그러고 나서 커밋을 하면 rack 디렉터리는 rack 프로젝트의 파일들을 직접 복사해 넣은 것과 똑같다. 복사한 것과 다른 점은 브랜치를 자유롭게 바꿀 수 있고 최신 버전의 Rack 프로젝트의 코드를 쉽게 끌어 올 수 있다는 점이다.

$ git checkout rack_branch
$ git pull

 

그리고 git merge -s subtree 라는 명령어를 사용하여

  • master 브랜치와 Merge 할 수 있고 원하든 원하지 않든 간에 히스토리도 함께 Merge된다.
  • 수정 내용만 Merge하거나 커밋 메시지를 다시 작성하려면 -s subtree 옵션에다가 --squash, --no-commit를 함께 사용해야 한다. 
$ git checkout master
$ git merge --squash -s subtree --no-commit rack_branch
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested

 

Rack 프로젝트의 최신 코드를 가져다가 Merge했고 이제 커밋하면 된다. 물론 반대로 하는 것도 가능하다. rack 디렉터리로 이동해서 코드를 수정하고 rack_branch 브랜치로 Merge한다. 그리고 Rack 프로젝트 저장소에 Push할 수 있다. 

 

rack 디렉터리와 rack_branch 브랜치와의 차이점도 비교할 수 있다. 일반적인 diff 명령은 사용할 수 없고 git diff-tree 명령을 사용해야 한다.

$ git diff-tree -p rack_branch

 

또 rack 디렉터리와 저장소의 master 브랜치와 비교할 수 있다. 

$ git diff-tree -p rack_remote/master

' > 프로 Git' 카테고리의 다른 글

Git 도구-6 - 서브모듈  (0) 2020.07.30
Git 도구-5 - Git으로 버그 찾기  (0) 2020.07.29
Git 도구-4 - 히스토리 단장하기  (0) 2020.07.29
Git 도구-3 - Stashing  (0) 2020.07.29
Git 도구-2 - 대화형 명령어  (0) 2020.07.29