How can I rewrite history so that all files, except the ones I already moved, are in a subdirectory?
我在git下有一个项目。有一天,我把所有项目文件从当前目录移到了项目下的foo/bar/。我用git mv做的。然后我又添加了一些文件,并对已经存在的文件做了一些更改。
因此,现在当我查看foo/bar/file.c的历史时,我只能看到在移动文件之后所做的更改。
我试图用各种方法来解决这个问题(使用子目录过滤器等的filter-branch),但没有任何帮助,所以我被安排在这里。如果你能给我任何帮助,我将不胜感激。谢谢!
要用移动的文件重写历史记录,请执行以下操作:
如果您希望项目的历史看起来像所有文件都在foo/bar目录中,那么您需要做一点手术。使用带有"树过滤器"的git filter-branch重写提交,以便在不存在foo/bar的任何地方创建该提交,并将所有文件移动到该提交:
1 2 3 4 5
| git filter-branch --prune-empty --tree-filter '
if [ ! -e foo/bar ]; then
mkdir -p foo/bar
git ls-tree --name-only $GIT_COMMIT | xargs -I files mv files foo/bar
fi' |
现在,历史记录将被记录,就好像所有文件都始终位于foo/bar中一样。
要查看移动文件的历史记录,请执行以下操作:
如果您只想查看在过去某个时间点被移动或重命名的文件的历史记录,那么只需使用--follow选项到git log:
1
| git log --follow foo/bar/file.c |
号
- @亚历山大:你可以试着把一行(所有内容都在单引号中)放在一个实际的脚本中,用bash-shebang行。我认为filter-branch可能是想在sh而不是bash中运行这个程序。
- 我取得了一些进展。似乎if语句中的双括号导致了这个错误。单支架工作得更好,但还是不行。问题是,在我将所有内容移动到foo/bar/i之前,我有一个名为foo的目录,其中包含项目中的一些内容。所以现在它抱怨"不能移动foo' to a subdirectory of itself, foo/bar/foo’"。
- @亚历山大:你只需要相应地调整剧本。类似于:使用grep从要移动的文件列表中过滤出foo的if [[ ! -e foo/bar ]]; then mkdir -p foo/bar; git ls-tree --name-only $GIT_COMMIT | grep -v ^foo$ | xargs -I files mv files foo/bar; fi。注意,我还假设在您移动它下面的所有内容之前,bar子目录从未存在过。如果情况并非如此,那么您还需要在脚本中说明这一点。
- 除了回购最初包含子模块的情况外,这一点非常有效。子模块路径将不会更新。我假设我可以移除子模块,然后在新路径下重新添加它,并添加一个提交,但这并没有那么优雅。我的谷歌搜索有什么遗漏吗?
- 使用[[给我带来了一个问题,因为没有这样的命令(它是一个shell bulitin),使用[和]解决了这个问题。
- +因为这救了我一天。
- 然而,这似乎是可行的,被删除的文件会保留在重写的存储库中。
- 如果您对大型回购交易的结果不满意,这是否可以轻易地撤销?
- @是的,可以很容易地解开。git filter-branch将保留refs/原件下原始分支机构的副本。所以,在FOO分支上,你可以做一些像git reset --hard original/foo这样的事情来恢复原来的状态。
总结一下我所做的工作。对我有用的命令是:
1
| if [ ! -e foo/bar ]; then mkdir -p foo/bar; git ls-tree --name-only $GIT_COMMIT | grep -v ^foo$ | xargs -I files mv files foo/bar || echo""; fi |
我在末尾添加的echo命令确保即使mv失败,整个命令也将继续运行。它没有移动foo/bar/foo的内容,但我可以接受。
多谢丹的塑造(!!!!)杰弗洛米帮忙。