Восстановить подмодули git из .gitmodules

У меня есть папка, которая была репозиторием git. Он содержит несколько файлов и файл .gitmodules. Теперь, когда я делаю git init, а затем git submodule init, вывод последней команды - ничего. Как я могу помочь git увидеть подмодули, определенные в файле .gitmodules, без повторного запуска git submodule add вручную?

Обновление: это мой файл .gitmodules:

[submodule "vim-pathogen"]
    path = vim-pathogen
    url = git://github.com/tpope/vim-pathogen.git
[submodule "bundle/python-mode"]
    path = bundle/python-mode
    url = git://github.com/klen/python-mode.git
[submodule "bundle/vim-fugitive"]
    path = bundle/vim-fugitive
    url = git://github.com/tpope/vim-fugitive.git
[submodule "bundle/ctrlp.vim"]
    path = bundle/ctrlp.vim
    url = git://github.com/kien/ctrlp.vim.git
[submodule "bundle/vim-tomorrow-theme"]
    path = bundle/vim-tomorrow-theme
    url = git://github.com/chriskempson/vim-tomorrow-theme.git

и вот список этого каталога:

drwxr-xr-x  4 evgeniuz 100 4096 июня  29 12:06 .
drwx------ 60 evgeniuz 100 4096 июня  29 11:43 ..
drwxr-xr-x  2 evgeniuz 100 4096 июня  29 10:03 autoload
drwxr-xr-x  7 evgeniuz 100 4096 июня  29 12:13 .git
-rw-r--r--  1 evgeniuz 100  542 июня  29 11:45 .gitmodules
-rw-r--r--  1 evgeniuz 100  243 июня  29 11:18 .vimrc

так что, безусловно, он находится на высшем уровне. каталог git не изменяется, выполняется только git init


person evgeniuz    schedule 29.06.2012    source источник
comment
Присутствуют ли уже подмодули в том смысле, что если вы переходите в какой-либо каталог подмодуля, там присутствуют файлы, и git rev-parse --show-toplevel дает вам подмодуль, а не каталог супермодуля?   -  person Mark Longair    schedule 29.06.2012
comment
нет, директорий подмодулей нет. представьте, что эта папка полностью пуста, есть только .gitmodules файл   -  person evgeniuz    schedule 29.06.2012
comment
А, я понимаю, в чем проблема - я обновил свой ответ.   -  person Mark Longair    schedule 29.06.2012


Ответы (7)


git submodule init рассматривает для инициализации только подмодули, которые уже находятся в индексе (т. Е. «Поставлены»). Я бы написал короткий сценарий, который анализирует .gitmodules, и для каждой пары url и path запускается:

git submodule add <url> <path>

Например, вы можете использовать следующий сценарий:

#!/bin/sh

set -e

git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
    while read path_key path
    do
        url_key=$(echo $path_key | sed 's/\.path/.url/')
        url=$(git config -f .gitmodules --get "$url_key")
        git submodule add $url $path
    done

Это основано на том, как сам git-submodule.sh script сам анализирует .gitmodules файл.

person Mark Longair    schedule 29.06.2012
comment
ты имеешь ввиду git submodule update --init? все эти команды незаметно завершаются ошибкой. Похоже, что git не видит модули, определенные только в .gitmodules - person evgeniuz; 29.06.2012
comment
Видите ли вы какие-либо выходные данные от git config --list | grep submodule после git submodule init? (Как сказано в документации, git submodule init должен инициализировать подмодули, т.е. зарегистрировать каждое имя подмодуля и URL-адрес, найденные в .gitmodules, в .git/config.) - person Mark Longair; 29.06.2012
comment
Нет, ничего. git config --list дает только стандартные значения, без упоминания подмодулей - person evgeniuz; 29.06.2012
comment
@Shark: это странно - не могли бы вы обновить свой вопрос содержимым вашего .gitmodules файла? Кроме того, ваш .gitmodules файл определенно находится на верхнем уровне вашего репозитория, а не в подкаталоге? - person Mark Longair; 29.06.2012
comment
Спасибо, я подумал, что есть более общий способ, чем сценарий :) - person evgeniuz; 29.06.2012
comment
@Shark: да, я тоже - и я вполне могу упустить что-то очевидное! Однако я думаю, что идея инициализации из .gitmodules заключается в том, что он будет зафиксирован в той же фиксации (или близко к той же фиксации), что и тот, который добавил подмодули в дерево, поэтому наличие только .gitmodules, но без подмодулей в индексе , это необычная ситуация. - person Mark Longair; 29.06.2012
comment
git submodule update --init -f - будет постоянно форкнуть подмодули к немуs dirs. - person iegik; 18.01.2013
comment
Спасибо за сценарий, @MarkLongair! - person James Conroy-Finn; 02.11.2016
comment
Спасибо за решение! Удобство использования git - худшее, что я когда-либо видел! - person DenisKolodin; 02.11.2016
comment
Одна из проблем здесь заключается в том, что, поскольку исходный .git недоступен, вы не знаете, к какому идентификатору фиксации был привязан подмодуль, это затрудняет получение точной версии подмодуля, которая была у вас в исходном репозитории. - person CMCDragonkai; 05.01.2017
comment
Есть ли у кого-нибудь решение для разбора ветки в .gitmodules файле и добавления подмодуля с помощью правильной ветки? - person Fred Stark; 27.07.2017
comment
К сожалению, если подмодуль содержит пробелы, например, после git submodule add URL 'path with spaces', это решение не сработает. Возможно, попробуйте что-нибудь вроде git config -f .gitmodules -z --get-regexp '^submodule\..*\.path$' | sed -z 's/\n.*$//' | tr '\0' '\n' | while read keys; .. - person Tino; 09.11.2017
comment
Если некоторые подмодули уже находятся в индексе, сценарий завершится. Чтобы этого избежать: git submodule add $ url $ path || истинный - person schwart; 19.11.2018
comment
Это было очень полезно. Спасибо!! - person pztrick; 16.04.2021

Расширяя ответ @Mark Longair, я написал сценарий bash для автоматизации шагов 2 и 3 следующего процесса:

  1. Клонируйте "шаблонное" репо, чтобы начать новый проект
  2. Удалите папку .git и повторно инициализируйте как новое репо.
  3. Повторно инициализировать подмодули, запрашивая ввод перед удалением папок.

#!/bin/bash

set -e
rm -rf .git
git init

git config -f .gitmodules --get-regexp '^submodule\..*\.path$' > tempfile

while read -u 3 path_key path
do
    url_key=$(echo $path_key | sed 's/\.path/.url/')
    url=$(git config -f .gitmodules --get "$url_key")

    read -p "Are you sure you want to delete $path and re-initialize as a new submodule? " yn
    case $yn in
        [Yy]* ) rm -rf $path; git submodule add $url $path; echo "$path has been initialized";;
        [Nn]* ) exit;;
        * ) echo "Please answer yes or no.";;
    esac

done 3<tempfile

rm tempfile

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

Перенос вывода из git config в цикл чтения вызывал проблемы с приглашением для ввода, поэтому вместо этого он выводит его во временный файл. Любые улучшения в моем первом сценарии на bash были бы очень кстати :)


Большое спасибо Марку, https://stackoverflow.com/a/226724/193494, bash: вложенное интерактивное чтение в цикле, которое также использует чтение, и tnettenba @ chat.freenode.net за то, что помогли мне прийти к этому решению!

person Kevin C.    schedule 08.03.2013

Расширение отличного ответа @Mark Longair, чтобы добавить подмодуль, уважающий имя ветки и репо.

#!/bin/sh

set -e

git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
    while read path_key path
    do
        name=$(echo $path_key | sed 's/\submodule\.\(.*\)\.path/\1/')
        url_key=$(echo $path_key | sed 's/\.path/.url/')
        branch_key=$(echo $path_key | sed 's/\.path/.branch/')
        url=$(git config -f .gitmodules --get "$url_key")
        branch=$(git config -f .gitmodules --get "$branch_key" || echo "master")
        git submodule add -b $branch --name $name $url $path || continue
    done
person mauron85    schedule 12.11.2018
comment
Это отлично сработало, однако оно также дублировало каждую запись в файле .gitmodules, который требует некоторой ручной очистки постфактум. - person Marcus Ottosson; 18.08.2020

У меня была аналогичная проблема. git submodule init молча терпел неудачу.

Когда я это сделал:

git submodule add <url> <path>

Я получил:

The following path is ignored by one of your .gitignore files: ...

Я думаю, что причиной могут быть пути .gitignore (d).

person Henry    schedule 27.06.2014

Обновленная версия скрипта от @ mark-longair. Этот также поддерживает ветки, обрабатывает случай, когда некоторые из подмодулей уже существуют в .git/config, и при необходимости выполняет резервное копирование существующих каталогов с тем же именем, что и пути к подмодулям.

git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
    while read path_key path
    do
        url_key=$(echo $path_key | sed 's/\.path/.url/');
        branch_key=$(echo $path_key | sed 's/\.path/.branch/');
        # If the url_key doesn't yet exist then backup up the existing
        # directory if necessary and add the submodule
        if [ ! $(git config --get "$url_key") ]; then
            if [ -d "$path" ] && [ ! $(git config --get "$url_key") ]; then
                mv "$path" "$path""_backup_""$(date +'%Y%m%d%H%M%S')";
            fi;
            url=$(git config -f .gitmodules --get "$url_key");
            # If a branch is specified then use that one, otherwise
            # default to master
            branch=$(git config -f .gitmodules --get "$branch_key");
            if [ ! "$branch" ]; then branch="master"; fi;
            git submodule add -f -b "$branch" "$url" "$path";
        fi;
    done;

# In case the submodule exists in .git/config but the url is out of date

git submodule sync;

# Now actually pull all the modules. I used to use this...
#
# git submodule update --init --remote --force --recursive
# ...but the submodules would check out in detached HEAD state and I 
# didn't like that, so now I do this...

git submodule foreach --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)';
person bokov    schedule 07.01.2020

Я знаю, что это было давно, но я хочу поделиться этой версией, которая вызывает git config только один раз, не требует скрипта, а также обрабатывает ветки:

git config -f .gitmodules --get-regexp '^submodule\.' | perl -lane'
$conf{$F[0]} = $F[1]}{
@mods = map {s,\.path$,,; $_} grep {/\.path$/} keys(%conf);
sub expand{$i = shift; map {$conf{$i . $_}} qw(.path .url .branch)}
for $i (@mods){
    ($path, $url, $branch) = expand($i);
    print(qq{rm -rf $path});
    print(qq{git submodule add -b $branch $url $path});
}
'

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

Это работает с простым копированием и вставкой в ​​консоли, но вставка в сценарий оболочки должна быть тривиальной.

пример вывода:

rm -rf third-party/dht
git submodule add -b post-0.25-transmission https://github.com/transmission/dht third-party/dht
rm -rf third-party/libutp
git submodule add -b post-3.3-transmission https://github.com/transmission/libutp third-party/libutp
rm -rf third-party/libb64
git submodule add -b post-1.2.1-transmission https://github.com/transmission/libb64 third-party/libb64
rm -rf third-party/libnatpmp
git submodule add -b post-20151025-transmission https://github.com/transmission/libnatpmp third-party/libnatpmp
rm -rf third-party/miniupnpc
git submodule add -b post-2.0.20170509-transmission https://github.com/transmission/miniupnpc third-party/miniupnpc
rm -rf third-party/libevent
git submodule add -b post-2.0.22-transmission https://github.com/transmission/libevent third-party/libevent
person alexgirao    schedule 13.03.2018

Для пользователей zsh попробуйте мою функцию, которая поддерживает DRY_RUN=1, чтобы увидеть, какие команды будут выполняться, и использует только git для анализа файла вместо sed.

gsub_file() {(
  set -eu

  cd "$(git rev-parse --show-toplevel)"

  submodule_paths=(
    "${(@f)$(git config --file ./.gitmodules --get-regexp "path" | awk '{ print $2 }')}"
  )
  submodule_urls=(
    "${(@f)$(git config --file ./.gitmodules --get-regexp "url" | awk '{ print $2 }')}"
  )
  submodule_branches=(
    "${(@f)$(git config --file ./.gitmodules --get-regexp "branch" | awk '{ print $2 }')}"
  )

  sh_c() {
    echo + "$*"
    if [ "${DRY_RUN-}" ]; then
      return
    fi
    eval "$@"
  }

  for (( i=1; i <= ${#submodule_paths[@]}; i++ )) do
    p="${submodule_paths[$i]}"
    if [ -d "$p" ]; then
      continue
    fi

    url="${submodule_urls[$i]}"
    unset b
    if [ "${submodule_branches[$i]-}" ]; then
      b="-b ${submodule_branches[$i]}" 
    fi
    sh_c git submodule add "${b-}" "$url" "$p"
  done
)}
person nhooyr    schedule 14.09.2020