Handle quoted strings in g:fugitive_git_executable

Taking the option that was historically a shell command and treating it
as a space-delimited argument list means that there's no longer any way
to include a space in an argument.  Let's rectify that by reusing a
simplified version of the argument parser in s:SplitExpandChain(), which
handles both single and double quoted strings in a way similar to how
shells do it.

Of course, the ideal solution is to just use a list of strings to begin
with.  Support for that is added here as well.

References: https://github.com/tpope/vim-fugitive/issues/1751

# This is the commit message #2:

squash! Accept argument list for g:fugitive_git_executable
This commit is contained in:
Tim Pope
2021-05-21 02:53:40 -04:00
parent dd01e40106
commit 28afd12151

View File

@@ -6,12 +6,6 @@ if exists('g:autoloaded_fugitive')
endif endif
let g:autoloaded_fugitive = 1 let g:autoloaded_fugitive = 1
if !exists('g:fugitive_git_executable')
let g:fugitive_git_executable = 'git'
elseif g:fugitive_git_executable =~# '^\w\+='
let g:fugitive_git_executable = 'env ' . g:fugitive_git_executable
endif
" Section: Utility " Section: Utility
function! s:function(name) abort function! s:function(name) abort
@@ -233,6 +227,41 @@ endfunction
" Section: Git " Section: Git
function! s:GitCmd() abort
if !exists('g:fugitive_git_executable')
return ['git']
elseif type(g:fugitive_git_executable) == type([])
return g:fugitive_git_executable
else
let dquote = '"\%([^"]\|""\|\\"\)*"\|'
let string = g:fugitive_git_executable
let list = []
if string =~# '^\w\+='
call add(list, 'env')
endif
while string =~# '\S'
let arg = matchstr(string, '^\s*\%(' . dquote . '''[^'']*''\|\\.\|[^[:space:] |]\)\+')
let string = strpart(string, len(arg))
let arg = substitute(arg, '^\s\+', '', '')
let arg = substitute(arg,
\ '\(' . dquote . '''\%(''''\|[^'']\)*''\|\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
\ '\=submatch(0)[0] ==# "\\" ? submatch(0)[1] : submatch(0)[1:-2]', 'g')
call add(list, arg)
endwhile
return list
endif
endfunction
function! s:GitShellCmd() abort
if !exists('g:fugitive_git_executable')
return 'git'
elseif type(g:fugitive_git_executable) == type([])
return s:shellesc(g:fugitive_git_executable)
else
return g:fugitive_git_executable
endif
endfunction
function! s:UserCommandCwd(dir) abort function! s:UserCommandCwd(dir) abort
let tree = s:Tree(a:dir) let tree = s:Tree(a:dir)
return len(tree) ? FugitiveVimPath(tree) : getcwd() return len(tree) ? FugitiveVimPath(tree) : getcwd()
@@ -242,7 +271,13 @@ function! s:UserCommandList(...) abort
if !fugitive#GitVersion(1, 8, 5) if !fugitive#GitVersion(1, 8, 5)
throw 'fugitive: Git 1.8.5 or higher required' throw 'fugitive: Git 1.8.5 or higher required'
endif endif
let git = split(get(g:, 'fugitive_git_command', g:fugitive_git_executable), '\s\+') if !exists('g:fugitive_git_command')
let git = s:GitCmd()
elseif type(g:fugitive_git_command) == type([])
let git = g:fugitive_git_command
else
let git = split(g:fugitive_git_command, '\s\+')
endif
let flags = [] let flags = []
if a:0 && type(a:1) == type({}) if a:0 && type(a:1) == type({})
let git = copy(get(a:1, 'git', git)) let git = copy(get(a:1, 'git', git))
@@ -270,13 +305,14 @@ endfunction
let s:git_versions = {} let s:git_versions = {}
function! fugitive#GitVersion(...) abort function! fugitive#GitVersion(...) abort
if !has_key(s:git_versions, g:fugitive_git_executable) let git = s:GitShellCmd()
let s:git_versions[g:fugitive_git_executable] = matchstr(s:SystemError(g:fugitive_git_executable.' --version')[0], '\d[^[:space:]]\+') if !has_key(s:git_versions, git)
let s:git_versions[git] = matchstr(s:SystemError(git.' --version')[0], '\d[^[:space:]]\+')
endif endif
if !a:0 if !a:0
return s:git_versions[g:fugitive_git_executable] return s:git_versions[git]
endif endif
let components = split(s:git_versions[g:fugitive_git_executable], '\D\+') let components = split(s:git_versions[git], '\D\+')
if empty(components) if empty(components)
return -1 return -1
endif endif
@@ -368,7 +404,7 @@ function! fugitive#PrepareDirEnvGitArgv(...) abort
if !fugitive#GitVersion(1, 8, 5) if !fugitive#GitVersion(1, 8, 5)
throw 'fugitive: Git 1.8.5 or higher required' throw 'fugitive: Git 1.8.5 or higher required'
endif endif
let git = split(g:fugitive_git_executable) let git = s:GitCmd()
if a:0 && type(a:1) ==# type([]) if a:0 && type(a:1) ==# type([])
let cmd = a:000[1:-1] + a:1 let cmd = a:000[1:-1] + a:1
else else
@@ -845,7 +881,7 @@ function! s:repo_prepare(...) dict abort
endfunction endfunction
function! s:repo_git_command(...) dict abort function! s:repo_git_command(...) dict abort
let git = g:fugitive_git_executable . ' --git-dir='.s:shellesc(self.git_dir) let git = s:GitShellCmd() . ' --git-dir='.s:shellesc(self.git_dir)
return git.join(map(copy(a:000),'" ".s:shellesc(v:val)'),'') return git.join(map(copy(a:000),'" ".s:shellesc(v:val)'),'')
endfunction endfunction
@@ -2095,7 +2131,7 @@ function! fugitive#BufReadStatus() abort
if &bufhidden ==# '' if &bufhidden ==# ''
setlocal bufhidden=delete setlocal bufhidden=delete
endif endif
let b:dispatch = '-dir=' . fnameescape(len(s:Tree()) ? s:Tree() : s:Dir()) . ' ' . g:fugitive_git_executable . ' fetch --all' let b:dispatch = '-dir=' . fnameescape(len(s:Tree()) ? s:Tree() : s:Dir()) . ' ' . s:GitShellCmd() . ' fetch --all'
call fugitive#MapJumps() call fugitive#MapJumps()
call s:Map('n', '-', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>') call s:Map('n', '-', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>')
call s:Map('x', '-', ":<C-U>execute <SID>Do('Toggle',1)<CR>", '<silent>') call s:Map('x', '-', ":<C-U>execute <SID>Do('Toggle',1)<CR>", '<silent>')
@@ -2822,7 +2858,7 @@ function! fugitive#Command(line1, line2, range, bang, mods, arg) abort
call extend(args, split(alias, '\s\+'), 'keep') call extend(args, split(alias, '\s\+'), 'keep')
endif endif
let name = substitute(get(args, 0, ''), '\%(^\|-\)\(\l\)', '\u\1', 'g') let name = substitute(get(args, 0, ''), '\%(^\|-\)\(\l\)', '\u\1', 'g')
let git = split(get(g:, 'fugitive_git_command', g:fugitive_git_executable), '\s\+') let git = s:UserCommandList()
let options = {'git': git, 'dir': dir, 'flags': flags} let options = {'git': git, 'dir': dir, 'flags': flags}
if pager is# -1 && exists('*s:' . name . 'Subcommand') && get(args, 1, '') !=# '--help' if pager is# -1 && exists('*s:' . name . 'Subcommand') && get(args, 1, '') !=# '--help'
try try
@@ -2981,10 +3017,11 @@ endfunction
let s:exec_paths = {} let s:exec_paths = {}
function! s:ExecPath() abort function! s:ExecPath() abort
if !has_key(s:exec_paths, g:fugitive_git_executable) let git = s:GitShellCmd()
let s:exec_paths[g:fugitive_git_executable] = s:sub(system(g:fugitive_git_executable.' --exec-path'),'\n$','') if !has_key(s:exec_paths, git)
let s:exec_paths[git] = s:sub(system(git.' --exec-path'),'\n$','')
endif endif
return s:exec_paths[g:fugitive_git_executable] return s:exec_paths[git]
endfunction endfunction
let s:subcommands_before_2_5 = [ let s:subcommands_before_2_5 = [
@@ -5567,9 +5604,9 @@ endfunction
function! s:Keywordprg() abort function! s:Keywordprg() abort
let args = ' --git-dir='.escape(s:Dir(),"\\\"' ") let args = ' --git-dir='.escape(s:Dir(),"\\\"' ")
if has('gui_running') && !has('win32') if has('gui_running') && !has('win32')
return g:fugitive_git_executable . ' --no-pager' . args . ' log -1' return s:GitShellCmd() . ' --no-pager' . args . ' log -1'
else else
return g:fugitive_git_executable . args . ' show' return s:GitShellCmd() . args . ' show'
endif endif
endfunction endfunction