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.

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.
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 argument | File | Source |
$LOCAL | <tmpdir>/main.u | conflicted definitions on the current branch (main) |
$REMOTE | <tmpdir>/topic.u | conflicted definitions on the incoming branch (topic) |
$BASE | <tmpdir>/main-topic-base.u | conflicted definitions in the common ancestor of main and topic |
$MERGED | main-topic-merged.u | the 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.