web-dev-qa-db-ja.com

GITサブモジュールHEADがマスターから切り離されるのはなぜですか?

GITサブモジュールを使用しています。サーバーから変更をプルした後、サブモジュールヘッドが何度もmasterブランチから切り離されます。

なぜ起こるのですか?

私はいつもしなければなりません:

git branch
git checkout master

サブモジュールが常にmasterブランチを指していることを確認するにはどうすればよいですか?

122
om471987

個人的には、時間の経過とともに動作を停止する可能性のある外部リンクに向かう回答を嫌い、私の回答を確認します here(質問が重複していない限り)-他の主題の行、しかし全体的に等しい:「私は答えていません、ドキュメントを読んでください。」

では、質問に戻ります:なぜ起こるのですか?

あなたが説明した状況

サーバーから変更をプルした後、サブモジュールヘッドが何度もmasterブランチから切り離されます。

これは、submodulesをあまり頻繁に使用しない場合、またはsubmodulesで開始したばかりの場合の一般的なケースです。私は、私たち全員サブモジュール 's 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>
  • 原因:親リポジトリがサブモジュールブランチを追跡するように構成されていません。解決策:次の2つのコマンドで新しいサブモジュールを追加して、サブモジュールにリモートブランチを追跡させます。
    • まず、リモート<branch>を追跡するようgitに指示します。
    • 次に、リモートからサブモジュールを更新するようにgitに指示します。
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ 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>

ただし、正しいブランチを追跡するようにサブモジュールを設定した場合でも、サブモジュールがHEAD detached at <commit-hash>を取得する状況で自分自身を見つけることができます

よくあるケースでは、上記の設定の問題の1つに関連していたため、DETACHED HEADはすでに修正されています。ただし、サブモジュールが独自のリモートブランチを追跡しているため、障害の新たな可能性が開かれるため、親リポジトリはサブモジュールの状態を管理しなくなっています(親リポジトリにコミットされたコミットハッシュを使用)。

親およびサブモジュールのパスで$ git statusを実行して、すべてが正しく追跡され、すべてが最新であることを確認してから、$ cd <parent-repo>およびgit submodule update --remoteを実行します。ご覧のとおり、もう一度git statusを実行すると、今のところすべて問題ありません。

すべてが正しく構成されているように見え、DETACHED HEADの問題が発生する可能性がない場合に、次のことを見てみましょう。

$ 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状態にできるケースがいくつかありますが、特定のケースをデバッグする方法についてもう少し理解していただければ幸いです。

168
mkungla

常にデタッチするのにうんざりしているので、シェルスクリプトを使用してすべてのモジュール用にビルドします。私はすべてのサブモジュールがマスター上にあると仮定します:ここにスクリプトがあります:

#!/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 

親モジュールから実行する

27
j2emanue

.gitmodulebranchオプションを追加すると、サブモジュールの切り離された動作に関連なしになります。 @mkunglaがあなたに言ったことはナンセンスです。

git submodule --helpから、HEADデタッチはデフォルトの動作です of git submodule update --remote

まず、追跡するブランチを指定する必要がないがあります。 Origin/masterは、追跡されるデフォルトのブランチです。

-リモート

スーパープロジェクトの記録されたSHA-1を使用してサブモジュールを更新する代わりに、サブモジュールのリモート追跡ブランチのステータスを使用します。使用されるリモートはブランチのリモート(branch.<name>.remote)、デフォルトはOriginです。リモートブランチはデフォルトはmasterを使用しました。

なぜ

では、なぜupdateの後にHEADが切り離されるのでしょうか? submodule.$name.updateのデフォルトの動作はcheckoutであるためです。

- チェックアウト

サブモジュールのdetached HEADでスーパープロジェクトに記録されたコミットをチェックアウトします。これはデフォルトの動作です。このオプションの主な用途は、checkout以外の値に設定されたときにsubmodule.$name.updateをオーバーライドすることです。

つまり、デフォルトでは特定のコミットIDをチェックアウトしています。明らかに、HEADが切り離されます。

どうやって

サブモジュールをリモートブランチに自動的にマージする場合は、--mergeまたは--rebaseを使用します。

- マージ

このオプションは更新にのみ有効コマンドです。スーパープロジェクトに記録されたコミットをサブモジュールの現在のブランチにマージします。このオプションが指定された場合、サブモジュールのHEADは切り離されないになります。

-リベース

現在のブランチを、スーパープロジェクトに記録されているコミットにリベースします。このオプションが指定された場合、サブモジュールのHEADは切り離されないになります。

あなたがする必要があるのは、

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

--mergemergeまたはrebaseに設定することにより、--rebaseまたはgit submodule updatesubmodule.$name.updateのデフォルト動作として作成するオプションもあります。

.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

私の答えはすべてマニュアルに基づいています。 git submodule --help

15
Simba

ここで私の答えを確認してください: Gitサブモジュール:ブランチ/タグを指定

必要に応じて、「branch = master」行を.gitmodulesファイルに手動で追加できます。リンクを読んで、意味を確認してください。

編集:ブランチで既存のサブモジュールプロジェクトを追跡するには、代わりにVonCの指示に従ってください:

Gitサブモジュール:ブランチ/タグを指定

10
Johnny Z

サブモジュールを作成してブランチをチェックアウトする別の方法は、ルートフォルダーの.gitmodulesファイルに移動し、次のようにモジュール構成にbranchフィールドを追加することです。

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

8
frontendgirl

他の人が言ったように、これが起こる理由は、親リポジトリがサブモジュール内の特定のコミット(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を使用、 この回答 を参照)、このエイリアスを自動的に呼び出すチェックアウト後フックを作成できます。

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

次に、git submodule updateまたはgit submodule-checkout-branchを呼び出す必要はありません。git checkoutを実行するだけで、すべてのサブモジュールがそれぞれのコミットに更新され、対応するブランチ(存在する場合)がチェックアウトされます。

0
deltacrux