1 " fugitive.vim - A Git wrapper so awesome, it should be illegal
2 " Maintainer: Tim Pope <http://tpo.pe/>
4 " GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim
6 if exists('g:loaded_fugitive') || &cp
9 let g:loaded_fugitive = 1
11 if !exists('g:fugitive_git_executable')
12 let g:fugitive_git_executable = 'git'
17 function! s:function(name) abort
18 return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),''))
21 function! s:sub(str,pat,rep) abort
22 return substitute(a:str,'\v\C'.a:pat,a:rep,'')
25 function! s:gsub(str,pat,rep) abort
26 return substitute(a:str,'\v\C'.a:pat,a:rep,'g')
29 function! s:shellesc(arg) abort
30 if a:arg =~ '^[A-Za-z0-9_/.-]\+$'
32 elseif &shell =~# 'cmd' && a:arg !~# '"'
35 return shellescape(a:arg)
39 function! s:fnameescape(file) abort
40 if exists('*fnameescape')
41 return fnameescape(a:file)
43 return escape(a:file," \t\n*?[{`$\\%#'\"|!<")
47 function! s:throw(string) abort
48 let v:errmsg = 'fugitive: '.a:string
56 let v:warningmsg = a:str
59 function! s:shellslash(path)
60 if exists('+shellslash') && !&shellslash
61 return s:gsub(a:path,'\\','/')
68 let rev = s:buffer().rev()
70 return matchstr(getline('.'),'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (new commits)\)\=$\|^\d\{6} \x\{40\} \d\t\zs.*')
75 function! s:add_methods(namespace, method_names) abort
76 for name in a:method_names
77 let s:{a:namespace}_prototype[name] = s:function('s:'.a:namespace.'_'.name)
82 function! s:command(definition) abort
83 let s:commands += [a:definition]
86 function! s:define_commands()
87 for command in s:commands
88 exe 'command! -buffer '.command
92 function! s:compatibility_check()
93 if exists('b:git_dir') && exists('*GitBranchInfoCheckGitDir') && !exists('g:fugitive_did_compatibility_warning')
94 let g:fugitive_did_compatibility_warning = 1
95 call s:warn("See http://github.com/tpope/vim-fugitive/issues#issue/1 for why you should remove git-branch-info.vim")
99 augroup fugitive_utility
101 autocmd User Fugitive call s:define_commands()
102 autocmd VimEnter * call s:compatibility_check()
105 let s:abstract_prototype = {}
108 " Initialization {{{1
110 function! s:ExtractGitDir(path) abort
111 let path = s:shellslash(a:path)
112 if path =~? '^fugitive://.*//'
113 return matchstr(path,'fugitive://\zs.\{-\}\ze//')
115 let fn = fnamemodify(path,':s?[\/]$??')
119 if filereadable(fn . '/.git/HEAD')
120 return s:sub(simplify(fnamemodify(fn . '/.git',':p')),'\W$','')
121 elseif fn =~ '\.git$' && filereadable(fn . '/HEAD')
122 return s:sub(simplify(fnamemodify(fn,':p')),'\W$','')
125 let fn = fnamemodify(ofn,':h')
130 function! s:Detect(path)
131 if exists('b:git_dir') && b:git_dir ==# ''
134 if !exists('b:git_dir')
135 let dir = s:ExtractGitDir(a:path)
140 if exists('b:git_dir')
141 silent doautocmd User Fugitive
142 cnoremap <expr> <buffer> <C-R><C-G> <SID>recall()
143 let buffer = fugitive#buffer()
144 if expand('%:p') =~# '//'
145 call buffer.setvar('&path',s:sub(buffer.getvar('&path'),'^\.%(,|$)',''))
147 if stridx(buffer.getvar('&tags'),escape(b:git_dir.'/tags',', ')) == -1
148 call buffer.setvar('&tags',escape(b:git_dir.'/tags',', ').','.buffer.getvar('&tags'))
150 call buffer.setvar('&tags',escape(b:git_dir.'/'.&filetype.'.tags',', ').','.buffer.getvar('&tags'))
158 autocmd BufNewFile,BufReadPost * call s:Detect(expand('<amatch>:p'))
159 autocmd FileType netrw call s:Detect(expand('<afile>:p'))
160 autocmd VimEnter * if expand('<amatch>')==''|call s:Detect(getcwd())|endif
161 autocmd BufWinLeave * execute getwinvar(+winnr(), 'fugitive_leave')
167 let s:repo_prototype = {}
170 function! s:repo(...) abort
171 let dir = a:0 ? a:1 : (exists('b:git_dir') && b:git_dir !=# '' ? b:git_dir : s:ExtractGitDir(expand('%:p')))
173 if has_key(s:repos,dir)
174 let repo = get(s:repos,dir)
176 let repo = {'git_dir': dir}
177 let s:repos[dir] = repo
179 return extend(extend(repo,s:repo_prototype,'keep'),s:abstract_prototype,'keep')
181 call s:throw('not a git repository: '.expand('%:p'))
184 function! s:repo_dir(...) dict abort
185 return join([self.git_dir]+a:000,'/')
188 function! s:repo_tree(...) dict abort
190 let dir = fnamemodify(self.git_dir,':h')
191 return join([dir]+a:000,'/')
193 call s:throw('no work tree')
196 function! s:repo_bare() dict abort
197 return self.dir() !~# '/\.git$'
200 function! s:repo_translate(spec) dict abort
201 if a:spec ==# '.' || a:spec ==# '/.'
202 return self.bare() ? self.dir() : self.tree()
203 elseif a:spec =~# '^/'
204 return fnamemodify(self.dir(),':h').a:spec
205 elseif a:spec =~# '^:[0-3]:'
206 return 'fugitive://'.self.dir().'//'.a:spec[1].'/'.a:spec[3:-1]
207 elseif a:spec ==# ':'
208 if $GIT_INDEX_FILE =~# '/[^/]*index[^/]*\.lock$' && fnamemodify($GIT_INDEX_FILE,':p')[0:strlen(s:repo().dir())] ==# s:repo().dir('') && filereadable($GIT_INDEX_FILE)
209 return fnamemodify($GIT_INDEX_FILE,':p')
211 return self.dir('index')
213 elseif a:spec =~# '^:/'
214 let ref = self.rev_parse(matchstr(a:spec,'.[^:]*'))
215 return 'fugitive://'.self.dir().'//'.ref
216 elseif a:spec =~# '^:'
217 return 'fugitive://'.self.dir().'//0/'.a:spec[1:-1]
218 elseif a:spec =~# 'HEAD\|^refs/' && a:spec !~ ':' && filereadable(self.dir(a:spec))
219 return self.dir(a:spec)
220 elseif filereadable(s:repo().dir('refs/'.a:spec))
221 return self.dir('refs/'.a:spec)
222 elseif filereadable(s:repo().dir('refs/tags/'.a:spec))
223 return self.dir('refs/tags/'.a:spec)
224 elseif filereadable(s:repo().dir('refs/heads/'.a:spec))
225 return self.dir('refs/heads/'.a:spec)
226 elseif filereadable(s:repo().dir('refs/remotes/'.a:spec))
227 return self.dir('refs/remotes/'.a:spec)
228 elseif filereadable(s:repo().dir('refs/remotes/'.a:spec.'/HEAD'))
229 return self.dir('refs/remotes/'.a:spec,'/HEAD')
232 let ref = self.rev_parse(matchstr(a:spec,'[^:]*'))
233 let path = s:sub(matchstr(a:spec,':.*'),'^:','/')
234 return 'fugitive://'.self.dir().'//'.ref.path
236 return self.tree(a:spec)
241 call s:add_methods('repo',['dir','tree','bare','translate'])
243 function! s:repo_git_command(...) dict abort
244 let git = g:fugitive_git_executable . ' --git-dir='.s:shellesc(self.git_dir)
245 return git.join(map(copy(a:000),'" ".s:shellesc(v:val)'),'')
248 function! s:repo_git_chomp(...) dict abort
249 return s:sub(system(call(self.git_command,a:000,self)),'\n$','')
252 function! s:repo_git_chomp_in_tree(...) dict abort
253 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
256 execute cd.'`=s:repo().tree()`'
257 return call(s:repo().git_chomp, a:000, s:repo())
263 function! s:repo_rev_parse(rev) dict abort
264 let hash = self.git_chomp('rev-parse','--verify',a:rev)
265 if hash =~ '\<\x\{40\}$'
266 return matchstr(hash,'\<\x\{40\}$')
268 call s:throw('rev-parse '.a:rev.': '.hash)
271 call s:add_methods('repo',['git_command','git_chomp','git_chomp_in_tree','rev_parse'])
273 function! s:repo_dirglob(base) dict abort
274 let base = s:sub(a:base,'^/','')
275 let matches = split(glob(self.tree(s:gsub(base,'/','*&').'*/')),"\n")
276 call map(matches,'v:val[ strlen(self.tree())+(a:base !~ "^/") : -1 ]')
280 function! s:repo_superglob(base) dict abort
281 if a:base =~# '^/' || a:base !~# ':'
284 let heads = ["HEAD","ORIG_HEAD","FETCH_HEAD","MERGE_HEAD"]
285 let heads += sort(split(s:repo().git_chomp("rev-parse","--symbolic","--branches","--tags","--remotes"),"\n"))
286 call filter(heads,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base')
290 let base = s:sub(a:base,'^/','')
291 let matches = split(glob(self.tree(s:gsub(base,'/','*&').'*')),"\n")
292 call map(matches,'s:shellslash(v:val)')
293 call map(matches,'v:val !~ "/$" && isdirectory(v:val) ? v:val."/" : v:val')
294 call map(matches,'v:val[ strlen(self.tree())+(a:base !~ "^/") : -1 ]')
295 let results += matches
299 elseif a:base =~# '^:'
300 let entries = split(self.git_chomp('ls-files','--stage'),"\n")
301 call map(entries,'s:sub(v:val,".*(\\d)\\t(.*)",":\\1:\\2")')
302 if a:base !~# '^:[0-3]\%(:\|$\)'
303 call filter(entries,'v:val[1] == "0"')
304 call map(entries,'v:val[2:-1]')
306 call filter(entries,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base')
310 let tree = matchstr(a:base,'.*[:/]')
311 let entries = split(self.git_chomp('ls-tree',tree),"\n")
312 call map(entries,'s:sub(v:val,"^04.*\\zs$","/")')
313 call map(entries,'tree.s:sub(v:val,".*\t","")')
314 return filter(entries,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base')
318 call s:add_methods('repo',['dirglob','superglob'])
320 function! s:repo_config(conf) dict abort
321 return matchstr(system(s:repo().git_command('config').' '.a:conf),"[^\r\n]*")
324 function! s:repo_user() dict abort
325 let username = s:repo().config('user.name')
326 let useremail = s:repo().config('user.email')
327 return username.' <'.useremail.'>'
330 function! s:repo_aliases() dict abort
331 if !has_key(self,'_aliases')
332 let self._aliases = {}
333 for line in split(self.git_chomp('config','--get-regexp','^alias[.]'),"\n")
334 let self._aliases[matchstr(line,'\.\zs\S\+')] = matchstr(line,' \zs.*')
340 call s:add_methods('repo',['config', 'user', 'aliases'])
342 function! s:repo_keywordprg() dict abort
343 let args = ' --git-dir='.escape(self.dir(),"\\\"' ").' show'
344 if has('gui_running') && !has('win32')
345 return g:fugitive_git_executable . ' --no-pager' . args
347 return g:fugitive_git_executable . args
351 call s:add_methods('repo',['keywordprg'])
356 let s:buffer_prototype = {}
358 function! s:buffer(...) abort
359 let buffer = {'#': bufnr(a:0 ? a:1 : '%')}
360 call extend(extend(buffer,s:buffer_prototype,'keep'),s:abstract_prototype,'keep')
361 if buffer.getvar('git_dir') !=# ''
364 call s:throw('not a git repository: '.expand('%:p'))
367 function! fugitive#buffer(...) abort
368 return s:buffer(a:0 ? a:1 : '%')
371 function! s:buffer_getvar(var) dict abort
372 return getbufvar(self['#'],a:var)
375 function! s:buffer_setvar(var,value) dict abort
376 return setbufvar(self['#'],a:var,a:value)
379 function! s:buffer_getline(lnum) dict abort
380 return getbufline(self['#'],a:lnum)[0]
383 function! s:buffer_repo() dict abort
384 return s:repo(self.getvar('git_dir'))
387 function! s:buffer_type(...) dict abort
388 if self.getvar('fugitive_type') != ''
389 let type = self.getvar('fugitive_type')
390 elseif fnamemodify(self.spec(),':p') =~# '.\git/refs/\|\.git/\w*HEAD$'
392 elseif self.getline(1) =~ '^tree \x\{40\}$' && self.getline(2) == ''
394 elseif self.getline(1) =~ '^\d\{6\} \w\{4\} \x\{40\}\>\t'
396 elseif self.getline(1) =~ '^\d\{6\} \x\{40\}\> \d\t'
398 elseif isdirectory(self.spec())
399 let type = 'directory'
400 elseif self.spec() == ''
406 return !empty(filter(copy(a:000),'v:val ==# type'))
414 function! s:buffer_spec() dict abort
415 let bufname = bufname(self['#'])
417 for i in split(bufname,'[^:]\zs\\')
418 let retval = fnamemodify((retval==''?'':retval.'\').i,':.')
420 return s:shellslash(fnamemodify(retval,':p'))
425 function! s:buffer_spec() dict abort
426 let bufname = bufname(self['#'])
427 return s:shellslash(bufname == '' ? '' : fnamemodify(bufname,':p'))
432 function! s:buffer_name() dict abort
436 function! s:buffer_commit() dict abort
437 return matchstr(self.spec(),'^fugitive://.\{-\}//\zs\w*')
440 function! s:buffer_path(...) dict abort
441 let rev = matchstr(self.spec(),'^fugitive://.\{-\}//\zs.*')
443 let rev = s:sub(rev,'\w*','')
445 let rev = self.spec()[strlen(self.repo().tree()) : -1]
447 return s:sub(s:sub(rev,'.\zs/$',''),'^/',a:0 ? a:1 : '')
450 function! s:buffer_rev() dict abort
451 let rev = matchstr(self.spec(),'^fugitive://.\{-\}//\zs.*')
453 return ':'.rev[0].':'.rev[2:-1]
455 return s:sub(rev,'/',':')
456 elseif self.spec() =~ '\.git/index$'
458 elseif self.spec() =~ '\.git/refs/\|\.git/.*HEAD$'
459 return self.spec()[strlen(self.repo().dir())+1 : -1]
465 function! s:buffer_sha1() dict abort
466 if self.spec() =~ '^fugitive://' || self.spec() =~ '\.git/refs/\|\.git/.*HEAD$'
467 return self.repo().rev_parse(self.rev())
473 function! s:buffer_expand(rev) dict abort
474 if a:rev =~# '^:[0-3]$'
475 let file = a:rev.self.path(':')
476 elseif a:rev =~# '^[-:]/$'
477 let file = '/'.self.path()
478 elseif a:rev =~# '^-'
479 let file = 'HEAD^{}'.a:rev[1:-1].self.path(':')
480 elseif a:rev =~# '^@{'
481 let file = 'HEAD'.a:rev.self.path(':')
482 elseif a:rev =~# '^[~^]'
483 let commit = s:sub(self.commit(),'^\d=$','HEAD')
484 let file = commit.a:rev.self.path(':')
488 return s:sub(s:sub(file,'\%$',self.path()),'\.\@<=/$','')
491 function! s:buffer_containing_commit() dict abort
492 if self.commit() =~# '^\d$'
494 elseif self.commit() =~# '.'
501 call s:add_methods('buffer',['getvar','setvar','getline','repo','type','spec','name','commit','path','rev','sha1','expand','containing_commit'])
506 call s:command("-bang -nargs=? -complete=customlist,s:GitComplete Git :execute s:Git(<bang>0,<q-args>)")
508 function! s:ExecuteInTree(cmd) abort
509 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
512 execute cd.'`=s:repo().tree()`'
519 function! s:Git(bang,cmd) abort
521 return s:Edit('edit',1,a:cmd)
523 let git = s:repo().git_command()
524 if has('gui_running') && !has('win32')
525 let git .= ' --no-pager'
527 let cmd = matchstr(a:cmd,'\v\C.{-}%($|\\@<!%(\\\\)*\|)@=')
528 call s:ExecuteInTree('!'.git.' '.cmd)
529 call fugitive#reload_status()
530 return matchstr(a:cmd,'\v\C\\@<!%(\\\\)*\|\zs.*')
533 function! s:GitComplete(A,L,P) abort
534 if !exists('s:exec_path')
535 let s:exec_path = s:sub(system(g:fugitive_git_executable.' --exec-path'),'\n$','')
537 let cmds = map(split(glob(s:exec_path.'/git-*'),"\n"),'s:sub(v:val[strlen(s:exec_path)+5 : -1],"\\.exe$","")')
538 if a:L =~ ' [[:alnum:]-]\+ '
539 return s:repo().superglob(a:A)
541 return sort(cmds+keys(s:repo().aliases()))
543 return filter(sort(cmds+keys(s:repo().aliases())),'v:val[0:strlen(a:A)-1] ==# a:A')
550 function! s:DirComplete(A,L,P) abort
551 let matches = s:repo().dirglob(a:A)
555 call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Gcd :cd<bang> `=s:repo().bare() ? s:repo().dir(<q-args>) : s:repo().tree(<q-args>)`")
556 call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Glcd :lcd<bang> `=s:repo().bare() ? s:repo().dir(<q-args>) : s:repo().tree(<q-args>)`")
561 call s:command("-bar Gstatus :execute s:Status()")
563 function! s:Status() abort
567 nnoremap <buffer> <silent> q :<C-U>bdelete<CR>
569 return 'echoerr v:errmsg'
574 function! fugitive#reload_status() abort
575 let mytab = tabpagenr()
576 for tab in [mytab] + range(1,tabpagenr('$'))
577 for winnr in range(1,tabpagewinnr(tab,'$'))
578 if getbufvar(tabpagebuflist(tab)[winnr-1],'fugitive_type') ==# 'index'
579 execute 'tabnext '.tab
581 execute winnr.'wincmd w'
586 call s:BufReadIndex()
589 if exists('restorewinnr')
592 execute 'tabnext '.mytab
599 function! s:StageReloadSeek(target,lnum1,lnum2)
601 let f = matchstr(getline(a:lnum1-1),'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*')
602 if f !=# '' | let jump = f | endif
603 let f = matchstr(getline(a:lnum2+1),'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*')
604 if f !=# '' | let jump = f | endif
608 call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.jump.'\%( (new commits)\)\=\$','W')
611 function! s:StageDiff(diff) abort
612 let section = getline(search('^# .*:$','bcnW'))
613 let line = getline('.')
614 let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (new commits)\)\=$')
615 if filename ==# '' && section ==# '# Changes to be committed:'
616 return 'Git diff --cached'
617 elseif filename ==# ''
619 elseif line =~# '^#\trenamed:' && filename =~# ' -> '
620 let [old, new] = split(filename,' -> ')
621 execute 'Gedit '.s:fnameescape(':0:'.new)
622 return a:diff.' HEAD:'.s:fnameescape(old)
623 elseif section ==# '# Changes to be committed:'
624 execute 'Gedit '.s:fnameescape(':0:'.filename)
627 execute 'Gedit '.s:fnameescape('/'.filename)
632 function! s:StageDiffEdit() abort
633 let section = getline(search('^# .*:$','bcnW'))
634 let line = getline('.')
635 let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (new commits)\)\=$')
636 let arg = (filename ==# '' ? '.' : filename)
637 if section ==# '# Changes to be committed:'
638 return 'Git! diff --cached '.s:shellesc(arg)
639 elseif section ==# '# Untracked files:'
641 call repo.git_chomp_in_tree('add','--intent-to-add',arg)
645 if !search('^# Change\%(d but not updated\|s not staged for commit\):$','W')
646 call search('^# Change','W')
649 call s:StageReloadSeek(arg,line('.'),line('.'))
653 return 'Git! diff '.s:shellesc(arg)
657 function! s:StageToggle(lnum1,lnum2) abort
660 for lnum in range(a:lnum1,a:lnum2)
661 let line = getline(lnum)
663 if line ==# '# Changes to be committed:'
664 call repo.git_chomp_in_tree('reset','-q')
667 if !search('^# Untracked files:$','W')
668 call search('^# Change','W')
671 elseif line =~# '^# Change\%(d but not updated\|s not staged for commit\):$'
672 call repo.git_chomp_in_tree('add','-u')
675 if !search('^# Untracked files:$','W')
676 call search('^# Change','W')
679 elseif line ==# '# Untracked files:'
680 call repo.git_chomp_in_tree('add','.')
683 call search('^# Change','W')
686 let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (\a\+ [[:alpha:], ]\+)\)\=$')
690 if !exists('first_filename')
691 let first_filename = filename
694 let section = getline(search('^# .*:$','bnW'))
695 if line =~# '^#\trenamed:' && filename =~ ' -> '
696 let cmd = ['mv','--'] + reverse(split(filename,' -> '))
697 let filename = cmd[-1]
698 elseif section =~? ' to be '
699 let cmd = ['reset','-q','--',filename]
700 elseif line =~# '^#\tdeleted:'
701 let cmd = ['rm','--',filename]
703 let cmd = ['add','--',filename]
705 let output .= call(repo.git_chomp_in_tree,cmd,s:repo())."\n"
707 if exists('first_filename')
708 call s:StageReloadSeek(first_filename,a:lnum1,a:lnum2)
710 echo s:sub(s:gsub(output,'\n+','\n'),'\n$','')
712 return 'echoerr v:errmsg'
717 function! s:StagePatch(lnum1,lnum2) abort
721 for lnum in range(a:lnum1,a:lnum2)
722 let line = getline(lnum)
723 if line ==# '# Changes to be committed:'
724 return 'Git reset --patch'
725 elseif line =~# '^# Change\%(d but not updated\|s not staged for commit\):$'
726 return 'Git add --patch'
728 let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (new commits)\)\=$')
732 if !exists('first_filename')
733 let first_filename = filename
736 let section = getline(search('^# .*:$','bnW'))
737 if line =~# '^#\trenamed:' && filename =~ ' -> '
738 let reset += [split(filename,' -> ')[1]]
739 elseif section =~? ' to be '
740 let reset += [filename]
741 elseif line !~# '^#\tdeleted:'
742 let add += [filename]
747 execute "Git add --patch -- ".join(map(add,'s:shellesc(v:val)'))
750 execute "Git reset --patch -- ".join(map(add,'s:shellesc(v:val)'))
752 if exists('first_filename')
756 call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.first_filename.'\%( (new commits)\)\=\$','W')
759 return 'echoerr v:errmsg'
767 call s:command("-nargs=? -complete=customlist,s:CommitComplete Gcommit :execute s:Commit(<q-args>)")
769 function! s:Commit(args) abort
770 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
772 let msgfile = s:repo().dir('COMMIT_EDITMSG')
773 let outfile = tempname()
774 let errorfile = tempname()
776 execute cd.'`=s:repo().tree()`'
779 let old_editor = $GIT_EDITOR
780 let $GIT_EDITOR = 'false'
782 let command = 'env GIT_EDITOR=false '
784 let command .= s:repo().git_command('commit').' '.a:args
786 silent execute '!('.command.' > '.outfile.') >& '.errorfile
787 elseif a:args =~# '\%(^\| \)--interactive\>'
788 execute '!'.command.' 2> '.errorfile
790 silent execute '!'.command.' > '.outfile.' 2> '.errorfile
792 if !has('gui_running')
796 if filereadable(outfile)
797 for line in readfile(outfile)
803 let errors = readfile(errorfile)
804 let error = get(errors,-2,get(errors,-1,'!'))
805 if error =~# '\<false''\=\.$'
807 let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-[se]|--edit|--interactive)%($| )','')
808 let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-F|--file|-m|--message)%(\s+|\=)%(''[^'']*''|"%(\\.|[^"])*"|\\.|\S)*','')
809 let args = s:gsub(args,'%(^| )@<=[%#]%(:\w)*','\=expand(submatch(0))')
810 let args = '-F '.s:shellesc(msgfile).' '.args
811 if args !~# '\%(^\| \)--cleanup\>'
812 let args = '--cleanup=strip '.args
814 if bufname('%') == '' && line('$') == 1 && getline(1) == '' && !&mod
815 keepalt edit `=msgfile`
816 elseif s:buffer().type() ==# 'index'
817 keepalt edit `=msgfile`
818 execute (search('^#','n')+1).'wincmd+'
819 setlocal nopreviewwindow
821 keepalt split `=msgfile`
823 let b:fugitive_commit_arguments = args
824 setlocal bufhidden=delete filetype=gitcommit
833 return 'echoerr v:errmsg'
835 if exists('old_editor')
836 let $GIT_EDITOR = old_editor
839 call delete(errorfile)
841 call fugitive#reload_status()
845 function! s:CommitComplete(A,L,P) abort
846 if a:A =~ '^-' || type(a:A) == type(0) " a:A is 0 on :Gcommit -<Tab>
847 let args = ['-C', '-F', '-a', '-c', '-e', '-i', '-m', '-n', '-o', '-q', '-s', '-t', '-u', '-v', '--all', '--allow-empty', '--amend', '--author=', '--cleanup=', '--dry-run', '--edit', '--file=', '--include', '--interactive', '--message=', '--no-verify', '--only', '--quiet', '--reedit-message=', '--reuse-message=', '--signoff', '--template=', '--untracked-files', '--verbose']
848 return filter(args,'v:val[0 : strlen(a:A)-1] ==# a:A')
850 return s:repo().superglob(a:A)
854 function! s:FinishCommit()
855 let args = getbufvar(+expand('<abuf>'),'fugitive_commit_arguments')
857 call setbufvar(+expand('<abuf>'),'fugitive_commit_arguments','')
858 return s:Commit(args)
863 augroup fugitive_commit
865 autocmd VimLeavePre,BufDelete *.git/COMMIT_EDITMSG execute s:sub(s:FinishCommit(), '^echoerr (.*)', 'echohl ErrorMsg|echo \1|echohl NONE')
871 if !exists('g:fugitive_summary_format')
872 let g:fugitive_summary_format = '%s'
875 call s:command("-bang -nargs=? -complete=customlist,s:EditComplete Ggrep :execute s:Grep(<bang>0,<q-args>)")
876 call s:command("-bar -bang -nargs=* -complete=customlist,s:EditComplete Glog :execute s:Log('grep<bang>',<f-args>)")
878 function! s:Grep(bang,arg) abort
879 let grepprg = &grepprg
880 let grepformat = &grepformat
881 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
884 execute cd.'`=s:repo().tree()`'
885 let &grepprg = s:repo().git_command('--no-pager', 'grep', '-n')
886 let &grepformat = '%f:%l:%m'
887 exe 'grep! '.escape(matchstr(a:arg,'\v\C.{-}%($|[''" ]\@=\|)@='),'|')
888 let list = getqflist()
890 if bufname(entry.bufnr) =~ ':'
891 let entry.filename = s:repo().translate(bufname(entry.bufnr))
893 elseif a:arg =~# '\%(^\| \)--cached\>'
894 let entry.filename = s:repo().translate(':0:'.bufname(entry.bufnr))
898 call setqflist(list,'r')
899 if !a:bang && !empty(list)
900 return 'cfirst'.matchstr(a:arg,'\v\C[''" ]\zs\|.*')
902 return matchstr(a:arg,'\v\C[''" ]\|\zs.*')
905 let &grepprg = grepprg
906 let &grepformat = grepformat
911 function! s:Log(cmd,...)
912 let path = s:buffer().path('/')
913 if path =~# '^/\.git\%(/\|$\)' || index(a:000,'--') != -1
916 let cmd = ['--no-pager', 'log', '--no-color']
917 let cmd += [escape('--pretty=format:fugitive://'.s:repo().dir().'//%H'.path.'::'.g:fugitive_summary_format,'%')]
918 if empty(filter(a:000[0 : index(a:000,'--')],'v:val !~# "^-"'))
919 if s:buffer().commit() =~# '\x\{40\}'
920 let cmd += [s:buffer().commit()]
921 elseif s:buffer().path() =~# '^\.git/refs/\|^\.git/.*HEAD$'
922 let cmd += [s:buffer().path()[5:-1]]
925 let cmd += map(copy(a:000),'s:sub(v:val,"^\\%(%(:\\w)*)","\\=fnamemodify(s:buffer().path(),submatch(1))")')
927 let cmd += ['--',path[1:-1]]
929 let grepformat = &grepformat
930 let grepprg = &grepprg
931 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
934 execute cd.'`=s:repo().tree()`'
935 let &grepprg = call(s:repo().git_command,cmd,s:repo())
936 let &grepformat = '%f::%m'
939 let &grepformat = grepformat
940 let &grepprg = grepprg
946 " Gedit, Gpedit, Gsplit, Gvsplit, Gtabedit, Gread {{{1
948 function! s:Edit(cmd,bang,...) abort
950 if &previewwindow && getbufvar('','fugitive_type') ==# 'index'
953 let mywinnr = winnr()
954 for winnr in range(winnr('$'),1,-1)
955 if winnr != mywinnr && getwinvar(winnr,'&diff')
956 execute winnr.'wincmd w'
966 let args = s:gsub(a:0 ? a:1 : '', '\\@<!%(\\\\)*\zs[%#]', '\=s:buffer().expand(submatch(0))')
968 let git = s:repo().git_command()
970 silent call s:ExecuteInTree((a:cmd ==# 'read' ? '$read' : a:cmd).'!'.git.' --no-pager '.args)
972 silent execute '1,'.last.'delete_'
974 call fugitive#reload_status()
976 return 'redraw|echo '.string(':!'.git.' '.args)
978 let temp = tempname()
979 let s:temp_files[temp] = s:repo().dir()
980 silent execute a:cmd.' '.temp
984 let echo = s:Edit('read',1,args)
986 setlocal buftype=nowrite nomodified filetype=git foldmarker=<<<<<<<,>>>>>>>
987 if getline(1) !~# '^diff '
988 setlocal readonly nomodifiable
1001 let file = s:buffer().expand(a:1)
1002 elseif expand('%') ==# ''
1004 elseif s:buffer().commit() ==# '' && s:buffer().path('/') !~# '^/.git\>'
1005 let file = s:buffer().path(':')
1007 let file = s:buffer().path('/')
1010 let file = s:repo().translate(file)
1012 return 'echoerr v:errmsg'
1015 return 'silent %delete_|read '.s:fnameescape(file).'|silent 1delete_|diffupdate|'.line('.')
1017 return a:cmd.' '.s:fnameescape(file)
1021 function! s:EditComplete(A,L,P) abort
1022 return s:repo().superglob(a:A)
1025 function! s:EditRunComplete(A,L,P) abort
1027 return s:GitComplete(a:A,a:L,a:P)
1029 return s:repo().superglob(a:A)
1033 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Ge :execute s:Edit('edit<bang>',0,<f-args>)")
1034 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gedit :execute s:Edit('edit<bang>',0,<f-args>)")
1035 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditRunComplete Gpedit :execute s:Edit('pedit',<bang>0,<f-args>)")
1036 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditRunComplete Gsplit :execute s:Edit('split',<bang>0,<f-args>)")
1037 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditRunComplete Gvsplit :execute s:Edit('vsplit',<bang>0,<f-args>)")
1038 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditRunComplete Gtabedit :execute s:Edit('tabedit',<bang>0,<f-args>)")
1039 call s:command("-bar -bang -nargs=? -count -complete=customlist,s:EditRunComplete Gread :execute s:Edit((!<count> && <line1> ? '' : <count>).'read',<bang>0,<f-args>)")
1044 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gwrite :execute s:Write(<bang>0,<f-args>)")
1045 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gw :execute s:Write(<bang>0,<f-args>)")
1046 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gwq :execute s:Wq(<bang>0,<f-args>)")
1048 function! s:Write(force,...) abort
1049 if exists('b:fugitive_commit_arguments')
1050 return 'write|bdelete'
1051 elseif expand('%:t') == 'COMMIT_EDITMSG' && $GIT_INDEX_FILE != ''
1053 elseif s:buffer().type() == 'index'
1055 elseif s:buffer().path() ==# '' && getline(4) =~# '^+++ '
1056 let filename = getline(4)[6:-1]
1059 setlocal buftype=nowrite
1060 if matchstr(getline(2),'index [[:xdigit:]]\+\.\.\zs[[:xdigit:]]\{7\}') ==# s:repo().rev_parse(':0:'.filename)[0:6]
1061 let err = s:repo().git_chomp('apply','--cached','--reverse',s:buffer().spec())
1063 let err = s:repo().git_chomp('apply','--cached',s:buffer().spec())
1066 let v:errmsg = split(err,"\n")[0]
1067 return 'echoerr v:errmsg'
1071 return 'Gedit '.fnameescape(filename)
1074 let mytab = tabpagenr()
1075 let mybufnr = bufnr('')
1076 let path = a:0 ? a:1 : s:buffer().path()
1077 if path =~# '^:\d\>'
1078 return 'write'.(a:force ? '! ' : ' ').s:fnameescape(s:repo().translate(s:buffer().expand(path)))
1080 let always_permitted = (s:buffer().path() ==# path && s:buffer().commit() =~# '^0\=$')
1081 if !always_permitted && !a:force && s:repo().git_chomp_in_tree('diff','--name-status','HEAD','--',path) . s:repo().git_chomp_in_tree('ls-files','--others','--',path) !=# ''
1082 let v:errmsg = 'fugitive: file has uncommitted changes (use ! to override)'
1083 return 'echoerr v:errmsg'
1085 let file = s:repo().translate(path)
1087 for nr in range(1,bufnr('$'))
1088 if fnamemodify(bufname(nr),':p') ==# file
1093 if treebufnr > 0 && treebufnr != bufnr('')
1094 let temp = tempname()
1095 silent execute '%write '.temp
1096 for tab in [mytab] + range(1,tabpagenr('$'))
1097 for winnr in range(1,tabpagewinnr(tab,'$'))
1098 if tabpagebuflist(tab)[winnr-1] == treebufnr
1099 execute 'tabnext '.tab
1101 execute winnr.'wincmd w'
1102 let restorewinnr = 1
1105 let lnum = line('.')
1106 let last = line('$')
1107 silent execute '$read '.temp
1108 silent execute '1,'.last.'delete_'
1113 if exists('restorewinnr')
1116 execute 'tabnext '.mytab
1122 call writefile(readfile(temp,'b'),file,'b')
1125 execute 'write! '.s:fnameescape(s:repo().translate(path))
1129 let error = s:repo().git_chomp_in_tree('add', '--force', file)
1131 let error = s:repo().git_chomp_in_tree('add', file)
1134 let v:errmsg = 'fugitive: '.error
1135 return 'echoerr v:errmsg'
1137 if s:buffer().path() ==# path && s:buffer().commit() =~# '^\d$'
1141 let one = s:repo().translate(':1:'.path)
1142 let two = s:repo().translate(':2:'.path)
1143 let three = s:repo().translate(':3:'.path)
1144 for nr in range(1,bufnr('$'))
1145 if bufloaded(nr) && !getbufvar(nr,'&modified') && (bufname(nr) == one || bufname(nr) == two || bufname(nr) == three)
1146 execute nr.'bdelete'
1151 let zero = s:repo().translate(':0:'.path)
1152 for tab in range(1,tabpagenr('$'))
1153 for winnr in range(1,tabpagewinnr(tab,'$'))
1154 let bufnr = tabpagebuflist(tab)[winnr-1]
1155 let bufname = bufname(bufnr)
1156 if bufname ==# zero && bufnr != mybufnr
1157 execute 'tabnext '.tab
1159 execute winnr.'wincmd w'
1160 let restorewinnr = 1
1163 let lnum = line('.')
1164 let last = line('$')
1165 silent $read `=file`
1166 silent execute '1,'.last.'delete_'
1171 if exists('restorewinnr')
1174 execute 'tabnext '.mytab
1180 call fugitive#reload_status()
1184 function! s:Wq(force,...) abort
1185 let bang = a:force ? '!' : ''
1186 if exists('b:fugitive_commit_arguments')
1189 let result = call(s:function('s:Write'),[a:force]+a:000)
1190 if result =~# '^\%(write\|wq\|echoerr\)'
1191 return s:sub(result,'^write','wq')
1193 return result.'|quit'.bang
1200 call s:command("-bang -bar -nargs=? -complete=customlist,s:EditComplete Gdiff :execute s:Diff(<bang>0,<f-args>)")
1201 call s:command("-bar -nargs=? -complete=customlist,s:EditComplete Gvdiff :execute s:Diff(0,<f-args>)")
1202 call s:command("-bar -nargs=? -complete=customlist,s:EditComplete Gsdiff :execute s:Diff(1,<f-args>)")
1204 augroup fugitive_diff
1206 autocmd BufWinLeave * if s:diff_window_count() == 2 && &diff && getbufvar(+expand('<abuf>'), 'git_dir') !=# '' | call s:diffoff_all(getbufvar(+expand('<abuf>'), 'git_dir')) | endif
1207 autocmd BufWinEnter * if s:diff_window_count() == 1 && &diff && getbufvar(+expand('<abuf>'), 'git_dir') !=# '' | call s:diffoff() | endif
1210 function! s:diff_window_count()
1212 for nr in range(1,winnr('$'))
1213 let c += getwinvar(nr,'&diff')
1218 function! s:diffthis()
1220 let w:fugitive_diff_restore = 'setlocal nodiff noscrollbind'
1221 let w:fugitive_diff_restore .= ' scrollopt=' . &l:scrollopt
1222 let w:fugitive_diff_restore .= &l:wrap ? ' wrap' : ' nowrap'
1223 let w:fugitive_diff_restore .= ' foldmethod=' . &l:foldmethod
1224 let w:fugitive_diff_restore .= ' foldcolumn=' . &l:foldcolumn
1229 function! s:diffoff()
1230 if exists('w:fugitive_diff_restore')
1231 execute w:fugitive_diff_restore
1232 unlet w:fugitive_diff_restore
1238 function! s:diffoff_all(dir)
1239 for nr in range(1,winnr('$'))
1240 if getwinvar(nr,'&diff')
1242 execute nr.'wincmd w'
1243 let restorewinnr = 1
1245 if exists('b:git_dir') && b:git_dir ==# a:dir
1248 if exists('restorewinnr')
1255 function! s:buffer_compare_age(commit) dict abort
1256 let scores = {':0': 1, ':1': 2, ':2': 3, ':': 4, ':3': 5}
1257 let my_score = get(scores,':'.self.commit(),0)
1258 let their_score = get(scores,':'.a:commit,0)
1259 if my_score || their_score
1260 return my_score < their_score ? -1 : my_score != their_score
1261 elseif self.commit() ==# a:commit
1264 let base = self.repo().git_chomp('merge-base',self.commit(),a:commit)
1265 if base ==# self.commit()
1267 elseif base ==# a:commit
1270 let my_time = +self.repo().git_chomp('log','--max-count=1','--pretty=format:%at',self.commit())
1271 let their_time = +self.repo().git_chomp('log','--max-count=1','--pretty=format:%at',a:commit)
1272 return my_time < their_time ? -1 : my_time != their_time
1275 call s:add_methods('buffer',['compare_age'])
1277 function! s:Diff(bang,...) abort
1278 let split = a:bang ? 'split' : 'vsplit'
1279 if exists(':DiffGitCached')
1280 return 'DiffGitCached'
1281 elseif (!a:0 || a:1 == ':') && s:buffer().commit() =~# '^[0-1]\=$' && s:repo().git_chomp_in_tree('ls-files', '--unmerged', '--', s:buffer().path()) !=# ''
1283 execute 'leftabove '.split.' `=fugitive#buffer().repo().translate(s:buffer().expand('':2''))`'
1284 execute 'nnoremap <buffer> <silent> dp :diffput '.nr.'<Bar>diffupdate<CR>'
1287 execute 'rightbelow '.split.' `=fugitive#buffer().repo().translate(s:buffer().expand('':3''))`'
1288 execute 'nnoremap <buffer> <silent> dp :diffput '.nr.'<Bar>diffupdate<CR>'
1297 let file = s:buffer().path('/')
1299 let file = s:buffer().path(':0:')
1300 elseif a:1 =~# '^:/.'
1302 let file = s:repo().rev_parse(a:1).s:buffer().path(':')
1304 return 'echoerr v:errmsg'
1307 let file = s:buffer().expand(a:1)
1309 if file !~# ':' && file !~# '^/' && s:repo().git_chomp('cat-file','-t',file) =~# '^\%(tag\|commit\)$'
1310 let file = file.s:buffer().path(':')
1313 let file = s:buffer().path(s:buffer().commit() == '' ? ':0:' : '/')
1316 let spec = s:repo().translate(file)
1317 let commit = matchstr(spec,'\C[^:/]//\zs\x\+')
1318 if s:buffer().compare_age(commit) < 0
1319 execute 'rightbelow '.split.' `=spec`'
1321 execute 'leftabove '.split.' `=spec`'
1328 return 'echoerr v:errmsg'
1333 " Gmove, Gremove {{{1
1335 function! s:Move(force,destination)
1336 if a:destination =~# '^/'
1337 let destination = a:destination[1:-1]
1339 let destination = fnamemodify(s:sub(a:destination,'[%#]%(:\w)*','\=expand(submatch(0))'),':p')
1340 if destination[0:strlen(s:repo().tree())] ==# s:repo().tree('')
1341 let destination = destination[strlen(s:repo().tree('')):-1]
1344 if isdirectory(s:buffer().name())
1345 " Work around Vim parser idiosyncrasy
1346 let discarded = s:buffer().setvar('&swapfile',0)
1348 let message = call(s:repo().git_chomp_in_tree,['mv']+(a:force ? ['-f'] : [])+['--', s:buffer().path(), destination], s:repo())
1350 let v:errmsg = 'fugitive: '.message
1351 return 'echoerr v:errmsg'
1353 let destination = s:repo().tree(destination)
1354 if isdirectory(destination)
1355 let destination = fnamemodify(s:sub(destination,'/$','').'/'.expand('%:t'),':.')
1357 call fugitive#reload_status()
1358 if s:buffer().commit() == ''
1359 if isdirectory(destination)
1360 return 'edit '.s:fnameescape(destination)
1362 return 'saveas! '.s:fnameescape(destination)
1365 return 'file '.s:fnameescape(s:repo().translate(':0:'.destination)
1369 function! s:MoveComplete(A,L,P)
1371 return s:repo().superglob(a:A)
1373 let matches = split(glob(a:A.'*'),"\n")
1374 call map(matches,'v:val !~ "/$" && isdirectory(v:val) ? v:val."/" : v:val')
1379 function! s:Remove(force)
1380 if s:buffer().commit() ==# ''
1382 elseif s:buffer().commit() ==# '0'
1383 let cmd = ['rm','--cached']
1385 let v:errmsg = 'fugitive: rm not supported here'
1386 return 'echoerr v:errmsg'
1389 let cmd += ['--force']
1391 let message = call(s:repo().git_chomp_in_tree,cmd+['--',s:buffer().path()],s:repo())
1393 let v:errmsg = 'fugitive: '.s:sub(message,'error:.*\zs\n\(.*-f.*',' (add ! to force)')
1394 return 'echoerr '.string(v:errmsg)
1396 call fugitive#reload_status()
1397 return 'bdelete'.(a:force ? '!' : '')
1401 augroup fugitive_remove
1403 autocmd User Fugitive if s:buffer().commit() =~# '^0\=$' |
1404 \ exe "command! -buffer -bar -bang -nargs=1 -complete=customlist,s:MoveComplete Gmove :execute s:Move(<bang>0,<q-args>)" |
1405 \ exe "command! -buffer -bar -bang Gremove :execute s:Remove(<bang>0)" |
1412 augroup fugitive_blame
1414 autocmd BufReadPost *.fugitiveblame setfiletype fugitiveblame
1415 autocmd FileType fugitiveblame setlocal nomodeline | if exists('b:git_dir') | let &l:keywordprg = s:repo().keywordprg() | endif
1416 autocmd Syntax fugitiveblame call s:BlameSyntax()
1417 autocmd User Fugitive if s:buffer().type('file', 'blob') | exe "command! -buffer -bar -bang -range=0 -nargs=* Gblame :execute s:Blame(<bang>0,<line1>,<line2>,<count>,[<f-args>])" | endif
1420 function! s:Blame(bang,line1,line2,count,args) abort
1422 if s:buffer().path() == ''
1423 call s:throw('file or blob required')
1425 if filter(copy(a:args),'v:val !~# "^\\%(--root\|--show-name\\|-\\=\\%([ltwfs]\\|[MC]\\d*\\)\\+\\)$"') != []
1426 call s:throw('unsupported option')
1428 call map(a:args,'s:sub(v:val,"^\\ze[^-]","-")')
1429 let cmd = ['--no-pager', 'blame', '--show-number'] + a:args
1430 if s:buffer().commit() =~# '\D\|..'
1431 let cmd += [s:buffer().commit()]
1433 let cmd += ['--contents', '-']
1435 let basecmd = escape(call(s:repo().git_command,cmd+['--',s:buffer().path()],s:repo()),'!')
1437 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
1440 execute cd.'`=s:repo().tree()`'
1443 execute 'write !'.substitute(basecmd,' blame ',' blame -L '.a:line1.','.a:line2.' ','g')
1445 let error = tempname()
1446 let temp = error.'.fugitiveblame'
1448 silent! execute '%write !('.basecmd.' > '.temp.') >& '.error
1450 silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error
1457 call s:throw(join(readfile(error),"\n"))
1459 let bufnr = bufnr('')
1460 let restore = 'call setwinvar(bufwinnr('.bufnr.'),"&scrollbind",0)'
1462 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&wrap",1)'
1465 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&foldenable",1)'
1468 windo set noscrollbind
1469 exe winnr.'wincmd w'
1470 setlocal scrollbind nowrap nofoldenable
1471 let top = line('w0') + &scrolloff
1472 let current = line('.')
1473 let s:temp_files[temp] = s:repo().dir()
1474 exe 'leftabove vsplit '.temp
1475 let b:fugitive_blamed_bufnr = bufnr
1476 let w:fugitive_leave = restore
1477 let b:fugitive_blame_arguments = join(a:args,' ')
1481 execute "vertical resize ".(match(getline('.'),'\s\+\d\+)')+1)
1482 setlocal nomodified nomodifiable nonumber scrollbind nowrap foldcolumn=0 nofoldenable filetype=fugitiveblame
1483 if exists('+relativenumber')
1484 setlocal norelativenumber
1486 nnoremap <buffer> <silent> <CR> :<C-U>exe <SID>BlameJump('')<CR>
1487 nnoremap <buffer> <silent> P :<C-U>exe <SID>BlameJump('^'.v:count1)<CR>
1488 nnoremap <buffer> <silent> ~ :<C-U>exe <SID>BlameJump('~'.v:count1)<CR>
1489 nnoremap <buffer> <silent> o :<C-U>exe <SID>Edit((&splitbelow ? "botright" : "topleft")." split", 0, matchstr(getline('.'),'\x\+'))<CR>
1490 nnoremap <buffer> <silent> O :<C-U>exe <SID>Edit("tabedit", 0, matchstr(getline('.'),'\x\+'))<CR>
1500 return 'echoerr v:errmsg'
1504 function! s:BlameJump(suffix) abort
1505 let commit = matchstr(getline('.'),'^\^\=\zs\x\+')
1506 if commit =~# '^0\+$'
1509 let lnum = matchstr(getline('.'),'\d\+\ze\s\+[([:digit:]]')
1510 let path = matchstr(getline('.'),'^\^\=\zs\x\+\s\+\zs.\{-\}\ze\s*\d\+ ')
1512 let path = s:buffer(b:fugitive_blamed_bufnr).path()
1514 let args = b:fugitive_blame_arguments
1515 let offset = line('.') - line('w0')
1516 let bufnr = bufnr('%')
1517 let winnr = bufwinnr(b:fugitive_blamed_bufnr)
1519 exe winnr.'wincmd w'
1521 execute s:Edit('edit', 0, commit.a:suffix.':'.path)
1525 execute 'Gblame '.args
1527 let delta = line('.') - line('w0') - offset
1529 execute 'norm! 'delta."\<C-E>"
1531 execute 'norm! '(-delta)."\<C-Y>"
1537 function! s:BlameSyntax() abort
1538 let b:current_syntax = 'fugitiveblame'
1539 syn match FugitiveblameBoundary "^\^"
1540 syn match FugitiveblameBlank "^\s\+\s\@=" nextgroup=FugitiveblameAnnotation,fugitiveblameOriginalFile,FugitiveblameOriginalLineNumber skipwhite
1541 syn match FugitiveblameHash "\%(^\^\=\)\@<=\x\{7,40\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite
1542 syn match FugitiveblameUncommitted "\%(^\^\=\)\@<=0\{7,40\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite
1543 syn region FugitiveblameAnnotation matchgroup=FugitiveblameDelimiter start="(" end="\%( \d\+\)\@<=)" contained keepend oneline
1544 syn match FugitiveblameTime "[0-9:/+-][0-9:/+ -]*[0-9:/+-]\%( \+\d\+)\)\@=" contained containedin=FugitiveblameAnnotation
1545 syn match FugitiveblameLineNumber " \@<=\d\+)\@=" contained containedin=FugitiveblameAnnotation
1546 syn match FugitiveblameOriginalFile " \%(\f\+\D\@<=\|\D\@=\f\+\)\%(\%(\s\+\d\+\)\=\s\%((\|\s*\d\+)\)\)\@=" contained nextgroup=FugitiveblameOriginalLineNumber,FugitiveblameAnnotation skipwhite
1547 syn match FugitiveblameOriginalLineNumber " \@<=\d\+\%(\s(\)\@=" contained nextgroup=FugitiveblameAnnotation skipwhite
1548 syn match FugitiveblameOriginalLineNumber " \@<=\d\+\%(\s\+\d\+)\)\@=" contained nextgroup=FugitiveblameShort skipwhite
1549 syn match FugitiveblameShort "\d\+)" contained contains=FugitiveblameLineNumber
1550 syn match FugitiveblameNotCommittedYet "(\@<=Not Committed Yet\>" contained containedin=FugitiveblameAnnotation
1551 hi def link FugitiveblameBoundary Keyword
1552 hi def link FugitiveblameHash Identifier
1553 hi def link FugitiveblameUncommitted Function
1554 hi def link FugitiveblameTime PreProc
1555 hi def link FugitiveblameLineNumber Number
1556 hi def link FugitiveblameOriginalFile String
1557 hi def link FugitiveblameOriginalLineNumber Float
1558 hi def link FugitiveblameShort FugitiveblameDelimiter
1559 hi def link FugitiveblameDelimiter Delimiter
1560 hi def link FugitiveblameNotCommittedYet Comment
1566 call s:command("-bar -bang -count=0 -nargs=? -complete=customlist,s:EditComplete Gbrowse :execute s:Browse(<bang>0,<line1>,<count>,<f-args>)")
1568 function! s:Browse(bang,line1,count,...) abort
1570 let rev = a:0 ? substitute(a:1,'@[[:alnum:]_-]*\%(://.\{-\}\)\=$','','') : ''
1572 let expanded = s:buffer().rev()
1574 let expanded = s:buffer().path('/')
1576 let expanded = s:buffer().expand(rev)
1578 let full = s:repo().translate(expanded)
1580 if full =~# '^fugitive://'
1581 let commit = matchstr(full,'://.*//\zs\w\+')
1582 let path = matchstr(full,'://.*//\w\+\zs/.*')
1584 let type = s:repo().git_chomp('cat-file','-t',commit.s:sub(path,'^/',':'))
1588 let path = path[1:-1]
1589 elseif s:repo().bare()
1590 let path = '.git/' . full[strlen(s:repo().dir())+1:-1]
1593 let path = full[strlen(s:repo().tree())+1:-1]
1594 if path =~# '^\.git/'
1596 elseif isdirectory(full)
1602 if path =~# '^\.git/.*HEAD' && filereadable(s:repo().dir(path[5:-1]))
1603 let body = readfile(s:repo().dir(path[5:-1]))[0]
1604 if body =~# '^\x\{40\}$'
1608 elseif body =~# '^ref: refs/'
1609 let path = '.git/' . matchstr(body,'ref: \zs.*')
1613 if a:0 && a:1 =~# '@[[:alnum:]_-]*\%(://.\{-\}\)\=$'
1614 let remote = matchstr(a:1,'@\zs[[:alnum:]_-]\+\%(://.\{-\}\)\=$')
1615 elseif path =~# '^\.git/refs/remotes/.'
1616 let remote = matchstr(path,'^\.git/refs/remotes/\zs[^/]\+')
1618 let remote = 'origin'
1619 let branch = matchstr(rev,'^[[:alnum:]/._-]\+\ze[:^~@]')
1620 if branch ==# '' && path =~# '^\.git/refs/\w\+/'
1621 let branch = s:sub(path,'^\.git/refs/\w+/','')
1623 if filereadable(s:repo().dir('refs/remotes/'.branch))
1624 let remote = matchstr(branch,'[^/]\+')
1625 let rev = rev[strlen(remote)+1:-1]
1628 let branch = matchstr(s:repo().head_ref(),'\<refs/heads/\zs.*')
1631 let remote = s:repo().git_chomp('config','branch.'.branch.'.remote')
1632 if remote =~# '^\.\=$'
1633 let remote = 'origin'
1634 elseif rev[0:strlen(branch)-1] ==# branch && rev[strlen(branch)] =~# '[:^~@]'
1635 let rev = s:repo().git_chomp('config','branch.'.branch.'.merge')[11:-1] . rev[strlen(branch):-1]
1641 let raw = s:repo().git_chomp('config','remote.'.remote.'.url')
1646 let url = s:github_url(s:repo(),raw,rev,commit,path,type,a:line1,a:count)
1648 let url = s:instaweb_url(s:repo(),rev,commit,path,type,a:count ? a:line1 : 0)
1652 call s:throw("Instaweb failed to start and '".remote."' is not a GitHub remote")
1657 return 'echomsg '.string(url)
1659 return 'echomsg '.string(url).'|call fugitive#buffer().repo().git_chomp("web--browse",'.string(url).')'
1662 return 'echoerr v:errmsg'
1666 function! s:github_url(repo,url,rev,commit,path,type,line1,line2) abort
1668 let repo_path = matchstr(a:url,'^\%(https\=://\|git://\|git@\)github\.com[/:]\zs.\{-\}\ze\%(\.git\)\=$')
1672 let root = 'https://github.com/' . repo_path
1673 if path =~# '^\.git/refs/heads/'
1674 let branch = a:repo.git_chomp('config','branch.'.path[16:-1].'.merge')[11:-1]
1676 return root . '/commits/' . path[16:-1]
1678 return root . '/commits/' . branch
1680 elseif path =~# '^\.git/refs/.'
1681 return root . '/commits/' . matchstr(path,'[^/]\+$')
1682 elseif path =~# '.git/\%(config$\|hooks\>\)'
1683 return root . '/admin'
1684 elseif path =~# '^\.git\>'
1687 if a:rev =~# '^[[:alnum:]._-]\+:'
1688 let commit = matchstr(a:rev,'^[^:]*')
1689 elseif a:commit =~# '^\d\=$'
1690 let local = matchstr(a:repo.head_ref(),'\<refs/heads/\zs.*')
1691 let commit = a:repo.git_chomp('config','branch.'.local.'.merge')[11:-1]
1696 let commit = a:commit
1699 let url = s:sub(root . '/tree/' . commit . '/' . path,'/$','')
1700 elseif a:type == 'blob'
1701 let url = root . '/blob/' . commit . '/' . path
1702 if a:line2 && a:line1 == a:line2
1703 let url .= '#L' . a:line1
1705 let url .= '#L' . a:line1 . '-' . a:line2
1707 elseif a:type == 'tag'
1708 let commit = matchstr(getline(3),'^tag \zs.*')
1709 let url = root . '/tree/' . commit
1711 let url = root . '/commit/' . commit
1716 function! s:instaweb_url(repo,rev,commit,path,type,...) abort
1717 let output = a:repo.git_chomp('instaweb','-b','unknown')
1718 if output =~# 'http://'
1719 let root = matchstr(output,'http://.*').'/?p='.fnamemodify(a:repo.dir(),':t')
1723 if a:path =~# '^\.git/refs/.'
1724 return root . ';a=shortlog;h=' . matchstr(a:path,'^\.git/\zs.*')
1725 elseif a:path =~# '^\.git\>'
1729 if a:commit =~# '^\x\{40\}$'
1730 if a:type ==# 'commit'
1731 let url .= ';a=commit'
1733 let url .= ';h=' . a:repo.rev_parse(a:commit . (a:path == '' ? '' : ':' . a:path))
1735 if a:type ==# 'blob'
1736 let tmp = tempname()
1737 silent execute 'write !'.a:repo.git_command('hash-object','-w','--stdin').' > '.tmp
1738 let url .= ';h=' . readfile(tmp)[0]
1741 let url .= ';h=' . a:repo.rev_parse((a:commit == '' ? 'HEAD' : ':' . a:commit) . ':' . a:path)
1743 call s:throw('fugitive: cannot browse uncommitted file')
1746 let root .= ';hb=' . matchstr(a:repo.head_ref(),'[^ ]\+$')
1749 let url .= ';f=' . a:path
1752 let url .= '#l' . a:1
1760 function! s:ReplaceCmd(cmd,...) abort
1761 let fn = bufname('')
1762 let tmp = tempname()
1767 let old_index = $GIT_INDEX_FILE
1768 let $GIT_INDEX_FILE = a:1
1770 let prefix = 'env GIT_INDEX_FILE='.s:shellesc(a:1).' '
1774 call system('cmd /c "'.prefix.a:cmd.' > '.tmp.'"')
1776 call system(' ('.prefix.a:cmd.' > '.tmp.') ')
1779 if exists('old_index')
1780 let $GIT_INDEX_FILE = old_index
1783 silent exe 'keepalt file '.tmp
1785 silent exe 'keepalt file '.s:fnameescape(fn)
1787 if bufname('$') == tmp
1788 silent execute 'bwipeout '.bufnr('$')
1790 silent exe 'doau BufReadPost '.s:fnameescape(fn)
1793 function! s:BufReadIndex()
1794 if !exists('b:fugitive_display_format')
1795 let b:fugitive_display_format = filereadable(expand('%').'.lock')
1797 let b:fugitive_display_format = b:fugitive_display_format % 2
1798 let b:fugitive_type = 'index'
1800 let b:git_dir = s:repo().dir()
1802 if fnamemodify($GIT_INDEX_FILE !=# '' ? $GIT_INDEX_FILE : b:git_dir . '/index', ':p') ==# expand('%:p')
1805 let index = expand('%:p')
1807 if b:fugitive_display_format
1808 call s:ReplaceCmd(s:repo().git_command('ls-files','--stage'),index)
1811 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
1814 execute cd.'`=s:repo().tree()`'
1815 call s:ReplaceCmd(s:repo().git_command('status'),index)
1821 setlocal ro noma nomod nomodeline bufhidden=wipe
1825 nnoremap <buffer> <silent> <C-N> :call search('^#\t.*','W')<Bar>.<CR>
1826 nnoremap <buffer> <silent> <C-P> :call search('^#\t.*','Wbe')<Bar>.<CR>
1827 nnoremap <buffer> <silent> - :<C-U>execute <SID>StageToggle(line('.'),line('.')+v:count1-1)<CR>
1828 xnoremap <buffer> <silent> - :<C-U>execute <SID>StageToggle(line("'<"),line("'>"))<CR>
1829 nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += 1<Bar>exe <SID>BufReadIndex()<CR>
1830 nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= 1<Bar>exe <SID>BufReadIndex()<CR>
1831 nnoremap <buffer> <silent> C :<C-U>Gcommit<CR>
1832 nnoremap <buffer> <silent> cA :<C-U>Gcommit --amend --reuse-message=HEAD<CR>
1833 nnoremap <buffer> <silent> ca :<C-U>Gcommit --amend<CR>
1834 nnoremap <buffer> <silent> cc :<C-U>Gcommit<CR>
1835 nnoremap <buffer> <silent> D :<C-U>execute <SID>StageDiff('Gvdiff')<CR>
1836 nnoremap <buffer> <silent> dd :<C-U>execute <SID>StageDiff('Gvdiff')<CR>
1837 nnoremap <buffer> <silent> dh :<C-U>execute <SID>StageDiff('Gsdiff')<CR>
1838 nnoremap <buffer> <silent> ds :<C-U>execute <SID>StageDiff('Gsdiff')<CR>
1839 nnoremap <buffer> <silent> dp :<C-U>execute <SID>StageDiffEdit()<CR>
1840 nnoremap <buffer> <silent> dv :<C-U>execute <SID>StageDiff('Gvdiff')<CR>
1841 nnoremap <buffer> <silent> p :<C-U>execute <SID>StagePatch(line('.'),line('.')+v:count1-1)<CR>
1842 xnoremap <buffer> <silent> p :<C-U>execute <SID>StagePatch(line("'<"),line("'>"))<CR>
1843 nnoremap <buffer> <silent> q :<C-U>if bufnr('$') == 1<Bar>quit<Bar>else<Bar>bdelete<Bar>endif<CR>
1844 nnoremap <buffer> <silent> R :<C-U>edit<CR>
1846 return 'echoerr v:errmsg'
1850 function! s:FileRead()
1852 let repo = s:repo(s:ExtractGitDir(expand('<amatch>')))
1853 let path = s:sub(s:sub(matchstr(expand('<amatch>'),'fugitive://.\{-\}//\zs.*'),'/',':'),'^\d:',':&')
1854 let hash = repo.rev_parse(path)
1858 let type = repo.git_chomp('cat-file','-t',hash)
1860 " TODO: use count, if possible
1861 return "read !".escape(repo.git_command('cat-file',type,hash),'%#\')
1863 return 'echoerr v:errmsg'
1867 function! s:BufReadIndexFile()
1869 let b:fugitive_type = 'blob'
1870 let b:git_dir = s:repo().dir()
1871 call s:ReplaceCmd(s:repo().git_command('cat-file','blob',s:buffer().sha1()))
1872 if &bufhidden ==# ''
1873 setlocal bufhidden=delete
1876 catch /^fugitive: rev-parse/
1877 silent exe 'doau BufNewFile '.s:fnameescape(bufname(''))
1880 return 'echoerr v:errmsg'
1884 function! s:BufWriteIndexFile()
1885 let tmp = tempname()
1887 let path = matchstr(expand('<amatch>'),'//\d/\zs.*')
1888 let stage = matchstr(expand('<amatch>'),'//\zs\d')
1889 silent execute 'write !'.s:repo().git_command('hash-object','-w','--stdin').' > '.tmp
1890 let sha1 = readfile(tmp)[0]
1891 let old_mode = matchstr(s:repo().git_chomp('ls-files','--stage',path),'^\d\+')
1893 let old_mode = executable(s:repo().tree(path)) ? '100755' : '100644'
1895 let info = old_mode.' '.sha1.' '.stage."\t".path
1896 call writefile([info],tmp)
1898 let error = system('type '.tmp.'|'.s:repo().git_command('update-index','--index-info'))
1900 let error = system(s:repo().git_command('update-index','--index-info').' < '.tmp)
1902 if v:shell_error == 0
1904 silent execute 'doautocmd BufWritePost '.s:fnameescape(expand('%:p'))
1905 call fugitive#reload_status()
1908 return 'echoerr '.string('fugitive: '.error)
1915 function! s:BufReadObject()
1918 let b:git_dir = s:repo().dir()
1919 let hash = s:buffer().sha1()
1920 if !exists("b:fugitive_type")
1921 let b:fugitive_type = s:repo().git_chomp('cat-file','-t',hash)
1923 if b:fugitive_type !~# '^\%(tag\|commit\|tree\|blob\)$'
1924 return "echoerr 'fugitive: unrecognized git type'"
1926 let firstline = getline('.')
1927 if !exists('b:fugitive_display_format') && b:fugitive_type != 'blob'
1928 let b:fugitive_display_format = +getbufvar('#','fugitive_display_format')
1931 let pos = getpos('.')
1935 if b:fugitive_type == 'tree'
1936 let b:fugitive_display_format = b:fugitive_display_format % 2
1937 if b:fugitive_display_format
1938 call s:ReplaceCmd(s:repo().git_command('ls-tree',hash))
1940 call s:ReplaceCmd(s:repo().git_command('show','--no-color',hash))
1942 elseif b:fugitive_type == 'tag'
1943 let b:fugitive_display_format = b:fugitive_display_format % 2
1944 if b:fugitive_display_format
1945 call s:ReplaceCmd(s:repo().git_command('cat-file',b:fugitive_type,hash))
1947 call s:ReplaceCmd(s:repo().git_command('cat-file','-p',hash))
1949 elseif b:fugitive_type == 'commit'
1950 let b:fugitive_display_format = b:fugitive_display_format % 2
1951 if b:fugitive_display_format
1952 call s:ReplaceCmd(s:repo().git_command('cat-file',b:fugitive_type,hash))
1954 call s:ReplaceCmd(s:repo().git_command('show','--no-color','--pretty=format:tree %T%nparent %P%nauthor %an <%ae> %ad%ncommitter %cn <%ce> %cd%nencoding %e%n%n%s%n%n%b',hash))
1955 call search('^parent ')
1956 if getline('.') ==# 'parent '
1959 silent s/\%(^parent\)\@<! /\rparent /ge
1961 if search('^encoding \%(<unknown>\)\=$','W',line('.')+3)
1966 elseif b:fugitive_type ==# 'blob'
1967 call s:ReplaceCmd(s:repo().git_command('cat-file',b:fugitive_type,hash))
1969 call setpos('.',pos)
1970 setlocal ro noma nomod nomodeline
1971 if &bufhidden ==# ''
1972 setlocal bufhidden=delete
1974 if b:fugitive_type !=# 'blob'
1976 nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += v:count1<Bar>exe <SID>BufReadObject()<CR>
1977 nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= v:count1<Bar>exe <SID>BufReadObject()<CR>
1984 return 'echoerr v:errmsg'
1988 augroup fugitive_files
1990 autocmd BufReadCmd *.git/index exe s:BufReadIndex()
1991 autocmd BufReadCmd *.git/*index*.lock exe s:BufReadIndex()
1992 autocmd FileReadCmd fugitive://**//[0-3]/** exe s:FileRead()
1993 autocmd BufReadCmd fugitive://**//[0-3]/** exe s:BufReadIndexFile()
1994 autocmd BufWriteCmd fugitive://**//[0-3]/** exe s:BufWriteIndexFile()
1995 autocmd BufReadCmd fugitive://**//[0-9a-f][0-9a-f]* exe s:BufReadObject()
1996 autocmd FileReadCmd fugitive://**//[0-9a-f][0-9a-f]* exe s:FileRead()
1997 autocmd FileType git call s:JumpInit()
2003 let s:temp_files = {}
2005 augroup fugitive_temp
2007 autocmd BufNewFile,BufReadPost *
2008 \ if has_key(s:temp_files,expand('<amatch>:p')) |
2009 \ let b:git_dir = s:temp_files[expand('<amatch>:p')] |
2010 \ let b:git_type = 'temp' |
2011 \ call s:Detect(expand('<amatch>:p')) |
2012 \ setlocal bufhidden=delete |
2013 \ nnoremap <buffer> <silent> q :<C-U>bdelete<CR> |
2020 function! s:JumpInit() abort
2021 nnoremap <buffer> <silent> <CR> :<C-U>exe <SID>GF("edit")<CR>
2023 nnoremap <buffer> <silent> o :<C-U>exe <SID>GF("split")<CR>
2024 nnoremap <buffer> <silent> O :<C-U>exe <SID>GF("tabedit")<CR>
2025 nnoremap <buffer> <silent> P :<C-U>exe <SID>Edit('edit',0,<SID>buffer().commit().'^'.v:count1.<SID>buffer().path(':'))<CR>
2026 nnoremap <buffer> <silent> ~ :<C-U>exe <SID>Edit('edit',0,<SID>buffer().commit().'~'.v:count1.<SID>buffer().path(':'))<CR>
2027 nnoremap <buffer> <silent> C :<C-U>exe <SID>Edit('edit',0,<SID>buffer().containing_commit())<CR>
2028 nnoremap <buffer> <silent> cc :<C-U>exe <SID>Edit('edit',0,<SID>buffer().containing_commit())<CR>
2029 nnoremap <buffer> <silent> co :<C-U>exe <SID>Edit('split',0,<SID>buffer().containing_commit())<CR>
2030 nnoremap <buffer> <silent> cO :<C-U>exe <SID>Edit('tabedit',0,<SID>buffer().containing_commit())<CR>
2031 nnoremap <buffer> <silent> cp :<C-U>exe <SID>Edit('pedit',0,<SID>buffer().containing_commit())<CR>
2035 function! s:GF(mode) abort
2037 let buffer = s:buffer()
2038 let myhash = buffer.sha1()
2039 if myhash ==# '' && getline(1) =~# '^\%(commit\|tag\) \w'
2040 let myhash = matchstr(getline(1),'^\w\+ \zs\S\+')
2043 if buffer.type('tree')
2044 let showtree = (getline(1) =~# '^tree ' && getline(2) == "")
2045 if showtree && line('.') == 1
2047 elseif showtree && line('.') > 2
2048 return s:Edit(a:mode,0,buffer.commit().':'.s:buffer().path().(buffer.path() =~# '^$\|/$' ? '' : '/').s:sub(getline('.'),'/$',''))
2049 elseif getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40\}\t'
2050 return s:Edit(a:mode,0,buffer.commit().':'.s:buffer().path().(buffer.path() =~# '^$\|/$' ? '' : '/').s:sub(matchstr(getline('.'),'\t\zs.*'),'/$',''))
2053 elseif buffer.type('blob')
2054 let ref = expand("<cfile>")
2056 let sha1 = buffer.repo().rev_parse(ref)
2060 return s:Edit(a:mode,0,ref)
2066 if getline('.') =~# '^\d\{6\} \x\{40\} \d\t'
2067 let ref = matchstr(getline('.'),'\x\{40\}')
2068 let file = ':'.s:sub(matchstr(getline('.'),'\d\t.*'),'\t',':')
2069 return s:Edit(a:mode,0,file)
2071 elseif getline('.') =~# '^#\trenamed:.* -> '
2072 let file = '/'.matchstr(getline('.'),' -> \zs.*')
2073 return s:Edit(a:mode,0,file)
2074 elseif getline('.') =~# '^#\t[[:alpha:] ]\+: *.'
2075 let file = '/'.matchstr(getline('.'),': *\zs.\{-\}\ze\%( (new commits)\)\=$')
2076 return s:Edit(a:mode,0,file)
2077 elseif getline('.') =~# '^#\t.'
2078 let file = '/'.matchstr(getline('.'),'#\t\zs.*')
2079 return s:Edit(a:mode,0,file)
2080 elseif getline('.') =~# ': needs merge$'
2081 let file = '/'.matchstr(getline('.'),'.*\ze: needs merge$')
2082 return s:Edit(a:mode,0,file).'|Gdiff'
2084 elseif getline('.') ==# '# Not currently on any branch.'
2085 return s:Edit(a:mode,0,'HEAD')
2086 elseif getline('.') =~# '^# On branch '
2087 let file = 'refs/heads/'.getline('.')[12:]
2088 return s:Edit(a:mode,0,file)
2089 elseif getline('.') =~# "^# Your branch .*'"
2090 let file = matchstr(getline('.'),"'\\zs\\S\\+\\ze'")
2091 return s:Edit(a:mode,0,file)
2094 let showtree = (getline(1) =~# '^tree ' && getline(2) == "")
2096 if getline('.') =~# '^ref: '
2097 let ref = strpart(getline('.'),5)
2099 elseif getline('.') =~# '^commit \x\{40\}\>'
2100 let ref = matchstr(getline('.'),'\x\{40\}')
2101 return s:Edit(a:mode,0,ref)
2103 elseif getline('.') =~# '^parent \x\{40\}\>'
2104 let ref = matchstr(getline('.'),'\x\{40\}')
2105 let line = line('.')
2107 while getline(line) =~# '^parent '
2111 return s:Edit(a:mode,0,ref)
2113 elseif getline('.') =~ '^tree \x\{40\}$'
2114 let ref = matchstr(getline('.'),'\x\{40\}')
2115 if s:repo().rev_parse(myhash.':') == ref
2116 let ref = myhash.':'
2118 return s:Edit(a:mode,0,ref)
2120 elseif getline('.') =~# '^object \x\{40\}$' && getline(line('.')+1) =~ '^type \%(commit\|tree\|blob\)$'
2121 let ref = matchstr(getline('.'),'\x\{40\}')
2122 let type = matchstr(getline(line('.')+1),'type \zs.*')
2124 elseif getline('.') =~# '^\l\{3,8\} '.myhash.'$'
2127 elseif getline('.') =~# '^\l\{3,8\} \x\{40\}\>'
2128 let ref = matchstr(getline('.'),'\x\{40\}')
2129 echoerr "warning: unknown context ".matchstr(getline('.'),'^\l*')
2131 elseif getline('.') =~# '^[+-]\{3\} [ab/]'
2132 let ref = getline('.')[4:]
2134 elseif getline('.') =~# '^rename from '
2135 let ref = 'a/'.getline('.')[12:]
2136 elseif getline('.') =~# '^rename to '
2137 let ref = 'b/'.getline('.')[10:]
2139 elseif getline('.') =~# '^diff --git \%(a/.*\|/dev/null\) \%(b/.*\|/dev/null\)'
2140 let dref = matchstr(getline('.'),'\Cdiff --git \zs\%(a/.*\|/dev/null\)\ze \%(b/.*\|/dev/null\)')
2141 let ref = matchstr(getline('.'),'\Cdiff --git \%(a/.*\|/dev/null\) \zs\%(b/.*\|/dev/null\)')
2144 elseif getline('.') =~# '^index ' && getline(line('.')-1) =~# '^diff --git \%(a/.*\|/dev/null\) \%(b/.*\|/dev/null\)'
2145 let line = getline(line('.')-1)
2146 let dref = matchstr(line,'\Cdiff --git \zs\%(a/.*\|/dev/null\)\ze \%(b/.*\|/dev/null\)')
2147 let ref = matchstr(line,'\Cdiff --git \%(a/.*\|/dev/null\) \zs\%(b/.*\|/dev/null\)')
2150 elseif line('$') == 1 && getline('.') =~ '^\x\{40\}$'
2151 let ref = getline('.')
2157 let ref = s:sub(ref,'^a/','HEAD:')
2158 let ref = s:sub(ref,'^b/',':0:')
2160 let dref = s:sub(dref,'^a/','HEAD:')
2163 let ref = s:sub(ref,'^a/',myhash.'^:')
2164 let ref = s:sub(ref,'^b/',myhash.':')
2166 let dref = s:sub(dref,'^a/',myhash.'^:')
2170 if ref ==# '/dev/null'
2172 let ref = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'
2176 return s:Edit(a:mode,0,ref) . '|'.dcmd.' '.s:fnameescape(dref)
2178 return s:Edit(a:mode,0,ref)
2184 return 'echoerr v:errmsg'
2191 function! s:repo_head_ref() dict abort
2192 return readfile(s:repo().dir('HEAD'))[0]
2195 call s:add_methods('repo',['head_ref'])
2197 function! fugitive#statusline(...)
2198 if !exists('b:git_dir')
2202 if s:buffer().commit() != ''
2203 let status .= ':' . s:buffer().commit()[0:7]
2205 let head = s:repo().head_ref()
2206 if head =~# '^ref: '
2207 let status .= s:sub(head,'^ref: %(refs/%(heads/|remotes/|tags/)=)=','(').')'
2208 elseif head =~# '^\x\{40\}$'
2209 let status .= '('.head[0:7].')'
2211 if &statusline =~# '%[MRHWY]' && &statusline !~# '%[mrhwy]'
2212 return ',GIT'.status
2214 return '[Git'.status.']'
2220 " vim:set ft=vim ts=8 sw=2 sts=2: