0
0

Git: 変更内容の一部だけ残して内容を戻したい

Last updated at Posted at 2023-12-04

patch コマンドを知って、こういう使い方もできそうじゃないか、と思っていくつか試してみたらうまくいったので記事にしました。

この記事は何か

Git を使っていて、

  • 変更を stash し忘れたまま別件の改修を加えてしまった
  • フォーマッタが改修対象以外にも変更を発生させてしまった

など、ファイルの一部分だけを除いて内容をもとに戻したい、と思ったことはないでしょうか?

この記事では、Git と patch コマンドを使って、指定した変更箇所を除いて、内容を直前の commit に切り戻す方法を紹介します。

前提

前提として、以下のツールが使える必要があります。

  • Git
  • patch

Linux であればデフォルトで入っていたり、package で簡単に入れられると思います。
Windows の場合は WSL を使うのが早いのではないでしょうか。

どう実現するか

1. 現在の変更差分をファイル出力する

まずは、現在の変更差分を適当なファイルに出力しましょう。
ここでは、diff.patchというファイルに差分を出力することにします。

git diff > diff.patch

2. 残したい変更差分を抽出する

続いて 1. で出力した diff.patch を適当なエディタで編集し、欲しい差分だけ残して不要部分を削除していきます。
念のため編集前の diff.patch をコピーして残しておくことをお勧めします。

cp diff.patch diff.patch.bak

以下のような diff.patch が得られているとします。

diff --git a/sample.txt b/sample.txt
index 0edb856..6757c5e 100644
--- a/sample.txt
+++ b/sample.txt
@@ -1,5 +1,5 @@
-a
-b
+1: a
+2: b
 c
 d
 e
@@ -8,6 +8,7 @@ g
 h
 i
 j
+---
 k
 l
 m

この差分に対し、---の追加のみを残して、ほかの差分を削除したい場合は、以下のように diff.patchを変更します。

diff --git a/sample.txt b/sample.txt
index 0edb856..6757c5e 100644
--- a/sample.txt
+++ b/sample.txt
@@ -8,6 +8,7 @@ g
 h
 i
 j
+---
 k
 l
 m

残念ながら、残したい差分となくしたい差分が、同じhunk (マーカーで示されたブロック) 内に混在する場合は、単純に削除すればよい、というわけにはいきません。

例えば、以下のような diff.patch に対し、 --- の追加だけを残したい場合です。

diff --git a/sample.txt b/sample.txt
index 0edb856..4971857 100644
--- a/sample.txt
+++ b/sample.txt
@@ -16,6 +15,7 @@ o
 p
 q
 r
+---
 s
 t
 u
-v
-w
+V
+W

diff.patchを適切に修正することもできなくはないのですが、多少不要な差分が混じることを許容して差分削除を行い、ファイルを直接編集して差分を修正するのが早いと思います。

3. ファイルを直前の commit の内容に戻す

git checkout でファイルの内容を直前の commit に戻しましょう。

git checkout .

4. 変更差分を適用する

patch コマンドを使って diff.patch を適用します。

patch < diff.patch

patch コマンドが失敗した場合は、diff.patch.bak を適用し、1. の状態に戻しましょう。

git checkout .
patch < diff.patch.bak

5. diff.patchで取り切れなかった差分を取り除く

必要に応じ、 diff.patch の編集で取り切れなかった差分を直接ファイルで取り除きましょう。

6. 完成

これで完成です。
念のため git diff で差分を確認しておくとよいです。
また事故らないうちに変更を commit しておきましょう。。。

応用例

変更差分を 2つに分けて commit を作る

stash し忘れて別な変更を入れてしまった場合などは、以下の手順で修正することが、できます。

1. ちなみに1つ目の commit を作成する

どう実現するか の手順に沿って、1 つ目の commit の内容を作成します。
このとき、2.残したい変更差分を抽出するdiff.patch.bak を作っておいてください。

2. 現在の commit hash を記録する

target_hash=$(git rev-parse HEAD)

3. 1つ前の commit に戻る

git checkout HEAD^

4. 1 つ目の commit との差分を取る

2. 現在の commit hash を記録する で取得した、 1 つ目の commit との差分をファイルに出力します。
ここではこの差分ファイルを diff2.patch とします。

git diff ${target_hash} > diff2.patch

5. commit したい branch に checkout する

git checkout .
git checkout <branch_name>

6. 2 つ目の commit を作成する

4. 1 つ目の commit との差分を取る で作成した diff2.patchを使って、4. 変更差分を適用する 以降を実行する。

よくわかってないこと

patchファイルの編集により変更後の hunk 開始位置が普通にずれるのがだいぶもやもやしてます…
一応期待通り動きますが、どうしてうまくいくのか理解できてません… 教えて詳しい人…

まとめ

Git と patch を駆使して、変更内容の一部だけを切り戻す方法について解説しました。
意図しない変更が大量に混じってしまった場合に使うと便利なので、ぜひお試しあれ!!!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0