将Git仓库历史记录的一部分移动到另一个仓库中

7
这里有很多关于使用git filter-branch将文件夹从一个仓库移动到新仓库的帖子;我需要做的是将单个文件移动到新仓库。
我已经创建了新仓库,并将旧仓库从文件系统作为“remote”添加,创建了一个新的“根提交”(只是为新的单文件项目添加了一个README)。现在我需要将与该特定文件相关的提交移植到新的根提交上。
(我应该提到,在同一次提交中未修改此文件和其他任何文件;我认为这可能会使这个任务稍微容易些。)

可能是重复的问题:如何将一个Git仓库变基到另一个仓库上? - Josh Lee
2
不是的。这些不是拥有不同开发树的相同代码库或类似物。 - ELLIOTTCABLE
1
+1 很好的问题。从手册上并不立即明显如何做到这一点。 - Cascabel
4个回答

5

以下内容基于filter-branch手册中的一个示例:

git filter-branch --index-filter 'git ls-files -s | grep $'\t'<file-to-keep>$ | \
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && \
    mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' --prune-empty -- --all

索引过滤器使用git ls-files -s打印索引的当前内容,仅保留要保留的文件(grep非常强迫症——字段是以制表符分隔的,文件名是最后一个字段),然后使用该信息创建一个新索引,并将其移动到旧索引之上。 --prune-empty选项会导致filter-branch删除任何现在没有作用的提交(即它们只更改了其他文件),而-- --all则告诉它重写所有引用。
始终如此,对于filter-branch,最好在一个全新的克隆中执行此操作,这样如果您出现严重问题,您就可以安全地进行操作,尽管filter-branch确实会在refs/originals中保留备份。您可能还想阅读manpage中的缩小存储库的检查清单;总之,完成后,实际上摆脱所有不再需要的东西的最佳方法是简单地克隆已筛选的存储库。
即使在同一次提交中修改了该文件,这实际上也可以工作,尽管我认为您可以试图通过仅为所有更改了该文件的提交生成补丁,然后通过应用这些补丁构建一个新存储库来利用这一事实...但为什么要麻烦呢?
(附注:删除单个文件比保留单个文件要容易得多。在这种情况下,您只需使用git rm --cached --ignore-unmatch <filename>进行索引过滤器即可。)

看起来很不错!我一回家就会运行它,如果它有效的话,我会接受你的答案。 ^_^ 我感谢你的时间。 - ELLIOTTCABLE
哦,它失败了:Rewrite bb6a446cc395e38cdcb7af7953b151e0a706929d (1/15)mv: /Users/elliottcable/Code/Cest.c/.git-rewrite/t/../index.new: No such file or directory - ELLIOTTCABLE
@elliottcable:啊,我以前遇到过这种情况,但我不记得是什么原因导致的或者怎样解决。我在一个存储库上测试了这个,它是有效的...但是当我解决问题时,我有几次遇到了这个错误。我认为在变基期间工作目录存在一些混淆。既然您已经用另一种方法处理了它,我想现在不会尝试弄清楚它了。 - Cascabel

2

另一种解决方案是将旧存储库添加为另一个远程存储库,然后进行变基和挑选操作。

git clone old_repo
git clone new_repo
cd new_repo
git remote add temp_repo old_repo
git fetch temp_repo

# bring the changed from old_repo into new_repo
git rebase --onto <...> temp_repo/master
OR
git cherry-pick [list of relevant commits]

git remote rm temp_repo
git push origin HEAD

2

我最终使用了这个(本来与此无关的)帖子中的回答来构建解决方案。我没有将提交记录变基到一个新的树中,而是修改了旧的根,并使用--onto参数将内容变基到新的根:

我可以从Git仓库中删除初始提交吗?


0

在尝试了各种方法将文件或文件夹从一个Git存储库移动到另一个存储库后,唯一可靠的方法是以下步骤:

首先克隆您想要移动文件或文件夹的存储库,将该文件或文件夹移动到根目录,重写Git历史记录,然后克隆目标存储库并直接将带有历史记录的文件或文件夹拉入此目标存储库。

第一阶段

  1. 将仓库A复制一份,因为以下步骤会对此副本进行重大更改,您不应该推送!

    git clone --branch <branch> --origin origin --progress -v <git repository A url>
    例如:git clone --branch master --origin origin --progress -v https://username@giturl/scm/projects/myprojects.git
    

    (假设myprojects是您要从中复制的存储库)

  2. 进入该目录

    cd <git repository A directory>          例如:cd /c/Working/GIT/myprojects
    
  3. 删除与原始存储库的链接,以避免意外地进行任何远程更改(例如通过推送)

    git remote rm origin
    
  4. 浏览您的历史记录和文件,删除不在目录1中的任何内容。结果是将目录1的内容排出到存储库A的基础目录中。

    git filter-branch --subdirectory-filter <directory> -- --all
    例如:git filter-branch --subdirectory-filter subfolder1/subfolder2/FOLDER_TO_KEEP -- --all
    
  5. 仅适用于单个文件移动:浏览剩下的内容并删除除所需文件以外的所有内容。(您可能需要删除名称相同的不想要的文件并提交。)

    git filter-branch -f --index-filter \
    'git ls-files -s | grep $'\t'FILE_TO_KEEP$ |
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
    git update-index --index-info && \
    mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE || echo "Nothing to do"' --prune-empty -- --all
    例如:FILE_TO_KEEP = pom.xml,以仅保留FOLDER_TO_KEEP中的pom.xml文件。

第二阶段

  1. 清理步骤

    git reset --hard
    
  2. 清理步骤

    git gc --aggressive
    
  3. 清理步骤

    git prune
    

您可能想将这些文件导入到存储库B中的一个目录而不是根目录:

  1. 创建该目录

    mkdir <base directory>             例如:mkdir FOLDER_TO_KEEP
    
  2. 将文件移动到该目录中

    git mv * <base directory>          例如:git mv * FOLDER_TO_KEEP
    
  3. 将文件添加到该目录中

    git add .
    
  4. 提交您的更改,我们准备将这些文件合并到新存储库中

    git commit
    

第三阶段

  1. 如果您还没有B存储库的副本,请制作一个

    git clone <git repository B url>
    例如:git clone https://username@giturl/scm/projects/FOLDER_TO_KEEP.git
    

    (假设FOLDER_TO_KEEP是您要复制到的新存储库的名称)

  2. 进入该目录

    cd <git repository B directory>          例如:cd /c/Working/GIT/FOLDER_TO_KEEP
    
  3. 在存储库B中创建一个远程连接,作为存储库A中的一个分支

    git remote add repo-A-branch <git repository A directory>
    

    (repo-A-branch可以是任何东西 - 它只是一个任意的名称)

    例如:git remote add repo-A-branch /c/Working/GIT/myprojects
    
  4. 从此分支(仅包含要移动的目录)拉取到存储库B中。

    git pull repo-A-branch master
    

    拉取操作会同时复制文件和历史记录。注意:您可以使用合并而不是拉取,但拉取效果更好。

  5. 最后,您可能想要清理一下,删除与存储库A的远程连接

    git remote rm repo-A-branch
    
  6. 推送并完成。

    git push
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接