Git Submodule HEAD'ım neden master'dan ayrıldı?


163

Git alt modüllerini kullanıyorum. Sunucudan değişiklikleri aldıktan sonra, çoğu zaman alt modül kafam ana daldan ayrılır.

Neden oluyor?

Her zaman yapmam gerek:

git branch
git checkout master

Alt modülümün daima ana dalı işaret ettiğinden nasıl emin olabilirim?



@bitoiu Alt ağaçlara ve Google Repo'ya baktım. Henüz mükemmel bir çözüm
bulamadım

1
CI ortamında gitsubmodüller ile yaşadığım deneyim korkunç, belki bazı insanlar daha iyi deneyimlere sahip.
bitoiu

@JohnnyZ Teşekkürler. Alt modülün, ağacın başına değil bir taahhüdüne işaret ettiğini anladım. Ama neden şubeden ayrıldım. Bir şubeniz varsa, varsayılan olarak ona
eklenmemelidir

3
Sadece kötü olduklarını duyduğunuz için alt modülleri kapatmak için çok hızlı olmayın. Sürekli entegrasyon istiyorsanız zayıf bir çözümdür, ancak harici bir projeden kod gömmek istiyorsanız ve tüm çekimleri açıkça yönetiyorsanız, neredeyse mükemmel bir çözümdür. Kuruluşunuz tarafından kontrol edilmeyen çatalsız bir modülle entegre ediyorsanız, bu genellikle en iyi uygulamadır. Sorun şu ki, çok iyi çalışmadığı her türlü durumda cazip bir çözümdürler. En iyi tavsiye, nasıl çalıştıklarını okumak ve senaryonuzu değerlendirmektir.
Sarah G

Yanıtlar:


176

DÜZENLE:

Bkz @Simba Cevap geçerli çözüm için

submodule.<name>.updatedeğiştirmek istediğiniz dokümanlardır - varsayılancheckout
submodule.<name>.branch olarak izlenecek uzak dalı belirtin - varsayılanmaster


ESKİ CEVAP:

Şahsen burada, zaman içinde çalışmayı durdurabilecek ve cevabımı burada kontrol edebilecek dış bağlantılara yönlendiren cevaplardan nefret ediyorum (soru yinelenmediği sürece) - diğer konunun satırları arasındaki konuyu kapsayan, ancak genel olarak eşit olan soruya yönlendirme: "Ben yanıt vermiyorsa belgeleri okuyun. "

Soruya geri dönelim: Neden oluyor?

Tanımladığınız durum

Sunucudan değişiklikleri aldıktan sonra, çoğu zaman alt modül kafam ana daldan ayrılır.

Bu, alt modüllerin çok sık kullanılmadığı veya henüz alt modüller ile başladığı yaygın bir durumdur . İnanıyorum ki doğru söylüyorum, hepimiz alt modülümüzün KAFASININ ayrıldığı bir noktada bulunduk.

  • Neden: Alt modülünüz doğru dalı izlemiyor (varsayılan ana).
    Çözüm: Alt modülünüzün doğru dalı izlediğinden emin olun
$ 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>
  • Neden: Ana deponuz alt modül dalını izlemek için yapılandırılmamış.
    Çözüm: Aşağıdaki iki komutla yeni alt modüller ekleyerek alt modülün uzak dalını takip etmesini sağlayın.
    • İlk önce git uzaktan kumandanızı takip etmesini söylersiniz <branch>.
    • git'e ödeme yerine rebase veya birleştirme yapmasını söylersin
    • git'e alt modülünüzü uzaktan güncellemesini söylersiniz.
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ git config -f .gitmodules submodule.<submodule-path>.update rebase
    $ git submodule update --remote
  • Mevcut alt modülünüzü böyle eklemediyseniz, bunu kolayca düzeltebilirsiniz:
    • İlk önce alt modülünüzün izlenmesini istediğiniz dalı teslim aldığından emin olmak istiyorsunuz.
    $ 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>

Genel durumlarda, yukarıdaki yapılandırma sorunlarından biriyle ilişkili olduğu için ŞİMDİ AYRINTILI KAFANIZI düzelttiniz.

MÜSTAKİL KAFA sabitleme .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.

Ancak, alt modül için zaten yerel olarak bazı değişiklikler yapmayı başardınız ve taahhütte bulunduysanız, bunları uzaktan kumandaya ittikten sonra 'git checkout' işlemini gerçekleştirdiğinizde Git size bildirir:

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

Geçici bir dal oluşturmak için önerilen seçenek iyi olabilir ve daha sonra bu dalları vb. Birleştirebilirsiniz. Ancak kişisel git cherry-pick <hash>olarak bu durumda kullanırım.

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

Alt modüllerinizi DETACHED HEAD durumuna getirebileceğiniz başka durumlar olsa da, umarım şimdi özel durumunuzu nasıl ayıklayacağınızı biraz daha anlıyorsunuzdur.


2
HEAD, varsayılan davranışıdır git submodule update --remote. Lütfen Simba'nın cevabına bir göz atın, bence bu doğru cevap olmalı.
magomar

78

Bir ekleme branchseçeneği de .gitmoduleolan ilgili DEĞİL hiç Altmodüllerin müstakil davranışına. @Mkungla'dan gelen eski cevap yanlış veya eski.

Gönderen git submodule --help, BAŞ müstakil varsayılan davranıştır arasında git submodule update --remote.

İlk olarak, izlenecek bir dal belirtmeye gerek yoktur . origin/masterizlenecek varsayılan daldır.

--Remote

Alt modülü güncellemek için süper projenin kayıtlı SHA-1'ini kullanmak yerine alt modülün uzaktan izleme kolunun durumunu kullanın. Kullanılan uzaktan kumanda varsayılan olarak dalın remote ( branch.<name>.remote) öğesidirorigin . Kullanılan uzak dal varsayılan olarakmaster kullanılır .

Neden

Peki, HEAD neden sonra ayrıldı update? Bu kaynaklanır varsayılan modülü güncelleme davranışı:checkout .

--Çıkış yapmak

Alt projede ayrılmış bir HEAD üzerine süper projeye kaydedilen taahhüdü kontrol edin. Bu varsayılan davranıştır , bu seçeneğin ana kullanımı, submodule.$name.updatedışında bir değere ayarlandığında geçersiz kılmaktır checkout.

Bu garip güncelleme davranışını açıklamak için alt modüllerin nasıl çalıştığını anlamamız gerekir?

Pro Git kitabındaki Submodules ile başlayarak alıntı

Sbmodule DbConnector, çalışma dizininizde bir alt dizin olsa da Git, onu bir alt modül olarak görür ve o dizinde olmadığınızda içeriğini izlemez. Bunun yerine Git , söz konusu depodan belirli bir taahhüt olarak görür .

Ana repo ile alt modülü izler belirli bir noktada kendi durumuna , id taahhüt . Yani modülleri güncellediğinizde, taahhüt kimliğini yenisiyle güncellersiniz.

Nasıl

Alt modülün uzak dalla otomatik olarak birleştirilmesini istiyorsanız, --mergeveya öğesini kullanın --rebase.

--birleştirmek

Bu seçenek yalnızca güncelleme komutu için geçerlidir . Süper projede kaydedilen taahhüdü alt modülün geçerli dalına birleştirin. Bu seçenek verilirse, alt modülün KAFASI sökülmez .

--rebase

Geçerli dalı, üst projede kaydedilen taahhüde yeniden oluşturun. Bu seçenek verilirse, alt modülün KAFASI sökülmez .

Tek yapmanız gereken,

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

Önerilen takma ad:

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

# do submodule update with
git supdate

Bir yapma seçeneği de var --mergeya --rebasevarsayılan davranış olarak git submodule updateayarıyla, submodule.$name.updatehiç mergeveyarebase .

Aşağıda, alt modül güncellemesinin varsayılan güncelleme davranışının nasıl yapılandırılacağıyla ilgili bir örnek verilmiştir .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

Veya komut satırında yapılandırın,

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

Referanslar


6
Kullandığım git submodule update --remote --mergeve bir müstakil halde alt modülünü aşağı çeker. Ayrıca --rebaseaynı sonuç ile çalıştı .
Joe Strout

8
@JoeStrout Alt modülünüz zaten ayrılmışsa, yukarıdaki komutlarla güncelleme yapmadan önce ayrılmış durumu düzeltin. cdalt modüle, alt modülü belirli bir dala git checkout master,.
Simba

2
Veya - eğer bu birden fazla (özyinelemeli) alt modüller için çok fazla güçlük varsa - yapmanız yeterlidir git submodule foreach --recursive git checkout master.
stefanct

1
Ben sadece "git nasıl çalışır" tanımlarını kısmen anlıyorum. TBH Git'in nasıl çalıştığını anlamak istemiyorum, sadece kullanmak istiyorum. Artık anladığım alt modülleri ile sabitleyebileceğimi anlıyorum git submodule foreach --recursive git checkout master. Fakat git'in onları daima ayırmasını nasıl önleyebilirim? Her alt modül için yapılandırma seçeneklerini ayarlamak bir seçenek değildir!
Nicolas

Benim için koşmak git submodule update --remote --mergealt modülü ayrı bir HEAD durumunda bırakmadı, ancak DID'yi belirttiğiniz gibi git submodule updatedosyamı .gitmoduledüzenledikten sonra çalışarak alt modülü ayrı bir HEAD durumunda bıraktı.
bweber13

41

i sadece tüm modüller için inşa etmek için bir kabuk komut dosyası kullanmak her zaman ayırma bıktım. tüm alt modüllerin master olduğunu varsayalım: İşte komut dosyası:

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

ana modülünüzden çalıştırın


2
git submodule foreach git pull origin master - aradığım şey buydu .. kudos
csomakk

basit ve özlü! Teşekkürler!
zhekaus

12

Cevabımı buradan kontrol edin: Git alt modülleri: Bir dal / etiket belirtin

İsterseniz, "branch = master" satırını .gitmodules dosyanıza manuel olarak ekleyebilirsiniz. Ne demek istediğimi görmek için bağlantıyı okuyun.

DÜZENLEME: Mevcut bir alt modül projesini bir şubede izlemek için bunun yerine VonC'nin talimatlarını izleyin:

Git alt modülleri: Bir dal / etiket belirtin


14
Cevapların satır içi olduğunu varsayalım; Yanıtlara IIRC bağlantısı bir Stack Overflow sahte pastır.
Tony Topper

1
@TonyTopper Başka bir SO cevabına bağlanırken bile? IIRC sadece dış bağlantılar kaşlarını çatıyor, çünkü bunlar ortadan kaybolabilir ve daha sonra bağlantı öldü ve cevap, işe yaramaz. Yine de SO cevapları ile böyle bir tehlike yoktur, SO gitmedikçe (ve ne olursa olsun geri yüklenebilirler) asla gitmeyeceklerdir. Ayrıca soruyu cevapladı, branch = master" line into your .gitmoduleaslında tam cevap olduğu gibi, bu problemi benim için çözdü.
Mecki

9

Alt modülün şubeyi kontrol etmesini sağlamanın diğer bir yolu da .gitmodulesdosyayı kök klasörde gezdirmek ve branchmodül yapılandırmasındaki alanı aşağıdaki gibi eklemektir :

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


15
Benim için bu işe yaramıyor. Doğru ayarladım branch = my_wanted_branch. Ama koşmak git submodule update --remotehala bağımsız kafa olarak kontrol ediyor.
Andrius

Bunu yapın, sonra cd sudmodule & git co thebranche & cd .., sonra git submodule update --remote ve işe yarıyor!
pdem

Bu şekilde '.gitmodules' aktif kullanım altında değil mi (okunuyor), süper proje alt modüller-özyinelemeli şekilde veya alt modül başlatıldığında klonlanırken değil mi? Başka bir deyişle, kendi havuzunuz, '.gitmodules' dizinine konulan alt modül yapılandırma güncellemelerinden her zaman kazanç sağlamayan dosyayı güncellersiniz. Anladığım kadarıyla, '.gitmodules' repo klonlanırken oluşturulan config için bir şablon.
Na13-c

3

Diğer insanların söylediği gibi, bunun gerçekleşmesinin nedeni, ana repo'nun sadece alt modüldeki belirli bir taahhüde (SHA1'in) bir referans içermesidir - dallar hakkında hiçbir şey bilmez. Bu şekilde çalışması gerekir: bu taahhütte olan dal ileriye (ya da geriye) hareket etmiş olabilir ve ana repo da şubeye başvurmuşsa, bu gerçekleştiğinde kolayca kırılabilir.

Bununla birlikte, özellikle hem ana depoda hem de alt modülde aktif olarak gelişiyorsanız, detached HEADdurum kafa karıştırıcı ve potansiyel olarak tehlikeli olabilir. Durumdayken alt modülde işlem yaparsanız detached HEAD, bunlar sarkar ve işinizi kolayca kaybedebilirsiniz. (Sarkan taahhütler genellikle kullanılarak kurtarılabilir git reflog, ancak ilk etapta onlardan kaçınmak çok daha iyidir.)

Benim gibi iseniz, çoğu zaman alt modülde teslim alınan taahhüdün işaret eden bir şube varsa, aynı şubede müstakil HEAD durumunda olmak yerine bu şubeyi kontrol etmeyi tercih edersiniz. gitconfigDosyanıza aşağıdaki diğer adı ekleyerek bunu yapabilirsiniz :

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

Şimdi, yaptıktan sonra git submodule updatesadece aramanız gerekiyor git submodule-checkout-branchve ona işaret eden bir şubesi olan bir taahhütte kontrol edilen herhangi bir alt modül o şubeyi kontrol edecektir. Sıklıkla aynı taahhüde işaret eden birden fazla yerel şubeniz yoksa, bu genellikle istediğinizi yapar; değilse, en azından yaptığınız herhangi bir taahhüdün sarkık kalmak yerine gerçek bir şubeye gitmesini sağlayacaktır.

Ayrıca git'i ödeme sırasında alt modülleri otomatik olarak güncelleyecek şekilde ayarladıysanız (kullanarak git config --global submodule.recurse true, bu cevaba bakın ), bu takma adı otomatik olarak çağıran bir ödeme sonrası kancası yapabilirsiniz:

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

O zaman ya git submodule updateda aramak zorunda değilsiniz git submodule-checkout-branch, sadece yapmak git checkouttüm alt modülleri kendi taahhütlerine güncelleyecek ve ilgili şubeleri kontrol edecektir (eğer varsa).


0

En basit çözüm:

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

Sonra repo dizininde cd ve:

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

Ek okuma: Git alt modüllerin en iyi uygulamalarını sunar .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.