소스 검색

Fix git detection bug, git prompt colour issues

Git version detection was falling over when development versions of git
were used. We now make sure we only take the first 3 version numbers,
and not the patch number/development hash.

Git prompt colours are reset after the end of the prompt to ensure that
background colours do not spill into following elements.

Also:

- Update vim-plug.
- Standardise folder naming for dotfiles backups. There are different
  default formats between the GNU and BSD versions of `date`.
Weiyi Lou 10 년 전
부모
커밋
fb05ca71bc
4개의 변경된 파일87개의 추가작업 그리고 48개의 파일을 삭제
  1. 1 1
      setup.sh
  2. 82 45
      vim/autoload/plug.vim
  3. 1 1
      zsh/custom/cinaeco.zsh-theme
  4. 3 1
      zsh/custom/git.zsh

+ 1 - 1
setup.sh

@@ -18,7 +18,7 @@ linkup() {
 # }}}
 
 # Prepare folders
-backup_dir=~/.dotfilesbackup/$(date)
+backup_dir=~/.dotfilesbackup/$(date "+%Y-%m-%d-%H%M%S")
 mkdir -p "$backup_dir" ~/.ssh ${XDG_CONFIG_HOME:=$HOME/.config}
 echo "Prepared folders."
 

+ 82 - 45
vim/autoload/plug.vim

@@ -24,11 +24,14 @@
 "   " Using git URL
 "   Plug 'https://github.com/junegunn/vim-github-dashboard.git'
 "
+"   " Using a non-master branch
+"   Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' }
+
 "   " Plugin options
 "   Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' }
 "
 "   " Plugin outside ~/.vim/plugged with post-update hook
-"   Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': 'yes \| ./install' }
+"   Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
 "
 "   " Unmanaged plugin (manually installed and updated)
 "   Plug '~/my-prototype-plugin'
@@ -172,11 +175,14 @@ function! plug#end()
             call s:assoc(lod.map, cmd, name)
           endif
           call add(s:triggers[name].map, cmd)
-        elseif cmd =~ '^[A-Z]'
+        elseif cmd =~# '^[A-Z]'
           if exists(':'.cmd) != 2
             call s:assoc(lod.cmd, cmd, name)
           endif
           call add(s:triggers[name].cmd, cmd)
+        else
+          call s:err('Invalid `on` option: '.cmd.
+          \ '. Should start with an uppercase letter or `<Plug>`.')
         endif
       endfor
     endif
@@ -294,7 +300,7 @@ endif
 
 function! s:err(msg)
   echohl ErrorMsg
-  echom a:msg
+  echom '[vim-plug] '.a:msg
   echohl None
   return 0
 endfunction
@@ -605,7 +611,7 @@ function! s:prepare()
     silent %d _
   else
     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> D  :PlugDiff<cr>
     nnoremap <silent> <buffer> S  :PlugStatus<cr>
@@ -650,6 +656,7 @@ function! s:do(pull, force, todo)
     if a:force || installed || updated
       execute 'cd' s:esc(spec.dir)
       call append(3, '- Post-update hook for '. name .' ... ')
+      let error = ''
       let type = type(spec.do)
       if type == s:TYPE.string
         try
@@ -658,26 +665,60 @@ function! s:do(pull, force, todo)
           let g:_plug_do = '!'.escape(spec.do, '#!%')
           execute "normal! :execute g:_plug_do\<cr>\<cr>"
         finally
-          let result = v:shell_error ? ('Exit status: '.v:shell_error) : 'Done!'
+          if v:shell_error
+            let error = 'Exit status: ' . v:shell_error
+          endif
           unlet g:_plug_do
         endtry
       elseif type == s:TYPE.funcref
         try
           let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged')
           call spec.do({ 'name': name, 'status': status, 'force': a:force })
-          let result = 'Done!'
         catch
-          let result = 'Error: ' . v:exception
+          let error = v:exception
         endtry
       else
-        let result = 'Error: Invalid type!'
+        let error = 'Invalid hook type'
       endif
-      call setline(4, getline(4) . result)
+      call setline(4, empty(error) ? (getline(4) . 'OK')
+                                 \ : ('x' . getline(4)[1:] . error))
       cd -
     endif
   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
@@ -832,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.')
@@ -986,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,
@@ -997,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,
@@ -1019,7 +1061,7 @@ endwhile
 endfunction
 
 function! s:update_python()
-let py_exe = has('python3') ? 'python3' : 'python'
+let py_exe = has('python') ? 'python' : 'python3'
 execute py_exe "<< EOF"
 """ Due to use of signals this function is POSIX only. """
 import datetime
@@ -1048,14 +1090,9 @@ G_CLONE_OPT = vim.eval('s:clone_opt')
 G_PROGRESS = vim.eval('s:progress_opt(1)')
 G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads'))
 G_STOP = thr.Event()
-G_THREADS = {}
 
 class PlugError(Exception):
-  def __init__(self, msg):
-    self._msg = msg
-  @property
-  def msg(self):
-    return self._msg
+  pass
 class CmdTimedOut(PlugError):
   pass
 class CmdFailed(PlugError):
@@ -1246,7 +1283,7 @@ class Plugin(object):
         with self.lock:
           thread_vim_command("let s:update.new['{0}'] = 1".format(self.name))
     except PlugError as exc:
-      self.write(Action.ERROR, self.name, exc.msg)
+      self.write(Action.ERROR, self.name, [str(exc)])
     except KeyboardInterrupt:
       G_STOP.set()
       self.write(Action.ERROR, self.name, ['Interrupted!'])
@@ -1329,10 +1366,6 @@ class PlugThread(thr.Thread):
         work_q.task_done()
     except queue.Empty:
       pass
-    finally:
-      global G_THREADS
-      with lock:
-        del G_THREADS[thr.current_thread().name]
 
 class RefreshThread(thr.Thread):
   def __init__(self, lock):
@@ -1386,17 +1419,16 @@ def main():
   for work in plugs.items():
     work_q.put(work)
 
-  global G_THREADS
+  start_cnt = thr.active_count()
   for num in range(nthreads):
     tname = 'PlugT-{0:02}'.format(num)
     thread = PlugThread(tname, (buf_q, work_q, lock))
     thread.start()
-    G_THREADS[tname] = thread
   if mac_gui:
     rthread = RefreshThread(lock)
     rthread.start()
 
-  while not buf_q.empty() or len(G_THREADS) != 0:
+  while not buf_q.empty() or thr.active_count() != start_cnt:
     try:
       action, name, msg = buf_q.get(True, 0.25)
       buf.write(action, name, msg)
@@ -1677,42 +1709,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)
@@ -1730,7 +1766,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
@@ -1823,7 +1859,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
@@ -1939,7 +1976,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
@@ -1998,7 +2035,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)

+ 1 - 1
zsh/custom/cinaeco.zsh-theme

@@ -128,7 +128,7 @@ function git_prompt_status() {
     [[ $X == 'R' ]] && X_SET="$X_SET$ZSH_THEME_GIT_INDEX_RENAMED" && continue
     [[ $X == 'C' ]] && X_SET="$X_SET$ZSH_THEME_GIT_INDEX_COPIED" && continue
   done
-  local STATUS=" %{$FG[070]%}$X_SET%{$FG[124]%}$Y_SET%{$FG[220]%}$UN_SET$END"
+  local STATUS=" %{$FG[070]%}$X_SET%{$FG[124]%}$Y_SET%{$FG[220]%}$UN_SET$END%{$reset_color%}"
   echo $STATUS
 }
 

+ 3 - 1
zsh/custom/git.zsh

@@ -12,8 +12,10 @@ function git_compare_version() {
     t again'
   )
   # Sanitise installed version to 3 digits as well.
+  # The raw version string takes the form 'git version 1.2.3...'
   INSTALLED=$(git --version 2> /dev/null)
-  INSTALLED=${INSTALLED//[git version |.]/}
+  INSTALLED=${INSTALLED:12:5}
+  INSTALLED=${INSTALLED//./}
 
   if [[ $INSTALLED -gt $INPUT ]]; then
     echo 1