此问题基于Detach subdirectory into separate Git repository
我想分离一对子,而不是分离一个子目录。例如,我当前的目录树如下所示:
/apps
/AAA
/BBB
/CCC
/libs
/XXX
/YYY
/ZZZ
而我想这样做:
/apps
/AAA
/libs
/XXX
--subdirectory-filter
的{{1}}参数将不起作用,因为它在第一次运行时除了给定目录之外的所有内容。我认为使用git filter-branch
参数可以使用所有不需要的文件(尽管很乏味),但是如果我尝试不止一次运行它,我会收到以下消息:
--index-filter
有什么想法吗? TIA
答案 0 :(得分:124)
不必处理子shell并使用ext glob(如kynan所建议的那样),尝试这种更简单的方法:
git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- apps/AAA libs/XXX' --prune-empty -- --all
答案 1 :(得分:32)
计划是将各个目录拆分为自己的存储库,然后将它们合并在一起。以下手动步骤不使用极客使用的脚本,而是易于理解的命令,可以帮助将额外的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
如果您有两个以上的文件夹,请继续。现在你将有两个新的和临时的git存储库。
通过合并应用和库来征服
3 - 准备全新的回购:
mkdir my-desired-repo
cd my-desired-repo
git init
您需要至少进行一次提交。如果应该跳过以下三行,您的第一个回购将立即出现在您的回购根下:
touch a_file_and_make_a_commit # see user's feedback
git add a_file_and_make_a_commit
git commit -am "at least one commit is needed for it to work"
提交临时文件后,后面部分中的merge
命令将按预期停止。
根据用户的反馈,您可以选择添加a_file_and_make_a_commit
或.gitignore
等,而不是添加README.md
等随机文件。
4 - 首先合并应用程序repo:
git remote add apps-repo ../apps-repo
git fetch apps-repo
git merge -s ours --no-commit apps-repo/master # see below note.
git read-tree --prefix=apps -u apps-repo/master
git commit -m "import apps"
现在,您应该在新存储库中看到 apps 目录。 git log
应显示所有相关的历史提交消息。
注意:正如Chris在评论中所述,对于git的较新版本(&gt; = 2.9),您需要使用--allow-unrelated-histories
指定git merge
5 - 以相同的方式合并libs repo:
git remote add libs-repo ../libs-repo
git fetch libs-repo
git merge -s ours --no-commit libs-repo/master # see above note.
git read-tree --prefix=libs -u libs-repo/master
git commit -m "import libs"
如果您有超过2个repos要合并,请继续。
答案 2 :(得分:26)
为什么要多次运行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 ...)
是必要的。 extglob
由您的shell而非索引过滤器评估,该过滤器使用sh
内置eval
(其中extglob
不可用)。有关详细信息,请参阅How do I enable shell options in git?。
答案 3 :(得分:19)
在这里回答我自己的问题......经过大量的反复试验。
我设法使用git subtree
和git-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
然后我创建了一个新的空存储库,并将最后两个导入/拼接到其中:
cd ..
mkdir newRepo
cd newRepo
git init
git-stitch-repo ../aaaRepo:apps/AAA ../xxxRepo:libs/XXX | git fast-import
这会创建两个分支master-A
和master-B
,每个分支都包含其中一个拼接回购的内容。将它们组合起来并进行清理:
git checkout master-A
git pull . master-B
git checkout master
git branch -d master-A
git branch -d master-B
现在我不太确定如何/何时发生这种情况,但在第一个checkout
和pull
之后,代码会神奇地合并到主分支中(任何关于这里发生的事情的见解是理解!)
所有内容似乎都按预期工作,但如果我查看newRepo
提交历史记录,则当变更集同时影响apps/AAA
和libs/XXX
时,会有重复项。如果有办法删除重复项,那么它将是完美的。
答案 4 :(得分:15)
我遇到了类似的问题,在回顾了这里列出的各种方法之后,我发现了git-filter-repo。在官方git文档here中,建议使用它作为git-filter-branch的替代方法。
要从现有存储库中的一部分目录创建新存储库,可以使用以下命令:
git filter-repo --path <file_to_keep>
通过链接来过滤多个文件/文件夹:
git filter-repo --path keepthisfile --path keepthisfolder/
因此,要回答原始问题,使用git-filter-repo您只需要以下命令:
git filter-repo --path apps/AAA/ --path libs/XXX/
答案 5 :(得分:7)
我已经编写了一个git过滤器来解决这个问题。 它有着名的git_filter,位于github:
https://github.com/slobobaby/git_filter
它基于优秀的libgit2。
我需要拆分一个包含很多提交的大型存储库(~100000),并且基于git filter-branch的解决方案需要几天才能运行。 git_filter花一点时间做同样的事情。
答案 6 :(得分:7)
git splits
是一个bash脚本,它是git branch-filter
的包装器,是基于jkeating's solution创建的git扩展名。
这完全是出于这种情况。对于您的错误,请尝试使用git splits -f
选项强制删除备份。因为git splits
在新分支上运行,所以它不会重写当前分支,因此备份是无关紧要的。有关更多详细信息,请参阅自述文件,请务必在回购的副本/克隆中使用它(以防万一!)。
git splits
。 将目录拆分为本地分支
#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
在某处创建一个空的仓库。我们假设我们在GitHub上创建了一个名为xyz
的空仓库,其路径为:git@github.com:simpliwp/xyz.git
推送到新的回购。
#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
将新创建的远程仓库克隆到新的本地目录中
#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
答案 7 :(得分:3)
呀。在后续调用-f
时使用filter-branch
标志强制覆盖备份以覆盖该警告。 :)否则我认为你有解决方案(即用filter-branch
一次消除不需要的目录。)
答案 8 :(得分:1)
git clone git@example.com:thing.git
cd thing
git fetch
for originBranch in `git branch -r | grep -v master`; do
branch=${originBranch:7:${#originBranch}}
git checkout $branch
done
git checkout master
git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- dir1 dir2 .gitignore' --prune-empty -- --all
git remote set-url origin git@example.com:newthing.git
git push --all
答案 9 :(得分:-4)
删除refs / original中.git目录下的备份,如消息所示。该目录已隐藏。