├── autoload └── fugitive.vim ├── doc └── fugitive.txt ├── ftdetect └── fugitive.vim ├── ftplugin └── fugitiveblame.vim ├── plugin └── fugitive.vim └── syntax ├── fugitive.vim └── fugitiveblame.vim /doc/fugitive.txt: -------------------------------------------------------------------------------- 1 | *fugitive.txt* A Git wrapper so awesome, it should be illegal 2 | 3 | Author: Tim Pope 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-:G* 19 | :G [args] Same as :Git, but two characters shorter. 20 | 21 | *fugitive-summary* 22 | :Git With no arguments, bring up a summary window vaguely 23 | akin to git-status. If a summary window is already 24 | open for the current repository, it is focused 25 | instead. Press g? or see |fugitive-maps| for usage. 26 | 27 | *:Git* 28 | :Git {args} Run an arbitrary git command and display any output. 29 | On UNIX this uses a pty and on other platforms it uses 30 | a pipe, which will cause some behavior differences 31 | such as the absence of progress bars. Any file the 32 | command edits (for example, a commit message) will be 33 | loaded into a split window. Closing that window will 34 | resume running the command. A few Git subcommands 35 | have different behavior; these are documented below. 36 | 37 | *:Git!* 38 | :Git! {args} Run an arbitrary git command in the background and 39 | stream the output to the preview window. Requires a 40 | Vim with |setbufline()|. Press CTRL-D during an 41 | interactive :Git invocation to switch to this mode 42 | retroactively. 43 | 44 | *:Git_--paginate* *:Git_-p* 45 | :Git --paginate {args} Run an arbitrary git command, capture output to a temp 46 | :Git -p {args} file, and |:split| that temp file. Pass ++curwin as 47 | the first argument to |:edit| the temp file instead. 48 | A temp file is always used for commands like diff and 49 | log that typically uses a pager, and for any command 50 | that has the pager. Git configuration option set. 51 | 52 | :{range}Git! --paginate {args} 53 | :{range}Git! -p {args} Run an arbitrary git command, and insert the output 54 | after {range} in the current buffer. 55 | 56 | *:Git_blame* 57 | :Git blame [flags] Run git-blame [flags] on the current file and open the 58 | results in a scroll-bound vertical split. The 59 | following maps, which work on the cursor line commit 60 | where sensible, are provided: 61 | 62 | g? show this help 63 | A resize to end of author column 64 | C resize to end of commit column 65 | D resize to end of date/time column 66 | gq close blame, then |:Gedit| to return to work 67 | tree version 68 | close blame, and jump to patch that added line 69 | (or directly to blob for boundary commit) 70 | o jump to patch or blob in horizontal split 71 | O jump to patch or blob in new tab 72 | p jump to patch or blob in preview window 73 | - reblame at commit 74 | 75 | The maps |fugitive_P| and |fugitive_~| are also 76 | supported to reblame on a parent commit, but this is 77 | inherently fragile, as the line being blamed will no 78 | longer exist. The preferred alternative is to use 79 | to open up the commit, select the corresponding 80 | `-` line that you care about, and press twice 81 | more to reblame at that line. Viewing the commit also 82 | gives you additional context as to why the line 83 | changed. 84 | 85 | *g:fugitive_dynamic_colors* 86 | In the GUI or a 256 color terminal, commit hashes will 87 | be highlighted in different colors. To disable this: 88 | > 89 | let g:fugitive_dynamic_colors = 0 90 | < 91 | :[range]Git blame [...] If a range is given, just that part of the file will 92 | :Git blame [...] {file} be blamed, and a horizontal split without 93 | scrollbinding is used. You can also give an arbitrary 94 | filename. 95 | 96 | *:Git_difftool* 97 | :Git[!] difftool [args] Invoke `git diff [args]` and load the changes into the 98 | quickfix list. Each changed hunk gets a separate 99 | quickfix entry unless you pass an option like 100 | --name-only or --name-status. Jumps to the first 101 | change unless [!] is given. 102 | 103 | :Git difftool -y [args] Invoke `git diff [args]`, open each changed file in a 104 | new tab, and invoke |:Gdiffsplit!| against the 105 | appropriate commit. 106 | 107 | *:Git_mergetool* 108 | :Git mergetool [args] Like |:Git_difftool|, but target merge conflicts. 109 | 110 | Wrappers for Vim built-ins ~ 111 | 112 | These all directly map onto a built-in Vim command, and generally have names 113 | that prepend "G" to the command they are wrapping. For example, :Ggrep is G 114 | plus |:grep|. 115 | 116 | *:Ggrep* *:Git_grep* 117 | :Ggrep[!] [args] An approximation of |:grep|[!] with git-grep as 118 | :Git[!] grep -O [args] 'grepprg'. 119 | 120 | :Ggrep[!] --quiet [args] 121 | :Ggrep[!] -q [args] Like |:Ggrep|, but instead of displaying output, open 122 | the quickfix list. 123 | 124 | *:Glgrep* 125 | :Glgrep[!] [args] :Ggrep but for |:lgrep|. 126 | :0Git[!] grep -O [args] 127 | 128 | *:Gclog* 129 | :Gclog[!] [args] Use git-log [args] to load the commit history into the 130 | |quickfix| list. Jumps to the first commit unless [!] 131 | is given. This command wraps |:cfile|. 132 | 133 | The quickfix list can be awkward for many use cases 134 | and exhibits extremely poor performance with larger 135 | data sets. Consider using |:Git| log --oneline 136 | instead. 137 | 138 | :{range}Gclog[!] [args] Use git-log -L to load previous revisions of the given 139 | range of the current file into the |quickfix| list. 140 | The cursor is positioned on the first line of the 141 | first diff hunk for each commit. Use :0Gclog to 142 | target the entire file. 143 | 144 | *:Gllog* 145 | :Gllog [args] Like |:Gclog|, but use the location list instead of the 146 | |quickfix| list. 147 | 148 | *:Gcd* 149 | :Gcd [directory] |:cd| relative to the repository. 150 | 151 | *:Glcd* 152 | :Glcd [directory] |:lcd| relative to the repository. 153 | 154 | *:Gedit* *fugitive-:Ge* 155 | :Gedit [object] |:edit| a |fugitive-object|. 156 | 157 | *:Gsplit* 158 | :Gsplit [object] |:split| a |fugitive-object|. 159 | 160 | *:Gvsplit* 161 | :Gvsplit [object] |:vsplit| a |fugitive-object|. 162 | 163 | *:Gtabedit* 164 | :Gtabedit [object] |:tabedit| a |fugitive-object|. 165 | 166 | *:Gpedit* 167 | :Gpedit [object] |:pedit| a |fugitive-object|. 168 | 169 | *:Gdrop* 170 | :Gdrop [object] |:drop| a |fugitive-object|. 171 | 172 | *:Gread* *fugitive-:Gr* 173 | :Gread [object] Empty the buffer and |:read| a |fugitive-object|. 174 | When the argument is omitted, this is similar to 175 | git-checkout on a work tree file or git-add on a stage 176 | file, but without writing anything to disk. 177 | 178 | :{range}Gread [object] |:read| in a |fugitive-object| after {range}. 179 | 180 | *:Gwrite* *fugitive-:Gw* 181 | :Gwrite Write to the current file's path and stage the results. 182 | When run in a work tree file, it is effectively git 183 | add. Elsewhere, it is effectively git-checkout. A 184 | great deal of effort is expended to behave sensibly 185 | when the work tree or index version of the file is 186 | open in another buffer. 187 | 188 | :Gwrite {path} You can give |:Gwrite| an explicit path of where in 189 | the work tree to write. You can also give a path like 190 | :0:foo.txt or :0:% to write to just that stage in 191 | the index. 192 | 193 | *:Gwq* 194 | :Gwq [path] Like |:Gwrite| followed by |:quit| if the write 195 | succeeded. 196 | 197 | :Gwq! [path] Like |:Gwrite|! followed by |:quit|! if the write 198 | succeeded. 199 | 200 | *:Gdiffsplit* 201 | :Gdiffsplit [object] Perform a |vimdiff| against the given file, or if a 202 | commit is given, the current file in that commit. 203 | With no argument, the version in the index or work 204 | tree is used, and the work tree version is always 205 | placed to the right or bottom, depending on available 206 | width. Use Vim's |do| and |dp| to stage and unstage 207 | changes. 208 | 209 | *:Gdiffsplit!* 210 | :Gdiffsplit! Diff against any and all direct ancestors, retaining 211 | focus on the current window. During a merge conflict, 212 | this is a three-way diff against the "ours" and 213 | "theirs" ancestors. Additional d2o and d3o maps are 214 | provided to obtain the hunk from the "ours" or 215 | "theirs" ancestor, respectively. 216 | 217 | :Gdiffsplit! {object} Like |:Gdiffsplit|, but retain focus on the current 218 | window. 219 | 220 | *:Gvdiffsplit* 221 | :Gvdiffsplit [object] Like |:Gdiffsplit|, but always split vertically. 222 | 223 | *:Ghdiffsplit* 224 | :Gdiffsplit ++novertical [object] 225 | :Ghdiffsplit [object] Like |:Gdiffsplit|, but with "vertical" removed from 226 | 'diffopt'. The split will still be vertical if 227 | combined with |:vertical|. 228 | 229 | Other commands ~ 230 | 231 | These do not directly correspond to any built-in Vim command, and have a 232 | capital letter after the "G" to convey this. For example, the file move 233 | operation has nothing to do with the |:move| built-in, so it is named :GMove, 234 | not :Gmove. 235 | 236 | *:GMove* 237 | :GMove {destination} Wrapper around git-mv that renames the buffer 238 | afterward. Add a ! to pass -f. 239 | 240 | *:GRename* 241 | :GRename {destination} Like |:GMove| but operates relative to the parent 242 | directory of the current file. 243 | 244 | *:GDelete* 245 | :GDelete Wrapper around git-rm that deletes the buffer 246 | afterward. When invoked in an index file, --cached is 247 | passed. Add a ! to pass -f and forcefully discard the 248 | buffer. 249 | 250 | *:GRemove* *:GUnlink* 251 | :GRemove Like |:GDelete|, but keep the (now empty) buffer around. 252 | :GUnlink 253 | 254 | *:GBrowse* 255 | :GBrowse Open the current file, blob, tree, commit, or tag 256 | in your browser at the upstream hosting provider. 257 | Upstream providers can be added by installing an 258 | appropriate Vim plugin. For example, GitHub can be 259 | supported by installing rhubarb.vim, available at 260 | . 261 | 262 | :GBrowse {object} Like :GBrowse, but for a given |fugitive-object|. 263 | 264 | :{range}GBrowse [args] Appends an anchor to the URL that emphasizes the 265 | selected lines. This also forces the URL to include a 266 | commit rather than a branch name so it remains valid 267 | if the file changes. You can give a range of "0" to 268 | force this behavior without including an anchor. 269 | 270 | :GBrowse [...]@{remote} Force using the given remote rather than the remote 271 | for the current branch. The remote is used to 272 | determine which upstream repository to link to. 273 | 274 | :GBrowse {url} Open an arbitrary URL in your browser. 275 | 276 | :[range]GBrowse! [args] Like :GBrowse, but put the URL on the clipboard rather 277 | than opening it. 278 | 279 | MAPS *fugitive-maps* 280 | 281 | These maps are available in both the |fugitive-summary| buffer and Fugitive 282 | object buffers, although not all maps make sense in all buffers. Mappings 283 | that operate on the file or hunk under the cursor are generally available in 284 | visual mode to operate on multiple files or partial hunks. 285 | 286 | *fugitive-staging-maps* 287 | Staging/unstaging maps ~ 288 | 289 | *fugitive_s* 290 | s Stage (add) the file or hunk under the cursor. 291 | 292 | *fugitive_u* 293 | u Unstage (reset) the file or hunk under the cursor. 294 | 295 | *fugitive_-* 296 | - Stage or unstage the file or hunk under the cursor. 297 | 298 | *fugitive_U* 299 | U Unstage everything. 300 | 301 | *fugitive_X* 302 | X Discard the change under the cursor. This uses 303 | `checkout` or `clean` under the hood. A command is 304 | echoed that shows how to undo the change. Consult 305 | `:messages` to see it again. During a merge conflict, 306 | use 2X to call `checkout --ours` or 3X to call 307 | `checkout --theirs` . 308 | 309 | *fugitive_=* 310 | = Toggle an inline diff of the file under the cursor. 311 | 312 | *fugitive_>* 313 | > Insert an inline diff of the file under the cursor. 314 | 315 | *fugitive_<* 316 | < Remove the inline diff of the file under the cursor. 317 | 318 | *fugitive_gI* 319 | gI Open .git/info/exclude in a split and add the file 320 | under the cursor. Use a count to open .gitignore. 321 | 322 | *fugitive_I* 323 | I Invoke |:Git| add --patch or reset --patch on the file 324 | P under the cursor. On untracked files, this instead 325 | calls |:Git| add --intent-to-add. 326 | 327 | *fugitive_d* 328 | Diff maps ~ 329 | *fugitive_dp* 330 | dp Invoke |:Git| diff on the file under the cursor. 331 | Deprecated in favor of inline diffs. 332 | 333 | *fugitive_dd* 334 | dd Perform a |:Gdiffsplit| on the file under the cursor. 335 | 336 | *fugitive_dv* 337 | dv Perform a |:Gvdiffsplit| on the file under the cursor. 338 | 339 | *fugitive_ds* *fugitive_dh* 340 | ds Perform a |:Ghdiffsplit| on the file under the cursor. 341 | dh 342 | 343 | *fugitive_dq* 344 | dq Close all but the currently focused diff buffer, and 345 | invoke |:diffoff|!. 346 | 347 | *fugitive_d?* 348 | d? Show this help. 349 | 350 | *fugitive-navigation-maps* 351 | Navigation maps ~ 352 | 353 | *fugitive_* 354 | Open the file or |fugitive-object| under the cursor. 355 | In a blob, this and similar maps jump to the patch 356 | from the diff where this was added, or where it was 357 | removed if a count was given. If the line is still in 358 | the work tree version, passing a count takes you to 359 | it. 360 | 361 | *fugitive_o* 362 | o Open the file or |fugitive-object| under the cursor in 363 | a new split. 364 | 365 | *fugitive_gO* 366 | gO Open the file or |fugitive-object| under the cursor in 367 | a new vertical split. 368 | 369 | *fugitive_O* 370 | O Open the file or |fugitive-object| under the cursor in 371 | a new tab. 372 | 373 | *fugitive_p* 374 | p Open the file or |fugitive-object| under the cursor in 375 | a preview window. In the status buffer, 1p is 376 | required to bypass the legacy usage instructions. 377 | 378 | *fugitive_~* 379 | ~ Open the current file in the [count]th first ancestor. 380 | 381 | *fugitive_P* 382 | P Open the current file in the [count]th parent. 383 | Experimental: In the "Unpushed" section of the status 384 | buffer, this will populate the command line with a 385 | ":Git push" command for the commit under the cursor. 386 | 387 | *fugitive_C* 388 | C Open the commit containing the current file. 389 | 390 | *fugitive_CTRL-P* *fugitive_(* 391 | ( Jump to the previous file, hunk, or revision. 392 | 393 | *fugitive_CTRL-N* *fugitive_)* 394 | ) Jump to the next file, hunk, or revision. 395 | 396 | *fugitive_[c* 397 | [c Jump to previous hunk, expanding inline diffs 398 | automatically. (This shadows the Vim built-in |[c| 399 | that provides a similar operation in |diff| mode.) 400 | 401 | *fugitive_]c* 402 | ]c Jump to next hunk, expanding inline diffs 403 | automatically. (This shadows the Vim built-in |]c| 404 | that provides a similar operation in |diff| mode.) 405 | 406 | *fugitive_[/* *fugitive_[m* 407 | [/ Jump to previous file, collapsing inline diffs 408 | [m automatically. (Mnemonic: "/" appears in filenames, 409 | "m" appears in "filenames".) 410 | 411 | *fugitive_]/* *fugitive_]m* 412 | ]/ Jump to next file, collapsing inline diffs 413 | ]m automatically. (Mnemonic: "/" appears in filenames, 414 | "m" appears in "filenames".) 415 | 416 | *fugitive_i* 417 | i Jump to the next file or hunk, expanding inline diffs 418 | automatically. 419 | 420 | *fugitive_[[* 421 | [[ Jump [count] sections backward. 422 | 423 | *fugitive_]]* 424 | ]] Jump [count] sections forward. 425 | 426 | *fugitive_[]* 427 | [] Jump [count] section ends backward. 428 | 429 | *fugitive_][* 430 | ][ Jump [count] section ends forward. 431 | 432 | *fugitive_star* 433 | * On the first column of a + or - diff line, search for 434 | the corresponding - or + line. Otherwise, defer to 435 | built-in |star|. 436 | 437 | *fugitive_#* 438 | # Same as "*", but search backward. 439 | 440 | *fugitive_gu* 441 | gu Jump to file [count] in the "Untracked" or "Unstaged" 442 | section. 443 | 444 | *fugitive_gU* 445 | gU Jump to file [count] in the "Unstaged" section. 446 | 447 | *fugitive_gs* 448 | gs Jump to file [count] in the "Staged" section. 449 | 450 | *fugitive_gp* 451 | gp Jump to file [count] in the "Unpushed" section. 452 | 453 | *fugitive_gP* 454 | gP Jump to file [count] in the "Unpulled" section. 455 | 456 | *fugitive_gr* 457 | gr Jump to file [count] in the "Rebasing" section. 458 | 459 | *fugitive_gi* 460 | gi Open .git/info/exclude in a split. Use a count to 461 | open .gitignore. 462 | 463 | *fugitive_c* 464 | Commit maps ~ 465 | 466 | cc Create a commit. 467 | 468 | cvc Create a commit with -v. 469 | 470 | ca Amend the last commit and edit the message. 471 | 472 | cva Amend the last commit with -v. 473 | 474 | ce Amend the last commit without editing the message. 475 | 476 | cw Reword the last commit. 477 | 478 | cW Create an `amend!` commit that rewords the commit 479 | under the cursor. 480 | 481 | cf Create a `fixup!` commit for the commit under the 482 | cursor. 483 | 484 | cF Create a `fixup!` commit for the commit under the 485 | cursor and immediately rebase it. 486 | 487 | cs Create a `squash!` commit for the commit under the 488 | cursor. 489 | 490 | cS Create a `squash!` commit for the commit under the 491 | cursor and immediately rebase it. 492 | 493 | cn Create a `squash!` commit for the commit under the 494 | (formerly cA) cursor and edit the message. 495 | 496 | c Populate command line with ":Git commit ". 497 | 498 | *fugitive_cr* 499 | crc Revert the commit under the cursor. 500 | 501 | crn Revert the commit under the cursor in the index and 502 | work tree, but do not actually commit the changes. 503 | 504 | cr Populate command line with ":Git revert ". 505 | 506 | *fugitive_cm* 507 | cm Populate command line with ":Git merge ". 508 | 509 | c? Show this help. 510 | 511 | *fugitive_cb* 512 | *fugitive_co* 513 | Checkout/branch maps ~ 514 | 515 | coo Check out the commit under the cursor. 516 | 517 | cb Populate command line with ":Git branch ". 518 | 519 | co Populate command line with ":Git checkout ". 520 | 521 | cb? Show this help. 522 | co? 523 | 524 | *fugitive_cz* 525 | Stash maps ~ 526 | 527 | czz Push stash. Pass a [count] of 1 to add 528 | `--include-untracked` or 2 to add `--all`. 529 | 530 | czw Push stash of the work-tree. Like `czz` with 531 | `--keep-index`. 532 | 533 | czs Push stash of the stage. Does not accept a count. 534 | 535 | czA Apply topmost stash, or stash@{count}. 536 | 537 | cza Apply topmost stash, or stash@{count}, preserving the 538 | index. 539 | 540 | czP Pop topmost stash, or stash@{count}. 541 | 542 | czp Pop topmost stash, or stash@{count}, preserving the 543 | index. 544 | 545 | cz Populate command line with ":Git stash ". 546 | 547 | cz? Show this help. 548 | 549 | *fugitive_r* 550 | Rebase maps ~ 551 | 552 | ri Perform an interactive rebase. Uses ancestor of 553 | u commit under cursor as upstream if available. 554 | 555 | rf Perform an autosquash rebase without editing the todo 556 | list. Uses ancestor of commit under cursor as 557 | upstream if available. 558 | 559 | ru Perform an interactive rebase against @{upstream}. 560 | 561 | rp Perform an interactive rebase against @{push}. 562 | 563 | rr Continue the current rebase. 564 | 565 | rs Skip the current commit and continue the current 566 | rebase. 567 | 568 | ra Abort the current rebase. 569 | 570 | re Edit the current rebase todo list. 571 | 572 | rw Perform an interactive rebase with the commit under 573 | the cursor set to `reword`. 574 | 575 | rm Perform an interactive rebase with the commit under 576 | the cursor set to `edit`. 577 | 578 | rd Perform an interactive rebase with the commit under 579 | the cursor set to `drop`. 580 | 581 | r Populate command line with ":Git rebase ". 582 | 583 | r? Show this help. 584 | 585 | *fugitive-misc-maps* 586 | Miscellaneous maps ~ 587 | 588 | *fugitive_gq* *fugitive_q* 589 | gq Close the status buffer. 590 | 591 | *fugitive_.* 592 | . Start a |:| command line with the file under the 593 | cursor prepopulated. 594 | 595 | *fugitive_g?* 596 | g? Show help for |fugitive-maps|. 597 | 598 | *fugitive-global-maps* 599 | Global maps ~ 600 | 601 | *fugitive_c_CTRL-R_CTRL-G* 602 | On the command line, recall the path to the current 603 | |fugitive-object| (that is, a representation of the 604 | object recognized by |:Gedit|). 605 | 606 | *fugitive_y_CTRL-G* 607 | ["x]y Yank the path to the current |fugitive-object|. 608 | 609 | *g:fugitive_no_maps* 610 | Global maps can be disabled with the g:fugitive_no_maps option. 611 | > 612 | let g:fugitive_no_maps = 1 613 | < 614 | SPECIFYING OBJECTS *fugitive-object* *fugitive-revision* 615 | 616 | Fugitive objects are either work tree files or Git revisions as defined in the 617 | "SPECIFYING REVISIONS" section in the git-rev-parse man page, with expansions 618 | inspired by |cmdline-special| layered on top. For commands that accept an 619 | optional object, the default is the file in the index for work tree files and 620 | the work tree file for everything else. Example objects follow. 621 | 622 | Object Meaning ~ 623 | @ The commit referenced by @ aka HEAD 624 | master The commit referenced by master 625 | master^ The parent of the commit referenced by master 626 | master...other The merge base of master and other 627 | master: The tree referenced by master 628 | ./master The file named master in the working directory 629 | :(top)master The file named master in the work tree 630 | Makefile The file named Makefile in the work tree 631 | @^:Makefile The file named Makefile in the parent of HEAD 632 | :Makefile The file named Makefile in the index (writable) 633 | @~2:% The current file in the grandparent of HEAD 634 | :% The current file in the index 635 | :1:% The current file's common ancestor during a conflict 636 | :2:# The alternate file in the target branch during a conflict 637 | :3:#5 The file from buffer #5 in the merged branch during a conflict 638 | ! The commit owning the current file 639 | !:Makefile The file named Makefile in the commit owning the current file 640 | !3^2 The second parent of the commit owning buffer #3 641 | .git/config The repo config file 642 | : The |fugitive-summary| buffer 643 | - A temp file containing the last |:Git| invocation's output 644 | The file or commit under the cursor 645 | 646 | STATUSLINE *fugitive-statusline* 647 | 648 | *FugitiveStatusline()* *fugitive#statusline()* 649 | Add %{FugitiveStatusline()} to your statusline to get an indicator including 650 | the current branch and the currently edited file's commit. If you don't have 651 | a statusline, this one matches the default when 'ruler' is set: 652 | > 653 | set statusline=%<%f\ %h%m%r%{FugitiveStatusline()}%=%-14.(%l,%c%V%)\ %P 654 | < 655 | AUTOCOMMANDS *fugitive-autocommands* 656 | 657 | A handful of |User| |autocommands| are provided to allow extending and 658 | overriding Fugitive behaviors. Example usage: 659 | > 660 | autocmd User FugitiveBlob,FugitiveStageBlob call s:BlobOverrides() 661 | < 662 | *User_FugitiveTag* 663 | FugitiveTag After loading a tag object. 664 | 665 | *User_FugitiveCommit* 666 | FugitiveCommit After loading a commit object. 667 | 668 | *User_FugitiveTree* 669 | FugitiveTree After loading a tree (directory) object. 670 | 671 | *User_FugitiveBlob* 672 | FugitiveBlob After loading a committed blob (file) object. 673 | 674 | *User_FugitiveObject* 675 | FugitiveObject After loading any of the 4 above buffer types. 676 | 677 | *User_FugitiveStageBlob* 678 | FugitiveStageBlob After loading a staged blob (file) object. These 679 | buffers are 'modifiable' and oftentimes don't want the 680 | same behavior as the other buffer types. 681 | 682 | *User_FugitiveIndex* 683 | FugitiveIndex After loading the |fugitive-summary| buffer. 684 | 685 | *User_FugitivePager* 686 | FugitivePager After loading a temp file created by a command like 687 | :Git --paginate or :Git blame. 688 | 689 | *User_FugitiveEditor* 690 | FugitiveEditor After a :Git command (e.g., :Git commit) edits a file 691 | (e.g., the commit message). 692 | 693 | *User_FugitiveChanged* 694 | FugitiveChanged After any event which can potentially change the 695 | repository, for example, any invocation of |:Git|. 696 | Originally intended for expiring caches, but can have 697 | other uses. 698 | 699 | API *fugitive-api* 700 | 701 | Officially supported functions are documented inline in plugin/fugitive.vim. 702 | 703 | DEPRECATIONS *fugitive-deprecated* 704 | 705 | The following commands are deprecated in favor of replacements that adhere to 706 | a new naming scheme. Remember that |:Git| can be shortened to |:G|, so 707 | replacements using it are just one space character longer than the legacy 708 | version. 709 | 710 | *:Gremove* Superseded by |:GRemove|. 711 | *:Gdelete* Superseded by |:GDelete|. 712 | *:Gmove* Superseded by |:GMove|. 713 | *:Grename* Superseded by |:GRename|. 714 | *:Gbrowse* Superseded by |:GBrowse|. 715 | *:Gdiff* Superseded by |:Gdiffsplit| 716 | *:Gsdiff* Superseded by |:Ghdiffsplit| 717 | *:Gvdiff* Superseded by |:Gvdiffsplit| or |:vert| |:Gdiffsplit|. 718 | *:Gblame* Superseded by |:Git_blame|. 719 | *:Gcommit* Superseded by |:Git| commit. 720 | *:Gmerge* Superseded by |:Git| merge and |:Git_mergetool|. 721 | *:Gpull* Superseded by |:Git| pull. 722 | *:Grebase* Superseded by |:Git| rebase. 723 | *:Grevert* Superseded by |:Git| revert. 724 | *:Gpush* Superseded by |:Git| push. 725 | *:Gfetch* Superseded by |:Git| fetch. 726 | *:Glog* Superseded by |:Gclog|. 727 | *:Gstatus* Superseded by |:Git| (with no arguments). 728 | *:Gsplit!* Superseded by |:Git_--paginate|. 729 | *:Gvsplit!* Superseded by :vert Git --paginate. 730 | *:Gtabsplit!* Superseded by :tab Git --paginate. 731 | *:Gpedit!* Superseded by :Git! --paginate. 732 | 733 | *User_Fugitive* 734 | Fugitive used to support `:autocmd User Fugitive` to run an autocommand after 735 | loading any buffer belonging to a Git repository, but this has been phased 736 | out. Instead, one can leverage regular autocommand events like |BufNewFile| 737 | and |BufReadPost|, and check !empty(FugitiveGitDir()) to confirm Fugitive has 738 | found a repository. See also |fugitive-autocommands| for other, more 739 | selective events. 740 | 741 | ABOUT *fugitive-about* 742 | 743 | Grab the latest version or report a bug on GitHub: 744 | 745 | https://github.com/tpope/vim-fugitive 746 | 747 | vim:tw=78:et:ft=help:norl: 748 | -------------------------------------------------------------------------------- /ftdetect/fugitive.vim: -------------------------------------------------------------------------------- 1 | autocmd BufReadPost *.fugitiveblame setfiletype fugitiveblame 2 | -------------------------------------------------------------------------------- /ftplugin/fugitiveblame.vim: -------------------------------------------------------------------------------- 1 | if exists("b:did_ftplugin") || !exists("*FugitiveGitDir") 2 | finish 3 | endif 4 | let b:did_ftplugin = 1 5 | 6 | call fugitive#BlameFileType() 7 | -------------------------------------------------------------------------------- /plugin/fugitive.vim: -------------------------------------------------------------------------------- 1 | " fugitive.vim - A Git wrapper so awesome, it should be illegal 2 | " Maintainer: Tim Pope 3 | " Version: 3.7 4 | " GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim 5 | 6 | if exists('g:loaded_fugitive') 7 | finish 8 | endif 9 | let g:loaded_fugitive = 1 10 | 11 | let s:bad_git_dir = '/$\|^fugitive:' 12 | 13 | " FugitiveGitDir() returns the detected Git dir for the given buffer number, 14 | " or the current buffer if no argument is passed. This will be an empty 15 | " string if no Git dir was found. Use !empty(FugitiveGitDir()) to check if 16 | " Fugitive is active in the current buffer. Do not rely on this for direct 17 | " filesystem access; use FugitiveFind('.git/whatever') instead. 18 | function! FugitiveGitDir(...) abort 19 | if v:version < 704 20 | return '' 21 | elseif !a:0 || type(a:1) == type(0) && a:1 < 0 || a:1 is# get(v:, 'true', -1) 22 | if exists('g:fugitive_event') 23 | return g:fugitive_event 24 | endif 25 | let dir = get(b:, 'git_dir', '') 26 | if empty(dir) && (empty(bufname('')) && &filetype !=# 'netrw' || &buftype =~# '^\%(nofile\|acwrite\|quickfix\|terminal\|prompt\)$') 27 | return FugitiveExtractGitDir(getcwd()) 28 | elseif (!exists('b:git_dir') || b:git_dir =~# s:bad_git_dir) && &buftype =~# '^\%(nowrite\)\=$' 29 | let b:git_dir = FugitiveExtractGitDir(bufnr('')) 30 | return b:git_dir 31 | endif 32 | return dir =~# s:bad_git_dir ? '' : dir 33 | elseif type(a:1) == type(0) && a:1 isnot# 0 34 | if a:1 == bufnr('') && (!exists('b:git_dir') || b:git_dir =~# s:bad_git_dir) && &buftype =~# '^\%(nowrite\)\=$' 35 | let b:git_dir = FugitiveExtractGitDir(a:1) 36 | endif 37 | let dir = getbufvar(a:1, 'git_dir') 38 | return dir =~# s:bad_git_dir ? '' : dir 39 | elseif type(a:1) == type('') 40 | return substitute(s:Slash(a:1), '/$', '', '') 41 | elseif type(a:1) == type({}) 42 | return get(a:1, 'fugitive_dir', get(a:1, 'git_dir', '')) 43 | else 44 | return '' 45 | endif 46 | endfunction 47 | 48 | " FugitiveReal() takes a fugitive:// URL and returns the corresponding path in 49 | " the work tree. This may be useful to get a cleaner path for inclusion in 50 | " the statusline, for example. Note that the file and its parent directories 51 | " are not guaranteed to exist. 52 | " 53 | " This is intended as an abstract API to be used on any "virtual" path. For a 54 | " buffer named foo://bar, check for a function named FooReal(), and if it 55 | " exists, call FooReal("foo://bar"). 56 | function! FugitiveReal(...) abort 57 | let file = a:0 ? a:1 : @% 58 | if type(file) ==# type({}) 59 | let dir = FugitiveGitDir(file) 60 | let tree = s:Tree(dir) 61 | return s:VimSlash(empty(tree) ? dir : tree) 62 | elseif file =~# '^\a\a\+:' || a:0 > 1 63 | return call('fugitive#Real', [file] + a:000[1:-1]) 64 | elseif file =~# '^/\|^\a:\|^$' 65 | return file 66 | else 67 | return fnamemodify(file, ':p' . (file =~# '[\/]$' ? '' : ':s?[\/]$??')) 68 | endif 69 | endfunction 70 | 71 | " FugitiveFind() takes a Fugitive object and returns the appropriate Vim 72 | " buffer name. You can use this to generate Fugitive URLs ("HEAD:README") or 73 | " to get the absolute path to a file in the Git dir (".git/HEAD"), the common 74 | " dir (".git/config"), or the work tree (":(top)Makefile"). 75 | " 76 | " An optional second argument provides the Git dir, or the buffer number of a 77 | " buffer with a Git dir. The default is the current buffer. 78 | function! FugitiveFind(...) abort 79 | if a:0 && (type(a:1) ==# type({}) || type(a:1) ==# type(0)) 80 | return call('fugitive#Find', a:000[1:-1] + [FugitiveGitDir(a:1)]) 81 | else 82 | return fugitive#Find(a:0 ? a:1 : bufnr(''), FugitiveGitDir(a:0 > 1 ? a:2 : -1)) 83 | endif 84 | endfunction 85 | 86 | " FugitiveParse() takes a fugitive:// URL and returns a 2 element list 87 | " containing an object name ("commit:file") and the Git dir. It's effectively 88 | " the inverse of FugitiveFind(). 89 | function! FugitiveParse(...) abort 90 | let path = s:Slash(a:0 ? a:1 : @%) 91 | if path !~# '^fugitive://' 92 | return ['', ''] 93 | endif 94 | let [rev, dir] = fugitive#Parse(path) 95 | if !empty(dir) 96 | return [rev, dir] 97 | endif 98 | throw 'fugitive: invalid Fugitive URL ' . path 99 | endfunction 100 | 101 | " FugitiveGitVersion() queries the version of Git in use. Pass up to 3 102 | " arguments to return a Boolean of whether a certain minimum version is 103 | " available (FugitiveGitVersion(2,3,4) checks for 2.3.4 or higher) or no 104 | " arguments to get a raw string. 105 | function! FugitiveGitVersion(...) abort 106 | return call('fugitive#GitVersion', a:000) 107 | endfunction 108 | 109 | " FugitiveResult() returns an object encapsulating the result of the most 110 | " recent :Git command. Will be empty if no result is available. During a 111 | " User FugitiveChanged event, this is guaranteed to correspond to the :Git 112 | " command that triggered the event, or be empty if :Git was not the trigger. 113 | " Pass in the name of a temp buffer to get the result object for that command 114 | " instead. Contains the following keys: 115 | " 116 | " * "args": List of command arguments, starting with the subcommand. Will be 117 | " empty for usages like :Git --help. 118 | " * "git_dir": Git dir of the relevant repository. 119 | " * "exit_status": The integer exit code of the process. 120 | " * "flags": Flags passed directly to Git, like -c and --help. 121 | " * "file": Path to file containing command output. Not guaranteed to exist, 122 | " so verify with filereadable() before trying to access it. 123 | function! FugitiveResult(...) abort 124 | return call('fugitive#Result', a:000) 125 | endfunction 126 | 127 | " FugitiveExecute() runs Git with a list of arguments and returns a dictionary 128 | " with the following keys: 129 | " 130 | " * "exit_status": The integer exit code of the process. 131 | " * "stdout": The stdout produced by the process, as a list of lines. 132 | " * "stderr": The stdout produced by the process, as a list of lines. 133 | " 134 | " An optional second argument provides the Git dir, or the buffer number of a 135 | " buffer with a Git dir. The default is the current buffer. 136 | " 137 | " An optional final argument is a callback Funcref, for asynchronous 138 | " execution. 139 | function! FugitiveExecute(args, ...) abort 140 | return call('fugitive#Execute', [a:args] + a:000) 141 | endfunction 142 | 143 | " FugitiveShellCommand() turns an array of arguments into a Git command string 144 | " which can be executed with functions like system() and commands like :!. 145 | " Integer arguments will be treated as buffer numbers, and the appropriate 146 | " relative path inserted in their place. 147 | " 148 | " An optional second argument provides the Git dir, or the buffer number of a 149 | " buffer with a Git dir. The default is the current buffer. 150 | function! FugitiveShellCommand(...) abort 151 | return call('fugitive#ShellCommand', a:000) 152 | endfunction 153 | 154 | " FugitiveConfig() get returns an opaque structure that can be passed to other 155 | " FugitiveConfig functions in lieu of a Git directory. This can be faster 156 | " when performing multiple config queries. Do not rely on the internal 157 | " structure of the return value as it is not guaranteed. If you want a full 158 | " dictionary of every config value, use FugitiveConfigGetRegexp('.*'). 159 | " 160 | " An optional argument provides the Git dir, or the buffer number of a 161 | " buffer with a Git dir. The default is the current buffer. Pass a blank 162 | " string to limit to the global config. 163 | function! FugitiveConfig(...) abort 164 | return call('fugitive#Config', a:000) 165 | endfunction 166 | 167 | " FugitiveConfigGet() retrieves a Git configuration value. An optional second 168 | " argument can be either the object returned by FugitiveConfig(), or a Git 169 | " dir or buffer number to be passed along to FugitiveConfig(). 170 | function! FugitiveConfigGet(name, ...) abort 171 | return get(call('FugitiveConfigGetAll', [a:name] + (a:0 ? [a:1] : [])), -1, get(a:, 2, '')) 172 | endfunction 173 | 174 | " FugitiveConfigGetAll() is like FugitiveConfigGet() but returns a list of 175 | " all values. 176 | function! FugitiveConfigGetAll(name, ...) abort 177 | return call('fugitive#ConfigGetAll', [a:name] + a:000) 178 | endfunction 179 | 180 | " FugitiveConfigGetRegexp() retrieves a dictionary of all configuration values 181 | " with a key matching the given pattern. Like git config --get-regexp, but 182 | " using a Vim regexp. Second argument has same semantics as 183 | " FugitiveConfigGet(). 184 | function! FugitiveConfigGetRegexp(pattern, ...) abort 185 | return call('fugitive#ConfigGetRegexp', [a:pattern] + a:000) 186 | endfunction 187 | 188 | " FugitiveRemoteUrl() retrieves the remote URL for the given remote name, 189 | " defaulting to the current branch's remote or "origin" if no argument is 190 | " given. Similar to `git remote get-url`, but also attempts to resolve HTTP 191 | " redirects and SSH host aliases. 192 | " 193 | " An optional second argument provides the Git dir, or the buffer number of a 194 | " buffer with a Git dir. The default is the current buffer. 195 | function! FugitiveRemoteUrl(...) abort 196 | return call('fugitive#RemoteUrl', a:000) 197 | endfunction 198 | 199 | " FugitiveRemote() returns a data structure parsed from the remote URL. 200 | " For example, for remote URL "https://me@example.com:1234/repo.git", the 201 | " returned dictionary will contain the following: 202 | " 203 | " * "scheme": "https" 204 | " * "authority": "user@example.com:1234" 205 | " * "path": "/repo.git" (for SSH URLs this may be a relative path) 206 | " * "pathname": "/repo.git" (always coerced to absolute path) 207 | " * "host": "example.com:1234" 208 | " * "hostname": "example.com" 209 | " * "port": "1234" 210 | " * "user": "me" 211 | " * "path": "/repo.git" 212 | " * "url": "https://me@example.com:1234/repo.git" 213 | function! FugitiveRemote(...) abort 214 | return call('fugitive#Remote', a:000) 215 | endfunction 216 | 217 | " FugitiveDidChange() triggers a FugitiveChanged event and reloads the summary 218 | " buffer for the current or given buffer number's repository. You can also 219 | " give the result of a FugitiveExecute() and that context will be made 220 | " available inside the FugitiveChanged() event. 221 | " 222 | " Passing the special argument 0 (the number zero) softly expires summary 223 | " buffers for all repositories. This can be used after a call to system() 224 | " with unclear implications. 225 | function! FugitiveDidChange(...) abort 226 | return call('fugitive#DidChange', a:000) 227 | endfunction 228 | 229 | " FugitiveHead() retrieves the name of the current branch. If the current HEAD 230 | " is detached, FugitiveHead() will return the empty string, unless the 231 | " optional argument is given, in which case the hash of the current commit 232 | " will be truncated to the given number of characters. 233 | " 234 | " An optional second argument provides the Git dir, or the buffer number of a 235 | " buffer with a Git dir. The default is the current buffer. 236 | function! FugitiveHead(...) abort 237 | if a:0 && (type(a:1) ==# type({}) || type(a:1) ==# type('') && a:1 !~# '^\d\+$') 238 | let dir = FugitiveGitDir(a:1) 239 | let arg = get(a:, 2, 0) 240 | elseif a:0 > 1 241 | let dir = FugitiveGitDir(a:2) 242 | let arg = a:1 243 | else 244 | let dir = FugitiveGitDir() 245 | let arg = get(a:, 1, 0) 246 | endif 247 | if empty(dir) 248 | return '' 249 | endif 250 | return fugitive#Head(arg, dir) 251 | endfunction 252 | 253 | function! FugitivePath(...) abort 254 | if a:0 > 2 && type(a:1) ==# type({}) 255 | return fugitive#Path(a:2, a:3, FugitiveGitDir(a:1)) 256 | elseif a:0 && type(a:1) ==# type({}) 257 | return FugitiveReal(a:0 > 1 ? a:2 : @%) 258 | elseif a:0 > 1 259 | return fugitive#Path(a:1, a:2, FugitiveGitDir(a:0 > 2 ? a:3 : -1)) 260 | else 261 | return FugitiveReal(a:0 ? a:1 : @%) 262 | endif 263 | endfunction 264 | 265 | function! FugitiveStatusline(...) abort 266 | if empty(FugitiveGitDir(bufnr(''))) 267 | return '' 268 | endif 269 | return fugitive#Statusline() 270 | endfunction 271 | 272 | let s:resolved_git_dirs = {} 273 | function! FugitiveActualDir(...) abort 274 | let dir = call('FugitiveGitDir', a:000) 275 | if empty(dir) 276 | return '' 277 | endif 278 | if !has_key(s:resolved_git_dirs, dir) 279 | let s:resolved_git_dirs[dir] = s:ResolveGitDir(dir) 280 | endif 281 | return empty(s:resolved_git_dirs[dir]) ? dir : s:resolved_git_dirs[dir] 282 | endfunction 283 | 284 | let s:commondirs = {} 285 | function! FugitiveCommonDir(...) abort 286 | let dir = call('FugitiveActualDir', a:000) 287 | if empty(dir) 288 | return '' 289 | endif 290 | if has_key(s:commondirs, dir) 291 | return s:commondirs[dir] 292 | endif 293 | if getfsize(dir . '/HEAD') >= 10 294 | let cdir = get(s:ReadFile(dir . '/commondir', 1), 0, '') 295 | if cdir =~# '^/\|^\a:/' 296 | let s:commondirs[dir] = s:Slash(FugitiveVimPath(cdir)) 297 | elseif len(cdir) 298 | let s:commondirs[dir] = simplify(dir . '/' . cdir) 299 | else 300 | let s:commondirs[dir] = dir 301 | endif 302 | else 303 | let s:commondirs[dir] = dir 304 | endif 305 | return s:commondirs[dir] 306 | endfunction 307 | 308 | function! FugitiveWorkTree(...) abort 309 | let tree = s:Tree(FugitiveGitDir(a:0 ? a:1 : -1)) 310 | if tree isnot# 0 || a:0 > 1 311 | return tree 312 | else 313 | return '' 314 | endif 315 | endfunction 316 | 317 | function! FugitiveIsGitDir(...) abort 318 | if !a:0 || type(a:1) !=# type('') 319 | return !empty(call('FugitiveGitDir', a:000)) 320 | endif 321 | let path = substitute(a:1, '[\/]$', '', '') . '/' 322 | return len(path) && getfsize(path.'HEAD') > 10 && ( 323 | \ isdirectory(path.'objects') && isdirectory(path.'refs') || 324 | \ getftype(path.'commondir') ==# 'file') 325 | endfunction 326 | 327 | function! s:ReadFile(path, line_count) abort 328 | if v:version < 800 && !filereadable(a:path) 329 | return [] 330 | endif 331 | try 332 | return readfile(a:path, 'b', a:line_count) 333 | catch 334 | return [] 335 | endtry 336 | endfunction 337 | 338 | let s:worktree_for_dir = {} 339 | let s:dir_for_worktree = {} 340 | function! s:Tree(path) abort 341 | if a:path =~# '/\.git$' 342 | return len(a:path) ==# 5 ? '/' : a:path[0:-6] 343 | elseif a:path ==# '' 344 | return '' 345 | endif 346 | let dir = FugitiveActualDir(a:path) 347 | if !has_key(s:worktree_for_dir, dir) 348 | let s:worktree_for_dir[dir] = '' 349 | let ext_wtc_pat = 'v:val =~# "^\\s*worktreeConfig *= *\\%(true\\|yes\\|on\\|1\\) *$"' 350 | let config = s:ReadFile(dir . '/config', 50) 351 | if len(config) 352 | let ext_wtc_config = filter(copy(config), ext_wtc_pat) 353 | if len(ext_wtc_config) == 1 && filereadable(dir . '/config.worktree') 354 | let config += s:ReadFile(dir . '/config.worktree', 50) 355 | endif 356 | else 357 | let worktree = fnamemodify(FugitiveVimPath(get(s:ReadFile(dir . '/gitdir', 1), '0', '')), ':h') 358 | if worktree ==# '.' 359 | unlet! worktree 360 | endif 361 | if len(filter(s:ReadFile(FugitiveCommonDir(dir) . '/config', 50), ext_wtc_pat)) 362 | let config = s:ReadFile(dir . '/config.worktree', 50) 363 | endif 364 | endif 365 | if len(config) 366 | let wt_config = filter(copy(config), 'v:val =~# "^\\s*worktree *="') 367 | if len(wt_config) 368 | let worktree = FugitiveVimPath(matchstr(wt_config[0], '= *\zs.*')) 369 | elseif !exists('worktree') 370 | call filter(config,'v:val =~# "^\\s*bare *= *true *$"') 371 | if empty(config) 372 | let s:worktree_for_dir[dir] = 0 373 | endif 374 | endif 375 | endif 376 | if exists('worktree') 377 | let s:worktree_for_dir[dir] = s:Slash(resolve(worktree)) 378 | let s:dir_for_worktree[s:worktree_for_dir[dir]] = dir 379 | endif 380 | endif 381 | if s:worktree_for_dir[dir] =~# '^\.' 382 | return simplify(dir . '/' . s:worktree_for_dir[dir]) 383 | else 384 | return s:worktree_for_dir[dir] 385 | endif 386 | endfunction 387 | 388 | function! s:CeilingDirectories() abort 389 | if !exists('s:ceiling_directories') 390 | let s:ceiling_directories = [] 391 | let resolve = 1 392 | for dir in split($GIT_CEILING_DIRECTORIES, has('win32') ? ';' : ':', 1) 393 | if empty(dir) 394 | let resolve = 0 395 | elseif resolve 396 | call add(s:ceiling_directories, s:Slash(resolve(dir))) 397 | else 398 | call add(s:ceiling_directories, s:Slash(dir)) 399 | endif 400 | endfor 401 | endif 402 | return s:ceiling_directories + get(g:, 'ceiling_directories', [s:Slash(fnamemodify(expand('~'), ':h'))]) 403 | endfunction 404 | 405 | function! s:ResolveGitDir(git_dir) abort 406 | let type = getftype(a:git_dir) 407 | if type ==# 'dir' && FugitiveIsGitDir(a:git_dir) 408 | return a:git_dir 409 | elseif type ==# 'link' && FugitiveIsGitDir(a:git_dir) 410 | return resolve(a:git_dir) 411 | elseif type !=# '' 412 | let line = get(s:ReadFile(a:git_dir, 1), 0, '') 413 | let file_dir = s:Slash(FugitiveVimPath(matchstr(line, '^gitdir: \zs.*'))) 414 | if file_dir !~# '^/\|^\a:\|^$' && a:git_dir =~# '/\.git$' && FugitiveIsGitDir(a:git_dir[0:-5] . file_dir) 415 | return simplify(a:git_dir[0:-5] . file_dir) 416 | elseif file_dir =~# '^/\|^\a:' && FugitiveIsGitDir(file_dir) 417 | return file_dir 418 | endif 419 | endif 420 | return '' 421 | endfunction 422 | 423 | function! FugitiveExtractGitDir(path) abort 424 | if type(a:path) ==# type({}) 425 | return get(a:path, 'fugitive_dir', get(a:path, 'git_dir', '')) 426 | elseif type(a:path) == type(0) 427 | let path = s:Slash(a:path > 0 ? bufname(a:path) : bufname('')) 428 | if getbufvar(a:path, '&filetype') ==# 'netrw' 429 | let path = s:Slash(getbufvar(a:path, 'netrw_curdir', path)) 430 | endif 431 | else 432 | let path = s:Slash(a:path) 433 | endif 434 | if path =~# '^fugitive://' 435 | return fugitive#Parse(path)[1] 436 | elseif empty(path) 437 | return '' 438 | endif 439 | let pre = substitute(matchstr(path, '^\a\a\+\ze:'), '^.', '\u&', '') 440 | if len(pre) && exists('*' . pre . 'Real') 441 | let path = {pre}Real(path) 442 | endif 443 | let root = s:Slash(fnamemodify(path, ':p:h')) 444 | let previous = "" 445 | let env_git_dir = len($GIT_DIR) ? s:Slash(simplify(fnamemodify(FugitiveVimPath($GIT_DIR), ':p:s?[\/]$??'))) : '' 446 | call s:Tree(env_git_dir) 447 | let ceiling_directories = s:CeilingDirectories() 448 | while root !=# previous && root !~# '^$\|^//[^/]*$' 449 | if index(ceiling_directories, root) >= 0 450 | break 451 | endif 452 | if root ==# $GIT_WORK_TREE && FugitiveIsGitDir(env_git_dir) 453 | return env_git_dir 454 | elseif has_key(s:dir_for_worktree, root) 455 | return s:dir_for_worktree[root] 456 | endif 457 | let dir = substitute(root, '[\/]$', '', '') . '/.git' 458 | let resolved = s:ResolveGitDir(dir) 459 | if !empty(resolved) 460 | let s:resolved_git_dirs[dir] = resolved 461 | return dir is# resolved || s:Tree(resolved) is# 0 ? dir : resolved 462 | elseif FugitiveIsGitDir(root) 463 | let s:resolved_git_dirs[root] = root 464 | return root 465 | endif 466 | let previous = root 467 | let root = fnamemodify(root, ':h') 468 | endwhile 469 | return '' 470 | endfunction 471 | 472 | function! FugitiveDetect(...) abort 473 | if v:version < 704 474 | return '' 475 | endif 476 | if exists('b:git_dir') && b:git_dir =~# '^$\|' . s:bad_git_dir 477 | unlet b:git_dir 478 | endif 479 | if !exists('b:git_dir') 480 | let b:git_dir = FugitiveExtractGitDir(a:0 ? a:1 : bufnr('')) 481 | endif 482 | return '' 483 | endfunction 484 | 485 | function! FugitiveGitPath(path) abort 486 | return s:Slash(a:path) 487 | endfunction 488 | 489 | if exists('+shellslash') 490 | 491 | function! s:Slash(path) abort 492 | return tr(a:path, '\', '/') 493 | endfunction 494 | 495 | function! s:VimSlash(path) abort 496 | return tr(a:path, '\/', &shellslash ? '//' : '\\') 497 | endfunction 498 | 499 | function FugitiveVimPath(path) abort 500 | return tr(a:path, '\/', &shellslash ? '//' : '\\') 501 | endfunction 502 | 503 | else 504 | 505 | function! s:Slash(path) abort 506 | return a:path 507 | endfunction 508 | 509 | function! s:VimSlash(path) abort 510 | return a:path 511 | endfunction 512 | 513 | if has('win32unix') && filereadable('/git-bash.exe') 514 | function! FugitiveVimPath(path) abort 515 | return substitute(a:path, '^\(\a\):', '/\l\1', '') 516 | endfunction 517 | else 518 | function! FugitiveVimPath(path) abort 519 | return a:path 520 | endfunction 521 | endif 522 | 523 | endif 524 | 525 | function! s:ProjectionistDetect() abort 526 | let file = s:Slash(get(g:, 'projectionist_file', '')) 527 | let dir = FugitiveExtractGitDir(file) 528 | let base = matchstr(file, '^fugitive://.\{-\}//\x\+') 529 | if empty(base) 530 | let base = s:Tree(dir) 531 | endif 532 | if !empty(base) 533 | if exists('+shellslash') && !&shellslash 534 | let base = tr(base, '/', '\') 535 | endif 536 | let file = FugitiveFind('.git/info/projections.json', dir) 537 | if filereadable(file) 538 | call projectionist#append(base, file) 539 | endif 540 | endif 541 | endfunction 542 | 543 | let s:addr_other = has('patch-8.1.560') || has('nvim-0.5.0') ? '-addr=other' : '' 544 | let s:addr_tabs = has('patch-7.4.542') ? '-addr=tabs' : '' 545 | let s:addr_wins = has('patch-7.4.542') ? '-addr=windows' : '' 546 | 547 | if exists(':G') != 2 548 | command! -bang -nargs=? -range=-1 -complete=customlist,fugitive#Complete G exe fugitive#Command(, , +"", 0, "", ) 549 | endif 550 | command! -bang -nargs=? -range=-1 -complete=customlist,fugitive#Complete Git exe fugitive#Command(, , +"", 0, "", ) 551 | 552 | if exists(':Gstatus') != 2 && get(g:, 'fugitive_legacy_commands', 0) 553 | exe 'command! -bang -bar -range=-1' s:addr_other 'Gstatus exe fugitive#Command(, , +"", 0, "", )' 554 | \ '|echohl WarningMSG|echomsg ":Gstatus is deprecated in favor of :Git (with no arguments)"|echohl NONE' 555 | endif 556 | 557 | for s:cmd in ['Commit', 'Revert', 'Merge', 'Rebase', 'Pull', 'Push', 'Fetch', 'Blame'] 558 | if exists(':G' . tolower(s:cmd)) != 2 && get(g:, 'fugitive_legacy_commands', 0) 559 | exe 'command! -bang -nargs=? -range=-1 -complete=customlist,fugitive#' . s:cmd . 'Complete G' . tolower(s:cmd) 560 | \ 'echohl WarningMSG|echomsg ":G' . tolower(s:cmd) . ' is deprecated in favor of :Git ' . tolower(s:cmd) . '"|echohl NONE|' 561 | \ 'exe fugitive#Command(, , +"", 0, "", "' . tolower(s:cmd) . ' " . )' 562 | endif 563 | endfor 564 | unlet s:cmd 565 | 566 | exe "command! -bar -bang -nargs=? -complete=customlist,fugitive#CdComplete Gcd exe fugitive#Cd(, 0)" 567 | exe "command! -bar -bang -nargs=? -complete=customlist,fugitive#CdComplete Glcd exe fugitive#Cd(, 1)" 568 | 569 | exe 'command! -bang -nargs=? -range=-1' s:addr_wins '-complete=customlist,fugitive#GrepComplete Ggrep exe fugitive#GrepCommand(, , +"", 0, "", )' 570 | exe 'command! -bang -nargs=? -range=-1' s:addr_wins '-complete=customlist,fugitive#GrepComplete Glgrep exe fugitive#GrepCommand(0, > 0 ? : 0, +"", 0, "", )' 571 | 572 | exe 'command! -bang -nargs=? -range=-1 -complete=customlist,fugitive#LogComplete Gclog :exe fugitive#LogCommand(,,+"",0,"",, "c")' 573 | exe 'command! -bang -nargs=? -range=-1 -complete=customlist,fugitive#LogComplete GcLog :exe fugitive#LogCommand(,,+"",0,"",, "c")' 574 | exe 'command! -bang -nargs=? -range=-1 -complete=customlist,fugitive#LogComplete Gllog :exe fugitive#LogCommand(,,+"",0,"",, "l")' 575 | exe 'command! -bang -nargs=? -range=-1 -complete=customlist,fugitive#LogComplete GlLog :exe fugitive#LogCommand(,,+"",0,"",, "l")' 576 | 577 | exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Ge exe fugitive#Open("edit", 0, "", )' 578 | exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gedit exe fugitive#Open("edit", 0, "", )' 579 | exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gpedit exe fugitive#Open("pedit", 0, "", )' 580 | exe 'command! -bar -bang -nargs=* -range=-1' s:addr_other '-complete=customlist,fugitive#EditComplete Gsplit exe fugitive#Open(( > 0 ? : "").( ? "split" : "edit"), 0, "", )' 581 | exe 'command! -bar -bang -nargs=* -range=-1' s:addr_other '-complete=customlist,fugitive#EditComplete Gvsplit exe fugitive#Open(( > 0 ? : "").( ? "vsplit" : "edit!"), 0, "", )' 582 | exe 'command! -bar -bang -nargs=* -range=-1' s:addr_tabs '-complete=customlist,fugitive#EditComplete Gtabedit exe fugitive#Open(( >= 0 ? : "")."tabedit", 0, "", )' 583 | exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gdrop exe fugitive#DropCommand(, , +"", 0, "", )' 584 | 585 | if exists(':Gr') != 2 586 | exe 'command! -bar -bang -nargs=* -range=-1 -complete=customlist,fugitive#ReadComplete Gr exe fugitive#ReadCommand(, , +"", 0, "", )' 587 | endif 588 | exe 'command! -bar -bang -nargs=* -range=-1 -complete=customlist,fugitive#ReadComplete Gread exe fugitive#ReadCommand(, , +"", 0, "", )' 589 | 590 | exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gdiffsplit exe fugitive#Diffsplit(1, 0, "", )' 591 | exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Ghdiffsplit exe fugitive#Diffsplit(0, 0, "", )' 592 | exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gvdiffsplit exe fugitive#Diffsplit(0, 0, "vertical ", )' 593 | 594 | exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gw exe fugitive#WriteCommand(, , +"", 0, "", )' 595 | exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gwrite exe fugitive#WriteCommand(, , +"", 0, "", )' 596 | exe 'command! -bar -bang -nargs=* -complete=customlist,fugitive#EditComplete Gwq exe fugitive#WqCommand( , , +"", 0, "", )' 597 | 598 | exe 'command! -bar -bang -nargs=0 GRemove exe fugitive#RemoveCommand(, , +"", 0, "", )' 599 | exe 'command! -bar -bang -nargs=0 GUnlink exe fugitive#UnlinkCommand(, , +"", 0, "", )' 600 | exe 'command! -bar -bang -nargs=0 GDelete exe fugitive#DeleteCommand(, , +"", 0, "", )' 601 | exe 'command! -bar -bang -nargs=1 -complete=customlist,fugitive#CompleteObject GMove exe fugitive#MoveCommand( , , +"", 0, "", )' 602 | exe 'command! -bar -bang -nargs=1 -complete=customlist,fugitive#RenameComplete GRename exe fugitive#RenameCommand(, , +"", 0, "", )' 603 | if exists(':Gremove') != 2 && get(g:, 'fugitive_legacy_commands', 0) 604 | exe 'command! -bar -bang -nargs=0 Gremove exe fugitive#RemoveCommand(, , +"", 0, "", )' 605 | \ '|echohl WarningMSG|echomsg ":Gremove is deprecated in favor of :GRemove"|echohl NONE' 606 | elseif exists(':Gremove') != 2 && !exists('g:fugitive_legacy_commands') 607 | exe 'command! -bar -bang -nargs=0 Gremove echoerr ":Gremove has been removed in favor of :GRemove"' 608 | endif 609 | if exists(':Gdelete') != 2 && get(g:, 'fugitive_legacy_commands', 0) 610 | exe 'command! -bar -bang -nargs=0 Gdelete exe fugitive#DeleteCommand(, , +"", 0, "", )' 611 | \ '|echohl WarningMSG|echomsg ":Gdelete is deprecated in favor of :GDelete"|echohl NONE' 612 | elseif exists(':Gdelete') != 2 && !exists('g:fugitive_legacy_commands') 613 | exe 'command! -bar -bang -nargs=0 Gdelete echoerr ":Gdelete has been removed in favor of :GDelete"' 614 | endif 615 | if exists(':Gmove') != 2 && get(g:, 'fugitive_legacy_commands', 0) 616 | exe 'command! -bar -bang -nargs=1 -complete=customlist,fugitive#CompleteObject Gmove exe fugitive#MoveCommand( , , +"", 0, "", )' 617 | \ '|echohl WarningMSG|echomsg ":Gmove is deprecated in favor of :GMove"|echohl NONE' 618 | elseif exists(':Gmove') != 2 && !exists('g:fugitive_legacy_commands') 619 | exe 'command! -bar -bang -nargs=? -complete=customlist,fugitive#CompleteObject Gmove' 620 | \ 'echoerr ":Gmove has been removed in favor of :GMove"' 621 | endif 622 | if exists(':Grename') != 2 && get(g:, 'fugitive_legacy_commands', 0) 623 | exe 'command! -bar -bang -nargs=1 -complete=customlist,fugitive#RenameComplete Grename exe fugitive#RenameCommand(, , +"", 0, "", )' 624 | \ '|echohl WarningMSG|echomsg ":Grename is deprecated in favor of :GRename"|echohl NONE' 625 | elseif exists(':Grename') != 2 && !exists('g:fugitive_legacy_commands') 626 | exe 'command! -bar -bang -nargs=? -complete=customlist,fugitive#RenameComplete Grename' 627 | \ 'echoerr ":Grename has been removed in favor of :GRename"' 628 | endif 629 | 630 | exe 'command! -bar -bang -range=-1 -nargs=* -complete=customlist,fugitive#CompleteObject GBrowse exe fugitive#BrowseCommand(, , +"", 0, "", )' 631 | if exists(':Gbrowse') != 2 && get(g:, 'fugitive_legacy_commands', 0) 632 | exe 'command! -bar -bang -range=-1 -nargs=* -complete=customlist,fugitive#CompleteObject Gbrowse exe fugitive#BrowseCommand(, , +"", 0, "", )' 633 | \ '|if 1|redraw!|endif|echohl WarningMSG|echomsg ":Gbrowse is deprecated in favor of :GBrowse"|echohl NONE' 634 | elseif exists(':Gbrowse') != 2 && !exists('g:fugitive_legacy_commands') 635 | exe 'command! -bar -bang -range=-1 -nargs=* -complete=customlist,fugitive#CompleteObject Gbrowse' 636 | \ 'echoerr ":Gbrowse has been removed in favor of :GBrowse"' 637 | endif 638 | 639 | if v:version < 704 640 | finish 641 | endif 642 | 643 | let g:io_fugitive = { 644 | \ 'simplify': function('fugitive#simplify'), 645 | \ 'resolve': function('fugitive#resolve'), 646 | \ 'getftime': function('fugitive#getftime'), 647 | \ 'getfsize': function('fugitive#getfsize'), 648 | \ 'getftype': function('fugitive#getftype'), 649 | \ 'filereadable': function('fugitive#filereadable'), 650 | \ 'filewritable': function('fugitive#filewritable'), 651 | \ 'isdirectory': function('fugitive#isdirectory'), 652 | \ 'getfperm': function('fugitive#getfperm'), 653 | \ 'setfperm': function('fugitive#setfperm'), 654 | \ 'readfile': function('fugitive#readfile'), 655 | \ 'writefile': function('fugitive#writefile'), 656 | \ 'glob': function('fugitive#glob'), 657 | \ 'delete': function('fugitive#delete'), 658 | \ 'Real': function('FugitiveReal')} 659 | 660 | augroup fugitive 661 | autocmd! 662 | 663 | autocmd BufNewFile,BufReadPost * 664 | \ if exists('b:git_dir') && b:git_dir =~# '^$\|' . s:bad_git_dir | 665 | \ unlet b:git_dir | 666 | \ endif 667 | autocmd FileType netrw 668 | \ if exists('b:git_dir') && b:git_dir =~# '^$\|' . s:bad_git_dir | 669 | \ unlet b:git_dir | 670 | \ endif 671 | autocmd BufFilePost * unlet! b:git_dir 672 | 673 | autocmd FileType git 674 | \ call fugitive#MapCfile() 675 | autocmd FileType gitcommit 676 | \ call fugitive#MapCfile('fugitive#MessageCfile()') 677 | autocmd FileType git,gitcommit 678 | \ if &foldtext ==# 'foldtext()' | 679 | \ setlocal foldtext=fugitive#Foldtext() | 680 | \ endif 681 | autocmd FileType fugitive 682 | \ call fugitive#MapCfile('fugitive#PorcelainCfile()') 683 | autocmd FileType gitrebase 684 | \ let &l:include = '^\%(pick\|squash\|edit\|reword\|fixup\|drop\|[pserfd]\)\>' | 685 | \ if &l:includeexpr !~# 'Fugitive' | 686 | \ let &l:includeexpr = 'v:fname =~# ''^\x\{4,\}$'' && len(FugitiveGitDir()) ? FugitiveFind(v:fname) : ' . 687 | \ (len(&l:includeexpr) ? &l:includeexpr : 'v:fname') | 688 | \ endif | 689 | \ let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') . '|setl inex= inc=' 690 | 691 | autocmd BufReadCmd index{,.lock} nested 692 | \ if FugitiveIsGitDir(expand(':p:h')) | 693 | \ let b:git_dir = s:Slash(expand(':p:h')) | 694 | \ exe fugitive#BufReadStatus(v:cmdbang) | 695 | \ echohl WarningMSG | 696 | \ echo "fugitive: Direct editing of .git/" . expand('%:t') . " is deprecated" | 697 | \ echohl NONE | 698 | \ elseif filereadable(expand('')) | 699 | \ silent doautocmd BufReadPre | 700 | \ keepalt noautocmd read | 701 | \ silent 1delete_ | 702 | \ silent doautocmd BufReadPost | 703 | \ else | 704 | \ silent doautocmd BufNewFile | 705 | \ endif 706 | 707 | autocmd BufReadCmd fugitive://* nested exe fugitive#BufReadCmd() | 708 | \ if &path =~# '^\.\%(,\|$\)' | 709 | \ let &l:path = substitute(&path, '^\.,\=', '', '') | 710 | \ endif 711 | autocmd BufWriteCmd fugitive://* nested exe fugitive#BufWriteCmd() 712 | autocmd FileReadCmd fugitive://* nested exe fugitive#FileReadCmd() 713 | autocmd FileWriteCmd fugitive://* nested exe fugitive#FileWriteCmd() 714 | if exists('##SourceCmd') 715 | autocmd SourceCmd fugitive://* nested exe fugitive#SourceCmd() 716 | endif 717 | 718 | autocmd User Flags call Hoist('buffer', function('FugitiveStatusline')) 719 | 720 | autocmd User ProjectionistDetect call s:ProjectionistDetect() 721 | augroup END 722 | 723 | nmap