Neovim 3-Way Merge — LOCAL·BASE·REMOTE를 한눈에 보고 충돌을 판단하는 법
Neovim의 nvimdiff와 diffview.nvim으로 3-way merge를 설정하고, LOCAL·BASE·REMOTE 비교로 Git 충돌을 해결하는 실전 워크플로우
2026-08-12 · 최초 발행 2026-04-17
Git 충돌이 발생하면 대부분 에디터에서 <<<<<<< 마커를 직접 편집하며 해결한다. 하지만 복잡한 충돌에서는 "원본이 뭐였는지"를 모르면 올바른 판단이 어렵다. 3-way merge는 LOCAL(내 변경), REMOTE(상대 변경), BASE(공통 조상)를 동시에 보여주어 충돌의 맥락을 완전하게 파악할 수 있게 한다. Neovim은 내장 diff 모드(nvimdiff)와 플러그인(diffview.nvim)으로 이 환경을 제공한다.
3-Way Merge 개념
2-Way vs 3-Way 비교
| 구분 | 2-Way | 3-Way |
|---|---|---|
| 비교 대상 | LOCAL vs REMOTE | LOCAL vs BASE vs REMOTE |
| 원본 확인 | 불가 | BASE로 확인 가능 |
| 판단 근거 | "어느 쪽?" (감으로) | "누가 변경했는가?" (근거 있음) |
| 자동 해결 | 어려움 | 한쪽만 변경 시 자동 가능 |
3-Way Merge의 창 구성
| 창 | 의미 | 역할 |
|---|---|---|
| LOCAL | 현재 브랜치의 파일 | 내가 변경한 내용 확인 |
| BASE | 두 브랜치의 공통 조상 | 원래 코드 확인 (판단 기준) |
| REMOTE | 병합 대상 브랜치의 파일 | 상대가 변경한 내용 확인 |
| MERGED | 최종 결과물 | 이 창에서만 편집 |
Git Mergetool 설정
기본 설정
# Neovim을 기본 mergetool로 설정
git config --global merge.tool nvimdiff
# 레이아웃 설정 (LOCAL, BASE, REMOTE 순서)
git config --global mergetool.nvimdiff.layout "LOCAL,BASE,REMOTE / MERGED"
# 백업 파일(.orig) 생성 비활성화
git config --global mergetool.keepBackup false
# mergetool 실행 전 확인 프롬프트 비활성화
git config --global mergetool.prompt false
~/.gitconfig 확인
설정이 잘 반영됐는지 파일로 직접 확인할 수 있다.
[merge]
tool = nvimdiff
[mergetool]
keepBackup = false
prompt = false
[mergetool "nvimdiff"]
layout = "LOCAL,BASE,REMOTE / MERGED"
레이아웃 옵션
| 레이아웃 | 설명 |
|---|---|
LOCAL,BASE,REMOTE / MERGED |
상단 3분할 + 하단 결과 (권장) |
LOCAL,MERGED,REMOTE |
3분할 수평 (BASE 생략) |
MERGED |
결과만 표시 (충돌 마커 직접 편집) |
LOCAL,BASE,REMOTE / MERGED + BASE,LOCAL + BASE,REMOTE |
탭별 다른 뷰 |
실전 워크플로우
충돌 발생부터 해결까지
Step 1: 충돌 확인
$ git merge feature-branch
Auto-merging src/config.py
CONFLICT (content): Merge conflict in src/config.py
Automatic merge failed; fix conflicts and then commit the result.
$ git status
Unmerged paths:
both modified: src/config.py
Step 2: Mergetool 실행
git mergetool
Neovim이 4개 창으로 열린다.
┌──────────┬──────────┬──────────┐
│ LOCAL │ BASE │ REMOTE │
│ (내 코드) │ (원본) │ (상대 코드)│
├──────────┴──────────┴──────────┤
│ MERGED │
│ (여기서 편집!) │
└─────────────────────────────────┘
Step 3: 창 이동 및 비교
| 키 | 동작 |
|---|---|
Ctrl-w h |
왼쪽 창으로 이동 |
Ctrl-w j |
아래 창으로 이동 |
Ctrl-w k |
위 창으로 이동 |
Ctrl-w l |
오른쪽 창으로 이동 |
]c |
다음 diff 블록으로 이동 |
[c |
이전 diff 블록으로 이동 |
Ctrl-w = |
모든 창 크기 균등 분할 |
Step 4: 충돌 해결 (MERGED 창에서)
MERGED 창으로 이동(Ctrl-w j)한 뒤 아래 명령을 사용한다.
| 명령 | 동작 |
|---|---|
:diffget LOCAL |
LOCAL 버전 가져오기 |
:diffget BASE |
BASE 버전 가져오기 |
:diffget REMOTE |
REMOTE 버전 가져오기 |
:diffget LO |
LOCAL 약어 |
:diffget BA |
BASE 약어 |
:diffget RE |
REMOTE 약어 |
Visual 모드로 특정 영역만 선택하여 :diffget을 실행할 수도 있다.
" MERGED 창에서 충돌 블록 위에 커서를 놓고:
:diffget LO " 이 블록을 LOCAL 버전으로 교체
Step 5: 직접 편집
양쪽 변경을 모두 반영하거나 새로운 코드를 작성해야 하는 경우 충돌 마커를 삭제하고 직접 편집한다.
# 충돌 마커 (MERGED 창에 표시)
<<<<<<< HEAD
timeout = 30
||||||| (base)
timeout = 10
=======
timeout = 60
>>>>>>> feature-branch
# 직접 편집 결과
timeout = 45 # 둘 다 반영하여 새 값 결정
Step 6: 저장 및 종료
" 모든 창 저장 후 종료
:wqa
" 또는 MERGED 창만 저장 후 전체 종료
:w " MERGED 저장
:qa " 모든 창 종료
Step 7: 완료
# merge의 경우
git commit
# rebase의 경우
git rebase --continue
유용한 Neovim 설정
Diff 관련 옵션
~/.config/nvim/init.lua에 추가할 수 있는 설정이다.
-- diff 모드 설정
vim.opt.diffopt:append("vertical") -- diff를 수직 분할로 표시
vim.opt.diffopt:append("algorithm:patience") -- patience diff 알고리즘 (더 정확)
vim.opt.diffopt:append("indent-heuristic") -- 들여쓰기 기반 휴리스틱
-- diff 모드 진입 시 자동 설정
vim.api.nvim_create_autocmd("OptionSet", {
pattern = "diff",
callback = function()
if vim.v.option_new == "1" then
vim.opt_local.wrap = false
vim.opt_local.cursorline = true
end
end,
})
init.vim 사용자의 경우이다.
set diffopt+=vertical
set diffopt+=algorithm:patience
set diffopt+=indent-heuristic
autocmd OptionSet diff if v:option_new | setlocal nowrap cursorline | endif
키맵 추가
-- diff 모드 전용 키맵
vim.api.nvim_create_autocmd("OptionSet", {
pattern = "diff",
callback = function()
if vim.v.option_new == "1" then
local opts = { buffer = true, silent = true }
-- 충돌 블록 이동
vim.keymap.set("n", "<leader>gn", "]c", opts) -- 다음 충돌
vim.keymap.set("n", "<leader>gp", "[c", opts) -- 이전 충돌
-- diffget 단축키
vim.keymap.set("n", "<leader>gl", ":diffget LO<CR>", opts) -- LOCAL 선택
vim.keymap.set("n", "<leader>gr", ":diffget RE<CR>", opts) -- REMOTE 선택
vim.keymap.set("n", "<leader>gb", ":diffget BA<CR>", opts) -- BASE 선택
end
end,
})
diffview.nvim 플러그인
내장 nvimdiff보다 더 강력한 UI를 제공하는 플러그인이다.
설치 (lazy.nvim)
{
"sindrets/diffview.nvim",
dependencies = { "nvim-lua/plenary.nvim" },
cmd = { "DiffviewOpen", "DiffviewFileHistory" },
keys = {
{ "<leader>do", "<cmd>DiffviewOpen<cr>", desc = "Diffview Open" },
{ "<leader>dc", "<cmd>DiffviewClose<cr>", desc = "Diffview Close" },
{ "<leader>dh", "<cmd>DiffviewFileHistory<cr>", desc = "Diffview File History" },
},
opts = {
enhanced_diff_hl = true,
view = {
merge_tool = {
layout = "diff3_mixed", -- 3-way mixed 레이아웃
disable_diagnostics = true,
},
},
},
}
충돌 해결 워크플로우
# 충돌 발생 후
git mergetool # 대신 Neovim 안에서:
" Neovim 내에서 실행
:DiffviewOpen
" 충돌 파일 목록이 좌측 패널에 표시
" 파일 선택 → 3-way diff 자동 표시
" 충돌 해결 명령
" 커서가 충돌 블록 위에 있을 때:
]x " 다음 충돌으로 이동
[x " 이전 충돌으로 이동
" 특정 버전 선택 (충돌 블록 위에서)
dp " LOCAL 또는 REMOTE 쪽에서 실행하면 해당 버전으로 put
" 완료 후
:DiffviewClose
diffview.nvim vs nvimdiff 비교
| 기능 | nvimdiff (내장) | diffview.nvim |
|---|---|---|
| 설치 | 불필요 | 플러그인 필요 |
| 파일 탐색 | 없음 (mergetool이 순차 실행) | 좌측 패널에서 선택 |
| 3-way diff | 지원 | 지원 (더 나은 UI) |
| Git log 통합 | 없음 | :DiffviewFileHistory |
| 충돌 네비게이션 | ]c / [c |
]x / [x + 하이라이트 |
| 학습 곡선 | 낮음 | 중간 |
자주 쓰는 명령 치트시트
Git 명령
# mergetool 실행
git mergetool
# 특정 파일만 mergetool
git mergetool src/config.py
# merge 취소 (충돌 해결 전)
git merge --abort
# rebase 취소
git rebase --abort
# 충돌 파일 목록
git diff --name-only --diff-filter=U
Neovim diff 명령
" 기본 이동
]c " 다음 diff 블록
[c " 이전 diff 블록
" 가져오기/보내기
:diffget LO " LOCAL 버전 가져오기
:diffget RE " REMOTE 버전 가져오기
:diffget BA " BASE 버전 가져오기
:diffput " 현재 창의 내용을 상대 창으로 보내기
" diff 갱신 (편집 후 하이라이트 재계산)
:diffupdate
" diff 모드 토글
:diffthis " 현재 버퍼를 diff에 포함
:diffoff " 현재 버퍼를 diff에서 제외
:diffoff! " 모든 창에서 diff 해제
" 저장 및 종료
:wqa " 모든 창 저장 후 종료
:cq " 변경 취소 후 종료 (mergetool에 실패 반환)
마무리
3-way merge는 LOCAL, BASE, REMOTE 세 버전을 동시에 비교해 충돌의 맥락을 정확히 파악할 수 있게 해준다. 내장 nvimdiff만으로도 충분히 강력하며, diffview.nvim을 추가하면 파일 탐색과 Git 히스토리 통합까지 가능해진다. git config --global merge.tool nvimdiff 한 줄이면 설정이 끝나므로, 아직 설정하지 않았다면 지금 바로 적용해보는 것을 권장한다.