数コミット前に、 (マージの解決中に) 不要なファイルを誤っfilename.orig
て自分のリポジトリにコミットしましたが、今まで気付かなかったのです。リポジトリ履歴からファイルを完全に削除したい。
filename.orig
そもそもリポジトリに追加されていないような変更履歴を書き換えることは可能ですか?
数コミット前に、 (マージの解決中に) 不要なファイルを誤っfilename.orig
て自分のリポジトリにコミットしましたが、今まで気付かなかったのです。リポジトリ履歴からファイルを完全に削除したい。
filename.orig
そもそもリポジトリに追加されていないような変更履歴を書き換えることは可能ですか?
あなたの状況が質問に記載されているものでない場合は、このレシピを使用しないでください. このレシピは、不適切なマージを修正し、適切なコミットを修正済みのマージにリプレイするためのものです。
filter-branch
あなたが望むことをしますが、それは非常に複雑なコマンドであり、おそらくgit rebase
. それはおそらく個人的な好みです。ソリューションは同等の論理操作を一度に 1 ステップずつ実行しますがfilter-branch
、わずかに複雑な 1 つのコマンドで実行できます。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
名前。)
元のポスターには次のように記載されています。
不要なファイルを誤ってリポジトリにコミットしました...数コミット前に...リポジトリ履歴からファイルを完全に削除したいです。
filename.orig
そもそもリポジトリに追加されていないような変更履歴を書き換えることは可能ですか?
git からファイルの履歴を完全に削除するには、さまざまな方法があります。
元の投稿者の場合、コミットを修正すること自体は実際にはオプションではありません。彼は後でいくつかの追加のコミットを行ったからです。以前のコミットを修正します。
これらの解決策はすべて、履歴/コミットを別の方法で変更/再書き込みする必要があることに注意してください。そのため、コミットの古いコピーを持っている人は、履歴を新しい履歴と再同期するために余分な作業を行う必要があります。
以前のコミットで誤って変更 (ファイルの追加など) を行い、その変更の履歴をもう存在させたくない場合は、単純に以前のコミットを修正してファイルを削除することができます。
git rm <file>
git commit --amend --no-edit
解決策 #1 と同様に、以前のコミットを削除したいだけの場合は、単にその親にハード リセットを実行するオプションもあります。
git reset --hard HEAD^
そのコマンドは、ブランチを前の最初の親コミットにハードリセットします。
ただし、元の投稿者のように、変更を元に戻したいコミットの後にいくつかのコミットを行った場合でも、ハード リセットを使用して変更できますが、これにはリベースの使用も含まれます。履歴をさかのぼってコミットを修正するために使用できる手順は次のとおりです。
# 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 と同じことを達成することを可能にします。つまり、直前のコミットよりも履歴をさかのぼってコミットを変更または削除するため、どの解決策を使用するかはあなた次第です。インタラクティブなリベースは、パフォーマンス上の理由から、何百ものコミットのリベースには適していません。そのため、このような状況では、非インタラクティブなリベースまたはフィルター ブランチ ソリューション (以下を参照) を使用します。
インタラクティブなリベースを開始するには、次を使用します。
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)
変更または削除するコミットは、このリストの一番上になります。削除するには、リスト内のその行を削除するだけです。それ以外の場合は、次のように、 1行目の「pick」を「edit」に置き換えます。
edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
次に、 を入力しgit rebase --continue
ます。コミットを完全に削除することを選択した場合は、必要なことはそれだけです (検証以外は、このソリューションの最終ステップを参照してください)。一方、コミットを変更したい場合、git はコミットを再適用してからリベースを一時停止します。
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
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
ので、それもオプションとしてチェックしてみてください。実行可能な代替手段として、フィルターブランチのドキュメントで公式に言及されています。
git-filter-branch を使用すると、シェル スクリプトを使用して Git 履歴を複雑に書き換えることができますが、大きなファイルやパスワードなどの不要なデータを単純に削除するだけであれば、この柔軟性はおそらく必要ありません。これらの操作については、git-filter-branch の JVM ベースの代替であるBFG Repo-Cleanerを検討することをお勧めします。通常、これらのユースケースでは少なくとも 10 ~ 50 倍高速であり、まったく異なる特性を備えています。
ファイルの特定のバージョンは、1 回だけクリーンアップされます。BFG は、git-filter-branch とは異なり、ファイルが履歴内のどこで、いつコミットされたかに基づいて、異なる方法でファイルを処理する機会を与えません。この制約は、BFG のコア パフォーマンス上の利点を提供し、不良データをクレンジングするタスクに適しています。不良データがどこにあるのかは気にせず、ただそれを消したいだけです。
デフォルトでは、BFG はマルチコア マシンを最大限に活用し、コミット ファイル ツリーを並行してクレンジングします。git-filter-branch は、コミットを順次 (つまり、シングルスレッドの方法で) 消去しますが、各コミットに対して実行されるスクリプトで、独自の並列処理を含むフィルターを作成することは可能です。
コマンド オプションは、git-filter ブランチよりもはるかに制限的であり、不要なデータを削除するタスク専用です
--strip-blobs-bigger-than 1M
。
それ以降何もコミットしていない場合は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のマニュアルページの「例」セクションから推奨される方法です。
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
master ブランチが 1 つしかないため、理由はわかりません。とにかく、新しい空で裸のgitリポジトリにプッシュすることで、最終的にgitリポジトリを完全にクリーンアップしました。
$ 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 を変更する必要があるため、プロジェクトに取り組んでいるすべての人は、リポジトリの古いコピーを削除し、履歴を消去した後に新しいクローンを作成する必要があります。迷惑をかける人が多ければ多いほど、それを行う正当な理由が必要になります。余分なファイルが実際に問題を引き起こしているわけではありませんが、プロジェクトに取り組んでいるだけであれば、必要に応じて Git の履歴をクリーンアップすることもできます。に!
できるだけ簡単にするために、BFG Repo-Cleanergit-filter-branch
を使用することをお勧めします。これは、Git 履歴からファイルを削除するために特別に設計された、よりシンプルで高速な代替手段です。ここでの作業を楽にする 1 つの方法は、デフォルトですべての参照 (すべてのタグ、ブランチなど) を実際に処理することですが、 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
(最新のcommitにないもの) が削除されます。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
それを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
私が見つけた最も簡単な方法は、 Anoopjohn によって公開されleontalbot
た投稿である (コメントとして)によって提案されました。答えとして独自のスペースの価値があると思います:
(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
それを指摘した に送られます。
ノート
スクリプトには検証が含まれていないことに注意してください。そのため、間違いを犯さないようにし、何か問題が発生した場合に備えてバックアップを取っておくようにしてください。私にはうまくいきましたが、あなたの状況ではうまくいかないかもしれません。注意して使用してください(何が起こっているのか知りたい場合は、リンクをたどってください)。
これgit filter-branch
が設計されたものです。
以下も使用できます。
git reset HEAD file/path