fugitive.vim 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. " fugitive.vim - A Git wrapper so awesome, it should be illegal
  2. " Maintainer: Tim Pope <http://tpo.pe/>
  3. " Version: 3.7
  4. " GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim
  5. if exists('g:loaded_fugitive')
  6. finish
  7. endif
  8. let g:loaded_fugitive = 1
  9. let s:bad_git_dir = '/$\|^fugitive:'
  10. " FugitiveGitDir() returns the detected Git dir for the given buffer number,
  11. " or the current buffer if no argument is passed. This will be an empty
  12. " string if no Git dir was found. Use !empty(FugitiveGitDir()) to check if
  13. " Fugitive is active in the current buffer. Do not rely on this for direct
  14. " filesystem access; use FugitiveFind('.git/whatever') instead.
  15. function! FugitiveGitDir(...) abort
  16. if v:version < 704
  17. return ''
  18. elseif !a:0 || type(a:1) == type(0) && a:1 < 0 || a:1 is# get(v:, 'true', -1)
  19. if exists('g:fugitive_event')
  20. return g:fugitive_event
  21. endif
  22. let dir = get(b:, 'git_dir', '')
  23. if empty(dir) && (empty(bufname('')) || &buftype =~# '^\%(nofile\|acwrite\|quickfix\|terminal\|prompt\)$')
  24. return FugitiveExtractGitDir(getcwd())
  25. elseif (!exists('b:git_dir') || b:git_dir =~# s:bad_git_dir) && &buftype =~# '^\%(nowrite\)\=$'
  26. let b:git_dir = FugitiveExtractGitDir(bufnr(''))
  27. return b:git_dir
  28. endif
  29. return dir =~# s:bad_git_dir ? '' : dir
  30. elseif type(a:1) == type(0) && a:1 isnot# 0
  31. if a:1 == bufnr('') && (!exists('b:git_dir') || b:git_dir =~# s:bad_git_dir) && &buftype =~# '^\%(nowrite\)\=$'
  32. let b:git_dir = FugitiveExtractGitDir(a:1)
  33. endif
  34. let dir = getbufvar(a:1, 'git_dir')
  35. return dir =~# s:bad_git_dir ? '' : dir
  36. elseif type(a:1) == type('')
  37. return substitute(s:Slash(a:1), '/$', '', '')
  38. elseif type(a:1) == type({})
  39. return get(a:1, 'fugitive_dir', get(a:1, 'git_dir', ''))
  40. else
  41. return ''
  42. endif
  43. endfunction
  44. " FugitiveReal() takes a fugitive:// URL and returns the corresponding path in
  45. " the work tree. This may be useful to get a cleaner path for inclusion in
  46. " the statusline, for example. Note that the file and its parent directories
  47. " are not guaranteed to exist.
  48. "
  49. " This is intended as an abstract API to be used on any "virtual" path. For a
  50. " buffer named foo://bar, check for a function named FooReal(), and if it
  51. " exists, call FooReal("foo://bar").
  52. function! FugitiveReal(...) abort
  53. let file = a:0 ? a:1 : @%
  54. if type(file) ==# type({})
  55. let dir = FugitiveGitDir(file)
  56. let tree = s:Tree(dir)
  57. return s:VimSlash(empty(tree) ? dir : tree)
  58. elseif file =~# '^\a\a\+:' || a:0 > 1
  59. return call('fugitive#Real', [file] + a:000[1:-1])
  60. elseif file =~# '^/\|^\a:\|^$'
  61. return file
  62. else
  63. return fnamemodify(file, ':p' . (file =~# '[\/]$' ? '' : ':s?[\/]$??'))
  64. endif
  65. endfunction
  66. " FugitiveFind() takes a Fugitive object and returns the appropriate Vim
  67. " buffer name. You can use this to generate Fugitive URLs ("HEAD:README") or
  68. " to get the absolute path to a file in the Git dir (".git/HEAD"), the common
  69. " dir (".git/config"), or the work tree (":(top)Makefile").
  70. "
  71. " An optional second argument provides the Git dir, or the buffer number of a
  72. " buffer with a Git dir. The default is the current buffer.
  73. function! FugitiveFind(...) abort
  74. if a:0 && (type(a:1) ==# type({}) || type(a:1) ==# type(0))
  75. return call('fugitive#Find', a:000[1:-1] + [FugitiveGitDir(a:1)])
  76. else
  77. return fugitive#Find(a:0 ? a:1 : bufnr(''), FugitiveGitDir(a:0 > 1 ? a:2 : -1))
  78. endif
  79. endfunction
  80. " FugitiveParse() takes a fugitive:// URL and returns a 2 element list
  81. " containing an object name ("commit:file") and the Git dir. It's effectively
  82. " the inverse of FugitiveFind().
  83. function! FugitiveParse(...) abort
  84. let path = s:Slash(a:0 ? a:1 : @%)
  85. if path !~# '^fugitive://'
  86. return ['', '']
  87. endif
  88. let [rev, dir] = fugitive#Parse(path)
  89. if !empty(dir)
  90. return [rev, dir]
  91. endif
  92. throw 'fugitive: invalid Fugitive URL ' . path
  93. endfunction
  94. " FugitiveGitVersion() queries the version of Git in use. Pass up to 3
  95. " arguments to return a Boolean of whether a certain minimum version is
  96. " available (FugitiveGitVersion(2,3,4) checks for 2.3.4 or higher) or no
  97. " arguments to get a raw string.
  98. function! FugitiveGitVersion(...) abort
  99. return call('fugitive#GitVersion', a:000)
  100. endfunction
  101. " FugitiveResult() returns an object encapsulating the result of the most
  102. " recent :Git command. Will be empty if no result is available. During a
  103. " User FugitiveChanged event, this is guaranteed to correspond to the :Git
  104. " command that triggered the event, or be empty if :Git was not the trigger.
  105. " Pass in the name of a temp buffer to get the result object for that command
  106. " instead. Contains the following keys:
  107. "
  108. " * "args": List of command arguments, starting with the subcommand. Will be
  109. " empty for usages like :Git --help.
  110. " * "git_dir": Git dir of the relevant repository.
  111. " * "exit_status": The integer exit code of the process.
  112. " * "flags": Flags passed directly to Git, like -c and --help.
  113. " * "file": Path to file containing command output. Not guaranteed to exist,
  114. " so verify with filereadable() before trying to access it.
  115. function! FugitiveResult(...) abort
  116. return call('fugitive#Result', a:000)
  117. endfunction
  118. " FugitiveExecute() runs Git with a list of arguments and returns a dictionary
  119. " with the following keys:
  120. "
  121. " * "exit_status": The integer exit code of the process.
  122. " * "stdout": The stdout produced by the process, as a list of lines.
  123. " * "stderr": The stdout produced by the process, as a list of lines.
  124. "
  125. " An optional second argument provides the Git dir, or the buffer number of a
  126. " buffer with a Git dir. The default is the current buffer.
  127. "
  128. " An optional final argument is a callback Funcref, for asynchronous
  129. " execution.
  130. function! FugitiveExecute(args, ...) abort
  131. return call('fugitive#Execute', [a:args] + a:000)
  132. endfunction
  133. " FugitiveShellCommand() turns an array of arugments into a Git command string
  134. " which can be executed with functions like system() and commands like :!.
  135. " Integer arguments will be treated as buffer numbers, and the appropriate
  136. " relative path inserted in their place.
  137. "
  138. " An optional second argument provides the Git dir, or the buffer number of a
  139. " buffer with a Git dir. The default is the current buffer.
  140. function! FugitiveShellCommand(...) abort
  141. return call('fugitive#ShellCommand', a:000)
  142. endfunction
  143. " FugitiveConfig() get returns an opaque structure that can be passed to other
  144. " FugitiveConfig functions in lieu of a Git directory. This can be faster
  145. " when performing multiple config queries. Do not rely on the internal
  146. " structure of the return value as it is not guaranteed. If you want a full
  147. " dictionary of every config value, use FugitiveConfigGetRegexp('.*').
  148. "
  149. " An optional argument provides the Git dir, or the buffer number of a
  150. " buffer with a Git dir. The default is the current buffer. Pass a blank
  151. " string to limit to the global config.
  152. function! FugitiveConfig(...) abort
  153. return call('fugitive#Config', a:000)
  154. endfunction
  155. " FugitiveConfigGet() retrieves a Git configuration value. An optional second
  156. " argument can be either the object returned by FugitiveConfig(), or a Git
  157. " dir or buffer number to be passed along to FugitiveConfig().
  158. function! FugitiveConfigGet(name, ...) abort
  159. return get(call('FugitiveConfigGetAll', [a:name] + (a:0 ? [a:1] : [])), 0, get(a:, 2, ''))
  160. endfunction
  161. " FugitiveConfigGetAll() is like FugitiveConfigGet() but returns a list of
  162. " all values.
  163. function! FugitiveConfigGetAll(name, ...) abort
  164. return call('fugitive#ConfigGetAll', [a:name] + a:000)
  165. endfunction
  166. " FugitiveConfigGetRegexp() retrieves a dictionary of all configuration values
  167. " with a key matching the given pattern. Like git config --get-regexp, but
  168. " using a Vim regexp. Second argument has same semantics as
  169. " FugitiveConfigGet().
  170. function! FugitiveConfigGetRegexp(pattern, ...) abort
  171. return call('fugitive#ConfigGetRegexp', [a:pattern] + a:000)
  172. endfunction
  173. " FugitiveRemoteUrl() retrieves the remote URL for the given remote name,
  174. " defaulting to the current branch's remote or "origin" if no argument is
  175. " given. Similar to `git remote get-url`, but also attempts to resolve HTTP
  176. " redirects and SSH host aliases.
  177. "
  178. " An optional second argument provides the Git dir, or the buffer number of a
  179. " buffer with a Git dir. The default is the current buffer.
  180. function! FugitiveRemoteUrl(...) abort
  181. return call('fugitive#RemoteUrl', a:000)
  182. endfunction
  183. " FugitiveRemote() returns a data structure parsed from the remote URL.
  184. " For example, for remote URL "https://me@example.com:1234/repo.git", the
  185. " returned dictionary will contain the following:
  186. "
  187. " * "scheme": "https"
  188. " * "authority": "user@example.com:1234"
  189. " * "path": "/repo.git" (for SSH URLs this may be a relative path)
  190. " * "pathname": "/repo.git" (always coerced to absolute path)
  191. " * "host": "example.com:1234"
  192. " * "hostname": "example.com"
  193. " * "port": "1234"
  194. " * "user": "me"
  195. " * "path": "/repo.git"
  196. " * "url": "https://me@example.com:1234/repo.git"
  197. function! FugitiveRemote(...) abort
  198. return call('fugitive#Remote', a:000)
  199. endfunction
  200. " FugitiveDidChange() triggers a FugitiveChanged event and reloads the summary
  201. " buffer for the current or given buffer number's repository. You can also
  202. " give the result of a FugitiveExecute() and that context will be made
  203. " available inside the FugitiveChanged() event.
  204. "
  205. " Passing the special argument 0 (the number zero) softly expires summary
  206. " buffers for all repositories. This can be used after a call to system()
  207. " with unclear implications.
  208. function! FugitiveDidChange(...) abort
  209. return call('fugitive#DidChange', a:000)
  210. endfunction
  211. " FugitiveHead() retrieves the name of the current branch. If the current HEAD
  212. " is detached, FugitiveHead() will return the empty string, unless the
  213. " optional argument is given, in which case the hash of the current commit
  214. " will be truncated to the given number of characters.
  215. "
  216. " An optional second argument provides the Git dir, or the buffer number of a
  217. " buffer with a Git dir. The default is the current buffer.
  218. function! FugitiveHead(...) abort
  219. if a:0 && (type(a:1) ==# type({}) || type(a:1) ==# type('') && a:1 !~# '^\d\+$')
  220. let dir = FugitiveGitDir(a:1)
  221. let arg = get(a:, 2, 0)
  222. elseif a:0 > 1
  223. let dir = FugitiveGitDir(a:2)
  224. let arg = a:1
  225. else
  226. let dir = FugitiveGitDir()
  227. let arg = get(a:, 1, 0)
  228. endif
  229. if empty(dir)
  230. return ''
  231. endif
  232. return fugitive#Head(arg, dir)
  233. endfunction
  234. function! FugitivePath(...) abort
  235. if a:0 > 2 && type(a:1) ==# type({})
  236. return fugitive#Path(a:2, a:3, FugitiveGitDir(a:1))
  237. elseif a:0 && type(a:1) ==# type({})
  238. return FugitiveReal(a:0 > 1 ? a:2 : @%)
  239. elseif a:0 > 1
  240. return fugitive#Path(a:1, a:2, FugitiveGitDir(a:0 > 2 ? a:3 : -1))
  241. else
  242. return FugitiveReal(a:0 ? a:1 : @%)
  243. endif
  244. endfunction
  245. function! FugitiveStatusline(...) abort
  246. if empty(FugitiveGitDir(bufnr('')))
  247. return ''
  248. endif
  249. return fugitive#Statusline()
  250. endfunction
  251. let s:resolved_git_dirs = {}
  252. function! FugitiveActualDir(...) abort
  253. let dir = call('FugitiveGitDir', a:000)
  254. if empty(dir)
  255. return ''
  256. endif
  257. if !has_key(s:resolved_git_dirs, dir)
  258. let s:resolved_git_dirs[dir] = s:ResolveGitDir(dir)
  259. endif
  260. return empty(s:resolved_git_dirs[dir]) ? dir : s:resolved_git_dirs[dir]
  261. endfunction
  262. let s:commondirs = {}
  263. function! FugitiveCommonDir(...) abort
  264. let dir = call('FugitiveActualDir', a:000)
  265. if empty(dir)
  266. return ''
  267. endif
  268. if has_key(s:commondirs, dir)
  269. return s:commondirs[dir]
  270. endif
  271. if getfsize(dir . '/HEAD') >= 10
  272. let cdir = get(s:ReadFile(dir . '/commondir', 1), 0, '')
  273. if cdir =~# '^/\|^\a:/'
  274. let s:commondirs[dir] = s:Slash(FugitiveVimPath(cdir))
  275. elseif len(cdir)
  276. let s:commondirs[dir] = simplify(dir . '/' . cdir)
  277. else
  278. let s:commondirs[dir] = dir
  279. endif
  280. else
  281. let s:commondirs[dir] = dir
  282. endif
  283. return s:commondirs[dir]
  284. endfunction
  285. function! FugitiveWorkTree(...) abort
  286. let tree = s:Tree(FugitiveGitDir(a:0 ? a:1 : -1))
  287. if tree isnot# 0 || a:0 > 1
  288. return tree
  289. else
  290. return ''
  291. endif
  292. endfunction
  293. function! FugitiveIsGitDir(...) abort
  294. if !a:0 || type(a:1) !=# type('')
  295. return !empty(call('FugitiveGitDir', a:000))
  296. endif
  297. let path = substitute(a:1, '[\/]$', '', '') . '/'
  298. return len(path) && getfsize(path.'HEAD') > 10 && (
  299. \ isdirectory(path.'objects') && isdirectory(path.'refs') ||
  300. \ getftype(path.'commondir') ==# 'file')
  301. endfunction
  302. function! s:ReadFile(path, line_count) abort
  303. if v:version < 800 && !filereadable(a:path)
  304. return []
  305. endif
  306. try
  307. return readfile(a:path, 'b', a:line_count)
  308. catch
  309. return []
  310. endtry
  311. endfunction
  312. let s:worktree_for_dir = {}
  313. let s:dir_for_worktree = {}
  314. function! s:Tree(path) abort
  315. if a:path =~# '/\.git$'
  316. return len(a:path) ==# 5 ? '/' : a:path[0:-6]
  317. elseif a:path ==# ''
  318. return ''
  319. endif
  320. let dir = FugitiveActualDir(a:path)
  321. if !has_key(s:worktree_for_dir, dir)
  322. let s:worktree_for_dir[dir] = ''
  323. let ext_wtc_pat = 'v:val =~# "^\\s*worktreeConfig *= *\\%(true\\|yes\\|on\\|1\\) *$"'
  324. let config = s:ReadFile(dir . '/config', 50)
  325. if len(config)
  326. let ext_wtc_config = filter(copy(config), ext_wtc_pat)
  327. if len(ext_wtc_config) == 1 && filereadable(dir . '/config.worktree')
  328. let config += s:ReadFile(dir . '/config.worktree', 50)
  329. endif
  330. else
  331. let worktree = fnamemodify(FugitiveVimPath(get(s:ReadFile(dir . '/gitdir', 1), '0', '')), ':h')
  332. if worktree ==# '.'
  333. unlet! worktree
  334. endif
  335. if len(filter(s:ReadFile(FugitiveCommonDir(dir) . '/config', 50), ext_wtc_pat))
  336. let config = s:ReadFile(dir . '/config.worktree', 50)
  337. endif
  338. endif
  339. if len(config)
  340. let wt_config = filter(copy(config), 'v:val =~# "^\\s*worktree *="')
  341. if len(wt_config)
  342. let worktree = FugitiveVimPath(matchstr(wt_config[0], '= *\zs.*'))
  343. elseif !exists('worktree')
  344. call filter(config,'v:val =~# "^\\s*bare *= *true *$"')
  345. if empty(config)
  346. let s:worktree_for_dir[dir] = 0
  347. endif
  348. endif
  349. endif
  350. if exists('worktree')
  351. let s:worktree_for_dir[dir] = s:Slash(resolve(worktree))
  352. let s:dir_for_worktree[s:worktree_for_dir[dir]] = dir
  353. endif
  354. endif
  355. if s:worktree_for_dir[dir] =~# '^\.'
  356. return simplify(dir . '/' . s:worktree_for_dir[dir])
  357. else
  358. return s:worktree_for_dir[dir]
  359. endif
  360. endfunction
  361. function! s:CeilingDirectories() abort
  362. if !exists('s:ceiling_directories')
  363. let s:ceiling_directories = []
  364. let resolve = 1
  365. for dir in split($GIT_CEILING_DIRECTORIES, has('win32') ? ';' : ':', 1)
  366. if empty(dir)
  367. let resolve = 0
  368. elseif resolve
  369. call add(s:ceiling_directories, s:Slash(resolve(dir)))
  370. else
  371. call add(s:ceiling_directories, s:Slash(dir))
  372. endif
  373. endfor
  374. endif
  375. return s:ceiling_directories + get(g:, 'ceiling_directories', [s:Slash(fnamemodify(expand('~'), ':h'))])
  376. endfunction
  377. function! s:ResolveGitDir(git_dir) abort
  378. let type = getftype(a:git_dir)
  379. if type ==# 'dir' && FugitiveIsGitDir(a:git_dir)
  380. return a:git_dir
  381. elseif type ==# 'link' && FugitiveIsGitDir(a:git_dir)
  382. return resolve(a:git_dir)
  383. elseif type !=# ''
  384. let line = get(s:ReadFile(a:git_dir, 1), 0, '')
  385. let file_dir = s:Slash(FugitiveVimPath(matchstr(line, '^gitdir: \zs.*')))
  386. if file_dir !~# '^/\|^\a:\|^$' && a:git_dir =~# '/\.git$' && FugitiveIsGitDir(a:git_dir[0:-5] . file_dir)
  387. return simplify(a:git_dir[0:-5] . file_dir)
  388. elseif file_dir =~# '^/\|^\a:' && FugitiveIsGitDir(file_dir)
  389. return file_dir
  390. endif
  391. endif
  392. return ''
  393. endfunction
  394. function! FugitiveExtractGitDir(path) abort
  395. if type(a:path) ==# type({})
  396. return get(a:path, 'fugitive_dir', get(a:path, 'git_dir', ''))
  397. elseif type(a:path) == type(0)
  398. let path = s:Slash(a:path > 0 ? bufname(a:path) : bufname(''))
  399. else
  400. let path = s:Slash(a:path)
  401. endif
  402. if path =~# '^fugitive://'
  403. return fugitive#Parse(path)[1]
  404. elseif empty(path)
  405. return ''
  406. endif
  407. let pre = substitute(matchstr(path, '^\a\a\+\ze:'), '^.', '\u&', '')
  408. if len(pre) && exists('*' . pre . 'Real')
  409. let path = {pre}Real(path)
  410. endif
  411. let root = s:Slash(fnamemodify(path, ':p:h'))
  412. let previous = ""
  413. let env_git_dir = len($GIT_DIR) ? s:Slash(simplify(fnamemodify(FugitiveVimPath($GIT_DIR), ':p:s?[\/]$??'))) : ''
  414. call s:Tree(env_git_dir)
  415. let ceiling_directories = s:CeilingDirectories()
  416. while root !=# previous && root !~# '^$\|^//[^/]*$'
  417. if index(ceiling_directories, root) >= 0
  418. break
  419. endif
  420. if root ==# $GIT_WORK_TREE && FugitiveIsGitDir(env_git_dir)
  421. return env_git_dir
  422. elseif has_key(s:dir_for_worktree, root)
  423. return s:dir_for_worktree[root]
  424. endif
  425. let dir = substitute(root, '[\/]$', '', '') . '/.git'
  426. let resolved = s:ResolveGitDir(dir)
  427. if !empty(resolved)
  428. let s:resolved_git_dirs[dir] = resolved
  429. return dir is# resolved || s:Tree(resolved) is# 0 ? dir : resolved
  430. elseif FugitiveIsGitDir(root)
  431. let s:resolved_git_dirs[root] = root
  432. return root
  433. endif
  434. let previous = root
  435. let root = fnamemodify(root, ':h')
  436. endwhile
  437. return ''
  438. endfunction
  439. function! FugitiveDetect(...) abort
  440. if v:version < 704
  441. return ''
  442. endif
  443. if exists('b:git_dir') && b:git_dir =~# '^$\|' . s:bad_git_dir
  444. unlet b:git_dir
  445. endif
  446. if !exists('b:git_dir')
  447. let b:git_dir = FugitiveExtractGitDir(a:0 ? a:1 : bufnr(''))
  448. endif
  449. return ''
  450. endfunction
  451. function! FugitiveGitPath(path) abort
  452. return s:Slash(a:path)
  453. endfunction
  454. if exists('+shellslash')
  455. function! s:Slash(path) abort
  456. return tr(a:path, '\', '/')
  457. endfunction
  458. function! s:VimSlash(path) abort
  459. return tr(a:path, '\/', &shellslash ? '//' : '\\')
  460. endfunction
  461. function FugitiveVimPath(path) abort
  462. return tr(a:path, '\/', &shellslash ? '//' : '\\')
  463. endfunction
  464. else
  465. function! s:Slash(path) abort
  466. return a:path
  467. endfunction
  468. function! s:VimSlash(path) abort
  469. return a:path
  470. endfunction
  471. if has('win32unix') && filereadable('/git-bash.exe')
  472. function! FugitiveVimPath(path) abort
  473. return substitute(a:path, '^\(\a\):', '/\l\1', '')
  474. endfunction
  475. else
  476. function! FugitiveVimPath(path) abort
  477. return a:path
  478. endfunction
  479. endif
  480. endif
  481. function! s:ProjectionistDetect() abort
  482. let file = s:Slash(get(g:, 'projectionist_file', ''))
  483. let dir = FugitiveExtractGitDir(file)
  484. let base = matchstr(file, '^fugitive://.\{-\}//\x\+')
  485. if empty(base)
  486. let base = s:Tree(dir)
  487. endif
  488. if !empty(base)
  489. if exists('+shellslash') && !&shellslash
  490. let base = tr(base, '/', '\')
  491. endif
  492. let file = FugitiveFind('.git/info/projections.json', dir)
  493. if filereadable(file)
  494. call projectionist#append(base, file)
  495. endif
  496. endif
  497. endfunction
  498. let s:addr_other = has('patch-8.1.560') || has('nvim-0.5.0') ? '-addr=other' : ''
  499. let s:addr_tabs = has('patch-7.4.542') ? '-addr=tabs' : ''
  500. let s:addr_wins = has('patch-7.4.542') ? '-addr=windows' : ''
  501. if exists(':G') != 2
  502. command! -bang -nargs=? -range=-1 -complete=customlist,fugitive#Complete G exe fugitive#Command(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)
  503. endif
  504. command! -bang -nargs=? -range=-1 -complete=customlist,fugitive#Complete Git exe fugitive#Command(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)
  505. if exists(':Gstatus') != 2 && get(g:, 'fugitive_legacy_commands', 0)
  506. exe 'command! -bang -bar -range=-1' s:addr_other 'Gstatus exe fugitive#Command(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  507. \ '|echohl WarningMSG|echomsg ":Gstatus is deprecated in favor of :Git (with no arguments)"|echohl NONE'
  508. endif
  509. for s:cmd in ['Commit', 'Revert', 'Merge', 'Rebase', 'Pull', 'Push', 'Fetch', 'Blame']
  510. if exists(':G' . tolower(s:cmd)) != 2 && get(g:, 'fugitive_legacy_commands', 0)
  511. exe 'command! -bang -nargs=? -range=-1 -complete=customlist,fugitive#' . s:cmd . 'Complete G' . tolower(s:cmd)
  512. \ 'echohl WarningMSG|echomsg ":G' . tolower(s:cmd) . ' is deprecated in favor of :Git ' . tolower(s:cmd) . '"|echohl NONE|'
  513. \ 'exe fugitive#Command(<line1>, <count>, +"<range>", <bang>0, "<mods>", "' . tolower(s:cmd) . ' " . <q-args>)'
  514. endif
  515. endfor
  516. unlet s:cmd
  517. exe "command! -bar -bang -nargs=? -complete=customlist,fugitive#CdComplete Gcd exe fugitive#Cd(<q-args>, 0)"
  518. exe "command! -bar -bang -nargs=? -complete=customlist,fugitive#CdComplete Glcd exe fugitive#Cd(<q-args>, 1)"
  519. exe 'command! -bang -nargs=? -range=-1' s:addr_wins '-complete=customlist,fugitive#GrepComplete Ggrep exe fugitive#GrepCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  520. exe 'command! -bang -nargs=? -range=-1' s:addr_wins '-complete=customlist,fugitive#GrepComplete Glgrep exe fugitive#GrepCommand(0, <count> > 0 ? <count> : 0, +"<range>", <bang>0, "<mods>", <q-args>)'
  521. exe 'command! -bang -nargs=? -range=-1 -complete=customlist,fugitive#LogComplete Gclog :exe fugitive#LogCommand(<line1>,<count>,+"<range>",<bang>0,"<mods>",<q-args>, "c")'
  522. exe 'command! -bang -nargs=? -range=-1 -complete=customlist,fugitive#LogComplete GcLog :exe fugitive#LogCommand(<line1>,<count>,+"<range>",<bang>0,"<mods>",<q-args>, "c")'
  523. exe 'command! -bang -nargs=? -range=-1 -complete=customlist,fugitive#LogComplete Gllog :exe fugitive#LogCommand(<line1>,<count>,+"<range>",<bang>0,"<mods>",<q-args>, "l")'
  524. exe 'command! -bang -nargs=? -range=-1 -complete=customlist,fugitive#LogComplete GlLog :exe fugitive#LogCommand(<line1>,<count>,+"<range>",<bang>0,"<mods>",<q-args>, "l")'
  525. exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Ge exe fugitive#Open("edit<bang>", 0, "<mods>", <q-args>)'
  526. exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gedit exe fugitive#Open("edit<bang>", 0, "<mods>", <q-args>)'
  527. exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gpedit exe fugitive#Open("pedit", <bang>0, "<mods>", <q-args>)'
  528. exe 'command! -bar -bang -nargs=* -range=-1' s:addr_other '-complete=customlist,fugitive#EditComplete Gsplit exe fugitive#Open((<count> > 0 ? <count> : "").(<count> ? "split" : "edit"), <bang>0, "<mods>", <q-args>)'
  529. exe 'command! -bar -bang -nargs=* -range=-1' s:addr_other '-complete=customlist,fugitive#EditComplete Gvsplit exe fugitive#Open((<count> > 0 ? <count> : "").(<count> ? "vsplit" : "edit!"), <bang>0, "<mods>", <q-args>)'
  530. exe 'command! -bar -bang -nargs=* -range=-1' s:addr_tabs '-complete=customlist,fugitive#EditComplete Gtabedit exe fugitive#Open((<count> >= 0 ? <count> : "")."tabedit", <bang>0, "<mods>", <q-args>)'
  531. exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gdrop exe fugitive#DropCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  532. if exists(':Gr') != 2
  533. exe 'command! -bar -bang -nargs=* -range=-1 -complete=customlist,fugitive#ReadComplete Gr exe fugitive#ReadCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  534. endif
  535. exe 'command! -bar -bang -nargs=* -range=-1 -complete=customlist,fugitive#ReadComplete Gread exe fugitive#ReadCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  536. exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gdiffsplit exe fugitive#Diffsplit(1, <bang>0, "<mods>", <q-args>)'
  537. exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Ghdiffsplit exe fugitive#Diffsplit(0, <bang>0, "<mods>", <q-args>)'
  538. exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gvdiffsplit exe fugitive#Diffsplit(0, <bang>0, "vertical <mods>", <q-args>)'
  539. exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gw exe fugitive#WriteCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  540. exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gwrite exe fugitive#WriteCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  541. exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gwq exe fugitive#WqCommand( <line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  542. exe 'command! -bar -bang -nargs=0 GRemove exe fugitive#RemoveCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  543. exe 'command! -bar -bang -nargs=0 GUnlink exe fugitive#UnlinkCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  544. exe 'command! -bar -bang -nargs=0 GDelete exe fugitive#DeleteCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  545. exe 'command! -bar -bang -nargs=1 -complete=customlist,fugitive#CompleteObject GMove exe fugitive#MoveCommand( <line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  546. exe 'command! -bar -bang -nargs=1 -complete=customlist,fugitive#RenameComplete GRename exe fugitive#RenameCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  547. if exists(':Gremove') != 2 && get(g:, 'fugitive_legacy_commands', 0)
  548. exe 'command! -bar -bang -nargs=0 Gremove exe fugitive#RemoveCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  549. \ '|echohl WarningMSG|echomsg ":Gremove is deprecated in favor of :GRemove"|echohl NONE'
  550. elseif exists(':Gremove') != 2 && !exists('g:fugitive_legacy_commands')
  551. exe 'command! -bar -bang -nargs=0 Gremove echoerr ":Gremove has been removed in favor of :GRemove"'
  552. endif
  553. if exists(':Gdelete') != 2 && get(g:, 'fugitive_legacy_commands', 0)
  554. exe 'command! -bar -bang -nargs=0 Gdelete exe fugitive#DeleteCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  555. \ '|echohl WarningMSG|echomsg ":Gdelete is deprecated in favor of :GDelete"|echohl NONE'
  556. elseif exists(':Gdelete') != 2 && !exists('g:fugitive_legacy_commands')
  557. exe 'command! -bar -bang -nargs=0 Gdelete echoerr ":Gremove has been removed in favor of :GRemove"'
  558. endif
  559. if exists(':Gmove') != 2 && get(g:, 'fugitive_legacy_commands', 0)
  560. exe 'command! -bar -bang -nargs=1 -complete=customlist,fugitive#CompleteObject Gmove exe fugitive#MoveCommand( <line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  561. \ '|echohl WarningMSG|echomsg ":Gmove is deprecated in favor of :GMove"|echohl NONE'
  562. elseif exists(':Gmove') != 2 && !exists('g:fugitive_legacy_commands')
  563. exe 'command! -bar -bang -nargs=? -complete=customlist,fugitive#CompleteObject Gmove'
  564. \ 'echoerr ":Gmove has been removed in favor of :GMove"'
  565. endif
  566. if exists(':Grename') != 2 && get(g:, 'fugitive_legacy_commands', 0)
  567. exe 'command! -bar -bang -nargs=1 -complete=customlist,fugitive#RenameComplete Grename exe fugitive#RenameCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  568. \ '|echohl WarningMSG|echomsg ":Grename is deprecated in favor of :GRename"|echohl NONE'
  569. elseif exists(':Grename') != 2 && !exists('g:fugitive_legacy_commands')
  570. exe 'command! -bar -bang -nargs=? -complete=customlist,fugitive#RenameComplete Grename'
  571. \ 'echoerr ":Grename has been removed in favor of :GRename"'
  572. endif
  573. exe 'command! -bar -bang -range=-1 -nargs=* -complete=customlist,fugitive#CompleteObject GBrowse exe fugitive#BrowseCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  574. if exists(':Gbrowse') != 2 && get(g:, 'fugitive_legacy_commands', 0)
  575. exe 'command! -bar -bang -range=-1 -nargs=* -complete=customlist,fugitive#CompleteObject Gbrowse exe fugitive#BrowseCommand(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)'
  576. \ '|if <bang>1|redraw!|endif|echohl WarningMSG|echomsg ":Gbrowse is deprecated in favor of :GBrowse"|echohl NONE'
  577. elseif exists(':Gbrowse') != 2 && !exists('g:fugitive_legacy_commands')
  578. exe 'command! -bar -bang -range=-1 -nargs=* -complete=customlist,fugitive#CompleteObject Gbrowse'
  579. \ 'echoerr ":Gbrowse has been removed in favor of :GBrowse"'
  580. endif
  581. if v:version < 704
  582. finish
  583. endif
  584. let g:io_fugitive = {
  585. \ 'simplify': function('fugitive#simplify'),
  586. \ 'resolve': function('fugitive#resolve'),
  587. \ 'getftime': function('fugitive#getftime'),
  588. \ 'getfsize': function('fugitive#getfsize'),
  589. \ 'getftype': function('fugitive#getftype'),
  590. \ 'filereadable': function('fugitive#filereadable'),
  591. \ 'filewritable': function('fugitive#filewritable'),
  592. \ 'isdirectory': function('fugitive#isdirectory'),
  593. \ 'getfperm': function('fugitive#getfperm'),
  594. \ 'setfperm': function('fugitive#setfperm'),
  595. \ 'readfile': function('fugitive#readfile'),
  596. \ 'writefile': function('fugitive#writefile'),
  597. \ 'glob': function('fugitive#glob'),
  598. \ 'delete': function('fugitive#delete'),
  599. \ 'Real': function('FugitiveReal')}
  600. augroup fugitive
  601. autocmd!
  602. autocmd BufNewFile,BufReadPost *
  603. \ if exists('b:git_dir') && b:git_dir =~# '^$\|' . s:bad_git_dir |
  604. \ unlet b:git_dir |
  605. \ endif
  606. autocmd FileType netrw
  607. \ if exists('b:git_dir') && b:git_dir =~# '^$\|' . s:bad_git_dir |
  608. \ unlet b:git_dir |
  609. \ endif
  610. autocmd BufFilePost * unlet! b:git_dir
  611. autocmd FileType git
  612. \ call fugitive#MapCfile()
  613. autocmd FileType gitcommit
  614. \ call fugitive#MapCfile('fugitive#MessageCfile()')
  615. autocmd FileType git,gitcommit
  616. \ if &foldtext ==# 'foldtext()' |
  617. \ setlocal foldtext=fugitive#Foldtext() |
  618. \ endif
  619. autocmd FileType fugitive
  620. \ call fugitive#MapCfile('fugitive#PorcelainCfile()')
  621. autocmd FileType gitrebase
  622. \ let &l:include = '^\%(pick\|squash\|edit\|reword\|fixup\|drop\|[pserfd]\)\>' |
  623. \ if &l:includeexpr !~# 'Fugitive' |
  624. \ let &l:includeexpr = 'v:fname =~# ''^\x\{4,\}$'' && len(FugitiveGitDir()) ? FugitiveFind(v:fname) : ' .
  625. \ (len(&l:includeexpr) ? &l:includeexpr : 'v:fname') |
  626. \ endif |
  627. \ let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') . '|setl inex= inc='
  628. autocmd BufReadCmd index{,.lock} nested
  629. \ if FugitiveIsGitDir(expand('<amatch>:p:h')) |
  630. \ let b:git_dir = s:Slash(expand('<amatch>:p:h')) |
  631. \ exe fugitive#BufReadStatus(v:cmdbang) |
  632. \ echohl WarningMSG |
  633. \ echo "fugitive: Direct editing of .git/" . expand('%:t') . " is deprecated" |
  634. \ echohl NONE |
  635. \ elseif filereadable(expand('<amatch>')) |
  636. \ silent doautocmd BufReadPre |
  637. \ keepalt noautocmd read <amatch> |
  638. \ silent 1delete_ |
  639. \ silent doautocmd BufReadPost |
  640. \ else |
  641. \ silent doautocmd BufNewFile |
  642. \ endif
  643. autocmd BufReadCmd fugitive://* nested exe fugitive#BufReadCmd() |
  644. \ if &path =~# '^\.\%(,\|$\)' |
  645. \ let &l:path = substitute(&path, '^\.,\=', '', '') |
  646. \ endif
  647. autocmd BufWriteCmd fugitive://* nested exe fugitive#BufWriteCmd()
  648. autocmd FileReadCmd fugitive://* nested exe fugitive#FileReadCmd()
  649. autocmd FileWriteCmd fugitive://* nested exe fugitive#FileWriteCmd()
  650. if exists('##SourceCmd')
  651. autocmd SourceCmd fugitive://* nested exe fugitive#SourceCmd()
  652. endif
  653. autocmd User Flags call Hoist('buffer', function('FugitiveStatusline'))
  654. autocmd User ProjectionistDetect call s:ProjectionistDetect()
  655. augroup END
  656. nmap <script><silent> <Plug>fugitive:y<C-G> :<C-U>call setreg(v:register, fugitive#Object(@%))<CR>
  657. nmap <script> <Plug>fugitive: <Nop>
  658. if get(g:, 'fugitive_no_maps')
  659. finish
  660. endif
  661. function! s:Map(mode, lhs, rhs, flags) abort
  662. let flags = a:flags . (a:rhs =~# '<Plug>' ? '' : '<script>') . '<nowait>'
  663. let head = a:lhs
  664. let tail = ''
  665. let keys = get(g:, a:mode.'remap', {})
  666. if len(keys) && type(keys) == type({})
  667. while !empty(head)
  668. if has_key(keys, head)
  669. let head = keys[head]
  670. if empty(head)
  671. return
  672. endif
  673. break
  674. endif
  675. let tail = matchstr(head, '<[^<>]*>$\|.$') . tail
  676. let head = substitute(head, '<[^<>]*>$\|.$', '', '')
  677. endwhile
  678. endif
  679. if empty(mapcheck(head.tail, a:mode))
  680. exe a:mode.'map' flags head.tail a:rhs
  681. endif
  682. endfunction
  683. call s:Map('c', '<C-R><C-G>', 'fnameescape(fugitive#Object(@%))', '<expr>')
  684. call s:Map('n', 'y<C-G>', ':<C-U>call setreg(v:register, fugitive#Object(@%))<CR>', '<silent>')