中文版 | 一款原生的 macOS 桌面应用,提供三栏可视化差异编辑器。
A native macOS desktop application for resolving Git merge conflicts with a three-pane visual diff editor.
Splice replaces the confusing inline conflict markers (<<<<<<<, =======, >>>>>>>) with a clear side-by-side interface, letting you visually compare your changes (Yours) against incoming changes (Theirs) and the common ancestor (Base) to produce a clean merge result.
IntelliJ IDEA style merge tool, built as a standalone macOS app — no IDE required.
The main interface is split into three aligned panes:
| Pane | Position | Shows |
|---|---|---|
| Yours (Current) | Left | Your branch's version of the file |
| Result | Center | The evolving merge result |
| Theirs (Merged) | Right | The incoming branch's version |
Each pane scrolls in sync, and clicking a conflict in any pane highlights it across all three. An optional Base pane (common ancestor) can be toggled with Cmd+\.
Each conflict block in the side panes has action buttons (>> to accept, ✕ to ignore), inspired by IntelliJ IDEA's merge tool:
- Yours side:
>>accepts your version,✕ignores it (uses theirs) - Theirs side:
<<accepts their version,✕ignores it (uses yours) - Click only once per side per conflict — buttons disable after use
- Shows loading feedback (
◌spinner) while the backend processes
Click Magic Merge to automatically resolve all remaining conflicts. Splice uses heuristics to pick the correct version for each conflict, then lets you review and fix any that need manual attention. Magic Merge results can be fully undone with one click.
Full undo/redo stack (Cmd+Z / Cmd+Shift+Z) for all conflict resolution actions — accept, ignore, manual edit, and magic merge. Never worry about making a wrong choice.
Splice supports two editing modes:
Click ✏ Edit Inline at the bottom of an unresolved conflict to open a textarea showing both versions with labels:
// ─── Your version ───
your_code_here
// ─── Their version ───
their_code_here
The editor includes a live syntax-highlighted preview that updates as you type. Press Cmd+Enter to apply, Esc to cancel.
After resolving a conflict, you can continue editing the result directly in place — click anywhere on the green resolved text (or the ✏ button) to open an inline textarea. A floating action bar (✓ Save / ✕ Cancel) appears at the bottom. Press Cmd+Enter to save or Esc to cancel.
This makes it easy to:
- Tweak the merged result without re-resolving
- Manually blend both versions when neither side alone is sufficient
- Fix formatting after accepting a side
For complex edits, click the Manual Resolve button to open a full dialog with a larger editing area, syntax highlighting, and the original conflict context.
| Feature | Description |
|---|---|
| Syntax highlighting | Powered by highlight.js, supports all major languages (JavaScript, Rust, Python, Go, etc.) |
| Resolved conflict badge | Green ✓ Resolved badge above each resolved block, showing the method (Yours / Theirs / Both / Manual) |
| Conflict highlights | Resolved lines get a green left border; unresolved conflicts highlighted in red/orange; Base pane shows light blue |
| Click to highlight | Click a conflict block in any side pane to flash-highlight (✨ gold animation) the corresponding block in the result pane |
| Loading feedback | Buttons show ◌ spinner animation while the backend processes the resolution |
| Automatic alignment | After resolving, all three panes auto-scroll to show the same conflict block — even when content lengths differ |
| File-level diff | The status bar shows word-level diff statistics for each conflict |
With one click, configure Splice as your global Git mergetool:
# The app runs this for you:
git config --global merge.tool splice
git config --global mergetool.splice.cmd '/Applications/Splice.app/Contents/MacOS/splice ...'After configuration, simply run git mergetool in any conflicted repo — Splice launches automatically with the file loaded.
Splice includes a background file watcher that monitors configured Git repositories for new conflict markers:
- Click the Watch button in the status bar
- Select a Git repository directory
- Splice monitors that repo and shows a desktop notification when new conflicts appear
The watcher panel lists all tracked repositories, shows the number of pending conflicts, and lets you open any conflicted file with one click.
Toggle Auto-launch in the status bar to install Git hooks that automatically open Splice when a git merge produces conflicts.
Side pane headers and conflict markers show actual branch names from Git:
Yours (Current)→main(your current branch)Theirs (Merged)→feature/new-api(the branch being merged)- Conflict markers use branch names instead of generic labels
Splice is designed for keyboard-first power users. Every action has a shortcut. Press Cmd+/ or ? at any time to open the interactive shortcuts overlay.
| Shortcut | Action |
|---|---|
Cmd + ' |
Accept local (yours) |
Cmd + ; |
Accept both versions |
Cmd + . |
Accept remote (theirs) |
Cmd + M |
Magic Merge (auto-resolve all) |
Cmd + E |
Open inline editor for current conflict |
Cmd + \\ |
Toggle Base pane |
Tab |
Next conflict |
Shift + Tab |
Previous conflict |
Cmd + P |
Open conflict overview sidebar |
Cmd + O |
Open a conflicted file |
Cmd + S |
Save the resolved file |
Cmd + W |
Close the active tab |
Cmd + Z |
Undo last resolution |
Cmd + Shift + Z |
Redo last undo |
Cmd + Shift + D |
Toggle debug panel |
Cmd + / or ? |
Toggle this shortcuts overlay |
The following shortcuts work while editing conflict content in the inline editor:
| Shortcut | Action |
|---|---|
Cmd + Enter |
Confirm / Save the edited content |
Esc |
Cancel editing, restore original |
| Category | Shortcut | Action |
|---|---|---|
| File | Cmd+O |
Open a conflicted file |
Cmd+W |
Close the active tab | |
Cmd+S |
Save the resolved file | |
| Navigation | Tab |
Next conflict |
Shift+Tab |
Previous conflict | |
Cmd+P |
Open conflict overview sidebar | |
Cmd+\\ |
Toggle Base pane | |
| Resolution | Cmd+' |
Accept yours (local) |
Cmd+; |
Accept both | |
Cmd+. |
Accept theirs (remote) | |
Cmd+M |
Magic merge (auto-resolve) | |
Cmd+E |
Edit current conflict inline | |
Cmd+Enter |
Confirm inline edit | |
| History | Cmd+Z |
Undo last resolution |
Cmd+Shift+Z |
Redo last undo | |
| Help | Cmd+/ or ? |
Show shortcuts overlay |
| Debug | Cmd+Shift+D |
Toggle debug panel |
Splice supports opening multiple conflicted files simultaneously with a tabbed interface:
- Each tab maintains its own conflict state, undo history, and scroll position
- Switch between tabs with keyboard shortcuts or mouse clicks
- Close tabs with
Cmd+Wor the × button - The status bar shows global progress across all open files
Click the overview sidebar button (or use Cmd+Shift+O) to see a summary of all conflicts in the current file:
- Lists each conflict with its line number and current status (resolved / unresolved)
- Color-coded: red for unresolved, green for resolved
- Click any entry to jump directly to that conflict
- Shows total count and resolution progress
- Download the latest
.dmgfrom the Releases page - Open the
.dmgand dragSplice.appto yourApplicationsfolder - Right-click
Splice.appand select Open (first launch only, to bypass Gatekeeper)
# Prerequisites: Rust, Node.js, and system dependencies for Tauri v2
# See: https://v2.tauri.app/start/prerequisites/
git clone https://github.com/kennethfan/splice.git
cd splice
npm install
npx tauri build
cp -R src-tauri/target/release/bundle/macos/Splice.app /Applications/- Open Splice — You'll see the welcome screen with a
Configure Global Mergetoolbutton - Configure git — Click the button to set Splice as your default mergetool
- Open a conflicted file — Press
Cmd+Oor click the status bar button, and select a file with Git conflict markers
After configuring Splice as your mergetool, simply run:
git mergetoolSplice will automatically open with the conflicted file loaded.
- Click the Watch button in the status bar
- Select a Git repository directory
- Splice will monitor that repo and notify you when new conflicts are detected
- Unresolved conflicts: Click the
✏ Edit Inlinebutton at the bottom of the conflict region to edit with a syntax-highlighted textarea - Resolved conflicts: Click anywhere on the green resolved text to edit it in-place (inline textarea)
- Press
Cmd+Enterto confirm,Escto cancel
splice/
├── src/ # Frontend (React + TypeScript)
│ ├── App.tsx # Main application component
│ ├── main.tsx # Entry point
│ ├── components/ # React components
│ │ ├── BasePane.tsx # Base (common ancestor) pane
│ │ ├── ConflictBlock.tsx # Individual conflict block
│ │ ├── ConflictOverview.tsx # Sidebar overview of all conflicts
│ │ ├── DiffText.tsx # Word-level diff visualization
│ │ ├── HoverPreview.tsx # Hover preview for side panels
│ │ ├── MagicMergeDialog.tsx # Magic merge result dialog
│ │ ├── ManualResolveDialog.tsx # Manual resolve dialog
│ │ ├── ShortcutsOverlay.tsx # Keyboard shortcuts help overlay
│ │ ├── StatusBar.tsx # Bottom status bar
│ │ ├── TabBar.tsx # Multi-file tab bar
│ │ └── WatchedRepoPanel.tsx # Watched repositories panel
│ ├── hooks/ # Custom React hooks
│ │ ├── useBlockDiffs.ts # Word-level diff computation
│ │ ├── useKeyboard.ts # Keyboard shortcut registration
│ │ └── useSyncScroll.ts # Three-pane scroll synchronization
│ ├── lib/ # Utilities
│ │ ├── highlight.ts # Syntax highlighting (highlight.js)
│ │ ├── sound.ts # Notification sound effects
│ │ └── tauri.ts # Tauri IPC command bindings
│ └── styles/
│ └── splice.css # All application styles
├── src-tauri/ # Backend (Rust)
│ └── src/
│ ├── main.rs # Tauri app entry point
│ ├── lib.rs # Plugin registration
│ ├── commands/ # Tauri IPC commands
│ │ ├── close_session.rs
│ │ ├── configure_mergetool.rs
│ │ ├── diff_cmd.rs
│ │ ├── get_base.rs
│ │ ├── initial_session.rs
│ │ ├── magic_merge.rs
│ │ ├── open_file.rs
│ │ ├── resolve.rs
│ │ ├── save.rs
│ │ └── undo.rs
│ ├── diff/ # Diff engine
│ │ ├── engine.rs
│ │ └── mod.rs
│ ├── git/ # Git integration
│ │ ├── mergetool.rs
│ │ └── mod.rs
│ ├── parser/ # Conflict marker parser
│ │ ├── conflict.rs
│ │ ├── lexer.rs
│ │ └── mod.rs
│ └── watcher.rs # File system watcher
└── test/ # Test fixtures
- Three-pane layout — Inspired by IntelliJ IDEA's merge tool. The side panels show YOURS and THEIRS unchanged, while the center panel shows the evolving RESULT.
- Conflict markers — The Rust backend parses
<<<<<<</=======/>>>>>>>markers and presents them as structuredConflictBlockobjects to the frontend. - Scroll sync — Uses
data-conflict-idattributes instead of proportional ratio to keep all three panes aligned on the same conflict block (works even when resolved content has different lengths). - Inline editing — Uses controlled
<textarea>for both resolved and unresolved content. Resolved conflicts open a lightweight inline textarea; unresolved conflicts use a textarea with a live syntax-highlighted preview. Both correctly handle HTML tags in code.
| Layer | Technology |
|---|---|
| Desktop shell | Tauri v2 |
| Frontend | React 19 + TypeScript 5.8 |
| Backend | Rust with git2 |
| Diff engine | imara-diff |
| Syntax highlighting | highlight.js 11 |
| Build tool | Vite 7 |
| Testing | Vitest (frontend) + Cargo (backend) |
- Rust (via rustup)
- Node.js >= 18
- macOS system deps — Xcode Command Line Tools (
xcode-select --install)
For other platforms, see the Tauri v2 prerequisites guide.
# Install frontend dependencies
npm install
# Start in development mode (hot-reload)
npm run tauri:dev
# or: bash scripts/tauri-dev.sh# 1. Build the frontend and compile the native binary
npx tauri build
# 2. The built .app bundle will be at:
# src-tauri/target/release/bundle/macos/Splice.app
# 3. Install it to /Applications:
cp -R src-tauri/target/release/bundle/macos/Splice.app /Applications/
# 4. Launch:
open /Applications/Splice.appnpx tauri build && cp -R src-tauri/target/release/bundle/macos/Splice.app /Applications/| Artifact | Path |
|---|---|
| .app bundle | src-tauri/target/release/bundle/macos/Splice.app |
| .dmg image | src-tauri/target/release/bundle/dmg/Splice_0.1.0_x64.dmg |
| Rust binary | src-tauri/target/release/splice (debug at target/debug/splice) |
An automated installer is also available:
bash scripts/splice-install.shThis script will:
- Locate or build the Splice binary
- Configure
gitto use Splice as the default mergetool - Optionally create a symlink in your PATH
# Frontend tests (Vitest)
npx vitest run
# Backend tests (Cargo)
cd src-tauri && cargo test
# Full type check
npx tsc --noEmit
# Run all checks before committing
npx vitest run && cd src-tauri && cargo test && cd .. && npx tsc --noEmit| Check | Status |
|---|---|
| TypeScript | ✅ Zero errors |
| Frontend tests | ✅ 126/126 passing |
| Rust tests | ✅ 20/20 passing |
| Tauri build | ✅ Successful |
| # | Bug | Root cause |
|---|---|---|
| 1 | Content loss after resolve | lineIdx advanced by display lines instead of local lines |
| 2 | Side pane code offset | For-loop continue caused extra +1, skipping one line per conflict |
| 3 | Rust backend marker replacement | Multi-conflict >>>>>>> line counting off-by-one in save.rs |
| 4 | Button click no response | programmaticScrollRef RAF race condition stuck at true |
| 5 | Safety timer leak | Effect cleanup didn't cancel old 500ms timer |
| 6 | Undo button state not restored | usedSides ref not cleared on undo/redo |
| 7 | Undo inline editor not closed | inlineEditId/inlineEditText not reset on undo/redo |
| 8 | Undo magic merge buttons broken | handleUndoMagic didn't clear usedSides |
| 9 | HTML tags in inline editor | dangerouslySetInnerHTML interpreted code <tags> as HTML |
| 10 | Content lost on save | contentEditable div cleared by React re-render (empty vDOM children) |
- Color-coded conflict markers (red for Yours, green for Theirs)
- Collapsible conflict blocks in side panes
- Cmd+number shortcuts to jump to a specific conflict
- Improved diff visualization (word-level diffs inline)
- Dark/light theme toggle
APACHE 2.0