diff --git a/nvim/.config/nvim/after/ftplugin/git.vim b/nvim/.config/nvim/after/ftplugin/git.vim index 5eea627..84f8250 100644 --- a/nvim/.config/nvim/after/ftplugin/git.vim +++ b/nvim/.config/nvim/after/ftplugin/git.vim @@ -1,11 +1,10 @@ " Disable whitespace highlighting in git buffer autocmd BufEnter DisableWhitespace -xnoremap gb :call git_checkout_branch() - -" 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! `y - silent execute "Git checkout " . shellescape(@@) -endfunction +nmap gb :call git#git_branch_checkout() +nmap gB :call git#git_branch_delete() +nmap gd :call git#git_diff_commit() +nmap gl :call git#git_branch_log() +nmap gL :call git#git_branch_log_pretty() +nmap gp :call git#git_cherry_pick() +xmap gP :call git#git_cherry_pick_range() diff --git a/nvim/.config/nvim/after/plugin/fugitive.vim b/nvim/.config/nvim/after/plugin/fugitive.vim index 5bef9eb..0bbf64a 100644 --- a/nvim/.config/nvim/after/plugin/fugitive.vim +++ b/nvim/.config/nvim/after/plugin/fugitive.vim @@ -6,11 +6,12 @@ nnoremap gc :Git checkout -b nnoremap gC :Git checkout nnoremap gd :Ghdiffsplit! nnoremap gD :Gvdiffsplit! -nnoremap gl :Git log % -nnoremap gL :Git log -n 100 -xnoremap gl :call git_log_range() -xnoremap gL :call git_log_named_block() +nnoremap gl :Git log --stat % +nnoremap gL :Git log --stat -n 100 +xnoremap gl :call git#git_log_range() +xnoremap gL :call git#git_log_named_block() nnoremap gM :Git merge origin/ +nnoremap go :call git#git_log_compare() nnoremap gp :Git push nnoremap gP :Git push -u nnoremap gr :Git rebase origin/master @@ -24,23 +25,3 @@ nnoremap gw :Gwrite " For 3 way merge nnoremap dl :diffget //2 nnoremap dr :diffget //3 - -" 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! `y - silent execute "Git log --no-patch -L :" . shellescape(@@) . ":%" -endfunction diff --git a/nvim/.config/nvim/autoload/git.vim b/nvim/.config/nvim/autoload/git.vim new file mode 100644 index 0000000..34e7c6c --- /dev/null +++ b/nvim/.config/nvim/autoload/git.vim @@ -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 . 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 + 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 + 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! `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