如何重写Git历史记录,使所有文件都在一个子目录中?

8

我想要将多个Git仓库(例如repoA和repoB)合并为一个新的仓库。新仓库(repoNew)应该包含每个repoA和repoB在单独的子目录中。由于我迄今为止只在本地工作,我可以对仓库做任何操作。

在这种情况下,标准方法似乎是使用git filter-branch重写每个repoA和repoB的历史记录,使其看起来好像它们一直在子文件夹中,然后将它们合并到repoNew中。

第一步让我感到困扰。我很清楚类似于如何重写历史记录以使所有文件都在子目录中,除了我已经移动的文件? (丹·莫尔丁的答案)的SO答案,这正是我想要的。

他建议采取以下步骤:

git filter-branch --prune-empty --tree-filter '
if [[ ! -e repoA ]]; then
    mkdir -p repoA
    git ls-tree --name-only $GIT_COMMIT | xargs -i mv {} repoA
fi'

结果应该是,<repoA-GIT-base>下的文件夹结构现在应该在<repoA-GIT-base>/repoA中。然而,这并非如此。上述命令在不同的提交时会随机失败,并显示类似“mv: cannot move 'src' into 'repoA/src'” 的消息。

如何避免在重写历史时出现这些错误的提交?

编辑:

您应该考虑从移动中排除.gitignore,方法如下:

git filter-branch --prune-empty --tree-filter '
if [[ ! -e repoA ]]; then 
    mkdir -p repoA;
    git ls-tree --name-only $GIT_COMMIT | 
    grep -ve '^.gitignore$' | 
    xargs -i mv {} repoA; 
fi'

该命令似乎仍然随机失败。我尝试了几次,每次失败的 "unable to move" 发生在不同的提交上。我观察到,当我排除 .gitignore 时,通过所有提交的机会似乎增加了。我能够连续在我的三个不同存储库中执行移动操作而没有失败。当我再次在另一个临时副本的存储库上尝试时,它又失败了。
由于有时我也很难删除我的临时副本,因为某个进程据称正在使用某些文件,所以问题可能与 Windows 7 文件访问处理有关,但我无法作出严肃的假设。
当然,一直尝试直到成功是荒谬的,这可能在具有大量提交的存储库上无法奏效(我的只有约30个)。
信息:我在 Windows 7 64位企业版上使用 git-bash 和 git version 1.7.10.msysgit.1。

我在昨天的一个几乎相同的问题上发布了我的答案,只是作为将存储库合并在一起的替代方案。 - Gary Fixler
这是我最初想要做的方式,但我想保留单个文件的历史记录。我还编辑了问题,因为我忘记了一些重要的东西,抱歉。 - svenhuebner
2个回答

2

我猜你想找的是类似于git subhistory的东西。这是一个非常小的项目,看起来没有得到很好的维护,但它也几乎完全按照你所描述的功能进行设计。试试吧!


非常不错的项目。这个对我很有帮助。唯一的缺陷是它无法将子项目合并到空仓库中(即将多个项目合并到一个新的空仓库中,每个项目在一个子目录中),但是Git本身在这方面也不太稳定。 - dataless

1
我已经基于libgit2编写了一个程序,用于过滤git分支以实现其他目的,稍作修改后即可用于此处需要的功能。您可以尝试使用它。
该程序位于github上git_filter子目录中:

https://github.com/slobobaby/git_filter/tree/subdir

我刚刚在我们的10万次提交的代码库上进行了测试,用时43秒。
我编写了这个程序,是因为基于git filter-branch的解决方案需要数天甚至数周才能完成。
示例配置过滤了一个名为“test”的代码库,并将所有内容放在“test”子目录中,您可以更改此设置以实现您想要的效果。

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