Відновити підмодулі git з .gitmodules


77

У мене є папка, яка була git repo. Він містить деякі файли та файл .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робиться


Чи є підмодулі вже наявними, в тому сенсі, що якщо ви перейдете в будь-який каталог підмодулів, там будуть присутні файли, і git rev-parse --show-toplevelви отримаєте підмодуль, а не каталог "супермодуль"?
Mark Longair

ні, підмодульних папок немає. уявіть, ця папка повністю порожня лише за допомогою .gitmodulesфайлу
evgeniuz

Ах, я бачу, в чому проблема - я оновив свою відповідь.
Mark Longair

Відповіді:


104

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сценарій аналізує .gitmodulesфайл.


1
ти маєш на увазі git submodule update --init? всі ці команди мовчки відмовляються. Схоже, git не бачить модулів, визначених лише в.gitmodules
evgeniuz

Після git submodule init, чи бачите ви якийсь результат від git config --list | grep submodule? (Як зазначається в документації, git submodule initслід "Ініціалізувати підмодулі, тобто зареєструвати кожне ім’я та URL-адресу підмодуля, знайдені в .gitmodules .git/config").
Марк Лонґер

немає нічого. git config --listдає лише стандартні значення, жодної згадки про підмодулі
evgeniuz

@Shark: це дивно - чи могли б ви оновити своє питання вмістом вашого .gitmodulesфайлу? Крім того, ваш .gitmodulesфайл, безумовно, знаходиться на верхньому рівні вашого сховища, а не в підкаталозі?
Mark Longair

Дякую, я думав, що є більш загальний спосіб, ніж сценарій, правда :)
evgeniuz

15

Розширюючи відповідь @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: вкладене інтерактивне читання в циклі, який також використовує read , і tnettenba @ chat.freenode.net за допомогу мені прийти до цього рішення!


6

Розширення чудової відповіді @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

1
Це спрацювало чудово, однак він також продублював кожен запис у файлі .gitmodules, який вимагає певного очищення вручну.
Маркус Оттоссон,

3

У мене була подібна проблема. git submodule initпровалювався мовчки.

Коли я це зробив:

git submodule add <url> <path>

Я зрозумів, я отримав:

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

Я думаю, що причиною можуть бути шляхи .gitignore (d).


2

Оновлена ​​версія сценарію від @ 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)';

1

Я знаю, що це було давно, але я хочу поділитися цією версією, яка викликає 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

0

Для користувачів 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
)}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.