PatchFile
プログラムファイルというものは「生産完成品」ではなく、日々更新されるものです。すでに配布済みのプログラムに修正が必要になった場合は、その旨を利用者に通知して、最新の状態に更新してもらうことが必要です。このときの修正箇所、つまり何を削除して何を追記するかという変更点を一定のルールに従って書いたものがパッチファイル(差分ファイル)です。
容量やスピードに余裕のある現在では、ファイルまるごと入れ替え・・という発想が主流ですが、「小さな情報(差分)で、全体を修正する」方が資源の消費は少なくなるし、また「どこがどう変わったのかがわかる」という点でもパッチファイルの考え方は重要です。
Gitを用いたファイルのバージョン管理においても、パッチの概念やその記法を理解することが重要です。
パッチファイル(差分ファイル)の作成、パッチファイルを用いた古いファイルの修正(「パッチをあてる」といいます)、ともに ターミナル を使ってコマンドで処理するのが一般的です。
- diff コマンド:パッチファイル(差分ファイル)の生成
- patch コマンド:パッチをあてて、ファイルを修正する
パッチファイル(差分ファイル)は プレーンなテキストファイルで、拡張子としては .pach や .diff が使われます。
以下、簡単な事例で練習してみましょう。
準備
以下の2つのファイルを事例に説明しますので、準備して下さい。sample.txt が「修正が必要な現行ファイル」。sample.new.txt が「問題が修正された新ファイル」とします。
- sample.txt|修正が必要な現行ファイル
Sunday Monday Wednesday ← Tuesday が抜けている Thursday Fryday ← 綴りが違う(Fryday ではなく Friday が正しい) Saturday
- sample.new.txt|問題が修正された新ファイル
Sunday Monday Tuesday Wednesday Thursday Friday Saturday
これから何をするのか
ここからの説明が混乱しないように、ここで、「開発者」と「利用者」という言葉で、話の前提を確認しておきましょう。
一般にプログラムというものは100%完成した状態で配布されているわけではなく、利用者は常にバグを含んだファイルを利用しています。利用者から開発者側へ問題が報告されると、開発者側はそれを修正。そして修正版(最新版)と現行版との差分をパッチファイルとして提供するわけです。利用者はこれをダウンロードして、パッチをあてることによって、開発者の手元にある最新版と同じ状態にする・・これがパッチによる修正です。
- 問題を修正して最新版をつくり、これを現在稼働中の古いファイルと比較してパッチファイル(差分ファイル)を作る > 開発者
- パッチファイルを入手して、利用中のファイルを更新する > 利用者
混乱を避けるために、以下の説明では、必要に応じて、どちらの立場の話なのかを明記します。
パッチファイルの作成|開発者側
パッチファイルを作成するには、diff コマンドを使って、差分情報 を抽出します。その意味で、パッチファイルは差分ファイルとも言われます。
以下、ターミナルのプロンプトが当該ディレクトリを指しているとして・・
- まず ls コマンドで、カレントディレクトリのファイル一覧を確認します。
$ ls sample.txt sample.new.txt
新旧2つのファイルが存在しています。
- diff コマンドでパッチファイルを生成します。
通常は、現在稼動中のファイルが修正されるべき対象となるので、コマンドでは、旧ファイル 新ファイルの順に記載し、その差分情報をパッチファイルに書くことになります。$ diff -u sample.txt sample.new.txt > sample.patch
これで、sample.txt から sample.new.txt への変更点が記載されたパッチファイル sample.patch ができあがります。
[ -u ] は unified diff形式でパッチファイルを作る・・という意味のオプション指定です。
- 出来上がったパッチファイルの内容を catコマンドで確認してみましょう。
$ cat sample.patch ← 内容を表示するためのコマンド --- sample.txt 2017-09-08 12:43:04.000000000 +0900 +++ sample.new.txt 2017-09-08 12:42:16.000000000 +0900 @@ -1,6 +1,7 @@ Sunday Monday +Tuesday Wednesday Thursday -Fryday +Friday Saturday
- 記号にはそれぞれ以下のような意味があります
- 先頭行 --- :修正されるべき旧ファイルのファイル名
- 2行目 +++:問題が修正された新ファイルのファイル名
- @@ -1,6 +1,7 @@:
旧ファイルの変更範囲は1行目から6行分で、それに対する新ファイルの対応箇所は1行目から7行分とうことを意味しています。 - [+]:追加された行
- [ - ]:削除された行
パッチをあてる|利用者側
パッチをあてる(つまり現行のファイルを修正する)には、patch コマンドを使います。以下、修正したいファイルとパッチファイルが同じディレクトリにあって、ターミナルのカレントディレクトリは、そこに位置しているという前提でお話しします。
- まず、ls コマンドで ファイルがカレントディレクトリにあることを確認。
$ ls ・・・ sample.txt sample.patch ・・・・
修正の必要な現行のファイルが sample.txt、これを修正するパッチファイルが sample.patch です。
- patchコマンドでファイルにパッチをあてます。
いくつか方法があります。- 1) 適用するファイル名とパッチファイル名 ともに書く
$ patch -u sample.txt sample.patch
[ -u ] は unified diff形式のパッチファイルをあてる・・という意味のオプション指定です。 - 2) 適用するファイル名を省略して、パッチファイル名のみ書く
$ patch -u < sample.patch
通常パッチファイルというのは、その作成する段階で現行のファイルを旧ファイルとして扱います。このときパッチファイルの先頭行には「--- 現行のファイル名」が記載されるので、システムはその情報をもとに適用先を自動的に見つけることができます。パッチをあてる際には「適用するファイル名」は省略できるのが普通です。
- 1) 適用するファイル名とパッチファイル名 ともに書く
- 出来上がったパッチファイルの内容を catコマンドで確認してみましょう。
$ cat sample.txt ← 内容を表示するためのコマンド。以下が実際の内容 Sunday Monday Tuesday ← Tuesday が挿入されている Wednesday Thursday Friday ← 綴りが修正されている Saturday
これで、開発者が更新した sample.new.txt と同じ状態に更新されました。
ファイル名は、sample.txt のままで正解です。ファイル名を変更してしまうと、他のファイルとの関係がおかしくなるので、同じファイル名のまま、中身だけ新しくする・・ということでOKです。
ディレクトリ単位のパッチファイルの作成|開発者側
更新作業は、ファイル単体で行うこともあれば、大きくフォルダの中身全体、複数のファイルを一挙に・・ということもあります。この場合、パッチファイルに書き出される差分情報はディレクトリ全体に及ぶものになります。
- ここでは、開発者の手元に SampleProject というフォルダと、その最新版である SampleProject_new というものがあって、開発者が、この2つの差分を以下のように作成する・・という前提で説明します。
- 差分ファイルは、以下のようなコマンドで生成されます。
$ diff -u SampleProject SampleProject_new > SampleProject.diff
- 差分ファイル SampleProject.diff の内容は、以下のような感じです。
$ cat SampleProject.diff ← 内容を表示するためのコマンド diff -u SampleProject/index.html SampleProject_new/index.html --- SampleProject/index.html 2017-09-08 19:06:46.000000000 +0900 +++ SampleProject_new/index.html 2017-09-08 17:53:16.000000000 +0900 @@ -4,7 +4,7 @@ <head> <meta charset="UTF-8"> <link rel="stylesheet" href="style.css"> - <title>Sample Site</title> + <title>Sample Site New</title> </head> <body> Only in SampleProject_new: myScript.js diff -u SampleProject/style.css SampleProject_new/style.css --- SampleProject/style.css 2017-07-17 18:50:33.000000000 +0900 +++ SampleProject_new/style.css 2017-09-08 19:07:02.000000000 +0900 :
- 記号にはそれぞれ以下のような意味があります
- diff - u・・:フォルダ内の index.html 同士の比較です・・という意味
- --- :旧ファイルの名称 SampleProject/index.html
- +++ :新ファイルの名称 SampleProject_new/index.html
- Only in ・・:SampleProject_new にのみ、ファイル myScript.js がある
- diff -u ・・:フォルダ内の style.css 同士の比較です・・という意味
- --- :旧ファイルの名称 SampleProject/style.css
- +++ :新ファイルの名称 SampleProject_new/style.css
ディレクトリ単位でパッチをあてる|利用者側
開発者が diff を実行した環境(ファイルパス)と、利用者がパッチを適用する環境は、それぞれ異なるのが一般的です。そこで、差分適用に関してよくある状況、すなわち SampleProject.diff を適用対象フォルダ(SampleProject)の中へ投入して作業するという前提で説明します。
- cd コマンドで、差分を適用するフォルダ(SampleProject)の中へ入ります
$ cd ./SampleProject
- 必要なファイルがその中(SampleProjectの中)にあることを確認します。
$ ls index.html style.css SampleProject.diff
index.html、style.css は、修正の必要な現行のファイル。これを修正するパッチファイルが SampleProject.diff です。
- patchコマンドでファイルにパッチをあてます。
$ patch -u -p1 < SampleProject.diff
ここで -p1について。ここでは、SampleProjectの中に差分ファイルを置いている前提なので、ディレクトリ名の情報つまり、/SampleProject を無視して、その下にあるファイルに処理を行うことになります。しかし差分ファイルには SampleProject/index.html のようにパスが書かれていて、現在の作業位置から見るとおかしなことになります。その際に必要になるのが、-p[数字] というオプションです。数字の部分は無視するパスの階層の数です。この例では、-p1 でパスを1つ分取り除くことで、目の前にいる index.html に差分が適用できることになります。
このように、ディレクトリ単位での処理を行う場合、diffを実行した環境と、pachを実行する環境が異なることが多く、パスの情報が邪魔になることがあります。-p[数字] はそのような邪魔なパス情報を無視するために利用されます。
- 実行すると以下のようなメッセージが出ます。
patching file index.html patching file style.css
カレントディレクトリにある index.html とstyle.css に対して、正常に修正が行われたという意味です。ここでは、SampleProject_newにしか存在しないファイルである myScript.js は補完されません。
補足
- ちなみに、SampleProjectフォルダにSampleProject.diffを入れずに作業する場合、つまり カレントはSampleProject フォルダが直下に見える階層で、そこに SampleProject.diff がある場合・・
$ ls SampleProject SampleProject.diff
修正の必要なファイルのあるフォルダが SampleProject、パッチファイルが SampleProject.diff です。
- これは、開発者が diff を実行したときと同じ位置関係になります。したがってパッチをあてる際は、パッチファイルに書かれた情報がそのまま利用できるので、オプションは -p0 、すなわちパスの情報はそのまますべて利用するものとして実行します。
$ patch -u -p0 < SampleProject.diff
- 実行すると以下のようなメッセージが出ます。
patching file SampleProject/index.html patching file SampleProject/style.css
現在の位置から見て、SampleProject フォルダの中の2つのファイルが正常に修正されたことがわかります。