从Git数据库完全删除提交

20

我需要将某个提交从git的提交历史中彻底删除。我希望能够删除提交 abc123...,使得执行 git checkout abc123... 时返回 error: pathspec 'abc123...' did not match any file(s) known to git.

QA 删除Git分支中的提交 部分回答了如何在HEAD中删除对提交的引用,但它并未涵盖查找所有包含特定提交的分支,也没有涵盖一旦特定提交成为悬空提交后如何使其失效和清除。

我应该如何实现这个目标?


这个提交做了什么?你想让它在下一个提交中继续发生吗?还是它添加了一个你想要完全删除的文件?无论哪种情况,你都必须使用某种形式的历史重写。 - Matthew Flaschen
我通过搜索“git obliterate”在网上找到了这篇有用的帖子 - Adam Monsen
2
这不是 在 Git 中删除分支的提交记录 的重复。那个问题问的是:“如何从分支中删除一个提交记录?”我的问题是:“如何从Git的数据库中完全删除一个提交记录?” - Ethan Reesor
@EthanReesor 您说得完全正确。我不明白为什么它四个月前没有重新打开。能够有选择地删除对象——不是将所有无法访问的东西都清除,而只是要删除的对象——会很有用。 - Guildenstern
2个回答

18
  1. List all branches that contain the commit:

    git branch --contains COMMITSHA
    
  2. Remove commit from these branches:

    git checkout BRANCH
    git rebase -i COMMITSHA^
    # delete line with commit and save
    

    If a changed branch is tracked in any remote, push it there with override:

    git push --force REMOTE BRANCH
    

    e.g:

    git push --force origin master
    

    (Note that, depending on your development process, the commit may appear in untracked remote branches as well.)

  3. Purge the commit, so it can not be restored from your local repo:

    git reflog expire --all BRANCH1 BRANCH2 # list all branches you changed
    git prune --expire now
    

    Note that you must also run this command on all remote repositories that had this commit. If you have no access to the remote repo, you have to cross your fingers — commit will eventually expire by itself and will be purged by git gc.

    Be warned that above commands will remove all dangling objects from Git repo and all the history of branch changes — so you wouldn't be able to restore (by non-forensic means) anything lost prior to its run.

  4. Tell all collaborators to fetch changed branches and update any work they might have based on them. They should do as follows:

    git fetch REMOTE      
    

    For each branch that is based on a branch you changed above (including the branch itself if they have it locally):

    git checkout BRANCH
    git rebase REMOTE/BRANCH
    git reflog expire --all BRANCH
    

    After they're done:

    git prune --expire now
    

如果存储库启用了reflogs,则此操作实际上不会删除所涉及的提交对象。 - Amber
这个涵盖了移除 reflogs 的内容。 - Adam Monsen
已将非手动 reflog 清理添加到答案中(尚未尝试...) - Alexander Gladysh
1
@AlexanderGladysh 你不应该使用 git rebase -i --preserve-merges COMMITSHA^ 来避免提交图的平坦化吗? - Harry

1
确保没有引用需要该提交(将其重置回之前,或者重新打基础),从 .git/objects 删除对象(它将位于以哈希的前两个字符命名的文件夹中,并且文件名将是其余的哈希)。
但是请注意,如果您将此提交推送到公共存储库,则仅在本地删除不会将其从远程存储库中删除。

哎呀,手动操作.git/objects可能不是个好主意,你可以使用git prune命令完成这项任务。 - Alexander Gladysh
@AlexanderGladysh git prune 包括更多步骤以确保对象实际上被删除(例如确保清除 reflogs 等)。直接删除对象文件不需要这些步骤,并且通常是安全的,只要您不依赖该提交进行任何操作。 - Amber
git reflog expire --all BRANCH 应该可以做到这一点,不是吗? - Alexander Gladysh
如果您已经成功执行了步骤1,并且没有引用需要该提交,则仅在此处执行步骤2是安全的;但是,您没有提供如何确保这一点的任何说明。 - IMSoP
如果提交最近被创建,这看起来像是一个完整的解决方案。但是,如果对象已经被移动到了打包文件中,它将无法运行。 - Guildenstern

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接