372

私は友人と一緒にプロジェクトに取り組んでいましたが、彼は編集してはならない大量のファイルを編集してしまいました。どういうわけか、私は彼の作品を自分の作品にマージしました。これらのファイルへの編集を含むコミットを削除する方法を見つけようとして、私は長い間探して遊んでいました。リバートとリベースの間でトスされているようで、簡単な例はありません。ドキュメントは、私が自分よりも多くのことを知っていると想定しています。

したがって、ここに質問の簡略版があります:

次のシナリオで、コミット 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.
4

14 に答える 14

367

これには、次の 4 つの方法があります。

  • きれいな方法で、元に戻しますが、元に戻すことをログに記録します:

    git revert --strategy resolve <commit>
    
  • 厳しい方法で、最後のコミットのみを完全に削除します。

    git reset --soft "HEAD^"
    

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

  • リベース (最後の 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: ブランチで作業していて、プッシュを強制する必要がある場合は、git push --force他のブランチを上書きする可能性があるため、絶対に避ける必要があります (現在のチェックアウトが別のブランチにある場合でも、ブランチに変更を加えた場合)。push :を強制するときは、常にリモート ブランチを指定することをお勧めしますgit push --force origin your_branch

于 2012-08-16T17:11:07.613 に答える
193

簡単な解決策は次のとおりです。

git rebase -i HEAD~x

(注:xはコミット数です)

実行すると、メモ帳ファイルが開きます。dropあなたのコミット以外に入力してください。
Vim を知らない場合は、編集したい各単語選択をクリックしてから、"I" キーを押してください (挿入モード)。入力が完了したら、「esc」キーを押して挿入モードを終了します。



ここに画像の説明を入力

これで完了です... git ダッシュボードを同期するだけで、変更がリモートにプッシュされます。

ドロップするコミットがすでにリモートにある場合は、強制的にプッシュする必要があります。--force は 有害と見なされるため、使用してgit push --force-with-leaseください。

于 2016-12-02T07:31:39.070 に答える
82

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

  1. 元に戻される行は、その後のコミットによって変更されることはありません。
  2. 履歴の後半に他の「隣接する」コミットはありません。

「隣接」の定義は、コンテキスト diff からのデフォルトの行数 (3) に基づいています。

$ 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 番目の回答は非常に興味深いものでした。Revert Strategy と呼ばれる (Git v1.7.2-rc2 で利用可能ですが) まだ正式にリリースされていない機能があります。次のように git を呼び出すことができます。

git revert --strategy resolve <コミット>

そして、あなたが何を意味したかを理解するのにより良い仕事をするはずです. 利用可能な戦略のリストが何かも、戦略の定義もわかりません。

于 2010-07-08T19:26:04.780 に答える
40

あなたの選択は

  1. エラーを維持し、修正を導入し、
  2. エラーを削除し、履歴を変更します。

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

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

履歴が変更されることを誰も気にしない場合は、履歴を書き直して、コミット2を削除できます(選択肢2)。これを行う簡単な方法は、を使用することgit rebase -i 8230fa3です。これにより、エディターに移動し、コミットを削除して(そして、他のコミットメッセージの横に「選択」を続けることで、誤ったコミットを含めないように選択できます。これを行った結果をよく読んでください。

于 2010-05-30T11:26:50.253 に答える
20

で不要なコミットを削除できます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.

トピック ブランチをリモートにプッシュする場合、コミット履歴が変更されているため、プッシュを強制する必要がある場合があります。他の人が同じブランチで作業している場合は、注意してください。

于 2014-01-24T17:04:46.193 に答える
9

ここでの他の回答から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 me@myself.com

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

( 「致命的: 単一のリビジョンが必要です; 上流の HEAD~5 が無効です」という結果になることに注意してくださいgit rebase -i HEAD~5。 ) テキスト エディター ( @Dennis の回答のスクリーンショットを参照) が次の内容で開きます。

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

そのため、リクエストした 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

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

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

基本的に、gitHEAD が 2 回目のコミットに到達したときに、aaaa+ bbbb;の内容があったことがわかります。そして、追加されたcccc+のパッチがddddあり、既存のコンテンツに追加する方法がわかりません。

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

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

ああ、競合を解決したことを示すには、実行する前に , を実行する必要 git addがあります。folder/file.txtgit 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 .) の内容の次のような履歴が得られます (これは、say または他のツールで調べることもできます):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 commit の内容)を保持することにした場合、次のようになります。

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これは、コミット/リビジョンの削除に関してどのように機能するかを理解し始めるために、私が見つけたかった種類の読書でした。他の人にも役立つことを願っています...

于 2015-03-15T10:36:15.247 に答える
3

そのため、ある時点で悪いコミットがマージコミットに組み込まれたようです。マージ コミットはまだプルされていますか? はいの場合は、git revert;を使用します。歯を食いしばって対立を乗り切らなければなりません。いいえの場合は、おそらくリベースまたはリバートのいずれかを実行できますが、マージ コミットの前に実行してから、マージをやり直すことができます。

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

2 番目のケースでは、マージに問題を修正します。マージ以降にさらに作業を行ったかどうかに応じて、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>
  • 一時的なブランチを削除します
于 2010-05-30T13:22:06.250 に答える
1

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

したがって、コミット履歴は次のようになります。

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

あなたは本当にC、D、およびD'を取り除きたいと思っています。同僚の作業を自分の作業にマージしたと言うので、これらのコミットはすでに「そこに」あるため、たとえば git rebase を使用してコミットを削除することはできません。私を信じてください、私は試しました。

今、私は2つの方法を見つけます:

  • E と F を同僚や他の誰か (通常は「オリジン」サーバー) にまだプッシュしていない場合でも、当面はそれらを履歴から削除できます。これは保存したい作品です。これは、

    git reset D'
    

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

    この時点で、コミット E と F はなくなり、変更はローカル ワークスペースで再びコミットされていない変更になります。この時点で、それらをブランチに移動するか、パッチに変換して後で使用できるように保存します。ここで、同僚の作業を自動git revertまたは手動で元に戻します。それが終わったら、その上で作業を再生します。マージの競合が発生する可能性がありますが、少なくとも、同僚のコードではなく、自分が記述し​​たコードに含まれます。

  • 同僚のコミット後に行った作業を既にプッシュしている場合でも、手動または を使用して「リバース パッチ」を取得しようとすることができますがgit revert、作業が「途中」であるため、いわば取得する可能性があります。より多くのマージの競合と、より紛らわしいもの。それがあなたの行き着いたもののように見えます...

于 2014-12-09T20:01:43.287 に答える
1

git revert --strategy resolve コミットがマージの場合: git revert --strategy resolve -m 1 を使用

于 2019-10-02T13:17:28.187 に答える