Fix q deprecation message
[vim-fugitive.git] / plugin / fugitive.vim
blobb7d191fd4d6eb26848925d1f71abeb33ceef27ab
1 " fugitive.vim - A Git wrapper so awesome, it should be illegal
2 " Maintainer:   Tim Pope <http://tpo.pe/>
3 " Version:      3.0
4 " GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim
6 if exists('g:loaded_fugitive')
7   finish
8 endif
9 let g:loaded_fugitive = 1
11 function! FugitiveGitDir(...) abort
12   if !a:0 || a:1 ==# -1
13     return get(b:, 'git_dir', '')
14   elseif type(a:1) == type(0)
15     return getbufvar(a:1, 'git_dir')
16   elseif type(a:1) == type('')
17     return substitute(s:Slash(a:1), '/$', '', '')
18   else
19     return ''
20   endif
21 endfunction
23 " FugitiveReal() takes a fugitive:// URL and returns the corresponding path in
24 " the work tree.  This may be useful to get a cleaner path for inclusion in
25 " the statusline, for example.  Note that the file and its parent directories
26 " are not guaranteed to exist.
28 " This is intended as an abstract API to be used on any "virtual" path.  For a
29 " buffer named foo://bar, check for a function named FooReal(), and if it
30 " exists, call FooReal("foo://bar").
31 function! FugitiveReal(...) abort
32   let file = a:0 ? a:1 : @%
33   if file =~# '^\a\a\+:' || a:0 > 1
34     return call('fugitive#Real', [file] + a:000[1:-1])
35   elseif file =~# '^/\|^\a:\|^$'
36     return file
37   else
38     return fnamemodify(file, ':p' . (file =~# '[\/]$' ? '' : ':s?[\/]$??'))
39   endif
40 endfunction
42 " FugitiveFind() takes a Fugitive object and returns the appropriate Vim
43 " buffer name.  You can use this to generate Fugitive URLs ("HEAD:README") or
44 " to get the absolute path to a file in the Git dir (".git/HEAD"), the common
45 " dir (".git/config"), or the work tree (":(top)Makefile").
47 " An optional second argument provides the Git dir, or the buffer number of a
48 " buffer with a Git dir.  The default is the current buffer.
49 function! FugitiveFind(...) abort
50   return fugitive#Find(a:0 ? a:1 : bufnr(''), FugitiveGitDir(a:0 > 1 ? a:2 : -1))
51 endfunction
53 function! FugitivePath(...) abort
54   if a:0 > 1
55     return fugitive#Path(a:1, a:2, FugitiveGitDir(a:0 > 2 ? a:3 : -1))
56   else
57     return FugitiveReal(a:0 ? a:1 : @%)
58   endif
59 endfunction
61 " FugitiveParse() takes a fugitive:// URL and returns a 2 element list
62 " containing the Git dir and an object name ("commit:file").  It's effectively
63 " then inverse of FugitiveFind().
64 function! FugitiveParse(...) abort
65   let path = s:Slash(a:0 ? a:1 : @%)
66   if path !~# '^fugitive:'
67     return ['', '']
68   endif
69   let vals = matchlist(path, '\c^fugitive:\%(//\)\=\(.\{-\}\)\%(//\|::\)\(\x\{40,\}\|[0-3]\)\(/.*\)\=$')
70   if len(vals)
71     return [(vals[2] =~# '^.$' ? ':' : '') . vals[2] . substitute(vals[3], '^/', ':', ''), vals[1]]
72   endif
73   let v:errmsg = 'fugitive: invalid Fugitive URL ' . path
74   throw v:errmsg
75 endfunction
77 " FugitivePrepare() constructs a Git command string which can be executed with
78 " functions like system() and commands like :!.  Integer arguments will be
79 " treated as buffer numbers, and the appropriate relative path inserted in
80 " their place.
82 " If the first argument is a string that looks like a path or an empty string,
83 " it will be used as the Git dir.  If it's a buffer number, the Git dir for
84 " that buffer will be used.  The default is the current buffer.
85 function! FugitivePrepare(...) abort
86   return call('fugitive#Prepare', a:000)
87 endfunction
89 function! FugitiveConfig(...) abort
90   if a:0 == 2 && type(a:2) != type({})
91     return fugitive#Config(a:1, FugitiveGitDir(a:2))
92   elseif a:0 == 1 && a:1 !~# '^[[:alnum:]-]\+\.'
93     return fugitive#Config(FugitiveGitDir(a:1))
94   else
95     return call('fugitive#Config', a:000)
96   endif
97 endfunction
99 function! FugitiveRemoteUrl(...) abort
100   return fugitive#RemoteUrl(a:0 ? a:1 : '', FugitiveGitDir(a:0 > 1 ? a:2 : -1))
101 endfunction
103 function! FugitiveHead(...) abort
104   let dir = FugitiveGitDir(a:0 > 1 ? a:2 : -1)
105   if empty(dir)
106     return ''
107   endif
108   return fugitive#Head(a:0 ? a:1 : 0, dir)
109 endfunction
111 function! FugitiveStatusline(...) abort
112   if !exists('b:git_dir')
113     return ''
114   endif
115   return fugitive#Statusline()
116 endfunction
118 function! FugitiveCommonDir(...) abort
119   let dir = FugitiveGitDir(a:0 ? a:1 : -1)
120   if empty(dir)
121     return ''
122   endif
123   return fugitive#CommonDir(dir)
124 endfunction
126 function! FugitiveWorkTree(...) abort
127   return s:Tree(FugitiveGitDir(a:0 ? a:1 : -1))
128 endfunction
130 function! FugitiveIsGitDir(path) abort
131   let path = substitute(a:path, '[\/]$', '', '') . '/'
132   return len(a:path) && getfsize(path.'HEAD') > 10 && (
133         \ isdirectory(path.'objects') && isdirectory(path.'refs') ||
134         \ getftype(path.'commondir') ==# 'file')
135 endfunction
137 let s:worktree_for_dir = {}
138 let s:dir_for_worktree = {}
139 function! s:Tree(path) abort
140   let dir = a:path
141   if dir =~# '/\.git$'
142     return len(dir) ==# 5 ? '/' : dir[0:-6]
143   elseif dir ==# ''
144     return ''
145   endif
146   if !has_key(s:worktree_for_dir, dir)
147     let s:worktree_for_dir[dir] = ''
148     let config_file = dir . '/config'
149     if filereadable(config_file)
150       let config = readfile(config_file,'',10)
151       call filter(config,'v:val =~# "^\\s*worktree *="')
152       if len(config) == 1
153         let worktree = s:Slash(FugitiveVimPath(matchstr(config[0], '= *\zs.*')))
154       endif
155     elseif filereadable(dir . '/gitdir')
156       let worktree = s:Slash(fnamemodify(FugitiveVimPath(readfile(dir . '/gitdir')[0]), ':h'))
157       if worktree ==# '.'
158         unlet! worktree
159       endif
160     endif
161     if exists('worktree')
162       let s:worktree_for_dir[dir] = worktree
163       let s:dir_for_worktree[s:worktree_for_dir[dir]] = dir
164     endif
165   endif
166   if s:worktree_for_dir[dir] =~# '^\.'
167     return simplify(dir . '/' . s:worktree_for_dir[dir])
168   else
169     return s:worktree_for_dir[dir]
170   endif
171 endfunction
173 function! FugitiveExtractGitDir(path) abort
174   let path = s:Slash(a:path)
175   if path =~# '^fugitive:'
176     return matchstr(path, '\C^fugitive:\%(//\)\=\zs.\{-\}\ze\%(//\|::\|$\)')
177   elseif isdirectory(path)
178     let path = fnamemodify(path, ':p:s?/$??')
179   else
180     let path = fnamemodify(path, ':p:h:s?/$??')
181   endif
182   let pre = substitute(matchstr(path, '^\a\a\+\ze:'), '^.', '\u&', '')
183   if len(pre) && exists('*' . pre . 'Real')
184     let path = s:Slash({pre}Real(path))
185   endif
186   let root = resolve(path)
187   if root !=# path
188     silent! exe (haslocaldir() ? 'lcd' : exists(':tcd') && haslocaldir(-1) ? 'tcd' : 'cd') '.'
189   endif
190   let previous = ""
191   let env_git_dir = len($GIT_DIR) ? s:Slash(simplify(fnamemodify(FugitiveVimPath($GIT_DIR), ':p:s?[\/]$??'))) : ''
192   call s:Tree(env_git_dir)
193   while root !=# previous
194     if root =~# '\v^//%([^/]+/?)?$'
195       break
196     endif
197     if index(split($GIT_CEILING_DIRECTORIES, ':'), root) >= 0
198       break
199     endif
200     if root ==# $GIT_WORK_TREE && FugitiveIsGitDir(env_git_dir)
201       return env_git_dir
202     elseif has_key(s:dir_for_worktree, root)
203       return s:dir_for_worktree[root]
204     endif
205     let dir = substitute(root, '[\/]$', '', '') . '/.git'
206     let type = getftype(dir)
207     if type ==# 'dir' && FugitiveIsGitDir(dir)
208       return dir
209     elseif type ==# 'link' && FugitiveIsGitDir(dir)
210       return resolve(dir)
211     elseif type !=# '' && filereadable(dir)
212       let line = get(readfile(dir, '', 1), 0, '')
213       let file_dir = s:Slash(FugitiveVimPath(matchstr(line, '^gitdir: \zs.*')))
214       if file_dir !~# '^/\|^\a:' && FugitiveIsGitDir(root . '/' . file_dir)
215         return simplify(root . '/' . file_dir)
216       elseif len(file_dir) && FugitiveIsGitDir(file_dir)
217         return file_dir
218       endif
219     elseif FugitiveIsGitDir(root)
220       return root
221     endif
222     let previous = root
223     let root = fnamemodify(root, ':h')
224   endwhile
225   return ''
226 endfunction
228 function! FugitiveDetect(path) abort
229   if exists('b:git_dir') && b:git_dir =~# '^$\|/$\|^fugitive:'
230     unlet b:git_dir
231   endif
232   if !exists('b:git_dir')
233     let dir = FugitiveExtractGitDir(a:path)
234     if dir !=# ''
235       let b:git_dir = dir
236     endif
237   endif
238   if exists('b:git_dir')
239     return fugitive#Init()
240   endif
241 endfunction
243 function! FugitiveVimPath(path) abort
244   if exists('+shellslash') && !&shellslash
245     return tr(a:path, '/', '\')
246   else
247     return a:path
248   endif
249 endfunction
251 function! FugitiveGitPath(path) abort
252   return s:Slash(a:path)
253 endfunction
255 function! s:Slash(path) abort
256   if exists('+shellslash')
257     return tr(a:path, '\', '/')
258   else
259     return a:path
260   endif
261 endfunction
263 function! s:ProjectionistDetect() abort
264   let file = s:Slash(get(g:, 'projectionist_file', ''))
265   let dir = FugitiveExtractGitDir(file)
266   let base = matchstr(file, '^fugitive://.\{-\}//\x\+')
267   if empty(base)
268     let base = s:Tree(dir)
269   endif
270   if len(base)
271     if exists('+shellslash') && !&shellslash
272       let base = tr(base, '/', '\')
273     endif
274     let file = FugitiveCommonDir(dir) . '/info/projections.json'
275     if filereadable(file)
276       call projectionist#append(base, file)
277     endif
278   endif
279 endfunction
281 augroup fugitive
282   autocmd!
284   autocmd BufNewFile,BufReadPost * call FugitiveDetect(expand('<amatch>:p'))
285   autocmd FileType           netrw call FugitiveDetect(fnamemodify(get(b:, 'netrw_curdir', expand('<amatch>')), ':p'))
286   autocmd User NERDTreeInit,NERDTreeNewRoot
287         \ if exists('b:NERDTree.root.path.str') |
288         \   call FugitiveDetect(b:NERDTree.root.path.str()) |
289         \ endif
290   autocmd VimEnter * if empty(expand('<amatch>'))|call FugitiveDetect(getcwd())|endif
291   autocmd CmdWinEnter * call FugitiveDetect(expand('#:p'))
293   autocmd FileType git
294         \ if len(FugitiveGitDir()) |
295         \   call fugitive#MapJumps() |
296         \   call fugitive#MapCfile() |
297         \ endif
298   autocmd FileType gitcommit
299         \ if len(FugitiveGitDir()) |
300         \   call fugitive#MapCfile('fugitive#MessageCfile()') |
301         \ endif
302   autocmd FileType fugitive
303         \ if len(FugitiveGitDir()) |
304         \   call fugitive#MapCfile('fugitive#StatusCfile()') |
305         \ endif
306   autocmd FileType gitrebase
307         \ let &l:include = '^\%(pick\|squash\|edit\|reword\|fixup\|drop\|[pserfd]\)\>' |
308         \ if len(FugitiveGitDir()) |
309         \   let &l:includeexpr = 'v:fname =~# ''^\x\{4,\}$'' ? FugitiveFind(v:fname) : ' .
310         \   (len(&l:includeexpr) ? &l:includeexpr : 'v:fname') |
311         \ endif |
312         \ let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') . '|setl inex= inc='
314   autocmd BufReadCmd index{,.lock}
315         \ if FugitiveIsGitDir(expand('<amatch>:p:h')) |
316         \   let b:git_dir = s:Slash(expand('<amatch>:p:h')) |
317         \   exe fugitive#BufReadStatus() |
318         \ elseif filereadable(expand('<amatch>')) |
319         \   silent doautocmd BufReadPre |
320         \   keepalt read <amatch> |
321         \   1delete_ |
322         \   silent doautocmd BufReadPost |
323         \ else |
324         \   silent doautocmd BufNewFile |
325         \ endif
327   autocmd BufReadCmd    fugitive://*//*             exe fugitive#BufReadCmd()
328   autocmd BufWriteCmd   fugitive://*//[0-3]/*       exe fugitive#BufWriteCmd()
329   autocmd FileReadCmd   fugitive://*//*             exe fugitive#FileReadCmd()
330   autocmd FileWriteCmd  fugitive://*//[0-3]/*       exe fugitive#FileWriteCmd()
331   if exists('##SourceCmd')
332     autocmd SourceCmd     fugitive://*//*    nested exe fugitive#SourceCmd()
333   endif
335   autocmd User Flags call Hoist('buffer', function('FugitiveStatusline'))
337   autocmd User ProjectionistDetect call s:ProjectionistDetect()
338 augroup END
340 let g:io_fugitive = {
341       \ 'simplify': function('fugitive#simplify'),
342       \ 'resolve': function('fugitive#resolve'),
343       \ 'getftime': function('fugitive#getftime'),
344       \ 'getfsize': function('fugitive#getfsize'),
345       \ 'getftype': function('fugitive#getftype'),
346       \ 'filereadable': function('fugitive#filereadable'),
347       \ 'filewritable': function('fugitive#filewritable'),
348       \ 'isdirectory': function('fugitive#isdirectory'),
349       \ 'getfperm': function('fugitive#getfperm'),
350       \ 'setfperm': function('fugitive#setfperm'),
351       \ 'readfile': function('fugitive#readfile'),
352       \ 'writefile': function('fugitive#writefile'),
353       \ 'glob': function('fugitive#glob'),
354       \ 'delete': function('fugitive#delete'),
355       \ 'Real': function('FugitiveReal')}