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 비교

3-Way MergeBASE (공통 조상)LOCAL이변경했나?REMOTE가변경했나?MERGED (결과)2-Way MergeLOCAL어느 쪽이맞는가?REMOTE
구분 2-Way 3-Way
비교 대상 LOCAL vs REMOTE LOCAL vs BASE vs REMOTE
원본 확인 불가 BASE로 확인 가능
판단 근거 "어느 쪽?" (감으로) "누가 변경했는가?" (근거 있음)
자동 해결 어려움 한쪽만 변경 시 자동 가능

3-Way Merge의 창 구성

하단Neovim nvimdiff 레이아웃LOCAL(내 브랜치)BASE(공통 조상)REMOTE(상대 브랜치)MERGED(최종 결과 여기서 편집)
의미 역할
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 탭별 다른 뷰

실전 워크플로우

충돌 발생부터 해결까지

NoYesgit merge / rebase충돌 발생?자동 완료git status로 충돌 파일 확인git mergetool 실행Neovim 3-way diff 열림LOCAL/BASE/REMOTE 비교MERGED 창에서 편집:wqa로 저장 종료git add + commit / rebase--continue

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 한 줄이면 설정이 끝나므로, 아직 설정하지 않았다면 지금 바로 적용해보는 것을 권장한다.

Neovim3-way mergediffview.nvimGit 충돌mergetool