diff --git a/plugin/fugitive.vim b/plugin/fugitive.vim index d9d7b0e02c8096da7032a1b66ecd7b015259a13a..6030d7cab7b3a8187668cd0c26e6eca45c426645 100644 --- a/plugin/fugitive.vim +++ b/plugin/fugitive.vim @@ -298,49 +298,86 @@ function! s:repo_bare() dict abort endif endfunction +function! s:repo_disambiguate(spec) dict abort + if a:spec =~# '^:[0-3]:\|^:\=/\|^:\=$\|^\x\{40\}$' + return a:spec + elseif a:spec =~# '^:' + return ':0' . a:spec + elseif a:spec ==# '^\.' + return '/' . a:spec + elseif a:spec =~# '^[^:~^]*\.\.' + let before = self.qualify_ref(matchstr(a:spec, '.\{-\}\ze\.\.')) + let after = self.qualify_ref(matchstr(a:spec, '\.\.\.\=\zs.*')) + if before =~# '^/' || after =~# '^/' + return '/' . a:spec + else + return before . matchstr(a:spec, '\.\.\.\=') . after + endif + endif + let head = s:sub(a:spec, '[:~^].*|\@\{.*', '') + let rest = strpart(a:spec, len(head)) + if head ==# '@' + return 'HEAD'.rest + endif + if head =~# "[[?*\001-\037\177]".'\|\%([./]\|\.lock\)\%(/\|$\)' + return '/' . a:spec + endif + let packed = {} + if filereadable(self.dir('packed-refs')) + for [v, k] in map(readfile(self.dir('packed-refs')), 'split(v:val, " ")') + let packed[k] = v + endfor + endif + for pattern in ['%s', 'refs/%s', 'refs/tags/%s', 'refs/heads/%s', 'refs/remotes/%s', 'refs/remotes/%s/HEAD'] + let ref = printf(pattern, head) + if filereadable(self.dir(ref)) || has_key(packed, ref) + return ref . rest + endif + endfor + let tag = matchstr(head, '.*\ze-\d\+-g\x\+$') + if !empty(tag) && (filereadable(self.dir('refs/tags/'.tag)) || has_key(packed, ref)) + return head . rest + endif + if head =~# '^\x\{4,40\}$' + try + return self.rev_parse(head) . rest + catch /^fugitive:/ + endtry + endif + return '/' . a:spec +endfunction + function! s:repo_translate(spec) dict abort - if a:spec ==# '.' || a:spec ==# '/.' - return self.bare() ? self.dir() : self.tree() - elseif a:spec =~# '^/\=\.git$' && self.bare() + let spec = self.disambiguate(a:spec) + if spec =~# '^/\=\.git$' && self.bare() return self.dir() - elseif a:spec =~# '^/\=\.git/' - return self.dir(s:sub(a:spec, '^/=\.git/', '')) - elseif a:spec =~# '^/' - return self.tree().a:spec - elseif a:spec =~# '^:[0-3]:' - return 'fugitive://'.self.dir().'//'.a:spec[1].'/'.a:spec[3:-1] - elseif a:spec ==# ':' + elseif spec =~# '^/\=\.git/' + return self.dir(s:sub(spec, '^/=\.git/', '')) + elseif spec =~# '^/' + return self.tree().spec + elseif spec =~# '^:[0-3]:' + return 'fugitive://'.self.dir().'//'.spec[1].'/'.spec[3:-1] + elseif spec ==# ':' if $GIT_INDEX_FILE =~# '/[^/]*index[^/]*\.lock$' && fnamemodify($GIT_INDEX_FILE,':p')[0:strlen(self.dir())] ==# self.dir('') && filereadable($GIT_INDEX_FILE) return fnamemodify($GIT_INDEX_FILE,':p') else return self.dir('index') endif - elseif a:spec =~# '^:/' - let ref = self.rev_parse(matchstr(a:spec,'.[^:]*')) + elseif spec =~# '^:/' + let ref = self.rev_parse(matchstr(spec,'.[^:]*')) return 'fugitive://'.self.dir().'//'.ref - elseif a:spec =~# '^:' - return 'fugitive://'.self.dir().'//0/'.a:spec[1:-1] - elseif a:spec =~# 'HEAD\|^refs/' && a:spec !~ ':' && filereadable(self.dir(a:spec)) - return self.dir(a:spec) - elseif filereadable(self.dir('refs/'.a:spec)) - return self.dir('refs/'.a:spec) - elseif filereadable(self.dir('refs/tags/'.a:spec)) - return self.dir('refs/tags/'.a:spec) - elseif filereadable(self.dir('refs/heads/'.a:spec)) - return self.dir('refs/heads/'.a:spec) - elseif filereadable(self.dir('refs/remotes/'.a:spec)) - return self.dir('refs/remotes/'.a:spec) - elseif filereadable(self.dir('refs/remotes/'.a:spec.'/HEAD')) - return self.dir('refs/remotes/'.a:spec,'/HEAD') - else - try - let ref = self.rev_parse(matchstr(a:spec,'[^:]*')) - let path = s:sub(matchstr(a:spec,':.*'),'^:','/') - return 'fugitive://'.self.dir().'//'.ref.path - catch /^fugitive:/ - return self.tree(a:spec) - endtry endif + + let ref = matchstr(spec,'[^:]*') + let path = s:sub(matchstr(spec,':.*'),'^:','/') + if empty(path) && ref !~# '[~^]' + if filereadable(self.dir(ref)) + return self.dir(ref) + else + return self.dir('packed-refs') + endif + endif + return 'fugitive://'.self.dir().'//'.self.rev_parse(ref).path endfunction function! s:repo_head(...) dict abort @@ -359,7 +396,7 @@ function! s:repo_head(...) dict abort return branch endfunction -call s:add_methods('repo',['dir','tree','bare','translate','head']) +call s:add_methods('repo',['dir','tree','bare','disambiguate','translate','head']) function! s:repo_git_command(...) dict abort let git = g:fugitive_git_executable . ' --git-dir='.s:shellesc(self.git_dir)