Introducing mdiff, a code review app

Some time ago, I wanted to have improved diff experience for my own work and personal projects. There were a lot of diff tools in the market. Some of them are cli based like delta, some of them are GUI based like Kaleidoscope (whih went subscription and boy I hate that so much). Some time ago I came across a tool called Diffle. It was a web based diff tool that was very good at what it did and have good integration with Git and agentic workflows.

I liked Diffle. I just kept wishing it were an app I could open from the Dock instead of having to open a local server and then open a browser tab. I wanted to be able to click an icon, pick a repository, and start reading. A terminal is a perfectly good place to work with Git, I wanted the review itself to have its own window. Why? personal preference, nothing more.

The review UI was already close to what I wanted: readable diffs, code navigation, and comments attached to the code. The part that bothered me was getting into it. Run a command, start a local server, end up in a browser tab. So a GUI app would be ideal.

So I forked it. That fork became mdiff, a code review app using Diffle engine and review UI under the hood (however, it is a hard fork). It handles local changes, GitHub pull requests, and GitLab merge requests (Mainly because I use CERN gitlab but also supports gitlab.com and other selfhosted instances). I also worked on the less visible parts: how patches are loaded, how comments survive edits, and how much work the engine does when one file changes. Those turned out to matter just as much as the window.

Reviewing a sample refund calculation, with a comment attached to the currency-handling code. All captures in this post use a demo repository.

Start with the branch and narrow it down

A branch often contains several kinds of changes: the actual feature, a rename, a generated client, and a small cleanup that arrived along the way. Reading all of that as one long patch makes it easy to lose track of which change explains which.

In mdiff, I can start with the whole comparison and then select an individual commit. The file tree follows that selection. Returning to All changes brings back the branch view. Split and unified layouts, file filtering, and viewed markers help with the less interesting but necessary business of getting through the patch.

Moving from the complete branch to the commit that introduces refunds, then back to the full comparison.

The app opens repositories and comparisons through its UI. Recent reviews are available when I come back, and Open in IDE discovers installed editors so I can move from reading a change to working on it.

A small refund bug

A review tool should keep me focused on bugs like this one. The demo branch adds a function that totals sales and refunds by account. It remembers each account's currency using:

currency.setdefault(entry.account, entry.currency)

That looks harmless until an account has entries in two currencies. The function keeps the first currency and adds all the amounts together. Decimal arithmetic can preserve every digit while the result is still meaningless: ten euros plus ten pounds is not twenty euros.

I can leave a note on that line, asking for the same mixed-currency validation used elsewhere in the project and a regression test. The note appears beside the code and in the discussion panel.

Leaving a line comment without switching away from the diff.

Review notes stay in the repository's Git metadata directory, outside the worktree. They survive reopening the review. When code moves, mdiff uses the quoted text and nearby lines to relocate comments, ambiguous matches become stale rather than quietly attaching to an arbitrary copy of the same code. Saved discussions are searchable too.

Comments can also be copied with their code context when I want to take the feedback into an editor or coding agent.

Search the code that disappeared

One detail I wanted was explicit control over which side of a change I was searching. A normal file search cannot find an import that has already been deleted.

mdiff can search added lines, removed lines, both sides, diff context, or full files. Results carry their side and line number, so a match in removed code takes me to the deletion. This is useful during API migrations, renames, or any review where the interesting question is whether an old call was really removed everywhere.

Searching removed lines for round_amount finds the import being replaced in ledger.py.

When I need more context, mdiff can use locally installed language servers for symbol information, definitions, and references. Those servers need to be installed separately. GitHub and GitLab connections cover hosted reviews, but the app can also use a local Git repository for reviews without a remote.

How mdiff runs locally

mdiff uses Tauri: a Rust backend and a web interface rendered in the system WebView. The review components are still web code, while the app owns the window and the engine's lifetime. Requests reach the engine through Tauri IPC, without a local HTTP server listening on a port.

I also changed the engine for how desktop reviews get used. Patches are cached within a memory budget. A save to one tracked file can refresh that file without rescanning the entire review. Large diffs load in chunks, and obsolete reads can be cancelled when the comparison changes.

Language-server documents have a memory budget too, with incremental updates when the server supports them. Review data now lives in SQLite, with the previous JSON preserved during migration. Larger commit-series comparisons reuse commit fingerprints and stream patch data instead of retaining the complete patch output in memory.

Appearance is configurable as well: I added the most famous developer tools themes:20 light themes, 20 dark themes, and separate choices for code and interface fonts. At least according to my own taste, the default theme is readable and the default font is legible. The app can also use the system theme and font.

How to try mdiff

I didn't work much on the distribution side, but the app is available for macOS and Linux through a homebrew cask and a shell script install that every security advice will tell you not to use. And the best thing would be currently to either build it yourself or manually download the release from GitHub. The app is not signed with an Apple Developer ID or notarized, so macOS will block it until you remove the quarantine attribute.

On macOS with Homebrew:

brew tap MohamedElashri/mdiff \\ 
https://github.com/MohamedElashri/mdiff \\ 
&& brew install --cask MohamedElashri/mdiff/mdiff

On macOS or Linux, the install script can install or update the app:

curl -fsSL https://raw.githubusercontent.com/MohamedElashri/mdiff/main/install.sh | bash

Git is required. Linux packages target x86-64, macOS packages are available for Apple Silicon and Intel. There is manual update mechanism, but the app does not check for updates automatically. The app is still in early development, so expect bugs and missing features.

The code and downloads are on GitHub. mdiff is GPL-3.0-only and maintains its own fork of the Diffle engine and review UI, with the upstream attribution retained. Moritz Wilksch and Pavel Zwerschke's Diffle introduction is worth reading for the project that made this starting point possible.

Good luck and happy reviewing (AI code review)!

参与讨论