Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Add fugitive.vim

Features include:

* :Gblame for interactive vertical split with git blame output.
* :Ggrep to search the work tree (or any arbitrary commit) with git grep,
  skipping over that which is not tracked in the repository.
* :Glog loads all previous revisions of a file into the quickfix list so
  you can iterate over them and watch the file evolve.
* :Gbrowse to open the current file on GitHub, with optional line range
  (try it in visual mode).
  • Loading branch information...
commit d836af8afdca00c12240114ca57d1cffa7913499 1 parent 52b2ee1
Dan Croak authored October 23, 2012
313  vim/doc/fugitive.txt
... ...
@@ -0,0 +1,313 @@
  1
+*fugitive.txt*  A Git wrapper so awesome, it should be illegal
  2
+
  3
+Author:  Tim Pope <http://tpo.pe/>
  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
+Whenever you edit a file from a Git repository, a set of commands is defined
  11
+that serve as a gateway to Git.
  12
+
  13
+COMMANDS                                        *fugitive-commands*
  14
+
  15
+These commands are local to the buffers in which they work (generally, buffers
  16
+that are part of Git repositories).
  17
+
  18
+                                                *fugitive-:Git*
  19
+:Git [args]             Run an arbitrary git command. Similar to :!git [args]
  20
+                        but chdir to the repository tree first.
  21
+
  22
+                                                *fugitive-:Git!*
  23
+:Git! [args]            Like |:Git|, but capture the output into a temp file,
  24
+                        and edit that temp file.
  25
+
  26
+                                                *fugitive-:Gcd*
  27
+:Gcd [directory]        |:cd| relative to the repository.
  28
+
  29
+                                                *fugitive-:Glcd*
  30
+:Glcd [directory]       |:lcd| relative to the repository.
  31
+
  32
+                                                *fugitive-:Gstatus*
  33
+:Gstatus                Bring up the output of git-status in the preview
  34
+                        window.  The following maps, which work on the cursor
  35
+                        line file where sensible, are provided:
  36
+
  37
+                        <C-N> next file
  38
+                        <C-P> previous file
  39
+                        <CR>  |:Gedit|
  40
+                        -     |:Git| add
  41
+                        -     |:Git| reset (staged files)
  42
+                        cA    |:Gcommit| --amend --reuse-message=HEAD
  43
+                        ca    |:Gcommit| --amend
  44
+                        cc    |:Gcommit|
  45
+                        cva   |:Gcommit| --amend --verbose
  46
+                        cvc   |:Gcommit| --verbose
  47
+                        D     |:Gdiff|
  48
+                        ds    |:Gsdiff|
  49
+                        dp    |:Git!| diff (p for patch; use :Gw to apply)
  50
+                        dp    |:Git| add --intent-to-add (untracked files)
  51
+                        dv    |:Gvdiff|
  52
+                        O     |:Gtabedit|
  53
+                        o     |:Gsplit|
  54
+                        p     |:Git| add --patch
  55
+                        p     |:Git| reset --patch (staged files)
  56
+                        q     close status
  57
+                        R     reload status
  58
+                        S     |:Gvsplit|
  59
+
  60
+                                                *fugitive-:Gcommit*
  61
+:Gcommit [args]         A wrapper around git-commit.  If there is nothing
  62
+                        to commit, |:Gstatus| is called instead.  Unless the
  63
+                        arguments given would skip the invocation of an editor
  64
+                        (e.g., -m), a split window will be used to obtain a
  65
+                        commit message.  Write and close that window (:wq or
  66
+                        |:Gwrite|) to finish the commit.  Unlike when running
  67
+                        the actual git-commit command, it is possible (but
  68
+                        unadvisable) to muck with the index with commands like
  69
+                        git-add and git-reset while a commit message is
  70
+                        pending.
  71
+
  72
+                                                *fugitive-:Ggrep*
  73
+:Ggrep [args]           |:grep| with git-grep as 'grepprg'.
  74
+
  75
+                                                *fugitive-:Glgrep*
  76
+:Glgrep [args]          |:lgrep| with git-grep as 'grepprg'.
  77
+
  78
+                                                *fugitive-:Glog*
  79
+:Glog [args]            Load all previous revisions of the current file into
  80
+                        the quickfix list.  Additional git-log arguments can
  81
+                        be given (for example, --reverse).  If "--" appears as
  82
+                        an argument, no file specific filtering is done, and
  83
+                        previous commits rather than previous file revisions
  84
+                        are loaded.
  85
+
  86
+                                                *fugitive-:Gllog*
  87
+:Gllog [args]           Like |:Glog|, but use the location list instead of the
  88
+                        quickfix list.
  89
+
  90
+                                        *fugitive-:Gedit* *fugitive-:Ge*
  91
+:Gedit [revision]       |:edit| a |fugitive-revision|.
  92
+
  93
+                                                *fugitive-:Gsplit*
  94
+:Gsplit [revision]      |:split| a |fugitive-revision|.
  95
+
  96
+                                                *fugitive-:Gvsplit*
  97
+:Gvsplit [revision]     |:vsplit| a |fugitive-revision|.
  98
+
  99
+                                                *fugitive-:Gtabedit*
  100
+:Gtabedit [revision]    |:tabedit| a |fugitive-revision|.
  101
+
  102
+                                                *fugitive-:Gpedit*
  103
+:Gpedit [revision]      |:pedit| a |fugitive-revision|.
  104
+
  105
+:Gsplit! [args]                 *fugitive-:Gsplit!* *fugitive-:Gvsplit!*
  106
+:Gvsplit! [args]                *fugitive-:Gtabedit!* *fugitive-:Gpedit!*
  107
+:Gtabedit! [args]       Like |:Git!|, but open the resulting temp file in a
  108
+:Gpedit! [args]         split, tab, or preview window.
  109
+
  110
+                                                *fugitive-:Gread*
  111
+:Gread [revision]       Empty the buffer and |:read| a |fugitive-revision|.
  112
+                        When the argument is omitted, this is similar to
  113
+                        git-checkout on a work tree file or git-add on a stage
  114
+                        file, but without writing anything to disk.
  115
+
  116
+:{range}Gread [revision]
  117
+                        |:read| in a |fugitive-revision| after {range}.
  118
+
  119
+                                                *fugitive-:Gread!*
  120
+:Gread! [args]          Empty the buffer and |:read| the output of a Git
  121
+                        command.  For example, :Gread! show HEAD:%.
  122
+
  123
+:{range}Gread! [args]  |:read| the output of a Git command after {range}.
  124
+
  125
+                                                *fugitive-:Gwrite*
  126
+:Gwrite                 Write to the current file's path and stage the results.
  127
+                        When run in a work tree file, it is effectively git
  128
+                        add.  Elsewhere, it is effectively git-checkout.  A
  129
+                        great deal of effort is expended to behave sensibly
  130
+                        when the work tree or index version of the file is
  131
+                        open in another buffer.
  132
+
  133
+:Gwrite {path}          You can give |:Gwrite| an explicit path of where in
  134
+                        the work tree to write.  You can also give a path like
  135
+                        :0:foo.txt or even :0 to write to just that stage in
  136
+                        the index.
  137
+
  138
+                                                *fugitive-:Gwq*
  139
+:Gwq [path]             Like |:Gwrite| followed by |:quit| if the write
  140
+                        succeeded.
  141
+
  142
+:Gwq! [path]            Like |:Gwrite|! followed by |:quit|! if the write
  143
+                        succeeded.
  144
+
  145
+                                                *fugitive-:Gdiff*
  146
+:Gdiff [revision]       Perform a |vimdiff| against the current file in the
  147
+                        given revision.  With no argument, the version in the
  148
+                        index is used (which means a three-way diff during a
  149
+                        merge conflict, making it a git-mergetool
  150
+                        alternative).  The newer of the two files is placed
  151
+                        to the right.  Use |do| and |dp| and write to the
  152
+                        index file to simulate "git add --patch".
  153
+
  154
+                                                *fugitive-:Gsdiff*
  155
+:Gsdiff [revision]      Like |:Gdiff|, but split horizontally.
  156
+
  157
+                                                *fugitive-:Gvdiff*
  158
+:Gvdiff [revision]      Identical to |:Gdiff|.  For symmetry with |:Gsdiff|.
  159
+
  160
+                                                *fugitive-:Gmove*
  161
+:Gmove {destination}    Wrapper around git-mv that renames the buffer
  162
+                        afterward.  The destination is relative to the current
  163
+                        directory except when started with a /, in which case
  164
+                        it is relative to the work tree.  Add a ! to pass -f.
  165
+
  166
+                                                *fugitive-:Gremove*
  167
+:Gremove                Wrapper around git-rm that deletes the buffer
  168
+                        afterward.  When invoked in an index file, --cached is
  169
+                        passed.  Add a ! to pass -f and forcefully discard the
  170
+                        buffer.
  171
+
  172
+                                                *fugitive-:Gblame*
  173
+:Gblame [flags]         Run git-blame on the file and open the results in a
  174
+                        scroll bound vertical split.  Press enter on a line to
  175
+                        reblame the file as it was in that commit.  You can
  176
+                        give any of ltfnsewMC as flags and they will be passed
  177
+                        along to git-blame.  The following maps, which work on
  178
+                        the cursor line commit where sensible, are provided:
  179
+
  180
+                        A     resize to end of author column
  181
+                        C     resize to end of commit column
  182
+                        D     resize to end of date/time column
  183
+                        q     close blame and return to blamed window
  184
+                        gq    q, then |:Gedit| to return to work tree version
  185
+                        i     q, then open commit
  186
+                        o     open commit in horizontal split
  187
+                        O     open commit in new tab
  188
+                        -     reblame at commit
  189
+                        ~     reblame at [count]th first grandparent
  190
+                        P     reblame at [count]th parent (like HEAD^[count])
  191
+
  192
+:[range]Gblame [flags]  Run git-blame on the given range.
  193
+
  194
+                                                *fugitive-:Gbrowse*
  195
+:[range]Gbrowse         If the remote for the current branch is on GitHub,
  196
+                        open the current file, blob, tree, commit, or tag
  197
+                        (with git-web--browse) on GitHub.  Otherwise, open the
  198
+                        current file, blob, tree, commit, or tag in
  199
+                        git-instaweb (if you have issues, verify you can run
  200
+                        "git instaweb" from a terminal).  If a range is given,
  201
+                        it is appropriately appended to the URL as an anchor.
  202
+
  203
+                        To use with GitHub FI, point g:fugitive_github_domains
  204
+                        at a list of domains:
  205
+>
  206
+                        let g:fugitive_github_domains = ['git.example.com']
  207
+~
  208
+:[range]Gbrowse!        Like :Gbrowse, but put the URL on the clipboard rather
  209
+                        than opening it.
  210
+
  211
+:[range]Gbrowse {revision}
  212
+                        Like :Gbrowse, but for a given |fugitive-revision|.  A
  213
+                        useful value here is -, which ties the URL to the
  214
+                        latest commit rather than a volatile branch.
  215
+
  216
+:[range]Gbrowse [...]@{remote}
  217
+                        Force using the given remote rather than the remote
  218
+                        for the current branch.  The remote is used to
  219
+                        determine which GitHub repository to link to.
  220
+
  221
+MAPPINGS                                        *fugitive-mappings*
  222
+
  223
+These maps are available everywhere.
  224
+
  225
+                                                *fugitive-c_CTRL-R_CTRL-G*
  226
+<C-R><C-G>              On the command line, recall the path to the current
  227
+                        object (that is, a representation of the object
  228
+                        recognized by |:Gedit|).
  229
+
  230
+                                                *fugitive-y_CTRL-G*
  231
+["x]y<C-G>              Yank the commit SHA and path to the current object.
  232
+
  233
+These maps are available in Git objects.
  234
+
  235
+                                                *fugitive-<CR>*
  236
+<CR>                    Jump to the revision under the cursor.
  237
+
  238
+                                                *fugitive-o*
  239
+o                       Jump to the revision under the cursor in a new split.
  240
+
  241
+                                                *fugitive-S*
  242
+S                       Jump to the revision under the cursor in a new
  243
+                        vertical split.
  244
+
  245
+                                                *fugitive-O*
  246
+O                       Jump to the revision under the cursor in a new tab.
  247
+
  248
+                                                *fugitive--*
  249
+-                       Go to the tree containing the current tree or blob.
  250
+
  251
+                                                *fugitive-~*
  252
+~                       Go to the current file in the [count]th first
  253
+                        ancestor.
  254
+
  255
+                                                *fugitive-P*
  256
+P                       Go to the current file in the [count]th parent.
  257
+
  258
+                                                *fugitive-C*
  259
+C                       Go to the commit containing the current file.
  260
+
  261
+                                                *fugitive-a*
  262
+a                       Show the current tag, commit, or tree in an alternate
  263
+                        format.
  264
+
  265
+SPECIFYING REVISIONS                            *fugitive-revision*
  266
+
  267
+Fugitive revisions are similar to Git revisions as defined in the "SPECIFYING
  268
+REVISIONS" section in the git-rev-parse man page.  For commands that accept an
  269
+optional revision, the default is the file in the index for work tree files
  270
+and the work tree file for everything else.  Example revisions follow.
  271
+
  272
+Revision        Meaning ~
  273
+HEAD            .git/HEAD
  274
+master          .git/refs/heads/master
  275
+HEAD^{}         The commit referenced by HEAD
  276
+HEAD^           The parent of the commit referenced by HEAD
  277
+HEAD:           The tree referenced by HEAD
  278
+/HEAD           The file named HEAD in the work tree
  279
+Makefile        The file named Makefile in the work tree
  280
+HEAD^:Makefile  The file named Makefile in the parent of HEAD
  281
+:Makefile       The file named Makefile in the index (writable)
  282
+-               The current file in HEAD
  283
+^               The current file in the previous commit
  284
+~3              The current file 3 commits ago
  285
+:               .git/index (Same as |:Gstatus|)
  286
+:0              The current file in the index
  287
+:1              The current file's common ancestor during a conflict
  288
+:2              The current file in the target branch during a conflict
  289
+:3              The current file in the merged branch during a conflict
  290
+:/foo           The most recent commit with "foo" in the message
  291
+
  292
+STATUSLINE                                      *fugitive-statusline*
  293
+
  294
+                                                *fugitive#statusline()*
  295
+Add %{fugitive#statusline()} to your statusline to get an indicator including
  296
+the current branch and the currently edited file's commit.  If you don't have
  297
+a statusline, this one matches the default when 'ruler' is set:
  298
+>
  299
+    set statusline=%<%f\ %h%m%r%{fugitive#statusline()}%=%-14.(%l,%c%V%)\ %P
  300
+<
  301
+                                                *fugitive#head(...)*
  302
+Use fugitive#head() to return the name of the current branch. If the current
  303
+HEAD is detached, fugitive#head() will return the empty string, unless the
  304
+optional argument is given, in which case the hash of the current commit will
  305
+be truncated to the given number of characters.
  306
+
  307
+ABOUT                                           *fugitive-about*
  308
+
  309
+Grab the latest version or report a bug on GitHub:
  310
+
  311
+http://github.com/tpope/vim-fugitive
  312
+
  313
+ vim:tw=78:et:ft=help:norl:
2,532  vim/plugin/fugitive.vim
... ...
@@ -0,0 +1,2532 @@
  1
+" fugitive.vim - A Git wrapper so awesome, it should be illegal
  2
+" Maintainer:   Tim Pope <http://tpo.pe/>
  3
+" Version:      1.2
  4
+" GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim
  5
+
  6
+if exists('g:loaded_fugitive') || &cp
  7
+  finish
  8
+endif
  9
+let g:loaded_fugitive = 1
  10
+
  11
+if !exists('g:fugitive_git_executable')
  12
+  let g:fugitive_git_executable = 'git'
  13
+endif
  14
+
  15
+" Utility {{{1
  16
+
  17
+function! s:function(name) abort
  18
+  return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),''))
  19
+endfunction
  20
+
  21
+function! s:sub(str,pat,rep) abort
  22
+  return substitute(a:str,'\v\C'.a:pat,a:rep,'')
  23
+endfunction
  24
+
  25
+function! s:gsub(str,pat,rep) abort
  26
+  return substitute(a:str,'\v\C'.a:pat,a:rep,'g')
  27
+endfunction
  28
+
  29
+function! s:shellesc(arg) abort
  30
+  if a:arg =~ '^[A-Za-z0-9_/.-]\+$'
  31
+    return a:arg
  32
+  elseif &shell =~# 'cmd'
  33
+    return '"'.s:gsub(s:gsub(a:arg, '"', '""'), '\%', '"%"').'"'
  34
+  else
  35
+    return shellescape(a:arg)
  36
+  endif
  37
+endfunction
  38
+
  39
+function! s:fnameescape(file) abort
  40
+  if exists('*fnameescape')
  41
+    return fnameescape(a:file)
  42
+  else
  43
+    return escape(a:file," \t\n*?[{`$\\%#'\"|!<")
  44
+  endif
  45
+endfunction
  46
+
  47
+function! s:throw(string) abort
  48
+  let v:errmsg = 'fugitive: '.a:string
  49
+  throw v:errmsg
  50
+endfunction
  51
+
  52
+function! s:warn(str)
  53
+  echohl WarningMsg
  54
+  echomsg a:str
  55
+  echohl None
  56
+  let v:warningmsg = a:str
  57
+endfunction
  58
+
  59
+function! s:shellslash(path)
  60
+  if exists('+shellslash') && !&shellslash
  61
+    return s:gsub(a:path,'\\','/')
  62
+  else
  63
+    return a:path
  64
+  endif
  65
+endfunction
  66
+
  67
+function! s:recall()
  68
+  let rev = s:sub(s:buffer().rev(), '^/', '')
  69
+  if rev ==# ':'
  70
+    return matchstr(getline('.'),'^#\t\%([[:alpha:] ]\+: *\)\=\zs.\{-\}\ze\%( ([^()[:digit:]]\+)\)\=$\|^\d\{6} \x\{40\} \d\t\zs.*')
  71
+  endif
  72
+  return rev
  73
+endfunction
  74
+
  75
+function! s:add_methods(namespace, method_names) abort
  76
+  for name in a:method_names
  77
+    let s:{a:namespace}_prototype[name] = s:function('s:'.a:namespace.'_'.name)
  78
+  endfor
  79
+endfunction
  80
+
  81
+let s:commands = []
  82
+function! s:command(definition) abort
  83
+  let s:commands += [a:definition]
  84
+endfunction
  85
+
  86
+function! s:define_commands()
  87
+  for command in s:commands
  88
+    exe 'command! -buffer '.command
  89
+  endfor
  90
+endfunction
  91
+
  92
+augroup fugitive_utility
  93
+  autocmd!
  94
+  autocmd User Fugitive call s:define_commands()
  95
+augroup END
  96
+
  97
+let s:abstract_prototype = {}
  98
+
  99
+" }}}1
  100
+" Initialization {{{1
  101
+
  102
+function! fugitive#is_git_dir(path) abort
  103
+  let path = s:sub(a:path, '[\/]$', '') . '/'
  104
+  return isdirectory(path.'objects') && isdirectory(path.'refs') && getfsize(path.'HEAD') > 10
  105
+endfunction
  106
+
  107
+function! fugitive#extract_git_dir(path) abort
  108
+  if s:shellslash(a:path) =~# '^fugitive://.*//'
  109
+    return matchstr(s:shellslash(a:path), '\C^fugitive://\zs.\{-\}\ze//')
  110
+  endif
  111
+  let root = s:shellslash(simplify(fnamemodify(a:path, ':p:s?[\/]$??')))
  112
+  let previous = ""
  113
+  while root !=# previous
  114
+    let dir = s:sub(root, '[\/]$', '') . '/.git'
  115
+    let type = getftype(dir)
  116
+    if type ==# 'dir' && fugitive#is_git_dir(dir)
  117
+      return dir
  118
+    elseif type ==# 'link' && fugitive#is_git_dir(dir)
  119
+      return resolve(dir)
  120
+    elseif type !=# '' && filereadable(dir)
  121
+      let line = get(readfile(dir, '', 1), 0, '')
  122
+      if line =~# '^gitdir: \.' && fugitive#is_git_dir(root.'/'.line[8:-1])
  123
+        return simplify(root.'/'.line[8:-1])
  124
+      elseif line =~# '^gitdir: ' && fugitive#is_git_dir(line[8:-1])
  125
+        return line[8:-1]
  126
+      endif
  127
+    elseif fugitive#is_git_dir(root)
  128
+      return root
  129
+    endif
  130
+    let previous = root
  131
+    let root = fnamemodify(root, ':h')
  132
+  endwhile
  133
+  return ''
  134
+endfunction
  135
+
  136
+function! s:Detect(path)
  137
+  if exists('b:git_dir') && (b:git_dir ==# '' || b:git_dir =~# '/$')
  138
+    unlet b:git_dir
  139
+  endif
  140
+  if !exists('b:git_dir')
  141
+    let dir = fugitive#extract_git_dir(a:path)
  142
+    if dir !=# ''
  143
+      let b:git_dir = dir
  144
+    endif
  145
+  endif
  146
+  if exists('b:git_dir')
  147
+    silent doautocmd User Fugitive
  148
+    cnoremap <buffer> <expr> <C-R><C-G> <SID>recall()
  149
+    nnoremap <buffer> <silent> y<C-G> :call setreg(v:register, <SID>recall())<CR>
  150
+    let buffer = fugitive#buffer()
  151
+    if expand('%:p') =~# '//'
  152
+      call buffer.setvar('&path', s:sub(buffer.getvar('&path'), '^\.%(,|$)', ''))
  153
+    endif
  154
+    " Look for tags file in .git dir and add them to &tags
  155
+    " See http://tbaggery.com/2011/08/08/effortless-ctags-with-git.html
  156
+    let tagsfile = b:git_dir.'/tags'
  157
+    if stridx(buffer.getvar('&tags'), escape(tagsfile, ', ')) == -1
  158
+      if filereadable(tagsfile)
  159
+        call buffer.setvar('&tags', escape(tagsfile, ', ').','.buffer.getvar('&tags'))
  160
+      endif
  161
+      if &filetype !=# ''
  162
+        let tagsfile = b:git_dir.'/'.&filetype.'.tags'
  163
+        if filereadable(tagsfile)
  164
+          call buffer.setvar('&tags', escape(tagsfile, ', ').','.buffer.getvar('&tags'))
  165
+        endif
  166
+      endif
  167
+    endif
  168
+  endif
  169
+endfunction
  170
+
  171
+augroup fugitive
  172
+  autocmd!
  173
+  autocmd BufNewFile,BufReadPost * call s:Detect(expand('<amatch>:p'))
  174
+  autocmd FileType           netrw call s:Detect(expand('%:p'))
  175
+  autocmd User NERDTreeInit,NERDTreeNewRoot call s:Detect(b:NERDTreeRoot.path.str())
  176
+  autocmd VimEnter * if expand('<amatch>')==''|call s:Detect(getcwd())|endif
  177
+  autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('<abuf>')), 'fugitive_leave')
  178
+augroup END
  179
+
  180
+" }}}1
  181
+" Repository {{{1
  182
+
  183
+let s:repo_prototype = {}
  184
+let s:repos = {}
  185
+
  186
+function! s:repo(...) abort
  187
+  let dir = a:0 ? a:1 : (exists('b:git_dir') && b:git_dir !=# '' ? b:git_dir : fugitive#extract_git_dir(expand('%:p')))
  188
+  if dir !=# ''
  189
+    if has_key(s:repos, dir)
  190
+      let repo = get(s:repos, dir)
  191
+    else
  192
+      let repo = {'git_dir': dir}
  193
+      let s:repos[dir] = repo
  194
+    endif
  195
+    return extend(extend(repo, s:repo_prototype, 'keep'), s:abstract_prototype, 'keep')
  196
+  endif
  197
+  call s:throw('not a git repository: '.expand('%:p'))
  198
+endfunction
  199
+
  200
+function! fugitive#repo(...)
  201
+  return call('s:repo', a:000)
  202
+endfunction
  203
+
  204
+function! s:repo_dir(...) dict abort
  205
+  return join([self.git_dir]+a:000,'/')
  206
+endfunction
  207
+
  208
+function! s:repo_configured_tree() dict abort
  209
+  if !has_key(self,'_tree')
  210
+    let self._tree = ''
  211
+    if filereadable(self.dir('config'))
  212
+      let config = readfile(self.dir('config'),'',10)
  213
+      call filter(config,'v:val =~# "^\\s*worktree *="')
  214
+      if len(config) == 1
  215
+        let self._tree = matchstr(config[0], '= *\zs.*')
  216
+      endif
  217
+    endif
  218
+  endif
  219
+  if self._tree =~# '^\.'
  220
+    return simplify(self.dir(self._tree))
  221
+  else
  222
+    return self._tree
  223
+  endif
  224
+endfunction
  225
+
  226
+function! s:repo_tree(...) dict abort
  227
+  if self.dir() =~# '/\.git$'
  228
+    let dir = self.dir()[0:-6]
  229
+  else
  230
+    let dir = self.configured_tree()
  231
+  endif
  232
+  if dir ==# ''
  233
+    call s:throw('no work tree')
  234
+  else
  235
+    return join([dir]+a:000,'/')
  236
+  endif
  237
+endfunction
  238
+
  239
+function! s:repo_bare() dict abort
  240
+  if self.dir() =~# '/\.git$'
  241
+    return 0
  242
+  else
  243
+    return self.configured_tree() ==# ''
  244
+  endtry
  245
+endfunction
  246
+
  247
+function! s:repo_translate(spec) dict abort
  248
+  if a:spec ==# '.' || a:spec ==# '/.'
  249
+    return self.bare() ? self.dir() : self.tree()
  250
+  elseif a:spec =~# '^/\=\.git$' && self.bare()
  251
+    return self.dir()
  252
+  elseif a:spec =~# '^/\=\.git/'
  253
+    return self.dir(s:sub(a:spec, '^/=\.git/', ''))
  254
+  elseif a:spec =~# '^/'
  255
+    return self.tree().a:spec
  256
+  elseif a:spec =~# '^:[0-3]:'
  257
+    return 'fugitive://'.self.dir().'//'.a:spec[1].'/'.a:spec[3:-1]
  258
+  elseif a:spec ==# ':'
  259
+    if $GIT_INDEX_FILE =~# '/[^/]*index[^/]*\.lock$' && fnamemodify($GIT_INDEX_FILE,':p')[0:strlen(self.dir())] ==# self.dir('') && filereadable($GIT_INDEX_FILE)
  260
+      return fnamemodify($GIT_INDEX_FILE,':p')
  261
+    else
  262
+      return self.dir('index')
  263
+    endif
  264
+  elseif a:spec =~# '^:/'
  265
+    let ref = self.rev_parse(matchstr(a:spec,'.[^:]*'))
  266
+    return 'fugitive://'.self.dir().'//'.ref
  267
+  elseif a:spec =~# '^:'
  268
+    return 'fugitive://'.self.dir().'//0/'.a:spec[1:-1]
  269
+  elseif a:spec =~# 'HEAD\|^refs/' && a:spec !~ ':' && filereadable(self.dir(a:spec))
  270
+    return self.dir(a:spec)
  271
+  elseif filereadable(self.dir('refs/'.a:spec))
  272
+    return self.dir('refs/'.a:spec)
  273
+  elseif filereadable(self.dir('refs/tags/'.a:spec))
  274
+    return self.dir('refs/tags/'.a:spec)
  275
+  elseif filereadable(self.dir('refs/heads/'.a:spec))
  276
+    return self.dir('refs/heads/'.a:spec)
  277
+  elseif filereadable(self.dir('refs/remotes/'.a:spec))
  278
+    return self.dir('refs/remotes/'.a:spec)
  279
+  elseif filereadable(self.dir('refs/remotes/'.a:spec.'/HEAD'))
  280
+    return self.dir('refs/remotes/'.a:spec,'/HEAD')
  281
+  else
  282
+    try
  283
+      let ref = self.rev_parse(matchstr(a:spec,'[^:]*'))
  284
+      let path = s:sub(matchstr(a:spec,':.*'),'^:','/')
  285
+      return 'fugitive://'.self.dir().'//'.ref.path
  286
+    catch /^fugitive:/
  287
+      return self.tree(a:spec)
  288
+    endtry
  289
+  endif
  290
+endfunction
  291
+
  292
+function! s:repo_head(...) dict abort
  293
+    let head = s:repo().head_ref()
  294
+
  295
+    if head =~# '^ref: '
  296
+      let branch = s:sub(head,'^ref: %(refs/%(heads/|remotes/|tags/)=)=','')
  297
+    elseif head =~# '^\x\{40\}$'
  298
+      " truncate hash to a:1 characters if we're in detached head mode
  299
+      let len = a:0 ? a:1 : 0
  300
+      let branch = len ? head[0:len-1] : ''
  301
+    endif
  302
+
  303
+    return branch
  304
+endfunction
  305
+
  306
+call s:add_methods('repo',['dir','configured_tree','tree','bare','translate','head'])
  307
+
  308
+function! s:repo_git_command(...) dict abort
  309
+  let git = g:fugitive_git_executable . ' --git-dir='.s:shellesc(self.git_dir)
  310
+  return git.join(map(copy(a:000),'" ".s:shellesc(v:val)'),'')
  311
+endfunction
  312
+
  313
+function! s:repo_git_chomp(...) dict abort
  314
+  return s:sub(system(call(self.git_command,a:000,self)),'\n$','')
  315
+endfunction
  316
+
  317
+function! s:repo_git_chomp_in_tree(...) dict abort
  318
+  let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
  319
+  let dir = getcwd()
  320
+  try
  321
+    execute cd.'`=s:repo().tree()`'
  322
+    return call(s:repo().git_chomp, a:000, s:repo())
  323
+  finally
  324
+    execute cd.'`=dir`'
  325
+  endtry
  326
+endfunction
  327
+
  328
+function! s:repo_rev_parse(rev) dict abort
  329
+  let hash = self.git_chomp('rev-parse','--verify',a:rev)
  330
+  if hash =~ '\<\x\{40\}$'
  331
+    return matchstr(hash,'\<\x\{40\}$')
  332
+  endif
  333
+  call s:throw('rev-parse '.a:rev.': '.hash)
  334
+endfunction
  335
+
  336
+call s:add_methods('repo',['git_command','git_chomp','git_chomp_in_tree','rev_parse'])
  337
+
  338
+function! s:repo_dirglob(base) dict abort
  339
+  let base = s:sub(a:base,'^/','')
  340
+  let matches = split(glob(self.tree(s:gsub(base,'/','*&').'*/')),"\n")
  341
+  call map(matches,'v:val[ strlen(self.tree())+(a:base !~ "^/") : -1 ]')
  342
+  return matches
  343
+endfunction
  344
+
  345
+function! s:repo_superglob(base) dict abort
  346
+  if a:base =~# '^/' || a:base !~# ':'
  347
+    let results = []
  348
+    if a:base !~# '^/'
  349
+      let heads = ["HEAD","ORIG_HEAD","FETCH_HEAD","MERGE_HEAD"]
  350
+      let heads += sort(split(s:repo().git_chomp("rev-parse","--symbolic","--branches","--tags","--remotes"),"\n"))
  351
+      call filter(heads,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base')
  352
+      let results += heads
  353
+    endif
  354
+    if !self.bare()
  355
+      let base = s:sub(a:base,'^/','')
  356
+      let matches = split(glob(self.tree(s:gsub(base,'/','*&').'*')),"\n")
  357
+      call map(matches,'s:shellslash(v:val)')
  358
+      call map(matches,'v:val !~ "/$" && isdirectory(v:val) ? v:val."/" : v:val')
  359
+      call map(matches,'v:val[ strlen(self.tree())+(a:base !~ "^/") : -1 ]')
  360
+      let results += matches
  361
+    endif
  362
+    return results
  363
+
  364
+  elseif a:base =~# '^:'
  365
+    let entries = split(self.git_chomp('ls-files','--stage'),"\n")
  366
+    call map(entries,'s:sub(v:val,".*(\\d)\\t(.*)",":\\1:\\2")')
  367
+    if a:base !~# '^:[0-3]\%(:\|$\)'
  368
+      call filter(entries,'v:val[1] == "0"')
  369
+      call map(entries,'v:val[2:-1]')
  370
+    endif
  371
+    call filter(entries,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base')
  372
+    return entries
  373
+
  374
+  else
  375
+    let tree = matchstr(a:base,'.*[:/]')
  376
+    let entries = split(self.git_chomp('ls-tree',tree),"\n")
  377
+    call map(entries,'s:sub(v:val,"^04.*\\zs$","/")')
  378
+    call map(entries,'tree.s:sub(v:val,".*\t","")')
  379
+    return filter(entries,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base')
  380
+  endif
  381
+endfunction
  382
+
  383
+call s:add_methods('repo',['dirglob','superglob'])
  384
+
  385
+function! s:repo_config(conf) dict abort
  386
+  return matchstr(system(s:repo().git_command('config').' '.a:conf),"[^\r\n]*")
  387
+endfun
  388
+
  389
+function! s:repo_user() dict abort
  390
+  let username = s:repo().config('user.name')
  391
+  let useremail = s:repo().config('user.email')
  392
+  return username.' <'.useremail.'>'
  393
+endfun
  394
+
  395
+function! s:repo_aliases() dict abort
  396
+  if !has_key(self,'_aliases')
  397
+    let self._aliases = {}
  398
+    for line in split(self.git_chomp('config','--get-regexp','^alias[.]'),"\n")
  399
+      let self._aliases[matchstr(line,'\.\zs\S\+')] = matchstr(line,' \zs.*')
  400
+    endfor
  401
+  endif
  402
+  return self._aliases
  403
+endfunction
  404
+
  405
+call s:add_methods('repo',['config', 'user', 'aliases'])
  406
+
  407
+function! s:repo_keywordprg() dict abort
  408
+  let args = ' --git-dir='.escape(self.dir(),"\\\"' ")
  409
+  if has('gui_running') && !has('win32')
  410
+    return g:fugitive_git_executable . ' --no-pager' . args . ' log -1'
  411
+  else
  412
+    return g:fugitive_git_executable . args . ' show'
  413
+  endif
  414
+endfunction
  415
+
  416
+call s:add_methods('repo',['keywordprg'])
  417
+
  418
+" }}}1
  419
+" Buffer {{{1
  420
+
  421
+let s:buffer_prototype = {}
  422
+
  423
+function! s:buffer(...) abort
  424
+  let buffer = {'#': bufnr(a:0 ? a:1 : '%')}
  425
+  call extend(extend(buffer,s:buffer_prototype,'keep'),s:abstract_prototype,'keep')
  426
+  if buffer.getvar('git_dir') !=# ''
  427
+    return buffer
  428
+  endif
  429
+  call s:throw('not a git repository: '.expand('%:p'))
  430
+endfunction
  431
+
  432
+function! fugitive#buffer(...) abort
  433
+  return s:buffer(a:0 ? a:1 : '%')
  434
+endfunction
  435
+
  436
+function! s:buffer_getvar(var) dict abort
  437
+  return getbufvar(self['#'],a:var)
  438
+endfunction
  439
+
  440
+function! s:buffer_setvar(var,value) dict abort
  441
+  return setbufvar(self['#'],a:var,a:value)
  442
+endfunction
  443
+
  444
+function! s:buffer_getline(lnum) dict abort
  445
+  return getbufline(self['#'],a:lnum)[0]
  446
+endfunction
  447
+
  448
+function! s:buffer_repo() dict abort
  449
+  return s:repo(self.getvar('git_dir'))
  450
+endfunction
  451
+
  452
+function! s:buffer_type(...) dict abort
  453
+  if self.getvar('fugitive_type') != ''
  454
+    let type = self.getvar('fugitive_type')
  455
+  elseif fnamemodify(self.spec(),':p') =~# '.\git/refs/\|\.git/\w*HEAD$'
  456
+    let type = 'head'
  457
+  elseif self.getline(1) =~ '^tree \x\{40\}$' && self.getline(2) == ''
  458
+    let type = 'tree'
  459
+  elseif self.getline(1) =~ '^\d\{6\} \w\{4\} \x\{40\}\>\t'
  460
+    let type = 'tree'
  461
+  elseif self.getline(1) =~ '^\d\{6\} \x\{40\}\> \d\t'
  462
+    let type = 'index'
  463
+  elseif isdirectory(self.spec())
  464
+    let type = 'directory'
  465
+  elseif self.spec() == ''
  466
+    let type = 'null'
  467
+  else
  468
+    let type = 'file'
  469
+  endif
  470
+  if a:0
  471
+    return !empty(filter(copy(a:000),'v:val ==# type'))
  472
+  else
  473
+    return type
  474
+  endif
  475
+endfunction
  476
+
  477
+if has('win32')
  478
+
  479
+  function! s:buffer_spec() dict abort
  480
+    let bufname = bufname(self['#'])
  481
+    let retval = ''
  482
+    for i in split(bufname,'[^:]\zs\\')
  483
+      let retval = fnamemodify((retval==''?'':retval.'\').i,':.')
  484
+    endfor
  485
+    return s:shellslash(fnamemodify(retval,':p'))
  486
+  endfunction
  487
+
  488
+else
  489
+
  490
+  function! s:buffer_spec() dict abort
  491
+    let bufname = bufname(self['#'])
  492
+    return s:shellslash(bufname == '' ? '' : fnamemodify(bufname,':p'))
  493
+  endfunction
  494
+
  495
+endif
  496
+
  497
+function! s:buffer_name() dict abort
  498
+  return self.spec()
  499
+endfunction
  500
+
  501
+function! s:buffer_commit() dict abort
  502
+  return matchstr(self.spec(),'^fugitive://.\{-\}//\zs\w*')
  503
+endfunction
  504
+
  505
+function! s:buffer_path(...) dict abort
  506
+  let rev = matchstr(self.spec(),'^fugitive://.\{-\}//\zs.*')
  507
+  if rev != ''
  508
+    let rev = s:sub(rev,'\w*','')
  509
+  elseif self.repo().bare()
  510
+    let rev = '/.git'.self.spec()[strlen(self.repo().dir()) : -1]
  511
+  else
  512
+    let rev = self.spec()[strlen(self.repo().tree()) : -1]
  513
+  endif
  514
+  return s:sub(s:sub(rev,'.\zs/$',''),'^/',a:0 ? a:1 : '')
  515
+endfunction
  516
+
  517
+function! s:buffer_rev() dict abort
  518
+  let rev = matchstr(self.spec(),'^fugitive://.\{-\}//\zs.*')
  519
+  if rev =~ '^\x/'
  520
+    return ':'.rev[0].':'.rev[2:-1]
  521
+  elseif rev =~ '.'
  522
+    return s:sub(rev,'/',':')
  523
+  elseif self.spec() =~ '\.git/index$'
  524
+    return ':'
  525
+  elseif self.spec() =~ '\.git/refs/\|\.git/.*HEAD$'
  526
+    return self.spec()[strlen(self.repo().dir())+1 : -1]
  527
+  else
  528
+    return self.path('/')
  529
+  endif
  530
+endfunction
  531
+
  532
+function! s:buffer_sha1() dict abort
  533
+  if self.spec() =~ '^fugitive://' || self.spec() =~ '\.git/refs/\|\.git/.*HEAD$'
  534
+    return self.repo().rev_parse(self.rev())
  535
+  else
  536
+    return ''
  537
+  endif
  538
+endfunction
  539
+
  540
+function! s:buffer_expand(rev) dict abort
  541
+  if a:rev =~# '^:[0-3]$'
  542
+    let file = a:rev.self.path(':')
  543
+  elseif a:rev =~# '^[-:]/$'
  544
+    let file = '/'.self.path()
  545
+  elseif a:rev =~# '^-'
  546
+    let file = 'HEAD^{}'.a:rev[1:-1].self.path(':')
  547
+  elseif a:rev =~# '^@{'
  548
+    let file = 'HEAD'.a:rev.self.path(':')
  549
+  elseif a:rev =~# '^[~^]'
  550
+    let commit = s:sub(self.commit(),'^\d=$','HEAD')
  551
+    let file = commit.a:rev.self.path(':')
  552
+  else
  553
+    let file = a:rev
  554
+  endif
  555
+  return s:sub(s:sub(file,'\%$',self.path()),'\.\@<=/$','')
  556
+endfunction
  557
+
  558
+function! s:buffer_containing_commit() dict abort
  559
+  if self.commit() =~# '^\d$'
  560
+    return ':'
  561
+  elseif self.commit() =~# '.'
  562
+    return self.commit()
  563
+  else
  564
+    return 'HEAD'
  565
+  endif
  566
+endfunction
  567
+
  568
+function! s:buffer_up(...) dict abort
  569
+  let rev = self.rev()
  570
+  let c = a:0 ? a:1 : 1
  571
+  while c
  572
+    if rev =~# '^[/:]$'
  573
+      let rev = 'HEAD'
  574
+    elseif rev =~# '^:'
  575
+      let rev = ':'
  576
+    elseif rev =~# '^refs/[^^~:]*$\|^[^^~:]*HEAD$'
  577
+      let rev .= '^{}'
  578
+    elseif rev =~# '^/\|:.*/'
  579
+      let rev = s:sub(rev, '.*\zs/.*', '')
  580
+    elseif rev =~# ':.'
  581
+      let rev = matchstr(rev, '^[^:]*:')
  582
+    elseif rev =~# ':$'
  583
+      let rev = rev[0:-2]
  584
+    else
  585
+      return rev.'~'.c
  586
+    endif
  587
+    let c -= 1
  588
+  endwhile
  589
+  return rev
  590
+endfunction
  591
+
  592
+call s:add_methods('buffer',['getvar','setvar','getline','repo','type','spec','name','commit','path','rev','sha1','expand','containing_commit','up'])
  593
+
  594
+" }}}1
  595
+" Git {{{1
  596
+
  597
+call s:command("-bang -nargs=? -complete=customlist,s:GitComplete Git :execute s:Git(<bang>0,<q-args>)")
  598
+
  599
+function! s:ExecuteInTree(cmd) abort
  600
+  let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
  601
+  let dir = getcwd()
  602
+  try
  603
+    execute cd.'`=s:repo().tree()`'
  604
+    execute a:cmd
  605
+  finally
  606
+    execute cd.'`=dir`'
  607
+  endtry
  608
+endfunction
  609
+
  610
+function! s:Git(bang,cmd) abort
  611
+  if a:bang
  612
+    return s:Edit('edit',1,a:cmd)
  613
+  endif
  614
+  let git = s:repo().git_command()
  615
+  if has('gui_running') && !has('win32')
  616
+    let git .= ' --no-pager'
  617
+  endif
  618
+  let cmd = matchstr(a:cmd,'\v\C.{-}%($|\\@<!%(\\\\)*\|)@=')
  619
+  call s:ExecuteInTree('!'.git.' '.cmd)
  620
+  call fugitive#reload_status()
  621
+  return matchstr(a:cmd,'\v\C\\@<!%(\\\\)*\|\zs.*')
  622
+endfunction
  623
+
  624
+function! s:GitComplete(A,L,P) abort
  625
+  if !exists('s:exec_path')
  626
+    let s:exec_path = s:sub(system(g:fugitive_git_executable.' --exec-path'),'\n$','')
  627
+  endif
  628
+  let cmds = map(split(glob(s:exec_path.'/git-*'),"\n"),'s:sub(v:val[strlen(s:exec_path)+5 : -1],"\\.exe$","")')
  629
+  if a:L =~ ' [[:alnum:]-]\+ '
  630
+    return s:repo().superglob(a:A)
  631
+  elseif a:A == ''
  632
+    return sort(cmds+keys(s:repo().aliases()))
  633
+  else
  634
+    return filter(sort(cmds+keys(s:repo().aliases())),'v:val[0:strlen(a:A)-1] ==# a:A')
  635
+  endif
  636
+endfunction
  637
+
  638
+" }}}1
  639
+" Gcd, Glcd {{{1
  640
+
  641
+function! s:DirComplete(A,L,P) abort
  642
+  let matches = s:repo().dirglob(a:A)
  643
+  return matches
  644
+endfunction
  645
+
  646
+call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Gcd  :cd<bang>  `=s:repo().bare() ? s:repo().dir(<q-args>) : s:repo().tree(<q-args>)`")
  647
+call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Glcd :lcd<bang> `=s:repo().bare() ? s:repo().dir(<q-args>) : s:repo().tree(<q-args>)`")
  648
+
  649
+" }}}1
  650
+" Gstatus {{{1
  651
+
  652
+call s:command("-bar Gstatus :execute s:Status()")
  653
+
  654
+function! s:Status() abort
  655
+  try
  656
+    Gpedit :
  657
+    wincmd P
  658
+    nnoremap <buffer> <silent> q    :<C-U>bdelete<CR>
  659
+  catch /^fugitive:/
  660
+    return 'echoerr v:errmsg'
  661
+  endtry
  662
+  return ''
  663
+endfunction
  664
+
  665
+function! fugitive#reload_status() abort
  666
+  let mytab = tabpagenr()
  667
+  for tab in [mytab] + range(1,tabpagenr('$'))
  668
+    for winnr in range(1,tabpagewinnr(tab,'$'))
  669
+      if getbufvar(tabpagebuflist(tab)[winnr-1],'fugitive_type') ==# 'index'
  670
+        execute 'tabnext '.tab
  671
+        if winnr != winnr()
  672
+          execute winnr.'wincmd w'
  673
+          let restorewinnr = 1
  674
+        endif
  675
+        try
  676
+          if !&modified
  677
+            call s:BufReadIndex()
  678
+          endif
  679
+        finally
  680
+          if exists('restorewinnr')
  681
+            wincmd p
  682
+          endif
  683
+          execute 'tabnext '.mytab
  684
+        endtry
  685
+      endif
  686
+    endfor
  687
+  endfor
  688
+endfunction
  689
+
  690
+function! s:stage_info(lnum) abort
  691
+  let filename = matchstr(getline(a:lnum),'^#\t\zs.\{-\}\ze\%( ([^()[:digit:]]\+)\)\=$')
  692
+  let lnum = a:lnum
  693
+  while lnum && getline(lnum) !~# '^#.*:$'
  694
+    let lnum -= 1
  695
+  endwhile
  696
+  if !lnum
  697
+    return ['', '']
  698
+  elseif getline(lnum+1) =~# '^# .*"git \%(reset\|rm --cached\) ' || getline(lnum) ==# '# Changes to be committed:'
  699
+    return [matchstr(filename, ': *\zs.*'), 'staged']
  700
+  elseif getline(lnum+2) =~# '^# .*"git checkout ' || getline(lnum) ==# '# Changes not staged for commit:'
  701
+    return [matchstr(filename, ': *\zs.*'), 'unstaged']
  702
+  elseif getline(lnum+1) =~# '^# .*"git add/rm ' || getline(lnum) ==# '# Unmerged paths:'
  703
+    return [matchstr(filename, ': *\zs.*'), 'unmerged']
  704
+  else
  705
+    return [filename, 'untracked']
  706
+  endif
  707
+endfunction
  708
+
  709
+function! s:StageNext(count)
  710
+  for i in range(a:count)
  711
+    call search('^#\t.*','W')
  712
+  endfor
  713
+  return '.'
  714
+endfunction
  715
+
  716
+function! s:StagePrevious(count)
  717
+  if line('.') == 1 && exists(':CtrlP')
  718
+    return 'CtrlP '.fnameescape(s:repo().tree())
  719
+  else
  720
+    for i in range(a:count)
  721
+      call search('^#\t.*','Wbe')
  722
+    endfor
  723
+    return '.'
  724
+  endif
  725
+endfunction
  726
+
  727
+function! s:StageReloadSeek(target,lnum1,lnum2)
  728
+  let jump = a:target
  729
+  let f = matchstr(getline(a:lnum1-1),'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*')
  730
+  if f !=# '' | let jump = f | endif
  731
+  let f = matchstr(getline(a:lnum2+1),'^#\t\%([[:alpha:] ]\+: *\)\=\zs.*')
  732
+  if f !=# '' | let jump = f | endif
  733
+  silent! edit!
  734
+  1
  735
+  redraw
  736
+  call search('^#\t\%([[:alpha:] ]\+: *\)\=\V'.jump.'\%( ([^()[:digit:]]\+)\)\=\$','W')
  737
+endfunction
  738
+
  739
+function! s:StageDiff(diff) abort
  740
+  let [filename, section] = s:stage_info(line('.'))
  741
+  if filename ==# '' && section ==# 'staged'
  742
+    return 'Git! diff --cached'
  743
+  elseif filename ==# ''
  744
+    return 'Git! diff'
  745
+  elseif filename =~# ' -> '
  746
+    let [old, new] = split(filename,' -> ')
  747
+    execute 'Gedit '.s:fnameescape(':0:'.new)
  748
+    return a:diff.' HEAD:'.s:fnameescape(old)
  749
+  elseif section ==# 'staged'
  750
+    execute 'Gedit '.s:fnameescape(':0:'.filename)
  751
+    return a:diff.' -'
  752
+  else
  753
+    execute 'Gedit '.s:fnameescape('/'.filename)
  754
+    return a:diff
  755
+  endif
  756
+endfunction
  757
+
  758
+function! s:StageDiffEdit() abort
  759
+  let [filename, section] = s:stage_info(line('.'))
  760
+  let arg = (filename ==# '' ? '.' : filename)
  761
+  if section ==# 'staged'
  762
+    return 'Git! diff --cached '.s:shellesc(arg)
  763
+  elseif section ==# 'untracked'
  764
+    let repo = s:repo()
  765
+    call repo.git_chomp_in_tree('add','--intent-to-add',arg)
  766
+    if arg ==# '.'
  767
+      silent! edit!
  768
+      1
  769
+      if !search('^# .*:\n#.*\n# .*"git checkout \|^# Changes not staged for commit:$','W')
  770
+        call search('^# .*:$','W')
  771
+      endif
  772
+    else
  773
+      call s:StageReloadSeek(arg,line('.'),line('.'))
  774
+    endif
  775
+    return ''
  776
+  else
  777
+    return 'Git! diff '.s:shellesc(arg)
  778
+  endif
  779
+endfunction
  780
+
  781
+function! s:StageToggle(lnum1,lnum2) abort
  782
+  try
  783
+    let output = ''
  784
+    for lnum in range(a:lnum1,a:lnum2)
  785
+      let [filename, section] = s:stage_info(lnum)
  786
+      let repo = s:repo()
  787
+      if getline('.') =~# '^# .*:$'
  788
+        if section ==# 'staged'
  789
+          call repo.git_chomp_in_tree('reset','-q')
  790
+          silent! edit!
  791
+          1
  792
+          if !search('^# .*:\n# .*"git add .*\n#\n\|^# Untracked files:$','W')
  793
+            call search('^# .*:$','W')
  794
+          endif
  795
+          return ''
  796
+        elseif section ==# 'unstaged'
  797
+          call repo.git_chomp_in_tree('add','-u')
  798
+          silent! edit!
  799
+          1
  800
+          if !search('^# .*:\n# .*"git add .*\n#\n\|^# Untracked files:$','W')
  801
+            call search('^# .*:$','W')
  802
+          endif
  803
+          return ''
  804
+        else
  805
+          call repo.git_chomp_in_tree('add','.')
  806
+          silent! edit!
  807
+          1
  808
+          call search('^# .*:$','W')
  809
+          return ''
  810
+        endif
  811
+      endif
  812
+      if filename ==# ''
  813
+        continue
  814
+      endif
  815
+      if !exists('first_filename')
  816
+        let first_filename = filename
  817
+      endif
  818
+      execute lnum
  819
+      if filename =~ ' -> '
  820
+        let cmd = ['mv','--'] + reverse(split(filename,' -> '))
  821
+        let filename = cmd[-1]
  822
+      elseif section ==# 'staged'
  823
+        let cmd = ['reset','-q','--',filename]
  824
+      elseif getline(lnum) =~# '^#\tdeleted:'
  825
+        let cmd = ['rm','--',filename]
  826
+      elseif getline(lnum) =~# '^#\tmodified:'
  827
+        let cmd = ['add','--',filename]
  828
+      else
  829
+        let cmd = ['add','-A','--',filename]
  830
+      endif
  831
+      let output .= call(repo.git_chomp_in_tree,cmd,s:repo())."\n"
  832
+    endfor
  833
+    if exists('first_filename')
  834
+      call s:StageReloadSeek(first_filename,a:lnum1,a:lnum2)
  835
+    endif
  836
+    echo s:sub(s:gsub(output,'\n+','\n'),'\n$','')
  837
+  catch /^fugitive:/
  838
+    return 'echoerr v:errmsg'
  839
+  endtry
  840
+  return 'checktime'
  841
+endfunction
  842
+
  843
+function! s:StagePatch(lnum1,lnum2) abort
  844
+  let add = []
  845
+  let reset = []
  846
+
  847
+  for lnum in range(a:lnum1,a:lnum2)
  848
+    let [filename, section] = s:stage_info(lnum)
  849
+    if getline('.') =~# '^# .*:$' && section ==# 'staged'
  850
+      return 'Git reset --patch'
  851
+    elseif getline('.') =~# '^# .*:$' && section ==# 'unstaged'
  852
+      return 'Git add --patch'
  853
+    elseif getline('.') =~# '^# .*:$' && section ==# 'untracked'
  854
+      return 'Git add -N .'
  855
+    elseif filename ==# ''
  856
+      continue
  857
+    endif
  858
+    if !exists('first_filename')
  859
+      let first_filename = filename
  860
+    endif
  861
+    execute lnum