我使用 git filter-branch 所做的更改发生了什么变化?

What happened to the changes that I made using git filter-branch?

我第一次尝试使用 git filter-branch 重写我的 git 历史。我通过编写一个 (Python) 脚本(我们称之为 edit_file)来完成此操作,该脚本对文件进行编辑(我们称之为 target_file)。然后,我 运行 这个命令:

git filter-branch --tree-filter "path/to/edit_file" HEAD

我得到了一个很好的输出流,这似乎表明我已经得到了我想要的效果,但是在查看时 target_file,我没有看到任何变化。当我直接 运行 edit_file 时,我的工作副本中的 target_file 成功接收到我想要的编辑。

听起来好像我的变化存在于git扭曲的头脑中某个深沉、黑暗和潮湿的隐蔽处,我只需要一个魔法咒语来召唤提出我的改变。我不知道这是否正确,我也不明白从哪里开始寻找,因为我读过的所有 material(包括官方 git 书)都表明一旦 git filter-branch完成后,我正在处理的 b运行ch 应该具有 edit_file 将在 target_file...

的每个版本上执行的更改

哈尔普?

对不起,如果这有点啰嗦,但我不知道需要哪些细节(因为这是混淆的核心特征)。


更多详情:

我说它看起来像 filter-branch 的原因是:

  1. 我可以在每次提交时看到 edit_file 运行ning 的输出,它表明所有提交都成功了。 edit_file 的输出随着它在不同版本的 target_file 上运行而发生变化,并且随着 git filter-branch 穿越历史,我能够看到 edit_file 的不同输出。

  2. 最后看到这个:

    Ref 'refs/heads/my-branch' was rewritten
    

PS:在做git filter-b运行ch之前,我运行

git checkout -b my-branch

创建一个名为 my-b运行ch 的新 b运行ch(并检查它),以防 git filter-b运行ch 发生大错特错。


看到 git filter-branch ... 保持 target_file 不变后,我 运行 git checkout -b my-branch,但我想那什么也没做。我认为它可能会做一些事情,因为 git filter-branch 的最后一行似乎是说 b运行ch my-branch 已被更改,但老实说我不明白那行是什么意思。

It sounds like my changes exist within some deep, dark, and dank recess in git's twisted mind, and I just need a magical incantation to summon forth my changes.

这就是你不使用 git filter-branch anymore (it is obsolete, along with BFG)

的原因

您使用 git filter-repo

所以...看起来 git filter-branch 除了在 .git 目录中留下奇怪的备份 poop 之外没有做任何事情。 git gc 不会出于任何原因清理它(也许应该有一个 git clean-poop 命令和 gc?)。不确定会怎样,除了

cd ..  # Assuming you are at the root of your repo
git clone --no-local original fresh-copy
cd fresh-copy

是的,即使我们正在制作本地副本,也需要 --no-local,因为 This. Is. GIIIIIIIIIT!这可能是您在尝试 filter-branch 或 filter-repo 之前应该做的。不知道为什么文档不建议这样做,但无论如何。不要做我所做的,只是通过创建一个新的分支来跳过。将 git filter-X 视为核武器。您不仅需要掩体来保护您;你需要一次性的平行宇宙

咬牙切齿后,我终于得到了 VonC 的建议,使用 git-filter-repo 工作。如果像我一样,你的平台没有足够新的 git 版本来使用 git-filter-repo(需要 >= 2.22),你可以做类似

sudo add-apt-repository ppa:git-core/ppa
# followed by the usual
sudo apt-get update
# song and dance routine...
sudo apt-get install git --upgrade

按照git *nix download page上的建议升级到"latest and greatest"。这对我不起作用(所以请注意盲目复制和粘贴上述建议),但显然,我有一个非常疯狂的系统,所以你可能比我幸运。无论如何...

一旦你获得足够新的 git 版本,你唯一需要的就是 git-filter-repo 脚本本身(令人惊讶的是,它只包含一个主文件) .所以只需下载 straight from github,并将其粘贴到您的 PATH 中的任何位置。记得先 chmod +x 把它搞出来。

您很可能不想使用 --path 标志,因为它不会只针对您要编辑的一个文件。相反,--path 将核对所有其他文件。

考虑到这一点,您需要做的就是这样:

git-filter-repo --blob-callback 'import sys
sys.path.append("dir/where/your/edit_file/py/file/lives")
import my_module

new = my_module.modify(blob.data.decode())
new_bytes = new.encode()
assert isinstance(new_bytes, bytes), ""
blob.data = new_bytes
'

是的,这是一个命令。 blob.data 包含 git-filter-repo 正在检查的任何文件的内容。此外,请注意它是一个字节对象,而不是 str。让我再次强调一个非常重要的观点:这个操作遍历每个文件(在每次提交中)。因此,如果您只打算修改一个文件,您的 my_module.modify 函数最好是非常有选择性的。 (git-filter-repo 真正需要的是一种让您的脚本检测 blob 路径的方法,而不仅仅是给您文件的内容。但希望您可以通过文件的内容来识别您的文件,而不仅仅是它的路径。)如果你搞砸了,这没什么大不了的,因为你可以核对新副本目录并重新开始。

在这次磨难之后,不要挖出你的眼睛。你的眼睛太宝贵了,不能被像 git 这样愚蠢的东西毁掉。淋浴时请随意大哭一场。嘿,至少你终于让它工作了,you weren't eaten by sharks

哦,还有一件事:您在第一步中所做的 git 克隆不会复制任何子模块,因为这样做很有意义并且太容易了。因此,您还必须在新副本中执行此操作(即使 git 克隆确实复制了 .gitmodules 文件):

git submodule init
git submodule update

PS:您可以从互联网上提供的各种 git 手册页生成器中获得一些治疗价值。他们确实读起来像 git 手册页,尽管它们实际上是随机的胡言乱语。