Mastering Three-Way Merge with Neovim, Fugitive, and Delta
Mastering Three-Way Merge with Neovim, Fugitive, and Delta
Table of Contents
- Introduction
- Installation
- Configuration
- Understanding Three-Way Merge
- Basic Operations
- Advanced Usage
- Real-World Example
- Pro Tips and Tricks
- Troubleshooting
Introduction
Three-way merging is a common challenge in Git workflows. This guide will show you how to set up and use a powerful combination of tools - Neovim/Vim with Fugitive plugin and Delta - to handle merge conflicts efficiently. This setup provides a terminal-based, keyboard-driven workflow that can significantly speed up your merge resolution process.
Installation
Prerequisites
- Git (2.x or higher)
- Neovim (0.5.x or higher) or Vim (8.x or higher)
- A package manager (we’ll use vim-plug in this guide)
Step 1: Install Delta
1
2
3
4
5
6
7
8
# macOS
brew install git-delta
# Ubuntu/Debian
apt install git-delta
# Arch Linux
pacman -S git-delta
Step 2: Install Fugitive
Add to your init.vim
or .vimrc
:
1
2
3
4
" Using vim-plug
call plug#begin()
Plug 'tpope/vim-fugitive'
call plug#end()
For Neovim with Lua:
1
2
3
4
5
6
7
8
-- Using Packer
use 'tpope/vim-fugitive'
-- Or using Lazy.nvim
{
'tpope/vim-fugitive',
cmd = { 'Git', 'G' }
}
Then install:
1
:PlugInstall
Configuration
Git Configuration
Add to your ~/.gitconfig
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[merge]
tool = nvimdiff
conflictstyle = diff3
[mergetool "nvimdiff"]
cmd = nvim -d $LOCAL $REMOTE $MERGED -c '$wincmd w' -c 'wincmd J'
[diff]
tool = nvimdiff
colorMoved = default
[core]
pager = delta
[delta]
navigate = true
light = false
side-by-side = true
line-numbers = true
syntax-theme = gruvbox-dark
file-style = bold yellow ul
file-decoration-style = none
hunk-header-style = file line-number syntax
Neovim Configuration
Add to your init.vim
or init.lua
:
1
2
3
4
5
6
7
8
9
10
-- Helpful keymaps for Git operations
vim.keymap.set('n', '<leader>gs', ':Git<CR>')
vim.keymap.set('n', '<leader>gd', ':Gdiffsplit<CR>')
vim.keymap.set('n', '<leader>gc', ':Git commit<CR>')
vim.keymap.set('n', '<leader>gb', ':Git blame<CR>')
vim.keymap.set('n', '<leader>gm', ':Git mergetool<CR>')
-- Improve diff experience
vim.opt.diffopt:append('algorithm:patience')
vim.opt.diffopt:append('indent-heuristic')
Understanding Three-Way Merge
In a three-way merge, you’re working with three versions of a file:
- LOCAL (your current branch changes)
- REMOTE (incoming changes from another branch)
- BASE (common ancestor of both branches)
The merged result becomes the MERGED version.
Basic Operations
Starting a Merge
1
2
3
4
5
# Start your merge
git merge feature-branch
# If conflicts occur, launch the merge tool
git mergetool
Window Layout
When you open the merge tool, you’ll see this layout:
1
2
3
4
5
6
7
8
9
10
+----------------+------------------+
| LOCAL | REMOTE |
| (your branch) | (their branch) |
+----------------+------------------+
| BASE |
| (common ancestor) |
+----------------+------------------+
| MERGED |
| (final result) |
+----------------+------------------+
Essential Commands
1
2
3
4
5
6
7
8
9
10
11
12
13
" Navigation
]c " Jump to next conflict
[c " Jump to previous conflict
" Resolving conflicts
:diffget //2 " Get changes from LOCAL
:diffget //3 " Get changes from REMOTE
do " Get changes from current window
dp " Put changes to other window
" Utility
:diffupdate " Refresh diff highlighting
:wqa " Save and exit all windows
Advanced Usage
Custom Functions
Add these to your Neovim config for enhanced functionality:
1
2
3
4
5
6
7
8
9
10
11
12
13
-- Quick conflict resolution
vim.keymap.set('n', '<leader>gj', ':diffget //3<CR>') -- get from right
vim.keymap.set('n', '<leader>gf', ':diffget //2<CR>') -- get from left
-- Show conflict stats
vim.cmd([[
function! ConflictStats()
let l:conflict_pattern = '^<<<<<<< '
let l:conflicts = search(l:conflict_pattern, 'n')
echo "Remaining conflicts: " . l:conflicts
endfunction
]])
vim.keymap.set('n', '<leader>gc', ':call ConflictStats()<CR>')
Real-World Example
Let’s walk through resolving a merge conflict:
- Create a conflict scenario: ```bash
Create a test repository
git init test-merge cd test-merge
Create initial file
echo “Initial content” > file.txt git add file.txt git commit -m “Initial commit”
Create and modify branch1
git checkout -b branch1 echo “Branch 1 changes” » file.txt git commit -am “Branch 1 changes”
Create and modify branch2 from main
git checkout main git checkout -b branch2 echo “Branch 2 changes” » file.txt git commit -am “Branch 2 changes”
Try to merge
git checkout main git merge branch1 git merge branch2 # This will create a conflict
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2. Resolve the conflict:
```bash
# Open merge tool
git mergetool
# In Neovim:
# 1. Navigate to conflict with ]c
# 2. Review changes in each window
# 3. Choose changes:
# - :diffget //2 for LOCAL changes
# - :diffget //3 for REMOTE changes
# 4. Save with :w
# 5. Exit with :wqa
# Complete the merge
git add .
git commit -m "Merge branch2: resolve conflicts"
Pro Tips and Tricks
- Create shell aliases:
1 2 3
# Add to ~/.bashrc or ~/.zshrc alias gmt='git mergetool' alias gd='git difftool'
- Use Fugitive’s blame feature:
1
:Git blame " Show blame information
- Quick diff review:
1
:Gdiffsplit " Open current file in diff view
- Customize the merge view:
1 2 3 4
" Adjust diff colors highlight DiffAdd cterm=bold ctermfg=10 ctermbg=17 gui=none guifg=bg guibg=Red highlight DiffDelete cterm=bold ctermfg=10 ctermbg=17 gui=none guifg=bg guibg=Red highlight DiffChange cterm=bold ctermfg=10 ctermbg=17 gui=none guifg=bg guibg=Red
Troubleshooting
Common Issues and Solutions
- Delta not working
- Check if Delta is installed:
delta --version
- Verify Git config:
git config --get core.pager
- Check if Delta is installed:
- Merge tool not opening
- Check Git merge tool setting:
git config --get merge.tool
- Verify Neovim installation:
nvim --version
- Check Git merge tool setting:
- Diff colors not showing
- Check terminal color support:
echo $TERM
- Try setting:
set termguicolors
in Neovim
- Check terminal color support:
Best Practices
- Always review the entire file before saving
- Use
git status
to verify all conflicts are resolved - Write clear commit messages explaining merge decisions
- Take advantage of Fugitive’s
:Git log
to understand change history
Remember to complete your merge with:
1
2
git add .
git commit -m "Merge complete: [describe resolution strategy]"
This setup provides a powerful, efficient workflow for handling merge conflicts. With practice, you’ll find that resolving conflicts becomes much faster and more intuitive using these tools together.