Merge Tool Support

Unison integrates with merge tools, allowing you to resolve conflicts using the same UI you might already use for Git or other version control workflows.

If a merge command fails due to conflicts and a merge tool is configured, Unison will launch your chosen UI to help you resolve the conflicts.

A VS Code screenshot showing a merge conflict resolution view

Setup

Set the UCM_MERGETOOL environment variable to the command your desired editor or tool uses for 3-way merges.

For example, in bash or zsh, you might add a line like this to your ~/.bashrc or ~/.zshrc file so that merge conflicts in Unison will automatically be opened in your chosen tool:

export UCM_MERGETOOL='/path/to/your/merge/tool \
  --merge "$REMOTE" "$LOCAL" "$BASE" "$MERGED"'

You can also provide the UCM_MERGETOOL variable at the command line when you run ucm to use a merge tool for a single UCM session without changing your shell configuration.

UCM_MERGETOOL='/path/to/your/merge/tool \
  --merge "$REMOTE" "$LOCAL" "$BASE" "$MERGED"' ucm

Each merge tool has its own command-line api for invoking a 3-way merge, but most support the same core functionality with four basic concepts. Your merge tool will need to refer to the following variables, which Unison will substitute when it invokes the command:

  • $LOCAL: the current branch's version of the file
  • $REMOTE: the incoming branch's version of the file
  • $BASE: the common ancestor's version of the file
  • $MERGED: the output file to write the merged result to

These variables refer to temporary files created by Unison for the merge. You won't need to create or clean them up yourself.

Example configuration

The exact command set in the UCM_MERGETOOL variable will depend on your operating system and the merge tool you want to use. Here are some examples for some popular editors and merge tools.

Check the developer docs of your tool of choice for the exact command-line syntax on your operating system.

VS Code

UCM_MERGETOOL='"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code" \
  --merge "$REMOTE" "$LOCAL" "$BASE" "$MERGED"'

IntelliJ IDEA

UCM_MERGETOOL='"/Applications/IntelliJ IDEA CE.app/Contents/MacOS/idea" \
  merge "$LOCAL" "$REMOTE" "$BASE" "$MERGED"'

FileMerge

UCM_MERGETOOL='opendiff \
  "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED"'

P4Merge

UCM_MERGETOOL='/Applications/p4merge.app/Contents/MacOS/p4merge \
  "$BASE" "$LOCAL" "$REMOTE" "$MERGED" 2>&1 >/dev/null &'

Emacs

UCM_MERGETOOL='emacs --eval "
(progn
  (defun ediff-write-merge-buffer ()
    (let ((file ediff-merge-store-file))
      (set-buffer ediff-buffer-C)
      (write-region (point-min) (point-max) file)
      (message \"Merge buffer saved in: %s\" file)
      (set-buffer-modified-p nil)
      (sit-for 1)))
  (setq ediff-quit-hook '\''kill-emacs
        ediff-quit-merge-hook '\''ediff-write-merge-buffer)
  (ediff-merge-files-with-ancestor \"$LOCAL\" \"$REMOTE\" \"$BASE\" nil \"$MERGED\"))"'

Neovim

UCM_MERGETOOL='nvim -d "$LOCAL" "$MERGED" "$BASE" "$REMOTE" \
  -c "wincmd w" -c "wincmd J"'

Merge file conventions

The workflow for handling conflicts doesn't involve much manual file management, but for debugging and setup purposes, it can be helpful to know the conventions for the files created during a merge.

Merging a topic branch into main will create the following files, corresponding to the variables above:

myProject/main> merge /topic
Merge tool argumentFileSource
$LOCAL<tmpdir>/main.uconflicted definitions on the current branch (main)
$REMOTE<tmpdir>/topic.uconflicted definitions on the incoming branch (topic)
$BASE<tmpdir>/main-topic-base.uconflicted definitions in the common ancestor of main and topic
$MERGEDmain-topic-merged.uthe output file to write the merged result to (main-topic-merged.u)

Of the files, only the main-topic-merged.u file is preserved after the merge completes, since it contains the final merged result.