将(移动)子目录分离到单独的Git存储库中

时间:2008-12-11 13:57:04

标签: git git-subtree git-filter-branch

我有一个Git存储库,其中包含许多子目录。现在我发现其中一个子目录与另一个子目录无关,应该分离到一个单独的存储库。

如何在将文件的历史记录保存在子目录中的同时执行此操作?

我想我可以制作一个克隆并删除每个克隆的不需要的部分,但我想这会给我一个完整的树,当检查旧版本等。这可能是可以接受的,但我希望能够假装这两个存储库没有共享历史记录。

为了说清楚,我有以下结构:

XYZ/
    .git/
    XY1/
    ABC/
    XY2/

但我想这样:

XYZ/
    .git/
    XY1/
    XY2/
ABC/
    .git/
    ABC/

26 个答案:

答案 0 :(得分:1224)

Easy Way™

事实证明,这是一种常见且有用的做法,git的霸主使它变得非常容易,但你必须有更新版本的git(> = 1.7.11 2012年5月)。有关如何安装最新的git,请参阅附录。此外,下面的演练中还有一个真实示例

  1. 准备旧的仓库

    pushd <big-repo>
    git subtree split -P <name-of-folder> -b <name-of-new-branch>
    popd
    

    注意: <name-of-folder>不得包含前导或尾随字符。例如,名为subproject的文件夹必须作为subproject传递,而不是./subproject/

    Windows用户注意事项:当您的文件夹深度为&gt; 1,<name-of-folder>必须有* nix样式文件夹分隔符(/)。例如,名为path1\path2\subproject的文件夹必须作为path1/path2/subproject

  2. 传递
  3. 创建新的仓库

    mkdir <new-repo>
    pushd <new-repo>
    
    git init
    git pull </path/to/big-repo> <name-of-new-branch>
    
  4. 将新回购链接链接到Github或任何地方

    git remote add origin <git@github.com:my-user/new-repo.git>
    git push origin -u master
    
  5. 清理,,如果需要

    popd # get out of <new-repo>
    pushd <big-repo>
    
    git rm -rf <name-of-folder>
    

    注意:这会在存储库中保留所有历史引用。如果您真的担心提交密码或需要减少密码,请参阅下面的附录 .git文件夹的文件大小。

  6. ...

    操作实例

    这些与上述相同的步骤,但遵循我的存储库的确切步骤,而不是使用<meta-named-things>

    这是我在节点中实现JavaScript浏览器模块的项目:

    tree ~/Code/node-browser-compat
    
    node-browser-compat
    ├── ArrayBuffer
    ├── Audio
    ├── Blob
    ├── FormData
    ├── atob
    ├── btoa
    ├── location
    └── navigator
    

    我想将单个文件夹btoa拆分为单独的git存储库

    pushd ~/Code/node-browser-compat/
    git subtree split -P btoa -b btoa-only
    popd
    

    我现在有一个新的分支btoa-only,只有btoa的提交,我想创建一个新的存储库。

    mkdir ~/Code/btoa/
    pushd ~/Code/btoa/
    git init
    git pull ~/Code/node-browser-compat btoa-only
    

    接下来我在Github或bitbucket上创建一个新的repo,或者其他什么并添加它是origin(btw,“origin”只是一个约定,不是命令的一部分 - 你可以称之为“remote-服务器“或任何你喜欢的”

    git remote add origin git@github.com:node-browser-compat/btoa.git
    git push origin -u master
    

    快乐的一天!

    注意:如果您使用README.md.gitignoreLICENSE创建了一个仓库,则需要先提取:

    git pull origin -u master
    git push origin -u master
    

    最后,我想要从较大的仓库中删除该文件夹

    git rm -rf btoa
    

    ...

    附录

    OS X上的最新git

    获取最新版本的git:

    brew install git
    

    要获得OS X的酿造:

    http://brew.sh

    Ubuntu上的最新git

    sudo apt-get update
    sudo apt-get install git
    git --version
    

    如果这不起作用(你有一个非常旧的版本的ubuntu),试试

    sudo add-apt-repository ppa:git-core/ppa
    sudo apt-get update
    sudo apt-get install git
    

    如果仍然无效,请尝试

    sudo chmod +x /usr/share/doc/git/contrib/subtree/git-subtree.sh
    sudo ln -s \
    /usr/share/doc/git/contrib/subtree/git-subtree.sh \
    /usr/lib/git-core/git-subtree
    

    感谢评论中的rui.araujo。

    清除历史

    默认情况下从git中删除文件实际上并没有从git中删除它们,它只是提交它们不再存在。如果要实际删除历史引用(即您已提交密码),则需要执行以下操作:

    git filter-branch --prune-empty --tree-filter 'rm -rf <name-of-folder>' HEAD
    

    之后,您可以检查您的文件或文件夹是否已不再显示在git历史记录中

    git log -- <name-of-folder> # should show nothing
    

    但是,无法“推送”删除到github 等。如果您尝试收到错误,则必须git pull才能git push - 然后您又回到了历史中的所有内容。

    因此,如果你想从“origin”中删除历史记录 - 意思是从github,bitbucket等删除它 - 你需要删除repo并重新推送repo的修剪副本。但是等等 - 还有更多! - 如果你真的担心要删除密码或类似的东西,你需要修剪备份(见下文)。

    使.git更小

    前面提到的删除历史记录命令仍然留下了一堆备份文件 - 因为git非常友好,可以帮助您不会意外毁坏您的回购。它最终将在几天和几个月内删除孤立的文件,但是如果你意识到你不小心删除了你不想要的内容,它会在那里留下一段时间。

    因此,如果你真的想立即清空垃圾箱减少克隆大小,你必须做所有这些非常奇怪的事情:

    rm -rf .git/refs/original/ && \
    git reflog expire --all && \
    git gc --aggressive --prune=now
    
    git reflog expire --all --expire-unreachable=0
    git repack -A -d
    git prune
    

    那就是说,我建议不要执行这些步骤,除非你知道你需要 - 以防你修剪了错误的子目录,你知道吗?

    ,当你推送回购时,不应该克隆备份文件,它们只会在你的本地副本中。

    信用

答案 1 :(得分:1189)

更新:此过程非常常见,git团队使用新工具git subtree使其变得更加简单。见这里:Detach (move) subdirectory into separate Git repository


您希望克隆您的存储库,然后使用git filter-branch标记除新回购中所需的子目录之外的所有内容,以便进行垃圾回收。

  1. 要克隆本地存储库:

    git clone /XYZ /ABC
    

    (注意:存储库将使用硬链接克隆,但这不是问题,因为硬链接文件本身不会被修改 - 将创建新的文件。)

  2. 现在,让我们保留我们想要重写的有趣分支,然后删除原点以避免在那里推送并确保原始提交不会被原点引用:

    cd /ABC
    for i in branch1 br2 br3; do git branch -t $i origin/$i; done
    git remote rm origin
    

    或所有远程分支:

    cd /ABC
    for i in $(git branch -r | sed "s/.*origin\///"); do git branch -t $i origin/$i; done
    git remote rm origin
    
  3. 现在您可能还想删除与子项目无关的标签;您也可以稍后再这样做,但您可能需要再次修剪您的仓库。我没有这样做,并为所有标签得到WARNING: Ref 'refs/tags/v0.1' is unchanged(因为它们都与子项目无关);此外,在移除此类标签后,将回收更多空间。显然git filter-branch应该能够重写其他标签,但我无法验证这一点。如果要删除所有代码,请使用git tag -l | xargs git tag -d

  4. 然后使用filter-branch并重置以排除其他文件,以便修剪它们。我们还添加--tag-name-filter cat --prune-empty来删除空提交并重写标记(请注意,这将不得不剥离其签名):

    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC -- --all
    

    或者,仅重写HEAD分支并忽略标记和其他分支:

    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC HEAD
    
  5. 然后删除备份reflogs,以便可以真正回收空间(虽然现在操作具有破坏性)

    git reset --hard
    git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
    git reflog expire --expire=now --all
    git gc --aggressive --prune=now
    

    现在你有一个ABC子目录的本地git存储库,其中保留了所有历史记录。

  6. 注意:对于大多数用途,git filter-branch确实应该添加参数-- --all。是的,这确实是 - - space - - all。这需要是命令的最后一个参数。正如Matli发现的那样,这使得项目分支和标签保留在新的仓库中。

    编辑:合并了以下评论中的各种建议,以确保,例如,存储库实际上已缩小(以前并非总是如此)。

答案 2 :(得分:132)

Paul's answer创建一个包含/ ABC的新存储库,但不会从/ XYZ中删除/ ABC。以下命令将从/ XYZ中删除/ ABC:

git filter-branch --tree-filter "rm -rf ABC" --prune-empty HEAD

当然,首先在'clone --no-hardlinks'存储库中进行测试,然后使用Paul列出的reset,gc和prune命令进行测试。

答案 3 :(得分:94)

我发现为了从新存储库中正确删除旧历史记录,您需要在filter-branch步骤之后再做一些工作。

  1. 执行克隆和过滤器:

    git clone --no-hardlinks foo bar; cd bar
    git filter-branch --subdirectory-filter subdir/you/want
    
  2. 删除对旧历史记录的每个引用。 “origin”跟踪你的克隆,“original”是filter-branch保存旧东西的地方:

    git remote rm origin
    git update-ref -d refs/original/refs/heads/master
    git reflog expire --expire=now --all
    
  3. 即使是现在,您的历史记录可能会卡在fsck无法触及的包文件中。撕碎它,创建一个新的packfile并删除未使用的对象:

    git repack -ad
    
  4. an explanation of this中有manual for filter-branch

答案 4 :(得分:39)

编辑:添加了Bash脚本。

这里给出的答案对我来说只是部分起作用;缓存中还有很多大文件。终于有效了(在freenode上的#git下班后):

git clone --no-hardlinks file:///SOURCE /tmp/blubb
cd blubb
git filter-branch --subdirectory-filter ./PATH_TO_EXTRACT  --prune-empty --tag-name-filter cat -- --all
git clone file:///tmp/blubb/ /tmp/blooh
cd /tmp/blooh
git reflog expire --expire=now --all
git repack -ad
git gc --prune=now

使用以前的解决方案,存储库大小约为100 MB。这个降低到1.7 MB。也许它有助于某人:)


以下bash脚本自动完成任务:

!/bin/bash

if (( $# < 3 ))
then
    echo "Usage:   $0 </path/to/repo/> <directory/to/extract/> <newName>"
    echo
    echo "Example: $0 /Projects/42.git first/answer/ firstAnswer"
    exit 1
fi


clone=/tmp/${3}Clone
newN=/tmp/${3}

git clone --no-hardlinks file://$1 ${clone}
cd ${clone}

git filter-branch --subdirectory-filter $2  --prune-empty --tag-name-filter cat -- --all

git clone file://${clone} ${newN}
cd ${newN}

git reflog expire --expire=now --all
git repack -ad
git gc --prune=now

答案 5 :(得分:23)

这已不再那么复杂了,您可以在您的repo克隆上使用git filter-branch命令来剔除您不想要的子目录,然后推送到新的遥控器。

git filter-branch --prune-empty --subdirectory-filter <YOUR_SUBDIR_TO_KEEP> master
git push <MY_NEW_REMOTE_URL> -f .

答案 6 :(得分:19)

更新:git-subtree模块非常实用,git团队将其拉入核心并使其成为git subtree。见这里:Detach (move) subdirectory into separate Git repository

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/

答案 7 :(得分:18)

以下是CoolAJ86 "The Easy Way™" answer的一个小修改,以便拆分多个子文件夹(让我们说sub1sub2)到一个新的git存储库。

Easy Way™(多个子文件夹)

  1. 准备旧的仓库

    pushd <big-repo>
    git filter-branch --tree-filter "mkdir <name-of-folder>; mv <sub1> <sub2> <name-of-folder>/" HEAD
    git subtree split -P <name-of-folder> -b <name-of-new-branch>
    popd
    

    注意: <name-of-folder>不得包含前导或尾随字符。例如,名为subproject的文件夹必须作为subproject传递,而不是./subproject/

    Windows用户注意事项:当您的文件夹深度为&gt; 1,<name-of-folder>必须有* nix样式文件夹分隔符(/)。例如,名为path1\path2\subproject的文件夹必须作为path1/path2/subproject传递。此外,请勿mv使用move命令。

    最后注意事项:与基本答案的唯一且重大差异是脚本的第二行&#34; git filter-branch...&#34;

  2. 创建新的仓库

    mkdir <new-repo>
    pushd <new-repo>
    
    git init
    git pull </path/to/big-repo> <name-of-new-branch>
    
  3. 将新回购链接链接到Github或任何地方

    git remote add origin <git@github.com:my-user/new-repo.git>
    git push origin -u master
    
  4. 清理,,如果需要

    popd # get out of <new-repo>
    pushd <big-repo>
    
    git rm -rf <name-of-folder>
    

    注意:这会在存储库中保留所有历史引用。如果您真的担心提交密码,请参阅原始答案中的附录。您需要减小.git文件夹的文件大小。

答案 8 :(得分:11)

原始问题想要XYZ / ABC /(*文件)成为ABC / ABC /(*文件)。在为我自己的代码实现接受的答案后,我注意到它实际上将XYZ / ABC /(*文件)更改为ABC /(*文件)。 filter-branch手册页甚至说,

  

结果将包含该目录(并且仅包含)作为其项目根目录。“

换句话说,它将顶级文件夹“提升”一级。这是一个重要的区别,因为,例如,在我的历史中,我已经重命名了一个顶级文件夹。通过将文件夹“提升”到一个级别,git在我进行重命名的提交时失去了连续性。

I lost contiuity after filter-branch

我对问题的回答是制作2个存储库副本并手动删除要保留在每个存储库中的文件夹。该手册页支持我:

  

[...]如果简单的单一提交足以解决您的问题,请避免使用[此命令]

答案 9 :(得分:7)

要添加到Paul's answer,我发现要最终恢复空间,我必须将HEAD推送到一个干净的存储库,并减少.git / objects / pack目录的大小。

$ mkdir ...ABC.git
$ cd ...ABC.git
$ git init --bare

在gc修剪之后,也可以:

$ git push ...ABC.git HEAD

然后你可以做

$ git clone ...ABC.git

并且ABC / .git的大小减少

实际上,推送清理存储库不需要一些耗时的步骤(例如git gc),即:

$ git clone --no-hardlinks /XYZ /ABC
$ git filter-branch --subdirectory-filter ABC HEAD
$ git reset --hard
$ git push ...ABC.git HEAD

答案 10 :(得分:6)

现在正确的方法如下:

git filter-branch --prune-empty --subdirectory-filter FOLDER_NAME [first_branch] [another_branch]

GitHub现在甚至对此类案件有small article

但请务必先将原始仓库克隆到单独的目录中(因为它会删除所有文件和其他目录,并且您可能需要使用它们)。

所以你的算法应该是:

  1. 将您的远程仓库克隆到另一个目录
  2. 使用git filter-branch只留下某个子目录下的文件,推送到新的远程
  3. 创建提交以从原始远程仓库中删除此子目录

答案 11 :(得分:5)

这里的大多数(全部?)答案似乎都依赖某种形式的git filter-branch --subdirectory-filter及其同类。这可能“大多数时候”工作,但是对于某些情况,例如重命名文件夹的情况,例如:

 ABC/
    /move_this_dir # did some work here, then renamed it to

ABC/
    /move_this_dir_renamed

如果你使用普通的git过滤器样式来提取“move_me_renamed”,那么当你最初的move_this_dir(ref)时,你将丢失从后面发生的文件更改历史记录。

因此看来真正保持所有更改历史记录的唯一方法(如果你的是这样的情况),实质上是复制存储库(创建一个新的存储库,设置成为原点),然后核对其他所有内容并将子目录重命名为父目录,如下所示:

  1. 在本地克隆多模块项目
  2. 分支 - 检查那里有什么:git branch -a
  3. 检查要包含在拆分中的每个分支,以便在工作站上获取本地副本:git checkout --track origin/branchABC
  4. 在新目录中制作副本:cp -r oldmultimod simple
  5. 进入新项目副本:cd simple
  6. 摆脱此项目中不需要的其他模块:
  7. git rm otherModule1 other2 other3
  8. 现在只剩下目标模块的子目录
  9. 删除模块子目录,以便模块根目录成为新项目根目录
  10. git mv moduleSubdir1/* .
  11. 删除relic subdir:rmdir moduleSubdir1
  12. 随时检查更改:git status
  13. 创建新的git repo并复制其URL以将该项目指向它:
  14. git remote set-url origin http://mygithost:8080/git/our-splitted-module-repo
  15. 验证这是好的:git remote -v
  16. 将更改推送到远程仓库:git push
  17. 转到远程仓库并检查它是否全部
  18. 对所需的任何其他分支重复此操作:git checkout branch2
  19. 这是在the github doc "Splitting a subfolder out into a new repository"步骤6-11之后将模块推送到新的仓库。

    这不会为您节省.git文件夹中的任何空间,但它会保留这些文件的所有更改历史记录,即使是重命名也是如此。如果没有“很多”历史遗失等等,这可能是不值得的。但至少你保证不会丢失旧的提交!

答案 12 :(得分:4)

我确实遇到了这个问题,但所有基于git filter-branch的标准解决方案都非常慢。如果你有一个小的存储库,那么这可能不是问题,这对我来说。我编写了另一个基于libgit2的git过滤程序,作为第一步,为主存储库的每次过滤创建分支,然后在下一步将它们推送到清理存储库。在我的存储库(500Mb 100000提交)上,标准的git filter-branch方法需要数天时间。我的程序需要几分钟才能进行相同的过滤。

它有一个神奇的名字git_filter并且住在这里:

https://github.com/slobobaby/git_filter

在GitHub上。

我希望它对某人有用。

答案 13 :(得分:4)

对于它的价值,这里是如何在Windows机器上使用GitHub。假设您在C:\dir1居住了一个克隆的回购。目录结构如下所示:C:\dir1\dir2\dir3dir3目录是我想要成为新的独立仓库的目录。

<强> Github上:

  1. 创建新的存储库:MyTeam/mynewrepo
  2. Bash提示:

    1. $ cd c:/Dir1
    2. $ git filter-branch --prune-empty --subdirectory-filter dir2/dir3 HEAD
      退回:Ref 'refs/heads/master' was rewritten(fyi:dir2 / dir3区分大小写。)

    3. $ git remote add some_name git@github.com:MyTeam/mynewrepo.git
      git remote add origin etc。没用,退回“remote origin already exists

    4. $ git push --progress some_name master

答案 14 :(得分:3)

当我mentioned above时,我不得不使用反向解决方案(删除所有不接触我的dir/subdir/targetdir的提交),这似乎很好地删除了大约95%的提交(根据需要)。但是,还有两个小问题。

第一次filter-branch完成了删除引入或修改代码的提交的工作,但显然,合并提交位于Gitiverse的工作站下方。

这是一个化妆品问题,我可以和一起生活(他说......避免眼睛慢慢退缩)

SECOND 剩下的几个提交几乎都是 ALL 重复了!我似乎已经获得了第二个冗余的时间表,该时间表涵盖了项目的整个历史。有趣的事情(你可以从下面的图片中看到)是,我的三个本地分支并非都在同一时间线上(这就是为什么它存在并且不仅仅是垃圾收集)。

我唯一可以想象的是,其中一个已删除的提交可能是filter-branch 实际上已删除的单个合并提交,并且创建了并行时间轴,现在每个-unmerged strand获取了自己的提交副本。 (耸肩我的TARDiS在哪里?)我很确定我可以解决这个问题,虽然我真的很想知道它是怎么发生的。

在疯狂的mergefest-O-RAMA的情况下,我可能会离开那一个,因为它已经在我的承诺历史中如此坚定 - 每当我走近时都会对我施加威胁 - 它似乎没有实际上是造成任何非美容问题,因为它在Tower.app中非常漂亮。

答案 15 :(得分:3)

更简单的方法

  1. 安装git splits。我基于jkeating's solution将其创建为git扩展。
  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 XY1 XY2

  3. 在某处创建一个空的仓库。我们假设我们在GitHub上创建了一个名为xyz的空仓库,其路径为: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. 将新创建的远程仓库克隆到新的本地目录中 #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

答案 16 :(得分:3)

使用此过滤器命令删除子目录,同时保留标记和分支:

git filter-branch --index-filter \
"git rm -r -f --cached --ignore-unmatch DIR" --prune-empty \
--tag-name-filter cat -- --all

答案 17 :(得分:3)

我建议GitHub's guide to splitting subfolders into a new repository。这些步骤类似于Paul's answer,但我发现他们的说明更容易理解。

我修改了说明,以便它们申请本地存储库,而不是在GitHub上托管的存储库。

  

Splitting a subfolder out into a new repository

     
      
  1. 打开Git Bash。

  2.   
  3. 将当前工作目录更改为您要创建新存储库的位置。

  4.   
  5. 克隆包含子文件夹的存储库。

  6.         

    git clone OLD-REPOSITORY-FOLDER NEW-REPOSITORY-FOLDER
    
         
        
    1. 将当前工作目录更改为克隆的存储库。
    2.         

      cd REPOSITORY-NAME
      
           
          
      1. 要从存储库中的其余文件中过滤掉子文件夹,请运行git filter-branch,提供以下信息:      
            
        • FOLDER-NAME:项目中您要创建单独存储库的文件夹。      
              
          • 提示:Windows用户应使用/分隔文件夹。
          •   
        •   
        • BRANCH-NAME:当前项目的默认分支,例如mastergh-pages
        •   
      2.         

        git filter-branch --prune-empty --subdirectory-filter FOLDER-NAME  BRANCH-NAME 
        # Filter the specified branch in your directory and remove empty commits
        Rewrite 48dc599c80e20527ed902928085e7861e6b3cbe6 (89/89)
        Ref 'refs/heads/BRANCH-NAME' was rewritten
        

答案 18 :(得分:2)

https://github.com/vangorra/git_split

查看git_split项目

将git目录转换为自己位置的自己的存储库。没有子树有趣的业务。此脚本将获取您的git存储库中的现有目录,并将该目录转换为其自己的独立存储库。在此过程中,它将复制您提供的目录的整个更改历史记录。

./git_split.sh <src_repo> <src_branch> <relative_dir_path> <dest_repo>
        src_repo  - The source repo to pull from.
        src_branch - The branch of the source repo to pull from. (usually master)
        relative_dir_path   - Relative path of the directory in the source repo to split.
        dest_repo - The repo to push to.

答案 19 :(得分:2)

在垃圾收集之前,您可能需要“git reflog expire --expire = now --all”之类的东西来实际清理文件。 git filter-branch只删除历史记录中的引用,但不删除保存数据的reflog条目。当然,先测试一下。

虽然我的初始条件有所不同,但我的磁盘使用率却大幅下降。也许--subdirectory-filter否定了这种需要,但我对此表示怀疑。

答案 20 :(得分:2)

在使用git filter-branch(也许是git的较新版本运行2.22+时,它说要使用此新工具git-filter-repo。这个工具肯定可以简化我的工作。

使用filter-repo进行过滤

根据原始问题创建XYZ存储库的命令:

# create local clone of original repo in directory XYZ
tmp $ git clone git@github.com:user/original.git XYZ

# switch to working in XYZ
tmp $ cd XYZ

# keep subdirectories XY1 and XY2 (dropping ABC)
XYZ $ git filter-repo --path XY1 --path XY2

# note: original remote origin was dropped
# (protecting against accidental pushes overwriting original repo data)

# XYZ $ ls -1
# XY1
# XY2

# XYZ $ git log --oneline
# last commit modifying ./XY1 or ./XY2
# first commit modifying ./XY1 or ./XY2

# point at new hosted, dedicated repo
XYZ $ git remote add origin git@github.com:user/XYZ.git

# push (and track) remote master
XYZ $ git push -u origin master

假设:  *远程XYZ仓库在推送之前是新的并且是空的

过滤和移动

就我而言,我还想移动几个目录以获得更一致的结构。最初,我运行了一个简单的filter-repo命令,然后运行了git mv dir-to-rename,但是我发现使用--path-rename选项可以使我的历史略好。现在,我看到的是5 hours ago(在GitHub UI中),它与原始仓库中的修改时间相匹配,而不是在新仓库中看到上次修改的last year

而不是...

git filter-repo --path XY1 --path XY2 --path inconsistent
git mv inconsistent XY3  # which updates last modification time

我最终跑了...

git filter-repo --path XY1 --path XY2 --path inconsistent --path-rename inconsistent:XY3
笔记:
  • 我认为Git Rev News blog post很好地解释了创建另一个回购过滤工具的原因。
  • 我最初尝试在原始存储库中创建与目标存储库名称匹配的子目录的路径,然后然后过滤(使用git filter-repo --subdirectory-filter dir-matching-new-repo-name)。该命令正确地将该子目录转换为复制的本地存储库的根目录,但是它还导致创建该子目录只进行了三次提交的历史记录。 (我还没有意识到--path可以被多次指定;因此避免了在源存储库中创建子目录的需要。)由于有人在我注意到我已经承诺了到源存储库的时候未能保留历史记录,我只是在git reset commit-before-subdir-move --hard命令之后使用了clone,并在--force命令中添加了filter-repo以使其能够在经过稍微修改的本地克隆上运行。
git clone ...
git reset HEAD~7 --hard      # roll back before mistake
git filter-repo ... --force  # tell filter-repo the alterations are expected
  • 由于我不了解git的扩展模式,所以对安装感到很困惑,但是最终我克隆了git-filter-repo并将其符号链接到$(git --exec-path)
ln -s ~/github/newren/git-filter-repo/git-filter-repo $(git --exec-path)

答案 21 :(得分:1)

将它放入你的gitconfig:

reduce-to-subfolder = !sh -c 'git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter cookbooks/unicorn HEAD && git reset --hard && git for-each-ref refs/original/ | cut -f 2 | xargs -n 1 git update-ref -d && git reflog expire --expire=now --all && git gc --aggressive --prune=now && git remote rm origin'

答案 22 :(得分:1)

我确定git子树一切都很精彩,但是我想要移动的git托管代码的子目录都是eclipse。 所以如果你使用egit,那很容易。 获取要移动的项目和团队 - >断开连接,然后将团队&gt;共享到新位置。它将默认尝试使用旧的仓库位置,但您可以取消选中使用现有的选择并选择新位置来移动它。 所有人都欢呼。

答案 23 :(得分:0)

您可以轻松尝试https://help.github.com/enterprise/2.15/user/articles/splitting-a-subfolder-out-into-a-new-repository/

这对我有用。我在上述步骤中遇到的问题是

  1. 在此命令git filter-branch --prune-empty --subdirectory-filter FOLDER-NAME BRANCH-NAMEBRANCH-NAME主控

  2. 如果由于保护问题提交时最后一步失败,请遵循-https://docs.gitlab.com/ee/user/project/protected_branches.html

答案 24 :(得分:0)

我找到了非常简单的解决方案, 想法是复制存储库,然后删除不必要的部分。 它是这样工作的:

1)克隆要拆分的存储库

Messenger

2)移至git文件夹

git clone git@git.thehost.io:testrepo/test.git

2)删除不必要的文件夹并提交

cd test/

3)使用BFG

删除不必要的文件夹表单历史记录
rm -r ABC/
git add .
enter code here
git commit -m 'Remove ABC'
  

对于乘法文件夹,您可以使用逗号

cd ..
java -jar bfg.jar --delete-folders "{ABC}" test
cd test/
git reflog expire --expire=now --all && git gc --prune=now --aggressive

4)检查历史记录中是否不包含您刚刚删除的文件/文件夹

java -jar bfg.jar --delete-folders "{ABC1,ABC2}" metric.git

5)现在您有了没有ABC的干净存储库, 所以就把它推到新的起源

git log --diff-filter=D --summary | grep delete

就是这样。您可以重复步骤以获取另一个存储库

只需删除XY1,XY2并重命名XYZ->步骤3上的ABC

答案 25 :(得分:0)

发现这篇精彩的文章 Original reference 易于理解。如果无法访问,请在此处记录下来。

1.准备当前存储库

$ cd path/to/repository
$ git subtree split -P my-folder -b my-folder
Created branch 'my-folder'
aecbdc3c8fe2932529658f5ed40d95c135352eff

文件夹的名称必须是相对路径,从存储库的根目录开始。

2.创建新存储库

$ cd my-folder
$ git init
Initialized empty Git repository in /Users/adamwest/Projects/learngit/shop/my-folder/.git/
$ git add .
$ git commit -m "initial commit"
[master (root-commit) 192c10b] initial commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 file

这里我们只需要 cd 到新文件夹,初始化新存储库,并提交任何内容。

3.添加新的远程仓库并推送

$ git remote add origin git@github.com:robertlyall/my-folder.git
$ git push origin -u master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 199 bytes | 199.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:robertlyall/my-folder.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

我们从 GitHub 远程添加新的存储库,然后将我们的第一个提交推送到它。

4.从主存储库中删除文件夹并推送

$ cd ../
$ git rm -rf my-folder
rm 'my-folder/file'
$ git commit -m "Remove old folder"
[master 56aedbe] remove old folder
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 my-folder/file
$ git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 217 bytes | 217.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:robertlyall/shop.git
   74dd8b3..56aedbe  master -> master

最后,我们 cd 回到根目录,从我们的主存储库中删除该文件夹,然后提交并推送更改。 现在,我们在主存储库中有该文件夹,但链接到一个完全独立的存储库,可以在多个项目中重复使用。