【问题标题】:Restore git submodules from .gitmodules从 .gitmodules 恢复 git 子模块
【发布时间】:2012-06-30 19:19:27
【问题描述】:

我有一个文件夹,它是一个 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 给你的是子模块而不是“超级模块”目录?
  • 不,子模块目录不存在。想象一下这个文件夹完全是空的,只有.gitmodules 文件
  • 啊,我明白问题所在了——我已经更新了我的答案。

标签: git git-submodules


【解决方案1】:

git submodule init 只考虑已经在索引中的子模块(即“staged”)进行初始化。我会编写一个解析.gitmodules 的简短脚本,并为每个urlpath 对运行:

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 文件。

【讨论】:

  • 你的意思是git submodule update --init?所有这些命令都会默默地失败。看起来 git 没有看到仅在 .gitmodules 中定义的模块
  • git submodule init 之后,您是否看到来自git config --list | grep submodule 的任何输出? (正如文档所说,git submodule init 应该“初始化子模块,即将 .gitmodules 中找到的每个子模块名称和 url 注册到 .git/config”。)
  • 不,没什么。 git config --list 只给出标准值,没有提到子模块
  • @Shark:这很奇怪——你能用.gitmodules 文件的内容更新你的问题吗?另外,您的 .gitmodules 文件是否肯定位于存储库的顶层,而不是子目录?
  • @Shark:是的,我也是——我很可能遗漏了一些明显的东西!但是,我认为从.gitmodules 初始化的想法是,它将在与将子模块添加到树中的提交相同的提交(或接近相同的提交)中提交,因此只有.gitmodules,但没有索引中的子模块,是一种不寻常的情况。
【解决方案2】:

扩展 @Mark Longair 的回答,我编写了一个 bash 脚本来自动执行以下过程的第 2 步和第 3 步:

  1. 克隆“样板”存储库以开始新项目
  2. 删除 .git 文件夹并重新初始化为新的 repo
  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 脚本进行任何改进:)


非常感谢 Mark、https://stackoverflow.com/a/226724/193494bash: nested interactive read within a loop that's also using read 和 tnettenba @chat.freenode.net 帮助我找到了这个解决方案!

【讨论】:

    【解决方案3】:

    我遇到了类似的问题。 git submodule init 默默地失败了。

    当我这样做时:

    git submodule add &lt;url&gt; &lt;path&gt;

    我明白了:

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

    我认为 .gitignore(d) 路径可能是原因。

    【讨论】:

      【解决方案4】:

      我知道已经有一段时间了,但我想分享这个版本,它只调用一次 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});
      }
      '
      

      唯一的副作用是命令的输出,不会执行任何操作,因此您可以在提交之前进行审核。

      这适用于在控制台进行简单的复制和粘贴,但放入 shell 脚本应该很简单。

      示例输出:

      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
      

      【讨论】:

        【解决方案5】:

        扩展优秀的@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
        

        【讨论】:

        • 这很好用,但是它也复制了 .gitmodules 文件中的每个条目,事后需要手动清理。
        【解决方案6】:

        @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)';
        

        【讨论】:

          【解决方案7】:

          对于 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
          )}
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-06-30
            • 1970-01-01
            • 1970-01-01
            • 2013-09-07
            • 2012-05-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多