我意外地将一个不需要的文件(filename.orig
在解析合并时)提交到我的存储库几个提交之前,直到现在我才注意到它.我想从存储库历史记录中完全删除该文件.
是否可以重写更改历史记录,以便filename.orig
从未首先添加到存储库中?
如果您的情况不是问题中描述的情况,请不要使用此配方.此配方用于修复错误的合并,并将您的好提交重放到固定合并上.
虽然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
# 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
(请注意,您实际上并不需要临时分支,您可以使用'分离的HEAD'来执行此操作,但是您需要记下git commit --amend
步骤生成的提交ID 以提供给git rebase
命令而不是使用临时分支名称.)
原始海报说明:
我不小心将一个不需要的文件...提交到我的存储库几个提交之前...我想从存储库历史记录中完全删除该文件.
是否可以重写更改历史记录,以便
filename.orig
从未首先添加到存储库中?
有很多不同的方法可以完全从git中删除文件的历史记录:
修改提交.
硬重置(可能加上一个rebase).
非交互式rebase.
交互式rebase.
过滤分支.
在原始海报的情况下,修改提交本身并不是一个选项,因为他之后做了几个额外的提交,但为了完整起见,我还将解释如何做,对于其他任何想要的人修改他们以前的提交.
请注意,所有这些解决方案都涉及以另一种方式更改/重写历史记录/提交,因此任何拥有旧提交副本的人都必须执行额外的工作以将其历史记录与新历史记录重新同步.
如果您在先前的提交中意外地进行了更改(例如添加文件),并且您不希望该更改的历史记录再次存在,那么您可以简单地修改先前的提交以从中删除该文件:
git rmgit commit --amend --no-edit
与解决方案#1一样,如果您只想摆脱之前的提交,那么您还可以选择简单地对其父进行硬重置:
git reset --hard HEAD^
您的分支命令将难以恢复到以前的1 日父提交.
但是,如果像原始海报一样,您在提交之后进行了几次提交以撤消更改,则仍然可以使用硬重置来修改它,但这样做也涉及使用rebase.以下是您可以用来在历史记录中进一步修改提交的步骤:
# Create a new branch at the commit you want to amend
git checkout -b temp
# Amend the commit
git rm
git commit --amend --no-edit
# Rebase your previous branch onto this new commit, starting from the old-commit
git rebase --preserve-merges --onto temp master
# Verify your changes
git diff master@{1}
如果您只想完全从历史记录中删除提交,这将有效:
# Create a new branch at the parent-commit of the commit that you want to remove
git branch temp
# Rebase onto the parent-commit, starting from the commit-to-remove
git rebase --preserve-merges --onto temp master
# Or use `-p` insteda of the longer `--preserve-merges`
git rebase -p --onto temp master
# Verify your changes
git diff master@{1}
此解决方案将允许您完成与解决方案#2和#3相同的操作,即修改或删除历史记录中的提交,而不是之前的提交,因此您选择使用哪种解决方案取决于您.出于性能原因,交互式rebase不太适合重新设置数百次提交,所以我会在这种情况下使用非交互式rebase或filter分支解决方案(见下文).
要开始交互式rebase,请使用以下命令:
git rebase --interactive ~
# Or `-i` instead of the longer `--interactive`
git rebase -i ~
这将导致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)
您要修改或删除的提交将位于此列表的顶部.要删除它,只需删除列表中的行.否则,将"选择"对1"编辑" 圣行,就像这样:
edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
接下来,输入git rebase --continue
.如果您选择完全删除提交,那么您需要做的就是(除了验证,请参阅此解决方案的最后一步).另一方面,如果您想修改提交,那么git将重新应用提交,然后暂停rebase.
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
此时,您可以删除文件并修改提交,然后继续rebase:
git rmgit commit --amend --no-edit git rebase --continue
而已.作为最后一步,无论您是修改提交还是完全删除提交,最好通过将其与rebase之前的状态区分开来来验证没有对您的分支进行其他意外更改:
git diff master@{1}
最后,如果您想要从历史记录中彻底消除文件存在的所有痕迹,那么此解决方案是最佳选择,并且其他任何解决方案都不能完成任务.
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch '
这将从
根提交开始从所有提交中删除.如果您只是想重写提交范围HEAD~5..HEAD
,那么您可以将其作为附加参数传递给filter-branch
,如本答案中所指出的
:
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch ' HEAD~5..HEAD
同样,在filter-branch
完成之后,通过在过滤操作之前将分支与其先前状态区分开来来验证没有其他意外更改通常是个好主意:
git diff master@{1}
我听说BFG Repo Cleaner工具的运行速度比git filter-branch
你快,所以你可能也想把它作为一个选项来检查.它甚至在过滤器分支文档中正式提到作为一种可行的替代方案:
其他资源git-filter-branch允许您对Git历史记录进行复杂的shell脚本重写,但如果您只是删除大文件或密码等不需要的数据,则可能不需要这种灵活性.对于那些操作,您可能需要考虑BFG Repo-Cleaner,一种基于JVM的git-filter-branch替代方案,对于这些用例,通常至少快10-50倍,并且具有完全不同的特性:
文件的任何特定版本都只清理一次.与git-filter-branch不同,BFG不会根据历史记录中提交的位置或时间以不同方式处理文件.这种约束给出了BFG的核心性能优势,并且非常适合清洗坏数据的任务-你不在乎哪里坏的数据,你只是希望它消失了.
默认情况下,BFG充分利用多核机器,并行清理提交文件树.GIT-滤波器分支清洁提交顺序(即,在单线程方式),虽然它是 可以写入过滤器,包括它们自己的parallellism,在针对每一个执行的脚本提交.
该命令选项都远远超过git的过滤分支更严格,并致力于只是为了消除不必要的数据-例如任务:
--strip-blobs-bigger-than 1M
.
ProGit§6.4Git工具 - 重写历史.
git-filter-branch(1)手册页.
git-commit(1)手册页.
git-reset(1)手册页.
git-rebase(1)手册页.
BFG Repo Cleaner(参见创作者自己的回答).
如果你还没有提交任何东西,只git rm
需要文件和git commit --amend
.
如果你有
git filter-branch \ --index-filter 'git rm --cached --ignore-unmatch path/to/file/filename.orig' merge-point..HEAD
将通过每一个变化来自merge-point
于HEAD
,删除filename.orig和重写的变化.使用--ignore-unmatch
意味着如果由于某种原因,更改中缺少filename.orig,命令将不会失败.这是git-filter-branch手册页中 Examples部分的推荐方法.
Windows用户注意事项:文件路径必须使用正斜杠
这是最好的方法:http:
//github.com/guides/completely-remove-a-file-from-all-revisions
请务必先备份文件的副本.
编辑
不幸的是,Neon的编辑在审核过程中被拒绝了.
请参阅下面的Neons帖子,它可能包含有用的信息!
例如,删除*.gz
意外提交到git存储库的所有文件:
$ 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 repo,例如
$ 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,因此每个正在处理该项目的人都需要删除他们旧的repo副本,并在清理完历史记录后进行新的克隆.不方便的人越多,你就越需要一个充分的理由去做 - 你多余的文件并没有真正造成问题,但如果你只是在处理这个项目,那么如果你想要你也可以清理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.orig
(不在您的最新提交中)的文件.这比使用git-filter-branch
同样的东西要容易得多!
完全披露:我是BFG Repo-Cleaner的作者.
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