하나의 프로젝트에 두개의 git프로젝트가 필요한 경우(git submodule)

JellyPower·2023년 5월 23일
0

나만 몰랐던 git

목록 보기
1/1
post-thumbnail

어떤 경우에 두 개의 git 프로젝트가 필요한가?

  • 개발을 진행하다보면 개발이 진행되고 있는 외부 모듈을 사용하거나, 전체 프로젝트에서 자체적으로 사용할 모듈을 내가 개발하고 테스트하는 등의 환경을 조성해야 하는 경우가 있다.
  • 이러한 경우, 하나의 프로젝트에 1) 메인 프로젝트의 깃, 2) 내가 사용하고자 하는 모듈 프로젝트의 깃 이렇게 총 2개의 깃 프로젝트가 하나의 프로젝트에 포함돼야 하는 경우가 있다.
  • 이렇게 되면 라이브러리를 커스터마이징 하는 것도 힘들 뿐더러 배포하는 것은 더욱 힘들어진다. 모듈에 대한 git프로젝트가 변경될 때마다 전체 프로젝트에 대한 git에 영향을 끼치고 원치 않는 변경이 발생할 수 있는 것이다.
  • 이러한 경우 사용할 수 있는 것이 바로 git의 기능중 하나인 Submodules이다.

사용방법

git submodule add ~

  • ”메인 프로젝트의 깃”“내가 사용하고자 하는 모듈의 깃”을 바로 클론해서 가져오는 방법이다.
git submodule add {클론하고자 하는 git 주소} {클론하고자 하는 폴더}
  • 위와 같이 사용하면 바로 클론해서 가지고 오면서 동시에 깃 서브모듈으로 등록을 바로 해준다.

명령어의 결과

$ git submodule add https://github.com/jellypower/VulkanExam.git SubmoduleFolder
  • 임시로 깃을 파서 서브모듈 명령어를 수행해봤다. 그러면 다음과 같이 “클론하고자 하는 폴더” 안에 해당 모듈이 알아서 clone되고 .gitmodules라는 파일이 생긴다.

  • 해당 파일의 내용은 다음과 같다.
[submodule "SubmoduleFolder"]
	path = SubmoduleFolder
	url = https://github.com/jellypower/VulkanExam.git
  • 파일의 구성은 간단하다. 서브모듈 이름, 경로, url 등의 설정내용이 포함된다.
  • 만약 여러개의 서브모듈을 가지고 왔으면 여러개의 하나의 .gitmodules파일에 여러개의 submodule설정이 존재할 것이다.
  • 자동으로 생성된 해당 .gitmodules파일은 .gitignore처럼 stage, commit, pull, push된다.
    • 그렇기에 “전체 프로젝트”를 다른 사람이 clone하면 해당 모듈의 출처를 알 수 있다.
💡 다른 사람들도 해당 submodule을 pull, push하며 사용할텐데 만약 내부적으로 나만 접근 가능한 URL을 해당 submodule의 URL으로 해놓으면 다른사람은 프로젝트의 진행에 차질을 빚을 수 있으니 모두가 접근 가능한 URL으로 지정해놓자.

Submodule의 status추적

  • submodule을 변경시키고 변경된 상태를 살펴보자.
cd SubmoduleFolder
touch temp
cd ..
git status
  • 위와 같이 서브모듈 안에 임의의 폴더를 만들고 원래 프로젝트로 돌아가서 상태를 살펴보았다.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
  (commit or discard the untracked or modified content in submodules)
        modified:   SubmoduleFolder (untracked content)
  • 위와 같이 해당 서브모듈이 수정되긴 했지만 untracked content라고 알려준다. 실제로 해당 모듈의 존재 자체는 add 할 수 있어도 모듈 내부의 내용은 add하거나 commit할 수 없게 설정된다.
  • 그러나 해당 서브모듈 내부는 몰라도 어느 commit 지점을 사용하고 있는지에 대해서는 알고있어야 하기에 메인 프로젝트를 commit할 때 해당 서브모듈을 add해주긴 해야한다.
  • submodule의 내부가 어떻게 변화하든 메인 프로젝트는 그저 submodule이라는 하나의 파일이 변경된 것처럼 작동한다 생각하면 될 것이다.

Submodule을 가진 프로젝트 clone하기

  • 위에 말했듯 Submodule은 메인 프로젝트에서 그 내부를 추적하지 않는다. 그래서 git clone으로 해당 프로젝트를 가져오면 해당 모듈의 내용과 폴더는 clone해와도 해당 모듈 폴더의 내부는 clone해오지 않는다.
    git clone https://github.com/jellypower/SubmoduleExam.git
    cd SubmoduleFolder
    ls
    #모듈 폴더의 내부는 clone해오지 않았기에 ls 명령어를 쳐도 아무것도 안나온다.
    (실제로 임시로 위에 만든 git 프로젝트를 push하고 클론해봤다. 위처럼 ls로 폴더의 목록을 찾아보면 아무것도 나오지 않는다.)
  • 그래서 Submodule을 가진 프로젝트를 clone하기 위해선 추가적인 절차를 조금 거쳐야 한다.
    git submodule init
    git submodule update
  • git submodule init 명령어를 통해 로컬에 있는 .git에게 어떠한 서브 모듈이 있는지를 등록해주고 git submodule update를 입력하면 등록된 서브모듈로부터 자동으로 fetch해오게 된다.
    git submodule init
    git submodule update
    cd SubmoduleFolder
    • 위와 같이 명령어를 쳐주면

      $ ls
      HelloTriangleApp/  HelloTriangleApp.sln
    • 이렇게 서브모듈들의 데이터들도 저장소로부터 fetch된다.

      git submodule update --init
    • 위 기능은 위처럼 하나의 명령어로도 작동 가능하다.

  • 사실 더 쉽게 하는 방법이 있다.
    git clone --recurse-submodules https://github.com/jellypower/SubmoduleExam.git
    cd SubmoduleExam
    cd Submodulefolder
    
    $ ls
    HelloTriangleApp/  HelloTriangleApp.sln
    • 위처럼 git clone --recurse-submodules 명령어를 사용하면 한 번에 서브모듈까지 전부 가지고온다.

submodule의 추가기능들

업데이트된 서브모듈 가져오기

  • 프로젝트 진행중에 서브모듈들이 업데이트 됐을 때, 해당 서브모듈의 깃 리포지토리 하나하나에 들어가서 git pull, git fetch를 진행해줘도 되지만 이를 한 번에 해줄 수도 있다.
    git submodule update --remote {서브모듈 이름}
    • 위와 같이 명령어를 써주면 서브모듈들을 알아서 fetch하고 merge해준다. {서브모듈 이름}이 없으면 모든 서브모듈들을 업데이트 해준다.

      git config -f .gitmodules submodule.{서브모듈 이름}.branch {브랜치 이름}
    • 알아서 업데이트 해주는 서브모듈의 브랜치는 보통 리모트 저장소의 default branch이다.(보통 main(master), HEAD가 달려있는 놈) 만약 알아서 업데이트 해주는 브랜치를 내가 원하는 브랜치로 바꿔주고 싶으면 위처럼 바꿔줄 수 있다.

    • 아래 링크처럼 수동으로 바꿔줄 수도 있다.

      git submodule 다른 브랜치 추적하도록 하기

    • 위 구문으로 설정을 바꿔주면 .gitmodules의 내용을 바꿈으로 별도의 커밋을 해줘야 한다.

  • 이번에는 메인 프로젝트에서 git pull을 진행했을 때를 생각해보자. git pull 명령어는 재귀적으로 서브모듈들이 변화했다는 것을 fetch해오지만 업데이트는 해주지 않는다.
    git pull
    git submodule update --init --recursive
    • 이러한 상황에선 위와 같이 해주면 서브모듈들을 업데이트 할 수 있을 것이다. --recursive 태그는 서브모듈들의 서브모듈들까지 재귀적으로 업데이트 해준다는 뜻이다.

      git pull --recurse-submodules
    • 위처럼 한 줄로도 가능하다.

원격 저장소의 주소가 바뀌었을 때

  • 현재 프로젝트가 호스팅하는 플랫폼을 바꾸거나 하는 등의 원격저장소 주소가 바뀌는 경우가 생길 수 있다.
  • 그런 경우에 git pull --recurse-submodulesgit submodule update가 실패할 수도 있다.
  • 이를 해결하기 위해선,
    1. .gitmodules 파일로 가서 주소를 업데이트 한다.

    2. 아래 명령어를 입력한다.

      # copy the new URL to your local config
      $ git submodule sync --recursive
      # update the submodule from the new URL
      $ git submodule update --init --recursive
  • 그러면 원격저장소의 주소를 업데이트 할 수 있다.

서브모듈의 변경사항 한 번에 적용하기

  • 프로젝트를 진행하면서 서브모듈 프로젝트들의 내용을 업데이트하던 중 다른 프로그래머가 리모트의 서브모듈 프로젝트들의 내용을 업데이트 했다고 생각해보자. 그런 상황에서 내가 업데이트된 서브모듈의 변경사항을 내려받고 싶으면 git submodule update 명령어를 써주면 된다.
  • 그런데, 이렇게 하면 내가 현재 작성 및 커밋 해왔던 코드들에서 update해온 커밋들로 현재 작업하는 HEAD가 옮겨진다.
  • 이를 위해 각 서브모듈들로 들어가서 git merge해줄 수도 있지만, 메인 프로젝트에서 서브모듈들을 업데이트하면서 머지까지 한 번에 해줄 수 있다.
    git submodule update --remote --merge
    git submodule update --remote --rebase
    (리베이스도 가능하다.)
💡 변경된 사항들을 commit하지 않고 submodule update나 git pull등을 진행한다면 문제를 일으킬 수 있으니 그러지 말자. 변경사항을 적용해버리지만 저장되지 않은 작업들에 대해선 overwrite를 하지 않기 때문에 변화한 내용들에 대해 변경사항을 적용하지 않을 수 있다.

서브모듈까지 다 같이 push하기

  • 내가 로컬에서 메인 프로젝트와 서브모듈을 모두 수정하고 push했다고 생각해보자. 메인 프로젝트의 push는 서브모듈들의 세부 변경 사항을 추적하지 않기 때문에 깜빡하고 서브모듈을 push하지 않았으면 다른 사람들의 프로젝트에서 conflict를 일으킬 수도 있다.
  • 이러한 실수를 방지하기 위한 push명령어의 키워드가 있다.
git push --recurse-submodules=check
  • 위 명령어를 입력하게 되면 메인 프로젝트를 push하기 전에 서브모듈들의 변경사항이 푸시되지 않았으면 push가 fail하게 된다.
git push --recurse-submodules=on-demand
  • 위 명령어를 입력하게 되면 메인 프로젝트를 push하기 전에 서브모듈들을 모두 알아서 push해준다. 서브모듈의 push가 fail하면 메인프로젝트의 push도 fail한다.
git config push.recurseSubmodules check
git config push.recurseSubmodules on-demand
  • 위처럼 config로 push의 기본동작 자체를 서브모듈에 종속되도록 아예 바꿔줄 수 있다.

학습배경

  • 최근 언리얼으로 온라인 게임 제작 프로젝트를 진행하며 가벼운 매칭 서버 모듈을 같이 개발하고 있었다. 그러던 와중 모듈의 버전관리와 전체 게임 프로젝트의 버전 관리를 구분하고 싶어 찾아본 것이 바로 Submodule이다.
  • 실제로 현업의 관점에서 생각해보면 프로젝트의 크기가 커지고 하나의 게임에 단순 인게임 정보 뿐 아니라 로그인 서버, 매칭 서버, 인벤토리DB와 같은 모듈들이 계속해서 붙어나갈텐데 그러한 경우에 큰 도움을 줄 수 있을것 같다.

레퍼런스

  • 추가적으로 submodule을 사용할 때 유용한 명령어, submodule의 자식이 가 충돌했을 때 어떻게 해결해야 할지("서브모듈 Merge 하기" 챕터)에 대한 자세한 내용은 아래 링크를 통해 찾을 수 있다.

Git - 서브모듈

profile
게임엔진코드싸개(진)

0개의 댓글