Xinqi Bao's Git

chore(coc): tune completion and diagnostic settings
[dotfiles.git] / .config / nvim / autoload / plug.vim
index 8332537..f479fba 100644 (file)
@@ -1,67 +1,36 @@
 " vim-plug: Vim plugin manager
 " ============================
 "
 " vim-plug: Vim plugin manager
 " ============================
 "
-" Download plug.vim and put it in ~/.vim/autoload
+" 1. Download plug.vim and put it in 'autoload' directory
 "
 "
+"   # Vim
 "   curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
 "     https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
 "
 "   curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
 "     https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
 "
-" Edit your .vimrc
+"   # Neovim
+"   sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \
+"     https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
 "
 "
-"   call plug#begin('~/.vim/plugged')
+" 2. Add a vim-plug section to your ~/.vimrc (or ~/.config/nvim/init.vim for Neovim)
 "
 "
-"   " Make sure you use single quotes
+"   call plug#begin()
 "
 "
-"   " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align
-"   Plug 'junegunn/vim-easy-align'
+"   " List your plugins here
+"   Plug 'tpope/vim-sensible'
 "
 "
-"   " Any valid git URL is allowed
-"   Plug 'https://github.com/junegunn/vim-github-dashboard.git'
-"
-"   " Multiple Plug commands can be written in a single line using | separators
-"   Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets'
-"
-"   " On-demand loading
-"   Plug 'scrooloose/nerdtree', { 'on':  'NERDTreeToggle' }
-"   Plug 'tpope/vim-fireplace', { 'for': 'clojure' }
-"
-"   " Using a non-master branch
-"   Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' }
-"
-"   " Using a tagged release; wildcard allowed (requires git 1.9.2 or above)
-"   Plug 'fatih/vim-go', { 'tag': '*' }
-"
-"   " Plugin options
-"   Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' }
-"
-"   " Plugin outside ~/.vim/plugged with post-update hook
-"   Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
-"
-"   " Unmanaged plugin (manually installed and updated)
-"   Plug '~/my-prototype-plugin'
-"
-"   " Initialize plugin system
 "   call plug#end()
 "
 "   call plug#end()
 "
-" Then reload .vimrc and :PlugInstall to install plugins.
-"
-" Plug options:
+" 3. Reload the file or restart Vim, then you can,
 "
 "
-"| 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                  |
-"| `as`                    | Use different name 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        |
+"     :PlugInstall to install plugins
+"     :PlugUpdate  to update plugins
+"     :PlugDiff    to review the changes from the last update
+"     :PlugClean   to remove plugins no longer in the list
 "
 "
-" More information: https://github.com/junegunn/vim-plug
+" For more information, see https://github.com/junegunn/vim-plug
 "
 "
 "
 "
-" Copyright (c) 2017 Junegunn Choi
+" Copyright (c) 2024 Junegunn Choi
 "
 " MIT License
 "
 "
 " MIT License
 "
@@ -106,7 +75,7 @@ if s:is_win && &shellslash
 else
   let s:me = resolve(expand('<sfile>:p'))
 endif
 else
   let s:me = resolve(expand('<sfile>:p'))
 endif
-let s:base_spec = { 'branch': 'master', 'frozen': 0 }
+let s:base_spec = { 'branch': '', 'frozen': 0 }
 let s:TYPE = {
 \   'string':  type(''),
 \   'list':    type([]),
 let s:TYPE = {
 \   'string':  type(''),
 \   'list':    type([]),
@@ -116,6 +85,94 @@ let s:TYPE = {
 let s:loaded = get(s:, 'loaded', {})
 let s:triggers = get(s:, 'triggers', {})
 
 let s:loaded = get(s:, 'loaded', {})
 let s:triggers = get(s:, 'triggers', {})
 
+function! s:is_powershell(shell)
+  return a:shell =~# 'powershell\(\.exe\)\?$' || a:shell =~# 'pwsh\(\.exe\)\?$'
+endfunction
+
+function! s:isabsolute(dir) abort
+  return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)')
+endfunction
+
+function! s:git_dir(dir) abort
+  let gitdir = s:trim(a:dir) . '/.git'
+  if isdirectory(gitdir)
+    return gitdir
+  endif
+  if !filereadable(gitdir)
+    return ''
+  endif
+  let gitdir = matchstr(get(readfile(gitdir), 0, ''), '^gitdir: \zs.*')
+  if len(gitdir) && !s:isabsolute(gitdir)
+    let gitdir = a:dir . '/' . gitdir
+  endif
+  return isdirectory(gitdir) ? gitdir : ''
+endfunction
+
+function! s:git_origin_url(dir) abort
+  let gitdir = s:git_dir(a:dir)
+  let config = gitdir . '/config'
+  if empty(gitdir) || !filereadable(config)
+    return ''
+  endif
+  return matchstr(join(readfile(config)), '\[remote "origin"\].\{-}url\s*=\s*\zs\S*\ze')
+endfunction
+
+function! s:git_revision(dir) abort
+  let gitdir = s:git_dir(a:dir)
+  let head = gitdir . '/HEAD'
+  if empty(gitdir) || !filereadable(head)
+    return ''
+  endif
+
+  let line = get(readfile(head), 0, '')
+  let ref = matchstr(line, '^ref: \zs.*')
+  if empty(ref)
+    return line
+  endif
+
+  if filereadable(gitdir . '/' . ref)
+    return get(readfile(gitdir . '/' . ref), 0, '')
+  endif
+
+  if filereadable(gitdir . '/packed-refs')
+    for line in readfile(gitdir . '/packed-refs')
+      if line =~# ' ' . ref
+        return matchstr(line, '^[0-9a-f]*')
+      endif
+    endfor
+  endif
+
+  return ''
+endfunction
+
+function! s:git_local_branch(dir) abort
+  let gitdir = s:git_dir(a:dir)
+  let head = gitdir . '/HEAD'
+  if empty(gitdir) || !filereadable(head)
+    return ''
+  endif
+  let branch = matchstr(get(readfile(head), 0, ''), '^ref: refs/heads/\zs.*')
+  return len(branch) ? branch : 'HEAD'
+endfunction
+
+function! s:git_origin_branch(spec)
+  if len(a:spec.branch)
+    return a:spec.branch
+  endif
+
+  " The file may not be present if this is a local repository
+  let gitdir = s:git_dir(a:spec.dir)
+  let origin_head = gitdir.'/refs/remotes/origin/HEAD'
+  if len(gitdir) && filereadable(origin_head)
+    return matchstr(get(readfile(origin_head), 0, ''),
+                  \ '^ref: refs/remotes/origin/\zs.*')
+  endif
+
+  " The command may not return the name of a branch in detached HEAD state
+  let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir))
+  return v:shell_error ? '' : result[-1]
+endfunction
+
 if s:is_win
   function! s:plug_call(fn, ...)
     let shellslash = &shellslash
 if s:is_win
   function! s:plug_call(fn, ...)
     let shellslash = &shellslash
@@ -150,10 +207,11 @@ endfunction
 
 function! plug#begin(...)
   if a:0 > 0
 
 function! plug#begin(...)
   if a:0 > 0
-    let s:plug_home_org = a:1
     let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p'))
   elseif exists('g:plug_home')
     let home = s:path(g:plug_home)
     let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p'))
   elseif exists('g:plug_home')
     let home = s:path(g:plug_home)
+  elseif has('nvim')
+    let home = stdpath('data') . '/plugged'
   elseif !empty(&rtp)
     let home = s:path(split(&rtp, ',')[0]) . '/plugged'
   else
   elseif !empty(&rtp)
     let home = s:path(split(&rtp, ',')[0]) . '/plugged'
   else
@@ -179,7 +237,7 @@ function! s:define_commands()
   endif
   if has('win32')
   \ && &shellslash
   endif
   if has('win32')
   \ && &shellslash
-  \ && (&shell =~# 'cmd\.exe' || &shell =~# 'powershell\.exe')
+  \ && (&shell =~# 'cmd\(\.exe\)\?$' || s:is_powershell(&shell))
     return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.')
   endif
   if !has('nvim')
     return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.')
   endif
   if !has('nvim')
@@ -262,7 +320,7 @@ function! plug#end()
   endif
   let lod = { 'ft': {}, 'map': {}, 'cmd': {} }
 
   endif
   let lod = { 'ft': {}, 'map': {}, 'cmd': {} }
 
-  if exists('g:did_load_filetypes')
+  if get(g:, 'did_load_filetypes', 0)
     filetype off
   endif
   for name in g:plugs_order
     filetype off
   endif
   for name in g:plugs_order
@@ -301,6 +359,9 @@ function! plug#end()
       if !empty(types)
         augroup filetypedetect
         call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim')
       if !empty(types)
         augroup filetypedetect
         call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim')
+        if has('nvim-0.5.0')
+          call s:source(s:rtp(plug), 'ftdetect/**/*.lua', 'after/ftdetect/**/*.lua')
+        endif
         augroup END
       endif
       for type in types
         augroup END
       endif
       for type in types
@@ -311,13 +372,15 @@ function! plug#end()
 
   for [cmd, names] in items(lod.cmd)
     execute printf(
 
   for [cmd, names] in items(lod.cmd)
     execute printf(
-    \ 'command! -nargs=* -range -bang -complete=file %s call s:lod_cmd(%s, "<bang>", <line1>, <line2>, <q-args>, %s)',
-    \ cmd, string(cmd), string(names))
+    \ has('patch-7.4.1898')
+    \ ? 'command! -nargs=* -range -bang -complete=file %s call s:lod_cmd(%s, "<bang>", <line1>, <line2>, <q-args>, <q-mods> ,%s)'
+    \ : 'command! -nargs=* -range -bang -complete=file %s call s:lod_cmd(%s, "<bang>", <line1>, <line2>, <q-args>, %s)'
+    \ , cmd, string(cmd), string(names))
   endfor
 
   for [map, names] in items(lod.map)
     for [mode, map_prefix, key_prefix] in
   endfor
 
   for [map, names] in items(lod.map)
     for [mode, map_prefix, key_prefix] in
-          \ [['i', '<C-O>', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']]
+          \ [['i', '<C-\><C-O>', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']]
       execute printf(
       \ '%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, %s, "%s")<CR>',
       \ mode, map, map_prefix, string(map), string(names), mode != 'i', key_prefix)
       execute printf(
       \ '%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, %s, "%s")<CR>',
       \ mode, map, map_prefix, string(map), string(names), mode != 'i', key_prefix)
@@ -348,6 +411,9 @@ endfunction
 
 function! s:load_plugin(spec)
   call s:source(s:rtp(a:spec), 'plugin/**/*.vim', 'after/plugin/**/*.vim')
 
 function! s:load_plugin(spec)
   call s:source(s:rtp(a:spec), 'plugin/**/*.vim', 'after/plugin/**/*.vim')
+  if has('nvim-0.5.0')
+    call s:source(s:rtp(a:spec), 'plugin/**/*.lua', 'after/plugin/**/*.lua')
+  endif
 endfunction
 
 function! s:reload_plugins()
 endfunction
 
 function! s:reload_plugins()
@@ -419,7 +485,7 @@ if s:is_win
     let batchfile = s:plug_tempname().'.bat'
     call writefile(s:wrap_cmds(a:cmd), batchfile)
     let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0})
     let batchfile = s:plug_tempname().'.bat'
     call writefile(s:wrap_cmds(a:cmd), batchfile)
     let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0})
-    if &shell =~# 'powershell\.exe'
+    if s:is_powershell(&shell)
       let cmd = '& ' . cmd
     endif
     return [batchfile, cmd]
       let cmd = '& ' . cmd
     endif
     return [batchfile, cmd]
@@ -565,6 +631,9 @@ function! s:lod(names, types, ...)
     let rtp = s:rtp(g:plugs[name])
     for dir in a:types
       call s:source(rtp, dir.'/**/*.vim')
     let rtp = s:rtp(g:plugs[name])
     for dir in a:types
       call s:source(rtp, dir.'/**/*.vim')
+      if has('nvim-0.5.0')  " see neovim#14686
+        call s:source(rtp, dir.'/**/*.lua')
+      endif
     endfor
     if a:0
       if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2))
     endfor
     if a:0
       if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2))
@@ -584,11 +653,19 @@ function! s:lod_ft(pat, names)
   call s:doautocmd('filetypeindent', 'FileType')
 endfunction
 
   call s:doautocmd('filetypeindent', 'FileType')
 endfunction
 
-function! s:lod_cmd(cmd, bang, l1, l2, args, names)
-  call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
-  call s:dobufread(a:names)
-  execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
-endfunction
+if has('patch-7.4.1898')
+  function! s:lod_cmd(cmd, bang, l1, l2, args, mods, names)
+    call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
+    call s:dobufread(a:names)
+    execute printf('%s %s%s%s %s', a:mods, (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
+  endfunction
+else
+  function! s:lod_cmd(cmd, bang, l1, l2, args, names)
+    call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
+    call s:dobufread(a:names)
+    execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
+  endfunction
+endif
 
 function! s:lod_map(map, names, with_prefix, prefix)
   call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
 
 function! s:lod_map(map, names, with_prefix, prefix)
   call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
@@ -646,25 +723,25 @@ function! s:parse_options(arg)
     endif
     let opts.tag = a:arg
   elseif type == s:TYPE.dict
     endif
     let opts.tag = a:arg
   elseif type == s:TYPE.dict
-    call extend(opts, a:arg)
     for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as']
     for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as']
-      if has_key(opts, opt)
-      \ && (type(opts[opt]) != s:TYPE.string || empty(opts[opt]))
+      if has_key(a:arg, opt)
+      \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt]))
         throw printf(opt_errfmt, opt, 'string')
       endif
     endfor
     for opt in ['on', 'for']
         throw printf(opt_errfmt, opt, 'string')
       endif
     endfor
     for opt in ['on', 'for']
-      if has_key(opts, opt)
-      \ && type(opts[opt]) != s:TYPE.list
-      \ && (type(opts[opt]) != s:TYPE.string || empty(opts[opt]))
+      if has_key(a:arg, opt)
+      \ && type(a:arg[opt]) != s:TYPE.list
+      \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt]))
         throw printf(opt_errfmt, opt, 'string or list')
       endif
     endfor
         throw printf(opt_errfmt, opt, 'string or list')
       endif
     endfor
-    if has_key(opts, 'do')
-      \ && type(opts.do) != s:TYPE.funcref
-      \ && (type(opts.do) != s:TYPE.string || empty(opts.do))
+    if has_key(a:arg, 'do')
+      \ && type(a:arg.do) != s:TYPE.funcref
+      \ && (type(a:arg.do) != s:TYPE.string || empty(a:arg.do))
         throw printf(opt_errfmt, 'do', 'string or funcref')
     endif
         throw printf(opt_errfmt, 'do', 'string or funcref')
     endif
+    call extend(opts, a:arg)
     if has_key(opts, 'dir')
       let opts.dir = s:dirpath(s:plug_expand(opts.dir))
     endif
     if has_key(opts, 'dir')
       let opts.dir = s:dirpath(s:plug_expand(opts.dir))
     endif
@@ -716,11 +793,12 @@ endfunction
 function! s:syntax()
   syntax clear
   syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber
 function! s:syntax()
   syntax clear
   syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber
-  syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX
+  syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX,plugAbort
   syn match plugNumber /[0-9]\+[0-9.]*/ contained
   syn match plugBracket /[[\]]/ contained
   syn match plugX /x/ contained
   syn match plugNumber /[0-9]\+[0-9.]*/ contained
   syn match plugBracket /[[\]]/ contained
   syn match plugX /x/ contained
-  syn match plugDash /^-/
+  syn match plugAbort /\~/ contained
+  syn match plugDash /^-\{1}\ /
   syn match plugPlus /^+/
   syn match plugStar /^*/
   syn match plugMessage /\(^- \)\@<=.*/
   syn match plugPlus /^+/
   syn match plugStar /^*/
   syn match plugMessage /\(^- \)\@<=.*/
@@ -738,11 +816,13 @@ function! s:syntax()
   syn match plugError /^x.*/
   syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/
   syn match plugH2 /^.*:\n-\+$/
   syn match plugError /^x.*/
   syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/
   syn match plugH2 /^.*:\n-\+$/
+  syn match plugH2 /^-\{2,}/
   syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
   hi def link plug1       Title
   hi def link plug2       Repeat
   hi def link plugH2      Type
   hi def link plugX       Exception
   syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
   hi def link plug1       Title
   hi def link plug2       Repeat
   hi def link plugH2      Type
   hi def link plugX       Exception
+  hi def link plugAbort   Ignore
   hi def link plugBracket Structure
   hi def link plugNumber  Number
 
   hi def link plugBracket Structure
   hi def link plugNumber  Number
 
@@ -778,7 +858,7 @@ function! s:lastline(msg)
 endfunction
 
 function! s:new_window()
 endfunction
 
 function! s:new_window()
-  execute get(g:, 'plug_window', 'vertical topleft new')
+  execute get(g:, 'plug_window', '-tabnew')
 endfunction
 
 function! s:plug_window_exists()
 endfunction
 
 function! s:plug_window_exists()
@@ -840,7 +920,7 @@ function! s:prepare(...)
     endif
   endfor
 
     endif
   endfor
 
-  call s:job_abort()
+  call s:job_abort(0)
   if s:switch_in()
     if b:plug_preview == 1
       pc
   if s:switch_in()
     if b:plug_preview == 1
       pc
@@ -850,7 +930,7 @@ function! s:prepare(...)
     call s:new_window()
   endif
 
     call s:new_window()
   endif
 
-  nnoremap <silent> <buffer> q  :if b:plug_preview==1<bar>pc<bar>endif<bar>bd<cr>
+  nnoremap <silent> <buffer> q :call <SID>close_pane()<cr>
   if a:0 == 0
     call s:finish_bindings()
   endif
   if a:0 == 0
     call s:finish_bindings()
   endif
@@ -872,6 +952,17 @@ function! s:prepare(...)
   endif
 endfunction
 
   endif
 endfunction
 
+function! s:close_pane()
+  if b:plug_preview == 1
+    pc
+    let b:plug_preview = -1
+  elseif exists('s:jobs') && !empty(s:jobs)
+    call s:job_abort(1)
+  else
+    bd
+  endif
+endfunction
+
 function! s:assign_name()
   " Assign buffer name
   let prefix = '[Plugins]'
 function! s:assign_name()
   " Assign buffer name
   let prefix = '[Plugins]'
@@ -890,9 +981,9 @@ function! s:chsh(swap)
     set shell=sh
   endif
   if a:swap
     set shell=sh
   endif
   if a:swap
-    if &shell =~# 'powershell\.exe' || &shell =~# 'pwsh$'
+    if s:is_powershell(&shell)
       let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s'
       let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s'
-    elseif &shell =~# 'sh' || &shell =~# 'cmd\.exe'
+    elseif &shell =~# 'sh' || &shell =~# 'cmd\(\.exe\)\?$'
       set shellredir=>%s\ 2>&1
     endif
   endif
       set shellredir=>%s\ 2>&1
     endif
   endif
@@ -931,6 +1022,11 @@ function! s:is_updated(dir)
 endfunction
 
 function! s:do(pull, force, todo)
 endfunction
 
 function! s:do(pull, force, todo)
+  if has('nvim')
+    " Reset &rtp to invalidate Neovim cache of loaded Lua modules
+    " See https://github.com/junegunn/vim-plug/pull/1157#issuecomment-1809226110
+    let &rtp = &rtp
+  endif
   for [name, spec] in items(a:todo)
     if !isdirectory(spec.dir)
       continue
   for [name, spec] in items(a:todo)
     if !isdirectory(spec.dir)
       continue
@@ -989,14 +1085,21 @@ function! s:hash_match(a, b)
   return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0
 endfunction
 
   return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0
 endfunction
 
+function! s:disable_credential_helper()
+  return s:git_version_requirement(2) && get(g:, 'plug_disable_credential_helper', 1)
+endfunction
+
 function! s:checkout(spec)
   let sha = a:spec.commit
 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_revision(a:spec.dir)
+  let error = 0
+  if !empty(output) && !s:hash_match(sha, s:lines(output)[0])
+    let credential_helper = s:disable_credential_helper() ? '-c credential.helper= ' : ''
     let output = s:system(
     let output = s:system(
-          \ 'git fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir)
+          \ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir)
+    let error = v:shell_error
   endif
   endif
-  return output
+  return [output, error]
 endfunction
 
 function! s:finish(pull)
 endfunction
 
 function! s:finish(pull)
@@ -1057,7 +1160,7 @@ function! s:update_impl(pull, force, args) abort
   let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
                   \ remove(args, -1) : get(g:, 'plug_threads', 16)
 
   let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
                   \ remove(args, -1) : get(g:, 'plug_threads', 16)
 
-  let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
+  let managed = filter(deepcopy(g:plugs), 's:is_managed(v:key)')
   let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') :
                          \ filter(managed, 'index(args, v:key) >= 0')
 
   let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') :
                          \ filter(managed, 'index(args, v:key) >= 0')
 
@@ -1109,7 +1212,8 @@ function! s:update_impl(pull, force, args) abort
   normal! 2G
   silent! redraw
 
   normal! 2G
   silent! redraw
 
-  let s:clone_opt = []
+  " Set remote name, overriding a possible user git config's clone.defaultRemoteName
+  let s:clone_opt = ['--origin', 'origin']
   if get(g:, 'plug_shallow', 1)
     call extend(s:clone_opt, ['--depth', '1'])
     if s:git_version_requirement(1, 7, 10)
   if get(g:, 'plug_shallow', 1)
     call extend(s:clone_opt, ['--depth', '1'])
     if s:git_version_requirement(1, 7, 10)
@@ -1190,9 +1294,11 @@ function! s:update_finish()
       if !pos
         continue
       endif
       if !pos
         continue
       endif
+      let out = ''
+      let error = 0
       if has_key(spec, 'commit')
         call s:log4(name, 'Checking out '.spec.commit)
       if has_key(spec, 'commit')
         call s:log4(name, 'Checking out '.spec.commit)
-        let out = s:checkout(spec)
+        let [out, error] = s:checkout(spec)
       elseif has_key(spec, 'tag')
         let tag = spec.tag
         if tag =~ '\*'
       elseif has_key(spec, 'tag')
         let tag = spec.tag
         if tag =~ '\*'
@@ -1205,19 +1311,16 @@ function! s:update_finish()
         endif
         call s:log4(name, 'Checking out '.tag)
         let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir)
         endif
         call s:log4(name, 'Checking out '.tag)
         let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir)
-      else
-        let branch = get(spec, 'branch', 'master')
-        call s:log4(name, 'Merging origin/'.s:esc(branch))
-        let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1'
-              \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('origin/'.branch).' 2>&1')), spec.dir)
+        let error = v:shell_error
       endif
       endif
-      if !v:shell_error && filereadable(spec.dir.'/.gitmodules') &&
+      if !error && filereadable(spec.dir.'/.gitmodules') &&
             \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir))
         call s:log4(name, 'Updating submodules. This may take a while.')
         let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir)
             \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir))
         call s:log4(name, 'Updating submodules. This may take a while.')
         let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir)
+        let error = v:shell_error
       endif
       let msg = s:format_message(v:shell_error ? 'x': '-', name, out)
       endif
       let msg = s:format_message(v:shell_error ? 'x': '-', name, out)
-      if v:shell_error
+      if error
         call add(s:update.errors, name)
         call s:regress_bar()
         silent execute pos 'd _'
         call add(s:update.errors, name)
         call s:regress_bar()
         silent execute pos 'd _'
@@ -1241,7 +1344,12 @@ function! s:update_finish()
   endif
 endfunction
 
   endif
 endfunction
 
-function! s:job_abort()
+function! s:mark_aborted(name, message)
+  let attrs = { 'running': 0, 'error': 1, 'abort': 1, 'lines': [a:message] }
+  let s:jobs[a:name] = extend(get(s:jobs, a:name, {}), attrs)
+endfunction
+
+function! s:job_abort(cancel)
   if (!s:nvim && !s:vim8) || !exists('s:jobs')
     return
   endif
   if (!s:nvim && !s:vim8) || !exists('s:jobs')
     return
   endif
@@ -1255,8 +1363,18 @@ function! s:job_abort()
     if j.new
       call s:rm_rf(g:plugs[name].dir)
     endif
     if j.new
       call s:rm_rf(g:plugs[name].dir)
     endif
+    if a:cancel
+      call s:mark_aborted(name, 'Aborted')
+    endif
   endfor
   endfor
-  let s:jobs = {}
+
+  if a:cancel
+    for todo in values(s:update.todo)
+      let todo.abort = 1
+    endfor
+  else
+    let s:jobs = {}
+  endif
 endfunction
 
 function! s:last_non_empty_line(lines)
 endfunction
 
 function! s:last_non_empty_line(lines)
@@ -1270,6 +1388,16 @@ function! s:last_non_empty_line(lines)
   return ''
 endfunction
 
   return ''
 endfunction
 
+function! s:bullet_for(job, ...)
+  if a:job.running
+    return a:job.new ? '+' : '*'
+  endif
+  if get(a:job, 'abort', 0)
+    return '~'
+  endif
+  return a:job.error ? 'x' : get(a:000, 0, '-')
+endfunction
+
 function! s:job_out_cb(self, data) abort
   let self = a:self
   let data = remove(self.lines, -1) . a:data
 function! s:job_out_cb(self, data) abort
   let self = a:self
   let data = remove(self.lines, -1) . a:data
@@ -1278,9 +1406,10 @@ function! s:job_out_cb(self, data) abort
   " To reduce the number of buffer updates
   let self.tick = get(self, 'tick', -1) + 1
   if !self.running || self.tick % len(s:jobs) == 0
   " To reduce the number of buffer updates
   let self.tick = get(self, 'tick', -1) + 1
   if !self.running || self.tick % len(s:jobs) == 0
-    let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-')
     let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines)
     let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines)
-    call s:log(bullet, self.name, result)
+    if len(result)
+      call s:log(s:bullet_for(self), self.name, result)
+    endif
   endif
 endfunction
 
   endif
 endfunction
 
@@ -1293,7 +1422,7 @@ endfunction
 
 function! s:job_cb(fn, job, ch, data)
   if !s:plug_window_exists() " plug window closed
 
 function! s:job_cb(fn, job, ch, data)
   if !s:plug_window_exists() " plug window closed
-    return s:job_abort()
+    return s:job_abort(0)
   endif
   call call(a:fn, [a:job, a:data])
 endfunction
   endif
   call call(a:fn, [a:job, a:data])
 endfunction
@@ -1304,16 +1433,17 @@ function! s:nvim_cb(job_id, data, event) dict abort
     \ s:job_cb('s:job_exit_cb', self, 0, a:data)
 endfunction
 
     \ s:job_cb('s:job_exit_cb', self, 0, a:data)
 endfunction
 
-function! s:spawn(name, cmd, opts)
-  let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''],
-            \ 'new': get(a:opts, 'new', 0) }
+function! s:spawn(name, spec, queue, opts)
+  let job = { 'name': a:name, 'spec': a:spec, 'running': 1, 'error': 0, 'lines': [''],
+            \ 'new': get(a:opts, 'new', 0), 'queue': copy(a:queue) }
+  let Item = remove(job.queue, 0)
+  let argv = type(Item) == s:TYPE.funcref ? call(Item, [a:spec]) : Item
   let s:jobs[a:name] = job
 
   if s:nvim
     if has_key(a:opts, 'dir')
       let job.cwd = a:opts.dir
     endif
   let s:jobs[a:name] = job
 
   if s:nvim
     if has_key(a:opts, 'dir')
       let job.cwd = a:opts.dir
     endif
-    let argv = a:cmd
     call extend(job, {
     \ 'on_stdout': function('s:nvim_cb'),
     \ 'on_stderr': function('s:nvim_cb'),
     call extend(job, {
     \ 'on_stdout': function('s:nvim_cb'),
     \ 'on_stderr': function('s:nvim_cb'),
@@ -1329,7 +1459,7 @@ function! s:spawn(name, cmd, opts)
             \ 'Invalid arguments (or job table is full)']
     endif
   elseif s:vim8
             \ 'Invalid arguments (or job table is full)']
     endif
   elseif s:vim8
-    let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})'))
+    let cmd = join(map(copy(argv), 'plug#shellescape(v:val, {"script": 0})'))
     if has_key(a:opts, 'dir')
       let cmd = s:with_cd(cmd, a:opts.dir, 0)
     endif
     if has_key(a:opts, 'dir')
       let cmd = s:with_cd(cmd, a:opts.dir, 0)
     endif
@@ -1349,27 +1479,33 @@ function! s:spawn(name, cmd, opts)
       let job.lines   = ['Failed to start job']
     endif
   else
       let job.lines   = ['Failed to start job']
     endif
   else
-    let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd]))
+    let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [argv, a:opts.dir] : [argv]))
     let job.error = v:shell_error != 0
     let job.running = 0
   endif
 endfunction
 
 function! s:reap(name)
     let job.error = v:shell_error != 0
     let job.running = 0
   endif
 endfunction
 
 function! s:reap(name)
-  let job = s:jobs[a:name]
+  let job = remove(s:jobs, a:name)
   if job.error
     call add(s:update.errors, a:name)
   elseif get(job, 'new', 0)
     let s:update.new[a:name] = 1
   endif
   if job.error
     call add(s:update.errors, a:name)
   elseif get(job, 'new', 0)
     let s:update.new[a:name] = 1
   endif
-  let s:update.bar .= job.error ? 'x' : '='
 
 
-  let bullet = job.error ? 'x' : '-'
+  let more = len(get(job, 'queue', []))
   let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines)
   let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines)
-  call s:log(bullet, a:name, empty(result) ? 'OK' : result)
-  call s:bar()
+  if len(result)
+    call s:log(s:bullet_for(job), a:name, result)
+  endif
 
 
-  call remove(s:jobs, a:name)
+  if !job.error && more
+    let job.spec.queue = job.queue
+    let s:update.todo[a:name] = job.spec
+  else
+    let s:update.bar .= s:bullet_for(job, '=')
+    call s:bar()
+  endif
 endfunction
 
 function! s:bar()
 endfunction
 
 function! s:bar()
@@ -1422,6 +1558,16 @@ function! s:update_vim()
   call s:tick()
 endfunction
 
   call s:tick()
 endfunction
 
+function! s:checkout_command(spec)
+  let a:spec.branch = s:git_origin_branch(a:spec)
+  return ['git', 'checkout', '-q', a:spec.branch, '--']
+endfunction
+
+function! s:merge_command(spec)
+  let a:spec.branch = s:git_origin_branch(a:spec)
+  return ['git', 'merge', '--ff-only', 'origin/'.a:spec.branch]
+endfunction
+
 function! s:tick()
   let pull = s:update.pull
   let prog = s:progress_opt(s:nvim || s:vim8)
 function! s:tick()
   let pull = s:update.pull
   let prog = s:progress_opt(s:nvim || s:vim8)
@@ -1436,24 +1582,39 @@ while 1 " Without TCO, Vim stack is bound to explode
 
   let name = keys(s:update.todo)[0]
   let spec = remove(s:update.todo, name)
 
   let name = keys(s:update.todo)[0]
   let spec = remove(s:update.todo, name)
-  let new  = empty(globpath(spec.dir, '.git', 1))
+  if get(spec, 'abort', 0)
+    call s:mark_aborted(name, 'Skipped')
+    call s:reap(name)
+    continue
+  endif
 
 
-  call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...')
-  redraw
+  let queue = get(spec, 'queue', [])
+  let new = empty(globpath(spec.dir, '.git', 1))
+
+  if empty(queue)
+    call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...')
+    redraw
+  endif
 
   let has_tag = has_key(spec, 'tag')
 
   let has_tag = has_key(spec, 'tag')
-  if !new
+  if len(queue)
+    call s:spawn(name, spec, queue, { 'dir': spec.dir })
+  elseif !new
     let [error, _] = s:git_validate(spec, 0)
     if empty(error)
       if pull
     let [error, _] = s:git_validate(spec, 0)
     if empty(error)
       if pull
-        let cmd = ['git', 'fetch']
+        let cmd = s:disable_credential_helper() ? ['git', '-c', 'credential.helper=', 'fetch'] : ['git', 'fetch']
         if has_tag && !empty(globpath(spec.dir, '.git/shallow'))
           call extend(cmd, ['--depth', '99999999'])
         endif
         if !empty(prog)
           call add(cmd, prog)
         endif
         if has_tag && !empty(globpath(spec.dir, '.git/shallow'))
           call extend(cmd, ['--depth', '99999999'])
         endif
         if !empty(prog)
           call add(cmd, prog)
         endif
-        call s:spawn(name, cmd, { 'dir': spec.dir })
+        let queue = [cmd, split('git remote set-head origin -a')]
+        if !has_tag && !has_key(spec, 'commit')
+          call extend(queue, [function('s:checkout_command'), function('s:merge_command')])
+        endif
+        call s:spawn(name, spec, queue, { 'dir': spec.dir })
       else
         let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 }
       endif
       else
         let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 }
       endif
@@ -1468,7 +1629,7 @@ while 1 " Without TCO, Vim stack is bound to explode
     if !empty(prog)
       call add(cmd, prog)
     endif
     if !empty(prog)
       call add(cmd, prog)
     endif
-    call s:spawn(name, extend(cmd, [spec.uri, s:trim(spec.dir)]), { 'new': 1 })
+    call s:spawn(name, spec, [extend(cmd, [spec.uri, s:trim(spec.dir)]), function('s:checkout_command'), function('s:merge_command')], { 'new': 1 })
   endif
 
   if !s:jobs[name].running
   endif
 
   if !s:jobs[name].running
@@ -2128,9 +2289,9 @@ function! plug#shellescape(arg, ...)
   let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {}
   let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh')
   let script = get(opts, 'script', 1)
   let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {}
   let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh')
   let script = get(opts, 'script', 1)
-  if shell =~# 'cmd\.exe'
+  if shell =~# 'cmd\(\.exe\)\?$'
     return s:shellesc_cmd(a:arg, script)
     return s:shellesc_cmd(a:arg, script)
-  elseif shell =~# 'powershell\.exe' || shell =~# 'pwsh$'
+  elseif s:is_powershell(shell)
     return s:shellesc_ps1(a:arg)
   endif
   return s:shellesc_sh(a:arg)
     return s:shellesc_ps1(a:arg)
   endif
   return s:shellesc_sh(a:arg)
@@ -2167,7 +2328,10 @@ endfunction
 
 function! s:with_cd(cmd, dir, ...)
   let script = a:0 > 0 ? a:1 : 1
 
 function! s:with_cd(cmd, dir, ...)
   let script = a:0 > 0 ? a:1 : 1
-  return printf('cd%s %s && %s', s:is_win ? ' /d' : '', plug#shellescape(a:dir, {'script': script}), a:cmd)
+  let pwsh = s:is_powershell(&shell)
+  let cd = s:is_win && !pwsh ? 'cd /d' : 'cd'
+  let sep = pwsh ? ';' : '&&'
+  return printf('%s %s %s %s', cd, plug#shellescape(a:dir, {'script': script, 'shell': &shell}), sep, a:cmd)
 endfunction
 
 function! s:system(cmd, ...)
 endfunction
 
 function! s:system(cmd, ...)
@@ -2182,7 +2346,7 @@ function! s:system(cmd, ...)
         return system(a:cmd)
       endif
       let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})'))
         return system(a:cmd)
       endif
       let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})'))
-      if &shell =~# 'powershell\.exe'
+      if s:is_powershell(&shell)
         let cmd = '& ' . cmd
       endif
     else
         let cmd = '& ' . cmd
       endif
     else
@@ -2211,54 +2375,57 @@ endfunction
 function! s:git_validate(spec, check_branch)
   let err = ''
   if isdirectory(a:spec.dir)
 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_local_branch(a:spec.dir), s:git_origin_url(a:spec.dir)]
     let remote = result[-1]
     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")
       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
+    elseif !a:check_branch
+      return ['', 0]
+    elseif has_key(a:spec, 'commit')
+      let sha = s:git_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)',
                               \ a:spec.commit[:6], sha[:6]),
                       \ 'PlugUpdate required.'], "\n")
       endif
         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 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 && a:spec.tag !~ '\*'
+        let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.',
+              \ (empty(tag) ? 'N/A' : tag), a:spec.tag)
+      endif
     elseif a:check_branch
     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 && a:spec.tag !~ '\*'
-          let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.',
-                \ (empty(tag) ? 'N/A' : tag), a:spec.tag)
-        endif
-      " Check branch
-      elseif a:spec.branch !=# branch
+      let current_branch = result[0]
+      let origin_branch = s:git_origin_branch(a:spec)
+      if origin_branch !=# current_branch
         let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.',
         let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.',
-              \ branch, a:spec.branch)
+              \ current_branch, origin_branch)
       endif
       if empty(err)
       endif
       if empty(err)
-        let [ahead, behind] = split(s:lastline(s:system([
-        \ 'git', 'rev-list', '--count', '--left-right',
-        \ printf('HEAD...origin/%s', a:spec.branch)
-        \ ], a:spec.dir)), '\t')
-        if !v:shell_error && ahead
-          if behind
+        let ahead_behind = split(s:lastline(s:system([
+          \ 'git', 'rev-list', '--count', '--left-right',
+          \ printf('HEAD...origin/%s', origin_branch)
+          \ ], a:spec.dir)), '\t')
+        if v:shell_error || len(ahead_behind) != 2
+          let err = "Failed to compare with the origin. The default branch might have changed.\nPlugClean required."
+        else
+          let [ahead, behind] = ahead_behind
+          if ahead && 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"
             " 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
+                  \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', origin_branch, ahead, behind)
+          elseif ahead
             let err = printf("Ahead of origin/%s by %d commit(s).\n"
                   \ .'Cannot update until local changes are pushed.',
             let err = printf("Ahead of origin/%s by %d commit(s).\n"
                   \ .'Cannot update until local changes are pushed.',
-                  \ a:spec.branch, ahead)
+                  \ origin_branch, ahead)
           endif
         endif
       endif
           endif
         endif
       endif
@@ -2271,7 +2438,7 @@ endfunction
 
 function! s:rm_rf(dir)
   if isdirectory(a:dir)
 
 function! s:rm_rf(dir)
   if isdirectory(a:dir)
-    call s:system(s:is_win
+    return s:system(s:is_win
     \ ? 'rmdir /S /Q '.plug#shellescape(a:dir)
     \ : ['rm', '-rf', a:dir])
   endif
     \ ? 'rmdir /S /Q '.plug#shellescape(a:dir)
     \ : ['rm', '-rf', a:dir])
   endif
@@ -2287,7 +2454,7 @@ function! s:clean(force)
   let errs = {}
   let [cnt, total] = [0, len(g:plugs)]
   for [name, spec] in items(g:plugs)
   let errs = {}
   let [cnt, total] = [0, len(g:plugs)]
   for [name, spec] in items(g:plugs)
-    if !s:is_managed(name)
+    if !s:is_managed(name) || get(spec, 'frozen', 0)
       call add(dirs, spec.dir)
     else
       let [err, clean] = s:git_validate(spec, 1)
       call add(dirs, spec.dir)
     else
       let [err, clean] = s:git_validate(spec, 1)
@@ -2355,6 +2522,7 @@ endfunction
 function! s:delete(range, force)
   let [l1, l2] = a:range
   let force = a:force
 function! s:delete(range, force)
   let [l1, l2] = a:range
   let force = a:force
+  let err_count = 0
   while l1 <= l2
     let line = getline(l1)
     if line =~ '^- ' && isdirectory(line[2:])
   while l1 <= l2
     let line = getline(l1)
     if line =~ '^- ' && isdirectory(line[2:])
@@ -2363,11 +2531,22 @@ function! s:delete(range, force)
       let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1)
       let force = force || answer > 1
       if answer
       let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1)
       let force = force || answer > 1
       if answer
-        call s:rm_rf(line[2:])
+        let err = s:rm_rf(line[2:])
         setlocal modifiable
         setlocal modifiable
-        call setline(l1, '~'.line[1:])
-        let s:clean_count += 1
-        call setline(4, printf('Removed %d directories.', s:clean_count))
+        if empty(err)
+          call setline(l1, '~'.line[1:])
+          let s:clean_count += 1
+        else
+          delete _
+          call append(l1 - 1, s:format_message('x', line[1:], err))
+          let l2 += len(s:lines(err))
+          let err_count += 1
+        endif
+        let msg = printf('Removed %d directories.', s:clean_count)
+        if err_count > 0
+          let msg .= printf(' Failed to remove %d directories.', err_count)
+        endif
+        call setline(4, msg)
         setlocal nomodifiable
       endif
     endif
         setlocal nomodifiable
       endif
     endif
@@ -2507,26 +2686,34 @@ function! s:preview_commit()
 
   let sha = matchstr(getline('.'), '^  \X*\zs[0-9a-f]\{7,9}')
   if empty(sha)
 
   let sha = matchstr(getline('.'), '^  \X*\zs[0-9a-f]\{7,9}')
   if empty(sha)
-    return
+    let name = matchstr(getline('.'), '^- \zs[^:]*\ze:$')
+    if empty(name)
+      return
+    endif
+    let title = 'HEAD@{1}..'
+    let command = 'git diff --no-color HEAD@{1}'
+  else
+    let title = sha
+    let command = 'git show --no-color --pretty=medium '.sha
+    let name = s:find_name(line('.'))
   endif
 
   endif
 
-  let name = s:find_name(line('.'))
   if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir)
     return
   endif
 
   if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir)
     return
   endif
 
-  if exists('g:plug_pwindow') && !s:is_preview_window_open()
-    execute g:plug_pwindow
-    execute 'e' sha
+  if !s:is_preview_window_open()
+    execute get(g:, 'plug_pwindow', 'vertical rightbelow new')
+    execute 'e' title
   else
   else
-    execute 'pedit' sha
+    execute 'pedit' title
     wincmd P
   endif
     wincmd P
   endif
-  setlocal previewwindow filetype=git buftype=nofile nobuflisted modifiable
+  setlocal previewwindow filetype=git buftype=nofile bufhidden=wipe nobuflisted modifiable
   let batchfile = ''
   try
     let [sh, shellcmdflag, shrd] = s:chsh(1)
   let batchfile = ''
   try
     let [sh, shellcmdflag, shrd] = s:chsh(1)
-    let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha
+    let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && '.command
     if s:is_win
       let [batchfile, cmd] = s:batchfile(cmd)
     endif
     if s:is_win
       let [batchfile, cmd] = s:batchfile(cmd)
     endif
@@ -2576,20 +2763,23 @@ function! s:diff()
     endif
     call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:')
     for [k, v] in plugs
     endif
     call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:')
     for [k, v] in plugs
-      let range = origin ? '..origin/'.v.branch : 'HEAD@{1}..'
-      let cmd = ['git', 'log', '--graph', '--color=never']
-      if s:git_version_requirement(2, 10, 0)
-        call add(cmd, '--no-show-signature')
-      endif
-      call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range])
-      if has_key(v, 'rtp')
-        call extend(cmd, ['--', v.rtp])
-      endif
-      let diff = s:system_chomp(cmd, v.dir)
-      if !empty(diff)
-        let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : ''
-        call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)')))
-        let cnts[origin] += 1
+      let branch = s:git_origin_branch(v)
+      if len(branch)
+        let range = origin ? '..origin/'.branch : 'HEAD@{1}..'
+        let cmd = ['git', 'log', '--graph', '--color=never']
+        if s:git_version_requirement(2, 10, 0)
+          call add(cmd, '--no-show-signature')
+        endif
+        call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range])
+        if has_key(v, 'rtp')
+          call extend(cmd, ['--', v.rtp])
+        endif
+        let diff = s:system_chomp(cmd, v.dir)
+        if !empty(diff)
+          let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : ''
+          call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)')))
+          let cnts[origin] += 1
+        endif
       endif
       let bar .= '='
       call s:progress_bar(2, bar, len(total))
       endif
       let bar .= '='
       call s:progress_bar(2, bar, len(total))
@@ -2649,9 +2839,9 @@ function! s:snapshot(force, ...) abort
   1
   let anchor = line('$') - 3
   let names = sort(keys(filter(copy(g:plugs),
   1
   let anchor = line('$') - 3
   let names = sort(keys(filter(copy(g:plugs),
-        \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)')))
+        \'has_key(v:val, "uri") && isdirectory(v:val.dir)')))
   for name in reverse(names)
   for name in reverse(names)
-    let sha = s:system_chomp(['git', 'rev-parse', '--short', 'HEAD'], g:plugs[name].dir)
+    let sha = has_key(g:plugs[name], 'commit') ? g:plugs[name].commit : s:git_revision(g:plugs[name].dir)
     if !empty(sha)
       call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha))
       redraw
     if !empty(sha)
       call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha))
       redraw