Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# Empty context lines inside the embedded Git patches intentionally contain a space.
src/demo.rs whitespace=-blank-at-eol
# The signature covers these exact bytes, including line endings.
tests/fixtures/update/payload.txt -text
83 changes: 83 additions & 0 deletions .github/workflows/desktop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Windows and Linux alpha checks
on:
push:
branches: [master]
paths-ignore: ['site/**', '**/*.md', 'assets/screenshot.png']
pull_request:
paths-ignore: ['site/**', '**/*.md', 'assets/screenshot.png']
workflow_dispatch:
permissions:
contents: read
concurrency:
group: desktop-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-24.04
platform: linux
target: x86_64-unknown-linux-gnu
- os: windows-latest
platform: windows
target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.os }}
timeout-minutes: 90
env:
CI: 'true'
APPIMAGE_EXTRACT_AND_RUN: '1'
steps:
- name: Use LF checkouts and predictable Git fixtures
run: git config --global core.autocrlf false
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install Linux build and graphics dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y pkg-config gcc g++ clang cmake libfontconfig-dev libwayland-dev libwebkit2gtk-4.1-dev libxkbcommon-x11-dev libx11-xcb-dev libssl-dev libzstd-dev libvulkan1 mesa-vulkan-drivers xvfb xdg-utils libfuse2t64
- name: Check source
run: cargo fmt --check
- name: Functional tests
run: cargo test --locked
- name: Clippy
run: cargo clippy --locked --all-targets --features ui-tests -- -D warnings
- name: Linux renderer and interaction tests
if: runner.os == 'Linux'
run: xvfb-run -a cargo test --locked --features ui-tests
- name: Build native executable
run: cargo build --locked --bins
- name: Linux native startup
if: runner.os == 'Linux'
run: xvfb-run -a python scripts/smoke-desktop.py target/debug/diffy
- name: Windows native startup
if: runner.os == 'Windows'
run: python scripts/smoke-desktop.py target/debug/diffy.exe
- name: Install pinned packager
run: cargo install cargo-packager --version 0.11.8 --locked --root target/tools
- name: Package alpha
run: python scripts/release-desktop.py --platform ${{ matrix.platform }} --unsigned
- name: Validate packaged installation
if: runner.os == 'Windows'
run: python scripts/check-desktop-package.py --platform windows
- name: Validate Linux packaged installation and signed update
if: runner.os == 'Linux'
run: xvfb-run -a python scripts/check-desktop-package.py --platform linux
- uses: actions/upload-artifact@v4
if: always()
with:
name: Diffy-${{ matrix.platform }}-x86_64-alpha-checks
path: |
dist/unsigned/${{ matrix.platform }}/
target/previews/
if-no-files-found: warn
6 changes: 6 additions & 0 deletions .github/workflows/site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ jobs:
- run: npm run build
env:
DODO_CHECKOUT_URL: ${{ matrix.checkout }}
DIFFY_RELEASED_PLATFORMS: ${{ matrix.checkout == '' && 'macos' || 'macos,windows,linux' }}
- run: node scripts/check-build.mjs
env:
DODO_CHECKOUT_URL: ${{ matrix.checkout }}
DIFFY_RELEASED_PLATFORMS: ${{ matrix.checkout == '' && 'macos' || 'macos,windows,linux' }}
deploy:
name: Deploy production
needs: build
Expand Down Expand Up @@ -61,6 +66,7 @@ jobs:
- run: npm run build
env:
DODO_CHECKOUT_URL: ${{ vars.DODO_CHECKOUT_URL }}
DIFFY_RELEASED_PLATFORMS: ${{ vars.DIFFY_RELEASED_PLATFORMS || 'macos' }}
- name: Upload production site
uses: cloudflare/wrangler-action@953926a2e2182532811c01a25e53647d93bf07c0 # v4
with:
Expand Down
46 changes: 40 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 19 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
[package]
name = "diffy"
version = "0.2.3"
version = "0.2.4"
edition = "2024"
description = "A small desktop Git diff viewer"
publish = false
default-run = "diffy"
license = "MIT"
repository = "https://github.com/hitSlop/diffy"

Expand All @@ -21,6 +22,11 @@ ignore = "=0.4.33"
notify = "=8.2.0"
nucleo-matcher = "=0.3.1"
async-channel = "=2.5.0"
directories = "=6.0.0"
fs2 = "=0.4.3"
interprocess = "=2.4.4"
tempfile = "3.27"
which = "8"
lru = "=0.18.5"
gix = { version = "=0.89.0", default-features = false, features = ["basic", "status", "sha1"] }
tracing = "0.1"
Expand All @@ -38,11 +44,22 @@ objc2-app-kit = { version = "=0.3.2", default-features = false, features = ["std
objc2-foundation = { version = "=0.3.2", default-features = false, features = ["std", "NSString", "NSURL"] }

[target.'cfg(windows)'.dependencies]
interprocess = "=2.4.4"
windows-sys = { version = "=0.61.2", features = ["Win32_UI_WindowsAndMessaging"] }

[dev-dependencies]
criterion = { version = "0.8", default-features = false }

[build-dependencies]
embed-resource = "=3.0.11"

[[bin]]
name = "diffy"
path = "src/main.rs"

[[bin]]
name = "diffy-cli"
path = "src/terminal.rs"

[[bench]]
name = "performance"
harness = false
Expand Down
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# <img src="assets/icon/icon.iconset/icon_128x128.png" alt="Diffy chameleon" width="40" height="40" align="middle"> Diffy

[Download the latest version for macOS](https://github.com/hitSlop/diffy/releases/latest) · Apple Silicon · macOS 13+
[Downloads and installation](https://diffy.hitslop.com/downloads) · macOS **beta** · Windows/Linux **alpha**

![Diffy on macOS: a split Rust diff, project change badges, a searchable file list grouped by directory, and project import](assets/screenshot.png)

A small native Rust/GPUI Kit diff viewer for macOS.
A small native Rust/GPUI Kit diff viewer. macOS is in beta; Windows and Linux are early alphas with limited hardware testing. One purchase supports every platform: **$14.99 during beta**, regular price ~~$24.99~~. Windows/Linux download links become available after release validation and publication.

- Pure-black surfaces and colored squircle project icons that grow gently on hover. Badges count unique changed files across Staged and Changes (partially staged files count once).
- Bundled Commit Mono Regular and Medium throughout the app, with programming ligatures disabled.
Expand All @@ -29,7 +29,7 @@ A small native Rust/GPUI Kit diff viewer for macOS.

## Run

Requires Rust and Xcode Command Line Tools. Git must be on PATH for repository mode.
Requires Rust 1.92+ and the native platform toolchain. macOS needs Xcode Command Line Tools; Windows needs Visual Studio 2022 C++ Build Tools, the Windows SDK, and CMake with the MSVC Rust toolchain; Linux needs the development libraries listed in RELEASING.md. Git must be installed for repository mode.

```sh
cargo run --locked
Expand Down Expand Up @@ -68,10 +68,12 @@ diffy --version

`diffy .` adds or selects the repository in the existing Diffy window and brings it forward. A closed window is recreated; a stopped app is launched. Relative paths are resolved in your terminal, and nested folders resolve to the repository root. `diffy` without paths opens your saved projects. Reinstall the command if you move `Diffy.app`; normal updates at the same location keep it working.

CLI installation is currently macOS-only. Development binaries still accept repository paths directly through `cargo run`.
CLI installation supports all three desktop targets. Linux installs `~/.local/bin/diffy`, pointing to the outer AppImage when packaged. Windows copies a managed console launcher to its local app-data directory and shows instructions for adding the directory to your user Path. The Windows installer includes `diffy-cli.exe` beside the GUI executable. Development binaries accept repository paths directly through `cargo run`. Windows/Linux invocations forward requests to the existing app through a bounded per-user local socket; demo mode stays isolated.

## Keyboard shortcuts

Use **Ctrl** instead of **⌘** on Windows and Linux. Menu and footer hints adapt to the platform.

| Shortcut | Action |
|---|---|
| ⌘P | Search projects and commands |
Expand All @@ -98,7 +100,7 @@ Unmerged files remain readable with their conflict markers. Resolve and stage th

Git writes affect the selected file's index entries only. Unstaging preserves working-tree contents, including on unborn branches. Rename actions include both paths; copy actions affect the destination only. Errors retain the last good preview.

Preferences live at `~/Library/Application Support/Diffy/state.json` and contain paths and viewer settings, never patches or source contents. Imported project images live beside it in `project-icons/`, named by their SHA-256 contents so matching filenames cannot overwrite other icons. Back up the folder to keep both preferences and images. Existing image references remain readable; selecting an image again copies it into managed storage. Tests inject temporary storage; `DIFFY_STATE_PATH` can override the path for isolated development sessions and also relocates the icon folder.
Preferences live at `~/Library/Application Support/Diffy/state.json` on macOS, `$XDG_CONFIG_HOME/diffy/state.json` (default `~/.config/diffy/state.json`) on Linux, and `%LOCALAPPDATA%/hitSlop/Diffy/config/state.json` on Windows and contain paths and viewer settings, never patches or source contents. Imported project images live beside it in `project-icons/`, named by their SHA-256 contents so matching filenames cannot overwrite other icons. Back up the folder to keep both preferences and images. Existing image references remain readable; selecting an image again copies it into managed storage. Tests inject temporary storage; `DIFFY_STATE_PATH` can override the path for isolated development sessions and also relocates the icon folder.

## Build a macOS app

Expand Down Expand Up @@ -138,11 +140,11 @@ Built with [GPUI Kit](https://gpui-kit.com/) and [Commit Mono](https://commitmon

## macOS release

Diffy uses the bundle identifier `com.hitslop.diffy`. The first release targets Apple Silicon Macs running macOS 13 or later. Source code is MIT licensed; dependency and font licenses remain in effect.
Diffy uses the bundle identifier `com.hitslop.diffy`. The macOS beta targets Apple Silicon Macs running macOS 13 or later. Windows x86-64 NSIS and Linux x86-64 AppImage alphas use the same release version. Linux initially targets Ubuntu 24.04+ with a working Vulkan driver; other distributions and ARM targets are not yet verified. Source code is MIT licensed; dependency and font licenses remain in effect.

Existing preferences are migrated from `~/Library/Application Support/Differ/state.json` only when no Diffy preferences exist. The original file is retained. `DIFFY_STATE_PATH` takes precedence over the legacy `DIFFER_STATE_PATH` override; explicit overrides skip migration.

Packaged builds check for updates quietly after startup. **Check for Updates…** is also available in the Diffy menu and ⌘P command palette. Updates download and verify only after clicking **Update and Restart**. Git writes finish before installation; projects and preferences survive the restart. Development binaries cannot install updates.
Packaged builds check for updates quietly after startup. **Check for Updates…** is also available in the Diffy menu and ⌘P command palette. Updates download and verify only after clicking **Update and Restart**. Git writes finish before installation; projects and preferences survive the restart. Development binaries cannot install updates. Windows installs verified NSIS updates and relaunches; Linux replaces and relaunches the outer AppImage. Windows publisher signing is not configured for the alpha, separately from updater payload signatures. Updates require a writable installation location.

See [RELEASING.md](RELEASING.md) for packaging, local signing, key backups, draft releases, and update verification.

Expand All @@ -155,3 +157,7 @@ DIFFY_TRACE_PATH="$PWD/target/diffy-trace.json" cargo run --locked -- /path/to/r
```

The benchmark compares eager loading, Git metadata refresh, selected-file parsing, cached navigation, and the complete gix metadata adapter on disposable repositories. `DIFFY_BENCH_ONLY=metadata` (or `eager`, `selected_preview`, `cached_navigation`, `gix_metadata`, `gix_status_prototype`) isolates a case for memory measurements. The gix adapter benchmark fails if it falls back to Git; the earlier filename-only prototype remains a separate comparison. Traces are opt in, stay local, and contain timing spans without paths or source contents. Open them in Perfetto. gix is a runtime dependency; Criterion remains development-only.

## Windows and Linux packages

Native build and package checks run in `.github/workflows/desktop.yml`. See [RELEASING.md](RELEASING.md) for NSIS/AppImage packaging, updater signatures, release assembly, and validation. `directories` supplies native settings locations; `interprocess` and an OS file lock coordinate terminal requests. Windows/Linux are alpha until broader hardware testing is available.
Loading
Loading