서브모듈이 포함되어 있는 프로젝트를 클론하면 git submodule update명령어를 사용하게 되는데, 이때 서브모듈의 브랜치를 확인하면 *(HEAD detached at 커밋id) 형태인 것을 확인 할 수 있다. 분리되었다뇨... 브랜치랑 다른 뭔가 무서운(?)상태를 checkout하고 있는 것이다.

HEAD? detached HEAD?

우선 HEAD를 알아보자. git은 HEAD라는 포인터를 하나 가지고 있다. 이 포인터는 지금 작업하는 로컬 브랜치를 가리킨다. 다른 말로 하면 HEAD란 현재 작업하는 브랜치의 마지막 커밋이다. git checkout @@으로 @@브랜치로 HEAD를 옮기며 작업한다고 하는데, 그 브랜치는 특정 커밋을 가리킨다. 그렇기 때문에, 해당 커밋의 스냅샷 모양으로 작업 내용이 전환되는 것이다. 간단히 설명하자면, HEAD는 브랜치를 브랜치는 커밋을 가리키는 것이고, HEAD는 커밋을 간접적으로 가리키고 있는 것이다.

그렇다면 detached HEAD는 브랜치를 거치지 않고 바로 커밋을 가리키고 있는 것이다.
브랜치를 거치지 않고 커밋을 바로 가리킨다는 것은, 버전관리시스템의 본질을 흐리는 것이라고 생각한다. 버전관리시스템이란 목적별로, 필요에 따라 브랜치를 만들어서 작업을 하고 커밋에 따라 스냅샷으로 변화내용을 관리하여 병렬적인 동시작업, 안정성확보 등이 가능하게 하는 것이다. 그런데 브랜치가 없다는 것은... 이러한 흐름이 불가능하다는 것이다. 따르는 브랜치가 없는데 변화내용을 관리한다는 것이 의미가 없다. 그래서 commit이 불완전(?)하고(불가능하진 않다), push는 아예 불가능하다.

서브모듈에서 detached HEAD

처음부터 서브모듈을 직접 add하여 사용하면 서브모듈 디렉토리는 master 브랜치를 체크아웃한다.
반면, 서브모듈이 포함된 프로젝트를 clone하게 되면 git submodule update를 해야하는데, 이 명령 실행시 커밋을 바로 가리키게 되면서 detached HEAD가 되게 된다.

서브모듈에선 보통 아래 명령어 실행 시 볼 수 있다. 모두 git submodule update이후로 메인프로젝트 안 서브모듈 디렉토리에서 볼 수 있다.

  1. 브랜치 확인

    • ~/main-project-directory/submodule$ git branch
    * (HEAD detached from 5e6f33d)
     master
  2. 파일 확인

    • ~/main-project-directory/submodule$ git status
    HEAD detached at 5e6f33d
    Changes not staged for commit:
  3. 커밋 메세지

    • ~/main-project-directory/submodule$ git commit -m "메세지"
    [deatached HEAD 5e6f33d] 메세지

해결방법

사실 처음 서브모듈을 업데이트 할 때, git submodule update --remote --merge처럼 --merge옵션을 붙여 업데이트 시점에 바로 브랜치에 병합하고 나서 코드 작성을 시작하는 것이 제일 좋다.

어쨌든 detached HEAD에서 커밋이 가능하다. 브랜치 없는 커밋이 충분히 만들어질 수 있다는 것이다. 그 커밋은 붕붕 떠있는 상황이고, 브랜치가 없어 push가 되지 않고 버전관리가 불가능하다.

이미 branch없이 붕붕 떠있는 커밋이 생겼다면, 브랜치에 커밋을 안정적으로 붙여줘야한다.

Warning: you are leaving 1 commit behind, not connected to any of your branches.
  • 임시 브랜치를 만들어 합치기

    1. git checkout -b 임시브랜치 (새로운)임시 브랜치 만들어 체크아웃
    2. git checkout 정착할 브랜치 정착할 브랜치(보통 master)로 이동
    3. git merge 임시브랜치 정착할 브랜치에 임시브랜치 머지
    4. git branch -d 임시브랜치 필요없다면 임시브랜치 삭제
  • 기존 브랜치에 커밋 붙이기

    1. git reflog로 둥둥 떠다니는 커밋의 id를 확인
    2. git checkout 기존브랜치 기존 브랜치로 이동
    3. git cherry-pick 커밋id 떠다니던 커밋 기존브랜치에 붙이기

방법이 어떠하든 결국 둥둥 떠있는 커밋을 브랜치에 붙이는게(?) 포인트이다!

마무리

항상 브랜치 확인하는 습관을 들여야 한다. 코드를 작성하기 전에 내가 어디서 작업하고 있는지는 알아야한다. 브랜치를 확인했는데 detached HEAD 상황이라면, 브랜치를 안정적으로 설정한 뒤 작업하는 것이 제일 바람직하다. detached HEAD는 꼭 서브모듈이 아니라도 커밋으로 이동하다보면 생기는 경우가 있었는데, 정리해두니 맘이 편하다.

Reference & Learn More

stackoverflow: Why is my Git submodule HEAD detached from master?
Pro Git Reference git-submodule

Comments