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

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

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

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

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

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

14 ответов

Решение

Примечание: в 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

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

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

Вы знаете, что у вас есть подмодуль 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
    

    РЕДАКТИРОВАТЬ: выше поддерживается только имя ветви, но не SHA или TAG.

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

    git submodule update --remote
    

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

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

Обратите внимание, что если у вас есть субмодуль, который еще не отслеживает ветку, то ( если у вас 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, являющимся именем удаленного репо восходящего потока, с которого был клонирован подмодуль.
git remote -v внутри этого подмодуля будет отображаться. Обычно это "происхождение")

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

    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 в .gitmodules за update --remote,
Особая ценность . используется, чтобы указать, что имя ветви в подмодуле должно совпадать с именем текущей ветви в текущем репозитории.


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

    git submodule update --recursive --remote

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

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

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

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

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

Обратите внимание на использование $toplevel Рекомендовано в комментариях Alexander Pogrebnyak.
$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 checkout $branch' –

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

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

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

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

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

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

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

# add submodule to track master branch
git submodule add -b master [URL to Git repo];

# update your submodule
git submodule update --remote 

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

Пример того, как я использую подмодули 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 

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

В существующих ответах отсутствует второй шаг и они перегружены деталями.

Чтобы переключить существующий подмодуль для отслеживания нового удаленного URL-адреса и/или новой ветки:

  1. Отредактируйте источник правды в .

Например, из

      [submodule "api"]
    path = api
    url = https://github.com/<original_repo>/api.git

к

      [submodule "api"]
    path = api
    url = https://github.com/<another_repo>/api.git
    branch = work-in-progress
  1. Обновите описание подмодулей, кэшированных git, в .git/modulesиз только что отредактированного источника правды, указанного в .gitmodulesзапустив git submodule sync.

  2. Обновите извлеченные подмодули в рабочей копии, используя git submodule update --init --recursive --remote.

  3. Зафиксируйте изменения.

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

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

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

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

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

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

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

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

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

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

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

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

У меня есть это в моем файле.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-"

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

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

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

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

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

Единственный эффект выбора ветви для подмодуля заключается в том, что всякий раз, когда вы передаете --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'

Подмодуль git add -b development - имя-ветви-имя- https://branch.git/

Используйте приведенные ниже команды

Добавить подмодуль (ветвь-ветвь)

      git submodule add -b stage git@github.optum.com:orgname/${reponame}.git

Подмодуль обновления (ветка-ветвь)

      ######Clone########

> git clone -b master --single-branch --recurse-submodules git@github.com:orgname/project.git  
or  
> git clone -b stage --single-branch --recurse-submodules git@github.com:orgname/project.git

######Update#######
> git submodule update  --remote (only for remote branch -ie master) 
or 
> git submodule update --recursive --remote

> git submodule update --init --recursive (for remaining branchs)

if you get fatal need single revision error then use below commands:-

before:-

*** stage
  remotes/origin/stage**

git branch -a

git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/{branch name}
ex:- git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/stage

after:-
*** stage
  remotes/origin/HEAD -> origin/stage
  remotes/origin/stage**

then 

> git reset --hard

#hard reset revisions#

> git submodule foreach git reset --hard origin/develop

> git submodule foreach git pull origin develop

после фиксации и нажатия соответственно. Теперь подмодули будут обновляться.

Другие вопросы по тегам