Updating plug

This commit is contained in:
Alex Yatskov 2015-12-12 21:40:42 +09:00
parent 089c174d6d
commit f29accedec

View File

@ -611,7 +611,7 @@ function! s:prepare()
silent %d _ silent %d _
else else
call s:new_window() call s:new_window()
nnoremap <silent> <buffer> q :if b:plug_preview==1<bar>pc<bar>endif<bar>echo<bar>q<cr> nnoremap <silent> <buffer> q :if b:plug_preview==1<bar>pc<bar>endif<bar>bd<cr>
nnoremap <silent> <buffer> R :silent! call <SID>retry()<cr> nnoremap <silent> <buffer> R :silent! call <SID>retry()<cr>
nnoremap <silent> <buffer> D :PlugDiff<cr> nnoremap <silent> <buffer> D :PlugDiff<cr>
nnoremap <silent> <buffer> S :PlugStatus<cr> nnoremap <silent> <buffer> S :PlugStatus<cr>
@ -687,6 +687,38 @@ function! s:do(pull, force, todo)
endfor endfor
endfunction endfunction
function! s:hash_match(a, b)
return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0
endfunction
function! s:checkout(plugs)
for [name, spec] in items(a:plugs)
let sha = spec.commit
call append(3, '- Checking out '.sha[:6].' of '.name.' ... ')
redraw
let error = []
let output = s:lines(s:system('git rev-parse HEAD', spec.dir))
if v:shell_error
let error = output
elseif !s:hash_match(sha, output[0])
let output = s:lines(s:system(
\ 'git fetch --depth 999999 && git checkout '.sha, spec.dir))
if v:shell_error
let error = output
endif
endif
if empty(error)
call setline(4, getline(4) . 'OK')
else
call setline(4, 'x'.getline(4)[1:] . 'Error')
for line in reverse(error)
call append(4, ' '.line)
endfor
endif
endfor
endfunction
function! s:finish(pull) function! s:finish(pull)
let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen')) let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen'))
if new_frozen if new_frozen
@ -729,7 +761,7 @@ endfunction
function! s:update_impl(pull, force, args) abort function! s:update_impl(pull, force, args) abort
let args = copy(a:args) let args = copy(a:args)
let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
\ remove(args, -1) : get(g:, 'plug_threads', s:is_win ? 1 : 16) \ remove(args, -1) : get(g:, 'plug_threads', 16)
let managed = filter(copy(g:plugs), 's:is_managed(v:key)') let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') : let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') :
@ -766,9 +798,8 @@ function! s:update_impl(pull, force, args) abort
echohl None echohl None
endif endif
let python = (has('python') || has('python3')) && !s:is_win && !has('win32unix') let python = (has('python') || has('python3')) && (!s:nvim || has('vim_starting'))
\ && (!s:nvim || has('vim_starting')) let ruby = has('ruby') && !s:nvim && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running'))
let ruby = has('ruby') && !s:nvim && (v:version >= 703 || v:version == 702 && has('patch374'))
let s:update = { let s:update = {
\ 'start': reltime(), \ 'start': reltime(),
@ -841,6 +872,7 @@ function! s:update_finish()
let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt
endif endif
if s:switch_in() if s:switch_in()
call s:checkout(filter(copy(s:update.all), 'has_key(v:val, "commit")'))
call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'has_key(v:val, "do")')) call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'has_key(v:val, "do")'))
call s:finish(s:update.pull) call s:finish(s:update.pull)
call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.')
@ -870,7 +902,13 @@ function! s:job_handler(job_id, data, event) abort
endif endif
if a:event == 'stdout' if a:event == 'stdout'
let self.result .= substitute(s:to_s(a:data), '[\r\n]', '', 'g') . "\n" let complete = empty(a:data[-1])
let lines = map(filter(a:data, 'len(v:val) > 0'), 'split(v:val, "[\r\n]")[-1]')
call extend(self.lines, lines)
let self.result = join(self.lines, "\n")
if !complete
call remove(self.lines, -1)
endif
" To reduce the number of buffer updates " To reduce the number of buffer updates
let self.tick = get(self, 'tick', -1) + 1 let self.tick = get(self, 'tick', -1) + 1
if self.tick % len(s:jobs) == 0 if self.tick % len(s:jobs) == 0
@ -887,7 +925,7 @@ function! s:job_handler(job_id, data, event) abort
endfunction endfunction
function! s:spawn(name, cmd, opts) function! s:spawn(name, cmd, opts)
let job = { 'name': a:name, 'running': 1, 'error': 0, 'result': '', let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [], 'result': '',
\ 'new': get(a:opts, 'new', 0), \ 'new': get(a:opts, 'new', 0),
\ 'on_stdout': function('s:job_handler'), \ 'on_stdout': function('s:job_handler'),
\ 'on_exit' : function('s:job_handler'), \ 'on_exit' : function('s:job_handler'),
@ -995,8 +1033,8 @@ while 1 " Without TCO, Vim stack is bound to explode
let merge = s:shellesc(has_tag ? spec.tag : 'origin/'.spec.branch) let merge = s:shellesc(has_tag ? spec.tag : 'origin/'.spec.branch)
if !new if !new
let [valid, msg] = s:git_valid(spec, 0) let error = s:git_validate(spec, 0)
if valid if empty(error)
if pull if pull
let fetch_opt = (has_tag && !empty(globpath(spec.dir, '.git/shallow'))) ? '--depth 99999999' : '' let fetch_opt = (has_tag && !empty(globpath(spec.dir, '.git/shallow'))) ? '--depth 99999999' : ''
call s:spawn(name, call s:spawn(name,
@ -1006,7 +1044,7 @@ while 1 " Without TCO, Vim stack is bound to explode
let s:jobs[name] = { 'running': 0, 'result': 'Already installed', 'error': 0 } let s:jobs[name] = { 'running': 0, 'result': 'Already installed', 'error': 0 }
endif endif
else else
let s:jobs[name] = { 'running': 0, 'result': msg, 'error': 1 } let s:jobs[name] = { 'running': 0, 'result': error, 'error': 1 }
endif endif
else else
call s:spawn(name, call s:spawn(name,
@ -1028,9 +1066,8 @@ endwhile
endfunction endfunction
function! s:update_python() function! s:update_python()
let py_exe = has('python3') ? 'python3' : 'python' let py_exe = has('python') ? 'python' : 'python3'
execute py_exe "<< EOF" execute py_exe "<< EOF"
""" Due to use of signals this function is POSIX only. """
import datetime import datetime
import functools import functools
import os import os
@ -1057,14 +1094,11 @@ G_CLONE_OPT = vim.eval('s:clone_opt')
G_PROGRESS = vim.eval('s:progress_opt(1)') G_PROGRESS = vim.eval('s:progress_opt(1)')
G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads'))
G_STOP = thr.Event() G_STOP = thr.Event()
G_THREADS = {} G_IS_WIN = vim.eval('s:is_win') == '1'
class PlugError(Exception): class PlugError(Exception):
def __init__(self, msg): def __init__(self, msg):
self._msg = msg self.msg = msg
@property
def msg(self):
return self._msg
class CmdTimedOut(PlugError): class CmdTimedOut(PlugError):
pass pass
class CmdFailed(PlugError): class CmdFailed(PlugError):
@ -1075,10 +1109,9 @@ class Action(object):
INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-']
class Buffer(object): class Buffer(object):
def __init__(self, lock, num_plugs, is_pull, is_win): def __init__(self, lock, num_plugs, is_pull):
self.bar = '' self.bar = ''
self.event = 'Updating' if is_pull else 'Installing' self.event = 'Updating' if is_pull else 'Installing'
self.is_win = is_win
self.lock = lock self.lock = lock
self.maxy = int(vim.eval('winheight(".")')) self.maxy = int(vim.eval('winheight(".")'))
self.num_plugs = num_plugs self.num_plugs = num_plugs
@ -1106,8 +1139,7 @@ class Buffer(object):
with self.lock: with self.lock:
vim.command('normal! 2G') vim.command('normal! 2G')
if not self.is_win: vim.command('redraw')
vim.command('redraw')
def write(self, action, name, lines): def write(self, action, name, lines):
first, rest = lines[0], lines[1:] first, rest = lines[0], lines[1:]
@ -1138,7 +1170,8 @@ class Buffer(object):
class Command(object): class Command(object):
def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None):
self.cmd = cmd self.cmd = cmd
self.cmd_dir = cmd_dir if cmd_dir:
self.cmd = 'cd {0} && {1}'.format(cmd_dir, self.cmd)
self.timeout = timeout self.timeout = timeout
self.callback = cb if cb else (lambda msg: None) self.callback = cb if cb else (lambda msg: None)
self.clean = clean if clean else (lambda: None) self.clean = clean if clean else (lambda: None)
@ -1188,9 +1221,11 @@ class Command(object):
try: try:
tfile = tempfile.NamedTemporaryFile(mode='w+b') tfile = tempfile.NamedTemporaryFile(mode='w+b')
self.proc = subprocess.Popen(self.cmd, cwd=self.cmd_dir, stdout=tfile, preexec_fn = not G_IS_WIN and os.setsid or None
stderr=subprocess.STDOUT, shell=True, self.proc = subprocess.Popen(self.cmd, stdout=tfile,
preexec_fn=os.setsid) stderr=subprocess.STDOUT,
stdin=subprocess.PIPE, shell=True,
preexec_fn=preexec_fn)
thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,))
thrd.start() thrd.start()
@ -1208,7 +1243,7 @@ class Command(object):
if first_line or random.random() < G_LOG_PROB: if first_line or random.random() < G_LOG_PROB:
first_line = False first_line = False
line = nonblock_read(tfile.name) line = '' if G_IS_WIN else nonblock_read(tfile.name)
if line: if line:
self.callback([line]) self.callback([line])
@ -1232,7 +1267,10 @@ class Command(object):
def terminate(self): def terminate(self):
""" Terminate process and cleanup. """ """ Terminate process and cleanup. """
if self.alive: if self.alive:
os.killpg(self.proc.pid, signal.SIGTERM) if G_IS_WIN:
os.kill(self.proc.pid, signal.SIGINT)
else:
os.killpg(self.proc.pid, signal.SIGTERM)
self.clean() self.clean()
class Plugin(object): class Plugin(object):
@ -1338,10 +1376,6 @@ class PlugThread(thr.Thread):
work_q.task_done() work_q.task_done()
except queue.Empty: except queue.Empty:
pass pass
finally:
global G_THREADS
with lock:
del G_THREADS[thr.current_thread().name]
class RefreshThread(thr.Thread): class RefreshThread(thr.Thread):
def __init__(self, lock): def __init__(self, lock):
@ -1387,25 +1421,23 @@ def main():
nthreads = int(vim.eval('s:update.threads')) nthreads = int(vim.eval('s:update.threads'))
plugs = vim.eval('s:update.todo') plugs = vim.eval('s:update.todo')
mac_gui = vim.eval('s:mac_gui') == '1' mac_gui = vim.eval('s:mac_gui') == '1'
is_win = vim.eval('s:is_win') == '1'
lock = thr.Lock() lock = thr.Lock()
buf = Buffer(lock, len(plugs), G_PULL, is_win) buf = Buffer(lock, len(plugs), G_PULL)
buf_q, work_q = queue.Queue(), queue.Queue() buf_q, work_q = queue.Queue(), queue.Queue()
for work in plugs.items(): for work in plugs.items():
work_q.put(work) work_q.put(work)
global G_THREADS start_cnt = thr.active_count()
for num in range(nthreads): for num in range(nthreads):
tname = 'PlugT-{0:02}'.format(num) tname = 'PlugT-{0:02}'.format(num)
thread = PlugThread(tname, (buf_q, work_q, lock)) thread = PlugThread(tname, (buf_q, work_q, lock))
thread.start() thread.start()
G_THREADS[tname] = thread
if mac_gui: if mac_gui:
rthread = RefreshThread(lock) rthread = RefreshThread(lock)
rthread.start() rthread.start()
while not buf_q.empty() or len(G_THREADS) != 0: while not buf_q.empty() or thr.active_count() != start_cnt:
try: try:
action, name, msg = buf_q.get(True, 0.25) action, name, msg = buf_q.get(True, 0.25)
buf.write(action, name, msg) buf.write(action, name, msg)
@ -1448,16 +1480,20 @@ function! s:update_ruby()
def killall pid def killall pid
pids = [pid] pids = [pid]
unless `which pgrep 2> /dev/null`.empty? if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
children = pids pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil }
until children.empty? else
children = children.map { |pid| unless `which pgrep 2> /dev/null`.empty?
`pgrep -P #{pid}`.lines.map { |l| l.chomp } children = pids
}.flatten until children.empty?
pids += children children = children.map { |pid|
`pgrep -P #{pid}`.lines.map { |l| l.chomp }
}.flatten
pids += children
end
end end
pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
end end
pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
end end
require 'thread' require 'thread'
@ -1483,7 +1519,7 @@ function! s:update_ruby()
$curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
$curbuf[2] = '[' + bar.ljust(tot) + ']' $curbuf[2] = '[' + bar.ljust(tot) + ']'
VIM::command('normal! 2G') VIM::command('normal! 2G')
VIM::command('redraw') unless iswin VIM::command('redraw')
} }
where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } }
log = proc { |name, result, type| log = proc { |name, result, type|
@ -1595,8 +1631,8 @@ function! s:update_ruby()
exists = File.directory? dir exists = File.directory? dir
ok, result = ok, result =
if exists if exists
dir = iswin ? dir : esc(dir) chdir = "#{cd} #{iswin ? dir : esc(dir)}"
ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url", nil, nil, nil ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url", nil, nil, nil
current_uri = data.lines.to_a.last current_uri = data.lines.to_a.last
if !ret if !ret
if data =~ /^Interrupted|^Timeout/ if data =~ /^Interrupted|^Timeout/
@ -1612,7 +1648,7 @@ function! s:update_ruby()
if pull if pull
log.call name, 'Updating ...', :update log.call name, 'Updating ...', :update
fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : '' fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : ''
bt.call "#{cd} #{dir} && git fetch #{fetch_opt} #{progress} 2>&1 && git checkout -q #{checkout} 2>&1 && git merge --ff-only #{merge} 2>&1 && #{subm}", name, :update, nil bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1 && git checkout -q #{checkout} 2>&1 && git merge --ff-only #{merge} 2>&1 && #{subm}", name, :update, nil
else else
[true, skip] [true, skip]
end end
@ -1686,42 +1722,46 @@ function! s:system_chomp(...)
return v:shell_error ? '' : substitute(ret, '\n$', '', '') return v:shell_error ? '' : substitute(ret, '\n$', '', '')
endfunction endfunction
function! s:git_valid(spec, check_branch) function! s:git_validate(spec, check_branch)
let ret = 1 let err = ''
let msg = 'OK'
if isdirectory(a:spec.dir) if isdirectory(a:spec.dir)
let result = s:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url', a:spec.dir)) let result = s:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url', a:spec.dir))
let remote = result[-1] let remote = result[-1]
if v:shell_error if v:shell_error
let msg = join([remote, 'PlugClean required.'], "\n") let err = join([remote, 'PlugClean required.'], "\n")
let ret = 0
elseif !s:compare_git_uri(remote, a:spec.uri) elseif !s:compare_git_uri(remote, a:spec.uri)
let msg = join(['Invalid URI: '.remote, let err = join(['Invalid URI: '.remote,
\ 'Expected: '.a:spec.uri, \ 'Expected: '.a:spec.uri,
\ 'PlugClean required.'], "\n") \ 'PlugClean required.'], "\n")
let ret = 0 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 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)',
\ a:spec.commit[:6], sha[:6]),
\ 'PlugUpdate required.'], "\n")
endif
elseif a:check_branch elseif a:check_branch
let branch = result[0] let branch = result[0]
" Check tag " Check tag
if has_key(a:spec, 'tag') if has_key(a:spec, 'tag')
let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir)
if a:spec.tag !=# tag if a:spec.tag !=# tag
let msg = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.',
\ (empty(tag) ? 'N/A' : tag), a:spec.tag) \ (empty(tag) ? 'N/A' : tag), a:spec.tag)
let ret = 0
endif endif
" Check branch " Check branch
elseif a:spec.branch !=# branch elseif a:spec.branch !=# branch
let msg = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.',
\ branch, a:spec.branch) \ branch, a:spec.branch)
let ret = 0
endif endif
endif endif
else else
let msg = 'Not found' let err = 'Not found'
let ret = 0
endif endif
return [ret, msg] return err
endfunction endfunction
function! s:rm_rf(dir) function! s:rm_rf(dir)
@ -1739,7 +1779,7 @@ function! s:clean(force)
let dirs = [] let dirs = []
let [cnt, total] = [0, len(g:plugs)] let [cnt, total] = [0, len(g:plugs)]
for [name, spec] in items(g:plugs) for [name, spec] in items(g:plugs)
if !s:is_managed(name) || s:git_valid(spec, 0)[0] if !s:is_managed(name) || empty(s:git_validate(spec, 0))
call add(dirs, spec.dir) call add(dirs, spec.dir)
endif endif
let cnt += 1 let cnt += 1
@ -1832,7 +1872,8 @@ function! s:status()
for [name, spec] in items(g:plugs) for [name, spec] in items(g:plugs)
if has_key(spec, 'uri') if has_key(spec, 'uri')
if isdirectory(spec.dir) if isdirectory(spec.dir)
let [valid, msg] = s:git_valid(spec, 1) let err = s:git_validate(spec, 1)
let [valid, msg] = [empty(err), empty(err) ? 'OK' : err]
else else
let [valid, msg] = [0, 'Not found. Try PlugInstall.'] let [valid, msg] = [0, 'Not found. Try PlugInstall.']
endif endif
@ -1948,7 +1989,7 @@ function! s:diff()
redraw redraw
let cnt = 0 let cnt = 0
for [k, v] in items(g:plugs) for [k, v] in filter(items(g:plugs), '!has_key(v:val[1], "commit")')
if !isdirectory(v.dir) || !s:is_managed(k) if !isdirectory(v.dir) || !s:is_managed(k)
continue continue
endif endif
@ -2007,7 +2048,7 @@ function! s:snapshot(...) abort
redraw redraw
let dirs = sort(map(values(filter(copy(g:plugs), let dirs = sort(map(values(filter(copy(g:plugs),
\'has_key(v:val, "uri") && isdirectory(v:val.dir)')), 'v:val.dir')) \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)')), 'v:val.dir'))
let anchor = line('$') - 1 let anchor = line('$') - 1
for dir in reverse(dirs) for dir in reverse(dirs)
let sha = s:system_chomp('git rev-parse --short HEAD', dir) let sha = s:system_chomp('git rev-parse --short HEAD', dir)