내 Git 하위 모듈 HEAD가 마스터에서 분리 된 이유는 무엇입니까?


162

Git 하위 모듈을 사용하고 있습니다. 서버에서 변경 사항을 가져온 후 여러 번 내 하위 모듈 헤드가 마스터 분기에서 분리됩니다.

왜 그런가요?

나는 항상해야합니다 :

git branch
git checkout master

하위 모듈이 항상 마스터 분기를 가리 키도록하려면 어떻게해야합니까?


1
이 답변을 읽었습니까? stackoverflow.com/questions/1777854/…
Johnny Z

@bitoiu 하위 트리와 Google Repo를 살펴 보았습니다. 나는 아직 완벽한 해결책을 얻지 못했습니다 :(
om471987

1
CI 환경에서 gitsubmodule에 대한 나의 경험은 끔찍합니다. 어쩌면 다른 사람들은 더 나은 경험을 가지고있을 것입니다.
bitoiu

@JohnnyZ 감사합니다. 하위 모듈은 트리의 헤드가 아닌 커밋을 가리키는 것으로 이해했습니다. 그러나 왜 지점에서 분리 되었습니까? 하나의 브랜치가있는 경우 기본적으로 브랜치에 연결되어 있지 않아야합니다.
om471987

3
서브 모듈이 나쁘다고 들었 기 때문에 너무 빨리 해지하지 마십시오. 지속적인 통합을 원한다면 좋지 않은 솔루션이지만 외부 프로젝트의 코드를 포함하고 모든 풀을 명시 적으로 관리하려는 경우 거의 완벽한 솔루션입니다. 조직에서 제어하지 않는 비 포크 모듈과 통합하는 경우 가장 좋은 방법입니다. 문제는 그것들이 전혀 잘 작동하지 않는 다른 모든 상황에서 유혹적인 해결책이라는 것입니다. 최선의 조언은 작동 방식을 읽고 시나리오를 평가하는 것입니다.
Sarah G

답변:


176

편집하다:

유효한 솔루션 은 @Simba Answer 를 참조하십시오.

submodule.<name>.update변경하려는 게다가, 참조되는 문서를 - 기본checkout
submodule.<name>.branch 원격 지사를 지정 추적합니다 - 기본master


오래된 답변 :

개인적으로 나는 시간이 지남에 따라 작동을 멈출 수있는 외부 링크로 직접 연결되는 답변을 싫어하고 (질문이 중복되지 않는 한) 여기에서 내 답변을 확인 하십시오 . 응답하지 않으면 설명서를 읽으십시오. "

다시 질문으로 돌아가십시오. 왜 그런 일이 발생합니까?

당신이 묘사 한 상황

서버에서 변경 사항을 가져온 후 여러 번 내 하위 모듈 헤드가 마스터 분기에서 분리됩니다.

이것은 하나가 사용하지 않는 일반적인 경우입니다 서브 모듈을 너무 자주하거나 함께 시작 서브 모듈 . 서브 모듈 의 HEAD가 분리 되는 시점에 우리 모두 가 있었다고 말한 것이 정확하다고 생각합니다 .

  • 원인 : 서브 모듈이 올바른 분기를 추적하지 않습니다 (기본 마스터).
    해결 방법 : 하위 모듈이 올바른 분기를 추적하고 있는지 확인하십시오
$ cd <submodule-path>
# if the master branch already exists locally:
# (From git docs - branch)
# -u <upstream>
# --set-upstream-to=<upstream>
#    Set up <branchname>'s tracking information so <upstream>
#    is considered <branchname>'s upstream branch.
#    If no <branchname> is specified, then it defaults to the current branch.
$ git branch -u <origin>/<branch> <branch>
# else:
$ git checkout -b <branch> --track <origin>/<branch>
  • 원인 : 부모 저장소가 하위 모듈 분기를 추적하도록 구성되어 있지 않습니다.
    원 원인 인 및 해해 결결 방 방법 법 : 다음 두 명령으로 새 하위 모듈을 추가하여 하위 모듈이 원격 분기를 추적하도록하십시오.
    • 먼저 git에게 리모컨을 추적하도록 지시하십시오 <branch>.
    • 체크 아웃 대신 리베이스 또는 병합을 수행하도록 git에 지시합니다.
    • git에게 원격에서 서브 모듈을 업데이트하도록 지시합니다.
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ git config -f .gitmodules submodule.<submodule-path>.update rebase
    $ git submodule update --remote
  • 이와 같은 기존 서브 모듈을 추가하지 않은 경우 쉽게 수정할 수 있습니다.
    • 먼저 서브 모듈에 추적하려는 분기가 체크 아웃되어 있는지 확인하십시오.
    $ cd <submodule-path>
    $ git checkout <branch>
    $ cd <parent-repo-path>
    # <submodule-path> is here path releative to parent repo root
    # without starting path separator
    $ git config -f .gitmodules submodule.<submodule-path>.branch <branch>
    $ git config -f .gitmodules submodule.<submodule-path>.update <rebase|merge>

일반적인 경우, 위의 구성 문제 중 하나와 관련되어 있으므로 DETACHED HEAD가 이미 수정되었습니다.

DETACHED HEAD를 고칠 때 .update = checkout

$ cd <submodule-path> # and make modification to your submodule
$ git add .
$ git commit -m"Your modification" # Let's say you forgot to push it to remote.
$ cd <parent-repo-path>
$ git status # you will get
Your branch is up-to-date with '<origin>/<branch>'.
Changes not staged for commit:
    modified:   path/to/submodule (new commits)
# As normally you would commit new commit hash to your parent repo
$ git add -A
$ git commit -m"Updated submodule"
$ git push <origin> <branch>.
$ git status
Your branch is up-to-date with '<origin>/<branch>'.
nothing to commit, working directory clean
# If you now update your submodule
$ git submodule update --remote
Submodule path 'path/to/submodule': checked out 'commit-hash'
$ git status # will show again that (submodule has new commits)
$ cd <submodule-path>
$ git status
HEAD detached at <hash>
# as you see you are DETACHED and you are lucky if you found out now
# since at this point you just asked git to update your submodule
# from remote master which is 1 commit behind your local branch
# since you did not push you submodule chage commit to remote. 
# Here you can fix it simply by. (in submodules path)
$ git checkout <branch>
$ git push <origin>/<branch>
# which will fix the states for both submodule and parent since 
# you told already parent repo which is the submodules commit hash 
# to track so you don't see it anymore as untracked.

그러나 하위 모듈에 대해 이미 로컬로 변경하고 커밋 한 경우 'git checkout'을 실행할 때 원격으로 푸시하면 Git이 알려줍니다.

$ git checkout <branch>
Warning: you are leaving 1 commit behind, not connected to any of your branches:
If you want to keep it by creating a new branch, this may be a good time to do so with:

임시 지점을 만드는 권장 옵션이 좋을 수 있습니다. 그런 다음 이러한 지점 등을 병합하면됩니다. 그러나 개인적 git cherry-pick <hash>으로이 경우 에만 사용 합니다.

$ git cherry-pick <hash> # hash which git showed you related to DETACHED HEAD
# if you get 'error: could not apply...' run mergetool and fix conflicts
$ git mergetool
$ git status # since your modifications are staged just remove untracked junk files
$ rm -rf <untracked junk file(s)>
$ git commit # without arguments
# which should open for you commit message from DETACHED HEAD
# just save it or modify the message.
$ git push <origin> <branch>
$ cd <parent-repo-path>
$ git add -A # or just the unstaged submodule
$ git commit -m"Updated <submodule>"
$ git push <origin> <branch>

하위 모듈을 DETACHED HEAD 상태로 전환 할 수있는 경우가 더 있지만 특정 사례를 디버깅하는 방법을 조금 더 이해하기를 바랍니다.


2
HEAD detached는 기본 동작입니다 git submodule update --remote. Simba의 답변을 살펴보십시오. 정답이라고 생각합니다.
magomar

78

branch옵션 을 추가하는 것은 서브 모듈의 분리 된 동작과 전혀 관련.gitmodule없습니다 . @mkungla의 이전 답변이 잘못되었거나 더 이상 사용되지 않습니다.

에서 git submodule --help, HEAD가 분리하면 기본 동작git submodule update --remote.

먼저 추적 할 분기를 지정할 필요가 없습니다 . origin/master추적 할 기본 분기입니다.

--먼

수퍼 프로젝트의 기록 된 SHA-1을 사용하여 서브 모듈을 업데이트하는 대신 서브 모듈의 원격 추적 브랜치 상태를 사용하십시오. 사용되는 리모컨은 지점의 리모컨 ( branch.<name>.remote)이며 기본값은origin 입니다. 사용 된 원격 분기의 기본값은master 입니다.

그렇다면 왜 HEAD가 분리 update됩니까? 이는 기본 모듈 업데이트 동작으로checkout 인해 발생합니다 .

--점검

서브 모듈에서 분리 된 HEAD 에서 수퍼 프로젝트에 기록 된 커밋 을 확인하십시오. 이것이 기본 동작 이며,이 옵션의 주된 용도는 submodule.$name.update다른 값으로 설정 될 때 재정의 하는 것 checkout입니다.

이 이상한 업데이트 동작을 설명하려면 하위 모듈의 작동 방식을 이해해야합니까?

Pro Git 에서 서브 모듈로 시작하여 인용

sbmodule DbConnector은 작업 디렉토리의 하위 디렉토리 이지만 Git 은 하위 디렉토리로 간주하고 해당 디렉토리에 없을 때 내용을 추적하지 않습니다. 대신 Git 은 해당 저장소특정 커밋 으로 간주 합니다 .

메인 리포지토리 는 특정 시점커밋 ID 에서 서브 모듈 의 상태를 추적합니다 . 따라서 모듈을 업데이트하면 커밋 ID를 새 것으로 업데이트합니다.

어떻게

하위 모듈을 원격 지점과 자동으로 병합하려면 --merge또는을 사용하십시오 --rebase.

-병합

이 옵션은 update 명령 에만 유효합니다 . 수퍼 프로젝트에 기록 된 커밋을 서브 모듈의 현재 브랜치에 병합하십시오. 이 옵션을 지정하면 서브 모듈의 HEAD가 분리되지 않습니다 .

-리베이스

현재 브랜치를 수퍼 프로젝트에 기록 된 커밋으로 리베이스하십시오. 이 옵션을 지정하면 서브 모듈의 HEAD가 분리되지 않습니다 .

당신이해야 할 일은

git submodule update --remote --merge
# or
git submodule update --remote --rebase

추천 별칭 :

git config alias.supdate 'submodule update --remote --merge'

# do submodule update with
git supdate

만드는 옵션도 있습니다 --merge또는 --rebase의 기본 동작으로 git submodule update설정하여, submodule.$name.updatemerge또는 rebase.

하위 모듈 업데이트의 기본 업데이트 동작을 구성하는 방법에 대한 예는 다음과 같습니다 .gitmodule.

[submodule "bash/plugins/dircolors-solarized"]
    path = bash/plugins/dircolors-solarized
    url = https://github.com/seebi/dircolors-solarized.git
    update = merge # <-- this is what you need to add

또는 명령 행에서 구성하십시오.

# replace $name with a real submodule name
git config -f .gitmodules submodule.$name.update merge

참고 문헌


6
사용 git submodule update --remote --merge하고 하위 모듈을 분리 된 상태로 풀다운합니다. --rebase같은 결과로 시도 했습니다.
Joe Strout

8
@JoeStrout 서브 모듈이 이미 분리 된 경우 위 명령으로 업데이트하기 전에 분리 된 상태를 수정하십시오. cd을 사용하여 하위 모듈을 특정 분기에 체크 아웃하십시오 git checkout master.
Simba

2
또는 여러 개의 (재귀적인) 서브 모듈에 너무 많은 번거 로움이 있다면 간단히하라 git submodule foreach --recursive git checkout master.
stefanct

1
나는 "git의 작동 방식"설명에 부분적으로 만 이해한다. TBH git의 작동 방식을 이해하는 데 관심이 없으며 사용하고 싶습니다. 이제 분리 된 하위 모듈을로 고정 할 수 있음을 이해합니다 git submodule foreach --recursive git checkout master. 그러나 자식이 항상 분리하지 못하게하려면 어떻게해야합니까? 각 하위 모듈에 대한 구성 옵션을 설정 하는 것은 옵션이 아닙니다!
Nicolas

나를 위해 실행 git submodule update --remote --merge하면 하위 모듈이 분리 된 HEAD 상태로 유지되지 않았지만 DID가 표시 한 것처럼 파일을 git submodule update편집 한 후 실행 .gitmodule하면 하위 모듈이 분리 된 HEAD 상태로 유지됩니다.
bweber13

41

나는 항상 분리하는 것에 지쳤으므로 모든 모듈에 대해 셸 스크립트를 사용합니다. 나는 모든 서브 모듈이 마스터에 있다고 가정합니다 : 여기 스크립트가 있습니다 :

#!/bin/bash
echo "Good Day Friend, building all submodules while checking out from MASTER branch."

git submodule update 
git submodule foreach git checkout master 
git submodule foreach git pull origin master 

부모 모듈에서 실행하십시오.


2
git submodule foreach git pull origin
master-

간단하고 간결합니다! 감사!
zhekaus

12

내 대답을 확인하십시오 : Git 하위 모듈 : 지점 / 태그 지정

원하는 경우 "branch = master"줄을 .gitmodules 파일에 수동으로 추가 할 수 있습니다. 의미를 알아 보려면 링크를 읽으십시오.

편집 : 지점에서 기존 서브 모듈 프로젝트를 추적하려면 대신 VonC의 지침을 따르십시오.

자식 서브 모듈 : 브랜치 / 태그 지정


14
답변은 인라인 인 것으로 가정합니다. IIRC를 답변에 연결하는 것은 스택 오버플로 가짜 pas입니다.
Tony Topper

1
@TonyTopper 다른 SO 답변에 연결하는 경우에도? IIRC는 외부 링크만이 눈에 띄지 않게됩니다. 링크가 사라지면 링크가 죽었고 대답은 쓸모가 없습니다. 그러나 SO 답변에는 그러한 위험이 없으므로 SO가 사라지지 않는 한 사라지지 않습니다 (무슨 일이 있어도 복원 할 수 있음). 또한 그는 branch = master" line into your .gitmodule전체 답변 과 마찬가지로 그 문제에 대한 답을 얻었습니다.
Mecki

9

서브 모듈이 브랜치를 체크 아웃하게하는 다른 방법 .gitmodules은 루트 폴더에있는 파일 로 이동하여 branch다음과 같이 모듈 구성에 필드 를 추가하는 것입니다 .

branch = <branch-name-you-want-module-to-checkout>


15
나를 위해 이것은 작동하지 않습니다. 올바르게 설정했습니다 branch = my_wanted_branch. 그러나 git submodule update --remote그것을 실행 하면 여전히 분리 된 헤드로 확인됩니다.
Andrius

이렇게하면 cd sudmodule & git co thebranche & cd .., git submodule update --remote 그리고 작동합니다!
pdem

슈퍼 프로젝트가 서브 모듈-재귀 적 방식으로 복제되거나 서브 모듈이 초기화되는 동안에 만 '.gitmodules'가 활발하게 사용되고 있습니까 (읽고 있는가) 그렇지 않습니까? 즉, 자체 저장소는 '.gitmodules'에 배치 된 하위 모듈 구성 업데이트에서 항상 이익이되는 파일을 업데이트합니다. 내 이해에서 '.gitmodules'는 repo가 ​​복제되는 동안 생성 된 구성의 템플릿입니다.
Na13-c

3

다른 사람들이 말했듯이, 이것이 발생하는 이유는 부모 저장소에 하위 모듈의 특정 커밋에 대한 참조 (SHA1) 만 포함하기 때문에 분기에 대해 아무것도 알지 못하기 때문입니다. 이것이 작동하는 방식입니다. 커밋에 있던 브랜치가 앞으로 (또는 뒤로) 움직일 수 있으며, 부모 리포지토리가 브랜치를 참조하면 브랜치가 발생할 때 쉽게 깨질 수 있습니다.

그러나 특히 상위 리포지토리와 하위 모듈에서 적극적으로 개발하는 경우 detached HEAD상태가 혼란스럽고 잠재적으로 위험 할 수 있습니다. 서브 모듈이 커밋 된 detached HEAD상태 에서 커밋을 수행하면 서브 모듈 이 매달려있어 작업을 쉽게 잃을 수 있습니다. (매달려 커밋은 일반적으로을 사용하여 구제 할 수 git reflog있지만 처음에는 피하는 것이 훨씬 좋습니다.)

저와 같은 경우 서브 모듈에 커밋이 체크 아웃되었음을 나타내는 분기가있는 경우 대부분 동일한 커밋에서 분리 된 HEAD 상태가 아닌 해당 분기를 체크 아웃합니다. gitconfig파일에 다음 별명을 추가하여이를 수행 할 수 있습니다.

[alias]
    submodule-checkout-branch = "!f() { git submodule -q foreach 'branch=$(git branch --no-column --format=\"%(refname:short)\" --points-at `git rev-parse HEAD` | grep -v \"HEAD detached\" | head -1); if [[ ! -z $branch && -z `git symbolic-ref --short -q HEAD` ]]; then git checkout -q \"$branch\"; fi'; }; f"

이제는 git submodule update을 호출 git submodule-checkout-branch하면됩니다. 분기를 가리키는 커밋에서 체크 아웃 된 하위 모듈은 해당 분기를 체크 아웃합니다. 동일한 커밋을 가리키는 로컬 브랜치가 여러 개없는 경우가 많으며 일반적으로 원하는 것을 수행합니다. 그렇지 않다면, 적어도 커밋하는 것이 커밋되지 않고 실제 브랜치로 이동하게합니다.

또한 체크 아웃시 서브 모듈을 자동으로 업데이트하도록 git을 설정 한 경우 ( git config --global submodule.recurse true, this answer 참조 )이 별칭을 자동으로 호출하는 체크 아웃 후 후크를 만들 수 있습니다.

$ cat .git/hooks/post-checkout 
#!/bin/sh
git submodule-checkout-branch

그럼 당신은 전화 중 하나를 할 필요가 없습니다 git submodule update또는 git submodule-checkout-branch단지하고, git checkout각각의 커밋 모든 서브 모듈을 업데이트하고 (있는 경우) 해당 지점을 확인합니다.


0

가장 간단한 해결책은 다음과 같습니다.

git clone --recursive git@github.com:name/repo.git

그런 다음 repo 디렉토리에서 cd하고 다음을 수행하십시오.

git submodule update --init
git submodule foreach -q --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'
git config --global status.submoduleSummary true

추가 자료 : Git 서브 모듈 모범 사례 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.