mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-02-03 05:20:04 +08:00
182 lines
5.4 KiB
VimL
182 lines
5.4 KiB
VimL
function! s:_vital_loaded(V) abort
|
|
let s:INI = a:V.import('Text.INI')
|
|
let s:Path = a:V.import('System.Filepath')
|
|
let s:String = a:V.import('Data.String')
|
|
let s:Store = a:V.import('System.Store')
|
|
endfunction
|
|
|
|
function! s:_vital_depends() abort
|
|
return [
|
|
\ 'Text.INI',
|
|
\ 'System.Filepath',
|
|
\ 'Data.String',
|
|
\ 'System.Store',
|
|
\]
|
|
endfunction
|
|
|
|
function! s:new(path) abort
|
|
let path = s:Path.remove_last_separator(expand(a:path))
|
|
let dirpath = isdirectory(path) ? path : fnamemodify(path, ':p:h')
|
|
let dirpath = simplify(s:Path.abspath(s:Path.realpath(path)))
|
|
|
|
" Find worktree
|
|
let dgit = finddir('.git', fnameescape(dirpath) . ';')
|
|
let dgit = empty(dgit) ? '' : fnamemodify(dgit, ':p:h')
|
|
let fgit = findfile('.git', fnameescape(dirpath) . ';')
|
|
let fgit = empty(fgit) ? '' : fnamemodify(fgit, ':p')
|
|
let worktree = len(dgit) > len(fgit) ? dgit : fgit
|
|
let worktree = empty(worktree) ? '' : fnamemodify(worktree, ':h')
|
|
if empty(worktree)
|
|
return {}
|
|
endif
|
|
|
|
" Find repository
|
|
let repository = s:Path.join(worktree, '.git')
|
|
if filereadable(repository)
|
|
" A '.git' may be a file which was created by '--separate-git-dir' option
|
|
let lines = readfile(repository)
|
|
if empty(lines)
|
|
throw printf(
|
|
\ 'vital: Git: An invalid .git file has found at "%s".',
|
|
\ repository,
|
|
\)
|
|
endif
|
|
let gitdir = matchstr(lines[0], '^gitdir:\s*\zs.\+$')
|
|
let is_abs = s:Path.is_absolute(gitdir)
|
|
let repository = is_abs ? gitdir : repository[:-5] . gitdir
|
|
let repository = empty(repository) ? '' : fnamemodify(repository, ':p:h')
|
|
endif
|
|
|
|
" Find commondir
|
|
let commondir = ''
|
|
if filereadable(s:Path.join(repository, 'commondir'))
|
|
let commondir = readfile(s:Path.join(repository, 'commondir'))[0]
|
|
let commondir = s:Path.join(repository, commondir)
|
|
endif
|
|
|
|
let git = {
|
|
\ 'worktree': simplify(s:Path.realpath(worktree)),
|
|
\ 'repository': simplify(s:Path.realpath(repository)),
|
|
\ 'commondir': simplify(s:Path.realpath(commondir)),
|
|
\}
|
|
lockvar git.worktree
|
|
lockvar git.repository
|
|
lockvar git.commondir
|
|
return git
|
|
endfunction
|
|
|
|
function! s:abspath(git, path) abort
|
|
let relpath = s:Path.realpath(expand(a:path))
|
|
if s:Path.is_absolute(relpath)
|
|
return relpath
|
|
endif
|
|
return s:Path.join(a:git.worktree, relpath)
|
|
endfunction
|
|
|
|
function! s:relpath(git, path) abort
|
|
let abspath = s:Path.realpath(expand(a:path))
|
|
if s:Path.is_relative(abspath)
|
|
return abspath
|
|
endif
|
|
let pattern = s:String.escape_pattern(a:git.worktree . s:Path.separator())
|
|
return abspath =~# '^' . pattern
|
|
\ ? matchstr(abspath, '^' . pattern . '\zs.*')
|
|
\ : abspath
|
|
endfunction
|
|
|
|
function! s:resolve(git, path) abort
|
|
let path = s:Path.realpath(a:path)
|
|
let path1 = s:Path.join(a:git.repository, path)
|
|
let path2 = empty(a:git.commondir)
|
|
\ ? ''
|
|
\ : s:Path.join(a:git.commondir, path)
|
|
return filereadable(path1) || isdirectory(path1)
|
|
\ ? path1
|
|
\ : filereadable(path2) || isdirectory(path2)
|
|
\ ? path2
|
|
\ : path1
|
|
endfunction
|
|
|
|
" The search paths are documented at
|
|
" https://git-scm.com/docs/git-rev-parse
|
|
function! s:ref(git, refname) abort
|
|
let refname = a:refname ==# '@' ? 'HEAD' : a:refname
|
|
let candidates = [
|
|
\ refname,
|
|
\ printf('refs/%s', refname),
|
|
\ printf('refs/tags/%s', refname),
|
|
\ printf('refs/heads/%s', refname),
|
|
\ printf('refs/remotes/%s', refname),
|
|
\ printf('refs/remotes/%s/HEAD', refname),
|
|
\]
|
|
let packed_refs = s:_get_packed_refs(a:git)
|
|
for candidate in candidates
|
|
let ref = s:_get_reference(a:git, candidate, packed_refs)
|
|
if !empty(ref)
|
|
return ref
|
|
endif
|
|
endfor
|
|
return {}
|
|
endfunction
|
|
|
|
|
|
" Private --------------------------------------------------------------------
|
|
function! s:_get_packed_refs(git) abort
|
|
let path = s:resolve(a:git, 'packed-refs')
|
|
if !filereadable(path)
|
|
return []
|
|
endif
|
|
let store = s:Store.of(path)
|
|
let packed_refs = store.get('packed-refs', [])
|
|
if !empty(packed_refs)
|
|
return packed_refs
|
|
endif
|
|
let packed_refs = readfile(path)
|
|
call store.set('packed-refs', packed_refs)
|
|
return packed_refs
|
|
endfunction
|
|
|
|
function! s:_get_reference(git, refname, packed_refs) abort
|
|
let ref = s:_get_reference_trad(a:git, a:refname, a:packed_refs)
|
|
if !empty(ref)
|
|
return ref
|
|
endif
|
|
return s:_get_reference_packed(a:git, a:refname, a:packed_refs)
|
|
endfunction
|
|
|
|
function! s:_get_reference_trad(git, refname, packed_refs) abort
|
|
let path = s:resolve(a:git, a:refname)
|
|
if !filereadable(path)
|
|
return {}
|
|
endif
|
|
let content = get(readfile(path), 0, '')
|
|
if content =~# '^ref:'
|
|
return s:_get_reference(
|
|
\ a:git,
|
|
\ matchstr(content, '^ref:\s\+\zs.\+'),
|
|
\ a:packed_refs,
|
|
\)
|
|
endif
|
|
let name = matchstr(a:refname, '^refs/\%(heads\|remotes\|tags\)/\zs.*')
|
|
return {
|
|
\ 'name': empty(name) ? content[:7] : name,
|
|
\ 'path': a:refname,
|
|
\ 'hash': content,
|
|
\}
|
|
endfunction
|
|
|
|
function! s:_get_reference_packed(git, refname, packed_refs) abort
|
|
let m = matchstr(a:packed_refs, '\s' . a:refname . '$')
|
|
if empty(m)
|
|
return {}
|
|
endif
|
|
let m = matchlist(m, '^\([0-9a-f]\+\)\s\+\(.*\)$')
|
|
let refname = m[2]
|
|
let name = matchstr(refname, '^refs/\%(heads\|remotes\|tags\)/\zs.*')
|
|
return {
|
|
\ 'name': name,
|
|
\ 'path': refname,
|
|
\ 'hash': m[1],
|
|
\}
|
|
endfunction
|