Преглед изворни кода

Merge pull request #316 from junegunn/commit-hash

Add support for commit hashes
Junegunn Choi пре 10 година
родитељ
комит
8fb0efe4e5
3 измењених фајлова са 117 додато и 31 уклоњено
  1. 10 10
      README.md
  2. 59 21
      plug.vim
  3. 48 0
      test/workflow.vader

+ 10 - 10
README.md

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

+ 59 - 21
plug.vim

@@ -687,6 +687,38 @@ function! s:do(pull, force, todo)
   endfor
 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)
   let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen'))
   if new_frozen
@@ -841,6 +873,7 @@ function! s:update_finish()
     let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt
   endif
   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:finish(s:update.pull)
     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)
 
   if !new
-    let [valid, msg] = s:git_valid(spec, 0)
-    if valid
+    let error = s:git_validate(spec, 0)
+    if empty(error)
       if pull
         let fetch_opt = (has_tag && !empty(globpath(spec.dir, '.git/shallow'))) ? '--depth 99999999' : ''
         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 }
       endif
     else
-      let s:jobs[name] = { 'running': 0, 'result': msg, 'error': 1 }
+      let s:jobs[name] = { 'running': 0, 'result': error, 'error': 1 }
     endif
   else
     call s:spawn(name,
@@ -1686,42 +1719,46 @@ function! s:system_chomp(...)
   return v:shell_error ? '' : substitute(ret, '\n$', '', '')
 endfunction
 
-function! s:git_valid(spec, check_branch)
-  let ret = 1
-  let msg = 'OK'
+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 remote.origin.url', a:spec.dir))
     let remote = result[-1]
     if v:shell_error
-      let msg = join([remote, 'PlugClean required.'], "\n")
-      let ret = 0
+      let err = join([remote, 'PlugClean required.'], "\n")
     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,
                     \ '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
       let branch = result[0]
       " Check tag
       if has_key(a:spec, 'tag')
         let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir)
         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)
-          let ret = 0
         endif
       " Check 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)
-        let ret = 0
       endif
     endif
   else
-    let msg = 'Not found'
-    let ret = 0
+    let err = 'Not found'
   endif
-  return [ret, msg]
+  return err
 endfunction
 
 function! s:rm_rf(dir)
@@ -1739,7 +1776,7 @@ function! s:clean(force)
   let dirs = []
   let [cnt, total] = [0, len(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)
     endif
     let cnt += 1
@@ -1832,7 +1869,8 @@ function! s:status()
   for [name, spec] in items(g:plugs)
     if has_key(spec, 'uri')
       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
         let [valid, msg] = [0, 'Not found. Try PlugInstall.']
       endif
@@ -1948,7 +1986,7 @@ function! s:diff()
   redraw
 
   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)
       continue
     endif
@@ -2007,7 +2045,7 @@ function! s:snapshot(...) abort
   redraw
 
   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
   for dir in reverse(dirs)
     let sha = s:system_chomp('git rev-parse --short HEAD', dir)

+ 48 - 0
test/workflow.vader

@@ -1210,3 +1210,51 @@ Execute (#221 Shallow-clone disabled by tag):
   Assert !filereadable('.git/shallow')
   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