Git:多个项目可以使用相同的子模块工作副本吗?

Git:多个项目可以使用相同的子模块工作副本吗?,git,git-submodules,Git,Git Submodules,我是Git的新手。 比方说,我有两个git存储库,其中添加了相同的库作为子模块: /home/projects/project1/library_XYZ /home/projects/project2/library_XYZ 也就是说,我正在同时处理项目和库。当我对库进行更改时,比如在/home/projects/project1/library_XYZ中,我必须将这些更改推送到/home/projects/project2/library_XYZ中,使它们可用于project2,对吗?我认为

我是Git的新手。 比方说,我有两个git存储库,其中添加了相同的库作为子模块:

/home/projects/project1/library_XYZ
/home/projects/project2/library_XYZ
也就是说,我正在同时处理项目和库。当我对库进行更改时,比如在
/home/projects/project1/library_XYZ
中,我必须将这些更改推送到
/home/projects/project2/library_XYZ
中,使它们可用于
project2
,对吗?我认为这是不愉快的,原因有二:

  • 我将不得不构建两次
    library\u XYZ
  • 我有一个不必要的冗余,与实际的项目组织相矛盾
有没有办法让Git将子模块
library_XYZ
克隆到相同的本地目录,即使文件按如下方式组织

/home/projects/project1
/home/projects/project2
/home/projects/library_XYZ
library_XYZ
仍然是这两个项目的子模块


我认为这可能与有关,这是没有答案的,尽管我的设置有些不同。

将共享依赖项设置为子模块很容易。git submodule命令不会自动执行此操作,但子模块只不过是一个嵌套的存储库——git不要求任何实际的存储库或其工作树位于任何特定位置

设置libraryXYZ repo以用作共享子模块

# a submodule is just a repository. We're going to share this one.
git clone u://r/libraryXYZ 

# and keep its worktree right here:
( cd libraryXYZ; git config core.worktree .. )
然后,从任意位置使用子模块克隆项目,并将其设置为使用共享模块:

git clone u://r/project1
cd project1
git submodule init
echo gitdir: path/to/shared/libraryXYZ/.git > libraryXYZ/.git
现在
project1
libraryXYZ
子模块将使用共享的
libraryXYZ
repo和工作树

将构建系统设置为使用相同的工作树,就完成了。当然,您可以让git告诉您在任何给定的回购协议中这些内容的位置:

# for example to see where all a project's submodules' parts are kept
git submodule foreach git rev-parse --git-dir --show-toplevel

# or for just one:
git --git-dir=$project1/libraryXYZ/.git rev-parse --git-dir --show-toplevel

(后期编辑:值得记住,这可能会使从一个项目执行
git子模块更新有点太容易,而没有意识到您还更改了共享依赖项的每个其他项目的构建环境。)

自git 2.5以来,您可以执行以下操作:

  • 删除/home/projects/project2/library\u XYZ
  • 删除/home/projects/project2/.git/modules/library\u XYZ
  • cd/home/projects/project1/library\u XYZ
  • 在/home/projects/project1/library\u XYZ中创建分支项目2
  • 运行
    git工作树添加.././project2/library\u XYZ project2

  • 现在,/home/projects/project1/.git/modules/library_XYZ在两个项目之间共享。

    我和你一样有一些问题:作为子模块的大型通用实用程序库,以及许多依赖它的项目。我不想为实用程序库的每个实例创建单独的签出

    jthill提出的解决方案很好,但它只解决了问题的前一半,即如何让git满意

    缺少的是如何让构建系统满意,它需要实际的文件,而不关心gitlink引用

    但是如果你把他的想法和一个符号链接结合起来,你就会得到你想要的

    为了实现这一点,让我们从您的示例中的项目开始

    /home/projects/project1
    /home/projects/project2
    /home/projects/library_XYZ
    
    假设project1和project2都已将library_XYZ作为子模块添加,并且当前所有三个项目都包含library_XYZ的完整签出

    要用指向库签出的共享符号链接替换库子模块的完全签出,请执行以下操作:

    sharedproject="/home/projects/library_XYZ"
    superproject="/home/projects/project1"
    submodule="library_XYZ"
    cd "$superproject"
    (cd -- "$submodule" && git status) # Verify that no uncommited changes exist!
    (cd -- "$submodule" && git push -- "$sharedproject") # Save any local-only commits
    git submodule deinit -- "$submodule" # Get rid of submodule's check-out
    rm -rf .git/modules/"$submodule" # as well as of its local repository
    mkdir -p .submods
    git mv -- "$submodule" .submods/
    echo "gitdir: $sharedproject.git" > ".submods/$submodule/.git"
    ln -s -- "$sharedproject" "$submodule"
    echo "/$submodule" >> .gitignore
    
    然后对/home/projects/project2重复与$superproject相同的步骤

    下面是对所做工作的解释:

    首先,使用“git submodule deinit”删除子模块签出,留下库作为空目录。确保在执行此操作之前提交任何更改,因为这将删除签出

    接下来,我们使用“git push”to/home/projects/library_XYZ将所有尚未推送到共享项目的本地提交保存到签出

    如果由于未为此设置远程规范或参照规范而无法执行此操作,则可以执行以下操作:

    (saved_from=$(basename -- "$superproject"); \
     cd -- "$submodule" \
     && git push -- "$sharedproject" \
                 "refs/heads/*:refs/remotes/$saved_from/*")
    
    这将子模块本地存储库的所有分支的备份保存为/home/projects/library_XYZ中的远程分支。$superproject目录的basename将用作远程项目的名称,即。E本例中的project1或project2

    当然,在/home/projects/library_XYZ中不存在真正的远程名称,但是保存的分支将显示为在那里执行“git branch-r”时显示的分支

    作为保护措施,上述命令中的refspec不会以“+”开头,因此“git push”不会意外地覆盖/home/projects/library_XYZ中已经存在的任何分支

    接下来,将删除.git/modules/library_XYZ以节省空间。我们可以这样做,因为我们不再需要使用“git submodule init”或“git submodule update”。这种情况是因为我们将与子模块共享/home/projects/library_XYZ的签出目录和.git目录,从而避免两者的本地副本

    然后让git将空的子模块目录重命名为“.submods/library_XYZ”,这是一个项目中的文件永远不会直接使用的(隐藏)目录

    接下来,我们将jthill的部分解决方案应用于该问题,并在.submods/library_XYZ中创建一个gitlink文件,使git将/home/projects/library_XYZ作为子模块的工作树和git repo

    现在有了新的东西:我们创建了一个相对名称为“library_XYZ”的符号链接,它指向/home/projects/library_XYZ。此符号链接将不受版本控制,因此我们将其添加到.gitignore文件中

    project1和project2中的所有生成文件都将使用库_XYZ符号链接,就像它是一个普通的子目录一样,但实际上可以从/home/projects/library _XYZ中的工作树中找到文件

    除了git,没有人真正使用.submods/library_XYZ

    然而
    $ test ! -e library_XYZ && ln -s .submods/library_XYZ
    
    library_XYZ/libsharedutils.a:
            cd library_XYZ && $(MAKE) libsharedutils.a
    
    library_XYZ/libsharedutils.a:
            test ! -e library_XYZ && ln -s .submods/library_XYZ
            cd library_XYZ && $(MAKE) libsharedutils.a
    
    (n=create_missing_dirs.sh && cat > "$n" << 'EOF' && chmod +x -- "$n")
    #! /bin/sh
    for dir in .submods/*
    do
            sym=${dir#*/}
            if test -d "$dir" && test ! -e "$sym"
            then
                    echo "Creating $sym"
                    ln -snf -- "$dir" "$sym"
            fi
    done
    EOF
    
    sharedproject="/home/projects/library_XYZ"
    submodule="library_XYZ"
    ln -sn -- "$sharedproject" "$submodule"
    echo "gitdir: $sharedproject.git" > ".submods/$submodule/.git"
    
    sudo mount -o bind /home/projects/library_XYZ /home/projects/project1/library_XYZ
    
    cd /home/projects/project1/
    git submodule deinit library_XYZ
    rm -rf .git/modules/library_XYZ