将许多子目录分离到一个新的、单独的 Git 存储库中
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2982055/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Detach many subdirectories into a new, separate Git repository
提问by prisonerjohn
This question is based on Detach subdirectory into separate Git repository
这个问题是基于Detach 子目录到单独的 Git 存储库
Instead of detaching a single subdirectory, I want to detach a couple. For example, my current directory tree looks like this:
我想分离一对,而不是分离单个子目录。例如,我当前的目录树如下所示:
/apps
/AAA
/BBB
/CCC
/libs
/XXX
/YYY
/ZZZ
And I would like this instead:
我想要这个:
/apps
/AAA
/libs
/XXX
The --subdirectory-filter
argument to git filter-branch
won't work because it gets rid of everything except for the given directory the first time it's run. I thought using the --index-filter
argument for all unwanted files would work (albeit tedious), but if I try running it more than once, I get the following message:
的--subdirectory-filter
参数git filter-branch
将不起作用,因为它在第一次运行时清除了给定目录之外的所有内容。我认为--index-filter
对所有不需要的文件使用该参数会起作用(尽管很乏味),但是如果我尝试多次运行它,我会收到以下消息:
Cannot create a new backup.
A previous backup already exists in refs/original/
Force overwriting the backup with -f
Any ideas? TIA
有任何想法吗?TIA
采纳答案by prisonerjohn
Answering my own question here... after a lot of trial and error.
在这里回答我自己的问题......经过大量的反复试验。
I managed to do this using a combination of git subtree
and git-stitch-repo
. These instructions are based on:
我管理这个使用的组合做git subtree
和git-stitch-repo
。这些说明基于:
- Sharing code between projects with git subtree – http://psionides.jogger.pl/2010/02/04/sharing-code-between-projects-with-git-subtree/
- The Right Tool for the Job: git-stitch-repo – http://ifup.org/2009/02/07/the-right-tool-for-the-job-git-stitch-repo/
- 使用 git subtree 在项目之间共享代码 – http://psionides.jogger.pl/2010/02/04/sharing-code-between-projects-with-git-subtree/
- 适合工作的工具:git-stitch-repo – http://ifup.org/2009/02/07/the-right-tool-for-the-job-git-stitch-repo/
First, I pulled out the directories I wanted to keep into their own separate repository:
首先,我将想要保留的目录提取到它们自己的单独存储库中:
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
I then created a new empty repository, and imported/stitched the last two into it:
然后我创建了一个新的空存储库,并将最后两个导入/缝合到其中:
cd ..
mkdir newRepo
cd newRepo
git init
git-stitch-repo ../aaaRepo:apps/AAA ../xxxRepo:libs/XXX | git fast-import
This creates two branches, master-A
and master-B
, each holding the content of one of the stitched repos. To combine them and clean up:
这将创建两个分支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
Now I'm not quite sure how/when this happens, but after the first checkout
and the pull
, the code magically merges into the master branch (any insight on what's going on here is appreciated!)
现在我不太确定这是如何/何时发生的,但是在第一个checkout
和之后pull
,代码神奇地合并到主分支中(对这里发生的事情的任何见解表示赞赏!)
Everything seems to have worked as expected, except that if I look through the newRepo
commit history, there are duplicates when the changeset affected both apps/AAA
and libs/XXX
. If there is a way to remove duplicates, then it would be perfect.
一切似乎都按预期工作,除了如果我查看newRepo
提交历史记录,当变更集影响apps/AAA
和libs/XXX
. 如果有一种方法可以删除重复项,那就太完美了。
回答by David Smiley
Instead of having to deal with a subshell and using ext glob (as kynan suggested), try this much simpler approach:
不必处理子外壳并使用 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
As mentioned by void.pointerin his/her comment, this will remove everything except apps/AAA
and libs/XXX
from current repository.
正如提到void.pointer在他/她的意见,这将删除一切,除了apps/AAA
和libs/XXX
从当前仓库。
Prune empty merge commits
修剪空合并提交
This leaves behind lots of empty merges. These can be removed by another pass as described by raphinessein his answer:
这留下了许多空的合并。这些可以通过raphinesse在他的回答中描述的另一遍移除:
git filter-branch --prune-empty --parent-filter \
'sed "s/-p //g" | xargs -r git show-branch --independent | sed "s/\</-p /g"'
?? Warning: The above must use GNU version of sed
and xargs
otherwise it would remove all commits as xargs
fails. brew install gnu-sed findutils
and then use gsed
and gxargs
:
?? 警告:的上述必须使用GNU版本sed
和xargs
否则,它会删除所有提交xargs
失败。brew install gnu-sed findutils
然后使用gsed
和gxargs
:
git filter-branch --prune-empty --parent-filter \
'gsed "s/-p //g" | gxargs git show-branch --independent | gsed "s/\</-p /g"'
回答by chfw
Manual steps with simple git commands
使用简单 git 命令的手动步骤
The plan is to split individual directories into its own repos, then merge them together. The following manual steps did not employ geek-to-use scripts but easy-to-understand commands and could help merge extra N sub-folders into another single repository.
计划是将单个目录拆分为自己的存储库,然后将它们合并在一起。以下手动步骤没有使用极客使用的脚本,而是使用易于理解的命令,可以帮助将额外的 N 个子文件夹合并到另一个存储库中。
Divide
划分
Let's assume your original repo is: original_repo
让我们假设您的原始仓库是:original_repo
1 - Split apps:
1 - 拆分应用程序:
git clone original_repo apps-repo
cd apps-repo
git filter-branch --prune-empty --subdirectory-filter apps master
2 - Split libs
2 - 拆分库
git clone original_repo libs-repo
cd libs-repo
git filter-branch --prune-empty --subdirectory-filter libs master
Continue if you have more than 2 folders. Now you shall have two new and temporary git repository.
如果您有 2 个以上的文件夹,请继续。现在您将拥有两个新的临时 git 存储库。
Conquerby Merging apps and libs
通过合并应用程序和库来征服
3 - Prepare the brand new repo:
3 - 准备全新的回购:
mkdir my-desired-repo
cd my-desired-repo
git init
And you will need to make at least one commit. If the following three lines should be skipped, your first repo will appear immediate under your repo's root:
并且您至少需要进行一次提交。如果应跳过以下三行,您的第一个 repo 将立即出现在您的 repo 根目录下:
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"
With the temp file commited, merge
command in later section will stop as expected.
提交临时文件后,merge
后面部分中的命令将按预期停止。
Taking from user's feedback, instead of adding a random file like a_file_and_make_a_commit
, you can choose to add a .gitignore
, or README.md
etc.
根据用户的反馈,a_file_and_make_a_commit
您可以选择添加一个.gitignore
,或README.md
等,而不是添加一个随机文件。
4 - Merge apps repo first:
4 - 首先合并应用程序仓库:
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"
Now you should see appsdirectory inside your new repository. git log
should show all relevant historical commit messages.
现在您应该在新存储库中看到apps目录。git log
应该显示所有相关的历史提交消息。
Note: as Chris noted below in the comments, for newer version(>=2.9) of git, you need to specify --allow-unrelated-histories
with git merge
注:克里斯的评论,为的git的新版本(> = 2.9)低于所指出的,你需要指定--allow-unrelated-histories
用git merge
5 - Merge libs repo next in the same way:
5 - 接下来以相同的方式合并库库:
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"
Continue if you have more than 2 repos to merge.
如果要合并的存储库超过 2 个,请继续。
Reference: Merge a subdirectory of another repository with git
回答by kynan
Why would you want to run filter-branch
more than once? You can do it all in one sweep, so no need to force it (note that you need extglob
enabled in your shell for this to work):
为什么要filter-branch
多次运行?您可以一次性完成所有操作,因此无需强制执行(请注意,您需要extglob
在 shell 中启用此功能才能工作):
git filter-branch --index-filter "git rm -r -f --cached --ignore-unmatch $(ls -xd apps/!(AAA) libs/!(XXX))" --prune-empty -- --all
This should get rid of all the changes in the unwanted subdirectories and keep all your branches and commits (unless they only affect files in the pruned subdirectories, by virtue of --prune-empty
) - no issue with duplicate commits etc.
这应该消除不需要的子目录中的所有更改并保留所有分支和提交(除非它们仅影响修剪子目录中的文件,因为--prune-empty
) - 重复提交等没有问题。
After this operation the unwanted directories will be listed as untracked by git status
.
此操作后,不需要的目录将被列为未跟踪git status
。
The $(ls ...)
is necessary s.t. the extglob
is evaluated by your shell instead of the index filter, which uses the sh
builtin eval
(where extglob
is not available). See How do I enable shell options in git?for further details on that.
在$(ls ...)
必要的STextglob
是由你的shell而不是索引过滤器,它使用评估sh
内置eval
(其中extglob
不可用)。请参阅如何在 git 中启用 shell 选项?了解更多详情。
回答by slobobaby
I have writen a git filter to solve exactly this problem. It has the fantastic name of git_filter and is located at github here:
我写了一个 git 过滤器来解决这个问题。它有一个很棒的名字 git_filter,位于 github 上:
https://github.com/slobobaby/git_filter
https://github.com/slobobaby/git_filter
It is based on the excellent libgit2.
它基于优秀的 libgit2。
I needed to split a large repository with many commits (~100000) and the solutions based on git filter-branch took several days to run. git_filter takes a minute to do the same thing.
我需要拆分一个包含许多提交(~100000)的大型存储库,并且基于 git filter-branch 的解决方案需要几天才能运行。git_filter 需要一分钟来做同样的事情。
回答by AndrewD
Use 'git splits' git extension
使用 'git splits' git 扩展
git splits
is a bash script that is a wrapper around git branch-filter
that I created as a git extension, based on jkeating's solution.
git splits
是一个 bash 脚本,它是git branch-filter
我根据jkeating 的解决方案创建的作为 git 扩展名的包装器。
It was made exactly for this situation. For your error, try using the git splits -f
option to force removal of the backup. Because git splits
operates on a new branch, it won't rewrite your current branch, so the backup is extraneous. See the readme for more detail and be sure to use it on a copy/clone of your repo ( just in case!).
它正是为这种情况而制作的。对于您的错误,请尝试使用git splits -f
强制删除备份的选项。因为git splits
在一个新的分支上操作,它不会重写你当前的分支,所以备份是无关紧要的。有关更多详细信息,请参阅自述文件,并确保在您的存储库的副本/克隆上使用它(以防万一!)。
- install
git splits
. Split the directories into a local branch
#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/ZZZCreate an empty repo somewhere. We'll assume we've created an empty repo called
xyz
on GitHub that has path :[email protected]:simpliwp/xyz.git
Push to the new repo.
#add a new remote origin for the empty repo so we can push to the empty repo on GitHub git remote add origin_xyz [email protected]:simpliwp/xyz.git #push the branch to the empty repo's master branch git push origin_xyz XYZ:master
Clone the newly created remote repo into a new local directory
#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 [email protected]:simpliwp/xyz.git
- 安装
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在某处创建一个空的仓库。我们假设我们已经
xyz
在 GitHub 上创建了一个名为path的空仓库:[email protected]: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 [email protected]: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 [email protected]:simpliwp/xyz.git
回答by Richard Barraclough
git clone [email protected]: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 [email protected]:newthing.git
git push --all
回答by Jakob Borg
Yeah. Force overwriting the backup by using the -f
flag on subsequent calls to filter-branch
to override that warning. :) Otherwise I think you have the solution (that is, eradicate an unwanted directory at a time with filter-branch
).
是的。通过-f
在后续调用中使用标志来强制覆盖备份filter-branch
以覆盖该警告。:) 否则,我认为您有解决方案(即,使用 一次根除不需要的目录filter-branch
)。
回答by elmo
An easy solution: git-filter-repo
一个简单的解决方案:git-filter-repo
I had a similar issue and, after reviewing the various approaches listed here, I discovered git-filter-repo. It is recommended as an alternative to git-filter-branch in the official git documentation here.
我遇到了类似的问题,在查看了此处列出的各种方法后,我发现了git-filter-repo。在此处的官方 git 文档中,建议将其作为 git-filter-branch 的替代方案。
To create a new repository from a subset of directories in an existing repository, you can use the command:
要从现有存储库中的目录子集创建新存储库,您可以使用以下命令:
git filter-repo --path <file_to_remove>
Filter multiple files/folders by chaining them:
通过链接多个文件/文件夹来过滤它们:
git filter-repo --path keepthisfile --path keepthisfolder/
So, to answer the original question, with git-filter-repo you would just need the following command:
因此,要回答原始问题,使用 git-filter-repo 您只需要以下命令:
git filter-repo --path apps/AAA/ --path libs/XXX/
回答by user5200576
Delete the backup present under the .git directory in refs/original like the message suggests. The directory is hidden.
像消息建议的那样,删除 refs/original 中 .git 目录下的备份。目录是隐藏的。