Add support for commit hashes

Close #315
This commit is contained in:
Junegunn Choi 2015-11-19 03:23:59 +09:00
parent e00be1b493
commit e11e045577
3 changed files with 117 additions and 31 deletions

View File

@ -14,7 +14,7 @@ A minimalist Vim plugin manager.
- Creates shallow clones to minimize disk space usage and download time - Creates shallow clones to minimize disk space usage and download time
- On-demand loading for [faster startup time][startup-time] - On-demand loading for [faster startup time][startup-time]
- Can review and rollback updates - Can review and rollback updates
- Branch/tag support - Branch/tag/commit support
- Post-update hooks - Post-update hooks
- Support for externally managed plugins - Support for externally managed plugins
@ -92,8 +92,8 @@ Reload .vimrc and `:PlugInstall` to install plugins.
### `Plug` options ### `Plug` options
| Option | Description | | Option | Description |
| -------------- | ------------------------------------------------ | | ----------------------- | ------------------------------------------------ |
| `branch`/`tag` | Branch or tag of the repository to use | | `branch`/`tag`/`commit` | Branch/tag/commit of the repository to use |
| `rtp` | Subdirectory that contains Vim plugin | | `rtp` | Subdirectory that contains Vim plugin |
| `dir` | Custom directory for the plugin | | `dir` | Custom directory for the plugin |
| `do` | Post-update hook (string or funcref) | | `do` | Post-update hook (string or funcref) |

View File

@ -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
@ -841,6 +873,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.')
@ -995,8 +1028,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 +1039,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,
@ -1686,42 +1719,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 +1776,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 +1869,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 +1986,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 +2045,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)

View File

@ -1210,3 +1210,51 @@ Execute (#221 Shallow-clone disabled by tag):
Assert !filereadable('.git/shallow') Assert !filereadable('.git/shallow')
cd - cd -
Execute (Commit hash support):
call plug#begin(g:temp_plugged)
Plug 'junegunn/goyo.vim', { 'commit': 'ffffffff' }
Plug 'junegunn/vim-emoji', { 'commit': '9db7fcfee0d90dafdbcb7a32090c0a9085eb054a' }
call plug#end()
PlugUpdate
Log getline(1, '$')
AssertEqual ['x Checking out fffffff of goyo.vim ... Error',
\' error: pathspec ''ffffffff'' did not match any file(s) known to git.',
\'- Checking out 9db7fcf of vim-emoji ... OK'], getline(5, 7)
let hash = system(printf('cd %s && git rev-parse HEAD', g:plugs['vim-emoji'].dir))[:-2]
AssertEqual '9db7fcfee0d90dafdbcb7a32090c0a9085eb054a', hash
" Validate error formatting
PlugStatus
Log getline(1, '$')
AssertEqual ['Finished. 1 error(s).',
\'[==]',
\'',
\'x goyo.vim:'], getline(1, 4)
Assert getline(5) =~ ' Invalid HEAD (expected: fffffff, actual: [0-9a-f]\{7})'
AssertEqual [' PlugUpdate required.',
\'- vim-emoji: OK'], getline(6, '$')
" Plugins with commit option should not appear in PlugDiff output
PlugDiff
AssertEqual 'No updates.', getline(1)
" Nor in PlugSnapshot output
PlugSnapshot
AssertEqual 9, line('$')
q
Execute (Commit hash support - cleared):
call plug#begin(g:temp_plugged)
Plug 'junegunn/goyo.vim'
Plug 'junegunn/vim-emoji'
call plug#end()
PlugInstall
let hash = system(printf('cd %s && git rev-parse HEAD', g:plugs['vim-emoji'].dir))[:-2]
AssertEqual '9db7fcfee0d90dafdbcb7a32090c0a9085eb054a', hash
PlugUpdate
let hash = system(printf('cd %s && git rev-parse HEAD', g:plugs['vim-emoji'].dir))[:-2]
AssertNotEqual '9db7fcfee0d90dafdbcb7a32090c0a9085eb054a', hash
q