nvim: fugitive: Refactor and add some more git helpers

This commit is contained in:
Sanchayan Maity 2021-04-21 21:42:31 +05:30
parent f65f7ffb3f
commit b9e5fb64bf
3 changed files with 149 additions and 32 deletions

View file

@ -1,11 +1,10 @@
" Disable whitespace highlighting in git buffer
autocmd BufEnter <buffer> DisableWhitespace
xnoremap <Leader>gb :<C-U>call <SID>git_checkout_branch()<CR>
" We would like to checkout a branch by visually selecting a branch name in
" the output of git branch -a.
function! s:git_checkout_branch() abort
normal! `<v`>y
silent execute "Git checkout " . shellescape(@@)
endfunction
nmap <buffer> <Leader>gb :call git#git_branch_checkout()<CR>
nmap <buffer> <Leader>gB :call git#git_branch_delete()<CR>
nmap <buffer> <Leader>gd :call git#git_diff_commit()<CR>
nmap <buffer> <Leader>gl :call git#git_branch_log()<CR>
nmap <buffer> <Leader>gL :call git#git_branch_log_pretty()<CR>
nmap <buffer> <Leader>gp :call git#git_cherry_pick()<CR>
xmap <buffer> <Leader>gP :<C-U>call git#git_cherry_pick_range()<CR>

View file

@ -6,11 +6,12 @@ nnoremap <Leader>gc :Git checkout -b<SPACE>
nnoremap <Leader>gC :Git checkout<SPACE>
nnoremap <Leader>gd :Ghdiffsplit!<CR>
nnoremap <Leader>gD :Gvdiffsplit!<CR>
nnoremap <Leader>gl :Git log %<CR>
nnoremap <Leader>gL :Git log -n 100<CR>
xnoremap <Leader>gl :<C-U>call <SID>git_log_range()<CR>
xnoremap <Leader>gL :<C-U>call <SID>git_log_named_block()<CR>
nnoremap <Leader>gl :Git log --stat %<CR>
nnoremap <Leader>gL :Git log --stat -n 100<CR>
xnoremap <Leader>gl :<C-U>call git#git_log_range()<CR>
xnoremap <Leader>gL :call git#git_log_named_block()<CR>
nnoremap <Leader>gM :Git merge origin/
nnoremap <Leader>go :call git#git_log_compare()<CR>
nnoremap <Leader>gp :Git push<CR>
nnoremap <Leader>gP :Git push -u<SPACE>
nnoremap <Leader>gr :Git rebase origin/master<CR>
@ -24,23 +25,3 @@ nnoremap <Leader>gw :Gwrite<CR>
" For 3 way merge
nnoremap <Leader>dl :diffget //2<CR>
nnoremap <Leader>dr :diffget //3<CR>
" The next two functions allow scoping diffs by line range of a file and named
" block in a file. Inspired by reading the following article.
" https://susanpotter.net/software/tracking-diffs-by-scoping-to-file-range-function-method-or-class-changes-in-git/
" Use these two links as reference to come up with this function
" https://vi.stackexchange.com/questions/17606/vmap-and-visual-block-how-do-i-write-a-function-to-operate-once-for-the-entire
" https://stackoverflow.com/questions/41238238/how-to-map-vim-visual-mode-to-replace-my-selected-text-parts
function! s:git_log_range() abort
let [lnum1, col1] = getpos("'<")[1:2]
let [lnum2, col2] = getpos("'>")[1:2]
silent execute "Git log --no-patch -L " . lnum1 . "," . lnum2 . ":%"
endfunction
" Taken from https://learnvimscriptthehardway.stevelosh.com/chapters/33.html.
" Modified and stripped to do what I needed.
function! s:git_log_named_block() abort
normal! `<v`>y
silent execute "Git log --no-patch -L :" . shellescape(@@) . ":%"
endfunction

View file

@ -0,0 +1,137 @@
" The purpose of creating this autoload is with functions like git_branch_log
" which opens another 'git' filetype buffer defining such a function in
" after/ftplugin/ft.vim results in a complain about duplicated function since
" anything defined there will be loaded again. See :help autoload.
" Delete the branch on the current line in the output of Git branch -a.
function! git#git_branch_delete() abort
let line = trim(getline('.'))
let branch = split(line, "/")
if len(branch) == 1
" Handles the case for local branch
execute "Git branch -D " . branch[0]
else
" Handles the case for remote remotes/remote_name/branch_name
let remote = branch[1]
let branch = branch[2]
execute "Git push -d " . remote . " " . branch
execute "Git branch -D " . branch
endif
silent execute "bd"
endfunction
" Checkout the branch on the current line in the output of Git branch -a.
function! git#git_branch_checkout() abort
let line = trim(getline('.'))
let branch = split(line, "/")
if len(branch) == 1
" Handles the case for local branch
silent execute "Git checkout " . branch[0]
else
" Handles the case for remote remotes/remote_name/branch_name
let remote = join(branch[1:2], "/")
silent execute "Git checkout " . remote
silent execute "Git checkout -b " . branch[2]
endif
silent execute "bd"
endfunction
" Get the log of the branch on current line in the output of Git branch -a.
function! git#git_branch_log() abort
let line = trim(getline('.'))
let branch = split(line, "/")
" Close the buffer opened by Git branch -a
silent execute "bd"
if len(branch) == 1
" Handles the case for local branch
execute "Git log " . branch[0]
else
" Handles the case for remote remotes/remote_name/branch_name
let remote = join(branch[1:2], "/")
execute "Git log " . remote
endif
endfunction
" Get the log of the branch on current line in the output of Git branch -a.
" This one returns a log where a line has the form <SHA> <commit-msg>. This
" acts as a precursor to what we run cherry-pick on.
function! git#git_branch_log_pretty() abort
let line = trim(getline('.'))
let branch = split(line, "/")
" Close the buffer opened by Git branch -a
silent execute "bd"
if len(branch) == 1
" Handles the case for local branch
execute "Git log --pretty=oneline --no-merges " . branch[0]
else
" Handles the case for remote remotes/remote_name/branch_name
let remote = join(branch[1:2], "/")
execute "Git log --pretty=oneline --no-merges " . remote
endif
endfunction
" Run Git cherry-pick on the commit in the line which we are at. To be used
" after running some variation of Git log.
function! git#git_cherry_pick() abort
" A line in Git log is of the form commit <SHA>
let line = trim(getline('.'))
let commit = split(line, " ")
silent execute "Git cherry-pick " . commit[1]
endfunction
" Git cherry-pick a range of commits. To be run on output of Git log
" --pretty=oneline. See git_branch_log_pretty.
function! git#git_cherry_pick_range() abort
let [lnum1, col1] = getpos("'<")[1:2]
let [lnum2, col2] = getpos("'>")[1:2]
let lines = reverse(getline(lnum1, lnum2))
if len(lines) == 0
return ''
endif
let oldCommit = split(lines[0], " ")[:0][0]
let newCommit = split(lines[-1], " ")[:0][0]
execute "Git cherry-pick " . oldCommit . "^.." . newCommit
silent execute "bd"
endfunction
" Run Git difftool on the commit in the line which we are at. To be used after
" running some variation of Git log.
function! git#git_diff_commit() abort
" A line in Git log is of the form commit <SHA>
let line = trim(getline('.'))
let commit = split(line, " ")
silent execute "Git difftool -y " . commit[1] . "^!"
endfunction
" The next two functions allow scoping diffs by line range of a file and named
" block in a file. Inspired by reading the following article.
" https://susanpotter.net/software/tracking-diffs-by-scoping-to-file-range-function-method-or-class-changes-in-git/
" Use these two links as reference to come up with this function
" https://vi.stackexchange.com/questions/17606/vmap-and-visual-block-how-do-i-write-a-function-to-operate-once-for-the-entire
" https://stackoverflow.com/questions/41238238/how-to-map-vim-visual-mode-to-replace-my-selected-text-parts
function! git#git_log_range() abort
let [lnum1, col1] = getpos("'<")[1:2]
let [lnum2, col2] = getpos("'>")[1:2]
silent execute "Git log --no-patch -L " . lnum1 . "," . lnum2 . ":%"
endfunction
" Taken from https://learnvimscriptthehardway.stevelosh.com/chapters/33.html.
" Modified and stripped to do what I needed.
function! git#git_log_named_block() abort
normal! `<v`>y
silent execute "Git log --no-patch -L :" . trim(shellescape(@@)) . ":%"
endfunction
" Compares current branch against master. For eg. git log master..current.
" This combined with Git difftool -y are helpful for MR reviews.
function! git#git_log_compare() abort
let default = split(trim(system('git symbolic-ref refs/remotes/origin/HEAD')), '/')
let current = trim(system('git rev-parse --abbrev-ref HEAD'))
let are_we_on_default = match(default[3], current)
if (are_we_on_default == 0)
echom "We are already on default branch. Nothing to compare."
else
silent execute "Git log " . default[3] . ".." . current
endif
endfunction