Move vim gittab commands to a plugin
dblume

dblume commited on 2023-12-16 18:37:40
Showing 5 changed files, with 422 additions and 196 deletions.

... ...
@@ -0,0 +1,19 @@
1
+Copyright (c) 2023, David Blume
2
+
3
+Permission is hereby granted, free of charge, to any person obtaining
4
+a copy of this software and associated documentation files (the "Software"),
5
+to deal in the Software without restriction, including without limitation
6
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
+and/or sell copies of the Software, and to permit persons to whom the
8
+Software is furnished to do so, subject to the following conditions:
9
+
10
+The above copyright notice and this permission notice shall be included
11
+in all copies or substantial portions of the Software.
12
+
13
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19
+DEALINGS IN THE SOFTWARE.
... ...
@@ -0,0 +1,98 @@
1
+[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/dblume/gittab/main/LICENSE)
2
+![vim8](https://img.shields.io/badge/vim-8.x-green.svg)
3
+
4
+## Vim Git-Tab
5
+
6
+These are handy context-sensitive Git commands in Vim that load the results in
7
+a new tab. The commands automatically infer what you want from the current
8
+filename and whether there's a commit hash under the cursor.
9
+
10
+These commands are for investigating the history of files. The supported
11
+commands are:
12
+
13
+* `Blame`
14
+* `Log`
15
+* `Show` and `ShowFile`
16
+* `Diff`
17
+
18
+If you want to do more with Git while in Vim, consider Tim Pope's [vim-fugitive](https://github.com/tpope/vim-fugitive).
19
+
20
+## Installation
21
+
22
+Install into Vim's built-in package support:
23
+
24
+    mkdir -p ~/.vim/pack/plugins/start
25
+    cd ~/.vim/pack/plugins/start
26
+    git clone --filter=blob:none -b main --single-branch https://github.com/dblume/gittab
27
+    vim -u NONE -c "helptags gittab/doc" -c q
28
+
29
+## The Commands
30
+
31
+When you're browsing a file in a Git repository, these commands provide a very
32
+simple and convenient flow for digging into their history.
33
+
34
+All you have to know is Blame, Log, Show, and Diff.
35
+
36
+#### :Blame
37
+
38
+When you're on a regular file or a `:ShowFile` buffer, opens up a `git blame` 
39
+buffer in a new tab for the file at that commit, and positions the
40
+cursor at the same relative spot.
41
+
42
+Example, run Blame on a `:ShowFile` buffer named "git show 1234abcd:README.md",
43
+and you get a `:Blame` buffer named "git blame 1234abcd -- README.md".
44
+
45
+#### :Log
46
+
47
+When you're on a regular file or a `:ShowFile` buffer, opens up a `git log`
48
+buffer in a new tab. By default, runs log as:
49
+
50
+    git log --no-color --graph --date=short --pretty="format:%h %ad %s %an %d"
51
+
52
+And you can pass in additional Git arguments like `--all`.
53
+
54
+    :Log --all
55
+
56
+The reason Log defaults to one-line logs is because `:Show` and `:Diff` are so
57
+easy to use to dive in deeper to the individual commits.
58
+
59
+Handy arguments are `--all`, `--merges`, `--date-order`, `--first-parent`, and
60
+`--ancestry-path`.
61
+
62
+#### :Show and :ShowFile
63
+
64
+These require the cursor to be positioned on a hash, so you'd most likely be
65
+in a `:Log` or `:Blame` buffer when you want to use these commands.
66
+
67
+When you're on a `:Blame` buffer, a `:Log` buffer, **`:Show`** opens a
68
+buffer in a new tab that shows the full commit message for the hash under the
69
+cursor.
70
+
71
+**`:ShowFile`** opens a buffer in a new tab that shows the contents of the file
72
+at the hash under the cursor.
73
+
74
+Ex., If the cursor is on "1234abcd" on `:Blame` buffer `git blame -- README.md`
75
+then:
76
+
77
+| Command   | Resultant Buffer |
78
+| ---       | ---              |
79
+| :Show     | git show 1234abcd -- README.md |
80
+| :ShowFile | git show 1234abcd:README.md |
81
+
82
+#### :Diff
83
+
84
+If you're on a regular file that's different from HEAD, `:Diff` will perform a
85
+`git diff` on the file from HEAD. If it's the same as HEAD, then `:Diff` will
86
+perform a `git diff` against that file's previous commit.
87
+
88
+If the cursor is on a commit hash (as available on :Blame, :Log, and :Show 
89
+buffers), then `:Diff` will perform a diff against the previous commit to that
90
+one.
91
+
92
+## Is it any good?
93
+
94
+[Yes](https://news.ycombinator.com/item?id=3067434).
95
+
96
+## Licence
97
+
98
+This software uses the [MIT License](https://raw.githubusercontent.com/dblume/gittab/main/LICENSE.txt)
... ...
@@ -0,0 +1,107 @@
1
+*gittab.txt*  Plugin that provides simple Git functions in Vim
2
+
3
+                        _______ __     ______      __
4
+                       / ____(_) /_   /_  __/___ _/ /_
5
+                      / / __/ / __/    / / / __ `/ __ \
6
+                     / /_/ / / /_     / / / /_/ / /_/ /
7
+                     \____/_/\__/    /_/  \__,_/_.___/
8
+
9
+==============================================================================
10
+Gittab                                                                |gittab|
11
+Introduction ........................................... |gittab-introduction|
12
+Commands ................................................... |gittab-commands|
13
+  :Blame ............................................................ |:Blame|
14
+  :Log ................................................................ |:Log|
15
+  :Show .............................................................. |:Show|
16
+  :ShowFile ...................................................... |:ShowFile|
17
+  :Diff .............................................................. :|Diff|
18
+
19
+
20
+==============================================================================
21
+Introduction                                      *gittab* *gittab-introduction*
22
+
23
+These are handy context-sensitive Git commands in Vim that load the results in
24
+a new tab. The commands automatically infer what you want from the current
25
+filename and whether there's a commit hash under the cursor.
26
+
27
+These commands are for investigating the history of files. The supported
28
+commands are:
29
+
30
+  :Blame
31
+  :Log
32
+  :Show and :ShowFile
33
+  :Diff
34
+
35
+When you're browsing a file in a Git repository, these commands provide a very
36
+simple and convenient flow for digging into their history.
37
+
38
+All you have to know is Blame, Log, Show, and Diff.
39
+
40
+==============================================================================
41
+The Commands                                                 *gittab-commands*
42
+
43
+:Blame                                                                *:Blame*
44
+------
45
+
46
+When you're on a regular file or a `:ShowFile` buffer, opens up a "git blame"
47
+buffer in a new tab for the file at that commit, and positions the
48
+cursor at the same relative spot.
49
+
50
+Example, run Blame on a `:ShowFile` buffer named "git show 1234abcd:README.md",
51
+and you get a `:Blame` buffer named "git blame 1234abcd -- README.md".
52
+
53
+:Log {args}                                                             *:Log*
54
+-----------
55
+
56
+When you're on a regular file or a `:ShowFile` buffer, opens up a git log
57
+buffer in a new tab. By default, runs log as:
58
+
59
+    git log --no-color --graph --date=short --pretty="format:%h %ad %s %an %d"
60
+
61
+And you can pass in additional Git arguments like --all.
62
+
63
+    :Log --all
64
+
65
+The reason Log defaults to one-line logs is because `:Show` and `:Diff` are so
66
+easy to use to dive in deeper to the individual commits.
67
+
68
+Handy arguments are --all, --merges, --date-order, --first-parent, and
69
+--ancestry-path.
70
+                              
71
+:Show                                                                 *:Show*
72
+-----
73
+
74
+These require the cursor to be positioned on a hash, so you'd most likely be
75
+in a `:Log` or `:Blame` buffer when you want to use these commands.
76
+
77
+When you're on a `:Blame` buffer, a `:Log` buffer, `:Show` opens a
78
+buffer in a new tab that shows the full commit message for the hash under the
79
+cursor.
80
+
81
+:ShowFile                                                         *:ShowFile*
82
+---------
83
+
84
+`:ShowFile` opens a buffer in a new tab that shows the contents of the file
85
+at the hash under the cursor.
86
+
87
+Ex., If the cursor is on "1234abcd" on `:Blame` buffer "git blame -- README.md"
88
+then:
89
+
90
+| Command   | Resultant Buffer               |
91
+| ---       | ---                            |
92
+| :Show     | git show 1234abcd -- README.md |
93
+| :ShowFile | git show 1234abcd:README.md    |
94
+
95
+:Diff                                                                 *:Diff*
96
+-----
97
+
98
+If you're on a regular file that's different from HEAD, `:Diff` will perform a
99
+`git diff` on the file from HEAD. If it's the same as HEAD, then `:Diff` will
100
+perform a `git diff` against that file's previous commit.
101
+
102
+If the cursor is on a commit hash (as available on :Blame, :Log, and :Show
103
+buffers), then `:Diff` will perform a diff against the previous commit to that
104
+one.
105
+
106
+==============================================================================
107
+vim:tw=78:sw=4:ts=8:ft=help:norl:
... ...
@@ -0,0 +1,197 @@
1
+if exists("g:loaded_gittab") || &cp | finish | endif
2
+let g:loaded_gittab = 1
3
+
4
+"A helper function that tries to show a buffer if it already exists
5
+function! s:ShowBufInNewTab(bufname)
6
+   let l:bnr = bufnr(a:bufname)
7
+   if l:bnr > 0
8
+       tabnew
9
+       exec 'buffer ' . l:bnr
10
+       return 1
11
+   endif
12
+   return 0
13
+endfunction
14
+
15
+"A helper function that tries to show a buffer if it already exists
16
+function! s:ShowBufInNewSplit(bufname)
17
+   let l:bnr = bufnr(a:bufname)
18
+   if l:bnr > 0
19
+"       bo vne
20
+       vne
21
+       exec 'buffer ' . l:bnr
22
+       return 1
23
+   endif
24
+   return 0
25
+endfunction
26
+
27
+function! s:GitBlame()
28
+    let l:hash = expand('<cword>')
29
+    let l:currentView = winsaveview()
30
+    " If in a Blame window already, do blame for some prior commit
31
+    if l:hash =~ '^[0-9a-f]\{7,40}$' && stridx(expand('%'), ' -- ') != -1
32
+        let l:fname = split(expand('%'), ' -- ')[-1]
33
+        let l:bufname = 'git blame ' . l:hash . '^ -- ' . l:fname
34
+        if !s:ShowBufInNewTab(l:bufname)
35
+            exec 'tabnew | r! git blame ' . l:hash . '^ -- ' . shellescape(l:fname)
36
+            exec 'silent :file ' . fnameescape(l:bufname)
37
+        endif
38
+    else
39
+        let l:fname = expand('%')
40
+        let l:hash = ''
41
+        " Show fnames will have ':' in them.
42
+        if stridx(l:fname, ':') != -1
43
+            let l:fname_parts = split(l:fname, ':')
44
+            let l:fname = l:fname_parts[-1]
45
+            let l:hash = split(l:fname_parts[0], ' ')[-1]
46
+        endif
47
+        if strlen(l:hash)
48
+            let l:bufname = 'git blame ' . l:hash . ' -- ' . l:fname
49
+            if !s:ShowBufInNewTab(l:bufname)
50
+                exec 'tabnew | r! git blame ' . l:hash . ' -- ' . shellescape(l:fname)
51
+                exec 'silent :file ' . fnameescape(l:bufname)
52
+            endif
53
+        else
54
+            let l:bufname = 'git blame -- ' . l:fname
55
+            if !s:ShowBufInNewTab(l:bufname)
56
+                exec 'tabnew | r! git blame -- ' . shellescape(l:fname)
57
+                exec 'silent :file ' . fnameescape(l:bufname)
58
+            endif
59
+        endif
60
+    endif
61
+    0d_
62
+    call winrestview(l:currentView)
63
+    setl buftype=nofile
64
+endfunction
65
+command Blame :call s:GitBlame()
66
+
67
+function! s:GitShow(commit_or_file)
68
+    let l:fname = expand('%')
69
+    let l:hash = expand('<cword>')
70
+    if l:hash =~ '^[0-9a-f]\{7,40}$'
71
+        if stridx(l:fname, ' -- ') != -1
72
+            let l:fname = split(l:fname, ' -- ')[-1]
73
+        endif
74
+        if a:commit_or_file != "file"
75
+            let l:bufname = 'git show ' . l:hash . ' -- ' . l:fname
76
+            if !s:ShowBufInNewTab(l:bufname)
77
+                " Have Show show all the affected files, so don't actually use  "--"
78
+                " exec 'tabnew | r! git show ' . l:hash . ' -- ' . shellescape(l:fname)
79
+                exec 'tabnew | r! git show ' . l:hash
80
+                " We lie here (' -- ') to have a filename the other git commands can use.
81
+                exec 'silent :file ' . fnameescape(l:bufname)
82
+            endif
83
+            0d_
84
+        else
85
+            let l:currentView = winsaveview()
86
+            let l:bufname = 'git show ' . l:hash . ':' . l:fname
87
+            if !s:ShowBufInNewTab(l:bufname)
88
+                exec 'tabnew | r! git show ' . l:hash . ':' . shellescape(l:fname)
89
+                exec 'silent :file ' . fnameescape(l:bufname)
90
+            endif
91
+            0d_
92
+            call winrestview(l:currentView)
93
+        endif
94
+        setl buftype=nofile
95
+    else
96
+        echo l:hash . ' is not a git hash.'
97
+    endif
98
+endfunction
99
+command Show :call s:GitShow("commit")
100
+command ShowFile :call s:GitShow("file")
101
+
102
+function! s:GitDiff()
103
+    let l:fname = expand('%:.')
104
+    let l:buf = winbufnr(0)
105
+    let l:commit = 'HEAD'
106
+    let l:hash = expand('<cword>')
107
+    let l:currentView = winsaveview()
108
+
109
+    " If the current word is a hash, then diff that vs. previous
110
+    if l:hash =~ '^[0-9a-f]\{7,40}$' && stridx(expand('%'), ' -- ') != -1
111
+        let l:fname = split(expand('%'), ' -- ')[-1]
112
+        let l:bufname = 'git show ' . l:hash . '^:' . l:fname
113
+        if !s:ShowBufInNewTab(l:bufname)
114
+            exec ':tabnew | silent r! git show ' . l:hash . '^:$(git rev-parse --show-prefix)' . shellescape(l:fname)
115
+            setl buftype=nofile
116
+            0d_
117
+            exec 'silent :file ' . fnameescape(l:bufname)
118
+	endif
119
+
120
+        let l:bufname = 'git show ' . l:hash . ':' . l:fname
121
+        if !s:ShowBufInNewSplit(l:bufname)
122
+            exec 'vne | silent r! git show ' . l:hash . ':$(git rev-parse --show-prefix)' . shellescape(l:fname)
123
+            setl buftype=nofile
124
+            exec 'silent :file ' . fnameescape(l:bufname)
125
+            0d_
126
+        endif
127
+    elseif stridx(expand('%'), ':') != -1
128
+        " If we're in a 'git show' buffer, then extract fname and hash from there
129
+        let l:fname_parts = split(l:fname, ':')
130
+        let l:fname = l:fname_parts[-1]
131
+        let l:hash = split(l:fname_parts[0], ' ')[-1]
132
+	" TODO: Below few lines are identical to above, so remove dupes.
133
+        let l:bufname = 'git show ' . l:hash . '^:' . l:fname
134
+        if !s:ShowBufInNewTab(l:bufname)
135
+            exec ':tabnew | silent r! git show ' . l:hash . '^:$(git rev-parse --show-prefix)' . shellescape(l:fname)
136
+            setl buftype=nofile
137
+            0d_
138
+            exec 'silent :file ' . fnameescape(l:bufname)
139
+	endif
140
+
141
+        let l:bufname = 'git show ' . l:hash . ':' . l:fname
142
+        if !s:ShowBufInNewSplit(l:bufname)
143
+            exec 'vne | silent r! git show ' . l:hash . ':$(git rev-parse --show-prefix)' . shellescape(l:fname)
144
+            setl buftype=nofile
145
+            exec 'silent :file ' . fnameescape(l:bufname)
146
+            0d_
147
+        endif
148
+    else
149
+        " If the buffer is not different then repo, then diff HEAD vs file's previous commit
150
+        let l:o = system("git status --porcelain | grep " . l:fname)
151
+        if v:shell_error != 0
152
+            let l:commit = system('git log -2 --pretty=format:"%h" -- ' . l:fname . ' | tail -n 1')
153
+        endif
154
+
155
+        let l:bufname = 'git show ' . l:commit . ':' . l:fname
156
+        " Bug if l:filename includes ".."
157
+        if !s:ShowBufInNewTab(l:bufname)
158
+            exec ':tabnew | r! git show ' . l:commit . ':$(git rev-parse --show-prefix)' . l:fname
159
+            setl buftype=nofile
160
+            0d_
161
+            exec 'silent :file ' . fnameescape(l:bufname)
162
+        endif
163
+        exec 'vert sb '.l:buf
164
+    endif
165
+    call winrestview(l:currentView)
166
+    windo diffthis
167
+    setl buftype=nofile
168
+    wincmd r
169
+    wincmd l
170
+endfunction
171
+command Diff :call s:GitDiff()
172
+
173
+function! s:GitLog(...)
174
+    let l:fname = expand('%')
175
+    if stridx(l:fname, ' -- ') != -1
176
+        let l:fname = split(l:fname, ' -- ')[-1]
177
+    elseif stridx(l:fname, ':') != -1
178
+        let l:fname = split(l:fname, ':')[-1]
179
+    endif
180
+    let l:args = a:1
181
+    if strlen(l:args)
182
+        " Add a space to the end
183
+	let l:args = l:args . " "
184
+    endif
185
+    let l:bufname = 'git log ' . l:args . '-- ' . l:fname
186
+    if !s:ShowBufInNewTab(l:bufname)
187
+        exec 'tabnew | r! git log --no-color --graph --date=short ' . l:args . '--pretty="format:\%h \%ad \%s \%an \%d" -- ' . shellescape(l:fname)
188
+        setl buftype=nofile
189
+        0d_
190
+        exec 'silent :file ' . fnameescape(l:bufname)
191
+    endif
192
+endfunction
193
+
194
+" Handy arguments are --all, --merges, --date-order, --first-parent, --ancestry-path
195
+command -nargs=* Log :call s:GitLog(<q-args>)
196
+
197
+" vim:set ft=vim sw=4 sts=4 et:
... ...
@@ -103,6 +103,7 @@ let g:currentmode={
103 103
     \ 's'  : 'SELECT',
104 104
     \ 'S'  : 'S·LINE',
105 105
     \ 'i'  : 'INSERT',
106
+    \ 'r'  : 'replace',
106 107
     \ 'R'  : 'REPLACE',
107 108
     \ 'Rv' : 'V·REPLACE',
108 109
     \ 'c'  : 'COMMAND',
... ...
@@ -167,202 +168,6 @@ function! OpenCurrentAsNewTab()
167 168
 endfunction
168 169
 nmap <leader>o :call OpenCurrentAsNewTab()<CR>
169 170
 
170
-" git blame, show, diff and log
171
-" Delete these functions when you install https://github.com/tpope/vim-fugitive
172
-
173
-"A helper function that tries to show a buffer if it already exists
174
-function! ShowBufInNewTab(bufname)
175
-   let l:bnr = bufnr(a:bufname)
176
-   if l:bnr > 0
177
-       tabnew
178
-       exec 'buffer ' . l:bnr
179
-       return 1
180
-   endif
181
-   return 0
182
-endfunction
183
-
184
-"A helper function that tries to show a buffer if it already exists
185
-function! ShowBufInNewSplit(bufname)
186
-   let l:bnr = bufnr(a:bufname)
187
-   if l:bnr > 0
188
-"       bo vne
189
-       vne
190
-       exec 'buffer ' . l:bnr
191
-       return 1
192
-   endif
193
-   return 0
194
-endfunction
195
-
196
-function! GitBlame()
197
-    let l:hash = expand('<cword>')
198
-    let l:currentView = winsaveview()
199
-    " If in a Blame window already, do blame for some prior commit
200
-    if l:hash =~ '^[0-9a-f]\{7,40}$' && stridx(expand('%'), ' -- ') != -1
201
-        let l:fname = split(expand('%'), ' -- ')[-1]
202
-        let l:bufname = 'git blame ' . l:hash . '^ -- ' . l:fname
203
-        if !ShowBufInNewTab(l:bufname)
204
-            exec 'tabnew | r! git blame ' . l:hash . '^ -- ' . shellescape(l:fname)
205
-            exec 'silent :file ' . fnameescape(l:bufname)
206
-        endif
207
-    else
208
-        let l:fname = expand('%')
209
-        let l:hash = ''
210
-        " Show fnames will have ':' in them.
211
-        if stridx(l:fname, ':') != -1
212
-            let l:fname_parts = split(l:fname, ':')
213
-            let l:fname = l:fname_parts[-1]
214
-            let l:hash = split(l:fname_parts[0], ' ')[-1]
215
-        endif
216
-        if strlen(l:hash)
217
-            let l:bufname = 'git blame ' . l:hash . ' -- ' . l:fname
218
-            if !ShowBufInNewTab(l:bufname)
219
-                exec 'tabnew | r! git blame ' . l:hash . ' -- ' . shellescape(l:fname)
220
-                exec 'silent :file ' . fnameescape(l:bufname)
221
-            endif
222
-        else
223
-            let l:bufname = 'git blame -- ' . l:fname
224
-            if !ShowBufInNewTab(l:bufname)
225
-                exec 'tabnew | r! git blame -- ' . shellescape(l:fname)
226
-                exec 'silent :file ' . fnameescape(l:bufname)
227
-            endif
228
-        endif
229
-    endif
230
-    0d_
231
-    call winrestview(l:currentView)
232
-    setl buftype=nofile
233
-endfunction
234
-command Blame :call GitBlame()
235
-
236
-function! GitShow(commit_or_file)
237
-    let l:fname = expand('%')
238
-    let l:hash = expand('<cword>')
239
-    if l:hash =~ '^[0-9a-f]\{7,40}$'
240
-        if stridx(l:fname, ' -- ') != -1
241
-            let l:fname = split(l:fname, ' -- ')[-1]
242
-        endif
243
-        if a:commit_or_file != "file"
244
-            let l:bufname = 'git show ' . l:hash . ' -- ' . l:fname
245
-            if !ShowBufInNewTab(l:bufname)
246
-                " Have Show show all the affected files, so don't actually use  "--"
247
-                " exec 'tabnew | r! git show ' . l:hash . ' -- ' . shellescape(l:fname)
248
-                exec 'tabnew | r! git show ' . l:hash
249
-                " We lie here (' -- ') to have a filename the other git commands can use.
250
-                exec 'silent :file ' . fnameescape(l:bufname)
251
-            endif
252
-            0d_
253
-        else
254
-            let l:currentView = winsaveview()
255
-            let l:bufname = 'git show ' . l:hash . ':' . l:fname
256
-            if !ShowBufInNewTab(l:bufname)
257
-                exec 'tabnew | r! git show ' . l:hash . ':' . shellescape(l:fname)
258
-                exec 'silent :file ' . fnameescape(l:bufname)
259
-            endif
260
-            0d_
261
-            call winrestview(l:currentView)
262
-        endif
263
-        setl buftype=nofile
264
-    else
265
-        echo l:hash . ' is not a git hash.'
266
-    endif
267
-endfunction
268
-command Show :call GitShow("commit")
269
-command ShowFile :call GitShow("file")
270
-
271
-function! GitDiff()
272
-    let l:fname = expand('%:.')
273
-    let l:buf = winbufnr(0)
274
-    let l:commit = 'HEAD'
275
-    let l:hash = expand('<cword>')
276
-    let l:currentView = winsaveview()
277
-
278
-    " If the current word is a hash, then diff that vs. previous
279
-    if l:hash =~ '^[0-9a-f]\{7,40}$' && stridx(expand('%'), ' -- ') != -1
280
-        let l:fname = split(expand('%'), ' -- ')[-1]
281
-        let l:bufname = 'git show ' . l:hash . '^:' . l:fname
282
-        if !ShowBufInNewTab(l:bufname)
283
-            exec ':tabnew | silent r! git show ' . l:hash . '^:$(git rev-parse --show-prefix)' . shellescape(l:fname)
284
-            setl buftype=nofile
285
-            0d_
286
-            exec 'silent :file ' . fnameescape(l:bufname)
287
-	endif
288
-
289
-        let l:bufname = 'git show ' . l:hash . ':' . l:fname
290
-        if !ShowBufInNewSplit(l:bufname)
291
-            exec 'vne | silent r! git show ' . l:hash . ':$(git rev-parse --show-prefix)' . shellescape(l:fname)
292
-            setl buftype=nofile
293
-            exec 'silent :file ' . fnameescape(l:bufname)
294
-            0d_
295
-        endif
296
-    elseif stridx(expand('%'), ':') != -1
297
-        " If we're in a 'git show' buffer, then extract fname and hash from there
298
-        let l:fname_parts = split(l:fname, ':')
299
-        let l:fname = l:fname_parts[-1]
300
-        let l:hash = split(l:fname_parts[0], ' ')[-1]
301
-	" TODO: Below few lines are identical to above, so remove dupes.
302
-        let l:bufname = 'git show ' . l:hash . '^:' . l:fname
303
-        if !ShowBufInNewTab(l:bufname)
304
-            exec ':tabnew | silent r! git show ' . l:hash . '^:$(git rev-parse --show-prefix)' . shellescape(l:fname)
305
-            setl buftype=nofile
306
-            0d_
307
-            exec 'silent :file ' . fnameescape(l:bufname)
308
-	endif
309
-
310
-        let l:bufname = 'git show ' . l:hash . ':' . l:fname
311
-        if !ShowBufInNewSplit(l:bufname)
312
-            exec 'vne | silent r! git show ' . l:hash . ':$(git rev-parse --show-prefix)' . shellescape(l:fname)
313
-            setl buftype=nofile
314
-            exec 'silent :file ' . fnameescape(l:bufname)
315
-            0d_
316
-        endif
317
-    else
318
-        " If the buffer is not different then repo, then diff HEAD vs file's previous commit
319
-        let l:o = system("git status --porcelain | grep " . l:fname)
320
-        if v:shell_error != 0
321
-            let l:commit = system('git log -2 --pretty=format:"%h" -- ' . l:fname . ' | tail -n 1')
322
-        endif
323
-
324
-        let l:bufname = 'git show ' . l:commit . ':' . l:fname
325
-        " Bug if l:filename includes ".."
326
-        if !ShowBufInNewTab(l:bufname)
327
-            exec ':tabnew | r! git show ' . l:commit . ':$(git rev-parse --show-prefix)' . l:fname
328
-            setl buftype=nofile
329
-            0d_
330
-            exec 'silent :file ' . fnameescape(l:bufname)
331
-        endif
332
-        exec 'vert sb '.l:buf
333
-    endif
334
-    call winrestview(l:currentView)
335
-    windo diffthis
336
-    setl buftype=nofile
337
-    wincmd r
338
-    wincmd l
339
-endfunction
340
-command Diff :call GitDiff()
341
-
342
-function! GitLog(...)
343
-    let l:fname = expand('%')
344
-    if stridx(l:fname, ' -- ') != -1
345
-        let l:fname = split(l:fname, ' -- ')[-1]
346
-    elseif stridx(l:fname, ':') != -1
347
-        let l:fname = split(l:fname, ':')[-1]
348
-    endif
349
-    let l:args = a:1
350
-    if strlen(l:args)
351
-        " Add a space to the end
352
-	let l:args = l:args . " "
353
-    endif
354
-    let l:bufname = 'git log ' . l:args . '-- ' . l:fname
355
-    if !ShowBufInNewTab(l:bufname)
356
-        exec 'tabnew | r! git log --no-color --graph --date=short ' . l:args . '--pretty="format:\%h \%ad \%s \%an \%d" -- ' . shellescape(l:fname)
357
-        setl buftype=nofile
358
-        0d_
359
-        exec 'silent :file ' . fnameescape(l:bufname)
360
-    endif
361
-endfunction
362
-
363
-" Handy arguments are --all, --merges, --date-order, --first-parent, --ancestry-path
364
-command -nargs=* Log :call GitLog(<q-args>)
365
-
366 171
 " pastetoggle
367 172
 nmap <leader>p :set invpaste paste?<cr>
368 173
 
369 174