git commit -a -m ...
命令,结果仓库膨胀了2.2GB。下次我做了一些修改,删除了视频文件,然后提交了所有更改,但是压缩文件仍然存在于仓库的历史记录中。我知道我可以从这些提交中创建分支,然后将一个分支合并到另一个分支上。但是我应该怎么做才能合并这两个提交,使得这个大文件不会在历史记录中显示,并且在垃圾回收过程中被清除掉呢?
git commit -a -m ...
命令,结果仓库膨胀了2.2GB。下次我做了一些修改,删除了视频文件,然后提交了所有更改,但是压缩文件仍然存在于仓库的历史记录中。java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git
git gc
清除无用的数据。git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push --force
git push --force
命令,否则远程仓库不会改变。 - Weiyigit gc
,然后重新执行您正在使用BFG进行的任何操作。一旦解决了这个问题,它就可以很好地工作。可能需要更明确的文档,但我不是最快的学习者;p - DaveRGPgit filter-branch
已被弃用,并且不再受支持。请参阅man页面获取更多信息。
你想做的事情非常具有破坏性,如果你对其他开发者公开历史记录。请参阅"从上游rebase恢复" 在 git rebase
文档中,以修复历史记录后的必要步骤。
你至少有两个选择:git filter-branch
和一个 交互式rebase,下面都有解释。
git filter-branch
我曾经遇到过类似的问题,通过一个Subversion导入得到庞大的二进制测试数据,并写了关于从git存储库中删除数据的文章。
假设你的git历史是:
$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A login.html
* cb14efd Remove DVD-rip
| D oops.iso
* ce36c98 Careless
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
git lola
是一个非标准但非常有用的别名(有关详细信息,请参阅本答案末尾的附录)。--name-status
开关用于 git log
,显示与每个提交相关的树修改。oops.iso
是错误添加并在下一次提交 cb14efd 中删除的 DVD 剪辑。使用上述博客文章中描述的技术,要执行的命令如下:git filter-branch --prune-empty -d /dev/shm/scratch \
--index-filter "git rm --cached -f --ignore-unmatch oops.iso" \
--tag-name-filter cat -- --all
选项:
--prune-empty
删除由于筛选操作而变为空(即不改变树形结构)的提交。在典型情况下,此选项可产生更清晰的历史记录。-d
指定一个尚不存在的临时目录,用于构建筛选后的历史记录。如果您在现代Linux发行版上运行,指定/dev/shm
中的树形目录将导致更快的执行速度。--index-filter
是主要操作,在历史记录的每个步骤中针对索引运行。您想要删除所有出现的oops.iso
,但它并不在所有提交中都存在。命令git rm --cached -f --ignore-unmatch oops.iso
会在出现时删除DVD副本,并在其他情况下不会出错。--tag-name-filter
描述如何重写标签名称。过滤器cat
为恒等操作。您的存储库可能没有任何标签,但我包括了此选项以保持完整性。--
指定git filter-branch
选项的结束。--all
在--
之后表示所有引用的简写形式。您的存储库可能只有一个引用(master),但我包括了此选项以保持完整性。$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A login.html
* e45ac59 Careless
| A other.html
|
| * f772d66 (refs/original/refs/heads/master) Login page
| | A login.html
| * cb14efd Remove DVD-rip
| | D oops.iso
| * ce36c98 Careless
|/ A oops.iso
| A other.html
|
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
other.html
,而“Remove DVD-rip”提交不再位于主分支上。标记为refs/original/refs/heads/master
的分支包含您的原始提交,以防您犯了错误。要删除它,请按照{{link1:“缩小存储库的清单”}}中的步骤进行操作。$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now
$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo
file:///...
克隆URL会复制对象,而不仅仅是创建硬链接。$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A login.html
* e45ac59 Careless
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
oops.iso
,“登录页面”得到了一个新的父级,所以它们的SHA1发生了变化。$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A login.html
* cb14efd Remove DVD-rip
| D oops.iso
* ce36c98 Careless
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
oops.iso
,就好像你从未添加过它一样,然后“Remove DVD-rip”对你来说就没有用了。因此,我们进行交互式变基的计划是保留“Admin page”,编辑“Careless”,并丢弃“Remove DVD-rip”。$ git rebase -i 5af4522
会启动一个带有以下内容的编辑器。pick ce36c98 Careless
pick cb14efd Remove DVD-rip
pick f772d66 Login page
# Rebase 5af4522..f772d66 onto 5af4522
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
edit ce36c98 Careless
pick f772d66 Login page
# Rebase 5af4522..f772d66 onto 5af4522
# ...
编辑
而不是选择
。Stopped at ce36c98... Careless
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue
-C HEAD
指示git重用旧的提交信息。最后,git rebase --continue
继续进行剩余的rebase操作。$ git lola --name-status
* 93174be (HEAD, master) Login page
| A login.html
* a570198 Careless
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
这个功能可以很好地展示你的代码树的图形,显示合并等分支结构。当然,有一些非常好用的图形界面工具可以展示这样的图形,但是lol = log --graph --decorate --pretty=oneline --abbrev-commit
git lol
的优势在于它可以在控制台或通过ssh
使用,因此对于远程开发或嵌入式开发来说非常有用...
So, just copy the following into
~/.gitconfig
for your full colorgit lola
action:
[alias] lol = log --graph --decorate --pretty=oneline --abbrev-commit lola = log --graph --decorate --pretty=oneline --abbrev-commit --all [color] branch = auto diff = auto interactive = auto status = auto
git push
命令中加上-f
(或--force
)选项:“通常情况下,该命令会拒绝更新一个不是本地参照所覆盖的远程参照的祖先的远程参照。这个标志禁用了这个检查。这可能会导致远程存储库丢失提交;请谨慎使用。” - Greg Bacongit filter-branch
已被弃用,并且不再受支持。请参阅man页面获取更多信息。
git filter-branch --tree-filter 'rm -f DVD-rip' HEAD
--tree-filter
选项在每次项目检出后运行指定的命令,然后重新提交结果。在这种情况下,您可以从每个快照中删除名为DVD-rip的文件,无论它是否存在。commit
(比如说35dsa2
),你可以用35dsa2..HEAD
替换HEAD
。在这种方式下,tree-filter
比index-filter
慢得多,因为它不会尝试检出所有提交并重写它们。如果你使用HEAD
,它会尝试那样做。 - alpha_989git push --all --force
命令,以使远程的历史记录与您现在在本地创建的修改版本相匹配。 (@stevec) - Noel Evans我看到的解决这个问题最好的答案是:https://dev59.com/AWkv5IYBdhLWcg3wZwG-#42544963。因为这个帖子在谷歌搜索排名上很高,但另一个帖子不在。
这个Shell脚本显示了存储库中所有Blob对象,按大小从小到大排序。
对于我的示例仓库,它比这里找到的其他脚本运行快了100倍。
在我的可靠的Athlon II X4系统上,它处理了包含5,622,155个对象的Linux内核存储库,只需要一分钟多点时间。
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print substr($0,6)}' \
| sort --numeric-sort --key=2 \
| cut --complement --characters=13-40 \
| numfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
当您运行上面的代码时,您将获得如下所示的漂亮的可读性良好的输出:
...
0d99bb931299 530KiB path/to/some-image.jpg
2ba44098e28f 12MiB path/to/hires-image.png
bd1741ddce0d 63MiB path/to/some-video-1080p.mp4
假设您希望从每个可以从 HEAD
访问到的提交中删除文件 a
和 b
,您可以使用以下命令:
git filter-branch --tree-filter 'rm -f a b' --prune-empty HEAD
git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD
--tag-name-filter cat
标记来重新标记新的相应提交,因为它们被重写了,即 git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' --tag-name-filter cat HEAD
(参见 这个相关答案)。 - naitsirhcgit filter-branch --index-filter 'git rm --cached --ignore-unmatch <filename>' HEAD
这个命令可以立即删除指定文件的 Git 历史记录。"workorder right of the bat" 句子不完整,无法翻译。请提供更多上下文或完整句子。 - eleijonmarckgit rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print substr($0,6)}' \
| sort --numeric-sort --key=2 \
| gnumfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
- Florian Oswald这个帖子中有很多很好的答案,但同时也有很多已经过时了。不再推荐使用git-filter-branch
,因为它在处理包含大量提交的大型代码库时非常慢且难以使用。
git-filter-repo
更快且更易于使用。
git-filter-repo
是一个Python脚本,可以在github上找到:https://github.com/newren/git-filter-repo。安装后,它看起来就像一个普通的git命令,可以通过git filter-repo
来调用。
你只需要一个文件:Python3脚本git-filter-repo。将其复制到包含在PATH变量中的路径中。在Windows上,你可能需要更改脚本的第一行(参考INSTALL.md)。你需要在系统上安装Python3,但这并不困难。
首先,你可以运行
git filter-repo --analyze
git filter-repo --invert-paths --path-match DVD-rip
Filter-repo非常快速。一项在我的电脑上用filter-branch大约花费9小时的任务,使用filter-repo只需4分钟即可完成。您可以通过filter-repo做更多好玩的事情。请参考文档。
警告:在副本上进行此操作。filter-repo的许多操作是无法撤销的。filter-repo将更改所有修改过的提交(当然也包括其下的所有子孙提交)的提交哈希!
在尝试了SO上的几乎所有答案之后,我终于找到了这个宝石,可以快速删除并清除我的存储库中的大文件,并允许我再次进行同步:http://www.zyxware.com/articles/4027/how-to-delete-files-permanently-from-your-local-and-remote-git-repositories
切换到您的本地工作文件夹并运行以下命令:
git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all
将FOLDERNAME替换为您希望从给定的Git仓库中删除的文件或文件夹。
完成后,请运行以下命令清理本地仓库:
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
现在将所有更改推送到远程仓库:
git push --all --force
这将清理远程代码库。
git push origin --tags --force
命令来在打标签的版本中从远程仓库中删除大文件。 - Kostas Stamosgit filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
# WARNING!!!
# This will rewrite completely your Bitbucket refs
# will delete all branches that you didn't have in your local
git push --all --prune --force
# Once you pushed, all your teammates need to clone repository again
# git pull will not work
git rm --cached files
从索引中删除了文件。Greg Bacon的建议更加完整,与我的建议相当类似,但他错过了--force索引,适用于多次使用filter-branch的情况,并且他写了很多信息,而我的版本就像它的摘要。 - Kostanos-f
选项而不仅仅是-rf
。结果如下:git rm --cached -rf --ignore-unmatch oops.iso
,而不是像@lfender6445建议的那样git rm --cached -r --ignore-unmatch oops.iso
。 - drstevok摆脱大文件
选项1:您不想保留这个大文件:
rm path/to/your/large/file # 删除大文件
选项2:您想将大文件保存到一个未跟踪的目录中
mkdir large_files # 创建目录large_files
touch .gitignore # 如果需要,创建.gitignore文件
'/large_files/' >> .gitignore # 不跟踪目录large_files
mv path/to/your/large/file large_files/ # 将大文件移动到未跟踪的目录中
保存您的更改
git add path/to/your/large/file # 将删除操作添加到索引中
git commit -m 'delete large file' # 提交删除操作
从所有提交中删除大文件
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch path/to/your/large/file" \
--prune-empty --tag-name-filter cat -- --all
git push <remote> <branch>
git filter-branch
命令,正如我所写的,我只是按照GitHub文档进行操作。我知道的是,该命令浏览您的.git
文件夹,并查找给定文件的所有记录,并将其从历史记录中删除。 - Kevin R.git filter-branch
后面添加 -f
。 - Sheldon请勿使用:
git filter-branch
在推送之后,这个命令可能不会改变远程仓库。如果你在使用它之后进行克隆,你会发现什么都没有改变,仓库的大小仍然很大。看起来这个命令现在已经过时了。例如,如果你按照 https://github.com/18F/C2/issues/439 中的步骤操作,这个方法是行不通的。
解决方案
这个解决方案基于以下方法:
git filter-repo
git rev-list --objects --all | grep -f <(git verify-pack -v .git/objects/pack/*.idx| sort -k 3 -n | cut -f 1 -d " " | tail -10)
git filter-repo --path-glob '../../src/../..' --invert-paths --force
git filter-repo --path-glob '*.zip' --invert-paths --force
git filter-repo --path-glob '*.a' --invert-paths --force
或者 无论你在步骤1中找到什么。
(3)
git remote add origin git@github.com:.../...git
git push --all --force
git push --tags --force
git filter-repo --analyze
来完成。 - undefined
git filter-repo
。你不应再使用git filter-branch
,因为它非常慢且经常难以使用。git filter-repo
快约100倍。 - Donat