web-dev-qa-db-ja.com

gitで変更を元に戻す(履歴を書き換えない)

スクリプトを変更してコミットしました。次に、他のいくつかの変更を加え、それらをリモートリポジトリなどにプッシュしました。

次に、最初に述べた変更が愚かであり、それを元に戻したいことに気付きました。差分を手動でコピー/貼り付けせずに、そのコミットを「適用解除」できますか?

例:2つのファイルa.pyおよびb.py

Commit 1:
I delete a function in a.py

Commit 2:
I change a few lines in b.py

Commit 3:
I change the docstring in a.py

その関数の削除を取り消して、「コミット1を削除するのではなく」「コミット4」として表示させることはできますか?

50
dbr

はい、これにはgit revertを使用できます。詳細は これに関するgitのマニュアルセクション を参照してください。

要点は次のように言うことができます:

git revert 4f4k2a

ここで、4f4k2aは元に戻したいコミットのIDであり、それはそれを元に戻そうとします。

61
Jesse Rusak

ただのコメント:

git revert aCommit

allコミットを元に戻します(「commitのすべてのファイル部分」のように):
それは逆パッチを計算し、それをHEADに適用してコミットします。

ここに2つの問題があります(最初の問題は簡単に解決されます)。

  • 常にコミットするので、-no-commitオプション: "git revert --no-commit aCommit ":これは、行内のインデックスに対する複数のコミットの効果を元に戻すときに役立ちます。
  • 特定のファイルには適用されません(a.pyが他の1000の変更を含むコミットの一部であり、元に戻したくない場合はどうなりますか?)
    そのため、特定のファイルを別のコミットで抽出したい場合は、 git-checkout 、具体的にはgit checkout <commit> <filename>構文(ただし、この場合に必要な構文とは異なります)

Easy Git (Elijah Newren)は Gitメーリングリスト にさらに「完全な復帰」を試みました。しかし、あまり成功していません:

人々は時々「変更を元に戻したい」と思っています。

今、これは次のようになります:

  • 32から29リビジョン前の変更、
  • 最後のコミット以降のすべての変更かもしれませんが、
  • 3コミット前からの変更か、または
  • 1つの特定のコミットである可能性があります。
  • ユーザーは、このような復帰を特定のファイルにサブセット化したい場合があります。

eg revertここに記載 ですが、それが現在のディストリビューションの一部であるかどうかはわかりません)

結局のところ、結局のところ「変更を元に戻す」ことになります。

eg revert --since HEAD~3  # Undo all changes since HEAD~3
eg revert --in HEAD~8     # much like git revert HEAD~8, but nocommit by default
eg revert --since HEAD foo.py  # Undo changes to foo.py since last commit
eg revert foo.py               # Same as above
eg revert --in trial~7 bar.c baz.  # Undo changes made in trial~7 to bar.[ch]

これらの種類の「データを元に戻す」は本当に異なるので、異なるコマンドを使用する必要があるか、またはこれらの操作の一部は単純なrevertコマンドでサポートされるべきではありませんか?
もちろん、ほとんどのユーザーはほとんどの場合、「eg revert FILE1 FILE2... "フォームですが、追加機能をサポートすることに害はありませんでした。

また...コアgitがそのような振る舞いを採用しないようにする根本的なものはありますか?

エリヤ

注:デフォルトのコミットは、一般化されたrevertコマンドおよび「git revert REVISION "を指定するとエラーになります(ユーザーに--inフラグを追加するように指示します)。


コミットされた50個のファイルのうち20個のファイルがあり、古いコミットXが原因で発生していたはずのない変更が導入されたことに気付いたとします。
少しの配管が必要です。
必要なのは、必要なすべての特定のファイルをリストする方法です元に戻す
のように「コミットXで行われた変更をキャンセルし、その後のすべての変更を保持するには」)、
次に、それぞれについて:

git-merge-file -p a.py X X^

ここでの問題は、保持したいa.pyのすべての後続の変更を消去せずに失われた関数を回復することです。
この手法は、「ネガティブマージ」と呼ばれることもあります。

以来git merge-file <current-file> <base-file> <other-file>手段
は、<base-file>から<other-file>から<current-file>、すべての変更を組み込みたいと言うことで、削除した関数を復元できます。)

  • from:X(関数が削除された場所)
  • to:X ^(関数がまだそこにあったXの前のコミット)

注: '-p '引数を使用すると、現在のファイルで何もせずに変更を最初に確認できます。確認したら、そのオプションを削除します。

git merge-fileis not that simple :そのようにファイルの以前のバージョンを参照することはできません。
(イライラするメッセージが何度も表示されます:error: Could not stat X
必ず:

git cat-file blob a.py > tmp/ori # current file before any modification
git cat-file blob HEAD~2:a.py > tmp/X # file with the function deleted
git cat-file blob HEAD~3:a.py > tmp/F # file with the function which was still there

git merge-file a.py tmp/X tmp/F # basically a RCS-style merge
                                 # note the inversed commit order: X as based, then F
                                 # that is why is is a "negative merge"
diff -u a.py tmp/ori # eyeball the merge result
git add a.py 
git commit -m "function restored" # and any other changes made from X are preserved!

これが以前のコミット内の多数のファイルに対して行われる場合...いくつかのスクリプトが適切です;)

33
VonC

VonC が指摘しているように、コミット内の変更を1つのファイルのみに戻すには、ブランチ(マスターまたはトランクなど)をcheckoutし、次にcheckout元に戻して新しいコミットとして扱いたいファイルのバージョン:

$ git checkout trunk
$ git checkout 4f4k2a^ a.py
$ git add a.py
$ git diff              #verify I'm only changing what I want; edit as needed
$ git commit -m 'recover function deleted from a.py in 4f4k2a'

これを直接実行する配管コマンドがあると思われますが、知っていれば使用しません。私はGitを信頼していないというわけではありませんが、自分自身を信頼していません。そのコミット以降、そのファイルの変更点を調べなければ、自分が知っているとは信じられません。見たら、diffを編集して新しいコミットをビルドする方が簡単です。おそらくそれは単なる個人的なワークスタイルです。

4
Paul

このgitは質問を元に戻す を見てください。最新のコミットを含む連続したシーケンスでない場合、古いコミットを元に戻す問題があるようです。

1
Mohan Nayaka