将多个文件和目录从一个git repo复制到另一个,同时保留其原始历史记录

时间:2016-07-04 10:25:50

标签: git github

我的要求:将一个git repo分解为多个git repos,保留与原始repo中相同的目录结构,并保留复制到新repo的文件的提交历史记录。 我已经尝试过了:

  1. 首先,我根据http://gbayer.com/development/moving-files-from-one-git-repository-to-another-preserving-history/中的建议尝试了 git filter-branch --subdirectory-filter 结果:历史记录已维护,但只能在运行时查看 git log - 关注 此外,在Github上无法看到原始提交历史记录。它只显示我的合并提交作为该文件的唯一提交,并且不显示任何先前的提交。我仍然可以忍受这种限制并接受它作为解决方案。但我对这种方法的另一个担忧是,对于我要复制的每个文件夹和每个文件,我需要多次克隆原始repo并且每次都重复所有这12或13个步骤。我想知道是否有更简单的方法,因为我移动了很多文件。此外,由于该职位已有5年历史,只是想知道是否有更新的更简单的解决方案? (令人惊讶的是,Google主要将此博客显示为第一个搜索结果)

  2. 我接下来尝试的是对早期Greg Bayer的帖子http://gbayer.com/development/moving-files-from-one-git-repository-to-another-preserving-history/#comment-2685894846的评论 这个解决方案通过使用 git子树分割使事情变得更简单,但结果与第一种情况中列出的结果相同。

  3. 然后我根据这个答案尝试了 git log --patch-with-stat git am 选项https://stackoverflow.com/a/11426261/5497551 结果:在应用修补程序时,这通常会在遇到合并时出错。 我尝试了使用 -m --first-parent 这个答案的建议之一。这解决了错误,但没有将任何合并扩展到它们的提交中,只是将合并列为单个提交。因此大多数提交历史都丢失了。 所以我添加了 - 3way 的另一个选项。这在提交过程中反复出现,并没有带来任何可接受的解决方案。

  4. 总之,我更倾向于使用第三种解决方案,如果只有一个选项让合并中的所有提交都列在新回购的历史中。否则我必须坚持第一个解决方案,这在我的情况下有点不方便和乏味。任何建议,帮助将不胜感激。

    感谢。

3 个答案:

答案 0 :(得分:4)

这对我有用(结合@ AD7six和@Olivier的答案)将我的orig-repo分成多个新的回购。我在此列出了仅创建一个新回购new-repo1的步骤。但同样也被用来创造其他人。

首先在Github上创建一个名为new-repo1

的新空回购
git clone [Github url of orig-repo]

git clone --no-hardlinks orig-repo new-repo1
cd new-repo1
git remote rm origin
git checkout -b master  //This step can be skipped. I had to do it since the default branch on my orig-repo was `develop`, but on the new-repo1 I wanted to create it as `master`

//I used a script here to delete files and directories not required in the new-repo1. 
//But if you have very few files/dirs to be deleted then you can do the below.
git rm <path of file 1 to be deleted>   
git rm <path of file 2 to be deleted>
git rm -rf <path of dir 1 to be deleted>

git commit -m "Deleted non-new-repo1 code"

git ls-files > keep-these.txt
git filter-branch --force --index-filter "git rm  --ignore-unmatch --cached -qr . ; cat $PWD/keep-these.txt | xargs git reset -q \$GIT_COMMIT --" --prune-empty --tag-name-filter cat -- --all

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now

git init
git remote add origin [Github url of new-repo1]
git push -u origin master

在此之后,我可以使用new-repo1

在Github上的git log以及命令行中查看文件的历史记录

答案 1 :(得分:0)

使用方法1,您是从本地目录还是URL克隆?如果从本地目录克隆,则应使用--no-hardlinks选项。否则,您在一个克隆中执行的操作可能会影响其他克隆的.git目录,因为git是硬链接文件。

我是这样做的:

  • 克隆本地存储库:

    git clone --no-hardlinks source_repo detached_repo
    
  • detached_repo中,删除原点(更多信息here以保留当前分支以外的分支):

    git remote rm origin
    
  • 删除您不想保留的标签。要删除所有代码,请使用git tag -l | xargs git tag -d

  • 使用filter-branch排除其他文件,因此可以对其进行修剪。我们还添加--tag-name-filter cat --prune-empty以删除空提交并重写标记(如果您要保留多个分支,请提供更多信息here):

    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter folder/to/keep HEAD
    
  • 然后删除备份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
    

    现在您拥有了folder/to/keep子目录的本地git存储库,并保留了所有历史记录。

修改

由于您需要保留多个子目录,因此我假设您有一个文件列表保存在名为files_to_keep的文件中。然后将git filter-branch步骤更改为:

git filter-branch --tag-name-filter cat --prune-empty \
    --index-filter 'git ls-tree -z -r --name-only --full-tree $GIT_COMMIT \
    | grep -z -v -F -f /absolute/path/to/files_to_keep \
    | xargs -0 -r git rm --cached -r' HEAD

您可以通过运行此命令生成要保留的文件列表:

git log --pretty=format: --name-status | cut -f2- | sort -u > all_files

并删除您不想保留的文件。

答案 2 :(得分:0)

对于这种情况,人们可能想尝试source

它基本上从一个仓库的给定文件或目录($object)创建补丁,并在保留历史记录的同时将它们应用于另一个仓库。

cd old_repo
git format-patch --thread -o "$temp" --root -- "$object"

然后将这些补丁应用于新的存储库:

cd new_repo
git am "$temp"/*.patch 

(如果需要,可以对旧存储库的不同部分重复此过程。)

详情请查询: