将许多子目录分离成一个新的单独的Git仓库

这个问题是基于Detach子目录到单独的Git仓库

而不是分离一个单独的子目录,我想分离一对夫妇。 例如,我目前的目录树看起来像这样:

/apps /AAA /BBB /CCC /libs /XXX /YYY /ZZZ 

我想这个:

 /apps /AAA /libs /XXX 

git filter-branch--subdirectory-filter参数将不起作用,因为它在第一次运行时除去给定目录的所有内容。 我认为对所有不需要的文件使用--index-filter参数会起作用(尽pipe很乏味),但是如果我尝试多次运行它,我会收到以下消息:

 Cannot create a new backup. A previous backup already exists in refs/original/ Force overwriting the backup with -f 

有任何想法吗? TIA

而不是必须处理一个子shell和使用ext glob(如kynanbuild议),试试这个更简单的方法:

 git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- apps/AAA libs/XXX' --prune-empty -- --all 

你为什么要多次运行filter-branch ? 你可以在一次扫描中完成所有的工作,所以不需要强制执行(注意你需要在你的shell中启用extglob才能工作):

 git filter-branch --index-filter "git rm -r -f --cached --ignore-unmatch $(ls -xd apps/!(AAA) libs/!(XXX))" --prune-empty -- --all 

这应该摆脱不需要的子目录中的所有变化,并保持所有的分支和提交(除非它们只影响修剪后的子目录中的文件,凭借--prune-empty ) – 没有重复提交等问题。

在这个操作之后,不需要的目录将被列为未被跟踪的git status

$(ls ...)是必要的st extglob由您的shell而不是使用sh内置eval (其中extglob不可用)的索引filtereval 。 请参阅如何在git中启用shell选项? 进一步的细节。

在这里回答我自己的问题…经过大量的试验和错误。

我设法使用git subtreegit-stitch-repo 。 这些说明基于:

首先,我把我想保留的目录放到他们自己的单独的存储库中:

 cd origRepo git subtree split -P apps/AAA -b aaa git subtree split -P libs/XXX -b xxx cd .. mkdir aaaRepo cd aaaRepo git init git fetch ../origRepo aaa git checkout -b master FETCH_HEAD cd .. mkdir xxxRepo cd xxxRepo git init git fetch ../origRepo xxx git checkout -b master FETCH_HEAD 

然后我创build一个新的空的存储库,并导入/缝合最后两个到它:

 cd .. mkdir newRepo cd newRepo git init git-stitch-repo ../aaaRepo:apps/AAA ../xxxRepo:libs/XXX | git fast-import 

这创build了两个分支, master-Amaster-B ,每个分支持有一个拼接仓库的内容。 把它们和清理结合起来:

 git checkout master-A git pull . master-B git checkout master git branch -d master-A git branch -d master-B 

现在我不太清楚这种情况是怎么发生的,但是在第一次checkoutpull ,代码奇迹般地融入了主分支(对于这里所发生的事情的任何了解都是值得赞赏的!

一切似乎都按预期工作,除了如果我查看newRepo提交历史logging,当变更集影响到apps/AAAlibs/XXX时,重复。 如果有办法删除重复,那么这将是完美的。

手动步骤与简单的git命令

计划是将各个目录分成自己的回购站,然后将它们合并在一起。 下面的手动步骤并没有使用通俗易懂的脚本,而是易于理解的命令,并且可以帮助将额外的N个子文件夹合并到另一个单独的存储库中。

划分

假设您的原始回购是: original_repo

1 – 拆分应用程序:

 git clone original_repo apps-repo cd apps-repo git filter-branch --prune-empty --subdirectory-filter apps master 

2 – 拆分库

 git clone original_repo libs-repo cd libs-repo git filter-branch --prune-empty --subdirectory-filter libs master 

继续如果你有超过2个文件夹。 现在你将有两个新的和临时的git仓库。

通过合并应用程序和库来征服

3 – 准备全新的回购:

 mkdir my-desired-repo cd my-desired-repo git init 

而且你将需要至less做一个提交。 如果以下三行应该跳过,您的第一个回购将立即出现在您的回购的根目录下:

 touch a_file_and_make_a_commit git add a_file_and_make_a_commit git commit -am "at least one commit is needed for it to work" 

随着临时文件提交,后面的部分中的merge命令将按预期停止。

4 – 先合并应用程序回购:

 git remote add apps-repo ../apps-repo git fetch apps-repo git merge -s ours --no-commit apps-repo/master git read-tree --prefix=apps -u apps-repo/master git commit -m "import apps" 

现在你应该看到你的新存储库中的应用程序目录。 git log应该显示所有相关的历史提交信息。

5 – 以相同的方式合并libs回购:

 git remote add libs-repo ../libs-repo git fetch libs-repo git merge -s ours --no-commit libs-repo/master git read-tree --prefix=libs -u libs-repo/master git commit -m "import libs" 

继续如果你有两个以上的回购合并。

参考: 用git合并另一个仓库的子目录

使用“混帐拆分”git扩展名

git splits是一个bash脚本,它是git branch-filter一个包装git branch-filter ,基于jkeating的解决scheme ,我创build了一个git扩展。

这是完全适合这种情况。 对于你的错误,尝试使用git splits -f选项来强制删除备份。 由于git splits在一个新的分支上运行,它不会重写你当前的分支,所以备份是无关紧要的。 请参阅自述文件以获取更多详细信息,并确保将其用于您的回购副本(以防万一!)

  1. 安装git splits
  2. 拆分目录到本地分支#change into your repo's directory cd /path/to/repo #checkout the branch git checkout XYZ
    #split multiple directories into new branch XYZ git splits -b XYZ apps/AAA libs/ZZZ
    #change into your repo's directory cd /path/to/repo #checkout the branch git checkout XYZ
    #split multiple directories into new branch XYZ git splits -b XYZ apps/AAA libs/ZZZ

  3. 在某处创build一个空的回购。 我们假设我们在GitHub上创build了一个名为xyz的空回购,其path为: git@github.com:simpliwp/xyz.git

  4. 推到新的回购。 #add a new remote origin for the empty repo so we can push to the empty repo on GitHub git remote add origin_xyz git@github.com:simpliwp/xyz.git #push the branch to the empty repo's master branch git push origin_xyz XYZ:master

  5. 将新创build的远程仓库克隆到新的本地目录中
    #change current directory out of the old repo cd /path/to/where/you/want/the/new/local/repo #clone the remote repo you just pushed to git clone git@github.com:simpliwp/xyz.git

我写了一个gitfilter来解决这个问题。 它有git_filter的奇妙名字,位于github这里:

https://github.com/slobobaby/git_filter

它基于优秀的libgit2。

我需要分裂一个大型的存储库提交了许多提交(~100000)和基于git filter-branch的解决scheme花了几天的时间来运行。 git_filter需要一分钟来做同样的事情。

是啊。 在后续调用filter-branch强制使用-f标志覆盖备份以覆盖该警告。 :)否则,我认为你有解决scheme(即,一次根除filter-branch根除一个不需要的目录)。

按照消息build议,删除refs / original中.git目录下的备份。 目录是隐藏的。