Git子树和多个目录

13

我有一个相当庞大的git仓库,其中有一个目录用于维护库代码。该目录包含许多子目录。

repo
+--- lib
|    +--- A
|    +--- B
...
|    +--- Z

现在假设我想要开源子目录 A,...,M 并保持子目录 N,...,Z 闭源。同时假设我想要:

  • A,...,M 放在一个开源仓库中。原因是目录 A,...,M 有相互依赖,将它们分成单独的仓库会很混乱。
  • 保留我闭源仓库的结构。例如,我可以创建子目录 lib/publib/pvt,但这会产生级联效应,需要在其他地方修改引用或需要大量符号链接 (lib/A -> lib/pub/A)。
  • 有一种类似于 git subtree 的解决方案,可以让我在我的闭源仓库或开源仓库中修改代码,并且可以轻松同步两个仓库之间的更改。

我已经在 stackoverflow 和 google 中搜索了解决方案,但似乎没有明显的解决方法。从概念上讲,git subtree 应该能够做到这一点,但它只能处理单个子目录。

我已经查看了 git-subtree 脚本并打算修改它。

https://github.com/git/git/blob/master/contrib/subtree/git-subtree.sh

我的理解是,如果我修改 subtree_for_commit() 函数,我就可以让 git subtree split 考虑多个目录进行拆分。但是,我对 git 的了解还不足以在不破坏东西的情况下修改它。

如果您有任何解决上述问题或修改 git-subtree 的其他指南,请告诉我。


1
我也有同样的需求,因为我的子目录A、B、C都是相互关联的,我想将它们与子目录D、E、F分离开来。可能需要使用filter-branch命令。 - enorl76
1
这个回答解决了你的问题吗?将许多子目录分离成一个新的、独立的Git仓库 - Josh Correia
4个回答

5

将子树与父项目中的文件分离

这似乎是一个常见的请求,但当文件夹像这样混合在一起时,我认为没有一个简单的答案。

我建议的将混合在其他文件夹中的库拆分出来的一般方法如下:

  1. Make a branch with the new root for the library directories:

    git subtree split -P lib/ -b temp-br
    git checkout temp-br
    
  2. Then use something to re-write history to remove the parts that aren't part of the library. I'm not expert on this but I was able to experiment and found something like this to work:

    git filter-branch --tag-name-filter cat --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch N O P Q R S T U V W X Y Z' HEAD
    

    Note: You might need to delete the back-up made by filter-branch if you make successive commands.

    git update-ref -d refs/original/refs/heads/temp-br
    
  3. Lastly, just create a new repo for the library and pull in everything that's left:

    cd <new-lib-repo>
    git init
    git pull <original-repo> temp-br
    

1
这个回答和问题有关系吗?他想要将多个子目录 git-subtree-split 到一个单独的仓库中... - enorl76
@enorl76,我认为它可以做到。filter-branch 将会删除在新仓库中不需要的目录。 - Agnel Kurian
当你执行filter-branch命令时,它会尝试在一个新的分支中备份你原来的内容,因为该命令是具有破坏性的。如果你再次运行该命令,以步骤方式过滤出其他内容,它会再次尝试备份,但由于已经有了备份,它会报错。如果你知道自己已经采取措施保留了原始分支,那么你可以忽略这个预防措施,update-ref命令将删除备份分支。 - johnb003

2

使用git subtree add

参见Git子树拆分两个目录,我认为您可以将该技术用于多个目录,甚至是多个仓库。

cd /repos/big-repo

# split out A..M branches
for N in {A..M}; do
  git subtree split --prefix=lib/$N --branch=split-$N
done

# create new repo
mkdir /repos/am-repo
cd /repos/am-repo
git init

# commit something or git-subtree add will complain and fail
touch .gitignore; git add .; git commit -m "begin history revision"

# split-in A..M branches
for N in {A..M}; do
  git subtree add --prefix=lib/$N ../big-repo split-$N
done

1
这不像将A到M作为单个存储库拆分出来那样运作。如果提交涉及A和M,则提交将在split-A和split-M分支中重复,而希望有一个包含A和M更改的单个提交。 - fdk1342
在使用git subtree add之后,我需要执行git filter-branch吗? - tibi

1

以下是基于git subtree的shell脚本,它比基于git filter-branch --tree-filter的解决方案快得多;副作用是会生成几个额外的git mvgit merge提交,并添加到最终的HEAD中。如果您对这些额外的空提交感到满意,可以尝试:

ids=0
lists=(\
    "a/b" \
    "c/d/e" \
)
# subtree each path
for dir in ${lists[@]}
do
    echo git subtree split -P $dir -b split_dir_$ids
    git subtree split -P $dir -b split_dir_$ids
    ((ids++))
done

# restore folder structure
for (( idx=0; idx < ${#lists[@]}; idx++ ))
do
    git checkout split_dir_$idx
    dir=${lists[$idx]}
    mkdir -p $dir
    dirPrefix=${$dir%%/*}
    find . -maxdepth 1 ! -name $dirPrefix -and ! -name '\.*' \
        -exec git mv {} $dir \;
done

# merge
git checkout split_dir_0
for (( idx=1; idx < ${#lists[@]}; idx++ ))
do
    git merge -q split_dir_$idx
done

git push -u `target remote` `target branch`

它会工作双向提交吗? - user1595858

-1

当您在目录src中有子目录和文件时,您想将其拆分为单独的存储库,然后成为子模块,答案并不多。假设您想要将dir2和file2移动到一个新的存储库srcpublic中,然后在原始存储库中执行以下操作:

git mv src/file2 src/dir2; git subtree split -P dir2 -b branch_dir2

在新的存储库中, subtree pull /dir2 branch_dir2; git mv dir2/file2 ../

新存储库: srcpublic - file2,dir2

原始存储库: src - file1,file2, dir1,dir2

当有数十个文件夹和文件时,将命令放入脚本中会很有帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接