Skip to content

Commit fd83fca

Browse files
committed
Support expansion flags
1 parent 1e37867 commit fd83fca

File tree

2 files changed

+69
-27
lines changed

2 files changed

+69
-27
lines changed

autoload/fugitive.vim

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,12 @@ function! s:shellesc(arg) abort
5353
endif
5454
endfunction
5555

56+
let s:fnameescape = " \t\n*?[{`$\\%#'\"|!<"
5657
function! s:fnameescape(file) abort
5758
if exists('*fnameescape')
5859
return fnameescape(a:file)
5960
else
60-
return escape(a:file," \t\n*?[{`$\\%#'\"|!<")
61+
return escape(a:file, s:fnameescape)
6162
endif
6263
endfunction
6364

@@ -641,18 +642,13 @@ function! s:Generate(rev, ...) abort
641642
return fugitive#Route(object, dir)
642643
endfunction
643644

644-
function! s:RemoveDot(path, ...) abort
645-
if a:path !~# '^\./'
646-
return a:path
647-
endif
648-
let dir = a:0 ? a:1 : get(b:, 'git_dir', '')
649-
let cdir = fugitive#CommonDir(dir)
650-
if len(filter(['', '/tags', '/heads', '/remotes'], 'getftime(cdir . "/refs" . v:val . a:path[1:-1]) >= 0')) ||
651-
\ a:path =~# 'HEAD$' && filereadable(dir . a:path[1:-1]) ||
652-
\ a:path =~# '^\./refs/' && filereadable(cdir . a:path[1:-1])
653-
return a:path
645+
function! s:DotRelative(path) abort
646+
let cwd = getcwd()
647+
let path = substitute(a:path, '^[~$]\i*', '\=expand(submatch(0))', '')
648+
if s:cpath(cwd . '/', (path . '/')[0 : len(cwd)])
649+
return '.' . strpart(path, len(cwd))
654650
endif
655-
return a:path[2:-1]
651+
return a:path
656652
endfunction
657653

658654
function! fugitive#Object(...) abort
@@ -681,26 +677,71 @@ function! fugitive#Object(...) abort
681677
endif
682678
endfunction
683679

680+
let s:var = '\%(%\|#<\=\d\+\|##\=\)'
681+
let s:flag = '\%(:[p8~.htre]\|:g\=s\(.\).\{-\}\1.\{-\}\1\)'
682+
let s:expand = '\%(\(' . s:var . '\)\(' . s:flag . '*\)\(:S\)\=\)'
683+
684+
function! s:BufName(var) abort
685+
if a:var ==# '%'
686+
return bufname(get(b:, 'fugitive_blamed_bufnr', ''))
687+
elseif a:var =~# '^#\d*$'
688+
let nr = getbufvar(+a:var[1:-1], 'fugitive_blamed_bufnr', '')
689+
return bufname(nr ? nr : +a:var[1:-1])
690+
else
691+
return expand(a:var)
692+
endif
693+
endfunction
694+
695+
function! s:ExpandVar(other, var, flags, esc) abort
696+
if a:other =~# '^\'
697+
return a:other[1:-1]
698+
elseif a:other =~# '^!'
699+
let buffer = s:BufName(len(a:other) > 1 ? '#'. a:other[1:-1] : '%')
700+
let owner = s:Owner(buffer)
701+
return len(owner) ? owner : '@'
702+
endif
703+
let flags = a:flags
704+
let file = s:DotRelative(fugitive#Real(s:BufName(a:var)))
705+
while len(flags)
706+
let flag = matchstr(flags, s:flag)
707+
let flags = strpart(flags, len(flag))
708+
if flag ==# ':.'
709+
let file = s:DotRelative(file)
710+
else
711+
let file = fnamemodify(file, flag)
712+
endif
713+
endwhile
714+
let file = s:Slash(file)
715+
return (len(a:esc) ? shellescape(file) : file)
716+
endfunction
717+
684718
function! s:Expand(rev) abort
685719
if a:rev =~# '^:[0-3]$'
686720
let file = a:rev . s:Relative(':')
687721
elseif a:rev =~# '^-'
688722
let file = 'HEAD^{}' . a:rev[1:-1] . s:Relative(':')
689723
elseif a:rev =~# '^@{'
690724
let file = 'HEAD' . a:rev. s:Relative(':')
691-
elseif a:rev =~# '^[~^]/\@!'
725+
elseif a:rev =~# '^\^[0-9~^{]\|^\~[0-9~^]'
692726
let commit = substitute(s:DirCommitFile(@%)[1], '^\d\=$', 'HEAD', '')
693727
let file = commit . a:rev . s:Relative(':')
694728
else
695729
let file = a:rev
696730
endif
697-
return s:sub(substitute(file,
698-
\ '\([%#]\)$\|\\\([[:punct:]]\)','\=len(submatch(2)) ? submatch(2) : fugitive#Path(expand(submatch(1)))','g'),
699-
\ '\.\@<=/$','')
731+
return substitute(file,
732+
\ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
733+
\ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),"")', 'g')
734+
endfunction
735+
736+
function! fugitive#Expand(object) abort
737+
return substitute(a:object,
738+
\ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
739+
\ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5))', 'g')
700740
endfunction
701741

702742
function! s:ShellExpand(cmd) abort
703-
return substitute(a:cmd, '\\\@<![%#]:\@!', '\=s:RemoveDot(fugitive#Path(expand(submatch(0)), "./"))', 'g')
743+
return substitute(a:cmd, '\(\\[!#%]\|!\d*\)\|' . s:expand,
744+
\ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5))', 'g')
704745
endfunction
705746

706747
let s:trees = {}

doc/fugitive.txt

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -312,30 +312,31 @@ a Show the current tag, commit, or tree in an alternate
312312
SPECIFYING OBJECTS *fugitive-object* *fugitive-revision*
313313

314314
Fugitive objects are either work tree files or Git revisions as defined in the
315-
"SPECIFYING REVISIONS" section in the git-rev-parse man page, with a few
316-
convenience notations thrown in for good measure. For commands that accept an
315+
"SPECIFYING REVISIONS" section in the git-rev-parse man page, with expansions
316+
inspired by |cmdline-special| layered on top. For commands that accept an
317317
optional object, the default is the file in the index for work tree files and
318318
the work tree file for everything else. Example objects follow.
319319

320320
Object Meaning ~
321321
HEAD .git/HEAD
322-
refs/heads/x .git/refs/heads/x
322+
refs/heads/x .git/refs/heads/x (in "common dir" if present)
323323
@ The commit referenced by @ aka HEAD
324324
master^ The parent of the commit referenced by master
325325
master: The tree referenced by master
326326
./master The file named master in the working directory
327327
Makefile The file named Makefile in the work tree
328328
@^:Makefile The file named Makefile in the parent of HEAD
329329
:Makefile The file named Makefile in the index (writable)
330-
@:% The current file in HEAD
331-
- The current file in HEAD
332-
-^ The current file in the previous commit
333-
-~3 The current file 3 commits ago
334-
: .git/index (Same as |:Gstatus|)
330+
@~2:% The current file in the grandparent of HEAD
335331
:% The current file in the index
336332
:1:% The current file's common ancestor during a conflict
337-
:2:% The current file in the target branch during a conflict
338-
:3:% The current file in the merged branch during a conflict
333+
:2:# The alternate file in the target branch during a conflict
334+
:3:#5 The file from buffer #5 in the merged branch during a conflict
335+
! The commit owning the current file
336+
!:Makefile The file named Makefile in the commit owning the current file
337+
!3^2 The second parent of the commit owning buffer #3
338+
.git/config The repo config file
339+
: Same as |:Gstatus|
339340

340341
STATUSLINE *fugitive-statusline*
341342

0 commit comments

Comments
 (0)