git-diff vs diff vs diff2html vs diff2html-cli
差分生成と可視化ツールの技術比較
git-diffdiffdiff2htmldiff2html-cli類似パッケージ:

差分生成と可視化ツールの技術比較

diffdiff2htmldiff2html-cligit-diff は、テキストの差分(diff)を扱うための JavaScript エコシステムを構成する主要なパッケージです。diff は文字列やオブジェクトの差分を計算するコアエンジンとして機能し、diff2html はその差分データを人間が読みやすい HTML 形式に変換して可視化します。diff2html-clidiff2html の機能を利用したコマンドラインツールであり、git-diff は Git の差分出力に特化したパッケージですが、維持状況や機能面で他の選択肢と比較する必要があります。これらはコードレビューツール、バージョン管理 UI、変更履歴の可視化など、フロントエンドおよびバックエンドのさまざまなシナリオで利用されます。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
git-diff364,85334-98年前ISC
diff09,121616 kB144日前BSD-3-Clause
diff2html03,3492.02 MB333ヶ月前MIT
diff2html-cli059258.9 kB162年前MIT

差分生成と可視化:diff vs diff2html vs diff2html-cli vs git-diff

JavaScript エコシステムにおいて、コードやテキストの「差分(diff)」を扱うことは、コードレビューツール、バージョン管理システム、設定ファイルの比較など、多くの重要なシナリオで必要となります。diffdiff2htmldiff2html-cligit-diff は、この課題に取り組むための代表的なパッケージですが、それぞれが解決する問題と役割が明確に異なります。

本稿では、これらのパッケージの技術的な違い、適切な使い分け、そして実装レベルの詳細を比較します。

🏗️ 役割とアーキテクチャ:計算 vs 描画 vs CLI

まず理解すべきは、これらが「差分の計算」と「差分の描画」という異なる工程を担当している点です。

diff は、2 つのテキストやオブジェクトの間の差異を「計算」するエンジンです。

  • 文字列、行、単語、配列、オブジェクトなど、さまざまな粒度で比較可能です。
  • 結果は JSON 形式や Unified Diff 形式などで出力されます。
  • 可視化機能は含まれていません。
// diff: 差分の計算
import * as Diff from 'diff';

const text1 = "Hello World";
const text2 = "Hello JavaScript";

const changes = Diff.diffWords(text1, text2);
// 結果:[{ value: 'Hello ' }, { value: 'World', removed: true }, { value: 'JavaScript', added: true }]

diff2html は、既存の差分データ(Unified Diff 形式など)を受け取り、HTML として「描画」します。

  • diff パッケージや git diff コマンドの出力を入力として想定しています。
  • 行の追加・削除を色分けし、シンタックスハイライトを適用した HTML を生成します。
  • ブラウザ上での表示に最適化されています。
// diff2html: 差分の描画
import { Diff2Html } from 'diff2html';

const diffInput = `
diff --git a/file.js b/file.js
index 123..456 100644
--- a/file.js
+++ b/file.js
@@ -1 +1 @@
-console.log('old')
+console.log('new')
`;

const html = Diff2Html.html(diffInput, {
  drawFileList: true,
  matching: 'lines',
  outputFormat: 'line-by-line'
});
// 結果:HTML 文字列(テーブル形式の差分ビュー)

diff2html-cli は、diff2html の機能をターミナルから利用できるようにしたツールです。

  • Node.js ライブラリとして組み込むのではなく、コマンドとして実行します。
  • CI/CD でプルリクエストの差分を HTML レポートとして保存するなどに使われます。
# diff2html-cli: コマンドラインでの利用
# Git の差分をパイプして HTML ファイルを生成
 git diff | npx diff2html-cli -i stdin -o diff-report.html

git-diff は、Git の差分出力を扱うことに特化しているとされますが、npm 上の同名パッケージは維持状況に注意が必要です。

  • 多くの場合、child_processgit diff を実行するラッパーか、差分文字列のパースを試みるものです。
  • 機能面で diff パッケージの Unified Diff パース機能と重複することが多く、依存を増やすだけのリスクがあります。
// git-diff: 例(パッケージによる実装差異大)
// 注意:多くの git-diff パッケージは更新が停止している可能性があります
import gitDiff from 'git-diff';

// 実装はパッケージに依存しますが、基本的には git コマンドのラッパーです
// const result = await gitDiff({ cwd: './repo' }); 

🛠️ 差分の計算方法:粒度と精度

差分を「どのように」計算するかは、ユースケースによって重要です。diff パッケージはこの点で最も柔軟です。

diff は、比較の粒度を明示的に選べます。

  • diffChars: 文字単位(スペルチェックなど)
  • diffWords: 単語単位(文章の比較)
  • diffLines: 行単位(コードの比較)
  • diffJson: JSON オブジェクトの構造比較
import * as Diff from 'diff';

// 行単位での比較(コードレビュー向け)
const lineDiff = Diff.diffLines(
  "line1\nline2\nline3",
  "line1\nline2_new\nline3"
);

lineDiff.forEach(part => {
  const color = part.added ? 'green' : part.removed ? 'red' : 'grey';
  // process colored part
});

// JSON 構造の比較
const jsonDiff = Diff.diffJson(
  { a: 1, b: 2 },
  { a: 1, b: 3 }
);

diff2html は計算を行いません。

  • 入力として「既に計算された差分文字列」を必要とします。
  • 計算ロジックを持たないため、入力データの形式(Unified Diff など)が正確である必要があります。
// diff2html: 計算は行わず、入力された diff 文字列をレンダリングするのみ
// 誤った形式の diff 文字列を入れると正しく表示されません
const html = Diff2Html.html(validUnifiedDiffString);

git-diff は、Git のアルゴリズムに依存します。

  • パッケージによっては内部で Git コマンドを呼び出すため、環境に Git がインストールされている必要があります。
  • 純粋な JavaScript 環境(ブラウザなど)では動作しないことが多いです。
// git-diff: 環境依存性が高い
// ブラウザでは通常動作せず、Node.js 環境かつ Git が必要
// const diff = require('git-diff');
// diff(oldPath, newPath); // 内部で git diff コマンドを実行

🎨 可視化とカスタマイズ:HTML 生成の柔軟性

差分をユーザーに見せる際、どのように表示するかは UX に直結します。

diff2html は豊富な設定オプションを提供します。

  • outputFormat: 行ごと(line-by-line)か、単語ごと(word-by-word)か。
  • matching: 行のマッチングアルゴリズム(lines, words, none)。
  • drawFileList: ファイル一覧を表示するかどうか。
  • CSS クラスを介したスタイリングが可能ですが、CSS ファイルの別途読み込みが必要です。
// diff2html: 詳細な設定が可能
const html = Diff2Html.html(diffString, {
  drawFileList: true,
  matching: 'words',
  outputFormat: 'word-by-word',
  renderNothingWhenEmpty: false
});

// 生成された HTML には特定のクラスが付与されるため、CSS で装飾可能
// .d2h-file-wrapper, .d2h-file-header など

diff は可視化機能を持ちません。

  • 自分で HTML を生成するか、diff2html などに渡す必要があります。
  • 配列形式で差分が返ってくるため、独自の UI を構築する場合はこちらから始めるのが適切です。
// diff: 独自のレンダリングロジックを実装する起点
const changes = Diff.diffLines(oldText, newText);

const html = changes.map(part => {
  const className = part.added ? 'added' : part.removed ? 'removed' : 'normal';
  return `<span class="${className}">${part.value}</span>`;
}).join('');

diff2html-cli は設定をコマンドライン引数で行います。

  • スクリプト内での微調整は難しいですが、CI 設定などでは便利です。
# diff2html-cli: 引数で設定を渡す
npx diff2html-cli -i stdin -o report.html --matching words --output-format word-by-word

git-diff は可視化機能を含みません。

  • 出力は通常テキスト形式であり、HTML 化には追加のツールが必要です。

⚠️ 維持状況とリスク:git-diff の注意点

パッケージ選定において、維持状況(Maintenance Status)は重要な要素です。

diffdiff2html は、それぞれ分野において事実上の標準(De-facto Standard)として長く維持されています。

  • diffkpdecker によって管理され、多くの主要ライブラリで依存されています。
  • diff2htmlrtfpessoa によって管理され、GitHub のプルリクエスト表示などでも類似の技術が使われています。
  • 定期的に更新され、セキュリティパッチや機能追加が行われています。

diff2html-clidiff2html に追随して更新されます。

  • 本体のライブラリが安定しているため、CLI ツールも信頼できます。

git-diff は注意が必要です。

  • npm には同名のパッケージが複数存在し、維持されていないもの(Last publish が数年前など)が含まれています。
  • 機能面でも diff パッケージの parseUnifiedDiff などで代用可能なことが多く、あえてリスクを取るメリットが薄いです。
  • 新しいプロジェクトでは、diff + diff2html の組み合わせ、または Git コマンドの直接実行を推奨します。
// 推奨されないパターン(git-diff パッケージへの依存)
// import gitDiff from 'git-diff'; // 維持されていない可能性あり

// 推奨パターン(diff パッケージの利用)
import * as Diff from 'diff';
const parsed = Diff.parsePatch(gitDiffOutputString);

🌐 実装シナリオ:組み合わせて使うのが正解

実際の開発では、これらを単独で使うよりも、組み合わせて使うことがほとんどです。

シナリオ 1: Web アプリ内のコード比較機能

ユーザーが 2 つのテキストを入力し、差分を表示したい場合。

  1. 計算: diff パッケージで差分を計算。
  2. 変換: 計算結果を Unified Diff 形式の文字列に変換(diff が提供する機能、または自作)。
  3. 描画: diff2html で HTML 生成。
import * as Diff from 'diff';
import { Diff2Html } from 'diff2html';

function compareAndRender(oldText, newText) {
  // 1. 差分計算
  const changes = Diff.diffLines(oldText, newText);
  
  // 2. Unified Diff 形式へ変換(簡易例)
  // 実際は Diff.createTwoFilesPatch などの使用を推奨
  const diffString = Diff.createTwoFilesPatch(
    'old.txt', 'new.txt', oldText, newText
  );

  // 3. HTML 描画
  return Diff2Html.html(diffString, {
    outputFormat: 'line-by-line'
  });
}

シナリオ 2: CI/CD での差分レポート

プルリクエスト作成時に、変更内容を HTML レポートとしてアーティファクトに残したい場合。

  1. 取得: git diff コマンドで差分テキストを取得。
  2. 変換: diff2html-cli で HTML ファイルを生成。
# CI スクリプト例
git diff origin/main...HEAD > changes.diff
npx diff2html-cli -i changes.diff -o report.html
# report.html をアーティファクトとしてアップロード

シナリオ 3: 独自の差分 UI を構築したい場合

diff2html のデザインが合わない場合。

  1. 計算: diff パッケージで差分配列を取得。
  2. 描画: React や Vue のコンポーネントで配列をマップして描画。
// React 例
import * as Diff from 'diff';

function DiffView({ old, new: newText }) {
  const diffs = Diff.diffLines(old, newText);

  return (
    <div className="diff-container">
      {diffs.map((part, index) => {
        const color = part.added ? '#e6ffec' : part.removed ? '#ffebe9' : 'white';
        return (
          <div key={index} style={{ backgroundColor: color }}>
            {part.value}
          </div>
        );
      })}
    </div>
  );
}

📊 比較サマリー

特徴diffdiff2htmldiff2html-cligit-diff
主な役割差分の計算差分の HTML 描画CLI での描画Git 差分ラッパー
入力文字列、オブジェクトUnified Diff 文字列Diff 文字列、ファイルGit リポジトリ、パス
出力差分配列、Patch 文字列HTML 文字列、JSONHTML ファイル差分文字列
UI 機能なし豊富(シンタックス HL 等)豊富なし
維持状況✅ 活発✅ 活発✅ 活発⚠️ 不明確(要確認)
推奨度⭐⭐⭐⭐⭐ (計算)⭐⭐⭐⭐⭐ (描画)⭐⭐⭐⭐ (CI 用)⭐ (非推奨)

💡 結論:正しいツールの組み合わせ

差分処理を要件とする場合、単一の「万能パッケージ」を探すのではなく、工程ごとに最適なツールを選ぶのが賢明です。

  1. 差分計算には diff を使う
    • 信頼性が高く、粒度の制御も可能です。Git の差分パースにも使えます。
  2. Web 表示には diff2html を使う
    • 美しく、機能豊富な HTML 出力がすぐに得られます。CSS の読み込みを忘れないようにしてください。
  3. CLI 利用には diff2html-cli を使う
    • スクリプト作成の手間を省けます。
  4. git-diff パッケージは避ける
    • 維持状況のリスクと、機能の重複を避けるため、diff パッケージまたは Git コマンドの直接実行を選択してください。

この構成が、長期的なメンテナンス性と機能性のバランスにおいて最も優れています。

選び方: git-diff vs diff vs diff2html vs diff2html-cli

  • git-diff:

    Git の差分出力に特化したパッケージですが、維持状況が不明確な場合が多く、新しいプロジェクトでの使用は推奨されません。代わりに、diff パッケージの unified diff パース機能を使用するか、child_process で Git コマンドを実行し、その出力を diff2html でレンダリングする構成が堅牢です。

  • diff:

    アプリケーション内でプログラム的に差分を計算する必要がある場合は diff を選択してください。これは最も標準的で維持されているライブラリであり、行単位、文字単位、単語単位など、細かな粒度での比較が可能です。Git の差分出力をパースする際にも、このライブラリの統一差分(unified diff)パーサーが信頼できます。

  • diff2html:

    計算された差分データを Web ブラウザ上で美しく表示する必要がある場合は diff2html を選択してください。Git の diff 出力や diff パッケージの出力を受け取り、シンタックスハイライト付きの HTML を生成します。React や Vue などのコンポーネント内で diff を可視化する場合の事実上の標準です。

  • diff2html-cli:

    CI/CD パイプラインやローカルのスクリプトから、コマンドラインで直接 HTML 形式の差分レポートを生成したい場合に選択してください。diff2html ライブラリをラップしており、Node.js のコードを書かずに素早くレポートを作成できます。

git-diff のREADME

git-diff


Linux Build Windows Build Coverage Status
npm Dependencies Status npm node

Returns the git diff of two strings


Introduction

git-diff will use git (if installed) and printf (if available) to get the real git diff of two strings, viz the actual diff output produced by git itself.

As a fallback, if either command is unavailable, git-diff will instead use the diff module to produce a very good fake git diff.

If desired, you may then console.log the returned git diff. An example of actual output:

Line diff v Word diff


Usage

npm install --save git-diff

git-diff takes 3 arguments, the old string to diff, the new string to diff and optionally an options object

git-diff returns the git difference or undefined where there is no difference.

String diff example usage:

var gitDiff = require('git-diff')
var oldStr = 'fred\nis\nfunny\n'
var newStr = 'paul\nis\nfunny\n'
var diff = gitDiff(oldStr, newStr)

var assert = require('assert')
assert.equal(diff, '@@ -1,3 +1,3 @@\n-fred\n+paul\n is\n funny\n')

File diff example usage:

var gitDiff = require('git-diff')
var readFileGo = require('readfile-go') // or your preferred file reader
var oldStr = readFileGo(__dirname + '/oldStr.txt')
var newStr = readFileGo(__dirname + '/newStr.txt')
var diff = gitDiff(oldStr, newStr)

Options object

Available options are:

color | flags | forceFake | noHeaders | save | wordDiff

Default options are:

var options = {
  color: false,      // Add color to the git diff returned?
  flags: null,       // A space separated string of git diff flags from https://git-scm.com/docs/git-diff#_options
  forceFake: false,  // Do not try and get a real git diff, just get me a fake? Faster but may not be 100% accurate
  noHeaders: false,  // Remove the ugly @@ -1,3 +1,3 @@ header?
  save: false,       // Remember the options for next time?
  wordDiff: false    // Get a word diff instead of a line diff?
}

Further assistance is given below for options that are not self explanatory.


flags (string | null) top

The flags option allows you to use any git diff flags

This only applies to real git diffs and will not effect the output if it is fake.

An example to illustrate:

var gitDiff = require('git-diff')
var oldStr = 'fred\n   is   \nfunny\n'
var newStr = 'paul\nis\n   funny   \n'
var diff = gitDiff(oldStr, newStr, {flags: '--diff-algorithm=minimal --ignore-all-space'})

var assert = require('assert')
assert.equal(diff, '@@ -1,3 +1,3 @@\n-fred\n+paul\n is\n    funny   \n')

Here, the use of --ignore-all-space prevents a difference being reported on the 2nd and 3rd lines.


forceFake (boolean) top

git-diff will initially attempt to use git and printf to get the real git diff.

If it cannot, it instead returns a very good fake git diff.

A fake git diff is faster to produce but may not be 100% representative of a real git diff.

The flags option is ignored when faking and fake diffs never have a header.

However, if a fake is good enough and speed is of the essence then you may want to force a fake git diff.

The forceFake option allows you to do exactly that:

var gitDiff = require('git-diff')
var oldStr = 'fred\nis\nfunny\n'
var newStr = 'paul\nis\nfunny\n'
var diff = gitDiff(oldStr, newStr, {forceFake: true})

var assert = require('assert')
assert.equal(diff, '-fred\n+paul\n is\n funny\n')

save (boolean) top

Its annoying to keep passing the same options every time.

git-diff, if instructed to do so, will remember previously used options for you.

When the {save: true} option is used in a call to git-diff subsequent calls remember the options.

var gitDiff = require('git-diff')
var oldStr = 'fred\nis\nfunny\n'
var newStr = 'paul\nis\nfunny\n'
var diff1 = gitDiff(oldStr, newStr, {save: true, wordDiff: true})
var diff2 = gitDiff(oldStr, newStr)

var assert = require('assert')
assert.equal(diff1, '@@ -1,3 +1,3 @@\n[-fred-]{+paul+}\nis\nfunny\n')
assert.equal(diff2, '@@ -1,3 +1,3 @@\n[-fred-]{+paul+}\nis\nfunny\n')

Here, the second call remembers that the wordDiff option is on. {wordDiff: true} is now the default.


Async execution

git-diff offers a promise based async solution:

var gitDiff = require('git-diff/async')
var oldStr = 'fred\nis\nfunny\n'
var newStr = 'paul\nis\nfunny\n'
gitDiff(oldStr, newStr).then(function(diff) {
  var assert = require('assert')
  assert.equal(diff, '@@ -1,3 +1,3 @@\n-fred\n+paul\n is\n funny\n')
})

FAQs

How good is the fake git diff?
The diff module used for the fake diff does not use the same difference algorithm as git. As such, a line diff is likely to be identical to a git line diff whereas a word diff will have some variance.

How can I tell whether the returned git diff is real or fake?
If the @@ -1,3 +1,3 @@ header is present then the returned git diff is real.
If the header is absent then either the noHeaders option is on or the returned git diff is fake.

Will my environment produce a real or fake git diff?
Linux and mac have the coveted printf command available. On Windows git bash makes printf accessible.
Assuming that git is installed, any of these environments will produce a real git diff.


Author says

What's the difference between how God treats the righteous and the wicked?

And God saw that the light was good. And God separated the light from the darkness. Genesis 1:4 ESV

And He will do it again:

Let both grow together until the harvest, and at harvest time I will tell the reapers, “Gather the weeds first and bind them in bundles to be burned, but gather the wheat into my barn.” Matthew 13:30 ESV

Much love :D