Parcourir la source

Keep track of the default branch of the origin (#1272)

Fix #1005

vim-plug will now run `git remote set-head origin -a` on PlugUpdate to
keep track of the default branch of the origin, so that it can still
update a plugin even if its default branch has changed.

This additional command will slow down the update process, but this is
an unavoidable price to pay for the correctness of the task. However,
vim-plug will run checkout and merge commands in parallel, so this
improvement will slightly offset the slowdown.
Junegunn Choi il y a 1 an
Parent
commit
ed19478ce2
2 fichiers modifiés avec 61 ajouts et 30 suppressions
  1. 59 29
      plug.vim
  2. 2 1
      test/workflow.vader

+ 59 - 29
plug.vim

@@ -1106,12 +1106,14 @@ endfunction
 function! s:checkout(spec)
 function! s:checkout(spec)
   let sha = a:spec.commit
   let sha = a:spec.commit
   let output = s:git_revision(a:spec.dir)
   let output = s:git_revision(a:spec.dir)
+  let error = 0
   if !empty(output) && !s:hash_match(sha, s:lines(output)[0])
   if !empty(output) && !s:hash_match(sha, s:lines(output)[0])
     let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : ''
     let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : ''
     let output = s:system(
     let output = s:system(
           \ 'git '.credential_helper.'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
 endfunction
 
 
 function! s:finish(pull)
 function! s:finish(pull)
@@ -1172,7 +1174,7 @@ function! s:update_impl(pull, force, args) abort
   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', 16)
                   \ 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)') :
   let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') :
                          \ filter(managed, 'index(args, v:key) >= 0')
                          \ filter(managed, 'index(args, v:key) >= 0')
 
 
@@ -1306,9 +1308,11 @@ function! s:update_finish()
       if !pos
       if !pos
         continue
         continue
       endif
       endif
+      let out = ''
+      let error = 0
       if has_key(spec, 'commit')
       if has_key(spec, 'commit')
         call s:log4(name, 'Checking out '.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')
       elseif has_key(spec, 'tag')
         let tag = spec.tag
         let tag = spec.tag
         if tag =~ '\*'
         if tag =~ '\*'
@@ -1321,19 +1325,16 @@ function! s:update_finish()
         endif
         endif
         call s:log4(name, 'Checking out '.tag)
         call s:log4(name, 'Checking out '.tag)
         let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir)
         let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir)
-      else
-        let branch = s:git_origin_branch(spec)
-        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))
             \ (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.')
         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 out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir)
+        let error = v:shell_error
       endif
       endif
       let msg = s:format_message(v:shell_error ? 'x': '-', name, out)
       let msg = s:format_message(v:shell_error ? 'x': '-', name, out)
-      if v:shell_error
+      if error
         call add(s:update.errors, name)
         call add(s:update.errors, name)
         call s:regress_bar()
         call s:regress_bar()
         silent execute pos 'd _'
         silent execute pos 'd _'
@@ -1396,7 +1397,9 @@ function! s:job_out_cb(self, data) abort
   if !self.running || self.tick % len(s:jobs) == 0
   if !self.running || self.tick % len(s:jobs) == 0
     let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-')
     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(bullet, self.name, result)
+    endif
   endif
   endif
 endfunction
 endfunction
 
 
@@ -1420,16 +1423,17 @@ function! s:nvim_cb(job_id, data, event) dict abort
     \ s:job_cb('s:job_exit_cb', self, 0, a:data)
     \ s:job_cb('s:job_exit_cb', self, 0, a:data)
 endfunction
 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
   let s:jobs[a:name] = job
 
 
   if s:nvim
   if s:nvim
     if has_key(a:opts, 'dir')
     if has_key(a:opts, 'dir')
       let job.cwd = a:opts.dir
       let job.cwd = a:opts.dir
     endif
     endif
-    let argv = a:cmd
     call extend(job, {
     call extend(job, {
     \ 'on_stdout': function('s:nvim_cb'),
     \ 'on_stdout': function('s:nvim_cb'),
     \ 'on_stderr': function('s:nvim_cb'),
     \ 'on_stderr': function('s:nvim_cb'),
@@ -1445,7 +1449,7 @@ function! s:spawn(name, cmd, opts)
             \ 'Invalid arguments (or job table is full)']
             \ 'Invalid arguments (or job table is full)']
     endif
     endif
   elseif s:vim8
   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')
     if has_key(a:opts, 'dir')
       let cmd = s:with_cd(cmd, a:opts.dir, 0)
       let cmd = s:with_cd(cmd, a:opts.dir, 0)
     endif
     endif
@@ -1465,27 +1469,34 @@ function! s:spawn(name, cmd, opts)
       let job.lines   = ['Failed to start job']
       let job.lines   = ['Failed to start job']
     endif
     endif
   else
   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.error = v:shell_error != 0
     let job.running = 0
     let job.running = 0
   endif
   endif
 endfunction
 endfunction
 
 
 function! s:reap(name)
 function! s:reap(name)
-  let job = s:jobs[a:name]
+  let job = remove(s:jobs, a:name)
   if job.error
   if job.error
     call add(s:update.errors, a:name)
     call add(s:update.errors, a:name)
   elseif get(job, 'new', 0)
   elseif get(job, 'new', 0)
     let s:update.new[a:name] = 1
     let s:update.new[a:name] = 1
   endif
   endif
-  let s:update.bar .= job.error ? 'x' : '='
 
 
-  let bullet = job.error ? 'x' : '-'
+  let more = len(get(job, 'queue', []))
+  let bullet = job.error ? 'x' : more ? (job.new ? '+' : '*') : '-'
   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(bullet, 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 .= job.error ? 'x' : '='
+    call s:bar()
+  endif
 endfunction
 endfunction
 
 
 function! s:bar()
 function! s:bar()
@@ -1538,6 +1549,16 @@ function! s:update_vim()
   call s:tick()
   call s:tick()
 endfunction
 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()
 function! s:tick()
   let pull = s:update.pull
   let pull = s:update.pull
   let prog = s:progress_opt(s:nvim || s:vim8)
   let prog = s:progress_opt(s:nvim || s:vim8)
@@ -1552,13 +1573,18 @@ while 1 " Without TCO, Vim stack is bound to explode
 
 
   let name = keys(s:update.todo)[0]
   let name = keys(s:update.todo)[0]
   let spec = remove(s:update.todo, name)
   let spec = remove(s:update.todo, name)
-  let new  = empty(globpath(spec.dir, '.git', 1))
+  let queue = get(spec, 'queue', [])
+  let new = empty(globpath(spec.dir, '.git', 1))
 
 
-  call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...')
-  redraw
+  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)
     let [error, _] = s:git_validate(spec, 0)
     if empty(error)
     if empty(error)
       if pull
       if pull
@@ -1569,7 +1595,11 @@ while 1 " Without TCO, Vim stack is bound to explode
         if !empty(prog)
         if !empty(prog)
           call add(cmd, prog)
           call add(cmd, prog)
         endif
         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
       else
         let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 }
         let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 }
       endif
       endif
@@ -1584,7 +1614,7 @@ while 1 " Without TCO, Vim stack is bound to explode
     if !empty(prog)
     if !empty(prog)
       call add(cmd, prog)
       call add(cmd, prog)
     endif
     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
   endif
 
 
   if !s:jobs[name].running
   if !s:jobs[name].running

+ 2 - 1
test/workflow.vader

@@ -983,7 +983,8 @@ Execute (PlugInstall!):
   Assert filereadable(g:plugs['vim-easy-align'].dir.'/installed2'),
   Assert filereadable(g:plugs['vim-easy-align'].dir.'/installed2'),
     \ 'vim-easy-align/installed2 should exist'
     \ 'vim-easy-align/installed2 should exist'
   AssertEqual '7f8cd78cb1fe52185b98b16a3749811f0cc508af', GitCommit('vim-pseudocl')
   AssertEqual '7f8cd78cb1fe52185b98b16a3749811f0cc508af', GitCommit('vim-pseudocl')
-  AssertEqual 'no-t_co', GitBranch('seoul256.vim')
+  " Was updated to the default branch of origin by previous PlugUpdate
+  AssertEqual 'master', GitBranch('seoul256.vim')
   AssertEqual '1.5.3', GitTag('goyo.vim')
   AssertEqual '1.5.3', GitTag('goyo.vim')
 
 
 Execute (When submodules are not initialized):
 Execute (When submodules are not initialized):