개요
개발을 하면서 난잡하게 코드들이 커밋되어 있는 하나의 리포지토리 A
에서 용도에 따라 코드를 분리 하여 관리를 하려고 한다. 재활용 가능한 모듈로 코드를 관리하기 위해 덜어낸 코드를 리포지토리 B
에 작업하였다. 이를 효과적으로 사용하기 위한 git submodule
명령어 사용법 정리.
서브모듈 추가하기
A
리포지토리에서 B
라는 별개의 리포지토리를 서브모듈로 사용하는 방법이다. 다른 독립된 Git
저장소를 Clone 해와서 현재의 Git
저장소 내에 포함되는 것으로 간주한다.
# Current Repo: A
$ git submodule add https://github.com/dongle94/B # repo B
위와 같이 가져오면 A
라는 리포지토리에 B
가 하위 디렉토리로 잡히게 된다. 또한 B
하위에 있는 파일들은 서브모듈로 A
리포지토리와는 별도로 관리되기에 .gitmodules
라는 파일이 생성되어 서브모듈이 연결되었다는 매핑 정보만 관리한다.
A
├─ Readme.md
├─ .gitmodules
└─ B
├─ ...
└─ ...
디렉토리 명 지정해서 가져오기
이 때 B
리포지토리를 B
가 아닌 다른 경로로 저장하고 싶다면 뒤에 <path>
를 붙여서 다르게 저장할 수 있다. 아래와 같이 실행한다면 B
가 아니라 repo_b
라는 경로에 서브모듈로 추가된다.
# git submodule add {url} {path}
$ git submodule add https://github.com/dongle94/B ./repo_b
추가 후 꼭 해야하는 것
B
리포지토리를 서브모듈로 추가했다면 A
리포지토리의 입장에선 B
라는 서브모듈이 추가되었다는 것 자체가 하나의 변경 사항이다. 그래서 해당 변경점을 commit
및 push
해야 원격 저장소에선 반영이 된다.
$ git add .gitmodules
$ git add B
$ git commit -m "add submodule"
$ git push origin main
이 때 B
에 대한 커밋은 매우 앞으로도 중요해지는데 아래에서 자세히 설명한다.
프로젝트 새로 가져오기
누군가가 위 처럼 A
라는 리포지토리에 B
라는 서브모듈을 추가하여 push
까지 하였고 다른 공동작업자가 이제 B
를 포함한 A
를 가져오려고 할 때 다음과 같이 git clone
명령어를 사용한다.
$ git clone https://github.com/dongle94/A
Cloning into 'A'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 14 (delta 1), reused 13 (delta 0)
Unpacking objects: 100% (14/14), done.
A
를 보면 위의 트리구조 처럼 Readme.md
, .gitmodules
, B
의 세가지 파일 및 디렉토리는 있는데 서브모듈 B
의 디렉토리가 비어있다.
A
├─ Readme.md
├─ .gitmodules
└─ B # empty directory
서브모듈의 내용물을 가져오는 것은 아래 명령어를 참고하여 추가작업이 필요하다.
$ git submodule init
$ git submodule update --remote
위의 init
이 들어간 명령어는 해당 A
리포지토리를 clone
해오고 처음 1회 입력해주면된다. 이는 .gitmodules
파일을 읽어 각 디렉토리와 원격저장소의 경로를 연결한다.
그 후 update --remote
구문을 통해 원격저장소로부터 서브모듈 B
를 추가적으로 clone
해오는 개념이다.
서브모듈이 수정되었을 때
위의 예시에서 서브모듈 B
가 수정되는 경우는 아래와 같이 3가지로 나눌 수 있다.
- 리포지토리
B
에 변경사항이 있는 경우 A
하위에 있는B
를 내가 직접 수정할 경우- 다른 사람이
B
를 직접 수정하여commit
&push
한 경우
리포지토리 B
에 변경사항이 있는 경우
A
하위 디렉토리가 아닌 본래의 B
프로젝트에 변경 사항이 있는 경우 일반적으로 B
프로젝트에 대해 commit
& push
를 수행하고 A
프로젝트에서 서브모듈을 업데이트한다.
# Current Directory: Original B
$ git commit -m "modify repo B"
$ git push origin main # for repo B
# Move A and Update submodule B
$ cd A # local repo A
$ git submodule update --remote
$ git commit -m "update submodule B"
$ git push origin main # for repo A
본래의 B
는 B
대로 원격 저장소에 커밋과 푸시가 진행되고, A
리포지토리 입장에서는 변경 사항이 있는 B
를 원격 저장소로부터 최신의 상태를 받아와 업데이트 한다.
A
하위에 있는 B
를 내가 직접 수정할 경우
근데 자체적으로 clone
해온 B
가 아니라 A
하위에 있는 서브모듈 B
에서 코드의 수정이나 파일의 변경사항이 있다면 당황하지 않고 다음과 같이 반영할 수 있다. A
하위 경로에 있는 서브모듈 B
도 git clone
해온 것과 동일하다고 생각하면 된다.
# Current directory: A
$ cd ./B
$ git commit -m "B의 변경사항"
$ git push origin main # for repo B
$ cd ../
$ git commit -m "update submodule B"
$ git push origin main # for repo A
자체적으로 clone
해온 저장소 B
나 서브모듈로 가져온 B
나 같은 것이라고 생각하면 이해가 쉽다.
다른 사람이 B
를 직접 수정하여 commit
& push
한 경우
내가 아닌 다른 공동작업자가 B
의 내용을 변경사항을 push
하여 내가 그것을 반영해야 할 때에는 바로 업데이트 하고 내 A
에 commit
하면 된다.
# Current directory: A
$ git submodule update --remote
$ git commit -m "update submodule B"
$ git push origin main # for repo A
주의할 점
다음은 본인이 submodule
을 최근 사용해보면서 느낀 중요한 컨셉 및 일부 에러에 대한 수정 사항이다.
서브모듈 커밋에 대해
위에서 적어온 서브모듈 B
의 변경사항을 update
하는 것은 이해가 쉽다. 그러나 B
의 상태가 변경되었으니 이를 commit
& push
하는 것은 보통의 것과는 조금 다르다.
B
의 변경사항을 commit
하는 것은 A
입장에서는 A
가 연결할 B
의 커밋 번호만을 갱신해주는 작업이다. 그래서 B
하위 경로의 파일 하나하나의 변경 사항을 A
의 git
에 저장하는 것이 아니라 서브모듈 B
의 커밋번호만을 관리하는 것이다.
위의 이미지는 깃허브에 서브모듈을 사용한 리포지토리의 예시다. 해당 리포지토리에서는 obj_detector
라는 폴더로 서브모듈을 이용한다. 이 때, 서브모듈 리포지토리의 파일들을 관리하는 것이 아닌 79d834e
라는 커밋 번호만을 원격 저장소에 기록함으로써, 해당 리포지토리의 서브모듈의 status를 저장한다.
서브모듈 업데이트가 안될 때
submodule update
명령어를 사용하여 업데이트를 시도했는데 안될 때가 있다. 아래는 바로 위의 obj_detector
가 포함된 로컬 저장소에서 업데이트를 시도했을 때 나올 수 있는 에러이다.
$ git submodule update --remote
>
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 1), reused 3 (delta 1), pack-reused 0
Unpacking objects: 100% (3/3), 389 bytes | 389.00 KiB/s, done.
From https://github.com/dongle94/Object-Detector
79d834e..ea448c7 main -> origin/main
* [new tag] v0.1.0 -> v0.1.0
fatal: Needed a single revision
Unable to find current origin/master revision in submodule path 'obj_detector'
핵심 에러메세지는 제일 아래의 두 줄이다.
fatal: Needed a single revision
Unable to find current origin/master revision in submodule path 'obj_detector'
서브모듈 경로 obj_detector
에 연결된 것을 origin/master
에서 찾을 수 없다고 한다. 과거 MS의 github 인수 이후, 마스터 브랜치가 이제 이름이 main
브랜치로 기본설정 되어 있는 부분이 있다.
그래서 설치되어있는 git
의 버전이 낮아 기본적으로 submodule update
를 origin/master
에서 하려고 하는것이 문제점이자 원인이다.
이에 대한 해결은 .gitmodules
파일을 수정해줘야한다. 위의 예시로 들었던 obj_detector
서브모듈을 사용하는 다른 리포지토리의 .gitmodules
파일을 열면 path
와 url
은 기본적으로 있는데 아래의 branch
는 원래는 없었다.
[submodule "obj_detector"]
path = obj_detector
url = https://github.com/dongle94/Object-Detector.git
branch = main
즉, 처음에 예시로 든 A
리포지토리의 .gitmodules
파일을 열어 branch = main
항목을 넣어 수정해준다. 그러면 제대로 origin/main
브랜치로 부터 정상적으로 업데이트를 수행할 수 있게된다.
댓글남기기