ときどき私がDVDリッピングをウェブサイトのプロジェクトに落とし込み、それから不用意にgit commit -a -m ...
にして、そしてレポを2.2ギグで膨らませました。次回編集を行い、ビデオファイルを削除し、すべてをコミットしましたが、圧縮ファイルは歴史上、リポジトリにまだ残っています。
それらのコミットからブランチを始めて、あるブランチを別のブランチにリベースできることを私は知っています。しかし、大きなファイルが履歴に表示されず、ガベージコレクションの手順で削除されないようにするために、2つのコミットをマージするにはどうすればいいですか。
Git履歴から不要なファイルを削除するために特別に設計されたgit-filter-branch
に代わる、シンプルで高速な BFGレポクリーナー を使用してください。
注意深く 使用法の指示 に従ってください、コア部分はちょうどこれです:
$ Java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git
サイズが100MBを超えるファイル(これは最新コミットには含まれていません)、Gitリポジトリの履歴から削除されます。その後、git gc
を使用してデッドデータを消去できます。
$ git gc --Prune=now --aggressive
BFGは通常git-filter-branch
を実行するより少なくとも 10-50x 速く、一般的に使いやすくなっています。
完全公開:私はBFGレポクリーナーの作者です。
あなたが他の開発者に歴史を公開しているなら、あなたがやりたいことは非常に破壊的です。履歴を修復した後に必要な手順については、 git rebase
のドキュメントの「上流からの回復」 を参照してください。
少なくとも2つの選択肢があります:git filter-branch
と対話式リベースです。どちらも以下で説明します。
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
スイッチを使うと、各コミットに関連してツリーの変更を見ることができます。
「不注意な」コミット(そのSHA1オブジェクト名はce36c98)では、ファイルoops.iso
は誤って追加され、次のコミットで削除されたDVDリッピングcb14efdです。前述のブログ投稿に記載されている手法を使用して、実行するコマンドは次のとおりです。
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-ripが存在する場合はそれを削除し、それ以外の場合は失敗しません。--tag-name-filter
はタグ名を書き換える方法を記述します。 cat
のフィルタは恒等操作です。上記のサンプルのようにあなたのリポジトリはタグを持っていないかもしれませんが、私は完全な一般性のためにこのオプションを含めました。--
はgit filter-branch
のオプションの終わりを指定します--all
に続く--
はすべての参照の省略形です。上記のサンプルのように、あなたのリポジトリは1つの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
新しい "Careless"コミットはother.html
のみを追加し、 "Remove DVD-rip"コミットはもうマスターブランチにはないことに注意してください。 refs/original/refs/heads/master
というラベルの付いたブランチには、間違いを犯した場合の元のコミットが含まれています。削除するには、 “リポジトリを縮小するためのチェックリスト” の手順に従います。
$ 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
最初の2つのコミット(「インデックス」と「管理ページ」)の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
あなたはそれを追加したことがないかのように「不注意」からoops.iso
を削除したいと思うでしょう、そして「Remove DVD-rip」はあなたにとって無駄です。そのため、インタラクティブなリベースを行うための私たちの計画は、「管理者ページ」を維持し、「不注意」を編集し、「DVDリッピングを削除」を破棄することです。
$ 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"で行を削除し、 "Careless"の操作を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
メッセージが示すように、私たちは編集したい「不注意な」コミットをしているので、2つのコマンドを実行します。
$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue
1つ目は、問題のあるファイルをインデックスから削除します。 2番目は更新されたインデックスになるように“不注意”を修正または修正し、-C HEAD
はgitに古いコミットメッセージを再利用するように指示します。最後に、git rebase --continue
は残りのリベース操作を進めます。
これは以下の歴史を与えます:
$ 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 --tree-filter 'rm -f DVD-rip' HEAD
--tree-filter
オプションは、プロジェクトをチェックアウトするたびに指定されたコマンドを実行してから結果を再コミットします。この場合は、DVD-ripというファイルを、存在するかどうかにかかわらず、すべてのスナップショットから削除します。
このリンクを参照してください。
(私がこの問題に対して私が見た最良の答えは、次のとおりです。 https://stackoverflow.com/a/42544963/714112 Googleの検索ランキングで上位に表示されますが、他の検索ランキングでは表示されません)。
このシェルスクリプトは、リポジトリ内のすべてのBLOBオブジェクトを最小のものから最大のものへとソートして表示します。
私のサンプルリポジトリでは、ここで見つけた他のものよりも 100倍高速に実行されました 。
私の信頼できるAthlon II X 4システムでは、それはLinux Kernel repositoryを5,622,155個のオブジェクトを1分以上で処理します。
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
上記のコードを実行すると、Nice 人間が読める形式の出力 のようになります。
...
0d99bb931299 530KiB path/to/some-image.jpg
2ba44098e28f 12MiB path/to/hires-image.png
bd1741ddce0d 63MiB path/to/some-video-1080p.mp4
a
から到達可能なすべてのコミットからファイルb
とHEAD
を削除したいとすると、次のコマンドを使用できます。
git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD
これらのコマンドは私の場合はうまくいきました:
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でテストしただけです)
# 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
SOでほとんどすべての答えを試した後、私はようやく私のレポジトリにある大きなファイルをすばやく削除して削除するこの宝石を見つけ、私は再び同期することができました。 http://www.zyxware.com/articles/ 4027 /ローカルおよびリモートのgitリポジトリからファイルを完全に削除する方法
ローカルの作業フォルダにCDをコピーして、次のコマンドを実行します。
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
これでリモートリポジトリがクリーンアップされます。
このコマンドは非常に破壊的になる可能性があることに注意してください。より多くの人々がレポに取り組んでいるならば、彼ら全員は新しい木を引かなければならないでしょう。あなたの目標がサイズを縮小しないことであるならば、3つの中央のコマンドは必要ではありません。フィルタブランチが削除されたファイルのバックアップを作成し、それが長期間そこに残ることができるからです。
$ 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
私は here と同じ問題に遭遇しましたが、私は この提案 に従うことで解決しましたが、git filter-branch --tree-filter 'rm -f path/to/file' HEAD
は私にとってはうまくいきました。
Pro-gitの本には 歴史の書き換え に関する章全体があります - filter-branch
/各コミットからのファイルの削除 セクションを見てください。
あなたのコミットがツリー全体を通るのではなく最近のものであることを知っているならば、次のようにしてください:git filter-branch --tree-filter 'rm LARGE_FILE.Zip' HEAD~10..HEAD
私は自分のサイトのginormous * .jpaバックアップを誤って保存していたbitbucketアカウントでこれに遭遇しました。
git filter-branch --Prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch MY-BIG-DIRECTORY-OR-FILE' --tag-name-filter cat -- --all
履歴を完全に書き換えるには、問題のフォルダをMY-BIG-DIRECTORY
に再配置します( tags を含む)。
出典: http://naleid.com/blog/2012/01/17/finding-and-purging-big-files-from-git-history
私は基本的にこの答えにあったことをしました: https://stackoverflow.com/a/11032521/1286423
(歴史のために、ここにコピー&ペーストします)
$ 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
私は物事の名前を変更したり動かしたりするのが好きなのでうまくいきませんでした。そのため、いくつかの大きなファイルが名前が変更されたフォルダーにありました、そして私はgcがそれらのファイルを指すtree
オブジェクトの中の参照のためにそれらのファイルへの参照を削除できなかったと思います。本当にそれを殺すための私の究極の解決策は、
# 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
私のリポジトリ(.git
)は32MBから388KBに変更されました。フィルタブランチでさえきれいにすることができませんでした。
branch filter
コマンドを使用してこれを行うことができます。
git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD
git filter-branch
は、コミット履歴から巨大なファイルを削除するために使用できる強力なコマンドです。ファイルはしばらくの間保持され、Gitは次のガベージコレクションでそれを削除します。下記はコミット履歴からファイルを削除する までの全プロセスです 。安全のために、最初に新しいブランチでコマンドを実行します。
# 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 -rm test
# Push it with force
$ git Push --force Origin master
Git Extensions を使用してください。これはUIツールです。それは、リポジトリ内のlageファイルを見つけてそれらを透過的に削除することを可能にする "Find large files"という名前のプラグインを持っています。
このツールを使用する前に 'git filter-branch'を使用しないでください。 'filter-branch'によって削除されたファイルを見つけることができません(Altough 'filter-branch'はリポジトリパックファイルからファイルを完全に削除しません)。 。
この問題に遭遇したとき、gitは私たちの歴史の中にそのファイルが一度存在したことを記憶しているので、git rm
だけでは十分ではないでしょう。
さらに悪いことには、BLOBを参照するとgitガベージコレクタがスペースをクリーンアップできなくなるため、リベースは簡単ではありません。これにはリモート参照とreflog参照が含まれます。
これらの参照をすべて削除することを試みる小さなスクリプトであるgit forget-blob
をまとめ、次にgit filter-branchを使用してブランチ内のすべてのコミットを書き換えます。
BLOBが完全に参照されなくなったら、git gc
はそれを取り除きます。
使い方はとても簡単ですgit forget-blob file-to-forget
。あなたはここでより多くの情報を得ることができます
Stack Overflowからの回答といくつかのブログエントリのおかげで、私はこれをまとめました。彼らへのクレジット!