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

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


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


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

尽管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 rm文件和git commit——modify。

如果你有

git filter-branch \
--index-filter 'git rm --cached --ignore-unmatch path/to/file/filename.orig' merge-point..HEAD

将从合并点到HEAD,删除文件名的每个更改。创建并重写更改。使用——ignore-unmatch意味着如果由于某种原因filename. conf命令不会失败。奥里格在变化中消失了。这是git-filter-branch手册页中的示例部分推荐的方法。

Windows用户注意:文件路径必须使用正斜杠


你还可以使用:

git重置HEAD文件/路径


这是最好的方法: http://github.com/guides/completely-remove-a-file-from-all-revisions

一定要先备份文件副本。

EDIT

Neon的编辑在审查过程中不幸被拒绝了。 请看下面的霓虹灯帖子,它可能包含有用的信息!


例如,删除所有不小心提交到git仓库的*.gz文件:

$ du -sh .git ==> e.g. 100M
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch *.gz' HEAD
$ git push origin master --force
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now
$ git gc --aggressive --prune=now

还是不管用吗?(我现在的git版本是1.7.6.1)

$ du -sh .git ==> e.g. 100M

不知道为什么,因为我只有一个主分支。无论如何,我终于得到了我的git回购真正清理了推到一个新的空的和裸露的git仓库,例如。

$ git init --bare /path/to/newcleanrepo.git
$ git push /path/to/newcleanrepo.git master
$ du -sh /path/to/newcleanrepo.git ==> e.g. 5M 

(是的!)

然后我把它克隆到一个新目录,并把它的。git文件夹移动到这个目录。如。

$ mv .git ../large_dot_git
$ git clone /path/to/newcleanrepo.git ../tmpdir
$ mv ../tmpdir/.git .
$ du -sh .git ==> e.g. 5M 

(是的!)终于清理干净了!)

在确认一切正常后,您可以删除../large_dot_git和../tmpdir目录(可能在几周或几个月后,以防万一……)


重写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做同样的事情要容易得多!

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


为了把它添加到Charles Bailey的解决方案中,我只是使用了git rebase -i来从之前的提交中删除不需要的文件,它就像一个魅力。 的步骤:

# Pick your commit with 'e'
$ git rebase -i

# Perform as many removes as necessary
$ git rm project/code/file.txt

# amend the commit
$ git commit --amend

# continue with rebase
$ git rebase --continue

简介:你有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)手册页。 好心眼巨人的回购清洁(也看到这个答案从创造者自己)。


我发现的最简单的方法是由leontalbot(作为评论)提出的,这是Anoopjohn发表的一篇文章。我认为有必要用自己的空间来回答:

(我将其转换为bash脚本)

#!/bin/bash
if [[ $1 == "" ]]; then
    echo "Usage: $0 FILE_OR_DIR [remote]";
    echo "FILE_OR_DIR: the file or directory you want to remove from history"
    echo "if 'remote' argument is set, it will also push to remote repository."
    exit;
fi
FOLDERNAME_OR_FILENAME=$1;

#The important part starts here: ------------------------

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch $FOLDERNAME_OR_FILENAME" -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

if [[ $2 == "remote" ]]; then
    git push --all --force
fi
echo "Done."

所有的功劳都归于Annopjohn和leontalbot,感谢他们指出了这一点。

NOTE

请注意,脚本不包括验证,因此请确保不会出错,并有备份以防出现错误。这招对我很管用,但对你可能就不管用了。小心使用它(如果你想知道发生了什么,请点击链接)。


You should probably clone your repository first.

Remove your file from all branches history:
git filter-branch --tree-filter 'rm -f filename.orig' -- --all

Remove your file just from the current branch:
git filter-branch --tree-filter 'rm -f filename.orig' -- --HEAD    

Lastly you should run to remove empty commits:
git filter-branch -f --prune-empty -- --all

当然,git filter-branch是正确的选择。

遗憾的是,这将不足以完全删除filename。来源于你的repo,因为它仍然可以被标签、reflog条目、遥控器等引用。

我建议删除所有这些引用,然后调用垃圾回收器。您可以使用本网站的git forget-blob脚本一步完成所有这些。

Git忘记blob filename.orig


如果这是你想要清理的最新提交,我尝试使用git版本2.14.3 (Apple git -98):

touch empty
git init
git add empty
git commit -m init

# 92K   .git
du -hs .git

dd if=/dev/random of=./random bs=1m count=5
git add random
git commit -m mistake

# 5.1M  .git
du -hs .git

git reset --hard HEAD^
git reflog expire --expire=now --all
git gc --prune=now

# 92K   .git
du -hs .git