Skip to main content

Meld, in your terminal: a faithful TUI port of the Meld diff/merge tool

Project description

tmeld — Meld, in your terminal

CI License: GPL v2+

A faithful terminal port of GNOME Meld: the same diff engine (vendored verbatim), the same colors, the same keybindings — over plain SSH. Two- and three-way file comparison and merging, folder comparison, and a version-control view.

tmeld three-way merge

tmeld a.py b.py                  # 2-way compare/edit
tmeld local.py base.py remote.py # 3-way merge (middle = merged file)
tmeld dirA dirB                  # folder comparison (Enter opens files)
tmeld .                          # version-control view (git, hg, svn, ...)
tmeld a b --diff c d --diff x y  # extra comparison tabs (like meld --diff)
tmeld --theme meld-dark a b      # Meld's dark scheme
tmeld --show-line-numbers a b    # line numbers (off by default, as in Meld)
tmeld --web a b                  # serve it to a browser instead (see bmeld)

Line numbers are hidden by default, matching Meld's defaults; the status bar always shows Ln N, Col N for the focused pane. --show-line-numbers brings them back (in bmeld too).

Installing

From PyPI: pipx install tmeld (or pip install tmeld).

As a Debian package: grab tmeld_*_all.deb from the GitHub release, or build one yourself:

sudo apt install build-essential debhelper dpkg-dev \
                 python3 python3-pip python3-venv git
git clone https://github.com/mitnits/tmeld.git
cd tmeld && maint/mkdeb.sh          # writes ../tmeld_<ver>+<date>.<sha>_all.deb
sudo apt install ../tmeld_*_all.deb  # apt, so Recommends come too

Building needs network: it fetches the pinned Textual from PyPI and bundles it under /usr/share/tmeld/lib, because tmeld needs a newer Textual than Debian ships. System Python packages are untouched. The result is Architecture: all, so one build serves every machine — copy the .deb around and apt install ./… it.

Use apt install ./file.deb rather than dpkg -i, so the recommended python3-aiohttp is pulled in. That is what bmeld needs, and it is deliberately not bundled: its C extensions would make the package architecture-dependent.

maint/mkdeb.sh stamps each build <version>+<date>.<sha>, so a rebuild installs over the previous one instead of being refused as the same version (0.4.0 < 0.4.0+20260708.f683ae7 < 0.5.0).

Keys (Meld's own)

Action Keys
Next / previous change Alt+Down, Ctrl+D / Alt+Up, Ctrl+E
Push chunk left / right Alt+Left / Alt+Right
Pull chunk from left / right Alt+Shift+Right / Alt+Shift+Left
Copy chunk above / below Alt+[ Alt+] / Alt+; Alt+'
Delete chunk Alt+Delete
Next / previous conflict (3-way) Ctrl+K / Ctrl+J
Merge all non-conflicting (3-way) Alt+M
Undo / cut / copy Ctrl+Z, Alt+Z / Ctrl+X, Alt+X / Ctrl+C, Alt+C
Save Ctrl+S
Next / previous pane Alt+PgDn / Alt+PgUp
Close tab / quit on the last one Ctrl+W (twice if unsaved), or the ✕
Next / previous tab Ctrl+Alt+PgDn / Ctrl+Alt+PgUp
Quit Esc, Ctrl+Q (Esc warns once if unsaved)

Gutter arrows between panes are clickable (they push the chunk); the right-edge map is click-to-jump. On macOS terminals, set "Option as Esc+" (iTerm2: Profiles → Keys) so Alt bindings arrive.

In folder comparisons: Enter compares the file under the cursor (or expands/collapses a folder), Alt+Left/Right copy the row to that neighbor pane, Delete deletes it (press twice to confirm), Alt+Down/Up jump between differing rows, and Alt+PgDn/PgUp move the focused pane (the column copy/delete act on). Meld's default filename filters (backups, VCS metadata, binaries, OS cruft) apply.

In the version-control view (tmeld . anywhere in a working copy): Enter compares a changed file against the repository (repo side read-only); a conflicted file opens as a remote/merge/local 3-way whose middle-pane saves resolve the working file. c commits (Meld's Ctrl+M IS Enter in a terminal, so the mnemonic moved), r reverts, Delete deletes, Ctrl+R/F5 rescans.

bmeld: Meld in your browser

pip install 'tmeld[web]' adds the bmeld command — the same program as tmeld --web, under its own name. --port implies --web, since a port means nothing in a terminal:

tmeld a b               # terminal
tmeld --web a b         # browser, random port
tmeld --port 8731 a b   # browser on a fixed port
bmeld a b               # exactly `tmeld --web a b`

Options that belong to the other front-end are refused rather than ignored (tmeld --bind 0.0.0.0 says --bind requires --web).

It starts a local server (127.0.0.1 only, token-protected) and prints a clickable URL — the same engine, palette and keybindings, rendered by CodeMirror with real SVG linkmap curves. Typing is local and instant; the server rediffs debounced snapshots and is the only thing that writes files. The process exits with the same mergetool contract as tmeld, so the .gitconfig stanza below works with cmd = bmeld ... too.

bmeld local.py base.py remote.py   # prints http://127.0.0.1:PORT/t/TOKEN
bmeld dirA dirB                    # folder comparison (Enter opens files)
bmeld .                            # version-control view (commit/revert)
bmeld a b --diff c d               # extra comparison tabs
bmeld --port 8731 a.py b.py        # fixed port: add a LocalForward line
                                   # to ~/.ssh/config and remote links
                                   # open on your local browser
bmeld --bind 0.0.0.0 a.py b.py     # reachable from other machines (see below)

File, folder, and version-control comparisons open as tabs (with ✕ close buttons); Enter/double-click a tree row opens a file comparison.

Over SSH, bmeld prints the port-forward one-liner instead of trying to open a browser. Sessions survive reloads; closing the tab without saving a merge exits 1 after a grace period (--grace).

By default bmeld listens on loopback only. --bind 0.0.0.0 accepts connections from other machines and prints a URL with this host's outbound address (override with --advertise HOST). Understand what that costs: the unguessable token in the URL becomes the only thing between the network and a process that reads and writes the files under comparison, and it travels in cleartext over HTTP. Use it on a network you trust, or keep the SSH tunnel.

Pixel linkmap (Tier 2)

On terminals with graphics support, the gutter between panes widens and Meld's anti-aliased connector curves are drawn there as real pixels — kitty graphics protocol (kitty, WezTerm, Ghostty) or sixel (iTerm2, recent VTE), auto-detected at startup. --graphics none|sixel|kitty overrides the probe. Everything else stays cell-based; without graphics you keep the compact 3-column gutter.

git mergetool

tmeld follows Meld's convention: tmeld $LOCAL $MERGED $REMOTE, the middle pane is the merged file. The exit code is 0 only if the middle pane was saved, so git can trust it:

[merge]
    tool = tmeld
[mergetool "tmeld"]
    cmd = tmeld "$LOCAL" "$MERGED" "$REMOTE"
    trustExitCode = true

tmeld -o OUTPUT local base remote redirects middle-pane saves to OUTPUT (like meld -o) if you'd rather keep the base file untouched.

For diffs: git difftool -x tmeld or

[diff]
    tool = tmeld
[difftool "tmeld"]
    cmd = tmeld "$LOCAL" "$REMOTE"

License and provenance

GPL-2.0-or-later (see LICENSE), like Meld — whose engine this project vendors byte-for-byte apart from mechanical import rewrites (tmeld/_vendor/meld/, pinned commit recorded in tmeld/_vendor/UPSTREAM, rewrites applied by maint/vendor.py). The vendored vc/ package is BSD 2-clause (its COPYING ships alongside).

tmeld is an independent project, not affiliated with or endorsed by the Meld or GNOME projects. All credit for the diff engine and the design this port imitates goes to Meld and its maintainers.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tmeld-0.5.0.tar.gz (257.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tmeld-0.5.0-py3-none-any.whl (240.3 kB view details)

Uploaded Python 3

File details

Details for the file tmeld-0.5.0.tar.gz.

File metadata

  • Download URL: tmeld-0.5.0.tar.gz
  • Upload date:
  • Size: 257.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for tmeld-0.5.0.tar.gz
Algorithm Hash digest
SHA256 f03029e572683b7a9e42f990ed13777a619d57863abc15fd6a7d615ceaacf1fd
MD5 beef2dff2887c8b2abdc71766ab1664f
BLAKE2b-256 250576dfe9133bf36ee04a598ce7c2aef5d18de4c844da009f23efc4e8ba8c8d

See more details on using hashes here.

File details

Details for the file tmeld-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: tmeld-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 240.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for tmeld-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 920ecb49b12a4141c3a75de46f8c860689eae31bb94f01e8b69162d3ab6086de
MD5 e1982dd5d568478d4e13d67586fe12f8
BLAKE2b-256 c600400a2d0df72128aca8d03f7de9d7a615dd185d6c61c278a7e06d310c4d0c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page