web-dev-qa-db-ja.com

複数のgitコミットを元に戻す方法

私はこのようなGitリポジトリを持っています:

A -> B -> C -> D -> HEAD

分岐の先頭がAを指すようにします。つまり、B、C、D、およびHEADを消し、headをAと同義にします。

私はリベースを試みることができるように聞こえます(間に変更をプッシュしたので適用されません)、または元に戻すことができます。しかし、どうすれば複数のコミットを元に戻すことができますか? 1つずつ元に戻しますか?順番は重要ですか?

754
Bill

コメントに書いた内容を拡張する

一般的なルールは、公開した履歴を書き換える(変更する)べきではないということです。誰かがその履歴に基づいている可能性があるためです。履歴を書き換える(変更する)と、変更のマージと更新が問題になります。

そのため、解決策はnew commitを作成し、は変更を元に戻す削除することです。これは git revert コマンドを使用して実行できます。

次の状況があります。

 A <-B <-C <-D <-マスター<-HEAD 

(ここの矢印は、ポインターの方向を示します。コミットの場合は「親」参照、ブランチヘッドの場合はトップコミット(ブランチ参照)、HEADの場合はブランチの名前参照)。

作成する必要があるものは次のとおりです。

 A <-B <-C <-D <-[(BCD)^-1] <-マスター<-HEAD 

ここで、「[(BCD)^-1]」は、コミットB、C、Dの変更を元に戻すコミットを意味します。数学では、(BCD)^-1 = D ^ -1 C ^ -1 B ^ -1次のコマンドを使用して、必要な状況を取得できます。

$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m "the commit message"

別の解決策は、コミットAの checkoutcontentsにして、この状態をコミットすることです。

$ git checkout -f A -- .
$ git commit -a

その後、次のような状況になります。

 A <-B <-C <-D <-A '<-マスター<-HEAD 

コミットA 'はコミットAと同じ内容ですが、異なるコミット(コミットメッセージ、親、コミット日)です。

Jeff Ferlandによるソリューション、Charles Baileyによる修正 は同じアイデアに基づいていますが、 git reset を使用しています:

$ git reset --hard A
$ git reset --soft @{1}  # (or ORIG_HEAD), which is D
$ git commit
1163
Jakub Narębski

そのためには、取り消したいコミットの範囲を指定して、 revert コマンドを使用するだけです。

あなたの例を考慮に入れて、あなたはこれをしなければならないでしょう(あなたがあなたがブランチ「マスター」にいると仮定すると):

git revert master~3..master

これはあなたのローカルにB、C、Dの逆コミットで新しいコミットを作成します(これらのコミットによってもたらされた変更を元に戻すことを意味します):

A <- B <- C <- D <- BCD' <- HEAD
187
Victor

私が役に立ったきれいな道

git revert --no-commit HEAD~3..

このコマンドは、最後の3回のコミットを1回のコミットだけで元に戻します。

履歴も書き換えません。

129
deepdive

Jakubの答えと同じように、これを使用すると、元に戻すための連続したコミットを簡単に選択できます。

# revert all commits from B to HEAD, inclusively
$ git revert --no-commit B..HEAD  
$ git commit -m 'message'
56
konyak
git reset --hard a
git reset --mixed d
git commit

それは彼ら全員にとって一度に復帰として機能するでしょう。良いコミットメッセージを出してください。

48
Jeff Ferland

まず作業コピーが変更されていないことを確認してください。その後:

git diff HEAD commit_sha_you_want_to_revert_to | git apply

そしてコミットするだけです。元に戻す理由を文書化することを忘れないでください。

32
mateusz.fiolka

私はこの質問に答えることができないことをとてもイライラしています。他のすべての質問は、正しく元に戻して履歴を保存する方法に関するものです。この質問は "私は枝の頭をAに向けたい、すなわちB、C、D、そしてHEADを消えるにし、頭をAと同義。」

git checkout <branch_name>
git reset --hard <commit Hash for A>
git Push -f

私はJakubの投稿をよく読んでいましたが、会社の何人かの人(Pull-RequestなしでpushへのPushへのアクセス権を持つ)は、5コミット前に行った間違いを修正して修正しようとしました。それだけでなく、1つか2つのプルリクエストが受け付けられましたが、今はそれは良くありませんでした。それを忘れて、私は最後の良いコミット(abc1234)を見つけて、そしてただ基本的なスクリプトを走らせました:

git checkout testing
git reset --hard abc1234
git Push -f

私はこのリポジトリで働いている他の5人に、彼らが最近の数時間と彼らの最新のテストからのWipe/Re-Branchの間の彼らの変化をもっとよくメモすると言った。物語の終わり。

24
Suamere

これはJakubの答えで提供された解決策の1つの拡張です

ロールバックする必要があるコミットがいくらか複雑で、コミットのいくつかがマージコミットであり、履歴の書き換えを避ける必要があるという状況に直面しました。一連のgit revertコマンドを使用することはできませんでした。追加された復帰の変更の間に結局競合が発生したためです。私は次のステップを使用しました。

まず、ブランチの先端にHEADを残したまま、ターゲットコミットの内容をチェックします。

$ git checkout -f <target-commit> -- .

( - は<target-commit>がファイルではなくコミットとして解釈されるようにします。。は現在のディレクトリを参照します。)

次に、ロールバックされているコミットに追加されたファイルを特定し、削除する必要があります。

$ git diff --name-status --cached <target-commit>

追加されたファイルは、行の先頭に "A"が表示され、それ以外に違いはないはずです。今、ファイルを削除する必要がある場合は、削除のためにこれらのファイルをステージングします。

$ git rm <filespec>[ <filespec> ...]

最後に、復帰をコミットします。

$ git commit -m 'revert to <target-commit>'

必要に応じて、目的の状態に戻っていることを確認してください。

$git diff <target-commit> <current-commit>

違いはないはずです。

7
Warren Dew

共有リポジトリのコミットのグループを元に戻す簡単な方法(人々が使っていて履歴を保存したい)は、git git revertと一緒にrev-listを使うことです。後者はコミットのリストをあなたに提供し、前者はそれ自身を元に戻します。

それには2つの方法があります。 1回のコミットで複数のコミットを元に戻すには、次のようにします。

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert -n $i; done

これはあなたが必要とするコミットのグループを元に戻すでしょう、しかしあなたの作業ツリーにすべての変更を残して、あなたはいつものようにそれらすべてをコミットするべきです。

もう1つの選択肢は、元に戻す変更ごとに1つのコミットを行うことです。

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert --no-edit -s $i; done

たとえば、次のようなコミットツリーがあるとします。

 o---o---o---o---o---o--->    
fff eee ddd ccc bbb aaa

変更を eee から bbb に戻すには、次のコマンドを実行します。

for i in `git rev-list eee^..bbb`; do git revert --no-edit -s $i; done
3
Ruslan Kabalin

私の意見では、非常に簡単でクリーンな方法は次のとおりです。

aに戻る

git checkout -f A

マスターの頭を現在の状態に向ける

git symbolic-ref HEAD refs/heads/master

保存する

git commit
0
nulll