104 let s:me = resolve(expand('<sfile>:p')) |
104 let s:me = resolve(expand('<sfile>:p')) |
105 set shellslash |
105 set shellslash |
106 else |
106 else |
107 let s:me = resolve(expand('<sfile>:p')) |
107 let s:me = resolve(expand('<sfile>:p')) |
108 endif |
108 endif |
109 let s:base_spec = { 'branch': 'master', 'frozen': 0 } |
109 let s:base_spec = { 'branch': '', 'frozen': 0 } |
110 let s:TYPE = { |
110 let s:TYPE = { |
111 \ 'string': type(''), |
111 \ 'string': type(''), |
112 \ 'list': type([]), |
112 \ 'list': type([]), |
113 \ 'dict': type({}), |
113 \ 'dict': type({}), |
114 \ 'funcref': type(function('call')) |
114 \ 'funcref': type(function('call')) |
115 \ } |
115 \ } |
116 let s:loaded = get(s:, 'loaded', {}) |
116 let s:loaded = get(s:, 'loaded', {}) |
117 let s:triggers = get(s:, 'triggers', {}) |
117 let s:triggers = get(s:, 'triggers', {}) |
|
118 |
|
119 function! s:isabsolute(dir) abort |
|
120 return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)') |
|
121 endfunction |
|
122 |
|
123 function! s:git_dir(dir) abort |
|
124 let gitdir = s:trim(a:dir) . '/.git' |
|
125 if isdirectory(gitdir) |
|
126 return gitdir |
|
127 endif |
|
128 if !filereadable(gitdir) |
|
129 return '' |
|
130 endif |
|
131 let gitdir = matchstr(get(readfile(gitdir), 0, ''), '^gitdir: \zs.*') |
|
132 if len(gitdir) && !s:isabsolute(gitdir) |
|
133 let gitdir = a:dir . '/' . gitdir |
|
134 endif |
|
135 return isdirectory(gitdir) ? gitdir : '' |
|
136 endfunction |
|
137 |
|
138 function! s:git_origin_url(dir) abort |
|
139 let gitdir = s:git_dir(a:dir) |
|
140 let config = gitdir . '/config' |
|
141 if empty(gitdir) || !filereadable(config) |
|
142 return '' |
|
143 endif |
|
144 return matchstr(join(readfile(config)), '\[remote "origin"\].\{-}url\s*=\s*\zs\S*\ze') |
|
145 endfunction |
|
146 |
|
147 function! s:git_revision(dir) abort |
|
148 let gitdir = s:git_dir(a:dir) |
|
149 let head = gitdir . '/HEAD' |
|
150 if empty(gitdir) || !filereadable(head) |
|
151 return '' |
|
152 endif |
|
153 |
|
154 let line = get(readfile(head), 0, '') |
|
155 let ref = matchstr(line, '^ref: \zs.*') |
|
156 if empty(ref) |
|
157 return line |
|
158 endif |
|
159 |
|
160 if filereadable(gitdir . '/' . ref) |
|
161 return get(readfile(gitdir . '/' . ref), 0, '') |
|
162 endif |
|
163 |
|
164 if filereadable(gitdir . '/packed-refs') |
|
165 for line in readfile(gitdir . '/packed-refs') |
|
166 if line =~# ' ' . ref |
|
167 return matchstr(line, '^[0-9a-f]*') |
|
168 endif |
|
169 endfor |
|
170 endif |
|
171 |
|
172 return '' |
|
173 endfunction |
|
174 |
|
175 function! s:git_local_branch(dir) abort |
|
176 let gitdir = s:git_dir(a:dir) |
|
177 let head = gitdir . '/HEAD' |
|
178 if empty(gitdir) || !filereadable(head) |
|
179 return '' |
|
180 endif |
|
181 let branch = matchstr(get(readfile(head), 0, ''), '^ref: refs/heads/\zs.*') |
|
182 return len(branch) ? branch : 'HEAD' |
|
183 endfunction |
|
184 |
|
185 function! s:git_origin_branch(spec) |
|
186 if len(a:spec.branch) |
|
187 return a:spec.branch |
|
188 endif |
|
189 |
|
190 " The file may not be present if this is a local repository |
|
191 let gitdir = s:git_dir(a:spec.dir) |
|
192 let origin_head = gitdir.'/refs/remotes/origin/HEAD' |
|
193 if len(gitdir) && filereadable(origin_head) |
|
194 return matchstr(get(readfile(origin_head), 0, ''), |
|
195 \ '^ref: refs/remotes/origin/\zs.*') |
|
196 endif |
|
197 |
|
198 " The command may not return the name of a branch in detached HEAD state |
|
199 let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir)) |
|
200 return v:shell_error ? '' : result[-1] |
|
201 endfunction |
118 |
202 |
119 if s:is_win |
203 if s:is_win |
120 function! s:plug_call(fn, ...) |
204 function! s:plug_call(fn, ...) |
121 let shellslash = &shellslash |
205 let shellslash = &shellslash |
122 try |
206 try |
644 if empty(a:arg) |
728 if empty(a:arg) |
645 throw printf(opt_errfmt, 'tag', 'string') |
729 throw printf(opt_errfmt, 'tag', 'string') |
646 endif |
730 endif |
647 let opts.tag = a:arg |
731 let opts.tag = a:arg |
648 elseif type == s:TYPE.dict |
732 elseif type == s:TYPE.dict |
649 call extend(opts, a:arg) |
|
650 for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] |
733 for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] |
651 if has_key(opts, opt) |
734 if has_key(a:arg, opt) |
652 \ && (type(opts[opt]) != s:TYPE.string || empty(opts[opt])) |
735 \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) |
653 throw printf(opt_errfmt, opt, 'string') |
736 throw printf(opt_errfmt, opt, 'string') |
654 endif |
737 endif |
655 endfor |
738 endfor |
656 for opt in ['on', 'for'] |
739 for opt in ['on', 'for'] |
657 if has_key(opts, opt) |
740 if has_key(a:arg, opt) |
658 \ && type(opts[opt]) != s:TYPE.list |
741 \ && type(a:arg[opt]) != s:TYPE.list |
659 \ && (type(opts[opt]) != s:TYPE.string || empty(opts[opt])) |
742 \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) |
660 throw printf(opt_errfmt, opt, 'string or list') |
743 throw printf(opt_errfmt, opt, 'string or list') |
661 endif |
744 endif |
662 endfor |
745 endfor |
663 if has_key(opts, 'do') |
746 if has_key(a:arg, 'do') |
664 \ && type(opts.do) != s:TYPE.funcref |
747 \ && type(a:arg.do) != s:TYPE.funcref |
665 \ && (type(opts.do) != s:TYPE.string || empty(opts.do)) |
748 \ && (type(a:arg.do) != s:TYPE.string || empty(a:arg.do)) |
666 throw printf(opt_errfmt, 'do', 'string or funcref') |
749 throw printf(opt_errfmt, 'do', 'string or funcref') |
667 endif |
750 endif |
|
751 call extend(opts, a:arg) |
668 if has_key(opts, 'dir') |
752 if has_key(opts, 'dir') |
669 let opts.dir = s:dirpath(s:plug_expand(opts.dir)) |
753 let opts.dir = s:dirpath(s:plug_expand(opts.dir)) |
670 endif |
754 endif |
671 else |
755 else |
672 throw 'Invalid argument type (expected: string or dictionary)' |
756 throw 'Invalid argument type (expected: string or dictionary)' |
2209 endfunction |
2293 endfunction |
2210 |
2294 |
2211 function! s:git_validate(spec, check_branch) |
2295 function! s:git_validate(spec, check_branch) |
2212 let err = '' |
2296 let err = '' |
2213 if isdirectory(a:spec.dir) |
2297 if isdirectory(a:spec.dir) |
2214 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)) |
2298 let result = [s:git_local_branch(a:spec.dir), s:git_origin_url(a:spec.dir)] |
2215 let remote = result[-1] |
2299 let remote = result[-1] |
2216 if v:shell_error |
2300 if empty(remote) |
2217 let err = join([remote, 'PlugClean required.'], "\n") |
2301 let err = join([remote, 'PlugClean required.'], "\n") |
2218 elseif !s:compare_git_uri(remote, a:spec.uri) |
2302 elseif !s:compare_git_uri(remote, a:spec.uri) |
2219 let err = join(['Invalid URI: '.remote, |
2303 let err = join(['Invalid URI: '.remote, |
2220 \ 'Expected: '.a:spec.uri, |
2304 \ 'Expected: '.a:spec.uri, |
2221 \ 'PlugClean required.'], "\n") |
2305 \ 'PlugClean required.'], "\n") |
2222 elseif a:check_branch && has_key(a:spec, 'commit') |
2306 elseif a:check_branch && has_key(a:spec, 'commit') |
2223 let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir)) |
2307 let sha = s:git_revision(a:spec.dir) |
2224 let sha = result[-1] |
2308 if empty(sha) |
2225 if v:shell_error |
|
2226 let err = join(add(result, 'PlugClean required.'), "\n") |
2309 let err = join(add(result, 'PlugClean required.'), "\n") |
2227 elseif !s:hash_match(sha, a:spec.commit) |
2310 elseif !s:hash_match(sha, a:spec.commit) |
2228 let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', |
2311 let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', |
2229 \ a:spec.commit[:6], sha[:6]), |
2312 \ a:spec.commit[:6], sha[:6]), |
2230 \ 'PlugUpdate required.'], "\n") |
2313 \ 'PlugUpdate required.'], "\n") |
2231 endif |
2314 endif |
2232 elseif a:check_branch |
2315 elseif a:check_branch |
2233 let branch = result[0] |
2316 let current_branch = result[0] |
2234 " Check tag |
2317 " Check tag |
|
2318 let origin_branch = s:git_origin_branch(a:spec) |
2235 if has_key(a:spec, 'tag') |
2319 if has_key(a:spec, 'tag') |
2236 let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) |
2320 let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) |
2237 if a:spec.tag !=# tag && a:spec.tag !~ '\*' |
2321 if a:spec.tag !=# tag && a:spec.tag !~ '\*' |
2238 let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', |
2322 let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', |
2239 \ (empty(tag) ? 'N/A' : tag), a:spec.tag) |
2323 \ (empty(tag) ? 'N/A' : tag), a:spec.tag) |
2240 endif |
2324 endif |
2241 " Check branch |
2325 " Check branch |
2242 elseif a:spec.branch !=# branch |
2326 elseif origin_branch !=# current_branch |
2243 let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', |
2327 let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', |
2244 \ branch, a:spec.branch) |
2328 \ current_branch, origin_branch) |
2245 endif |
2329 endif |
2246 if empty(err) |
2330 if empty(err) |
2247 let [ahead, behind] = split(s:lastline(s:system([ |
2331 let [ahead, behind] = split(s:lastline(s:system([ |
2248 \ 'git', 'rev-list', '--count', '--left-right', |
2332 \ 'git', 'rev-list', '--count', '--left-right', |
2249 \ printf('HEAD...origin/%s', a:spec.branch) |
2333 \ printf('HEAD...origin/%s', origin_branch) |
2250 \ ], a:spec.dir)), '\t') |
2334 \ ], a:spec.dir)), '\t') |
2251 if !v:shell_error && ahead |
2335 if !v:shell_error && ahead |
2252 if behind |
2336 if behind |
2253 " Only mention PlugClean if diverged, otherwise it's likely to be |
2337 " Only mention PlugClean if diverged, otherwise it's likely to be |
2254 " pushable (and probably not that messed up). |
2338 " pushable (and probably not that messed up). |
2255 let err = printf( |
2339 let err = printf( |
2256 \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" |
2340 \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" |
2257 \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', a:spec.branch, ahead, behind) |
2341 \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', origin_branch, ahead, behind) |
2258 else |
2342 else |
2259 let err = printf("Ahead of origin/%s by %d commit(s).\n" |
2343 let err = printf("Ahead of origin/%s by %d commit(s).\n" |
2260 \ .'Cannot update until local changes are pushed.', |
2344 \ .'Cannot update until local changes are pushed.', |
2261 \ a:spec.branch, ahead) |
2345 \ origin_branch, ahead) |
2262 endif |
2346 endif |
2263 endif |
2347 endif |
2264 endif |
2348 endif |
2265 endif |
2349 endif |
2266 else |
2350 else |
2586 if empty(plugs) |
2670 if empty(plugs) |
2587 continue |
2671 continue |
2588 endif |
2672 endif |
2589 call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:') |
2673 call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:') |
2590 for [k, v] in plugs |
2674 for [k, v] in plugs |
2591 let range = origin ? '..origin/'.v.branch : 'HEAD@{1}..' |
2675 let branch = s:git_origin_branch(v) |
2592 let cmd = ['git', 'log', '--graph', '--color=never'] |
2676 if len(branch) |
2593 if s:git_version_requirement(2, 10, 0) |
2677 let range = origin ? '..origin/'.branch : 'HEAD@{1}..' |
2594 call add(cmd, '--no-show-signature') |
2678 let cmd = ['git', 'log', '--graph', '--color=never'] |
2595 endif |
2679 if s:git_version_requirement(2, 10, 0) |
2596 call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range]) |
2680 call add(cmd, '--no-show-signature') |
2597 if has_key(v, 'rtp') |
2681 endif |
2598 call extend(cmd, ['--', v.rtp]) |
2682 call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range]) |
2599 endif |
2683 if has_key(v, 'rtp') |
2600 let diff = s:system_chomp(cmd, v.dir) |
2684 call extend(cmd, ['--', v.rtp]) |
2601 if !empty(diff) |
2685 endif |
2602 let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' |
2686 let diff = s:system_chomp(cmd, v.dir) |
2603 call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) |
2687 if !empty(diff) |
2604 let cnts[origin] += 1 |
2688 let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' |
|
2689 call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) |
|
2690 let cnts[origin] += 1 |
|
2691 endif |
2605 endif |
2692 endif |
2606 let bar .= '=' |
2693 let bar .= '=' |
2607 call s:progress_bar(2, bar, len(total)) |
2694 call s:progress_bar(2, bar, len(total)) |
2608 normal! 2G |
2695 normal! 2G |
2609 redraw |
2696 redraw |