utils 0.0.55 → 0.0.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.55
1
+ 0.0.56
@@ -0,0 +1,257 @@
1
+ *fugitive.txt* A Git wrapper so awesome, it should be illegal
2
+
3
+ Author: Tim Pope <vimNOSPAM@tpope.org> *fugitive-author*
4
+ License: Same terms as Vim itself (see |license|)
5
+
6
+ This plugin is only available if 'compatible' is not set.
7
+
8
+ INTRODUCTION *fugitive*
9
+
10
+ Install in ~/.vim, or in ~\vimfiles if you're on Windows and feeling lucky.
11
+ Vim 7.2 is recommended as it ships with syntax highlighting for many Git file
12
+ types.
13
+
14
+ If you're in a hurry to get started, here are some things to try:
15
+
16
+ In any file in your repository, run |:Gedit| HEAD. Press <CR> to jump to the
17
+ current branch. Press <CR> again to jump to the top most commit. Keep using
18
+ <CR> to explore parent commits, trees, and blobs. Use C in a tree or blob to
19
+ get back to the commit.
20
+
21
+ Edit a file in the work tree and make some changes. Use |:Gdiff| to open up
22
+ the indexed version. Use |do| and |dp| on various hunks to bring the files in
23
+ sync, or use |:Gread| to pull in all changes. Write the indexed version to
24
+ stage the file.
25
+
26
+ Run |:Gstatus| to check your repository's status. Use "-" to stage and reset
27
+ files and "p" to add/reset --patch them. Invoke |:Gcommit| to commit your
28
+ changes.
29
+
30
+ Run |:Gblame| in a work tree file to see a blame in a vertical split. Press
31
+ <CR> on any line to reopen and reblame that file as it stood in that commit.
32
+ Press o or O on any line to inspect that commit in a split or a tab.
33
+
34
+ Run |:Ggrep| to search the work tree or history. Run |:Gmove| to rename a
35
+ file. Run |:Gremove| to delete a file.
36
+
37
+ COMMANDS *fugitive-commands*
38
+
39
+ These commands are local to the buffers in which they work (generally, buffers
40
+ that are part of Git repositories).
41
+
42
+ *fugitive-:Git*
43
+ :Git [args] Run an arbitrary git command. Similar to :!git [args]
44
+ but chdir to the repository tree first.
45
+
46
+ *fugitive-:Gcd*
47
+ :Gcd [directory] |:cd| relative to the repository.
48
+
49
+ *fugitive-:Glcd*
50
+ :Glcd [directory] |:lcd| relative to the repository.
51
+
52
+ *fugitive-:Gstatus*
53
+ :Gstatus Bring up the output of git-status in the preview
54
+ window. In addition to standard motions, you can
55
+ use <C-N> and <C-P> to jump from filename to
56
+ filename. Press C to invoke |:Gcommit|. Press D to
57
+ |:Gdiff| the file on the cursor line, or ds to
58
+ |:Gsdiff|. Press - to stage or unstage the file on
59
+ the cursor line. Press p to do so on a per hunk basis
60
+ (--patch). All of D, -, and p have a different,
61
+ sensible (and hopefully intuitive) behavior when
62
+ invoked on a heading rather than a file name.
63
+
64
+ *fugitive-:Gcommit*
65
+ :Gcommit [args] A wrapper around git-commit. If there is nothing
66
+ to commit, |:Gstatus| is called instead. Unless the
67
+ arguments given would skip the invocation of an editor
68
+ (e.g., -m), a split window will be used to obtain a
69
+ commit message. Write and close that window (:wq or
70
+ |:Gwrite|) to finish the commit. Unlike when running
71
+ the actual git-commit command, it is possible (but
72
+ unadvisable) to muck with the index with commands like
73
+ git-add and git-reset while a commit message is
74
+ pending.
75
+
76
+ *fugitive-:Ggrep*
77
+ :Ggrep [args] |:grep| with git-grep as 'grepprg'.
78
+
79
+ *fugitive-:Glog*
80
+ :Glog [args] Load all previous revisions of the current file into
81
+ the quickfix list. Additional git-log arguments can
82
+ be given (for example, --reverse). If "--" appears as
83
+ an argument, no file specific filtering is done, and
84
+ commits are loaded into the quickfix list.
85
+
86
+ *fugitive-:Gedit* *fugitive-:Ge*
87
+ :Gedit [revision] |:edit| a |fugitive-revision|.
88
+
89
+ *fugitive-:Gsplit*
90
+ :Gsplit [revision] |:split| a |fugitive-revision|.
91
+
92
+ *fugitive-:Gvsplit*
93
+ :Gvsplit [revision] |:vsplit| a |fugitive-revision|.
94
+
95
+ *fugitive-:Gtabedit*
96
+ :Gtabedit [revision] |:tabedit| a |fugitive-revision|
97
+
98
+ *fugitive-:Gpedit*
99
+ :Gpedit [revision] |:pedit| a |fugitive-revision|
100
+
101
+ *fugitive-:Gread*
102
+ :Gread [revision] Empty the buffer and |:read| a |fugitive-revision|.
103
+ When the argument is omitted, this is similar to
104
+ git-checkout on a work tree file or git-add on a stage
105
+ file, but without writing anything to disk.
106
+
107
+ :{range}Gread [revision]
108
+ |:read| in a |fugitive-revision| after {range}.
109
+
110
+ *fugitive-:Gwrite*
111
+ :Gwrite Write to the current file's path and stage the results.
112
+ When run in a work tree file, it is effectively git
113
+ add. Elsewhere, it is effectively git-checkout. A
114
+ great deal of effort is expended to behave sensibly
115
+ when the work tree or index version of the file is
116
+ open in another buffer.
117
+
118
+ :Gwrite {path} You can give |:Gwrite| an explicit path of where in
119
+ the work tree to write. You can also give a path like
120
+ :0:foo.txt or even :0 to write to just that stage in
121
+ the index.
122
+
123
+ *fugitive-:Gwq*
124
+ :Gwq [path] Like |:Gwrite| followed by |:quit| if the write
125
+ succeeded.
126
+
127
+ :Gwq! [path] Like |:Gwrite|! followed by |:quit|! if the write
128
+ succeeded.
129
+
130
+ *fugitive-:Gdiff*
131
+ :Gdiff [revision] Perform a |vimdiff| against the current file in the
132
+ given revision. With no argument, the version in the
133
+ index is used (which means a three-way diff during a
134
+ merge conflict, making it a git-mergetool
135
+ alternative). The newer of the two files is placed
136
+ to the right. Use |do| and |dp| and write to the
137
+ index file to simulate "git add --patch".
138
+
139
+ *fugitive-:Gsdiff*
140
+ :Gsdiff [revision] Like |:Gdiff|, but split horizontally.
141
+
142
+ *fugitive-:Gvdiff*
143
+ :Gvdiff [revision] Identical to |:Gdiff|. For symmetry with |:Gsdiff|.
144
+
145
+ *fugitive-:Gmove*
146
+ :Gmove {destination} Wrapper around git-mv that renames the buffer
147
+ afterward. The destination is relative to the current
148
+ directory except when started with a /, in which case
149
+ it is relative to the work tree. Add a ! to pass -f.
150
+
151
+ *fugitive-:Gremove*
152
+ :Gremove Wrapper around git-rm that deletes the buffer
153
+ afterward. When invoked in an index file, --cached is
154
+ passed. Add a ! to pass -f and forcefully discard the
155
+ buffer.
156
+
157
+ *fugitive-:Gblame*
158
+ :Gblame [flags] Run git-blame on the file and open the results in a
159
+ scroll bound vertical split. Press enter on a line to
160
+ reblame the file as it was in that commit. You can
161
+ give any of ltwfsMC as flags and they will be passed
162
+ along to git-blame.
163
+
164
+ :[range]Gblame [flags] Run git-blame on the given range.
165
+
166
+ *fugitive-:Gbrowse*
167
+ :[range]Gbrowse If the remote for the current branch is on GitHub,
168
+ open the current file, blob, tree, commit, or tag
169
+ (with git-web--browse) on GitHub. Otherwise, open the
170
+ current file, blob, tree, commit, or tag in
171
+ git-instaweb (if you have issues, verify you can run
172
+ "git instaweb" from a terminal). If a range is given,
173
+ it is appropriately appended to the URL as an anchor.
174
+
175
+ :[range]Gbrowse! Like :Gbrowse, but put the URL on the clipboard rather
176
+ than opening it.
177
+
178
+ :[range]Gbrowse {revision}
179
+ Like :Gbrowse, but for a given |fugitive-revision|. A
180
+ useful value here is -, which ties the URL to the
181
+ latest commit rather than a volatile branch.
182
+
183
+ :[range]Gbrowse [...]@{remote}
184
+ Force using the given remote rather than the remote
185
+ for the current branch. The remote is used to
186
+ determine which GitHub repository to link to.
187
+
188
+ MAPPINGS *fugitive-mappings*
189
+
190
+ These maps are available in Git objects.
191
+
192
+ *fugitive-<CR>*
193
+ <CR> Jump to the revision under the cursor.
194
+
195
+ *fugitive-o*
196
+ o Jump to the revision under the cursor in a new split.
197
+
198
+ *fugitive-O*
199
+ O Jump to the revision under the cursor in a new tab.
200
+
201
+ *fugitive-~*
202
+ ~ Go to the current file in the [count]th first
203
+ ancestor.
204
+
205
+ *fugitive-P*
206
+ P Go to the current file in the [count]th parent.
207
+
208
+ *fugitive-C*
209
+ C Go to the commit containing the current file.
210
+
211
+ *fugitive-a*
212
+ a Show the current tag, commit, or tree in an alternate
213
+ format.
214
+
215
+ SPECIFYING REVISIONS *fugitive-revision*
216
+
217
+ Fugitive revisions are similar to Git revisions as defined in the "SPECIFYING
218
+ REVISIONS" section in the git-rev-parse man page. For commands that accept an
219
+ optional revision, the default is the file in the index for work tree files
220
+ and the work tree file for everything else. Example revisions follow.
221
+
222
+ Revision Meaning ~
223
+ HEAD .git/HEAD
224
+ master .git/refs/heads/master
225
+ HEAD^{} The commit referenced by HEAD
226
+ HEAD^ The parent of the commit referenced by HEAD
227
+ HEAD: The tree referenced by HEAD
228
+ /HEAD The file named HEAD in the work tree
229
+ Makefile The file named Makefile in the work tree
230
+ HEAD^:Makefile The file named Makefile in the parent of HEAD
231
+ :Makefile The file named Makefile in the index (writable)
232
+ - The current file in HEAD
233
+ ^ The current file in the previous commit
234
+ ~3 The current file 3 commits ago
235
+ : .git/index (Same as |:Gstatus|)
236
+ :0 The current file in the index
237
+ :1 The current file's common ancestor during a conflict
238
+ :2 The current file in the target branch during a conflict
239
+ :3 The current file in the merged branch during a conflict
240
+ :/foo The most recent commit with "foo" in the message
241
+
242
+ STATUSLINE *fugitive-statusline*
243
+
244
+ *fugitive#statusline()*
245
+ Add %{fugitive#statusline()} to your statusline to get an indicator including
246
+ the current branch and the currently edited file's commit. If you don't have
247
+ a statusline, this one matches the default when 'ruler' is set:
248
+ >
249
+ set statusline=%<%f\ %h%m%r%{fugitive#statusline()}%=%-14.(%l,%c%V%)\ %P
250
+ <
251
+ ABOUT *fugitive-about*
252
+
253
+ Grab the latest version or report a bug on GitHub:
254
+
255
+ http://github.com/tpope/vim-fugitive
256
+
257
+ vim:tw=78:et:ft=help:norl:
@@ -1,6 +1,6 @@
1
1
  " fugitive.vim - A Git wrapper so awesome, it should be illegal
2
2
  " Maintainer: Tim Pope <vimNOSPAM@tpope.org>
3
- " Version: 1.1
3
+ " Version: 1.2
4
4
  " GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim
5
5
 
6
6
  if exists('g:loaded_fugitive') || &cp
@@ -108,7 +108,7 @@ function! s:ExtractGitDir(path) abort
108
108
  let ofn = ""
109
109
  let nfn = fn
110
110
  while fn != ofn
111
- if isdirectory(fn . '/.git')
111
+ if filereadable(fn . '/.git/HEAD')
112
112
  return s:sub(simplify(fnamemodify(fn . '/.git',':p')),'\W$','')
113
113
  elseif fn =~ '\.git$' && filereadable(fn . '/HEAD')
114
114
  return s:sub(simplify(fnamemodify(fn,':p')),'\W$','')
@@ -132,19 +132,25 @@ function! s:Detect(path)
132
132
  if exists('b:git_dir')
133
133
  silent doautocmd User Fugitive
134
134
  cnoremap <expr> <buffer> <C-R><C-G> fugitive#buffer().rev()
135
+ let buffer = fugitive#buffer()
135
136
  if expand('%:p') =~# '//'
136
- let buffer = fugitive#buffer()
137
137
  call buffer.setvar('&path',s:sub(buffer.getvar('&path'),'^\.%(,|$)',''))
138
138
  endif
139
+ if b:git_dir !~# ',' && stridx(buffer.getvar('&tags'),b:git_dir.'/tags') == -1
140
+ if &filetype != ''
141
+ call buffer.setvar('&tags',buffer.getvar('&tags').','.b:git_dir.'/'.&filetype.'.tags')
142
+ endif
143
+ call buffer.setvar('&tags',buffer.getvar('&tags').','.b:git_dir.'/tags')
144
+ endif
139
145
  endif
140
146
  endfunction
141
147
 
142
148
  augroup fugitive
143
149
  autocmd!
144
150
  autocmd BufNewFile,BufReadPost * call s:Detect(expand('<amatch>:p'))
145
- autocmd FileType netrw call s:Detect(expand('<amatch>:p'))
151
+ autocmd FileType netrw call s:Detect(expand('<afile>:p'))
146
152
  autocmd VimEnter * if expand('<amatch>')==''|call s:Detect(getcwd())|endif
147
- autocmd BufWinLeave * execute getbufvar(+expand('<abuf>'), 'fugitive_restore')
153
+ autocmd BufWinLeave * execute getwinvar(+winnr(), 'fugitive_restore')
148
154
  augroup END
149
155
 
150
156
  " }}}1
@@ -248,8 +254,8 @@ endfunction
248
254
 
249
255
  function! s:repo_rev_parse(rev) dict abort
250
256
  let hash = self.git_chomp('rev-parse','--verify',a:rev)
251
- if hash =~ '^\x\{40\}$'
252
- return hash
257
+ if hash =~ '\<\x\{40\}$'
258
+ return matchstr(hash,'\<\x\{40\}$')
253
259
  endif
254
260
  call s:throw('rev-parse '.a:rev.': '.hash)
255
261
  endfunction
@@ -351,7 +357,7 @@ endfunction
351
357
  function! s:buffer_type(...) dict abort
352
358
  if self.getvar('fugitive_type') != ''
353
359
  let type = self.getvar('fugitive_type')
354
- elseif fnamemodify(self.name(),':p') =~# '.\git/refs/\|\.git/\w*HEAD$'
360
+ elseif fnamemodify(self.spec(),':p') =~# '.\git/refs/\|\.git/\w*HEAD$'
355
361
  let type = 'head'
356
362
  elseif self.getline(1) =~ '^tree \x\{40\}$' && self.getline(2) == ''
357
363
  let type = 'tree'
@@ -359,11 +365,11 @@ function! s:buffer_type(...) dict abort
359
365
  let type = 'tree'
360
366
  elseif self.getline(1) =~ '^\d\{6\} \x\{40\}\> \d\t'
361
367
  let type = 'index'
362
- elseif isdirectory(self.name())
368
+ elseif isdirectory(self.spec())
363
369
  let type = 'directory'
364
- elseif self.name() == ''
370
+ elseif self.spec() == ''
365
371
  let type = 'null'
366
- elseif filereadable(self.name())
372
+ elseif filereadable(self.spec())
367
373
  let type = 'file'
368
374
  else
369
375
  let type = ''
@@ -375,42 +381,61 @@ function! s:buffer_type(...) dict abort
375
381
  endif
376
382
  endfunction
377
383
 
384
+ if has('win32')
385
+
386
+ function! s:buffer_spec() dict abort
387
+ let bufname = bufname(self['#'])
388
+ let retval = ''
389
+ for i in split(bufname,'[^:]\zs\\')
390
+ let retval = fnamemodify((retval==''?'':retval.'\').i,':.')
391
+ endfor
392
+ return s:shellslash(fnamemodify(retval,':p'))
393
+ endfunction
394
+
395
+ else
396
+
397
+ function! s:buffer_spec() dict abort
398
+ let bufname = bufname(self['#'])
399
+ return s:shellslash(bufname == '' ? '' : fnamemodify(bufname,':p'))
400
+ endfunction
401
+
402
+ endif
403
+
378
404
  function! s:buffer_name() dict abort
379
- let bufname = bufname(self['#'])
380
- return s:shellslash(bufname == '' ? '' : fnamemodify(bufname,':p'))
405
+ return self.spec()
381
406
  endfunction
382
407
 
383
408
  function! s:buffer_commit() dict abort
384
- return matchstr(self.name(),'^fugitive://.\{-\}//\zs\w*')
409
+ return matchstr(self.spec(),'^fugitive://.\{-\}//\zs\w*')
385
410
  endfunction
386
411
 
387
412
  function! s:buffer_path(...) dict abort
388
- let rev = matchstr(self.name(),'^fugitive://.\{-\}//\zs.*')
413
+ let rev = matchstr(self.spec(),'^fugitive://.\{-\}//\zs.*')
389
414
  if rev != ''
390
415
  let rev = s:sub(rev,'\w*','')
391
416
  else
392
- let rev = self.name()[strlen(self.repo().tree()) : -1]
417
+ let rev = self.spec()[strlen(self.repo().tree()) : -1]
393
418
  endif
394
- return s:sub(rev,'^/',a:0 ? a:1 : '')
419
+ return s:sub(s:sub(rev,'.\zs/$',''),'^/',a:0 ? a:1 : '')
395
420
  endfunction
396
421
 
397
422
  function! s:buffer_rev() dict abort
398
- let rev = matchstr(self.name(),'^fugitive://.\{-\}//\zs.*')
423
+ let rev = matchstr(self.spec(),'^fugitive://.\{-\}//\zs.*')
399
424
  if rev =~ '^\x/'
400
425
  return ':'.rev[0].':'.rev[2:-1]
401
426
  elseif rev =~ '.'
402
427
  return s:sub(rev,'/',':')
403
- elseif self.name() =~ '\.git/index$'
428
+ elseif self.spec() =~ '\.git/index$'
404
429
  return ':'
405
- elseif self.name() =~ '\.git/refs/\|\.git/.*HEAD$'
406
- return self.name()[strlen(self.repo().dir())+1 : -1]
430
+ elseif self.spec() =~ '\.git/refs/\|\.git/.*HEAD$'
431
+ return self.spec()[strlen(self.repo().dir())+1 : -1]
407
432
  else
408
433
  return self.path()
409
434
  endif
410
435
  endfunction
411
436
 
412
437
  function! s:buffer_sha1() dict abort
413
- if self.name() =~ '^fugitive://' || self.name() =~ '\.git/refs/\|\.git/.*HEAD$'
438
+ if self.spec() =~ '^fugitive://' || self.spec() =~ '\.git/refs/\|\.git/.*HEAD$'
414
439
  return self.repo().rev_parse(self.rev())
415
440
  else
416
441
  return ''
@@ -420,6 +445,8 @@ endfunction
420
445
  function! s:buffer_expand(rev) dict abort
421
446
  if a:rev =~# '^:[0-3]$'
422
447
  let file = a:rev.self.path(':')
448
+ elseif a:rev =~# '^[-:]/$'
449
+ let file = '/'.self.path()
423
450
  elseif a:rev =~# '^-'
424
451
  let file = 'HEAD^{}'.a:rev[1:-1].self.path(':')
425
452
  elseif a:rev =~# '^@{'
@@ -430,7 +457,7 @@ function! s:buffer_expand(rev) dict abort
430
457
  else
431
458
  let file = a:rev
432
459
  endif
433
- return s:sub(file,'\%$',self.path())
460
+ return s:sub(s:sub(file,'\%$',self.path()),'\.\@<=/$','')
434
461
  endfunction
435
462
 
436
463
  function! s:buffer_containing_commit() dict abort
@@ -443,7 +470,7 @@ function! s:buffer_containing_commit() dict abort
443
470
  endif
444
471
  endfunction
445
472
 
446
- call s:add_methods('buffer',['getvar','setvar','getline','repo','type','name','commit','path','rev','sha1','expand','containing_commit'])
473
+ call s:add_methods('buffer',['getvar','setvar','getline','repo','type','spec','name','commit','path','rev','sha1','expand','containing_commit'])
447
474
 
448
475
  " }}}1
449
476
  " Git {{{1
@@ -538,10 +565,11 @@ function! fugitive#reload_status() abort
538
565
  endfor
539
566
  endfunction
540
567
 
541
- function! s:StageDiff() abort
568
+ function! s:StageDiff(...) abort
569
+ let cmd = a:0 ? a:1 : 'Gdiff'
542
570
  let section = getline(search('^# .*:$','bnW'))
543
571
  let line = getline('.')
544
- let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*')
572
+ let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (new commits)\)\=$')
545
573
  if filename ==# '' && section == '# Changes to be committed:'
546
574
  return 'Git diff --cached'
547
575
  elseif filename ==# ''
@@ -549,13 +577,13 @@ function! s:StageDiff() abort
549
577
  elseif line =~# '^#\trenamed:' && filename =~ ' -> '
550
578
  let [old, new] = split(filename,' -> ')
551
579
  execute 'Gedit '.s:fnameescape(':0:'.new)
552
- return 'Gdiff HEAD:'.s:fnameescape(old)
580
+ return cmd.' HEAD:'.s:fnameescape(old)
553
581
  elseif section == '# Changes to be committed:'
554
582
  execute 'Gedit '.s:fnameescape(':0:'.filename)
555
- return 'Gdiff -'
583
+ return cmd.' -'
556
584
  else
557
585
  execute 'Gedit '.s:fnameescape('/'.filename)
558
- return 'Gdiff'
586
+ return cmd
559
587
  endif
560
588
  endfunction
561
589
 
@@ -564,10 +592,34 @@ function! s:StageToggle(lnum1,lnum2) abort
564
592
  let output = ''
565
593
  for lnum in range(a:lnum1,a:lnum2)
566
594
  let line = getline(lnum)
567
- if getline('.') == '# Changes to be committed:'
568
- return 'Gcommit'
595
+ let repo = s:repo()
596
+ if line ==# '# Changes to be committed:'
597
+ call repo.git_chomp_in_tree('reset','-q')
598
+ silent! edit!
599
+ 1
600
+ if !search('^# Untracked files:$','W')
601
+ call search('^# Change','W')
602
+ endif
603
+ return ''
604
+ elseif line =~# '^# Change\%(d but not updated\|s not staged for commit\):$'
605
+ call repo.git_chomp_in_tree('add','-u')
606
+ silent! edit!
607
+ 1
608
+ if !search('^# Untracked files:$','W')
609
+ call search('^# Change','W')
610
+ endif
611
+ return ''
612
+ elseif line ==# '# Untracked files:'
613
+ " Work around Vim parser idiosyncrasy
614
+ call repo.git_chomp_in_tree('add','-N','.')
615
+ silent! edit!
616
+ 1
617
+ if !search('^# Change\%(d but not updated\|s not staged for commit\):$','W')
618
+ call search('^# Change','W')
619
+ endif
620
+ return ''
569
621
  endif
570
- let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*')
622
+ let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (\a\+ [[:alpha:], ]\+)\)\=$')
571
623
  if filename ==# ''
572
624
  continue
573
625
  endif
@@ -586,7 +638,7 @@ function! s:StageToggle(lnum1,lnum2) abort
586
638
  else
587
639
  let cmd = ['add','--',filename]
588
640
  endif
589
- let output .= call(s:repo().git_chomp_in_tree,cmd,s:repo())."\n"
641
+ let output .= call(repo.git_chomp_in_tree,cmd,s:repo())."\n"
590
642
  endfor
591
643
  if exists('first_filename')
592
644
  let jump = first_filename
@@ -597,7 +649,7 @@ function! s:StageToggle(lnum1,lnum2) abort
597
649
  silent! edit!
598
650
  1
599
651
  redraw
600
- call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.jump.'\$','W')
652
+ call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.jump.'\%( (new commits)\)\=\$','W')
601
653
  endif
602
654
  echo s:sub(s:gsub(output,'\n+','\n'),'\n$','')
603
655
  catch /^fugitive:/
@@ -612,12 +664,12 @@ function! s:StagePatch(lnum1,lnum2) abort
612
664
 
613
665
  for lnum in range(a:lnum1,a:lnum2)
614
666
  let line = getline(lnum)
615
- if line == '# Changes to be committed:'
667
+ if line ==# '# Changes to be committed:'
616
668
  return 'Git reset --patch'
617
- elseif line == '# Changed but not updated:'
669
+ elseif line =~# '^# Change\%(d but not updated\|s not staged for commit\):$'
618
670
  return 'Git add --patch'
619
671
  endif
620
- let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*')
672
+ let filename = matchstr(line,'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( (new commits)\)\=$')
621
673
  if filename ==# ''
622
674
  continue
623
675
  endif
@@ -645,7 +697,7 @@ function! s:StagePatch(lnum1,lnum2) abort
645
697
  silent! edit!
646
698
  1
647
699
  redraw
648
- call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.first_filename.'\$','W')
700
+ call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.first_filename.'\%( (new commits)\)\=\$','W')
649
701
  endif
650
702
  catch /^fugitive:/
651
703
  return 'echoerr v:errmsg'
@@ -667,16 +719,16 @@ function! s:Commit(args) abort
667
719
  let errorfile = tempname()
668
720
  try
669
721
  execute cd.'`=s:repo().tree()`'
670
- let command = ''
671
722
  if &shell =~# 'cmd'
723
+ let command = ''
672
724
  let old_editor = $GIT_EDITOR
673
725
  let $GIT_EDITOR = 'false'
674
- elseif &shell !~# 'csh'
675
- let command = 'GIT_EDITOR=false '
726
+ else
727
+ let command = 'env GIT_EDITOR=false '
676
728
  endif
677
729
  let command .= s:repo().git_command('commit').' '.a:args
678
730
  if &shell =~# 'csh'
679
- silent execute '!setenv GIT_EDITOR false; ('.command.' > '.outfile.') >& '.errorfile
731
+ silent execute '!('.command.' > '.outfile.') >& '.errorfile
680
732
  elseif a:args =~# '\%(^\| \)--interactive\>'
681
733
  execute '!'.command.' 2> '.errorfile
682
734
  else
@@ -690,8 +742,9 @@ function! s:Commit(args) abort
690
742
  endif
691
743
  return ''
692
744
  else
693
- let error = get(readfile(errorfile,'',1),0,'!')
694
- if error =~# "'false'\\.$"
745
+ let errors = readfile(errorfile)
746
+ let error = get(errors,-2,get(errors,-1,'!'))
747
+ if error =~# '\<false''\=\.$'
695
748
  let args = a:args
696
749
  let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-[se]|--edit|--interactive)%($| )','')
697
750
  let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-F|--file|-m|--message)%(\s+|\=)%(''[^'']*''|"%(\\.|[^"])*"|\\.|\S)*','')
@@ -700,13 +753,14 @@ function! s:Commit(args) abort
700
753
  if args !~# '\%(^\| \)--cleanup\>'
701
754
  let args = '--cleanup=strip '.args
702
755
  endif
756
+ let old_nr = bufnr('')
703
757
  if bufname('%') == '' && line('$') == 1 && getline(1) == '' && !&mod
704
758
  edit `=msgfile`
705
759
  else
706
- split `=msgfile`
760
+ keepalt split `=msgfile`
707
761
  endif
708
762
  if old_type ==# 'index'
709
- bdelete #
763
+ execute 'bdelete '.old_nr
710
764
  endif
711
765
  let b:fugitive_commit_arguments = args
712
766
  setlocal bufhidden=delete filetype=gitcommit
@@ -741,7 +795,6 @@ endfunction
741
795
 
742
796
  function! s:FinishCommit()
743
797
  let args = getbufvar(+expand('<abuf>'),'fugitive_commit_arguments')
744
- let g:args = args
745
798
  if !empty(args)
746
799
  call setbufvar(+expand('<abuf>'),'fugitive_commit_arguments','')
747
800
  return s:Commit(args)
@@ -849,11 +902,8 @@ function! s:Edit(cmd,...) abort
849
902
  catch /^fugitive:/
850
903
  return 'echoerr v:errmsg'
851
904
  endtry
852
- if a:cmd =~# 'read!$' || a:cmd ==# 'read'
853
- if a:cmd =~# '!$'
854
- call s:warn(':Gread! is deprecated. Use :Gread')
855
- endif
856
- return 'silent %delete|read '.s:fnameescape(file).'|silent 1delete_|diffupdate|'.line('.')
905
+ if a:cmd ==# 'read'
906
+ return 'silent %delete_|read '.s:fnameescape(file).'|silent 1delete_|diffupdate|'.line('.')
857
907
  else
858
908
  if &previewwindow && getbufvar('','fugitive_type') ==# 'index'
859
909
  wincmd p
@@ -875,9 +925,11 @@ call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gtabedit
875
925
  call s:command("-bar -bang -nargs=? -count -complete=customlist,s:EditComplete Gread :execute s:Edit((!<count> && <line1> ? '' : <count>).'read<bang>',<f-args>)")
876
926
 
877
927
  " }}}1
878
- " Gwrite {{{1
928
+ " Gwrite, Gwq {{{1
879
929
 
880
930
  call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gwrite :execute s:Write(<bang>0,<f-args>)")
931
+ call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gw :execute s:Write(<bang>0,<f-args>)")
932
+ call s:command("-bar -bang -nargs=? -complete=customlist,s:EditComplete Gwq :execute s:Wq(<bang>0,<f-args>)")
881
933
 
882
934
  function! s:Write(force,...) abort
883
935
  if exists('b:fugitive_commit_arguments')
@@ -941,7 +993,11 @@ function! s:Write(force,...) abort
941
993
  execute 'write! '.s:fnameescape(s:repo().translate(path))
942
994
  endif
943
995
 
944
- let error = s:repo().git_chomp_in_tree('add', file)
996
+ if a:force
997
+ let error = s:repo().git_chomp_in_tree('add', '--force', file)
998
+ else
999
+ let error = s:repo().git_chomp_in_tree('add', file)
1000
+ endif
945
1001
  if v:shell_error
946
1002
  let v:errmsg = 'fugitive: '.error
947
1003
  return 'echoerr v:errmsg'
@@ -993,16 +1049,57 @@ function! s:Write(force,...) abort
993
1049
  return 'checktime'
994
1050
  endfunction
995
1051
 
1052
+ function! s:Wq(force,...) abort
1053
+ let bang = a:force ? '!' : ''
1054
+ if exists('b:fugitive_commit_arguments')
1055
+ return 'wq'.bang
1056
+ endif
1057
+ let result = call(s:function('s:Write'),[a:force]+a:000)
1058
+ if result =~# '^\%(write\|wq\|echoerr\)'
1059
+ return s:sub(result,'^write','wq')
1060
+ else
1061
+ return result.'|quit'.bang
1062
+ endif
1063
+ endfunction
1064
+
996
1065
  " }}}1
997
1066
  " Gdiff {{{1
998
1067
 
999
- call s:command("-bar -nargs=? -complete=customlist,s:EditComplete Gdiff :execute s:Diff(<f-args>)")
1068
+ call s:command("-bang -bar -nargs=? -complete=customlist,s:EditComplete Gdiff :execute s:Diff(<bang>0,<f-args>)")
1069
+ call s:command("-bar -nargs=? -complete=customlist,s:EditComplete Gvdiff :execute s:Diff(0,<f-args>)")
1070
+ call s:command("-bar -nargs=? -complete=customlist,s:EditComplete Gsdiff :execute s:Diff(1,<f-args>)")
1000
1071
 
1001
1072
  augroup fugitive_diff
1002
- autocmd BufWinLeave * if winnr('$') == 2 && &diff && getbufvar(+expand('<abuf>'), 'git_dir') !=# '' | diffoff! | endif
1003
- autocmd BufWinEnter * if winnr('$') == 1 && &diff && getbufvar(+expand('<abuf>'), 'git_dir') !=# '' | diffoff | endif
1073
+ autocmd!
1074
+ autocmd BufWinLeave * if s:diff_window_count() == 2 && &diff && getbufvar(+expand('<abuf>'), 'git_dir') !=# '' | call s:diff_off_all(getbufvar(+expand('<abuf>'), 'git_dir')) | endif
1075
+ autocmd BufWinEnter * if s:diff_window_count() == 1 && &diff && getbufvar(+expand('<abuf>'), 'git_dir') !=# '' | diffoff | endif
1004
1076
  augroup END
1005
1077
 
1078
+ function! s:diff_window_count()
1079
+ let c = 0
1080
+ for nr in range(1,winnr('$'))
1081
+ let c += getwinvar(nr,'&diff')
1082
+ endfor
1083
+ return c
1084
+ endfunction
1085
+
1086
+ function! s:diff_off_all(dir)
1087
+ for nr in range(1,winnr('$'))
1088
+ if getwinvar(nr,'&diff')
1089
+ if nr != winnr()
1090
+ execute nr.'wincmd w'
1091
+ let restorewinnr = 1
1092
+ endif
1093
+ if exists('b:git_dir') && b:git_dir ==# a:dir
1094
+ diffoff
1095
+ endif
1096
+ if exists('restorewinnr')
1097
+ wincmd p
1098
+ endif
1099
+ endif
1100
+ endfor
1101
+ endfunction
1102
+
1006
1103
  function! s:buffer_compare_age(commit) dict abort
1007
1104
  let scores = {':0': 1, ':1': 2, ':2': 3, ':': 4, ':3': 5}
1008
1105
  let my_score = get(scores,':'.self.commit(),0)
@@ -1025,18 +1122,22 @@ endfunction
1025
1122
 
1026
1123
  call s:add_methods('buffer',['compare_age'])
1027
1124
 
1028
- function! s:Diff(...) abort
1125
+ function! s:Diff(bang,...) abort
1126
+ let split = a:bang ? 'split' : 'vsplit'
1029
1127
  if exists(':DiffGitCached')
1030
1128
  return 'DiffGitCached'
1031
1129
  elseif (!a:0 || a:1 == ':') && s:buffer().commit() =~# '^[0-1]\=$' && s:repo().git_chomp_in_tree('ls-files', '--unmerged', '--', s:buffer().path()) !=# ''
1032
- leftabove vsplit `=fugitive#buffer().repo().translate(s:buffer().expand(':2'))`
1033
- diffthis
1034
- wincmd p
1035
- rightbelow vsplit `=fugitive#buffer().repo().translate(s:buffer().expand(':3'))`
1036
- diffthis
1037
- wincmd p
1038
- diffthis
1039
- return ''
1130
+ let nr = bufnr('')
1131
+ execute 'leftabove '.split.' `=fugitive#buffer().repo().translate(s:buffer().expand('':2''))`'
1132
+ execute 'nnoremap <buffer> <silent> dp :diffput '.nr.'<Bar>diffupdate<CR>'
1133
+ diffthis
1134
+ wincmd p
1135
+ execute 'rightbelow '.split.' `=fugitive#buffer().repo().translate(s:buffer().expand('':3''))`'
1136
+ execute 'nnoremap <buffer> <silent> dp :diffput '.nr.'<Bar>diffupdate<CR>'
1137
+ diffthis
1138
+ wincmd p
1139
+ diffthis
1140
+ return ''
1040
1141
  elseif a:0
1041
1142
  if a:1 ==# ''
1042
1143
  return ''
@@ -1044,7 +1145,7 @@ function! s:Diff(...) abort
1044
1145
  let file = s:buffer().path('/')
1045
1146
  elseif a:1 ==# ':'
1046
1147
  let file = s:buffer().path(':0:')
1047
- elseif a:1 =~# '^:/'
1148
+ elseif a:1 =~# '^:/.'
1048
1149
  try
1049
1150
  let file = s:repo().rev_parse(a:1).s:buffer().path(':')
1050
1151
  catch /^fugitive:/
@@ -1063,9 +1164,9 @@ function! s:Diff(...) abort
1063
1164
  let spec = s:repo().translate(file)
1064
1165
  let commit = matchstr(spec,'\C[^:/]//\zs\x\+')
1065
1166
  if s:buffer().compare_age(commit) < 0
1066
- rightbelow vsplit `=spec`
1167
+ execute 'rightbelow '.split.' `=spec`'
1067
1168
  else
1068
- leftabove vsplit `=spec`
1169
+ execute 'leftabove '.split.' `=spec`'
1069
1170
  endif
1070
1171
  diffthis
1071
1172
  wincmd p
@@ -1088,6 +1189,10 @@ function! s:Move(force,destination)
1088
1189
  let destination = destination[strlen(s:repo().tree('')):-1]
1089
1190
  endif
1090
1191
  endif
1192
+ if isdirectory(s:buffer().name())
1193
+ " Work around Vim parser idiosyncrasy
1194
+ let discarded = s:buffer().setvar('&swapfile',0)
1195
+ endif
1091
1196
  let message = call(s:repo().git_chomp_in_tree,['mv']+(a:force ? ['-f'] : [])+['--', s:buffer().path(), destination], s:repo())
1092
1197
  if v:shell_error
1093
1198
  let v:errmsg = 'fugitive: '.message
@@ -1099,7 +1204,11 @@ function! s:Move(force,destination)
1099
1204
  endif
1100
1205
  call fugitive#reload_status()
1101
1206
  if s:buffer().commit() == ''
1102
- return 'saveas! '.s:fnameescape(destination)
1207
+ if isdirectory(destination)
1208
+ return 'edit '.s:fnameescape(destination)
1209
+ else
1210
+ return 'saveas! '.s:fnameescape(destination)
1211
+ endif
1103
1212
  else
1104
1213
  return 'file '.s:fnameescape(s:repo().translate(':0:'.destination)
1105
1214
  endif
@@ -1184,17 +1293,25 @@ function! s:Blame(bang,line1,line2,count,args) abort
1184
1293
  else
1185
1294
  let error = tempname()
1186
1295
  let temp = error.'.fugitiveblame'
1187
- silent! exe '%write !'.basecmd.' > '.temp.' 2> '.error
1296
+ if &shell =~# 'csh'
1297
+ silent! execute '%write !('.basecmd.' > '.temp.') >& '.error
1298
+ else
1299
+ silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error
1300
+ endif
1301
+ if exists('l:dir')
1302
+ execute cd.'`=dir`'
1303
+ unlet dir
1304
+ endif
1188
1305
  if v:shell_error
1189
1306
  call s:throw(join(readfile(error),"\n"))
1190
1307
  endif
1191
1308
  let bufnr = bufnr('')
1192
- let restore = 'call setbufvar('.bufnr.',"&scrollbind",0)'
1309
+ let restore = 'call setwinvar(bufwinnr('.bufnr.'),"&scrollbind",0)'
1193
1310
  if &l:wrap
1194
- let restore .= '|call setbufvar('.bufnr.',"&wrap",1)'
1311
+ let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&wrap",1)'
1195
1312
  endif
1196
1313
  if &l:foldenable
1197
- let restore .= '|call setbufvar('.bufnr.',"&foldenable",1)'
1314
+ let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&foldenable",1)'
1198
1315
  endif
1199
1316
  let winnr = winnr()
1200
1317
  windo set noscrollbind
@@ -1206,7 +1323,7 @@ function! s:Blame(bang,line1,line2,count,args) abort
1206
1323
  let b:git_dir = git_dir
1207
1324
  let b:fugitive_type = 'blame'
1208
1325
  let b:fugitive_blamed_bufnr = bufnr
1209
- let b:fugitive_restore = restore
1326
+ let w:fugitive_restore = restore
1210
1327
  let b:fugitive_blame_arguments = join(a:args,' ')
1211
1328
  call s:Detect(expand('%:p'))
1212
1329
  execute top
@@ -1292,6 +1409,200 @@ function! s:BlameSyntax() abort
1292
1409
  hi def link FugitiveblameNotCommittedYet Comment
1293
1410
  endfunction
1294
1411
 
1412
+ " }}}1
1413
+ " Gbrowse {{{1
1414
+
1415
+ call s:command("-bar -bang -count=0 -nargs=? -complete=customlist,s:EditComplete Gbrowse :execute s:Browse(<bang>0,<line1>,<count>,<f-args>)")
1416
+
1417
+ function! s:Browse(bang,line1,count,...) abort
1418
+ try
1419
+ let rev = a:0 ? substitute(a:1,'@[[:alnum:]_-]*\%(://.\{-\}\)\=$','','') : ''
1420
+ if rev ==# ''
1421
+ let expanded = s:buffer().rev()
1422
+ elseif rev ==# ':'
1423
+ let expanded = s:buffer().path('/')
1424
+ else
1425
+ let expanded = s:buffer().expand(rev)
1426
+ endif
1427
+ let full = s:repo().translate(expanded)
1428
+ let commit = ''
1429
+ if full =~# '^fugitive://'
1430
+ let commit = matchstr(full,'://.*//\zs\w\+')
1431
+ let path = matchstr(full,'://.*//\w\+\zs/.*')
1432
+ if commit =~ '..'
1433
+ let type = s:repo().git_chomp('cat-file','-t',commit.s:sub(path,'^/',':'))
1434
+ else
1435
+ let type = 'blob'
1436
+ endif
1437
+ let path = path[1:-1]
1438
+ elseif s:repo().bare()
1439
+ let path = '.git/' . full[strlen(s:repo().dir())+1:-1]
1440
+ let type = ''
1441
+ else
1442
+ let path = full[strlen(s:repo().tree())+1:-1]
1443
+ if path =~# '^\.git/'
1444
+ let type = ''
1445
+ elseif isdirectory(full)
1446
+ let type = 'tree'
1447
+ else
1448
+ let type = 'blob'
1449
+ endif
1450
+ endif
1451
+ if path =~# '^\.git/.*HEAD' && filereadable(s:repo().dir(path[5:-1]))
1452
+ let body = readfile(s:repo().dir(path[5:-1]))[0]
1453
+ if body =~# '^\x\{40\}$'
1454
+ let commit = body
1455
+ let type = 'commit'
1456
+ let path = ''
1457
+ elseif body =~# '^ref: refs/'
1458
+ let path = '.git/' . matchstr(body,'ref: \zs.*')
1459
+ endif
1460
+ endif
1461
+
1462
+ if a:0 && a:1 =~# '@[[:alnum:]_-]*\%(://.\{-\}\)\=$'
1463
+ let remote = matchstr(a:1,'@\zs[[:alnum:]_-]\+\%(://.\{-\}\)\=$')
1464
+ elseif path =~# '^\.git/refs/remotes/.'
1465
+ let remote = matchstr(path,'^\.git/refs/remotes/\zs[^/]\+')
1466
+ else
1467
+ let remote = 'origin'
1468
+ let branch = matchstr(rev,'^[[:alnum:]/._-]\+\ze[:^~@]')
1469
+ if branch ==# '' && path =~# '^\.git/refs/\w\+/'
1470
+ let branch = s:sub(path,'^\.git/refs/\w+/','')
1471
+ endif
1472
+ if filereadable(s:repo().dir('refs/remotes/'.branch))
1473
+ let remote = matchstr(branch,'[^/]\+')
1474
+ let rev = rev[strlen(remote)+1:-1]
1475
+ else
1476
+ if branch ==# ''
1477
+ let branch = matchstr(s:repo().head_ref(),'\<refs/heads/\zs.*')
1478
+ endif
1479
+ if branch != ''
1480
+ let remote = s:repo().git_chomp('config','branch.'.branch.'.remote')
1481
+ if remote ==# ''
1482
+ let remote = 'origin'
1483
+ elseif rev[0:strlen(branch)-1] ==# branch && rev[strlen(branch)] =~# '[:^~@]'
1484
+ let rev = s:repo().git_chomp('config','branch.'.branch.'.merge')[11:-1] . rev[strlen(branch):-1]
1485
+ endif
1486
+ endif
1487
+ endif
1488
+ endif
1489
+
1490
+ let raw = s:repo().git_chomp('config','remote.'.remote.'.url')
1491
+ if raw ==# ''
1492
+ let raw = remote
1493
+ endif
1494
+
1495
+ let url = s:github_url(s:repo(),raw,rev,commit,path,type,a:line1,a:count)
1496
+ if url == ''
1497
+ let url = s:instaweb_url(s:repo(),rev,commit,path,type,a:count ? a:line1 : 0)
1498
+ endif
1499
+
1500
+ if url == ''
1501
+ call s:throw("Instaweb failed to start and '".remote."' is not a GitHub remote")
1502
+ endif
1503
+
1504
+ if a:bang
1505
+ let @* = url
1506
+ return 'echomsg '.string(url)
1507
+ else
1508
+ return 'echomsg '.string(url).'|silent Git web--browse '.shellescape(url,1)
1509
+ endif
1510
+ catch /^fugitive:/
1511
+ return 'echoerr v:errmsg'
1512
+ endtry
1513
+ endfunction
1514
+
1515
+ function! s:github_url(repo,url,rev,commit,path,type,line1,line2) abort
1516
+ let path = a:path
1517
+ let repo_path = matchstr(a:url,'^\%(https\=://\|git://\|git@\)github\.com[/:]\zs.\{-\}\ze\%(\.git\)\=$')
1518
+ if repo_path ==# ''
1519
+ return ''
1520
+ endif
1521
+ let root = 'https://github.com/' . repo_path
1522
+ if path =~# '^\.git/refs/heads/'
1523
+ let branch = a:repo.git_chomp('config','branch.'.path[16:-1].'.merge')[11:-1]
1524
+ if branch ==# ''
1525
+ return root . '/commits/' . path[16:-1]
1526
+ else
1527
+ return root . '/commits/' . branch
1528
+ endif
1529
+ elseif path =~# '^\.git/refs/.'
1530
+ return root . '/commits/' . matchstr(path,'[^/]\+$')
1531
+ elseif path =~# '.git/\%(config$\|hooks\>\)'
1532
+ return root . '/admin'
1533
+ elseif path =~# '^\.git\>'
1534
+ return root
1535
+ endif
1536
+ if a:rev =~# '^[[:alnum:]._-]\+:'
1537
+ let commit = matchstr(a:rev,'^[^:]*')
1538
+ elseif a:commit =~# '^\d\=$'
1539
+ let local = matchstr(a:repo.head_ref(),'\<refs/heads/\zs.*')
1540
+ let commit = a:repo.git_chomp('config','branch.'.local.'.merge')[11:-1]
1541
+ if commit ==# ''
1542
+ let commit = local
1543
+ endif
1544
+ else
1545
+ let commit = a:commit
1546
+ endif
1547
+ if a:type == 'tree'
1548
+ let url = s:sub(root . '/tree/' . commit . '/' . path,'/$','')
1549
+ elseif a:type == 'blob'
1550
+ let url = root . '/blob/' . commit . '/' . path
1551
+ if a:line2 && a:line1 == a:line2
1552
+ let url .= '#L' . a:line1
1553
+ elseif a:line2
1554
+ let url .= '#L' . a:line1 . '-' . a:line2
1555
+ endif
1556
+ elseif a:type == 'tag'
1557
+ let commit = matchstr(getline(3),'^tag \zs.*')
1558
+ let url = root . '/tree/' . commit
1559
+ else
1560
+ let url = root . '/commit/' . commit
1561
+ endif
1562
+ return url
1563
+ endfunction
1564
+
1565
+ function! s:instaweb_url(repo,rev,commit,path,type,...) abort
1566
+ let output = a:repo.git_chomp('instaweb','-b','unknown')
1567
+ if output =~# 'http://'
1568
+ let root = matchstr(output,'http://.*').'/?p='.fnamemodify(a:repo.dir(),':t')
1569
+ else
1570
+ return ''
1571
+ endif
1572
+ if a:path =~# '^\.git/refs/.'
1573
+ return root . ';a=shortlog;h=' . matchstr(a:path,'^\.git/\zs.*')
1574
+ elseif a:path =~# '^\.git\>'
1575
+ return root
1576
+ endif
1577
+ let url = root
1578
+ if a:commit =~# '^\x\{40\}$'
1579
+ if a:type ==# 'commit'
1580
+ let url .= ';a=commit'
1581
+ endif
1582
+ let url .= ';h=' . a:repo.rev_parse(a:commit . (a:path == '' ? '' : ':' . a:path))
1583
+ else
1584
+ if a:type ==# 'blob'
1585
+ let tmp = tempname()
1586
+ silent execute 'write !'.a:repo.git_command('hash-object','-w','--stdin').' > '.tmp
1587
+ let url .= ';h=' . readfile(tmp)[0]
1588
+ else
1589
+ try
1590
+ let url .= ';h=' . a:repo.rev_parse((a:commit == '' ? 'HEAD' : ':' . a:commit) . ':' . a:path)
1591
+ catch /^fugitive:/
1592
+ call s:throw('fugitive: cannot browse uncommitted file')
1593
+ endtry
1594
+ endif
1595
+ let root .= ';hb=' . matchstr(a:repo.head_ref(),'[^ ]\+$')
1596
+ endif
1597
+ if a:path !=# ''
1598
+ let url .= ';f=' . a:path
1599
+ endif
1600
+ if a:0 && a:1
1601
+ let url .= '#l' . a:1
1602
+ endif
1603
+ return url
1604
+ endfunction
1605
+
1295
1606
  " }}}1
1296
1607
  " File access {{{1
1297
1608
 
@@ -1305,10 +1616,8 @@ function! s:ReplaceCmd(cmd,...) abort
1305
1616
  if &shell =~# 'cmd'
1306
1617
  let old_index = $GIT_INDEX_FILE
1307
1618
  let $GIT_INDEX_FILE = a:1
1308
- elseif &shell =~# 'csh'
1309
- let prefix = 'setenv GIT_INDEX_FILE '.s:shellesc(a:1).'; '
1310
1619
  else
1311
- let prefix = 'GIT_INDEX_FILE='.s:shellesc(a:1).' '
1620
+ let prefix = 'env GIT_INDEX_FILE='.s:shellesc(a:1).' '
1312
1621
  endif
1313
1622
  endif
1314
1623
  set noautowrite
@@ -1338,7 +1647,7 @@ function! s:BufReadIndex()
1338
1647
  if fnamemodify($GIT_INDEX_FILE !=# '' ? $GIT_INDEX_FILE : b:git_dir . '/index', ':p') ==# expand('%:p')
1339
1648
  let index = ''
1340
1649
  else
1341
- let index = expand('%')
1650
+ let index = expand('%:p')
1342
1651
  endif
1343
1652
  if b:fugitive_display_format
1344
1653
  call s:ReplaceCmd(s:repo().git_command('ls-files','--stage'),index)
@@ -1358,10 +1667,16 @@ function! s:BufReadIndex()
1358
1667
  nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += 1<Bar>exe <SID>BufReadIndex()<CR>
1359
1668
  nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= 1<Bar>exe <SID>BufReadIndex()<CR>
1360
1669
  nnoremap <buffer> <silent> D :<C-U>execute <SID>StageDiff()<CR>
1670
+ nnoremap <buffer> <silent> dd :<C-U>execute <SID>StageDiff()<CR>
1671
+ nnoremap <buffer> <silent> dh :<C-U>execute <SID>StageDiff('Gsdiff')<CR>
1672
+ nnoremap <buffer> <silent> ds :<C-U>execute <SID>StageDiff('Gsdiff')<CR>
1673
+ nnoremap <buffer> <silent> dv :<C-U>execute <SID>StageDiff()<CR>
1361
1674
  nnoremap <buffer> <silent> - :<C-U>execute <SID>StageToggle(line('.'),line('.')+v:count1-1)<CR>
1362
1675
  xnoremap <buffer> <silent> - :<C-U>execute <SID>StageToggle(line("'<"),line("'>"))<CR>
1363
1676
  nnoremap <buffer> <silent> p :<C-U>execute <SID>StagePatch(line('.'),line('.')+v:count1-1)<CR>
1364
1677
  xnoremap <buffer> <silent> p :<C-U>execute <SID>StagePatch(line("'<"),line("'>"))<CR>
1678
+ nnoremap <buffer> <silent> <C-N> :call search('^#\t.*','W')<Bar>.<CR>
1679
+ nnoremap <buffer> <silent> <C-P> :call search('^#\t.*','Wbe')<Bar>.<CR>
1365
1680
  call s:JumpInit()
1366
1681
  nunmap <buffer> P
1367
1682
  nunmap <buffer> ~
@@ -1415,7 +1730,11 @@ function! s:BufWriteIndexFile()
1415
1730
  endif
1416
1731
  let info = old_mode.' '.sha1.' '.stage."\t".path
1417
1732
  call writefile([info],tmp)
1418
- let error = system(s:repo().git_command('update-index','--index-info').' < '.tmp)
1733
+ if has('win32')
1734
+ let error = system('type '.tmp.'|'.s:repo().git_command('update-index','--index-info'))
1735
+ else
1736
+ let error = system(s:repo().git_command('update-index','--index-info').' < '.tmp)
1737
+ endif
1419
1738
  if v:shell_error == 0
1420
1739
  setlocal nomodified
1421
1740
  silent execute 'doautocmd BufWritePost '.s:fnameescape(expand('%:p'))
@@ -1539,9 +1858,9 @@ function! s:GF(mode) abort
1539
1858
  if showtree && line('.') == 1
1540
1859
  return ""
1541
1860
  elseif showtree && line('.') > 2
1542
- return s:Edit(a:mode,buffer.commit().':'.(buffer.path() == '' ? '' : buffer.path().'/').s:sub(getline('.'),'/$',''))
1861
+ return s:Edit(a:mode,buffer.commit().':'.s:buffer().path().(buffer.path() =~# '^$\|/$' ? '' : '/').s:sub(getline('.'),'/$',''))
1543
1862
  elseif getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40\}\t'
1544
- return s:Edit(a:mode,buffer.commit().':'.(buffer.path() == '' ? '' : buffer.path().'/').s:sub(matchstr(getline('.'),'\t\zs.*'),'/$',''))
1863
+ return s:Edit(a:mode,buffer.commit().':'.s:buffer().path().(buffer.path() =~# '^$\|/$' ? '' : '/').s:sub(matchstr(getline('.'),'\t\zs.*'),'/$',''))
1545
1864
  endif
1546
1865
 
1547
1866
  elseif buffer.type('blob')
@@ -1566,7 +1885,7 @@ function! s:GF(mode) abort
1566
1885
  let file = '/'.matchstr(getline('.'),' -> \zs.*')
1567
1886
  return s:Edit(a:mode,file)
1568
1887
  elseif getline('.') =~# '^#\t[[:alpha:] ]\+: *.'
1569
- let file = '/'.matchstr(getline('.'),': *\zs.*')
1888
+ let file = '/'.matchstr(getline('.'),': *\zs.\{-\}\ze\%( (new commits)\)\=$')
1570
1889
  return s:Edit(a:mode,file)
1571
1890
  elseif getline('.') =~# '^#\t.'
1572
1891
  let file = '/'.matchstr(getline('.'),'#\t\zs.*')
@@ -1629,6 +1948,13 @@ function! s:GF(mode) abort
1629
1948
  elseif getline('.') =~# '^diff --git \%(a/.*\|/dev/null\) \%(b/.*\|/dev/null\)'
1630
1949
  let dref = matchstr(getline('.'),'\Cdiff --git \zs\%(a/.*\|/dev/null\)\ze \%(b/.*\|/dev/null\)')
1631
1950
  let ref = matchstr(getline('.'),'\Cdiff --git \%(a/.*\|/dev/null\) \zs\%(b/.*\|/dev/null\)')
1951
+ let dcmd = 'Gdiff'
1952
+
1953
+ elseif getline('.') =~# '^index ' && getline(line('.')-1) =~# '^diff --git \%(a/.*\|/dev/null\) \%(b/.*\|/dev/null\)'
1954
+ let line = getline(line('.')-1)
1955
+ let dref = matchstr(line,'\Cdiff --git \zs\%(a/.*\|/dev/null\)\ze \%(b/.*\|/dev/null\)')
1956
+ let ref = matchstr(line,'\Cdiff --git \%(a/.*\|/dev/null\) \zs\%(b/.*\|/dev/null\)')
1957
+ let dcmd = 'Gdiff!'
1632
1958
 
1633
1959
  elseif line('$') == 1 && getline('.') =~ '^\x\{40\}$'
1634
1960
  let ref = getline('.')
@@ -1656,7 +1982,7 @@ function! s:GF(mode) abort
1656
1982
  endif
1657
1983
 
1658
1984
  if exists('dref')
1659
- return s:Edit(a:mode,ref) . '|Gdiff '.s:fnameescape(dref)
1985
+ return s:Edit(a:mode,ref) . '|'.dcmd.' '.s:fnameescape(dref)
1660
1986
  elseif ref != ""
1661
1987
  return s:Edit(a:mode,ref)
1662
1988
  endif
@@ -1698,6 +2024,18 @@ function! fugitive#statusline(...)
1698
2024
  endif
1699
2025
  endfunction
1700
2026
 
2027
+ function! s:repo_config(conf) dict abort
2028
+ return matchstr(system(s:repo().git_command('config').' '.a:conf),"[^\r\n]*")
2029
+ endfun
2030
+
2031
+ function! s:repo_user() dict abort
2032
+ let username = s:repo().config('user.name')
2033
+ let useremail = s:repo().config('user.email')
2034
+ return username.' <'.useremail.'>'
2035
+ endfun
2036
+
2037
+ call s:add_methods('repo',['config', 'user'])
2038
+
1701
2039
  " }}}1
1702
2040
 
1703
2041
  " vim:set ft=vim ts=8 sw=2 sts=2: