【问题标题】:Copy multiple files and dirs from one git repo to another while keeping their original history将多个文件和目录从一个 git 存储库复制到另一个,同时保留其原始历史记录
【发布时间】:2016-07-04 10:25:50
【问题描述】:

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

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

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

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

总之,我更喜欢使用第三种解决方案,只要有一个选项可以将合并中的所有提交都列在新仓库的历史记录中。否则我必须坚持第一个解决方案,这在我的情况下有点不方便和乏味。任何建议,帮助将不胜感激。

谢谢。

【问题讨论】:

  • and also preserve the commit history for all files - 所以只是删除你不想保留的位?您可能会发现这很有用:New repo with copied history of only currently tracked files.
  • 谢谢。当您说delete everything and just restore the files you want to keep: 时,您能帮我了解我们在哪一步恢复我想要保留的文件吗?因为 keep-these.txt 将列出当前存储库中存在的所有文件,对吗?还是我应该先删除不需要的文件,然后再执行 git ls-files > keep-these.txt ?我对 git 很陌生,所以不太熟悉它的所有概念。
  • 请编辑您的问题以明确。从问题描述中不清楚为什么 git rm somefolder; git commit -m "deleting somefolder" 没有做你想做的事——你特别要求保留 all 文件的提交历史;还要具体说明您所拥有的以及您期望的结果;将顶级文件夹/文件放在问题中 - 以及您想要实现的目标。
  • @AD7six 首先很抱歉这么晚才回复。您的回答帮助我实现了我想要的,因此我必须立即开始工作,因为我昨天发布了。现在,从我之前拥有的一个 repo 中,我创建了四个新的 repo,它们都有自己文件的 git 历史记录。我知道前面的问题没有澄清最后一部分 git history for their own files 因此已相应更新。
  • 但可能我一开始就考虑了很多,从那时起,我更关心拥有我正在移动的文件的历史而不是没有我不移动的文件的历史记录。但是你的回答帮助我完成了这两件事,因此我现在更开心了:)`为什么 git rm somefolder; git commit -m "deleting somefolder" 没有做你想做的事`正如我所说,我是 git 新手,因此我不知道你可以克隆一个 git repo 并只需通过删除和添加将其转换为新的 repo遥远的起源。之前没有任何帖子或论坛建议过这一点。

标签: git github


【解决方案1】:

这对我有用(结合 @AD7six 和 @Olivier 的答案)将我的 orig-repo 拆分为多个新的存储库。我在这里列出了只创建一个新的 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

在此之后,我可以查看 Github 上 new-repo1 中的文件历史记录,也可以通过命令行使用 git log 查看文件历史记录

【讨论】:

    【解决方案2】:

    使用方法 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
      

      现在您有了一个本地 git 存储库,其中包含 folder/to/keep 子目录,并保留了其所有历史记录。

    编辑

    由于您需要保留多个子目录,我假设您有一个文件列表要保存在一个名为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
    

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

    【讨论】:

    • 感谢您的回答。但我的要求是在新存储库中保留多个文件夹和文件,而不仅仅是一个 folder/to/keep。可以修改您的解决方案来做到这一点吗?
    • 另外,到底如何从 detached_repo 创建一个新的 Github 存储库?是否会使用--no-hardlinks 选项和清理步骤使我能够在detached_repo 上执行git init 并将其作为新的存储库推送到Github 上?另外我想澄清一下,这些步骤不会以任何方式修改原始存储库,即它的文件和历史记录不受影响?
    • 好的,我的第二条评论得到了答复。我做了一个git init 然后remote add origin &lt;url-of-new-empty-repo-on-Github&gt; git push -u origin develop
    • 我用一种支持多个子目录的方法编辑了我的答案。
    • 感谢您的更新。但是我不想尝试你的答案,正如我所意识到的那样,克隆 repo 并删除不需要的目录和文件就足够了,而且容易得多。然后为了删除不需要的历史记录,我使用了 AD7six 的 answere here
    【解决方案3】:

    对于这种情况,您可能想尝试一下git-import

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

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

    这些补丁随后会应用到新的存储库:

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

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

    详情请查阅:

    【讨论】:

    • 注意原来的链接已经不存在了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-30
    • 2012-05-18
    • 2019-10-13
    • 2011-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多