如何拆分git存储库并按照目录重命名?

hai*_*img 22 git git-filter-branch

我目前有一个包含许多项目的大型git存储库,每个项目都在自己的子目录中.我需要将它拆分为单独的存储库,每个项目都在自己的仓库中.

我试过了 git filter-branch --prune-empty --subdirectory-filter PROJECT master

但是,许多项目目录在其生命中经历了多次重命名,并且git filter-branch不遵循重命名,因此有效地提取的repo在上次重命名之前没有任何历史记录.

如何从一个大的git repo中有效地提取子目录,并将所有该目录重命名回到过去?

hai*_*img 17

感谢@Chronial,我根据自己的需要制作了一个脚本来按摩我的git repo:

git filter-branch --prune-empty --index-filter '
    # Delete files which are NOT needed
    git ls-files -z | egrep -zv  "^(NAME1|NAME2|NAME3)" | 
        xargs -0 -r git rm --cached -q             
    # Move files to root directory
    git ls-files -s | sed -e "s-\t\(NAME1\|NAME2\|NAME3\)/-\t-" |
        GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
        git update-index --index-info &&
        ( test ! -f "$GIT_INDEX_FILE.new" \
            || mv -f "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE" )
'
Run Code Online (Sandbox Code Playgroud)

基本上这是做什么的:

  1. 删除我需要的三个目录NAME1,NAME2或NAME3 之外的所有文件(一个项目在其生命周期内重命名为NAME1 - > NAME2 - > NAME3).

  2. 一切举动里面这三个目录到仓库的根.

  3. 我需要测试"$ GIT_INDEX_FILE.new"是否存在,因为将svn导入git会创建没有任何文件的提交(仅限目录的提交).仅当repo最初是使用'git svn clone'创建时才需要.

  • 只是为了增加我自己努力的_amazing_答案,任何使用Mac的人都需要自制GNU grep,sed和findutils烧瓶,并用gegrep替换egrep,用gxargs替换xargs,并用gsed sed。 (2认同)

Chr*_*ial 6

我认为git没有内置功能。您将必须构建自己的过滤器。只需使用git filter-branch --prune-empty --tree-filter YOURSCRIPT。然后,您的脚本将必须标识正确的文件夹(可能是其中的特定文件的名称,或者可能是该项目过去拥有的所有名称的列表),删除其他所有内容并将文件夹内容上移。

如果您的回购确实很大,并且您没有时间运行此脚本,则可以使用来更快地达到相同的效果--index-filter,但是编写该脚本会更加复杂。您将不得不使用git命令来修改索引,而不是使用文件系统修改命令。


Wol*_*ang 5

我有一个很大的存储库,需要从中提取一个文件夹。甚至--index-filter预计要花8个小时才能完成。这是我所做的:

  1. 获取该文件夹的所有过去名称的列表。就我而言,只有两个,old-namenew-name
  2. 对于每个名称:

    $ git checkout master
    $ git checkout -b filter-old-name
    $ git filter-branch --subdirectory-filter old-name
    
    Run Code Online (Sandbox Code Playgroud)

    这将为您提供几个断开的分支,每个分支都包含一个名称的历史记录。

  3. filter-old-name分支应该结束与提交其重命名的文件夹,和filter-new-name分支应该开始用相同的承诺。(如果存在多个重命名,则同样适用:您将得到相等数量的分支,每个分支与下一分支共享一个提交。)一个分支应删除所有内容,另一个应重新创建。确保这两个提交具有相同的内容;如果没有,则除了重命名文件外,还对文件进行了修改,您将需要合并更改。(就我而言,我没有这个问题,所以我不知道如何解决。)

    一个简单的检查方法是尝试在两个提交filter-new-name之上重新建立基础filter-old-name,然后将两个提交压缩在一起:git应该抱怨这会产生一个空的提交。(请注意,您将需要在备用分支上执行此操作,然后再将其删除:重定基础将从提交中删除Committer信息,从而丢失了一些您想要保留的历史记录。)

  4. 下一步是将两个分支移植在一起,跳过两次重命名文件夹的提交。(否则,将会有一个奇怪的跳转,其中所有内容都将被删除并重新创建。)这包括查找两个提交的完整SHA(全部40个字符!),并将其放入git的信息中,其中名称分支的提交优先,而旧的命名分支的提交第二。

    $ echo $NEW_NAME_SECOND_COMMIT_SHA1 $OLD_NAME_PENULTIMATE_COMMIT_SHA1 >> .git/info/grafts
    
    Run Code Online (Sandbox Code Playgroud)

    如果您已正确完成此操作,git log --graph则现在应显示从新历史记录的末尾到旧历史记录的开始的一行。

  5. 目前,这种嫁接是暂时的:它尚未成为历史的一部分,并且不会随同克隆或推送一起出现。使其永久化:

    $ git filter-branch
    
    Run Code Online (Sandbox Code Playgroud)

    这将重新过滤分支,而无需尝试进行任何进一步的更改,从而使嫁接成为永久性的(更改filter-new-name分支中的所有提交)。您现在应该可以删除该.git/info/grafts文件。

在所有这些操作的最后,您现在应该在filter-new-name分支的两个名称中都包含该文件夹的所有历史记录。然后,您可以使用此单独的存储库,或将其合并到另一个存储库中,或使用此历史记录进行任何操作。