如何将一个文件从一个Git仓库移动到另一个仓库并保留其历史记录

47
我正在尝试将一个单独的文件(称为foo.txt)从一个仓库移动到另一个(无关的)仓库,并保留其历史记录。ebneter's question显示了如何对子目录执行此操作。taw's question提供了一些提示和建议,但没有详细的步骤。jkeating's question看起来很有前途,但对我没有用。在谷歌搜索中,没有找到这个特定的用例。我正在寻找一系列清晰的命令来完成这个任务。
我开始遵循的命令序列是这样的:
$ git clone source-repo/ source-repo-copy
$ cd source-repo-copy
$ git filter-branch --tree-filter 'test ! "$@" = "foo.txt" && \
  git rm --cached --ignore-unmatch $@ || true' --prune-empty

我的筛选命令逻辑是使用 git rm 删除所有不是 foo.txt 的文件。我在命令中添加了 || true,以强制其返回零值,以满足 filter-branch 的要求。
我的意图是将 source-repo-copy 设置为目标代码库(target-repo)的远程仓库,并假设 git filter-branch 过滤掉除 foo.txt 以外的所有内容,然后将 source-repo-copy 拉取并合并到 target-repo 中。不幸的是,git filter-branch 命令似乎没有产生任何影响。它没有出现任何错误,并且似乎已经处理完 source-repo 中的 600 多个提交,但是当它完成后,git log 和 source-repo-copy 中的文件看起来都没有变化。难道不应该只剩下 foo.txt 文件,所有未涉及该文件的提交都从日志中消失吗?
此时,我不知道该如何继续了。有什么建议吗?

2
可能是重复的问题:如何将文件从一个 Git 存储库移动到另一个存储库(不是克隆),并保留历史记录。链接:https://dev59.com/z3M_5IYBdhLWcg3wdy_z - tripleee
1个回答

37

这对我有用,但是适用于整个目录。

此处所示。

~$ cd neu
~/neu$ git filter-branch --subdirectory-filter FooBar HEAD
~/neu$ git reset --hard
~/neu$ git remote rm origin
~/neu$ rm -r .git/refs/original/
~/neu$ git reflog expire --expire=now --all
~/neu$ git gc --aggressive
~/neu$ git prune
~/neu$ git remote add origin git://github.com/FooBar/neu.git

编辑:针对单个文件:

首先筛选目录:

 git filter-branch --prune-empty --subdirectory-filter myDirectory -- --all

过滤单个文件:

 git filter-branch -f --prune-empty --index-filter "git rm --cached --ignore-unmatch $(git ls-files | grep -v 'keepthisfile.txt')"

清理一些内容:

 git reset --hard
 git gc --aggressive
 git prune

这应该可以解决问题。


1
是的, --subdirectory-filter 的示例都似乎很简单,但我想移动单个文件,而不是子目录。有什么想法吗? - Randall Cook
1
我按照你的建议过滤了目录,使用'.'代替'myDirectory'。结果目录中没有任何文件(只有.git)。git log仅显示合并提交(共12个)。然后我过滤了单个文件,但由于git rm被调用时参数列表为空,所以出现了错误。我在命令的末尾添加了|| true,以防止git rm的投诉中止过滤器。之后,仍然没有文件,而且git log中只有4个合并提交。最后我使用了git reset/gc/prune,但没有看到任何变化。我想这没起作用。:( 我很高兴我在测试repo上尝试了这个。:) - Randall Cook
2
请在 GitHub 或其他平台上传带有你的目录结构的测试仓库(或者告诉我这个结构)。如果 Git 仓库包含 testDir/myFile{1..3}.txt,那么它就可以正常工作。如果目录为空,则单个文件的筛选器无法工作,请尝试忽略目录过滤器并使用单个文件的筛选器。有可能你的 shell 不会正确识别该命令。git filter-branch -f --prune-empty --index-filter 'git rm --cached --ignore-unmatch $(git ls-files | grep -v "keepFile.txt")' - blang
1
一开始我并不清楚如何针对单个文件而不是目录进行操作。这是我的理解,供其他有类似困惑的人参考:编辑中的两个 filter-branch 命令应该替换原始代码中的一个 filter-branch 命令。然后,“进行一些清理”命令实际上包含在原始代码部分的其余部分中,并且应该按照此方式运行。最后,您需要 git push origin new-branch - Micah Smith
这也可以用于多个文件,只需通过修改 grep 语句中的 or 来实现:grep -v 'file1\|file2\|file3' - Dolphin
显示剩余3条评论

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