422

数コミット前に、 (マージの解決中に) 不要なファイルを誤っfilename.origて自分のリポジトリにコミットしましたが、今まで気付かなかったのです。リポジトリ履歴からファイルを完全に削除したい。

filename.origそもそもリポジトリに追加されていないような変更履歴を書き換えることは可能ですか?

4

12 に答える 12

306

あなたの状況が質問に記載されているものでない場合は、このレシピを使用しないでください. このレシピは、不適切なマージを修正し、適切なコミットを修正済みのマージにリプレイするためのものです。

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名前。)

于 2008-11-21T13:02:21.957 に答える
222

はじめに: 5 つのソリューションが利用可能です

元のポスターには次のように記載されています。

不要なファイルを誤ってリポジトリにコミットしました...数コミット前に...リポジトリ履歴からファイルを完全に削除したいです。

filename.origそもそもリポジトリに追加されていないような変更履歴を書き換えることは可能ですか?

git からファイルの履歴を完全に削除するには、さまざまな方法があります。

  1. コミットの修正。
  2. ハード リセット (場合によってはさらにリベース)。
  3. 非インタラクティブなリベース。
  4. インタラクティブなリベース。
  5. ブランチのフィルタリング。

元の投稿者の場合、コミットを修正すること自体は実際にはオプションではありません。彼は後でいくつかの追加のコミットを行ったからです。以前のコミットを修正します。

これらの解決策はすべて、履歴/コミットを別の方法で変更/再書き込みする必要があることに注意してください。そのため、コミットの古いコピーを持っている人は、履歴を新しい履歴と再同期するために余分な作業を行う必要があります。


解決策 1: コミットを修正する

以前のコミットで誤って変更 (ファイルの追加など) を行い、その変更の履歴をもう存在させたくない場合は、単純に以前のコミットを修正してファイルを削除することができます。

git rm <file>
git commit --amend --no-edit

解決策 2: ハード リセット (場合によってはさらにリベース)

解決策 #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}

解決策 3: 非対話的なリベース

これは、履歴からコミットを完全に削除したい場合に機能します。

# 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}

解決策 4: インタラクティブなリベース

この解決策は、解決策 #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}

解決策 5: ブランチのフィルタリング

最後に、このソリューションは、履歴からファイルの存在のすべての痕跡を完全に消去したい場合に最適であり、他のソリューションはどれもそのタスクに完全に対応していません.

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

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

その他のリソース

  1. Pro Git § 6.4 Git ツール - 履歴の書き換え.
  2. git-filter-branch(1) マニュアルページ.
  3. git-commit(1) マニュアルページ
  4. git-reset(1) マニュアルページ.
  5. git-rebase(1) マニュアルページ.
  6. BFG Repo Cleaner (作成者自身からのこの回答も参照してください)。
于 2014-04-20T23:10:53.043 に答える
121

それ以降何もコミットしていない場合は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ユーザーへの注意:ファイルパスはスラッシュを使用する必要があります

于 2009-03-14T20:44:57.617 に答える
50

これが最善の方法です:
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(念のため、今から数週間または数か月後に...)

于 2010-02-04T05:52:43.073 に答える
27

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 の作成者です。

于 2013-03-31T12:35:49.690 に答える
16
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
于 2016-06-10T06:35:50.640 に答える
4

それを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
于 2013-10-16T13:10:21.617 に答える
4

私が見つけた最も簡単な方法は、 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それを指摘した に送られます。

ノート

スクリプトには検証が含まれていないことに注意してください。そのため、間違いを犯さないようにし、何か問題が発生した場合に備えてバックアップを取っておくようにしてください。私にはうまくいきましたが、あなたの状況ではうまくいかないかもしれません。注意して使用してください(何が起こっているのか知りたい場合は、リンクをたどってください)。

于 2016-05-17T02:26:14.967 に答える
0

これgit filter-branchが設計されたものです。

于 2008-11-21T10:26:19.447 に答える
-1

以下も使用できます。

git reset HEAD file/path

于 2009-09-03T04:00:45.910 に答える