1 " ============================================================================
3 " Description: Show diff for current buffer
4 " Maintainer: Clemens Buchacher <drizzd@aon.at>
7 " ============================================================================
8 if exists('loaded_diffit')
13 let s:diffit_version = '0'
15 "for line continuation - i.e dont want C in &cpo
19 map <silent> <Leader>d :call <SID>Diffit()<CR>
23 echon 'diffit: ' . a:msg
28 echon 'diffit: ' . a:msg
32 call s:Error('fatal: ' . a:msg)
36 function s:System(...)
37 let out = system(join(a:000))
39 call s:Die(a:0 . ' failed: ' . out)
46 for n in range(1, line('$'))
48 if l =~ '^diff --git ' ||
49 \l =~ '^diff --cc ' ||
50 \l =~ '^diff --combined ' ||
62 return [header, n - 1]
68 call winrestview(view)
72 if exists('b:diffit') && b:diffit == 1
79 if exists('b:diffit') && b:diffit == 1
85 function s:Read_diff(pathlist)
88 for path in a:pathlist
89 let out = s:System('git diff', '--', path, '>', diff)
97 function s:Write_diff(diff, orig)
100 silent exe 'read ' . a:diff
102 setlocal nomodifiable
105 let orig_pos = b:view['lnum']
106 let new_pos = s:Diffpos(orig_pos)
107 let view = copy(b:view)
108 let view['lnum'] = abs(new_pos)
110 let view['topline'] += new_pos - orig_pos
111 let view['topline'] = max([1, view['topline']])
113 let view['topline'] = -new_pos - 4
115 let view['curswant'] += 1
117 call winrestview(view)
119 call cursor(abs(s:Diffpos(0)), 1)
123 function s:Diffit_init()
125 let out = s:System('git rev-parse', '--is-inside-work-tree')
126 if v:shell_error == 128 || split(out)[0] != 'true'
127 call s:Error('not inside work tree')
130 call s:Error('git rev-parse failed: ' . out)
133 let out = s:System('git diff', '--name-only')
134 let pathlist = split(out, '\n')
136 call s:Info('no changes')
139 let orig_path = bufname('%')
140 let k = index(pathlist, orig_path)
142 call remove(pathlist, k)
143 call insert(pathlist, orig_path, 0)
145 let [diff, path] = s:Read_diff(pathlist)
146 let orig = path == orig_path
147 if getfsize(diff) == 0
148 call s:Info('no changes')
152 let view = winsaveview()
153 silent! exe 'edit ' . tempname()
154 let b:pathlist = pathlist
158 if !exists('b:current_syntax')
162 setlocal buftype=nofile
164 setlocal foldcolumn=0
169 nnoremap <silent> <buffer> s :call <SID>Stage_hunk(line('.'))<CR>
170 nnoremap <silent> <buffer> d :call <SID>Next_diff()<CR>
172 call s:Write_diff(diff, orig)
173 echon '"' . path . '"'
176 function s:Next_diff()
177 call remove(b:pathlist, 0)
178 let [diff, path] = s:Read_diff(b:pathlist)
179 if getfsize(diff) > 0
180 call s:Write_diff(diff, 0)
181 echon '"' . path . '"'
187 function s:Diffpos(orig_pos)
192 while search('^@@', 'W') > 0
193 let [start, length] = matchlist(getline('.'),
194 \'^@@ -[0-9]*,[0-9]* +\%(\([0-9]*\),\)\?\([0-9]*\)')[1:2]
199 let diffpos = -line('.')
201 if start > a:orig_pos
204 let diffpos = line('.')
205 let hunk_start = start
206 let hunk_end = hunk_start + length - 1
211 let pos = hunk_start - 1
212 let target_pos = min([a:orig_pos, hunk_end])
213 while diffpos < line('$')
214 if getline(diffpos) =~ '^-'
225 if getline(diffpos) =~ '^-'
232 function s:Stage_hunk(pos)
233 call cursor(a:pos, 1)
234 let h_start = search('^@@', 'bcW')
238 call cursor(h_start, 1)
239 let h_end = search('^@@', 'nW')-1
241 let h_end = line('$')
243 let h_range = h_start . ',' . h_end
245 let [patch, header_end] = s:Header()
246 call extend(patch, getline(h_start, h_end))
247 let patchfile = tempname()
248 call writefile(patch, patchfile)
249 let out = s:System('git apply', '--cached', '--whitespace=nowarn', patchfile)
252 silent exe h_range . 'delete _'
253 setlocal nomodifiable
254 if line('$') == header_end