バージョン管理ソフトウェアの管理下にないソースコードの変更点のパッチを、自分のgitリポジトリにマージしなければならないという状況があります。このときパッチの適用時に発生した衝突は.rejファイルに集められ、プログラマはこれを見ながら衝突を手動で解決する必要がありますが、このような作業は面倒でつらいものです。
しかし、Emacsを使うとこのような手動マージが多少楽に行える場合があります。
** 状況
バージョン管理されていないオリジナルのソースコードと、そのソースから派生している自分のプロジェクトがあります。
自分のプロジェクトは git で管理していて、様々な変更を加えています。
ある日オリジナルのソースコードが更新されて、自分のプロジェクトにもその更新分をマージしなければならなくなったとします。
>||
A1 A2
オリジナル (バージョン管理なし) o- – – – – o
|
自分のプロジェクト (gitで管理) o—o—o—o
A1′
||<
とりあえず変更元(A1)との差分を抽出します。
>||
A1 A2
オリジナル (バージョン管理なし) o- – – – – o
|
自分のプロジェクト (gitで管理) o—o—o—o
| | A1′
|<--------->|
A1とA1’の差分を抽出
||<
git diff
でパッチを作ります。
>||
$ git diff f0957e8e:src/foobar/ HEAD:src/foobar/ > diff.patch
||<
これを更新された非バージョン管理下のソースコード(A2)に適用してマージを試みます。
>||
A1 A2
オリジナル (バージョン管理なし) o- – – – – o
| \
自分のプロジェクト (gitで管理) o—o—o—-o
A1′ A2′
||<
このとき変更点に衝突がなければすんなりマージされA2'となりますが、普通は衝突するものです。実際に patch
コマンドを実行してみると…
>||
$ cd src/foobar/
$ patch -p1 -f < diff.patch
patching file `foobar.cpp'
Hunk #1 FAILED at 11.
Hunk #2 FAILED at 579.
Hunk #3 FAILED at 700.
Hunk #4 FAILED at 978.
Hunk #5 FAILED at 1077.
Hunk #6 FAILED at 1085.
Hunk #7 FAILED at 1150.
Hunk #8 FAILED at 1169.
Hunk #9 FAILED at 1267.
Hunk #10 FAILED at 1330.
Hunk #11 FAILED at 1408.
Hunk #12 FAILED at 1430.
Hunk #13 succeeded at 1382 with fuzz 2 (offset -68 lines).
Hunk #14 FAILED at 1533.
Hunk #15 FAILED at 2890.
...snip...
||<
…変更が衝突しておりHunkの適用が失敗しまくっています。Hunkはひとかたまりの差分で、diffによって生成されるパッチはこのHunkの集合で成りたっています。ほとんどは失敗ですが、#13のHunkのように成功しているものもあるようです。
** .rejファイル
patch
コマンドはHunkの適用に失敗すると、失敗した分のHunkを.rejという拡張子のファイルに集めます。プログラマはこの.rejファイルを見て失敗したHunkだけを手動でマージすることができます。
なお、git apply
でパッチを適用する場合は、--reject
オプションをつけると.rejファイルを生成してくれます。
** Emacsによる手動マージ (diff-mode)
Emacsでは diff
で出力したファイルを扱うための diff-mode
が標準でついており、コードのハイライトや編集支援の機能を利用できます。.rejファイルはUnified形式のdiffフォーマットで保存されるので、diff-mode
で読み込むことができます。
以下は diff-mode
でよく使うキーバインドです。その他のキーは <f1>-m
で見ることができます。
M-n | 次のHunkへ移動 |
M-p | 前のHunkへ移動 |
M-k | Hunkを削除 |
C-c C-a | Hunkを適用 |
C-c C-c | Hunkに対応するソースの行へジャンプ |
C-c C-e | Ediffで開く |
** 手動マージの手順
前述の diff-mode
で.rejファイルの取り扱いが多少楽になります。筆者は最初これをVC6でやろうとしていたので途中で心が折れました。
以下は diff-mode
を使った.rejファイルの手動マージの一連の作業例です。これがベストプラクティスではないと思いますが、読者の方の参考になれば幸いです。
*** 1. マージ対象に移動
diff-mode
で.rejファイルを開いたら、M-n/M-p
でマージするHunkに移動し、C-c C-c
で対応するソースの行にジャンプします。もし想定とは違う行にジャンプした場合は自分で正しい位置にカーソルを移動させます。
*** 2. 変更を手動で適用
.rejファイルのHunkの内容を見ながら、手動でソースを修正します。リージョンを選択して C-x r M-w
すると矩形でコピーできるので、diffの先頭マーカーを除いてコピーして、C-x r y
で貼り付けることができます。フリーカーソルで選択したい場合は M-x picture-mode
です。
*** 3. .rejファイルから変更したHunkを削除
手動マージすべきHunkが大量にある場合、自分がどこまでマージしたか忘れてしまう場合があります。そこで、Hunkの適用が完了するたびに M-k
でHunkを削除しておくと作業がやりやすくなります。
** まとめ
– Hunk FAILED
しても泣かない。
– diff-mode
を活用しよう。
– バージョン管理しよう。
投稿者紹介
- サーバ運用もできるプログラマと化した、2013年入社の元新人エンジニアです。
最近のエントリ
- 勉強会2017.03.10ユニキャストの勉強会のご紹介
- Ruby2014.05.16[ruby-install] Rubyのバージョン切替えスクリプトを書いた
- CentOS2014.05.14OpenLDAPサーバーを構築する
- Ruby2013.12.17AutomaticRubyとChatWorkを連携させる