mirror of
https://github.com/tpope/vim-fugitive.git
synced 2025-11-13 13:53:51 -05:00
Better support for ssh config
* Respect User and Port in addition to Hostname. * Support globs and negations in addition to literal host matching. * Parse global config file in addition to user config file. * Handle Include declarations. References: https://github.com/tpope/vim-rhubarb/issues/65
This commit is contained in:
@@ -711,32 +711,83 @@ function! s:Remote(dir) abort
|
|||||||
return remote =~# '^\.\=$' ? 'origin' : remote
|
return remote =~# '^\.\=$' ? 'origin' : remote
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
unlet! s:ssh_aliases
|
function! s:SshParseHost(value) abort
|
||||||
function! fugitive#SshHostAlias(...) abort
|
let patterns = []
|
||||||
if !exists('s:ssh_aliases')
|
let negates = []
|
||||||
let s:ssh_aliases = {}
|
for host in split(a:value, '\s\+')
|
||||||
if filereadable(expand('~/.ssh/config'))
|
let pattern = substitute(host, '[\\^$.*~?]', '\=submatch(0) == "*" ? ".*" : submatch(0) == "?" ? "." : "\\" . submatch(0)', 'g')
|
||||||
let hosts = []
|
if pattern[0] ==# '!'
|
||||||
for line in readfile(expand('~/.ssh/config'))
|
call add(negates, '\&\%(^' . pattern[1 : -1] . '$\)\@!')
|
||||||
let key = matchstr(line, '^\s*\zs\w\+\ze\s')
|
else
|
||||||
let value = matchstr(line, '^\s*\w\+\s\+\zs.*\S')
|
call add(patterns, pattern)
|
||||||
if key ==? 'host'
|
|
||||||
let hosts = split(value, '\s\+')
|
|
||||||
elseif key ==? 'hostname'
|
|
||||||
for host in hosts
|
|
||||||
if !has_key(s:ssh_aliases, host)
|
|
||||||
let s:ssh_aliases[host] = tolower(value)
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
endif
|
endif
|
||||||
|
endfor
|
||||||
|
return '^\%(' . join(patterns, '\|') . '\)$' . join(negates, '')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:SshParseConfig(into, root, file, ...) abort
|
||||||
|
if !filereadable(a:file)
|
||||||
|
return a:into
|
||||||
endif
|
endif
|
||||||
if a:0
|
let host = a:0 ? a:1 : '^\%(.*\)$'
|
||||||
return get(s:ssh_aliases, a:1, a:1)
|
for line in readfile(a:file)
|
||||||
else
|
let key = tolower(matchstr(line, '^\s*\zs\w\+\ze\s'))
|
||||||
return s:ssh_aliases
|
let value = matchstr(line, '^\s*\w\+\s\+\zs.*\S')
|
||||||
|
if key ==# 'match'
|
||||||
|
let host = value ==# 'all' ? '^\%(.*\)$' : ''
|
||||||
|
elseif key ==# 'host'
|
||||||
|
let host = s:SshParseHost(value)
|
||||||
|
elseif key ==# 'include'
|
||||||
|
call s:SshParseInclude(a:into, a:root, host, value)
|
||||||
|
elseif len(key) && len(host)
|
||||||
|
call extend(a:into, {key: []}, 'keep')
|
||||||
|
call add(a:into[key], [host, value])
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
return a:into
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:SshParseInclude(into, root, host, value) abort
|
||||||
|
for glob in split(a:value)
|
||||||
|
if glob !~# '^/'
|
||||||
|
let glob = a:root . glob
|
||||||
|
endif
|
||||||
|
for file in split(glob(glob), "\n")
|
||||||
|
call s:SshParseConfig(a:into, a:root, file, a:host)
|
||||||
|
endfor
|
||||||
|
endfor
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
unlet! s:ssh_config
|
||||||
|
function! fugitive#SshConfig(host, ...) abort
|
||||||
|
if !exists('s:ssh_config')
|
||||||
|
let s:ssh_config = {}
|
||||||
|
for file in [expand("~/.ssh/config"), "/etc/ssh/ssh_config"]
|
||||||
|
call s:SshParseConfig(s:ssh_config, substitute(file, '\w*$', '', ''), file)
|
||||||
|
endfor
|
||||||
endif
|
endif
|
||||||
|
let host_config = {}
|
||||||
|
for key in a:0 ? a:1 : keys(s:ssh_config)
|
||||||
|
for [host_pattern, value] in get(s:ssh_config, key, [])
|
||||||
|
if a:host =~# host_pattern
|
||||||
|
let host_config[key] = value
|
||||||
|
break
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
endfor
|
||||||
|
return host_config
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! fugitive#SshHostAlias(authority) abort
|
||||||
|
let [_, user, host, port; __] = matchlist(a:authority, '^\%(\([^/@]\+\)@\)\=\(.\{-\}\)\%(:\(\d\+\)\)\=$')
|
||||||
|
let c = fugitive#SshConfig(host, ['user', 'hostname', 'port'])
|
||||||
|
if empty(user)
|
||||||
|
let user = get(c, 'user', '')
|
||||||
|
endif
|
||||||
|
if empty(port)
|
||||||
|
let port = get(c, 'port', '')
|
||||||
|
endif
|
||||||
|
return (len(user) ? user . '@' : '') . get(c, 'hostname', host) . (port =~# '^\%(22\)\=$' ? '' : ':' . port)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
let s:redirects = {}
|
let s:redirects = {}
|
||||||
@@ -752,10 +803,23 @@ function! fugitive#ResolveRemote(remote) abort
|
|||||||
if len(s:redirects[a:remote])
|
if len(s:redirects[a:remote])
|
||||||
return s:redirects[a:remote]
|
return s:redirects[a:remote]
|
||||||
endif
|
endif
|
||||||
|
elseif a:remote =~# '^ssh://'
|
||||||
|
let authority = matchstr(a:remote, '[^/?#]*', 6)
|
||||||
|
return 'ssh://' . fugitive#SshHostAlias(authority) . strpart(a:remote, 6 + len(authority))
|
||||||
|
endif
|
||||||
|
let scp_authority = matchstr(a:remote, '^[^:/]\+\ze:\%(//\)\@!')
|
||||||
|
if empty(scp_authority)
|
||||||
|
return a:remote
|
||||||
|
endif
|
||||||
|
let path = strpart(a:remote, len(scp_authority) + 1)
|
||||||
|
let alias = fugitive#SshHostAlias(scp_authority)
|
||||||
|
if alias !~# ':'
|
||||||
|
return alias . ':' . path
|
||||||
|
elseif path =~# '^/'
|
||||||
|
return 'ssh://' . alias . path
|
||||||
|
else
|
||||||
|
return a:remote
|
||||||
endif
|
endif
|
||||||
return substitute(a:remote,
|
|
||||||
\ '^ssh://\%([^@:/]\+@\)\=\zs[^/:]\+\|^\%([^@:/]\+@\)\=\zs[^/:]\+\ze:/\@!',
|
|
||||||
\ '\=fugitive#SshHostAlias(submatch(0))', '')
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! fugitive#RemoteUrl(...) abort
|
function! fugitive#RemoteUrl(...) abort
|
||||||
|
|||||||
Reference in New Issue
Block a user