私はこれまで気付かずに、コミット前に誤って不要なファイル(マージの解決中にfilename.orig
)をリポジトリにコミットしました。リポジトリの履歴からファイルを完全に削除したいです。
そもそもfilename.orig
がリポジトリに追加されなかったように変更履歴を書き換えることは可能ですか?
あなたの状況が質問に記載されているものではない場合は、このレシピを使わないでください。このレシピは悪いマージを修正し、あなたの良いコミットを修正されたマージに再生するためのものです。
filter-branch
はあなたが望むことをするでしょうが、それはかなり複雑なコマンドです、そして、私はおそらくgit rebase
でこれをすることを選びます。それはおそらく個人的な好みです。 filter-branch
は単一の、少し複雑なコマンドでそれを行うことができますが、rebase
ソリューションは一度に1ステップずつ同等の論理演算を実行します。
以下のレシピを試してください。
# 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
(実際には一時的なブランチは必要ありません。 'detached HEAD'でこれを行うことができますが、git commit --amend
ステップで生成されるコミットIDをメモしてgit rebase
コマンドに渡す必要があります。一時的なブランチ名
元のポスターの状態:
私は誤って不要なファイルをコミットしました...数回前にリポジトリに...リポジトリの履歴からファイルを完全に削除したいです。
filename.orig
が最初にリポジトリに追加されないように変更履歴を書き換えることは可能ですか?
Gitからファイルの履歴を完全に削除するには、さまざまな方法があります。
オリジナルのポスターの場合、コミットを修正すること自体は実際には選択肢ではありません。彼は後でいくつかの追加のコミットを行ったためですが、完全を期すために、私はそれを行う方法も説明します以前のコミットを修正します。
これらのソリューションはすべて、変更/書き換え履歴/コミットを別の方法で含むことに注意してください。したがって、コミットの古いコピーを持つ人は誰でも履歴を新しい履歴と再同期するには追加の作業が必要です。
前のコミットで誤って変更(ファイルの追加など)を行った場合、その変更の履歴をこれ以上残したくない場合は、前のコミットを修正してファイルを削除できます。
git rm <file>
git commit --amend --no-edit
解決策#1と同様に、以前のコミットを単に削除したい場合は、単に親にハードリセットを行うオプションもあります。
git reset --hard HEAD^
このコマンドは、ブランチを前のバージョンにハードリセットします1st 親コミット。
ただし、、元のポスターのように、必要なコミットの後にいくつかのコミットを行った場合変更を取り消すには、ハードリセットを使用して変更を元に戻すことができますが、リベースを使用する必要があります。以下に、履歴をさかのぼってコミットを修正するために使用できる手順を示します。
# 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で「選択」を「編集」に置き換えますst 次のような行:
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
より速いと聞いたので、オプションとしてチェックアウトすることもできます。 実行可能な代替手段として filter-branch documentation でも公式に言及されています:
git-filter-branchを使用すると、Git履歴のシェルスクリプトによる複雑な書き直しを行うことができますが、単純に不要なデータを削除する場合には、この柔軟性はおそらく必要ありませんファイルまたはパスワード。これらの操作については、 BFG Repo-Cleaner 、git-filter-branchのJVMベースの代替手段であり、通常、これらのユースケースでは少なくとも10〜50倍速く、まったく異なる特徴:
ファイルの特定のバージョンは、onceだけが完全に消去されます。 git-filter-branchとは異なり、BFGは、履歴内のどこで、またはいつコミットされたかに基づいて、ファイルを異なる方法で処理する機会を与えません。この制約は、BFGのコアパフォーマンスの利点を提供し、不良データをクレンジングするタスクに適しています-気にしませんwhere itgone。
デフォルトでは、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のmanページ の例のセクションからの推奨方法です。
Windowsユーザーへの注意:ファイルパスはスラッシュを使用する必要があります
これが最善の方法です。
http://github.com/guides/completely-remove-a-file-from-all-revisions
最初にファイルのコピーを必ずバックアップしてください。
編集
Neon による編集は、残念ながらレビュー中に拒否されました。
下記のNeonsの投稿を参照してください。役に立つ情報が含まれているかもしれません!
例えば。誤って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
私は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
ディレクトリを削除できます(念のため、今から2、3週間以内に)
Gitの履歴を書き換えるには、影響を受けるすべてのコミットIDを変更する必要があるため、プロジェクトに取り組んでいるすべての人がリポジトリの古いコピーを削除し、履歴を消去した後に新しいクローンを作成する必要があります。不便な人が多ければ多いほど、それを行うには正当な理由が必要です。余計なファイルは実際には問題を引き起こしていませんが、youだけが作業している場合あなたがしたい場合は、プロジェクト、あなたは同様にGitの歴史をクリーンアップするかもしれません!
できるだけ簡単にするために、 BFG Repo-Cleaner を使用することをお勧めします。これは、Git履歴からファイルを削除するために特別に設計されたgit-filter-branch
のより簡単で速い代替手段です。ここであなたの生活を楽にする1つの方法は、デフォルトでallrefsを実際に処理することです(すべてのタグ、ブランチなど)が、 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
という名前のファイル( latestcommit にはありません)が削除されます。これは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
私が見つけた最も簡単な方法は(コメントとして)leontalbot
によって提案されたもので、これは 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
に行きます。
NOTE
スクリプトには検証が含まれていないので、間違いをしないようにし、何か問題が発生した場合に備えてバックアップを作成してください。それは私のために働いたが、それはあなたの状況ではうまくいかないかもしれません。注意して使用してください(何が起こっているのか知りたい場合はリンクをたどってください)。
確かに、git filter-branch
がその道です。
残念ながら、これはリポジトリからfilename.orig
を完全に削除するのに十分ではありません。タグ、reflogエントリ、リモート参照などでまだ参照されている可能性があるためです。
これらの参照もすべて削除してから、ガベージコレクタを呼び出すことをお勧めします。あなたは this のウェブサイトからgit forget-blob
スクリプトを使用して、これらすべてをワンステップで行うことができます。
git forget-blob filename.orig
それがあなたが片付けたい最新のコミットであるならば、私は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
これが git filter-branch
のために設計されたものです。