如何重写历史记录,以便除我已经移动的文件之外的所有文件都在子目录中?

Posted

技术标签:

【中文标题】如何重写历史记录,以便除我已经移动的文件之外的所有文件都在子目录中?【英文标题】:How can I rewrite history so that all files, except the ones I already moved, are in a subdirectory? 【发布时间】:2011-05-01 20:46:09 【问题描述】:

我在git 下有一个项目。有一天,我将所有项目文件从当前目录移动到项目下的foo/bar/。我使用git mv 做到了。然后我添加了更多文件并对现有文件进行了一些更改。

因此,现在当我查看foo/bar/file.c 的历史记录时,我只能看到我在移动文件后所做的更改。

我尝试以各种方式解决此问题(filter-branch 使用子目录过滤器等),但没有任何帮助,所以我在这里很累。我会很感激你能给我的任何帮助。 谢谢!

【问题讨论】:

【参考方案1】:

用移动的文件重写历史记录:

如果您希望项目的历史看起来好像所有文件都一直在目录foo/bar 中,那么您需要做一些小手术。使用带有“树过滤器”的git filter-branch 来重写提交,以便在任何不存在foo/bar 的地方创建它并将所有文件移动到它:

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

git log --follow foo/bar/file.c

【讨论】:

@Alexander:您可能想尝试使用 bash shebang 行将单行(单引号中的所有内容)放入实际脚本中。我认为filter-branch 可能试图在sh 中运行它,而不是bash 我取得了一些进展。似乎 if 语句中的双括号导致了此错误。单括号效果更好,但它仍然不起作用。问题是,在我将所有内容移至 foo/bar/ 之前,我有一个名为 foo 的目录,其中包含项目中的一些内容。所以现在它抱怨“无法移动foo' to a subdirectory of itself, foo/bar/foo'”。 @Alexander:您只需要相应地调整脚本。类似于: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 使用 grep 从要移动的文件列表中过滤掉 foo。请注意,我还假设 bar 子目录在您将所有内容移到它下之前从未存在过。如果不是这种情况,那么您还需要在脚本中考虑到这一点。 使用[[给我带来了问题,因为没有这样的命令(它是一个shell bulitin),使用[]解决了这个问题。 @TTT 是的,它可以轻松撤消。 git filter-branch 将在 refs/original 下保留原始分支的副本。因此,在分支 foo 上,您可以执行 git reset --hard original/foo 之类的操作来恢复原状。【参考方案2】:

在这里总结一下我所做的事情的简短摘要。对我有用的命令是:

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 的内容,但我可以忍受。

非常感谢 Dan Moulding (!!!) 和 Jefromi 的帮助。

【讨论】:

使用|| true 代替|| echo "" 更快。【参考方案3】:

安装 git-filter-repo:

git clone https://github.com/newren/git-filter-repo/

将 git-filter-repo 可执行文件添加到您的 $PATH(请参阅 INSTALL.MD 了解其他选项):

# this is just an example, you probably want to edit bashrc,
# or add a symlink to ~/bin or /usr/local/bin
PATH=$PATH:$PWD/git-filter-repo

现在 cd 到您要修改的 git 存储库,然后运行:

git filter-repo --to-subdirectory-filter foo/bar

【讨论】:

以上是关于如何重写历史记录,以便除我已经移动的文件之外的所有文件都在子目录中?的主要内容,如果未能解决你的问题,请参考以下文章

JQuery 隐藏除我搜索的 div 之外的所有 div [关闭]

.htaccess 阻止除我的 ip 之外的所有内容

无法在除我之外的其他浏览器/机器上运行我的 chrome 扩展:未打包的发行版中有些不匹配?

如何将分支内容移动到另一个存储库保留历史记录并避免复制原始存储库的完整历史记录?

可能破坏/重写历史的 Git 命令

为啥 git log 可能不显示移动文件的历史记录,我该怎么办?