你需要的是git filter-branch
,可以将整个仓库移动到一个子目录中,并通过使其看起来一直如此来保留历史记录。在使用之前备份你的仓库!
以下是具体步骤,在/foo/bar
中运行:
git filter-branch --commit-filter '
TREE="$1";
shift;
SUBTREE=`echo -e 040000 tree $TREE"\tbar" | git mktree`
git commit-tree $SUBTREE "$@"' -- --all
这将使得名为
/foo/bar
的代码库具有另一个名为“bar”的子目录,并包含其整个历史内容。然后,您可以将整个代码库移至
foo
级别并添加
baz
代码。
更新:
好的,这里是正在发生的事情。提交是指向“树”(将其视为代表整个文件系统子目录内容的SHA)的链接,以及一些“父”SHA和一些元数据链接作者/消息等。git commit-tree
命令是将所有这些内容组合在一起的低级位。 --commit-filter
参数在过滤器过程中被视为shell函数并在git commit-tree
位置运行,必须像它那样运作。
我所做的是获取第一个参数,即要提交的原始树,并使用git mktree
来构建一个新的“树对象”,该对象通过子文件夹进行描述,而另一个低级别的git命令则用于在其中执行操作。为此,我必须将某些看起来像git树的东西进入管道,即一组(mode SP type SP SHA TAB filename)行;因此echo命令。 mktree
的输出然后替代了第一次链接到真正的commit-tree
时的第一个参数;"$@"
是一种传递所有其他参数不变的方式,已经使用shift
除去了第一个参数。有关信息,请参见git帮助mktree
和git help commit-tree
。
因此,如果您需要多个级别,则必须嵌套几个额外级别的树对象(尚未经过测试,但这是一般想法):
git filter-branch --commit-filter '
TREE="$1"
shift
SUBTREE1=`echo -e 040000 tree $TREE"\tbar" | git mktree`
SUBTREE2=`echo -e 040000 tree $SUBTREE1"\tb" | git mktree`
SUBTREE3=`echo -e 040000 tree $SUBTREE2"\ta" | git mktree`
git commit-tree $SUBTREE3 "$@"' -- --all
这应该会将真正的内容下移至 a/b/bar
(注意顺序已颠倒)。
更新:整合了Matthew Alpert下面的改进。如果没有--all
,这只能在当前已检出的分支上工作,但由于问题是关于整个存储库的,所以这种方式更加合理,而不是逐个分支操作。