web-dev-qa-db-ja.com

gitから特定のコミットを元に戻す

たくさんのコミットとたくさんのファイルがあるgitツリーがあります。ここで、ファイルにのみ関係する特定のコミットを元に戻したいと思います。説明する:

> git init
Initialized empty Git repository in /home/psankar/specific/.git/
> echo "File a" > a
> git add a ; git commit -m "File a"
[master (root-commit) 5267c21] File a
 1 file changed, 1 insertion(+)
 create mode 100644 a
> echo "File b" > b
> git add b; git commit -m "File b"
[master 7b560ae] File b
 1 file changed, 1 insertion(+)
 create mode 100644 b
> echo "File c" > c
> git add c; git commit -m "File c"
[master fd6c132] File c
 1 file changed, 1 insertion(+)
 create mode 100644 c
> echo "b and c modified" > b ; cp b c
> git commit -a -m "b and c modified"
[master 1d8b062] b and c modified
 2 files changed, 2 insertions(+), 2 deletions(-)
> echo "a modified" > a
> git commit -a -m "a modified"
[master 5b7e0cd] a modified
 1 file changed, 1 insertion(+), 1 deletion(-)
> echo "c modified" > c
> git commit -a -m "c modified"
[master b49eb8e] c modified
 1 file changed, 1 insertion(+), 1 deletion(-)
> git log --pretty=oneline c
> git log --pretty=oneline c | cat
b49eb8e03af331bddf90342af7d076f831282bc9 c modified
1d8b062748f23d5b75a77f120930af6610b8ff98 b and c modified
fd6c13282ae887598d39bcd894c050878c53ccf1 File c

ここで、変更をaに戻すことなく、2つのコミットb49eb81d8b06のみを元に戻したいと思います。 IOWはファイル内のコミットのみを元に戻します(別のファイル内の他の中間コミット(数千の場合もあります)は元に戻しません)これはどのように可能ですか?

47
Sankar

git revertオプションで--no-commitを使用できます。あなたの例では:

$ git revert --no-commit b49eb8e 1d8b062
# Files that were modified in those 2 commits will be changed in your working directory
# If any of those 2 commits had changed the file 'a' then you could discard the revert for it:
$ git checkout a
$ git commit -a -m "Revert commits b49eb8e and 1d8b062"

コミットメッセージを提供しない場合、コミットメッセージエディタの起動時に準備されたメッセージを利用できます。

--no-commitオプションを省略すると、指定したコミットの変更が元に戻ります。これは、指定されたコミットの変更の逆を適用し、それをコミットすることにより達成されます。これにより、新しいコミットが作成され、元のコミットと元に戻されたコミットの両方がリポジトリの履歴に含まれます。

80
mamapitufo

ここには2つのケースがあります。

  1. すでにgitツリーをどこかにプッシュしていて、履歴を変更したくない場合。この場合、以前のコミットを元に戻す際に行った変更を表す新しいコミットが必要になります。 @mamapitufoの回答を使用する必要があります。

  2. 変更が適用されているブランチをプッシュしたことがない場合、履歴を変更できます。この場合、不要なコミットを完全に削除できます。これにより、履歴が簡略化され、同僚や一般の人々に間違った方向を向かわせることがなくなります。

2番目のケースでは、git rebase -iを実行する必要があります。変更する履歴の前にあるコミットを見つけます。これは、コミットのハッシュ、またはブランチまたはタグの名前です。たとえば、次のことができます

git rebase -i 23def8231

または、ブランチOrigin/dev_branchから開始して、dev_branchと呼ばれるブランチで削除するビットを含む作業を行った場合、

git rebase -i Origin/dev_branch

これで、リベースするすべてのコミットのリストを表示できるエディターウィンドウに移動します。これはvimである可能性があります-通常端末で編集しない場合は、デフォルトとして設定できます。もしそうなら、vimのクイックスタートガイドとオープンマインドが必要になるでしょう。

今、最も簡単なことはコミットを削除することです。これを行うには、行を削除するか、コメントを示す#を行の先頭に追加します。 (ファイルには、説明を行うためのコメントが既にいくつかあります。これらを無視しても削除しても効果はありません。)

終了したら、ファイルを保存してエディターを終了します。リベースは次のように行われます:gitは指定したコミットに戻ります。保存したリストを調べて、そのリストの各コミットを再生します。次に、そのプロセスの結果が、元々あったブランチの新しいバージョンになります。

覚えておくべき重要事項:

  • 行方不明になったり、削除する行が多すぎる場合は、ファイル内のすべてのコミット行を削除して保存することにより、リベースをキャンセルできます。リベースプロセスは、何も変更されずに終了します。
  • 競合が発生する可能性があります。たとえば、ファイルを編集するコミットを削除し、同じ場所を編集する後のコミットを残す場合。後のコミットは正しく適用されないため、手動またはマージツールで編集して必要なバージョンを取得する必要があります。

git rebase -iで他の多くの操作を行うこともできます。たとえば、コミットの順序を変更したり、いくつかを1つにまとめたり、コミット間に追加の変更を追加したり、メッセージを変更したりします。とても便利です。古典的なユースケースは、他の人があなたの変更を見る場所にプッシュバックする前にローカルブランチをクリーンアップすることです。

11
jwg