1 " Location: autoload/fugitive.vim
2 " Maintainer: Tim Pope <http://tpo.pe/>
4 if exists('g:autoloaded_fugitive')
7 let g:autoloaded_fugitive = 1
9 if !exists('g:fugitive_git_executable')
10 let g:fugitive_git_executable = 'git'
11 elseif g:fugitive_git_executable =~# '^\w\+='
12 let g:fugitive_git_executable = 'env ' . g:fugitive_git_executable
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:Uniq(list) abort
33 let str = string(a:list[i])
35 call remove(a:list, i)
44 function! s:winshell() abort
45 return has('win32') && &shellcmdflag !~# '^-'
48 function! s:shellesc(arg) abort
49 if type(a:arg) == type([])
50 return join(map(copy(a:arg), 's:shellesc(v:val)'))
51 elseif a:arg =~ '^[A-Za-z0-9_/:.-]\+$'
54 return '"'.s:gsub(s:gsub(a:arg, '"', '""'), '\%', '"%"').'"'
56 return shellescape(a:arg)
60 let s:fnameescape = " \t\n*?[{`$\\%#'\"|!<"
61 function! s:fnameescape(file) abort
62 if type(a:file) == type([])
63 return join(map(copy(a:file), 's:fnameescape(v:val)'))
64 elseif exists('*fnameescape')
65 return fnameescape(a:file)
67 return escape(a:file, s:fnameescape)
71 function! s:throw(string) abort
72 throw 'fugitive: '.a:string
75 function! s:DirCheck(...) abort
76 if !empty(a:0 ? s:Dir(a:1) : s:Dir())
78 elseif empty(bufname(''))
79 return 'return ' . string('echoerr "fugitive: blank buffer unsupported (edit a file from a repository)"')
81 return 'return ' . string('echoerr "fugitive: file does not belong to a Git repository"')
85 function! s:Mods(mods, ...) abort
86 let mods = substitute(a:mods, '\C<mods>', '', '')
87 let mods = mods =~# '\S$' ? mods . ' ' : mods
88 if a:0 && mods !~# '\<\%(aboveleft\|belowright\|leftabove\|rightbelow\|topleft\|botright\|tab\)\>'
89 let mods = a:1 . ' ' . mods
91 return substitute(mods, '\s\+', ' ', 'g')
94 function! s:Slash(path) abort
95 if exists('+shellslash')
96 return tr(a:path, '\', '/')
102 function! s:Resolve(path) abort
103 let path = resolve(a:path)
105 let path = FugitiveVimPath(fnamemodify(fnamemodify(path, ':h'), ':p') . fnamemodify(path, ':t'))
110 function! s:cpath(path, ...) abort
111 if exists('+fileignorecase') && &fileignorecase
112 let path = FugitiveVimPath(tolower(a:path))
114 let path = FugitiveVimPath(a:path)
116 return a:0 ? path ==# s:cpath(a:1) : path
119 function! s:Cd(...) abort
120 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : exists(':tcd') && haslocaldir(-1) ? 'tcd' : 'cd'
128 exe cd s:fnameescape(a:1)
129 return cd . ' ' . s:fnameescape(cwd)
132 let s:executables = {}
134 function! s:executable(binary) abort
135 if !has_key(s:executables, a:binary)
136 let s:executables[a:binary] = executable(a:binary)
138 return s:executables[a:binary]
141 let s:nowait = v:version >= 704 ? '<nowait>' : ''
143 function! s:Map(mode, lhs, rhs, ...) abort
144 for mode in split(a:mode, '\zs')
145 let flags = (a:0 ? a:1 : '') . (a:rhs =~# '<Plug>' ? '' : '<script>')
148 let keys = get(g:, mode.'remap', {})
149 if type(keys) == type([])
153 if has_key(keys, head)
154 let head = keys[head]
160 let tail = matchstr(head, '<[^<>]*>$\|.$') . tail
161 let head = substitute(head, '<[^<>]*>$\|.$', '', '')
163 if flags !~# '<unique>' || empty(mapcheck(head.tail, mode))
164 exe mode.'map <buffer>' s:nowait flags head.tail a:rhs
166 let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') .
167 \ '|sil! exe "' . mode . 'unmap <buffer> ' . head.tail . '"'
175 function! s:QuickfixGet(nr, ...) abort
177 return call('getqflist', a:000)
179 return call('getloclist', [a:nr] + a:000)
183 function! s:QuickfixSet(nr, ...) abort
185 return call('setqflist', a:000)
187 return call('setloclist', [a:nr] + a:000)
191 function! s:QuickfixCreate(nr, opts) abort
192 if has('patch-7.4.2200')
193 call s:QuickfixSet(a:nr, [], ' ', a:opts)
195 call s:QuickfixSet(a:nr, [], ' ')
199 function! s:QuickfixStream(nr, title, cmd, first, callback, ...) abort
200 call s:QuickfixCreate(a:nr, {'title': a:title})
202 exe a:nr < 0 ? 'copen' : 'lopen'
208 let lines = split(s:SystemError(s:shellesc(a:cmd))[0], "\n")
210 call extend(buffer, call(a:callback, a:000 + [line]))
212 call s:QuickfixSet(a:nr, remove(buffer, 0, -1), 'a')
216 call s:QuickfixSet(a:nr, extend(buffer, call(a:callback, a:000 + [0])), 'a')
218 if a:first && len(s:QuickfixGet(a:nr))
220 return a:nr < 0 ? 'cfirst' : 'lfirst'
228 function! s:UserCommandList(...) abort
229 let git = split(get(g:, 'fugitive_git_command', g:fugitive_git_executable), '\s\+')
230 let dir = a:0 ? s:Dir(a:1) : ''
232 let tree = s:Tree(dir)
234 call add(git, '--git-dir=' . FugitiveGitPath(dir))
235 elseif len(tree) && s:cpath(tree) !=# s:cpath(getcwd())
236 if fugitive#GitVersion(1, 8, 5)
237 call extend(git, ['-C', FugitiveGitPath(tree)])
239 throw 'fugitive: Git 1.8.5 or higher required to change directory'
246 function! s:UserCommand(...) abort
247 return s:shellesc(call('s:UserCommandList', a:0 ? [a:1] : []) + (a:0 ? a:2 : []))
250 let s:git_versions = {}
251 function! fugitive#GitVersion(...) abort
252 if !has_key(s:git_versions, g:fugitive_git_executable)
253 let s:git_versions[g:fugitive_git_executable] = matchstr(system(g:fugitive_git_executable.' --version'), '\d[^[:space:]]\+')
256 return s:git_versions[g:fugitive_git_executable]
258 let components = split(s:git_versions[g:fugitive_git_executable], '\D\+')
262 for i in range(len(a:000))
263 if a:000[i] > +get(components, i)
265 elseif a:000[i] < +get(components, i)
269 return a:000[i] ==# get(components, i)
272 let s:commondirs = {}
273 function! fugitive#CommonDir(dir) abort
277 if !has_key(s:commondirs, a:dir)
278 if getfsize(a:dir . '/HEAD') < 10
279 let s:commondirs[a:dir] = ''
280 elseif filereadable(a:dir . '/commondir')
281 let cdir = get(readfile(a:dir . '/commondir', 1), 0, '')
282 if cdir =~# '^/\|^\a:/'
283 let s:commondirs[a:dir] = s:Slash(FugitiveVimPath(cdir))
285 let s:commondirs[a:dir] = simplify(a:dir . '/' . cdir)
288 let s:commondirs[a:dir] = a:dir
291 return s:commondirs[a:dir]
294 function! s:Dir(...) abort
295 return a:0 ? FugitiveGitDir(a:1) : FugitiveGitDir()
298 function! s:Tree(...) abort
299 return a:0 ? FugitiveWorkTree(a:1) : FugitiveWorkTree()
302 function! s:HasOpt(args, ...) abort
303 let args = a:args[0 : index(a:args, '--')]
304 let opts = copy(a:000)
305 if type(opts[0]) == type([])
306 if empty(args) || index(opts[0], args[0]) == -1
312 if index(args, opt) != -1
318 function! s:PreparePathArgs(cmd, dir, literal) abort
319 let literal_supported = fugitive#GitVersion(1, 9)
320 if a:literal && literal_supported
321 call insert(a:cmd, '--literal-pathspecs')
323 let split = index(a:cmd, '--')
324 for i in range(split < 0 ? len(a:cmd) : split)
325 if type(a:cmd[i]) == type(0)
326 let a:cmd[i] = fugitive#Path(bufname(a:cmd[i]), './', a:dir)
332 for i in range(split + 1, len(a:cmd) - 1)
333 if type(a:cmd[i]) == type(0)
334 let a:cmd[i] = fugitive#Path(bufname(a:cmd[i]), './', a:dir)
336 let a:cmd[i] = fugitive#Path(a:cmd[i], './', a:dir)
337 elseif !literal_supported
338 let a:cmd[i] = substitute(a:cmd[i], '^:\%(/\|([^)]*)\)\=:\=', './', '')
344 let s:prepare_env = {
345 \ 'sequence.editor': 'GIT_SEQUENCE_EDITOR',
346 \ 'core.editor': 'GIT_EDITOR',
347 \ 'core.askpass': 'GIT_ASKPASS',
349 function! fugitive#PrepareDirEnvArgv(...) abort
350 if a:0 && type(a:1) ==# type([])
351 let cmd = a:000[1:-1] + a:1
353 let cmd = copy(a:000)
358 if cmd[i] =~# '^$\|[\/.]' && cmd[i] !~# '^-'
359 let dir = remove(cmd, 0)
360 elseif cmd[i] =~# '^--git-dir='
361 let dir = remove(cmd, 0)[10:-1]
362 elseif type(cmd[i]) ==# type(0)
363 let dir = s:Dir(remove(cmd, i))
364 elseif cmd[i] ==# '-c' && len(cmd) > i + 1
365 let key = matchstr(cmd[i+1], '^[^=]*')
366 if has_key(s:prepare_env, tolower(key)) || key !~# '\.'
367 let var = get(s:prepare_env, tolower(key), key)
368 let val = matchstr(cmd[i+1], '=\zs.*')
371 if fugitive#GitVersion(1, 8) && cmd[i+1] =~# '\.'
374 call remove(cmd, i, i + 1)
376 elseif cmd[i] =~# '^--.*pathspecs$'
377 let explicit_pathspec_option = 1
378 if fugitive#GitVersion(1, 9)
383 elseif cmd[i] !~# '^-'
392 call s:PreparePathArgs(cmd, dir, !exists('explicit_pathspec_option'))
393 return [dir, env, cmd]
396 function! s:BuildShell(dir, env, args) abort
397 let cmd = copy(a:args)
398 let tree = s:Tree(a:dir)
400 for [var, val] in items(a:env)
402 let pre .= 'set ' . var . '=' . s:shellesc(val) . '& '
404 let pre = (len(pre) ? pre : 'env ') . var . '=' . s:shellesc(val) . ' '
407 if empty(tree) || index(cmd, '--') == len(cmd) - 1
408 call insert(cmd, '--git-dir=' . FugitiveGitPath(a:dir))
409 elseif fugitive#GitVersion(1, 8, 5)
410 call extend(cmd, ['-C', FugitiveGitPath(tree)], 'keep')
412 let pre = 'cd ' . s:shellesc(tree) . (s:winshell() ? '& ' : '; ') . pre
414 return pre . g:fugitive_git_executable . ' ' . join(map(cmd, 's:shellesc(v:val)'))
417 function! fugitive#Prepare(...) abort
418 let [dir, env, argv] = call('fugitive#PrepareDirEnvArgv', a:000)
419 return s:BuildShell(dir, env, argv)
422 function! s:SystemError(cmd, ...) abort
424 if &shellredir ==# '>' && &shell =~# 'sh\|cmd'
425 let shellredir = &shellredir
429 set shellredir=>%s\ 2>&1
432 let out = call('system', [type(a:cmd) ==# type([]) ? fugitive#Prepare(a:cmd) : a:cmd] + a:000)
433 return [out, v:shell_error]
434 catch /^Vim\%((\a\+)\)\=:E484:/
435 let opts = ['shell', 'shellcmdflag', 'shellredir', 'shellquote', 'shellxquote', 'shellxescape', 'shellslash']
436 call filter(opts, 'exists("+".v:val) && !empty(eval("&".v:val))')
437 call map(opts, 'v:val."=".eval("&".v:val)')
438 call s:throw('failed to run `' . a:cmd . '` with ' . join(opts, ' '))
440 if exists('shellredir')
441 let &shellredir = shellredir
446 function! s:ChompError(...) abort
447 let [out, exec_error] = s:SystemError(call('fugitive#Prepare', a:000))
448 return [s:sub(out, '\n$', ''), exec_error]
451 function! s:ChompDefault(default, ...) abort
452 let [out, exec_error] = call('s:ChompError', a:000)
453 return exec_error ? a:default : out
456 function! s:LinesError(...) abort
457 let [out, exec_error] = call('s:ChompError', a:000)
458 return [len(out) && !exec_error ? split(out, "\n", 1) : [], exec_error]
461 function! s:NullError(...) abort
462 let [out, exec_error] = s:SystemError(call('fugitive#Prepare', a:000))
463 return [exec_error ? [] : split(out, "\1"), exec_error ? substitute(out, "\n$", "", "") : '', exec_error]
466 function! s:TreeChomp(...) abort
467 let cmd = call('fugitive#Prepare', a:000)
468 let [out, exec_error] = s:SystemError(cmd)
469 let out = s:sub(out, '\n$', '')
473 throw 'fugitive: error running `' . cmd . '`: ' . out
476 function! s:EchoExec(...) abort
477 echo call('s:ChompError', a:000)[0]
478 call fugitive#ReloadStatus(-1, 1)
482 function! fugitive#Head(...) abort
483 let dir = a:0 > 1 ? a:2 : s:Dir()
484 if empty(dir) || !filereadable(fugitive#Find('.git/HEAD', dir))
487 let head = readfile(fugitive#Find('.git/HEAD', dir))[0]
489 return substitute(head, '\C^ref: \%(refs/\%(heads/\|remotes/\|tags/\)\=\)\=', '', '')
490 elseif head =~# '^\x\{40,\}$'
491 let len = a:0 ? a:1 : 0
492 return len < 0 ? head : len ? head[0:len-1] : ''
498 function! fugitive#RevParse(rev, ...) abort
499 let [hash, exec_error] = s:ChompError([a:0 ? a:1 : s:Dir(), 'rev-parse', '--verify', a:rev, '--'])
500 if !exec_error && hash =~# '^\x\{40,\}$'
503 throw 'fugitive: rev-parse '.a:rev.': '.hash
506 function! s:ConfigTimestamps(dir, dict) abort
507 let files = ['/etc/gitconfig', '~/.gitconfig',
508 \ len($XDG_CONFIG_HOME) ? $XDG_CONFIG_HOME . '/git/config' : '~/.config/git/config']
510 call add(files, fugitive#Find('.git/config', a:dir))
512 call extend(files, get(a:dict, 'include.path', []))
513 return join(map(files, 'getftime(expand(v:val))'), ',')
517 function! fugitive#Config(...) abort
520 if a:0 >= 2 && type(a:2) == type({})
521 let name = substitute(a:1, '^[^.]\+\|[^.]\+$', '\L&', 'g')
522 return len(a:1) ? get(get(a:2, name, []), 0, '') : a:2
526 elseif a:0 == 1 && type(a:1) == type({})
528 elseif a:0 == 1 && a:1 =~# '^[[:alnum:]-]\+\.'
533 let name = substitute(name, '^[^.]\+\|[^.]\+$', '\L&', 'g')
534 let key = len(dir) ? dir : '_'
535 if has_key(s:config, key) && s:config[key][0] ==# s:ConfigTimestamps(dir, s:config[key][1])
536 let dict = s:config[key][1]
539 let [lines, message, exec_error] = s:NullError([dir, 'config', '--list', '-z'])
544 let key = matchstr(line, "^[^\n]*")
545 if !has_key(dict, key)
548 call add(dict[key], strpart(line, len(key) + 1))
550 let s:config[dir] = [s:ConfigTimestamps(dir, dict), dict]
553 return len(name) ? get(get(dict, name, []), 0, '') : dict
556 function! s:Remote(dir) abort
557 let head = FugitiveHead(0, a:dir)
558 let remote = len(head) ? fugitive#Config('branch.' . head . '.remote') : ''
560 while remote ==# '.' && i > 0
561 let head = matchstr(fugitive#Config('branch.' . head . '.merge'), 'refs/heads/\zs.*')
562 let remote = len(head) ? fugitive#Config('branch.' . head . '.remote') : ''
565 return remote =~# '^\.\=$' ? 'origin' : remote
568 function! fugitive#RemoteUrl(...) abort
569 let dir = a:0 > 1 ? a:2 : s:Dir()
570 let remote = !a:0 || a:1 =~# '^\.\=$' ? s:Remote(dir) : a:1
571 if !fugitive#GitVersion(2, 7)
572 return fugitive#Config('remote.' . remote . '.url')
574 return s:ChompDefault('', [dir, 'remote', 'get-url', remote, '--'])
577 " Section: Repository Object
579 function! s:add_methods(namespace, method_names) abort
580 for name in a:method_names
581 let s:{a:namespace}_prototype[name] = s:function('s:'.a:namespace.'_'.name)
585 function! s:Command(command, line1, line2, range, bang, mods, arg, args) abort
587 if a:command =~# '^\l[[:alnum:]-]\+$'
588 return fugitive#Command(a:line1, a:line2, a:range, a:bang, s:Mods(a:mods), a:command . ' ' . a:arg)
590 return s:{a:command}Command(a:line1, a:line2, a:range, a:line2, a:bang, s:Mods(a:mods), '', a:arg, a:args)
592 return 'echoerr ' . string(v:exception)
597 function! s:command(definition, ...) abort
598 let def = a:definition
599 if !has('patch-7.4.542')
600 let def = substitute(def, '-addr=\S\+ ', '', '')
602 if !has('patch-8.1.560')
603 let def = substitute(def, '-addr=other ', '', '')
606 call add(s:commands, def . ' execute s:Command(' . string(a:1) . ", <line1>, <count>, +'<range>', <bang>0, '<mods>', <q-args>, [<f-args>])")
608 call add(s:commands, def)
612 function! s:define_commands() abort
613 for command in s:commands
614 exe 'command! -buffer '.command
618 let s:repo_prototype = {}
621 function! fugitive#repo(...) abort
622 let dir = a:0 ? s:Dir(a:1) : (len(s:Dir()) ? s:Dir() : FugitiveExtractGitDir(expand('%:p')))
624 if has_key(s:repos, dir)
625 let repo = get(s:repos, dir)
627 let repo = {'git_dir': dir}
628 let s:repos[dir] = repo
630 return extend(repo, s:repo_prototype, 'keep')
632 call s:throw('not a Git repository')
635 function! s:repo_dir(...) dict abort
636 return join([self.git_dir]+a:000,'/')
639 function! s:repo_tree(...) dict abort
640 let dir = s:Tree(self.git_dir)
642 call s:throw('no work tree')
644 return join([dir]+a:000,'/')
648 function! s:repo_bare() dict abort
649 if self.dir() =~# '/\.git$'
652 return s:Tree(self.git_dir) ==# ''
656 function! s:repo_find(object) dict abort
657 return fugitive#Find(a:object, self.git_dir)
660 function! s:repo_translate(rev) dict abort
661 return s:Slash(fugitive#Find(substitute(a:rev, '^/', ':(top)', ''), self.git_dir))
664 function! s:repo_head(...) dict abort
665 return fugitive#Head(a:0 ? a:1 : 0, self.git_dir)
668 call s:add_methods('repo',['dir','tree','bare','find','translate','head'])
670 function! s:repo_prepare(...) dict abort
671 return call('fugitive#Prepare', [self.git_dir] + a:000)
674 function! s:repo_git_command(...) dict abort
675 let git = s:UserCommand() . ' --git-dir='.s:shellesc(self.git_dir)
676 return git.join(map(copy(a:000),'" ".s:shellesc(v:val)'),'')
679 function! s:repo_git_chomp(...) dict abort
680 let git = g:fugitive_git_executable . ' --git-dir='.s:shellesc(self.git_dir)
681 let output = git . join(map(copy(a:000),'" ".s:shellesc(v:val)'),'')
682 return s:sub(system(output), '\n$', '')
685 function! s:repo_git_chomp_in_tree(...) dict abort
686 let cdback = s:Cd(self.tree())
688 return call(self.git_chomp, a:000, self)
694 function! s:repo_rev_parse(rev) dict abort
695 return fugitive#RevParse(a:rev, self.git_dir)
698 call s:add_methods('repo',['prepare','git_command','git_chomp','git_chomp_in_tree','rev_parse'])
700 function! s:repo_superglob(base) dict abort
701 return map(fugitive#CompleteObject(a:base, self.git_dir), 'substitute(v:val, ''\\\(.\)'', ''\1'', "g")')
704 call s:add_methods('repo',['superglob'])
706 function! s:repo_config(name) dict abort
707 return fugitive#Config(a:name, self.git_dir)
710 function! s:repo_user() dict abort
711 let username = self.config('user.name')
712 let useremail = self.config('user.email')
713 return username.' <'.useremail.'>'
716 call s:add_methods('repo',['config', 'user'])
720 function! s:DirCommitFile(path) abort
721 let vals = matchlist(s:Slash(a:path), '\c^fugitive:\%(//\)\=\(.\{-\}\)\%(//\|::\)\(\x\{40,\}\|[0-3]\)\(/.*\)\=$')
728 function! s:DirRev(url) abort
729 let [dir, commit, file] = s:DirCommitFile(a:url)
730 return [dir, (commit =~# '^.$' ? ':' : '') . commit . substitute(file, '^/', ':', '')]
733 function! s:Owner(path, ...) abort
734 let dir = a:0 ? a:1 : s:Dir()
738 let actualdir = fugitive#Find('.git/', dir)
739 let [pdir, commit, file] = s:DirCommitFile(a:path)
740 if s:cpath(dir, pdir)
741 if commit =~# '^\x\{40,\}$'
743 elseif commit ==# '2'
746 if filereadable(actualdir . 'MERGE_HEAD')
747 let merge_head = 'MERGE_HEAD'
748 elseif filereadable(actualdir . 'REBASE_HEAD')
749 let merge_head = 'REBASE_HEAD'
754 return merge_head . '^{}'
755 elseif commit ==# '1'
756 return s:TreeChomp('merge-base', 'HEAD', merge_head, '--')
759 let path = fnamemodify(a:path, ':p')
760 if s:cpath(actualdir, strpart(path, 0, len(actualdir))) && a:path =~# 'HEAD$'
761 return strpart(path, len(actualdir))
763 let refs = fugitive#Find('.git/refs', dir)
764 if s:cpath(refs . '/', path[0 : len(refs)]) && path !~# '[\/]$'
765 return strpart(path, len(refs) - 4)
770 function! fugitive#Real(url) abort
774 let [dir, commit, file] = s:DirCommitFile(a:url)
776 let tree = s:Tree(dir)
777 return FugitiveVimPath((len(tree) ? tree : dir) . file)
779 let pre = substitute(matchstr(a:url, '^\a\a\+\ze:'), '^.', '\u&', '')
780 if len(pre) && pre !=? 'fugitive' && exists('*' . pre . 'Real')
781 let url = {pre}Real(a:url)
783 let url = fnamemodify(a:url, ':p' . (a:url =~# '[\/]$' ? '' : ':s?[\/]$??'))
785 return FugitiveVimPath(empty(url) ? a:url : url)
788 function! fugitive#Path(url, ...) abort
792 let dir = a:0 > 1 ? a:2 : s:Dir()
793 let tree = s:Tree(dir)
795 return fugitive#Real(a:url)
797 let path = s:Slash(fugitive#Real(a:url))
800 while s:cpath(tree . '/', (cwd . '/')[0 : len(tree)])
801 if s:cpath(cwd . '/', path[0 : len(cwd)])
802 if strpart(path, len(cwd) + 1) =~# '^\.git\%(/\|$\)'
805 return a:1[0:-2] . (empty(lead) ? './' : lead) . strpart(path, len(cwd) + 1)
807 let cwd = fnamemodify(cwd, ':h')
810 return a:1[0:-2] . path
813 let temp_state = s:TempState(url)
814 if has_key(temp_state, 'bufnr')
815 let url = bufname(temp_state.bufnr)
817 let url = s:Slash(fnamemodify(url, ':p'))
818 if url =~# '/$' && s:Slash(a:url) !~# '/$'
821 let [argdir, commit, file] = s:DirCommitFile(a:url)
822 if len(argdir) && s:cpath(argdir) !=# s:cpath(dir)
824 elseif len(dir) && s:cpath(url[0 : len(dir)]) ==# s:cpath(dir . '/')
825 let file = '/.git'.url[strlen(dir) : -1]
826 elseif len(tree) && s:cpath(url[0 : len(tree)]) ==# s:cpath(tree . '/')
827 let file = url[len(tree) : -1]
828 elseif s:cpath(url) ==# s:cpath(tree)
831 if empty(file) && a:1 =~# '^$\|^[.:]/$'
832 return FugitiveGitPath(fugitive#Real(a:url))
834 return substitute(file, '^/', a:1, '')
837 function! s:Relative(...) abort
838 return fugitive#Path(@%, a:0 ? a:1 : ':(top)', a:0 > 1 ? a:2 : s:Dir())
841 function! fugitive#Find(object, ...) abort
842 if type(a:object) == type(0)
843 let name = bufname(a:object)
844 return FugitiveVimPath(name =~# '^$\|^/\|^\a\+:' ? name : getcwd() . '/' . name)
845 elseif a:object =~# '^[~$]'
846 let prefix = matchstr(a:object, '^[~$]\i*')
847 let owner = expand(prefix)
848 return FugitiveVimPath((len(owner) ? owner : prefix) . strpart(a:object, len(prefix)))
849 elseif s:Slash(a:object) =~# '^$\|^/\|^\%(\a\a\+:\).*\%(//\|::\)' . (has('win32') ? '\|^\a:/' : '')
850 return FugitiveVimPath(a:object)
851 elseif s:Slash(a:object) =~# '^\.\.\=\%(/\|$\)'
852 return FugitiveVimPath(simplify(getcwd() . '/' . a:object))
854 let dir = a:0 ? a:1 : s:Dir()
856 let file = matchstr(a:object, '^\%(:\d:\|[^:]*:\)\zs.*', '', '')
857 let dir = FugitiveExtractGitDir(file)
859 return fnamemodify(FugitiveVimPath(len(file) ? file : a:object), ':p')
862 let rev = s:Slash(a:object)
863 let tree = s:Tree(dir)
864 let base = len(tree) ? tree : 'fugitive://' . dir . '//0'
866 let f = len(tree) ? tree . '/.git' : dir
867 elseif rev =~# '^\.git/'
868 let f = substitute(rev, '^\.git', '', '')
869 let cdir = fugitive#CommonDir(dir)
870 if f =~# '^/\.\./\.\.\%(/\|$\)'
871 let f = simplify(len(tree) ? tree . f[3:-1] : dir . f)
872 elseif f =~# '^/\.\.\%(/\|$\)'
873 let f = base . f[3:-1]
874 elseif cdir !=# dir && (
875 \ f =~# '^/\%(config\|hooks\|info\|logs/refs\|objects\|refs\|worktrees\)\%(/\|$\)' ||
876 \ f !~# '^/\%(index$\|index\.lock$\|\w*MSG$\|\w*HEAD$\|logs/\w*HEAD$\|logs$\|rebase-\w\+\)\%(/\|$\)' &&
877 \ getftime(FugitiveVimPath(dir . f)) < 0 && getftime(FugitiveVimPath(cdir . f)) >= 0)
878 let f = simplify(cdir . f)
880 let f = simplify(dir . f)
884 elseif rev =~# '^\.\%(/\|$\)'
885 let f = base . rev[1:-1]
886 elseif rev =~# '^::\%(/\|\a\+\:\)'
888 elseif rev =~# '^::\.\.\=\%(/\|$\)'
889 let f = simplify(getcwd() . '/' . rev[2:-1])
891 let f = base . '/' . rev[2:-1]
892 elseif rev =~# '^:\%([0-3]:\)\=\.\.\=\%(/\|$\)\|^:[0-3]:\%(/\|\a\+:\)'
893 let f = rev =~# '^:\%([0-3]:\)\=\.' ? simplify(getcwd() . '/' . matchstr(rev, '\..*')) : rev[3:-1]
894 if s:cpath(base . '/', (f . '/')[0 : len(base)])
895 let f = 'fugitive://' . dir . '//' . +matchstr(rev, '^:\zs\d\ze:') . '/' . strpart(f, len(base) + 1)
897 let altdir = FugitiveExtractGitDir(f)
898 if len(altdir) && !s:cpath(dir, altdir)
899 return fugitive#Find(a:object, altdir)
902 elseif rev =~# '^:[0-3]:'
903 let f = 'fugitive://' . dir . '//' . rev[1] . '/' . rev[3:-1]
905 if $GIT_INDEX_FILE =~# '/[^/]*index[^/]*\.lock$' && s:cpath(fnamemodify($GIT_INDEX_FILE,':p')[0:strlen(dir)]) ==# s:cpath(dir . '/') && filereadable($GIT_INDEX_FILE)
906 let f = fnamemodify($GIT_INDEX_FILE, ':p')
908 let f = fugitive#Find('.git/index', dir)
910 elseif rev =~# '^:(\%(top\|top,literal\|literal,top\|literal\))'
911 let f = matchstr(rev, ')\zs.*')
912 if f=~# '^\.\.\=\%(/\|$\)'
913 let f = simplify(getcwd() . '/' . f)
914 elseif f !~# '^/\|^\%(\a\a\+:\).*\%(//\|::\)' . (has('win32') ? '\|^\a:/' : '')
915 let f = base . '/' . f
917 elseif rev =~# '^:/\@!'
918 let f = 'fugitive://' . dir . '//0/' . rev[1:-1]
921 let commit = substitute(matchstr(rev, '^[^:.-][^:]*\|^:.*'), '^@\%($\|[~^]\|@{\)\@=', 'HEAD', '')
922 let file = substitute(matchstr(rev, '^[^:.-][^:]*\zs:.*'), '^:', '/', '')
923 if file =~# '^/\.\.\=\%(/\|$\)\|^//\|^/\a\+:'
924 let file = file =~# '^/\.' ? simplify(getcwd() . file) : file[1:-1]
925 if s:cpath(base . '/', (file . '/')[0 : len(base)])
926 let file = '/' . strpart(file, len(base) + 1)
928 let altdir = FugitiveExtractGitDir(file)
929 if len(altdir) && !s:cpath(dir, altdir)
930 return fugitive#Find(a:object, altdir)
935 let commits = split(commit, '\.\.\.-\@!', 1)
937 call map(commits, 'empty(v:val) || v:val ==# "@" ? "HEAD" : v:val')
938 let commit = matchstr(s:ChompDefault('', [dir, 'merge-base'] + commits + ['--']), '\<[0-9a-f]\{40,\}\>')
940 if commit !~# '^[0-9a-f]\{40,\}$'
941 let commit = matchstr(s:ChompDefault('', [dir, 'rev-parse', '--verify', commit, '--']), '\<[0-9a-f]\{40,\}\>')
944 let f = 'fugitive://' . dir . '//' . commit . file
946 let f = base . '/' . substitute(rev, '^:/:\=\|^[^:]\+:', '', '')
950 return FugitiveVimPath(f)
953 function! s:Generate(rev, ...) abort
954 return fugitive#Find(a:rev, a:0 ? a:1 : s:Dir())
957 function! s:DotRelative(path, ...) abort
958 let cwd = a:0 ? a:1 : getcwd()
959 let path = substitute(a:path, '^[~$]\i*', '\=expand(submatch(0))', '')
960 if len(cwd) && s:cpath(cwd . '/', (path . '/')[0 : len(cwd)])
961 return '.' . strpart(path, len(cwd))
966 function! fugitive#Object(...) abort
967 let dir = a:0 > 1 ? a:2 : s:Dir()
968 let [fdir, rev] = s:DirRev(a:0 ? a:1 : @%)
969 if s:cpath(dir) !=# s:cpath(fdir)
972 let tree = s:Tree(dir)
973 let full = a:0 ? a:1 : @%
974 let full = fnamemodify(full, ':p' . (s:Slash(full) =~# '/$' ? '' : ':s?/$??'))
975 if empty(rev) && empty(tree)
976 return FugitiveGitPath(full)
978 let rev = fugitive#Path(full, './', dir)
979 if rev =~# '^\./.git\%(/\|$\)'
980 return FugitiveGitPath(full)
983 if rev !~# '^\.\%(/\|$\)' || s:cpath(getcwd(), tree)
986 return FugitiveGitPath(tree . rev[1:-1])
990 let s:var = '\%(%\|#<\=\d\+\|##\=\)'
991 let s:flag = '\%(:[p8~.htre]\|:g\=s\(.\).\{-\}\1.\{-\}\1\)'
992 let s:expand = '\%(\(' . s:var . '\)\(' . s:flag . '*\)\(:S\)\=\)'
994 function! s:BufName(var) abort
996 return bufname(get(s:TempState(), 'bufnr', ''))
997 elseif a:var =~# '^#\d*$'
998 let nr = get(s:TempState(bufname(+a:var[1:-1])), 'bufnr', '')
999 return bufname(nr ? nr : +a:var[1:-1])
1001 return expand(a:var)
1005 function! s:ExpandVarLegacy(str) abort
1006 if get(g:, 'fugitive_legacy_quoting', 1)
1007 return substitute(a:str, '\\\ze[%#!]', '', 'g')
1013 function! s:ExpandVar(other, var, flags, esc, ...) abort
1014 let cwd = a:0 ? a:1 : getcwd()
1016 return a:other[1:-1]
1017 elseif a:other =~# '^'''
1018 return s:ExpandVarLegacy(substitute(a:other[1:-2], "''", "'", "g"))
1019 elseif a:other =~# '^"'
1020 return s:ExpandVarLegacy(substitute(a:other[1:-2], '""', '"', "g"))
1021 elseif a:other =~# '^!'
1022 let buffer = s:BufName(len(a:other) > 1 ? '#'. a:other[1:-1] : '%')
1023 let owner = s:Owner(buffer)
1024 return len(owner) ? owner : '@'
1027 let file = s:DotRelative(fugitive#Real(s:BufName(a:var)), cwd)
1029 let flag = matchstr(flags, s:flag)
1030 let flags = strpart(flags, len(flag))
1032 let file = s:DotRelative(file, cwd)
1034 let file = fnamemodify(file, flag)
1037 let file = s:Slash(file)
1038 return (len(a:esc) ? shellescape(file) : file)
1041 function! s:Expand(rev, ...) abort
1042 if a:rev =~# '^:[0-3]$'
1043 let file = len(expand('%')) ? a:rev . ':%' : '%'
1044 elseif a:rev ==# '>'
1046 elseif a:rev =~# '^>[~^]'
1047 let file = len(expand('%')) ? '!' . a:rev[1:-1] . ':%' : '%'
1048 elseif a:rev =~# '^>[> ]\@!'
1049 let file = len(expand('%')) ? a:rev[1:-1] . ':%' : '%'
1053 return substitute(file,
1054 \ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
1055 \ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),"", a:0 ? a:1 : getcwd())', 'g')
1058 function! fugitive#Expand(object) abort
1059 return substitute(a:object,
1060 \ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
1061 \ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5))', 'g')
1064 function! s:ExpandSplit(string, ...) abort
1066 let string = a:string
1067 let handle_bar = a:0 && a:1
1068 let dquote = handle_bar ? '"\%([^"]\|""\|\\"\)*"\|' : ''
1069 let cwd = a:0 > 1 ? a:2 : getcwd()
1070 while string =~# '\S'
1071 if handle_bar && string =~# '^\s*|'
1072 return [list, substitute(string, '^\s*', '', '')]
1074 let arg = matchstr(string, '^\s*\%(' . dquote . '''[^'']*''\|\\.\|[^[:space:] ' . (handle_bar ? '|' : '') . ']\)\+')
1075 let string = strpart(string, len(arg))
1076 let arg = substitute(arg, '^\s\+', '', '')
1077 if !exists('seen_separator')
1078 let arg = substitute(arg, '^\%([^:.][^:]*:\|^:\|^:[0-3]:\)\=\zs\.\.\=\%(/.*\)\=$',
1079 \ '\=s:DotRelative(s:Slash(simplify(getcwd() . "/" . submatch(0))), cwd)', '')
1081 let arg = substitute(arg,
1082 \ '\(' . dquote . '''\%(''''\|[^'']\)*''\|\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
1083 \ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5), cwd)', 'g')
1086 let seen_separator = 1
1089 return handle_bar ? [list, ''] : list
1092 function! s:SplitExpand(string, ...) abort
1093 return s:ExpandSplit(a:string, 0, a:0 ? a:1 : getcwd())
1096 function! s:SplitExpandChain(string, ...) abort
1097 return s:ExpandSplit(a:string, 1, a:0 ? a:1 : getcwd())
1102 function! s:TreeInfo(dir, commit) abort
1103 if a:commit =~# '^:\=[0-3]$'
1104 let index = get(s:indexes, a:dir, [])
1105 let newftime = getftime(fugitive#Find('.git/index', a:dir))
1106 if get(index, 0, -1) < newftime
1107 let [lines, exec_error] = s:LinesError([a:dir, 'ls-files', '--stage', '--'])
1108 let s:indexes[a:dir] = [newftime, {'0': {}, '1': {}, '2': {}, '3': {}}]
1113 let [info, filename] = split(line, "\t")
1114 let [mode, sha, stage] = split(info, '\s\+')
1115 let s:indexes[a:dir][1][stage][filename] = [newftime, mode, 'blob', sha, -2]
1116 while filename =~# '/'
1117 let filename = substitute(filename, '/[^/]*$', '', '')
1118 let s:indexes[a:dir][1][stage][filename] = [newftime, '040000', 'tree', '', 0]
1122 return [get(s:indexes[a:dir][1], a:commit[-1:-1], {}), newftime]
1123 elseif a:commit =~# '^\x\{40,\}$'
1124 if !has_key(s:trees, a:dir)
1125 let s:trees[a:dir] = {}
1127 if !has_key(s:trees[a:dir], a:commit)
1128 let [ftime, exec_error] = s:ChompError([a:dir, 'log', '-1', '--pretty=format:%ct', a:commit, '--'])
1130 let s:trees[a:dir][a:commit] = [{}, -1]
1131 return s:trees[a:dir][a:commit]
1133 let s:trees[a:dir][a:commit] = [{}, +ftime]
1134 let [lines, exec_error] = s:LinesError([a:dir, 'ls-tree', '-rtl', '--full-name', a:commit, '--'])
1136 return s:trees[a:dir][a:commit]
1139 let [info, filename] = split(line, "\t")
1140 let [mode, type, sha, size] = split(info, '\s\+')
1141 let s:trees[a:dir][a:commit][0][filename] = [+ftime, mode, type, sha, +size, filename]
1144 return s:trees[a:dir][a:commit]
1149 function! s:PathInfo(url) abort
1150 let [dir, commit, file] = s:DirCommitFile(a:url)
1151 if empty(dir) || !get(g:, 'fugitive_file_api', 1)
1152 return [-1, '000000', '', '', -1]
1154 let path = substitute(file[1:-1], '/*$', '', '')
1155 let [tree, ftime] = s:TreeInfo(dir, commit)
1156 let entry = empty(path) ? [ftime, '040000', 'tree', '', -1] : get(tree, path, [])
1157 if empty(entry) || file =~# '/$' && entry[2] !=# 'tree'
1158 return [-1, '000000', '', '', -1]
1164 function! fugitive#simplify(url) abort
1165 let [dir, commit, file] = s:DirCommitFile(a:url)
1169 if file =~# '/\.\.\%(/\|$\)'
1170 let tree = s:Tree(dir)
1172 let path = simplify(tree . file)
1173 if strpart(path . '/', 0, len(tree) + 1) !=# tree . '/'
1174 return FugitiveVimPath(path)
1178 return FugitiveVimPath('fugitive://' . simplify(dir) . '//' . commit . simplify(file))
1181 function! fugitive#resolve(url) abort
1182 let url = fugitive#simplify(a:url)
1183 if url =~? '^fugitive:'
1190 function! fugitive#getftime(url) abort
1191 return s:PathInfo(a:url)[0]
1194 function! fugitive#getfsize(url) abort
1195 let entry = s:PathInfo(a:url)
1196 if entry[4] == -2 && entry[2] ==# 'blob' && len(entry[3])
1197 let dir = s:DirCommitFile(a:url)[0]
1198 let entry[4] = +s:ChompDefault(-1, [dir, 'cat-file', '-s', entry[3]])
1203 function! fugitive#getftype(url) abort
1204 return get({'tree': 'dir', 'blob': 'file'}, s:PathInfo(a:url)[2], '')
1207 function! fugitive#filereadable(url) abort
1208 return s:PathInfo(a:url)[2] ==# 'blob'
1211 function! fugitive#filewritable(url) abort
1212 let [dir, commit, file] = s:DirCommitFile(a:url)
1213 if commit !~# '^\d$' || !filewritable(fugitive#Find('.git/index', dir))
1216 return s:PathInfo(a:url)[2] ==# 'blob' ? 1 : 2
1219 function! fugitive#isdirectory(url) abort
1220 return s:PathInfo(a:url)[2] ==# 'tree'
1223 function! fugitive#getfperm(url) abort
1224 let [dir, commit, file] = s:DirCommitFile(a:url)
1225 let perm = getfperm(dir)
1226 let fperm = s:PathInfo(a:url)[1]
1227 if fperm ==# '040000'
1228 let fperm = '000755'
1231 let perm = tr(perm, 'x', '-')
1233 if fperm !~# '[45]$'
1234 let perm = tr(perm, 'rw', '--')
1236 if commit !~# '^\d$'
1237 let perm = tr(perm, 'w', '-')
1239 return perm ==# '---------' ? '' : perm
1242 function! fugitive#setfperm(url, perm) abort
1243 let [dir, commit, file] = s:DirCommitFile(a:url)
1244 let entry = s:PathInfo(a:url)
1245 let perm = fugitive#getfperm(a:url)
1246 if commit !~# '^\d$' || entry[2] !=# 'blob' ||
1247 \ substitute(perm, 'x', '-', 'g') !=# substitute(a:perm, 'x', '-', 'g')
1250 let exec_error = s:SystemError([dir, 'update-index', '--index-info'],
1251 \ (a:perm =~# 'x' ? '000755 ' : '000644 ') . entry[3] . ' ' . commit . "\t" . file[1:-1])[1]
1252 return exec_error ? -1 : 0
1255 function! s:TempCmd(out, cmd) abort
1258 let cmd = (type(a:cmd) == type([]) ? fugitive#Prepare(a:cmd) : a:cmd)
1259 let redir = ' > ' . a:out
1261 let cmd_escape_char = &shellxquote == '(' ? '^' : '^^^'
1262 return s:SystemError('cmd /c "' . prefix . s:gsub(cmd, '[<>]', cmd_escape_char . '&') . redir . '"')
1263 elseif &shell =~# 'fish'
1264 return s:SystemError(' begin;' . prefix . cmd . redir . ';end ')
1266 return s:SystemError(' (' . prefix . cmd . redir . ') ')
1271 if !exists('s:blobdirs')
1274 function! s:BlobTemp(url) abort
1275 let [dir, commit, file] = s:DirCommitFile(a:url)
1279 if !has_key(s:blobdirs, dir)
1280 let s:blobdirs[dir] = tempname()
1282 let tempfile = s:blobdirs[dir] . '/' . commit . file
1283 let tempparent = fnamemodify(tempfile, ':h')
1284 if !isdirectory(tempparent)
1285 call mkdir(tempparent, 'p')
1287 if commit =~# '^\d$' || !filereadable(tempfile)
1288 let rev = s:DirRev(a:url)[1]
1289 let exec_error = s:TempCmd(tempfile, [dir, 'cat-file', 'blob', rev])[1]
1291 call delete(tempfile)
1295 return s:Resolve(tempfile)
1298 function! fugitive#readfile(url, ...) abort
1299 let entry = s:PathInfo(a:url)
1300 if entry[2] !=# 'blob'
1303 let temp = s:BlobTemp(a:url)
1307 return call('readfile', [temp] + a:000)
1310 function! fugitive#writefile(lines, url, ...) abort
1311 let url = type(a:url) ==# type('') ? a:url : ''
1312 let [dir, commit, file] = s:DirCommitFile(url)
1313 let entry = s:PathInfo(url)
1314 if commit =~# '^\d$' && entry[2] !=# 'tree'
1315 let temp = tempname()
1316 if a:0 && a:1 =~# 'a' && entry[2] ==# 'blob'
1317 call writefile(fugitive#readfile(url, 'b'), temp, 'b')
1319 call call('writefile', [a:lines, temp] + a:000)
1320 let [hash, exec_error] = s:ChompError([dir, 'hash-object', '-w', temp])
1321 let mode = len(entry[1]) ? entry[1] : '100644'
1322 if !exec_error && hash =~# '^\x\{40,\}$'
1323 let exec_error = s:SystemError([dir, 'update-index', '--index-info'],
1324 \ mode . ' ' . hash . ' ' . commit . "\t" . file[1:-1])[1]
1330 return call('writefile', [a:lines, a:url] + a:000)
1334 \ '/**/': '/\%([^./][^/]*/\)*',
1335 \ '/**': '/\%([^./][^/]\+/\)*[^./][^/]*',
1336 \ '**/': '[^/]*\%(/[^./][^/]*\)*',
1338 \ '/*': '/[^/.][^/]*',
1341 function! fugitive#glob(url, ...) abort
1342 let [dirglob, commit, glob] = s:DirCommitFile(a:url)
1343 let append = matchstr(glob, '/*$')
1344 let glob = substitute(glob, '/*$', '', '')
1345 let pattern = '^' . substitute(glob, '/\=\*\*/\=\|/\=\*\|[.?\$]\|^^', '\=get(s:globsubs, submatch(0), "\\" . submatch(0))', 'g')[1:-1] . '$'
1347 for dir in dirglob =~# '[*?]' ? split(glob(dirglob), "\n") : [dirglob]
1348 if empty(dir) || !get(g:, 'fugitive_file_api', 1) || !filereadable(fugitive#Find('.git/HEAD', dir))
1351 let files = items(s:TreeInfo(dir, commit)[0])
1353 call filter(files, 'v:val[1][2] ==# "tree"')
1355 call map(files, 'v:val[0]')
1356 call filter(files, 'v:val =~# pattern')
1357 let prepend = 'fugitive://' . dir . '//' . substitute(commit, '^:', '', '') . '/'
1359 call map(files, 'FugitiveVimPath(prepend . v:val . append)')
1360 call extend(results, files)
1365 return join(results, "\n")
1369 function! fugitive#delete(url, ...) abort
1370 let [dir, commit, file] = s:DirCommitFile(a:url)
1371 if a:0 && len(a:1) || commit !~# '^\d$'
1374 let entry = s:PathInfo(a:url)
1375 if entry[2] !=# 'blob'
1378 let exec_error = s:SystemError([dir, 'update-index', '--index-info'],
1379 \ '000000 0000000000000000000000000000000000000000 ' . commit . "\t" . file[1:-1])[1]
1380 return exec_error ? -1 : 0
1383 " Section: Buffer Object
1385 let s:buffer_prototype = {}
1387 function! fugitive#buffer(...) abort
1388 let buffer = {'#': bufnr(a:0 ? a:1 : '%')}
1389 call extend(buffer, s:buffer_prototype, 'keep')
1393 function! s:buffer_repo() dict abort
1394 return fugitive#repo(self['#'])
1397 function! s:buffer_type(...) dict abort
1398 return 'see b:fugitive_type'
1401 call s:add_methods('buffer', ['repo', 'type'])
1403 " Section: Completion
1405 function! s:FilterEscape(items, ...) abort
1406 let items = copy(a:items)
1407 if a:0 && type(a:1) == type('')
1408 call filter(items, 'strpart(v:val, 0, strlen(a:1)) ==# a:1')
1410 return map(items, 's:fnameescape(v:val)')
1413 function! s:GlobComplete(lead, pattern) abort
1416 elseif v:version >= 704
1417 let results = glob(a:lead . a:pattern, 0, 1)
1419 let results = split(glob(a:lead . a:pattern), "\n")
1421 call map(results, 'v:val !~# "/$" && isdirectory(v:val) ? v:val."/" : v:val')
1422 call map(results, 'v:val[ strlen(a:lead) : -1 ]')
1426 function! fugitive#CompletePath(base, ...) abort
1427 let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
1428 let tree = s:Tree(dir) . '/'
1429 let strip = '^\%(:/:\=\|:(top)\|:(top,literal)\|:(literal,top)\|:(literal)\)'
1430 let base = substitute(a:base, strip, '', '')
1431 if base =~# '^\.git/'
1432 let pattern = s:gsub(base[5:-1], '/', '*&').'*'
1433 let matches = s:GlobComplete(dir . '/', pattern)
1434 let cdir = fugitive#CommonDir(dir)
1435 if len(cdir) && s:cpath(dir) !=# s:cpath(cdir)
1436 call extend(matches, s:GlobComplete(cdir . '/', pattern))
1438 call s:Uniq(matches)
1439 call map(matches, "'.git/' . v:val")
1440 elseif base =~# '^\~/'
1441 let matches = map(s:GlobComplete(expand('~/'), base[2:-1] . '*'), '"~/" . v:val')
1442 elseif a:base =~# '^/\|^\a\+:\|^\.\.\=/\|^:(literal)'
1443 let matches = s:GlobComplete('', base . '*')
1444 elseif len(tree) > 1
1445 let matches = s:GlobComplete(tree, s:gsub(base, '/', '*&').'*')
1449 call map(matches, 's:fnameescape(s:Slash(matchstr(a:base, strip) . v:val))')
1453 function! fugitive#PathComplete(...) abort
1454 return call('fugitive#CompletePath', a:000)
1457 function! fugitive#CompleteObject(base, ...) abort
1458 let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
1460 let tree = s:Tree(dir) . '/'
1462 if len(tree) > 1 && s:cpath(tree, cwd[0 : len(tree) - 1])
1463 let subdir = strpart(cwd, len(tree)) . '/'
1466 if a:base =~# '^\.\=/\|^:(' || a:base !~# ':'
1468 if a:base =~# '^refs/'
1469 let results += map(s:GlobComplete(fugitive#CommonDir(dir) . '/', a:base . '*'), 's:Slash(v:val)')
1470 elseif a:base !~# '^\.\=/\|^:('
1471 let heads = ['HEAD', 'ORIG_HEAD', 'FETCH_HEAD', 'MERGE_HEAD', 'refs/']
1472 let heads += sort(s:LinesError(["rev-parse","--symbolic","--branches","--tags","--remotes"], dir)[0])
1473 if filereadable(fugitive#Find('.git/refs/stash', dir))
1474 let heads += ["stash"]
1475 let heads += sort(s:LinesError(["stash","list","--pretty=format:%gd"], dir)[0])
1477 call filter(heads,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base')
1478 let results += heads
1480 call map(results, 's:fnameescape(v:val)')
1482 let results += a:0 == 1 ? fugitive#CompletePath(a:base, dir) : fugitive#CompletePath(a:base)
1486 elseif a:base =~# '^:'
1487 let entries = s:LinesError(['ls-files','--stage'], dir)[0]
1488 if a:base =~# ':\./'
1489 call map(entries, 'substitute(v:val, "\\M\t\\zs" . subdir, "./", "")')
1491 call map(entries,'s:sub(v:val,".*(\\d)\\t(.*)",":\\1:\\2")')
1492 if a:base !~# '^:[0-3]\%(:\|$\)'
1493 call filter(entries,'v:val[1] == "0"')
1494 call map(entries,'v:val[2:-1]')
1498 let tree = matchstr(a:base, '.*[:/]')
1499 let entries = s:LinesError(['ls-tree', substitute(tree, ':\zs\./', '\=subdir', '')], dir)[0]
1500 call map(entries,'s:sub(v:val,"^04.*\\zs$","/")')
1501 call map(entries,'tree.s:sub(v:val,".*\t","")')
1504 return s:FilterEscape(entries, a:base)
1507 function! s:CompleteSub(subcommand, A, L, P, ...) abort
1508 let pre = strpart(a:L, 0, a:P)
1510 return fugitive#CompletePath(a:A)
1511 elseif a:A =~# '^-' || a:A is# 0
1512 return s:FilterEscape(split(s:ChompDefault('', a:subcommand, '--git-completion-helper'), ' '), a:A)
1514 return fugitive#CompleteObject(a:A, s:Dir())
1515 elseif type(a:1) == type(function('tr'))
1516 return call(a:1, [a:A, a:L, a:P])
1518 return s:FilterEscape(a:1, a:A)
1522 function! s:CompleteRevision(A, L, P) abort
1523 return s:FilterEscape(['HEAD', 'FETCH_HEAD', 'MERGE_HEAD', 'ORIG_HEAD'] +
1524 \ s:LinesError('rev-parse', '--symbolic', '--branches', '--tags', '--remotes')[0], a:A)
1527 function! s:CompleteRemote(A, L, P) abort
1528 let remote = matchstr(a:L, '\u\w*[! ] *\zs\S\+\ze ')
1530 let matches = s:LinesError('ls-remote', remote)[0]
1531 call filter(matches, 'v:val =~# "\t" && v:val !~# "{"')
1532 call map(matches, 's:sub(v:val, "^.*\t%(refs/%(heads/|tags/)=)=", "")')
1534 let matches = s:LinesError('remote')[0]
1536 return s:FilterEscape(matches, a:A)
1539 " Section: Buffer auto-commands
1541 function! s:ReplaceCmd(cmd) abort
1542 let temp = tempname()
1543 let [err, exec_error] = s:TempCmd(temp, a:cmd)
1545 call s:throw((len(err) ? err : filereadable(temp) ? join(readfile(temp), ' ') : 'unknown error running ' . a:cmd))
1547 let temp = s:Resolve(temp)
1548 let fn = expand('%:p')
1549 silent exe 'keepalt file '.temp
1550 let modelines = &modelines
1553 silent keepjumps noautocmd edit!
1555 let &modelines = modelines
1557 silent exe 'keepalt file '.s:fnameescape(fn)
1558 catch /^Vim\%((\a\+)\)\=:E302:/
1561 if s:cpath(fnamemodify(bufname('$'), ':p'), temp)
1562 silent execute 'bwipeout '.bufnr('$')
1567 function! s:QueryLog(refspec) abort
1568 let lines = s:LinesError(['log', '-n', '256', '--format=%h%x09%s', a:refspec, '--'])[0]
1569 call map(lines, 'split(v:val, "\t")')
1570 call map(lines, '{"type": "Log", "commit": v:val[0], "subject": v:val[-1]}')
1574 function! s:FormatLog(dict) abort
1575 return a:dict.commit . ' ' . a:dict.subject
1578 function! s:FormatRebase(dict) abort
1579 return a:dict.status . ' ' . a:dict.commit . ' ' . a:dict.subject
1582 function! s:FormatFile(dict) abort
1583 return a:dict.status . ' ' . a:dict.filename
1586 function! s:Format(val) abort
1587 if type(a:val) == type({})
1588 return s:Format{a:val.type}(a:val)
1589 elseif type(a:val) == type([])
1590 return map(copy(a:val), 's:Format(v:val)')
1596 function! s:AddHeader(key, value) abort
1601 while !empty(getline(before))
1604 call append(before - 1, [a:key . ':' . (len(a:value) ? ' ' . a:value : '')])
1605 if before == 1 && line('$') == 2
1610 function! s:AddSection(label, lines, ...) abort
1611 let note = a:0 ? a:1 : ''
1612 if empty(a:lines) && empty(note)
1615 call append(line('$'), ['', a:label . (len(note) ? ': ' . note : ' (' . len(a:lines) . ')')] + s:Format(a:lines))
1618 function! fugitive#BufReadStatus() abort
1619 let amatch = s:Slash(expand('%:p'))
1620 let b:fugitive_type = 'index'
1621 unlet! b:fugitive_reltime
1623 silent doautocmd BufReadPre
1624 let cmd = [fnamemodify(amatch, ':h')]
1625 setlocal noro ma nomodeline buftype=nowrite
1626 if s:cpath(fnamemodify($GIT_INDEX_FILE !=# '' ? $GIT_INDEX_FILE : fugitive#Find('.git/index'), ':p')) !=# s:cpath(amatch)
1627 let cmd += ['-c', 'GIT_INDEX_FILE=' . amatch]
1629 let cmd += ['status', '--porcelain', '-bz']
1630 let [output, message, exec_error] = s:NullError(cmd)
1632 throw 'fugitive: ' . message
1635 let head = matchstr(output[0], '^## \zs\S\+\ze\%($\| \[\)')
1637 if head =~# '\.\.\.'
1638 let [head, pull] = split(head, '\.\.\.')
1640 elseif head ==# 'HEAD' || empty(head)
1641 let head = FugitiveHead(11)
1647 let b:fugitive_status = {'Staged': {}, 'Unstaged': {}}
1648 let [staged, unstaged, untracked] = [[], [], []]
1650 while i < len(output)
1651 let line = output[i]
1652 let file = line[3:-1]
1658 if line[0:1] =~# '[RC]'
1659 let files = output[i] . ' -> ' . file
1662 if line[0] !~# '[ ?!#]'
1663 call add(staged, {'type': 'File', 'status': line[0], 'filename': files})
1665 if line[0:1] ==# '??'
1666 call add(untracked, {'type': 'File', 'status': line[1], 'filename': files})
1667 elseif line[1] !~# '[ !#]'
1668 call add(unstaged, {'type': 'File', 'status': line[1], 'filename': files})
1673 let b:fugitive_status['Staged'][dict.filename] = dict.status
1675 for dict in unstaged
1676 let b:fugitive_status['Unstaged'][dict.filename] = dict.status
1679 let config = fugitive#Config()
1681 let pull_type = 'Pull'
1683 let rebase = fugitive#Config('branch.' . branch . '.rebase', config)
1685 let rebase = fugitive#Config('pull.rebase', config)
1687 if rebase =~# '^\%(true\|yes\|on\|1\|interactive\)$'
1688 let pull_type = 'Rebase'
1689 elseif rebase =~# '^\%(false\|no|off\|0\|\)$'
1690 let pull_type = 'Merge'
1694 let push_remote = fugitive#Config('branch.' . branch . '.pushRemote', config)
1695 if empty(push_remote)
1696 let push_remote = fugitive#Config('remote.pushDefault', config)
1698 let push = len(push_remote) && len(branch) ? push_remote . '/' . branch : ''
1704 let unpulled = s:QueryLog(head . '..' . pull)
1709 let unpushed = s:QueryLog(push . '..' . head)
1714 if isdirectory(fugitive#Find('.git/rebase-merge/'))
1715 let rebasing_dir = fugitive#Find('.git/rebase-merge/')
1716 elseif isdirectory(fugitive#Find('.git/rebase-apply/'))
1717 let rebasing_dir = fugitive#Find('.git/rebase-apply/')
1721 let rebasing_head = 'detached HEAD'
1722 if exists('rebasing_dir') && filereadable(rebasing_dir . 'git-rebase-todo')
1723 let rebasing_head = substitute(readfile(rebasing_dir . 'head-name')[0], '\C^refs/heads/', '', '')
1725 let lines = readfile(rebasing_dir . 'git-rebase-todo')
1727 let hash = matchstr(line, '^[^a-z].*\s\zs[0-9a-f]\{4,\}\ze\.\.')
1733 if getfsize(rebasing_dir . 'done') > 0
1734 let done = readfile(rebasing_dir . 'done')
1735 call map(done, 'substitute(v:val, ''^\l\+\>'', "done", "")')
1736 let done[-1] = substitute(done[-1], '^\l\+\>', 'stop', '')
1737 let lines = done + lines
1741 let match = matchlist(line, '^\(\l\+\)\s\+\(\x\{4,\}\)\s\+\(.*\)')
1742 if len(match) && match[1] !~# 'exec\|merge\|label'
1743 call add(rebasing, {'type': 'Rebase', 'status': get(s:rebase_abbrevs, match[1], match[1]), 'commit': strpart(match[2], 0, len), 'subject': match[3]})
1748 let diff = {'Staged': [], 'Unstaged': []}
1750 let diff['Staged'] =
1751 \ s:LinesError(['diff', '--color=never', '--no-ext-diff', '--no-prefix', '--cached'])[0]
1754 let diff['Unstaged'] =
1755 \ s:LinesError(['diff', '--color=never', '--no-ext-diff', '--no-prefix'])[0]
1757 let b:fugitive_diff = diff
1758 let expanded = get(b:, 'fugitive_expanded', {'Staged': {}, 'Unstaged': {}})
1759 let b:fugitive_expanded = {'Staged': {}, 'Unstaged': {}}
1761 silent keepjumps %delete_
1763 call s:AddHeader('Head', head)
1764 call s:AddHeader(pull_type, pull)
1766 call s:AddHeader('Push', push)
1768 call s:AddSection('Rebasing ' . rebasing_head, rebasing)
1769 call s:AddSection('Untracked', untracked)
1770 call s:AddSection('Unstaged', unstaged)
1771 let unstaged_end = len(unstaged) ? line('$') : 0
1772 call s:AddSection('Staged', staged)
1773 let staged_end = len(staged) ? line('$') : 0
1774 call s:AddSection('Unpushed to ' . push, unpushed)
1775 call s:AddSection('Unpulled from ' . pull, unpulled)
1777 setlocal nomodified readonly noswapfile
1778 silent doautocmd BufReadPost
1779 setlocal nomodifiable
1780 if &bufhidden ==# ''
1781 setlocal bufhidden=delete
1783 let b:dispatch = ':Gfetch --all'
1784 call fugitive#MapJumps()
1785 call s:Map('n', '-', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>')
1786 call s:Map('x', '-', ":<C-U>execute <SID>Do('Toggle',1)<CR>", '<silent>')
1787 call s:Map('n', 's', ":<C-U>execute <SID>Do('Stage',0)<CR>", '<silent>')
1788 call s:Map('x', 's', ":<C-U>execute <SID>Do('Stage',1)<CR>", '<silent>')
1789 call s:Map('n', 'u', ":<C-U>execute <SID>Do('Unstage',0)<CR>", '<silent>')
1790 call s:Map('x', 'u', ":<C-U>execute <SID>Do('Unstage',1)<CR>", '<silent>')
1791 call s:Map('n', 'U', ":exe <SID>EchoExec('reset', '-q')<CR>", '<silent>')
1792 call s:MapMotion('gu', "exe <SID>StageJump(v:count, 'Untracked', 'Unstaged')")
1793 call s:MapMotion('gU', "exe <SID>StageJump(v:count, 'Unstaged', 'Untracked')")
1794 call s:MapMotion('gs', "exe <SID>StageJump(v:count, 'Staged')")
1795 call s:MapMotion('gp', "exe <SID>StageJump(v:count, 'Unpushed')")
1796 call s:MapMotion('gP', "exe <SID>StageJump(v:count, 'Unpulled')")
1797 call s:MapMotion('gr', "exe <SID>StageJump(v:count, 'Rebasing')")
1798 call s:Map('n', 'C', ":<C-U>Gcommit<CR>:echohl WarningMsg<Bar>echo ':Gstatus C is deprecated in favor of cc'<Bar>echohl NONE<CR>", '<silent>')
1799 call s:Map('n', 'a', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>')
1800 call s:Map('n', 'i', ":<C-U>execute <SID>NextExpandedHunk(v:count1)<CR>", '<silent>')
1801 call s:Map('n', "=", ":<C-U>execute <SID>StageInline('toggle',line('.'),v:count)<CR>", '<silent>')
1802 call s:Map('n', "<", ":<C-U>execute <SID>StageInline('hide', line('.'),v:count)<CR>", '<silent>')
1803 call s:Map('n', ">", ":<C-U>execute <SID>StageInline('show', line('.'),v:count)<CR>", '<silent>')
1804 call s:Map('x', "=", ":<C-U>execute <SID>StageInline('toggle',line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1805 call s:Map('x', "<", ":<C-U>execute <SID>StageInline('hide', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1806 call s:Map('x', ">", ":<C-U>execute <SID>StageInline('show', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1807 call s:Map('n', 'D', ":<C-U>execute <SID>StageDiff('Gdiffsplit')<Bar>redraw<Bar>echohl WarningMsg<Bar> echo ':Gstatus D is deprecated in favor of dd'<Bar>echohl NONE<CR>", '<silent>')
1808 call s:Map('n', 'dd', ":<C-U>execute <SID>StageDiff('Gdiffsplit')<CR>", '<silent>')
1809 call s:Map('n', 'dh', ":<C-U>execute <SID>StageDiff('Ghdiffsplit')<CR>", '<silent>')
1810 call s:Map('n', 'ds', ":<C-U>execute <SID>StageDiff('Ghdiffsplit')<CR>", '<silent>')
1811 call s:Map('n', 'dp', ":<C-U>execute <SID>StageDiffEdit()<CR>", '<silent>')
1812 call s:Map('n', 'dv', ":<C-U>execute <SID>StageDiff('Gvdiffsplit')<CR>", '<silent>')
1813 call s:Map('n', 'd?', ":<C-U>help fugitive_d<CR>", '<silent>')
1814 call s:Map('n', 'P', ":<C-U>execute <SID>StagePatch(line('.'),line('.')+v:count1-1)<CR>", '<silent>')
1815 call s:Map('x', 'P', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1816 call s:Map('n', 'p', ":<C-U>if v:count<Bar>silent exe <SID>GF('pedit')<Bar>else<Bar>echoerr 'Use = for inline diff, P for :Git add/reset --patch, 1p for :pedit'<Bar>endif<CR>", '<silent>')
1817 call s:Map('x', 'p', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1818 call s:Map('n', 'I', ":<C-U>execute <SID>StagePatch(line('.'),line('.'))<CR>", '<silent>')
1819 call s:Map('x', 'I', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1820 if empty(mapcheck('q', 'n'))
1821 nnoremap <buffer> <silent> q :<C-U>if bufnr('$') == 1<Bar>quit<Bar>else<Bar>bdelete<Bar>endif<Bar>echohl WarningMsg<Bar>echo ':Gstatus q is deprecated in favor of gq or the built-in <Lt>C-W>q'<Bar>echohl NONE<CR>
1823 call s:Map('n', 'gq', ":<C-U>if bufnr('$') == 1<Bar>quit<Bar>else<Bar>bdelete<Bar>endif<CR>", '<silent>')
1824 call s:Map('n', 'R', ":echohl WarningMsg<Bar>echo 'Reloading is automatic. Use :e to force'<Bar>echohl NONE<CR>", '<silent>')
1825 call s:Map('n', 'g<Bar>', ":<C-U>echoerr 'Changed to X'<CR>", '<silent>')
1826 call s:Map('x', 'g<Bar>', ":<C-U>echoerr 'Changed to X'<CR>", '<silent>')
1827 call s:Map('n', 'X', ":<C-U>execute <SID>StageDelete(line('.'), 0, v:count)<CR>", '<silent>')
1828 call s:Map('x', 'X', ":<C-U>execute <SID>StageDelete(line(\"'<\"), line(\"'>\"), v:count)<CR>", '<silent>')
1829 call s:Map('n', 'gI', ":<C-U>execute <SID>StageIgnore(line('.'), line('.'), v:count)<CR>", '<silent>')
1830 call s:Map('x', 'gI', ":<C-U>execute <SID>StageIgnore(line(\"'<\"), line(\"'>\"), v:count)<CR>", '<silent>')
1831 call s:Map('n', '.', ':<C-U> <C-R>=<SID>StageArgs(0)<CR><Home>')
1832 call s:Map('x', '.', ':<C-U> <C-R>=<SID>StageArgs(1)<CR><Home>')
1833 setlocal filetype=fugitive
1835 for [lnum, section] in [[staged_end, 'Staged'], [unstaged_end, 'Unstaged']]
1836 while len(getline(lnum))
1837 let filename = matchstr(getline(lnum), '^[A-Z?] \zs.*')
1838 if has_key(expanded[section], filename)
1839 call s:StageInline('show', lnum)
1845 let b:fugitive_reltime = reltime()
1848 return 'echoerr ' . string(v:exception)
1852 function! fugitive#FileReadCmd(...) abort
1853 let amatch = a:0 ? a:1 : expand('<amatch>')
1854 let [dir, rev] = s:DirRev(amatch)
1855 let line = a:0 > 1 ? a:2 : line("'[")
1857 return 'noautocmd ' . line . 'read ' . s:fnameescape(amatch)
1859 if rev !~# ':' && s:ChompDefault('', [dir, 'cat-file', '-t', rev]) =~# '^\%(commit\|tag\)$'
1860 let cmd = fugitive#Prepare(dir, 'log', '--pretty=format:%B', '-1', rev, '--')
1862 let cmd = fugitive#Prepare(dir, 'cat-file', '-p', rev)
1864 return line . 'read !' . escape(cmd, '!#%')
1867 function! fugitive#FileWriteCmd(...) abort
1868 let tmp = tempname()
1869 let amatch = a:0 ? a:1 : expand('<amatch>')
1870 let autype = a:0 > 1 ? 'Buf' : 'File'
1871 if exists('#' . autype . 'WritePre')
1872 execute 'doautocmd ' . autype . 'WritePre ' . s:fnameescape(amatch)
1875 let [dir, commit, file] = s:DirCommitFile(amatch)
1876 if commit !~# '^[0-3]$' || !v:cmdbang && (line("'[") != 1 || line("']") != line('$'))
1877 return "noautocmd '[,']write" . (v:cmdbang ? '!' : '') . ' ' . s:fnameescape(amatch)
1879 silent execute "'[,']write !".fugitive#Prepare(dir, 'hash-object', '-w', '--stdin', '--').' > '.tmp
1880 let sha1 = readfile(tmp)[0]
1881 let old_mode = matchstr(s:SystemError([dir, 'ls-files', '--stage', '.' . file])[0], '^\d\+')
1883 let old_mode = executable(s:Tree(dir) . file) ? '100755' : '100644'
1885 let info = old_mode.' '.sha1.' '.commit."\t".file[1:-1]
1886 let [error, exec_error] = s:SystemError([dir, 'update-index', '--index-info'], info . "\n")
1889 if exists('#' . autype . 'WritePost')
1890 execute 'doautocmd ' . autype . 'WritePost ' . s:fnameescape(amatch)
1894 return 'echoerr '.string('fugitive: '.error)
1901 let s:nomodeline = (v:version >= 704 ? '<nomodeline>' : '')
1903 function! fugitive#BufReadCmd(...) abort
1904 let amatch = a:0 ? a:1 : expand('<amatch>')
1906 let [dir, rev] = s:DirRev(amatch)
1908 return 'echo "Invalid Fugitive URL"'
1911 let b:fugitive_type = 'stage'
1913 let [b:fugitive_type, exec_error] = s:ChompError([dir, 'cat-file', '-t', rev])
1914 if exec_error && rev =~# '^:0'
1915 let sha = s:ChompDefault('', dir, 'write-tree', '--prefix=' . rev[3:-1])
1916 let exec_error = empty(sha)
1917 let b:fugitive_type = exec_error ? '' : 'tree'
1920 let error = b:fugitive_type
1921 unlet b:fugitive_type
1923 if empty(&bufhidden)
1924 setlocal bufhidden=delete
1927 let &l:readonly = !filewritable(fugitive#Find('.git/index', dir))
1928 return 'silent doautocmd BufNewFile'
1930 setlocal readonly nomodifiable
1931 return 'silent doautocmd BufNewFile|echo ' . string(error)
1933 elseif b:fugitive_type !~# '^\%(tag\|commit\|tree\|blob\)$'
1934 return "echoerr ".string("fugitive: unrecognized git type '".b:fugitive_type."'")
1936 if !exists('b:fugitive_display_format') && b:fugitive_type != 'blob'
1937 let b:fugitive_display_format = +getbufvar('#','fugitive_display_format')
1941 if b:fugitive_type !=# 'blob'
1945 setlocal noreadonly modifiable
1946 let pos = getpos('.')
1947 silent keepjumps %delete_
1951 silent doautocmd BufReadPre
1952 if b:fugitive_type ==# 'tree'
1953 let b:fugitive_display_format = b:fugitive_display_format % 2
1954 if b:fugitive_display_format
1955 call s:ReplaceCmd([dir, 'ls-tree', exists('sha') ? sha : rev])
1958 let sha = s:TreeChomp(dir, 'rev-parse', '--verify', rev, '--')
1960 call s:ReplaceCmd([dir, 'show', '--no-color', sha])
1962 elseif b:fugitive_type ==# 'tag'
1963 let b:fugitive_display_format = b:fugitive_display_format % 2
1964 if b:fugitive_display_format
1965 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
1967 call s:ReplaceCmd([dir, 'cat-file', '-p', rev])
1969 elseif b:fugitive_type ==# 'commit'
1970 let b:fugitive_display_format = b:fugitive_display_format % 2
1971 if b:fugitive_display_format
1972 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
1974 call s:ReplaceCmd([dir, 'show', '--no-color', '--pretty=format:tree%x20%T%nparent%x20%P%nauthor%x20%an%x20<%ae>%x20%ad%ncommitter%x20%cn%x20<%ce>%x20%cd%nencoding%x20%e%n%n%s%n%n%b', rev])
1975 keepjumps call search('^parent ')
1976 if getline('.') ==# 'parent '
1977 silent keepjumps delete_
1979 silent exe (exists(':keeppatterns') ? 'keeppatterns' : '') 'keepjumps s/\m\C\%(^parent\)\@<! /\rparent /e' . (&gdefault ? '' : 'g')
1981 keepjumps let lnum = search('^encoding \%(<unknown>\)\=$','W',line('.')+3)
1983 silent keepjumps delete_
1985 silent exe (exists(':keeppatterns') ? 'keeppatterns' : '') 'keepjumps 1,/^diff --git\|\%$/s/\r$//e'
1988 elseif b:fugitive_type ==# 'stage'
1989 call s:ReplaceCmd([dir, 'ls-files', '--stage'])
1990 elseif b:fugitive_type ==# 'blob'
1991 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
1994 keepjumps call setpos('.',pos)
1995 setlocal nomodified noswapfile
1996 let modifiable = rev =~# '^:.:' && b:fugitive_type !=# 'tree'
1997 let &l:readonly = !modifiable || !filewritable(fugitive#Find('.git/index', dir))
1998 if empty(&bufhidden)
1999 setlocal bufhidden=delete
2001 let &l:modifiable = modifiable
2002 if b:fugitive_type !=# 'blob'
2003 setlocal filetype=git foldmethod=syntax
2004 call s:Map('n', 'a', ":<C-U>let b:fugitive_display_format += v:count1<Bar>exe fugitive#BufReadCmd(@%)<CR>", '<silent>')
2005 call s:Map('n', 'i', ":<C-U>let b:fugitive_display_format -= v:count1<Bar>exe fugitive#BufReadCmd(@%)<CR>", '<silent>')
2007 call fugitive#MapJumps()
2011 return 'silent doautocmd' . s:nomodeline .
2012 \ ' BufReadPost' . (modifiable ? '' : '|setl nomodifiable')
2014 return 'echoerr ' . string(v:exception)
2018 function! fugitive#BufWriteCmd(...) abort
2019 return fugitive#FileWriteCmd(a:0 ? a:1 : expand('<amatch>'), 1)
2022 function! fugitive#SourceCmd(...) abort
2023 let amatch = a:0 ? a:1 : expand('<amatch>')
2024 let temp = s:BlobTemp(amatch)
2026 return 'noautocmd source ' . s:fnameescape(amatch)
2028 if !exists('g:virtual_scriptnames')
2029 let g:virtual_scriptnames = {}
2031 let g:virtual_scriptnames[temp] = amatch
2032 return 'source ' . s:fnameescape(temp)
2035 " Section: Temp files
2037 if !exists('s:temp_files')
2038 let s:temp_files = {}
2041 function! s:TempState(...) abort
2042 return get(s:temp_files, s:cpath(fnamemodify(a:0 ? a:1 : @%, ':p')), {})
2045 function! s:TempReadPre(file) abort
2046 if has_key(s:temp_files, s:cpath(a:file))
2047 let dict = s:temp_files[s:cpath(a:file)]
2049 setlocal bufhidden=delete nobuflisted
2050 setlocal buftype=nowrite
2051 if has_key(dict, 'modifiable')
2052 let &l:modifiable = dict.modifiable
2055 let b:git_dir = dict.dir
2056 call extend(b:, {'fugitive_type': 'temp'}, 'keep')
2061 function! s:TempReadPost(file) abort
2062 if has_key(s:temp_files, s:cpath(a:file))
2063 let dict = s:temp_files[s:cpath(a:file)]
2064 if has_key(dict, 'filetype') && dict.filetype !=# &l:filetype
2065 let &l:filetype = dict.filetype
2067 setlocal foldmarker=<<<<<<<,>>>>>>>
2068 if empty(mapcheck('q', 'n'))
2069 nnoremap <buffer> <silent> q :<C-U>bdelete<Bar>echohl WarningMsg<Bar>echo "Temp file q is deprecated in favor of the built-in <Lt>C-W>q"<Bar>echohl NONE<CR>
2072 call s:Map('n', 'gq', ":<C-U>bdelete<CR>", '<silent> <unique>')
2078 augroup fugitive_temp
2080 autocmd BufReadPre * exe s:TempReadPre( expand('<amatch>:p'))
2081 autocmd BufReadPost * exe s:TempReadPost(expand('<amatch>:p'))
2086 function! fugitive#Command(line1, line2, range, bang, mods, arg) abort
2088 let [args, after] = s:SplitExpandChain(a:arg, s:Tree(dir))
2090 let cmd = s:StatusCommand(a:line1, a:line2, a:range, a:line2, a:bang, a:mods, '', '', [])
2091 return (empty(cmd) ? 'exe' : cmd) . after
2093 let alias = get(s:Aliases(dir), args[0], '!')
2094 if get(args, 1, '') !=# '--help' && alias !~# '^!\|[\"'']' && !filereadable(s:ExecPath() . '/git-' . args[0])
2095 \ && !(has('win32') && filereadable(s:ExecPath() . '/git-' . args[0] . '.exe'))
2096 call remove(args, 0)
2097 call extend(args, split(alias, '\s\+'), 'keep')
2099 let name = substitute(args[0], '\%(^\|-\)\(\l\)', '\u\1', 'g')
2100 if exists('*s:' . name . 'Subcommand') && get(args, 1, '') !=# '--help'
2103 return 'exe ' . string(s:{name}Subcommand(a:line1, a:line2, a:range, a:bang, a:mods, args[1:-1])) . after
2105 return 'echoerr ' . string(v:exception)
2108 if a:bang || args[0] =~# '^-P$\|^--no-pager$\|diff\%(tool\)\@!\|log\|^show$' ||
2109 \ (args[0] ==# 'stash' && get(args, 1, '') ==# 'show') ||
2110 \ (args[0] ==# 'help' || get(args, 1, '') ==# '--help') && !s:HasOpt(args, '--web')
2111 return s:OpenExec((a:line2 > 0 ? a:line2 : '') . (a:line2 ? 'split' : 'edit'), a:mods, args, dir) . after
2113 if s:HasOpt(args, ['add', 'checkout', 'commit', 'stage', 'stash', 'reset'], '-p', '--patch') ||
2114 \ s:HasOpt(args, ['add', 'clean', 'stage'], '-i', '--interactive') ||
2115 \ index(['--paginate', '-p'], args[0]) >= 0
2116 let mods = substitute(s:Mods(a:mods), '\<tab\>', '-tab', 'g')
2117 let assign = len(dir) ? '|let b:git_dir = ' . string(dir) : ''
2119 if &autowrite || &autowriteall | silent! wall | endif
2120 return mods . (a:line2 ? 'split' : 'edit') . ' term://' . s:fnameescape(s:UserCommand(dir, args)) . assign . '|startinsert' . after
2121 elseif has('terminal')
2122 if &autowrite || &autowriteall | silent! wall | endif
2123 return 'exe ' . string(mods . 'terminal ' . (a:line2 ? '' : '++curwin ') . join(map(s:UserCommandList(dir) + args, 's:fnameescape(v:val)'))) . assign . after
2126 if has('gui_running') && !has('win32')
2127 call insert(args, '--no-pager')
2130 if has('nvim') && executable('env')
2131 let pre .= 'env GIT_TERMINAL_PROMPT=0 '
2133 return 'exe ' . string('!' . escape(pre . s:UserCommand(dir, args), '!#%')) . after
2136 let s:exec_paths = {}
2137 function! s:ExecPath() abort
2138 if !has_key(s:exec_paths, g:fugitive_git_executable)
2139 let s:exec_paths[g:fugitive_git_executable] = s:sub(system(g:fugitive_git_executable.' --exec-path'),'\n$','')
2141 return s:exec_paths[g:fugitive_git_executable]
2144 function! s:Subcommands() abort
2145 let exec_path = s:ExecPath()
2146 return map(split(glob(exec_path.'/git-*'),"\n"),'s:sub(v:val[strlen(exec_path)+5 : -1],"\\.exe$","")')
2150 function! s:Aliases(dir) abort
2151 if !has_key(s:aliases, a:dir)
2152 let s:aliases[a:dir] = {}
2153 let lines = s:NullError([a:dir, 'config', '-z', '--get-regexp', '^alias[.]'])[0]
2155 let s:aliases[a:dir][matchstr(line, '\.\zs.\{-}\ze\n')] = matchstr(line, '\n\zs.*')
2158 return s:aliases[a:dir]
2161 function! fugitive#Complete(lead, ...) abort
2162 let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
2163 let pre = a:0 > 1 ? strpart(a:1, 0, a:2) : ''
2164 let subcmd = matchstr(pre, '\u\w*[! ] *\zs[[:alnum:]-]\+\ze ')
2166 let results = sort(s:Subcommands() + keys(s:Aliases(dir)))
2167 elseif pre =~# ' -- '
2168 return fugitive#CompletePath(a:lead, dir)
2169 elseif a:lead =~# '^-'
2170 let results = split(s:ChompDefault('', dir, subcmd, '--git-completion-helper'), ' ')
2172 return fugitive#CompleteObject(a:lead, dir)
2174 return filter(results, 'strpart(v:val, 0, strlen(a:lead)) ==# a:lead')
2177 " Section: :Gcd, :Glcd
2179 function! fugitive#CdComplete(A, L, P) abort
2180 return filter(fugitive#CompletePath(a:A), 'v:val =~# "/$"')
2183 function! fugitive#Cd(path, ...) abort
2184 let path = substitute(a:path, '^:/:\=\|^:(\%(top\|top,literal\|literal,top\|literal\))', '', '')
2185 if path !~# '^/\|^\a\+:\|^\.\.\=\%(/\|$\)'
2188 let path = (empty(s:Tree(dir)) ? dir : s:Tree(dir)) . '/' . path
2190 return (a:0 && a:1 ? 'lcd ' : 'cd ') . s:fnameescape(FugitiveVimPath(path))
2195 call s:command("-bar -bang -range=-1 -addr=other Gstatus", "Status")
2197 function! s:StatusCommand(line1, line2, range, count, bang, mods, reg, arg, args, ...) abort
2198 let dir = a:0 ? a:1 : s:Dir()
2201 let mods = s:Mods(a:mods, &splitbelow ? 'botright' : 'topleft')
2202 let file = fugitive#Find(':', dir)
2203 let arg = ' +setl\ foldmethod=syntax\ foldlevel=1\|let\ w:fugitive_status=FugitiveGitDir() ' .
2204 \ s:fnameescape(file)
2205 for winnr in range(1, winnr('$'))
2206 if s:cpath(file, fnamemodify(bufname(winbufnr(winnr)), ':p'))
2208 call s:ReloadStatus()
2210 call s:ExpireStatus(dir)
2211 exe winnr . 'wincmd w'
2213 let w:fugitive_status = dir
2219 return mods . 'edit' . (a:bang ? '!' : '') . arg
2221 return mods . 'pedit' . arg . '|wincmd P'
2223 return mods . (a:count > 0 ? a:count : '') . 'split' . arg
2226 return 'echoerr ' . string(v:exception)
2231 function! s:StageJump(offset, section, ...) abort
2232 let line = search('^\%(' . a:section . '\)', 'nw')
2234 let line = search('^\%(' . a:1 . '\)', 'nw')
2239 for i in range(a:offset)
2240 call search(s:file_commit_pattern . '\|^$', 'W')
2241 if empty(getline('.')) && a:0 && getline(line('.') + 1) =~# '^\%(' . a:1 . '\)'
2242 call search(s:file_commit_pattern . '\|^$', 'W')
2244 if empty(getline('.'))
2248 call s:StageReveal()
2250 call s:StageReveal()
2257 function! s:StageSeek(info, fallback) abort
2259 if empty(info.section)
2262 let line = search('^' . info.section, 'wn')
2264 for section in get({'Staged': ['Unstaged', 'Untracked'], 'Unstaged': ['Untracked', 'Staged'], 'Untracked': ['Unstaged', 'Staged']}, info.section, [])
2265 let line = search('^' . section, 'wn')
2267 return line + (info.index > 0 ? 1 : 0)
2273 while len(getline(line))
2274 let filename = matchstr(getline(line), '^[A-Z?] \zs.*')
2276 \ ((info.filename[-1:-1] ==# '/' && filename[0 : len(info.filename) - 1] ==# info.filename) ||
2277 \ (filename[-1:-1] ==# '/' && filename ==# info.filename[0 : len(filename) - 1]) ||
2278 \ filename ==# info.filename)
2282 if getline(line+1) !~# '^@'
2283 exe s:StageInline('show', line)
2285 if getline(line+1) !~# '^@'
2288 let type = info.sigil ==# '-' ? '-' : '+'
2290 while offset < info.offset
2292 if getline(line) =~# '^@'
2293 let offset = +matchstr(getline(line), type . '\zs\d\+') - 1
2294 elseif getline(line) =~# '^[ ' . type . ']'
2296 elseif getline(line) !~# '^[ @\+-]'
2303 let commit = matchstr(getline(line), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\+')
2304 if len(commit) && commit ==# info.commit
2310 let i += getline(line) !~# '^[ @\+-]'
2313 return exists('backup') ? backup : line - 1
2316 function! s:ReloadStatus(...) abort
2317 call s:ExpireStatus(-1)
2318 if get(b:, 'fugitive_type', '') !=# 'index'
2321 let original_lnum = a:0 ? a:1 : line('.')
2322 let info = s:StageInfo(original_lnum)
2323 call fugitive#BufReadStatus()
2324 exe s:StageSeek(info, original_lnum)
2329 let s:last_time = reltime()
2330 if !exists('s:last_times')
2331 let s:last_times = {}
2334 function! s:ExpireStatus(bufnr) abort
2336 let s:last_time = reltime()
2339 let dir = s:Dir(a:bufnr)
2341 let s:last_times[s:cpath(dir)] = reltime()
2346 function! FugitiveReloadCheck() abort
2347 let t = b:fugitive_reltime
2348 return [t, reltimestr(reltime(s:last_time, t)),
2349 \ reltimestr(reltime(get(s:last_times, s:cpath(s:Dir()), t), t))]
2352 function! s:ReloadWinStatus(...) abort
2353 if get(b:, 'fugitive_type', '') !=# 'index' || &modified
2356 if !exists('b:fugitive_reltime')
2357 exe s:ReloadStatus()
2360 let t = b:fugitive_reltime
2361 if reltimestr(reltime(s:last_time, t)) =~# '-\|\d\{10\}\.' ||
2362 \ reltimestr(reltime(get(s:last_times, s:cpath(s:Dir()), t), t)) =~# '-\|\d\{10\}\.'
2363 exe s:ReloadStatus()
2367 function! s:ReloadTabStatus(...) abort
2368 let mytab = tabpagenr()
2369 let tab = a:0 ? a:1 : mytab
2370 for winnr in range(1, tabpagewinnr(tab, '$'))
2371 if getbufvar(tabpagebuflist(tab)[winnr-1], 'fugitive_type') ==# 'index'
2372 execute 'tabnext '.tab
2374 execute winnr.'wincmd w'
2375 let restorewinnr = 1
2378 call s:ReloadWinStatus()
2380 if exists('restorewinnr')
2384 execute 'tabnext '.mytab
2388 unlet! t:fugitive_reload_status
2391 function! fugitive#ReloadStatus(...) abort
2392 call s:ExpireStatus(a:0 ? a:1 : -2)
2393 if a:0 > 1 ? a:2 : s:CanAutoReloadStatus()
2395 let t:fugitive_reload_status = t
2396 for tabnr in exists('*settabvar') ? range(1, tabpagenr('$')) : []
2397 call settabvar(tabnr, 'fugitive_reload_status', t)
2399 call s:ReloadTabStatus()
2401 call s:ReloadWinStatus()
2405 function! s:CanAutoReloadStatus() abort
2406 return get(g:, 'fugitive_autoreload_status', !has('win32'))
2409 augroup fugitive_status
2411 autocmd BufWritePost * call fugitive#ReloadStatus(-1, 0)
2412 autocmd ShellCmdPost * nested call fugitive#ReloadStatus()
2413 autocmd BufDelete term://* nested call fugitive#ReloadStatus()
2415 autocmd FocusGained * call fugitive#ReloadStatus(-2, 0)
2417 autocmd BufEnter index,index.lock
2418 \ call s:ReloadWinStatus()
2420 \ if exists('t:fugitive_reload_status') |
2421 \ call s:ReloadTabStatus() |
2425 function! s:StageInfo(...) abort
2426 let lnum = a:0 ? a:1 : line('.')
2427 let sigil = matchstr(getline(lnum), '^[ @\+-]')
2430 let type = sigil ==# '-' ? '-' : '+'
2431 while lnum > 0 && getline(lnum) !~# '^@'
2432 if getline(lnum) =~# '^[ '.type.']'
2437 let offset += matchstr(getline(lnum), type.'\zs\d\+')
2438 while getline(lnum) =~# '^[ @\+-]'
2442 let slnum = lnum + 1
2445 while len(getline(slnum - 1)) && empty(section)
2447 let section = matchstr(getline(slnum), '^\u\l\+\ze.* (\d\+)$')
2448 if empty(section) && getline(slnum) !~# '^[ @\+-]'
2452 let text = matchstr(getline(lnum), '^[A-Z?] \zs.*')
2453 return {'section': section,
2454 \ 'heading': getline(slnum),
2458 \ 'relative': reverse(split(text, ' -> ')),
2459 \ 'paths': map(reverse(split(text, ' -> ')), 's:Tree() . "/" . v:val'),
2460 \ 'commit': matchstr(getline(lnum), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\{4,\}\ze '),
2461 \ 'status': matchstr(getline(lnum), '^[A-Z?]\ze \|^\%(\x\x\x\)\@!\l\+\ze [0-9a-f]'),
2465 function! s:Selection(arg1, ...) abort
2467 let arg1 = line('.')
2469 elseif a:arg1 ==# 'v'
2470 let arg1 = line("'<")
2471 let arg2 = line("'>")
2474 let arg2 = a:0 ? a:1 : 0
2478 let last = first - arg2 + 1
2484 while getline(first) =~# '^$\|^[A-Z][a-z]'
2487 if first > last || &filetype !=# 'fugitive'
2491 while getline(flnum) =~# '^[ @\+-]'
2494 let slnum = flnum + 1
2497 while len(getline(slnum - 1)) && empty(section)
2499 let heading = matchstr(getline(slnum), '^\u\l\+.* (\d\+)$')
2500 if empty(heading) && getline(slnum) !~# '^[ @\+-]'
2506 \ 'heading': heading,
2507 \ 'section': matchstr(heading, '^\u\l\+\ze.* (\d\+)$'),
2515 let line = getline(flnum)
2516 let lnum = first - (arg1 == flnum ? 0 : 1)
2517 let root = s:Tree() . '/'
2519 if line =~# '^\u\l\+\ze.* (\d\+)$'
2520 let template.heading = getline(lnum)
2521 let template.section = matchstr(template.heading, '^\u\l\+\ze.* (\d\+)$')
2522 let template.index = 0
2523 elseif line =~# '^[ @\+-]'
2524 let template.index -= 1
2525 if !results[-1].patch
2526 let results[-1].patch = lnum
2528 let results[-1].lnum = lnum
2529 elseif line =~# '^[A-Z?] '
2530 let filename = matchstr(line, '^[A-Z?] \zs.*')
2531 call add(results, extend(deepcopy(template), {
2533 \ 'filename': filename,
2534 \ 'relative': reverse(split(filename, ' -> ')),
2535 \ 'paths': map(reverse(split(filename, ' -> ')), 'root . v:val'),
2536 \ 'status': matchstr(line, '^[A-Z?]'),
2538 elseif line =~# '^\x\x\x\+ '
2539 call add(results, extend({
2541 \ 'commit': matchstr(line, '^\x\x\x\+'),
2542 \ }, template, 'keep'))
2543 elseif line =~# '^\l\+ \x\x\x\+ '
2544 call add(results, extend({
2546 \ 'commit': matchstr(line, '^\l\+ \zs\x\x\x\+'),
2547 \ 'status': matchstr(line, '^\l\+'),
2548 \ }, template, 'keep'))
2551 let template.index += 1
2552 let line = getline(lnum)
2554 if len(results) && results[0].patch && arg2 == 0
2555 while getline(results[0].patch) =~# '^[ \+-]'
2556 let results[0].patch -= 1
2558 while getline(results[0].lnum + 1) =~# '^[ \+-]'
2559 let results[0].lnum += 1
2565 function! s:StageArgs(visual) abort
2568 for record in s:Selection(a:visual ? 'v' : 'n')
2569 if len(record.commit)
2570 call add(commits, record.commit)
2572 call extend(paths, record.paths)
2574 if s:cpath(s:Tree(), getcwd())
2575 call map(paths, 'fugitive#Path(v:val, "./")')
2577 return join(map(commits + paths, 's:fnameescape(v:val)'), ' ')
2580 function! s:Do(action, visual) abort
2581 let line = getline('.')
2583 if !a:0 && !v:count && line =~# '^[A-Z][a-z]'
2584 let header = matchstr(line, '^\S\+\ze:')
2585 if len(header) && exists('*s:Do' . a:action . header . 'Header')
2586 let reload = s:Do{a:action}{header}Header(matchstr(line, ': \zs.*')) > 0
2588 let section = matchstr(line, '^\S\+')
2589 if exists('*s:Do' . a:action . section . 'Heading')
2590 let reload = s:Do{a:action}{section}Heading(line) > 0
2593 return reload ? s:ReloadStatus() : ''
2595 let selection = s:Selection(a:visual ? 'v' : 'n')
2599 call filter(selection, 'v:val.section ==# selection[0].section')
2603 for record in selection
2604 if exists('*s:Do' . a:action . record.section)
2605 let status = s:Do{a:action}{record.section}(record)
2612 let reload = reload || (status > 0)
2615 execute record.lnum + 1
2619 return 'echoerr ' . string(v:exception)
2622 execute s:ReloadStatus()
2624 if exists('success')
2625 call s:StageReveal()
2631 function! s:StageReveal(...) abort
2632 let begin = a:0 ? a:1 : line('.')
2633 if getline(begin) =~# '^@'
2635 while getline(end) =~# '^[ \+-]'
2638 elseif getline(begin) =~# '^commit '
2640 while end < line('$') && getline(end + 1) !~# '^commit '
2643 elseif getline(begin) =~# s:section_pattern
2645 while len(getline(end + 1))
2650 while line('.') > line('w0') + &scrolloff && end > line('w$')
2651 execute "normal! \<C-E>"
2656 let s:file_pattern = '^[A-Z?] .\|^diff --'
2657 let s:file_commit_pattern = s:file_pattern . '\|^\%(\l\{3,\} \)\=[0-9a-f]\{4,\} '
2658 let s:item_pattern = s:file_commit_pattern . '\|^@@'
2660 function! s:NextHunk(count) abort
2661 if &filetype ==# 'fugitive' && getline('.') =~# s:file_pattern
2662 exe s:StageInline('show')
2664 for i in range(a:count)
2665 if &filetype ==# 'fugitive'
2666 call search(s:file_pattern . '\|^@', 'W')
2667 if getline('.') =~# s:file_pattern
2668 exe s:StageInline('show')
2669 if getline(line('.') + 1) =~# '^@'
2674 call search('^@@', 'W')
2677 call s:StageReveal()
2681 function! s:PreviousHunk(count) abort
2682 for i in range(a:count)
2683 if &filetype ==# 'fugitive'
2684 let lnum = search(s:file_pattern . '\|^@','Wbn')
2685 call s:StageInline('show', lnum)
2686 call search('^? .\|^@','Wb')
2688 call search('^@@', 'Wb')
2691 call s:StageReveal()
2695 function! s:NextFile(count) abort
2696 for i in range(a:count)
2697 exe s:StageInline('hide')
2698 if !search(s:file_pattern, 'W')
2702 exe s:StageInline('hide')
2706 function! s:PreviousFile(count) abort
2707 exe s:StageInline('hide')
2708 for i in range(a:count)
2709 if !search(s:file_pattern, 'Wb')
2712 exe s:StageInline('hide')
2717 function! s:NextItem(count) abort
2718 for i in range(a:count)
2719 if !search(s:item_pattern, 'W') && getline('.') !~# s:item_pattern
2720 call search('^commit ', 'W')
2723 call s:StageReveal()
2727 function! s:PreviousItem(count) abort
2728 for i in range(a:count)
2729 if !search(s:item_pattern, 'Wbe') && getline('.') !~# s:item_pattern
2730 call search('^commit ', 'Wbe')
2733 call s:StageReveal()
2737 let s:section_pattern = '^[A-Z][a-z][^:]*$'
2738 let s:section_commit_pattern = s:section_pattern . '\|^commit '
2740 function! s:NextSection(count) abort
2741 let orig = line('.')
2742 if getline('.') !~# '^commit '
2745 for i in range(a:count)
2746 if !search(s:section_commit_pattern, 'W')
2750 if getline('.') =~# s:section_commit_pattern
2751 call s:StageReveal()
2752 return getline('.') =~# s:section_pattern ? '+' : ':'
2758 function! s:PreviousSection(count) abort
2759 let orig = line('.')
2760 if getline('.') !~# '^commit '
2763 for i in range(a:count)
2764 if !search(s:section_commit_pattern . '\|\%^', 'bW')
2768 if getline('.') =~# s:section_commit_pattern || line('.') == 1
2769 call s:StageReveal()
2770 return getline('.') =~# s:section_pattern ? '+' : ':'
2776 function! s:NextSectionEnd(count) abort
2778 if empty(getline('.'))
2781 for i in range(a:count)
2782 if !search(s:section_commit_pattern, 'W')
2786 return search('^.', 'Wb')
2789 function! s:PreviousSectionEnd(count) abort
2791 for i in range(a:count)
2792 if search(s:section_commit_pattern, 'Wb') <= 1
2802 return search('^.', 'Wb')
2805 function! s:PatchSearchExpr(reverse) abort
2806 let line = getline('.')
2807 if col('.') ==# 1 && line =~# '^[+-]'
2808 if line =~# '^[+-]\{3\} '
2809 let pattern = '^[+-]\{3\} ' . substitute(escape(strpart(line, 4), '^$.*[]~\'), '^\w/', '\\w/', '') . '$'
2811 let pattern = '^[+-]\s*' . escape(substitute(strpart(line, 1), '^\s*\|\s*$', '', ''), '^$.*[]~\') . '\s*$'
2814 return '?' . escape(pattern, '/') . "\<CR>"
2816 return '/' . escape(pattern, '/?') . "\<CR>"
2819 return a:reverse ? '#' : '*'
2822 function! s:StageInline(mode, ...) abort
2823 if &filetype !=# 'fugitive'
2826 let lnum1 = a:0 ? a:1 : line('.')
2827 let lnum = lnum1 + 1
2828 if a:0 > 1 && a:2 == 0
2829 let info = s:StageInfo(lnum - 1)
2830 if empty(info.paths) && len(info.section)
2831 while len(getline(lnum))
2840 while lnum > 0 && getline(lnum) =~# '^[ @\+-]'
2843 let info = s:StageInfo(lnum)
2844 if !has_key(b:fugitive_diff, info.section)
2847 if getline(lnum + 1) =~# '^[ @\+-]'
2848 let lnum2 = lnum + 1
2849 while getline(lnum2 + 1) =~# '^[ @\+-]'
2852 if a:mode !=# 'show'
2853 setlocal modifiable noreadonly
2854 exe 'silent keepjumps ' . (lnum + 1) . ',' . lnum2 . 'delete _'
2855 call remove(b:fugitive_expanded[info.section], info.filename)
2856 setlocal nomodifiable readonly nomodified
2860 if !has_key(b:fugitive_diff, info.section) || info.status !~# '^[ADMRU]$' || a:mode ==# 'hide'
2867 for line in b:fugitive_diff[info.section]
2868 if mode ==# 'await' && line[0] ==# '@'
2869 let mode = 'capture'
2871 if mode !=# 'head' && line !~# '^[ @\+-]'
2877 elseif mode ==# 'head' && substitute(line, "\t$", '', '') ==# '--- ' . info.relative[-1]
2879 elseif mode ==# 'head' && substitute(line, "\t$", '', '') ==# '+++ ' . info.relative[0]
2881 elseif mode ==# 'capture'
2882 call add(diff, line)
2883 elseif line[0] ==# '@'
2889 setlocal modifiable noreadonly
2890 silent call append(lnum, diff)
2891 let b:fugitive_expanded[info.section][info.filename] = [start, len(diff)]
2892 setlocal nomodifiable readonly nomodified
2898 function! s:NextExpandedHunk(count) abort
2899 for i in range(a:count)
2900 call s:StageInline('show', line('.'), 1)
2901 call search(s:file_pattern . '\|^@','W')
2906 function! s:StageDiff(diff) abort
2907 let lnum = line('.')
2908 let info = s:StageInfo(lnum)
2909 let prefix = info.offset > 0 ? '+' . info.offset : ''
2910 if empty(info.paths) && info.section ==# 'Staged'
2911 return 'Git! diff --no-ext-diff --cached'
2912 elseif empty(info.paths)
2913 return 'Git! diff --no-ext-diff'
2914 elseif len(info.paths) > 1
2915 execute 'Gedit' . prefix s:fnameescape(':0:' . info.paths[0])
2916 return a:diff . '! HEAD:'.s:fnameescape(info.paths[1])
2917 elseif info.section ==# 'Staged' && info.sigil ==# '-'
2918 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
2919 return a:diff . '! :0:%'
2920 elseif info.section ==# 'Staged'
2921 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
2922 return a:diff . '! @:%'
2923 elseif info.sigil ==# '-'
2924 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
2925 return a:diff . '! :(top)%'
2927 execute 'Gedit' prefix s:fnameescape(':(top)'.info.paths[0])
2932 function! s:StageDiffEdit() abort
2933 let info = s:StageInfo(line('.'))
2934 let arg = (empty(info.paths) ? s:Tree() : info.paths[0])
2935 if info.section ==# 'Staged'
2936 return 'Git! diff --no-ext-diff --cached '.s:fnameescape(arg)
2937 elseif info.status ==# '?'
2938 call s:TreeChomp('add', '--intent-to-add', '--', arg)
2939 return s:ReloadStatus()
2941 return 'Git! diff --no-ext-diff '.s:fnameescape(arg)
2945 function! s:StageApply(info, reverse, extra) abort
2946 if a:info.status ==# 'R'
2947 call s:throw('fugitive: patching renamed file not yet supported')
2949 let cmd = ['apply', '-p0', '--recount'] + a:extra
2951 let start = info.patch
2953 let lines = getline(start, end)
2954 if empty(filter(copy(lines), 'v:val =~# "^[+-]"'))
2957 while getline(end) =~# '^[-+ ]'
2959 if getline(end) =~# '^[' . (a:reverse ? '+' : '-') . ' ]'
2960 call add(lines, ' ' . getline(end)[1:-1])
2963 while start > 0 && getline(start) !~# '^@'
2965 if getline(start) =~# '^[' . (a:reverse ? '+' : '-') . ' ]'
2966 call insert(lines, ' ' . getline(start)[1:-1])
2967 elseif getline(start) =~# '^@'
2968 call insert(lines, getline(start))
2972 throw 'fugitive: cold not find hunk'
2973 elseif getline(start) !~# '^@@ '
2974 throw 'fugitive: cannot apply conflict hunk'
2976 let i = b:fugitive_expanded[info.section][info.filename][0]
2978 while get(b:fugitive_diff[info.section], i, '@') !~# '^@'
2979 call add(head, b:fugitive_diff[info.section][i])
2982 call extend(lines, head, 'keep')
2983 let temp = tempname()
2984 call writefile(lines, temp)
2986 call add(cmd, '--reverse')
2988 call extend(cmd, ['--', temp])
2989 let [output, exec_error] = s:ChompError(cmd)
2993 call s:throw(output)
2996 function! s:StageDelete(lnum1, lnum2, count) abort
3000 for info in s:Selection(a:lnum1, a:lnum2)
3001 if empty(info.paths)
3004 let hash = s:TreeChomp('hash-object', '-w', '--', info.paths[0])
3009 call s:StageApply(info, 1, info.section ==# 'Staged' ? ['--index'] : [])
3010 elseif info.status ==# '?'
3011 call s:TreeChomp('clean', '-f', '--', info.paths[0])
3013 call s:TreeChomp('checkout', '--ours', '--', info.paths[0])
3015 call s:TreeChomp('checkout', '--theirs', '--', info.paths[0])
3016 elseif info.status =~# '[ADU]' &&
3017 \ get(b:fugitive_status[info.section ==# 'Staged' ? 'Unstaged' : 'Staged'], info.filename, '') =~# '[AU]'
3018 call s:TreeChomp('checkout', info.section ==# 'Staged' ? '--ours' : '--theirs', '--', info.paths[0])
3019 elseif info.status ==# 'U'
3020 call s:TreeChomp('rm', '--', info.paths[0])
3021 elseif info.status ==# 'A'
3022 call s:TreeChomp('rm', '-f', '--', info.paths[0])
3023 elseif info.section ==# 'Unstaged'
3024 call s:TreeChomp('checkout', '--', info.paths[0])
3026 call s:TreeChomp('checkout', 'HEAD^{}', '--', info.paths[0])
3028 call add(restore, ':Gsplit ' . s:fnameescape(info.relative[0]) . '|Gread ' . hash[0:6])
3031 let err = '|echoerr ' . string(v:exception)
3036 exe s:ReloadStatus()
3037 call s:StageReveal()
3038 return 'checktime|redraw|echomsg ' . string('To restore, ' . join(restore, '|')) . err
3041 function! s:StageIgnore(lnum1, lnum2, count) abort
3043 for info in s:Selection(a:lnum1, a:lnum2)
3044 call extend(paths, info.relative)
3046 call map(paths, '"/" . v:val')
3047 exe 'Gsplit' (a:count ? '.gitignore' : '.git/info/exclude')
3048 let last = line('$')
3049 if last == 1 && empty(getline(1))
3050 call setline(last, paths)
3052 call append(last, paths)
3058 function! s:DoToggleHeadHeader(value) abort
3059 exe 'edit' s:fnameescape(s:Dir())
3060 call search('\C^index$', 'wc')
3063 function! s:DoStageUnpushedHeading(heading) abort
3064 let remote = matchstr(a:heading, 'to \zs[^/]\+\ze/')
3068 let branch = matchstr(a:heading, 'to \%([^/]\+/\)\=\zs\S\+')
3069 call feedkeys(':Gpush ' . remote . ' ' . 'HEAD:' . branch)
3072 function! s:DoToggleUnpushedHeading(heading) abort
3073 return s:DoStageUnpushedHeading(a:heading)
3076 function! s:DoStageUnpushed(record) abort
3077 let remote = matchstr(a:record.heading, 'to \zs[^/]\+\ze/')
3081 let branch = matchstr(a:record.heading, 'to \%([^/]\+/\)\=\zs\S\+')
3082 call feedkeys(':Gpush ' . remote . ' ' . a:record.commit . ':' . branch)
3085 function! s:DoToggleUnpushed(record) abort
3086 return s:DoStageUnpushed(a:record)
3089 function! s:DoUnstageUnpulledHeading(heading) abort
3090 call feedkeys(':Grebase')
3093 function! s:DoToggleUnpulledHeading(heading) abort
3094 call s:DoUnstageUnpulledHeading(a:heading)
3097 function! s:DoUnstageUnpulled(record) abort
3098 call feedkeys(':Grebase ' . a:record.commit)
3101 function! s:DoToggleUnpulled(record) abort
3102 call s:DoUnstageUnpulled(a:record)
3105 function! s:DoUnstageUnpushed(record) abort
3106 call feedkeys(':Grebase --autosquash ' . a:record.commit . '^')
3109 function! s:DoToggleStagedHeading(...) abort
3110 call s:TreeChomp('reset', '-q')
3114 function! s:DoUnstageStagedHeading(heading) abort
3115 return s:DoToggleStagedHeading(a:heading)
3118 function! s:DoToggleUnstagedHeading(...) abort
3119 call s:TreeChomp('add', '-u')
3123 function! s:DoStageUnstagedHeading(heading) abort
3124 return s:DoToggleUnstagedHeading(a:heading)
3127 function! s:DoToggleUntrackedHeading(...) abort
3128 call s:TreeChomp('add', '.')
3132 function! s:DoStageUntrackedHeading(heading) abort
3133 return s:DoToggleUntrackedHeading(a:heading)
3136 function! s:DoToggleStaged(record) abort
3138 return s:StageApply(a:record, 1, ['--cached'])
3140 call s:TreeChomp(['reset', '-q', '--'] + a:record.paths)
3145 function! s:DoUnstageStaged(record) abort
3146 return s:DoToggleStaged(a:record)
3149 function! s:DoToggleUnstaged(record) abort
3150 if a:record.patch && a:record.status !=# 'A'
3151 return s:StageApply(a:record, 0, ['--cached'])
3153 call s:TreeChomp(['add', '-A', '--'] + a:record.paths)
3158 function! s:DoStageUnstaged(record) abort
3159 return s:DoToggleUnstaged(a:record)
3162 function! s:DoUnstageUnstaged(record) abort
3163 if a:record.status ==# 'A'
3164 call s:TreeChomp(['reset', '-q', '--'] + a:record.paths)
3171 function! s:DoToggleUntracked(record) abort
3172 call s:TreeChomp(['add', '--'] + a:record.paths)
3176 function! s:DoStageUntracked(record) abort
3177 return s:DoToggleUntracked(a:record)
3180 function! s:StagePatch(lnum1,lnum2) abort
3185 for lnum in range(a:lnum1,a:lnum2)
3186 let info = s:StageInfo(lnum)
3187 if empty(info.paths) && info.section ==# 'Staged'
3188 return 'Git reset --patch'
3189 elseif empty(info.paths) && info.section ==# 'Unstaged'
3190 return 'Git add --patch'
3191 elseif empty(info.paths) && info.section ==# 'Untracked'
3192 return 'Git add --interactive'
3193 elseif empty(info.paths)
3197 if info.section ==# 'Staged'
3198 let reset += info.relative
3199 elseif info.section ==# 'Untracked'
3200 let intend += info.paths
3201 elseif info.status !~# '^D'
3202 let add += info.relative
3207 call s:TreeChomp(['add', '--intent-to-add', '--'] + intend)
3210 execute "Git add --patch -- ".join(map(add,'s:fnameescape(v:val)'))
3213 execute "Git reset --patch -- ".join(map(reset,'s:fnameescape(v:val)'))
3216 return 'echoerr ' . string(v:exception)
3218 return s:ReloadStatus()
3221 " Section: :Gcommit, :Grevert
3223 function! s:CommitInteractive(line1, line2, range, bang, mods, args, patch) abort
3224 let status = s:StatusCommand(a:line1, a:line2, a:range, a:line2, a:bang, a:mods, '', '', [])
3225 let status = len(status) ? status . '|' : ''
3227 return status . 'if search("^Unstaged")|exe "normal >"|exe "+"|endif'
3229 return status . 'if search("^Untracked\\|^Unstaged")|exe "+"|endif'
3233 function! s:CommitSubcommand(line1, line2, range, bang, mods, args, ...) abort
3234 let mods = substitute(s:Mods(a:mods), '\C\<tab\>', '-tab', 'g')
3235 let dir = a:0 ? a:1 : s:Dir()
3236 let tree = s:Tree(dir)
3237 let msgfile = fugitive#Find('.git/COMMIT_EDITMSG', dir)
3238 let outfile = tempname()
3241 let command = 'set GIT_EDITOR=false& '
3243 let command = 'env GIT_EDITOR=false '
3247 while get(argv, i, '--') !=# '--'
3248 if argv[i] =~# '^-[apzsneiovq].'
3249 call insert(argv, argv[i][0:1])
3250 let argv[i+1] = '-' . argv[i+1][2:-1]
3255 let command .= s:UserCommand(dir, ['commit'] + argv)
3256 if (&autowrite || &autowriteall) && !a:0
3259 if s:HasOpt(argv, '-i', '--interactive')
3260 return s:CommitInteractive(a:line1, a:line2, a:range, a:bang, a:mods, argv, 0)
3261 elseif s:HasOpt(argv, '-p', '--patch')
3262 return s:CommitInteractive(a:line1, a:line2, a:range, a:bang, a:mods, argv, 1)
3264 let [error_string, exec_error] = s:TempCmd(outfile, command)
3265 let errors = split(error_string, "\n")
3267 if !has('gui_running')
3271 echo join(errors, "\n")
3272 if filereadable(outfile)
3273 echo join(readfile(outfile), "\n")
3275 call fugitive#ReloadStatus(dir, 1)
3278 let error = get(errors,-2,get(errors,-1,'!'))
3279 if error =~# 'false''\=\.$'
3281 while get(argv, i, '--') !=# '--'
3282 if argv[i] =~# '^\%(-[eips]\|-[CcFm].\+\|--edit\|--interactive\|--patch\|--signoff\|--reedit-message=.*\|--reuse-message=.*\|--file=.*\|--message=.*\)$'
3283 call remove(argv, i)
3284 elseif argv[i] =~# '^\%(-[CcFm]\|--reedit-message\|--reuse-message\|--file\|--message\)$'
3285 call remove(argv, i, i + 1)
3287 if argv[i] =~# '^--cleanup\>'
3293 call insert(argv, '--no-signoff', i)
3294 call insert(argv, '--no-interactive', i)
3295 call insert(argv, '--no-edit', i)
3296 if !exists('cleanup')
3297 call insert(argv, '--cleanup=strip')
3299 call extend(argv, ['-F', msgfile], 'keep')
3300 if (bufname('%') == '' && line('$') == 1 && getline(1) == '' && !&modified) || a:line2 == 0
3301 execute mods . 'keepalt edit' s:fnameescape(msgfile)
3302 elseif s:HasOpt(argv, '-v') || mods =~# '\<tab\>'
3303 execute mods . 'keepalt -tabedit' s:fnameescape(msgfile)
3305 execute mods . 'keepalt split' s:fnameescape(msgfile)
3307 let b:fugitive_commit_arguments = argv
3308 setlocal bufhidden=wipe filetype=gitcommit
3310 elseif empty(errors)
3311 let out = readfile(outfile)
3312 echo get(out, -1, '') =~# 'stash\|\d' ? get(out, -2, '') : get(out, -1, '')
3315 echo join(errors, "\n")
3320 return 'echoerr ' . string(v:exception)
3322 call delete(outfile)
3326 function! s:RevertSubcommand(line1, line2, range, bang, mods, args) abort
3328 let no_commit = s:HasOpt(a:args, '-n', '--no-commit', '--no-edit', '--abort', '--continue', '--quit')
3329 let cmd = s:UserCommand(dir, ['revert'] + (no_commit ? [] : ['-n']) + a:args)
3330 let [out, exec_error] = s:SystemError(cmd)
3331 call fugitive#ReloadStatus(-1, 1)
3332 if no_commit || exec_error
3333 return 'echo ' . string(substitute(out, "\n$", '', ''))
3335 return s:CommitSubcommand(a:line1, a:line2, a:range, a:bang, a:mods, [], dir)
3338 function! s:CommitComplete(A, L, P) abort
3339 if a:A =~# '^--fixup=\|^--squash='
3340 let commits = s:LinesError(['log', '--pretty=format:%s', '@{upstream}..'])[0]
3341 let pre = matchstr(a:A, '^--\w*=''\=') . ':/^'
3343 call map(commits, 'pre . string(tr(v:val, "|\"^$*[]", "......."))[1:-1]')
3344 call filter(commits, 'strpart(v:val, 0, strlen(a:A)) ==# a:A')
3347 return s:FilterEscape(map(commits, 'pre . tr(v:val, "\\ !^$*?[]()''\"`&;<>|#", "....................")'), a:A)
3350 return s:CompleteSub('commit', a:A, a:L, a:P, function('fugitive#CompletePath'))
3355 function! s:RevertComplete(A, L, P) abort
3356 return s:CompleteSub('revert', a:A, a:L, a:P, function('s:CompleteRevision'))
3359 function! s:FinishCommit() abort
3360 let buf = +expand('<abuf>')
3361 let args = getbufvar(buf, 'fugitive_commit_arguments')
3363 call setbufvar(buf, 'fugitive_commit_arguments', [])
3364 if getbufvar(buf, 'fugitive_commit_rebase')
3365 call setbufvar(buf, 'fugitive_commit_rebase', 0)
3366 let s:rebase_continue = s:Dir(buf)
3368 return s:CommitSubcommand(-1, -1, 0, 0, '', args, s:Dir(buf))
3373 call s:command("-nargs=? -range=-1 -complete=customlist,s:CommitComplete Gcommit", "commit")
3374 call s:command("-nargs=? -range=-1 -complete=customlist,s:RevertComplete Grevert", "revert")
3376 " Section: :Gmerge, :Grebase, :Gpull
3378 function! s:MergeComplete(A, L, P) abort
3379 return s:CompleteSub('merge', a:A, a:L, a:P, function('s:CompleteRevision'))
3382 function! s:RebaseComplete(A, L, P) abort
3383 return s:CompleteSub('rebase', a:A, a:L, a:P, function('s:CompleteRevision'))
3386 function! s:PullComplete(A, L, P) abort
3387 return s:CompleteSub('pull', a:A, a:L, a:P, function('s:CompleteRemote'))
3390 function! s:RebaseSequenceAborter() abort
3391 if !exists('s:rebase_sequence_aborter')
3392 let temp = tempname() . '.sh'
3395 \ 'echo exec false | cat - "$1" > "$1.fugitive"',
3396 \ 'mv "$1.fugitive" "$1"'],
3398 let s:rebase_sequence_aborter = temp
3400 return s:rebase_sequence_aborter
3403 function! fugitive#Cwindow() abort
3404 if &buftype == 'quickfix'
3408 if &buftype == 'quickfix'
3414 let s:common_efm = ''
3416 \ . '%+Eusage:%.%#,'
3417 \ . '%+Eerror:%.%#,'
3418 \ . '%+Efatal:%.%#,'
3419 \ . '%-G%.%#%\e[K%.%#,'
3420 \ . '%-G%.%#%\r%.%\+'
3422 let s:rebase_abbrevs = {
3436 function! s:RebaseEdit(cmd, dir) abort
3437 let rebase_todo = s:fnameescape(fugitive#Find('.git/rebase-merge/git-rebase-todo', a:dir))
3439 if filereadable(rebase_todo)
3440 let new = readfile(rebase_todo)
3444 for i in range(len(new))
3445 if new[i] =~# '^\l\+\s\+[0-9a-f]\{5,\}\>'
3446 let sha = matchstr(new[i], '\C\<[a-f0-9]\{5,\}\>')
3448 let sha_length = len(s:TreeChomp(a:dir, 'rev-parse', '--short', sha))
3450 let shortened_sha = strpart(sha, 0, sha_length)
3451 let shas[shortened_sha] = sha
3452 let new[i] = substitute(new[i], sha, shortened_sha, '')
3455 call writefile(new, rebase_todo)
3457 return a:cmd . ' +setlocal\ bufhidden=wipe\|' . escape('let b:fugitive_rebase_shas = ' . string(shas), ' ') . ' ' . rebase_todo
3460 function! s:MergeRebase(cmd, bang, mods, args, ...) abort
3461 let dir = a:0 ? a:1 : s:Dir()
3463 let mods = s:Mods(a:mods)
3464 if a:cmd =~# '^rebase' && s:HasOpt(args, '-i', '--interactive')
3465 let cmd = fugitive#Prepare(dir, '-c', 'sequence.editor=sh ' . s:RebaseSequenceAborter(), 'rebase') . ' ' . s:shellesc(args)
3466 let out = system(cmd)[0:-2]
3467 for file in ['end', 'msgnum']
3468 let file = fugitive#Find('.git/rebase-merge/' . file, dir)
3469 if !filereadable(file)
3470 return 'echoerr ' . string("fugitive: " . out)
3472 call writefile([readfile(file)[0] - 1], file)
3474 call writefile([], fugitive#Find('.git/rebase-merge/done', dir))
3478 return s:RebaseEdit(mods . 'split', dir)
3479 elseif a:cmd =~# '^rebase' && s:HasOpt(args, '--edit-todo') && filereadable(fugitive#Find('.git/rebase-merge/git-rebase-todo', dir))
3480 return s:RebaseEdit(mods . 'split', dir)
3481 elseif a:cmd =~# '^rebase' && s:HasOpt(args, '--continue') && !a:0
3482 let rdir = fugitive#Find('.git/rebase-merge', dir)
3483 let exec_error = s:ChompError([dir, 'diff-index', '--cached', '--quiet', 'HEAD', '--'])[1]
3484 if exec_error && isdirectory(rdir)
3485 if getfsize(rdir . '/amend') <= 0
3486 return 'exe ' . string(mods . 'Gcommit -n -F ' . s:fnameescape(rdir .'/message') . ' -e') . '|let b:fugitive_commit_rebase = 1'
3487 elseif readfile(rdir . '/amend')[0] ==# fugitive#Head(-1, dir)
3488 return 'exe ' . string(mods . 'Gcommit --amend -n -F ' . s:fnameescape(rdir . '/message') . ' -e') . '|let b:fugitive_commit_rebase = 1'
3492 let had_merge_msg = filereadable(fugitive#Find('.git/MERGE_MSG', dir))
3495 let argv += s:AskPassArgs(dir) + ['pull', '--progress']
3497 call add(argv, a:cmd)
3499 if !s:HasOpt(args, '--no-edit', '--abort', '-m') && a:cmd !=# 'rebase'
3500 call add(argv, '--edit')
3502 if a:cmd ==# 'rebase' && s:HasOpt(args, '--autosquash') && !s:HasOpt(args, '--interactive', '-i')
3503 call add(argv, '--interactive')
3505 call extend(argv, args)
3507 let [mp, efm] = [&l:mp, &l:efm]
3509 let cdback = s:Cd(s:Tree(dir))
3510 let &l:errorformat = ''
3511 \ . '%-Gerror:%.%#false''.,'
3512 \ . '%-G%.%# ''git commit'' %.%#,'
3513 \ . '%+Emerge:%.%#,'
3514 \ . s:common_efm . ','
3515 \ . '%+ECannot %.%#: You have unstaged changes.,'
3516 \ . '%+ECannot %.%#: Your index contains uncommitted changes.,'
3517 \ . '%+EThere is no tracking information for the current branch.,'
3518 \ . '%+EYou are not currently on a branch. Please specify which,'
3519 \ . '%+I %#git rebase --continue,'
3520 \ . 'CONFLICT (%m): %f deleted in %.%#,'
3521 \ . 'CONFLICT (%m): Merge conflict in %f,'
3522 \ . 'CONFLICT (%m): Rename \"%f\"->%.%#,'
3523 \ . 'CONFLICT (%m): Rename %.%#->%f %.%#,'
3524 \ . 'CONFLICT (%m): There is a directory with name %f in %.%#,'
3525 \ . '%+ECONFLICT %.%#,'
3526 \ . '%+EKONFLIKT %.%#,'
3527 \ . '%+ECONFLIT %.%#,'
3528 \ . "%+EXUNG \u0110\u1ed8T %.%#,"
3529 \ . "%+E\u51b2\u7a81 %.%#,"
3531 if a:cmd =~# '^merge' && empty(args) &&
3532 \ (had_merge_msg || isdirectory(fugitive#Find('.git/rebase-apply', dir)) ||
3533 \ !empty(s:TreeChomp(dir, 'diff-files', '--diff-filter=U')))
3534 let cmd = g:fugitive_git_executable.' diff-files --name-status --diff-filter=U'
3536 let cmd = s:UserCommand(dir, argv)
3538 if !empty($GIT_SEQUENCE_EDITOR) || has('win32')
3539 let old_sequence_editor = $GIT_SEQUENCE_EDITOR
3540 let $GIT_SEQUENCE_EDITOR = 'true'
3542 let cmd = 'env GIT_SEQUENCE_EDITOR=true ' . cmd
3544 if !empty($GIT_EDITOR) || has('win32')
3545 let old_editor = $GIT_EDITOR
3546 let $GIT_EDITOR = 'false'
3548 let cmd = 'env GIT_EDITOR=false ' . substitute(cmd, '^env ', '', '')
3550 if !has('patch-8.1.0334') && has('terminal') && &autowrite
3551 let autowrite_was_set = 1
3555 let &l:makeprg = cmd
3556 silent noautocmd make!
3557 catch /^Vim\%((\a\+)\)\=:E211/
3558 let err = v:exception
3560 if exists('autowrite_was_set')
3564 let [&l:mp, &l:efm] = [mp, efm]
3565 if exists('old_editor')
3566 let $GIT_EDITOR = old_editor
3568 if exists('old_sequence_editor')
3569 let $GIT_SEQUENCE_EDITOR = old_sequence_editor
3573 call fugitive#ReloadStatus(dir, 1)
3574 if empty(filter(getqflist(),'v:val.valid && v:val.type !=# "I"'))
3575 if a:cmd =~# '^rebase' &&
3576 \ filereadable(fugitive#Find('.git/rebase-merge/amend', dir)) &&
3577 \ filereadable(fugitive#Find('.git/rebase-merge/done', dir)) &&
3578 \ get(readfile(fugitive#Find('.git/rebase-merge/done', dir)), -1, '') =~# '^[^e]'
3580 return 'exe ' . string(mods . 'Gcommit --amend -n -F ' . s:fnameescape(fugitive#Find('.git/rebase-merge/message', dir)) . ' -e') . '|let b:fugitive_commit_rebase = 1'
3581 elseif !had_merge_msg && filereadable(fugitive#Find('.git/MERGE_MSG', dir))
3583 return mods . 'Gcommit --no-status -n -t '.s:fnameescape(fugitive#Find('.git/MERGE_MSG', dir))
3586 let qflist = getqflist()
3591 let e.pattern = '^<<<<<<<'
3594 call fugitive#Cwindow()
3596 call setqflist(qflist, 'r')
3602 return exists('err') ? 'echoerr '.string(err) : 'exe'
3605 function! s:RebaseClean(file) abort
3606 if !filereadable(a:file)
3609 let old = readfile(a:file)
3611 for i in range(len(new))
3612 let new[i] = substitute(new[i], '^\l\>', '\=get(s:rebase_abbrevs,submatch(0),submatch(0))', '')
3614 let sha = matchstr(new[i], '\C\<[a-f0-9]\{5,\}\>')
3615 let rebase_shas = getbufvar(a:file, 'fugitive_rebase_shas')
3616 if len(sha) && type(rebase_shas) == type({}) && has_key(rebase_shas, sha)
3617 let new[i] = substitute(new[i], '\C\<' . sha . '\>', rebase_shas[sha], '')
3621 call writefile(new, a:file)
3626 function! s:MergeSubcommand(line1, line2, range, bang, mods, args) abort
3627 return s:MergeRebase('merge', a:bang, a:mods, a:args)
3630 function! s:RebaseSubcommand(line1, line2, range, bang, mods, args) abort
3631 return s:MergeRebase('rebase', a:bang, a:mods, a:args)
3634 function! s:PullSubcommand(line1, line2, range, bang, mods, args) abort
3635 return s:MergeRebase('pull', a:bang, a:mods, a:args)
3638 augroup fugitive_merge
3640 autocmd VimLeavePre,BufDelete git-rebase-todo
3641 \ if getbufvar(+expand('<abuf>'), '&bufhidden') ==# 'wipe' |
3642 \ call s:RebaseClean(expand('<afile>')) |
3643 \ if getfsize(FugitiveFind('.git/rebase-merge/done', +expand('<abuf>'))) == 0 |
3644 \ let s:rebase_continue = FugitiveGitDir(+expand('<abuf>')) |
3647 autocmd BufEnter * nested
3648 \ if exists('s:rebase_continue') |
3649 \ exe s:MergeRebase('rebase', 0, '', [getfsize(fugitive#Find('.git/rebase-merge/git-rebase-todo', s:rebase_continue)) > 0 ? '--continue' : '--abort'], remove(s:, 'rebase_continue')) |
3653 call s:command("-nargs=? -bang -complete=customlist,s:MergeComplete Gmerge", "merge")
3654 call s:command("-nargs=? -bang -complete=customlist,s:RebaseComplete Grebase", "rebase")
3655 call s:command("-nargs=? -bang -complete=customlist,s:PullComplete Gpull", "pull")
3657 " Section: :Ggrep, :Glog
3659 if !exists('g:fugitive_summary_format')
3660 let g:fugitive_summary_format = '%s'
3663 function! fugitive#GrepComplete(A, L, P) abort
3664 return s:CompleteSub('grep', a:A, a:L, a:P)
3667 function! fugitive#LogComplete(A, L, P) abort
3668 return s:CompleteSub('log', a:A, a:L, a:P)
3671 function! s:GrepParseLine(prefix, name_only, dir, line) abort
3672 let entry = {'valid': 1}
3673 let match = matchlist(a:line, '^\(.\{-\}\):\(\d\+\):\(\d\+:\)\=\(.*\)$')
3675 let entry.module = match[1]
3676 let entry.lnum = +match[2]
3677 let entry.col = +match[3]
3678 let entry.text = match[4]
3679 elseif a:line =~# '^git: \|^usage: \|^error: \|^fatal: '
3680 return {'text': a:line}
3682 let entry.module = matchstr(a:line, '\CBinary file \zs.*\ze matches$')
3683 if len(entry.module)
3684 let entry.text = 'Binary file'
3688 if empty(entry.module) && a:name_only
3689 let entry.module = a:line
3691 if empty(entry.module)
3692 return {'text': a:line}
3694 if entry.module !~# ':'
3695 let entry.filename = a:prefix . entry.module
3697 let entry.filename = fugitive#Find(entry.module, a:dir)
3702 function! s:GrepSubcommand(line1, line2, range, bang, mods, args) abort
3705 let listnr = a:line1 == 0 ? a:line1 : a:line2
3706 let cmd = ['--no-pager', 'grep', '-n', '--no-color', '--full-name']
3707 if fugitive#GitVersion(2, 19)
3708 call add(cmd, '--column')
3710 let tree = s:Tree(dir)
3711 if type(a:args) == type([])
3712 let [args, after] = [a:args, '']
3714 let [args, after] = s:SplitExpandChain(a:args, tree)
3716 let prefix = FugitiveVimPath(s:HasOpt(args, '--cached') || empty(tree) ? 'fugitive://' . dir . '//0/' : tree . '/')
3717 let name_only = s:HasOpt(args, '-l', '--files-with-matches', '--name-only', '-L', '--files-without-match')
3718 let title = [listnr < 0 ? ':Ggrep' : ':Glgrep'] + args
3720 exe listnr 'wincmd w'
3725 call s:QuickfixCreate(listnr, {'title': (listnr < 0 ? ':Ggrep ' : ':Glgrep ') . s:fnameescape(args)})
3726 let tempfile = tempname()
3727 if v:version >= 704 | exe 'silent doautocmd <nomodeline> QuickFixCmdPre ' (listnr < 0 ? 'Ggrep' : 'Glgrep') | endif
3728 exe '!' . escape(s:UserCommand(dir, cmd + args), '%#!')
3729 \ printf(&shellpipe . (&shellpipe =~# '%s' ? '' : ' %s'), s:shellesc(tempfile))
3730 let list = map(readfile(tempfile), 's:GrepParseLine(prefix, name_only, dir, v:val)')
3731 call s:QuickfixSet(listnr, list, 'a')
3732 if v:version >= 704 | exe 'silent doautocmd <nomodeline> QuickFixCmdPost ' (listnr < 0 ? 'Ggrep' : 'Glgrep') | endif
3733 if !has('gui_running')
3736 if !a:bang && !empty(list)
3737 return (listnr < 0 ? 'c' : 'l').'first' . after
3743 function! s:LogFlushQueue(state) abort
3744 let queue = remove(a:state, 'queue')
3745 if a:state.child_found
3746 call remove(queue, 0)
3748 if len(queue) && queue[-1] ==# {'text': ''}
3749 call remove(queue, -1)
3754 function! s:LogParse(state, dir, line) abort
3755 if a:state.context ==# 'hunk' && a:line =~# '^[-+ ]'
3758 let list = matchlist(a:line, '^\%(fugitive \(.\{-\}\)\t\|commit \|From \)\=\(\x\{40,\}\)\%( \(.*\)\)\=$')
3760 let a:state.context = 'commit'
3761 let a:state.base = 'fugitive://' . a:dir . '//' . list[2]
3762 let a:state.base_module = len(list[1]) ? list[1] : list[2]
3763 let a:state.message = list[3]
3764 if has_key(a:state, 'diffing')
3765 call remove(a:state, 'diffing')
3767 let queue = s:LogFlushQueue(a:state)
3768 let a:state.queue = [{
3770 \ 'filename': a:state.base . a:state.target,
3771 \ 'module': a:state.base_module . substitute(a:state.target, '^/', ':', ''),
3772 \ 'text': a:state.message}]
3773 let a:state.child_found = 0
3775 elseif type(a:line) == type(0)
3776 return s:LogFlushQueue(a:state)
3777 elseif a:line =~# '^diff'
3778 let a:state.context = 'diffhead'
3779 elseif a:line =~# '^[+-]\{3\} \w/' && a:state.context ==# 'diffhead'
3780 let a:state.diffing = a:line[5:-1]
3781 elseif a:line =~# '^@@[^@]*+\d' && has_key(a:state, 'diffing') && has_key(a:state, 'base')
3782 let a:state.context = 'hunk'
3783 if empty(a:state.target) || a:state.target ==# a:state.diffing
3784 let a:state.child_found = 1
3785 call add(a:state.queue, {
3787 \ 'filename': a:state.base . a:state.diffing,
3788 \ 'module': a:state.base_module . substitute(a:state.diffing, '^/', ':', ''),
3789 \ 'lnum': +matchstr(a:line, '+\zs\d\+'),
3790 \ 'text': a:state.message . matchstr(a:line, ' @@\+ .\+')})
3792 elseif a:state.follow &&
3793 \ a:line =~# '^ \%(mode change \d\|\%(create\|delete\) mode \d\|\%(rename\|copy\|rewrite\) .* (\d\+%)$\)'
3794 let rename = matchstr(a:line, '^ rename \zs.* => .*\ze (\d\+%)$')
3796 let rename = rename =~# '{.* => .*}' ? rename : '{' . rename . '}'
3797 if a:state.target ==# simplify('/' . substitute(rename, '{.* => \(.*\)}', '\1', ''))
3798 let a:state.target = simplify('/' . substitute(rename, '{\(.*\) => .*}', '\1', ''))
3801 if !get(a:state, 'ignore_summary')
3802 call add(a:state.queue, {'text': a:line})
3804 elseif a:state.context ==# 'commit' || a:state.context ==# 'init'
3805 call add(a:state.queue, {'text': a:line})
3810 function! fugitive#LogCommand(line1, count, range, bang, mods, args, type) abort
3813 let listnr = a:type =~# '^l' ? 0 : -1
3814 let [args, after] = s:SplitExpandChain(a:args, s:Tree(dir))
3815 let split = index(args, '--')
3817 let paths = args[split : -1]
3818 let args = args[0 : split - 1]
3825 if a:line1 == 0 && a:count
3826 let path = fugitive#Path(bufname(a:count), '/', dir)
3828 let path = fugitive#Path(@%, '/', dir)
3834 let state = {'context': 'init', 'child_found': 0, 'queue': [], 'follow': 0}
3835 if path =~# '^/\.git\%(/\|$\)\|^$'
3838 let range = "0," . (a:count ? a:count : bufnr(''))
3839 let extra = ['.' . path]
3840 if (empty(paths) || paths ==# ['--']) && !s:HasOpt(args, '--no-follow')
3841 let state.follow = 1
3842 if !s:HasOpt(args, '--follow')
3843 call insert(args, '--follow')
3845 if !s:HasOpt(args, '--summary')
3846 call insert(args, '--summary')
3847 let state.ignore_summary = 1
3851 if !s:HasOpt(args, '--merges', '--no-merges')
3852 call insert(args, '--no-merges')
3854 call add(args, '-L' . a:line1 . ',' . a:count . ':' . path[1:-1])
3856 if len(path) && empty(filter(copy(args), 'v:val =~# "^[^-]"'))
3857 let owner = s:Owner(@%, dir)
3859 call add(args, owner)
3865 if s:HasOpt(args, '-g', '--walk-reflogs')
3866 let format = "%gd\t%H %gs"
3868 let format = "%h\t%H " . g:fugitive_summary_format
3870 let cmd = ['--no-pager']
3871 if fugitive#GitVersion(1, 9)
3872 call extend(cmd, ['-c', 'diff.context=0', '-c', 'diff.noprefix=false', 'log'])
3874 call extend(cmd, ['log', '-U0', '--no-patch'])
3877 \ ['--no-color', '--no-ext-diff', '--pretty=format:fugitive ' . format] +
3878 \ args + paths + extra)
3879 let state.target = path
3880 let title = (listnr < 0 ? ':Gclog ' : ':Gllog ') . s:fnameescape(args + paths)
3881 if empty(paths + extra) && empty(a:type) && len(s:Relative('/'))
3882 let after = '|echohl WarningMsg|echo ' . string('Use :0Glog or :0Gclog for old behavior of targeting current file') . '|echohl NONE' . after
3884 return s:QuickfixStream(listnr, title, s:UserCommandList(dir) + cmd, !a:bang, s:function('s:LogParse'), state, dir) . after
3887 " Section: :Gedit, :Gpedit, :Gsplit, :Gvsplit, :Gtabedit, :Gread
3889 function! s:UsableWin(nr) abort
3890 return a:nr && !getwinvar(a:nr, '&previewwindow') && !getwinvar(a:nr, '&winfixwidth') &&
3891 \ (empty(getwinvar(a:nr, 'fugitive_status')) || getbufvar(winbufnr(a:nr), 'fugitive_type') !=# 'index') &&
3892 \ index(['gitrebase', 'gitcommit'], getbufvar(winbufnr(a:nr), '&filetype')) < 0 &&
3893 \ index(['nofile','help','quickfix'], getbufvar(winbufnr(a:nr), '&buftype')) < 0
3896 function! s:OpenParse(args) abort
3898 let args = copy(a:args)
3899 while !empty(args) && args[0] =~# '^+'
3900 call add(pre, ' ' . escape(remove(args, 0), ' |"'))
3903 let file = join(args)
3904 elseif empty(expand('%'))
3906 elseif empty(s:DirCommitFile(@%)[1]) && s:Relative('./') !~# '^\./\.git\>'
3911 return [s:Expand(file), join(pre)]
3914 function! s:DiffClose() abort
3915 let mywinnr = winnr()
3916 for winnr in [winnr('#')] + range(winnr('$'),1,-1)
3917 if winnr != mywinnr && getwinvar(winnr,'&diff')
3918 execute winnr.'wincmd w'
3928 function! s:BlurStatus() abort
3929 if (&previewwindow || exists('w:fugitive_status')) && get(b:,'fugitive_type', '') ==# 'index'
3930 let winnrs = filter([winnr('#')] + range(1, winnr('$')), 's:UsableWin(v:val)')
3932 exe winnrs[0].'wincmd w'
3942 function! s:OpenExec(cmd, mods, args, ...) abort
3943 let dir = a:0 ? s:Dir(a:1) : s:Dir()
3944 let temp = tempname()
3945 let columns = get(g:, 'fugitive_columns', 80)
3949 let env = 'set COLUMNS=' . columns . '& '
3951 let env = 'env COLUMNS=' . columns . ' '
3953 silent! execute '!' . escape(env . s:UserCommand(dir, ['--no-pager'] + a:args), '!#%') .
3954 \ (&shell =~# 'csh' ? ' >& ' . temp : ' > ' . temp . ' 2>&1')
3956 let temp = s:Resolve(temp)
3957 let first = join(readfile(temp, '', 2), "\n")
3958 if first =~# '\<\([[:upper:][:digit:]_-]\+(\d\+)\).*\1'
3959 let filetype = 'man'
3961 let filetype = 'git'
3963 let s:temp_files[s:cpath(temp)] = { 'dir': dir, 'filetype': filetype, 'modifiable': first =~# '^diff ' }
3967 silent execute s:Mods(a:mods) . a:cmd temp
3968 call fugitive#ReloadStatus(dir, 1)
3969 return 'echo ' . string(':!' . s:UserCommand(dir, a:args))
3972 function! fugitive#Open(cmd, bang, mods, arg, args) abort
3974 return s:OpenExec(a:cmd, a:mods, s:SplitExpand(a:arg, s:Tree()))
3977 let mods = s:Mods(a:mods)
3979 let [file, pre] = s:OpenParse(a:args)
3980 let file = s:Generate(file)
3982 return 'echoerr ' . string(v:exception)
3984 if file !~# '^\a\a\+:'
3985 let file = s:sub(file, '/$', '')
3990 return mods . a:cmd . pre . ' ' . s:fnameescape(file)
3993 function! fugitive#ReadCommand(line1, count, range, bang, mods, arg, args) abort
3994 let mods = s:Mods(a:mods)
3997 let delete = 'silent 1,' . line('$') . 'delete_|'
3998 let after = line('$')
4000 let delete = 'silent ' . a:line1 . ',' . a:count . 'delete_|'
4006 let args = s:SplitExpand(a:arg, s:Tree(dir))
4007 silent execute mods . after . 'read!' escape(s:UserCommand(dir, ['--no-pager'] + args), '!#%')
4008 execute delete . 'diffupdate'
4009 call fugitive#ReloadStatus()
4010 return 'redraw|echo '.string(':!'.s:UserCommand(dir, args))
4013 let [file, pre] = s:OpenParse(a:args)
4014 let file = s:Generate(file)
4016 return 'echoerr ' . string(v:exception)
4018 if file =~# '^fugitive:' && after is# 0
4019 return 'exe ' .string(mods . fugitive#FileReadCmd(file, 0, pre)) . '|diffupdate'
4022 exe after . 'foldopen!'
4024 return mods . after . 'read' . pre . ' ' . s:fnameescape(file) . '|' . delete . 'diffupdate' . (a:count < 0 ? '|' . line('.') : '')
4027 function! fugitive#ReadComplete(A, L, P) abort
4029 return fugitive#Complete(a:A, a:L, a:P)
4031 return fugitive#CompleteObject(a:A, a:L, a:P)
4035 " Section: :Gwrite, :Gwq
4037 function! fugitive#WriteCommand(line1, line2, range, bang, mods, arg, args) abort
4038 if exists('b:fugitive_commit_arguments')
4039 return 'write|bdelete'
4040 elseif expand('%:t') == 'COMMIT_EDITMSG' && $GIT_INDEX_FILE != ''
4042 elseif get(b:, 'fugitive_type', '') ==# 'index'
4044 elseif &buftype ==# 'nowrite' && getline(4) =~# '^+++ '
4045 let filename = getline(4)[6:-1]
4048 setlocal buftype=nowrite
4049 if matchstr(getline(2),'index [[:xdigit:]]\+\.\.\zs[[:xdigit:]]\{7\}') ==# fugitive#RevParse(':0:'.filename)[0:6]
4050 let [message, exec_error] = s:ChompError(['apply', '--cached', '--reverse', '--', expand('%:p')])
4052 let [message, exec_error] = s:ChompError(['apply', '--cached', '--', expand('%:p')])
4062 return 'Gedit '.fnameescape(filename)
4065 let mytab = tabpagenr()
4066 let mybufnr = bufnr('')
4068 let file = len(a:args) ? s:Generate(s:Expand(join(a:args, ' '))) : fugitive#Real(@%)
4070 return 'echoerr ' . string(v:exception)
4073 return 'echoerr '.string('fugitive: cannot determine file path')
4075 if file =~# '^fugitive:'
4076 return 'write' . (a:bang ? '! ' : ' ') . s:fnameescape(file)
4079 let always_permitted = s:cpath(fugitive#Real(@%), file) && empty(s:DirCommitFile(@%)[1])
4080 if !always_permitted && !a:bang && (len(s:TreeChomp('diff', '--name-status', 'HEAD', '--', file)) || len(s:TreeChomp('ls-files', '--others', '--', file)))
4081 let v:errmsg = 'fugitive: file has uncommitted changes (use ! to override)'
4082 return 'echoerr v:errmsg'
4085 for nr in range(1,bufnr('$'))
4086 if fnamemodify(bufname(nr),':p') ==# file
4091 if treebufnr > 0 && treebufnr != bufnr('')
4092 let temp = tempname()
4093 silent execute 'keepalt %write '.temp
4094 for tab in [mytab] + range(1,tabpagenr('$'))
4095 for winnr in range(1,tabpagewinnr(tab,'$'))
4096 if tabpagebuflist(tab)[winnr-1] == treebufnr
4097 execute 'tabnext '.tab
4099 execute winnr.'wincmd w'
4100 let restorewinnr = 1
4103 let lnum = line('.')
4104 let last = line('$')
4105 silent execute '$read '.temp
4106 silent execute '1,'.last.'delete_'
4112 if exists('restorewinnr')
4115 execute 'tabnext '.mytab
4122 call writefile(readfile(temp,'b'),file,'b')
4125 execute 'write! '.s:fnameescape(file)
4129 let [error, exec_error] = s:ChompError(['add', '--force', '--', file])
4131 let [error, exec_error] = s:ChompError(['add', '--', file])
4134 let v:errmsg = 'fugitive: '.error
4135 return 'echoerr v:errmsg'
4137 if s:cpath(fugitive#Real(@%), file) && s:DirCommitFile(@%)[1] =~# '^\d$'
4141 let one = s:Generate(':1:'.file)
4142 let two = s:Generate(':2:'.file)
4143 let three = s:Generate(':3:'.file)
4144 for nr in range(1,bufnr('$'))
4145 let name = fnamemodify(bufname(nr), ':p')
4146 if bufloaded(nr) && !getbufvar(nr,'&modified') && (name ==# one || name ==# two || name ==# three)
4147 execute nr.'bdelete'
4152 let zero = s:Generate(':0:'.file)
4153 silent execute 'doautocmd' s:nomodeline 'BufWritePost' s:fnameescape(zero)
4154 for tab in range(1,tabpagenr('$'))
4155 for winnr in range(1,tabpagewinnr(tab,'$'))
4156 let bufnr = tabpagebuflist(tab)[winnr-1]
4157 let bufname = fnamemodify(bufname(bufnr), ':p')
4158 if bufname ==# zero && bufnr != mybufnr
4159 execute 'tabnext '.tab
4161 execute winnr.'wincmd w'
4162 let restorewinnr = 1
4165 let lnum = line('.')
4166 let last = line('$')
4167 silent execute '$read '.s:fnameescape(file)
4168 silent execute '1,'.last.'delete_'
4173 if exists('restorewinnr')
4176 execute 'tabnext '.mytab
4182 call fugitive#ReloadStatus()
4186 function! fugitive#WqCommand(...) abort
4187 let bang = a:4 ? '!' : ''
4188 if exists('b:fugitive_commit_arguments')
4191 let result = call('fugitive#WriteCommand', a:000)
4192 if result =~# '^\%(write\|wq\|echoerr\)'
4193 return s:sub(result,'^write','wq')
4195 return result.'|quit'.bang
4199 augroup fugitive_commit
4201 autocmd VimLeavePre,BufDelete COMMIT_EDITMSG execute substitute(s:FinishCommit(), '\C^echoerr \(''[^'']*''\)*', 'redraw|echohl ErrorMsg|echo \1|echohl NONE', '')
4204 " Section: :Gpush, :Gfetch
4206 function! s:PushComplete(A, L, P) abort
4207 return s:CompleteSub('push', a:A, a:L, a:P, function('s:CompleteRemote'))
4210 function! s:FetchComplete(A, L, P) abort
4211 return s:CompleteSub('fetch', a:A, a:L, a:P, function('s:CompleteRemote'))
4214 function! s:AskPassArgs(dir) abort
4215 if (len($DISPLAY) || len($TERM_PROGRAM) || has('gui_running')) && fugitive#GitVersion(1, 8) &&
4216 \ empty($GIT_ASKPASS) && empty($SSH_ASKPASS) && empty(fugitive#Config('core.askPass', a:dir))
4217 if s:executable(s:ExecPath() . '/git-gui--askpass')
4218 return ['-c', 'core.askPass=' . s:ExecPath() . '/git-gui--askpass']
4219 elseif s:executable('ssh-askpass')
4220 return ['-c', 'core.askPass=ssh-askpass']
4226 function! s:Dispatch(bang, cmd, args) abort
4228 let [mp, efm, cc] = [&l:mp, &l:efm, get(b:, 'current_compiler', '')]
4230 let b:current_compiler = 'git'
4231 let &l:errorformat = s:common_efm
4232 let &l:makeprg = s:UserCommand(dir, s:AskPassArgs(dir) + [a:cmd] + a:args)
4233 if exists(':Make') == 2
4237 if !has('patch-8.1.0334') && has('terminal') && &autowrite
4238 let autowrite_was_set = 1
4242 silent noautocmd make!
4244 return 'call fugitive#Cwindow()|call fugitive#ReloadStatus()'
4247 let [&l:mp, &l:efm, b:current_compiler] = [mp, efm, cc]
4248 if empty(cc) | unlet! b:current_compiler | endif
4249 if exists('autowrite_was_set')
4255 function! s:PushSubcommand(line1, line2, range, bang, mods, args) abort
4256 return s:Dispatch(a:bang ? '!' : '', 'push', a:args)
4259 function! s:FetchSubcommand(line1, line2, range, bang, mods, args) abort
4260 return s:Dispatch(a:bang ? '!' : '', 'fetch', a:args)
4263 call s:command("-nargs=? -bang -complete=customlist,s:PushComplete Gpush", "push")
4264 call s:command("-nargs=? -bang -complete=customlist,s:FetchComplete Gfetch", "fetch")
4268 augroup fugitive_diff
4270 autocmd BufWinLeave *
4271 \ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 2 |
4272 \ call s:diffoff_all(s:Dir(+expand('<abuf>'))) |
4274 autocmd BufWinEnter *
4275 \ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 1 |
4276 \ call s:diffoff() |
4280 function! s:can_diffoff(buf) abort
4281 return getwinvar(bufwinnr(a:buf), '&diff') &&
4282 \ !empty(getwinvar(bufwinnr(a:buf), 'fugitive_diff_restore'))
4285 function! fugitive#CanDiffoff(buf) abort
4286 return s:can_diffoff(bufnr(a:buf))
4289 function! s:diff_modifier(count) abort
4290 let fdc = matchstr(&diffopt, 'foldcolumn:\zs\d\+')
4291 if &diffopt =~# 'horizontal' && &diffopt !~# 'vertical'
4293 elseif &diffopt =~# 'vertical'
4295 elseif winwidth(0) <= a:count * ((&tw ? &tw : 80) + (empty(fdc) ? 2 : fdc))
4302 function! s:diff_window_count() abort
4304 for nr in range(1,winnr('$'))
4305 let c += getwinvar(nr,'&diff')
4310 function! s:diff_restore() abort
4311 let restore = 'setlocal nodiff noscrollbind'
4312 \ . ' scrollopt=' . &l:scrollopt
4313 \ . (&l:wrap ? ' wrap' : ' nowrap')
4314 \ . ' foldlevel=999'
4315 \ . ' foldmethod=' . &l:foldmethod
4316 \ . ' foldcolumn=' . &l:foldcolumn
4317 \ . ' foldlevel=' . &l:foldlevel
4318 \ . (&l:foldenable ? ' foldenable' : ' nofoldenable')
4319 if has('cursorbind')
4320 let restore .= (&l:cursorbind ? ' ' : ' no') . 'cursorbind'
4325 function! s:diffthis() abort
4327 let w:fugitive_diff_restore = s:diff_restore()
4332 function! s:diffoff() abort
4333 if exists('w:fugitive_diff_restore')
4334 execute w:fugitive_diff_restore
4335 unlet w:fugitive_diff_restore
4341 function! s:diffoff_all(dir) abort
4342 let curwin = winnr()
4343 for nr in range(1,winnr('$'))
4344 if getwinvar(nr,'&diff')
4346 execute nr.'wincmd w'
4347 let restorewinnr = 1
4349 if s:Dir() ==# a:dir
4354 execute curwin.'wincmd w'
4357 function! s:CompareAge(mine, theirs) abort
4358 let scores = {':0': 1, ':1': 2, ':2': 3, ':': 4, ':3': 5}
4359 let mine = substitute(a:mine, '^:', '', '')
4360 let theirs = substitute(a:theirs, '^:', '', '')
4361 let my_score = get(scores, ':'.mine, 0)
4362 let their_score = get(scores, ':'.theirs, 0)
4363 if my_score || their_score
4364 return my_score < their_score ? -1 : my_score != their_score
4365 elseif mine ==# theirs
4368 let base = s:TreeChomp('merge-base', mine, theirs)
4371 elseif base ==# theirs
4374 let my_time = +s:TreeChomp('log', '--max-count=1', '--pretty=format:%at', a:mine, '--')
4375 let their_time = +s:TreeChomp('log', '--max-count=1', '--pretty=format:%at', a:theirs, '--')
4376 return my_time < their_time ? -1 : my_time != their_time
4379 function! s:IsConflicted() abort
4380 return len(@%) && !empty(s:ChompDefault('', 'ls-files', '--unmerged', '--', expand('%:p')))
4383 function! fugitive#Diffsplit(autodir, keepfocus, mods, arg, args) abort
4384 let args = copy(a:args)
4386 if get(args, 0) =~# '^+'
4387 let post = remove(args, 0)[1:-1]
4389 if exists(':DiffGitCached') && empty(args)
4390 return s:Mods(a:mods) . 'DiffGitCached' . (len(post) ? '|' . post : '')
4392 let commit = s:DirCommitFile(@%)[1]
4393 if a:mods =~# '\<tab\>'
4394 let mods = substitute(a:mods, '\<tab\>', '', 'g')
4395 let pre = 'tab split'
4397 let mods = 'keepalt ' . a:mods
4400 let back = exists('*win_getid') ? 'call win_gotoid(' . win_getid() . ')' : 'wincmd p'
4401 if (empty(args) || args[0] ==# ':') && a:keepfocus
4403 if empty(commit) && s:IsConflicted()
4404 let parents = [s:Relative(':2:'), s:Relative(':3:')]
4405 elseif empty(commit)
4406 let parents = [s:Relative(':0:')]
4407 elseif commit =~# '^\d\=$'
4408 let parents = [s:Relative('HEAD:')]
4409 elseif commit =~# '^\x\x\+$'
4410 let parents = s:LinesError(['rev-parse', commit . '^@'])[0]
4411 call map(parents, 's:Relative(v:val . ":")')
4415 if exists('parents') && len(parents) > 1
4417 let mods = (a:autodir ? s:diff_modifier(len(parents) + 1) : '') . s:Mods(mods, 'leftabove')
4419 execute mods 'split' s:fnameescape(s:Generate(parents[0]))
4420 call s:Map('n', 'dp', ':diffput '.nr.'<Bar>diffupdate<CR>', '<silent>')
4424 call s:Map('n', 'd2o', ':diffget '.nr2.'<Bar>diffupdate<CR>', '<silent>')
4425 let mods = substitute(mods, '\Cleftabove\|rightbelow\|aboveleft\|belowright', '\=submatch(0) =~# "f" ? "rightbelow" : "leftabove"', '')
4426 for i in range(len(parents)-1, 1, -1)
4427 execute mods 'split' s:fnameescape(s:Generate(parents[i]))
4428 call s:Map('n', 'dp', ':diffput '.nr.'<Bar>diffupdate<CR>', '<silent>')
4432 call s:Map('n', 'd' . (i + 2) . 'o', ':diffget '.nrx.'<Bar>diffupdate<CR>', '<silent>')
4440 let arg = join(args, ' ')
4445 let file = s:Relative()
4448 let file = s:Relative(':0:')
4449 elseif arg =~# '^:\d$'
4451 let file = s:Relative(arg . ':')
4454 let file = arg =~# '^:/.' ? fugitive#RevParse(arg) . s:Relative(':') : s:Expand(arg)
4456 return 'echoerr ' . string(v:exception)
4459 elseif exists('parents') && len(parents)
4460 let file = parents[-1]
4462 let file = s:Relative()
4463 elseif s:IsConflicted()
4464 let file = s:Relative(':1:')
4465 let post = 'echohl WarningMsg|echo "Use :Gdiffsplit! for 3 way diff"|echohl NONE|' . post
4468 let file = s:Relative(':0:')
4470 let spec = s:Generate(file)
4471 if spec =~# '^fugitive:' && empty(s:DirCommitFile(spec)[2])
4472 let spec = FugitiveVimPath(spec . s:Relative('/'))
4475 let restore = s:diff_restore()
4476 let w:fugitive_diff_restore = restore
4477 if s:CompareAge(commit, s:DirCommitFile(spec)[1]) < 0
4478 let mods = s:Mods(mods, 'rightbelow')
4480 let mods = s:Mods(mods, 'leftabove')
4482 let mods = (a:autodir ? s:diff_modifier(2) : '') . mods
4483 if &diffopt =~# 'vertical'
4484 let diffopt = &diffopt
4485 set diffopt-=vertical
4487 execute mods 'diffsplit' s:fnameescape(spec)
4488 let &l:readonly = &l:readonly
4490 let w:fugitive_diff_restore = restore
4492 if getwinvar('#', '&diff')
4499 return 'echoerr ' . string(v:exception)
4501 if exists('diffopt')
4502 let &diffopt = diffopt
4507 " Section: :Gmove, :Gremove
4509 function! s:Move(force, rename, destination) abort
4512 if s:DirCommitFile(@%)[1] !~# '^0\=$' || empty(@%)
4513 return 'echoerr ' . string('fugitive: mv not supported for this buffer')
4515 if a:destination =~# '^\.\.\=\%(/\|$\)'
4516 let destination = simplify(getcwd() . '/' . a:destination)
4517 elseif a:destination =~# '^\a\+:\|^/'
4518 let destination = a:destination
4519 elseif a:destination =~# '^:/:\='
4520 let destination = s:Tree(dir) . substitute(a:destination, '^:/:\=', '', '')
4521 elseif a:destination =~# '^:(\%(top\|top,literal\|literal,top\))'
4522 let destination = s:Tree(dir) . matchstr(a:destination, ')\zs.*')
4523 elseif a:destination =~# '^:(literal)'
4524 let destination = simplify(getcwd() . '/' . matchstr(a:destination, ')\zs.*'))
4526 let destination = expand('%:p:s?[\/]$??:h') . '/' . a:destination
4528 let destination = s:Tree(dir) . '/' . a:destination
4530 let destination = s:Slash(destination)
4534 let [message, exec_error] = s:ChompError(['mv'] + (a:force ? ['-f'] : []) + ['--', expand('%:p'), destination], dir)
4536 let v:errmsg = 'fugitive: '.message
4537 return 'echoerr v:errmsg'
4539 if isdirectory(destination)
4540 let destination = fnamemodify(s:sub(destination,'/$','').'/'.expand('%:t'),':.')
4542 call fugitive#ReloadStatus(dir)
4543 if empty(s:DirCommitFile(@%)[1])
4544 if isdirectory(destination)
4545 return 'keepalt edit '.s:fnameescape(destination)
4547 return 'keepalt saveas! '.s:fnameescape(destination)
4550 return 'file '.s:fnameescape(fugitive#Find(':0:'.destination, dir))
4554 function! fugitive#RenameComplete(A,L,P) abort
4555 if a:A =~# '^[.:]\=/'
4556 return fugitive#CompletePath(a:A)
4558 let pre = s:Slash(fnamemodify(expand('%:p:s?[\/]$??'), ':h')) . '/'
4559 return map(fugitive#CompletePath(pre.a:A), 'strpart(v:val, len(pre))')
4563 function! fugitive#MoveCommand(line1, line2, range, bang, mods, arg, args) abort
4564 return s:Move(a:bang, 0, a:arg)
4567 function! fugitive#RenameCommand(line1, line2, range, bang, mods, arg, args) abort
4568 return s:Move(a:bang, 1, a:arg)
4571 function! s:Remove(after, force) abort
4574 if len(@%) && s:DirCommitFile(@%)[1] ==# ''
4576 elseif s:DirCommitFile(@%)[1] ==# '0'
4577 let cmd = ['rm','--cached']
4579 return 'echoerr ' . string('fugitive: rm not supported for this buffer')
4582 let cmd += ['--force']
4584 let [message, exec_error] = s:ChompError(cmd + ['--', expand('%:p')], dir)
4586 let v:errmsg = 'fugitive: '.s:sub(message,'error:.*\zs\n\(.*-f.*',' (add ! to force)')
4587 return 'echoerr '.string(v:errmsg)
4589 call fugitive#ReloadStatus(dir)
4590 return a:after . (a:force ? '!' : '')
4594 function! fugitive#RemoveCommand(line1, line2, range, bang, mods, arg, args) abort
4595 return s:Remove('edit', a:bang)
4598 function! fugitive#DeleteCommand(line1, line2, range, bang, mods, arg, args) abort
4599 return s:Remove('bdelete', a:bang)
4604 function! s:Keywordprg() abort
4605 let args = ' --git-dir='.escape(s:Dir(),"\\\"' ")
4606 if has('gui_running') && !has('win32')
4607 return s:UserCommand() . ' --no-pager' . args . ' log -1'
4609 return s:UserCommand() . args . ' show'
4613 function! s:linechars(pattern) abort
4614 let chars = strlen(s:gsub(matchstr(getline('.'), a:pattern), '.', '.'))
4615 if exists('*synconcealed') && &conceallevel > 1
4616 for col in range(1, chars)
4617 let chars -= synconcealed(line('.'), col)[0]
4623 function! s:BlameBufnr(...) abort
4624 let state = s:TempState(bufname(a:0 ? a:1 : ''))
4625 if get(state, 'filetype', '') ==# 'fugitiveblame'
4626 return get(state, 'bufnr', -1)
4632 function! s:BlameCommitFileLnum(...) abort
4633 let line = a:0 ? a:1 : getline('.')
4634 let state = a:0 ? a:2 : s:TempState()
4635 let commit = matchstr(line, '^\^\=\zs\x\+')
4636 if commit =~# '^0\+$'
4638 elseif line !~# '^\^' && has_key(state, 'blame_reverse_end')
4639 let commit = get(s:LinesError('rev-list', '--ancestry-path', '--reverse', commit . '..' . state.blame_reverse_end)[0], 0, '')
4641 let lnum = +matchstr(line, ' \zs\d\+\ze \%((\| *\d\+)\)')
4642 let path = matchstr(line, '^\^\=[?*]*\x* \+\%(\d\+ \+\d\+ \+\)\=\zs.\{-\}\ze\s\+\%(\%( \d\+ \)\@<!([^()]*\w \d\+)\|\d\+ \)')
4643 if empty(path) && lnum
4644 let path = get(state, 'blame_file', '')
4646 return [commit, path, lnum]
4649 function! s:BlameLeave() abort
4650 let bufwinnr = bufwinnr(s:BlameBufnr())
4652 let bufnr = bufnr('')
4653 exe bufwinnr . 'wincmd w'
4654 return bufnr . 'bdelete'
4659 function! s:BlameQuit() abort
4660 let cmd = s:BlameLeave()
4663 elseif len(s:DirCommitFile(@%)[1])
4664 return cmd . '|Gedit'
4670 function! s:BlameComplete(A, L, P) abort
4671 return s:CompleteSub('blame', a:A, a:L, a:P)
4674 function! s:BlameSubcommand(line1, count, range, bang, mods, args) abort
4676 let flags = copy(a:args)
4682 if a:line1 > 0 && a:count > 0 && a:range != 1
4683 call extend(ranges, ['-L', a:line1 . ',' . a:count])
4685 while i < len(flags)
4686 let match = matchlist(flags[i], '^\(-[a-zABDFH-KN-RT-Z]\)\ze\(.*\)')
4687 if len(match) && len(match[2])
4688 call insert(flags, match[1])
4689 let flags[i+1] = '-' . match[2]
4693 if arg =~# '^-p$\|^--\%(help\|porcelain\|line-porcelain\|incremental\)$'
4695 elseif arg ==# '--contents' && i + 1 < len(flags)
4696 call extend(commits, remove(flags, i, i+1))
4698 elseif arg ==# '-L' && i + 1 < len(flags)
4699 call extend(ranges, remove(flags, i, i+1))
4701 elseif arg =~# '^--contents='
4702 call add(commits, remove(flags, i))
4704 elseif arg =~# '^-L.'
4705 call add(ranges, remove(flags, i))
4707 elseif arg =~# '^-[GLS]$\|^--\%(date\|encoding\|contents\|ignore-rev\|ignore-revs-file\)$'
4711 echo s:ChompError(['blame', arg])[0]
4716 if i + 1 < len(flags)
4717 call extend(files, remove(flags, i + 1, -1))
4719 call remove(flags, i)
4721 elseif arg !~# '^-' && (s:HasOpt(flags, '--not') || arg !~# '^\^')
4722 if index(flags, '--') >= 0
4723 call add(commits, remove(flags, i))
4726 if arg =~# '\.\.' && arg !~# '^\.\.\=\%(/\|$\)' && empty(commits)
4727 call add(commits, remove(flags, i))
4731 let dcf = s:DirCommitFile(fugitive#Find(arg))
4732 if len(dcf[1]) && empty(dcf[2])
4733 call add(commits, remove(flags, i))
4738 call add(files, remove(flags, i))
4743 let file = substitute(get(files, 0, get(s:TempState(), 'blame_file', s:Relative('./'))), '^\.\%(/\|$\)', '', '')
4744 if empty(commits) && len(files) > 1
4745 call add(commits, remove(files, 1))
4749 let cmd = ['--no-pager', '-c', 'blame.coloring=none', '-c', 'blame.blankBoundary=false', 'blame', '--show-number']
4750 call extend(cmd, filter(copy(flags), 'v:val !~# "\\v^%(-b|--%(no-)=color-.*|--progress)$"'))
4751 if a:count > 0 && empty(ranges)
4752 let cmd += ['-L', (a:line1 ? a:line1 : line('.')) . ',' . (a:line1 ? a:line1 : line('.'))]
4754 call extend(cmd, ranges)
4757 elseif empty(files) && len(matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$'))
4758 let cmd += [matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$')]
4759 elseif empty(files) && !s:HasOpt(flags, '--reverse')
4760 let cmd += ['--contents', '-']
4762 let basecmd = escape(fugitive#Prepare(cmd) . ' -- ' . s:shellesc(len(files) ? files : file), '!#%')
4763 let tempname = tempname()
4764 let error = tempname . '.err'
4765 let temp = tempname . (raw ? '' : '.fugitiveblame')
4767 silent! execute '%write !('.basecmd.' > '.temp.') >& '.error
4769 silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error
4774 let lines = readfile(error)
4776 let lines = readfile(temp)
4778 for i in range(len(lines))
4779 if lines[i] =~# '^error: \|^fatal: '
4787 if i != len(lines) - 1
4793 let temp_state = {'dir': s:Dir(), 'filetype': (raw ? '' : 'fugitiveblame'), 'blame_flags': flags, 'blame_file': file, 'modifiable': 0}
4794 if s:HasOpt(flags, '--reverse')
4795 let temp_state.blame_reverse_end = matchstr(get(commits, 0, ''), '\.\.\zs.*')
4797 if (a:line1 == 0 || a:range == 1) && a:count > 0
4798 let edit = s:Mods(a:mods) . get(['edit', 'split', 'pedit', 'vsplit', 'tabedit'], a:count - (a:line1 ? a:line1 : 1), 'split')
4799 return s:BlameCommit(edit, get(readfile(temp), 0, ''), temp_state)
4801 let temp = s:Resolve(temp)
4802 let s:temp_files[s:cpath(temp)] = temp_state
4803 if len(ranges + commits + files) || raw
4804 let mods = s:Mods(a:mods)
4806 exe 'silent keepalt' mods 'split' s:fnameescape(temp)
4807 elseif !&modified || a:bang || &bufhidden ==# 'hide' || (empty(&bufhidden) && &hidden)
4808 exe 'silent' mods 'edit' . (a:bang ? '! ' : ' ') . s:fnameescape(temp)
4810 return mods . 'edit ' . s:fnameescape(temp)
4814 if a:mods =~# '\<tab\>'
4817 let mods = substitute(a:mods, '\<tab\>', '', 'g')
4818 for winnr in range(winnr('$'),1,-1)
4819 if getwinvar(winnr, '&scrollbind')
4820 call setwinvar(winnr, '&scrollbind', 0)
4822 if exists('+cursorbind') && getwinvar(winnr, '&cursorbind')
4823 call setwinvar(winnr, '&cursorbind', 0)
4825 if s:BlameBufnr(winbufnr(winnr)) > 0
4826 execute winbufnr(winnr).'bdelete'
4829 let bufnr = bufnr('')
4830 let temp_state.bufnr = bufnr
4831 let restore = 'call setwinvar(bufwinnr('.bufnr.'),"&scrollbind",0)'
4832 if exists('+cursorbind')
4833 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&cursorbind",0)'
4836 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&wrap",1)'
4839 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&foldenable",1)'
4841 setlocal scrollbind nowrap nofoldenable
4842 if exists('+cursorbind')
4845 let top = line('w0') + &scrolloff
4846 let current = line('.')
4847 exe 'silent keepalt' (a:bang ? s:Mods(mods) . 'split' : s:Mods(mods, 'leftabove') . 'vsplit') s:fnameescape(temp)
4848 let w:fugitive_leave = restore
4852 if exists('+cursorbind')
4855 setlocal nonumber scrollbind nowrap foldcolumn=0 nofoldenable winfixwidth
4856 if exists('+relativenumber')
4857 setlocal norelativenumber
4859 execute "vertical resize ".(s:linechars('.\{-\}\ze\s\+\d\+)')+1)
4860 call s:Map('n', 'A', ":<C-u>exe 'vertical resize '.(<SID>linechars('.\\{-\\}\\ze [0-9:/+-][0-9:/+ -]* \\d\\+)')+1+v:count)<CR>", '<silent>')
4861 call s:Map('n', 'C', ":<C-u>exe 'vertical resize '.(<SID>linechars('^\\S\\+')+1+v:count)<CR>", '<silent>')
4862 call s:Map('n', 'D', ":<C-u>exe 'vertical resize '.(<SID>linechars('.\\{-\\}\\ze\\d\\ze\\s\\+\\d\\+)')+1-v:count)<CR>", '<silent>')
4869 return 'echoerr ' . string(v:exception)
4873 function! s:BlameCommit(cmd, ...) abort
4874 let line = a:0 ? a:1 : getline('.')
4875 let state = a:0 ? a:2 : s:TempState()
4876 let sigil = has_key(state, 'blame_reverse_end') ? '-' : '+'
4877 let mods = (s:BlameBufnr() < 0 ? '' : &splitbelow ? "botright " : "topleft ")
4878 let [commit, path, lnum] = s:BlameCommitFileLnum(line, state)
4879 if empty(commit) && len(path) && has_key(state, 'blame_reverse_end')
4880 let path = (len(state.blame_reverse_end) ? state.blame_reverse_end . ':' : ':(top)') . path
4881 return fugitive#Open(mods . a:cmd, 0, '', '+' . lnum . ' ' . s:fnameescape(path), ['+' . lnum, path])
4883 if commit =~# '^0*$'
4884 return 'echoerr ' . string('fugitive: no commit')
4886 if line =~# '^\^' && !has_key(state, 'blame_reverse_end')
4887 let path = commit . ':' . path
4888 return fugitive#Open(mods . a:cmd, 0, '', '+' . lnum . ' ' . s:fnameescape(path), ['+' . lnum, path])
4890 let cmd = fugitive#Open(mods . a:cmd, 0, '', commit, [commit])
4891 if cmd =~# '^echoerr'
4895 if a:cmd ==# 'pedit' || empty(path)
4898 if search('^diff .* b/\M'.escape(path,'\').'$','W')
4900 let head = line('.')
4901 while search('^@@ \|^diff ') && getline('.') =~# '^@@ '
4902 let top = +matchstr(getline('.'),' ' . sigil .'\zs\d\+')
4903 let len = +matchstr(getline('.'),' ' . sigil . '\d\+,\zs\d\+')
4904 if lnum >= top && lnum <= top + len
4905 let offset = lnum - top
4913 while offset > 0 && line('.') < line('$')
4915 if getline('.') =~# '^[ ' . sigil . ']'
4928 function! s:BlameJump(suffix, ...) abort
4929 let suffix = a:suffix
4930 let [commit, path, lnum] = s:BlameCommitFileLnum()
4932 return 'echoerr ' . string('fugitive: could not determine filename for blame')
4934 if commit =~# '^0*$'
4938 let offset = line('.') - line('w0')
4939 let flags = get(s:TempState(), 'blame_flags', [])
4941 if s:HasOpt(flags, '--reverse')
4942 call remove(flags, '--reverse')
4944 call add(flags, '--reverse')
4947 let blame_bufnr = s:BlameBufnr()
4949 let bufnr = bufnr('')
4950 let winnr = bufwinnr(blame_bufnr)
4952 exe winnr.'wincmd w'
4954 execute 'Gedit' s:fnameescape(commit . suffix . ':' . path)
4960 if exists(':Gblame')
4961 let my_bufnr = bufnr('')
4963 let blame_args = flags + [commit . suffix, '--', path]
4964 let result = s:BlameSubcommand(0, 0, 0, 0, '', blame_args)
4966 let blame_args = flags
4967 let result = s:BlameSubcommand(-1, -1, 0, 0, '', blame_args)
4969 if bufnr('') == my_bufnr
4974 let delta = line('.') - line('w0') - offset
4976 execute 'normal! '.delta."\<C-E>"
4978 execute 'normal! '.(-delta)."\<C-Y>"
4982 echo ':Gblame' s:fnameescape(blame_args)
4987 let s:hash_colors = {}
4989 function! fugitive#BlameSyntax() abort
4990 let conceal = has('conceal') ? ' conceal' : ''
4991 let config = fugitive#Config()
4992 let flags = get(s:TempState(), 'blame_flags', [])
4993 syn match FugitiveblameBlank "^\s\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalFile,FugitiveblameOriginalLineNumber skipwhite
4994 syn match FugitiveblameHash "\%(^\^\=[?*]*\)\@<=\<\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
4995 syn match FugitiveblameUncommitted "\%(^\^\=\)\@<=\<0\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
4996 if get(get(config, 'blame.blankboundary', ['x']), 0, 'x') =~# '^$\|^true$' || s:HasOpt(flags, '-b')
4997 syn match FugitiveblameBoundaryIgnore "^\^[*?]*\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
4999 syn match FugitiveblameBoundary "^\^"
5001 syn match FugitiveblameScoreDebug " *\d\+\s\+\d\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile contained skipwhite
5002 syn region FugitiveblameAnnotation matchgroup=FugitiveblameDelimiter start="(" end="\%(\s\d\+\)\@<=)" contained keepend oneline
5003 syn match FugitiveblameTime "[0-9:/+-][0-9:/+ -]*[0-9:/+-]\%(\s\+\d\+)\)\@=" contained containedin=FugitiveblameAnnotation
5004 exec 'syn match FugitiveblameLineNumber "\s*\d\+)\@=" contained containedin=FugitiveblameAnnotation' conceal
5005 exec 'syn match FugitiveblameOriginalFile "\s\%(\f\+\D\@<=\|\D\@=\f\+\)\%(\%(\s\+\d\+\)\=\s\%((\|\s*\d\+)\)\)\@=" contained nextgroup=FugitiveblameOriginalLineNumber,FugitiveblameAnnotation skipwhite' (s:HasOpt(flags, '--show-name', '-f') ? '' : conceal)
5006 exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s(\)\@=" contained nextgroup=FugitiveblameAnnotation skipwhite' (s:HasOpt(flags, '--show-number', '-n') ? '' : conceal)
5007 exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s\+\d\+)\)\@=" contained nextgroup=FugitiveblameShort skipwhite' (s:HasOpt(flags, '--show-number', '-n') ? '' : conceal)
5008 syn match FugitiveblameShort " \d\+)" contained contains=FugitiveblameLineNumber
5009 syn match FugitiveblameNotCommittedYet "(\@<=Not Committed Yet\>" contained containedin=FugitiveblameAnnotation
5010 hi def link FugitiveblameBoundary Keyword
5011 hi def link FugitiveblameHash Identifier
5012 hi def link FugitiveblameBoundaryIgnore Ignore
5013 hi def link FugitiveblameUncommitted Ignore
5014 hi def link FugitiveblameScoreDebug Debug
5015 hi def link FugitiveblameTime PreProc
5016 hi def link FugitiveblameLineNumber Number
5017 hi def link FugitiveblameOriginalFile String
5018 hi def link FugitiveblameOriginalLineNumber Float
5019 hi def link FugitiveblameShort FugitiveblameDelimiter
5020 hi def link FugitiveblameDelimiter Delimiter
5021 hi def link FugitiveblameNotCommittedYet Comment
5022 if !get(g:, 'fugitive_dynamic_colors', 1) && !s:HasOpt(flags, '--color-lines') || s:HasOpt(flags, '--no-color-lines')
5026 for lnum in range(1, line('$'))
5027 let hash = matchstr(getline(lnum), '^\^\=\zs\x\{6\}')
5028 if hash ==# '' || hash ==# '000000' || has_key(seen, hash)
5032 if &t_Co > 16 && get(g:, 'CSApprox_loaded') && !empty(findfile('autoload/csapprox/per_component.vim', escape(&rtp, ' ')))
5033 \ && empty(get(s:hash_colors, hash))
5034 let [s, r, g, b; __] = map(matchlist(hash, '\(\x\x\)\(\x\x\)\(\x\x\)'), 'str2nr(v:val,16)')
5035 let color = csapprox#per_component#Approximate(r, g, b)
5036 if color == 16 && &background ==# 'dark'
5039 let s:hash_colors[hash] = ' ctermfg='.color
5041 let s:hash_colors[hash] = ''
5043 exe 'syn match FugitiveblameHash'.hash.' "\%(^\^\=\)\@<='.hash.'\x\{1,34\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite'
5045 call s:BlameRehighlight()
5048 function! s:BlameRehighlight() abort
5049 for [hash, cterm] in items(s:hash_colors)
5050 if !empty(cterm) || has('gui_running') || has('termguicolors') && &termguicolors
5051 exe 'hi FugitiveblameHash'.hash.' guifg=#'.hash.get(s:hash_colors, hash, '')
5053 exe 'hi link FugitiveblameHash'.hash.' Identifier'
5058 function! s:BlameFileType() abort
5060 setlocal foldmethod=manual
5062 let &l:keywordprg = s:Keywordprg()
5064 let b:undo_ftplugin = 'setl keywordprg= foldmethod<'
5065 if exists('+concealcursor')
5066 setlocal concealcursor=nc conceallevel=2
5067 let b:undo_ftplugin .= ' concealcursor< conceallevel<'
5072 call s:Map('n', '<F1>', ':help fugitive-:Gblame<CR>', '<silent>')
5073 call s:Map('n', 'g?', ':help fugitive-:Gblame<CR>', '<silent>')
5074 if mapcheck('q', 'n') =~# '^$\|bdelete'
5075 call s:Map('n', 'q', ':exe <SID>BlameQuit()<Bar>echohl WarningMsg<Bar>echo ":Gblame q is deprecated in favor of gq"<Bar>echohl NONE<CR>', '<silent>')
5077 call s:Map('n', 'gq', ':exe <SID>BlameQuit()<CR>', '<silent>')
5078 call s:Map('n', '<2-LeftMouse>', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5079 call s:Map('n', '<CR>', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5080 call s:Map('n', '-', ':<C-U>exe <SID>BlameJump("")<CR>', '<silent>')
5081 call s:Map('n', 'P', ':<C-U>exe <SID>BlameJump("^".v:count1)<CR>', '<silent>')
5082 call s:Map('n', '~', ':<C-U>exe <SID>BlameJump("~".v:count1)<CR>', '<silent>')
5083 call s:Map('n', 'i', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5084 call s:Map('n', 'o', ':<C-U>exe <SID>BlameCommit("split")<CR>', '<silent>')
5085 call s:Map('n', 'O', ':<C-U>exe <SID>BlameCommit("tabedit")<CR>', '<silent>')
5086 call s:Map('n', 'p', ':<C-U>exe <SID>BlameCommit("pedit")<CR>', '<silent>')
5089 augroup fugitive_blame
5091 autocmd FileType fugitiveblame call s:BlameFileType()
5092 autocmd ColorScheme,GUIEnter * call s:BlameRehighlight()
5093 autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('<abuf>')), 'fugitive_leave')
5096 call s:command('-buffer -bang -range=-1 -nargs=? -complete=customlist,s:BlameComplete Gblame', 'blame')
5100 let s:redirects = {}
5102 function! fugitive#BrowseCommand(line1, count, range, bang, mods, arg, args) abort
5106 let validremote = '\.\|\.\=/.*\|[[:alnum:]_-]\+\%(://.\{-\}\)\='
5109 return 'echoerr ' . string('fugitive: ''-'' no longer required to get persistent URL if range given')
5111 return 'echoerr ' . string('fugitive: use :0Gbrowse instead of :Gbrowse -')
5114 let remote = matchstr(join(a:args, ' '),'@\zs\%('.validremote.'\)$')
5115 let rev = substitute(join(a:args, ' '),'@\%('.validremote.'\)$','','')
5121 let rev = s:DirRev(@%)[1]
5124 let expanded = s:Relative()
5126 let expanded = s:Expand(rev)
5128 let cdir = FugitiveVimPath(fugitive#CommonDir(dir))
5129 for subdir in ['tags/', 'heads/', 'remotes/']
5130 if expanded !~# '^[./]' && filereadable(cdir . '/refs/' . subdir . expanded)
5131 let expanded = '.git/refs/' . subdir . expanded
5134 let full = fugitive#Find(expanded, dir)
5136 if full =~? '^fugitive:'
5137 let [pathdir, commit, path] = s:DirCommitFile(full)
5138 if commit =~# '^:\=\d$'
5142 let type = s:TreeChomp('cat-file','-t',commit.s:sub(path,'^/',':'))
5143 let branch = matchstr(expanded, '^[^:]*')
5147 let path = path[1:-1]
5148 elseif empty(s:Tree(dir))
5149 let path = '.git/' . full[strlen(dir)+1:-1]
5152 let path = fugitive#Path(full, '/')[1:-1]
5153 if path =~# '^\.git/'
5155 elseif isdirectory(full) || empty(path)
5161 if type ==# 'tree' && !empty(path)
5162 let path = s:sub(path, '/\=$', '/')
5164 if path =~# '^\.git/.*HEAD$' && filereadable(dir . '/' . path[5:-1])
5165 let body = readfile(dir . '/' . path[5:-1])[0]
5166 if body =~# '^\x\{40,\}$'
5170 elseif body =~# '^ref: refs/'
5171 let path = '.git/' . matchstr(body,'ref: \zs.*')
5176 if path =~# '^\.git/refs/remotes/.'
5178 let remote = matchstr(path, '^\.git/refs/remotes/\zs[^/]\+')
5179 let branch = matchstr(path, '^\.git/refs/remotes/[^/]\+/\zs.\+')
5181 let merge = matchstr(path, '^\.git/refs/remotes/[^/]\+/\zs.\+')
5182 let path = '.git/refs/heads/'.merge
5184 elseif path =~# '^\.git/refs/heads/.'
5185 let branch = path[16:-1]
5186 elseif !exists('branch')
5187 let branch = FugitiveHead()
5190 let r = fugitive#Config('branch.'.branch.'.remote')
5191 let m = fugitive#Config('branch.'.branch.'.merge')[11:-1]
5192 if r ==# '.' && !empty(m)
5193 let r2 = fugitive#Config('branch.'.m.'.remote')
5196 let m = fugitive#Config('branch.'.m.'.merge')[11:-1]
5202 if r ==# '.' || r ==# remote
5204 if path =~# '^\.git/refs/heads/.'
5205 let path = '.git/refs/heads/'.merge
5210 let line1 = a:count > 0 ? a:line1 : 0
5211 let line2 = a:count > 0 ? a:count : 0
5212 if empty(commit) && path !~# '^\.git/'
5213 if a:count < 0 && !empty(merge)
5218 let owner = s:Owner(@%)
5219 let [commit, exec_error] = s:ChompError(['merge-base', 'refs/remotes/' . remote . '/' . merge, empty(owner) ? 'HEAD' : owner, '--'])
5223 if a:count > 0 && empty(a:args) && commit =~# '^\x\{40,\}$'
5224 let blame_list = tempname()
5225 call writefile([commit, ''], blame_list, 'b')
5226 let blame_in = tempname()
5227 silent exe '%write' blame_in
5228 let [blame, exec_error] = s:LinesError(['-c', 'blame.coloring=none', 'blame', '--contents', blame_in, '-L', a:line1.','.a:count, '-S', blame_list, '-s', '--show-number', './' . path])
5230 let blame_regex = '^\^\x\+\s\+\zs\d\+\ze\s'
5231 if get(blame, 0) =~# blame_regex && get(blame, -1) =~# blame_regex
5232 let line1 = +matchstr(blame[0], blame_regex)
5233 let line2 = +matchstr(blame[-1], blame_regex)
5235 call s:throw("Can't browse to uncommitted change")
5242 let commit = readfile(fugitive#Find('.git/HEAD', dir), '', 1)[0]
5245 while commit =~# '^ref: ' && i < 10
5246 let commit = readfile(cdir . '/' . commit[5:-1], '', 1)[0]
5254 let raw = fugitive#RemoteUrl(remote)
5259 if raw =~# '^https\=://' && s:executable('curl')
5260 if !has_key(s:redirects, raw)
5261 let s:redirects[raw] = matchstr(system('curl -I ' .
5262 \ s:shellesc(raw . '/info/refs?service=git-upload-pack')),
5263 \ 'Location: \zs\S\+\ze/info/refs?')
5265 if len(s:redirects[raw])
5266 let raw = s:redirects[raw]
5272 \ 'repo': fugitive#repo(dir),
5274 \ 'revision': 'No longer provided',
5282 for Handler in get(g:, 'fugitive_browse_handlers', [])
5283 let url = call(Handler, [copy(opts)])
5290 call s:throw("No Gbrowse handler installed for '".raw."'")
5293 let url = s:gsub(url, '[ <>]', '\="%".printf("%02X",char2nr(submatch(0)))')
5298 return 'echomsg '.string(url)
5299 elseif exists(':Browse') == 2
5300 return 'echomsg '.string(url).'|Browse '.url
5302 if !exists('g:loaded_netrw')
5303 runtime! autoload/netrw.vim
5305 if exists('*netrw#BrowseX')
5306 return 'echomsg '.string(url).'|call netrw#BrowseX('.string(url).', 0)'
5308 return 'echomsg '.string(url).'|call netrw#NetrwBrowseX('.string(url).', 0)'
5312 return 'echoerr ' . string(v:exception)
5316 " Section: Go to file
5318 nnoremap <SID>: :<C-U><C-R>=v:count ? v:count : ''<CR>
5319 function! fugitive#MapCfile(...) abort
5320 exe 'cnoremap <buffer> <expr> <Plug><cfile>' (a:0 ? a:1 : 'fugitive#Cfile()')
5321 let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') . '|sil! exe "cunmap <buffer> <Plug><cfile>"'
5322 if !exists('g:fugitive_no_maps')
5323 call s:Map('n', 'gf', '<SID>:find <Plug><cfile><CR>', '<silent><unique>', 1)
5324 call s:Map('n', '<C-W>f', '<SID>:sfind <Plug><cfile><CR>', '<silent><unique>', 1)
5325 call s:Map('n', '<C-W><C-F>', '<SID>:sfind <Plug><cfile><CR>', '<silent><unique>', 1)
5326 call s:Map('n', '<C-W>gf', '<SID>:tabfind <Plug><cfile><CR>', '<silent><unique>', 1)
5327 call s:Map('c', '<C-R><C-F>', '<Plug><cfile>', '<silent><unique>', 1)
5331 function! s:ContainingCommit() abort
5332 let commit = s:Owner(@%)
5333 return empty(commit) ? 'HEAD' : commit
5336 function! s:SquashArgument(...) abort
5337 if &filetype == 'fugitive'
5338 let commit = matchstr(getline('.'), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\{4,\}\ze ')
5339 elseif has_key(s:temp_files, s:cpath(expand('%:p')))
5340 let commit = matchstr(getline('.'), '\<\x\{4,\}\>')
5342 let commit = s:Owner(@%)
5344 return len(commit) && a:0 ? printf(a:1, commit) : commit
5347 function! s:RebaseArgument() abort
5348 return s:SquashArgument(' %s^')
5351 function! s:NavigateUp(count) abort
5352 let rev = substitute(s:DirRev(@%)[1], '^$', ':', 'g')
5356 let rev = matchstr(rev, '.*\ze/.\+', '')
5357 elseif rev =~# '.:.'
5358 let rev = matchstr(rev, '^.[^:]*:')
5371 function! s:MapMotion(lhs, rhs) abort
5372 call s:Map('n', a:lhs, ":<C-U>" . a:rhs . "<CR>", "<silent>")
5373 call s:Map('o', a:lhs, ":<C-U>" . a:rhs . "<CR>", "<silent>")
5374 call s:Map('x', a:lhs, ":<C-U>exe 'normal! gv'<Bar>" . a:rhs . "<CR>", "<silent>")
5377 function! fugitive#MapJumps(...) abort
5379 if get(b:, 'fugitive_type', '') ==# 'blob'
5380 let blame_map = 'Gblame<C-R>=v:count ? " --reverse" : ""<CR><CR>'
5381 call s:Map('n', '<2-LeftMouse>', ':<C-U>0,1' . blame_map, '<silent>')
5382 call s:Map('n', '<CR>', ':<C-U>0,1' . blame_map, '<silent>')
5383 call s:Map('n', 'o', ':<C-U>0,2' . blame_map, '<silent>')
5384 call s:Map('n', 'p', ':<C-U>0,3' . blame_map, '<silent>')
5385 call s:Map('n', 'gO', ':<C-U>0,4' . blame_map, '<silent>')
5386 call s:Map('n', 'O', ':<C-U>0,5' . blame_map, '<silent>')
5388 call s:Map('n', 'D', ":<C-U>call <SID>DiffClose()<Bar>Gdiffsplit!<Bar>redraw<Bar>echohl WarningMsg<Bar> echo ':Gstatus D is deprecated in favor of dd'<Bar>echohl NONE<CR>", '<silent>')
5389 call s:Map('n', 'dd', ":<C-U>call <SID>DiffClose()<Bar>Gdiffsplit!<CR>", '<silent>')
5390 call s:Map('n', 'dh', ":<C-U>call <SID>DiffClose()<Bar>Ghdiffsplit!<CR>", '<silent>')
5391 call s:Map('n', 'ds', ":<C-U>call <SID>DiffClose()<Bar>Ghdiffsplit!<CR>", '<silent>')
5392 call s:Map('n', 'dv', ":<C-U>call <SID>DiffClose()<Bar>Gvdiffsplit!<CR>", '<silent>')
5393 call s:Map('n', 'd?', ":<C-U>help fugitive_d<CR>", '<silent>')
5396 call s:Map('n', '<2-LeftMouse>', ':<C-U>exe <SID>GF("edit")<CR>', '<silent>')
5397 call s:Map('n', '<CR>', ':<C-U>exe <SID>GF("edit")<CR>', '<silent>')
5398 call s:Map('n', 'o', ':<C-U>exe <SID>GF("split")<CR>', '<silent>')
5399 call s:Map('n', 'gO', ':<C-U>exe <SID>GF("vsplit")<CR>', '<silent>')
5400 call s:Map('n', 'O', ':<C-U>exe <SID>GF("tabedit")<CR>', '<silent>')
5401 call s:Map('n', 'p', ':<C-U>exe <SID>GF("pedit")<CR>', '<silent>')
5403 if !exists('g:fugitive_no_maps')
5404 if exists(':CtrlP') && get(g:, 'ctrl_p_map') =~? '^<c-p>$'
5405 nnoremap <buffer> <silent> <C-P> :<C-U>execute line('.') == 1 ? 'CtrlP ' . fnameescape(<SID>Tree()) : <SID>PreviousItem(v:count1)<CR>
5407 nnoremap <buffer> <silent> <C-P> :<C-U>execute <SID>PreviousItem(v:count1)<CR>
5409 nnoremap <buffer> <silent> <C-N> :<C-U>execute <SID>NextItem(v:count1)<CR>
5411 call s:MapMotion('(', 'exe <SID>PreviousItem(v:count1)')
5412 call s:MapMotion(')', 'exe <SID>NextItem(v:count1)')
5413 call s:MapMotion('K', 'exe <SID>PreviousHunk(v:count1)')
5414 call s:MapMotion('J', 'exe <SID>NextHunk(v:count1)')
5415 call s:MapMotion('[c', 'exe <SID>PreviousHunk(v:count1)')
5416 call s:MapMotion(']c', 'exe <SID>NextHunk(v:count1)')
5417 call s:MapMotion('[/', 'exe <SID>PreviousFile(v:count1)')
5418 call s:MapMotion(']/', 'exe <SID>NextFile(v:count1)')
5419 call s:MapMotion('[m', 'exe <SID>PreviousFile(v:count1)')
5420 call s:MapMotion(']m', 'exe <SID>NextFile(v:count1)')
5421 call s:MapMotion('[[', 'exe <SID>PreviousSection(v:count1)')
5422 call s:MapMotion(']]', 'exe <SID>NextSection(v:count1)')
5423 call s:MapMotion('[]', 'exe <SID>PreviousSectionEnd(v:count1)')
5424 call s:MapMotion('][', 'exe <SID>NextSectionEnd(v:count1)')
5425 call s:Map('nxo', '*', '<SID>PatchSearchExpr(0)', '<expr>')
5426 call s:Map('nxo', '#', '<SID>PatchSearchExpr(1)', '<expr>')
5428 call s:Map('n', 'S', ':<C-U>echoerr "Use gO"<CR>', '<silent>')
5429 call s:Map('n', 'dq', ":<C-U>call <SID>DiffClose()<CR>", '<silent>')
5430 call s:Map('n', '-', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>NavigateUp(v:count1))<Bar> if getline(1) =~# '^tree \x\{40,\}$' && empty(getline(2))<Bar>call search('^'.escape(expand('#:t'),'.*[]~\').'/\=$','wc')<Bar>endif<CR>", '<silent>')
5431 call s:Map('n', 'P', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit().'^'.v:count1.<SID>Relative(':'))<CR>", '<silent>')
5432 call s:Map('n', '~', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit().'~'.v:count1.<SID>Relative(':'))<CR>", '<silent>')
5433 call s:Map('n', 'C', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5434 call s:Map('n', 'cp', ":<C-U>echoerr 'Use gC'<CR>", '<silent>')
5435 call s:Map('n', 'gC', ":<C-U>exe 'Gpedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5436 call s:Map('n', 'gc', ":<C-U>exe 'Gpedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5437 call s:Map('n', 'gi', ":<C-U>exe 'Gsplit' (v:count ? '.gitignore' : '.git/info/exclude')<CR>", '<silent>')
5438 call s:Map('x', 'gi', ":<C-U>exe 'Gsplit' (v:count ? '.gitignore' : '.git/info/exclude')<CR>", '<silent>')
5440 nnoremap <buffer> c<Space> :Git commit<Space>
5441 nnoremap <buffer> c<CR> :Git commit<CR>
5442 nnoremap <buffer> cv<Space> :Git commit -v<Space>
5443 nnoremap <buffer> cv<CR> :Git commit -v<CR>
5444 nnoremap <buffer> <silent> ca :<C-U>Gcommit --amend<CR>
5445 nnoremap <buffer> <silent> cc :<C-U>Gcommit<CR>
5446 nnoremap <buffer> <silent> ce :<C-U>Gcommit --amend --no-edit<CR>
5447 nnoremap <buffer> <silent> cw :<C-U>Gcommit --amend --only<CR>
5448 nnoremap <buffer> <silent> cva :<C-U>Gcommit -v --amend<CR>
5449 nnoremap <buffer> <silent> cvc :<C-U>Gcommit -v<CR>
5450 nnoremap <buffer> <silent> cRa :<C-U>Gcommit --reset-author --amend<CR>
5451 nnoremap <buffer> <silent> cRe :<C-U>Gcommit --reset-author --amend --no-edit<CR>
5452 nnoremap <buffer> <silent> cRw :<C-U>Gcommit --reset-author --amend --only<CR>
5453 nnoremap <buffer> cf :<C-U>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR>
5454 nnoremap <buffer> cF :<C-U><Bar>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR>
5455 nnoremap <buffer> cs :<C-U>Gcommit --squash=<C-R>=<SID>SquashArgument()<CR>
5456 nnoremap <buffer> cS :<C-U><Bar>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Gcommit --squash=<C-R>=<SID>SquashArgument()<CR>
5457 nnoremap <buffer> cA :<C-U>Gcommit --edit --squash=<C-R>=<SID>SquashArgument()<CR>
5458 nnoremap <buffer> <silent> c? :<C-U>help fugitive_c<CR>
5460 nnoremap <buffer> cr<Space> :Git revert<Space>
5461 nnoremap <buffer> cr<CR> :Git revert<CR>
5462 nnoremap <buffer> <silent> crc :<C-U>Grevert <C-R>=<SID>SquashArgument()<CR><CR>
5463 nnoremap <buffer> <silent> crn :<C-U>Grevert --no-commit <C-R>=<SID>SquashArgument()<CR><CR>
5464 nnoremap <buffer> <silent> cr? :help fugitive_cr<CR>
5466 nnoremap <buffer> cm<Space> :Git merge<Space>
5467 nnoremap <buffer> cm<CR> :Git merge<CR>
5468 nnoremap <buffer> <silent> cm? :help fugitive_cm<CR>
5470 nnoremap <buffer> cz<Space> :Git stash<Space>
5471 nnoremap <buffer> cz<CR> :Git stash<CR>
5472 nnoremap <buffer> <silent> cza :<C-U>exe <SID>EchoExec(['stash', 'apply', '--quiet', '--index', 'stash@{' . v:count . '}'])<CR>
5473 nnoremap <buffer> <silent> czA :<C-U>exe <SID>EchoExec(['stash', 'apply', '--quiet', 'stash@{' . v:count . '}'])<CR>
5474 nnoremap <buffer> <silent> czp :<C-U>exe <SID>EchoExec(['stash', 'pop', '--quiet', '--index', 'stash@{' . v:count . '}'])<CR>
5475 nnoremap <buffer> <silent> czP :<C-U>exe <SID>EchoExec(['stash', 'pop', '--quiet', 'stash@{' . v:count . '}'])<CR>
5476 nnoremap <buffer> <silent> czv :<C-U>exe 'Gedit' fugitive#RevParse('stash@{' . v:count . '}')<CR>
5477 nnoremap <buffer> <silent> czw :<C-U>exe <SID>EchoExec(['stash', '--keep-index'] + (v:count > 1 ? ['--all'] : v:count ? ['--include-untracked'] : []))<CR>
5478 nnoremap <buffer> <silent> czz :<C-U>exe <SID>EchoExec(['stash'] + (v:count > 1 ? ['--all'] : v:count ? ['--include-untracked'] : []))<CR>
5479 nnoremap <buffer> <silent> cz? :<C-U>help fugitive_cz<CR>
5481 nnoremap <buffer> co<Space> :Git checkout<Space>
5482 nnoremap <buffer> co<CR> :Git checkout<CR>
5483 nnoremap <buffer> coo :exe <SID>EchoExec(['checkout'] + split(<SID>SquashArgument()) + ['--'])<CR>
5484 nnoremap <buffer> co? :<C-U>help fugitive_co<CR>
5486 nnoremap <buffer> cb<Space> :Git branch<Space>
5487 nnoremap <buffer> cb<CR> :Git branch<CR>
5488 nnoremap <buffer> cb? :<C-U>help fugitive_cb<CR>
5490 nnoremap <buffer> r<Space> :Git rebase<Space>
5491 nnoremap <buffer> r<CR> :Git rebase<CR>
5492 nnoremap <buffer> <silent> ri :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><CR>
5493 nnoremap <buffer> <silent> rf :<C-U>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><CR>
5494 nnoremap <buffer> <silent> ru :<C-U>Grebase --interactive @{upstream}<CR>
5495 nnoremap <buffer> <silent> rp :<C-U>Grebase --interactive @{push}<CR>
5496 nnoremap <buffer> <silent> rw :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/reword/e<CR>
5497 nnoremap <buffer> <silent> rm :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/edit/e<CR>
5498 nnoremap <buffer> <silent> rd :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5499 nnoremap <buffer> <silent> rk :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5500 nnoremap <buffer> <silent> rx :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5501 nnoremap <buffer> <silent> rr :<C-U>Grebase --continue<CR>
5502 nnoremap <buffer> <silent> rs :<C-U>Grebase --skip<CR>
5503 nnoremap <buffer> <silent> re :<C-U>Grebase --edit-todo<CR>
5504 nnoremap <buffer> <silent> ra :<C-U>Grebase --abort<CR>
5505 nnoremap <buffer> <silent> r? :<C-U>help fugitive_r<CR>
5507 call s:Map('n', '.', ":<C-U> <C-R>=<SID>fnameescape(fugitive#Real(@%))<CR><Home>")
5508 call s:Map('x', '.', ":<C-U> <C-R>=<SID>fnameescape(fugitive#Real(@%))<CR><Home>")
5509 call s:Map('n', 'g?', ":<C-U>help fugitive-map<CR>", '<silent>')
5510 call s:Map('n', '<F1>', ":<C-U>help fugitive-map<CR>", '<silent>')
5514 function! s:StatusCfile(...) abort
5516 let lead = s:cpath(tree, getcwd()) ? './' : tree . '/'
5517 let info = s:StageInfo()
5518 let line = getline('.')
5519 if len(info.sigil) && len(info.section) && len(info.paths)
5520 if info.section ==# 'Unstaged' && info.sigil !=# '-'
5521 return [lead . info.relative[0], info.offset, 'normal!zv']
5522 elseif info.section ==# 'Staged' && info.sigil ==# '-'
5523 return ['@:' . info.relative[0], info.offset, 'normal!zv']
5525 return [':0:' . info.relative[0], info.offset, 'normal!zv']
5527 elseif len(info.paths)
5528 return [lead . info.relative[0]]
5529 elseif len(info.commit)
5530 return [info.commit]
5531 elseif line =~# '^\%(Head\|Merge\|Rebase\|Upstream\|Pull\|Push\): '
5532 return [matchstr(line, ' \zs.*')]
5538 function! fugitive#StatusCfile() abort
5539 let file = s:Generate(s:StatusCfile()[0])
5540 return empty(file) ? fugitive#Cfile() : s:fnameescape(file)
5543 function! s:MessageCfile(...) abort
5545 let lead = s:cpath(tree, getcwd()) ? './' : tree . '/'
5546 if getline('.') =~# '^.\=\trenamed:.* -> '
5547 return lead . matchstr(getline('.'),' -> \zs.*')
5548 elseif getline('.') =~# '^.\=\t\(\k\| \)\+\p\?: *.'
5549 return lead . matchstr(getline('.'),': *\zs.\{-\}\ze\%( ([^()[:digit:]]\+)\)\=$')
5550 elseif getline('.') =~# '^.\=\t.'
5551 return lead . matchstr(getline('.'),'\t\zs.*')
5552 elseif getline('.') =~# ': needs merge$'
5553 return lead . matchstr(getline('.'),'.*\ze: needs merge$')
5554 elseif getline('.') =~# '^\%(. \)\=Not currently on any branch.$'
5556 elseif getline('.') =~# '^\%(. \)\=On branch '
5557 return 'refs/heads/'.getline('.')[12:]
5558 elseif getline('.') =~# "^\\%(. \\)\=Your branch .*'"
5559 return matchstr(getline('.'),"'\\zs\\S\\+\\ze'")
5565 function! fugitive#MessageCfile() abort
5566 let file = s:Generate(s:MessageCfile())
5567 return empty(file) ? fugitive#Cfile() : s:fnameescape(file)
5570 function! s:cfile() abort
5572 let myhash = s:DirRev(@%)[1]
5575 let myhash = fugitive#RevParse(myhash)
5580 if empty(myhash) && getline(1) =~# '^\%(commit\|tag\) \w'
5581 let myhash = matchstr(getline(1),'^\w\+ \zs\S\+')
5584 let showtree = (getline(1) =~# '^tree ' && getline(2) == "")
5586 let treebase = substitute(s:DirCommitFile(@%)[1], '^\d$', ':&', '') . ':' .
5587 \ s:Relative('') . (s:Relative('') =~# '^$\|/$' ? '' : '/')
5589 if getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40,\}\t'
5590 return [treebase . s:sub(matchstr(getline('.'),'\t\zs.*'),'/$','')]
5592 return [treebase . s:sub(getline('.'),'/$','')]
5599 if getline('.') =~# '^\d\{6\} \x\{40,\} \d\t'
5600 let ref = matchstr(getline('.'),'\x\{40,\}')
5601 let file = ':'.s:sub(matchstr(getline('.'),'\d\t.*'),'\t',':')
5605 if getline('.') =~# '^ref: '
5606 let ref = strpart(getline('.'),5)
5608 elseif getline('.') =~# '^commit \x\{40,\}\>'
5609 let ref = matchstr(getline('.'),'\x\{40,\}')
5612 elseif getline('.') =~# '^parent \x\{40,\}\>'
5613 let ref = matchstr(getline('.'),'\x\{40,\}')
5614 let line = line('.')
5616 while getline(line) =~# '^parent '
5622 elseif getline('.') =~# '^tree \x\{40,\}$'
5623 let ref = matchstr(getline('.'),'\x\{40,\}')
5624 if len(myhash) && fugitive#RevParse(myhash.':') ==# ref
5625 let ref = myhash.':'
5629 elseif getline('.') =~# '^object \x\{40,\}$' && getline(line('.')+1) =~ '^type \%(commit\|tree\|blob\)$'
5630 let ref = matchstr(getline('.'),'\x\{40,\}')
5631 let type = matchstr(getline(line('.')+1),'type \zs.*')
5633 elseif getline('.') =~# '^\l\{3,8\} '.myhash.'$'
5634 let ref = s:DirRev(@%)[1]
5636 elseif getline('.') =~# '^\l\{3,8\} \x\{40,\}\>'
5637 let ref = matchstr(getline('.'),'\x\{40,\}')
5638 echoerr "warning: unknown context ".matchstr(getline('.'),'^\l*')
5640 elseif getline('.') =~# '^[+-]\{3\} [abciow12]\=/'
5641 let ref = getline('.')[4:]
5643 elseif getline('.') =~# '^[+-]' && search('^@@ -\d\+\%(,\d\+\)\= +\d\+','bnW')
5644 let type = getline('.')[0]
5645 let lnum = line('.') - 1
5647 while getline(lnum) !~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
5648 if getline(lnum) =~# '^[ '.type.']'
5653 let offset += matchstr(getline(lnum), type.'\zs\d\+')
5654 let ref = getline(search('^'.type.'\{3\} [abciow12]/','bnW'))[4:-1]
5655 let dcmds = [offset, 'normal!zv']
5657 elseif getline('.') =~# '^rename from '
5658 let ref = 'a/'.getline('.')[12:]
5659 elseif getline('.') =~# '^rename to '
5660 let ref = 'b/'.getline('.')[10:]
5662 elseif getline('.') =~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
5663 let diff = getline(search('^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)', 'bcnW'))
5664 let offset = matchstr(getline('.'), '+\zs\d\+')
5666 let dref = matchstr(diff, '\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5667 let ref = matchstr(diff, '\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5668 let dcmd = 'Gdiffsplit! +'.offset
5670 elseif getline('.') =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)'
5671 let dref = matchstr(getline('.'),'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5672 let ref = matchstr(getline('.'),'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5673 let dcmd = 'Gdiffsplit!'
5675 elseif getline('.') =~# '^index ' && getline(line('.')-1) =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)'
5676 let line = getline(line('.')-1)
5677 let dref = matchstr(line,'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5678 let ref = matchstr(line,'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5679 let dcmd = 'Gdiffsplit!'
5681 elseif line('$') == 1 && getline('.') =~ '^\x\{40,\}$'
5682 let ref = getline('.')
5684 elseif expand('<cword>') =~# '^\x\{7,\}\>'
5685 return [expand('<cword>')]
5700 let prefixes.a = myhash.'^:'
5701 let prefixes.b = myhash.':'
5703 let ref = substitute(ref, '^\(\w\)/', '\=get(prefixes, submatch(1), "HEAD:")', '')
5705 let dref = substitute(dref, '^\(\w\)/', '\=get(prefixes, submatch(1), "HEAD:")', '')
5708 if ref ==# '/dev/null'
5710 let ref = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'
5714 return [ref, dcmd . ' ' . s:fnameescape(dref)] + dcmds
5716 return [ref] + dcmds
5724 function! s:GF(mode) abort
5726 let results = &filetype ==# 'fugitive' ? s:StatusCfile() : &filetype ==# 'gitcommit' ? [s:MessageCfile()] : s:cfile()
5728 return 'echoerr ' . string(v:exception)
5731 return 'G' . a:mode .
5732 \ ' +' . escape(results[1], ' ') . ' ' .
5733 \ s:fnameescape(results[0]) . join(map(results[2:-1], '"|" . v:val'), '')
5734 elseif len(results) && len(results[0])
5735 return 'G' . a:mode . ' ' . s:fnameescape(results[0])
5741 function! fugitive#Cfile() abort
5743 let results = s:cfile()
5745 let cfile = expand('<cfile>')
5746 if &includeexpr =~# '\<v:fname\>'
5747 sandbox let cfile = eval(substitute(&includeexpr, '\C\<v:fname\>', '\=string(cfile)', 'g'))
5750 elseif len(results) > 1
5751 let pre = '+' . join(map(results[1:-1], 'escape(v:val, " ")'), '\|') . ' '
5753 return pre . s:fnameescape(s:Generate(results[0]))
5756 " Section: Statusline
5758 function! fugitive#Statusline(...) abort
5759 let dir = s:Dir(bufnr(''))
5764 let commit = s:DirCommitFile(@%)[1]
5766 let status .= ':' . commit[0:6]
5768 let status .= '('.FugitiveHead(7, dir).')'
5769 return '[Git'.status.']'
5772 function! fugitive#statusline(...) abort
5773 return fugitive#Statusline()
5776 function! fugitive#head(...) abort
5781 return fugitive#Head(a:0 ? a:1 : 0)
5786 function! fugitive#Foldtext() abort
5787 if &foldmethod !=# 'syntax'
5791 let line_foldstart = getline(v:foldstart)
5792 if line_foldstart =~# '^diff '
5793 let [add, remove] = [-1, -1]
5795 for lnum in range(v:foldstart, v:foldend)
5796 let line = getline(lnum)
5797 if filename ==# '' && line =~# '^[+-]\{3\} [abciow12]/'
5798 let filename = line[6:-1]
5802 elseif line =~# '^-'
5804 elseif line =~# '^Binary '
5809 let filename = matchstr(line_foldstart, '^diff .\{-\} [abciow12]/\zs.*\ze [abciow12]/')
5812 let filename = line_foldstart[5:-1]
5815 return 'Binary: '.filename
5817 return (add<10&&remove<100?' ':'') . add . '+ ' . (remove<10&&add<100?' ':'') . remove . '- ' . filename
5819 elseif line_foldstart =~# '^# .*:$'
5820 let lines = getline(v:foldstart, v:foldend)
5821 call filter(lines, 'v:val =~# "^#\t"')
5822 cal map(lines, "s:sub(v:val, '^#\t%(modified: +|renamed: +)=', '')")
5823 cal map(lines, "s:sub(v:val, '^([[:alpha:] ]+): +(.*)', '\\2 (\\1)')")
5824 return line_foldstart.' '.join(lines, ', ')
5829 function! fugitive#foldtext() abort
5830 return fugitive#Foldtext()
5833 augroup fugitive_folding
5835 autocmd User Fugitive
5836 \ if &filetype =~# '^git\%(commit\)\=$' && &foldtext ==# 'foldtext()' |
5837 \ set foldtext=fugitive#Foldtext() |
5841 " Section: Initialization
5843 function! fugitive#Init() abort
5844 if exists('#User#FugitiveBoot')
5846 let [save_mls, &modelines] = [&mls, 0]
5847 doautocmd User FugitiveBoot
5853 if stridx(&tags, escape(dir, ', ')) == -1 && &tags !~# '\.git' && !exists('s:tags_warning')
5854 let actualdir = fugitive#Find('.git/', dir)
5855 if filereadable(actualdir . 'tags')
5856 let s:tags_warning = 1
5858 echo "Fugitive .git/tags support removed in favor of `:set tags^=./.git/tags;`"
5863 let [save_mls, &modelines] = [&mls, 0]
5864 call s:define_commands()
5865 doautocmd User Fugitive
5871 function! fugitive#is_git_dir(path) abort
5872 return FugitiveIsGitDir(a:path)
5875 function! fugitive#extract_git_dir(path) abort
5876 return FugitiveExtractGitDir(a:path)
5879 function! fugitive#detect(path) abort
5880 return FugitiveDetect(a:path)