Handle arbitrary :Gbrowse revision

This commit is contained in:
Tim Pope
2011-02-26 15:40:59 -05:00
parent 95311ab2d9
commit 383b2a43df
2 changed files with 106 additions and 60 deletions

View File

@@ -166,17 +166,12 @@ that are part of Git repositories).
:[range]Gbrowse! Like :Gbrowse, but put the URL on the clipboard rather :[range]Gbrowse! Like :Gbrowse, but put the URL on the clipboard rather
than opening it. than opening it.
:[range]Gbrowse - Like :Gbrowse, but tie the URL to the current HEAD. :[range]Gbrowse {revision}
This is useful in a work tree file to ensure the link Like :Gbrowse, but for a given |fugitive-revision|. A
always goes to the file as it currently stands, rather useful value here is -, which ties the URL to the
than whatever the latest on master is. Note that latest commit rather than a volatile branch.
when editing a historical file, said link is tied by
default to the commit you're currently browsing. (The
intent is that ultimately, one will be able to specify
any |fugitive-revision| here, but the current
implementation is limited to this one special case.)
[range]Gbrowse [...]@{remote} :[range]Gbrowse [...]@{remote}
Force using the given remote rather than the remote Force using the given remote rather than the remote
for the current branch. The remote is used to for the current branch. The remote is used to
determine which GitHub repository to link to. determine which GitHub repository to link to.

View File

@@ -1372,36 +1372,87 @@ endfunction
" }}}1 " }}}1
" Gbrowse {{{1 " Gbrowse {{{1
call s:command("-bar -bang -count=0 -nargs=? Gbrowse :execute s:Browse(<bang>0,<line1>,<count>,<f-args>)") call s:command("-bar -bang -count=0 -nargs=? -complete=customlist,s:EditComplete Gbrowse :execute s:Browse(<bang>0,<line1>,<count>,<f-args>)")
function! s:Browse(bang,line1,count,...) abort function! s:Browse(bang,line1,count,...) abort
try try
let rev = a:0 ? substitute(a:1,'@[[:alnum:]_-]\+\%(://.\{-\}\)\=$','','') : '' let rev = a:0 ? substitute(a:1,'@[[:alnum:]_-]*\%(://.\{-\}\)\=$','','') : ''
if a:0 && a:1 =~# '@[[:alnum:]_-]\+\%(://.\{-\}\)\=$' if rev ==# ''
let expanded = s:buffer().rev()
elseif rev ==# ':'
let expanded = s:buffer().path('/')
else
let expanded = s:buffer().expand(rev)
endif
let full = s:repo().translate(expanded)
let commit = ''
if full =~# '^fugitive://'
let commit = matchstr(full,'://.*//\zs\w\+')
let path = matchstr(full,'://.*//\w\+\zs/.*')
if commit =~ '..'
let type = s:repo().git_chomp('cat-file','-t',commit.s:sub(path,'^/',':'))
else
let type = 'blob'
endif
let path = path[1:-1]
elseif s:repo().bare()
let path = '.git/' . full[strlen(s:repo().dir())+1:-1]
let type = ''
else
let path = full[strlen(s:repo().tree())+1:-1]
if path =~# '^\.git/'
let type = ''
elseif isdirectory(full)
let type = 'tree'
else
let type = 'blob'
endif
endif
if a:0 && a:1 =~# '@[[:alnum:]_-]*\%(://.\{-\}\)\=$'
let remote = matchstr(a:1,'@\zs[[:alnum:]_-]\+\%(://.\{-\}\)\=$') let remote = matchstr(a:1,'@\zs[[:alnum:]_-]\+\%(://.\{-\}\)\=$')
elseif s:buffer().path() =~# '^\.git/refs/remotes/.' elseif path =~# '^\.git/refs/remotes/.'
let remote = matchstr(s:buffer().path(),'^\.git/refs/remotes/\zs[^/]\+') let remote = matchstr(path,'^\.git/refs/remotes/\zs[^/]\+')
else else
let remote = 'origin' let remote = 'origin'
let branch = matchstr(s:repo().head_ref(),'\<refs/heads/\zs.*') let branch = matchstr(rev,'^[[:alnum:]/._-]\+\ze[:^~@]')
if branch != '' if branch =~# 'HEAD$' && filereadable(s:repo().dir(branch))
let remote = s:repo().git_chomp('config','branch.'.branch.'.remote') let branch = matchstr(readfile(s:repo().dir(branch))[0],'\<refs/heads/\zs.*')
if remote == '' elseif branch ==# '' && path =~# '^\.git/refs/\w\+/'
let remote = 'origin' let branch = s:sub(path,'^\.git/refs/\w+/','')
endif
if filereadable(s:repo().dir('refs/remotes/'.branch))
let remote = matchstr(branch,'[^/]\+')
let rev = rev[strlen(remote)+1:-1]
else
if branch ==# ''
let branch = matchstr(s:repo().head_ref(),'\<refs/heads/\zs.*')
endif
if branch != ''
let remote = s:repo().git_chomp('config','branch.'.branch.'.remote')
if remote ==# ''
let remote = 'origin'
elseif rev[0:strlen(branch)-1] ==# branch && rev[strlen(branch)] =~# '[:^~@]'
let rev = s:repo().git_chomp('config','branch.'.branch.'.merge')[11:-1] . rev[strlen(branch):-1]
endif
endif endif
endif endif
endif endif
let raw = s:repo().git_chomp('config','remote.'.remote.'.url') let raw = s:repo().git_chomp('config','remote.'.remote.'.url')
if raw ==# '' if raw ==# ''
let raw = remote let raw = remote
endif endif
let url = s:github_url(s:buffer(),raw,rev,a:line1,a:count)
let url = s:github_url(s:repo(),raw,rev,commit,path,type,a:line1,a:count)
if url == '' if url == ''
let url = s:instaweb_url(s:buffer(),rev,a:count ? a:line1 : 0) let url = s:instaweb_url(s:repo(),rev,commit,path,type,a:count ? a:line1 : 0)
endif endif
if url == '' if url == ''
call s:throw("Instaweb failed to start and '".remote."' is not a GitHub remote") call s:throw("Instaweb failed to start and '".remote."' is not a GitHub remote")
endif endif
if a:bang if a:bang
let @* = url let @* = url
return 'echomsg '.string(url) return 'echomsg '.string(url)
@@ -1413,49 +1464,48 @@ function! s:Browse(bang,line1,count,...) abort
endtry endtry
endfunction endfunction
function! s:github_url(buffer,url,rev,line1,line2) abort function! s:github_url(repo,url,rev,commit,path,type,line1,line2) abort
let self = a:buffer let path = a:path
let path = self.path() let repo_path = matchstr(a:url,'^\%(https\=://\|git://\|git@\)github\.com[/:]\zs.\{-\}\ze\%(\.git\)\=$')
let repo = matchstr(a:url,'^\%(https\=://\|git://\|git@\)github\.com[/:]\zs.\{-\}\ze\%(\.git\)\=$') if repo_path ==# ''
if repo ==# ''
return '' return ''
endif endif
let root = 'http://github.com/' . repo let root = 'http://github.com/' . repo_path
if path =~# '^\.git/refs/heads/' if path =~# '^\.git/refs/heads/'
let branch = self.repo().git_chomp('config','branch.'.path[16:-1].'.merge')[11:-1] let branch = a:repo.git_chomp('config','branch.'.path[16:-1].'.merge')[11:-1]
if branch ==# '' if branch ==# ''
return root . '/tree/' . path[16:-1] return root . '/commits/' . path[16:-1]
else else
return root . '/tree/' . branch return root . '/commits/' . branch
endif endif
elseif path =~# '^\.git/refs/.' elseif path =~# '^\.git/refs/.'
return root . '/tree/' . matchstr(path,'[^/]\+$') return root . '/commits/' . matchstr(path,'[^/]\+$')
elseif path ==# '.git/config' elseif path =~# '.git/\%(config$\|hooks\>\)'
return root . '/admin' return root . '/admin'
elseif path =~# '^\.git\>' elseif path =~# '^\.git\>'
return root return root
endif endif
if a:rev ==# '-' if a:rev =~# '^[[:alnum:]._-]\+:'
let commit = self.repo().rev_parse('HEAD') let commit = matchstr(a:rev,'^[^:]*')
elseif self.commit() =~# '^\x\{40\}$' elseif a:commit =~# '^\d\=$'
let commit = self.commit() let local = matchstr(a:repo.head_ref(),'\<refs/heads/\zs.*')
else let commit = a:repo.git_chomp('config','branch.'.local.'.merge')[11:-1]
let local = matchstr(self.repo().head_ref(),'\<refs/heads/\zs.*')
let commit = self.repo().git_chomp('config','branch.'.local.'.merge')[11:-1]
if commit ==# '' if commit ==# ''
let commit = local let commit = local
endif endif
else
let commit = a:commit
endif endif
if self.type() ==# 'directory' || self.type() ==# 'tree' if a:type == 'tree'
let url = s:sub(root . '/tree/' . commit . '/' . path,'/$','') let url = s:sub(root . '/tree/' . commit . '/' . path,'/$','')
elseif self.type() ==# 'file' || self.type() ==# 'blob' elseif a:type == 'blob'
let url = root . '/blob/' . commit . '/' . path let url = root . '/blob/' . commit . '/' . path
if a:line2 && a:line1 == a:line2 if a:line2 && a:line1 == a:line2
let url .= '#L' . a:line1 let url .= '#L' . a:line1
elseif a:line2 elseif a:line2
let url .= '#L' . a:line1 . '-' . a:line2 let url .= '#L' . a:line1 . '-' . a:line2
endif endif
elseif self.type() ==# 'tag' elseif a:type == 'tag'
let commit = matchstr(getline(3),'^tag \zs.*') let commit = matchstr(getline(3),'^tag \zs.*')
let url = root . '/tree/' . commit let url = root . '/tree/' . commit
else else
@@ -1464,39 +1514,40 @@ function! s:github_url(buffer,url,rev,line1,line2) abort
return url return url
endfunction endfunction
function! s:instaweb_url(buffer,rev,...) abort function! s:instaweb_url(repo,rev,commit,path,type,...) abort
let self = a:buffer let output = a:repo.git_chomp('instaweb','-b','unknown')
let path = self.path()
let output = self.repo().git_chomp('instaweb','-b','unknown')
if output =~# 'http://' if output =~# 'http://'
let root = matchstr(output,'http://.*').'/?p='.fnamemodify(self.repo().dir(),':t') let root = matchstr(output,'http://.*').'/?p='.fnamemodify(a:repo.dir(),':t')
else else
return '' return ''
endif endif
if path =~# '^\.git/refs/.' if a:path =~# '^\.git/refs/.'
return root . ';a=shortlog;h=' . matchstr(path,'^\.git/\zs.*') return root . ';a=shortlog;h=' . matchstr(a:path,'^\.git/\zs.*')
elseif path =~# '^\.git\>' elseif a:path =~# '^\.git\>'
return root return root
endif endif
let url = root let url = root
if self.commit() =~# '^\x\{40\}$' || a:rev ==# '-' if a:commit =~# '^\x\{40\}$'
let ref = a:rev ==# '-' ? 'HEAD' : self.commit() if a:type ==# 'commit'
let url .= ';h=' . self.repo().rev_parse(ref . path) let url .= ';a=commit'
endif
let url .= ';h=' . a:repo.rev_parse(a:commit . (a:path == '' ? '' : ':' . a:path))
else else
if self.type() ==# 'file' || self.type() ==# 'blob' if a:type ==# 'blob'
let tmp = tempname() let tmp = tempname()
silent execute 'write !'.self.repo().git_command('hash-object','-w','--stdin').' > '.tmp silent execute 'write !'.a:repo.git_command('hash-object','-w','--stdin').' > '.tmp
let url .= ';h=' . readfile(tmp)[0] let url .= ';h=' . readfile(tmp)[0]
else else
try try
let url .= ';h=' . self.repo().rev_parse(self.path((self.commit() == '' ? 'HEAD' : ':'.self.commit()).':')) let url .= ';h=' . a:repo.rev_parse((a:commit == '' ? 'HEAD' : ':' . a:commit) . ':' . a:path)
catch /^fugitive:/ catch /^fugitive:/
throw 'fugitive: cannot browse uncommitted file'
endtry endtry
endif endif
let root .= ';hb=' . matchstr(self.repo().head_ref(),'[^ ]\+$') let root .= ';hb=' . matchstr(a:repo.head_ref(),'[^ ]\+$')
endif endif
if path !=# '' if a:path !=# ''
let url .= ';f=' . path let url .= ';f=' . a:path
endif endif
if a:0 && a:1 if a:0 && a:1
let url .= '#l' . a:1 let url .= '#l' . a:1