IT/기초 지식

[git] git submodule 기초

개발자 두더지 2022. 4. 4. 21:49
728x90

일본의 블로그 글을 번역한 포스팅입니다. 오역 및 직역이 있을 수 있으며 내용 오류시 지적해주시면 수정하겠습니다.

 

git submodule이란?


  git submodule은 외부 git리포지토리를 자신의 git 리포지토리의 서브 디렉토리로 등록하고 특정 commit을 참고하는 구조이다. Subversion의 external와 유사하다.

 그럼 git submodule의 해설을 위해 수중의 리포지토리 A(/path/to/a)와 A의 submodule로써 자주 사용되는 예로써 Bootstrap을 등록하는 흐름을 보려고 한다.

 git submodule를 이해할 때 가장 중요한 것은 아래의 두 가지이다.

  • 리포지토리 A가 가리키는 submodule로서의 Bootstrap의 commit
  • 현재의 Bootstrap의 commit

 

 

git submodule add해보기


 리포지토리 A내에, submodule add해보자.

$ git submodule add https://github.com/twbs/bootstrap.git bootstrap
Cloning into 'bootstrap'...
remote: Counting objects: 73135, done.
remote: Compressing objects: 100% (15/15), done.
remote: Total 73135 (delta 6), reused 0 (delta 0), pack-reused 73120
Receiving objects: 100% (73135/73135), 85.84 MiB | 3.91 MiB/s, done.
Resolving deltas: 100% (44541/44541), done.
Checking connectivity... done.

 이것으로 수중의 bootstrap 디렉토리에 GitHub에 있는 Bootstrap이 submodule로 등록된다. git status를 살펴보면, .gitmodules이라는 디렉토리와 submodule의 안에 넣은 bootstrap이라는 디렉토리가 새롭게 등록된다.

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   .gitmodules
#       new file:   bootstrap
#

 diff도 확인해보자.

$ git diff --cached
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..2fdbccb
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "bootstrap"]
+       path = bootstrap
+       url = https://github.com/twbs/bootstrap.git
diff --git a/bootstrap b/bootstrap
new file mode 160000
index 0000000..5c02844
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1 @@
+Subproject commit 5c028448c1fc171cf4a0fd99d3a672b649bef2aa

 주목해야할 부분은 +Subproject commit 5c028448c1fc171cf4a0fd99d3a672b649bef2aa 의 diff로,  이것은 "Bootstrap의 5c028448c1fc171cf4a0fd99d3a672b649bef2aa 커밋을 botstrap 디렉토리에 Submodule로 등록한다"는 의미가 된다. 이것을 commit해 둔다.

$ git commit -m "Add Twitter Bootstrap as a submodule"

 그럼 시험삼아 bootstrap 디렉토리로 이동해, git show해보면, 확실히 현재 5c028448c1fc171cf4a0fd99d3a672b649bef2aa에 있다는 것을 알 수 있다.

$ cd bootstrap
$ git show
commit 5c028448c1fc171cf4a0fd99d3a672b649bef2aa
Merge: 5e7c5a6 0c4c1e4
Author: Chris Rebert <github@rebertia.com>
Date:   Wed Jul 29 15:00:54 2015 -0700

    Merge pull request #16908 from twbs/crbug-309483

    Remove http://crbug.com/309483 from the Wall of Browser Bugs

 

 

Submodule의 갱신


 지금 추가한 Bootstrap은 master 브랜치의 최신 커밋이었다. 프로젝트에서 다른 브랜치의 Bootstrap사용하고 싶어졌다는 상황을 가정한다고 하자. 예를 들어, 현재 진행중의 14226-rebased 브랜치를 사용하고 싶어졌다. 이때의 조작 순서는 다음과 같다.

  • submodule의 디렉토리에 들어가 대상 브랜치나 커밋을 체크아웃한다.
  • 다시 submodule의 디렉토리 밖으로 나가, 그 submodule의 현재 커밋을 기록한다(가리키는 곳을 변경한다).
$ cd bootstrap
$ git branch -a
* master
  remotes/origin/14226-rebased
  remotes/origin/HEAD -> origin/master
  remotes/origin/bhamodi-update-dependencies
  remotes/origin/bundler
  remotes/origin/derp
  remotes/origin/fix-15534
  remotes/origin/fix-popover-setContent
  remotes/origin/gh-pages
  remotes/origin/master
  remotes/origin/travis2

$ git checkout 14226-rebased
Branch 14226-rebased set up to track remote branch 14226-rebased from origin.
Switched to a new branch '14226-rebased'

 이 상태에서 디렉토리 밖으로 나가면,

$ cd ..
$ git status
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   bootstrap (new commits)

no changes added to commit (use "git add" and/or "git commit -a")

 bootstrap 디렉토리에 diff가 발생한 상태가 되어 있다. 

$ git diff
diff --git a/bootstrap b/bootstrap
index 5c02844..88697c0 160000
--- a/bootstrap
+++ b/bootstrap
@@ -1 +1 @@
-Subproject commit 5c028448c1fc171cf4a0fd99d3a672b649bef2aa
+Subproject commit 88697c03a9315b5f1944fc82534a824aafebda0e

 이것은 submodule이 커밋을 기록하는 구조로, 즉, 리포지토리 A의 참고하는 Bootstrap의 커밋이 5c028448c1fc171cf4a0fd99d3a672b649bef2aa에서 88697c03a9315b5f1944fc82534a824aafebda0e로 변경된다는 의미가 된다. 이것을 add하여 커밋해두자.

$ git add bootstrap
$ git commit -m "Update submodule: Bootstrap"

 

 

리포지토리 A에 다른 커밋을 체크아웃 하기 & git submodule update


 submodule에서 헷갈리는 부분이 리포지토리 A(submodule의 바깥쪽)와 submodule의 안이 연동되지 않다는 부분일 것이다. 연동되지 않는다는 것은 다음과 같은 의미이다.

 예를 들어, 현재 상황에서 리포지토리 A에 하나 전의 커밋을 체크아웃해보도록 하자. 먼저 체크아웃하기 위해 로그를 확인한다.

$ git log --oneline
284ffed Update submodule: Bootstrap
45db3c1 Add Twitter Bootstrap as a submodule
30ef9ed initial commit

 하나 전이기 때문에 45db3c1를 체크아웃한다.

$ git checkout 45db3c1
...
HEAD is now at 45db3c1... Add Twitter Bootstrap as a submodule

 이 상태에서 status를 살펴보자면, boostrap 디렉토리에 diff가 발생하지 않았다. 이것으로 "연동되지 않는다"는 점을 알 수 있다.

$ git status
HEAD detached at 45db3c1
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   bootstrap (new commits)

no changes added to commit (use "git add" and/or "git commit -a")

 외측의 리포지토리에 전의 커밋을 체크아웃했으므로, 그 커밋시점에서의 submodule으로써의 bootstrap은 5c028448c1fc171cf4a0fd99d3a672b649bef2aa가 기록되어 있다. 그러나, bootstrap의 안에는 방금의 조작의 영향으로 88697c03a9315b5f1944fc82534a824aafebda0e를 가리키고 있는 상태가 됐다. 

 리포지토리 A상에 다양한 브랜치나 커밋을 이동해도 submodule내에는 자동적으로 체크아웃되거나 하지 않는다. 따라서, bootstrap디렉토리 안을 커밋 88697c0가 가리키고 있는 bootstrap의 커밋 5c02844으로 한다는 조작이 아래의 커맨드가 된다.

$ git submodule update

 submodule update이라는 커맨드는 "submodule를 갱신한다"는 의미이다. 다음의 커맨드가 clean한 상태가 되어, bootstrap 디렉토리 안에 들어가 확인해보면 5c02844가 체크아웃된 상태가 되어 있다.

 

 

정리


  • submodule로써 추가된 디렉토리는 단지 외부 리포지토리로의 참고를 기록한 박스같은 것이다.
  • submodule의 외부는 그 상자의 어떤 커밋을 참고하고 있는가를 기록하고 있다.
  • submodule의 내부는 기본적으로 직접 조작하는 경우만 갱신된다.

참고자료

https://qiita.com/sotarok/items/0d525e568a6088f6f6bb#git-submodule-%E3%81%A8%E3%81%AF

728x90