Mengapa HEAD Submodule Git saya terlepas dari master?

163

Saya menggunakan submodules Git. Setelah menarik perubahan dari server, berkali-kali kepala submodule saya terlepas dari cabang utama.

Mengapa itu terjadi?

Saya harus selalu melakukan:

git branch
git checkout master

Bagaimana saya bisa memastikan bahwa submodule saya selalu menunjuk ke cabang master?

om471987
sumber
1
Apakah Anda membaca jawaban ini? stackoverflow.com/questions/1777854/…
Johnny Z
@bitoiu Saya melihat subtree dan Google Repo. Saya belum punya solusi sempurna :(
om471987
1
pengalaman saya dengan gitsubmodules, dalam lingkungan CI mengerikan, mungkin beberapa orang lain memiliki pengalaman yang lebih baik.
bitoiu
@ JohnnyZ Terima kasih. Saya mengerti bahwa submodule menunjuk komit dan bukan kepala pohon. Tapi Mengapa terlepas dari cabang. Jika Anda memiliki satu cabang, jangan dilampirkan secara default
om471987
3
Jangan terlalu cepat untuk mengabaikan submodul hanya karena Anda mendengar itu buruk. Mereka adalah solusi yang buruk jika Anda ingin integrasi berkelanjutan, tetapi mereka adalah solusi yang hampir sempurna jika Anda ingin menanamkan kode dari proyek eksternal dan Anda secara eksplisit mengelola semua tarikan. Ini sering merupakan praktik terbaik jika Anda mengintegrasikan dengan modul non-bercabang yang tidak dikendalikan oleh organisasi Anda. Masalahnya adalah mereka merupakan solusi yang menggoda dalam semua jenis situasi lain di mana mereka tidak bekerja dengan baik sama sekali. Saran terbaik adalah membaca bagaimana mereka bekerja dan mengevaluasi skenario Anda.
Sarah G

Jawaban:

176

EDIT:

Lihat @Simba Answer untuk solusi yang valid

submodule.<name>.updateadalah apa yang ingin Anda ubah, lihat dokumen - defaultcheckout
submodule.<name>.branch tentukan cabang jarak jauh yang akan dilacak - defaultmaster


JAWABAN TUA:

Secara pribadi saya benci jawaban di sini yang mengarahkan ke tautan eksternal yang mungkin berhenti bekerja dari waktu ke waktu dan memeriksa jawaban saya di sini (Kecuali jika pertanyaan itu rangkap) - mengarahkan ke pertanyaan yang mencakup subjek di antara baris subjek lain, tetapi secara keseluruhan sama dengan: "Saya tidak menjawab, baca dokumentasinya. "

Jadi kembali ke pertanyaan: Mengapa itu terjadi?

Situasi yang Anda jelaskan

Setelah menarik perubahan dari server, berkali-kali kepala submodule saya terlepas dari cabang utama.

Ini adalah kasus umum ketika seseorang tidak terlalu sering menggunakan submodul atau baru saja memulai dengan submodul . Saya percaya bahwa saya benar dalam menyatakan, bahwa kita semua telah ada di suatu titik di mana KEPALA submodul kita terlepas.

  • Penyebab: submodule Anda tidak melacak cabang yang benar (master default).
    Solusi: Pastikan submodule Anda melacak cabang yang benar
$ 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>
  • Penyebab: Repo orang tua Anda tidak dikonfigurasikan untuk melacak cabang submodula.
    Solusi: Buat submodule Anda melacak cabang jarak jauh dengan menambahkan submodul baru dengan dua perintah berikut.
    • Pertama, Anda memberi tahu git untuk melacak remote Anda <branch> .
    • Anda memberi tahu git untuk melakukan rebase atau menggabungkan alih-alih checkout
    • Anda memberi tahu git untuk memperbarui submodule Anda dari jarak jauh.
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ git config -f .gitmodules submodule.<submodule-path>.update rebase
    $ git submodule update --remote
  • Jika Anda belum menambahkan submodule yang ada seperti ini, Anda dapat dengan mudah memperbaikinya:
    • Pertama, Anda ingin memastikan bahwa submodule Anda memiliki cabang diperiksa yang ingin dilacak.
    $ 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>

Dalam kasus-kasus umum, Anda sudah memperbaiki HEAD DETACHED Anda sekarang karena itu terkait dengan salah satu masalah konfigurasi di atas.

memperbaiki KEPALA RINCI ketika .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.

Tetapi jika Anda berhasil membuat beberapa perubahan secara lokal sudah untuk submodule dan melakukan, mendorong ini ke remote maka ketika Anda menjalankan 'git checkout', Git memberi tahu Anda:

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

Opsi yang disarankan untuk membuat cabang sementara bisa bagus, dan kemudian Anda bisa menggabungkan cabang-cabang ini dll. Namun saya pribadi akan menggunakan hanya git cherry-pick <hash>dalam kasus ini.

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

Meskipun ada beberapa kasus lagi, Anda bisa mendapatkan submodul Anda ke status HEAD DETACHED, saya harap Anda sekarang mengerti sedikit lebih banyak tentang cara men-debug kasus khusus Anda.

mkungla
sumber
2
KEPALA terlepas adalah perilaku default git submodule update --remote. Silakan lihat jawaban Simba, saya pikir itu seharusnya jawaban yang tepat.
magomar
78

Menambahkan branchopsi di .gitmoduleadalah TIDAK terkait dengan perilaku terpisah dari submodul sama sekali. Jawaban lama dari @mkungla salah, atau usang.

Dari git submodule --help, HEAD detached adalah perilaku default darigit submodule update --remote .

Pertama, tidak perlu menentukan cabang yang akan dilacak . origin/masteradalah cabang default yang akan dilacak.

--terpencil

Alih-alih menggunakan SHA-1 superproyek yang direkam untuk memperbarui submodule, gunakan status cabang pelacak jarak jauh submodule. Remote yang digunakan adalah remote cabang ( branch.<name>.remote), default keorigin . Cabang jauh menggunakan default untukmaster .

Mengapa

Jadi mengapa HEAD terlepas setelah update? Hal ini disebabkan oleh perilaku pembaruan modul default:checkout .

--Periksa

Periksa komit yang direkam dalam proyek super pada HEAD terpisah dalam submodule. Ini adalah perilaku default , penggunaan utama dari opsi ini adalah untuk menimpa submodule.$name.updateketika diatur ke nilai selain checkout.

Untuk menjelaskan perilaku pembaruan yang aneh ini, kita perlu memahami bagaimana cara kerja submodula?

Kutipan dari Starting with Submodules dalam buku Pro Git

Meskipun sbmodule DbConnectoradalah subdirektori di direktori kerja Anda, Git melihatnya sebagai submodule dan tidak melacak isinya saat Anda tidak berada di direktori itu. Sebaliknya, Git melihatnya sebagai komit tertentu dari repositori itu .

Repo utama melacak submodule dengan statusnya pada titik tertentu , id komit . Jadi, ketika Anda memperbarui modul, Anda memperbarui id komit ke yang baru.

Bagaimana

Jika Anda ingin submodule digabung dengan cabang jarak jauh secara otomatis, gunakan --mergeatau --rebase.

--menggabungkan

Opsi ini hanya valid untuk perintah pembaruan . Gabungkan komit yang direkam dalam proyek super ke dalam cabang submodule saat ini. Jika opsi ini diberikan, KEPALA submodul tidak akan terlepas .

--rebase

Rebase cabang saat ini ke komit yang direkam dalam proyek super. Jika opsi ini diberikan, KEPALA submodul tidak akan terlepas .

Yang perlu Anda lakukan adalah,

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

Alias ​​yang disarankan:

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

# do submodule update with
git supdate

Ada juga opsi untuk membuat --mergeatau --rebasesebagai perilaku default git submodule update, dengan mengatur submodule.$name.updateke mergeataurebase .

Berikut adalah contoh tentang cara mengkonfigurasi perilaku pembaruan default pembaruan submodule di .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

Atau konfigurasikan di baris perintah,

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

Referensi

Simba
sumber
6
Saya menggunakan git submodule update --remote --merge, dan itu menarik submodule dalam keadaan terpisah. Juga dicoba --rebasedengan hasil yang sama.
Joe Strout
8
@ JooeStrout Jika submodule Anda sudah terlepas, perbaiki keadaan terlepas sebelum Anda melakukan pembaruan dengan perintah di atas. cdke dalam submodule, checkout submodule ke cabang tertentu dengan git checkout master,.
Simba
2
Atau - jika ini terlalu merepotkan untuk banyak submodul (rekursif) - lakukan saja git submodule foreach --recursive git checkout master.
stefanct
1
Saya hanya memahami sebagian deskripsi "bagaimana git bekerja". TBH Saya tidak benar-benar tertarik untuk memahami cara kerja git, saya hanya ingin menggunakannya. Sekarang saya mengerti bahwa saya dapat memperbaiki submodules yang terpisah git submodule foreach --recursive git checkout master. Tetapi bagaimana saya bisa mencegah agar git tidak selalu melepaskan mereka? Pengaturan opsi konfigurasi untuk setiap submodule bukan pilihan!
Nicolas
Bagi saya, menjalankan git submodule update --remote --mergetidak meninggalkan submodule dalam keadaan HEAD terpisah, tetapi berjalan git submodule updatesetelah mengedit .gitmodulefile saya saat Anda menunjukkan DID meninggalkan submodule dalam keadaan HEAD terpisah.
bweber13
41

Aku bosan selalu melepaskannya jadi aku hanya menggunakan skrip shell untuk membangunnya untuk semua modul saya. saya berasumsi semua submodule ada di master: ini skripnya:

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

jalankan dari modul induk Anda

j2emanue
sumber
2
git submodule foreach git pull master asal - ini yang saya cari .. kudos
csomakk
sederhana dan ringkas! Terima kasih!
zhekaus
12

Lihat jawaban saya di sini: Git submodules: Tentukan cabang / tag

Jika mau, Anda dapat menambahkan baris "branch = master" ke dalam file .gitmodules Anda secara manual. Baca tautan untuk melihat apa yang saya maksud.

EDIT: Untuk melacak proyek submodule yang ada di cabang, ikuti instruksi VonC di sini sebagai gantinya:

Git submodules: Tentukan cabang / tag

Johnny Z
sumber
14
Jawabannya seharusnya sejajar; Tautan IIRC ke jawaban adalah kecurangan Stack Overflow.
Tony Topper
1
@TonyTopper Bahkan ketika hanya menautkan ke jawaban SO yang lain? IIRC hanya tautan eksternal yang dikerutkan karena ini mungkin hilang dan kemudian tautannya mati dan jawabannya, yah, tidak berguna. Namun tidak ada bahaya dengan jawaban SO, mereka tidak akan pernah pergi, kecuali SO hilang (dan dapat dipulihkan tidak peduli apa yang terjadi). Dia juga telah menjawab pertanyaan itu, seperti branch = master" line into your .gitmodulejawaban sebenarnya, yang memecahkan masalah itu untuk saya.
Mecki
9

Cara lain untuk membuat submodule Anda untuk memeriksa cabang adalah dengan membuka .gitmodulesfile di folder root dan menambahkan bidang branchdalam konfigurasi modul sebagai berikut:

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

frontendgirl
sumber
15
Bagi saya ini tidak berhasil. Saya telah mengatur dengan benar branch = my_wanted_branch. Tetapi menjalankannya git submodule update --remotemasih memeriksa sebagai kepala terpisah.
Andrius
Lakukan ini, kemudian cd sudmodule & git co thebranche & cd .., lalu git perbarui submodule --remote dan berfungsi!
pdem
Bukankah dengan cara itu '. Gitmodules' sedang digunakan aktif (sedang dibaca) hanya saat superproyek sedang dikloning dengan cara submodul-rekursif atau submodul yang diinisialisasi? Dengan kata lain repositori Anda sendiri, Anda memperbarui file yang tidak selalu mendapat untung dari pembaruan konfigurasi submodule yang dimasukkan ke '.gitmodules'. Dalam pemahaman saya '.gitmodules' adalah template untuk konfigurasi yang dibuat saat repo sedang dikloning atau lebih.
Na13-c
3

Seperti yang dikatakan orang lain, alasan ini terjadi adalah bahwa repo induk hanya berisi referensi ke (SHA1 dari) komit tertentu dalam submodule - tidak tahu apa-apa tentang cabang. Beginilah seharusnya: cabang yang berada di komit itu mungkin telah bergerak maju (atau mundur), dan jika repo induk mereferensikan cabang maka itu dapat dengan mudah pecah ketika itu terjadi.

Namun, terutama jika Anda secara aktif berkembang di kedua induk repo dan submodule, detached HEADnegara dapat membingungkan dan berpotensi berbahaya. Jika Anda membuat komitmen dalam submodule saat sedang dalam detached HEADkeadaan, ini menjadi menggantung dan Anda dapat dengan mudah kehilangan pekerjaan Anda. (Menggantung komit biasanya bisa diselamatkan menggunakan git reflog, tapi jauh lebih baik untuk menghindari mereka di tempat pertama.)

Jika Anda seperti saya, maka sebagian besar waktu jika ada cabang dalam submodule yang menunjuk ke komit yang diperiksa, Anda lebih suka memeriksa cabang itu daripada berada dalam keadaan HEAD yang terlepas pada komit yang sama. Anda dapat melakukan ini dengan menambahkan alias berikut ke gitconfigfile Anda :

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

Sekarang, setelah melakukan git submodule updateAnda hanya perlu menelepon git submodule-checkout-branch, dan setiap submodule yang diperiksa pada komit yang memiliki cabang menunjuk ke sana akan memeriksa cabang itu. Jika Anda tidak sering memiliki beberapa cabang lokal semuanya menunjuk ke komit yang sama, maka ini biasanya akan melakukan apa yang Anda inginkan; jika tidak, maka setidaknya itu akan memastikan bahwa setiap komit yang Anda lakukan pergi ke cabang yang sebenarnya alih-alih dibiarkan menggantung.

Selain itu, jika Anda telah mengatur git untuk secara otomatis memperbarui submodules pada checkout (menggunakan git config --global submodule.recurse true, lihat jawaban ini ), Anda dapat membuat post-checkout hook yang memanggil alias ini secara otomatis:

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

Maka Anda tidak perlu menelepon salah satu git submodule updateatau git submodule-checkout-branch, hanya melakukan git checkoutakan memperbarui semua submodul ke komitmen masing-masing dan memeriksa cabang yang sesuai (jika ada).

deltacrux
sumber
0

Solusi paling sederhana adalah:

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

Kemudian cd di direktori repo dan:

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

Bacaan tambahan: Dapatkan praktik terbaik submodul .

noccoa0
sumber