utils 0.0.55 → 0.0.56

Sign up to get free protection for your applications and to get access to all the features.
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: