別の Git リポジトリーから変更を取り込む。

Git

別の Git リポジトリーで適用した変更を取り込みたくて方法を探していたのですけれど、そういえば Git を使って差分パッチを作れることを思い出して、それを使って別リポジトリーの変更を取り込んでみることにしました。


ある Git リポジトリーでの変更を、別の類似したリポジトリーに適用したくなりました。

そう思って調べてみると git cherry-pick でも異なるリポジトリーの 変更を取り込む方法が見つかりました。ただ、それにはリモートリポジトリーに登録したり チェックアウトしたりする必要があって、継続的に変更を適用したいならまだしも、たまたま生じたちょっとした変更を取り込むには大げさなようにも感じてみたり。

そう思っていたら、そういえば git diff でパッチを作る方法があったのを思い出したので、 今回はそれを使って変更を適用してみることにしました。

コミットからパッチを生成する

変更を加えたコミットからパッチを生成するには git diff を使います。

たとえば、最終コミットと直近のコミットからパッチを生成したいのであれば、次のようにすることで、 変更情報が記載された /tmp/fix.patch というパッチファイルが出来上がります。

git diff HEAD~1 > /tmp/fix.patch

パッチを適用する

パッチファイルが出来上がったら、それを変更したいリポジトリーで適用します。

適用したいリポジトリーのルートフォルダーに移動したら、次のコマンドを実行して、先ほど作成した fix.patch の内容を適用します。

patch -p1 -i /tmp/fix.patch

変更したいリポジトリーのルートフォルダーに移動しなくても -d オプションを指定することで、適用したいフォルダーの場所を指定できます。

階層が異なる場合

パッチに記載されたファイル名と、実際に置かれたファイルの階層が異なる場合は -pN オプションを使ってパッチのディレクトリ階層を N つ掘り下げて適用できるので、これを使って最適な階層からパッチを当てられます。

たとえば、パッチファイルには a/A/B/file.txt という階層で適用先のファイル情報が記載されているのに、適用したい先の実際は C/file.txt だったとすると、適用させる先を適切にするには フォルダー C/ に移動して、パッチに書かれている適用先情報のフォルダー 3 階層を無視する ようにします。

cd C/ 
patch -p3 -i /tmp/fix.patch

そうすると、カレントフォルダーにある file.txt に対してパッチを当てられるようになります。 こんな感じに調整することで、パッチを異なる階層に適用できるようになります。

最初の説明で、適用元と適用先とでフォルダー構成が同じときでも、パッチを当てるときに -p1 を指定して 1 階層を無視していたのは、どうやら最初で紹介した git diff で作成したパッチには、変更前のフォルダーを a/ から始まるものとして、変更後のフォルダーを b/ から始まるものとして、ファイル情報が埋め込まれている様子です。そのため、パッチを適用するときには、便宜上つけられた最初の 1 階層を無視して適用する必要があるようです。

パッチを生成するときに git diff コマンドに --no-prefix をつけると、変更前と変更後とを表すプレフィックス a/b/ を含めないようにすることもできます。けれどパッチを適用するときは、オプションが -p1 から -p0 になる程度な印象なので、わざわざこのオプションを覚えて使うよりも、適用時に使う -p オプションの意味を理解しておいた方が有意義のような気がします。