我有一个 Git 存储库,其中包含许多子目录。 现在我发现其中一个子目录与另一个子目录无关,应该分离到一个单独的存储库。
如何在保留子目录中文件的历史记录的同时执行此操作?
我想我可以制作一个克隆并删除每个克隆中不需要的部分,但我想这会在检查旧版本等时为我提供完整的树。这可能是可以接受的,但我更愿意能够假装两个存储库没有共享历史记录。
只是为了清楚起见,我有以下结构:
XYZ/
.git/
XY1/
ABC/
XY2/
但我想要这样:
XYZ/
.git/
XY1/
XY2/
ABC/
.git/
ABC/
I have a Git repository which contains a number of subdirectories. Now I have found that one of the subdirectories is unrelated to the other and should be detached to a separate repository.
How can I do this while keeping the history of the files within the subdirectory?
I guess I could make a clone and remove the unwanted parts of each clone, but I suppose this would give me the complete tree when checking out an older revision etc. This might be acceptable, but I would prefer to be able to pretend that the two repositories doesn't have a shared history.
Just to make it clear, I have the following structure:
XYZ/
.git/
XY1/
ABC/
XY2/
But I would like this instead:
XYZ/
.git/
XY1/
XY2/
ABC/
.git/
ABC/
发布评论
评论(26)
The Easy Way™
事实证明,这是一种常见且有用的做法,Git 的统治者使它变得非常简单(在版本 1.7.11 - 2012 年 5 月中添加)。 此外,下面的演练中有一个真实示例。
准备旧仓库
注意:
不得包含前导或尾随字符。 例如,名为subproject
的文件夹必须作为subproject
传递,而不是./subproject/
注意:
是您将在现有/旧存储库中创建的分支,不是 新的[稍后出现]。Windows 用户注意事项:当您的文件夹深度 > 时 1、
必须有 *nix 风格的文件夹分隔符 (/)。 例如,名为path1\path2\subproject
的文件夹必须作为path1/path2/subproject
传递
创建新的存储库
将新的存储库链接到 GitHub 或其他位置
Cleanup inside
,如果需要注意:这会将所有历史引用保留在存储库中。 如果您确实担心提交密码或者需要减小
.git
文件夹的文件大小,请参阅下面的附录。演练
这些步骤与上面相同,但是按照我的存储库的具体步骤进行操作,而不是使用
。这是我在节点中实现 JavaScript 浏览器模块的项目:
我想将单个文件夹
btoa
拆分到一个单独的 Git 存储库中我现在有一个新分支
btoa-only< /code>,只有
btoa
的提交,我想创建一个新的存储库。接下来,我在 GitHub 或 Bitbucket 等上创建一个新的存储库,并将其添加为
origin
快乐的一天!
注意:如果您使用
README.md
、.gitignore
和LICENSE
创建存储库,则需要首先拉:最后,我想从更大的存储库中删除该文件夹
清除历史记录
默认情况下,从 Git 中删除文件实际上并不会删除它们;而是将它们删除。 它只是承诺他们不再存在了。 如果你想真正删除历史引用(即你提交了密码),你需要这样做:
之后,你可以检查你的文件或文件夹是否不再显示在 Git 历史记录中:
但是,你无法将删除“推送”到 GitHub 等。 如果你尝试,你会收到一个错误,并且你必须先
git pull
,然后才能git push
- 然后你又回到历史记录中的所有内容。因此,如果您想从“源”删除历史记录(即从 GitHub、Bitbucket 等删除历史记录),您需要删除存储库并重新推送存储库的修剪副本。 但是等等 - 还有更多! - 如果您真的担心删除密码或类似的东西,您需要修剪备份(见下文)。
使
.git
更小前面提到的删除历史命令仍然会留下一堆备份文件 - 因为 Git 非常友善地帮助您避免意外破坏您的存储库。 它最终会在数天和数月内删除孤立的文件,但会将它们保留一段时间,以防您意识到不小心删除了您不想删除的内容。
因此,如果您真的想立即清空垃圾箱以减少存储库的克隆大小,您必须立即执行所有这些非常奇怪的操作:
也就是说,我建议除非您知道需要,否则不要执行这些步骤 - 以防万一您确实修剪了错误的子目录,您知道吗? 当您推送存储库时,备份文件不应被克隆,它们只会位于您的本地副本中。
信用
The Easy Way™
It turns out that this is such a common and useful practice that the overlords of Git made it really easy (added in version 1.7.11 - May 2012). Also, there's a real-world example in the walkthrough below.
Prepare the old repo
Note:
<name-of-folder>
must NOT contain leading or trailing characters. For instance, the folder namedsubproject
MUST be passed assubproject
, NOT./subproject/
Note:
<name-of-new-branch>
is a branch you will be creating in the existing/old repo, NOT the new one [that comes later].Note for Windows users: When your folder depth is > 1,
<name-of-folder>
must have *nix style folder separator (/). For instance, the folder namedpath1\path2\subproject
MUST be passed aspath1/path2/subproject
Create the new repo
Link the new repo to GitHub or wherever
Cleanup inside
<big-repo>
, if desiredNote: This leaves all the historical references in the repository. See the Appendix below if you're actually concerned about having committed a password or you need to decreasing the file size of your
.git
folder.Walkthrough
These are the same steps as above, but following my exact steps for my repository instead of using
<meta-named-things>
.Here's a project I have for implementing JavaScript browser modules in node:
I want to split out a single folder,
btoa
, into a separate Git repositoryI now have a new branch,
btoa-only
, that only has commits forbtoa
and I want to create a new repository.Next, I create a new repo on GitHub or Bitbucket, or whatever and add it as the
origin
Happy day!
Note: If you created a repo with a
README.md
,.gitignore
andLICENSE
, you will need to pull first:Lastly, I'll want to remove the folder from the bigger repo
Clearing your history
By default, removing files from Git doesn't actually remove them; it just commits that they aren't there any more. If you want to actually remove the historical references (i.e. you committed a password), you need to do this:
After that, you can check that your file or folder no longer shows up in the Git history at all:
However, you can't "push" deletes to GitHub and the like. If you try, you'll get an error and you'll have to
git pull
before you cangit push
- and then you're back to having everything in your history.So if you want to delete history from the "origin" - meaning to delete it from GitHub, Bitbucket, etc - you'll need to delete the repo and re-push a pruned copy of the repo. But wait - there's more! - if you're really concerned about getting rid of a password or something like that you'll need to prune the backup (see below).
Making
.git
smallerThe aforementioned delete history command still leaves behind a bunch of backup files - because Git is all too kind in helping you to not ruin your repo by accident. It will eventually delete orphaned files over the days and months, but it leaves them there for a while in case you realize that you accidentally deleted something you didn't want to.
So if you really want to empty the trash to reduce the clone size of a repo immediately you have to do all of this really weird stuff:
That said, I'd recommend not performing these steps unless you know that you need to - just in case you did prune the wrong subdirectory, y'know? The backup files shouldn't get cloned when you push the repo, they'll just be in your local copy.
Credit
更新:这个过程非常常见,以至于 git 团队使用新工具
git subtree
使其变得更加简单。 请参阅此处:将子目录分离(移动)到单独的 Git 存储库您想要克隆存储库,然后使用 git filter-branch 来标记新存储库中除您希望进行垃圾收集的子目录之外的所有内容。
克隆本地存储库:
(注意:将使用硬链接克隆存储库,但这不是问题,因为硬链接文件本身不会被修改 - 将创建新文件。)
现在,让我们保留我们也想重写有趣的分支,然后删除源以避免推送到那里并确保旧的提交不会被源引用:
<前><代码>cd /ABC
对于 i 在分支 1 br2 br3 中; 执行 gitbranch -t $i origin/$i; 完毕
git 远程 rm 原点
或对于所有远程分支:
<前><代码>cd /ABC
for i in $(git Branch -r | sed "s/.*origin\///"); 执行 gitbranch -t $i origin/$i; 完毕
git 远程 rm 原点
现在您可能还想删除与子项目无关的标签; 您也可以稍后执行此操作,但您可能需要再次修剪您的存储库。 我没有这样做,并收到了
警告:所有标签的 Ref 'refs/tags/v0.1' 未更改
(因为它们都与子项目无关); 此外,删除此类标签后,将回收更多空间。 显然 git filter-branch 应该能够重写其他标签,但我无法验证这一点。 如果要删除所有标签,请使用 git tag -l | xargs git tag -d.然后使用filter-branch和reset来排除其他文件,这样它们就可以被修剪。 我们还添加
--tag-name-filter cat --prune-empty
来删除空提交并重写标签(请注意,这将必须去除它们的签名):或者,仅重写 HEAD 分支并忽略标签和其他分支:
然后删除备份引用日志,以便真正回收空间(尽管现在该操作具有破坏性)
现在您有了 ABC 子目录的本地 git 存储库,并保留了其所有历史记录。
注意:对于大多数用途,
git filter-branch
确实应该添加参数-- --all
。 是的,这确实是--空间--全部
。 这需要是命令的最后一个参数。 正如 Matli 发现的那样,这会将项目分支和标签保留在新存储库中。编辑:合并了下面评论中的各种建议,以确保存储库实际上已缩小(以前情况并非总是如此)。
Update: This process is so common, that the git team made it much simpler with a new tool,
git subtree
. See here: Detach (move) subdirectory into separate Git repositoryYou want to clone your repository and then use
git filter-branch
to mark everything but the subdirectory you want in your new repo to be garbage-collected.To clone your local repository:
(Note: the repository will be cloned using hard-links, but that is not a problem since the hard-linked files will not be modified in themselves - new ones will be created.)
Now, let us preserve the interesting branches which we want to rewrite as well, and then remove the origin to avoid pushing there and to make sure that old commits will not be referenced by the origin:
or for all remote branches:
Now you might want to also remove tags which have no relation with the subproject; you can also do that later, but you might need to prune your repo again. I did not do so and got a
WARNING: Ref 'refs/tags/v0.1' is unchanged
for all tags (since they were all unrelated to the subproject); additionally, after removing such tags more space will be reclaimed. Apparentlygit filter-branch
should be able to rewrite other tags, but I could not verify this. If you want to remove all tags, usegit tag -l | xargs git tag -d
.Then use filter-branch and reset to exclude the other files, so they can be pruned. Let's also add
--tag-name-filter cat --prune-empty
to remove empty commits and to rewrite tags (note that this will have to strip their signature):or alternatively, to only rewrite the HEAD branch and ignore tags and other branches:
Then delete the backup reflogs so the space can be truly reclaimed (although now the operation is destructive)
and now you have a local git repository of the ABC sub-directory with all its history preserved.
Note: For most uses,
git filter-branch
should indeed have the added parameter-- --all
. Yes that's really --space--all
. This needs to be the last parameters for the command. As Matli discovered, this keeps the project branches and tags included in the new repo.Edit: various suggestions from comments below were incorporated to make sure, for instance, that the repository is actually shrunk (which was not always the case before).
Paul 的答案 创建一个包含 /ABC 的新存储库,但不会从 /XYZ 中删除 /ABC。 以下命令将从 /XYZ 中删除 /ABC:
当然,首先在“clone --no-hardlinks”存储库中测试它,然后使用 Paul 列出的重置、gc 和修剪命令。
Paul's answer creates a new repository containing /ABC, but does not remove /ABC from within /XYZ. The following command will remove /ABC from within /XYZ:
Of course, test it in a 'clone --no-hardlinks' repository first, and follow it with the reset, gc and prune commands Paul lists.
我发现,为了从新存储库中正确删除旧历史记录,您必须在
filter-branch
步骤之后做更多的工作。进行克隆和过滤:
删除对旧历史记录的所有引用。 “origin”是跟踪你的克隆,“original”是过滤器分支保存旧东西的地方:
即使是现在,您的历史记录也可能被困在 fsck 不会触及的包文件中。 将其撕成碎片,创建一个新的包文件并删除未使用的对象:
有 过滤器手册中对此的解释分支。
I’ve found that in order to properly delete the old history from the new repository, you have to do a little more work after the
filter-branch
step.Do the clone and the filter:
Remove every reference to the old history. “origin” was keeping track of your clone, and “original” is where filter-branch saves the old stuff:
Even now, your history might be stuck in a packfile that fsck won’t touch. Tear it to shreds, creating a new packfile and deleting the unused objects:
There is an explanation of this in the manual for filter-branch.
当使用较新版本的
git
(2.22+
也许?)运行git filter-branch
时,它会提示使用这个新工具git-filter-repo。 这个工具确实为我简化了事情。使用filter-repo
命令进行过滤,根据原始问题创建
XYZ
存储库:假设:
* 远程 XYZ 存储库在推送之前是新的且空的
过滤和移动
在我的情况下,我还想移动几个目录以获得更一致的结构。 最初,我运行了简单的
filter-repo
命令,然后运行 git mv dir-to-rename
,但我发现使用可以获得稍微“更好”的历史记录>--path-rename
选项。 现在,我在新存储库中看到的移动文件上的上次修改时间不再是5 小时前
,而是看到了去年
(在 GitHub UI 中),它与原始存储库中的修改时间相匹配。而不是...
我最终跑了...
Notes:
--path
可以指定多次;因此,无需在源存储库中创建子目录。)因为当我时有人已提交到源存储库注意到我未能继承历史记录,我只是在clone
命令后使用了git reset commit-before-subdir-move --hard
,并添加了--force
到filter-repo
命令以使其在稍微修改的本地克隆上运行。git
的扩展模式,我对安装感到困惑,但最终我克隆了 git-filter-repo 并将其符号链接到$(git --exec-path)
:When running
git filter-branch
using a newer version ofgit
(2.22+
maybe?), it says to use this new tool git-filter-repo. This tool certainly simplified things for me.Filtering with filter-repo
Commands to create the
XYZ
repo from the original question:assumptions:
* remote XYZ repo was new and empty before the push
Filtering and moving
In my case, I also wanted to move a couple of directories for a more consistent structure. Initially, I ran that simple
filter-repo
command followed bygit mv dir-to-rename
, but I found I could get a slightly "better" history using the--path-rename
option. Instead of seeing last modified5 hours ago
on moved files in the new repo I now seelast year
(in the GitHub UI), which matches the modified times in the original repo.Instead of...
I ultimately ran...
Notes:
git filter-repo --subdirectory-filter dir-matching-new-repo-name
). That command correctly converted that subdirectory to the root of the copied local repo, but it also resulted in a history of only the three commits it took to create the subdirectory. (I hadn't realized that--path
could be specified multiple times; thereby, obviating the need to create a subdirectory in the source repo.) Since someone had committed to the source repo by the time I noticed that I'd failed to carry forward the history, I just usedgit reset commit-before-subdir-move --hard
after theclone
command, and added--force
to thefilter-repo
command to get it to operate on the slightly modified local clone.git
, but ultimately I cloned git-filter-repo and symlinked it to$(git --exec-path)
:编辑:添加了 Bash 脚本。
这里给出的答案对我来说仅部分有效; 许多大文件保留在缓存中。 最终有效的方法(下班后在 freenode 上的 #git 中):
使用之前的解决方案,存储库大小约为 100 MB。 这个将其减少到 1.7 MB。 也许它对某人有帮助:)
以下 bash 脚本会自动执行该任务:
Edit: Bash script added.
The answers given here worked just partially for me; Lots of big files remained in the cache. What finally worked (after hours in #git on freenode):
With the previous solutions, the repository size was around 100 MB. This one brought it down to 1.7 MB. Maybe it helps somebody :)
The following bash script automates the task:
这不再那么复杂,您只需使用 git filter-branch 命令即可您的存储库的克隆,以剔除您不需要的子目录,然后推送到新的远程目录。
This is no longer so complex you can just use the git filter-branch command on a clone of you repo to cull the subdirectories you don't want and then push to the new remote.
更新:git-subtree 模块非常有用,以至于 git 团队将其纳入核心并使其成为
git subtree
。 请参阅此处:将子目录分离(移动)到单独的 Git 存储库git-subtree 可能对此很有用
http://github。 com/apenwarr/git-subtree/blob/master/git-subtree.txt(已弃用)
http://psionides.jogger.pl/2010/02/04/sharing-code- Between-projects-with-git-subtree/
Update: The git-subtree module was so useful that the git team pulled it into core and made it
git subtree
. See here: Detach (move) subdirectory into separate Git repositorygit-subtree may be useful for this
http://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt (deprecated)
http://psionides.jogger.pl/2010/02/04/sharing-code-between-projects-with-git-subtree/
这是对 CoolAJ86 的 “The Easy Way™”答案,以便将多个子文件夹(假设
sub1
和sub2
)拆分为一个新的 git 存储库。Easy Way™(多个子文件夹)
准备旧存储库
注意:
不得包含前导或尾随字符。 例如,名为subproject
的文件夹必须作为subproject
传递,而不是./subproject/
Windows 用户注意事项:当您的文件夹深度 > 时 1、
必须有 *nix 风格的文件夹分隔符 (/)。 例如,名为path1\path2\subproject
的文件夹必须作为path1/path2/subproject
传递。 此外,不要使用mv
命令,而是使用move
。最后一点:与基本答案的独特且巨大的区别是脚本的第二行“
git filter-branch...
”创建新的存储库< /p>
将新的存储库链接到 Github 或任何地方
清理,如果需要
注意:这会将所有历史引用保留在存储库中。如果您确实担心提交密码或需要,请参阅原始答案中的附录减少
.git
文件夹的文件大小。Here is a small modification to CoolAJ86's "The Easy Way™" answer in order to split multiple sub folders (let's say
sub1
andsub2
) into a new git repository.The Easy Way™ (multiple sub folders)
Prepare the old repo
Note:
<name-of-folder>
must NOT contain leading or trailing characters. For instance, the folder namedsubproject
MUST be passed assubproject
, NOT./subproject/
Note for windows users: when your folder depth is > 1,
<name-of-folder>
must have *nix style folder separator (/). For instance, the folder namedpath1\path2\subproject
MUST be passed aspath1/path2/subproject
. Moreover don't usemv
command butmove
.Final note: the unique and big difference with the base answer is the second line of the script "
git filter-branch...
"Create the new repo
Link the new repo to Github or wherever
Cleanup, if desired
Note: This leaves all the historical references in the repository.See the Appendix in the original answer if you're actually concerned about having committed a password or you need to decreasing the file size of your
.git
folder.最初的问题希望 XYZ/ABC/(*files) 变为 ABC/ABC/(*files)。 在为我自己的代码实现接受的答案后,我注意到它实际上将 XYZ/ABC/(*files) 更改为 ABC/(*files)。 过滤器分支手册页甚至说,
换句话说,它将顶级文件夹“向上”提升了一级。这是一个重要的区别,因为,例如, 我重命名了一个顶级文件夹。通过将文件夹“向上”提升一级,git 在我进行重命名的提交处失去了连续性。
在我的历史记录中, png" alt="I Lost contiuity after filter-branch">
我对这个问题的回答是制作存储库的 2 个副本,并手动删除要保留在每个副本中的文件夹。手册页为我提供了支持这:
The original question wants XYZ/ABC/(*files) to become ABC/ABC/(*files). After implementing the accepted answer for my own code, I noticed that it actually changes XYZ/ABC/(*files) into ABC/(*files). The filter-branch man page even says,
In other words, it promotes the top-level folder "up" one level. That's an important distinction because, for example, in my history I had renamed a top-level folder. By promoting folders "up" one level, git loses continuity at the commit where I did the rename.
My answer to the question then is to make 2 copies of the repository and manually delete the folder(s) you want to keep in each. The man page backs me up with this:
要添加到 Paul 的答案,我发现为了最终恢复空间,我必须将 HEAD 推送到一个干净的存储库并修剪减小 .git/objects/pack 目录的大小。
即
在 gc 修剪之后,还可以执行以下操作:
然后您可以执行
此操作,并且 ABC/.git 的大小会减小
实际上,推送到清理存储库不需要一些耗时的步骤(例如 git gc),即:
To add to Paul's answer, I found that to ultimately recover space, I have to push HEAD to a clean repository and that trims down the size of the .git/objects/pack directory.
i.e.
After the gc prune, also do:
Then you can do
and the size of ABC/.git is reduced
Actually, some of the time consuming steps (e.g. git gc) aren't needed with the push to clean repository, i.e.:
看来这里的大多数(全部?)答案都依赖于某种形式的 git filter-branch --subdirectory-filter 及其同类。 这可能“大多数时候”有效,但对于某些情况,例如,当您重命名文件夹时,例如:
如果您执行正常的 git 过滤器样式来提取“move_this_dir_renamed”,您将丢失它之后发生的文件更改历史记录最初是“move_this_dir”(ref)。
因此,似乎真正保留所有更改历史记录的唯一方法(如果您的情况是这样),本质上是复制存储库(创建一个新的存储库,将其设置为origin),然后核对其他所有内容并将子目录重命名为父目录,如下所示:
gitbranch-a
git checkout --track origin/branchABC
cp -r oldmultimod simple
cd simple
git rm otherModule1 other2 other3
git mv moduleSubdir1/* 。
rmdir moduleSubdir1
git status
git remote set-url origin http://mygithost:8080/git/our-splitted-module-repo< /code>
git remote -v
git push
git checkoutbranch2
这遵循github 文档“将子文件夹拆分到新存储库中” 步骤 6-11 将模块推送到新存储库。
这不会为您节省 .git 文件夹中的任何空间,但它会保留这些文件的所有更改历史记录,即使是在重命名期间也是如此。 如果没有“大量”历史丢失等,这可能不值得。但至少保证您不会丢失较旧的提交!
It appears that most (all?) of the answers here rely on some form of
git filter-branch --subdirectory-filter
and its ilk. This may work "most times" however for some cases, for instance the case of when you renamed the folder, ex:If you do a normal git filter style to extract "move_this_dir_renamed" you will lose file change history that occurred from back when it was initially "move_this_dir" (ref).
It thus appears that the only way to really keep all change history (if yours is a case like this), is, in essence, to copy the repository (create a new repo, set that to be the origin), then nuke everything else and rename the subdirectory to the parent like this:
git branch -a
git checkout --track origin/branchABC
cp -r oldmultimod simple
cd simple
git rm otherModule1 other2 other3
git mv moduleSubdir1/* .
rmdir moduleSubdir1
git status
git remote set-url origin http://mygithost:8080/git/our-splitted-module-repo
git remote -v
git push
git checkout branch2
This follows the github doc "Splitting a subfolder out into a new repository" steps 6-11 to push the module to a new repo.
This will not save you any space in your .git folder, but it will preserve all your change history for those files even across renames. And this may not be worth it if there isn't "a lot" of history lost, etc. But at least you are guaranteed not to lose older commits!
现在正确的方法如下:
git filter-branch --prune-empty --subdirectory-filter FOLDER_NAME [first_branch] [another_branch]
GitHub 现在甚至有 关于此类情况的小文章。
但请务必首先将原始存储库克隆到单独的目录(因为它会删除所有文件和其他目录,并且您可能需要使用它们)。
所以你的算法应该是:
Proper way now is the following:
git filter-branch --prune-empty --subdirectory-filter FOLDER_NAME [first_branch] [another_branch]
GitHub now even have small article about such cases.
But be sure to clone your original repo to separate directory first (as it would delete all the files and other directories and you probable need to work with them).
So your algorithm should be:
git filter-branch
left only files under some subdirectory, push to new remote我推荐 GitHub 将子文件夹拆分为新文件夹的指南存储库。 这些步骤与 Paul 的回答类似,但我发现他们的说明更容易理解。
我修改了说明,以便它们申请本地存储库,而不是托管在 GitHub 上的存储库。
I recommend GitHub's guide to splitting subfolders into a new repository. The steps are similar to Paul's answer, but I found their instructions easier to understand.
I have modified the instructions so that they apply for a local repository, rather than one hosted on GitHub.
我确实遇到了这个问题,但所有基于 git filter-branch 的标准解决方案都非常慢。 如果您有一个小型存储库,那么这可能不是问题,这对我来说是。 我编写了另一个基于 libgit2 的 git 过滤程序,该程序第一步为主存储库的每个过滤创建分支,然后将它们推送到干净的存储库作为下一步。 在我的存储库(500Mb 100000 次提交)上,标准 git 过滤分支方法需要几天时间。 我的程序需要几分钟才能完成相同的过滤。
它有一个美妙的名字 git_filter 并居住在这里:
https://github.com/slobababy/git_filter
on GitHub。
我希望它对某人有用。
I had exactly this problem but all the standard solutions based on git filter-branch were extremely slow. If you have a small repository then this may not be a problem, it was for me. I wrote another git filtering program based on libgit2 which as a first step creates branches for each filtering of the primary repository and then pushes these to clean repositories as the next step. On my repository (500Mb 100000 commits) the standard git filter-branch methods took days. My program takes minutes to do the same filtering.
It has the fabulous name of git_filter and lives here:
https://github.com/slobobaby/git_filter
on GitHub.
I hope it is useful to someone.
使用此过滤命令删除子目录,同时保留标签和分支:
Use this filter command to remove a subdirectory, while preserving your tags and branches:
无论如何,以下是如何在 Windows 计算机上使用 GitHub。 假设您有一个克隆的存储库位于
C:\dir1
中。 目录结构如下所示:C:\dir1\dir2\dir3
。dir3
目录是我想要成为一个新的单独存储库的目录。Github:
MyTeam/mynewrepo
Bash 提示:
$ cd c:/Dir1
$ git filter-branch --prune-empty --subdirectory-filter dir2/dir3 HEAD
返回:
引用'refs/heads/master'被重写
(仅供参考:dir2/dir3区分大小写。)$ git remote add some_name [电子邮件受保护]:MyTeam/mynewrepo.git
git 远程添加源等
。 不起作用,返回“远程源已存在
”$ git push --progress some_name master
For what it's worth, here is how using GitHub on a Windows machine. Let's say you have a cloned repo in residing in
C:\dir1
. The directory structure looks like this:C:\dir1\dir2\dir3
. Thedir3
directory is the one I want to be a new separate repo.Github:
MyTeam/mynewrepo
Bash Prompt:
$ cd c:/Dir1
$ git filter-branch --prune-empty --subdirectory-filter dir2/dir3 HEAD
Returned:
Ref 'refs/heads/master' was rewritten
(fyi: dir2/dir3 is case sensitive.)$ git remote add some_name [email protected]:MyTeam/mynewrepo.git
git remote add origin etc
. did not work, returned "remote origin already exists
"$ git push --progress some_name master
正如我上面提到的,我不得不使用相反的解决方案(删除所有提交都没有触及我的
dir/subdir/targetdir
),这似乎工作得很好,删除了大约 95% 的提交(根据需要)。 然而,还存在两个小问题。首先,
filter-branch
在删除引入或修改代码的提交方面做得非常出色,但显然,合并提交在吉蒂宇宙。这是一个外观问题,我可能可以忍受(他说......慢慢后退,眼睛别开)。
第二剩下的少数提交几乎全部都是重复的! 我似乎获得了第二条冗余的时间线,几乎涵盖了该项目的整个历史。 有趣的是(您可以从下图中看到),我的三个本地分支并不都在同一时间线上(这当然就是它存在的原因,而不仅仅是垃圾收集)。
我唯一能想象的是,删除的提交之一也许是
filter-branch
实际上删除的单个合并提交,这创建了并行时间线,因为每个现在未合并的链都获取了自己的提交副本。 (耸耸肩我的 TARDiS 在哪里?)我很确定我可以解决这个问题,尽管我真的很想了解它是如何发生的。在疯狂的 mergefest-O-RAMA 的情况下,我可能会独自留下那个,因为它已经在我的提交历史中牢牢地根深蒂固——每当我靠近时都会对我构成威胁——它似乎并没有真正导致任何非美观的问题,因为它在 Tower.app 中非常漂亮。
As I mentioned above, I had to use the reverse solution (deleting all commits not touching my
dir/subdir/targetdir
) which seemed to work pretty well removing about 95% of the commits (as desired). There are, however, two small issues remaining.FIRST,
filter-branch
did a bang up job of removing commits which introduce or modify code but apparently, merge commits are beneath its station in the Gitiverse.This is a cosmetic issue which I can probably live with (he says...backing away slowly with eyes averted).
SECOND the few commits that remain are pretty much ALL duplicated! I seem to have acquired a second, redundant timeline that spans just about the entire history of the project. The interesting thing (which you can see from the picture below), is that my three local branches are not all on the same timeline (which is, certainly why it exists and isn't just garbage collected).
The only thing I can imagine is that one of the deleted commits was, perhaps, the single merge commit that
filter-branch
actually did delete, and that created the parallel timeline as each now-unmerged strand took its own copy of the commits. (shrug Where's my TARDiS?) I'm pretty sure I can fix this issue, though I'd really love to understand how it happened.In the case of crazy mergefest-O-RAMA, I'll likely be leaving that one alone since it has so firmly entrenched itself in my commit history—menacing at me whenever I come near—, it doesn't seem to be actually causing any non-cosmetic problems and because it is quite pretty in Tower.app.
最简单的方法
git splits
。 我将其创建为 git 扩展,基于 jkeating 的解决方案。将目录拆分为本地分支
<代码>
#更改到您的存储库目录
cd /路径/到/repo
#检查分支
git checkout XYZ
#将多个目录拆分为新分支XYZ
git split -b XYZ XY1 XY2
在某处创建一个空的存储库。 我们假设我们在 GitHub 上创建了一个名为
xyz
的空存储库,其路径为:[email protected]:simpliwp/xyz.git
推送到新存储库。
<代码>
#为空存储库添加一个新的远程源,以便我们可以推送到 GitHub 上的空存储库
git Remote add origin_xyz [电子邮件受保护]:simpliwp/xyz.git
#将分支推送到空仓库的主分支
git push origin_xyz XYZ:master
将新创建的远程存储库克隆到新的本地目录
<代码>
#将当前目录从旧存储库中更改出来
cd /path/to/where/you/want/the/new/local/repo
#克隆您刚刚推送到的远程存储库
git clone [电子邮件受保护]:simpliwp/xyz.git
The Easier Way
git splits
. I created it as a git extension, based on jkeating's solution.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 XY1 XY2
Create 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 reflog expire --expire=now --all”之类的内容来实际清除文件。 git filter-branch 只是删除历史记录中的引用,但不会删除保存数据的引用日志条目。 当然,先测试一下。
尽管我的初始条件有些不同,但这样做时我的磁盘使用率急剧下降。 也许 --subdirectory-filter 否定了这种需要,但我对此表示怀疑。
You might need something like "git reflog expire --expire=now --all" before the garbage collection to actually clean the files out. git filter-branch just removes references in the history, but doesn't remove the reflog entries that hold the data. Of course, test this first.
My disk usage dropped dramatically in doing this, though my initial conditions were somewhat different. Perhaps --subdirectory-filter negates this need, but I doubt it.
查看 git_split 项目 https://github.com/vangorra/git_split
将 git 目录变成自己的目录存储库位于自己的位置。 没有子树有趣的事情。 该脚本将获取 git 存储库中的现有目录,并将该目录转换为自己的独立存储库。 在此过程中,它将复制您提供的目录的整个更改历史记录。
Check out git_split project at https://github.com/vangorra/git_split
Turn git directories into their very own repositories in their own location. No subtree funny business. This script will take an existing directory in your git repository and turn that directory into an independent repository of its own. Along the way, it will copy over the entire change history for the directory you provided.
将其放入您的 gitconfig 中:
Put this into your gitconfig:
我确信 git 子树一切都很好,但我想要移动的 git 管理代码的子目录都在 eclipse 中。
因此,如果您使用 egit,那就非常简单了。
获取您想要移动的项目并进行分组 -> 断开连接,然后分组 -> 将其共享到新位置。 它将默认尝试使用旧的存储库位置,但您可以取消选中使用现有选择并选择新位置来移动它。
万岁。
I'm sure git subtree is all fine and wonderful, but my subdirectories of git managed code that I wanted to move was all in eclipse.
So if you're using egit, it's painfully easy.
Take the project you want to move and team->disconnect it, and then team->share it to the new location. It will default to trying to use the old repo location, but you can uncheck the use-existing selection and pick the new place to move it.
All hail egit.
您可以轻松尝试 https://help.github.com/enterprise/2.15/user/articles/splitting-a-subfolder-out-into-a-new-repository/
这对我有用。 我在上面给出的步骤中遇到的问题是
在此命令中
git filter-branch --prune-empty --subdirectory-filter FOLDER-NAME BRANCH-NAME
在此命令中,
BRANCH-NAME
是 master如果由于保护问题而提交时最后一步失败,请遵循 - https://docs.gitlab.com/ee/user/project/protected_branches.html
You can easily try the https://help.github.com/enterprise/2.15/user/articles/splitting-a-subfolder-out-into-a-new-repository/
This worked for me. The issues i faced in the steps given above are
in this command
git filter-branch --prune-empty --subdirectory-filter FOLDER-NAME BRANCH-NAME
The
BRANCH-NAME
is masterif the last step fails when committing due to protection issue follow - https://docs.gitlab.com/ee/user/project/protected_branches.html
我找到了非常直接的解决方案,
这个想法是复制存储库,然后删除不必要的部分。
它的工作原理如下:
1) 克隆您想要拆分的存储库
2) 移动到 git 文件夹
2) 删除不必要的文件夹并提交
3) 使用 BFG
4) 检查历史记录是否不包含您刚刚删除的文件/文件夹
5) 现在您有了干净的存储库,没有 ABC,
所以只需将其推入新的原点
即可。 您可以重复这些步骤来获取另一个存储库,
只需删除 XY1,XY2 并重命名 XYZ -> ABC 第 3 步
I've found quite straight forward solution,
The idea is to copy repository and then just remove unnecessary part.
This is how it works:
1) Clone a repository you'd like to split
2) Move to git folder
2) Remove unnecessary folders and commit it
3) Remove unnecessary folder(s) form history with BFG
4) Check that history doesn't contains the files/folders you just deleted
5) Now you have clean repository without ABC,
so just push it into new origin
That's it. You can repeat the steps to get another repository,
just remove XY1,XY2 and rename XYZ -> ABC on step 3
发现这篇精彩的文章原始参考易于理解。 在这里记录它,以防它无法访问。
1. 准备当前存储库
文件夹的名称必须是相对路径,从存储库的根目录开始。
2. 创建新的存储库
这里我们只需要 cd 到新文件夹,初始化新的存储库,并提交任何内容。
3.添加新的远程存储库并推送
我们在此处添加来自 GitHub 的远程新存储库,然后将我们的第一个提交推送到它。
4. 从主存储库中删除文件夹并推送
最后,我们回到根目录,从主存储库中删除该文件夹,然后提交并推送更改。
现在,我们的主存储库中有该文件夹,但链接到一个完全独立的存储库,可以在多个项目中重复使用。
Found this wonderful article Original reference easy to follow. Documenting it here in case if it get's inaccessible.
1. Preparing the current repository
The name of the folder must be a relative path, starting from the root of the repository.
2. Creating the new repository
Here we just need to cd to the new folder, initialise the new repository, and commit any contents.
3.Add new remote repository and push
We add the new repository remote from GitHub here, then push our first commit to it.
4. Remove folder from main repository and push
Finally, we cd back to the rooot directory, remove the folder from our main repository, then commit and push the change.
Now, we have the folder in our main repository but linked to a completely separate repository that can be reused across multiple projects.