31 Commits
v2.1 ... v2.2

Author SHA1 Message Date
Tim Pope
34719016ec fugitive.vim 2.2
* Provide :Gpush, :Gfetch, :Gmerge, and Gpull.
* Use -L to handle :Glog range.
* :Gcommit -v opens message in new tab.
* API for custom :Gbrowse handlers.
* Invoke :Browse if available to open URL.
* Colorize hashes in :Gblame buffer.
* Set cursorbind and nobuflisted in :Gblame buffers.
* :Gblame in blame buffer toggles buffer.
2015-01-20 01:37:22 -05:00
Daniel Hahler
2c8461db08 Use <nomodeline> with Fugitive autocmds, and un-silent them
Closes #580.
2014-11-21 03:25:20 -05:00
John Whitley
d3b98d9886 Make configured_tree a caching global function
This implements the changes suggested in tpope/fugitive#415.
s:repo_configured_tree is now a global, s:configured_tree() that caches
the bidirectional relation between the worktree and the git_dir.
extract_git_dir() now uses that relation to check whether the
directories it scans are valid worktrees known by the repo at $GIT_DIR.
2014-11-06 13:49:30 -05:00
Tim Pope
5699f4613c Fix instaweb support
Closes #571.
2014-11-06 13:47:26 -05:00
Tim Pope
0374322ba5 Fix :Glog
Closes #545.
2014-09-02 12:05:34 -04:00
Tim Pope
90ee6fb5d2 Pass line1 and line2 as 0 for :Gbrowse without range
Closes #530.
2014-07-27 12:14:42 -04:00
Tim Pope
04fe4bfcd9 Set nobuflisted in blame buffers 2014-07-23 17:55:15 -04:00
Tom McDonald
7423d72b51 Ensure clipboard support before using * register
Closes #526.
2014-07-23 17:46:47 -04:00
Tim Pope
24d4098ceb Change arity of browse API
It's debatable whether the repo object should be passed at all, so let's
not commit to a positional parameter for it.

References #445.
2014-07-22 20:48:40 -04:00
Tim Pope
5aaa65736d Browse handler API
Taking experimental out of the name, but small tweaks may occur before
then next release.

For future compatibility, any third party handlers should bail and
return an empty string if any of the following are true:

* More than 2 arguments are given.
* The second argument isn't a dictionary.
* The dictionary doesn't contain a "remote" key.

Closes #445.
2014-07-22 00:18:24 -04:00
Tim Pope
5d1c219ee5 Fix load order issue 2014-07-17 21:05:03 -04:00
Tim Pope
a739112bfc Experimentally expose browse API
References #445.
2014-07-17 20:16:28 -04:00
Tim Pope
d376506177 Try including helptags instructions in README 2014-07-17 19:57:09 -04:00
Tim Pope
9af975c82c Don't run pre-commit hook on merge 2014-07-07 21:23:22 -04:00
Tim Pope
11f89ba749 Only change 'tags' if tags file exists
Closes #402.  References #426.
2014-07-06 00:20:19 -04:00
Tim Pope
45e5317200 Try harder to avoid -esp on :Gcommit follow-up
Closes #516.
2014-07-05 19:01:00 -04:00
Tim Pope
ee2b0ecdb8 Provide :Gpush and :Gfetch
Closes #450.
2014-06-30 14:30:44 -04:00
Tim Pope
94a5d6fe2f Fix :Gcommit when closing message lands in different project 2014-06-30 14:02:53 -04:00
Tim Pope
0cd33c6170 :Gcommit -v opens message in new tab
Closes #513.  References #480.
2014-06-29 14:52:43 -04:00
Tim Pope
6239f5ed8e Ignore ^[[K lines from progress output 2014-06-29 14:46:16 -04:00
Tim Pope
188692556a Fix subcommand complete when cursor mid-line 2014-06-29 11:02:47 -04:00
Tim Pope
716f3d2d4e Expose list of global git subcommands 2014-06-29 10:54:57 -04:00
Tim Pope
8576741d61 Don't quote url argument to :Browse 2014-06-26 17:08:59 -04:00
Tim Pope
32957cb552 Invoke :Browse if available to open URL
Example that invokes open(1) on OS X:

    command! -bar -nargs=1 Browse silent! !open <args>

Closes #509.
2014-06-26 17:07:07 -04:00
Tim Pope
fdc8569c18 :Gblame in blame buffer deletes buffer
Closes #511.
2014-06-26 14:40:09 -04:00
Tim Pope
7fb703534a :Gmerge and :Gpull 2014-06-25 13:05:14 -04:00
Tim Pope
41cdbdcd62 Force :Gstatus U to root of tree
References #97.
2014-06-25 10:29:17 -04:00
Tim Pope
91900baad1 Set cursorbind in :Gblame 2014-06-24 23:10:57 -04:00
Tim Pope
fb5661211d Colorize hashes in blame
References #369.
2014-06-24 21:54:25 -04:00
Tim Pope
276f89837f Use -L to handle :Glog range
Closes #507.  References #286.
2014-06-24 19:36:47 -04:00
Tim Pope
4581cd4217 Support -L in :Glog errorformat
References #507.
2014-06-24 19:36:47 -04:00
3 changed files with 376 additions and 93 deletions

View File

@@ -55,15 +55,13 @@ and `Git!` to open the output of a command in a temp file.
## Installation
If you don't have a preferred installation method, I recommend
installing [pathogen.vim](https://github.com/tpope/vim-pathogen), and
then simply copy and paste:
If you don't have a preferred installation method, one option is to install
[pathogen.vim](https://github.com/tpope/vim-pathogen), and then copy
and paste:
cd ~/.vim/bundle
git clone git://github.com/tpope/vim-fugitive.git
Once help tags have been generated, you can view the manual with
`:help fugitive`.
vim -u NONE -c "helptags vim-fugitive/doc" -c q
If your Vim version is below 7.2, I recommend also installing
[vim-git](https://github.com/tpope/vim-git) for syntax highlighting and

View File

@@ -63,12 +63,32 @@ that are part of Git repositories).
to commit, |:Gstatus| is called instead. Unless the
arguments given would skip the invocation of an editor
(e.g., -m), a split window will be used to obtain a
commit message. Write and close that window (:wq or
|:Gwrite|) to finish the commit. Unlike when running
the actual git-commit command, it is possible (but
unadvisable) to muck with the index with commands like
git-add and git-reset while a commit message is
pending.
commit message, or a new tab if -v is given. Write
and close that window (:wq or |:Gwrite|) to finish the
commit. Unlike when running the actual git-commit
command, it is possible (but unadvisable) to alter the
index with commands like git-add and git-reset while a
commit message is pending.
*fugitive-:Gmerge*
:Gmerge [args] Calls git-merge and loads errors and conflicted files
into the quickfix list. Opens a |:Gcommit| style
split window for the commit message if the merge
succeeds. If called during a merge conflict, the
conflicted files from the current index are loaded
into the quickfix list.
*fugitive-:Gpull*
:Gpull [args] Like |:Gmerge|, but for git-pull.
*fugitive-:Gpush*
:Gpush [args] Invoke git-push, load the results into the quickfix
list, and invoke |:cwindow| to reveal any errors.
|:Dispatch| is used if available for asynchronous
invocation.
*fugitive-:Gfetch*
:Gfetch [args] Like |:Gpush|, but for git-fetch.
*fugitive-:Ggrep*
:Ggrep [args] |:grep| with git-grep as 'grepprg'.
@@ -84,6 +104,11 @@ that are part of Git repositories).
previous commits rather than previous file revisions
are loaded.
:{range}Glog [args] Use git-log -L to load previous revisions of the given
range of the current file into the quickfix list. The
cursor is positioned on the first line of the first
diff hunk for each commit.
*fugitive-:Gllog*
:Gllog [args] Like |:Glog|, but use the location list instead of the
quickfix list.

View File

@@ -1,6 +1,6 @@
" fugitive.vim - A Git wrapper so awesome, it should be illegal
" Maintainer: Tim Pope <http://tpo.pe/>
" Version: 2.1
" Version: 2.2
" GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim
if exists('g:loaded_fugitive') || &cp
@@ -145,6 +145,13 @@ function! fugitive#extract_git_dir(path) abort
if root ==# $GIT_WORK_TREE && fugitive#is_git_dir($GIT_DIR)
return $GIT_DIR
endif
if fugitive#is_git_dir($GIT_DIR)
" Ensure that we've cached the worktree
call s:configured_tree($GIT_DIR)
if has_key(s:dir_for_worktree, root)
return s:dir_for_worktree[root]
endif
endif
let dir = s:sub(root, '[\/]$', '') . '/.git'
let type = getftype(dir)
if type ==# 'dir' && fugitive#is_git_dir(dir)
@@ -178,20 +185,34 @@ function! fugitive#detect(path) abort
endif
endif
if exists('b:git_dir')
silent doautocmd User FugitiveBoot
if exists('#User#FugitiveBoot')
try
let [save_mls, &modelines] = [&mls, 0]
doautocmd User FugitiveBoot
finally
let &mls = save_mls
endtry
endif
cnoremap <buffer> <expr> <C-R><C-G> fnameescape(<SID>recall())
nnoremap <buffer> <silent> y<C-G> :call setreg(v:register, <SID>recall())<CR>
let buffer = fugitive#buffer()
if expand('%:p') =~# '//'
call buffer.setvar('&path', s:sub(buffer.getvar('&path'), '^\.%(,|$)', ''))
endif
if stridx(buffer.getvar('&tags'), escape(b:git_dir.'/tags', ', ')) == -1
call buffer.setvar('&tags', escape(b:git_dir.'/tags', ', ').','.buffer.getvar('&tags'))
if &filetype !=# ''
if stridx(buffer.getvar('&tags'), escape(b:git_dir, ', ')) == -1
if filereadable(b:git_dir.'/tags')
call buffer.setvar('&tags', escape(b:git_dir.'/tags', ', ').','.buffer.getvar('&tags'))
endif
if &filetype !=# '' && filereadable(b:git_dir.'/'.&filetype.'.tags')
call buffer.setvar('&tags', escape(b:git_dir.'/'.&filetype.'.tags', ', ').','.buffer.getvar('&tags'))
endif
endif
silent doautocmd User Fugitive
try
let [save_mls, &modelines] = [&mls, 0]
doautocmd User Fugitive
finally
let &mls = save_mls
endtry
endif
endfunction
@@ -209,6 +230,8 @@ augroup END
let s:repo_prototype = {}
let s:repos = {}
let s:worktree_for_dir = {}
let s:dir_for_worktree = {}
function! s:repo(...) abort
let dir = a:0 ? a:1 : (exists('b:git_dir') && b:git_dir !=# '' ? b:git_dir : fugitive#extract_git_dir(expand('%:p')))
@@ -232,21 +255,23 @@ function! s:repo_dir(...) dict abort
return join([self.git_dir]+a:000,'/')
endfunction
function! s:repo_configured_tree() dict abort
if !has_key(self,'_tree')
let self._tree = ''
if filereadable(self.dir('config'))
let config = readfile(self.dir('config'),'',10)
function! s:configured_tree(git_dir) abort
if !has_key(s:worktree_for_dir, a:git_dir)
let s:worktree_for_dir[a:git_dir] = ''
let config_file = a:git_dir . '/config'
if filereadable(config_file)
let config = readfile(config_file,'',10)
call filter(config,'v:val =~# "^\\s*worktree *="')
if len(config) == 1
let self._tree = matchstr(config[0], '= *\zs.*')
let s:worktree_for_dir[a:git_dir] = matchstr(config[0], '= *\zs.*')
let s:dir_for_worktree[s:worktree_for_dir[a:git_dir]] = a:git_dir
endif
endif
endif
if self._tree =~# '^\.'
return simplify(self.dir(self._tree))
if s:worktree_for_dir[a:git_dir] =~# '^\.'
return simplify(a:git_dir . '/' . s:worktree_for_dir[a:git_dir])
else
return self._tree
return s:worktree_for_dir[a:git_dir]
endif
endfunction
@@ -254,7 +279,7 @@ function! s:repo_tree(...) dict abort
if self.dir() =~# '/\.git$'
let dir = self.dir()[0:-6]
else
let dir = self.configured_tree()
let dir = s:configured_tree(self.git_dir)
endif
if dir ==# ''
call s:throw('no work tree')
@@ -267,7 +292,7 @@ function! s:repo_bare() dict abort
if self.dir() =~# '/\.git$'
return 0
else
return self.configured_tree() ==# ''
return s:configured_tree(self.git_dir) ==# ''
endif
endfunction
@@ -332,7 +357,7 @@ function! s:repo_head(...) dict abort
return branch
endfunction
call s:add_methods('repo',['dir','configured_tree','tree','bare','translate','head'])
call s:add_methods('repo',['dir','tree','bare','translate','head'])
function! s:repo_git_command(...) dict abort
let git = g:fugitive_git_executable . ' --git-dir='.s:shellesc(self.git_dir)
@@ -648,15 +673,19 @@ function! s:Git(bang, args) abort
return matchstr(a:args, '\v\C\\@<!%(\\\\)*\|\zs.*')
endfunction
function! s:GitComplete(A,L,P) abort
function! fugitive#git_commands() abort
if !exists('s:exec_path')
let s:exec_path = s:sub(system(g:fugitive_git_executable.' --exec-path'),'\n$','')
endif
let cmds = map(split(glob(s:exec_path.'/git-*'),"\n"),'s:sub(v:val[strlen(s:exec_path)+5 : -1],"\\.exe$","")')
if a:L =~ ' [[:alnum:]-]\+ '
return s:repo().superglob(a:A)
else
return map(split(glob(s:exec_path.'/git-*'),"\n"),'s:sub(v:val[strlen(s:exec_path)+5 : -1],"\\.exe$","")')
endfunction
function! s:GitComplete(A, L, P) abort
if strpart(a:L, 0, a:P) !~# ' [[:alnum:]-]\+ '
let cmds = fugitive#git_commands()
return filter(sort(cmds+keys(s:repo().aliases())), 'strpart(v:val, 0, strlen(a:A)) ==# a:A')
else
return s:repo().superglob(a:A)
endif
endfunction
@@ -792,9 +821,9 @@ function! s:StageUndo() abort
if section ==# 'untracked'
call delete(s:repo().tree(filename))
elseif section ==# 'unstaged'
call repo.git_chomp('checkout', '--', filename)
call repo.git_chomp_in_tree('checkout', '--', filename)
else
call repo.git_chomp('checkout', 'HEAD', '--', filename)
call repo.git_chomp_in_tree('checkout', 'HEAD', '--', filename)
endif
call s:StageReloadSeek(filename, line('.'), line('.'))
let @" = hash
@@ -960,15 +989,16 @@ endfunction
call s:command("-nargs=? -complete=customlist,s:CommitComplete Gcommit :execute s:Commit(<q-args>)")
function! s:Commit(args) abort
function! s:Commit(args, ...) abort
let repo = a:0 ? a:1 : s:repo()
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
let msgfile = s:repo().dir('COMMIT_EDITMSG')
let msgfile = repo.dir('COMMIT_EDITMSG')
let outfile = tempname()
let errorfile = tempname()
try
try
execute cd.s:fnameescape(s:repo().tree())
execute cd.s:fnameescape(repo.tree())
if s:winshell()
let command = ''
let old_editor = $GIT_EDITOR
@@ -976,7 +1006,7 @@ function! s:Commit(args) abort
else
let command = 'env GIT_EDITOR=false '
endif
let command .= s:repo().git_command('commit').' '.a:args
let command .= repo.git_command('commit').' '.a:args
if &shell =~# 'csh'
noautocmd silent execute '!('.command.' > '.outfile.') >& '.errorfile
elseif a:args =~# '\%(^\| \)-\%(-interactive\|p\|-patch\)\>'
@@ -1002,15 +1032,18 @@ function! s:Commit(args) abort
let error = get(errors,-2,get(errors,-1,'!'))
if error =~# 'false''\=\.$'
let args = a:args
let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-[esp]|--edit|--interactive|patch|--signoff)%($| )','')
let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-[esp]|--edit|--interactive|--patch|--signoff)%($| )','')
let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-c|--reedit-message|--reuse-message|-F|--file|-m|--message)%(\s+|\=)%(''[^'']*''|"%(\\.|[^"])*"|\\.|\S)*','')
let args = s:gsub(args,'%(^| )@<=[%#]%(:\w)*','\=expand(submatch(0))')
let args = s:sub(args, '\ze -- |$', ' --no-edit --no-interactive --no-signoff')
let args = '-F '.s:shellesc(msgfile).' '.args
if args !~# '\%(^\| \)--cleanup\>'
let args = '--cleanup=strip '.args
endif
if bufname('%') == '' && line('$') == 1 && getline(1) == '' && !&mod
execute 'keepalt edit '.s:fnameescape(msgfile)
elseif a:args =~# '\%(^\| \)-\%(-verbose\|\w*v\)\>'
execute 'keepalt tabedit '.s:fnameescape(msgfile)
elseif s:buffer().type() ==# 'index'
execute 'keepalt edit '.s:fnameescape(msgfile)
execute (search('^#','n')+1).'wincmd+'
@@ -1052,11 +1085,130 @@ function! s:FinishCommit() abort
let args = getbufvar(+expand('<abuf>'),'fugitive_commit_arguments')
if !empty(args)
call setbufvar(+expand('<abuf>'),'fugitive_commit_arguments','')
return s:Commit(args)
return s:Commit(args, s:repo(getbufvar(+expand('<abuf>'),'git_dir')))
endif
return ''
endfunction
" Section: Gmerge, Gpull
call s:command("-nargs=? -bang -complete=custom,s:RevisionComplete Gmerge " .
\ "execute s:Merge('merge', <bang>0, <q-args>)")
call s:command("-nargs=? -bang -complete=custom,s:RemoteComplete Gpull " .
\ "execute s:Merge('pull --progress', <bang>0, <q-args>)")
function! s:RevisionComplete(A, L, P) abort
return s:repo().git_chomp('rev-parse', '--symbolic', '--branches', '--tags', '--remotes')
\ . "\nHEAD\nFETCH_HEAD\nORIG_HEAD"
endfunction
function! s:RemoteComplete(A, L, P) abort
let remote = matchstr(a:L, ' \zs\S\+\ze ')
if !empty(remote)
let matches = split(s:repo().git_chomp('ls-remote', remote), "\n")
call filter(matches, 'v:val =~# "\t" && v:val !~# "{"')
call map(matches, 's:sub(v:val, "^.*\t%(refs/%(heads/|tags/)=)=", "")')
else
let matches = split(s:repo().git_chomp('remote'), "\n")
endif
return join(matches, "\n")
endfunction
function! fugitive#cwindow() abort
if &buftype == 'quickfix'
cwindow
else
botright cwindow
if &buftype == 'quickfix'
wincmd p
endif
endif
endfunction
let s:common_efm = ''
\ . '%+Egit:%.%#,'
\ . '%+Eusage:%.%#,'
\ . '%+Eerror:%.%#,'
\ . '%+Efatal:%.%#,'
\ . '%-G%.%#%\e[K%.%#,'
\ . '%-G%.%#%\r%.%\+'
function! s:Merge(cmd, bang, args) abort
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
let cwd = getcwd()
let [mp, efm] = [&l:mp, &l:efm]
let had_merge_msg = filereadable(s:repo().dir('MERGE_MSG'))
try
let &l:errorformat = ''
\ . '%-Gerror:%.%#false''.,'
\ . '%-G%.%# ''git commit'' %.%#,'
\ . '%+Emerge:%.%#,'
\ . s:common_efm . ','
\ . '%+ECannot %.%#: You have unstaged changes.,'
\ . '%+ECannot %.%#: Your index contains uncommitted changes.,'
\ . '%+EThere is no tracking information for the current branch.,'
\ . '%+EYou are not currently on a branch. Please specify which,'
\ . 'CONFLICT (%m): %f deleted in %.%#,'
\ . 'CONFLICT (%m): Merge conflict in %f,'
\ . 'CONFLICT (%m): Rename \"%f\"->%.%#,'
\ . 'CONFLICT (%m): Rename %.%#->%f %.%#,'
\ . 'CONFLICT (%m): There is a directory with name %f in %.%#,'
\ . '%+ECONFLICT %.%#,'
\ . '%+EKONFLIKT %.%#,'
\ . '%+ECONFLIT %.%#,'
\ . "%+EXUNG \u0110\u1ed8T %.%#,"
\ . "%+E\u51b2\u7a81 %.%#,"
\ . 'U%\t%f'
if a:cmd =~# '^merge' && empty(a:args) &&
\ (had_merge_msg || isdirectory(s:repo().dir('rebase-apply')) ||
\ !empty(s:repo().git_chomp('diff-files', '--diff-filter=U')))
let &l:makeprg = g:fugitive_git_executable.' diff-files --name-status --diff-filter=U'
else
let &l:makeprg = s:sub(g:fugitive_git_executable.' -c core.editor=false '.
\ a:cmd . (a:args =~# ' \%(--no-edit\|--abort\|-m\)\>' ? '' : ' --edit') . ' ' . a:args,
\ ' *$', '')
endif
if !empty($GIT_EDITOR)
let old_editor = $GIT_EDITOR
let $GIT_EDITOR = 'false'
endif
execute cd fnameescape(s:repo().tree())
silent noautocmd make!
catch /^Vim\%((\a\+)\)\=:E211/
let err = v:exception
finally
redraw!
let [&l:mp, &l:efm] = [mp, efm]
if exists('old_editor')
let $GIT_EDITOR = old_editor
endif
execute cd fnameescape(cwd)
endtry
call fugitive#reload_status()
if empty(filter(getqflist(),'v:val.valid'))
if !had_merge_msg && filereadable(s:repo().dir('MERGE_MSG'))
cclose
return 'Gcommit --no-status -n -t '.s:shellesc(s:repo().dir('MERGE_MSG'))
endif
endif
let qflist = getqflist()
let found = 0
for e in qflist
if !empty(e.bufnr)
let found = 1
let e.pattern = '^<<<<<<<'
endif
endfor
call fugitive#cwindow()
if found
call setqflist(qflist, 'r')
if !a:bang
return 'cfirst'
endif
endif
return exists('err') ? 'echoerr '.string(err) : ''
endfunction
" Section: Ggrep, Glog
if !exists('g:fugitive_summary_format')
@@ -1065,8 +1217,8 @@ endif
call s:command("-bang -nargs=? -complete=customlist,s:EditComplete Ggrep :execute s:Grep('grep',<bang>0,<q-args>)")
call s:command("-bang -nargs=? -complete=customlist,s:EditComplete Glgrep :execute s:Grep('lgrep',<bang>0,<q-args>)")
call s:command("-bar -bang -nargs=* -range=0 -complete=customlist,s:EditComplete Glog :call s:Log('grep<bang>',<count>,<f-args>)")
call s:command("-bar -bang -nargs=* -complete=customlist,s:EditComplete Gllog :call s:Log('lgrep<bang>',<count>,<f-args>)")
call s:command("-bar -bang -nargs=* -range=0 -complete=customlist,s:EditComplete Glog :call s:Log('grep<bang>',<line1>,<count>,<f-args>)")
call s:command("-bar -bang -nargs=* -range=0 -complete=customlist,s:EditComplete Gllog :call s:Log('lgrep<bang>',<line1>,<count>,<f-args>)")
function! s:Grep(cmd,bang,arg) abort
let grepprg = &grepprg
@@ -1107,13 +1259,13 @@ function! s:Grep(cmd,bang,arg) abort
endtry
endfunction
function! s:Log(cmd, count, ...) abort
function! s:Log(cmd, line1, line2, ...) abort
let path = s:buffer().path('/')
if path =~# '^/\.git\%(/\|$\)' || index(a:000,'--') != -1
let path = ''
endif
let cmd = ['--no-pager', 'log', '--no-color']
let cmd += ['--pretty=format:fugitive://'.s:repo().dir().'//%H'.path.':'.(a:count ? a:count : '').'::'.g:fugitive_summary_format]
let cmd += ['--pretty=format:fugitive://'.s:repo().dir().'//%H'.path.'::'.g:fugitive_summary_format]
if empty(filter(a:000[0 : index(a:000,'--')],'v:val !~# "^-"'))
if s:buffer().commit() =~# '\x\{40\}'
let cmd += [s:buffer().commit()]
@@ -1123,7 +1275,11 @@ function! s:Log(cmd, count, ...) abort
end
let cmd += map(copy(a:000),'s:sub(v:val,"^\\%(%(:\\w)*)","\\=fnamemodify(s:buffer().path(),submatch(1))")')
if path =~# '/.'
let cmd += ['--',path[1:-1]]
if a:line2
let cmd += ['-L', a:line1 . ',' . a:line2 . ':' . path[1:-1]]
else
let cmd += ['--', path[1:-1]]
endif
endif
let grepformat = &grepformat
let grepprg = &grepprg
@@ -1132,7 +1288,7 @@ function! s:Log(cmd, count, ...) abort
try
execute cd.'`=s:repo().tree()`'
let &grepprg = escape(call(s:repo().git_command,cmd,s:repo()),'%#')
let &grepformat = '%f:%l::%m,%f:::%m'
let &grepformat = '%Cdiff %.%#,%C--- %.%#,%C+++ %.%#,%Z@@ -%\d%\+\,%\d%\+ +%l\,%\d%\+ @@,%-G-%.%#,%-G+%.%#,%-G %.%#,%A%f::%m,%-G%.%#'
exe a:cmd
finally
let &grepformat = grepformat
@@ -1401,6 +1557,35 @@ augroup fugitive_commit
autocmd VimLeavePre,BufDelete COMMIT_EDITMSG execute s:sub(s:FinishCommit(), '^echoerr (.*)', 'echohl ErrorMsg|echo \1|echohl NONE')
augroup END
" Section: Gpush, Gfetch
call s:command("-nargs=? -bang -complete=custom,s:RemoteComplete Gpush execute s:Dispatch('<bang>', 'push '.<q-args>)")
call s:command("-nargs=? -bang -complete=custom,s:RemoteComplete Gfetch execute s:Dispatch('<bang>', 'fetch '.<q-args>)")
function! s:Dispatch(bang, args)
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
let cwd = getcwd()
let [mp, efm, cc] = [&l:mp, &l:efm, get(b:, 'current_compiler', '')]
try
let b:current_compiler = 'git'
let &l:errorformat = s:common_efm
let &l:makeprg = g:fugitive_git_executable . ' ' . a:args
execute cd fnameescape(s:repo().tree())
if exists(':Make') == 2
noautocmd Make
else
silent noautocmd make!
redraw!
return 'call fugitive#cwindow()'
endif
return ''
finally
let [&l:mp, &l:efm, b:current_compiler] = [mp, efm, cc]
if empty(cc) | unlet! b:current_compiler | endif
execute cd fnameescape(cwd)
endtry
endfunction
" Section: Gdiff
call s:command("-bang -bar -nargs=* -complete=customlist,s:EditComplete Gdiff :execute s:Diff('',<f-args>)")
@@ -1666,6 +1851,7 @@ augroup fugitive_blame
autocmd FileType fugitiveblame setlocal nomodeline | if exists('b:git_dir') | let &l:keywordprg = s:repo().keywordprg() | endif
autocmd Syntax fugitiveblame call s:BlameSyntax()
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
autocmd ColorScheme,GUIEnter * call s:RehighlightBlame()
augroup END
function! s:linechars(pattern) abort
@@ -1679,6 +1865,9 @@ function! s:linechars(pattern) abort
endfunction
function! s:Blame(bang,line1,line2,count,args) abort
if exists('b:fugitive_blamed_bufnr')
return 'bdelete'
endif
try
if s:buffer().path() == ''
call s:throw('file or blob required')
@@ -1720,12 +1909,18 @@ function! s:Blame(bang,line1,line2,count,args) abort
endif
for winnr in range(winnr('$'),1,-1)
call setwinvar(winnr, '&scrollbind', 0)
if exists('+cursorbind')
call setwinvar(winnr, '&cursorbind', 0)
endif
if getbufvar(winbufnr(winnr), 'fugitive_blamed_bufnr')
execute winbufnr(winnr).'bdelete'
endif
endfor
let bufnr = bufnr('')
let restore = 'call setwinvar(bufwinnr('.bufnr.'),"&scrollbind",0)'
if exists('+cursorbind')
let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&cursorbind",0)'
endif
if &l:wrap
let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&wrap",1)'
endif
@@ -1733,6 +1928,9 @@ function! s:Blame(bang,line1,line2,count,args) abort
let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&foldenable",1)'
endif
setlocal scrollbind nowrap nofoldenable
if exists('+cursorbind')
setlocal cursorbind
endif
let top = line('w0') + &scrolloff
let current = line('.')
let s:temp_files[tolower(temp)] = { 'dir': s:repo().dir(), 'args': cmd }
@@ -1743,6 +1941,9 @@ function! s:Blame(bang,line1,line2,count,args) abort
execute top
normal! zt
execute current
if exists('+cursorbind')
setlocal cursorbind
endif
setlocal nomodified nomodifiable nonumber scrollbind nowrap foldcolumn=0 nofoldenable winfixwidth filetype=fugitiveblame
if exists('+concealcursor')
setlocal concealcursor=nc conceallevel=2
@@ -1854,6 +2055,8 @@ function! s:BlameJump(suffix) abort
return ''
endfunction
let s:hash_colors = {}
function! s:BlameSyntax() abort
let b:current_syntax = 'fugitiveblame'
let conceal = has('conceal') ? ' conceal' : ''
@@ -1872,7 +2075,7 @@ function! s:BlameSyntax() abort
syn match FugitiveblameNotCommittedYet "(\@<=Not Committed Yet\>" contained containedin=FugitiveblameAnnotation
hi def link FugitiveblameBoundary Keyword
hi def link FugitiveblameHash Identifier
hi def link FugitiveblameUncommitted Function
hi def link FugitiveblameUncommitted Ignore
hi def link FugitiveblameTime PreProc
hi def link FugitiveblameLineNumber Number
hi def link FugitiveblameOriginalFile String
@@ -1880,6 +2083,37 @@ function! s:BlameSyntax() abort
hi def link FugitiveblameShort FugitiveblameDelimiter
hi def link FugitiveblameDelimiter Delimiter
hi def link FugitiveblameNotCommittedYet Comment
let seen = {}
for lnum in range(1, line('$'))
let hash = matchstr(getline(lnum), '^\^\=\zs\x\{6\}')
if hash ==# '' || hash ==# '000000' || has_key(seen, hash)
continue
endif
let seen[hash] = 1
if &t_Co > 16 && exists('g:CSApprox_loaded')
\ && empty(get(s:hash_colors, hash))
let [s, r, g, b; __] = map(matchlist(hash, '\(\x\x\)\(\x\x\)\(\x\x\)'), 'str2nr(v:val,16)')
let color = csapprox#per_component#Approximate(r, g, b)
if color == 16 && &background ==# 'dark'
let color = 8
endif
let s:hash_colors[hash] = ' ctermfg='.color
else
let s:hash_colors[hash] = ''
endif
exe 'syn match FugitiveblameHash'.hash.' "\%(^\^\=\)\@<='.hash.'\x\{1,34\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite'
endfor
call s:RehighlightBlame()
endfunction
function! s:RehighlightBlame() abort
for [hash, cterm] in items(s:hash_colors)
if !empty(cterm) || has('gui_running')
exe 'hi FugitiveblameHash'.hash.' guifg=#'.hash.get(s:hash_colors, hash, '')
else
exe 'hi link FugitiveblameHash'.hash.' Identifier'
endif
endfor
endfunction
" Section: Gbrowse
@@ -1964,18 +2198,32 @@ function! s:Browse(bang,line1,count,...) abort
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 > 0 ? a:line1 : 0)
endif
for Handler in g:fugitive_browse_handlers
let url = call(Handler, [{
\ 'repo': s:repo(),
\ 'remote': raw,
\ 'revision': rev,
\ 'commit': commit,
\ 'path': path,
\ 'type': type,
\ 'line1': a:count > 0 ? a:line1 : 0,
\ 'line2': a:count > 0 ? a:count : 0}])
if !empty(url)
break
endif
endfor
if url == ''
call s:throw("Instaweb failed to start and '".remote."' is not a GitHub remote")
if empty(url)
call s:throw("Instaweb failed to start and '".remote."' is not a supported remote")
endif
if a:bang
let @* = url
if has('clipboard')
let @* = url
endif
return 'echomsg '.string(url)
elseif exists(':Browse') == 2
return 'echomsg '.string(url).'|Browse '.url
else
return 'echomsg '.string(url).'|call netrw#NetrwBrowseX('.string(url).', 0)'
endif
@@ -1984,24 +2232,27 @@ function! s:Browse(bang,line1,count,...) abort
endtry
endfunction
function! s:github_url(repo,url,rev,commit,path,type,line1,line2) abort
let path = a:path
function! s:github_url(opts, ...) abort
if a:0 || type(a:opts) != type({})
return ''
endif
let domain_pattern = 'github\.com'
let domains = exists('g:fugitive_github_domains') ? g:fugitive_github_domains : []
for domain in domains
let domain_pattern .= '\|' . escape(split(domain, '://')[-1], '.')
endfor
let repo = matchstr(a:url,'^\%(https\=://\|git://\|git@\)\=\zs\('.domain_pattern.'\)[/:].\{-\}\ze\%(\.git\)\=$')
let repo = matchstr(get(a:opts, 'remote'), '^\%(https\=://\|git://\|git@\)\=\zs\('.domain_pattern.'\)[/:].\{-\}\ze\%(\.git\)\=$')
if repo ==# ''
return ''
endif
let path = a:opts.path
if index(domains, 'http://' . matchstr(repo, '^[^:/]*')) >= 0
let root = 'http://' . s:sub(repo,':','/')
else
let root = 'https://' . s:sub(repo,':','/')
endif
if path =~# '^\.git/refs/heads/'
let branch = a:repo.git_chomp('config','branch.'.path[16:-1].'.merge')[11:-1]
let branch = a:opts.repo.git_chomp('config','branch.'.path[16:-1].'.merge')[11:-1]
if branch ==# ''
return root . '/commits/' . path[16:-1]
else
@@ -2014,27 +2265,27 @@ function! s:github_url(repo,url,rev,commit,path,type,line1,line2) abort
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 a:opts.revision =~# '^[[:alnum:]._-]\+:'
let commit = matchstr(a:opts.revision,'^[^:]*')
elseif a:opts.commit =~# '^\d\=$'
let local = matchstr(a:opts.repo.head_ref(),'\<refs/heads/\zs.*')
let commit = a:opts.repo.git_chomp('config','branch.'.local.'.merge')[11:-1]
if commit ==# ''
let commit = local
endif
else
let commit = a:commit
let commit = a:opts.commit
endif
if a:type == 'tree'
if a:opts.type == 'tree'
let url = s:sub(root . '/tree/' . commit . '/' . path,'/$','')
elseif a:type == 'blob'
elseif a:opts.type == 'blob'
let url = root . '/blob/' . commit . '/' . path
if a:line2 > 0 && a:line1 == a:line2
let url .= '#L' . a:line1
elseif a:line2 > 0
let url .= '#L' . a:line1 . '-' . a:line2
if get(a:opts, 'line2') && a:opts.line1 == a:opts.line2
let url .= '#L' . a:opts.line1
elseif get(a:opts, 'line2')
let url .= '#L' . a:opts.line1 . '-' . a:opts.line2
endif
elseif a:type == 'tag'
elseif a:opts.type == 'tag'
let commit = matchstr(getline(3),'^tag \zs.*')
let url = root . '/tree/' . commit
else
@@ -2043,47 +2294,54 @@ function! s:github_url(repo,url,rev,commit,path,type,line1,line2) abort
return url
endfunction
function! s:instaweb_url(repo,rev,commit,path,type,...) abort
let output = a:repo.git_chomp('instaweb','-b','unknown')
function! s:instaweb_url(opts) abort
let output = a:opts.repo.git_chomp('instaweb','-b','unknown')
if output =~# 'http://'
let root = matchstr(output,'http://.*').'/?p='.fnamemodify(a:repo.dir(),':t')
let root = matchstr(output,'http://.*').'/?p='.fnamemodify(a:opts.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\>'
if a:opts.path =~# '^\.git/refs/.'
return root . ';a=shortlog;h=' . matchstr(a:opts.path,'^\.git/\zs.*')
elseif a:opts.path =~# '^\.git\>'
return root
endif
let url = root
if a:commit =~# '^\x\{40\}$'
if a:type ==# 'commit'
if a:opts.commit =~# '^\x\{40\}$'
if a:opts.type ==# 'commit'
let url .= ';a=commit'
endif
let url .= ';h=' . a:repo.rev_parse(a:commit . (a:path == '' ? '' : ':' . a:path))
let url .= ';h=' . a:opts.repo.rev_parse(a:opts.commit . (a:opts.path == '' ? '' : ':' . a:opts.path))
else
if a:type ==# 'blob'
if a:opts.type ==# 'blob'
let tmp = tempname()
silent execute 'write !'.a:repo.git_command('hash-object','-w','--stdin').' > '.tmp
silent execute 'write !'.a:opts.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)
let url .= ';h=' . a:opts.repo.rev_parse((a:opts.commit == '' ? 'HEAD' : ':' . a:opts.commit) . ':' . a:opts.path)
catch /^fugitive:/
call s:throw('fugitive: cannot browse uncommitted file')
endtry
endif
let root .= ';hb=' . matchstr(a:repo.head_ref(),'[^ ]\+$')
let root .= ';hb=' . matchstr(a:opts.repo.head_ref(),'[^ ]\+$')
endif
if a:path !=# ''
let url .= ';f=' . a:path
if a:opts.path !=# ''
let url .= ';f=' . a:opts.path
endif
if a:0 && a:1
let url .= '#l' . a:1
if get(a:opts, 'line1')
let url .= '#l' . a:opts.line1
endif
return url
endfunction
if !exists('g:fugitive_browse_handlers')
let g:fugitive_browse_handlers = []
endif
call extend(g:fugitive_browse_handlers,
\ [s:function('s:github_url'), s:function('s:instaweb_url')])
" Section: File access
function! s:ReplaceCmd(cmd,...) abort
@@ -2260,7 +2518,9 @@ function! s:BufWriteIndexFile() abort
endif
if v:shell_error == 0
setlocal nomodified
silent execute 'doautocmd BufWritePost '.s:fnameescape(expand('%:p'))
if exists('#BufWritePost')
execute 'doautocmd BufWritePost '.s:fnameescape(expand('%:p'))
endif
call fugitive#reload_status()
return ''
else
@@ -2387,7 +2647,7 @@ augroup fugitive_temp
\ let b:git_type = 'temp' |
\ let b:git_args = s:temp_files[tolower(expand('<afile>:p'))].args |
\ call fugitive#detect(expand('<afile>:p')) |
\ setlocal bufhidden=delete |
\ setlocal bufhidden=delete nobuflisted |
\ nnoremap <buffer> <silent> q :<C-U>bdelete<CR>|
\ endif
augroup END