Skip to content

Commit ed19478

Browse files
authored
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.
1 parent e2974a3 commit ed19478

File tree

2 files changed

+61
-30
lines changed

2 files changed

+61
-30
lines changed

Diff for: plug.vim

+59-29
Original file line numberDiff line numberDiff line change
@@ -1106,12 +1106,14 @@ endfunction
11061106
function! s:checkout(spec)
11071107
let sha = a:spec.commit
11081108
let output = s:git_revision(a:spec.dir)
1109+
let error = 0
11091110
if !empty(output) && !s:hash_match(sha, s:lines(output)[0])
11101111
let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : ''
11111112
let output = s:system(
11121113
\ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir)
1114+
let error = v:shell_error
11131115
endif
1114-
return output
1116+
return [output, error]
11151117
endfunction
11161118

11171119
function! s:finish(pull)
@@ -1172,7 +1174,7 @@ function! s:update_impl(pull, force, args) abort
11721174
let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
11731175
\ remove(args, -1) : get(g:, 'plug_threads', 16)
11741176

1175-
let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
1177+
let managed = filter(deepcopy(g:plugs), 's:is_managed(v:key)')
11761178
let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') :
11771179
\ filter(managed, 'index(args, v:key) >= 0')
11781180

@@ -1306,9 +1308,11 @@ function! s:update_finish()
13061308
if !pos
13071309
continue
13081310
endif
1311+
let out = ''
1312+
let error = 0
13091313
if has_key(spec, 'commit')
13101314
call s:log4(name, 'Checking out '.spec.commit)
1311-
let out = s:checkout(spec)
1315+
let [out, error] = s:checkout(spec)
13121316
elseif has_key(spec, 'tag')
13131317
let tag = spec.tag
13141318
if tag =~ '\*'
@@ -1321,19 +1325,16 @@ function! s:update_finish()
13211325
endif
13221326
call s:log4(name, 'Checking out '.tag)
13231327
let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir)
1324-
else
1325-
let branch = s:git_origin_branch(spec)
1326-
call s:log4(name, 'Merging origin/'.s:esc(branch))
1327-
let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1'
1328-
\. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('origin/'.branch).' 2>&1')), spec.dir)
1328+
let error = v:shell_error
13291329
endif
1330-
if !v:shell_error && filereadable(spec.dir.'/.gitmodules') &&
1330+
if !error && filereadable(spec.dir.'/.gitmodules') &&
13311331
\ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir))
13321332
call s:log4(name, 'Updating submodules. This may take a while.')
13331333
let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir)
1334+
let error = v:shell_error
13341335
endif
13351336
let msg = s:format_message(v:shell_error ? 'x': '-', name, out)
1336-
if v:shell_error
1337+
if error
13371338
call add(s:update.errors, name)
13381339
call s:regress_bar()
13391340
silent execute pos 'd _'
@@ -1396,7 +1397,9 @@ function! s:job_out_cb(self, data) abort
13961397
if !self.running || self.tick % len(s:jobs) == 0
13971398
let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-')
13981399
let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines)
1399-
call s:log(bullet, self.name, result)
1400+
if len(result)
1401+
call s:log(bullet, self.name, result)
1402+
endif
14001403
endif
14011404
endfunction
14021405

@@ -1420,16 +1423,17 @@ function! s:nvim_cb(job_id, data, event) dict abort
14201423
\ s:job_cb('s:job_exit_cb', self, 0, a:data)
14211424
endfunction
14221425

1423-
function! s:spawn(name, cmd, opts)
1424-
let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''],
1425-
\ 'new': get(a:opts, 'new', 0) }
1426+
function! s:spawn(name, spec, queue, opts)
1427+
let job = { 'name': a:name, 'spec': a:spec, 'running': 1, 'error': 0, 'lines': [''],
1428+
\ 'new': get(a:opts, 'new', 0), 'queue': copy(a:queue) }
1429+
let Item = remove(job.queue, 0)
1430+
let argv = type(Item) == s:TYPE.funcref ? call(Item, [a:spec]) : Item
14261431
let s:jobs[a:name] = job
14271432

14281433
if s:nvim
14291434
if has_key(a:opts, 'dir')
14301435
let job.cwd = a:opts.dir
14311436
endif
1432-
let argv = a:cmd
14331437
call extend(job, {
14341438
\ 'on_stdout': function('s:nvim_cb'),
14351439
\ 'on_stderr': function('s:nvim_cb'),
@@ -1445,7 +1449,7 @@ function! s:spawn(name, cmd, opts)
14451449
\ 'Invalid arguments (or job table is full)']
14461450
endif
14471451
elseif s:vim8
1448-
let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})'))
1452+
let cmd = join(map(copy(argv), 'plug#shellescape(v:val, {"script": 0})'))
14491453
if has_key(a:opts, 'dir')
14501454
let cmd = s:with_cd(cmd, a:opts.dir, 0)
14511455
endif
@@ -1465,27 +1469,34 @@ function! s:spawn(name, cmd, opts)
14651469
let job.lines = ['Failed to start job']
14661470
endif
14671471
else
1468-
let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd]))
1472+
let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [argv, a:opts.dir] : [argv]))
14691473
let job.error = v:shell_error != 0
14701474
let job.running = 0
14711475
endif
14721476
endfunction
14731477

14741478
function! s:reap(name)
1475-
let job = s:jobs[a:name]
1479+
let job = remove(s:jobs, a:name)
14761480
if job.error
14771481
call add(s:update.errors, a:name)
14781482
elseif get(job, 'new', 0)
14791483
let s:update.new[a:name] = 1
14801484
endif
1481-
let s:update.bar .= job.error ? 'x' : '='
14821485

1483-
let bullet = job.error ? 'x' : '-'
1486+
let more = len(get(job, 'queue', []))
1487+
let bullet = job.error ? 'x' : more ? (job.new ? '+' : '*') : '-'
14841488
let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines)
1485-
call s:log(bullet, a:name, empty(result) ? 'OK' : result)
1486-
call s:bar()
1489+
if len(result)
1490+
call s:log(bullet, a:name, result)
1491+
endif
14871492

1488-
call remove(s:jobs, a:name)
1493+
if !job.error && more
1494+
let job.spec.queue = job.queue
1495+
let s:update.todo[a:name] = job.spec
1496+
else
1497+
let s:update.bar .= job.error ? 'x' : '='
1498+
call s:bar()
1499+
endif
14891500
endfunction
14901501

14911502
function! s:bar()
@@ -1538,6 +1549,16 @@ function! s:update_vim()
15381549
call s:tick()
15391550
endfunction
15401551

1552+
function! s:checkout_command(spec)
1553+
let a:spec.branch = s:git_origin_branch(a:spec)
1554+
return ['git', 'checkout', '-q', a:spec.branch, '--']
1555+
endfunction
1556+
1557+
function! s:merge_command(spec)
1558+
let a:spec.branch = s:git_origin_branch(a:spec)
1559+
return ['git', 'merge', '--ff-only', 'origin/'.a:spec.branch]
1560+
endfunction
1561+
15411562
function! s:tick()
15421563
let pull = s:update.pull
15431564
let prog = s:progress_opt(s:nvim || s:vim8)
@@ -1552,13 +1573,18 @@ while 1 " Without TCO, Vim stack is bound to explode
15521573

15531574
let name = keys(s:update.todo)[0]
15541575
let spec = remove(s:update.todo, name)
1555-
let new = empty(globpath(spec.dir, '.git', 1))
1576+
let queue = get(spec, 'queue', [])
1577+
let new = empty(globpath(spec.dir, '.git', 1))
15561578

1557-
call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...')
1558-
redraw
1579+
if empty(queue)
1580+
call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...')
1581+
redraw
1582+
endif
15591583

15601584
let has_tag = has_key(spec, 'tag')
1561-
if !new
1585+
if len(queue)
1586+
call s:spawn(name, spec, queue, { 'dir': spec.dir })
1587+
elseif !new
15621588
let [error, _] = s:git_validate(spec, 0)
15631589
if empty(error)
15641590
if pull
@@ -1569,7 +1595,11 @@ while 1 " Without TCO, Vim stack is bound to explode
15691595
if !empty(prog)
15701596
call add(cmd, prog)
15711597
endif
1572-
call s:spawn(name, cmd, { 'dir': spec.dir })
1598+
let queue = [cmd, split('git remote set-head origin -a')]
1599+
if !has_tag && !has_key(spec, 'commit')
1600+
call extend(queue, [function('s:checkout_command'), function('s:merge_command')])
1601+
endif
1602+
call s:spawn(name, spec, queue, { 'dir': spec.dir })
15731603
else
15741604
let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 }
15751605
endif
@@ -1584,7 +1614,7 @@ while 1 " Without TCO, Vim stack is bound to explode
15841614
if !empty(prog)
15851615
call add(cmd, prog)
15861616
endif
1587-
call s:spawn(name, extend(cmd, [spec.uri, s:trim(spec.dir)]), { 'new': 1 })
1617+
call s:spawn(name, spec, [extend(cmd, [spec.uri, s:trim(spec.dir)]), function('s:checkout_command'), function('s:merge_command')], { 'new': 1 })
15881618
endif
15891619

15901620
if !s:jobs[name].running

Diff for: test/workflow.vader

+2-1
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,8 @@ Execute (PlugInstall!):
983983
Assert filereadable(g:plugs['vim-easy-align'].dir.'/installed2'),
984984
\ 'vim-easy-align/installed2 should exist'
985985
AssertEqual '7f8cd78cb1fe52185b98b16a3749811f0cc508af', GitCommit('vim-pseudocl')
986-
AssertEqual 'no-t_co', GitBranch('seoul256.vim')
986+
" Was updated to the default branch of origin by previous PlugUpdate
987+
AssertEqual 'master', GitBranch('seoul256.vim')
987988
AssertEqual '1.5.3', GitTag('goyo.vim')
988989

989990
Execute (When submodules are not initialized):

0 commit comments

Comments
 (0)