<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
  <title>github.com/ivankovic</title>
  <link>https://aggr.aymericbeaumet.com/sources/github.com/ivankovic/</link>
  <description>Browse aymeric&#39;s, an independent, searchable archive of readable snapshots from followed feeds, preserved in Git with aggr.</description>
  <language>en</language>
  <atom:link href="https://aggr.aymericbeaumet.com/sources/github.com/ivankovic/rss.xml" rel="self" type="application/rss+xml"/>
  <lastBuildDate>Sun, 1 Nov 2026 13:02:30 +0000</lastBuildDate>
  <generator>aggr</generator>
  <item>
    <title>Show HN: OmniDiff – Opensource, super fast, content aware Git difftool, in Rust</title>
    <link>https://aggr.aymericbeaumet.com/items/hnrss-org/2026-11-01-show-hn-omnidiff-opensource-super-fast-content-aware-git/</link>
    <guid isPermaLink="false">urn:aggr:item:f41eb6a42b24ecf82b05eac1c237168a39140174</guid>
    <atom:link href="https://github.com/ivankovic/omnidiff" rel="via" type="text/html"/>
    <pubDate>Sun, 1 Nov 2026 13:02:30 +0000</pubDate>
    <dc:creator>FabCH</dc:creator>
    <description>I have been researching what the ultimate diff tool would look like, based on actual diffs in open source repositories. OmniDiff can diff: code (25 languages), config, images, PDFs, archives, cursors, fonts... It&#39;s fast. It can diff C++…</description>
    <content:encoded>&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff/actions/workflows/ci.yml&quot;&gt;&lt;picture class=&quot;article-picture article-badge&quot; style=&quot;--image-width:160px;--image-ratio:160 / 24&quot;&gt;&lt;img src=&quot;https://github.com/ivankovic/omnidiff/actions/workflows/ci.yml/badge.svg&quot; width=&quot;160&quot; height=&quot;24&quot; alt=&quot;CI&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff/releases/latest&quot;&gt;&lt;picture class=&quot;article-picture article-badge&quot; style=&quot;--image-width:160px;--image-ratio:160 / 24&quot;&gt;&lt;img src=&quot;https://camo.githubusercontent.com/7dff2891d356c84988ab8521a838f45366fa2b940fa83d936078fbad04068fcf/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f6976616e6b6f7669632f6f6d6e6964696666&quot; width=&quot;160&quot; height=&quot;24&quot; alt=&quot;Latest release&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/omnidiff&quot;&gt;&lt;picture class=&quot;article-picture article-badge&quot; style=&quot;--image-width:160px;--image-ratio:160 / 24&quot;&gt;&lt;img src=&quot;https://camo.githubusercontent.com/eada782cef76c65fd5848e3725786cf02e3ca77e61f355eeda8e69b0ba622426/68747470733a2f2f646f63732e72732f6f6d6e69646966662f62616467652e737667&quot; width=&quot;160&quot; height=&quot;24&quot; alt=&quot;docs.rs&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff/blob/main/CONTRIBUTING.md#coverage&quot;&gt;&lt;picture class=&quot;article-picture article-badge&quot; style=&quot;--image-width:160px;--image-ratio:160 / 24&quot;&gt;&lt;img src=&quot;https://camo.githubusercontent.com/b17a2577a7e5ce004a0707ba4214d58793889d5b16a2959168c7823920864a97/68747470733a2f2f696d672e736869656c64732e696f2f656e64706f696e743f75726c3d68747470733a2f2f7261772e67697468756275736572636f6e74656e742e636f6d2f6976616e6b6f7669632f6f6d6e69646966662f6d61696e2f72657365617263682f646174612f636f7665726167652f62616467652e6a736f6e&quot; width=&quot;160&quot; height=&quot;24&quot; alt=&quot;coverage&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff/blob/main/LICENSE&quot;&gt;&lt;picture class=&quot;article-picture article-badge&quot; style=&quot;--image-width:160px;--image-ratio:160 / 24&quot;&gt;&lt;img src=&quot;https://camo.githubusercontent.com/004694064871acd5881966289c5d91c0ac05dd35d052d9d99ae0957766bf537d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4147504c2d2d332e302d2d6f6e6c792d626c7565&quot; width=&quot;160&quot; height=&quot;24&quot; alt=&quot;License: AGPL v3&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Fast, robust, accurate content-aware diffing.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Robust:&lt;/strong&gt; can diff 99.95% of code changes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content-aware:&lt;/strong&gt; 98.8% of changes diffed using content-specific algorithms&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;th&gt;Fast: under 100ms&lt;/th&gt;
&lt;th&gt;Accurate: perfect&lt;/th&gt;
&lt;th&gt;Accurate: near-perfect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Code&lt;/td&gt;
&lt;td&gt;98%&lt;/td&gt;
&lt;td&gt;70%&lt;/td&gt;
&lt;td&gt;92%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pictures&lt;/td&gt;
&lt;td&gt;96%&lt;/td&gt;
&lt;td&gt;69%&lt;/td&gt;
&lt;td&gt;91%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursors&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;85%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fonts&lt;/td&gt;
&lt;td&gt;95%&lt;/td&gt;
&lt;td&gt;82%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Archives&lt;/td&gt;
&lt;td&gt;88%&lt;/td&gt;
&lt;td&gt;87%&lt;/td&gt;
&lt;td&gt;87%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message catalogs&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;83%&lt;/td&gt;
&lt;td&gt;83%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDFs&lt;/td&gt;
&lt;td&gt;57%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Speed measured on a T14s, Intel Core i7-1370P with 32 GB RAM (see &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff#fast&quot;&gt;Fast&lt;/a&gt;). PDFs have fewer than ten judged test cases (see &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff#accurate&quot;&gt;Accurate&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://ivankovic.github.io/omnidiff/showcase/&quot;&gt;See it in the browser&lt;/a&gt;&lt;/strong&gt;: twenty real code changes, recorded from the command-line tool and compared side by side in Unix &lt;code&gt;diff&lt;/code&gt; and in OmniDiff, and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://ivankovic.github.io/omnidiff/showcase/content.html&quot;&gt;content examples&lt;/a&gt;: an OpenDocument file, a compiled translation catalog, and pictures, still and animated.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff/blob/main/assets/diff-vs-omnidiff.gif&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://github.com/ivankovic/omnidiff/raw/main/assets/diff-vs-omnidiff.gif&quot; alt=&quot;An animation of one Python refactoring painted two ways. A vertical bar sweeps left to right and back across a two-pane diff. On one side of the bar, GNU diff marks whole lines as deleted and inserted; on the other, OmniDiff paints only the parts that changed - sum(numbers) and len(numbers) rather than the whole assignment, and numbers shown as moved rather than rewritten.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Various light and dark themes are available:&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff/blob/main/assets/readme-screenshot.png&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://github.com/ivankovic/omnidiff/raw/main/assets/readme-screenshot.png&quot; alt=&quot;A screenshot of OmniDiff&amp;#39;s two-panel terminal UI in a light theme, showing the same Python refactoring, with the changed right-hand sides highlighted rather than whole lines&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Files are diffed based on type. A PDF diagram with one box renamed, and a web font with twenty icons redrawn:&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff/blob/main/assets/content/pdf-x-governikus-ausweisapp-522d8b0b-communicationmodel_en.gif&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://github.com/ivankovic/omnidiff/raw/main/assets/content/pdf-x-governikus-ausweisapp-522d8b0b-communicationmodel_en.gif&quot; alt=&quot;An animation in three parts. First, git diff on a PDF prints only &amp;quot;Binary files a/CommunicationModel_en.pdf and b/CommunicationModel_en.pdf differ&amp;quot;. Then OmniDiff, as git&amp;#39;s external diff, reports that the metadata and page 1 changed and where. Then OmniDiff&amp;#39;s viewer lists the changed members and shows page 1 before and after side by side, the renamed box outlined on both, and then the difference alone.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff/blob/main/assets/content/woff2-x-fortawesome-font-awesome-b476ed9a-fa-solid-900.gif&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://github.com/ivankovic/omnidiff/raw/main/assets/content/woff2-x-fortawesome-font-awesome-b476ed9a-fa-solid-900.gif&quot; alt=&quot;An animation of a Font Awesome web font. git diff prints only &amp;quot;Binary files differ&amp;quot;; OmniDiff reports 21 changed members and lists each changed glyph; its viewer shows the font&amp;#39;s name table with the version and style changed, one glyph before and after, and then all twenty changed icons in a grid, each change outlined.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;h2&gt;From source&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;cargo install --locked omnidiff
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command builds OmniDiff from source, with the dependency versions it was tested with. You need a C compiler on &lt;code&gt;PATH&lt;/code&gt; and a Rust toolchain, rustc 1.92 or later. The build compiles every tree-sitter grammar from C. The first &lt;code&gt;cargo install&lt;/code&gt; takes a few minutes, because of this and the &lt;code&gt;lto = &amp;quot;fat&amp;quot;&lt;/code&gt; release profile.&lt;/p&gt;
&lt;h2&gt;Prebuilt binaries&lt;/h2&gt;
&lt;p&gt;Prebuilt binaries for Linux, macOS (Intel and Apple Silicon), and Windows are attached to every &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff/releases/latest&quot;&gt;GitHub release&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Homebrew&lt;/h2&gt;
&lt;p&gt;macOS and Linux:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;brew install ivankovic/omnidiff/omnidiff
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Debian and Ubuntu&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;.deb&lt;/code&gt; packages for amd64 and arm64 are available in a signed apt repository:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;sudo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; install&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;d&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;m&lt;/span&gt; 0755 /etc/apt/keyrings&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;curl&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;fsSL&lt;/span&gt; https://ivankovic.github.io/omnidiff/apt/omnidiff-archive-keyring.gpg &lt;span class=&quot;syntax-punctuation syntax-separator syntax-continuation syntax-line syntax-shell&quot;&gt;\
&lt;/span&gt;&lt;/span&gt;  &lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-pipe syntax-shell&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;sudo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; tee /etc/apt/keyrings/omnidiff-archive-keyring.gpg &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-echo syntax-shell&quot;&gt;echo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;deb [signed-by=/etc/apt/keyrings/omnidiff-archive-keyring.gpg] &lt;span class=&quot;syntax-constant syntax-character syntax-escape syntax-shell&quot;&gt;\
&lt;/span&gt;https://ivankovic.github.io/omnidiff/apt stable main&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-punctuation syntax-separator syntax-continuation syntax-line syntax-shell&quot;&gt;\
&lt;/span&gt;&lt;/span&gt;  &lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-pipe syntax-shell&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;sudo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; tee /etc/apt/sources.list.d/omnidiff.list &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;sudo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; apt update&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-and syntax-shell&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;sudo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; apt install omnidiff&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Nix and NixOS&lt;/h2&gt;
&lt;p&gt;On NixOS, or anywhere with Nix installed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;nix run github:ivankovic/omnidiff
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If flakes are not enabled in your Nix configuration, add &lt;code&gt;--extra-experimental-features &#39;nix-command flakes&#39;&lt;/code&gt; after &lt;code&gt;nix&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Arch and Gentoo&lt;/h2&gt;
&lt;p&gt;Recipes for Arch and Gentoo live in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff/blob/main/packaging&quot;&gt;&lt;code&gt;packaging/&lt;/code&gt;&lt;/a&gt;: the PKGBUILD builds locally with &lt;code&gt;makepkg -si&lt;/code&gt;, and the Gentoo ebuild is ready for an overlay. The two distros are overwhelmed and their repositories open and close to new packages sporadically.&lt;/p&gt;
&lt;h2&gt;Editor integration&lt;/h2&gt;
&lt;p&gt;VS Code does not yet let an extension replace its built-in diff view. The API is implemented upstream but unreleased; the extension will adopt it when it ships.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;VS Code&lt;/strong&gt; - &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff-vscode&quot;&gt;omnidiff-vscode&lt;/a&gt;. Search for &lt;strong&gt;OmniDiff&lt;/strong&gt; in the Extensions view, or &lt;code&gt;code --install-extension ivankovic.omnidiff&lt;/code&gt;. Also on &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://open-vsx.org/extension/ivankovic/omnidiff&quot;&gt;Open VSX&lt;/a&gt; for VSCodium, Cursor and Windsurf.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Neovim&lt;/strong&gt; - &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff.nvim&quot;&gt;omnidiff.nvim&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Using OmniDiff&lt;/h2&gt;
&lt;h2&gt;The interactive TUI&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;omnidiff                  # open an empty viewer; press o to pick each file
omnidiff BEFORE AFTER     # open directly into the diff of two files
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Press &lt;code&gt;?&lt;/code&gt; in the viewer for the full list of keybindings.&lt;/p&gt;
&lt;p&gt;The TUI uses 24-bit color when the terminal advertises it with &lt;code&gt;COLORTERM=truecolor&lt;/code&gt;, and the nearest 256 colors otherwise (macOS Terminal.app, or most terminals over ssh, which does not forward &lt;code&gt;COLORTERM&lt;/code&gt;). If your terminal supports 24-bit color but does not advertise it, run &lt;code&gt;export COLORTERM=truecolor&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;In a browser&lt;/h2&gt;
&lt;p&gt;For reviewing changes in a browser, see &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/codereview&quot;&gt;codereview&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note: codereview is at v0.0.0; use it at your own risk.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Headless / batch mode&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;omnidiff --headless BEFORE AFTER&lt;/code&gt;, or its synonym &lt;code&gt;--batch&lt;/code&gt;, prints the diff as plain text, with optional color, instead of opening the TUI. Use this for scripts or CI. Headless mode also starts automatically whenever stdout is not a terminal, for example when piped into &lt;code&gt;less&lt;/code&gt; or redirected to a file. Because of this, &lt;code&gt;omnidiff BEFORE AFTER | less&lt;/code&gt; works without the flag.&lt;/p&gt;
&lt;p&gt;OmniDiff collapses long runs of unchanged lines. It keeps 3 lines of context on each side of a change (override with &lt;code&gt;--context N&lt;/code&gt;), the same convention as &lt;code&gt;diff -u&lt;/code&gt;. OmniDiff also prefixes each hunk with the nearest enclosing function, class, or struct line, when that line is not otherwise visible. This shows the location of a change deep inside a large file.&lt;/p&gt;
&lt;p&gt;Colors are on by default; pass &lt;code&gt;--color never&lt;/code&gt;, or set &lt;code&gt;NO_COLOR=1&lt;/code&gt;, to disable ANSI colors, for example when you redirect output to a file - &lt;code&gt;--color always&lt;/code&gt; forces them even under &lt;code&gt;NO_COLOR&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;OmniDiff exits &lt;code&gt;0&lt;/code&gt; on success and &lt;code&gt;2&lt;/code&gt; on error. For scripting, pass &lt;code&gt;--exit-code&lt;/code&gt; to additionally get &lt;code&gt;1&lt;/code&gt; when the files differ. This follows the &lt;code&gt;diff(1)&lt;/code&gt; convention. &lt;code&gt;--exit-code&lt;/code&gt; is opt-in rather than the default for the same reason &lt;code&gt;git diff&lt;/code&gt; exits &lt;code&gt;0&lt;/code&gt; even when files differ: OmniDiff&#39;s usual non-interactive callers are version control systems driving it as a display tool, and they read a non-zero exit as &amp;quot;the tool failed&amp;quot; - &lt;code&gt;jj&lt;/code&gt; warns on every file, and &lt;code&gt;git difftool&lt;/code&gt; with &lt;code&gt;difftool.trustExitCode=true&lt;/code&gt; aborts the whole diff. (The 7-argument &lt;code&gt;GIT_EXTERNAL_DIFF&lt;/code&gt; form stays at &lt;code&gt;0&lt;/code&gt; even with &lt;code&gt;--exit-code&lt;/code&gt;, since git treats a non-zero exit there as fatal.)&lt;/p&gt;
&lt;h2&gt;JSON output&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;omnidiff --mode json BEFORE AFTER&lt;/code&gt; prints the diff as one JSON object. Its fields are described in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff/blob/main/src/tui/json_output.schema.json&quot;&gt;&lt;code&gt;src/tui/json_output.schema.json&lt;/code&gt;&lt;/a&gt;, a JSON Schema.&lt;/p&gt;
&lt;h2&gt;Git integration&lt;/h2&gt;
&lt;p&gt;OmniDiff is a &lt;code&gt;git difftool&lt;/code&gt; backend. Run the interactive setup wizard, which asks whether to configure it globally or for the current repository only:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;omnidiff git configure
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configure by hand&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;git config difftool.omnidiff.cmd &amp;#39;omnidiff &amp;quot;$LOCAL&amp;quot; &amp;quot;$REMOTE&amp;quot;&amp;#39;
git difftool --tool=omnidiff
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run &lt;code&gt;git config diff.tool omnidiff&lt;/code&gt; to make plain &lt;code&gt;git difftool&lt;/code&gt; use OmniDiff by default, without needing &lt;code&gt;--tool&lt;/code&gt;. If you do not want git to ask &amp;quot;view diff ... [Y/n]?&amp;quot; before every file, run &lt;code&gt;git config difftool.prompt false&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;git difftool&lt;/code&gt; opens the interactive TUI. &lt;code&gt;git diff&lt;/code&gt; and &lt;code&gt;git log -p&lt;/code&gt; never do.&lt;/strong&gt; &lt;code&gt;git diff&lt;/code&gt; pipes its output through git&#39;s pager, and a full-screen TUI cannot draw onto a pipe, so OmniDiff always falls back to plain text there regardless of terminal or &lt;code&gt;GIT_EXTERNAL_DIFF&lt;/code&gt; config (see &amp;quot;Headless / batch mode&amp;quot; above). If you want the interactive viewer from git, use &lt;code&gt;git difftool&lt;/code&gt;, not &lt;code&gt;git diff&lt;/code&gt;. If &lt;code&gt;git difftool&lt;/code&gt; still doesn&#39;t open interactively over SSH, reconnect with &lt;code&gt;ssh -t&lt;/code&gt; — the session needs an allocated pseudo-terminal; tmux panes always have one.&lt;/p&gt;
&lt;p&gt;OmniDiff also works directly with &lt;code&gt;git diff&lt;/code&gt; and &lt;code&gt;git log -p&lt;/code&gt;, through &lt;code&gt;GIT_EXTERNAL_DIFF&lt;/code&gt;. This path needs no &lt;code&gt;difftool&lt;/code&gt; config:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;GIT_EXTERNAL_DIFF=omnidiff git diff
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pictures, archives, fonts and the rest of the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff#supported-content&quot;&gt;supported content&lt;/a&gt; get a short report of what changed in them. Any other binary file gets a one-line &lt;code&gt;Binary file &amp;lt;path&amp;gt; differs&lt;/code&gt; notice instead of a diff, the same stand-in git and &lt;code&gt;diff(1)&lt;/code&gt; print for them. Neither ever blocks the rest of a &lt;code&gt;git diff&lt;/code&gt;: an external diff that exits non-zero makes git abandon the &lt;em&gt;entire&lt;/em&gt; run, so OmniDiff reports an unshowable file as a successful diff of nothing rather than as a failure.&lt;/p&gt;
&lt;h2&gt;Jujutsu (jj) integration&lt;/h2&gt;
&lt;p&gt;jj does not read git&#39;s &lt;code&gt;difftool&lt;/code&gt;/&lt;code&gt;diff.external&lt;/code&gt; settings, even in a colocated repo, so it needs its own configuration. Run the setup wizard:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;omnidiff jj configure
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configure by hand&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;jj config set --user merge-tools.omnidiff.program omnidiff
jj config set --user merge-tools.omnidiff.diff-args &amp;#39;[&amp;quot;$left&amp;quot;,&amp;quot;$right&amp;quot;]&amp;#39;
jj config set --user merge-tools.omnidiff.diff-invocation-mode file-by-file
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That registers &lt;code&gt;jj diff --tool omnidiff&lt;/code&gt;. To make it the default for plain &lt;code&gt;jj diff&lt;/code&gt; as well:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;jj config set --user ui.diff-formatter omnidiff
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;--repo&lt;/code&gt; in place of &lt;code&gt;--user&lt;/code&gt; to configure the current repository only.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;diff-invocation-mode = &amp;quot;file-by-file&amp;quot;&lt;/code&gt; is required.&lt;/strong&gt; jj&#39;s default hands a diff tool two &lt;em&gt;directory&lt;/em&gt; trees; OmniDiff diffs two files, so without this setting every invocation fails. With it, jj passes one changed file pair at a time, keeping each file&#39;s real path and extension, so language detection works exactly as it does under git.&lt;/p&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;jj diff&lt;/code&gt; runs its formatter under a pager, so OmniDiff renders in its non-interactive text mode there - the same output &lt;code&gt;git diff&lt;/code&gt; gets. jj has no equivalent of &lt;code&gt;git difftool&lt;/code&gt;&#39;s interactive per-file viewer (its terminal-attached hook, &lt;code&gt;ui.diff-editor&lt;/code&gt;, is for &lt;code&gt;jj diffedit&lt;/code&gt;/&lt;code&gt;jj split&lt;/code&gt;, which edit the right-hand side and read it back - not something a read-only viewer should claim to do), so for the full-screen TUI on a jj repo, run &lt;code&gt;omnidiff BEFORE AFTER&lt;/code&gt; directly.&lt;/p&gt;
&lt;h2&gt;Supported content&lt;/h2&gt;
&lt;p&gt;The language is detected from the file extension. A file with an unknown extension is diffed as plain text, line by line, so nothing is refused.&lt;/p&gt;
&lt;p&gt;25 languages are parsed with a tree-sitter grammar and diffed structurally:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;File extensions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.c&lt;/code&gt;, &lt;code&gt;.h&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C#&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.cs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C++&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.cc&lt;/code&gt;, &lt;code&gt;.cpp&lt;/code&gt;, &lt;code&gt;.cxx&lt;/code&gt;, &lt;code&gt;.hpp&lt;/code&gt;, &lt;code&gt;.hh&lt;/code&gt;, &lt;code&gt;.hxx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.css&lt;/code&gt;, &lt;code&gt;.scss&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.go&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.html&lt;/code&gt;, &lt;code&gt;.htm&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JavaScript&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.js&lt;/code&gt;, &lt;code&gt;.mjs&lt;/code&gt;, &lt;code&gt;.cjs&lt;/code&gt;, &lt;code&gt;.jsx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kotlin&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.kt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lua&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.lua&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Perl&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.pl&lt;/code&gt;, &lt;code&gt;.pm&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.php&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.py&lt;/code&gt;, &lt;code&gt;.pyi&lt;/code&gt;, &lt;code&gt;.pyw&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;R&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.r&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ruby&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.rb&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.rs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scala&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.scala&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shell (Bash)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.bash&lt;/code&gt;, &lt;code&gt;.sh&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Swift&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.swift&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TSX&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.tsx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.ts&lt;/code&gt;, &lt;code&gt;.mts&lt;/code&gt;, &lt;code&gt;.cts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vimscript&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.vim&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XML&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.xml&lt;/code&gt;, &lt;code&gt;.xht&lt;/code&gt;, &lt;code&gt;.xhtml&lt;/code&gt;, &lt;code&gt;.svg&lt;/code&gt;, &lt;code&gt;.glif&lt;/code&gt;, &lt;code&gt;.plist&lt;/code&gt;, &lt;code&gt;.ui&lt;/code&gt;, &lt;code&gt;.qrc&lt;/code&gt;, &lt;code&gt;.glade&lt;/code&gt;, &lt;code&gt;.xib&lt;/code&gt;, &lt;code&gt;.storyboard&lt;/code&gt;, &lt;code&gt;.xaml&lt;/code&gt;, &lt;code&gt;.vcxproj&lt;/code&gt;, &lt;code&gt;.filters&lt;/code&gt;, &lt;code&gt;.csproj&lt;/code&gt;, &lt;code&gt;.fsproj&lt;/code&gt;, &lt;code&gt;.vbproj&lt;/code&gt;, &lt;code&gt;.props&lt;/code&gt;, &lt;code&gt;.targets&lt;/code&gt;, &lt;code&gt;.nuspec&lt;/code&gt;, &lt;code&gt;.resx&lt;/code&gt;, &lt;code&gt;.wxs&lt;/code&gt;, &lt;code&gt;.iml&lt;/code&gt;, &lt;code&gt;.manifest&lt;/code&gt;, &lt;code&gt;.config&lt;/code&gt;, &lt;code&gt;.policy&lt;/code&gt;, &lt;code&gt;.xsd&lt;/code&gt;, &lt;code&gt;.xsl&lt;/code&gt;, &lt;code&gt;.xslt&lt;/code&gt;, &lt;code&gt;.xlf&lt;/code&gt;, &lt;code&gt;.xliff&lt;/code&gt;, &lt;code&gt;.kml&lt;/code&gt;, &lt;code&gt;.gpx&lt;/code&gt;, &lt;code&gt;.rss&lt;/code&gt;, &lt;code&gt;.atom&lt;/code&gt;, &lt;code&gt;.graphml&lt;/code&gt;, &lt;code&gt;.dae&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YAML&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.yaml&lt;/code&gt;, &lt;code&gt;.yml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Recognised by extension but diffed as plain text, since no grammar is compiled in: Bazel (&lt;code&gt;.bazel&lt;/code&gt;), Dart (&lt;code&gt;.dart&lt;/code&gt;), Emacs Lisp (&lt;code&gt;.el&lt;/code&gt;), Markdown (&lt;code&gt;.md&lt;/code&gt;, &lt;code&gt;.markdown&lt;/code&gt;), Protocol Buffers (&lt;code&gt;.proto&lt;/code&gt;), SQL (&lt;code&gt;.sql&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Files that are not text are recognised by their bytes, never by their names, and diffed by what they hold:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;th&gt;Formats&lt;/th&gt;
&lt;th&gt;Diffed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pictures&lt;/td&gt;
&lt;td&gt;PNG, JPEG, GIF, WebP, BMP, ICO, TIFF&lt;/td&gt;
&lt;td&gt;pixel by pixel, as the eye compares them: how much changed and where; animations frame by frame&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Archives&lt;/td&gt;
&lt;td&gt;zip (and so jar, Office documents, EPUB), tar, gzip, xz, bzip2&lt;/td&gt;
&lt;td&gt;file by file, each by what it is, archives inside archives included; Java class files as a listing of their fields and methods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fonts&lt;/td&gt;
&lt;td&gt;TrueType, OpenType, WOFF, WOFF2, EOT&lt;/td&gt;
&lt;td&gt;glyph by glyph, each drawn, and the font&#39;s names and metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursors&lt;/td&gt;
&lt;td&gt;Windows &lt;code&gt;.cur&lt;/code&gt; and &lt;code&gt;.ani&lt;/code&gt;, X cursors, Hyprland &lt;code&gt;.hlc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;size by size and frame by frame&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message catalogs&lt;/td&gt;
&lt;td&gt;gettext &lt;code&gt;.mo&lt;/code&gt;, Qt &lt;code&gt;.qm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;message by message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documents&lt;/td&gt;
&lt;td&gt;PDF&lt;/td&gt;
&lt;td&gt;page by page, each page drawn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text in other encodings&lt;/td&gt;
&lt;td&gt;UTF-16 and UTF-32 with a byte order mark&lt;/td&gt;
&lt;td&gt;as text, like any other file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Only the members that changed are opened, so a font with two edited glyphs shows two glyphs. Each change is also named by how much of it a reader would see: invisible (only the bytes differ), imperceptible, artifacts (what compression or resampling leaves behind), edited, redrawn or replaced - for text, whitespace, formatting and rewritten stand in for the second, third and fifth.&lt;/p&gt;
&lt;p&gt;The TUI shows pictures as pictures: in the terminal&#39;s own graphics where it speaks the kitty, sixel or iTerm2 protocol, and in Unicode half blocks everywhere else. &lt;code&gt;b&lt;/code&gt; switches to half blocks and back, and &lt;code&gt;OMNIDIFF_GRAPHICS=halfblocks&lt;/code&gt; (or &lt;code&gt;kitty&lt;/code&gt;, &lt;code&gt;sixel&lt;/code&gt;, &lt;code&gt;iterm2&lt;/code&gt;) overrides the detection; inside tmux, the terminal in front of the pane decides. An archive, a font or any other container lists its changed members beside the one selected, &lt;code&gt;g&lt;/code&gt; shows every changed glyph or picture at once, and &lt;code&gt;Enter&lt;/code&gt; opens a text member in the full diff view.&lt;/p&gt;
&lt;p&gt;Anything else binary gets the one-line &lt;code&gt;Binary file &amp;lt;path&amp;gt; differs&lt;/code&gt; notice.&lt;/p&gt;
&lt;h2&gt;License&lt;/h2&gt;
&lt;p&gt;Copyright (C) 2026 Marko Ivankovic&lt;/p&gt;
&lt;p&gt;This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License.&lt;/p&gt;
&lt;p&gt;See the LICENSE file for the full text of the License.&lt;/p&gt;
&lt;h2&gt;Cannot use AGPL software?&lt;/h2&gt;
&lt;p&gt;A commercial license is available as a monthly subscription through &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/sponsors/ivankovic&quot;&gt;GitHub Sponsors&lt;/a&gt;. It covers internal use of omnidiff by your organisation without the source-disclosure obligations of the AGPL. The terms are in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff/blob/main/LICENSE-COMMERCIAL&quot;&gt;LICENSE-COMMERCIAL&lt;/a&gt;. Pick the tier that names the commercial license as a benefit. For invoicing or other arrangements, contact me at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;mailto:marko@ivankovic.me&quot;&gt;marko@ivankovic.me&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Guiding principles&lt;/h2&gt;
&lt;h2&gt;Fast&lt;/h2&gt;
&lt;p&gt;OmniDiff&#39;s goal is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A median diff in 100ms or less.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A 99th-percentile diff in 1000ms or less.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both are met. Over the 2,302 fixtures in &lt;code&gt;src/test/data/diffs/&lt;/code&gt;, measured 2026-10-31 on a laptop, an Intel Core i7-1370P (6 performance and 8 efficiency cores, 20 threads) with 32 GB RAM: &lt;strong&gt;p50 4.2ms, p90 35.1ms, p99 177ms&lt;/strong&gt;, slowest 482ms. &lt;strong&gt;100ms is the 97.6th percentile&lt;/strong&gt; — 55 fixtures take longer than that, and none takes longer than a second.&lt;/p&gt;
&lt;p&gt;Times belong to the machine that measured them, so each machine keeps its own (see &lt;code&gt;research/data/machines.csv&lt;/code&gt;): the Intel Xeon E3-1275 v5 server that measured the research figures takes about 2.5 times as long per fixture. Runtime is reported, never gated: &lt;code&gt;make check-quality&lt;/code&gt; prints this distribution against the machine&#39;s own committed baseline on every push, and warns when the runtime is more than twice the baseline&#39;s.&lt;/p&gt;
&lt;p&gt;Other content is timed over pairs sampled from the same corpus, decoding included - unpacking an archive, drawing a font&#39;s glyphs or a PDF&#39;s pages - measured 2026-10-17 on the same laptop:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;th&gt;Pairs&lt;/th&gt;
&lt;th&gt;p50&lt;/th&gt;
&lt;th&gt;p90&lt;/th&gt;
&lt;th&gt;p99&lt;/th&gt;
&lt;th&gt;Under 100ms&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pictures&lt;/td&gt;
&lt;td&gt;230&lt;/td&gt;
&lt;td&gt;1.8ms&lt;/td&gt;
&lt;td&gt;41ms&lt;/td&gt;
&lt;td&gt;526ms&lt;/td&gt;
&lt;td&gt;96.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursors&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;0.6ms&lt;/td&gt;
&lt;td&gt;1.3ms&lt;/td&gt;
&lt;td&gt;15ms&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fonts&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;6.4ms&lt;/td&gt;
&lt;td&gt;57ms&lt;/td&gt;
&lt;td&gt;826ms&lt;/td&gt;
&lt;td&gt;95%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Archives&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;2.6ms&lt;/td&gt;
&lt;td&gt;156ms&lt;/td&gt;
&lt;td&gt;1,938ms&lt;/td&gt;
&lt;td&gt;88%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message catalogs&lt;/td&gt;
&lt;td&gt;81&lt;/td&gt;
&lt;td&gt;0.4ms&lt;/td&gt;
&lt;td&gt;1.9ms&lt;/td&gt;
&lt;td&gt;4.7ms&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDFs&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;69ms&lt;/td&gt;
&lt;td&gt;310ms&lt;/td&gt;
&lt;td&gt;985ms&lt;/td&gt;
&lt;td&gt;57%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Archives miss the 99th-percentile goal: the slowest hold thousands of changed members. PDFs meet both goals, but only 57% of them take under 100ms, since a PDF draws every changed page. &lt;code&gt;make benchmark-content&lt;/code&gt; measures this and writes &lt;code&gt;research/data/performance/content_speed/&amp;lt;machine&amp;gt;.csv&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Robust&lt;/h2&gt;
&lt;p&gt;OmniDiff&#39;s goal is to process 100% of all commits.&lt;/p&gt;
&lt;p&gt;The full test dataset holds the git commit history of about 7,500 open-source git repositories, as available on the main branch. This list of repositories comes from the Gentoo Linux distribution. Find it in &lt;code&gt;list_of_repositories.csv&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Measured over every modified code file in the most recent 50 commits of each of those repositories - 442,530 readable before/after pairs in 25 languages, diffed with no size cap under a 120-second budget and a 6 GB memory cap per process - &lt;strong&gt;442,322 (99.95%) completed successfully.&lt;/strong&gt; 96 pairs ran past the budget and 112 past the memory cap; the latter are twenty files, nineteen of them generated or embedded - tree-sitter parser tables, codegen, minified bundles, a PNG as a C array - and one commit of a 40,000-line single-header C++ library. Given 24 GB and 300 s, 11 of the 28 files that first hit the cap complete. The run, its harness and every pair that did not complete are documented in &lt;code&gt;research/data/performance/PROVENANCE.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Beyond code, over every file changed in the same window - 2,018,010 changes in 7,352 repositories, pictures, fonts and archives included - &lt;strong&gt;98.8% are diffed as text or by their content&lt;/strong&gt; (98.2% counting each repository at most 1,000 times, so that a few giant ones do not decide it; 6,481 repositories entirely). What is left is mostly text in legacy encodings, compiled objects, audio, and EOT fonts compressed with MicroType Express. The census is documented in &lt;code&gt;research/data/corpus_stats/PROVENANCE.md&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Accurate&lt;/h2&gt;
&lt;p&gt;OmniDiff must match a human&#39;s own reading of a change, measured against the hand-authored ground-truth mappings in &lt;code&gt;src/test/data/diffs/&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;90% of test cases with zero mismatched bytes.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;99% of test cases with at most 1% of bytes mismatched.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Neither is met yet. 1,419 of the 2,302 fixtures carry a hand-painted ground truth - every byte of both files labelled with what a human says happened to it - and OmniDiff&#39;s own highlighting is compared against it byte by byte, under each of its two highlighting presets (&lt;code&gt;--full&lt;/code&gt;, which keeps brackets, separators and leading whitespace, and &lt;code&gt;--minimal&lt;/code&gt;, which drops them):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;preset&lt;/th&gt;
&lt;th&gt;zero mismatched bytes&lt;/th&gt;
&lt;th&gt;at most 1% mismatched&lt;/th&gt;
&lt;th&gt;mismatched bytes, whole corpus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--full&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;851 (60.0%)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,241 (87.5%)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;99,157 of 73.3M (0.14%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--minimal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;987 (69.6%)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,299 (91.5%)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;51,740 of 73.3M (0.07%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The whole-corpus rate is far below 1% because most of each file is unchanged and nobody gets that wrong; the per-test-case numbers are the ones that count, since a reader meets the mistakes one diff at a time. Most of what is left is not in the matching: rendering the human&#39;s own node mapping still disagrees with the painting on 91% of the &lt;code&gt;--full&lt;/code&gt; bytes and 91% of the &lt;code&gt;--minimal&lt;/code&gt; ones, so the highlighting rules own the gap more than the matcher does.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;make update-painting-attribution&lt;/code&gt; measures this and writes one row per fixture and preset to &lt;code&gt;research/data/quality/painting_attribution.csv&lt;/code&gt;, which the table is counted from; &lt;code&gt;make check-painting-attribution&lt;/code&gt; fails if any fixture gets worse.&lt;/p&gt;
&lt;p&gt;Other content is judged rather than painted: a human names how much each change is (invisible, imperceptible, artifacts, edited, redrawn or replaced, see &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff#supported-content&quot;&gt;supported content&lt;/a&gt;) and OmniDiff&#39;s verdict is compared with theirs. A test case is perfect when every verdict matches, and near-perfect when every one is at most one level off:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;th&gt;Judged&lt;/th&gt;
&lt;th&gt;Perfect&lt;/th&gt;
&lt;th&gt;Near-perfect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pictures&lt;/td&gt;
&lt;td&gt;130&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;90 (69%)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;118 (91%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursors&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11 (85%)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;13 (100%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fonts&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9 (82%)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11 (100%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Archives&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;20 (87%)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;20 (87%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message catalogs&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10 (83%)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10 (83%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDFs&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4 (100%)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4 (100%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;make benchmark-content&lt;/code&gt; counts these too.&lt;/p&gt;
&lt;h2&gt;AI policy&lt;/h2&gt;
&lt;p&gt;This project uses substantial AI assistance, currently Claude Code. Most commits disclose this with a &lt;code&gt;Co-Authored-By&lt;/code&gt; trailer and a link to the session that produced them. This project does not hide that fact. This project does not treat AI assistance as a lesser way to write software.&lt;/p&gt;
&lt;p&gt;Contributions are not accepted at this time, to keep the development speed high (see &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff/blob/main/CONTRIBUTING.md&quot;&gt;CONTRIBUTING.md&lt;/a&gt;). When that changes, AI-assisted contributions will be as welcome as any other, disclosed the same way: whoever submits the work is responsible for understanding it and standing behind it.&lt;/p&gt;
&lt;h2&gt;For Developers, human or otherwise&lt;/h2&gt;
&lt;p&gt;See &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ivankovic/omnidiff/blob/main/CONTRIBUTING.md&quot;&gt;CONTRIBUTING.md&lt;/a&gt; for the technology overview, code-quality and testing expectations, project structure, and what CI checks on every push and PR. &lt;code&gt;AGENTS.md&lt;/code&gt; has additional AI-agent-specific conventions.&lt;/p&gt;
</content:encoded>
    <category>news</category>
  </item>
</channel>
</rss>
