38 Commits

Author SHA1 Message Date
Tim Pope
ef87434b87 Provide :Git difftool and :Git mergetool
References https://github.com/tpope/vim-fugitive/issues/132
2020-01-05 16:36:11 -05:00
Tim Pope
2bcf755c6e Run QuickFix autocommands on :Gclog 2020-01-05 16:36:11 -05:00
Tim Pope
295f41bbf4 Save diff context in :Gclog 2020-01-05 16:17:58 -05:00
Tim Pope
9ec6b55d03 Improve display of :Gclog --summary 2020-01-05 01:50:24 -05:00
Tim Pope
3604fab3b7 Fix whitespace in README 2020-01-05 01:50:24 -05:00
Tim Pope
6d011f1ea5 Improve :Gclog window title 2020-01-05 00:36:36 -05:00
Tim Pope
726fdf1c94 Support ...commit as HEAD...commit 2020-01-03 19:47:08 -05:00
Tim Pope
3bf602b13d Use underlying commit not tag for :Gedit tag:path 2019-12-27 17:19:42 -05:00
Tim Pope
3729c351e1 Fix unknown function error on Vim < 7.2.061 2019-12-27 17:19:42 -05:00
Tim Pope
7848779d3b Don't error if we can't :bwipeout
References https://github.com/tpope/vim-fugitive/issues/1418
2019-12-27 17:19:42 -05:00
Jon Parise
b68b6d4329 Mention vim-phabricator for Phabricator browsing 2019-12-11 17:49:38 -05:00
Tim Pope
6d4564a05e Fix timer invocation
References https://github.com/tpope/vim-fugitive/issues/1329
2019-12-07 10:14:14 -05:00
Tim Pope
5d37b17e34 Match Git semantics for GIT_CEILING_DIRECTORIES symlink resolution
Also support a generic g:ceiling_directories that can be shared with
other plugins that do this sort of detection.

References https://github.com/tpope/vim-fugitive/issues/1412
2019-12-06 18:03:32 -05:00
Tim Pope
b48a572475 Use timers to avoid Vim patch 8.1.1756
Closes https://github.com/tpope/vim-fugitive/issues/1329
2019-12-06 14:39:27 -05:00
Tim Pope
e1ab8fff09 Handle both amended and new commits during rebase
References https://github.com/tpope/vim-fugitive/issues/1329
2019-12-06 14:39:27 -05:00
Tim Pope
d3a2bdbe2c Don't abort rebase after committing on last step
References https://github.com/tpope/vim-fugitive/issues/1329
2019-12-06 14:12:35 -05:00
Tim Pope
42c6fd1440 Don't override explicit empty string for core.askPass
References https://github.com/tpope/vim-fugitive/issues/1410
2019-12-06 12:56:59 -05:00
Tim Pope
671a85daba Fix erroneous ? escaping in * pattern 2019-11-30 17:19:52 -05:00
Tim Pope
6d9dd103c0 Fix false positive matching filename in blame 2019-11-30 17:15:44 -05:00
Tim Pope
3b1da8b217 Remove debugging function 2019-11-27 16:34:28 -05:00
Tim Pope
67efbf66e0 Don't fall back to pwd for Git dir of blank filename
In particular, this fixes `FugitiveFind(..., '')` to respect the empty
Git dir argument rather than falling back to detection on the current
working directory.  Which in turn fixes :Gstatus using the current
working directory when called from a buffer that does not belong to a
repository.

References https://github.com/tpope/vim-fugitive/issues/1408
2019-11-27 15:27:52 -05:00
Tim Pope
eed46c1f24 Perform automatic :diffoff on :Gdiffsplit /external/file buffers
Closes https://github.com/tpope/vim-fugitive/issues/1402
2019-11-21 22:08:37 -05:00
Tim Pope
dc5320630d Provide explanatory error on :Gdiff ~
References https://github.com/tpope/vim-fugitive/issues/1402
2019-11-19 17:48:43 -05:00
Tim Pope
13fdeb5fa7 Don't show bogus unstaged changes in bare repository
References https://github.com/tpope/vim-fugitive/pull/1262
2019-11-17 22:19:33 -05:00
Omar Sandoval
3936a74584 Reset scrollbind/cursorbind after reblaming
The following sequence of commands leaves the buffer with scrollbind and
cursorbind set:

  :Gblame
  Reblame with one of -, ~, or P
  Quit with gq

What's happening here is:

1. In BlameSubcommand, we set scrollbind and cursorbind on the buffer.
   We also set w:fugitive_leave on the blame buffer to reset scrollbind
   and cursorbind when the blame buffer is closed.
2. In BlameJump, we execute Gedit, which changes the window to a new
   buffer. Then, we delete the blame buffer, at which point we try to
   reset scrollbind and cursorbind. However, the original buffer isn't
   on a window anymore, so this doesn't do anything.
3. In BlameQuit, we go back to the original buffer. Note this snippet
   from `:help local-options`: "if this buffer has been edited in this
   window, the [option] values from back then are used". When the
   original buffer was last used, scrollbind and cursorbind were still
   set. Therefore, the buffer ends up with scrollbind and cursorbind
   set after leaving Gblame.

The fix is to delete the blame buffer _before_ changing to the new
buffer in BlameJump. This ensures that we restore the options while the
original buffer is still around (which is how BlameQuit does it, too).
2019-11-17 21:08:23 -05:00
Tim Pope
a2d76c4583 Fix completion of stash@\{<Tab>
References https://github.com/tpope/vim-fugitive/issues/1396
2019-11-16 19:48:31 -05:00
Farid
fd029d83bf Add built-in package management installation way 2019-11-16 19:48:19 -05:00
Tim Pope
0004f585fd Don't use arrow notation for unstaged modifications
Closes https://github.com/tpope/vim-fugitive/issues/1392
2019-11-15 20:31:29 -05:00
Tim Pope
b24f98fbb1 Cache fugitive#Head() to minimize statusline IO
Closes https://github.com/tpope/vim-fugitive/issues/1384
2019-11-15 20:05:18 -05:00
Tim Pope
8c84ea6fdb Replace --no-patch with -s for Git < 1.8.4
Closes https://github.com/tpope/vim-fugitive/issues/1398
2019-11-12 21:31:36 -05:00
Tim Pope
1e57d678ba Add PayPal donation link 2019-11-08 17:41:09 -05:00
Tim Pope
f6acae50ea Improve robustness of status --porcelain=v1 parsing
References https://github.com/tpope/vim-fugitive/issues/1388
2019-10-27 18:53:19 -04:00
Tim Pope
0356edf6b8 Optimize away log calls in :Gstatus if no upstream changes 2019-10-26 09:05:49 -04:00
Tim Pope
e8c5b604db Improve robustness of status --porcelain=v2 parsing
Closes https://github.com/tpope/vim-fugitive/issues/1388
2019-10-26 08:49:23 -04:00
Tim Pope
b09c5d2523 Resolve symlinks in configured work tree
Closes https://github.com/tpope/vim-fugitive/issues/1382
2019-10-22 05:13:42 -04:00
Tim Pope
395d947644 Force --no-edit on squash maps
References https://github.com/tpope/vim-fugitive/pull/1378
2019-10-20 15:37:41 -04:00
Ben
0a77016ef5 Disable signcolumn during Gblame (#1379) 2019-10-17 18:14:07 -04:00
Tim Pope
06e34204af Fix error message on blank buffer with no Git dir 2019-10-11 17:21:16 -04:00
5 changed files with 457 additions and 106 deletions

1
.github/FUNDING.yml vendored
View File

@@ -1 +1,2 @@
github: tpope github: tpope
custom: ["https://www.paypal.me/vimpope"]

View File

@@ -44,13 +44,15 @@ Use `:Gbrowse` to open the current file on the web front-end of your favorite
hosting provider, with optional line range (try it in visual mode). Plugins hosting provider, with optional line range (try it in visual mode). Plugins
are available for popular providers such as [GitHub][rhubarb.vim], are available for popular providers such as [GitHub][rhubarb.vim],
[GitLab][fugitive-gitlab.vim], [Bitbucket][fubitive.vim], [GitLab][fugitive-gitlab.vim], [Bitbucket][fubitive.vim],
[Gitee][fugitive-gitee.vim], and [Pagure][pagure]. [Gitee][fugitive-gitee.vim], [Pagure][pagure], and
[Phabricator][vim-phabricator].
[rhubarb.vim]: https://github.com/tpope/vim-rhubarb [rhubarb.vim]: https://github.com/tpope/vim-rhubarb
[fugitive-gitlab.vim]: https://github.com/shumphrey/fugitive-gitlab.vim [fugitive-gitlab.vim]: https://github.com/shumphrey/fugitive-gitlab.vim
[fubitive.vim]: https://github.com/tommcdo/vim-fubitive [fubitive.vim]: https://github.com/tommcdo/vim-fubitive
[fugitive-gitee.vim]: https://github.com/linuxsuren/fugitive-gitee.vim [fugitive-gitee.vim]: https://github.com/linuxsuren/fugitive-gitee.vim
[pagure]: https://github.com/FrostyX/vim-fugitive-pagure [pagure]: https://github.com/FrostyX/vim-fugitive-pagure
[vim-phabricator]: https://github.com/jparise/vim-phabricator
Add `%{FugitiveStatusline()}` to `'statusline'` to get an indicator Add `%{FugitiveStatusline()}` to `'statusline'` to get an indicator
with the current branch in your statusline. with the current branch in your statusline.
@@ -69,13 +71,12 @@ For more information, see `:help fugitive`.
## Installation ## Installation
If you don't have a preferred installation method, one option is to install Install using your favorite package manager, or use Vim's built-in package support:
[pathogen.vim](https://github.com/tpope/vim-pathogen), and then copy
and paste:
cd ~/.vim/bundle mkdir -p ~/.vim/pack/tpope/start
git clone https://github.com/tpope/vim-fugitive.git cd ~/.vim/pack/tpope/start
vim -u NONE -c "helptags vim-fugitive/doc" -c q git clone https://tpope.io/vim/fugitive.git
vim -u NONE -c "helptags fugitive/doc" -c q
## FAQ ## FAQ

View File

@@ -76,7 +76,7 @@ function! s:DirCheck(...) abort
if !empty(a:0 ? s:Dir(a:1) : s:Dir()) if !empty(a:0 ? s:Dir(a:1) : s:Dir())
return '' return ''
elseif empty(bufname('')) elseif empty(bufname(''))
return 'return ' . string('echoerr "fugitive: blank buffer unsupported (edit a file from a repository)"') return 'return ' . string('echoerr "fugitive: working directory does not belong to a Git repository"')
else else
return 'return ' . string('echoerr "fugitive: file does not belong to a Git repository"') return 'return ' . string('echoerr "fugitive: file does not belong to a Git repository"')
endif endif
@@ -206,8 +206,11 @@ function! s:QuickfixCreate(nr, opts) abort
endif endif
endfunction endfunction
function! s:QuickfixStream(nr, title, cmd, first, callback, ...) abort function! s:QuickfixStream(nr, event, title, cmd, first, callback, ...) abort
call s:QuickfixCreate(a:nr, {'title': a:title}) let opts = {'title': a:title, 'context': {'items': []}}
call s:QuickfixCreate(a:nr, opts)
let event = (a:nr < 0 ? 'c' : 'l') . 'fugitive-' . a:event
silent exe s:DoAutocmd('QuickFixCmdPre ' . event)
let winnr = winnr() let winnr = winnr()
exe a:nr < 0 ? 'copen' : 'lopen' exe a:nr < 0 ? 'copen' : 'lopen'
if winnr != winnr() if winnr != winnr()
@@ -219,12 +222,20 @@ function! s:QuickfixStream(nr, title, cmd, first, callback, ...) abort
for line in lines for line in lines
call extend(buffer, call(a:callback, a:000 + [line])) call extend(buffer, call(a:callback, a:000 + [line]))
if len(buffer) >= 20 if len(buffer) >= 20
let contexts = map(copy(buffer), 'get(v:val, "context", {})')
lockvar contexts
call extend(opts.context.items, contexts)
unlet contexts
call s:QuickfixSet(a:nr, remove(buffer, 0, -1), 'a') call s:QuickfixSet(a:nr, remove(buffer, 0, -1), 'a')
redraw redraw
endif endif
endfor endfor
call s:QuickfixSet(a:nr, extend(buffer, call(a:callback, a:000 + [0])), 'a') call extend(buffer, call(a:callback, a:000 + [0]))
call extend(opts.context.items, map(copy(buffer), 'get(v:val, "context", {})'))
lockvar opts.context.items
call s:QuickfixSet(a:nr, buffer, 'a')
silent exe s:DoAutocmd('QuickFixCmdPost ' . event)
if a:first && len(s:QuickfixGet(a:nr)) if a:first && len(s:QuickfixGet(a:nr))
call s:BlurStatus() call s:BlurStatus()
return a:nr < 0 ? 'cfirst' : 'lfirst' return a:nr < 0 ? 'cfirst' : 'lfirst'
@@ -489,12 +500,21 @@ function! s:EchoExec(...) abort
return 'checktime' return 'checktime'
endfunction endfunction
let s:head_cache = {}
function! fugitive#Head(...) abort function! fugitive#Head(...) abort
let dir = a:0 > 1 ? a:2 : s:Dir() let dir = a:0 > 1 ? a:2 : s:Dir()
if empty(dir) || !filereadable(fugitive#Find('.git/HEAD', dir)) if empty(dir)
return '' return ''
endif endif
let head = readfile(fugitive#Find('.git/HEAD', dir))[0] let file = fugitive#Find('.git/HEAD', dir)
let ftime = getftime(file)
if ftime == -1
return ''
elseif ftime != get(s:head_cache, dir, [-1])[0]
let s:head_cache[dir] = [ftime, readfile(file)[0]]
endif
let head = s:head_cache[dir][1]
if head =~# '^ref: ' if head =~# '^ref: '
return substitute(head, '\C^ref: \%(refs/\%(heads/\|remotes/\|tags/\)\=\)\=', '', '') return substitute(head, '\C^ref: \%(refs/\%(heads/\|remotes/\|tags/\)\=\)\=', '', '')
elseif head =~# '^\x\{40,\}$' elseif head =~# '^\x\{40,\}$'
@@ -905,8 +925,8 @@ function! fugitive#Find(object, ...) abort
let f = 'fugitive://' . dir . '//0/' . rev[1:-1] let f = 'fugitive://' . dir . '//0/' . rev[1:-1]
else else
if !exists('f') if !exists('f')
let commit = substitute(matchstr(rev, '^[^:.-][^:]*\|^:.*'), '^@\%($\|[~^]\|@{\)\@=', 'HEAD', '') let commit = substitute(matchstr(rev, '^\%([^:.-]\|\.\.[^/:]\)[^:]*\|^:.*'), '^@\%($\|[~^]\|@{\)\@=', 'HEAD', '')
let file = substitute(matchstr(rev, '^[^:.-][^:]*\zs:.*'), '^:', '/', '') let file = substitute(matchstr(rev, '^\%([^:.-]\|\.\.[^/:]\)[^:]*\zs:.*'), '^:', '/', '')
if file =~# '^/\.\.\=\%(/\|$\)\|^//\|^/\a\+:' if file =~# '^/\.\.\=\%(/\|$\)\|^//\|^/\a\+:'
let file = file =~# '^/\.' ? simplify(getcwd() . file) : file[1:-1] let file = file =~# '^/\.' ? simplify(getcwd() . file) : file[1:-1]
if s:cpath(base . '/', (file . '/')[0 : len(base)]) if s:cpath(base . '/', (file . '/')[0 : len(base)])
@@ -925,7 +945,7 @@ function! fugitive#Find(object, ...) abort
let commit = matchstr(s:ChompDefault('', [dir, 'merge-base'] + commits + ['--']), '\<[0-9a-f]\{40,\}\>') let commit = matchstr(s:ChompDefault('', [dir, 'merge-base'] + commits + ['--']), '\<[0-9a-f]\{40,\}\>')
endif endif
if commit !~# '^[0-9a-f]\{40,\}$' if commit !~# '^[0-9a-f]\{40,\}$'
let commit = matchstr(s:ChompDefault('', [dir, 'rev-parse', '--verify', commit, '--']), '\<[0-9a-f]\{40,\}\>') let commit = matchstr(s:ChompDefault('', [dir, 'rev-parse', '--verify', commit . (len(file) ? '^{}' : ''), '--']), '\<[0-9a-f]\{40,\}\>')
endif endif
if len(commit) if len(commit)
let f = 'fugitive://' . dir . '//' . commit . file let f = 'fugitive://' . dir . '//' . commit . file
@@ -1391,10 +1411,11 @@ call s:add_methods('buffer', ['repo', 'type'])
function! s:FilterEscape(items, ...) abort function! s:FilterEscape(items, ...) abort
let items = copy(a:items) let items = copy(a:items)
call map(items, 's:fnameescape(v:val)')
if a:0 && type(a:1) == type('') if a:0 && type(a:1) == type('')
call filter(items, 'strpart(v:val, 0, strlen(a:1)) ==# a:1') call filter(items, 'strpart(v:val, 0, strlen(a:1)) ==# a:1')
endif endif
return map(items, 's:fnameescape(v:val)') return items
endfunction endfunction
function! s:GlobComplete(lead, pattern) abort function! s:GlobComplete(lead, pattern) abort
@@ -1460,16 +1481,15 @@ function! fugitive#CompleteObject(base, ...) abort
let results = [] let results = []
if a:base =~# '^refs/' if a:base =~# '^refs/'
let results += map(s:GlobComplete(fugitive#CommonDir(dir) . '/', a:base . '*'), 's:Slash(v:val)') let results += map(s:GlobComplete(fugitive#CommonDir(dir) . '/', a:base . '*'), 's:Slash(v:val)')
call map(results, 's:fnameescape(v:val)')
elseif a:base !~# '^\.\=/\|^:(' elseif a:base !~# '^\.\=/\|^:('
let heads = s:CompleteHeads(dir) let heads = s:CompleteHeads(dir)
if filereadable(fugitive#Find('.git/refs/stash', dir)) if filereadable(fugitive#Find('.git/refs/stash', dir))
let heads += ["stash"] let heads += ["stash"]
let heads += sort(s:LinesError(["stash","list","--pretty=format:%gd"], dir)[0]) let heads += sort(s:LinesError(["stash","list","--pretty=format:%gd"], dir)[0])
endif endif
call filter(heads,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base') let results += s:FilterEscape(heads, a:base)
let results += heads
endif endif
call map(results, 's:fnameescape(v:val)')
if !empty(tree) if !empty(tree)
let results += a:0 == 1 ? fugitive#CompletePath(a:base, dir) : fugitive#CompletePath(a:base) let results += a:0 == 1 ? fugitive#CompletePath(a:base, dir) : fugitive#CompletePath(a:base)
endif endif
@@ -1550,7 +1570,7 @@ function! s:ReplaceCmd(cmd) abort
endtry endtry
call delete(temp) call delete(temp)
if s:cpath(fnamemodify(bufname('$'), ':p'), temp) if s:cpath(fnamemodify(bufname('$'), ':p'), temp)
silent execute 'bwipeout '.bufnr('$') silent! execute 'bwipeout '.bufnr('$')
endif endif
endtry endtry
endfunction endfunction
@@ -1620,6 +1640,7 @@ function! fugitive#BufReadStatus() abort
let b:fugitive_files = {'Staged': {}, 'Unstaged': {}} let b:fugitive_files = {'Staged': {}, 'Unstaged': {}}
let [staged, unstaged, untracked] = [[], [], []] let [staged, unstaged, untracked] = [[], [], []]
let props = {}
if fugitive#GitVersion(2, 11) if fugitive#GitVersion(2, 11)
let cmd += ['status', '--porcelain=v2', '-bz'] let cmd += ['status', '--porcelain=v2', '-bz']
@@ -1628,21 +1649,13 @@ function! fugitive#BufReadStatus() abort
throw 'fugitive: ' . message throw 'fugitive: ' . message
endif endif
let i = match(output, '^[^#]') let i = 0
let head = matchlist(output[:i], '^# branch\.head \zs.*$')[0]
let pull = get(matchlist(output[:i], '^# branch\.upstream \zs.*$'), 0, '')
if len(pull)
let branch = head
elseif head ==# '(detached)'
let head = matchlist(output[:i], '^# branch\.oid \zs.*$')[0][:10]
let branch = ''
else
let branch = head
endif
while i < len(output) while i < len(output)
let line = output[i] let line = output[i]
if line[0] ==# '?' let prop = matchlist(line, '# \(\S\+\) \(.*\)')
if len(prop)
let props[prop[1]] = prop[2]
elseif line[0] ==# '?'
call add(untracked, {'type': 'File', 'status': line[0], 'filename': line[2:-1]}) call add(untracked, {'type': 'File', 'status': line[0], 'filename': line[2:-1]})
elseif line[0] !=# '#' elseif line[0] !=# '#'
if line[0] ==# 'u' if line[0] ==# 'u'
@@ -1652,11 +1665,14 @@ function! fugitive#BufReadStatus() abort
endif endif
if line[0] ==# '2' if line[0] ==# '2'
let i += 1 let i += 1
let file = output[i] . ' -> ' . matchstr(file, ' \zs.*') let file = matchstr(file, ' \zs.*')
let files = output[i] . ' -> ' . file
else
let files = file
endif endif
let sub = matchstr(line, '^[12u] .. \zs....') let sub = matchstr(line, '^[12u] .. \zs....')
if line[2] !=# '.' if line[2] !=# '.'
call add(staged, {'type': 'File', 'status': line[2], 'filename': file, 'sub': sub}) call add(staged, {'type': 'File', 'status': line[2], 'filename': files, 'sub': sub})
endif endif
if line[3] !=# '.' if line[3] !=# '.'
call add(unstaged, {'type': 'File', 'status': get({'C':'M','M':'?','U':'?'}, matchstr(sub, 'S\.*\zs[CMU]'), line[3]), 'filename': file, 'sub': sub}) call add(unstaged, {'type': 'File', 'status': get({'C':'M','M':'?','U':'?'}, matchstr(sub, 'S\.*\zs[CMU]'), line[3]), 'filename': file, 'sub': sub})
@@ -1664,6 +1680,15 @@ function! fugitive#BufReadStatus() abort
endif endif
let i += 1 let i += 1
endwhile endwhile
let branch = substitute(get(props, 'branch.head', '(unknown)'), '\C^(\%(detached\|unknown\))$', '', '')
if len(branch)
let head = branch
elseif has_key(props, 'branch.oid')
let head = props['branch.oid'][0:10]
else
let head = FugitiveHead(11)
endif
let pull = get(props, 'branch.upstream', '')
else " git < 2.11 else " git < 2.11
let cmd += ['status', '--porcelain', '-bz'] let cmd += ['status', '--porcelain', '-bz']
let [output, message, exec_error] = s:NullError(cmd) let [output, message, exec_error] = s:NullError(cmd)
@@ -1671,6 +1696,9 @@ function! fugitive#BufReadStatus() abort
throw 'fugitive: ' . message throw 'fugitive: ' . message
endif endif
while get(output, 0, '') =~# '^\l\+:'
call remove(output, 0)
endwhile
let head = matchstr(output[0], '^## \zs\S\+\ze\%($\| \[\)') let head = matchstr(output[0], '^## \zs\S\+\ze\%($\| \[\)')
let pull = '' let pull = ''
if head =~# '\.\.\.' if head =~# '\.\.\.'
@@ -1702,11 +1730,15 @@ function! fugitive#BufReadStatus() abort
if line[0:1] ==# '??' if line[0:1] ==# '??'
call add(untracked, {'type': 'File', 'status': line[1], 'filename': files}) call add(untracked, {'type': 'File', 'status': line[1], 'filename': files})
elseif line[1] !~# '[ !#]' elseif line[1] !~# '[ !#]'
call add(unstaged, {'type': 'File', 'status': line[1], 'filename': files, 'sub': ''}) call add(unstaged, {'type': 'File', 'status': line[1], 'filename': file, 'sub': ''})
endif endif
endwhile endwhile
endif endif
if empty(s:Tree())
let [unstaged, untracked] = [[], []]
endif
for dict in staged for dict in staged
let b:fugitive_files['Staged'][dict.filename] = dict let b:fugitive_files['Staged'][dict.filename] = dict
endfor endfor
@@ -1738,12 +1770,12 @@ function! fugitive#BufReadStatus() abort
let push = pull let push = pull
endif endif
if len(pull) if len(pull) && get(props, 'branch.ab') !~# ' -0$'
let unpulled = s:QueryLog(head . '..' . pull) let unpulled = s:QueryLog(head . '..' . pull)
else else
let unpulled = [] let unpulled = []
endif endif
if len(push) if len(push) && !(push ==# pull && get(props, 'branch.ab') =~# '^+0 ')
let unpushed = s:QueryLog(push . '..' . head) let unpushed = s:QueryLog(push . '..' . head)
else else
let unpushed = [] let unpushed = []
@@ -1803,6 +1835,9 @@ function! fugitive#BufReadStatus() abort
if push !=# pull if push !=# pull
call s:AddHeader('Push', push) call s:AddHeader('Push', push)
endif endif
if empty(s:Tree())
call s:AddHeader('Bare', 'yes')
endif
call s:AddSection('Rebasing ' . rebasing_head, rebasing) call s:AddSection('Rebasing ' . rebasing_head, rebasing)
call s:AddSection('Untracked', untracked) call s:AddSection('Untracked', untracked)
call s:AddSection('Unstaged', unstaged) call s:AddSection('Unstaged', unstaged)
@@ -2136,7 +2171,10 @@ function! fugitive#Command(line1, line2, range, bang, mods, arg) abort
if exists('*s:' . name . 'Subcommand') && get(args, 1, '') !=# '--help' if exists('*s:' . name . 'Subcommand') && get(args, 1, '') !=# '--help'
try try
exe s:DirCheck(dir) exe s:DirCheck(dir)
return 'exe ' . string(s:{name}Subcommand(a:line1, a:line2, a:range, a:bang, a:mods, args[1:-1])) . after let result = s:{name}Subcommand(a:line1, a:line2, a:range, a:bang, a:mods, args[1:-1])
if type(result) == type('')
return 'exe ' . string(result) . after
endif
catch /^fugitive:/ catch /^fugitive:/
return 'echoerr ' . string(v:exception) return 'echoerr ' . string(v:exception)
endtry endtry
@@ -2391,22 +2429,20 @@ endif
function! s:ExpireStatus(bufnr) abort function! s:ExpireStatus(bufnr) abort
if a:bufnr == -2 if a:bufnr == -2
let s:head_cache = {}
let s:last_time = reltime() let s:last_time = reltime()
return '' return ''
endif endif
let dir = s:Dir(a:bufnr) let dir = s:Dir(a:bufnr)
if len(dir) if len(dir)
let s:last_times[s:cpath(dir)] = reltime() let s:last_times[s:cpath(dir)] = reltime()
if has_key(s:head_cache, dir)
call remove(s:head_cache, dir)
endif
endif endif
return '' return ''
endfunction endfunction
function! FugitiveReloadCheck() abort
let t = b:fugitive_reltime
return [t, reltimestr(reltime(s:last_time, t)),
\ reltimestr(reltime(get(s:last_times, s:cpath(s:Dir()), t), t))]
endfunction
function! s:ReloadWinStatus(...) abort function! s:ReloadWinStatus(...) abort
if get(b:, 'fugitive_type', '') !=# 'index' || &modified if get(b:, 'fugitive_type', '') !=# 'index' || &modified
return return
@@ -2885,9 +2921,9 @@ function! s:PatchSearchExpr(reverse) abort
let pattern = '^[+-]\s*' . escape(substitute(strpart(line, 1), '^\s*\|\s*$', '', ''), '^$.*[]~\') . '\s*$' let pattern = '^[+-]\s*' . escape(substitute(strpart(line, 1), '^\s*\|\s*$', '', ''), '^$.*[]~\') . '\s*$'
endif endif
if a:reverse if a:reverse
return '?' . escape(pattern, '/') . "\<CR>" return '?' . escape(pattern, '/?') . "\<CR>"
else else
return '/' . escape(pattern, '/?') . "\<CR>" return '/' . escape(pattern, '/') . "\<CR>"
endif endif
endif endif
return a:reverse ? '#' : '*' return a:reverse ? '#' : '*'
@@ -3467,7 +3503,7 @@ function! s:FinishCommit() abort
call setbufvar(buf, 'fugitive_commit_arguments', []) call setbufvar(buf, 'fugitive_commit_arguments', [])
if getbufvar(buf, 'fugitive_commit_rebase') if getbufvar(buf, 'fugitive_commit_rebase')
call setbufvar(buf, 'fugitive_commit_rebase', 0) call setbufvar(buf, 'fugitive_commit_rebase', 0)
let s:rebase_continue = s:Dir(buf) let s:rebase_continue = [s:Dir(buf), 0]
endif endif
return s:CommitSubcommand(-1, -1, 0, 0, '', args, s:Dir(buf)) return s:CommitSubcommand(-1, -1, 0, 0, '', args, s:Dir(buf))
endif endif
@@ -3674,11 +3710,11 @@ function! s:MergeRebase(cmd, bang, mods, args, ...) abort
call fugitive#ReloadStatus(dir, 1) call fugitive#ReloadStatus(dir, 1)
if empty(filter(getqflist(),'v:val.valid && v:val.type !=# "I"')) if empty(filter(getqflist(),'v:val.valid && v:val.type !=# "I"'))
if a:cmd =~# '^rebase' && if a:cmd =~# '^rebase' &&
\ filereadable(fugitive#Find('.git/rebase-merge/amend', dir)) &&
\ filereadable(fugitive#Find('.git/rebase-merge/done', dir)) && \ filereadable(fugitive#Find('.git/rebase-merge/done', dir)) &&
\ get(readfile(fugitive#Find('.git/rebase-merge/done', dir)), -1, '') =~# '^[^e]' \ get(readfile(fugitive#Find('.git/rebase-merge/done', dir)), -1, '') =~# '^[^bep]'
cclose cclose
return 'exe ' . string(mods . 'Gcommit --amend -n -F ' . s:fnameescape(fugitive#Find('.git/rebase-merge/message', dir)) . ' -e') . '|let b:fugitive_commit_rebase = 1' let amend = filereadable(fugitive#Find('.git/rebase-merge/amend', dir)) ? '--amend ' : ''
return 'exe ' . string(mods . 'Gcommit ' . amend . '-n -F ' . s:fnameescape(fugitive#Find('.git/rebase-merge/message', dir)) . ' -e') . '|let b:fugitive_commit_rebase = 1'
elseif !had_merge_msg && filereadable(fugitive#Find('.git/MERGE_MSG', dir)) elseif !had_merge_msg && filereadable(fugitive#Find('.git/MERGE_MSG', dir))
cclose cclose
return mods . 'Gcommit --no-status -n -t '.s:fnameescape(fugitive#Find('.git/MERGE_MSG', dir)) return mods . 'Gcommit --no-status -n -t '.s:fnameescape(fugitive#Find('.git/MERGE_MSG', dir))
@@ -3736,21 +3772,264 @@ function! s:PullSubcommand(line1, line2, range, bang, mods, args) abort
return s:MergeRebase('pull', a:bang, a:mods, a:args) return s:MergeRebase('pull', a:bang, a:mods, a:args)
endfunction endfunction
function! s:RebaseContinue(arg, ...) abort
let [dir, edit_todo] = a:arg
exe s:MergeRebase('rebase', 0, '', [edit_todo && getfsize(fugitive#Find('.git/rebase-merge/git-rebase-todo', dir)) <= 0 ? '--abort' : '--continue'], dir)
endfunction
augroup fugitive_merge augroup fugitive_merge
autocmd! autocmd!
autocmd VimLeavePre,BufDelete git-rebase-todo autocmd VimLeavePre,BufDelete git-rebase-todo
\ if getbufvar(+expand('<abuf>'), '&bufhidden') ==# 'wipe' | \ if getbufvar(+expand('<abuf>'), '&bufhidden') ==# 'wipe' |
\ call s:RebaseClean(expand('<afile>')) | \ call s:RebaseClean(expand('<afile>')) |
\ if getfsize(FugitiveFind('.git/rebase-merge/done', +expand('<abuf>'))) == 0 | \ if getfsize(FugitiveFind('.git/rebase-merge/done', +expand('<abuf>'))) == 0 |
\ let s:rebase_continue = FugitiveGitDir(+expand('<abuf>')) | \ let s:rebase_continue = [FugitiveGitDir(+expand('<abuf>')), 1] |
\ endif | \ endif |
\ endif \ endif
autocmd BufEnter * nested autocmd BufEnter * nested
\ if exists('s:rebase_continue') | \ if exists('s:rebase_continue') |
\ exe s:MergeRebase('rebase', 0, '', [getfsize(fugitive#Find('.git/rebase-merge/git-rebase-todo', s:rebase_continue)) > 0 ? '--continue' : '--abort'], remove(s:, 'rebase_continue')) | \ if has('timers') |
\ call timer_start(0, function('s:RebaseContinue', [remove(s:, 'rebase_continue')])) |
\ else |
\ call s:RebaseContinue(remove(s:, 'rebase_continue')) |
\ endif |
\ endif \ endif
augroup END augroup END
" Section: :Git difftool, :Git mergetool
function! s:ToolItems(state, from, to, offsets, text, ...) abort
let items = []
for i in range(len(a:state.diff))
let diff = a:state.diff[i]
let path = (i == len(a:state.diff) - 1) ? a:to : a:from
if empty(path)
return []
endif
let item = {
\ 'valid': a:0 ? a:1 : 1,
\ 'filename': diff.filename . FugitiveVimPath(path),
\ 'lnum': matchstr(get(a:offsets, i), '\d\+'),
\ 'text': a:text}
if len(get(diff, 'module', ''))
let item.module = diff.module . path
endif
call add(items, item)
endfor
let diff = items[0:-2]
let items[-1].context = {'diff': items[0:-2]}
return [items[-1]]
endfunction
function! s:ToolToFrom(str) abort
if a:str =~# ' => '
let str = a:str =~# '{.* => .*}' ? a:str : '{' . a:str . '}'
return [substitute(str, '{.* => \(.*\)}', '\1', ''),
\ substitute(str, '{\(.*\) => .*}', '\1', '')]
else
return [a:str, a:str]
endif
endfunction
function! s:ToolParse(state, line) abort
if type(a:line) !=# type('') || a:state.mode ==# 'hunk' && a:line =~# '^[ +-]'
return []
elseif a:line =~# '^diff '
let a:state.mode = 'diffhead'
let a:state.from = ''
let a:state.to = ''
elseif a:state.mode ==# 'diffhead' && a:line =~# '^--- [^/]'
let a:state.from = a:line[4:-1]
let a:state.to = a:state.from
elseif a:state.mode ==# 'diffhead' && a:line =~# '^+++ [^/]'
let a:state.to = a:line[4:-1]
if empty(get(a:state, 'from', ''))
let a:state.from = a:state.to
endif
elseif a:line[0] ==# '@'
let a:state.mode = 'hunk'
if has_key(a:state, 'from')
let offsets = split(matchstr(a:line, '^@\+ \zs[-+0-9, ]\+\ze @'), ' ')
return s:ToolItems(a:state, a:state.from, a:state.to, offsets, matchstr(a:line, ' @@\+ \zs.*'))
endif
elseif a:line =~# '^[A-Z]\d*\t.\|^:.*\t.'
" --raw, --name-status
let [status; files] = split(a:line, "\t")
return s:ToolItems(a:state, files[0], files[-1], [], a:state.name_only ? '' : status)
elseif a:line =~# '^ \S.* |'
" --stat
let [_, to, changes; __] = matchlist(a:line, '^ \(.\{-\}\) \+|\zs \(.*\)$')
let [to, from] = s:ToolToFrom(to)
return s:ToolItems(a:state, from, to, [], changes)
elseif a:line =~# '^ *\([0-9.]\+%\) .'
" --dirstat
let [_, changes, to; __] = matchlist(a:line, '^ *\([0-9.]\+%\) \(.*\)')
return s:ToolItems(a:state, to, to, [], changes)
elseif a:line =~# '^\(\d\+\|-\)\t\(\d\+\|-\)\t.'
" --numstat
let [_, add, remove, to; __] = matchlist(a:line, '^\(\d\+\|-\)\t\(\d\+\|-\)\t\(.*\)')
let [to, from] = s:ToolToFrom(to)
return s:ToolItems(a:state, from, to, [], add ==# '-' ? 'Binary file' : '+' . add . ' -' . remove, add !=# '-')
elseif a:state.mode !=# 'diffhead' && a:state.mode !=# 'hunk' && len(a:line) || a:line =~# '^git: \|^usage: \|^error: \|^fatal: '
return [{'text': a:line}]
endif
return []
endfunction
function! s:ToolStream(dir, line1, line2, range, bang, mods, args, state, title) abort
let i = 0
let argv = copy(a:args)
let prompt = 1
let state = a:state
while i < len(argv)
let match = matchlist(argv[i], '^\(-[a-zABDFH-KN-RT-Z]\)\ze\(.*\)')
if len(match) && len(match[2])
call insert(argv, match[1])
let argv[i+1] = '-' . match[2]
continue
endif
let arg = argv[i]
if arg =~# '^-t$\|^--tool=\|^--tool-help$\|^--help$'
return -1
elseif arg =~# '^-y$\|^--no-prompt$'
let prompt = 0
call remove(argv, i)
continue
elseif arg ==# '--prompt'
let prompt = 1
call remove(argv, i)
continue
elseif arg =~# '^--\%(no-\)\=\(symlinks\|trust-exit-code\|gui\)$'
call remove(argv, i)
continue
elseif arg ==# '--'
break
endif
let i += 1
endwhile
let a:state.mode = 'init'
let a:state.from = ''
let a:state.to = ''
let exec = s:UserCommandList(a:dir) + ['--no-pager', '-c', 'diff.context=0', 'diff', '--no-ext-diff', '--no-color', '--no-prefix'] + argv
if prompt
return s:QuickfixStream(a:line2, 'difftool', a:title, exec, !a:bang, s:function('s:ToolParse'), a:state)
else
let filename = ''
let cmd = []
let tabnr = tabpagenr() + 1
for line in split(s:SystemError(s:shellesc(exec))[0], "\n")
for item in s:ToolParse(a:state, line)
if len(get(item, 'filename', '')) && item.filename != filename
call add(cmd, 'tabedit ' . s:fnameescape(item.filename))
for i in reverse(range(len(get(item.context, 'diff', []))))
call add(cmd, (i ? 'rightbelow' : 'leftabove') . ' vert Gdiffsplit! ' . s:fnameescape(item.context.diff[i].filename))
endfor
call add(cmd, 'wincmd =')
let filename = item.filename
endif
endfor
endfor
return join(cmd, '|') . (empty(cmd) ? '' : '|' . tabnr . 'tabnext')
endif
endfunction
function! s:MergetoolSubcommand(line1, line2, range, bang, mods, args) abort
let dir = s:Dir()
let i = 0
let argv = copy(a:args)
let prompt = 1
let title = ':Git mergetool' . (len(a:args) ? ' ' . s:fnameescape(a:args) : '')
let cmd = ['diff', '--diff-filter=U']
let state = {'name_only': 0}
let state.diff = [{'prefix': ':2:', 'module': ':2:'}, {'prefix': ':3:', 'module': ':3:'}, {'prefix': ':(top)'}]
call map(state.diff, 'extend(v:val, {"filename": fugitive#Find(v:val.prefix, dir)})')
return s:ToolStream(dir, a:line1, a:line2, a:range, a:bang, a:mods, ['--diff-filter=U'] + a:args, state, title)
endfunction
function! s:DifftoolSubcommand(line1, line2, range, bang, mods, args) abort
let dir = s:Dir()
let i = 0
let argv = copy(a:args)
let commits = []
let cached = 0
let reverse = 1
let prompt = 1
let state = {'name_only': 0}
let merge_base_against = {}
let dash = (index(argv, '--') > i ? ['--'] : [])
while i < len(argv)
let match = matchlist(argv[i], '^\(-[a-zABDFH-KN-RT-Z]\)\ze\(.*\)')
if len(match) && len(match[2])
call insert(argv, match[1])
let argv[i+1] = '-' . match[2]
continue
endif
let arg = argv[i]
if arg ==# '--cached'
let cached = 1
elseif arg ==# '-R'
let reverse = 1
elseif arg ==# '--name-only'
let state.name_only = 1
let argv[0] = '--name-status'
elseif arg ==# '--'
break
elseif arg !~# '^-\|^\.\.\=\%(/\|$\)'
let parsed = s:LinesError(['rev-parse', '--revs-only', substitute(arg, ':.*', '', '')] + dash)[0]
call map(parsed, '{"uninteresting": v:val =~# "^\\^", "prefix": substitute(v:val, "^\\^", "", "") . ":"}')
let merge_base_against = {}
if arg =~# '\.\.\.' && len(parsed) > 2
let display = map(split(arg, '\.\.\.', 1), 'empty(v:val) ? "@" : v:val')
if len(display) == 2
let parsed[0].module = display[1] . ':'
let parsed[1].module = display[0] . ':'
endif
let parsed[2].module = arg . ':'
if empty(commits)
let merge_base_against = parsed[0]
let parsed = [parsed[2]]
endif
elseif arg =~# '\.\.' && len(parsed) == 2
let display = map(split(arg, '\.\.', 1), 'empty(v:val) ? "@" : v:val')
if len(display) == 2
let parsed[0].module = display[0] . ':'
let parsed[1].module = display[1] . ':'
endif
elseif len(parsed) == 1
let parsed[0].module = arg . ':'
endif
call extend(commits, parsed)
endif
let i += 1
endwhile
let title = ':Git difftool' . (len(a:args) ? ' ' . s:fnameescape(a:args) : '')
if len(merge_base_against)
call add(commits, merge_base_against)
endif
let commits = filter(copy(commits), 'v:val.uninteresting') + filter(commits, '!v:val.uninteresting')
if cached
if empty(commits)
call add(commits, {'prefix': '@:', 'module': '@:'})
endif
call add(commits, {'prefix': ':0:', 'module': ':0:'})
elseif len(commits) < 2
call add(commits, {'prefix': ':(top)'})
if len(commits) < 2
call insert(commits, {'prefix': ':0:', 'module': ':0:'})
endif
endif
if reverse
let commits = [commits[-1]] + repeat([commits[0]], len(commits) - 1)
call reverse(commits)
endif
if len(commits) > 2
call add(commits, remove(commits, 0))
endif
call map(commits, 'extend(v:val, {"filename": fugitive#Find(v:val.prefix, dir)})')
let state.diff = commits
return s:ToolStream(dir, a:line1, a:line2, a:range, a:bang, a:mods, argv, state, title)
endfunction
" Section: :Ggrep, :Glog " Section: :Ggrep, :Glog
if !exists('g:fugitive_summary_format') if !exists('g:fugitive_summary_format')
@@ -3838,10 +4117,16 @@ function! s:GrepSubcommand(line1, line2, range, bang, mods, args) abort
endif endif
endfunction endfunction
function! s:LogFlushQueue(state) abort let s:log_diff_context = '{"filename": fugitive#Find(v:val . from, a:dir), "lnum": get(offsets, v:key), "module": strpart(v:val, 0, len(a:state.base_module)) . from}'
function! s:LogFlushQueue(state, dir) abort
let queue = remove(a:state, 'queue') let queue = remove(a:state, 'queue')
if a:state.child_found if a:state.child_found && get(a:state, 'ignore_summary')
call remove(queue, 0) call remove(queue, 0)
elseif len(queue) && len(a:state.target) && len(get(a:state, 'parents', []))
let from = substitute(a:state.target, '^/', ':', '')
let offsets = []
let queue[0].context.diff = map(copy(a:state.parents), s:log_diff_context)
endif endif
if len(queue) && queue[-1] ==# {'text': ''} if len(queue) && queue[-1] ==# {'text': ''}
call remove(queue, -1) call remove(queue, -1)
@@ -3850,41 +4135,65 @@ function! s:LogFlushQueue(state) abort
endfunction endfunction
function! s:LogParse(state, dir, line) abort function! s:LogParse(state, dir, line) abort
if a:state.context ==# 'hunk' && a:line =~# '^[-+ ]' if a:state.mode ==# 'hunk' && a:line =~# '^[-+ ]'
return [] return []
endif endif
let list = matchlist(a:line, '^\%(fugitive \(.\{-\}\)\t\|commit \|From \)\=\(\x\{40,\}\)\%( \(.*\)\)\=$') let list = matchlist(a:line, '^\%(fugitive \(.\{-\}\)\t\|commit \|From \)\=\(\x\{40,\}\)\%( \(.*\)\)\=$')
if len(list) if len(list)
let a:state.context = 'commit' let queue = s:LogFlushQueue(a:state, a:dir)
let a:state.mode = 'commit'
let a:state.base = 'fugitive://' . a:dir . '//' . list[2] let a:state.base = 'fugitive://' . a:dir . '//' . list[2]
let a:state.base_module = len(list[1]) ? list[1] : list[2] if len(list[1])
let a:state.message = list[3] let [a:state.base_module; a:state.parents] = split(list[1], ' ')
if has_key(a:state, 'diffing') else
call remove(a:state, 'diffing') let a:state.base_module = list[2]
let a:state.parents = []
endif endif
let queue = s:LogFlushQueue(a:state) let a:state.message = list[3]
let a:state.from = ''
let a:state.to = ''
let context = {}
let a:state.queue = [{ let a:state.queue = [{
\ 'valid': 1, \ 'valid': 1,
\ 'context': context,
\ 'filename': a:state.base . a:state.target, \ 'filename': a:state.base . a:state.target,
\ 'module': a:state.base_module . substitute(a:state.target, '^/', ':', ''), \ 'module': a:state.base_module . substitute(a:state.target, '^/', ':', ''),
\ 'text': a:state.message}] \ 'text': a:state.message}]
let a:state.child_found = 0 let a:state.child_found = 0
return queue return queue
elseif type(a:line) == type(0) elseif type(a:line) == type(0)
return s:LogFlushQueue(a:state) return s:LogFlushQueue(a:state, a:dir)
elseif a:line =~# '^diff' elseif a:line =~# '^diff'
let a:state.context = 'diffhead' let a:state.mode = 'diffhead'
elseif a:line =~# '^[+-]\{3\} \w/' && a:state.context ==# 'diffhead' let a:state.from = ''
let a:state.diffing = a:line[5:-1] let a:state.to = ''
elseif a:line =~# '^@@[^@]*+\d' && has_key(a:state, 'diffing') && has_key(a:state, 'base') elseif a:state.mode ==# 'diffhead' && a:line =~# '^--- \w/'
let a:state.context = 'hunk' let a:state.from = a:line[6:-1]
if empty(a:state.target) || a:state.target ==# a:state.diffing let a:state.to = a:state.from
elseif a:state.mode ==# 'diffhead' && a:line =~# '^+++ \w/'
let a:state.to = a:line[6:-1]
if empty(get(a:state, 'from', ''))
let a:state.from = a:state.to
endif
elseif a:line =~# '^@@[^@]*+\d' && len(get(a:state, 'to', '')) && has_key(a:state, 'base')
let a:state.mode = 'hunk'
if empty(a:state.target) || a:state.target ==# '/' . a:state.to
if !a:state.child_found && len(a:state.queue) && a:state.queue[-1] ==# {'text': ''}
call remove(a:state.queue, -1)
endif
let a:state.child_found = 1 let a:state.child_found = 1
let offsets = map(split(matchstr(a:line, '^@\+ \zs[-+0-9, ]\+\ze @'), ' '), '+matchstr(v:val, "\\d\\+")')
let context = {}
if len(a:state.parents)
let from = ":" . a:state.from
let context.diff = map(copy(a:state.parents), s:log_diff_context)
endif
call add(a:state.queue, { call add(a:state.queue, {
\ 'valid': 1, \ 'valid': 1,
\ 'filename': a:state.base . a:state.diffing, \ 'context': context,
\ 'module': a:state.base_module . substitute(a:state.diffing, '^/', ':', ''), \ 'filename': FugitiveVimPath(a:state.base . '/' . a:state.to),
\ 'lnum': +matchstr(a:line, '+\zs\d\+'), \ 'module': a:state.base_module . ':' . a:state.to,
\ 'lnum': offsets[-1],
\ 'text': a:state.message . matchstr(a:line, ' @@\+ .\+')}) \ 'text': a:state.message . matchstr(a:line, ' @@\+ .\+')})
endif endif
elseif a:state.follow && elseif a:state.follow &&
@@ -3899,7 +4208,7 @@ function! s:LogParse(state, dir, line) abort
if !get(a:state, 'ignore_summary') if !get(a:state, 'ignore_summary')
call add(a:state.queue, {'text': a:line}) call add(a:state.queue, {'text': a:line})
endif endif
elseif a:state.context ==# 'commit' || a:state.context ==# 'init' elseif a:state.mode ==# 'commit' || a:state.mode ==# 'init'
call add(a:state.queue, {'text': a:line}) call add(a:state.queue, {'text': a:line})
endif endif
return [] return []
@@ -3922,32 +4231,36 @@ function! fugitive#LogCommand(line1, count, range, bang, mods, args, type) abort
endif endif
if a:line1 == 0 && a:count if a:line1 == 0 && a:count
let path = fugitive#Path(bufname(a:count), '/', dir) let path = fugitive#Path(bufname(a:count), '/', dir)
let titlepre = ':0,' . a:count
elseif a:count >= 0 elseif a:count >= 0
let path = fugitive#Path(@%, '/', dir) let path = fugitive#Path(@%, '/', dir)
let titlepre = a:count == 0 ? ':0,' . bufnr('') : ':'
else else
let path = '' let titlepre = ':'
let path = ''
endif endif
let range = '' let range = ''
let extra = [] let extra_args = []
let state = {'context': 'init', 'child_found': 0, 'queue': [], 'follow': 0} let extra_paths = []
let state = {'mode': 'init', 'child_found': 0, 'queue': [], 'follow': 0}
if path =~# '^/\.git\%(/\|$\)\|^$' if path =~# '^/\.git\%(/\|$\)\|^$'
let path = '' let path = ''
elseif a:line1 == 0 elseif a:line1 == 0
let range = "0," . (a:count ? a:count : bufnr('')) let range = "0," . (a:count ? a:count : bufnr(''))
let extra = ['.' . path] let extra_paths = ['.' . path]
if (empty(paths) || paths ==# ['--']) && !s:HasOpt(args, '--no-follow') if (empty(paths) || paths ==# ['--']) && !s:HasOpt(args, '--no-follow')
let state.follow = 1 let state.follow = 1
if !s:HasOpt(args, '--follow') if !s:HasOpt(args, '--follow')
call insert(args, '--follow') call insert(extra_args, '--follow')
endif endif
if !s:HasOpt(args, '--summary') if !s:HasOpt(args, '--summary')
call insert(args, '--summary') call insert(extra_args, '--summary')
let state.ignore_summary = 1 let state.ignore_summary = 1
endif endif
endif endif
elseif a:count > 0 elseif a:count > 0
if !s:HasOpt(args, '--merges', '--no-merges') if !s:HasOpt(args, '--merges', '--no-merges')
call insert(args, '--no-merges') call insert(extra_args, '--no-merges')
endif endif
call add(args, '-L' . a:line1 . ',' . a:count . ':' . path[1:-1]) call add(args, '-L' . a:line1 . ',' . a:count . ':' . path[1:-1])
endif endif
@@ -3957,29 +4270,29 @@ function! fugitive#LogCommand(line1, count, range, bang, mods, args, type) abort
call add(args, owner) call add(args, owner)
endif endif
endif endif
if empty(extra) if empty(extra_paths)
let path = '' let path = ''
endif endif
if s:HasOpt(args, '-g', '--walk-reflogs') if s:HasOpt(args, '-g', '--walk-reflogs')
let format = "%gd\t%H %gs" let format = "%gd %P\t%H %gs"
else else
let format = "%h\t%H " . g:fugitive_summary_format let format = "%h %P\t%H " . g:fugitive_summary_format
endif endif
let cmd = ['--no-pager'] let cmd = ['--no-pager']
if fugitive#GitVersion(1, 9) if fugitive#GitVersion(1, 9)
call extend(cmd, ['-c', 'diff.context=0', '-c', 'diff.noprefix=false', 'log']) call extend(cmd, ['-c', 'diff.context=0', '-c', 'diff.noprefix=false', 'log'])
else else
call extend(cmd, ['log', '-U0', '--no-patch']) call extend(cmd, ['log', '-U0', '-s'])
endif endif
call extend(cmd, call extend(cmd,
\ ['--no-color', '--no-ext-diff', '--pretty=format:fugitive ' . format] + \ ['--no-color', '--no-ext-diff', '--pretty=format:fugitive ' . format] +
\ args + paths + extra) \ args + extra_args + paths + extra_paths)
let state.target = path let state.target = path
let title = (listnr < 0 ? ':Gclog ' : ':Gllog ') . s:fnameescape(args + paths) let title = titlepre . (listnr < 0 ? 'Gclog ' : 'Gllog ') . s:fnameescape(args + paths)
if empty(paths + extra) && empty(a:type) && len(s:Relative('/')) if empty(paths + extra_paths) && empty(a:type) && len(s:Relative('/'))
let after = '|echohl WarningMsg|echo ' . string('Use :0Glog or :0Gclog for old behavior of targeting current file') . '|echohl NONE' . after let after = '|echohl WarningMsg|echo ' . string('Use :0Glog or :0Gclog for old behavior of targeting current file') . '|echohl NONE' . after
endif endif
return s:QuickfixStream(listnr, title, s:UserCommandList(dir) + cmd, !a:bang, s:function('s:LogParse'), state, dir) . after return s:QuickfixStream(listnr, 'log', title, s:UserCommandList(dir) + cmd, !a:bang, s:function('s:LogParse'), state, dir) . after
endfunction endfunction
" Section: :Gedit, :Gpedit, :Gsplit, :Gvsplit, :Gtabedit, :Gread " Section: :Gedit, :Gpedit, :Gsplit, :Gvsplit, :Gtabedit, :Gread
@@ -4371,7 +4684,7 @@ endfunction
function! s:AskPassArgs(dir) abort function! s:AskPassArgs(dir) abort
if (len($DISPLAY) || len($TERM_PROGRAM) || has('gui_running')) && fugitive#GitVersion(1, 8) && if (len($DISPLAY) || len($TERM_PROGRAM) || has('gui_running')) && fugitive#GitVersion(1, 8) &&
\ empty($GIT_ASKPASS) && empty($SSH_ASKPASS) && empty(fugitive#Config('core.askPass', a:dir)) \ empty($GIT_ASKPASS) && empty($SSH_ASKPASS) && empty(get(fugitive#Config(a:dir), 'core.askpass', []))
if s:executable(s:ExecPath() . '/git-gui--askpass') if s:executable(s:ExecPath() . '/git-gui--askpass')
return ['-c', 'core.askPass=' . s:ExecPath() . '/git-gui--askpass'] return ['-c', 'core.askPass=' . s:ExecPath() . '/git-gui--askpass']
elseif s:executable('ssh-askpass') elseif s:executable('ssh-askpass')
@@ -4497,14 +4810,11 @@ endfunction
function! s:diffoff_all(dir) abort function! s:diffoff_all(dir) abort
let curwin = winnr() let curwin = winnr()
for nr in range(1,winnr('$')) for nr in range(1,winnr('$'))
if getwinvar(nr,'&diff') if getwinvar(nr, '&diff') && !empty(getwinvar(nr, 'fugitive_diff_restore'))
if nr != winnr() if nr != winnr()
execute nr.'wincmd w' execute nr.'wincmd w'
let restorewinnr = 1
endif
if s:Dir() ==# a:dir
call s:diffoff()
endif endif
call s:diffoff()
endif endif
endfor endfor
execute curwin.'wincmd w' execute curwin.'wincmd w'
@@ -4605,6 +4915,8 @@ function! fugitive#Diffsplit(autodir, keepfocus, mods, arg, args) abort
elseif arg =~# '^:\d$' elseif arg =~# '^:\d$'
exe s:DirCheck() exe s:DirCheck()
let file = s:Relative(arg . ':') let file = s:Relative(arg . ':')
elseif arg =~# '^[~^]\d*$'
return 'echoerr ' . string('fugitive: change ' . arg . ' to !' . arg . ' to diff against ancestor')
else else
try try
let file = arg =~# '^:/.' ? fugitive#RevParse(arg) . s:Relative(':') : s:Expand(arg) let file = arg =~# '^:/.' ? fugitive#RevParse(arg) . s:Relative(':') : s:Expand(arg)
@@ -4794,7 +5106,7 @@ function! s:BlameCommitFileLnum(...) abort
let commit = get(s:LinesError('rev-list', '--ancestry-path', '--reverse', commit . '..' . state.blame_reverse_end)[0], 0, '') let commit = get(s:LinesError('rev-list', '--ancestry-path', '--reverse', commit . '..' . state.blame_reverse_end)[0], 0, '')
endif endif
let lnum = +matchstr(line, ' \zs\d\+\ze \%((\| *\d\+)\)') let lnum = +matchstr(line, ' \zs\d\+\ze \%((\| *\d\+)\)')
let path = matchstr(line, '^\^\=[?*]*\x* \+\%(\d\+ \+\d\+ \+\)\=\zs.\{-\}\ze\s\+\%(\%( \d\+ \)\@<!([^()]*\w \d\+)\|\d\+ \)') let path = matchstr(line, '^\^\=[?*]*\x* \+\%(\d\+ \+\d\+ \+\)\=\zs.\{-\}\ze\s*\d\+ \%((\| *\d\+)\)')
if empty(path) && lnum if empty(path) && lnum
let path = get(state, 'blame_file', '') let path = get(state, 'blame_file', '')
endif endif
@@ -5012,6 +5324,9 @@ function! s:BlameSubcommand(line1, count, range, bang, mods, args) abort
if exists('+relativenumber') if exists('+relativenumber')
setlocal norelativenumber setlocal norelativenumber
endif endif
if exists('+signcolumn')
setlocal signcolumn=no
endif
execute "vertical resize ".(s:linechars('.\{-\}\ze\s\+\d\+)')+1) execute "vertical resize ".(s:linechars('.\{-\}\ze\s\+\d\+)')+1)
call s:Map('n', 'A', ":<C-u>exe 'vertical resize '.(<SID>linechars('.\\{-\\}\\ze [0-9:/+-][0-9:/+ -]* \\d\\+)')+1+v:count)<CR>", '<silent>') call s:Map('n', 'A', ":<C-u>exe 'vertical resize '.(<SID>linechars('.\\{-\\}\\ze [0-9:/+-][0-9:/+ -]* \\d\\+)')+1+v:count)<CR>", '<silent>')
call s:Map('n', 'C', ":<C-u>exe 'vertical resize '.(<SID>linechars('^\\S\\+')+1+v:count)<CR>", '<silent>') call s:Map('n', 'C', ":<C-u>exe 'vertical resize '.(<SID>linechars('^\\S\\+')+1+v:count)<CR>", '<silent>')
@@ -5106,12 +5421,10 @@ function! s:BlameJump(suffix, ...) abort
let winnr = bufwinnr(blame_bufnr) let winnr = bufwinnr(blame_bufnr)
if winnr > 0 if winnr > 0
exe winnr.'wincmd w' exe winnr.'wincmd w'
exe bufnr.'bdelete'
endif endif
execute 'Gedit' s:fnameescape(commit . suffix . ':' . path) execute 'Gedit' s:fnameescape(commit . suffix . ':' . path)
execute lnum execute lnum
if winnr > 0
exe bufnr.'bdelete'
endif
endif endif
if exists(':Gblame') if exists(':Gblame')
let my_bufnr = bufnr('') let my_bufnr = bufnr('')
@@ -5606,8 +5919,8 @@ function! fugitive#MapJumps(...) abort
nnoremap <buffer> <silent> cRw :<C-U>Gcommit --reset-author --amend --only<CR> nnoremap <buffer> <silent> cRw :<C-U>Gcommit --reset-author --amend --only<CR>
nnoremap <buffer> cf :<C-U>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR> nnoremap <buffer> cf :<C-U>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR>
nnoremap <buffer> cF :<C-U><Bar>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR> nnoremap <buffer> cF :<C-U><Bar>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR>
nnoremap <buffer> cs :<C-U>Gcommit --squash=<C-R>=<SID>SquashArgument()<CR> nnoremap <buffer> cs :<C-U>Gcommit --no-edit --squash=<C-R>=<SID>SquashArgument()<CR>
nnoremap <buffer> cS :<C-U><Bar>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Gcommit --squash=<C-R>=<SID>SquashArgument()<CR> nnoremap <buffer> cS :<C-U><Bar>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Gcommit --no-edit --squash=<C-R>=<SID>SquashArgument()<CR>
nnoremap <buffer> cA :<C-U>Gcommit --edit --squash=<C-R>=<SID>SquashArgument()<CR> nnoremap <buffer> cA :<C-U>Gcommit --edit --squash=<C-R>=<SID>SquashArgument()<CR>
nnoremap <buffer> <silent> c? :<C-U>help fugitive_c<CR> nnoremap <buffer> <silent> c? :<C-U>help fugitive_c<CR>

View File

@@ -103,9 +103,23 @@ that are part of Git repositories).
:Glgrep[!] [args] |:lgrep|[!] with git-grep as 'grepprg'. :Glgrep[!] [args] |:lgrep|[!] with git-grep as 'grepprg'.
:0Git[!] grep [args] :0Git[!] grep [args]
*:Git-difftool*
:Git[!] difftool [args] Invoke `git diff [args]` and load the changes into the
quickfix list. Each changed hunk gets a separate
quickfix entry unless you pass an option like
--name-only or --name-status. Jumps to the first
change unless [!] is given.
:Git difftool -y [args] Invoke `git diff [args]`, open each changed file in a
new tab, and invoke `:Gdiffsplit` against the
appropriate commit.
*:Git-mergetool*
:Git mergetool [args] Like |:Git-difftool|, but target merge conflicts.
*:Gclog* *:Glog* *:Gclog* *:Glog*
:Gclog[!] [args] Use git-log [args] to load the commit history into the :Gclog[!] [args] Use git-log [args] to load the commit history into the
:Glog[!] [args] |quickfix| list. Jump to the first commit unless [!] :Glog[!] [args] |quickfix| list. Jumps to the first commit unless [!]
is given. is given.
:{range}Gclog[!] [args] Use git-log -L to load previous revisions of the given :{range}Gclog[!] [args] Use git-log -L to load previous revisions of the given

View File

@@ -157,16 +157,16 @@ function! s:Tree(path) abort
let config = readfile(config_file,'',10) let config = readfile(config_file,'',10)
call filter(config,'v:val =~# "^\\s*worktree *="') call filter(config,'v:val =~# "^\\s*worktree *="')
if len(config) == 1 if len(config) == 1
let worktree = s:Slash(FugitiveVimPath(matchstr(config[0], '= *\zs.*'))) let worktree = FugitiveVimPath(matchstr(config[0], '= *\zs.*'))
endif endif
elseif filereadable(dir . '/gitdir') elseif filereadable(dir . '/gitdir')
let worktree = s:Slash(fnamemodify(FugitiveVimPath(readfile(dir . '/gitdir')[0]), ':h')) let worktree = fnamemodify(FugitiveVimPath(readfile(dir . '/gitdir')[0]), ':h')
if worktree ==# '.' if worktree ==# '.'
unlet! worktree unlet! worktree
endif endif
endif endif
if exists('worktree') if exists('worktree')
let s:worktree_for_dir[dir] = worktree let s:worktree_for_dir[dir] = s:Slash(resolve(worktree))
let s:dir_for_worktree[s:worktree_for_dir[dir]] = dir let s:dir_for_worktree[s:worktree_for_dir[dir]] = dir
endif endif
endif endif
@@ -177,10 +177,29 @@ function! s:Tree(path) abort
endif endif
endfunction endfunction
function! s:CeilingDirectories() abort
if !exists('s:ceiling_directories')
let s:ceiling_directories = []
let resolve = 1
for dir in split($GIT_CEILING_DIRECTORIES, has('win32') ? ';' : ':', 1)
if empty(dir)
let resolve = 0
elseif resolve
call add(s:ceiling_directories, resolve(dir))
else
call add(s:ceiling_directories, dir)
endif
endfor
endif
return s:ceiling_directories + get(g:, 'ceiling_directories', [])
endfunction
function! FugitiveExtractGitDir(path) abort function! FugitiveExtractGitDir(path) abort
let path = s:Slash(a:path) let path = s:Slash(a:path)
if path =~# '^fugitive:' if path =~# '^fugitive:'
return matchstr(path, '\C^fugitive:\%(//\)\=\zs.\{-\}\ze\%(//\|::\|$\)') return matchstr(path, '\C^fugitive:\%(//\)\=\zs.\{-\}\ze\%(//\|::\|$\)')
elseif empty(path)
return ''
elseif isdirectory(path) elseif isdirectory(path)
let path = fnamemodify(path, ':p:s?/$??') let path = fnamemodify(path, ':p:s?/$??')
else else
@@ -201,7 +220,7 @@ function! FugitiveExtractGitDir(path) abort
if root =~# '\v^//%([^/]+/?)?$' if root =~# '\v^//%([^/]+/?)?$'
break break
endif endif
if index(split($GIT_CEILING_DIRECTORIES, ':'), root) >= 0 if index(s:CeilingDirectories(), root) >= 0
break break
endif endif
if root ==# $GIT_WORK_TREE && FugitiveIsGitDir(env_git_dir) if root ==# $GIT_WORK_TREE && FugitiveIsGitDir(env_git_dir)
@@ -285,6 +304,9 @@ function! s:ProjectionistDetect() abort
endif endif
endfunction endfunction
if v:version + has('patch061') < 703
runtime! autoload/fugitive.vim
endif
let g:io_fugitive = { let g:io_fugitive = {
\ 'simplify': function('fugitive#simplify'), \ 'simplify': function('fugitive#simplify'),
\ 'resolve': function('fugitive#resolve'), \ 'resolve': function('fugitive#resolve'),