悪いマージをどのように修正し、良いマージを修正されたマージにリプレイしますか?

StackOverflow https://stackoverflow.com/questions/307828

質問

私はこれまで気づかずに、数回前に誤って不要なファイル(マージの解決中にfilename.orig)をリポジトリにコミットしました。リポジトリ履歴からファイルを完全に削除したい。

最初にリポジトリに<=>が追加されないように変更履歴を書き換えることはできますか?

役に立ちましたか?

解決

あなたの状況が質問で説明されている状況でない場合は、このレシピを使用しないでください。このレシピは、不適切なマージを修正し、修正されたマージに適切なコミットを再生するためのものです。

filter-branchは必要な処理を実行しますが、非常に複雑なコマンドであり、おそらくgit rebaseを使用して実行することを選択するでしょう。おそらく個人的な好みです。 rebaseは単一のやや複雑なコマンドで実行できますが、git commit --amendソリューションでは同等の論理演算を一度に1ステップずつ実行します。

次のレシピを試してください:

(実際には一時的なブランチは必要ないことに注意してください、「デタッチされたHEAD」でこれを行うことができますが、<=>ステップによって生成されたコミットIDをメモして、< =>一時的なブランチ名を使用するのではなくコマンド。)

他のヒント

イントロ:5つのソリューションが利用可能です

元のポスターの状態:

  

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

     

それは    filename.origが決してなかったような変更履歴を書き換えることが可能    そもそもリポジトリに追加しましたか?

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

  1. コミットの修正。
  2. ハードリセット(おそらくプラスリベース)。
  3. 非インタラクティブなリベース。
  4. インタラクティブなリベース。
  5. ブランチのフィルタリング。

元のポスターの場合、コミットを修正することは実際にはオプションではありません それ自体で、彼はその後いくつかの追加のコミットを行ったが、 完全性について、私はそれを行う方法も説明します。 以前のコミットを修正したい。

これらのソリューションにはすべて、変更/書き換え履歴/コミットが含まれます ある意味で別の方法で、コミットの古いコピーを持っている人は誰でもする必要があります 履歴を新しい履歴と再同期するための追加の作業。


解決策1:コミットの修正

以前に誤って変更(ファイルの追加など)を行った場合 コミットし、その変更の履歴がもう存在しないようにします。 前のコミットを修正してファイルを削除するだけです:

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

ソリューション2:ハードリセット(場合によってはリベースを追加)

ソリューション#1のように、以前のコミットを削除したいだけなら、 また、単に親にハードリセットを行うオプションもあります。

git reset --hard HEAD^

そのコマンドは、ブランチを前の1番目の st にハードリセットします コミット。

、元のポスターのように、その後いくつかのコミットを行った場合 変更を取り消すコミット、まだハードリセットを使用できます 変更しますが、変更にはリベースの使用も含まれます。手順は次のとおりです 履歴をさかのぼってコミットを修正するために使用できます:


ソリューション3:非対話型リベース

これは、履歴からコミットを完全に削除する場合にのみ機能します:


ソリューション4:インタラクティブなリベース

このソリューションにより、ソリューション#2および #3、つまり、コミットをすぐに変更するよりも履歴内で変更または削除する 前回のコミットですので、どのソリューションを使用するかはあなた次第です。 インタラクティブなリベースは、何百ものコミットをリベースするのには適していません。 パフォーマンス上の理由から、非インタラクティブなリベースまたはフィルターブランチを使用します そのような状況でのソリューション(以下を参照)。

インタラクティブなリベースを開始するには、次を使用します:

これにより、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)

変更または削除するコミットは、このリストの一番上にあります。 削除するには、リスト内のその行を削除するだけです。それ以外の場合は、<!> quot; pick <!> quot;を置き換えます。と <!> quot; edit <!> quot;次のように、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

それだけです。最後のステップとして、コミットを変更したか削除したか 完全に、他の予期しない変更がないことを確認することは常に良い考えです dによってあなたのブランチに作られましたリベースする前の状態でそれを取得します:

git diff master@{1}

ソリューション5:ブランチのフィルタリング

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

これは、ルートコミットから開始して、すべてのコミットから<file>を削除します。もし 代わりに、コミット範囲HEAD~5..HEADを書き直したいだけです。 で指摘されているように、追加の引数としてfilter-branchに渡します この回答

繰り返しますが、git filter-branchが完了したら、通常は確認することをお勧めします ブランチをその差分と比較することによって、他の予期しない変更がないこと フィルタリング操作前の前の状態:

<*>

フィルターブランチの代替:BFG Repo Cleaner

BFGリポジトリクリーナーツールは--strip-blobs-bigger-than 1Mよりも高速に実行されると聞いたので、オプションとしても。 実行可能な代替手段として filter-branchドキュメントでも公式に言及されています:

  

git-filter-branchを使用すると、複雑なシェルスクリプトによる書き換えを行うことができます   Gitの履歴を表示しますが、おそらく<!>#8217;   あなたは<!>#8217;単に大きなファイルやパスワードのような不要なデータを削除しています。   これらの操作については、 BFGを検討してください。   JVMベースのRepo-Cleaner   git-filter-branchの代替。通常は少なくとも10〜50倍高速   これらのユースケース、およびまったく異なる特性を持つ:

     
      
  • ファイルの特定のバージョンは、一度だけ正確に消去されます。 BFGは、git-filter-branchとは異なり、処理する機会を与えません   ファイルは、どこでいつコミットされたかに基づいて異なります   歴史。この制約により、以下のコアパフォーマンスの利点が得られます。   BFG、および不良データをクレンジングするタスクに適しています-あなたはドン<!>#8217; t   悪いデータがどこにあるかを気にしなさい、あなたはただそれをなくなった

  •   
  • デフォルトでは、BFGはマルチコアマシンを最大限に活用し、コミットファイルツリーを並行してクレンジングします。 git-filter-branch cleans    ですが、順次コミット(つまり、シングルスレッド方式で)   独自の並列性を含むフィルターを書くことが可能   各コミットに対して実行されるスクリプト。

  •   
  • コマンドオプション   git-filterブランチよりも制限が厳しく、   不要なデータを削除するタスク-例:<=>。

  •   

その他のリソース

  1. Pro Git <!>#167; 6.4 Gitツール-履歴の書き換え
  2. git-filter-branch(1)マニュアルページ
  3. git-commit(1)マニュアルページ
  4. git-reset(1)マニュアルページ
  5. git-rebase(1)マニュアルページ
  6. BFGリポジトリクリーナー作成者自身からのこの回答)。

それ以降何もコミットしていない場合は、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 による編集は、レビュー中に残念ながら拒否されました。
以下のネオンの投稿を参照してください。有用な情報が含まれている可能性があります!


E.g。誤って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 

(yes!)

次に、それを新しいディレクトリに複製し、その.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ディレクトリを削除できます(念のため...数週間または1か月後に...)

Gitの履歴を書き換えるには、影響を受けるすべてのコミットIDを変更する必要があるため、プロジェクトで作業している全員がリポジトリの古いコピーを削除し、履歴を消去した後に新しいクローンを作成する必要があります。不便な人が多いほど、それを行う正当な理由が必要になります-余分なファイルは実際には問題を引き起こしていませんが、プロジェクトで作業しているのがあなただけなら、必要に応じてGitの履歴を更新してください!

できるだけ簡単にするために、 BFG Repo-Cleaner は、Git履歴からファイルを削除するために特別に設計されたgit-filter-branchのよりシンプルで高速な代替手段です。ここでの作業を簡単にする1つの方法は、デフォルトで all の参照(すべてのタグ、ブランチなど)を実際に処理することですが、 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という名前のファイル( 最新コミット)は削除されます。これは、<=>を使用して同じことを行うよりもかなり簡単です!

完全開示:私は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、およびそれを指摘するために<=>に割り当てられます。

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

間違いなく、git filter-branchが道です。

残念ながら、タグ、reflogエントリ、リモートなどで参照できるため、filename.origをリポジトリから完全に削除するだけでは不十分です。

これらの参照もすべて削除してから、ガベージコレクターを呼び出すことをお勧めします。 git forget-blobスクリプトを使用できますblob / "rel =" nofollow noreferrer ">このウェブサイトでこれらすべてを1ステップで実行できます。

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 の設計対象です。

>

次も使用できます。

git reset HEAD file/path

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top