Как указать ветку / тег при добавлении подмодуля Git?

756

Как git submodule add -bработает?

После добавления подмодуля с определенной ветвью новый клонированный репозиторий (после git submodule update --init) будет при определенной фиксации, а не сама ветвь ( git statusв подмодуле отображается «В настоящее время нет ни одной ветви»).

Я не могу найти какую - либо информацию о .gitmodulesили .git/configо филиале подмодуль или какой - либо конкретной фиксации, так как делает Git понять это?

Кроме того, возможно ли указать тег вместо ветви?

Я использую версию 1.6.5.2.

Иван
источник
3
Если у вас есть существующий подмодуль , который не отслеживает ветку еще , но вы хотите теперь будет отслеживать ветку ... увидеть мой ответ ниже
VonC

Ответы:

745

Примечание: в Git 1.8.2 добавлена ​​возможность отслеживать ветки. Смотрите некоторые ответы ниже.


Это немного сбивает с толку, чтобы привыкнуть к этому, но подмодули не на ветке. Они, как вы говорите, просто указатель на определенный коммит репозитория подмодуля.

Это означает, что когда кто-то проверяет ваш репозиторий или извлекает ваш код и выполняет обновление подмодуля git, подмодуль извлекается для этой конкретной фиксации.

Это отлично подходит для подмодуля, который не часто меняется, потому что тогда у каждого в проекте может быть подмодуль при одном и том же коммите.

Если вы хотите переместить субмодуль в определенный тег:

cd submodule_directory
git checkout v1.0
cd ..
git add submodule_directory
git commit -m "moved submodule to v1.0"
git push

Затем другой разработчик, который хочет, чтобы submodule_directory был изменен на этот тег, делает это

git pull
git submodule update --init

git pullизменения, которые фиксируют их каталог подмодулей, на которые указывает. git submodule updateна самом деле сливается в новом коде.

djacobs7
источник
8
Это очень хорошее объяснение, спасибо! И, конечно же, прочитав ваш ответ, я понял, что коммит сохраняется внутри самого подмодуля (submodule / .git / HEAD).
Иван
4
Похоже, это не работает на git 1.7.4.4. cd my_submodule; git checkout [ref in submodule's repositoryдоходность fatal: reference is not a tree: .... Это как будто gitбудет работать только в родительском хранилище.
Джеймс А. Розен
3
Хорошо использовать подмодули git даже для проектов, которые часто обновляются. Ядро Linux использует его, и это не так уж плохо
10
Это git checkout v1.0ветка или тег?
Бернхард Доблер
8
Считайте тег понятным человеку псевдонимом коммита. А коммит - это набор определенного состояния для каждого файла. Ветвь, по сути, одно и то же, за исключением того, что вы можете вносить в нее изменения.
Deadbabykitten
658

Я хотел бы добавить ответ, который на самом деле является просто конгломератом других ответов, но я думаю, что он может быть более полным.

Вы знаете, что у вас есть подмодуль Git, когда у вас есть эти две вещи.

  1. Ваша .gitmodulesзапись имеет следующий вид:

    [submodule "SubmoduleTestRepo"]
        path = SubmoduleTestRepo
        url = https://github.com/jzaccone/SubmoduleTestRepo.git
    
  2. У вас есть объект подмодуля (в данном примере с именем SubmoduleTestRepo) в вашем Git-репозитории. GitHub показывает их как «субмодульные» объекты. Или сделать git submodule statusиз командной строки. Объекты подмодуля Git представляют собой особые виды объектов Git, и они содержат информацию SHA для конкретного коммита.

    Всякий раз, когда вы делаете git submodule update, он будет заполнять ваш подмодуль контентом из коммита. Он знает, где найти коммит из-за информации в .gitmodules.

    Теперь все, что -bнужно сделать, это добавить одну строку в ваш .gitmodulesфайл. Итак, следуя тому же примеру, это будет выглядеть так:

    [submodule "SubmoduleTestRepo"]
        path = SubmoduleTestRepo
        url = https://github.com/jzaccone/SubmoduleTestRepo.git
        branch = master
    

    Примечание: в .gitmodulesфайле поддерживается только имя ветви , но SHA и TAG не поддерживаются! (вместо этого фиксация ветки каждого модуля может отслеживаться и обновляться с помощью « git add .», например, например git add ./SubmoduleTestRepo, и вам не нужно .gitmodulesкаждый раз менять файл)

    Объект субмодуля по-прежнему указывает на конкретный коммит. Единственное, что -bпокупает опция, - это возможность добавить --remoteфлаг к вашему обновлению в соответствии с ответом Фогеллы:

    git submodule update --remote
    

    Вместо того, чтобы заполнять содержимое подмодуля коммитом, на который указывает субмодуль, он заменяет этот коммит последним коммитом в основной ветке, ПОТОМ он заполняет субмодуль этим коммитом. Это может быть сделано в два этапа с помощью ответа djacobs7. Поскольку вы обновили коммит, на который указывает объект подмодуля, вы должны зафиксировать измененный объект субмодуля в своем Git-репозитории.

    git submodule add -bэто не какой-то волшебный способ держать все в курсе с веткой. Он просто добавляет информацию о ветви в .gitmodulesфайл и дает вам возможность обновить объект подмодуля до самого последнего коммита указанной ветви перед его заполнением.

Джонни З
источник
14
Этот ответ должен иметь больше голосов. Я читал много сообщений за прошедший день, и это проясняет всю путаницу. Исходя из мира SVN и используя внешние источники - хочется верить, что отслеживание веток в git-субмодуле волшебным образом поддерживает все в актуальном состоянии - но это не так! Вы должны явно обновить их! Как вы упоминаете, вы должны зафиксировать измененные объекты субмодуля.
дтмланд
12
Это отслеживание веток также работает с тегами ? Вместо ветки я указал тег в моем, .gitmodulesи после этого $ git submodule update --init --remote TestModuleя получил сообщение об ошибке fatal: Needed a single revisionи Unable to find current origin/TestTag revision in submodule path 'TestModule'. Делая это с реальной веткой, это работает. Есть ли способ указать тег .gitmodulesбез указания точного коммита?
Hhut
5
Это не похоже на работу. Я обновил хэш .gitmodulesи побежал, git submodule updateи ничего не произошло?
CMCDragonkai
2
Каким-то образом это не работает для меня. С SHA Commit Id, я всегда получаю сообщение об ошибке «Не удалось найти текущую версию (я проверил номер ревизии головы и его правильное) Однако , если я использую мастер работает..
infoclogged
2
Ввод SHA в атрибут ответвления также не работает для меня. Это использование также не поддерживается документами: git-scm.com/docs/gitmodules
Якуб Боченски
340

(Git 2.22, Q2 2019, представил git submodule set-branch --branch aBranch -- <submodule_path>)

Обратите внимание , что если у вас есть существующий подмодуль , который не отслеживание ветки еще , то ( если у вас есть GIT 1.8.2+ ):

  • Убедитесь, что родительское хранилище знает, что его подмодуль теперь отслеживает ветку:

    cd /path/to/your/parent/repo
    git config -f .gitmodules submodule.<path>.branch <branch>
    
  • Убедитесь, что ваш подмодуль на самом деле не позднее этой ветки:

    cd path/to/your/submodule
    git checkout -b branch --track origin/branch
      # if the master branch already exist:
      git branch -u origin/master master
    

         (с именем origin, являющимся именем удаленного репо восходящего потока, с которого был клонирован подмодуль.
         A git remote -vвнутри этого подмодуля будет отображать его. Обычно это «origin»)

  • Не забудьте записать новое состояние вашего подмодуля в родительском репо:

    cd /path/to/your/parent/repo
    git add path/to/your/submodule
    git commit -m "Make submodule tracking a branch"
    
  • Последующее обновление для этого подмодуля должно будет использовать --remoteопцию:

    # update your submodule
    # --remote will also fetch and ensure that
    # the latest commit from the branch is used
    git submodule update --remote
    
    # to avoid fetching use
    git submodule update --remote --no-fetch 
    

Обратите внимание, что с Git 2.10+ (3 квартал 2016 года) вы можете использовать ' .' в качестве имени ветви:

Название филиала записывается как submodule.<name>.branchв .gitmodulesfor update --remote.
Специальное значение .используется, чтобы указать, что имя ветви в подмодуле должно совпадать с именем текущей ветви в текущем репозитории .

Но , как заметил по LubosD

С git checkout, если имя ветви следовать, « .», он будет убивать вашу неизрасходованную работу!
Используйте git switchвместо этого.

Это означает, что Git 2.23 (август 2019) или больше.

Смотрите " Confused bygit checkout "


Если вы хотите обновить все свои подмодули, следуя ветке:

    git submodule update --recursive --remote

Обратите внимание, что результатом для каждого обновленного подмодуля почти всегда будет отдельная ГОЛОВА , как отмечает Дэн Кэмерон в своем ответе .

( Клинт отмечает в комментариях, что, если вы запустите git submodule update --remoteи полученный sha1 будет таким же, как ветвь, в которой в данный момент находится подмодуль, он ничего не сделает и оставит подмодуль все еще «в этой ветке», а не в отдельном состоянии головы. )

Чтобы убедиться, что ветвь действительно извлечена (и это не изменит SHA1 специальной записи, представляющей подмодуль для родительского репо), он предлагает:

git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; git switch $branch'

Каждый подмодуль будет по-прежнему ссылаться на тот же SHA1, но если вы сделаете новые коммиты, вы сможете их подтолкнуть, потому что на них будет ссылаться ветвь, которую вы хотите отследить подмодуль.
После этого продвижения в подмодуле не забудьте вернуться к родительскому репо, добавить, зафиксировать и отправить новый SHA1 для этих модифицированных подмодулей.

Обратите внимание на использование $toplevel, рекомендованное в комментариях по Александр Погребняк .
$toplevelбыл представлен в git1.7.2 в мае 2010 года: commit f030c96 .

он содержит абсолютный путь к каталогу верхнего уровня (где .gitmodulesнаходится).

dtmlandдобавляет в комментариях :

Сценарий foreach не сможет извлекать подмодули, которые не следуют за веткой.
Тем не менее, эта команда дает вам оба:

 git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; [ "$branch" = "" ] && git checkout master || git switch $branch' –

Та же команда, но проще для чтения:

git submodule foreach -q --recursive \
    'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; \
     [ "$branch" = "" ] && \
     git checkout master || git switch $branch' –

umläute уточняет команду dtmland с упрощенной версией в комментариях :

git submodule foreach -q --recursive 'git switch $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'

несколько строк:

git submodule foreach -q --recursive \
  'git switch \
  $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'

До Git 2.26 (Q1 2020) выборка, которой велено рекурсивно извлекать обновления в подмодулях, неизбежно создает пакеты вывода, и становится трудно обнаружить сообщения об ошибках.

Команда научена перечислять подмодули, которые имели ошибки в конце операции .

См. Коммит 0222540 (16 января 2020 г.) Эмили Шаффер ( nasamuffin) .
(Слиты Junio C Hamano - gitster- в фиксации b5c71cc , 5 февраля 2020)

fetch: подчеркнуть неудачу во время выборки субмодуля

Подписано: Эмили Шаффер

В тех случаях, когда выборка подмодуля завершается неудачно, когда имеется много подмодулей, ошибка из-за одиночной неудачной выборки подмодуля скрывается под действием других подмодулей, если было выполнено более одной выборки fetch-by-oid.
Вызовите ошибку поздно, чтобы пользователь знал, что что-то пошло не так и где .

Потому что fetch_finish()вызывается только синхронно, run_processes_parallel,мьютекс не требуется вокруг submodules_with_errors.

VonC
источник
1
Вопрос: если у меня есть папка subModule1 и я хочу отслеживать основную ветку, будет ли получающаяся команда выглядеть следующим образом: git config -f .gitmodules submodule.subModule1.branch master
BraveNewMath
1
foreachСценарий не будет зависеть от зашиты <path>, если вы заменяете <path>с $toplevel/.
Александр Погребняк
1
foreachСкрипт не кассовые подмодули, которые не является ветвью. Тем не менее, эта команда дает вам оба:git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; [ "$branch" = "" ] && git checkout master || git checkout $branch'
dtmland
2
вот упрощенная версия @ dtmland - х сценариев:git submodule foreach -q --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'
umläute
1
Оу! На самом деле скрипт foreach не нужен. Мы должны выполнить обновление подмодуля с ключом --merge или --rebase: git submodule update --remote --mergeили git submodule update --remote --rebase. Эти команды отслеживают удаленную ветвь.
GregTom
206

В Git 1.8.2 добавлена ​​возможность отслеживать ветки.

# add submodule to track master branch
git submodule add -b branch_name URL_to_Git_repo optional_directory_rename

# update your submodule
git submodule update --remote 

Смотрите также Git submodules

vogella
источник
4
Это относится и к тегам?
ThorSummoner
1
Как добавление субмодуля таким образом отражается на .gitmodulesфайле?
Евгений
1
Спасибо, я только что использовал информацию, которая помогла мне создать папку подмодуля, которая синхронизируется с веб-сайтом GitHub gh-pages: полный пример на github.com/o2platform/fluentnode/issues/22
Динис Круз
4
Вы можете заблокировать в теге с git submodule add -b tags/<sometag> <url>которой вы можете увидеть , как линия branch = tags/<sometag>в.gitmodules
KCD
9
@KCD Какая версия git может делать это с тегами. Мой не работает?
CMCDragonkai
58

Пример того, как я использую подмодули Git.

  1. Создать новый репозиторий
  2. Затем клонируйте другой репозиторий как подмодуль
  3. Тогда у нас есть этот подмодуль, использующий тег V3.1.2
  4. И тогда мы совершаем.

И это выглядит примерно так:

git init 
vi README
git add README
git commit 
git submodule add git://github.com/XXXXX/xxx.yyyy.git stm32_std_lib
git status

git submodule init
git submodule update

cd stm32_std_lib/
git reset --hard V3.1.2 
cd ..
git commit -a

git submodule status 

Может быть, это помогает (хотя я использую тег, а не ветку)?

Johan
источник
4
Это в основном тот же ответ, что и djacobs7, но все равно спасибо :)
Иван
1
Должны ли вы быть в состоянии совершить изменение после вашего git reset --hard V3.1.2? Я просто получаю "ничего не коммитить" с git statusродительским каталогом.
Ник Рэдфорд
1
@Ivan: Не могли бы вы объяснить, как это совпадает с ответом djacobs7? Насколько я вижу, его ответ даже не включает команду «submodule add», вместо этого репозиторий добавляется напрямую, без какой-либо ссылки на исходное git-репозиторий модуля. По крайней мере, когда я попробовал этот подход, в .gitmodules не было ссылки.
Мишель Мюллер
Ответ djacobs7 не включает полное объяснение, начиная с добавления подмодуля. Он предполагает, что у вас уже есть это.
CodeMonkey
Разве это не просто добавить все содержимое подмодуля в виде отслеживаемых объектов в ваш основной репо?
user1312695
38

По моему опыту, переключение ветвей в суперпроекте или будущих проверках будет по-прежнему вызывать отдельные заголовки подмодулей, независимо от того, правильно ли добавлен и отслежен подмодуль (т.е. ответы @ djacobs7 и @Johnny Z).

И вместо того, чтобы вручную проверять правильную ветку вручную или через скрипт git submodule foreach .

Это проверит файл конфигурации подмодуля для свойства ветки и извлечет установленную ветку.

git submodule foreach -q --recursive 'branch="$(git config -f <path>.gitmodules submodule.$name.branch)"; git checkout $branch'

Дэн Кэмерон
источник
Ницца. +1. Я включил вашу команду в мой ответ .
VonC
33

Подмодули Git немного странные - они всегда находятся в режиме «отстраненной головы» - они не обновляются до последнего коммита на ветке, как вы могли ожидать.

Это имеет некоторый смысл, когда вы думаете об этом, хотя. Допустим, я создаю репозиторий foo с панелью субмодулей . Я отправляю свои изменения и говорю вам проверить коммит a7402be из репозитория foo .

Затем представьте, что кто-то вносит изменения в панель репозитория, прежде чем вы сможете сделать свой клон.

Когда вы проверяете коммит a7402be из репозитория foo , вы ожидаете получить тот же код, который я нажал. Вот почему подмодули не обновляются, пока вы не скажете им явно, а затем сделаете новый коммит.

Лично я думаю, что подмодули - самая запутанная часть Git. Есть много мест, которые могут объяснить подмодули лучше, чем я. Я рекомендую Pro Git от Скотта Чакона.

Neall
источник
Я думаю, что пришло время начать читать некоторые книги Git, спасибо за рекомендацию.
Иван
Извините, но вы не уточнили, будет ли получено то же самое, что вы выдвинули на a7402be, или получите последний бар, хотя ваша версия foo. Спасибо :)
ммм
6
Проблема в том, что должна быть опция «сохранить этот подмодуль в ветви X», чтобы, если вы хотите, чтобы он автоматически обновлялся, вы могли это сделать. Это сделало бы подмодули намного более полезными для управления, например, установкой WordPress, где плагины - это все репозитории Git, без необходимости повторного сохранения суперпроекта для каждого обновляемого плагина.
Джеркларк
@jeremyclark git clone git://github.com/git/git.gitи нажмите эту функцию ...? = D
Аластер
1
Самым запутанным в Git является то, что даже после более чем десятилетнего периода разработки инструмент, предназначенный для того, чтобы помочь мне выполнить свою работу, все еще имеет такой плохой пользовательский опыт, и по совершенно не зависящим от меня причинам людям нравится показывать пальцем Git all время.
0xC0000022L
20

Чтобы переключить ветвь для подмодуля (при условии, что у вас уже есть подмодуль как часть репозитория):

  • cd в корень вашего хранилища, содержащего подмодули
  • Открыть .gitmodulesдля редактирования
  • Добавьте строку ниже, path = ...и url = ...это говорит branch = your-branch, для каждого подмодуля; сохранить файл .gitmodules.
  • затем без изменения каталога сделать $ git submodule update --remote

... это должно получить последние коммиты в указанной ветви для каждого измененного таким образом подмодуля.

инженер
источник
10

У меня есть это в моем файле .gitconfig. Это все еще черновик, но оказался полезным на данный момент. Это помогает мне всегда подключать субмодули к их ветке.

[alias]

######################
#
#Submodules aliases
#
######################


#git sm-trackbranch : places all submodules on their respective branch specified in .gitmodules
#This works if submodules are configured to track a branch, i.e if .gitmodules looks like :
#[submodule "my-submodule"]
#   path = my-submodule
#   url = git@wherever.you.like/my-submodule.git
#   branch = my-branch
sm-trackbranch = "! git submodule foreach -q --recursive 'branch=\"$(git config -f $toplevel/.gitmodules submodule.$name.branch)\"; git checkout $branch'"

#sm-pullrebase :
# - pull --rebase on the master repo
# - sm-trackbranch on every submodule
# - pull --rebase on each submodule
#
# Important note :
#- have a clean master repo and subrepos before doing this !
#- this is *not* equivalent to getting the last committed 
#  master repo + its submodules: if some submodules are tracking branches 
#  that have evolved since the last commit in the master repo,
#  they will be using those more recent commits !
#
#  (Note : On the contrary, git submodule update will stick 
#to the last committed SHA1 in the master repo)
#
sm-pullrebase = "! git pull --rebase; git submodule update; git sm-trackbranch ; git submodule foreach 'git pull --rebase' "

# git sm-diff will diff the master repo *and* its submodules
sm-diff = "! git diff && git submodule foreach 'git diff' "

#git sm-push will ask to push also submodules
sm-push = push --recurse-submodules=on-demand

#git alias : list all aliases
#useful in order to learn git syntax
alias = "!git config -l | grep alias | cut -c 7-"
Паскаль Т.
источник
3

Мы используем Quack для извлечения определенного модуля из другого репозитория Git. Нам нужно извлекать код без всей кодовой базы предоставленного репозитория - нам нужен очень специфический модуль / файл из этого огромного репозитория, и его следует обновлять каждый раз, когда мы запускаем update.

Итак, мы достигли этого следующим образом:

Создать конфигурацию

name: Project Name

modules:
  local/path:
    repository: https://github.com/<username>/<repo>.git
    path: repo/path
    branch: dev
  other/local/path/filename.txt:
    repository: https://github.com/<username>/<repo>.git
    hexsha: 9e3e9642cfea36f4ae216d27df100134920143b9
    path: repo/path/filename.txt

profiles:
  init:
    tasks: ['modules']

В приведенной выше конфигурации он создает один каталог из предоставленного репозитория GitHub, как указано в конфигурации первого модуля, а другой - извлечь и создать файл из данного репозитория.

Другие разработчики просто нужно запустить

$ quack

И это тянет код из вышеуказанных конфигураций.

Любовь Шарма
источник
2

Единственный эффект выбора ветви для подмодуля состоит в том, что всякий раз, когда вы передаете --remoteопцию в git submodule updateкомандной строке, Git будет проверять в режиме отдельного HEAD (если --checkoutвыбрано поведение по умолчанию ) последний коммит этой выбранной удаленной ветви.

Вы должны быть особенно осторожны при использовании этой функции удаленного отслеживания ветвей для подмодулей Git, если вы работаете с мелкими клонами подмодулей. Ветвь, которую вы выбираете для этой цели в настройках субмодуля, НЕ является той, которая будет клонирована во время git submodule update --remote. Если вы передадите также --depthпараметр и не будете указывать Git, какую ветку вы хотите клонировать - и на самом деле вы не можете это сделать в git submodule updateкомандной строке !! -, он будет неявно вести себя, как описано в git-clone(1)документации, git clone --single-branchкогда явный --branchпараметр отсутствует, и поэтому он будет клонировать только первичную ветвь .

Неудивительно, что после этапа клонирования, выполненного git submodule updateкомандой, он, наконец, попытается проверить последний коммит для удаленной ветви, которую вы ранее настроили для подмодуля, и, если это не основной модуль, он не является частью ваш местный мелкий клон, и поэтому он потерпит неудачу с

фатальный: требуется одна ревизия

Невозможно найти текущий источник / ревизию NotThePrimaryBranch в пути к субмодулю 'mySubmodule'

LuKePicci
источник
как исправить ошибку - нужна одна ревизия?
NidhinSPradeep