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

问题描述 投票:0回答:3

我想将多个 Git 存储库(例如 repoA 和 repoB)合并到一个新存储库中。新存储库 (repoNew) 应在单独的子目录中包含每个 repoA 和 repoB。由于到目前为止我只在本地工作,所以我可以对存储库做任何我想做的事情。

在这种情况下,标准方法似乎是使用

git filter-branch
重写每个 repoA 和 repoB 的历史记录,使其看起来好像它们一直位于子文件夹中,然后将它们合并到 repoNew 中。

第一步是困扰我的问题。我很清楚这样的答案,例如如何重写历史记录,以便除我已经移动的文件之外的所有文件都位于子目录中?(丹·莫尔丁的答案),这正是我想要的。

他提出了如下建议:

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:无法将 'src' 移动到 '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'

该命令似乎仍然随机失败。我尝试了几次,每次不同的提交时都会出现“无法移动”的失败。我观察到,当我排除

.gitignore
时,完成所有提交的机会似乎增加了。我能够在所有三个不同的存储库上连续执行移动,而不会失败。当我只是为了好玩而在其中一个存储库的另一个废弃副本上再次尝试时,它再次失败了。

由于据称某个进程使用某些文件,有时我也很难删除我的一次性副本,因此该问题可能与 Windows 7 文件访问处理有关,但我无法在那里做出认真的假设。

继续尝试直到成功当然是荒谬的,并且可能不适用于具有大量提交的存储库(我的只有〜30)。

信息:我在 Windows 7 64 位企业版上使用 git-bash 和 git 版本 1.7.10.msysgit.1。

git git-filter-branch git-rewrite-history
3个回答
2
投票

我怀疑你正在寻找类似于 git subhistory 的东西。这是一个非常小的项目,似乎维护得不好,但它的设计目的几乎完全按照您所描述的那样。尝试一下!


1
投票

我已经编写了一个基于 libgit2 的程序来过滤 git 分支以用于另一个目的,我对此稍作修改以实现您想要的功能。你可以试试。

它位于 github 上 git_filter 的 subdir 分支中:

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

我刚刚在我们的 100000 提交存储库上测试了它,花了 43 秒。

我编写该程序是因为基于 git 过滤器分支的解决方案需要几天到几周的时间才能完成。

示例配置过滤“测试”存储库并将所有内容放入“测试”子目录中 - 您可以更改它以执行您想要的操作。


0
投票

如果要将根文件夹中的所有java文件移动到repoA子文件夹中,可以使用以下命令。

c="mkdir -p repoA; find . -maxdepth 1  -name '*.java' -exec mv -f '{}' repoA/ \;"
FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch --prune-empty --tree-filter "$c"

几个需要注意的地方

  1. mkdir -p repoA
    :使用
    -p
    ,如果
    repoA
    已经存在,此命令不会抛出错误。
  2. 您最好使用
    find
    而不是
    mv *.java repoA
    。如果没有 java 文件,Bash 不会扩展 glob,因此 mv 移动名为 *.java
    literal
    文件。
    shopt -s nullglob
    没有帮助,因为此选项将
    *.java
    计算为空字符串,因此 mv 命令变为
    mv repoA
    ,这是无效的。
  3. 使用
    -f
    选项和
    mv
    来允许覆盖。
  4. 将 while 树过滤器命令保存在变量中
    c
    以避免引用问题。
  5. 设置临时envariable
    variable FILTER_BRANCH_SQUELCH_WARNING=1
    以跳过git的警告。
© www.soinside.com 2019 - 2024. All rights reserved.