Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fast git #937

Merged
merged 16 commits into from
Sep 8, 2020
144 changes: 122 additions & 22 deletions plug.vim
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,104 @@ let s:TYPE = {
let s:loaded = get(s:, 'loaded', {})
let s:triggers = get(s:, 'triggers', {})

function! s:isabsolute(dir) abort
return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)')
endfunction

function! s:get_gitdir(dir) abort
let l:gitdir = a:dir . '/.git'
if isdirectory(l:gitdir)
return l:gitdir
endif
try
let l:line = readfile(l:gitdir)[0]
if l:line =~# '^gitdir: '
let l:gitdir = l:line[8:]
if !s:isabsolute(l:gitdir)
let l:gitdir = a:dir . '/' . l:gitdir
endif
if isdirectory(l:gitdir)
return l:gitdir
endif
endif
catch
endtry
return ''
endfunction

function! s:git_get_remote_origin_url(dir) abort
let gitdir = s:get_gitdir(a:dir)
if gitdir ==# ''
return v:null
endif
try
let lines = readfile(gitdir . '/config')
let [n, ll, url] = [0, len(lines), '']
while n < ll
let line = trim(lines[n])
if stridx(line, '[remote "origin"]') != 0
let n += 1
continue
endif
let n += 1
while n < ll
let line = trim(lines[n])
if line ==# '['
break
endif
let url = matchstr(line, '^url\s*=\s*\zs[^ #]\+')
if !empty(url)
break
endif
let n += 1
endwhile
let n += 1
endwhile
return url
catch
return v:null
endtry
endfunction

function! s:git_get_revision(dir) abort
let l:gitdir = s:get_gitdir(a:dir)
if l:gitdir ==# ''
return v:null
endif
try
let l:line = readfile(l:gitdir . '/HEAD')[0]
if l:line =~# '^ref: '
let l:ref = l:line[5:]
if filereadable(l:gitdir . '/' . l:ref)
return readfile(l:gitdir . '/' . l:ref)[0]
endif
for l:line in readfile(l:gitdir . '/packed-refs')
if l:line =~# ' ' . l:ref
return substitute(l:line, '^\([0-9a-f]*\) ', '\1', '')
endif
endfor
endif
catch
endtry
return v:null
endfunction

function! s:git_get_branch(dir) abort
let l:gitdir = s:get_gitdir(a:dir)
if l:gitdir ==# ''
return v:null
endif
try
let l:line = readfile(l:gitdir . '/HEAD')[0]
if l:line =~# '^ref: refs/heads/'
return l:line[16:]
endif
return ''
catch
return v:null
endtry
endfunction

if s:is_win
function! s:plug_call(fn, ...)
let shellslash = &shellslash
Expand Down Expand Up @@ -961,8 +1059,8 @@ endfunction

function! s:checkout(spec)
let sha = a:spec.commit
let output = s:system('git rev-parse HEAD', a:spec.dir)
if !v:shell_error && !s:hash_match(sha, s:lines(output)[0])
let output = s:git_get_revision(a:spec.dir)
if !s:hash_match(sha, s:lines(output)[0])
let output = s:system(
\ 'git fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir)
endif
Expand Down Expand Up @@ -2126,18 +2224,17 @@ endfunction
function! s:git_validate(spec, check_branch)
let err = ''
if isdirectory(a:spec.dir)
let result = s:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url', a:spec.dir))
let result = [s:git_get_branch(a:spec.dir), s:git_get_remote_origin_url(a:spec.dir)]
let remote = result[-1]
if v:shell_error
if empty(remote)
let err = join([remote, 'PlugClean required.'], "\n")
elseif !s:compare_git_uri(remote, a:spec.uri)
let err = join(['Invalid URI: '.remote,
\ 'Expected: '.a:spec.uri,
\ 'PlugClean required.'], "\n")
elseif a:check_branch && has_key(a:spec, 'commit')
let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir))
let sha = result[-1]
if v:shell_error
let sha = s:git_get_revision(a:spec.dir)
if empty(sha)
let err = join(add(result, 'PlugClean required.'), "\n")
elseif !s:hash_match(sha, a:spec.commit)
let err = join([printf('Invalid HEAD (expected: %s, actual: %s)',
Expand All @@ -2159,20 +2256,23 @@ function! s:git_validate(spec, check_branch)
\ branch, a:spec.branch)
endif
if empty(err)
let [ahead, behind] = split(s:lastline(s:system(printf(
\ 'git rev-list --count --left-right HEAD...origin/%s',
\ a:spec.branch), a:spec.dir)), '\t')
if !v:shell_error && ahead
if behind
" Only mention PlugClean if diverged, otherwise it's likely to be
" pushable (and probably not that messed up).
let err = printf(
\ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n"
\ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', a:spec.branch, ahead, behind)
else
let err = printf("Ahead of origin/%s by %d commit(s).\n"
\ .'Cannot update until local changes are pushed.',
\ a:spec.branch, ahead)
let result = split(s:lastline(s:system(printf(
\ 'git rev-list --count --left-right HEAD...origin/%s',
\ a:spec.branch), a:spec.dir)), '\t')
if !v:shell_error && len(result) == 2
let [ahead, behind] = result
if ahead
if behind
" Only mention PlugClean if diverged, otherwise it's likely to be
" pushable (and probably not that messed up).
let err = printf(
\ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n"
\ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', a:spec.branch, ahead, behind)
else
let err = printf("Ahead of origin/%s by %d commit(s).\n"
\ .'Cannot update until local changes are pushed.',
\ a:spec.branch, ahead)
endif
endif
endif
endif
Expand Down Expand Up @@ -2561,7 +2661,7 @@ function! s:snapshot(force, ...) abort
let names = sort(keys(filter(copy(g:plugs),
\'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)')))
for name in reverse(names)
let sha = s:system_chomp('git rev-parse --short HEAD', g:plugs[name].dir)
let sha = s:git_get_revision(g:plugs[name].dir)[:6]
if !empty(sha)
call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha))
redraw
Expand Down