我在几次提交之前意外地将一个不需要的文件(filename.orig
在解析合并时)提交到我的存储库,直到现在我才注意到它。我想从存储库历史记录中完全删除该文件。
是否可以重写更改历史记录,以便filename.orig
从未首先添加到存储库?
答案 0 :(得分:292)
如果您的情况不是问题中描述的情况,请不要使用此食谱。此配方用于修复错误合并,并将您的好提交重播到固定合并。
虽然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
(请注意,您实际上并不需要临时分支,您可以使用'分离的HEAD'来执行此操作,但您需要记下git commit --amend
步骤生成的提交ID以提供给git rebase
命令,而不是使用临时分支名称。)
答案 1 :(得分:199)
原始海报说明:
我意外地将不需要的文件提交给我的存储库几次提交 之前...我想从存储库历史记录中完全删除该文件。
是的吗? 可以重写更改历史记录,以便filename.orig
永远不会 首先添加到存储库?
有许多不同的方法可以完全删除文件的历史记录 GIT中:
在原始海报的情况下,修改提交并不是一个真正的选择 因为他之后做了几次额外的提交,但是为了这个缘故 为了完整性,我还将解释如何做到这一点,对于其他任何正义的人 想要修改他们之前的提交。
请注意,所有这些解决方案都涉及更改/重写历史记录/提交 在某种程度上,所以任何拥有旧版本的提交都必须这样做 将历史记录与新历史重新同步的额外工作。
如果您不小心在之前进行了更改(例如添加文件) 提交,然后您不希望该更改的历史存在 你可以简单地修改先前的提交以从中删除文件:
git rm <file>
git commit --amend --no-edit
就像解决方案#1一样,如果你只是想摆脱以前的提交,那么你 也可以选择简单地对其父级进行硬重置:
git reset --hard HEAD^
该命令会将您的分支硬重置为前一个 st 父级 提交。
然而 ,如果像原始海报一样,您之后做了几次提交 要撤消更改的提交,您仍然可以使用硬重置 修改它,但这样做也涉及使用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 --preserve-merges --onto temp <old-commit> 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 <parent-commit>
# Rebase onto the parent-commit, starting from the commit-to-remove
git rebase --preserve-merges --onto temp <commit-to-remove> master
# Or use `-p` insteda of the longer `--preserve-merges`
git rebase -p --onto temp <commit-to-remove> master
# Verify your changes
git diff master@{1}
此解决方案将允许您完成与解决方案#2和解决方案相同的事情 #3,即修改或删除历史记录中的提交,而不是立即提交 之前的提交,您选择使用哪种解决方案取决于您。 交互式rebase并不适合用于改变数百次提交 性能原因,所以我会使用非交互式rebase或filter分支 在这种情况下的解决方案(见下文)。
要开始交互式rebase,请使用以下命令:
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)
您要修改或删除的提交将位于此列表的顶部。 要删除它,只需删除列表中的行。否则,请更换&#34; pick&#34;同 &#34;编辑&#34;在1 st 行上,如下:
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
此时,您可以删除该文件并修改提交,然后继续 变基:
git rm <file>
git commit --amend --no-edit
git rebase --continue
那就是它。最后一步,无论是修改提交还是删除提交 完全地,验证没有其他意外的变化总是一个好主意 通过在变基之前将其与状态区分开来,对你的分支进行了分析:
git diff master@{1}
最后,如果你想完全消除所有的痕迹,这个解决方案是最好的 文件存在于历史中,其他解决方案都没有完全符合 任务。
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>'
这将从根提交开始从所有提交中删除<file>
。如果
相反,你只想重写提交范围HEAD~5..HEAD
,然后就可以了
将其作为filter-branch
的附加参数传递,如中所述
this answer:
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>' HEAD~5..HEAD
同样,在filter-branch
完成后,验证通常是一个好主意
通过使用它来分支你的分支没有其他意想不到的变化
过滤操作前的先前状态:
git diff master@{1}
我听说BFG Repo Cleaner工具的运行速度比git filter-branch
快,所以您可能也希望将其作为一个选项进行检查。 它甚至在filter-branch documentation中正式提到作为一种可行的替代方案:
git-filter-branch允许您进行复杂的shell脚本重写 你的Git历史,但如果你可能不需要这种灵活性 你只是删除不需要的数据,如大文件或密码。 对于那些操作,您可能需要考虑基于JVM的The BFG Repo-Cleaner 替代git-filter-branch,通常至少快10-50倍 那些用例,具有完全不同的特征:
文件的任何特定版本都会完全一次清除。与git-filter-branch不同,BFG不会给你机会处理 文件的不同之处取决于文件在何处或何时提交 历史。这种约束赋予了The的核心性能优势 BFG,非常适合清理坏数据的任务 - 你没有 关注哪里坏数据,你只是想要消失。
默认情况下,BFG充分利用多核机器,并行清理提交文件树。 git-filter-branch清理 顺序提交(即以单线程方式),但是 可以编写包含自己的并行性的过滤器 针对每次提交执行的脚本。
command options很多 比git-filter分支更严格,专用于 删除不需要的数据的任务 - 例如:
--strip-blobs-bigger-than 1M
。
答案 2 :(得分:118)
如果您之后没有提交任何内容,只需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 man page中示例部分的推荐方式。
Windows用户请注意:文件路径必须使用正斜杠
答案 3 :(得分:47)
这是最好的方法:
http://github.com/guides/completely-remove-a-file-from-all-revisions
请务必先备份文件的副本。
修改强>
Neon编辑在审核过程中遗憾地被拒绝了 请参阅下面的Neons帖子,它可能包含有用的信息!
E.g。删除意外提交到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 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文件夹移到这个目录中。 e.g。
$ 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
目录(可能在几周或几个月后,以防万一...)
答案 4 :(得分:26)
重写Git历史记录需要更改所有受影响的提交ID,因此每个正在处理该项目的人都需要删除他们的旧版本repo,并在清理完历史记录后进行全新的克隆。不方便的人越多,你就越需要一个充分的理由去做 - 你多余的文件并没有真正导致问题,但如果只有你正在处理这个项目,那么你也可以清理一下如果你想要Git历史记录!
为了尽可能简化,我建议使用BFG Repo-Cleaner,这是git-filter-branch
的一种更简单,更快的替代方案,专门用于从Git历史记录中删除文件。它让你的生活更轻松的一种方式是,它实际上默认处理所有引用(所有标记,分支等),但它也更快10 - 50x。
您应该仔细按照此处的步骤进行操作: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
的文件(不在您的latest commit中)。这比使用git-filter-branch
做同样的事情要容易得多!
完全披露:我是BFG Repo-Cleaner的作者。
答案 5 :(得分:13)
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
答案 6 :(得分:4)
只是将其添加到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
答案 7 :(得分:4)
我找到的最简单的方法是由leontalbot
(作为评论)建议的,这是一个post published by 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
以指出。
注意强>
请注意,该脚本不包含验证,因此请确保您不会犯错,并且如果出现问题,您还有备份。它对我有用,但它可能不适用于你的情况。请谨慎使用(如果您想知道发生了什么,请点击链接。)
答案 8 :(得分:3)
当然,x64\vc14\staticlib
是可行的方法。
可悲的是,这不足以从您的仓库中完全删除git filter-branch
,因为它仍然可以被标签,reflog条目,遥控器等引用。
我建议同时删除所有这些引用,然后调用垃圾收集器。您可以使用this网站上的filename.orig
脚本一步完成所有这些操作。
git forget-blob
答案 9 :(得分:1)
如果它是您想要清理的最新提交,我尝试使用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
答案 10 :(得分:0)
这是git filter-branch
的目的。
答案 11 :(得分:-1)
您也可以使用:
git reset HEAD file/path