When solving git conflicts with git mergetool as explained in 2025-11-21-21-40-resolve-git-conflicts-easily, there are situations where I figured-out the conflit, but I still need to remove the conflict markers.
Turns out, vim has a native way to do so with :diffget REMOTE or :diffget BASE or :diffget LOCAL (see Vim: diff.txt).
But which is which ? When looking at a diff3 conflict, the conflict markers only mention HEAD, \(COMMID_ID or \)BRANCH_NAME:
class Square : public Rectangle {
public:
<<<<<<< HEAD
static Rectangle* DefaultEmptyObject(); // (1)
||||||| 8134ad7
//this comment is a bit outdated // (2)
static Rectangle* DefaultEmpty();
=======
//this comment is a bit outdated // (3)
static const Rectangle* DefaultEmpty();
>>>>>>> new-branch
};
Imagine a world where vim would annotate conflict markers with the appropriate names:
class Square : public Rectangle {
public:
<<<<<<< HEAD (LOCAL/ours)
static Rectangle* DefaultEmptyObject(); // (1)
||||||| 8134ad7 (BASE)
//this comment is a bit outdated // (2)
static Rectangle* DefaultEmpty();
======= (REMOTE/theirs)
//this comment is a bit outdated // (3)
static const Rectangle* DefaultEmpty();
>>>>>>> new-branch
};
Imagine no more! Here is a .vimrc snippet that make use of vim textprops to add those annotations:
" ---------------------------------------------------------------------------
" Git conflict marker virtual labels via text properties (vanilla Vim).
" ---------------------------------------------------------------------------
" ---- Highlight groups --------------------
augroup GitConflictVirtTextHl
autocmd!
autocmd ColorScheme * call s:DefineHighlight()
augroup END
function! s:DefineHighlight() abort
highlight default link GitConflictVirtTextLocal DiffText
highlight default link GitConflictVirtTextBase DiffChange
highlight default link GitConflictVirtTextSep DiffChange
highlight default link GitConflictVirtTextRemote DiffText
endfunction
call s:DefineHighlight()
" ---- Property types --------------------------------------------------------
function! s:EnsurePropTypes() abort
if exists('b:git_conflict_prop_types_defined') && b:git_conflict_prop_types_defined
return
endif
" Define types (global) and ignore "already exists" errors.
for [l:name, l:hl] in [
\ ['GitConflictLocal', 'GitConflictVirtTextLocal'],
\ ['GitConflictBase', 'GitConflictVirtTextBase'],
\ ['GitConflictSep', 'GitConflictVirtTextSep'],
\ ['GitConflictRemote', 'GitConflictVirtTextRemote'],
\ ]
try
call prop_type_add(l:name, {'highlight': l:hl, 'combine': v:true})
catch /^Vim\%((\a\+)\)\=:E969/ " E969: Property type already exists
" fine
endtry
endfor
let b:git_conflict_prop_types_defined = 1
endfunction
function! GitConflictClearVirtText() abort
if !has('textprop') | return | endif
call s:EnsurePropTypes()
" Remove only our types.
for t in ['GitConflictLocal', 'GitConflictBase', 'GitConflictSep', 'GitConflictRemote']
call prop_remove({'type': t, 'all': 1})
endfor
endfunction
function! GitConflictAnnotateVirtText() abort
if !has('textprop') | return | endif
call s:EnsurePropTypes()
" Clean slate first, otherwise edits can leave stale labels behind.
call GitConflictClearVirtText()
let l:last = line('$')
for lnum in range(1, l:last)
let l:line = getline(lnum)
if l:line =~# '^<<<<<<<\s\+'
call s:AddVirtText(lnum, 'GitConflictLocal', ' (LOCAL/ours)')
elseif l:line =~# '^=======$'
call s:AddVirtText(lnum, 'GitConflictBase', ' (BASE)')
elseif l:line =~# '^>>>>>>>\s\+'
call s:AddVirtText(lnum, 'GitConflictRemote', ' (REMOTE/theirs)')
endif
endif
endfor
endfunction
function! s:AddVirtText(lnum, type, text) abort
call prop_add(a:lnum, 0, {
\ 'type': a:type,
\ 'text': a:text,
\ 'text_align': 'after',
\ })
endfunction
" ---- Auto-run only when conflict markers exist -----------------------------
function! s:MaybeAnnotate() abort
" Fast pre-check: only run if any marker exists in a small scan window,
" otherwise full-buffer scanning on every TextChanged can be costly.
" If you prefer, remove this and always annotate.
let l:max = min([line('$'), 500])
for lnum in range(1, l:max)
let l = getline(lnum)
if l =~# '^<<<<<<<\s\+' || l =~# '^>>>>>>>\s\+' || l =~# '^=======$' || l =~# '^|||||||\s\+'
call GitConflictAnnotateVirtText()
return
endif
endfor
" No markers found (at least early in file): clear any stale props.
call GitConflictClearVirtText()
endfunction
augroup GitConflictVirtText
autocmd!
" Run on load and when entering the buffer/window.
autocmd BufReadPost,BufWinEnter * call s:MaybeAnnotate()
" Update after edits. (You can remove TextChangedI if you prefer.)
" autocmd TextChanged,TextChangedI * call s:MaybeAnnotate()
augroup END
" Optional user commands.
command! GitConflictVirtTextOn call GitConflictAnnotateVirtText()
command! GitConflictVirtTextOff call GitConflictClearVirtText()