Permalink
Browse files

更新 fugitive 到版本 1.2

  • Loading branch information...
1 parent c4d6cd5 commit 6d07e42999a82c95af8a45b7301dff41a2f6a60a @lilydjwg committed Aug 22, 2011
Showing with 473 additions and 97 deletions.
  1. +45 −8 doc/fugitive.txt
  2. +4 −1 doc/tags
  3. +424 −88 plugin/fugitive.vim
View
53 doc/fugitive.txt
@@ -1,6 +1,6 @@
*fugitive.txt* A Git wrapper so awesome, it should be illegal
-Author: Tim Pope <vimNOSPAM@tpope.info> *fugitive-author*
+Author: Tim Pope <vimNOSPAM@tpope.org> *fugitive-author*
License: Same terms as Vim itself (see |license|)
This plugin is only available if 'compatible' is not set.
@@ -51,10 +51,15 @@ that are part of Git repositories).
*fugitive-:Gstatus*
:Gstatus Bring up the output of git-status in the preview
- window. Press D to |:Gdiff| the file on the cursor
- line. Press - to stage or unstage the file on the
- cursor line. Press p to do so on a per hunk basis
- (--patch). Press C to invoke |:Gcommit|.
+ window. In addition to standard motions, you can
+ use <C-N> and <C-P> to jump from filename to
+ filename. Press C to invoke |:Gcommit|. Press D to
+ |:Gdiff| the file on the cursor line, or ds to
+ |:Gsdiff|. Press - to stage or unstage the file on
+ the cursor line. Press p to do so on a per hunk basis
+ (--patch). All of D, -, and p have a different,
+ sensible (and hopefully intuitive) behavior when
+ invoked on a heading rather than a file name.
*fugitive-:Gcommit*
:Gcommit [args] A wrapper around git-commit. If there is nothing
@@ -102,9 +107,6 @@ that are part of Git repositories).
:{range}Gread [revision]
|:read| in a |fugitive-revision| after {range}.
- *fugitive-:Gread!*
-:Gread! [revision] Deprecated synonym for |:Gread|.
-
*fugitive-:Gwrite*
:Gwrite Write to the current file's path and stage the results.
When run in a work tree file, it is effectively git
@@ -118,6 +120,13 @@ that are part of Git repositories).
:0:foo.txt or even :0 to write to just that stage in
the index.
+ *fugitive-:Gwq*
+:Gwq [path] Like |:Gwrite| followed by |:quit| if the write
+ succeeded.
+
+:Gwq! [path] Like |:Gwrite|! followed by |:quit|! if the write
+ succeeded.
+
*fugitive-:Gdiff*
:Gdiff [revision] Perform a |vimdiff| against the current file in the
given revision. With no argument, the version in the
@@ -127,6 +136,12 @@ that are part of Git repositories).
to the right. Use |do| and |dp| and write to the
index file to simulate "git add --patch".
+ *fugitive-:Gsdiff*
+:Gsdiff [revision] Like |:Gdiff|, but split horizontally.
+
+ *fugitive-:Gvdiff*
+:Gvdiff [revision] Identical to |:Gdiff|. For symmetry with |:Gsdiff|.
+
*fugitive-:Gmove*
:Gmove {destination} Wrapper around git-mv that renames the buffer
afterward. The destination is relative to the current
@@ -148,6 +163,28 @@ that are part of Git repositories).
:[range]Gblame [flags] Run git-blame on the given range.
+ *fugitive-:Gbrowse*
+:[range]Gbrowse If the remote for the current branch is on GitHub,
+ open the current file, blob, tree, commit, or tag
+ (with git-web--browse) on GitHub. Otherwise, open the
+ current file, blob, tree, commit, or tag in
+ git-instaweb (if you have issues, verify you can run
+ "git instaweb" from a terminal). If a range is given,
+ it is appropriately appended to the URL as an anchor.
+
+:[range]Gbrowse! Like :Gbrowse, but put the URL on the clipboard rather
+ than opening it.
+
+:[range]Gbrowse {revision}
+ Like :Gbrowse, but for a given |fugitive-revision|. A
+ useful value here is -, which ties the URL to the
+ latest commit rather than a volatile branch.
+
+:[range]Gbrowse [...]@{remote}
+ Force using the given remote rather than the remote
+ for the current branch. The remote is used to
+ determine which GitHub repository to link to.
+
MAPPINGS *fugitive-mappings*
These maps are available in Git objects.
View
5 doc/tags
@@ -472,6 +472,7 @@ ftp pi_netrw.txt /*ftp*
fugitive fugitive.txt /*fugitive*
fugitive#statusline() fugitive.txt /*fugitive#statusline()*
fugitive-:Gblame fugitive.txt /*fugitive-:Gblame*
+fugitive-:Gbrowse fugitive.txt /*fugitive-:Gbrowse*
fugitive-:Gcd fugitive.txt /*fugitive-:Gcd*
fugitive-:Gcommit fugitive.txt /*fugitive-:Gcommit*
fugitive-:Gdiff fugitive.txt /*fugitive-:Gdiff*
@@ -484,12 +485,14 @@ fugitive-:Glog fugitive.txt /*fugitive-:Glog*
fugitive-:Gmove fugitive.txt /*fugitive-:Gmove*
fugitive-:Gpedit fugitive.txt /*fugitive-:Gpedit*
fugitive-:Gread fugitive.txt /*fugitive-:Gread*
-fugitive-:Gread! fugitive.txt /*fugitive-:Gread!*
fugitive-:Gremove fugitive.txt /*fugitive-:Gremove*
+fugitive-:Gsdiff fugitive.txt /*fugitive-:Gsdiff*
fugitive-:Gsplit fugitive.txt /*fugitive-:Gsplit*
fugitive-:Gstatus fugitive.txt /*fugitive-:Gstatus*
fugitive-:Gtabedit fugitive.txt /*fugitive-:Gtabedit*
+fugitive-:Gvdiff fugitive.txt /*fugitive-:Gvdiff*
fugitive-:Gvsplit fugitive.txt /*fugitive-:Gvsplit*
+fugitive-:Gwq fugitive.txt /*fugitive-:Gwq*
fugitive-:Gwrite fugitive.txt /*fugitive-:Gwrite*
fugitive-<CR> fugitive.txt /*fugitive-<CR>*
fugitive-C fugitive.txt /*fugitive-C*
View
512 plugin/fugitive.vim
@@ -1,6 +1,6 @@
" fugitive.vim - A Git wrapper so awesome, it should be illegal
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
-" Version: 1.1
+" Version: 1.2
" GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim
if exists('g:loaded_fugitive') || &cp
@@ -108,7 +108,7 @@ function! s:ExtractGitDir(path) abort
let ofn = ""
let nfn = fn
while fn != ofn
- if isdirectory(fn . '/.git')
+ if filereadable(fn . '/.git/HEAD')
return s:sub(simplify(fnamemodify(fn . '/.git',':p')),'\W$','')
elseif fn =~ '\.git$' && filereadable(fn . '/HEAD')
return s:sub(simplify(fnamemodify(fn,':p')),'\W$','')
@@ -132,19 +132,25 @@ function! s:Detect(path)
if exists('b:git_dir')
silent doautocmd User Fugitive
cnoremap <expr> <buffer> <C-R><C-G> fugitive#buffer().rev()
+ let buffer = fugitive#buffer()
if expand('%:p') =~# '//'
- let buffer = fugitive#buffer()
call buffer.setvar('&path',s:sub(buffer.getvar('&path'),'^\.%(,|$)',''))
endif
+ if b:git_dir !~# ',' && stridx(buffer.getvar('&tags'),b:git_dir.'/tags') == -1
+ if &filetype != ''
+ call buffer.setvar('&tags',buffer.getvar('&tags').','.b:git_dir.'/'.&filetype.'.tags')
+ endif
+ call buffer.setvar('&tags',buffer.getvar('&tags').','.b:git_dir.'/tags')
+ endif
endif
endfunction
augroup fugitive
autocmd!
autocmd BufNewFile,BufReadPost * call s:Detect(expand('<amatch>:p'))
- autocmd FileType netrw call s:Detect(expand('<amatch>:p'))
+ autocmd FileType netrw call s:Detect(expand('<afile>:p'))
autocmd VimEnter * if expand('<amatch>')==''|call s:Detect(getcwd())|endif
- autocmd BufWinLeave * execute getbufvar(+expand('<abuf>'), 'fugitive_restore')
+ autocmd BufWinLeave * execute getwinvar(+winnr(), 'fugitive_restore')
augroup END
" }}}1
@@ -248,8 +254,8 @@ endfunction
function! s:repo_rev_parse(rev) dict abort
let hash = self.git_chomp('rev-parse','--verify',a:rev)
- if hash =~ '^\x\{40\}$'
- return hash
+ if hash =~ '\<\x\{40\}$'
+ return matchstr(hash,'\<\x\{40\}$')
endif
call s:throw('rev-parse '.a:rev.': '.hash)
endfunction
@@ -351,19 +357,19 @@ endfunction
function! s:buffer_type(...) dict abort
if self.getvar('fugitive_type') != ''
let type = self.getvar('fugitive_type')
- elseif fnamemodify(self.name(),':p') =~# '.\git/refs/\|\.git/\w*HEAD$'
+ elseif fnamemodify(self.spec(),':p') =~# '.\git/refs/\|\.git/\w*HEAD$'
let type = 'head'
elseif self.getline(1) =~ '^tree \x\{40\}$' && self.getline(2) == ''
let type = 'tree'
elseif self.getline(1) =~ '^\d\{6\} \w\{4\} \x\{40\}\>\t'
let type = 'tree'
elseif self.getline(1) =~ '^\d\{6\} \x\{40\}\> \d\t'
let type = 'index'
- elseif isdirectory(self.name())
+ elseif isdirectory(self.spec())
let type = 'directory'
- elseif self.name() == ''
+ elseif self.spec() == ''
let type = 'null'
- elseif filereadable(self.name())
+ elseif filereadable(self.spec())
let type = 'file'
else
let type = ''
@@ -375,42 +381,61 @@ function! s:buffer_type(...) dict abort
endif
endfunction
+if has('win32')
+
+ function! s:buffer_spec() dict abort
+ let bufname = bufname(self['#'])
+ let retval = ''
+ for i in split(bufname,'[^:]\zs\\')
+ let retval = fnamemodify((retval==''?'':retval.'\').i,':.')
+ endfor
+ return s:shellslash(fnamemodify(retval,':p'))
+ endfunction
+
+else
+
+ function! s:buffer_spec() dict abort
+ let bufname = bufname(self['#'])
+ return s:shellslash(bufname == '' ? '' : fnamemodify(bufname,':p'))
+ endfunction
+
+endif
+
function! s:buffer_name() dict abort
- let bufname = bufname(self['#'])
- return s:shellslash(bufname == '' ? '' : fnamemodify(bufname,':p'))
+ return self.spec()
endfunction
function! s:buffer_commit() dict abort
- return matchstr(self.name(),'^fugitive://.\{-\}//\zs\w*')
+ return matchstr(self.spec(),'^fugitive://.\{-\}//\zs\w*')
endfunction
function! s:buffer_path(...) dict abort
- let rev = matchstr(self.name(),'^fugitive://.\{-\}//\zs.*')
+ let rev = matchstr(self.spec(),'^fugitive://.\{-\}//\zs.*')
if rev != ''
let rev = s:sub(rev,'\w*','')
else
- let rev = self.name()[strlen(self.repo().tree()) : -1]
+ let rev = self.spec()[strlen(self.repo().tree()) : -1]
endif
- return s:sub(rev,'^/',a:0 ? a:1 : '')
+ return s:sub(s:sub(rev,'.\zs/$',''),'^/',a:0 ? a:1 : '')
endfunction
function! s:buffer_rev() dict abort
- let rev = matchstr(self.name(),'^fugitive://.\{-\}//\zs.*')
+ let rev = matchstr(self.spec(),'^fugitive://.\{-\}//\zs.*')
if rev =~ '^\x/'
return ':'.rev[0].':'.rev[2:-1]
elseif rev =~ '.'
return s:sub(rev,'/',':')
- elseif self.name() =~ '\.git/index$'
+ elseif self.spec() =~ '\.git/index$'
return ':'
- elseif self.name() =~ '\.git/refs/\|\.git/.*HEAD$'
- return self.name()[strlen(self.repo().dir())+1 : -1]
+ elseif self.spec() =~ '\.git/refs/\|\.git/.*HEAD$'
+ return self.spec()[strlen(self.repo().dir())+1 : -1]
else
return self.path()
endif
endfunction
function! s:buffer_sha1() dict abort
- if self.name() =~ '^fugitive://' || self.name() =~ '\.git/refs/\|\.git/.*HEAD$'
+ if self.spec() =~ '^fugitive://' || self.spec() =~ '\.git/refs/\|\.git/.*HEAD$'
return self.repo().rev_parse(self.rev())
else
return ''
@@ -420,6 +445,8 @@ endfunction
function! s:buffer_expand(rev) dict abort
if a:rev =~# '^:[0-3]$'
let file = a:rev.self.path(':')
+ elseif a:rev =~# '^[-:]/$'
+ let file = '/'.self.path()
elseif a:rev =~# '^-'
let file = 'HEAD^{}'.a:rev[1:-1].self.path(':')
elseif a:rev =~# '^@{'
@@ -430,7 +457,7 @@ function! s:buffer_expand(rev) dict abort
else
let file = a:rev
endif
- return s:sub(file,'\%$',self.path())
+ return s:sub(s:sub(file,'\%$',self.path()),'\.\@<=/$','')
endfunction
function! s:buffer_containing_commit() dict abort
@@ -443,7 +470,7 @@ function! s:buffer_containing_commit() dict abort
endif
endfunction
-call s:add_methods('buffer',['getvar','setvar','getline','repo','type','name','commit','path','rev','sha1','expand','containing_commit'])
+call s:add_methods('buffer',['getvar','setvar','getline','repo','type','spec','name','commit','path','rev','sha1','expand','containing_commit'])
" }}}1
" Git {{{1
@@ -538,24 +565,25 @@ function! fugitive#reload_status() abort
endfor
endfunction
-function! s:StageDiff() abort
+function! s:StageDiff(...) abort
+ let cmd = a:0 ? a:1 : 'Gdiff'
let section = getline(search('^# .*:$','bnW'))
let line = getline('.')
- let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*')
+ let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (new commits)\)\=$')
if filename ==# '' && section == '# Changes to be committed:'
return 'Git diff --cached'
elseif filename ==# ''
return 'Git diff'
elseif line =~# '^#\trenamed:' && filename =~ ' -> '
let [old, new] = split(filename,' -> ')
execute 'Gedit '.s:fnameescape(':0:'.new)
- return 'Gdiff HEAD:'.s:fnameescape(old)
+ return cmd.' HEAD:'.s:fnameescape(old)
elseif section == '# Changes to be committed:'
execute 'Gedit '.s:fnameescape(':0:'.filename)
- return 'Gdiff -'
+ return cmd.' -'
else
execute 'Gedit '.s:fnameescape('/'.filename)
- return 'Gdiff'
+ return cmd
endif
endfunction
@@ -564,10 +592,34 @@ function! s:StageToggle(lnum1,lnum2) abort
let output = ''
for lnum in range(a:lnum1,a:lnum2)
let line = getline(lnum)
- if getline('.') == '# Changes to be committed:'
- return 'Gcommit'
+ let repo = s:repo()
+ if line ==# '# Changes to be committed:'
+ call repo.git_chomp_in_tree('reset','-q')
+ silent! edit!
+ 1
+ if !search('^# Untracked files:$','W')
+ call search('^# Change','W')
+ endif
+ return ''
+ elseif line =~# '^# Change\%(d but not updated\|s not staged for commit\):$'
+ call repo.git_chomp_in_tree('add','-u')
+ silent! edit!
+ 1
+ if !search('^# Untracked files:$','W')
+ call search('^# Change','W')
+ endif
+ return ''
+ elseif line ==# '# Untracked files:'
+ " Work around Vim parser idiosyncrasy
+ call repo.git_chomp_in_tree('add','-N','.')
+ silent! edit!
+ 1
+ if !search('^# Change\%(d but not updated\|s not staged for commit\):$','W')
+ call search('^# Change','W')
+ endif
+ return ''
endif
- let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*')
+ let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (\a\+ [[:alpha:], ]\+)\)\=$')
if filename ==# ''
continue
endif
@@ -586,7 +638,7 @@ function! s:StageToggle(lnum1,lnum2) abort
else
let cmd = ['add','--',filename]
endif
- let output .= call(s:repo().git_chomp_in_tree,cmd,s:repo())."\n"
+ let output .= call(repo.git_chomp_in_tree,cmd,s:repo())."\n"
endfor
if exists('first_filename')
let jump = first_filename
@@ -597,7 +649,7 @@ function! s:StageToggle(lnum1,lnum2) abort
silent! edit!
1
redraw
- call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.jump.'\$','W')
+ call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.jump.'\%( (new commits)\)\=\$','W')
endif
echo s:sub(s:gsub(output,'\n+','\n'),'\n$','')
catch /^fugitive:/
@@ -612,12 +664,12 @@ function! s:StagePatch(lnum1,lnum2) abort
for lnum in range(a:lnum1,a:lnum2)
let line = getline(lnum)
- if line == '# Changes to be committed:'
+ if line ==# '# Changes to be committed:'
return 'Git reset --patch'
- elseif line == '# Changed but not updated:'
+ elseif line =~# '^# Change\%(d but not updated\|s not staged for commit\):$'
return 'Git add --patch'
endif
- let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*')
+ let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (new commits)\)\=$')
if filename ==# ''
continue
endif
@@ -645,7 +697,7 @@ function! s:StagePatch(lnum1,lnum2) abort
silent! edit!
1
redraw
- call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.first_filename.'\$','W')
+ call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.first_filename.'\%( (new commits)\)\=\$','W')
endif
catch /^fugitive:/
return 'echoerr v:errmsg'
@@ -656,7 +708,7 @@ endfunction
" }}}1
" Gcommit {{{1
-call s:command("-nargs=? -complete=customlist,s:CommitComplete Gcommit :execute s:Commit(<q-args>)|startinsert")
+call s:command("-nargs=? -complete=customlist,s:CommitComplete Gcommit :execute s:Commit(<q-args>)")
function! s:Commit(args) abort
let old_type = s:buffer().type()
@@ -667,16 +719,16 @@ function! s:Commit(args) abort
let errorfile = tempname()
try
execute cd.'`=s:repo().tree()`'
- let command = ''
if &shell =~# 'cmd'
+ let command = ''
let old_editor = $GIT_EDITOR
let $GIT_EDITOR = 'false'
- elseif &shell !~# 'csh'
- let command = 'GIT_EDITOR=false '
+ else
+ let command = 'env GIT_EDITOR=false '
endif
let command .= s:repo().git_command('commit').' '.a:args
if &shell =~# 'csh'
- silent execute '!setenv GIT_EDITOR false; ('.command.' > '.outfile.') >& '.errorfile
+ silent execute '!('.command.' > '.outfile.') >& '.errorfile
elseif a:args =~# '\%(^\| \)--interactive\>'
execute '!'.command.' 2> '.errorfile
else
@@ -690,8 +742,9 @@ function! s:Commit(args) abort
endif
return ''
else
- let error = get(readfile(errorfile,'',1),0,'!')
- if error =~# "'false'\\.$"
+ let errors = readfile(errorfile)
+ let error = get(errors,-2,get(errors,-1,'!'))
+ if error =~# '\<false''\=\.$'
let args = a:args
let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-[se]|--edit|--interactive)%($| )','')
let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-F|--file|-m|--message)%(\s+|\=)%(''[^'']*''|"%(\\.|[^"])*"|\\.|\S)*','')
@@ -700,13 +753,14 @@ function! s:Commit(args) abort
if args !~# '\%(^\| \)--cleanup\>'
let args = '--cleanup=strip '.args
endif
+ let old_nr = bufnr('')
if bufname('%') == '' && line('$') == 1 && getline(1) == '' && !&mod
edit `=msgfile`
else
- split `=msgfile`
+ keepalt split `=msgfile`
endif
if old_type ==# 'index'
- bdelete #
+ execute 'bdelete '.old_nr
endif
let b:fugitive_commit_arguments = args
setlocal bufhidden=delete filetype=gitcommit
@@ -741,7 +795,6 @@ endfunction
function! s:FinishCommit()
let args = getbufvar(+expand('<abuf>'),'fugitive_commit_arguments')
- let g:args = args
if !empty(args)
call setbufvar(+expand('<abuf>'),'fugitive_commit_arguments','')
return s:Commit(args)
@@ -751,7 +804,7 @@ endfunction
augroup fugitive_commit
autocmd!
- autocmd BufWritePost *.git/COMMIT_EDITMSG execute s:sub(s:FinishCommit(), '^echoerr (.*)', 'echohl ErrorMsg|echo \1|echohl NONE')
+ autocmd VimLeavePre,BufDelete *.git/COMMIT_EDITMSG execute s:sub(s:FinishCommit(), '^echoerr (.*)', 'echohl ErrorMsg|echo \1|echohl NONE')
augroup END
" }}}1
@@ -772,8 +825,6 @@ function! s:Grep(bang,arg) abort
try
execute cd.'`=s:repo().tree()`'
let &grepprg = s:repo().git_command('--no-pager', 'grep', '-n')
- " 有时指定 --git-dir 会没有输出
- let &grepprg = substitute(&grepprg, '--git-dir=\S\+\s', '', '')
let &grepformat = '%f:%l:%m'
exe 'grep! '.escape(matchstr(a:arg,'\v\C.{-}%($|[''" ]\@=\|)@='),'|')
let list = getqflist()
@@ -851,11 +902,8 @@ function! s:Edit(cmd,...) abort
catch /^fugitive:/
return 'echoerr v:errmsg'
endtry
- if a:cmd =~# 'read!$' || a:cmd ==# 'read'
- if a:cmd =~# '!$'
- call s:warn(':Gread! is deprecated. Use :Gread')
- endif
- return 'silent %delete|read '.s:fnameescape(file).'|silent 1delete_|diffupdate|'.line('.')
+ if a:cmd ==# 'read'
+ return 'silent %delete_|read '.s:fnameescape(file).'|silent 1delete_|diffupdate|'.line('.')
else
if &previewwindow && getbufvar('','fugitive_type') ==# 'index'
wincmd p
@@ -877,9 +925,11 @@ call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gtabedit
call s:command("-bar -bang -nargs=? -count -complete=customlist,s:EditComplete Gread :execute s:Edit((!<count> && <line1> ? '' : <count>).'read<bang>',<f-args>)")
" }}}1
-" Gwrite {{{1
+" Gwrite, Gwq {{{1
call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gwrite :execute s:Write(<bang>0,<f-args>)")
+call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gw :execute s:Write(<bang>0,<f-args>)")
+call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gwq :execute s:Wq(<bang>0,<f-args>)")
function! s:Write(force,...) abort
if exists('b:fugitive_commit_arguments')
@@ -943,7 +993,11 @@ function! s:Write(force,...) abort
execute 'write! '.s:fnameescape(s:repo().translate(path))
endif
- let error = s:repo().git_chomp_in_tree('add', file)
+ if a:force
+ let error = s:repo().git_chomp_in_tree('add', '--force', file)
+ else
+ let error = s:repo().git_chomp_in_tree('add', file)
+ endif
if v:shell_error
let v:errmsg = 'fugitive: '.error
return 'echoerr v:errmsg'
@@ -995,16 +1049,57 @@ function! s:Write(force,...) abort
return 'checktime'
endfunction
+function! s:Wq(force,...) abort
+ let bang = a:force ? '!' : ''
+ if exists('b:fugitive_commit_arguments')
+ return 'wq'.bang
+ endif
+ let result = call(s:function('s:Write'),[a:force]+a:000)
+ if result =~# '^\%(write\|wq\|echoerr\)'
+ return s:sub(result,'^write','wq')
+ else
+ return result.'|quit'.bang
+ endif
+endfunction
+
" }}}1
" Gdiff {{{1
-call s:command("-bar -nargs=? -complete=customlist,s:EditComplete Gdiff :execute s:Diff(<f-args>)")
+call s:command("-bang -bar -nargs=? -complete=customlist,s:EditComplete Gdiff :execute s:Diff(<bang>0,<f-args>)")
+call s:command("-bar -nargs=? -complete=customlist,s:EditComplete Gvdiff :execute s:Diff(0,<f-args>)")
+call s:command("-bar -nargs=? -complete=customlist,s:EditComplete Gsdiff :execute s:Diff(1,<f-args>)")
augroup fugitive_diff
- autocmd BufWinLeave * if winnr('$') == 2 && &diff && getbufvar(+expand('<abuf>'), 'git_dir') !=# '' | diffoff! | endif
- autocmd BufWinEnter * if winnr('$') == 1 && &diff && getbufvar(+expand('<abuf>'), 'git_dir') !=# '' | diffoff | endif
+ autocmd!
+ autocmd BufWinLeave * if s:diff_window_count() == 2 && &diff && getbufvar(+expand('<abuf>'), 'git_dir') !=# '' | call s:diff_off_all(getbufvar(+expand('<abuf>'), 'git_dir')) | endif
+ autocmd BufWinEnter * if s:diff_window_count() == 1 && &diff && getbufvar(+expand('<abuf>'), 'git_dir') !=# '' | diffoff | endif
augroup END
+function! s:diff_window_count()
+ let c = 0
+ for nr in range(1,winnr('$'))
+ let c += getwinvar(nr,'&diff')
+ endfor
+ return c
+endfunction
+
+function! s:diff_off_all(dir)
+ for nr in range(1,winnr('$'))
+ if getwinvar(nr,'&diff')
+ if nr != winnr()
+ execute nr.'wincmd w'
+ let restorewinnr = 1
+ endif
+ if exists('b:git_dir') && b:git_dir ==# a:dir
+ diffoff
+ endif
+ if exists('restorewinnr')
+ wincmd p
+ endif
+ endif
+ endfor
+endfunction
+
function! s:buffer_compare_age(commit) dict abort
let scores = {':0': 1, ':1': 2, ':2': 3, ':': 4, ':3': 5}
let my_score = get(scores,':'.self.commit(),0)
@@ -1027,26 +1122,30 @@ endfunction
call s:add_methods('buffer',['compare_age'])
-function! s:Diff(...) abort
+function! s:Diff(bang,...) abort
+ let split = a:bang ? 'split' : 'vsplit'
if exists(':DiffGitCached')
return 'DiffGitCached'
elseif (!a:0 || a:1 == ':') && s:buffer().commit() =~# '^[0-1]\=$' && s:repo().git_chomp_in_tree('ls-files', '--unmerged', '--', s:buffer().path()) !=# ''
- leftabove vsplit `=fugitive#buffer().repo().translate(s:buffer().expand(':2'))`
- diffthis
- wincmd p
- rightbelow vsplit `=fugitive#buffer().repo().translate(s:buffer().expand(':3'))`
- diffthis
- wincmd p
- diffthis
- return ''
+ let nr = bufnr('')
+ execute 'leftabove '.split.' `=fugitive#buffer().repo().translate(s:buffer().expand('':2''))`'
+ execute 'nnoremap <buffer> <silent> dp :diffput '.nr.'<Bar>diffupdate<CR>'
+ diffthis
+ wincmd p
+ execute 'rightbelow '.split.' `=fugitive#buffer().repo().translate(s:buffer().expand('':3''))`'
+ execute 'nnoremap <buffer> <silent> dp :diffput '.nr.'<Bar>diffupdate<CR>'
+ diffthis
+ wincmd p
+ diffthis
+ return ''
elseif a:0
if a:1 ==# ''
return ''
elseif a:1 ==# '/'
let file = s:buffer().path('/')
elseif a:1 ==# ':'
let file = s:buffer().path(':0:')
- elseif a:1 =~# '^:/'
+ elseif a:1 =~# '^:/.'
try
let file = s:repo().rev_parse(a:1).s:buffer().path(':')
catch /^fugitive:/
@@ -1065,9 +1164,9 @@ function! s:Diff(...) abort
let spec = s:repo().translate(file)
let commit = matchstr(spec,'\C[^:/]//\zs\x\+')
if s:buffer().compare_age(commit) < 0
- rightbelow vsplit `=spec`
+ execute 'rightbelow '.split.' `=spec`'
else
- leftabove vsplit `=spec`
+ execute 'leftabove '.split.' `=spec`'
endif
diffthis
wincmd p
@@ -1090,6 +1189,10 @@ function! s:Move(force,destination)
let destination = destination[strlen(s:repo().tree('')):-1]
endif
endif
+ if isdirectory(s:buffer().name())
+ " Work around Vim parser idiosyncrasy
+ let discarded = s:buffer().setvar('&swapfile',0)
+ endif
let message = call(s:repo().git_chomp_in_tree,['mv']+(a:force ? ['-f'] : [])+['--', s:buffer().path(), destination], s:repo())
if v:shell_error
let v:errmsg = 'fugitive: '.message
@@ -1101,7 +1204,11 @@ function! s:Move(force,destination)
endif
call fugitive#reload_status()
if s:buffer().commit() == ''
- return 'saveas! '.s:fnameescape(destination)
+ if isdirectory(destination)
+ return 'edit '.s:fnameescape(destination)
+ else
+ return 'saveas! '.s:fnameescape(destination)
+ endif
else
return 'file '.s:fnameescape(s:repo().translate(':0:'.destination)
endif
@@ -1186,17 +1293,25 @@ function! s:Blame(bang,line1,line2,count,args) abort
else
let error = tempname()
let temp = error.'.fugitiveblame'
- silent! exe '%write !'.basecmd.' > '.temp.' 2> '.error
+ if &shell =~# 'csh'
+ silent! execute '%write !('.basecmd.' > '.temp.') >& '.error
+ else
+ silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error
+ endif
+ if exists('l:dir')
+ execute cd.'`=dir`'
+ unlet dir
+ endif
if v:shell_error
call s:throw(join(readfile(error),"\n"))
endif
let bufnr = bufnr('')
- let restore = 'call setbufvar('.bufnr.',"&scrollbind",0)'
+ let restore = 'call setwinvar(bufwinnr('.bufnr.'),"&scrollbind",0)'
if &l:wrap
- let restore .= '|call setbufvar('.bufnr.',"&wrap",1)'
+ let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&wrap",1)'
endif
if &l:foldenable
- let restore .= '|call setbufvar('.bufnr.',"&foldenable",1)'
+ let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&foldenable",1)'
endif
let winnr = winnr()
windo set noscrollbind
@@ -1208,7 +1323,7 @@ function! s:Blame(bang,line1,line2,count,args) abort
let b:git_dir = git_dir
let b:fugitive_type = 'blame'
let b:fugitive_blamed_bufnr = bufnr
- let b:fugitive_restore = restore
+ let w:fugitive_restore = restore
let b:fugitive_blame_arguments = join(a:args,' ')
call s:Detect(expand('%:p'))
execute top
@@ -1295,6 +1410,200 @@ function! s:BlameSyntax() abort
endfunction
" }}}1
+" Gbrowse {{{1
+
+call s:command("-bar -bang -count=0 -nargs=? -complete=customlist,s:EditComplete Gbrowse :execute s:Browse(<bang>0,<line1>,<count>,<f-args>)")
+
+function! s:Browse(bang,line1,count,...) abort
+ try
+ let rev = a:0 ? substitute(a:1,'@[[:alnum:]_-]*\%(://.\{-\}\)\=$','','') : ''
+ if rev ==# ''
+ let expanded = s:buffer().rev()
+ elseif rev ==# ':'
+ let expanded = s:buffer().path('/')
+ else
+ let expanded = s:buffer().expand(rev)
+ endif
+ let full = s:repo().translate(expanded)
+ let commit = ''
+ if full =~# '^fugitive://'
+ let commit = matchstr(full,'://.*//\zs\w\+')
+ let path = matchstr(full,'://.*//\w\+\zs/.*')
+ if commit =~ '..'
+ let type = s:repo().git_chomp('cat-file','-t',commit.s:sub(path,'^/',':'))
+ else
+ let type = 'blob'
+ endif
+ let path = path[1:-1]
+ elseif s:repo().bare()
+ let path = '.git/' . full[strlen(s:repo().dir())+1:-1]
+ let type = ''
+ else
+ let path = full[strlen(s:repo().tree())+1:-1]
+ if path =~# '^\.git/'
+ let type = ''
+ elseif isdirectory(full)
+ let type = 'tree'
+ else
+ let type = 'blob'
+ endif
+ endif
+ if path =~# '^\.git/.*HEAD' && filereadable(s:repo().dir(path[5:-1]))
+ let body = readfile(s:repo().dir(path[5:-1]))[0]
+ if body =~# '^\x\{40\}$'
+ let commit = body
+ let type = 'commit'
+ let path = ''
+ elseif body =~# '^ref: refs/'
+ let path = '.git/' . matchstr(body,'ref: \zs.*')
+ endif
+ endif
+
+ if a:0 && a:1 =~# '@[[:alnum:]_-]*\%(://.\{-\}\)\=$'
+ let remote = matchstr(a:1,'@\zs[[:alnum:]_-]\+\%(://.\{-\}\)\=$')
+ elseif path =~# '^\.git/refs/remotes/.'
+ let remote = matchstr(path,'^\.git/refs/remotes/\zs[^/]\+')
+ else
+ let remote = 'origin'
+ let branch = matchstr(rev,'^[[:alnum:]/._-]\+\ze[:^~@]')
+ if branch ==# '' && path =~# '^\.git/refs/\w\+/'
+ let branch = s:sub(path,'^\.git/refs/\w+/','')
+ endif
+ if filereadable(s:repo().dir('refs/remotes/'.branch))
+ let remote = matchstr(branch,'[^/]\+')
+ let rev = rev[strlen(remote)+1:-1]
+ else
+ if branch ==# ''
+ let branch = matchstr(s:repo().head_ref(),'\<refs/heads/\zs.*')
+ endif
+ if branch != ''
+ let remote = s:repo().git_chomp('config','branch.'.branch.'.remote')
+ if remote ==# ''
+ let remote = 'origin'
+ elseif rev[0:strlen(branch)-1] ==# branch && rev[strlen(branch)] =~# '[:^~@]'
+ let rev = s:repo().git_chomp('config','branch.'.branch.'.merge')[11:-1] . rev[strlen(branch):-1]
+ endif
+ endif
+ endif
+ endif
+
+ let raw = s:repo().git_chomp('config','remote.'.remote.'.url')
+ if raw ==# ''
+ let raw = remote
+ endif
+
+ let url = s:github_url(s:repo(),raw,rev,commit,path,type,a:line1,a:count)
+ if url == ''
+ let url = s:instaweb_url(s:repo(),rev,commit,path,type,a:count ? a:line1 : 0)
+ endif
+
+ if url == ''
+ call s:throw("Instaweb failed to start and '".remote."' is not a GitHub remote")
+ endif
+
+ if a:bang
+ let @* = url
+ return 'echomsg '.string(url)
+ else
+ return 'echomsg '.string(url).'|silent Git web--browse '.shellescape(url,1)
+ endif
+ catch /^fugitive:/
+ return 'echoerr v:errmsg'
+ endtry
+endfunction
+
+function! s:github_url(repo,url,rev,commit,path,type,line1,line2) abort
+ let path = a:path
+ let repo_path = matchstr(a:url,'^\%(https\=://\|git://\|git@\)github\.com[/:]\zs.\{-\}\ze\%(\.git\)\=$')
+ if repo_path ==# ''
+ return ''
+ endif
+ let root = 'https://github.com/' . repo_path
+ if path =~# '^\.git/refs/heads/'
+ let branch = a:repo.git_chomp('config','branch.'.path[16:-1].'.merge')[11:-1]
+ if branch ==# ''
+ return root . '/commits/' . path[16:-1]
+ else
+ return root . '/commits/' . branch
+ endif
+ elseif path =~# '^\.git/refs/.'
+ return root . '/commits/' . matchstr(path,'[^/]\+$')
+ elseif path =~# '.git/\%(config$\|hooks\>\)'
+ return root . '/admin'
+ elseif path =~# '^\.git\>'
+ return root
+ endif
+ if a:rev =~# '^[[:alnum:]._-]\+:'
+ let commit = matchstr(a:rev,'^[^:]*')
+ elseif a:commit =~# '^\d\=$'
+ let local = matchstr(a:repo.head_ref(),'\<refs/heads/\zs.*')
+ let commit = a:repo.git_chomp('config','branch.'.local.'.merge')[11:-1]
+ if commit ==# ''
+ let commit = local
+ endif
+ else
+ let commit = a:commit
+ endif
+ if a:type == 'tree'
+ let url = s:sub(root . '/tree/' . commit . '/' . path,'/$','')
+ elseif a:type == 'blob'
+ let url = root . '/blob/' . commit . '/' . path
+ if a:line2 && a:line1 == a:line2
+ let url .= '#L' . a:line1
+ elseif a:line2
+ let url .= '#L' . a:line1 . '-' . a:line2
+ endif
+ elseif a:type == 'tag'
+ let commit = matchstr(getline(3),'^tag \zs.*')
+ let url = root . '/tree/' . commit
+ else
+ let url = root . '/commit/' . commit
+ endif
+ return url
+endfunction
+
+function! s:instaweb_url(repo,rev,commit,path,type,...) abort
+ let output = a:repo.git_chomp('instaweb','-b','unknown')
+ if output =~# 'http://'
+ let root = matchstr(output,'http://.*').'/?p='.fnamemodify(a:repo.dir(),':t')
+ else
+ return ''
+ endif
+ if a:path =~# '^\.git/refs/.'
+ return root . ';a=shortlog;h=' . matchstr(a:path,'^\.git/\zs.*')
+ elseif a:path =~# '^\.git\>'
+ return root
+ endif
+ let url = root
+ if a:commit =~# '^\x\{40\}$'
+ if a:type ==# 'commit'
+ let url .= ';a=commit'
+ endif
+ let url .= ';h=' . a:repo.rev_parse(a:commit . (a:path == '' ? '' : ':' . a:path))
+ else
+ if a:type ==# 'blob'
+ let tmp = tempname()
+ silent execute 'write !'.a:repo.git_command('hash-object','-w','--stdin').' > '.tmp
+ let url .= ';h=' . readfile(tmp)[0]
+ else
+ try
+ let url .= ';h=' . a:repo.rev_parse((a:commit == '' ? 'HEAD' : ':' . a:commit) . ':' . a:path)
+ catch /^fugitive:/
+ call s:throw('fugitive: cannot browse uncommitted file')
+ endtry
+ endif
+ let root .= ';hb=' . matchstr(a:repo.head_ref(),'[^ ]\+$')
+ endif
+ if a:path !=# ''
+ let url .= ';f=' . a:path
+ endif
+ if a:0 && a:1
+ let url .= '#l' . a:1
+ endif
+ return url
+endfunction
+
+" }}}1
" File access {{{1
function! s:ReplaceCmd(cmd,...) abort
@@ -1307,10 +1616,8 @@ function! s:ReplaceCmd(cmd,...) abort
if &shell =~# 'cmd'
let old_index = $GIT_INDEX_FILE
let $GIT_INDEX_FILE = a:1
- elseif &shell =~# 'csh'
- let prefix = 'setenv GIT_INDEX_FILE '.s:shellesc(a:1).'; '
else
- let prefix = 'GIT_INDEX_FILE='.s:shellesc(a:1).' '
+ let prefix = 'env GIT_INDEX_FILE='.s:shellesc(a:1).' '
endif
endif
set noautowrite
@@ -1340,7 +1647,7 @@ function! s:BufReadIndex()
if fnamemodify($GIT_INDEX_FILE !=# '' ? $GIT_INDEX_FILE : b:git_dir . '/index', ':p') ==# expand('%:p')
let index = ''
else
- let index = expand('%')
+ let index = expand('%:p')
endif
if b:fugitive_display_format
call s:ReplaceCmd(s:repo().git_command('ls-files','--stage'),index)
@@ -1360,10 +1667,16 @@ function! s:BufReadIndex()
nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += 1<Bar>exe <SID>BufReadIndex()<CR>
nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= 1<Bar>exe <SID>BufReadIndex()<CR>
nnoremap <buffer> <silent> D :<C-U>execute <SID>StageDiff()<CR>
+ nnoremap <buffer> <silent> dd :<C-U>execute <SID>StageDiff()<CR>
+ nnoremap <buffer> <silent> dh :<C-U>execute <SID>StageDiff('Gsdiff')<CR>
+ nnoremap <buffer> <silent> ds :<C-U>execute <SID>StageDiff('Gsdiff')<CR>
+ nnoremap <buffer> <silent> dv :<C-U>execute <SID>StageDiff()<CR>
nnoremap <buffer> <silent> - :<C-U>execute <SID>StageToggle(line('.'),line('.')+v:count1-1)<CR>
xnoremap <buffer> <silent> - :<C-U>execute <SID>StageToggle(line("'<"),line("'>"))<CR>
nnoremap <buffer> <silent> p :<C-U>execute <SID>StagePatch(line('.'),line('.')+v:count1-1)<CR>
xnoremap <buffer> <silent> p :<C-U>execute <SID>StagePatch(line("'<"),line("'>"))<CR>
+ nnoremap <buffer> <silent> <C-N> :call search('^#\t.*','W')<Bar>.<CR>
+ nnoremap <buffer> <silent> <C-P> :call search('^#\t.*','Wbe')<Bar>.<CR>
call s:JumpInit()
nunmap <buffer> P
nunmap <buffer> ~
@@ -1417,7 +1730,11 @@ function! s:BufWriteIndexFile()
endif
let info = old_mode.' '.sha1.' '.stage."\t".path
call writefile([info],tmp)
- let error = system(s:repo().git_command('update-index','--index-info').' < '.tmp)
+ if has('win32')
+ let error = system('type '.tmp.'|'.s:repo().git_command('update-index','--index-info'))
+ else
+ let error = system(s:repo().git_command('update-index','--index-info').' < '.tmp)
+ endif
if v:shell_error == 0
setlocal nomodified
silent execute 'doautocmd BufWritePost '.s:fnameescape(expand('%:p'))
@@ -1541,9 +1858,9 @@ function! s:GF(mode) abort
if showtree && line('.') == 1
return ""
elseif showtree && line('.') > 2
- return s:Edit(a:mode,buffer.commit().':'.(buffer.path() == '' ? '' : buffer.path().'/').s:sub(getline('.'),'/$',''))
+ return s:Edit(a:mode,buffer.commit().':'.s:buffer().path().(buffer.path() =~# '^$\|/$' ? '' : '/').s:sub(getline('.'),'/$',''))
elseif getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40\}\t'
- return s:Edit(a:mode,buffer.commit().':'.(buffer.path() == '' ? '' : buffer.path().'/').s:sub(matchstr(getline('.'),'\t\zs.*'),'/$',''))
+ return s:Edit(a:mode,buffer.commit().':'.s:buffer().path().(buffer.path() =~# '^$\|/$' ? '' : '/').s:sub(matchstr(getline('.'),'\t\zs.*'),'/$',''))
endif
elseif buffer.type('blob')
@@ -1568,7 +1885,7 @@ function! s:GF(mode) abort
let file = '/'.matchstr(getline('.'),' -> \zs.*')
return s:Edit(a:mode,file)
elseif getline('.') =~# '^#\t[[:alpha:] ]\+: *.'
- let file = '/'.matchstr(getline('.'),': *\zs.*')
+ let file = '/'.matchstr(getline('.'),': *\zs.\{-\}\ze\%( (new commits)\)\=$')
return s:Edit(a:mode,file)
elseif getline('.') =~# '^#\t.'
let file = '/'.matchstr(getline('.'),'#\t\zs.*')
@@ -1631,6 +1948,13 @@ function! s:GF(mode) abort
elseif getline('.') =~# '^diff --git \%(a/.*\|/dev/null\) \%(b/.*\|/dev/null\)'
let dref = matchstr(getline('.'),'\Cdiff --git \zs\%(a/.*\|/dev/null\)\ze \%(b/.*\|/dev/null\)')
let ref = matchstr(getline('.'),'\Cdiff --git \%(a/.*\|/dev/null\) \zs\%(b/.*\|/dev/null\)')
+ let dcmd = 'Gdiff'
+
+ elseif getline('.') =~# '^index ' && getline(line('.')-1) =~# '^diff --git \%(a/.*\|/dev/null\) \%(b/.*\|/dev/null\)'
+ let line = getline(line('.')-1)
+ let dref = matchstr(line,'\Cdiff --git \zs\%(a/.*\|/dev/null\)\ze \%(b/.*\|/dev/null\)')
+ let ref = matchstr(line,'\Cdiff --git \%(a/.*\|/dev/null\) \zs\%(b/.*\|/dev/null\)')
+ let dcmd = 'Gdiff!'
elseif line('$') == 1 && getline('.') =~ '^\x\{40\}$'
let ref = getline('.')
@@ -1658,7 +1982,7 @@ function! s:GF(mode) abort
endif
if exists('dref')
- return s:Edit(a:mode,ref) . '|Gdiff '.s:fnameescape(dref)
+ return s:Edit(a:mode,ref) . '|'.dcmd.' '.s:fnameescape(dref)
elseif ref != ""
return s:Edit(a:mode,ref)
endif
@@ -1700,6 +2024,18 @@ function! fugitive#statusline(...)
endif
endfunction
+function! s:repo_config(conf) dict abort
+ return matchstr(system(s:repo().git_command('config').' '.a:conf),"[^\r\n]*")
+endfun
+
+function! s:repo_user() dict abort
+ let username = s:repo().config('user.name')
+ let useremail = s:repo().config('user.email')
+ return username.' <'.useremail.'>'
+endfun
+
+call s:add_methods('repo',['config', 'user'])
+
" }}}1
" vim:set ft=vim ts=8 sw=2 sts=2:

0 comments on commit 6d07e42

Please sign in to comment.