web-dev-qa-db-ja.com

特定のコミットを削除

私は友人とプロジェクトで働いていました、そして、彼は編集されるべきではなかったファイルの束を編集しました。どういうわけか、私はそれを引っ張ったとき、または私が欲しい特定のファイルだけを選び出そうとしたときに、彼の仕事を私のものにマージしました。私は長い間探していて、それらのファイルへの編集を含むコミットを削除する方法を見つけようとしていました。 docsは私が私がする以上に知っていると仮定します。

それで、ここに質問の簡略版があります:

次のシナリオで、コミット2を削除するにはどうすればよいですか?

$ mkdir git_revert_test && cd git_revert_test

$ git init
Initialized empty Git repository in /Users/josh/deleteme/git_revert_test/.git/

$ echo "line 1" > myfile

$ git add -A

$ git commit -m "commit 1"
[master (root-commit) 8230fa3] commit 1
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 myfile

$ echo "line 2" >> myfile

$ git commit -am "commit 2"
[master 342f9bb] commit 2
 1 files changed, 1 insertions(+), 0 deletions(-)

$ echo "line 3" >> myfile

$ git commit -am "commit 3"
[master 1bcb872] commit 3
 1 files changed, 1 insertions(+), 0 deletions(-)

期待される結果は

$ cat myfile
line 1
line 3

これは私が元に戻そうとしている方法の例です。

$ git revert 342f9bb
Automatic revert failed.  After resolving the conflicts,
mark the corrected paths with 'git add <paths>' or 'git rm <paths>'
and commit the result.
234
Joshua Cheek

元に戻す差分を計算するときにGitが使用するアルゴリズムには、次のものが必要です。

  1. 元に戻される行はその後のコミットによって変更されません。
  2. 歴史の後半で他の「隣接」コミットがないこと。

「隣接」の定義は、コンテキストdiffからのデフォルトの行数3に基づいています。したがって、 'myfile'が次のように構成されているとします。

$ cat >myfile <<EOF
line 1
junk
junk
junk
junk
line 2
junk
junk
junk
junk
line 3
EOF
$ git add myfile
$ git commit -m "initial check-in"
 1 files changed, 11 insertions(+), 0 deletions(-)
 create mode 100644 myfile

$ Perl -p -i -e 's/line 2/this is the second line/;' myfile
$ git commit -am "changed line 2 to second line"
[master d6cbb19] changed line 2
 1 files changed, 1 insertions(+), 1 deletions(-)

$ Perl -p -i -e 's/line 3/this is the third line/;' myfile
$ git commit -am "changed line 3 to third line"
[master dd054fe] changed line 3
 1 files changed, 1 insertions(+), 1 deletions(-)

$ git revert d6cbb19
Finished one revert.
[master 2db5c47] Revert "changed line 2"
 1 files changed, 1 insertions(+), 1 deletions(-)

それからそれはすべて期待通りに動作します。

2番目の答えは非常におもしろかったです。 (Git v1.7.2-rc2では利用可能ですが)Revert Strategyと呼ばれる機能はまだ正式にはリリースされていません。このようにgitを起動することができます。

git revert - 戦略解決 <commit>

そしてそれはあなたが何を意味するのかを理解するためにより良い仕事をするべきです。私は利用可能な戦略のリストが何であるか、また戦略の定義も知らない。

56
Hal Eisen

その方法は4つあります。

  • 元の状態に戻しますが、元の状態に戻します。

    git revert --strategy resolve <commit>
    
  • 面倒なことに、最後のコミットだけを完全に削除します。

    git reset --soft "HEAD^"
    

注:最後のコミット以降のファイル内のすべての変更も破棄されるため、git reset --hardは避けてください。 --softが機能しない場合は、--mixedまたは--keepを試してください。

  • Rebase(最後の5つのコミットのログを表示し、不要な行を削除するか、複数のコミットを1つにまとめたり、他のことをしたりする、これは非常に用途の広いツールです)。

    git rebase -i HEAD~5
    

そして間違いがあった場合:

git rebase --abort
  • クイックリベース:そのIDを使用して特定のコミットのみを削除します。

    git rebase --onto commit-id^ commit-id
    
  • 代替案:あなたも試してみることができます:

    git cherry-pick commit-id
    
  • さらに別の方法:

    git revert --no-commit
    
  • 最後の手段として、履歴編集の完全な自由が必要な場合(例えば、gitがあなたが望むものを編集することを許可していないので)、これを使用することができます 非常に速い オープンソースアプリケーション: - reposurgeon

注:もちろん、これらの変更はすべてローカルで行われます。後で変更をリモートに適用するには、git Pushを実行する必要があります。そして、あなたのリポジトリがコミットを削除したくない場合( "早送りはできません"。すでにプッシュしたコミットを削除したい場合)、git Push -fを使って強制的に変更をプッシュすることができます。

注意2:ブランチで作業しているときにPushを強制する必要がある場合は、他のブランチを上書きする可能性があるため、git Push --forceを絶対に避けてください(現在のチェックアウトが別のブランチであっても)。優先してくださいプッシュするときは必ずリモートブランチを指定してくださいgit Push --force Origin your_branch

232
gaborous

とても簡単な方法

git rebase -i HEAD〜x

(x =コミット数)

メモ帳ファイルを実行すると、コミット以外に 'put'ドロップが開きます enter image description here

これで作業は完了です... gitダッシュボードを同期するだけで、変更がリモートにプッシュされます。ドロップしたコミットがすでにリモートにある場合は、アップデートを強制する必要があります。 --forceは 有害 と見なされるため、git Push --force-with-leaseを使用してください。

75
JD-V

あなたの選択は

  1. エラーを修正して修正を加える
  2. エラーを取り除き、履歴を変更する。

誤った変更が他の誰かによって拾われた場合は(1)を選択し、(2)エラーがプライベートなプッシュされていないブランチに限定されている場合は選択する必要があります。

Git revertは自動化されたツールで(1)、以前のコミットを元に戻す新しいコミットを作成します。プロジェクトの履歴にエラーと削除が表示されますが、リポジトリから取得した人が更新しても問題に遭遇することはありません。あなたの例では自動化された方法では動いていないので、(2行目を削除するために) 'myfile'を編集し、衝突に対処するためにgit add myfilegit commitを実行する必要があります。その後、あなたは自分の履歴の中で4つのコミットを行い、コミット4はコミット2を元に戻します。

あなたの歴史が変わっても気にしないのであれば、それを書き換えてコミット2(選択肢2)を削除することができます。これを行う簡単な方法はgit rebase -i 8230fa3を使うことです。これはあなたをエディタに落とすでしょう、そしてあなたはコミットを削除することによって(そして他のコミットメッセージの隣に "pick"を維持することによって)誤ったコミットを含めないことを選択できます。 これをする

36
Andrew Walker

アプローチ1

最初に元に戻す必要があるコミットハッシュ(例:1406cd61)を取得します。簡単な修正は、コマンドの下になります、

$ git revert 1406cd61

あなたが1406cd61のコミット後に1406cd61ファイルに関連するより多くの変更をコミットした場合、上記の単純なコマンドは機能しません。それからあなたはチェリーピッキングである以下のステップをしなければなりません。

アプローチ2

以下のアクションの流れに従ってください。私たちは--forceを使用しているため、これを行うにはgitリポジトリに管理者権限が必要です。

Step 1:削除したいコミットの前のコミットを探しますgit log

ステップ2:コミットするチェックアウトgit checkout <commit hash>

Step 3:現在のチェックアウトコミットを使って新しいブランチを作るgit checkout -b <new branch>

Step 4:削除したコミットの後にコミットを追加する必要がありますgit cherry-pick <commit hash>

Step 5:他にもコミットしたいコミットがある場合は、Step 4を繰り返してください。

Step 6:全てのコミットがあなたの新しいブランチに追加され、コミットされたら。すべてが正しい状態にあり、意図したとおりに機能していることを確認してください。すべてがコミットされたことを再確認します。git status

ステップ7:あなたの壊れたブランチに切り替えますgit checkout <broken branch>

Step 8:git reset --hard <commit hash>を削除したいコミットの前に、壊れたブランチのコミットへのハードリセットを行います。

Step 9:あなたの固定ブランチをこのブランチにマージしますgit merge <branch name>

Step 10:マージした変更をOriginにプッシュします。警告:これはリモートレポジトリを上書きします。 git Push --force Origin <branch name>

ステップ2と3をステップ8に置き換えてステップ7と9を実行しないことで、新しいブランチを作成せずにプロセスを実行できます。

20
tk_

git rebaseを使って不要なコミットを削除できます。同僚のトピックブランチから自分のトピックブランチへのコミットをいくつか含めたとしますが、後でそれらのコミットを望まないと決めます。

git checkout -b tmp-branch my-topic-branch  # Use a temporary branch to be safe.
git rebase -i master  # Interactively rebase against master branch.

この時点で、テキストエディタはインタラクティブリベースビューを開きます。例えば

git-rebase-todo

  1. 行を削除して、不要なコミットを削除します。
  2. 保存して終了

リベースが成功しなかった場合は、一時的なブランチを削除して別の方法を試してください。それ以外の場合は、次の手順に進みます。

git checkout my-topic-branch
git reset --hard tmp-branch  # Overwrite your topic branch with the temp branch.
git branch -d tmp-branch  # Delete the temporary branch.

トピックブランチをリモートにプッシュしている場合は、コミット履歴が変更されたため、プッシュを強制する必要があるかもしれません。他の人が同じブランチに取り組んでいる場合は、彼らに頭を上げてください。

17
Dennis

ここの他の回答から、git rebase -iを使用してコミットを削除する方法と混同されたので、ここでテストケースを書き留めてよいことを願っています(OPと非常によく似ています)。

bashスクリプトは、/tmpフォルダーにテストリポジトリを作成するために貼り付けることができます。

set -x

rm -rf /tmp/myrepo*
cd /tmp

mkdir myrepo_git
cd myrepo_git
git init
git config user.name me
git config user.email [email protected]

mkdir folder
echo aaaa >> folder/file.txt
git add folder/file.txt
git commit -m "1st git commit"

echo bbbb >> folder/file.txt
git add folder/file.txt
git commit -m "2nd git commit"

echo cccc >> folder/file.txt
git add folder/file.txt
git commit -m "3rd git commit"

echo dddd >> folder/file.txt
git add folder/file.txt
git commit -m "4th git commit"

echo eeee >> folder/file.txt
git add folder/file.txt
git commit -m "5th git commit"

この時点で、次の内容のfile.txtがあります。

aaaa
bbbb
cccc
dddd
eeee

この時点で、HEADは5回目のコミット、HEAD〜1は4回目、HEAD〜4は1回目のコミットになります(したがって、HEAD〜5は存在しません)。 3番目のコミットを削除したいとしましょう-myrepo_gitディレクトリでこのコマンドを発行できます:

git rebase -i HEAD~4

git rebase -i HEAD~5は「致命的:単一のリビジョンが必要です;上流のHEAD〜5が無効です」という結果になります。)テキストエディタ(スクリーンショットを参照) in @ Dennis 'answer )これらのコンテンツで開きます:

pick 5978582 2nd git commit
pick 448c212 3rd git commit
pick b50213c 4th git commit
pick a9c8fa1 5th git commit

# Rebase b916e7f..a9c8fa1 onto b916e7f
# ...

したがって、すべてのコミットを取得しますsince(ただしnot include)要求されたHEAD 〜4。 pick 448c212 3rd git commit行を削除して、ファイルを保存します。 git rebaseからこの応答を受け取ります:

error: could not apply b50213c... 4th git commit

When you have resolved this problem run "git rebase --continue".
If you would prefer to skip this patch, instead run "git rebase --skip".
To check out the original branch and stop rebasing run "git rebase --abort".
Could not apply b50213c... 4th git commit

この時点で、myrepo_git/folder/file.txtをテキストエディターで開きます。変更されていることがわかります。

aaaa
bbbb
<<<<<<< HEAD
=======
cccc
dddd
>>>>>>> b50213c... 4th git commit

基本的に、gitは、HEADが2番目のコミットを取得すると、aaaa + bbbb;のコンテンツがあったことを確認します。そして、追加されたcccc + ddddのパッチがあり、既存のコンテンツに追加する方法がわかりません。

したがって、ここでgitはあなたのために決定することはできません-それはyouです。決定を下す必要があります。3番目のコミットを削除することで、それによって導入された変更(ここではcccc行)-そうでない場合。削除しない場合は、テキストエディターを使用してfolder/file.txtccccを含む余分な行を削除するだけで、次のようになります。

aaaa
bbbb
dddd

...そしてfolder/file.txtを保存します。これで、myrepo_gitディレクトリで次のコマンドを発行できます。

$ nano folder/file.txt  # text editor - edit, save
$ git rebase --continue
folder/file.txt: needs merge
You must edit all merge conflicts and then
mark them as resolved using git add

ああ-したがって、競合を解決したことを示すために、mustgit add the folder/file.txt、を実行する前にgit rebase --continue

$ git add folder/file.txt
$ git rebase --continue

ここでテキストエディターが再び開き、4th git commit行が表示されます。ここで、コミットメッセージを変更する機会があります(この場合、4th (and removed 3rd) commitなどに意味のあるものに変更できます)。したくないとしましょう-保存せずにテキストエディタを終了してください。それを行うと、次のものが得られます。

$ git rebase --continue
[detached HEAD b8275fc] 4th git commit
 1 file changed, 1 insertion(+)
Successfully rebased and updated refs/heads/master.

この時点で、gitk .の内容のfolder/file.txtまたは他のツールを使用して検査することもできます(明らかに、元のコミットのタイムスタンプは変更されていません):

1st git commit  |  +aaaa
----------------------------------------------
2nd git commit  |   aaaa
                |  +bbbb
----------------------------------------------
4th git commit  |   aaaa
                |   bbbb
                |  +dddd
----------------------------------------------
5th git commit  |   aaaa
                |   bbbb
                |   dddd
                |  +eeee

また、以前にcccc行(削除した3番目のgitコミットの内容)を保持することにした場合、次のようになります。

1st git commit  |  +aaaa
----------------------------------------------
2nd git commit  |   aaaa
                |  +bbbb
----------------------------------------------
4th git commit  |   aaaa
                |   bbbb
                |  +cccc
                |  +dddd
----------------------------------------------
5th git commit  |   aaaa
                |   bbbb
                |   cccc
                |   dddd
                |  +eeee

さて、これは、コミット/リビジョンの削除に関してgit rebaseがどのように機能するかを理解するために、私が見つけたいと思っていた種類の読み物でした。他の人にも役立つことを願っています...

7
sdaau

ですから、ある時点で悪いコミットがマージコミットに組み込まれたようです。マージコミットはまだ実行されましたか?もしそうなら、git revertを使いたいでしょう。あなたはあなたの歯を削り、そして衝突を乗り越えなければならないでしょう。いいえの場合は、おそらくリベースするか元に戻すことができますが、そうすることができますbeforeマージコミットをしてからマージをやり直します。

私たちが最初のケースのためにあなたに与えることができる多くの助けが本当にありません。復帰を試み、そして自動復帰が失敗したことを発見した後、あなたは衝突を調べそしてそれらを適切に修正しなければなりません。これはマージ競合を修正するのとまったく同じプロセスです。 git statusを使用して競合がどこにあるかを確認し、マージされていないファイルを編集し、競合しているハンクを見つけ、それらを解決する方法を見つけ、競合しているファイルを追加し、最後にコミットします。 git commitを単独で使用する場合(no -m <message>)、エディタに表示されるメッセージは、git revertによって作成されたテンプレートメッセージになります。どのように競合を修正したかについてのメモを追加してから、保存してコミットを終了できます。

2つ目のケースは、問題を解決するためのbeforeあなたのマージです。マージ以降に作業が増えたかどうかによって、2つのサブケースがあります。まだ行っていない場合は、単にgit reset --hard HEAD^を使用してマージを中止し、元に戻してからマージをやり直すことができます。しかし、私はあなたが持っていると思います。それで、あなたは結局このような何かをすることになるでしょう:

  • マージの直前に一時的なブランチを作成し、チェックアウトする
  • 元に戻す(または不正なコミットを削除するにはgit rebase -i <something before the bad commit> <temporary branch>を使用)
  • マージをやり直す
  • その後の作業をリベースします。git rebase --onto <temporary branch> <old merge commit> <real branch>
  • 一時的なブランチを削除する
2
Cascabel

それで、あなたはいくつかの仕事をし、それをプッシュし、それらをAとBと呼びましょう。あなたの同僚もいくつかの仕事をし、CとDをコミットします。また、あなたの同僚は彼が持つべきではないことをいくつか変更したことを発見しました(Fをコミット)。

だからあなたのコミット履歴はこのようになります:

A -- B -- C -- D -- D' -- E -- F

あなたは本当にC、D、そしてD 'を取り除きたいのです。同僚の作業を自分の作業にマージしたと言っているので、これらのコミットはすでに「そこに」あります。 git rebaseはありません。私を信じて、試してみました。

今、私は2つの方法を考えています。

  • eとFを同僚や他の人(通常は "Origin"サーバー)にプッシュしていない場合でも、しばらくの間はそれらを履歴から削除することができます。これはあなたが保存したい作品です。これは

    git reset D'
    

    (D 'をgit logから取得できる実際のコミットハッシュで置き換えます。

    この時点で、コミットEとFは消えており、変更はローカルワークスペース内のコミットされていない変更です。この時点でそれらをブランチに移動するか、パッチに変換して後で保存します。さて、同僚の仕事をgit revertで自動的に、または手動で元に戻します。それが終わったら、その上で作品を再生します。あなたはマージ競合を持っているかもしれませんが、少なくともそれらはあなたの同僚のコードの代わりにあなたが書いたコードの中にあるでしょう。

  • 同僚のコミットの後に行った作業をすでにプッシュしている場合でも、手動またはgit revertを使用して「リバースパッチ」を試すことができますが、作業は「邪魔になる」ので、おそらく次のようになります。より多くのマージコンフリクトとより紛らわしいものを得ます。それがあなたが終わったものであるように見えます...

1
Frans