4 Commits

Author SHA1 Message Date
Junegunn Choi
854b081934 Open vim-plug window in a new tab (#1274)
* Open vim-plug window in a new tab not to disrupt the current window layout
* Open preview window on the right to better show the diff

If you prefer the old layout, use the following configuration:

    let g:plug_window = 'vertical topleft new'
    let g:plug_pwindow = 'above 12new'
2024-03-07 01:16:12 +09:00
R. N. West
3049761d47 Add dark mode vim-plug logo and use correct logo version in README (#1267) 2024-03-07 01:14:08 +09:00
Junegunn Choi
ed19478ce2 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.
2024-03-07 00:18:49 +09:00
Junegunn Choi
e2974a3367 Fix PlugClean error when the default branch has changed (#1269)
Fix #1253
2024-03-05 16:13:23 +09:00
5 changed files with 97 additions and 47 deletions

View File

@@ -16,8 +16,15 @@
</div> </div>
<br> <br>
<img src="https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.png" height="75" alt="vim-plug">[![build](https://img.shields.io/github/actions/workflow/status/junegunn/vim-plug/test.yml?branch=master)](https://github.com/junegunn/vim-plug/actions/workflows/test.yml?query=branch%3Amaster) <h1 title="vim-plug">
=== <picture>
<source media="(prefers-color-scheme: dark)" srcset="./plug-dark.png">
<img src="./plug.png" height="75" alt="vim-plug">
</picture>
<a href="https://github.com/junegunn/vim-plug/actions/workflows/test.yml?query=branch%3Amaster">
<img src="https://img.shields.io/github/actions/workflow/status/junegunn/vim-plug/test.yml?branch=master">
</a>
</h1>
A minimalist Vim plugin manager. A minimalist Vim plugin manager.
@@ -243,8 +250,8 @@ More examples can be found in:
| `g:plug_timeout` | 60 | Time limit of each task in seconds (*Ruby & Python*) | | `g:plug_timeout` | 60 | Time limit of each task in seconds (*Ruby & Python*) |
| `g:plug_retries` | 2 | Number of retries in case of timeout (*Ruby & Python*) | | `g:plug_retries` | 2 | Number of retries in case of timeout (*Ruby & Python*) |
| `g:plug_shallow` | 1 | Use shallow clone | | `g:plug_shallow` | 1 | Use shallow clone |
| `g:plug_window` | `vertical topleft new` | Command to open plug window | | `g:plug_window` | `-tabnew` | Command to open plug window |
| `g:plug_pwindow` | `above 12new` | Command to open preview window in `PlugDiff` | | `g:plug_pwindow` | `vertical rightbelow new` | Command to open preview window in `PlugDiff` |
| `g:plug_url_format` | `https://git::@github.com/%s.git` | `printf` format to build repo URL (Only applies to the subsequent `Plug` commands) | | `g:plug_url_format` | `https://git::@github.com/%s.git` | `printf` format to build repo URL (Only applies to the subsequent `Plug` commands) |

View File

@@ -1,4 +1,4 @@
plug.txt plug Last change: February 23 2024 plug.txt plug Last change: March 7 2024
PLUG - TABLE OF CONTENTS *plug* *plug-toc* PLUG - TABLE OF CONTENTS *plug* *plug-toc*
============================================================================== ==============================================================================
@@ -230,8 +230,8 @@ Reload .vimrc and `:PlugInstall` to install plugins.
`g:plug_timeout` | 60 | Time limit of each task in seconds (Ruby & Python) `g:plug_timeout` | 60 | Time limit of each task in seconds (Ruby & Python)
`g:plug_retries` | 2 | Number of retries in case of timeout (Ruby & Python) `g:plug_retries` | 2 | Number of retries in case of timeout (Ruby & Python)
`g:plug_shallow` | 1 | Use shallow clone `g:plug_shallow` | 1 | Use shallow clone
`g:plug_window` | `verticaltopleftnew` | Command to open plug window `g:plug_window` | `-tabnew` | Command to open plug window
`g:plug_pwindow` | `above12new` | Command to open preview window in `PlugDiff` `g:plug_pwindow` | `vertical rightbelow new` | Command to open preview window in `PlugDiff`
`g:plug_url_format` | `https://git::@github.com/%s.git` | `printf` format to build repo URL (Only applies to the subsequent `Plug` commands) `g:plug_url_format` | `https://git::@github.com/%s.git` | `printf` format to build repo URL (Only applies to the subsequent `Plug` commands)
--------------------+-----------------------------------+----------------------------------------------------------------------------------- --------------------+-----------------------------------+-----------------------------------------------------------------------------------

BIN
plug-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -878,7 +878,7 @@ function! s:lastline(msg)
endfunction endfunction
function! s:new_window() function! s:new_window()
execute get(g:, 'plug_window', 'vertical topleft new') execute get(g:, 'plug_window', '-tabnew')
endfunction endfunction
function! s:plug_window_exists() function! s:plug_window_exists()
@@ -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 error = v:shell_error
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)
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,8 +1397,10 @@ 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)
if len(result)
call s:log(bullet, self.name, result) call s:log(bullet, self.name, result)
endif endif
endif
endfunction endfunction
function! s:job_exit_cb(self, data) abort function! s:job_exit_cb(self, data) abort
@@ -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) function! s:spawn(name, spec, queue, opts)
let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''], let job = { 'name': a:name, 'spec': a:spec, 'running': 1, 'error': 0, 'lines': [''],
\ 'new': get(a:opts, 'new', 0) } \ '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) if len(result)
call s:bar() 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 queue = get(spec, 'queue', [])
let new = empty(globpath(spec.dir, '.git', 1)) let new = empty(globpath(spec.dir, '.git', 1))
if empty(queue)
call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...')
redraw 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
@@ -2360,18 +2390,21 @@ function! s:git_validate(spec, check_branch)
\ current_branch, origin_branch) \ current_branch, origin_branch)
endif endif
if empty(err) if empty(err)
let [ahead, behind] = split(s:lastline(s:system([ let ahead_behind = split(s:lastline(s:system([
\ 'git', 'rev-list', '--count', '--left-right', \ 'git', 'rev-list', '--count', '--left-right',
\ printf('HEAD...origin/%s', origin_branch) \ printf('HEAD...origin/%s', origin_branch)
\ ], a:spec.dir)), '\t') \ ], a:spec.dir)), '\t')
if !v:shell_error && ahead if v:shell_error || len(ahead_behind) != 2
if behind 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 " Only mention PlugClean if diverged, otherwise it's likely to be
" pushable (and probably not that messed up). " pushable (and probably not that messed up).
let err = printf( let err = printf(
\ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" \ "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.', origin_branch, ahead, behind) \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', origin_branch, ahead, behind)
else elseif ahead
let err = printf("Ahead of origin/%s by %d commit(s).\n" let err = printf("Ahead of origin/%s by %d commit(s).\n"
\ .'Cannot update until local changes are pushed.', \ .'Cannot update until local changes are pushed.',
\ origin_branch, ahead) \ origin_branch, ahead)
@@ -2651,8 +2684,8 @@ function! s:preview_commit()
return return
endif endif
if exists('g:plug_pwindow') && !s:is_preview_window_open() if !s:is_preview_window_open()
execute g:plug_pwindow execute get(g:, 'plug_pwindow', 'vertical rightbelow new')
execute 'e' title execute 'e' title
else else
execute 'pedit' title execute 'pedit' title

View File

@@ -371,6 +371,9 @@ Execute (PlugDiff - 'No updates.'):
q q
Execute (New commits on remote, PlugUpdate, then PlugDiff): Execute (New commits on remote, PlugUpdate, then PlugDiff):
let g:plug_window = 'vertical topleft new'
let g:plug_pwindow = 'above 12new'
for repo in ['seoul256.vim', 'vim-emoji'] for repo in ['seoul256.vim', 'vim-emoji']
for _ in range(2) for _ in range(2)
call system(printf('cd /tmp/vim-plug-test/junegunn/%s && git commit --allow-empty -m "update"', repo)) call system(printf('cd /tmp/vim-plug-test/junegunn/%s && git commit --allow-empty -m "update"', repo))
@@ -458,6 +461,8 @@ Execute (New commits on remote, PlugUpdate, then PlugDiff):
AssertEqual 1, &previewwindow AssertEqual 1, &previewwindow
pclose pclose
unlet g:plug_window g:plug_pwindow
Execute (Test g:plug_pwindow): Execute (Test g:plug_pwindow):
let g:plug_pwindow = 'below 5new' let g:plug_pwindow = 'below 5new'
PlugDiff PlugDiff
@@ -983,7 +988,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):
@@ -1708,6 +1714,8 @@ Execute (#530 - Comparison of incompatible git URIs):
Assert !CompareURI('https://github.com/junegunn/vim-plug.git', 'https://github.com:12345/junegunn/vim-plug.git') Assert !CompareURI('https://github.com/junegunn/vim-plug.git', 'https://github.com:12345/junegunn/vim-plug.git')
Execute (#532 - Reuse plug window): Execute (#532 - Reuse plug window):
let g:plug_window = 'vertical topleft new'
let g:plug_pwindow = 'above 12new'
call plug#begin() call plug#begin()
Plug 'junegunn/goyo.vim' Plug 'junegunn/goyo.vim'
call plug#end() call plug#end()
@@ -1734,6 +1742,8 @@ Execute (#532 - Reuse plug window):
AssertEqual 2, winnr('$'), 'Three windows after PlugStatus (but got '.winnr('$').')' AssertEqual 2, winnr('$'), 'Three windows after PlugStatus (but got '.winnr('$').')'
q q
unlet g:plug_window g:plug_pwindow
Execute (#766 - Allow cloning into an empty directory): Execute (#766 - Allow cloning into an empty directory):
let d = '/tmp/vim-plug-test/goyo-already' let d = '/tmp/vim-plug-test/goyo-already'
call system('rm -rf ' . d) call system('rm -rf ' . d)