Warum ist mein Git Submodule HEAD vom Master getrennt?

162

Ich benutze Git-Submodule. Nach dem Abrufen von Änderungen vom Server wird mein Submodulkopf häufig vom Hauptzweig getrennt.

Warum passiert das?

Ich muss immer tun:

git branch
git checkout master

Wie kann ich sicherstellen, dass mein Submodul immer auf den Hauptzweig zeigt?

om471987
quelle
1
Hast du diese Antwort gelesen? stackoverflow.com/questions/1777854/…
Johnny Z
@bitoiu Ich habe mir Teilbaum und Google Repo angesehen. Ich habe noch keine perfekte Lösung :(
om471987
1
Meine Erfahrung mit Gitsubmodulen in einer CI-Umgebung ist schrecklich, vielleicht haben einige andere Leute bessere Erfahrungen.
Bitoiu
@ JohnnyZ Danke. Ich habe verstanden, dass das Submodul auf ein Commit und nicht auf den Kopf des Baums verweist. Aber warum vom Ast losgelöst. Wenn Sie einen Zweig haben, sollte dieser nicht standardmäßig angehängt werden
om471987
3
Seien Sie nicht zu schnell, um Submodule zu schließen, nur weil Sie gehört haben, dass sie schlecht sind. Sie sind eine schlechte Lösung, wenn Sie eine kontinuierliche Integration wünschen, aber sie sind eine nahezu perfekte Lösung, wenn Sie Code aus einem externen Projekt einbetten möchten und alle Pulls explizit verwalten. Dies ist häufig die beste Vorgehensweise, wenn Sie in ein nicht gegabeltes Modul integrieren, das nicht von Ihrer Organisation gesteuert wird. Das Problem ist, dass sie eine verlockende Lösung in allen möglichen anderen Situationen sind, in denen sie überhaupt nicht sehr gut funktionieren. Der beste Rat ist, sich über deren Funktionsweise zu informieren und Ihr Szenario zu bewerten.
Sarah G

Antworten:

176

BEARBEITEN:

Eine gültige Lösung finden Sie unter @ Simba- Antwort

submodule.<name>.updateist das, was Sie ändern möchten. Weitere Informationen finden Sie in den Dokumenten - Standardmäßig wirdcheckout
submodule.<name>.branch der zu verfolgende Remote-Zweig angegeben - Standardmaster


ALTE ANTWORT:

Persönlich hasse ich Antworten hier, die auf externe Links verweisen, die im Laufe der Zeit möglicherweise nicht mehr funktionieren, und überprüfe meine Antwort hier (es sei denn, die Frage ist doppelt vorhanden) - auf Fragen, die das Thema zwischen den Zeilen eines anderen Themas abdecken, aber insgesamt gleich sind: "Ich bin nicht antworten, lesen Sie die Dokumentation. "

Zurück zur Frage: Warum passiert das?

Situation, die Sie beschrieben haben

Nach dem Abrufen von Änderungen vom Server wird mein Submodulkopf häufig vom Hauptzweig getrennt.

Dies ist ein häufiger Fall, wenn man Submodule nicht zu oft verwendet oder gerade mit Submodulen begonnen hat . Ich glaube, dass ich zu Recht sage, dass wir alle irgendwann dort waren, wo sich der KOPF unseres Submoduls löst.

  • Ursache: Ihr Submodul verfolgt nicht den richtigen Zweig (Standardmaster).
    Lösung: Stellen Sie sicher, dass Ihr Submodul den richtigen Zweig verfolgt
$ 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>
  • Ursache: Ihr übergeordnetes Repo ist nicht für die Verfolgung des Submodulzweigs konfiguriert.
    Lösung: Lassen Sie Ihr Submodul seinen Remote-Zweig verfolgen, indem Sie mit den folgenden beiden Befehlen neue Submodule hinzufügen.
    • Zuerst sagst du git, dass er deine Fernbedienung verfolgen soll <branch>.
    • Sie weisen git an, Rebase oder Merge anstelle von Checkout durchzuführen
    • Sie weisen git an, Ihr Submodul von remote zu aktualisieren.
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ git config -f .gitmodules submodule.<submodule-path>.update rebase
    $ git submodule update --remote
  • Wenn Sie Ihr vorhandenes Submodul nicht wie folgt hinzugefügt haben, können Sie dies leicht beheben:
    • Zuerst möchten Sie sicherstellen, dass in Ihrem Submodul der Zweig ausgecheckt ist, den Sie verfolgen möchten.
    $ 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>

In den meisten Fällen haben Sie Ihren DETACHED HEAD bereits behoben, da er mit einem der oben genannten Konfigurationsprobleme zusammenhängt.

Befestigen des abgenommenen Kopfes wenn .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.

Wenn Sie es jedoch geschafft haben, einige Änderungen lokal bereits für das Submodul vorzunehmen und diese festzuschreiben, haben Sie diese auf remote verschoben. Wenn Sie dann 'git checkout' ausgeführt haben, benachrichtigt Git Sie:

$ 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:

Die empfohlene Option zum Erstellen eines temporären Zweigs kann gut sein, und dann können Sie diese Zweige usw. einfach zusammenführen. Ich persönlich würde sie jedoch nur git cherry-pick <hash>in diesem Fall verwenden.

$ 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>

Obwohl es noch einige weitere Fälle gibt, in denen Sie Ihre Submodule in den Status DETACHED HEAD versetzen können, hoffe ich, dass Sie jetzt ein bisschen mehr verstehen, wie Sie Ihren speziellen Fall debuggen können.

mkungla
quelle
2
HEAD distanziert ist das Standardverhalten von git submodule update --remote. Bitte schauen Sie sich Simbas Antwort an. Ich denke, das sollte die richtige Antwort sein.
Magomar
77

Das Hinzufügen einer branchOption in .gitmodulehängt NICHT mit dem getrennten Verhalten von Submodulen zusammen. Die alte Antwort von @mkungla ist falsch oder veraltet.

Von git submodule --help, HEAD getrennt ist das Standardverhalten von git submodule update --remote.

Erstens muss kein zu verfolgender Zweig angegeben werden . origin/masterist der Standardzweig, der verfolgt werden soll.

--Fernbedienung

Verwenden Sie den Status des Fernverfolgungszweigs des Submoduls, anstatt den aufgezeichneten SHA-1 des Superprojekts zum Aktualisieren des Submoduls zu verwenden. Die verwendete Fernbedienung ist die Fernbedienung remote ( branch.<name>.remote) ( standardmäßig)origin . Der verwendete Remote-Zweig verwendet standardmäßigmaster .

Warum

Warum wird HEAD danach abgetrennt update? Dies wird durch das Standardverhalten der Modulaktualisierungcheckout verursacht : .

--Auschecken

Überprüfen Sie das im Superprojekt aufgezeichnete Commit auf einem abgetrennten HEAD im Submodul. Dies ist das Standardverhalten. Diese Option wird hauptsächlich zum Überschreiben verwendet, submodule.$name.updatewenn ein anderer Wert als festgelegt wird checkout.

Um dieses seltsame Update-Verhalten zu erklären, müssen wir verstehen, wie Submodule funktionieren.

Zitat aus Beginn mit Submodulen in Buch Pro Git

Obwohl sbmodule DbConnectorein Unterverzeichnis in Ihrem Arbeitsverzeichnis ist, sieht Git es als Submodul und verfolgt seinen Inhalt nicht, wenn Sie sich nicht in diesem Verzeichnis befinden. Stattdessen sieht Git es als ein bestimmtes Commit aus diesem Repository an .

Das Haupt-Repo verfolgt das Submodul mit seinem Status an einem bestimmten Punkt , der Commit-ID . Wenn Sie also Module aktualisieren, aktualisieren Sie die Festschreibungs-ID auf eine neue.

Wie

Wenn Sie möchten, dass das Submodul automatisch mit dem Remote-Zweig zusammengeführt wird, verwenden Sie --mergeoder --rebase.

--verschmelzen

Diese Option ist nur für den Aktualisierungsbefehl gültig . Führen Sie das im Superprojekt aufgezeichnete Commit in den aktuellen Zweig des Submoduls ein. Wenn diese Option angegeben ist, wird der HEAD des Submoduls nicht getrennt .

--rebase

Setzen Sie den aktuellen Zweig auf das im Superprojekt aufgezeichnete Commit zurück. Wenn diese Option angegeben ist, wird der HEAD des Submoduls nicht getrennt .

Alles was Sie tun müssen ist,

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

Empfohlener Alias:

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

# do submodule update with
git supdate

Es gibt auch eine Option, um --mergeoder --rebaseals Standardverhalten von git submodule update, indem Sie submodule.$name.updateauf mergeoder rebase.

Hier ist ein Beispiel zum Konfigurieren des Standardaktualisierungsverhaltens der Submodulaktualisierung in .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

Oder konfigurieren Sie es in der Befehlszeile,

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

Verweise

Simba
quelle
6
Ich benutze git submodule update --remote --merge, und es zieht das Submodul in einem getrennten Zustand herunter. Auch --rebasemit dem gleichen Ergebnis versucht .
Joe Strout
8
@JoeStrout Wenn Ihr Submodul bereits getrennt wurde, korrigieren Sie den getrennten Status, bevor Sie ein Update mit den oben genannten Befehlen durchführen. cdIn das Submodul auschecken Sie das Submodul zu einem bestimmten Zweig mit , git checkout master.
Simba
2
Oder - wenn dies für mehrere (rekursive) Submodule zu mühsam ist - einfach tun git submodule foreach --recursive git checkout master.
stefanct
1
Ich verstehe die Beschreibungen "wie Git funktioniert" nur teilweise. TBH Ich bin nicht wirklich daran interessiert zu verstehen, wie Git funktioniert, ich möchte es nur verwenden. Jetzt verstehe ich, dass ich abgetrennte Submodule mit reparieren kann git submodule foreach --recursive git checkout master. Aber wie kann ich verhindern, dass Git sie immer ablöst? Das Festlegen von Konfigurationsoptionen für jedes Submodul ist keine Option!
Nicolas
Für mich hat das Ausführen git submodule update --remote --mergedas Submodul nicht in einem getrennten HEAD-Zustand belassen, sondern git submodule updatenach dem Bearbeiten meiner .gitmoduleDatei, wie Sie angegeben haben, das Submodul in einem getrennten HEAD-Zustand belassen.
bweber13
41

Ich habe es satt, mich immer zu lösen, also benutze ich einfach ein Shell-Skript, um es für alle meine Module zu erstellen. Ich gehe davon aus, dass alle Submodule auf Master sind: Hier ist das Skript:

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

Führen Sie es von Ihrem übergeordneten Modul aus

j2emanue
quelle
2
Git Submodul für jeden Git Pull Origin Master - das ist, wonach ich gesucht habe .. Kudos
Csomakk
einfach und prägnant! Vielen Dank!
Zhekaus
12

Schauen Sie sich meine Antwort hier an: Git-Submodule: Geben Sie einen Zweig / Tag an

Wenn Sie möchten, können Sie die Zeile "branch = master" manuell in Ihre .gitmodules-Datei einfügen. Lesen Sie den Link, um zu sehen, was ich meine.

BEARBEITEN: Um ein vorhandenes Submodulprojekt in einer Zweigstelle zu verfolgen, befolgen Sie stattdessen die Anweisungen von VonC hier:

Git-Submodule: Geben Sie einen Zweig / ein Tag an

Johnny Z.
quelle
14
Es wird angenommen, dass die Antworten inline sind. Die IIRC-Verknüpfung mit Antworten ist ein Stack Overflow-Fauxpas.
Tony Topper
1
@ TonyTopper Auch wenn Sie nur auf eine andere SO-Antwort verlinken? IIRC nur externe Links sind verpönt, da diese möglicherweise verschwinden und der Link dann tot ist und die Antwort nutzlos ist. Bei SO-Antworten besteht jedoch keine solche Gefahr, sie werden niemals verschwinden, es sei denn, SO verschwindet (und kann wiederhergestellt werden, egal was passiert). Auch er hat die Frage beantwortet, da branch = master" line into your .gitmoduletatsächlich die vollständige Antwort dieses Problem für mich gelöst hat.
Mecki
9

Die andere Möglichkeit, Ihr Submodul zum Auschecken des Zweigs zu veranlassen, besteht darin, die .gitmodulesDatei im Stammordner abzurufen und das Feld branchin der Modulkonfiguration wie folgt hinzuzufügen :

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

Frontendgirl
quelle
15
Bei mir funktioniert das nicht. Ich habe richtig eingestellt branch = my_wanted_branch. Aber git submodule update --remotewenn man es laufen lässt, wird es immer noch als losgelöster Kopf ausgecheckt.
Andrius
Tun Sie dies, dann cd sudmodule & git co thebranche & cd .., dann git submodule update --remote und es funktioniert!
pdem
Ist es nicht so, dass '.gitmodules' nur aktiv verwendet wird (gelesen wird), während das Superprojekt auf submodul-rekursive Weise geklont oder das Submodul initialisiert wird? Mit anderen Worten, Sie aktualisieren in Ihrem eigenen Repository die Datei, in der nicht immer von Submodul-Konfigurationsaktualisierungen profitiert wird, die auf '.gitmodules' abgelegt werden. Nach meinem Verständnis ist '.gitmodules' eine Vorlage für die Konfiguration, die erstellt wird, während das Repo geklont wird.
Na13-c
3

Wie andere Leute gesagt haben, ist der Grund dafür, dass das übergeordnete Repo nur einen Verweis auf (den SHA1 von) einem bestimmten Commit im Submodul enthält - es weiß nichts über Zweige. So sollte es funktionieren: Der Zweig, der sich bei diesem Commit befand, hat sich möglicherweise vorwärts (oder rückwärts) bewegt, und wenn das übergeordnete Repo auf den Zweig verwiesen hat, kann er in diesem Fall leicht brechen.

Insbesondere wenn Sie sich sowohl im übergeordneten Repo als auch im Submodul aktiv entwickeln, detached HEADkann der Status verwirrend und potenziell gefährlich sein. Wenn Sie im Submodul Commits ausführen, während es sich im detached HEADStatus befindet, baumeln diese und Sie können leicht Ihre Arbeit verlieren. (Dangling Commits können normalerweise mit gerettet werden git reflog, aber es ist viel besser, sie zuerst zu vermeiden.)

Wenn Sie wie ich sind, sollten Sie die meiste Zeit, wenn sich im Submodul ein Zweig befindet, der auf das Auschecken des Commits verweist, diesen Zweig lieber auschecken, als sich beim gleichen Commit im getrennten HEAD-Status zu befinden. Sie können dies tun, indem Sie Ihrer gitconfigDatei den folgenden Alias ​​hinzufügen :

[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"

Nachdem git submodule updateSie dies getan haben, müssen Sie nur noch aufrufen git submodule-checkout-branch, und jedes Submodul, das bei einem Commit ausgecheckt wird, auf das ein Zweig zeigt, wird diesen Zweig auschecken. Wenn Sie nicht oft mehrere lokale Zweige haben, die alle auf dasselbe Commit verweisen, wird dies normalerweise das tun, was Sie wollen. Wenn nicht, wird zumindest sichergestellt, dass alle von Ihnen getätigten Commits auf einen tatsächlichen Zweig übertragen werden, anstatt hängen zu bleiben.

Wenn Sie git so eingerichtet haben, dass Submodule beim Auschecken automatisch aktualisiert werden ( git config --global submodule.recurse truesiehe diese Antwort ), können Sie einen Post-Checkout-Hook erstellen, der diesen Alias ​​automatisch aufruft:

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

Dann müssen Sie entweder nicht aufrufen git submodule updateoder aktualisieren git submodule-checkout-brancheinfach git checkoutalle Submodule auf ihre jeweiligen Commits und überprüfen die entsprechenden Zweige (falls vorhanden).

Deltacrux
quelle
0

Die einfachste Lösung ist:

git clone --recursive [email protected]:name/repo.git

Dann CD im Repo-Verzeichnis und:

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

Zusätzliche Lektüre: Best Practices für Git-Submodule .

noccoa0
quelle