我不小心提交了一个不需要的文件(文件名。Orig而解决合并)到我的仓库几个提交前,没有我注意到它直到现在。我想从存储库历史记录中完全删除该文件。

是否有可能重写更改历史这样的文件名。奥里格从一开始就没有被添加到存储库中?


当前回答

这就是git filter-branch的设计目的。

其他回答

简介:你有5个可用的解决方案

最初的海报写道:

我不小心提交了一个不想要的文件……多次提交到我的存储库 前……我想从存储库历史记录中完全删除该文件。

它是 可能重写更改历史这样的文件名。奥里格从来没有 首先添加到存储库中?

有许多不同的方法可以完全删除文件的历史记录 git:

修改提交。 硬重置(可能加上一个rebase)。 非交互式变基。 交互式重置。 过滤分支。

在原始海报的情况下,修改提交实际上不是一个选项 它本身,因为他后来又作了几次补充,但为了看在份上 完整的,我也将解释如何做,为任何人谁只是 想要修改之前的提交。

请注意,所有这些解决方案都涉及修改/重写历史记录/提交 以某种方式,所以任何拥有旧提交副本的人都必须这样做 额外的工作,重新同步他们的历史与新的历史。


解决方案1:修改提交

如果您不小心在之前的文件中进行了更改(如添加文件) 提交,那么您就不希望该更改的历史再存在了 你可以简单地修改之前的提交来删除文件:

git rm <file>
git commit --amend --no-edit

解决方案2:硬重置(可能加上一个基数调整)

就像解决方案#1,如果你只是想摆脱你之前的提交,那么你 也可以选择简单地对其父进行硬重置:

git reset --hard HEAD^

该命令将硬重置你的分支到前一个父节点 提交。

但是,如果像最初的海报一样,您已经提交了几次 要撤消更改的提交,仍然可以使用硬重置 修改它,但这样做也涉及到使用rebase。这里有一些步骤 你可以用它来修改历史上更远的提交:

# Create a new branch at the commit you want to amend
git checkout -b temp <commit>

# Amend the commit
git rm <file>
git commit --amend --no-edit

# Rebase your previous branch onto this new commit, starting from the old-commit
git rebase --rebase-merges --onto temp <old-commit> master

# Verify your changes
git diff master@{1}

解决方案3:非交互式Rebase

如果你只是想从历史记录中完全删除一个提交,这将是有效的:

# Create a new branch at the parent-commit of the commit that you want to remove
git branch temp <parent-commit>

# Rebase onto the parent-commit, starting from the commit-to-remove
git rebase --rebase-merges --onto temp <commit-to-remove> master

# Or use `-r` instead of the longer `--rebase-merges`
git rebase -r --onto temp <commit-to-remove> master

# Verify your changes
git diff master@{1}

解决方案4:交互式资源库

这个解决方案将允许您完成与解决方案#2和 #3,即修改或删除历史上更早的提交,而不是立即提交 以前的提交,所以你选择使用哪种解决方案是由你决定的。 交互式数据库重构并不适合对数百个提交进行数据库重构 由于性能原因,所以我将使用非交互式的rebases或filter分支 解决方案(见下文)在这种情况下。

要开始交互式重基,请使用以下命令:

git rebase --interactive <commit-to-amend-or-remove>~

# Or `-i` instead of the longer `--interactive`
git rebase -i <commit-to-amend-or-remove>~

类的父类将导致git将提交历史倒回 提交要修改或删除的。然后,它将为您提供一个列表 在git设置使用的任何编辑器中,以相反的顺序倒卷提交(这是 默认为Vim):

pick 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
pick 7668f34 Modify Bash config to use Homebrew recommended PATH
pick 475593a Add global .gitignore file for OS X
pick 1b7f496 Add alias for Dr Java to Bash config (OS X)

要修改或删除的提交将位于此列表的顶部。 要删除它,只需在列表中删除它的行。否则,将“pick”替换为 edit在第一行,像这样:

edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`

接下来,输入git rebase—continue。如果你选择完全删除提交, 然后,这就是你需要做的所有事情(除了验证,请参阅最后一步 这个解决方案)。另一方面,如果您想修改提交,则使用git 将重新应用提交,然后暂停变基。

Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

此时,您可以删除文件并修改提交,然后继续 变基:

git rm <file>
git commit --amend --no-edit
git rebase --continue

就是这样。最后一步,是修改提交还是删除提交 完全正确,验证没有其他意外更改总是一个好主意 是通过将你的分支与它在重基之前的状态进行差异来创建的:

git diff master@{1}

方案5:过滤分支

最后,如果你想彻底清除所有痕迹,这个解决方案是最好的 一个文件的存在历史,没有其他的解决方案是相当 这个任务。

git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>'

这将从根提交开始从所有提交中删除<file>。如果 相反,你只需要重写提交范围HEAD~5..头,然后你可以 将其作为附加参数传递给filter-branch,如中所指出的 这个答案:

git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>' HEAD~5..HEAD

同样,在过滤器分支完成之后,进行验证通常是个好主意 通过将您的分支与它的分支进行区分,没有其他意想不到的更改 过滤操作前的前一状态:

git diff master@{1}

过滤器分支替代方案:BFG回购清洁

我听说BFG Repo Cleaner工具比git filter-branch运行得更快,所以你可能也想把它作为一个选项来检查。它甚至在过滤器分支文档中作为一个可行的替代方案被正式提及:

git-filter-branch allows you to make complex shell-scripted rewrites of your Git history, but you probably don’t need this flexibility if you’re simply removing unwanted data like large files or passwords. For those operations you may want to consider The BFG Repo-Cleaner, a JVM-based alternative to git-filter-branch, typically at least 10-50x faster for those use-cases, and with quite different characteristics: Any particular version of a file is cleaned exactly once. The BFG, unlike git-filter-branch, does not give you the opportunity to handle a file differently based on where or when it was committed within your history. This constraint gives the core performance benefit of The BFG, and is well-suited to the task of cleansing bad data - you don’t care where the bad data is, you just want it gone. By default The BFG takes full advantage of multi-core machines, cleansing commit file-trees in parallel. git-filter-branch cleans commits sequentially (ie in a single-threaded manner), though it is possible to write filters that include their own parallellism, in the scripts executed against each commit. The command options are much more restrictive than git-filter branch, and dedicated just to the tasks of removing unwanted data- e.g: --strip-blobs-bigger-than 1M.

额外的资源

Pro Git§6.4 Git工具-重写历史。 git-filter-branch(1) Manual Page。 git-commit(1) Manual Page。 git-reset(1)手动页面。 git-rebase(1)手册页。 好心眼巨人的回购清洁(也看到这个答案从创造者自己)。

如果你的情况不是问题中描述的情况,请不要使用这个方法。这个配方是用来修复一个坏的合并,并在一个固定的合并中重新播放你好的提交。

尽管filter-branch会做你想做的事情,但这是一个相当复杂的命令,我可能会选择用git rebase来做这件事。这可能是个人喜好。Filter-branch可以在一个稍微复杂一点的命令中完成,而rebase解决方案则是一次执行一步等效的逻辑操作。

试试下面的食谱:

# create and check out a temporary branch at the location of the bad merge
git checkout -b tmpfix <sha1-of-merge>

# remove the incorrectly added file
git rm somefile.orig

# commit the amended merge
git commit --amend

# go back to the master branch
git checkout master

# replant the master branch onto the corrected merge
git rebase tmpfix

# delete the temporary branch
git branch -d tmpfix

(注意,你实际上不需要一个临时分支,你可以用一个'detached HEAD'来做到这一点,但你需要注意git commit——modify步骤生成的提交id,以提供给git rebase命令,而不是使用临时分支名称。)

你还可以使用:

git重置HEAD文件/路径

重写Git历史记录需要更改所有受影响的提交id,因此每个参与项目的人都需要删除他们的旧回购副本,并在清理历史记录后进行新的克隆。它给人带来的不便越多,你就越需要一个好的理由来这样做——你多余的文件并没有真正造成问题,但如果你只是在做这个项目,你也可以清理Git历史记录,如果你想的话!

为了尽可能简单,我建议使用BFG Repo-Cleaner,这是一个更简单、更快的Git -filter-branch的替代方案,专门用于从Git历史记录中删除文件。它让你的生活更轻松的一种方式是它实际上默认处理所有的引用(所有标签,分支等),但它也快了10 - 50倍。

你应该仔细遵循以下步骤:http://rtyley.github.com/bfg-repo-cleaner/#usage -但核心是:下载BFG jar(需要Java 6或以上)并运行以下命令:

$ java -jar bfg.jar --delete-files filename.orig my-repo.git

您的整个存储库历史将被扫描,任何名为filename的文件。origin(不在最近一次提交中)将被删除。这比使用git-filter-branch做同样的事情要容易得多!

完全披露:我是好心眼巨人回收清理器的作者。

这就是git filter-branch的设计目的。