아래의 내용은 정리하면서 검증한 부분도 있지만, 검증이 되지 않고 정리된 내용도 있을 수 있으므로, 반드시 관련된 정보를 추가로 검색하여 검증해야 한다.
Git 프로젝트는 하나의 독립된 라이브러리라고 생각할 수 있다. 따라서 특정 프로젝트를 진행할 때 또 다른 프로젝트를 내부적으로 사용해야하는 경우가 빈번하게 발생할 수 있다. 일반적으로 이런 상황에서는 필요한 라이브러리의 특정 버전을 설치하거나 소스를 복사해서 사용하는 방법을 사용하는데 이에 따른 버전관리나 소스의 변경등에 대한 대처가 만만치 않다.
Git와 Submodule을 사용하면 Git Repository 내에 또 다른 독립적인 Repository를 운영하는 구조를 만들 수 있다. 당연히 각 Repository는 독립적으로 관리된다.
이런 상황이 새로운 것일까? Node 환경이라면 이미 npm을 이용해서 package를 관리하며 사용하고 있다. 그런데 가져다 쓸 패키지가 git에 repository로만 존재하는 경우라면 어떻게 해야할 까? 이런 상황에 대체하기 위한 것이 submodule이고 정리하면 “submodule은 git repository 하위에 다른 git repository를 관리하기 위한 도구” 라고 생각하면 된다. 자세한 내용은 Pro Git의 내용을 참고!
git submodule에 대한 공식 옵션은 다음의 명령으로 확인 가능하다.
$ git submodule --help
main project에 submodule을 추가하면 submodule들을 관리하기 위한 .gitmodules 파일이 생성되고 여기에 submoudle에 대한 폴더와 repository 정보가 관리되며 main project는 추가된 submodule에 대한 commit point를 바라보고 있게 된다.
따라서 main project와 submodule 간의 처리는 아래와 같은 순서에 주의해야 한다.
main project가 submodule에 대한 commit point를 연계하고 있기 떄문에 위의 순서가 맞지 않으면 잘못된 submodule의 commit point를 바라보게 되므로 문제가 발생할 수 있다.
$ git submodule add [-b branch_name] <repository address> [path]
# path를 생략하면 현재 경로 밑으로 repository 이름으로 생성된다.
정상적으로 추가되면 main project 경로에 .gitmodules
파일이 생성되며, 내용은 아래와 같이 구성된다.
[submodule "themes/hugo-theme-docdock"]
path = themes/hugo-theme-docdock
url = https://github.com/vjeantet/hugo-theme-docdock.git
위와 같이 Root Repository (여기서는 Hugo로 만든 사이트) 를 기준으로 경로와 git repository url이 관리되고 있는 것을 확인할 수 있다.
submodule이 추가된 것은 main project가 변경된 것을 의미하기 때문에 commit을 수행해 줘야 한다. 특정 branch로 지정할 경우는 -b <branch name>
을 사용하면 된다. 또한 나중에 submodule에 대한 branch를 설정할 경우는 아래와 같이 처리할 수도 있다.
$ git config -f .gitmodules submodule.<submodule path>.branch <branch name>
기본적인 git 상태 확인은 아래의 명령을 사용하며 submodule과 연동해서 사용할 수 있다.
# main project 상태 확인
$ git status
# submodule 상태 확인
$ git submodule status
# main project 상태확인에 submodule 정보 추가해서 보기 설정
$ git config status.submodulesummary 1
$ git diff --cached <submodule path>
new file mode 160000
index 0000000..51dbdcf
--- /dev/null
+++ b/themes/hugo-theme-learn
@@ -0,0 +1 @@
+Subproject commit 51dbdcf4aaef01d02e78a6ea76b2a6fde5842b55
위의 내용을 검토해 보면 다음과 같은 특징이 존재한다.
Detached HEAD
문제Detached HEAD 문제란 submodule update
명령이 수행되면 해당 submodule이 어떤 branch에도 속하지 않는 분리된 HEAD 정보를 가지는 상태를 말하는 것으로 branch를 지정해 줘야 한다. 따라서 아래와 같이 각 단계별 명령을 수행할 때 지정해 주는 것이 좋다.
# 처음 clone할 때
$ git submodule init
$ git submodule udpate
$ git submodule foreach git checkout <branch name>
# pull/update할 때
$ git pull
$ git submodule update --remote --merge
# push할 때
$ git push --recurse-submodules=check
위에서 알아본 명령들을 별칭(Alias) 등록을 통해서 단축 명령으로 수행할 수도 있다.
# git sdiff 단축 정의
$ git config alias.sdiff '!'"git diff && submodule foreach 'git diff'"
# git spush 단축 정의
$ git config alias.spush 'push --recurse-submodules=check'
# git supdate 단축 정의
$ git config alias.supdate 'submodule update --remote --merge'
main project에 아래와 같이 정보를 설정해 두면 활용하기 쉽다.
# login
$ git config credential.helper cache
# status
$ git config status.submodulesummary 1
# diff
$ git config diff.submodule log
Parent git project clone : .gitmodules
파일이 존재하며, 파일내에 어떤 폴더에 어떤 git repository를 사용하는지에 대한 정보를 알 수 있다.
$ git clone <main project repository>
submodule clone
$ git submodule init # submodule 초기화
$ git submodule update # submodule 갱신(다운로드 - Detached HEAD 상태로 어떤 branch에도 속하지 않는 상태)
$ git submodule foreach git checkout <branch name> # 지정한 branch로 설정
위의 clone 과정을 한번에 처리할 수도 있다.
$ git clone –recurse-submodules <main project repository>
$ git submodule foreach git checkout
$ git submodule update --remote --merge
만일 새로운 branch로 작업하는 경우
$ git submodule foreach git checkout -b <feature name>
변경된 코드들 commit
$ git submodule foreach git add -A .
$ git submodule foreach git commit -am "commit message"
$ git submodule foreach git checkout <branch name>
$ git submodule foreach git merge <feature name>
push submodules
$ git submodule foreach git push
commit and push main project
$ git commit -am "commit message"
$ git push –recurse-submodules=check
위에서 언급한 것과 같이 push를 할 때는 반드시 submodule들을 먼저 처리하고 main project를 처리 해야 한다. 따라서 submodule들이 모두 push되었는지를 확인해야 한다.
$ git push --recurse-submodules=check # submodule들이 모두 push된 상태인지를 검사하고 확인되면 push 처리
$ git push --recurse-submodules=on-demand # submodule들을 모두 push하고 성공하면 push 처리
매번 명령어를 지정해서 처리하는 것을 항상 처리할 수 될 수 있도록 하기 위해서는 아래와 같이 설정으로 처리해 두면 된다.
$ git config push.recurseSubmodules check
$ git config push.recurseSubmodules on-demand
.gitmodules
파일에서 대상 submodule 정보를 삭제한다.변경된 내용을 stage 처리 한다.
$ git add .gitmodules
.git/config
파일에서 대상 submodule 정보를 삭제한다.
git 에서 cached 인덱스 정보를 제거한다.
$ git rm –cached <path to submodule> # 경로 맨 뒤의 '/'는 사용하지 않는다.
./git/modules
폴더에서 대상 submodule 폴더 정보를 삭제한다.
$ rm .git/modules/<path to submodule> # 경로 맨 뒤의 '/'는 사용하지 않는다.
변경된 내용을 commit 한다.
제거된 submodule 폴더를 삭제한다.
$ rm -rf <path to submodule>
위의 내용은 기본적인 git 명령을 사용한 것이고 submodule 명령을 통하면 다음과 같이 더 간단하게 처리가 가능할 수 있다.
git에서 submodule 관련 정보 삭제
$ git submodule deinit <path to submodule>
git에서 submodule 삭제
$ git rm <path to submodule>
변경 내용 commit
submodule 폴더 삭제
$ rm .git/modules/<path to submodule> # 경로 맨 뒤의 '/'는 사용하지 않는다.