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 테마는 잘 가져왔지만, 아쉽게도 이를 적용하려면 손질이 필요하다. Hugoconfig.toml을 통해서 사이트 빌딩을 위한 설정들을 진행할 수 있는데, 친철하게도 Minimo 테마의 홈페이지에서 테마를 사용하기 위한 템플릿 파일을 제공해준다. 이걸 수정해서 알맞게 사용하면 된다. (config.toml 항목들의 내용에 대해선 다루지 않을 예정이다. 자세한 내용은 공식문서를 참고)

config.toml 파일을 설정하고 난 후 사이트를 확인(hugo 명령어 후 public 폴더를 확인, 혹은 hugo serve 명령어를 실행해보면 아래와 같은 예쁜 사이트를 확인할 수 있다.

img

블로그 배포하기

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 디렉토리와 themeChanges 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 단계에 올라간 것과 달리, publictheme/minimo 디렉터리들은 그 자체만 반영되는 것을 확인할 수 있다. 실제로 이 상태를 Commit & Push하면 Github에서는 아래와 같이 보이게 된다.

깃헙 커밋1

publicthemes/minimo 디렉토리에 이상한 아이콘과 함께 @ 뒤에 난수가 표시되어있는 것을 볼 수 있다. 이는 사실 해당 Repository의 MD5 Hash의 앞 7자를 가져온 것이며, Submodule을 정의할 당시 해당 Repository의 Commit Hash를 정의한 것이다.

만약 복원한다면?

OS 재설치 등으로 블로그 Workspace를 복구해야할 일이 생기게 된다면, Submodule의 기능은 빛을 발휘한다고 생각한다. Workspace를 복구하기 위해선 이런 순서로 하면 된다.

  1. Workspace용 Repository를 Clone하기
  2. Submodule 업데이트

Imgur

실제로 터미널에서 재현해보면 이렇다.

Kaybobs-MacBook-Pro:Desktop lkaybob$ git clone git@github.com: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' (git@github.com: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의 숨은 기능들이 많은 것은 아닌가 생각하게 되었다.