vim-jp / vimdoc-ja / diff

diff - Vim日本語ドキュメント

メインヘルプファイルに戻る English | 日本語 | 編集
diff.txt      For Vim バージョン 9.1.  Last change: 2024 Feb 01


                VIMリファレンスマニュアル    by Bram Moolenaar


                                diff vimdiff gvimdiff diff-mode
このファイルでは +diff 機能 (同じファイルの2つから8つまでのバージョン間の違
いを表示する機能) について解説する。

基本はユーザーマニュアルのセクション08.7に記載されている。

1. 差分モードを開始する         start-vimdiff
2. 差分を眺める                 view-diffs
3. 差分へ移動する               jumpto-diffs
4. 差分を写す                   copy-diffs
5. 差分モードのオプション       diff-options

==============================================================================
1. 差分モードを開始する                                 start-vimdiff

差分モードで編集を開始するいちばん簡単な方法は "vimdiff" コマンドである。これ
はVimを通常どおり起動して、加えて引数で与えたファイル間の違いを表示する。

        vimdiff file1 file2 [file3 [file4]]

これは以下に等しい:

        vim -d file1 file2 [file3 [file4]]

"gvimdiff" もしくは "vim -d -g" を使うこともできる。その際はGUIがスタートする。
"viewdiff" もしくは "gviewdiff" を使うこともできる。その際は読込専用モードでス
タートする。
"r" が先頭に付け加われば制限モードになる(-Z参照)。

2つ目以降の引数にはディレクトリ名を指定することもできる。その際には第1引数の
ファイル名がそのディレクトリ名に追加され、ファイルを検索するのに利用される。

デフォルトでは、内部diffライブラリが使用される。'diffopt' または 'diffexpr' が
設定されていると外部の "diff" コマンドが使用される。これは、そのようなdiffプロ
グラムが利用可能な場合にのみ機能する。

diff はカレントタブページ tab-page にローカルである。他のタブページにある
ウィンドウとの差分を見ることはできない。これによって、複数の差分を同時に見るこ
とが可能になっている。それぞれを別々のタブで開けばよい。

Vimが各ファイルについてウィンドウを開く時に起こることは、-O引数を使った時に
起こることに似ている。これには垂直分割が使用される。水平分割を行いたいならば
-o引数を追加する:

        vimdiff -o file1 file2 [file3 [file4]]

常に水平分割にしたければ 'diffopt' に "horizontal" を含めること。

編集される各ファイルには以下のオプションが設定される:

        'diff'          on
        'scrollbind'    on
        'cursorbind'    on
        'scrollopt'     "hor" を入れる
        'wrap'          off、または 'diffopt' が "followwrap" を含むならそのま
                        まにする。
        'foldmethod'    "diff"
        'foldcolumn'    2

これらのオプションはウィンドウローカルに設定される。別のファイルを開いた時に
は、これらはグローバルの値へリセットされる。
このオプションはさらにそのファイルを再読み込みするときモードラインから上書きさ
れることがある。しかし 'diff' がオンのとき、'foldmethod' と 'wrap' はモードラ
インからはセットされない。
簡単にオプションを元に戻すには :diffoff を参照。

表示される差分はバッファ内の違いである。だからファイルを読み込んだ後に変更を行
えば、その変更分は差分として表示される。特に外部 diff コマンドを使用している際、
全ての変更が即表示に反映されるわけではないので、時々 ":diffupdate" を行うと良
いだろう。

差分モードで起動した時の特別な設定を.vimrcファイルに記すことができる。このよう
にすれば良い:

        if &diff
           setup for diff mode
        else
           setup for non-diff mode
        endif

既にVimを利用している時には、3つの方法で差分モードへ移行することができる。

                                                        E98
:diffs[plit] {filename}                                 :diffs :diffsplit
                ファイル{filename}の新しいウィンドウを開く。現在と新しく開く
                ウィンドウについて "vimdiff" と同様のオプションをセットする。
                'diffexpr' も参照。

                                                        :difft :diffthis
:difft[his]     現在のウィンドウを差分ウィンドウの1つにする。これにより
                "vimdiff" と同じオプションが設定される。

:diffp[atch] {patchfile}                         E816 :diffp :diffpatch
                {patchfile}内の差分情報を現在のバッファへ適用し、結果を新しく
                作成したバッファへ出力する。オプションは "vimdiff" と同様に設
                定される。
                {patchexpr}の形式は "patch" プログラムか 'patchexpr' が取り扱
                える形式ならどのようなものでもかまわない。
                {patchfile}は現在のファイルに対して適用可能な差分情報だけを含
                んでなければならないことに注意。もしも{patchfile}が他のファイ
                ル用の差分情報を含んでいた場合は、結果は予想不可能となる。Vim
                は現在のディレクトリのファイルが偶発的に書き換えられてしまうの
                を避けるためディレクトリを/tmpへ変更する。しかし様々な ".rej"
                ファイルが作成されてしまう問題は依然としてある。また差分情報内
                にファイルが絶対パスとして与えられた場合には、やはり適用されて
                しまう。
                "patch" コマンドの使用は、restricted-mode では許可されていま
                せん。

このコマンドを垂直分割で使うには、:verticalを先行させる。例:

        :vert diffsplit main.c~
        :vert diffpatch /tmp/diff

常に垂直分割にしたければ 'diffopt' に "vertical" を含めること。

                                                        E96
'diff' オプションは最大で8つのバッファにまで同時に設定できる。

オプションの値はバッファへ記憶されるので、しばらくの間異なるファイルを編集し、
また同じファイルへ戻って再び差分モードを継続することができる。

                                                        :diffo :diffoff
:diffo[ff]      カレントウィンドウの差分モードを終了する。'diff' が設定されて
                いてもいなくても、関連オプションはリセットされる。

:diffo[ff]!     カレントウィンドウとカレントタブページのすべてのウィンドウの差
                分モードを終了する。'diff' が設定されているウィンドウのみ、関
                連オプションがリセットされる。カレントウィンドウの 'diff' オプ
                ションが設定されていない場合は、そのウィンドウの関連オプション
                は変更されない。
                隠れバッファも、diff対象のバッファの一覧から削除される。

コマンド :diffoff は関連するオプションを差分モード実行前の設定値に戻す。それ
は :diffsplit:diffpatch:diffthis を実行したときの設定値、または Vim
を差分モードで起動したときの設定値である。:diffoff を2回実行したときは最後に
保存された値が復元される。
それ以外の場合はデフォルト値に戻す:

        'diff'          off
        'scrollbind'    off
        'cursorbind'    off
        'scrollopt'     "hor" を外す
        'wrap'          on、または 'diffopt' が "followwrap" を含むならそのま
                        まにする。
        'foldmethod'    "manual"
        'foldcolumn'    0

'foldenable' はほとんどの場合、オフにリセットされる。これは、'foldmethod' が
"manual" に戻されたときである。折り畳み自体はクリアされないが、表示されるべき
ではない。'foldenable' をリセットすることが、それを行う最善の方法である。

==============================================================================
2. 差分を眺める                                                 view-diffs

差分ウィンドウには同じテキストが表示され、異なる部分が強調表示される。テキスト
をスクロールした際には 'scrollbind' オプションにより、他のウィンドウも同じよう
にスクロールされる。垂直分割をしている場合、テキストの位置は正しく同期する。

テキストの位置は次のような場合に狂っていく:
'wrap' が有効で、幾つかの行が折り返され複数行を占めている時
- 折り畳みが一方のウィンドウでは開かれているが、もう一方では閉じられている
'scrollbind' が無効になっている
- テキストが変更された
'diffopt' に "filler" が指定されておらず、削除・追加された行が配置を狂わせて
  いる
'diff' オプションが設定されているウィンドウで編集されている全てのバッファが差
分へ連結される。これは隠し(hidden)バッファにもあてはまる。これを可能にするには
初めに1つのウィンドウでそれらが編集される必要がある。隠れバッファを取り除くに
は、:diffoff! を用いること。

                                        :DiffOrig diff-original-file
'diff' はウィンドウローカルのオプションであるから、1つのバッファをあるウィンド
ウでは差分モードで、別のウィンドウでは通常のウィンドウで表示することも可能であ
る。ファイルを読み込んで以来バッファに対して行った変更を表示することも可能であ
る。だが、Vimは1つのファイルに対して複数のバッファを持つことはできないから、別
のバッファを作る必要がある。
次のコマンドが便利である:
         command DiffOrig vert new | set bt=nofile | r ++edit # | 0d_
                \ | diffthis | wincmd p | diffthis
(これはdefaults.vimに書かれている)。":DiffOrig" を実行すると、カレントバッフ
ァと元のファイルの差分を見ることができる。

アンロードされたバッファの差分をとることはできない。隠れバッファの差分をとるこ
とはできる。コマンド ":hide" を使うと、バッファをアンロードせずにウィンドウを
閉じることができる。そのときバッファを差分対象から外したいならば、隠れバッファ
にする前に ":set nodiff" をする。

                                                :dif :diff :diffupdate
:dif[fupdate][!]                差分の強調と折り畳みを更新する。

テキストを変更した時には、Vimは差分情報を最新に保とうと試みる。これの大部分は
挿入と削除をされた行 (複数も可) に着目して行われる。1行内で行われた変更、及び
それよりも複雑な変更に対しては差分情報は更新されない。差分情報を強制的に更新す
るには次のコマンドを使う:

        :diffupdate

!が含まれている場合、Vimはファイルが外部で変更され、再読み込みが必要かどうかを
チェックする。その際、:checktime を使ったときと同様に、変更されたそれぞれの
ファイルについてプロンプトが表示される。

Vimは片方のウィンドウには存在しないがもう一方には存在する行については補充して
表示する。これらはもう一方のファイルで追加されたかこのファイルで削除された行で
ある。'diffopt' オプションから "filler" を削除するとVimはこのような行の補充は
行わない。


変更されていないテキストについては折り畳みを使用して隠される。折り畳みに使用で
きる全てのコマンドについてはfoldingを参照。

差分の近辺の折り畳みに含まれない領域はコンテキストと呼び、その行数を 'diffopt'
オプションで設定できる。以下の例ではこのコンテキストを3行に設定している:

        :set diffopt=filler,context:3


差分は以下のハイライトグループで強調表示される:

hl-DiffAdd    DiffAdd         追加(挿入)された行。このバッファに存在する行
                                は、別のバッファには存在しない。
hl-DiffChange DiffChange      変更された行。
hl-DiffText   DiffText        変更された行の中の変更されたテキスト。Vimは異
                                なる最初の文字と、最後の文字を発見する (検索は
                                行末から行われる)。その文字の間のテキストが強
                                調される。これはその間にあるテキストが例え同じ
                                だったとしても強調されることを意味する。ここで
                                は 'diffopt' の "iwhite" と "icase" が適用され
                                る。
hl-DiffDelete DiffDelete      削除された行。補充された行についても、実際その
                                バッファには存在していないことから、このグルー
                                プが適用される。

==============================================================================
3. 差分へ移動する                                       jumpto-diffs

差分へ移動するのに2つのコマンドを使える:
                                                                [c
        [c              前(上方)の変更の先頭へ移動する。
                        カウントが与えられた場合、その回数繰り返される。
                                                                ]c
        ]c              次(下方)の変更の先頭へ移動する。
                        カウントが与えられた場合、その回数繰り返される。

カーソルの動く方向に変更がなかった場合にはエラーになる。

==============================================================================
4. 差分を写す                   copy-diffs E99 E100 E101 E102 E103
                                                                merge

あるバッファから別のバッファへテキストを複写する2つのコマンドがある。結果的に
ある範囲について2つのバッファの内容は等しくなる。

                                                        :diffg :diffget
:[range]diffg[et] [bufspec]
                現在のバッファをもう1つのバッファと同じくなるように変更する。
                [bufspec] が与えられた時は、そのバッファが使用される。
                [bufspec] がカレントバッファである場合は何も起こらない。
                そうでなければ差分モードのバッファが他に1つしかない時にだけ動
                作する。
                [range]については以下を参照。

                                                :diffpu :diffput E793
:[range]diffpu[t] [bufspec]
                もう1つのバッファを現在のバッファと同じくなるように変更する。
                ":diffget" と同様だが現在のバッファではなく、もう一方のバッファ
                が変更を受ける。
                [bufspec] が省略され、かつ 'modifiable' がオンで差分モードにあ
                るバッファが2個以上あると、このコマンドは失敗する。
                [range]については以下を参照。

                                                        do
[count]do       範囲のない ":diffget" と同じ。"o" は "obtain" の意味 ("dgg" と
                区別できないので、"dg" は使えない)。Note: これはビジュアル
                モードでは機能しない。
                [count] を与えた場合、それは ":diffget" に対して [bufspec]引数
                として用いられる。

                                                        dp
[count]dp       範囲のない ":diffput" と同じ。Note: これはビジュアルモードでは
                機能しない。
                [count] を与えた場合、それは ":diffput" に対して [bufspec]引数
                として用いられる。


[range]が与えられない場合にはカーソルの位置かその上の差分が適用される。[range]
が使われた時にはその範囲だけを適用(put/get)しようと試みる。削除された場合には
必ずしも可能なわけではない。

バッファの最後の行のさらに下方に削除された行があることも考えられる。そのとき
カーソルが最終行にあり、最終行より上に差異がないとき、":diffget" と "do" コマ
ンドはそれらの行を取得する。

超えた位置の行をもう一方のバッファから取得するには、最終行+1の行番号を指定す
る。次のコマンドはもう一方のバッファから完全な差分情報を受け取る:

        :1,$+1diffget

削除された行は画面に表示こそされているが、テキストラインとしては数えられていな
いことに注意。消された範囲にカーソルを移動することはできない。もう一方のバッ
ファから、削除された行を ":diffget" で取得するには対象行の下方で行う必要があ
る。
                                                                E787
変更を受けるバッファが読み込み専用で、 FileChangedRO で引き起こされる自動コ
マンドがバッファを変更するとき、このコマンドは失敗する。
この自動コマンドはバッファを変更してはならない。

引数 [bufspec]にはバッファ番号、バッファ名のパターンもしくはバッファ名の一部を
使用できる。例:

        :diffget                差分モードにある別のバッファを使用する
        :diffget 3              3番のバッファを使用する。
        :diffget v2             差分モードにある "v2" にマッチするバッファを使
                                用する (例, "file.c.v2")

==============================================================================
5. 差分モードオプション                                 diff-options

'diffopt' と 'fillchars' の "diff" 項目も参照。

                                            diff-slow diff_translations
行がとても長いと diff 構文ハイライトが遅くなるかもしれない。その場合は特にたく
さんの異なったローカライゼーションをマッチしようと試みるからである。ローカライ
ゼーションを無効化して構文ハイライトを高速化するには、グローバル変数
g:diff_translations を 0 に設定する:

    let g:diff_translations = 0

この変数を設定した後、構文スクリプトを再読み込みする:

    set syntax=diff



差分を発見する                                 diff-diffexpr

'diffexpr' オプションは、2つのファイルを比較し差分を取得する内部diffサポートも
しくは標準的な "diff" プログラム以外の何かを利用する場合に設定する。 E959

'diffexpr' が空ならば、Vimはfile1とfile2間の差分を得るために次のコマンドを使用
する:

        diff file1 file2 > outfile

">" は 'shellredir' の値に置き換えられる。

"diff" の出力は通常の "ed" 形式の差分またはユニファイド差分でなければならない。
コンテキスト差分では動作しない。またユニファイド差分ではコンテキスト行なしが利
用できる。つまり "diff -u" では動作せず、"diff -U0" を使うこと。

この例は、Vimが "ed" 形式の差分に求めるフォーマットを示している:

        1a2
        > bbb
        4d4
        < 111
        7c7
        < GGG
        ---
        > ggg

項目 "1a2" が 行 "bbb" を追加する。
項目 "4d4" が 行 "111" を削除する。
項目 "7c7" が 行 "GGG" を 行 "ggg" で置き換える。

'diffexpr' が空でなければ、差分ファイルを述べた形式で取得するためにそれを評価
実行する。これらの変数がファイル名として設定される:

        v:fname_in              基準となるファイル
        v:fname_new             同ファイルの新バージョン
        v:fname_out             結果の差分ファイルを書き込む場所

その上、'diffexpr' は 'diffopt' オプションの "icase" と "iwhite" についても考
慮するべきだろう。'diffexpr' は 'lines' と 'columns' の値を変更できない。

引数なしでの関数呼び出しを使用する利点は高速なことである、
expr-option-function を参照のこと。

例 (これは 'diffexpr' が空の時とほぼ同じように働く):

        set diffexpr=MyDiff()
        function MyDiff()
           let opt = ""
           if &diffopt =~ "icase"
             let opt = opt .. "-i "
           endif
           if &diffopt =~ "iwhite"
             let opt = opt .. "-b "
           endif
           silent execute "!diff -a --binary " .. opt .. v:fname_in .. " " .. v:fname_new ..
                \  " > " .. v:fname_out
           redraw!
        endfunction

引数の "-a" は強制的にテキストファイルとして比較するために使われる。バイナリで
の比較は使いにくい。引数の "--binary" はファイルをバイナリモードで読み込むため
に使われる。DOSでCTRL-Zをテキストの終わりとしないためである。

シェルコマンドを実行し、ディスプレイに何かが表示されるかどうかによっては、
redraw! コマンドは必要ないかもしれない。

'diffexpr' の式が s: か <SID> で始まる場合、スクリプトID(local-function)
に置き換えられる。例:
                set diffexpr=s:MyDiffExpr()
                set diffexpr=<SID>SomeDiffExpr()
そうでない場合、式はオプションが設定されたスクリプトのコンテキストで評価され、
その結果スクリプトローカルな要素を使用できる。

                                                E810 E97
Vimは差分の出力結果が妥当であるか検証する。妥当でない場合、エラーメッセージを
得るだろう。起こりうるエラーは:
-  プログラム "diff" を実行できなかった。
-  プログラム "diff" が通常の "ed" スタイルの差分を出力しなかった(上記参照)。
-  'shell' と関連するオプションが正しく設定されていなかった。":!sort" のような
   フィルタリングコマンドが正しく動作するか確認すること。
-  'diffexpr' を使っているが動作しなかった。
エラーメッセージがよくわからないときは 'verbose' オプションを設定してより多く
のメッセージを見ることができる。

MS-Windows 用の Vim インストーラには diff プログラムが含まれている。もし diff
プログラムを持っていない場合はどこかから diff.exe をダウンロードすること。例え
ば次の場所から入手できる。
http://gnuwin32.sourceforge.net/packages/diffutils.htm.


パッチを使用する                               diff-patchexpr

'patchexpr' オプションは、標準的な "patch" プログラム以外の何かを利用する場合
に設定する。

'patchexpr' が空ならば、Vimは "patch" を次のように呼び出す:

        patch -o outfile origfile < patchfile

これはほとんどのバージョンの "patch" で正しく働くだろう。行中間のCRが、改行記
号として解釈され問題を起こすことはあるかもしれない。

デフォルトが正しく働かないのならば、同様の働きをする式を 'patchexpr' に設定す
る。これらの変数がファイル名として設定される。

        v:fname_in              基準となるファイル
        v:fname_diff            パッチファイル
        v:fname_out             パッチ適用結果を出力するファイル

引数なしでの関数呼び出しを使用する利点は高速なことである、
expr-option-function を参照のこと。

例 (これは 'patchexpr' を空にしたのと同じ働きをする):

        set patchexpr=MyPatch()
        function MyPatch()
           :call system("patch -o " .. v:fname_out .. " " .. v:fname_in ..
           \  " < " .. v:fname_diff)
        endfunction

利用する "patch" プログラムが望んでいない副作用をしていないことを確認する必要
がある。例えば消されるべき付加的なファイルが生成されていないか用心する必要があ
る。ファイルにパッチをあてる以上のことは何もすべきではない。
   Vimは 'patchexpr' を実行する前に "/tmp" か他の一時ディレクトリへ現在のディ
レクトリを移動する。これにはカレントディレクトリの別のファイルへ偶然にパッチが
あたってしまうのを避ける狙いがある。Vimはv:fname_inで始まり ".rej" や ".orig"
で終わる名前のファイルを消すこともする。

'patchexpr' の式が s: か <SID> で始まる場合、スクリプトID(local-function)
に置き換えられる。例:
                set patchexpr=s:MyPatchExpr()
                set patchexpr=<SID>SomePatchExpr()
そうでない場合、式はオプションが設定されたスクリプトのコンテキストで評価され、
その結果スクリプトローカルな要素を使用できる。


diff 関数の例                                  diff-func-examples

diff() 関数を使用して 2 つの文字列のリスト間の差分インデックスを計算する例を
以下に示す。

  " いくつかの行が変更された
  :echo diff(['abc', 'def', 'ghi'], ['abx', 'rrr', 'xhi'], {'output': 'indices'})
   [{'from_idx': 0, 'from_count': 3, 'to_idx': 0, 'to_count': 3}]

  " 冒頭に数行を追加した
  :echo diff(['ghi'], ['abc', 'def', 'ghi'], {'output': 'indices'})
   [{'from_idx': 0, 'from_count': 0, 'to_idx': 0, 'to_count': 2}]

  " 冒頭から数行を削除した
  :echo diff(['abc', 'def', 'ghi'], ['ghi'], {'output': 'indices'})
   [{'from_idx': 0, 'from_count': 2, 'to_idx': 0, 'to_count': 0}]

  " 途中に数行を追加した
  :echo diff(['abc', 'jkl'], ['abc', 'def', 'ghi', 'jkl'], {'output': 'indices'})
   [{'from_idx': 1, 'from_count': 0, 'to_idx': 1, 'to_count': 2}]

  " 途中の数行を削除した
  :echo diff(['abc', 'def', 'ghi', 'jkl'], ['abc', 'jkl'], {'output': 'indices'})
   [{'from_idx': 1, 'from_count': 2, 'to_idx': 1, 'to_count': 0}]

  " 末尾に数行を追加した
  :echo diff(['abc'], ['abc', 'def', 'ghi'], {'output': 'indices'})
   [{'from_idx': 1, 'from_count': 0, 'to_idx': 1, 'to_count': 2}]

  " 末尾から数行を削除した
  :echo diff(['abc', 'def', 'ghi'], ['abc'], {'output': 'indices'})
   [{'from_idx': 1, 'from_count': 2, 'to_idx': 1, 'to_count': 0}]

  " ばらばらな変更
  :echo diff(['ab', 'def', 'ghi', 'jkl'], ['abc', 'def', 'ghi', 'jk'], {'output': 'indices', 'context': 0})
   [{'from_idx': 0, 'from_count': 1, 'to_idx': 0, 'to_count': 1},
    {'from_idx': 3, 'from_count': 1, 'to_idx': 3, 'to_count': 1}]

  " コンテキスト長 1 のばらばらな変更
  :echo diff(['ab', 'def', 'ghi', 'jkl'], ['abc', 'def', 'ghi', 'jk'], {'output': 'indices', 'context': 1})
   [{'from_idx': 0, 'from_count': 4, 'to_idx': 0, 'to_count': 4}]



 vim:tw=78:ts=8:noet:ft=help:norl: