Чому мій Git Submodule HEAD відмежований від головного?


162

Я використовую підмодулі Git. Після витягування змін із сервера, багато разів головка мого підмодуля відривається від головного відділення.

Чому це відбувається?

Я завжди повинен робити:

git branch
git checkout master

Як я можу переконатися, що мій підмодуль завжди вказує на головну гілку?


1
Ви читали цю відповідь? stackoverflow.com/questions/1777854/…
Johnny Z

@bitoiu Я подивився на піддерево і Google Repo. У мене ще немає ідеального рішення :(
om471987

1
мій досвід роботи з гітсумодулями, в умовах ІС, жахливий, можливо, деякі інші люди мають кращий досвід.
bitoiu

@JohnnyZ Дякую Я зрозумів, що підмодуль вказує на фіксацію, а не на голову дерева. Але чому відірвався від гілки. Якщо у вас є одна гілка, чи не слід її додавати за замовчуванням
om471987

3
Не надто швидко відхиляйте підмодулі тільки тому, що ви почули, що вони погані. Вони є поганим рішенням, якщо ви хочете безперервної інтеграції, але вони є майже ідеальним рішенням, якщо ви хочете вставити код із зовнішнього проекту, і ви явно керуєте всіма результатами. Це часто є найкращою практикою, якщо ви інтегруєтесь із модулем без роздвоєння, який не контролюється вашою організацією. Проблема полягає в тому, що вони є спокусливим рішенням у всіх інших ситуаціях, коли вони взагалі не дуже добре працюють. Найкраща порада - прочитати, як вони працюють, та оцінити ваш сценарій.
Сара Г

Відповіді:


176

Редагувати:

Див @Simba відповідь для дійсного рішення

submodule.<name>.updateте, що ви хочете змінити, див. документи - за замовчуваннямcheckout
submodule.<name>.branch вказати віддалену гілку, яку слід відслідковувати - за замовчуваннямmaster


СТАРИЙ ВІДПОВІДЬ:

Особисто я ненавиджу відповіді, які прямують до зовнішніх посилань, які можуть перестати працювати з часом, і перевіряю свою відповідь тут (якщо питання не є дублюючим) - спрямовуючи на запитання, яке охоплює тему між рядками іншої теми, але загалом дорівнює: "Я не відповідаючи, прочитайте документацію ".

Тож повернемось до питання: чому це відбувається?

Ситуацію, яку ви описали

Після витягування змін із сервера, багато разів головка мого підмодуля відривається від головного відділення.

Це звичайний випадок, коли субмодули не використовуються занадто часто або щойно розпочалися з субмодулів . Я вважаю, що я правильно заявляю, що всі ми були там в якийсь момент, коли НАШИЙ підмодуль відривається.

  • Причина: Ваш підмодуль не відстежує правильну гілку (головний майстер).
    Рішення. Переконайтеся, що ваш підмодуль відстежує правильну гілку
$ 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>

У звичайних випадках ви вже виправили свою РОЗШИРЕНУ ГОЛОВУ, оскільки вона була пов'язана з однією з проблем з конфігурацією, наведеною вище.

фіксація ДЕТЕЧОВАНОГО ГОЛОВА коли .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 - це поведінка за замовчуванням git submodule update --remote. Погляньте, будь ласка, на відповідь Сімба, я вважаю, що це повинна бути правильна відповідь.
магмар

78

Додавання branchопції в .gitmoduleце НЕ має відношення до відокремленому поведінки подмодулей на всіх. Стара відповідь від @mkungla невірна або застаріла.

З git submodule --help, ГОЛОВКА стоїть окремо поведінка за умовчанням в 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 сприймає це як певний фіксатор із цього сховища .

Основне репо відстежує підмодуль із його станом у певній точці , ідентифікатор фіксації . Отже, коли ви оновлюєте модулі, ви оновлюєте ідентифікатор фіксації до нового.

Як

Якщо ви хочете, щоб підмодуль автоматично злився з віддаленою гілкою, використовуйте --mergeабо --rebase.

--мілюватися

Ця опція діє лише для команди оновлення . Об’єднайте комітку, записану в надпроекті, у поточну гілку підмодуля. Якщо ця опція задана, 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.updateна mergeабо 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

8
@JoeStrout Якщо ваш підмодуль вже від'єднаний, виправте стан відключеного, перш ніж здійснити оновлення з вищевказаними командами. cdв подмодуль, перевірка подмодуль в конкретній галузі с, git checkout master.
Сімба

2
Або - якщо це занадто багато клопоту для декількох (рекурсивних) підмодулів - просто зробіть git submodule foreach --recursive git checkout master.
stefanct

1
Я лише частково розумію описи "як працює git". TBH Мені не дуже цікаво розуміти, як працює git, я просто хочу його використовувати. Тепер я розумію, що я можу зафіксувати окремі підмодулі за допомогою git submodule foreach --recursive git checkout master. Але як я можу запобігти тому, щоб git завжди відривав їх? Налаштування параметрів конфігурацій для кожного підмодуля - це не варіант!
Ніколя

Для мене біг git submodule update --remote --mergeне залишив підмодуль у відокремленому стані HEAD, але працює git submodule updateпісля редагування мого .gitmoduleфайлу, як ви вказали, DID залишає підмодуль у відірваному стані 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 - ось що я шукав .. kudos
csomakk

просто і стисло! Дякую!
zhekaus

12

Перевірте мою відповідь тут: Підмодулі Git: Вкажіть гілку / тег

Якщо ви хочете, ви можете додати рядок "гілка = головний" у файл .gitmodules вручну. Прочитайте посилання, щоб побачити, що я маю на увазі.

EDIT: Щоб відслідковувати існуючий проект підмодуля у філії, дотримуйтесь тут інструкцій VonC:

Підмодулі Git: Вкажіть гілку / тег


14
Відповіді, мабуть, мають рядковий характер; IIRC, що посилається на відповіді, є штучним пасом Staflow Overflow.
Тоні Топпер

1
@TonyTopper Навіть при посиланні на іншу відповідь? IIRC лише нахмурі лише зовнішні посилання, оскільки вони можуть відійти, і тоді посилання мертве, і відповідь, ну, марний. Але відповіді ТА не мають такої небезпеки, вони ніколи не зникнуть, якщо тільки 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його все одно перевіряється як відсторонена голова.
Андрій

Зробіть це, потім cd sudmodule & git co thebranche & cd .., потім оновлення підмодулю git - remote і воно працює!
pdem

Чи не так, що ".gitmodules" використовується під активним використанням (читається) лише тоді, коли суперпроект клонується субмодулями-рекурсивним чином або підмодуль ініціалізується? Іншими словами, у власному сховищі ви оновлюєте файл, у якому не завжди отримує прибуток від оновлень конфігурації підмодуля, розміщених у ".gitmodules". На моє розуміння,.
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, див. Цю відповідь ), ви можете зробити гачок після оформлення замовлення, який автоматично називає цей псевдонім:

$ 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

Потім CD в каталозі repo і:

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 .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.