Hugo를 통해 알아본 Git Submodule
git submodule 발견
Giithub Pages로 블로그를 새로 설정하면서 Hugo를 정적 사이트 빌더로 골랐다. Jekyll을 이용해볼까 생각했는데, Jeykyll은 Ruby 개발환경이 설정되어야하기 때문에, 이전에 잠깐 배웠던 Go 기반의 Hugo를 사용해보기로 했다. 귀차니즘이 이걸 또
테마는 거창한 것 없이 Minimo로 심플하게 구성하기로 했다. Hugo를 설정하던 중 git submodule
이라는 명령어를 발견하게 되었다.
우선, Hugo를 사용하여 Site Workspace (내 식대로 정의하자면)를 만들면 다음과 같은 결과를 확인할 수 있다.
Kaybobs-MacBook-Pro:Development lkaybob$ hugo new site example
Congratulations! Your new Hugo site is created in /Users/lkaybob/Desktop/Desktop/example.
Just a few more steps and you're ready to go:
1. Download a theme into the same-named folder.
Choose a theme from https://themes.gohugo.io/, or
create your own with the "hugo new theme <THEMENAME>" command.
2. Perhaps you want to add some content. You can add single files
with "hugo new <SECTIONNAME>/<FILENAME>.<FORMAT>".
3. Start the built-in live server via "hugo server".
Visit https://gohugo.io/ for quickstart guide and full documentation.
Kaybobs-MacBook-Pro:Development lkaybob$ cd example/
Kaybobs-MacBook-Pro:example lkaybob$ ls
archetypes content layouts themes
config.toml data static
만들고 나서 hugo
명령어를 통해 정적인 사이트를 생성하면 (혹은 hugo serve
를 실행해 localhost:1313
으로 접속하면) 빈 페이지만 있는 것을 볼 수 있을 것이다. 여기에 테마를 입히고 사이트를 만들어가면 되는 것이다.
마지막 ls
의 결과를 보면 themes
라는 폴더가 보인다. 그럼 테마를 ZIP 파일로 다운받아서 설정해야하고 있었지만, Minimo 테마 공식문서에서는 테마를 설치하는 방법으로 다행히도 Git Submodule을 이용하는 것을 추천하고 있다.
git submodule add https://github.com/MunifTanjim/minimo themes/minimo
git submodule init
git submodule update
그럼 왜 Submodule을 추천하는 것일까?
왜 필요하지?
이런 문제가 있다고 해보자. 아래와 같이 한 Repository의 Directory(예를 들어 위치 A) 안에서 여러 개의 Repository(B, C, D, …)가 공존하고 있는 것이다. (사실 말이 안 되는거지만)
.
└── website // Git Repository A
├── .git
| └── ...
├── public
| ├── bootstrap // Git Repository B
| | └── ...
| ├── jquery // Git Repository C
| | └── ...
| └── vue.js // Git Repository D
| └── ...
├── index.js
└── content
├── menu.json
└── greetings.json
Repo A는 Repo B, C, D를 필요로 하기 때문에, 이들에 대해 의존성을 가진다고 볼 수 있다. 그렇다면, 본인이 Repo A를 Git으로 관리하고 싶다면 어떻게 해야할까? 그냥 맘 편하게 싹 다 커밋하고 푸시를 할까?
문제는 B, C, D의 변경사항은 각자의 Repo 굳이 A의 Commit에는 반영될 필요는 없는 것이다.
단순하게 생각하면 .gitignore
에 위 Directory들을 추가하면 A의 Repo에는 반영이 되지 않게 된다. 그러나 문제는, Git의 입장에서는 해당 Directory들을 깡그리 무시하는 것 뿐이지, 의존성을 가지고 있음을 알려주는 것은 아니다.
따라서 나는 Git Submodule은 이렇게 Repository 간의 의존관계를 해결하기 위한 하나의 방법이라고 받아들였다.
Minimo 설정하기
이제 Minimo 테마를 현재 블로그에 적용해보자. Hugo
를 통해 생성한 사이트 경로에 아래와 같이 실행한다.
Kaybobs-MacBook-Pro:example lkaybob$ git init
Initialized empty Git repository in /Users/lkaybob/Desktop/example/.git/
Kaybobs-MacBook-Pro:example lkaybob$ git submodule add https://github.com/MunifTanjim/minimo themes/minimo
Cloning into '/Users/lkaybob/Desktop/example/themes/minimo'...
remote: Counting objects: 3030, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 3030 (delta 0), reused 1 (delta 0), pack-reused 3024
Receiving objects: 100% (3030/3030), 1.45 MiB | 1.24 MiB/s, done.
Resolving deltas: 100% (1470/1470), done.
# 아래는 Git Submodule이 잘 설정되었는지 확인하기 위함
Kaybobs-MacBook-Pro:example lkaybob$ cat .gitmodules
[submodule "themes/minimo"]
path = themes/minimo
url = https://github.com/MunifTanjim/minimo
Kaybobs-MacBook-Pro:example lkaybob$ git submodule
f7d501029d288d3073d3bb58ff35a4453e1e7fdd themes/minimo (2.4.0-4-gf7d5010)
Minimo 테마는 잘 가져왔지만, 아쉽게도 이를 적용하려면 손질이 필요하다. Hugo
는 config.toml
을 통해서 사이트 빌딩을 위한 설정들을 진행할 수 있는데, 친철하게도 Minimo 테마의 홈페이지에서 테마를 사용하기 위한 템플릿 파일을 제공해준다. 이걸 수정해서 알맞게 사용하면 된다. (config.toml
항목들의 내용에 대해선 다루지 않을 예정이다. 자세한 내용은 공식문서를 참고)
config.toml
파일을 설정하고 난 후 사이트를 확인(hugo
명령어 후 public
폴더를 확인, 혹은 hugo serve
명령어를 실행해보면 아래와 같은 예쁜 사이트를 확인할 수 있다.
블로그 배포하기
Jekyll을 이용했으면 Jekyll Workspace를 Commit & Push해서 바로 사이트 공개를 할 수 있겠지만, 아쉽게도 여기선 Hugo를 이용했기 때문에 Local Workspace에서 Static Site 형태로 빌드해서 공개해야한다. 그래서 Hugo 공식 문서에서는 블로그 [Workspace에 대한 Repository](#만약 복원을 해야한다면??) 따로, 공개용 Repository 따로 설정할 것을 권장하고 있다. 문서를 확인해보면 여기서도 git submodule
을 사용하는 방법으로 권장하고 있는 것을 알 수 있으며, 이를 쉽게 하기 위해서 Shell 스크립트 템플릿까지 제공해주고 있다.
관리용 Repository 설정하기
먼저 블로그 Workspace를 Git Repository에 올려보자. Git Repository의 Remote를 설정하고 git status
를 실행하면 다음과 같이 나오는 것을 볼 수 있다.
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .gitmodules
new file: public
new file: themes/minimo
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)
(commit or discard the untracked or modified content in submodules)
modified: public (modified content, untracked content)
modified: themes/minimo (modified content)
Untracked files:
(use "git add <file>..." to include in what will be committed)
...
Submodule로 지정했던 public
디렉토리와 theme
이 Changes to be committed에 들어가있는 것을 확인할 수 있는데, 이는 Submodule로 설정한 파일들이 무식하게 다 Commit될 것이라는 것이 아니라, 우리가 설정할 Git Repository Git Submodule이 반영되었다는 의미를 나티내는 것이다. 실제로 git add *
를 실행 후 git status
를 실행하면 아래와 같이 나오게 된다.
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .gitmodules
new file: archetypes/default.md
new file: config.toml
new file: content/about/_index.md
new file: content/post/first-page.md
new file: content/post/git-submodule-with-hugo.md
new file: content/post/misc/_index.md
new file: deploy
new file: public
new file: static/css/custom.css
new file: static/images/meme/facepalm.jpg
new file: static/js/custom.js
new file: themes/minimo
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)
(commit or discard the untracked or modified content in submodules)
modified: public (modified content, untracked content)
modified: themes/minimo (modified content)
...
하위 파일들이 모두 Staging 단계에 올라간 것과 달리, public
과 theme/minimo
디렉터리들은 그 자체만 반영되는 것을 확인할 수 있다. 실제로 이 상태를 Commit & Push하면 Github에서는 아래와 같이 보이게 된다.
public
과 themes/minimo
디렉토리에 이상한 아이콘과 함께 @ 뒤에 난수가 표시되어있는 것을 볼 수 있다. 이는 사실 해당 Repository의 MD5 Hash의 앞 7자를 가져온 것이며, Submodule을 정의할 당시 해당 Repository의 Commit Hash를 정의한 것이다.
만약 복원한다면?
OS 재설치 등으로 블로그 Workspace를 복구해야할 일이 생기게 된다면, Submodule의 기능은 빛을 발휘한다고 생각한다. Workspace를 복구하기 위해선 이런 순서로 하면 된다.
- Workspace용 Repository를 Clone하기
- Submodule 업데이트
실제로 터미널에서 재현해보면 이렇다.
Kaybobs-MacBook-Pro:Desktop lkaybob$ git clone [email protected]:lkaybob/blog-hugo.git
Cloning into 'blog-hugo'...
Enter passphrase for key '/Users/lkaybob/.ssh/id_rsa':
remote: Counting objects: 24, done.
remote: Compressing objects: 100% (15/15), done.
remote: Total 24 (delta 0), reused 24 (delta 0), pack-reused 0
Receiving objects: 100% (24/24), 38.86 KiB | 0 bytes/s, done.
Kaybobs-MacBook-Pro:Desktop lkaybob$ cd blog-hugo/
Kaybobs-MacBook-Pro:blog-hugo lkaybob$ ls
archetypes config.toml content deploy public static themes
Kaybobs-MacBook-Pro:blog-hugo lkaybob$ git submodule init
Submodule 'public' ([email protected]:lkaybob/lkaybob.github.io) registered for path 'public'
Submodule 'themes/minimo' (https://github.com/MunifTanjim/minimo) registered for path 'themes/minimo'
Kaybobs-MacBook-Pro:blog-hugo lkaybob$ git submodule update
Cloning into '/Users/lkaybob/Desktop/blog-hugo/public'...
Enter passphrase for key '/Users/lkaybob/.ssh/id_rsa':
Cloning into '/Users/lkaybob/Desktop/blog-hugo/themes/minimo'...
Submodule path 'public': checked out 'f5fb897f37e2ed0e39b63e8b812742d645d6685a'
Submodule path 'themes/minimo': checked out 'bce1e29313d71001eac3a4de82449014de432c45'
git submodule
의 Man Page를 보면 update에 대해서는 이렇게 설명하고 있다.
Update the registered submodules to match what the superproject expects by cloning missing submodules and updating the working tree of the submodules.
즉, 현재 Repository에서 빠져있는 Submodule을 update Command가 채워준다는 것이다. 다만, Github에서 이 Submodule을 표시하는 방법에서 유추할 수 있듯이, 이렇게 채워넣은 Submodule은 Submodule을 연결해줄 당시의 Commit으로 Clone하게 된다. 따라서 해당 Submodule의 Repository에서 최신(origin의 HEAD)으로 가져오려고 한다면, 아래와 같이 실행하면 된다.
Kaybobs-MacBook-Pro:blog-hugo lkaybob$ git submodule update --remote
Enter passphrase for key '/Users/lkaybob/.ssh/id_rsa':
Submodule path 'themes/minimo': checked out 'f7d501029d288d3073d3bb58ff35a4453e1e7fdd'
글 작성당일 기준으로 Minimo Repository에 대해서 최신 Commit으로 Update된 것을 확인할 수 있다.
마치며
개발자 입장에서는 언어에 관계없이 Package Manager는 많이 쓰는 것은 사실이다. 사실 의존성들을 알아서 해결해주는 일들을 Package Manager들이 다해서 그렇지, Git Submodule을 통해 원초적인 의존성에 대해서 생각해본 것은 처음이다. 그리고 내가 알지 못한 Git의 숨은 기능들이 많은 것은 아닌가 생각하게 되었다.