From 8b39d29d947618913ba1db32de605a6335875b99 Mon Sep 17 00:00:00 2001 From: Tim Pope Date: Fri, 17 Jun 2022 23:52:30 -0400 Subject: [PATCH] Rework ref handing in :GBrowse This is still a garbage fire, but at least now the use of `.git/refs/` paths has been relegated to the public interface. This also fixes a lot of quirks, and hopefully doesn't introduce too many new ones. --- autoload/fugitive.vim | 151 ++++++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 73 deletions(-) diff --git a/autoload/fugitive.vim b/autoload/fugitive.vim index 1660bcc..473fe2b 100644 --- a/autoload/fugitive.vim +++ b/autoload/fugitive.vim @@ -7275,26 +7275,25 @@ function! fugitive#BrowseCommand(line1, count, range, bang, mods, arg, ...) abor endif endif endif - let refdir = fugitive#Find('.git/refs', dir) - for subdir in ['tags/', 'heads/', 'remotes/'] - if expanded !~# '^[./]' && filereadable(refdir . '/' . subdir . expanded) - let expanded = '.git/refs/' . subdir . expanded - endif - endfor let full = s:Generate(expanded, dir) let commit = '' - if full =~? '^fugitive:' + let ref = '' + let forbid_ref_as_commit = 0 + if full =~# '^fugitive:' let [dir, commit, path] = s:DirCommitFile(full) - if commit =~# '^:\=\d$' + if commit =~# '^\d\=$' let commit = '' - endif - if commit =~ '..' - let type = s:TreeChomp(['cat-file','-t',commit.s:sub(path,'^/',':')], dir) - let branch = matchstr(expanded, '^[^:]*') - elseif empty(path) || path ==# '/' - let type = 'tree' + let type = path =~# '^/\=$' ? 'tree' : 'blob' else - let type = 'blob' + let ref_match = matchlist(expanded, '^\(@{\@!\|[^:~^@]\+\)\(:\%(//\)\@!\|[~^@]\|$\)') + let ref = get(ref_match, 1, '') + let forbid_ref_as_commit = ref =~# '^@\=$' || ref_match[2] !~# '^:\=$' + if empty(path) && !forbid_ref_as_commit + let type = 'ref' + else + let type = s:ChompDefault(empty(path) ? 'commit': 'blob', + \ ['cat-file', '-t', commit . substitute(path, '^/', ':', '')], dir) + endif endif let path = path[1:-1] elseif empty(s:Tree(dir)) @@ -7302,47 +7301,41 @@ function! fugitive#BrowseCommand(line1, count, range, bang, mods, arg, ...) abor let type = '' else let path = fugitive#Path(full, '/')[1:-1] - if path =~# '^\.git/' - let type = '' - elseif isdirectory(full) || empty(path) + if empty(path) || isdirectory(full) let type = 'tree' else let type = 'blob' endif endif - if type ==# 'tree' && !empty(path) - let path = s:sub(path, '/\=$', '/') + if path =~# '^\.git/' + let ref = matchstr(path, '^.git/\zs\%(refs/[^/]\+/[^/].*\|\w*HEAD\)$') + let type = empty(ref) ? 'root': 'ref' + let path = '' endif - let actual_dir = fugitive#Find('.git/', dir) - if path =~# '^\.git/.*HEAD$' && filereadable(actual_dir . path[5:-1]) - let body = readfile(actual_dir . path[5:-1])[0] - if body =~# '^\x\{40,\}$' - let commit = body - let type = 'commit' - let path = '' - elseif body =~# '^ref: refs/' - let path = '.git/' . matchstr(body,'ref: \zs.*') + if empty(ref) || ref ==# 'HEAD' || ref ==# '@' + let ref = fugitive#Head(-1, dir) + endif + if ref =~# '^\x\{40,\}$' + let ref = '' + elseif !empty(ref) && ref !~# '^refs/' + let ref = FugitiveExecute(['rev-parse', '--symbolic-full-name', ref], dir).stdout[0] + if ref !~# '^refs/' + let ref = '' endif endif let merge = '' - if path =~# '^\.git/refs/remotes/.' - if empty(remote) - let remote = matchstr(path, '^\.git/refs/remotes/\zs[^/]\+') - let branch = matchstr(path, '^\.git/refs/remotes/[^/]\+/\zs.\+') - else - let merge = matchstr(path, '^\.git/refs/remotes/[^/]\+/\zs.\+') - let branch = merge - let path = '.git/refs/heads/'.merge - endif - elseif path =~# '^\.git/refs/heads/.' - let branch = path[16:-1] - elseif !exists('branch') - let branch = FugitiveHead(0, dir) - endif - if !empty(branch) - let r = FugitiveConfigGet('branch.'.branch.'.remote', config) - let m = FugitiveConfigGet('branch.'.branch.'.merge', config)[11:-1] + if !empty(remote) && ref =~# '^refs/remotes/[^/]\+/[^/]\|^refs/heads/[^/]' + let merge = matchstr(ref, '^refs/\%(heads/\|remotes/[^/]\+/\)\zs.\+') + let ref = 'refs/heads/' . merge + elseif ref =~# '^refs/remotes/[^/]\+/[^/]' + let remote = matchstr(ref, '^refs/remotes/\zs[^/]\+') + let merge = matchstr(ref, '^refs/remotes/[^/]\+/\zs.\+') + let ref = 'refs/heads/' . merge + elseif ref =~# '^refs/heads/[^/]' + let merge = strpart(ref, 11) + let r = FugitiveConfigGet('branch.' . merge . '.remote', config) + let m = FugitiveConfigGet('branch.' . merge . '.merge', config)[11:-1] if r ==# '.' && !empty(m) let r2 = FugitiveConfigGet('branch.'.m.'.remote', config) if r2 !~# '^\.\=$' @@ -7350,32 +7343,45 @@ function! fugitive#BrowseCommand(line1, count, range, bang, mods, arg, ...) abor let m = FugitiveConfigGet('branch.'.m.'.merge', config)[11:-1] endif endif - if empty(remote) + if r !~# '^\.\=$' let remote = r endif - if r ==# '.' || r ==# remote - let remote_ref = 'refs/remotes/' . remote . '/' . branch + if !empty(remote) + let remote_ref = 'refs/remotes/' . remote . '/' . merge if FugitiveConfigGet('push.default', config) ==# 'upstream' || \ !filereadable(FugitiveFind('.git/' . remote_ref, dir)) && empty(s:ChompDefault('', ['rev-parse', '--verify', remote_ref, '--'], dir)) let merge = m - if path =~# '^\.git/refs/heads/.' - let path = '.git/refs/heads/'.merge - endif - else - let merge = branch + let ref = 'refs/heads/' . merge endif endif endif + if empty(remote) || remote ==# '.' + let remote = s:RemoteDefault(config) + endif + if empty(merge) || empty(remote) + let provider_ref = ref + else + let provider_ref = 'refs/remotes/' . remote . '/' . merge + endif + if forbid_ref_as_commit || a:count >= 0 + let ref = '' + if type ==# 'ref' + let type = 'commit' + endif + elseif type ==# 'ref' && ref =~# '^refs/\%(heads\|tags\)/[^/]' + let commit = matchstr(ref, '^\Crefs/\%(heads\|tags\)/\zs.*') + endif + let line1 = a:count > 0 && type ==# 'blob' ? a:line1 : 0 let line2 = a:count > 0 && type ==# 'blob' ? a:count : 0 - if empty(commit) && path !~# '^\.git/' + if empty(commit) && type =~# '^\%(tree\|blob\)$' if a:count < 0 - let commit = merge - elseif len(merge) + let commit = matchstr(ref, '^\Crefs/\%(heads\|tags\)/\zs.*') + elseif len(provider_ref) let owner = s:Owner(@%, dir) - let commit = s:ChompDefault('', ['merge-base', 'refs/remotes/' . remote . '/' . merge, empty(owner) ? '@' : owner, '--'], dir) - if line2 > 0 && empty(arg) && commit =~# '^\x\{40,\}$' + let commit = s:ChompDefault('', ['merge-base', provider_ref, empty(owner) ? '@' : owner, '--'], dir) + if line2 > 0 && empty(arg) && commit =~# '^\x\{40,\}$' && type ==# 'blob' let blame_list = tempname() call writefile([commit, ''], blame_list, 'b') let blame_cmd = ['-c', 'blame.coloring=none', 'blame', '-L', line1.','.line2, '-S', blame_list, '-s', '--show-number'] @@ -7400,23 +7406,10 @@ function! fugitive#BrowseCommand(line1, count, range, bang, mods, arg, ...) abor endif endif if empty(commit) - let commit = readfile(fugitive#Find('.git/HEAD', dir), '', 1)[0] + let commit = fugitive#RevParse(empty(ref) ? 'HEAD' : ref, dir) endif - let i = 0 - while commit =~# '^ref: ' && i < 10 - let ref_file = refdir[0 : -5] . commit[5:-1] - if getfsize(ref_file) > 0 - let commit = readfile(ref_file, '', 1)[0] - else - let commit = fugitive#RevParse(commit[5:-1], dir) - endif - let i -= 1 - endwhile endif - if empty(remote) || remote ==# '.' - let remote = s:RemoteDefault(config) - endif if remote =~# ':' let remote_url = remote else @@ -7436,6 +7429,18 @@ function! fugitive#BrowseCommand(line1, count, range, bang, mods, arg, ...) abor \ 'line1': line1, \ 'line2': line2} + if empty(path) + if type ==# 'ref' && ref =~# '^refs/' + let opts.path = '.git/' . ref + let opts.type = '' + elseif type ==# 'root' + let opts.path ='.git/index' + let opts.type = '' + endif + elseif type ==# 'tree' && !empty(path) + let opts.path = s:sub(path, '/\=$', '/') + endif + let url = '' for Handler in get(g:, 'fugitive_browse_handlers', []) let url = call(Handler, [copy(opts)])