fugitive.vim (58590B)
1 " fugitive.vim - A Git wrapper so awesome, it should be illegal 2 " Maintainer: Tim Pope <vimNOSPAM@tpope.org> 3 " Version: 1.1 4 " GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim 5 6 if exists('g:loaded_fugitive') || &cp 7 finish 8 endif 9 let g:loaded_fugitive = 1 10 11 if !exists('g:fugitive_git_executable') 12 let g:fugitive_git_executable = 'git' 13 endif 14 15 " Utility {{{1 16 17 function! s:function(name) abort 18 return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),'')) 19 endfunction 20 21 function! s:sub(str,pat,rep) abort 22 return substitute(a:str,'\v\C'.a:pat,a:rep,'') 23 endfunction 24 25 function! s:gsub(str,pat,rep) abort 26 return substitute(a:str,'\v\C'.a:pat,a:rep,'g') 27 endfunction 28 29 function! s:shellesc(arg) abort 30 if a:arg =~ '^[A-Za-z0-9_/.-]\+$' 31 return a:arg 32 elseif &shell =~# 'cmd' && a:arg !~# '"' 33 return '"'.a:arg.'"' 34 else 35 return shellescape(a:arg) 36 endif 37 endfunction 38 39 function! s:fnameescape(file) abort 40 if exists('*fnameescape') 41 return fnameescape(a:file) 42 else 43 return escape(a:file," \t\n*?[{`$\\%#'\"|!<") 44 endif 45 endfunction 46 47 function! s:throw(string) abort 48 let v:errmsg = 'fugitive: '.a:string 49 throw v:errmsg 50 endfunction 51 52 function! s:warn(str) 53 echohl WarningMsg 54 echomsg a:str 55 echohl None 56 let v:warningmsg = a:str 57 endfunction 58 59 function! s:shellslash(path) 60 if exists('+shellslash') && !&shellslash 61 return s:gsub(a:path,'\\','/') 62 else 63 return a:path 64 endif 65 endfunction 66 67 function! s:add_methods(namespace, method_names) abort 68 for name in a:method_names 69 let s:{a:namespace}_prototype[name] = s:function('s:'.a:namespace.'_'.name) 70 endfor 71 endfunction 72 73 let s:commands = [] 74 function! s:command(definition) abort 75 let s:commands += [a:definition] 76 endfunction 77 78 function! s:define_commands() 79 for command in s:commands 80 exe 'command! -buffer '.command 81 endfor 82 endfunction 83 84 function! s:compatibility_check() 85 if exists('b:git_dir') && exists('*GitBranchInfoCheckGitDir') && !exists('g:fugitive_did_compatibility_warning') 86 let g:fugitive_did_compatibility_warning = 1 87 call s:warn("See http://github.com/tpope/vim-fugitive/issues#issue/1 for why you should remove git-branch-info.vim") 88 endif 89 endfunction 90 91 augroup fugitive_utility 92 autocmd! 93 autocmd User Fugitive call s:define_commands() 94 autocmd VimEnter * call s:compatibility_check() 95 augroup END 96 97 let s:abstract_prototype = {} 98 99 " }}}1 100 " Initialization {{{1 101 102 function! s:ExtractGitDir(path) abort 103 let path = s:shellslash(a:path) 104 if path =~? '^fugitive://.*//' 105 return matchstr(path,'fugitive://\zs.\{-\}\ze//') 106 endif 107 let fn = fnamemodify(path,':s?[\/]$??') 108 let ofn = "" 109 let nfn = fn 110 while fn != ofn 111 if isdirectory(fn . '/.git') 112 return s:sub(simplify(fnamemodify(fn . '/.git',':p')),'\W$','') 113 elseif fn =~ '\.git$' && filereadable(fn . '/HEAD') 114 return s:sub(simplify(fnamemodify(fn,':p')),'\W$','') 115 endif 116 let ofn = fn 117 let fn = fnamemodify(ofn,':h') 118 endwhile 119 return '' 120 endfunction 121 122 function! s:Detect(path) 123 if exists('b:git_dir') && b:git_dir ==# '' 124 unlet b:git_dir 125 endif 126 if !exists('b:git_dir') 127 let dir = s:ExtractGitDir(a:path) 128 if dir != '' 129 let b:git_dir = dir 130 endif 131 endif 132 if exists('b:git_dir') 133 silent doautocmd User Fugitive 134 cnoremap <expr> <buffer> <C-R><C-G> fugitive#buffer().rev() 135 let buffer = fugitive#buffer() 136 if expand('%:p') =~# '//' 137 call buffer.setvar('&path',s:sub(buffer.getvar('&path'),'^\.%(,|$)','')) 138 endif 139 if b:git_dir !~# ',' && stridx(buffer.getvar('&tags'),b:git_dir.'/tags') == -1 140 call buffer.setvar('&tags',buffer.getvar('&tags').','.b:git_dir.'/tags') 141 endif 142 endif 143 endfunction 144 145 augroup fugitive 146 autocmd! 147 autocmd BufNewFile,BufReadPost * call s:Detect(expand('<amatch>:p')) 148 autocmd FileType netrw call s:Detect(expand('<afile>:p')) 149 autocmd VimEnter * if expand('<amatch>')==''|call s:Detect(getcwd())|endif 150 autocmd BufWinLeave * execute getwinvar(+winnr(), 'fugitive_restore') 151 augroup END 152 153 " }}}1 154 " Repository {{{1 155 156 let s:repo_prototype = {} 157 let s:repos = {} 158 159 function! s:repo(...) abort 160 let dir = a:0 ? a:1 : (exists('b:git_dir') && b:git_dir !=# '' ? b:git_dir : s:ExtractGitDir(expand('%:p'))) 161 if dir !=# '' 162 if has_key(s:repos,dir) 163 let repo = get(s:repos,dir) 164 else 165 let repo = {'git_dir': dir} 166 let s:repos[dir] = repo 167 endif 168 return extend(extend(repo,s:repo_prototype,'keep'),s:abstract_prototype,'keep') 169 endif 170 call s:throw('not a git repository: '.expand('%:p')) 171 endfunction 172 173 function! s:repo_dir(...) dict abort 174 return join([self.git_dir]+a:000,'/') 175 endfunction 176 177 function! s:repo_tree(...) dict abort 178 if !self.bare() 179 let dir = fnamemodify(self.git_dir,':h') 180 return join([dir]+a:000,'/') 181 endif 182 call s:throw('no work tree') 183 endfunction 184 185 function! s:repo_bare() dict abort 186 return self.dir() !~# '/\.git$' 187 endfunction 188 189 function! s:repo_translate(spec) dict abort 190 if a:spec ==# '.' || a:spec ==# '/.' 191 return self.bare() ? self.dir() : self.tree() 192 elseif a:spec =~# '^/' 193 return fnamemodify(self.dir(),':h').a:spec 194 elseif a:spec =~# '^:[0-3]:' 195 return 'fugitive://'.self.dir().'//'.a:spec[1].'/'.a:spec[3:-1] 196 elseif a:spec ==# ':' 197 if $GIT_INDEX_FILE =~# '/[^/]*index[^/]*\.lock$' && fnamemodify($GIT_INDEX_FILE,':p')[0:strlen(s:repo().dir())] ==# s:repo().dir('') && filereadable($GIT_INDEX_FILE) 198 return fnamemodify($GIT_INDEX_FILE,':p') 199 else 200 return self.dir('index') 201 endif 202 elseif a:spec =~# '^:/' 203 let ref = self.rev_parse(matchstr(a:spec,'.[^:]*')) 204 return 'fugitive://'.self.dir().'//'.ref 205 elseif a:spec =~# '^:' 206 return 'fugitive://'.self.dir().'//0/'.a:spec[1:-1] 207 elseif a:spec =~# 'HEAD\|^refs/' && a:spec !~ ':' && filereadable(self.dir(a:spec)) 208 return self.dir(a:spec) 209 elseif filereadable(s:repo().dir('refs/'.a:spec)) 210 return self.dir('refs/'.a:spec) 211 elseif filereadable(s:repo().dir('refs/tags/'.a:spec)) 212 return self.dir('refs/tags/'.a:spec) 213 elseif filereadable(s:repo().dir('refs/heads/'.a:spec)) 214 return self.dir('refs/heads/'.a:spec) 215 elseif filereadable(s:repo().dir('refs/remotes/'.a:spec)) 216 return self.dir('refs/remotes/'.a:spec) 217 elseif filereadable(s:repo().dir('refs/remotes/'.a:spec.'/HEAD')) 218 return self.dir('refs/remotes/'.a:spec,'/HEAD') 219 else 220 try 221 let ref = self.rev_parse(matchstr(a:spec,'[^:]*')) 222 let path = s:sub(matchstr(a:spec,':.*'),'^:','/') 223 return 'fugitive://'.self.dir().'//'.ref.path 224 catch /^fugitive:/ 225 return self.tree(a:spec) 226 endtry 227 endif 228 endfunction 229 230 call s:add_methods('repo',['dir','tree','bare','translate']) 231 232 function! s:repo_git_command(...) dict abort 233 let git = g:fugitive_git_executable . ' --git-dir='.s:shellesc(self.git_dir) 234 return git.join(map(copy(a:000),'" ".s:shellesc(v:val)'),'') 235 endfunction 236 237 function! s:repo_git_chomp(...) dict abort 238 return s:sub(system(call(self.git_command,a:000,self)),'\n$','') 239 endfunction 240 241 function! s:repo_git_chomp_in_tree(...) dict abort 242 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' 243 let dir = getcwd() 244 try 245 execute cd.'`=s:repo().tree()`' 246 return call(s:repo().git_chomp, a:000, s:repo()) 247 finally 248 execute cd.'`=dir`' 249 endtry 250 endfunction 251 252 function! s:repo_rev_parse(rev) dict abort 253 let hash = self.git_chomp('rev-parse','--verify',a:rev) 254 if hash =~ '^\x\{40\}$' 255 return hash 256 endif 257 call s:throw('rev-parse '.a:rev.': '.hash) 258 endfunction 259 260 call s:add_methods('repo',['git_command','git_chomp','git_chomp_in_tree','rev_parse']) 261 262 function! s:repo_dirglob(base) dict abort 263 let base = s:sub(a:base,'^/','') 264 let matches = split(glob(self.tree(s:gsub(base,'/','*&').'*/')),"\n") 265 call map(matches,'v:val[ strlen(self.tree())+(a:base !~ "^/") : -1 ]') 266 return matches 267 endfunction 268 269 function! s:repo_superglob(base) dict abort 270 if a:base =~# '^/' || a:base !~# ':' 271 let results = [] 272 if a:base !~# '^/' 273 let heads = ["HEAD","ORIG_HEAD","FETCH_HEAD","MERGE_HEAD"] 274 let heads += sort(split(s:repo().git_chomp("rev-parse","--symbolic","--branches","--tags","--remotes"),"\n")) 275 call filter(heads,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base') 276 let results += heads 277 endif 278 if !self.bare() 279 let base = s:sub(a:base,'^/','') 280 let matches = split(glob(self.tree(s:gsub(base,'/','*&').'*')),"\n") 281 call map(matches,'s:shellslash(v:val)') 282 call map(matches,'v:val !~ "/$" && isdirectory(v:val) ? v:val."/" : v:val') 283 call map(matches,'v:val[ strlen(self.tree())+(a:base !~ "^/") : -1 ]') 284 let results += matches 285 endif 286 return results 287 288 elseif a:base =~# '^:' 289 let entries = split(self.git_chomp('ls-files','--stage'),"\n") 290 call map(entries,'s:sub(v:val,".*(\\d)\\t(.*)",":\\1:\\2")') 291 if a:base !~# '^:[0-3]\%(:\|$\)' 292 call filter(entries,'v:val[1] == "0"') 293 call map(entries,'v:val[2:-1]') 294 endif 295 call filter(entries,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base') 296 return entries 297 298 else 299 let tree = matchstr(a:base,'.*[:/]') 300 let entries = split(self.git_chomp('ls-tree',tree),"\n") 301 call map(entries,'s:sub(v:val,"^04.*\\zs$","/")') 302 call map(entries,'tree.s:sub(v:val,".*\t","")') 303 return filter(entries,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base') 304 endif 305 endfunction 306 307 call s:add_methods('repo',['dirglob','superglob']) 308 309 function! s:repo_keywordprg() dict abort 310 let args = ' --git-dir='.escape(self.dir(),"\\\"' ").' show' 311 if has('gui_running') && !has('win32') 312 return g:fugitive_git_executable . ' --no-pager' . args 313 else 314 return g:fugitive_git_executable . args 315 endif 316 endfunction 317 318 call s:add_methods('repo',['keywordprg']) 319 320 " }}}1 321 " Buffer {{{1 322 323 let s:buffer_prototype = {} 324 325 function! s:buffer(...) abort 326 let buffer = {'#': bufnr(a:0 ? a:1 : '%')} 327 call extend(extend(buffer,s:buffer_prototype,'keep'),s:abstract_prototype,'keep') 328 if buffer.getvar('git_dir') !=# '' 329 return buffer 330 endif 331 call s:throw('not a git repository: '.expand('%:p')) 332 endfunction 333 334 function! fugitive#buffer(...) abort 335 return s:buffer(a:0 ? a:1 : '%') 336 endfunction 337 338 function! s:buffer_getvar(var) dict abort 339 return getbufvar(self['#'],a:var) 340 endfunction 341 342 function! s:buffer_setvar(var,value) dict abort 343 return setbufvar(self['#'],a:var,a:value) 344 endfunction 345 346 function! s:buffer_getline(lnum) dict abort 347 return getbufline(self['#'],a:lnum)[0] 348 endfunction 349 350 function! s:buffer_repo() dict abort 351 return s:repo(self.getvar('git_dir')) 352 endfunction 353 354 function! s:buffer_type(...) dict abort 355 if self.getvar('fugitive_type') != '' 356 let type = self.getvar('fugitive_type') 357 elseif fnamemodify(self.spec(),':p') =~# '.\git/refs/\|\.git/\w*HEAD$' 358 let type = 'head' 359 elseif self.getline(1) =~ '^tree \x\{40\}$' && self.getline(2) == '' 360 let type = 'tree' 361 elseif self.getline(1) =~ '^\d\{6\} \w\{4\} \x\{40\}\>\t' 362 let type = 'tree' 363 elseif self.getline(1) =~ '^\d\{6\} \x\{40\}\> \d\t' 364 let type = 'index' 365 elseif isdirectory(self.spec()) 366 let type = 'directory' 367 elseif self.spec() == '' 368 let type = 'null' 369 elseif filereadable(self.spec()) 370 let type = 'file' 371 else 372 let type = '' 373 endif 374 if a:0 375 return !empty(filter(copy(a:000),'v:val ==# type')) 376 else 377 return type 378 endif 379 endfunction 380 381 function! s:buffer_spec() dict abort 382 let bufname = bufname(self['#']) 383 return s:shellslash(bufname == '' ? '' : fnamemodify(bufname,':p')) 384 endfunction 385 386 function! s:buffer_name() dict abort 387 return self.spec() 388 endfunction 389 390 function! s:buffer_commit() dict abort 391 return matchstr(self.spec(),'^fugitive://.\{-\}//\zs\w*') 392 endfunction 393 394 function! s:buffer_path(...) dict abort 395 let rev = matchstr(self.spec(),'^fugitive://.\{-\}//\zs.*') 396 if rev != '' 397 let rev = s:sub(rev,'\w*','') 398 else 399 let rev = self.spec()[strlen(self.repo().tree()) : -1] 400 endif 401 return s:sub(rev,'^/',a:0 ? a:1 : '') 402 endfunction 403 404 function! s:buffer_rev() dict abort 405 let rev = matchstr(self.spec(),'^fugitive://.\{-\}//\zs.*') 406 if rev =~ '^\x/' 407 return ':'.rev[0].':'.rev[2:-1] 408 elseif rev =~ '.' 409 return s:sub(rev,'/',':') 410 elseif self.spec() =~ '\.git/index$' 411 return ':' 412 elseif self.spec() =~ '\.git/refs/\|\.git/.*HEAD$' 413 return self.spec()[strlen(self.repo().dir())+1 : -1] 414 else 415 return self.path() 416 endif 417 endfunction 418 419 function! s:buffer_sha1() dict abort 420 if self.spec() =~ '^fugitive://' || self.spec() =~ '\.git/refs/\|\.git/.*HEAD$' 421 return self.repo().rev_parse(self.rev()) 422 else 423 return '' 424 endif 425 endfunction 426 427 function! s:buffer_expand(rev) dict abort 428 if a:rev =~# '^:[0-3]$' 429 let file = a:rev.self.path(':') 430 elseif a:rev =~# '^-' 431 let file = 'HEAD^{}'.a:rev[1:-1].self.path(':') 432 elseif a:rev =~# '^@{' 433 let file = 'HEAD'.a:rev.self.path(':') 434 elseif a:rev =~# '^[~^]' 435 let commit = s:sub(self.commit(),'^\d=$','HEAD') 436 let file = commit.a:rev.self.path(':') 437 else 438 let file = a:rev 439 endif 440 return s:sub(file,'\%$',self.path()) 441 endfunction 442 443 function! s:buffer_containing_commit() dict abort 444 if self.commit() =~# '^\d$' 445 return ':' 446 elseif self.commit() =~# '.' 447 return self.commit() 448 else 449 return 'HEAD' 450 endif 451 endfunction 452 453 call s:add_methods('buffer',['getvar','setvar','getline','repo','type','spec','name','commit','path','rev','sha1','expand','containing_commit']) 454 455 " }}}1 456 " Git {{{1 457 458 call s:command("-bang -nargs=? -complete=customlist,s:GitComplete Git :execute s:Git(<bang>0,<q-args>)") 459 460 function! s:ExecuteInTree(cmd) abort 461 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' 462 let dir = getcwd() 463 try 464 execute cd.'`=s:repo().tree()`' 465 execute a:cmd 466 finally 467 execute cd.'`=dir`' 468 endtry 469 endfunction 470 471 function! s:Git(bang,cmd) abort 472 let git = s:repo().git_command() 473 if has('gui_running') && !has('win32') 474 let git .= ' --no-pager' 475 endif 476 let cmd = matchstr(a:cmd,'\v\C.{-}%($|\\@<!%(\\\\)*\|)@=') 477 call s:ExecuteInTree('!'.git.' '.cmd) 478 call fugitive#reload_status() 479 return matchstr(a:cmd,'\v\C\\@<!%(\\\\)*\|\zs.*') 480 endfunction 481 482 function! s:GitComplete(A,L,P) abort 483 if !exists('s:exec_path') 484 let s:exec_path = s:sub(system(g:fugitive_git_executable.' --exec-path'),'\n$','') 485 endif 486 let cmds = map(split(glob(s:exec_path.'/git-*'),"\n"),'s:sub(v:val[strlen(s:exec_path)+5 : -1],"\\.exe$","")') 487 if a:L =~ ' [[:alnum:]-]\+ ' 488 return s:repo().superglob(a:A) 489 elseif a:A == '' 490 return cmds 491 else 492 return filter(cmds,'v:val[0 : strlen(a:A)-1] ==# a:A') 493 endif 494 endfunction 495 496 " }}}1 497 " Gcd, Glcd {{{1 498 499 function! s:DirComplete(A,L,P) abort 500 let matches = s:repo().dirglob(a:A) 501 return matches 502 endfunction 503 504 call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Gcd :cd<bang> `=s:repo().bare() ? s:repo().dir(<q-args>) : s:repo().tree(<q-args>)`") 505 call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Glcd :lcd<bang> `=s:repo().bare() ? s:repo().dir(<q-args>) : s:repo().tree(<q-args>)`") 506 507 " }}}1 508 " Gstatus {{{1 509 510 call s:command("-bar Gstatus :execute s:Status()") 511 512 function! s:Status() abort 513 try 514 Gpedit : 515 wincmd P 516 nnoremap <buffer> <silent> q :<C-U>bdelete<CR> 517 catch /^fugitive:/ 518 return 'echoerr v:errmsg' 519 endtry 520 return '' 521 endfunction 522 523 function! fugitive#reload_status() abort 524 let mytab = tabpagenr() 525 for tab in [mytab] + range(1,tabpagenr('$')) 526 for winnr in range(1,tabpagewinnr(tab,'$')) 527 if getbufvar(tabpagebuflist(tab)[winnr-1],'fugitive_type') ==# 'index' 528 execute 'tabnext '.tab 529 if winnr != winnr() 530 execute winnr.'wincmd w' 531 let restorewinnr = 1 532 endif 533 try 534 if !&modified 535 call s:BufReadIndex() 536 endif 537 finally 538 if exists('restorewinnr') 539 wincmd p 540 endif 541 execute 'tabnext '.mytab 542 endtry 543 endif 544 endfor 545 endfor 546 endfunction 547 548 function! s:StageDiff(bang) abort 549 let section = getline(search('^# .*:$','bnW')) 550 let line = getline('.') 551 let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*') 552 if filename ==# '' && section == '# Changes to be committed:' 553 return 'Git diff --cached' 554 elseif filename ==# '' 555 return 'Git diff' 556 elseif line =~# '^#\trenamed:' && filename =~ ' -> ' 557 let [old, new] = split(filename,' -> ') 558 execute 'Gedit '.s:fnameescape(':0:'.new) 559 return 'Gdiff'.a:bang.' HEAD:'.s:fnameescape(old) 560 elseif section == '# Changes to be committed:' 561 execute 'Gedit '.s:fnameescape(':0:'.filename) 562 return 'Gdiff'.a:bang.' -' 563 else 564 execute 'Gedit '.s:fnameescape('/'.filename) 565 return 'Gdiff'.a:bang 566 endif 567 endfunction 568 569 function! s:StageToggle(lnum1,lnum2) abort 570 try 571 let output = '' 572 for lnum in range(a:lnum1,a:lnum2) 573 let line = getline(lnum) 574 if getline('.') == '# Changes to be committed:' 575 return 'Gcommit' 576 endif 577 let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*') 578 if filename ==# '' 579 continue 580 endif 581 if !exists('first_filename') 582 let first_filename = filename 583 endif 584 execute lnum 585 let section = getline(search('^# .*:$','bnW')) 586 if line =~# '^#\trenamed:' && filename =~ ' -> ' 587 let cmd = ['mv','--'] + reverse(split(filename,' -> ')) 588 let filename = cmd[-1] 589 elseif section =~? ' to be ' 590 let cmd = ['reset','-q','--',filename] 591 elseif line =~# '^#\tdeleted:' 592 let cmd = ['rm','--',filename] 593 else 594 let cmd = ['add','--',filename] 595 endif 596 let output .= call(s:repo().git_chomp_in_tree,cmd,s:repo())."\n" 597 endfor 598 if exists('first_filename') 599 let jump = first_filename 600 let f = matchstr(getline(a:lnum1-1),'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*') 601 if f !=# '' | let jump = f | endif 602 let f = matchstr(getline(a:lnum2+1),'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*') 603 if f !=# '' | let jump = f | endif 604 silent! edit! 605 1 606 redraw 607 call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.jump.'\$','W') 608 endif 609 echo s:sub(s:gsub(output,'\n+','\n'),'\n$','') 610 catch /^fugitive:/ 611 return 'echoerr v:errmsg' 612 endtry 613 return 'checktime' 614 endfunction 615 616 function! s:StagePatch(lnum1,lnum2) abort 617 let add = [] 618 let reset = [] 619 620 for lnum in range(a:lnum1,a:lnum2) 621 let line = getline(lnum) 622 if line == '# Changes to be committed:' 623 return 'Git reset --patch' 624 elseif line == '# Changed but not updated:' 625 return 'Git add --patch' 626 endif 627 let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*') 628 if filename ==# '' 629 continue 630 endif 631 if !exists('first_filename') 632 let first_filename = filename 633 endif 634 execute lnum 635 let section = getline(search('^# .*:$','bnW')) 636 if line =~# '^#\trenamed:' && filename =~ ' -> ' 637 let reset += [split(filename,' -> ')[1]] 638 elseif section =~? ' to be ' 639 let reset += [filename] 640 elseif line !~# '^#\tdeleted:' 641 let add += [filename] 642 endif 643 endfor 644 try 645 if !empty(add) 646 execute "Git add --patch -- ".join(map(add,'s:shellesc(v:val)')) 647 endif 648 if !empty(reset) 649 execute "Git reset --patch -- ".join(map(add,'s:shellesc(v:val)')) 650 endif 651 if exists('first_filename') 652 silent! edit! 653 1 654 redraw 655 call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.first_filename.'\$','W') 656 endif 657 catch /^fugitive:/ 658 return 'echoerr v:errmsg' 659 endtry 660 return 'checktime' 661 endfunction 662 663 " }}}1 664 " Gcommit {{{1 665 666 call s:command("-nargs=? -complete=customlist,s:CommitComplete Gcommit :execute s:Commit(<q-args>)") 667 668 function! s:Commit(args) abort 669 let old_type = s:buffer().type() 670 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' 671 let dir = getcwd() 672 let msgfile = s:repo().dir('COMMIT_EDITMSG') 673 let outfile = tempname() 674 let errorfile = tempname() 675 try 676 execute cd.'`=s:repo().tree()`' 677 let command = '' 678 if &shell =~# 'cmd' 679 let old_editor = $GIT_EDITOR 680 let $GIT_EDITOR = 'false' 681 elseif &shell !~# 'csh' 682 let command = 'GIT_EDITOR=false ' 683 endif 684 let command .= s:repo().git_command('commit').' '.a:args 685 if &shell =~# 'csh' 686 silent execute '!setenv GIT_EDITOR false; ('.command.' > '.outfile.') >& '.errorfile 687 elseif a:args =~# '\%(^\| \)--interactive\>' 688 execute '!'.command.' 2> '.errorfile 689 else 690 silent execute '!'.command.' > '.outfile.' 2> '.errorfile 691 endif 692 if !v:shell_error 693 if filereadable(outfile) 694 for line in readfile(outfile) 695 echo line 696 endfor 697 endif 698 return '' 699 else 700 let error = get(readfile(errorfile),-2,'!') 701 if error =~# "'false'\\.$" 702 let args = a:args 703 let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-[se]|--edit|--interactive)%($| )','') 704 let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-F|--file|-m|--message)%(\s+|\=)%(''[^'']*''|"%(\\.|[^"])*"|\\.|\S)*','') 705 let args = s:gsub(args,'%(^| )@<=[%#]%(:\w)*','\=expand(submatch(0))') 706 let args = '-F '.s:shellesc(msgfile).' '.args 707 if args !~# '\%(^\| \)--cleanup\>' 708 let args = '--cleanup=strip '.args 709 endif 710 if bufname('%') == '' && line('$') == 1 && getline(1) == '' && !&mod 711 edit `=msgfile` 712 else 713 split `=msgfile` 714 endif 715 if old_type ==# 'index' 716 bdelete # 717 endif 718 let b:fugitive_commit_arguments = args 719 setlocal bufhidden=delete filetype=gitcommit 720 return '1' 721 elseif error ==# '!' 722 return s:Status() 723 else 724 call s:throw(error) 725 endif 726 endif 727 catch /^fugitive:/ 728 return 'echoerr v:errmsg' 729 finally 730 if exists('old_editor') 731 let $GIT_EDITOR = old_editor 732 endif 733 call delete(outfile) 734 call delete(errorfile) 735 execute cd.'`=dir`' 736 call fugitive#reload_status() 737 endtry 738 endfunction 739 740 function! s:CommitComplete(A,L,P) abort 741 if a:A =~ '^-' || type(a:A) == type(0) " a:A is 0 on :Gcommit -<Tab> 742 let args = ['-C', '-F', '-a', '-c', '-e', '-i', '-m', '-n', '-o', '-q', '-s', '-t', '-u', '-v', '--all', '--allow-empty', '--amend', '--author=', '--cleanup=', '--dry-run', '--edit', '--file=', '--include', '--interactive', '--message=', '--no-verify', '--only', '--quiet', '--reedit-message=', '--reuse-message=', '--signoff', '--template=', '--untracked-files', '--verbose'] 743 return filter(args,'v:val[0 : strlen(a:A)-1] ==# a:A') 744 else 745 return s:repo().superglob(a:A) 746 endif 747 endfunction 748 749 function! s:FinishCommit() 750 let args = getbufvar(+expand('<abuf>'),'fugitive_commit_arguments') 751 let g:args = args 752 if !empty(args) 753 call setbufvar(+expand('<abuf>'),'fugitive_commit_arguments','') 754 return s:Commit(args) 755 endif 756 return '' 757 endfunction 758 759 augroup fugitive_commit 760 autocmd! 761 autocmd VimLeavePre,BufDelete *.git/COMMIT_EDITMSG execute s:sub(s:FinishCommit(), '^echoerr (.*)', 'echohl ErrorMsg|echo \1|echohl NONE') 762 augroup END 763 764 " }}}1 765 " Ggrep, Glog {{{1 766 767 if !exists('g:fugitive_summary_format') 768 let g:fugitive_summary_format = '%s' 769 endif 770 771 call s:command("-bang -nargs=? -complete=customlist,s:EditComplete Ggrep :execute s:Grep(<bang>0,<q-args>)") 772 call s:command("-bar -bang -nargs=* -complete=customlist,s:EditComplete Glog :execute s:Log('grep<bang>',<f-args>)") 773 774 function! s:Grep(bang,arg) abort 775 let grepprg = &grepprg 776 let grepformat = &grepformat 777 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' 778 let dir = getcwd() 779 try 780 execute cd.'`=s:repo().tree()`' 781 let &grepprg = s:repo().git_command('--no-pager', 'grep', '-n') 782 let &grepformat = '%f:%l:%m' 783 exe 'grep! '.escape(matchstr(a:arg,'\v\C.{-}%($|[''" ]\@=\|)@='),'|') 784 let list = getqflist() 785 for entry in list 786 if bufname(entry.bufnr) =~ ':' 787 let entry.filename = s:repo().translate(bufname(entry.bufnr)) 788 unlet! entry.bufnr 789 elseif a:arg =~# '\%(^\| \)--cached\>' 790 let entry.filename = s:repo().translate(':0:'.bufname(entry.bufnr)) 791 unlet! entry.bufnr 792 endif 793 endfor 794 call setqflist(list,'r') 795 if !a:bang && !empty(list) 796 return 'cfirst'.matchstr(a:arg,'\v\C[''" ]\zs\|.*') 797 else 798 return matchstr(a:arg,'\v\C[''" ]\|\zs.*') 799 endif 800 finally 801 let &grepprg = grepprg 802 let &grepformat = grepformat 803 execute cd.'`=dir`' 804 endtry 805 endfunction 806 807 function! s:Log(cmd,...) 808 let path = s:buffer().path('/') 809 if path =~# '^/\.git\%(/\|$\)' || index(a:000,'--') != -1 810 let path = '' 811 endif 812 let cmd = ['--no-pager', 'log', '--no-color'] 813 let cmd += [escape('--pretty=format:fugitive://'.s:repo().dir().'//%H'.path.'::'.g:fugitive_summary_format,'%')] 814 if empty(filter(a:000[0 : index(a:000,'--')],'v:val !~# "^-"')) 815 if s:buffer().commit() =~# '\x\{40\}' 816 let cmd += [s:buffer().commit()] 817 elseif s:buffer().path() =~# '^\.git/refs/\|^\.git/.*HEAD$' 818 let cmd += [s:buffer().path()[5:-1]] 819 endif 820 end 821 let cmd += map(copy(a:000),'s:sub(v:val,"^\\%(%(:\\w)*)","\\=fnamemodify(s:buffer().path(),submatch(1))")') 822 if path =~# '/.' 823 let cmd += ['--',path[1:-1]] 824 endif 825 let grepformat = &grepformat 826 let grepprg = &grepprg 827 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' 828 let dir = getcwd() 829 try 830 execute cd.'`=s:repo().tree()`' 831 let &grepprg = call(s:repo().git_command,cmd,s:repo()) 832 let &grepformat = '%f::%m' 833 exe a:cmd 834 finally 835 let &grepformat = grepformat 836 let &grepprg = grepprg 837 execute cd.'`=dir`' 838 endtry 839 endfunction 840 841 " }}}1 842 " Gedit, Gpedit, Gsplit, Gvsplit, Gtabedit, Gread {{{1 843 844 function! s:Edit(cmd,...) abort 845 if a:0 && a:1 == '' 846 return '' 847 elseif a:0 848 let file = s:buffer().expand(a:1) 849 elseif s:buffer().commit() ==# '' && s:buffer().path('/') !~# '^/.git\>' 850 let file = s:buffer().path(':') 851 else 852 let file = s:buffer().path('/') 853 endif 854 try 855 let file = s:repo().translate(file) 856 catch /^fugitive:/ 857 return 'echoerr v:errmsg' 858 endtry 859 if a:cmd =~# 'read!$' || a:cmd ==# 'read' 860 if a:cmd =~# '!$' 861 call s:warn(':Gread! is deprecated. Use :Gread') 862 endif 863 return 'silent %delete|read '.s:fnameescape(file).'|silent 1delete_|diffupdate|'.line('.') 864 else 865 if &previewwindow && getbufvar('','fugitive_type') ==# 'index' 866 wincmd p 867 endif 868 return a:cmd.' '.s:fnameescape(file) 869 endif 870 endfunction 871 872 function! s:EditComplete(A,L,P) abort 873 return s:repo().superglob(a:A) 874 endfunction 875 876 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Ge :execute s:Edit('edit<bang>',<f-args>)") 877 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gedit :execute s:Edit('edit<bang>',<f-args>)") 878 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gpedit :execute s:Edit('pedit<bang>',<f-args>)") 879 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gsplit :execute s:Edit('split<bang>',<f-args>)") 880 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gvsplit :execute s:Edit('vsplit<bang>',<f-args>)") 881 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gtabedit :execute s:Edit('tabedit<bang>',<f-args>)") 882 call s:command("-bar -bang -nargs=? -count -complete=customlist,s:EditComplete Gread :execute s:Edit((!<count> && <line1> ? '' : <count>).'read<bang>',<f-args>)") 883 884 " }}}1 885 " Gwrite {{{1 886 887 call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gwrite :execute s:Write(<bang>0,<f-args>)") 888 889 function! s:Write(force,...) abort 890 if exists('b:fugitive_commit_arguments') 891 return 'write|bdelete' 892 elseif expand('%:t') == 'COMMIT_EDITMSG' && $GIT_INDEX_FILE != '' 893 return 'wq' 894 elseif s:buffer().type() == 'index' 895 return 'Gcommit' 896 endif 897 let mytab = tabpagenr() 898 let mybufnr = bufnr('') 899 let path = a:0 ? a:1 : s:buffer().path() 900 if path =~# '^:\d\>' 901 return 'write'.(a:force ? '! ' : ' ').s:fnameescape(s:repo().translate(s:buffer().expand(path))) 902 endif 903 let always_permitted = (s:buffer().path() ==# path && s:buffer().commit() =~# '^0\=$') 904 if !always_permitted && !a:force && s:repo().git_chomp_in_tree('diff','--name-status','HEAD','--',path) . s:repo().git_chomp_in_tree('ls-files','--others','--',path) !=# '' 905 let v:errmsg = 'fugitive: file has uncommitted changes (use ! to override)' 906 return 'echoerr v:errmsg' 907 endif 908 let file = s:repo().translate(path) 909 let treebufnr = 0 910 for nr in range(1,bufnr('$')) 911 if fnamemodify(bufname(nr),':p') ==# file 912 let treebufnr = nr 913 endif 914 endfor 915 916 if treebufnr > 0 && treebufnr != bufnr('') 917 let temp = tempname() 918 silent execute '%write '.temp 919 for tab in [mytab] + range(1,tabpagenr('$')) 920 for winnr in range(1,tabpagewinnr(tab,'$')) 921 if tabpagebuflist(tab)[winnr-1] == treebufnr 922 execute 'tabnext '.tab 923 if winnr != winnr() 924 execute winnr.'wincmd w' 925 let restorewinnr = 1 926 endif 927 try 928 let lnum = line('.') 929 let last = line('$') 930 silent execute '$read '.temp 931 silent execute '1,'.last.'delete_' 932 silent write! 933 silent execute lnum 934 let did = 1 935 finally 936 if exists('restorewinnr') 937 wincmd p 938 endif 939 execute 'tabnext '.mytab 940 endtry 941 endif 942 endfor 943 endfor 944 if !exists('did') 945 call writefile(readfile(temp,'b'),file,'b') 946 endif 947 else 948 execute 'write! '.s:fnameescape(s:repo().translate(path)) 949 endif 950 951 if a:force 952 let error = s:repo().git_chomp_in_tree('add', '--force', file) 953 else 954 let error = s:repo().git_chomp_in_tree('add', file) 955 endif 956 if v:shell_error 957 let v:errmsg = 'fugitive: '.error 958 return 'echoerr v:errmsg' 959 endif 960 if s:buffer().path() ==# path && s:buffer().commit() =~# '^\d$' 961 set nomodified 962 endif 963 964 let one = s:repo().translate(':1:'.path) 965 let two = s:repo().translate(':2:'.path) 966 let three = s:repo().translate(':3:'.path) 967 for nr in range(1,bufnr('$')) 968 if bufloaded(nr) && !getbufvar(nr,'&modified') && (bufname(nr) == one || bufname(nr) == two || bufname(nr) == three) 969 execute nr.'bdelete' 970 endif 971 endfor 972 973 unlet! restorewinnr 974 let zero = s:repo().translate(':0:'.path) 975 for tab in range(1,tabpagenr('$')) 976 for winnr in range(1,tabpagewinnr(tab,'$')) 977 let bufnr = tabpagebuflist(tab)[winnr-1] 978 let bufname = bufname(bufnr) 979 if bufname ==# zero && bufnr != mybufnr 980 execute 'tabnext '.tab 981 if winnr != winnr() 982 execute winnr.'wincmd w' 983 let restorewinnr = 1 984 endif 985 try 986 let lnum = line('.') 987 let last = line('$') 988 silent $read `=file` 989 silent execute '1,'.last.'delete_' 990 silent execute lnum 991 set nomodified 992 diffupdate 993 finally 994 if exists('restorewinnr') 995 wincmd p 996 endif 997 execute 'tabnext '.mytab 998 endtry 999 break 1000 endif 1001 endfor 1002 endfor 1003 call fugitive#reload_status() 1004 return 'checktime' 1005 endfunction 1006 1007 " }}}1 1008 " Gdiff {{{1 1009 1010 call s:command("-bang -bar -nargs=? -complete=customlist,s:EditComplete Gdiff :execute s:Diff(<bang>0,<f-args>)") 1011 1012 augroup fugitive_diff 1013 autocmd! 1014 autocmd BufWinLeave * if s:diff_window_count() == 2 && &diff && getbufvar(+expand('<abuf>'), 'git_dir') !=# '' | execute 'windo call s:diff_off()' | endif 1015 autocmd BufWinEnter * if s:diff_window_count() == 1 && &diff && getbufvar(+expand('<abuf>'), 'git_dir') !=# '' | call s:diff_off() | endif 1016 augroup END 1017 1018 function! s:diff_window_count() 1019 let c = 0 1020 for nr in range(1,winnr('$')) 1021 let c += getwinvar(nr,'&diff') 1022 endfor 1023 return c 1024 endfunction 1025 1026 function! s:diff_off() 1027 if &l:diff 1028 diffoff 1029 endif 1030 endfunction 1031 1032 function! s:buffer_compare_age(commit) dict abort 1033 let scores = {':0': 1, ':1': 2, ':2': 3, ':': 4, ':3': 5} 1034 let my_score = get(scores,':'.self.commit(),0) 1035 let their_score = get(scores,':'.a:commit,0) 1036 if my_score || their_score 1037 return my_score < their_score ? -1 : my_score != their_score 1038 elseif self.commit() ==# a:commit 1039 return 0 1040 endif 1041 let base = self.repo().git_chomp('merge-base',self.commit(),a:commit) 1042 if base ==# self.commit() 1043 return -1 1044 elseif base ==# a:commit 1045 return 1 1046 endif 1047 let my_time = +self.repo().git_chomp('log','--max-count=1','--pretty=format:%at',self.commit()) 1048 let their_time = +self.repo().git_chomp('log','--max-count=1','--pretty=format:%at',a:commit) 1049 return my_time < their_time ? -1 : my_time != their_time 1050 endfunction 1051 1052 call s:add_methods('buffer',['compare_age']) 1053 1054 function! s:Diff(bang,...) abort 1055 let split = a:bang ? 'split' : 'vsplit' 1056 if exists(':DiffGitCached') 1057 return 'DiffGitCached' 1058 elseif (!a:0 || a:1 == ':') && s:buffer().commit() =~# '^[0-1]\=$' && s:repo().git_chomp_in_tree('ls-files', '--unmerged', '--', s:buffer().path()) !=# '' 1059 execute 'leftabove '.split.' `=fugitive#buffer().repo().translate(s:buffer().expand('':2''))`' 1060 diffthis 1061 wincmd p 1062 execute 'rightbelow '.split.' `=fugitive#buffer().repo().translate(s:buffer().expand('':3''))`' 1063 diffthis 1064 wincmd p 1065 diffthis 1066 return '' 1067 elseif a:0 1068 if a:1 ==# '' 1069 return '' 1070 elseif a:1 ==# '/' 1071 let file = s:buffer().path('/') 1072 elseif a:1 ==# ':' 1073 let file = s:buffer().path(':0:') 1074 elseif a:1 =~# '^:/' 1075 try 1076 let file = s:repo().rev_parse(a:1).s:buffer().path(':') 1077 catch /^fugitive:/ 1078 return 'echoerr v:errmsg' 1079 endtry 1080 else 1081 let file = s:buffer().expand(a:1) 1082 endif 1083 if file !~# ':' && file !~# '^/' && s:repo().git_chomp('cat-file','-t',file) =~# '^\%(tag\|commit\)$' 1084 let file = file.s:buffer().path(':') 1085 endif 1086 else 1087 let file = s:buffer().path(s:buffer().commit() == '' ? ':0:' : '/') 1088 endif 1089 try 1090 let spec = s:repo().translate(file) 1091 let commit = matchstr(spec,'\C[^:/]//\zs\x\+') 1092 if s:buffer().compare_age(commit) < 0 1093 execute 'rightbelow '.split.' `=spec`' 1094 else 1095 execute 'leftabove '.split.' `=spec`' 1096 endif 1097 diffthis 1098 wincmd p 1099 diffthis 1100 return '' 1101 catch /^fugitive:/ 1102 return 'echoerr v:errmsg' 1103 endtry 1104 endfunction 1105 1106 " }}}1 1107 " Gmove, Gremove {{{1 1108 1109 function! s:Move(force,destination) 1110 if a:destination =~# '^/' 1111 let destination = a:destination[1:-1] 1112 else 1113 let destination = fnamemodify(s:sub(a:destination,'[%#]%(:\w)*','\=expand(submatch(0))'),':p') 1114 if destination[0:strlen(s:repo().tree())] ==# s:repo().tree('') 1115 let destination = destination[strlen(s:repo().tree('')):-1] 1116 endif 1117 endif 1118 let message = call(s:repo().git_chomp_in_tree,['mv']+(a:force ? ['-f'] : [])+['--', s:buffer().path(), destination], s:repo()) 1119 if v:shell_error 1120 let v:errmsg = 'fugitive: '.message 1121 return 'echoerr v:errmsg' 1122 endif 1123 let destination = s:repo().tree(destination) 1124 if isdirectory(destination) 1125 let destination = fnamemodify(s:sub(destination,'/$','').'/'.expand('%:t'),':.') 1126 endif 1127 call fugitive#reload_status() 1128 if s:buffer().commit() == '' 1129 return 'saveas! '.s:fnameescape(destination) 1130 else 1131 return 'file '.s:fnameescape(s:repo().translate(':0:'.destination) 1132 endif 1133 endfunction 1134 1135 function! s:MoveComplete(A,L,P) 1136 if a:A =~ '^/' 1137 return s:repo().superglob(a:A) 1138 else 1139 let matches = split(glob(a:A.'*'),"\n") 1140 call map(matches,'v:val !~ "/$" && isdirectory(v:val) ? v:val."/" : v:val') 1141 return matches 1142 endif 1143 endfunction 1144 1145 function! s:Remove(force) 1146 if s:buffer().commit() ==# '' 1147 let cmd = ['rm'] 1148 elseif s:buffer().commit() ==# '0' 1149 let cmd = ['rm','--cached'] 1150 else 1151 let v:errmsg = 'fugitive: rm not supported here' 1152 return 'echoerr v:errmsg' 1153 endif 1154 if a:force 1155 let cmd += ['--force'] 1156 endif 1157 let message = call(s:repo().git_chomp_in_tree,cmd+['--',s:buffer().path()],s:repo()) 1158 if v:shell_error 1159 let v:errmsg = 'fugitive: '.s:sub(message,'error:.*\zs\n\(.*-f.*',' (add ! to force)') 1160 return 'echoerr '.string(v:errmsg) 1161 else 1162 call fugitive#reload_status() 1163 return 'bdelete'.(a:force ? '!' : '') 1164 endif 1165 endfunction 1166 1167 augroup fugitive_remove 1168 autocmd! 1169 autocmd User Fugitive if s:buffer().commit() =~# '^0\=$' | 1170 \ exe "command! -buffer -bar -bang -nargs=1 -complete=customlist,s:MoveComplete Gmove :execute s:Move(<bang>0,<q-args>)" | 1171 \ exe "command! -buffer -bar -bang Gremove :execute s:Remove(<bang>0)" | 1172 \ endif 1173 augroup END 1174 1175 " }}}1 1176 " Gblame {{{1 1177 1178 augroup fugitive_blame 1179 autocmd! 1180 autocmd BufReadPost *.fugitiveblame setfiletype fugitiveblame 1181 autocmd FileType fugitiveblame setlocal nomodeline | if exists('b:git_dir') | let &l:keywordprg = s:repo().keywordprg() | endif 1182 autocmd Syntax fugitiveblame call s:BlameSyntax() 1183 autocmd User Fugitive if s:buffer().type('file', 'blob') | exe "command! -buffer -bar -bang -range=0 -nargs=* Gblame :execute s:Blame(<bang>0,<line1>,<line2>,<count>,[<f-args>])" | endif 1184 augroup END 1185 1186 function! s:Blame(bang,line1,line2,count,args) abort 1187 try 1188 if s:buffer().path() == '' 1189 call s:throw('file or blob required') 1190 endif 1191 if filter(copy(a:args),'v:val !~# "^\\%(--root\|--show-name\\|-\\=\\%([ltwfs]\\|[MC]\\d*\\)\\+\\)$"') != [] 1192 call s:throw('unsupported option') 1193 endif 1194 call map(a:args,'s:sub(v:val,"^\\ze[^-]","-")') 1195 let git_dir = s:repo().dir() 1196 let cmd = ['--no-pager', 'blame', '--show-number'] + a:args 1197 if s:buffer().commit() =~# '\D\|..' 1198 let cmd += [s:buffer().commit()] 1199 else 1200 let cmd += ['--contents', '-'] 1201 endif 1202 let basecmd = call(s:repo().git_command,cmd+['--',s:buffer().path()],s:repo()) 1203 try 1204 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' 1205 if !s:repo().bare() 1206 let dir = getcwd() 1207 execute cd.'`=s:repo().tree()`' 1208 endif 1209 if a:count 1210 execute 'write !'.substitute(basecmd,' blame ',' blame -L '.a:line1.','.a:line2.' ','g') 1211 else 1212 let error = tempname() 1213 let temp = error.'.fugitiveblame' 1214 if &shell =~# 'csh' 1215 silent! execute '%write !('.basecmd.' > '.temp.') >& '.error 1216 else 1217 silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error 1218 endif 1219 if v:shell_error 1220 call s:throw(join(readfile(error),"\n")) 1221 endif 1222 let bufnr = bufnr('') 1223 let restore = 'call setwinvar(bufwinnr('.bufnr.'),"&scrollbind",0)' 1224 if &l:wrap 1225 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&wrap",1)' 1226 endif 1227 if &l:foldenable 1228 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&foldenable",1)' 1229 endif 1230 let winnr = winnr() 1231 windo set noscrollbind 1232 exe winnr.'wincmd w' 1233 setlocal scrollbind nowrap nofoldenable 1234 let top = line('w0') + &scrolloff 1235 let current = line('.') 1236 exe 'leftabove vsplit '.temp 1237 let b:git_dir = git_dir 1238 let b:fugitive_type = 'blame' 1239 let b:fugitive_blamed_bufnr = bufnr 1240 let w:fugitive_restore = restore 1241 let b:fugitive_blame_arguments = join(a:args,' ') 1242 call s:Detect(expand('%:p')) 1243 execute top 1244 normal! zt 1245 execute current 1246 execute "vertical resize ".(match(getline('.'),'\s\+\d\+)')+1) 1247 setlocal nomodified nomodifiable bufhidden=delete nonumber scrollbind nowrap foldcolumn=0 nofoldenable filetype=fugitiveblame 1248 nnoremap <buffer> <silent> q :<C-U>bdelete<CR> 1249 nnoremap <buffer> <silent> <CR> :<C-U>exe <SID>BlameJump('')<CR> 1250 nnoremap <buffer> <silent> P :<C-U>exe <SID>BlameJump('^'.v:count1)<CR> 1251 nnoremap <buffer> <silent> ~ :<C-U>exe <SID>BlameJump('~'.v:count1)<CR> 1252 nnoremap <buffer> <silent> o :<C-U>exe <SID>Edit((&splitbelow ? "botright" : "topleft")." split", matchstr(getline('.'),'\x\+'))<CR> 1253 nnoremap <buffer> <silent> O :<C-U>exe <SID>Edit("tabedit", matchstr(getline('.'),'\x\+'))<CR> 1254 syncbind 1255 endif 1256 finally 1257 if exists('l:dir') 1258 execute cd.'`=dir`' 1259 endif 1260 endtry 1261 return '' 1262 catch /^fugitive:/ 1263 return 'echoerr v:errmsg' 1264 endtry 1265 endfunction 1266 1267 function! s:BlameJump(suffix) abort 1268 let commit = matchstr(getline('.'),'^\^\=\zs\x\+') 1269 if commit =~# '^0\+$' 1270 let commit = ':0' 1271 endif 1272 let lnum = matchstr(getline('.'),'\d\+\ze\s\+[([:digit:]]') 1273 let path = matchstr(getline('.'),'^\^\=\zs\x\+\s\+\zs.\{-\}\ze\s*\d\+ ') 1274 if path ==# '' 1275 let path = s:buffer(b:fugitive_blamed_bufnr).path() 1276 endif 1277 let args = b:fugitive_blame_arguments 1278 let offset = line('.') - line('w0') 1279 let bufnr = bufnr('%') 1280 let winnr = bufwinnr(b:fugitive_blamed_bufnr) 1281 if winnr > 0 1282 exe winnr.'wincmd w' 1283 endif 1284 execute s:Edit('edit',commit.a:suffix.':'.path) 1285 if winnr > 0 1286 exe bufnr.'bdelete' 1287 endif 1288 execute 'Gblame '.args 1289 execute lnum 1290 let delta = line('.') - line('w0') - offset 1291 if delta > 0 1292 execute 'norm! 'delta."\<C-E>" 1293 elseif delta < 0 1294 execute 'norm! '(-delta)."\<C-Y>" 1295 endif 1296 syncbind 1297 return '' 1298 endfunction 1299 1300 function! s:BlameSyntax() abort 1301 let b:current_syntax = 'fugitiveblame' 1302 syn match FugitiveblameBoundary "^\^" 1303 syn match FugitiveblameBlank "^\s\+\s\@=" nextgroup=FugitiveblameAnnotation,fugitiveblameOriginalFile,FugitiveblameOriginalLineNumber skipwhite 1304 syn match FugitiveblameHash "\%(^\^\=\)\@<=\x\{7,40\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite 1305 syn match FugitiveblameUncommitted "\%(^\^\=\)\@<=0\{7,40\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite 1306 syn region FugitiveblameAnnotation matchgroup=FugitiveblameDelimiter start="(" end="\%( \d\+\)\@<=)" contained keepend oneline 1307 syn match FugitiveblameTime "[0-9:/+-][0-9:/+ -]*[0-9:/+-]\%( \+\d\+)\)\@=" contained containedin=FugitiveblameAnnotation 1308 syn match FugitiveblameLineNumber " \@<=\d\+)\@=" contained containedin=FugitiveblameAnnotation 1309 syn match FugitiveblameOriginalFile " \%(\f\+\D\@<=\|\D\@=\f\+\)\%(\%(\s\+\d\+\)\=\s\%((\|\s*\d\+)\)\)\@=" contained nextgroup=FugitiveblameOriginalLineNumber,FugitiveblameAnnotation skipwhite 1310 syn match FugitiveblameOriginalLineNumber " \@<=\d\+\%(\s(\)\@=" contained nextgroup=FugitiveblameAnnotation skipwhite 1311 syn match FugitiveblameOriginalLineNumber " \@<=\d\+\%(\s\+\d\+)\)\@=" contained nextgroup=FugitiveblameShort skipwhite 1312 syn match FugitiveblameShort "\d\+)" contained contains=FugitiveblameLineNumber 1313 syn match FugitiveblameNotCommittedYet "(\@<=Not Committed Yet\>" contained containedin=FugitiveblameAnnotation 1314 hi def link FugitiveblameBoundary Keyword 1315 hi def link FugitiveblameHash Identifier 1316 hi def link FugitiveblameUncommitted Function 1317 hi def link FugitiveblameTime PreProc 1318 hi def link FugitiveblameLineNumber Number 1319 hi def link FugitiveblameOriginalFile String 1320 hi def link FugitiveblameOriginalLineNumber Float 1321 hi def link FugitiveblameShort FugitiveblameDelimiter 1322 hi def link FugitiveblameDelimiter Delimiter 1323 hi def link FugitiveblameNotCommittedYet Comment 1324 endfunction 1325 1326 " }}}1 1327 " File access {{{1 1328 1329 function! s:ReplaceCmd(cmd,...) abort 1330 let fn = bufname('') 1331 let tmp = tempname() 1332 let aw = &autowrite 1333 let prefix = '' 1334 try 1335 if a:0 && a:1 != '' 1336 if &shell =~# 'cmd' 1337 let old_index = $GIT_INDEX_FILE 1338 let $GIT_INDEX_FILE = a:1 1339 elseif &shell =~# 'csh' 1340 let prefix = 'setenv GIT_INDEX_FILE '.s:shellesc(a:1).'; ' 1341 else 1342 let prefix = 'GIT_INDEX_FILE='.s:shellesc(a:1).' ' 1343 endif 1344 endif 1345 set noautowrite 1346 silent exe '!'.escape(prefix.a:cmd,'%#').' > '.tmp 1347 finally 1348 let &autowrite = aw 1349 if exists('old_index') 1350 let $GIT_INDEX_FILE = old_index 1351 endif 1352 endtry 1353 silent exe 'keepalt file '.tmp 1354 silent edit! 1355 silent exe 'keepalt file '.s:fnameescape(fn) 1356 call delete(tmp) 1357 silent exe 'doau BufReadPost '.s:fnameescape(fn) 1358 endfunction 1359 1360 function! s:BufReadIndex() 1361 if !exists('b:fugitive_display_format') 1362 let b:fugitive_display_format = filereadable(expand('%').'.lock') 1363 endif 1364 let b:fugitive_display_format = b:fugitive_display_format % 2 1365 let b:fugitive_type = 'index' 1366 try 1367 let b:git_dir = s:repo().dir() 1368 setlocal noro ma 1369 if fnamemodify($GIT_INDEX_FILE !=# '' ? $GIT_INDEX_FILE : b:git_dir . '/index', ':p') ==# expand('%:p') 1370 let index = '' 1371 else 1372 let index = expand('%:p') 1373 endif 1374 if b:fugitive_display_format 1375 call s:ReplaceCmd(s:repo().git_command('ls-files','--stage'),index) 1376 set ft=git nospell 1377 else 1378 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' 1379 let dir = getcwd() 1380 try 1381 execute cd.'`=s:repo().tree()`' 1382 call s:ReplaceCmd(s:repo().git_command('status'),index) 1383 finally 1384 execute cd.'`=dir`' 1385 endtry 1386 set ft=gitcommit 1387 endif 1388 setlocal ro noma nomod nomodeline bufhidden=delete 1389 nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += 1<Bar>exe <SID>BufReadIndex()<CR> 1390 nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= 1<Bar>exe <SID>BufReadIndex()<CR> 1391 nnoremap <buffer> <silent> D :<C-U>execute <SID>StageDiff('')<CR> 1392 nnoremap <buffer> <silent> dd :<C-U>execute <SID>StageDiff('')<CR> 1393 nnoremap <buffer> <silent> dh :<C-U>execute <SID>StageDiff('!')<CR> 1394 nnoremap <buffer> <silent> - :<C-U>execute <SID>StageToggle(line('.'),line('.')+v:count1-1)<CR> 1395 xnoremap <buffer> <silent> - :<C-U>execute <SID>StageToggle(line("'<"),line("'>"))<CR> 1396 nnoremap <buffer> <silent> p :<C-U>execute <SID>StagePatch(line('.'),line('.')+v:count1-1)<CR> 1397 xnoremap <buffer> <silent> p :<C-U>execute <SID>StagePatch(line("'<"),line("'>"))<CR> 1398 nnoremap <buffer> <silent> <C-N> :call search('^#\t.*','W')<Bar>.<CR> 1399 nnoremap <buffer> <silent> <C-P> :call search('^#\t.*','Wbe')<Bar>.<CR> 1400 call s:JumpInit() 1401 nunmap <buffer> P 1402 nunmap <buffer> ~ 1403 nnoremap <buffer> <silent> C :<C-U>Gcommit<CR> 1404 catch /^fugitive:/ 1405 return 'echoerr v:errmsg' 1406 endtry 1407 endfunction 1408 1409 function! s:FileRead() 1410 try 1411 let repo = s:repo(s:ExtractGitDir(expand('<amatch>'))) 1412 let path = s:sub(s:sub(matchstr(expand('<amatch>'),'fugitive://.\{-\}//\zs.*'),'/',':'),'^\d:',':&') 1413 let hash = repo.rev_parse(path) 1414 if path =~ '^:' 1415 let type = 'blob' 1416 else 1417 let type = repo.git_chomp('cat-file','-t',hash) 1418 endif 1419 " TODO: use count, if possible 1420 return "read !".escape(repo.git_command('cat-file',type,hash),'%#\') 1421 catch /^fugitive:/ 1422 return 'echoerr v:errmsg' 1423 endtry 1424 endfunction 1425 1426 function! s:BufReadIndexFile() 1427 try 1428 let b:fugitive_type = 'blob' 1429 let b:git_dir = s:repo().dir() 1430 call s:ReplaceCmd(s:repo().git_command('cat-file','blob',s:buffer().sha1())) 1431 return '' 1432 catch /^fugitive: rev-parse/ 1433 silent exe 'doau BufNewFile '.s:fnameescape(bufname('')) 1434 return '' 1435 catch /^fugitive:/ 1436 return 'echoerr v:errmsg' 1437 endtry 1438 endfunction 1439 1440 function! s:BufWriteIndexFile() 1441 let tmp = tempname() 1442 try 1443 let path = matchstr(expand('<amatch>'),'//\d/\zs.*') 1444 let stage = matchstr(expand('<amatch>'),'//\zs\d') 1445 silent execute 'write !'.s:repo().git_command('hash-object','-w','--stdin').' > '.tmp 1446 let sha1 = readfile(tmp)[0] 1447 let old_mode = matchstr(s:repo().git_chomp('ls-files','--stage',path),'^\d\+') 1448 if old_mode == '' 1449 let old_mode = executable(s:repo().tree(path)) ? '100755' : '100644' 1450 endif 1451 let info = old_mode.' '.sha1.' '.stage."\t".path 1452 call writefile([info],tmp) 1453 if has('win32') 1454 let error = system('type '.tmp.'|'.s:repo().git_command('update-index','--index-info')) 1455 else 1456 let error = system(s:repo().git_command('update-index','--index-info').' < '.tmp) 1457 endif 1458 if v:shell_error == 0 1459 setlocal nomodified 1460 silent execute 'doautocmd BufWritePost '.s:fnameescape(expand('%:p')) 1461 call fugitive#reload_status() 1462 return '' 1463 else 1464 return 'echoerr '.string('fugitive: '.error) 1465 endif 1466 finally 1467 call delete(tmp) 1468 endtry 1469 endfunction 1470 1471 function! s:BufReadObject() 1472 try 1473 setlocal noro ma 1474 let b:git_dir = s:repo().dir() 1475 let hash = s:buffer().sha1() 1476 if !exists("b:fugitive_type") 1477 let b:fugitive_type = s:repo().git_chomp('cat-file','-t',hash) 1478 endif 1479 if b:fugitive_type !~# '^\%(tag\|commit\|tree\|blob\)$' 1480 return "echoerr 'fugitive: unrecognized git type'" 1481 endif 1482 let firstline = getline('.') 1483 if !exists('b:fugitive_display_format') && b:fugitive_type != 'blob' 1484 let b:fugitive_display_format = +getbufvar('#','fugitive_display_format') 1485 endif 1486 1487 let pos = getpos('.') 1488 silent %delete 1489 setlocal endofline 1490 1491 if b:fugitive_type == 'tree' 1492 let b:fugitive_display_format = b:fugitive_display_format % 2 1493 if b:fugitive_display_format 1494 call s:ReplaceCmd(s:repo().git_command('ls-tree',hash)) 1495 else 1496 call s:ReplaceCmd(s:repo().git_command('show',hash)) 1497 endif 1498 elseif b:fugitive_type == 'tag' 1499 let b:fugitive_display_format = b:fugitive_display_format % 2 1500 if b:fugitive_display_format 1501 call s:ReplaceCmd(s:repo().git_command('cat-file',b:fugitive_type,hash)) 1502 else 1503 call s:ReplaceCmd(s:repo().git_command('cat-file','-p',hash)) 1504 endif 1505 elseif b:fugitive_type == 'commit' 1506 let b:fugitive_display_format = b:fugitive_display_format % 2 1507 if b:fugitive_display_format 1508 call s:ReplaceCmd(s:repo().git_command('cat-file',b:fugitive_type,hash)) 1509 else 1510 call s:ReplaceCmd(s:repo().git_command('show','--pretty=format:tree %T%nparent %P%nauthor %an <%ae> %ad%ncommitter %cn <%ce> %cd%nencoding %e%n%n%s%n%n%b',hash)) 1511 call search('^parent ') 1512 if getline('.') ==# 'parent ' 1513 silent delete_ 1514 else 1515 silent s/\%(^parent\)\@<! /\rparent /ge 1516 endif 1517 if search('^encoding \%(<unknown>\)\=$','W',line('.')+3) 1518 silent delete_ 1519 end 1520 1 1521 endif 1522 elseif b:fugitive_type ==# 'blob' 1523 call s:ReplaceCmd(s:repo().git_command('cat-file',b:fugitive_type,hash)) 1524 endif 1525 call setpos('.',pos) 1526 setlocal ro noma nomod nomodeline 1527 if b:fugitive_type !=# 'blob' 1528 set filetype=git 1529 nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += v:count1<Bar>exe <SID>BufReadObject()<CR> 1530 nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= v:count1<Bar>exe <SID>BufReadObject()<CR> 1531 else 1532 call s:JumpInit() 1533 endif 1534 1535 return '' 1536 catch /^fugitive:/ 1537 return 'echoerr v:errmsg' 1538 endtry 1539 endfunction 1540 1541 augroup fugitive_files 1542 autocmd! 1543 autocmd BufReadCmd *.git/index exe s:BufReadIndex() 1544 autocmd BufReadCmd *.git/*index*.lock exe s:BufReadIndex() 1545 autocmd FileReadCmd fugitive://**//[0-3]/** exe s:FileRead() 1546 autocmd BufReadCmd fugitive://**//[0-3]/** exe s:BufReadIndexFile() 1547 autocmd BufWriteCmd fugitive://**//[0-3]/** exe s:BufWriteIndexFile() 1548 autocmd BufReadCmd fugitive://**//[0-9a-f][0-9a-f]* exe s:BufReadObject() 1549 autocmd FileReadCmd fugitive://**//[0-9a-f][0-9a-f]* exe s:FileRead() 1550 autocmd FileType git call s:JumpInit() 1551 augroup END 1552 1553 " }}}1 1554 " Go to file {{{1 1555 1556 function! s:JumpInit() abort 1557 nnoremap <buffer> <silent> <CR> :<C-U>exe <SID>GF("edit")<CR> 1558 if !&modifiable 1559 nnoremap <buffer> <silent> o :<C-U>exe <SID>GF("split")<CR> 1560 nnoremap <buffer> <silent> O :<C-U>exe <SID>GF("tabedit")<CR> 1561 nnoremap <buffer> <silent> P :<C-U>exe <SID>Edit('edit',<SID>buffer().commit().'^'.v:count1.<SID>buffer().path(':'))<CR> 1562 nnoremap <buffer> <silent> ~ :<C-U>exe <SID>Edit('edit',<SID>buffer().commit().'~'.v:count1.<SID>buffer().path(':'))<CR> 1563 nnoremap <buffer> <silent> C :<C-U>exe <SID>Edit('edit',<SID>buffer().containing_commit())<CR> 1564 nnoremap <buffer> <silent> cc :<C-U>exe <SID>Edit('edit',<SID>buffer().containing_commit())<CR> 1565 nnoremap <buffer> <silent> co :<C-U>exe <SID>Edit('split',<SID>buffer().containing_commit())<CR> 1566 nnoremap <buffer> <silent> cO :<C-U>exe <SID>Edit('tabedit',<SID>buffer().containing_commit())<CR> 1567 nnoremap <buffer> <silent> cp :<C-U>exe <SID>Edit('pedit',<SID>buffer().containing_commit())<CR> 1568 endif 1569 endfunction 1570 1571 function! s:GF(mode) abort 1572 try 1573 let buffer = s:buffer() 1574 let myhash = buffer.sha1() 1575 1576 if buffer.type('tree') 1577 let showtree = (getline(1) =~# '^tree ' && getline(2) == "") 1578 if showtree && line('.') == 1 1579 return "" 1580 elseif showtree && line('.') > 2 1581 return s:Edit(a:mode,buffer.commit().':'.(buffer.path() == '' ? '' : buffer.path().'/').s:sub(getline('.'),'/$','')) 1582 elseif getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40\}\t' 1583 return s:Edit(a:mode,buffer.commit().':'.(buffer.path() == '' ? '' : buffer.path().'/').s:sub(matchstr(getline('.'),'\t\zs.*'),'/$','')) 1584 endif 1585 1586 elseif buffer.type('blob') 1587 let ref = expand("<cfile>") 1588 try 1589 let sha1 = buffer.repo().rev_parse(ref) 1590 catch /^fugitive:/ 1591 endtry 1592 if exists('sha1') 1593 return s:Edit(a:mode,ref) 1594 endif 1595 1596 else 1597 1598 " Index 1599 if getline('.') =~# '^\d\{6\} \x\{40\} \d\t' 1600 let ref = matchstr(getline('.'),'\x\{40\}') 1601 let file = ':'.s:sub(matchstr(getline('.'),'\d\t.*'),'\t',':') 1602 return s:Edit(a:mode,file) 1603 1604 elseif getline('.') =~# '^#\trenamed:.* -> ' 1605 let file = '/'.matchstr(getline('.'),' -> \zs.*') 1606 return s:Edit(a:mode,file) 1607 elseif getline('.') =~# '^#\t[[:alpha:] ]\+: *.' 1608 let file = '/'.matchstr(getline('.'),': *\zs.*') 1609 return s:Edit(a:mode,file) 1610 elseif getline('.') =~# '^#\t.' 1611 let file = '/'.matchstr(getline('.'),'#\t\zs.*') 1612 return s:Edit(a:mode,file) 1613 elseif getline('.') =~# ': needs merge$' 1614 let file = '/'.matchstr(getline('.'),'.*\ze: needs merge$') 1615 return s:Edit(a:mode,file).'|Gdiff' 1616 1617 elseif getline('.') ==# '# Not currently on any branch.' 1618 return s:Edit(a:mode,'HEAD') 1619 elseif getline('.') =~# '^# On branch ' 1620 let file = 'refs/heads/'.getline('.')[12:] 1621 return s:Edit(a:mode,file) 1622 elseif getline('.') =~# "^# Your branch .*'" 1623 let file = matchstr(getline('.'),"'\\zs\\S\\+\\ze'") 1624 return s:Edit(a:mode,file) 1625 endif 1626 1627 let showtree = (getline(1) =~# '^tree ' && getline(2) == "") 1628 1629 if getline('.') =~# '^ref: ' 1630 let ref = strpart(getline('.'),5) 1631 1632 elseif getline('.') =~# '^parent \x\{40\}\>' 1633 let ref = matchstr(getline('.'),'\x\{40\}') 1634 let line = line('.') 1635 let parent = 0 1636 while getline(line) =~# '^parent ' 1637 let parent += 1 1638 let line -= 1 1639 endwhile 1640 return s:Edit(a:mode,ref) 1641 1642 elseif getline('.') =~ '^tree \x\{40\}$' 1643 let ref = matchstr(getline('.'),'\x\{40\}') 1644 if s:repo().rev_parse(myhash.':') == ref 1645 let ref = myhash.':' 1646 endif 1647 return s:Edit(a:mode,ref) 1648 1649 elseif getline('.') =~# '^object \x\{40\}$' && getline(line('.')+1) =~ '^type \%(commit\|tree\|blob\)$' 1650 let ref = matchstr(getline('.'),'\x\{40\}') 1651 let type = matchstr(getline(line('.')+1),'type \zs.*') 1652 1653 elseif getline('.') =~# '^\l\{3,8\} '.myhash.'$' 1654 return '' 1655 1656 elseif getline('.') =~# '^\l\{3,8\} \x\{40\}\>' 1657 let ref = matchstr(getline('.'),'\x\{40\}') 1658 echoerr "warning: unknown context ".matchstr(getline('.'),'^\l*') 1659 1660 elseif getline('.') =~# '^[+-]\{3\} [ab/]' 1661 let ref = getline('.')[4:] 1662 1663 elseif getline('.') =~# '^rename from ' 1664 let ref = 'a/'.getline('.')[12:] 1665 elseif getline('.') =~# '^rename to ' 1666 let ref = 'b/'.getline('.')[10:] 1667 1668 elseif getline('.') =~# '^diff --git \%(a/.*\|/dev/null\) \%(b/.*\|/dev/null\)' 1669 let dref = matchstr(getline('.'),'\Cdiff --git \zs\%(a/.*\|/dev/null\)\ze \%(b/.*\|/dev/null\)') 1670 let ref = matchstr(getline('.'),'\Cdiff --git \%(a/.*\|/dev/null\) \zs\%(b/.*\|/dev/null\)') 1671 let dcmd = 'Gdiff' 1672 1673 elseif getline('.') =~# '^index ' && getline(line('.')-1) =~# '^diff --git \%(a/.*\|/dev/null\) \%(b/.*\|/dev/null\)' 1674 let line = getline(line('.')-1) 1675 let dref = matchstr(line,'\Cdiff --git \zs\%(a/.*\|/dev/null\)\ze \%(b/.*\|/dev/null\)') 1676 let ref = matchstr(line,'\Cdiff --git \%(a/.*\|/dev/null\) \zs\%(b/.*\|/dev/null\)') 1677 let dcmd = 'Gdiff!' 1678 1679 elseif line('$') == 1 && getline('.') =~ '^\x\{40\}$' 1680 let ref = getline('.') 1681 else 1682 let ref = '' 1683 endif 1684 1685 if myhash ==# '' 1686 let ref = s:sub(ref,'^a/','HEAD:') 1687 let ref = s:sub(ref,'^b/',':0:') 1688 if exists('dref') 1689 let dref = s:sub(dref,'^a/','HEAD:') 1690 endif 1691 else 1692 let ref = s:sub(ref,'^a/',myhash.'^:') 1693 let ref = s:sub(ref,'^b/',myhash.':') 1694 if exists('dref') 1695 let dref = s:sub(dref,'^a/',myhash.'^:') 1696 endif 1697 endif 1698 1699 if ref ==# '/dev/null' 1700 " Empty blob 1701 let ref = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391' 1702 endif 1703 1704 if exists('dref') 1705 return s:Edit(a:mode,ref) . '|'.dcmd.' '.s:fnameescape(dref) 1706 elseif ref != "" 1707 return s:Edit(a:mode,ref) 1708 endif 1709 1710 endif 1711 return '' 1712 catch /^fugitive:/ 1713 return 'echoerr v:errmsg' 1714 endtry 1715 endfunction 1716 1717 " }}}1 1718 " Statusline {{{1 1719 1720 function! s:repo_head_ref() dict abort 1721 return readfile(s:repo().dir('HEAD'))[0] 1722 endfunction 1723 1724 call s:add_methods('repo',['head_ref']) 1725 1726 function! fugitive#statusline(...) 1727 if !exists('b:git_dir') 1728 return '' 1729 endif 1730 let status = '' 1731 if s:buffer().commit() != '' 1732 let status .= ':' . s:buffer().commit()[0:7] 1733 endif 1734 let head = s:repo().head_ref() 1735 if head =~# '^ref: ' 1736 let status .= s:sub(head,'^ref: %(refs/%(heads/|remotes/|tags/)=)=','(').')' 1737 elseif head =~# '^\x\{40\}$' 1738 let status .= '('.head[0:7].')' 1739 endif 1740 if &statusline =~# '%[MRHWY]' && &statusline !~# '%[mrhwy]' 1741 return ',GIT'.status 1742 else 1743 return '[Git'.status.']' 1744 endif 1745 endfunction 1746 1747 function! s:repo_config(conf) dict abort 1748 return matchstr(system(s:repo().git_command('config').' '.a:conf),"[^\r\n]*") 1749 endfun 1750 1751 function! s:repo_user() dict abort 1752 let username = s:repo().config('user.name') 1753 let useremail = s:repo().config('user.email') 1754 return username.' <'.useremail.'>' 1755 endfun 1756 1757 call s:add_methods('repo',['config', 'user']) 1758 1759 " }}}1 1760 1761 " vim:set ft=vim ts=8 sw=2 sts=2: