# OmniDiff: 파일 종류에 맞춰 변경을 비교하기
# OmniDiff: comparing files by their content

- Site: https://select1.dev/2026-11-02 (English), https://select1.dev/ko/2026-11-02 (Korean)
- Entry: 023
- Published: 2026-11-02 (KST). Launch date: 2026-11-01
- Pick: OmniDiff <https://github.com/ivankovic/omnidiff> (source: Show HN)
- Verified: ran
- What: OmniDiff 는 파일의 종류에 맞춰 변경 내용을 비교해 보여 주는 프로덕트다. 코드는 문법 구조로 비교한다. 이미지는 픽셀로, PDF 는 페이지로 비교한다.
- What (en): OmniDiff is a product that compares changes according to the kind of file. It compares code by its syntax. It compares images pixel by pixel and PDFs page by page.
- Why: 오늘의 픽으로 선정한 이유는 이미지 같은 바이너리 파일의 변경도 리뷰할 수 있기 때문이다.
- Why (en): It is today’s pick because it lets you review changes in files that Git treats as binary.
- Verdict: 작동한다. 확장자를 바꾼 PNG 에서도 바뀐 영역의 위치와 크기를 정확히 찾았다.
- Verdict (en): It works. It found the exact position and size of the changed region, even in PNG files renamed to .txt.
- Tags: diff, git, rust, code-review, devtool, ai-built

## Checked against the README

- [pass] 텍스트가 아닌 파일은 이름이 아니라 바이트로 종류를 판별한다(Files that are not text are recognised by their bytes, never by their names) — 사각형 하나만 다른 200×120 PNG 두 장을 비교했다. 확장자를 .txt 로 바꾼 사본을 비교해도 같은 결과(바뀐 픽셀 12.96%, 61×51 영역)가 출력됐다
- [pass] 종료 코드는 성공 0, 오류 2 이고, --exit-code 를 붙이면 파일이 다를 때 1 이다(OmniDiff exits 0 on success and 2 on error … --exit-code … 1 when the files differ) — --exit-code 를 붙이자 같은 파일은 0, 다른 파일은 1, 없는 파일은 2 를 반환했다. 붙이지 않으면 다른 파일에도 0 을 반환했다
- [pass] GIT_EXTERNAL_DIFF 로 git diff 에 연결하면 textconv 설정 없이 이미지 같은 파일의 변경도 보고한다(OmniDiff also works directly with git diff … through GIT_EXTERNAL_DIFF) — 새 Git 저장소에서 PNG 와 Python 파일을 바꿨다. git diff 는 PNG 에 Binary files differ 만 출력했다. OmniDiff 를 GIT_EXTERNAL_DIFF 로 지정하자 PNG 에 바뀐 픽셀의 비율과 영역이 출력됐다

## Measured

- 저장소의 테스트 파일 쌍 2,302건을 --headless 로 하나씩 비교한 시간. 4코어 Xeon 2.1 GHz 가상 머신, 쌍마다 프로세스를 새로 실행: 중앙값 26.5 ms, p90 162 ms, p99 858 ms, 100 ms 안에 끝난 쌍 82.2%, 1초를 넘은 쌍 16건 (Python 의 subprocess 로 쌍마다 OmniDiff 를 실행해 경과 시간을 쟀다. OmniDiff 는 확장자로 언어를 판별하므로, 테스트 파일의 .test 접미사를 뗀 이름으로 심볼릭 링크를 만들어 비교했다. 실행 파일만 시작하는 데는 약 3 ms 가 걸렸다)

## 한국어

## 무엇이 문제인가

코드 리뷰에서는 diff 로 변경을 읽는다. 변경이 라인의 일부에만 있거나 파일이 텍스트가 아니면, 라인 단위 diff 로는 무엇이 바뀌었는지 알기 어렵다.

제작자는 README 에서 같은 코드 변경을 GNU diff 와 OmniDiff 로 나란히 보여 준다. GNU diff 는 라인 안의 일부만 바뀌어도 그 라인 전체를 지우고 새로 추가한 것으로 표시한다. Git 공식 문서에 따르면 git diff 의 `--word-diff` 옵션도 라인 단위 diff 의 결과 안에서 단어 단위 변경을 계산한다. git diff 는 바이너리로 판단한 파일에는 "Binary files differ" 만 출력한다. 내용을 비교하려면 사용자가 파일을 텍스트로 바꾸는 프로그램을 textconv 로 지정해야 한다. 같은 문서에 따르면 이 변환에서는 정보 일부가 사라진다.

## 어떻게 해결했나

OmniDiff 는 2026년 11월 1일 Hacker News 에 Show HN 글로 올라왔다. OmniDiff 는 파일의 종류를 먼저 판별하고, 그 종류에 적합한 방법으로 비교한다. 코드는 확장자로 언어를 판별하고, 지원하는 25개 언어의 코드는 AST 로 바꿔 구조끼리 비교한다. 텍스트가 아닌 파일은 확장자가 아니라 내용의 바이트로 종류를 판별한다. 이미지는 픽셀 단위로, 압축 파일은 안에 든 파일 단위로, 폰트는 글자 모양 단위로, PDF 는 페이지 단위로 비교한다.

결과는 키보드로 조작하는 터미널 화면, 일반 텍스트, JSON 으로 볼 수 있다. 환경 변수 `GIT_EXTERNAL_DIFF` 로 Git 에 연결하면 git diff 의 출력도 OmniDiff 가 만든다. `git difftool` 로 연결하면 파일마다 OmniDiff 의 터미널 화면이 열린다.

## 무엇이 다른가

첫째, 코드의 변경을 표시하는 단위가 다르다. OmniDiff 는 코드를 AST 로 비교해 라인 전체가 아니라 라인 안에서 변경된 부분만 표시하고, 옮긴 코드는 따로 표시한다. 제작자는 직접 표시한 정답과 바이트 단위로 대조해 정확도를 측정했다. 다만 README 에 따르면 정답이 있는 1,419건 가운데 OmniDiff 의 표시가 정답과 모두 같은 경우는 `--full` 설정에서 60.0%, 강조 범위가 좁은 `--minimal` 에서 69.6% 다. 목표는 90% 다.

둘째, 텍스트가 아닌 파일을 다루는 방식이 다르다. OmniDiff 는 이미지에서 바뀐 픽셀의 비율과 영역을, 폰트에서 바뀐 글자 모양을 보고한다. 트레이드오프는 압축을 풀거나 렌더링해서 비교하기 때문에 시간이 더 걸린다는 점이다. README 에 따르면 PDF 비교는 57% 가 100 ms 안에 끝난다. 압축 파일은 p99 가 1,938 ms 로 목표인 1초를 넘는다.

## 어떻게 만들었나

- 커밋 1,514개는 2026년 2월 11일부터 11월 1일까지 작성됐다. 그중 795개에 Claude 와 함께 작성했다는 Co-authored-by 트레일러가 있다.
- 제작자는 README 의 AI 정책에서 개발에 Claude Code 의 도움을 많이 받는다고 밝혔다.
- 제작자는 Gentoo Linux 의 패키지 목록에서 오픈소스 저장소 약 7,500개를 골라, 비교가 실패 없이 끝나는지 시험했다. README 에 따르면 코드 파일의 변경 442,530건 가운데 99.95% 가 건당 120초, 메모리 6 GB 안에서 끝났다. 나머지는 시간이나 메모리 한도를 넘었다.

## 실행해 보니

Rust 1.97 로 저장소를 빌드하고, 4코어 가상 머신에서 결과를 텍스트로 출력하는 `--headless` 모드로 실행했다.

1. 빌드. `cargo build --release --locked` 는 6분 1초가 걸렸다. 실행 파일은 54 MB 였다.
2. 이미지. 200×120 PNG 두 장에서 사각형 하나를 바꿔 비교했다. OmniDiff 는 픽셀의 12.96% 가 바뀌었다고 보고하고, 바뀐 영역의 크기(61×51)와 위치를 출력했다. 실제로 바꾼 영역과 같았다. 두 PNG 파일의 확장자를 .txt 로 바꿔도 결과가 같았다.
3. Git. 새 저장소에 PNG 와 Python 파일을 커밋하고 바꿨다. git diff 는 PNG 에 "Binary files … differ" 만 출력했다. `GIT_EXTERNAL_DIFF` 로 OmniDiff 를 지정하자 PNG 에는 바뀐 픽셀의 비율과 영역이 출력됐다. Python 파일에서는 OmniDiff 가 `total` 이 `sum(numbers)` 로 바뀐 부분을 표시하고, `return` 라인은 5번째 라인에서 2번째 라인으로 옮겼다고 표시했다.
4. 종료 코드. `--exit-code` 를 붙이자 같은 파일은 0, 다른 파일은 1, 없는 파일은 2 를 반환했다.
5. 속도. 저장소에 든 테스트 파일 쌍 2,302건을 하나씩 비교했다. 비교 시간의 중앙값은 26.5 ms, p99 는 858 ms 였고, 1초를 넘은 쌍이 16건이었다. README 에 따르면 제작자의 노트북에서는 같은 2,302건의 중앙값이 4.2 ms, p99 가 177 ms 였고 1초를 넘은 쌍이 없었다. 제작자의 Xeon 서버에서는 노트북보다 약 2.5배 오래 걸린다. 오늘은 쌍마다 프로세스를 새로 실행했다. 실행 파일을 시작하는 데는 약 3 ms 가 걸렸다. README 의 측정이 시작 시간을 포함하는지는 확인하지 못했다.

```
Picture logo.png: PNG 200x120 RGB8, 419 bytes -> PNG 200x120 RGB8, 453 bytes
  12.96% of pixels changed (3111 of 24000), in 1 region:
    61x51 at (120, 30)
=== after: stats.py ===
1    def mean(numbers):
Moved from line 5
2 |~     return sum(numbers) / len(numbers)
```

## 확인하지 못한 것

- 터미널 화면. kitty 처럼 그래픽을 지원하는 터미널에서 이미지를 그림으로 보여 주는지 확인하자.
- PDF 와 폰트. README 의 예처럼 PDF 다이어그램에서 이름 하나를 바꿔 비교하고, 폰트도 글자 하나의 모양을 바꿔 비교해 보자.

## 판정

작동한다. 확장자를 바꾼 PNG 에서도 바뀐 영역의 위치와 크기를 정확히 찾았다. 다만 4코어 가상 머신에서 측정한 비교 시간의 중앙값은 README 에 적힌 제작자 노트북의 값보다 약 6배 길었다. 이미지 같은 바이너리 파일의 변경도 리뷰해야 하는 경우에 써 볼 만하다.

### 용어

- AST: 소스 코드를 문법 구조에 따라 트리로 나타낸 자료 구조. 컴파일러와 분석 도구가 코드를 읽을 때 만든다.
- p99: 측정값을 작은 순서로 늘어놓았을 때 99% 지점의 값이다. 요청 100개 가운데 가장 느린 1개를 빼면 나머지는 모두 이 값보다 빠르다.

## English

## The problem

In code review, people read changes as diffs. When a change touches only part of a line, or the file is not text, a line-based diff makes it hard to see what changed.

In the README, the maker shows the same code changes side by side in GNU diff and OmniDiff. Even when only part of a line changes, GNU diff marks the whole line as deleted and added again. According to the official Git documentation, even `--word-diff` looks for word changes only inside the lines that the line-based diff marked as changed. When git diff judges files to be binary, it prints only "Binary files … differ". To compare their content, you have to set a textconv program that turns the file into text. The same documentation says this conversion loses some information.

## How it solves it

OmniDiff was posted on Hacker News as a Show HN on November 1, 2026. It identifies the file type, then chooses how to compare it. For code, it picks the language from the file extension. It parses code in 25 languages into an AST and compares the structure. It recognizes files that are not text by their bytes, not their names. OmniDiff compares images pixel by pixel. It compares archives file by file, fonts glyph by glyph and PDFs page by page.

You can see the result in an interactive terminal UI, as plain text or as JSON. When you connect it to Git with the `GIT_EXTERNAL_DIFF` environment variable, Git shows OmniDiff’s output in place of its own. With `git difftool`, OmniDiff opens its terminal UI for each file.

## What is different

The first difference is how code changes are marked. OmniDiff compares the code as an AST and marks only the parts that changed inside a line, not the whole line. It also marks moved code separately. The maker measured accuracy byte by byte against labels the maker added by hand. According to the README, 1,419 cases have such labels. OmniDiff’s marks match the labels exactly in 60.0 percent of them with `--full` and in 69.6 percent with `--minimal`, which highlights less. The goal is 90 percent.

The second difference is how OmniDiff handles files that are not text. OmniDiff reports how much of an image changed and where. It also lists the glyphs that changed in a font. The tradeoff is time, because it unpacks and renders the files to compare them. According to the README, 57 percent of PDF comparisons finish in under 100 ms. For archives, the p99 is 1,938 ms, over the one-second goal.

## How it was built

- The repository has 1,514 commits from February 11 to November 1, 2026. Of these, 795 have a Co-authored-by trailer for Claude.
- In the README’s AI policy, the maker says the project is built with substantial help from Claude Code.
- The maker chose about 7,500 open-source repositories from Gentoo Linux’s package list and tested whether comparisons finish without failing. According to the README, 99.95 percent of 442,530 code file changes finished within 120 seconds and 6 GB of memory each. The rest went over the time or memory limit.

## Running it

I built the repository with Rust 1.97 and ran it on a 4-core virtual machine in `--headless` mode, which prints the result as text.

1. Build. `cargo build --release --locked` took 6 minutes and 1 second. The binary was 54 MB.
2. Images. I compared two 200×120 PNG files with one rectangle changed. OmniDiff reported that 12.96 percent of the pixels changed. It printed the changed region’s size (61×51) and position. These matched the region I changed. Renaming both files to .txt gave the same result.
3. Git. I committed a PNG file and a Python file in a new repository, then changed them. git diff printed only "Binary files … differ" for the PNG file. I set OmniDiff as `GIT_EXTERNAL_DIFF`, and it reported the share and region of changed pixels in the PNG file. For the Python file, it showed `total` changed to `sum(numbers)` and marked the `return` line as moved from line 5 to line 2.
4. Exit codes. With `--exit-code`, identical files returned 0, different files 1 and a missing file 2.
5. Speed. I compared the 2,302 test file pairs in the repository one at a time. The median was 26.5 ms and the p99 was 858 ms. Of the 2,302 pairs, 16 took over a second. The README reports a median of 4.2 ms and a p99 of 177 ms for the same pairs on a laptop. No pair took over a second there. It says the maker’s Xeon server takes about 2.5 times as long as the laptop. Today I started a new process for each pair. Starting the binary took about 3 ms. I did not check whether the README’s numbers include start-up time.

```
Picture logo.png: PNG 200x120 RGB8, 419 bytes -> PNG 200x120 RGB8, 453 bytes
  12.96% of pixels changed (3111 of 24000), in 1 region:
    61x51 at (120, 30)
=== after: stats.py ===
1    def mean(numbers):
Moved from line 5
2 |~     return sum(numbers) / len(numbers)
```

## Not checked

- The terminal UI. Check that a terminal with graphics support, such as kitty, shows images as pictures.
- PDFs and fonts. Change the label of one box in a PDF diagram, as in the README’s example, and compare the files. Then change the shape of one glyph in a font and compare.

## Verdict

It works. It found the exact position and size of the changed region, even in PNG files renamed to .txt. But on a 4-core virtual machine, the median comparison time was about six times the maker’s laptop figure in the README. It is worth trying if you also review changes to binary files such as images.

### Terms

- AST: Abstract syntax tree: a tree that shows the structure of source code. Compilers and analysis tools build it to read code.
- p99: The 99th percentile: the value below which 99 percent of measurements fall. Out of 100 requests, all but the slowest one finish within it.

## Launches the same day

- Contra NES port <https://epoyraz.github.io/nes-contra-us/> (Show HN, 88): 게임 포트. NES 게임 Contra(1987)의 어셈블리 코드를 C 로, 다시 WebAssembly 로 바꿔 브라우저에서 실행한다.
- OmniDiff <https://github.com/ivankovic/omnidiff> (Show HN, 76): Git diff 도구. 코드는 문법 구조로, 이미지와 PDF 는 내용으로 비교한다.
- Koppar LTE modem <https://github.com/Koppar-AI/koppar-lte-modem> (Show HN, 60): USB-C LTE 모뎀 보드. 컴퓨터에서 SIM 으로 데이터 통신, 문자, 음성 통화를 할 수 있게 한다.
- BBRouter <https://bbrouter.com/bbrouter/ben-eater-sap-1> (Show HN, 46): 3D 브레드보드 회로 편집기. Ben Eater 의 8비트 컴퓨터를 브라우저에서 시뮬레이션한다.
- JOMP <https://hellojomp.com/> (Show HN, 18): 인터랙티브 실험과 생성 예술을 모은 사이트.
- AVault <https://github.com/arpanjot-coding/AVault> (GitHub, 477): Android 통화 녹음 앱. 루팅 없이 녹음해 하드웨어로 암호화한 저장소에 보관한다고 소개한다.
- stack-diagram <https://github.com/caru-ini/stack-diagram> (GitHub, 301): 에이전트 스킬. 스택을 한 문장으로 설명하면 공식 로고가 든 구조도를 그린다.
- NOIR <https://github.com/Verlintas/NOIR> (GitHub, 222): Android 개인정보 점검 도구. adb 로 앱의 권한과 데이터를 보내는 곳을 확인한다.
- Screen Studio 4.0 <https://www.producthunt.com/products/screen-studio> (Product Hunt, 311): 화면 녹화 앱의 새 버전. 대본으로 편집하기 같은 기능을 더했다고 소개한다.
- ClawsGO Science <https://www.producthunt.com/products/clawsgo-science> (Product Hunt, 189): AI 연구 에이전트. 자체 클라우드 실험실을 갖췄다고 소개한다.
- Riverside Agents <https://www.producthunt.com/products/riverside-fm> (Product Hunt, 162): 영상 제작 AI 에이전트 모음. 제작, 편집, 클립 만들기, 게시를 처리한다고 소개한다.

## Getting started

- Steps: cargo install --locked omnidiff ; omnidiff --headless before.py after.py ; git config difftool.omnidiff.cmd 'omnidiff "$LOCAL" "$REMOTE"'
- Needs: Rust 1.92 이상과 C 컴파일러. 첫 빌드는 4코어 가상 머신에서 6분이 걸렸다
- Cost: AGPL-3.0, 무료. 제작자는 AGPL 의 소스 공개 의무를 지지 않고 회사 안에서 사용하려면 GitHub Sponsors 에서 월 구독으로 상용 라이선스를 받으라고 안내한다.
- Note: 제작자에 따르면 지금은 외부 기여를 받지 않는다.
