Git命令自动子树将项目与子模块合并?

时间:2011-03-24 23:49:55

标签: git git-submodules

我找到了Using Git, what's the best way to subtree merge an external project that has submodules?但是虽然相关,却没有回答我的问题。

假设我有我的父项目和我的子项目,其中包含子模块。如何将子项目子树合并到父项目的子目录中,并保持对子模块的引用有效?

我尝试过这样的事情:

git remote add -f child_remote git://github.com/me/child.git
git checkout -b child_branch child_remote/master
git submodule init
git submodule update
git checkout master
git read-tree --prefix=child_directory/ -u child_branch

但是read-tree丢失了子模块,而.gitmodules只出现在子目录中。当我尝试执行git submodule init和git submodule update时,我得到的错误如“在.gitmodules中没有找到子模块映射'对于路径'child_directory / submodule_directory'”。

我还尝试修改http://help.github.com/subtree-merge/处的说明以使用子模块,但无济于事。

我知道我可以通过手动修改.gitmodules文件来引用正确的路径,或者在没有子模块的Child项目中合并,然后将它们重新添加到Parent项目中来实现这一点,但我考虑这两种解决方案黑客攻击。我正在寻找可以自动运行的实际命令,而无需手动编辑任何内容。

2 个答案:

答案 0 :(得分:0)

您可以更改.gitmodules中的路径,然后执行git submodule init && git submodule update,也可以将子模块转换为子树。最后一个选项涉及某种脚本,因为没有自动的方法来执行此操作。

使用shell脚本时,您可以使用这些函数来获取子模块及其名称(来自git-submodules源代码):

# This lists the unmerged submodules
module_list()
{
    git ls-files --error-unmatch --stage -- "$@" |
    perl -e '
    my %unmerged = ();
    my ($null_sha1) = ("0" x 40);
    while (<STDIN>) {
        chomp;
        my ($mode, $sha1, $stage, $path) =
            /^([0-7]+) ([0-9a-f]{40}) ([0-3])\t(.*)$/;
        next unless $mode eq "160000";
        if ($stage ne "0") {
            if (!$unmerged{$path}++) {
                print "$mode $null_sha1 U\t$path\n";
            }
            next;
        }
        print "$_\n";
    }
    '
}

# This gets the submodule's name from the .gitignore file in your webroot
module_name()
{
    # Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
    re=$(printf '%s\n' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
    name=$( git config -f $2/.gitmodules --get-regexp '^submodule\..*\.path$' |
        sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
    test -z "$name" &&
    die "$(eval_gettext "No submodule mapping found in .gitmodules for path '\$path'")"
    echo "$name"
}

然后,当您迭代结果时,您可以提取用于子树的名称和URL。

module_list | while read mode sha1 stage path
do
    #the path as your repo sees it
    full_path=$path

    #The path as .gitmodules in your child_directory sees it
    fake_path=${path[*]/[child_directory]\//}

    #Get the name of the submodule
    stupid_name=$(module_name "$fake_path" "[child_directory]")

    #Get the git URL from the .gitmodules file
    git_url=$(git config -f [child_directory]/.gitmodules submodule."$stupid_name".url)

    #Gets the clean name to use as remote name
    new_remote=$(echo $git_url | cut -d. -f2 | cut -d/ -f2- | cut -d/ -f2)

    #Remove the submodules folder
    rm -rf $path 

    #Add the subtree
    git remote add -f $new_remote $git_url
    git merge -s ours --no-commit $new_remote/master 
    git read-tree --prefix=$full_path/ -u $new_remote/master
    git commit -m "$new_remote merged in $full_path"
done 

我确信有更好的方法可以做到这一点,但它可以让我知道要做什么

答案 1 :(得分:0)

为什么不在项目中使用子模块?你现在可以全面使用'git submodule update --recursive`。

还有git-slave可以帮助您跨子模块自动化分支。