Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/git/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何从Git存储库中的提交历史记录中删除大型文件?_Git_Version Control_Git Rebase_Git Rewrite History - Fatal编程技术网

如何从Git存储库中的提交历史记录中删除大型文件?

如何从Git存储库中的提交历史记录中删除大型文件?,git,version-control,git-rebase,git-rewrite-history,Git,Version Control,Git Rebase,Git Rewrite History,我不小心把一张DVD翻录到了一个网站项目中,然后不小心git commit-a-m…,然后,zap,回购协议被2.2 gig的数据膨胀了。下一次我做了一些编辑,删除了视频文件,并提交了所有内容,但压缩文件仍然在存储库中,在历史上 我知道我可以从这些提交中启动分支,并将一个分支重设为另一个分支。但是,我应该如何合并这两个提交,以便大文件不会显示在历史记录中,并在垃圾收集过程中被清理?如果您已经向其他开发人员发布了历史记录,那么您想要做的是高度破坏性的。有关修复历史记录后的必要步骤,请参阅 您至少有

我不小心把一张DVD翻录到了一个网站项目中,然后不小心
git commit-a-m…
,然后,zap,回购协议被2.2 gig的数据膨胀了。下一次我做了一些编辑,删除了视频文件,并提交了所有内容,但压缩文件仍然在存储库中,在历史上


我知道我可以从这些提交中启动分支,并将一个分支重设为另一个分支。但是,我应该如何合并这两个提交,以便大文件不会显示在历史记录中,并在垃圾收集过程中被清理?

如果您已经向其他开发人员发布了历史记录,那么您想要做的是高度破坏性的。有关修复历史记录后的必要步骤,请参阅

您至少有两个选项:
git filter branch
和一个交互式重基,下面将对这两个选项进行解释

使用
git过滤器分支
我在Subversion导入的大量二进制测试数据中遇到了类似的问题,并写了一篇文章

假设您的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
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html
请注意,这是一个非标准但非常有用的别名。使用
--name status
开关,我们可以看到与每个提交相关联的树修改

在“不小心”提交(其SHA1对象名称为ce36c98)中,文件
oops.iso
是意外添加的DVD rip,在下一次提交cb14efd时删除。使用上述博客文章中描述的技术,要执行的命令是:

git过滤器分支--prune empty-d/dev/shm/scratch\
--索引过滤器“git rm--cached-f--ignore unmatch oops.iso”\
--标签名称过滤器cat--all
选项:

  • --prune empty
    删除由于筛选操作而变为空的提交(即,不更改树)。在典型情况下,此选项会生成更清晰的历史记录
  • -d
    命名一个临时目录,该目录尚不存在,无法用于构建筛选的历史记录。如果您在现代Linux发行版上运行,请指定
  • --索引过滤器
    是主事件,在历史记录中的每个步骤都会针对索引运行。您想在任何地方删除
    oops.iso
    ,但它并不存在于所有提交中。git rm--cached-f--ignore unmatch oops.iso命令在DVD rip存在时删除它,否则不会失败
  • --标记名过滤器
    描述如何重写标记名。
    cat
    的过滤器是标识操作。您的存储库,就像上面的示例一样,可能没有任何标记,但出于全面的通用性,我包含了这个选项
  • --
    指定git筛选器分支的选项结尾
  • --all
    后面的
    --
    是所有参考文献的简写。与上面的示例一样,您的存储库可能只有一个ref(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
的分支包含您的原始提交,以防您出错。要将其卸下,请按照中的步骤进行操作

对于更简单的替代方法,请克隆存储库以丢弃不需要的位

$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo
使用
file:///...
clone URL复制对象,而不是仅创建硬链接

现在您的历史记录是:

$ 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
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html
前两次提交的SHA1对象名称(“索引”和“管理页面”)保持不变,因为筛选操作没有修改这些提交。“粗心”丢失了
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
$ 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
您想从“粗心”中删除
oops.iso
,就好像您从未添加过它一样,然后“删除DVD rip”对您毫无用处。因此,我们的计划进入一个交互式的重新基址是保持“管理页面”,编辑“粗心”,并放弃“删除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
# ...
也就是说,我们删除了带有“Remove DVD rip”的行,并将“carlose”上的操作更改为
edit
,而不是
pick

保存退出编辑器会在命令提示符下显示以下消息

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
第一个从索引中删除有问题的文件。第二个修改或修正“carlose”作为更新的索引,并且
-C HEAD
指示git重用旧的提交消息。最后,
git-rebase--continue
继续执行rebase操作的其余部分

这提供了以下方面的历史:

$ 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
* 93174be (HEAD, master) Login page
| A     login.html
* a570198 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

这正是您想要的。

请注意,此命令可能具有非常大的破坏性。如果有更多的人在回购协议上工作,他们将不得不重新开始。如果您的目标不是减小尺寸,那么中间的三个命令是不必要的。因为筛选器分支会创建已删除文件的备份,并且可以在那里保留很长时间

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force
git过滤器分支--树过滤器'rm-f path/to/file'头
虽然我遇到了与前面描述的相同的问题,但对我来说效果很好,我通过以下方法解决了这个问题


pro git的书中有一整章是关于-看看这一节。

这些命令在我的案例中起作用:

git 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
它与上述版本没有什么不同

对于那些需要将其推送到github/bitbucket的用户(我只使用bitbucket进行了测试):

使用,这是git筛选器分支的一个更简单、更快的替代方法,专为从git历史记录中删除不需要的文件而设计

仔细遵循以下步骤,核心部分就是:

$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git
有任何文件需要更新吗
git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all
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 filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force
# First, apply what's in the answer linked in the front
# and before doing the gc --prune --aggressive, do:

# Go back at the origin of the repository
git checkout -b newinit <sha1 of first commit>
# Create a parallel initial commit
git commit --amend
# go back on the master branch that has big file
# still referenced in history, even though 
# we thought we removed them.
git checkout master
# rebase on the newinit created earlier. By reapply patches,
# it will really forget about the references to hidden big files.
git rebase newinit

# Do the previous part (checkout + rebase) for each branch
# still connected to the original initial commit, 
# so we remove all the references.

# Remove the .git/logs folder, also containing references
# to commits that could make git gc not remove them.
rm -rf .git/logs/

# Then you can do a garbage collection,
# and the hidden files really will get gc'ed
git gc --prune --aggressive
# Do it in a new testing branch
$ git checkout -b test

# Remove file-name from every commit on the new branch
# --index-filter, rewrite index without checking out
# --cached, remove it from index but not include working tree
# --ignore-unmatch, ignore if files to be removed are absent in a commit
# HEAD, execute the specified command for each commit reached from HEAD by parent link
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch file-name' HEAD

# The output is OK, reset it to the prior branch master
$ git checkout master
$ git reset --soft test

# Remove test branch
$ git branch -d test

# Push it with force
$ git push --force origin master
git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch bigfile.txt' --prune-empty --tag-name-filter cat -- --all
git filter-repo --analyze
git filter-repo --invert-paths --path-match DVD-rip