This commit is contained in:
Stefan Liebl 2015-02-19 12:55:54 +01:00
parent 121cd1ddca
commit f8ab290619
4 changed files with 306 additions and 117 deletions

View File

@ -38,5 +38,5 @@ ScriptID SourceID Filename
2975 22815 fugitive.vim 2975 22815 fugitive.vim
2830 22798 csv.vim 2830 22798 csv.vim
3849 22637 git-time-lapse 3849 22637 git-time-lapse
4932 22787 diffchar.vim 4932 22924 diffchar.vim
4955 22442 Merginal 4955 22916 Merginal

View File

@ -140,15 +140,30 @@ endfunction
"Like merginal#runGitCommandInTreeReturnResult but split result to lines "Like merginal#runGitCommandInTreeReturnResult but split result to lines
function! merginal#runGitCommandInTreeReturnResultLines(repo,command) function! merginal#runGitCommandInTreeReturnResultLines(repo,command)
let l:dir=getcwd() return split(merginal#runGitCommandInTreeReturnResult(a:repo, a:command),
execute 'cd '.fnameescape(a:repo.tree()) \ '\r\n\|\n\|\r')
try
return split(merginal#system(a:repo.git_command().' '.a:command),'\r\n\|\n\|\r')
finally
execute 'cd '.fnameescape(l:dir)
endtry
endfunction endfunction
function! s:cleanup_term_codes(s)
let s = substitute(a:s, '\t', ' ', 'g')
" Remove terminal escape codes for colors (based on
" www.commandlinefu.com/commands/view/3584/).
let s = substitute(s, '\v\[([0-9]{1,3}(;[0-9]{1,3})?)?[m|K]', '', 'g')
return s
endfunction
function! merginal#runGitCommandInTreeEcho(repo,command)
let l:lines = merginal#runGitCommandInTreeReturnResultLines(a:repo, a:command)
if len(l:lines) == 1
" Output a single/empty line to make Vim wait for Enter.
echo ' '
endif
for l:line in l:lines
echo "[output]" s:cleanup_term_codes(l:line)
endfor
endfunction
"Returns 1 if there was a merginal bufffer to close "Returns 1 if there was a merginal bufffer to close
function! merginal#closeMerginalBuffer() function! merginal#closeMerginalBuffer()
let l:merginalWindowNumber=bufwinnr('Merginal:') let l:merginalWindowNumber=bufwinnr('Merginal:')
@ -300,6 +315,53 @@ function! merginal#fileDetails(lineNumber)
return l:result return l:result
endfunction endfunction
"For the commit in the specified line, retrieve it's hash
function! merginal#commitHash(lineNumber)
if !exists('b:merginal_repo')
throw 'Unable to get commit details outside the merginal window'
endif
if line(a:lineNumber)<=b:headerLinesCount
throw 'Unable to get commit details for the header of the merginal window'
endif
"echo a:lineNumber
if type(0) == type(a:lineNumber)
let l:lineNumber = a:lineNumber
else
let l:lineNumber = line(a:lineNumber)
endif
while b:headerLinesCount < l:lineNumber && !empty(getline(l:lineNumber))
let l:lineNumber -= 1
endwhile
let l:lineNumber += 1
return split(getline(l:lineNumber))[0]
endfunction
"For the commit in the specified line, retrieve:
" - fullHash
" - authorName
" - timestamp
" - subject
" - body
function! merginal#commitDetails(lineNumber)
let l:commitHash = merginal#commitHash(a:lineNumber)
let l:entryFormat = join(['%H', '%aN', '%aE', '%ai', '%B'], '%x01')
let l:commitLines = split(
\ merginal#system(b:merginal_repo.git_command('--no-pager', 'log', '-1', '--format='.l:entryFormat, l:commitHash)),
\ '\%x01')
let l:result = {}
let l:result.fullHash = l:commitLines[0]
let l:result.authorName = l:commitLines[1]
let l:result.authorEmail = l:commitLines[2]
let l:result.timestamp = l:commitLines[3]
let l:commitMessage = split(l:commitLines[4], '\r\n\|\n\|\r')
let l:result.subject = l:commitMessage[0]
let l:result.body = join(l:commitMessage[2 :], "\n")
return l:result
endfunction
function! merginal#getLocalBranchNamesThatTrackARemoteBranch(remoteBranchName) function! merginal#getLocalBranchNamesThatTrackARemoteBranch(remoteBranchName)
"Get verbose list of branches "Get verbose list of branches
let l:branchList=split(merginal#system(b:merginal_repo.git_command('branch','-vv')),'\r\n\|\n\|\r') let l:branchList=split(merginal#system(b:merginal_repo.git_command('branch','-vv')),'\r\n\|\n\|\r')
@ -351,6 +413,8 @@ function! merginal#openBranchListBuffer(...)
endfunction endfunction
augroup merginal augroup merginal
autocmd!
autocmd User Merginal_BranchList nnoremap <buffer> q <C-w>q
autocmd User Merginal_BranchList nnoremap <buffer> R :call merginal#tryRefreshBranchListBuffer(0)<Cr> autocmd User Merginal_BranchList nnoremap <buffer> R :call merginal#tryRefreshBranchListBuffer(0)<Cr>
autocmd User Merginal_BranchList nnoremap <buffer> C :call <SID>checkoutBranchUnderCursor()<Cr> autocmd User Merginal_BranchList nnoremap <buffer> C :call <SID>checkoutBranchUnderCursor()<Cr>
autocmd User Merginal_BranchList nnoremap <buffer> cc :call <SID>checkoutBranchUnderCursor()<Cr> autocmd User Merginal_BranchList nnoremap <buffer> cc :call <SID>checkoutBranchUnderCursor()<Cr>
@ -364,12 +428,14 @@ augroup merginal
autocmd User Merginal_BranchList nnoremap <buffer> mm :call <SID>mergeBranchUnderCursor()<Cr> autocmd User Merginal_BranchList nnoremap <buffer> mm :call <SID>mergeBranchUnderCursor()<Cr>
autocmd User Merginal_BranchList nnoremap <buffer> mf :call <SID>mergeBranchUnderCursorUsingFugitive()<Cr> autocmd User Merginal_BranchList nnoremap <buffer> mf :call <SID>mergeBranchUnderCursorUsingFugitive()<Cr>
autocmd User Merginal_BranchList nnoremap <buffer> rb :call <SID>rebaseBranchUnderCursor()<Cr> autocmd User Merginal_BranchList nnoremap <buffer> rb :call <SID>rebaseBranchUnderCursor()<Cr>
autocmd User Merginal_BranchList nnoremap <buffer> ps :call <SID>remoteActionForBranchUnderCursor('push',0)<Cr> autocmd User Merginal_BranchList nnoremap <buffer> ps :call <SID>remoteActionForBranchUnderCursor('push',[])<Cr>
autocmd User Merginal_BranchList nnoremap <buffer> pS :call <SID>remoteActionForBranchUnderCursor('push',1)<Cr> autocmd User Merginal_BranchList nnoremap <buffer> pS :call <SID>remoteActionForBranchUnderCursor('push',['--force'])<Cr>
autocmd User Merginal_BranchList nnoremap <buffer> pl :call <SID>remoteActionForBranchUnderCursor('pull',0)<Cr> autocmd User Merginal_BranchList nnoremap <buffer> pl :call <SID>remoteActionForBranchUnderCursor('pull',[])<Cr>
autocmd User Merginal_BranchList nnoremap <buffer> pf :call <SID>remoteActionForBranchUnderCursor('fetch',0)<Cr> autocmd User Merginal_BranchList nnoremap <buffer> pr :call <SID>remoteActionForBranchUnderCursor('pull',['--rebase'])<Cr>
autocmd User Merginal_BranchList nnoremap <buffer> pf :call <SID>remoteActionForBranchUnderCursor('fetch',[])<Cr>
autocmd User Merginal_BranchList nnoremap <buffer> gd :call <SID>diffWithBranchUnderCursor()<Cr> autocmd User Merginal_BranchList nnoremap <buffer> gd :call <SID>diffWithBranchUnderCursor()<Cr>
autocmd User Merginal_BranchList nnoremap <buffer> rn :call <SID>renameBranchUnderCursor()<Cr> autocmd User Merginal_BranchList nnoremap <buffer> rn :call <SID>renameBranchUnderCursor()<Cr>
autocmd User Merginal_BranchList nnoremap <buffer> gl :call <SID>historyLogForBranchUnderCursor()<Cr>
augroup END augroup END
"If the current buffer is a branch list buffer - refresh it! "If the current buffer is a branch list buffer - refresh it!
@ -380,12 +446,11 @@ function! merginal#tryRefreshBranchListBuffer(jumpToCurrentBranch)
setlocal modifiable setlocal modifiable
"Clear the buffer: "Clear the buffer:
normal ggdG silent normal! gg"_dG
"Write the branch list: "Write the branch list:
call setline(1,l:branchList) call setline(1,l:branchList)
setlocal nomodifiable setlocal nomodifiable
if a:jumpToCurrentBranch if a:jumpToCurrentBranch
"Find the current branch's index "Find the current branch's index
let l:currentBranchIndex=-1 let l:currentBranchIndex=-1
@ -409,7 +474,7 @@ endfunction
function! s:checkoutBranchUnderCursor() function! s:checkoutBranchUnderCursor()
if 'Merginal:Branches'==bufname('') if 'Merginal:Branches'==bufname('')
let l:branch=merginal#branchDetails('.') let l:branch=merginal#branchDetails('.')
echo merginal#runGitCommandInTreeReturnResult(b:merginal_repo,'--no-pager checkout '.shellescape(l:branch.handle)) call merginal#runGitCommandInTreeEcho(b:merginal_repo,'--no-pager checkout '.shellescape(l:branch.handle))
call merginal#reloadBuffers() call merginal#reloadBuffers()
call merginal#tryRefreshBranchListBuffer(0) call merginal#tryRefreshBranchListBuffer(0)
endif endif
@ -427,11 +492,11 @@ function! s:trackBranchUnderCursor(promptForName)
let l:newBranchName=input('Track `'.l:branch.handle.'` as: ',l:newBranchName) let l:newBranchName=input('Track `'.l:branch.handle.'` as: ',l:newBranchName)
if empty(l:newBranchName) if empty(l:newBranchName)
echo ' ' echo ' '
echo 'Branch tracking canceled by the user' echom 'Branch tracking canceled by user.'
return return
endif endif
endif endif
echo merginal#runGitCommandInTreeReturnResult(b:merginal_repo,'--no-pager checkout -b '.shellescape(l:newBranchName).' --track '.shellescape(l:branch.handle)) call merginal#runGitCommandInTreeEcho(b:merginal_repo,'--no-pager checkout -b '.shellescape(l:newBranchName).' --track '.shellescape(l:branch.handle))
call merginal#reloadBuffers() call merginal#reloadBuffers()
call merginal#tryRefreshBranchListBuffer(0) call merginal#tryRefreshBranchListBuffer(0)
endif endif
@ -443,10 +508,10 @@ function! s:promptToCreateNewBranch()
let l:newBranchName=input('Branch `'.b:merginal_repo.head().'` to: ') let l:newBranchName=input('Branch `'.b:merginal_repo.head().'` to: ')
if empty(l:newBranchName) if empty(l:newBranchName)
echo ' ' echo ' '
echo 'Branch creation canceled by the user' echom 'Branch creation canceled by user.'
return return
endif endif
echo merginal#runGitCommandInTreeReturnResult(b:merginal_repo,'--no-pager checkout -b '.shellescape(l:newBranchName)) call merginal#runGitCommandInTreeEcho(b:merginal_repo,'--no-pager checkout -b '.shellescape(l:newBranchName))
call merginal#reloadBuffers() call merginal#reloadBuffers()
call merginal#tryRefreshBranchListBuffer(1) call merginal#tryRefreshBranchListBuffer(1)
endif endif
@ -458,15 +523,14 @@ function! s:deleteBranchUnderCursor()
let l:branch=merginal#branchDetails('.') let l:branch=merginal#branchDetails('.')
let l:answer=0 let l:answer=0
if l:branch.isLocal if l:branch.isLocal
let l:answer='yes'==input('Delete branch `'.l:branch.handle.'`?(type "yes" to confirm) ') let l:answer='yes'==input('Delete branch `'.l:branch.handle.'`? (type "yes" to confirm) ')
elseif l:branch.isRemote elseif l:branch.isRemote
"Deleting remote branches needs a special warning "Deleting remote branches needs a special warning
let l:answer='yes-remote'==input('Delete remote(!) branch `'.l:branch.handle.'`?(type "yes-remote" to confirm) ') let l:answer='yes-remote'==input('Delete remote(!) branch `'.l:branch.handle.'`? (type "yes-remote" to confirm) ')
endif endif
if l:answer if l:answer
if l:branch.isLocal if l:branch.isLocal
echo ' ' call merginal#runGitCommandInTreeEcho(b:merginal_repo,'--no-pager branch -D '.shellescape(l:branch.handle))
echo merginal#runGitCommandInTreeReturnResult(b:merginal_repo,'--no-pager branch -D '.shellescape(l:branch.handle))
else else
execute '!'.b:merginal_repo.git_command('push').' '.shellescape(l:branch.remote).' --delete '.shellescape(l:branch.name) execute '!'.b:merginal_repo.git_command('push').' '.shellescape(l:branch.remote).' --delete '.shellescape(l:branch.name)
endif endif
@ -474,7 +538,7 @@ function! s:deleteBranchUnderCursor()
call merginal#tryRefreshBranchListBuffer(0) call merginal#tryRefreshBranchListBuffer(0)
else else
echo ' ' echo ' '
echo 'Branch deletion canceled by the user' echom 'Branch deletion canceled by user.'
endif endif
endif endif
endfunction endfunction
@ -483,8 +547,7 @@ endfunction
function! s:mergeBranchUnderCursor() function! s:mergeBranchUnderCursor()
if 'Merginal:Branches'==bufname('') if 'Merginal:Branches'==bufname('')
let l:branch=merginal#branchDetails('.') let l:branch=merginal#branchDetails('.')
echo ' ' call merginal#runGitCommandInTreeEcho(b:merginal_repo,'merge --no-commit '.shellescape(l:branch.handle))
echo merginal#runGitCommandInTreeReturnResult(b:merginal_repo,'merge --no-commit '.shellescape(l:branch.handle))
call merginal#reloadBuffers() call merginal#reloadBuffers()
if v:shell_error if v:shell_error
call merginal#openMergeConflictsBuffer(winnr()) call merginal#openMergeConflictsBuffer(winnr())
@ -512,8 +575,7 @@ endfunction
function! s:rebaseBranchUnderCursor() function! s:rebaseBranchUnderCursor()
if 'Merginal:Branches'==bufname('') if 'Merginal:Branches'==bufname('')
let l:branch=merginal#branchDetails('.') let l:branch=merginal#branchDetails('.')
echo ' ' call merginal#runGitCommandInTreeEcho(b:merginal_repo,'rebase '.shellescape(l:branch.handle))
echo merginal#runGitCommandInTreeReturnResult(b:merginal_repo,'rebase '.shellescape(l:branch.handle))
call merginal#reloadBuffers() call merginal#reloadBuffers()
if v:shell_error if v:shell_error
call merginal#openRebaseConflictsBuffer(winnr()) call merginal#openRebaseConflictsBuffer(winnr())
@ -522,7 +584,7 @@ function! s:rebaseBranchUnderCursor()
endfunction endfunction
"Run various remote actions "Run various remote actions
function! s:remoteActionForBranchUnderCursor(remoteAction,force) function! s:remoteActionForBranchUnderCursor(remoteAction,flags)
if 'Merginal:Branches'==bufname('') if 'Merginal:Branches'==bufname('')
let l:branch=merginal#branchDetails('.') let l:branch=merginal#branchDetails('.')
if l:branch.isLocal if l:branch.isLocal
@ -597,9 +659,9 @@ function! s:remoteActionForBranchUnderCursor(remoteAction,force)
endif endif
let l:gitCommandWithArgs=[a:remoteAction] let l:gitCommandWithArgs=[a:remoteAction]
if a:force for l:flag in a:flags
call add(l:gitCommandWithArgs,'--force') call add(l:gitCommandWithArgs,l:flag)
endif endfor
let l:reloadBuffers=0 let l:reloadBuffers=0
@ -645,10 +707,7 @@ function! s:diffWithBranchUnderCursor()
if 'Merginal:Branches'==bufname('') if 'Merginal:Branches'==bufname('')
\|| 'Merginal:RebaseAmend'==bufname('') \|| 'Merginal:RebaseAmend'==bufname('')
let l:branch=merginal#branchDetails('.') let l:branch=merginal#branchDetails('.')
if l:branch.isCurrent call merginal#openDiffFilesBuffer(l:branch.handle)
throw 'Can not diff against the current branch'
endif
call merginal#openDiffFilesBuffer(l:branch)
endif endif
endfunction endfunction
@ -663,10 +722,10 @@ function! s:renameBranchUnderCursor()
let l:newName=input('Rename `'.l:branch.handle.'` to: ',l:branch.name) let l:newName=input('Rename `'.l:branch.handle.'` to: ',l:branch.name)
echo ' ' echo ' '
if empty(l:newName) if empty(l:newName)
echo 'Branch rename canceled by the user' echom 'Branch rename canceled by user.'
return return
elseif l:newName==l:branch.name elseif l:newName==l:branch.name
echo 'Branch name was not modified' echom 'Branch name was not modified.'
return return
endif endif
@ -677,6 +736,16 @@ function! s:renameBranchUnderCursor()
endif endif
endfunction endfunction
"Opens the history log buffer
function! s:historyLogForBranchUnderCursor()
if 'Merginal:Branches'==bufname('')
\|| 'Merginal:RebaseAmend'==bufname('')
let l:branch=merginal#branchDetails('.')
call merginal#openHistoryLogBuffer(l:branch)
endif
endfunction
"Open the merge conflicts buffer for resolving merge conflicts "Open the merge conflicts buffer for resolving merge conflicts
@ -691,6 +760,7 @@ function! merginal#openMergeConflictsBuffer(...)
endfunction endfunction
augroup merginal augroup merginal
autocmd User Merginal_MergeConflicts nnoremap <buffer> q <C-w>q
autocmd User Merginal_MergeConflicts nnoremap <buffer> R :call merginal#tryRefreshMergeConflictsBuffer(0)<Cr> autocmd User Merginal_MergeConflicts nnoremap <buffer> R :call merginal#tryRefreshMergeConflictsBuffer(0)<Cr>
autocmd User Merginal_MergeConflicts nnoremap <buffer> <Cr> :call <SID>openMergeConflictUnderCursor()<Cr> autocmd User Merginal_MergeConflicts nnoremap <buffer> <Cr> :call <SID>openMergeConflictUnderCursor()<Cr>
autocmd User Merginal_MergeConflicts nnoremap <buffer> A :call <SID>addConflictedFileToStagingArea()<Cr> autocmd User Merginal_MergeConflicts nnoremap <buffer> A :call <SID>addConflictedFileToStagingArea()<Cr>
@ -719,7 +789,7 @@ function! s:refreshConflictsBuffer(fileToJumpTo,headerLines)
setlocal modifiable setlocal modifiable
"Clear the buffer: "Clear the buffer:
normal ggdG silent normal! gg"_dG
"Write the branch list: "Write the branch list:
call setline(1,a:headerLines+l:conflicts) call setline(1,a:headerLines+l:conflicts)
let b:headerLinesCount=len(a:headerLines) let b:headerLinesCount=len(a:headerLines)
@ -773,7 +843,7 @@ function! s:addConflictedFileToStagingArea()
if empty(l:file.name) if empty(l:file.name)
return return
endif endif
echo merginal#runGitCommandInTreeReturnResult(b:merginal_repo,'--no-pager add '.shellescape(fnamemodify(l:file.name,':p'))) call merginal#runGitCommandInTreeEcho(b:merginal_repo,'--no-pager add '.shellescape(fnamemodify(l:file.name,':p')))
if 'Merginal:Conflicts'==bufname('') if 'Merginal:Conflicts'==bufname('')
if merginal#tryRefreshMergeConflictsBuffer(0) if merginal#tryRefreshMergeConflictsBuffer(0)
@ -789,7 +859,7 @@ function! s:addConflictedFileToStagingArea()
else else
if merginal#tryRefreshRebaseConflictsBuffer(0) if merginal#tryRefreshRebaseConflictsBuffer(0)
echo 'Added the last file of this patch.' echo 'Added the last file of this patch.'
echo 'Continue to the next patch(y/n)?' echo 'Continue to the next patch (y/N)?'
let l:answer=getchar() let l:answer=getchar()
if char2nr('y')==l:answer || char2nr('Y')==l:answer if char2nr('y')==l:answer || char2nr('Y')==l:answer
call s:rebaseAction('continue') call s:rebaseAction('continue')
@ -800,19 +870,20 @@ function! s:addConflictedFileToStagingArea()
endfunction endfunction
"Open the diff files buffer for diffing agains another branch "Open the diff files buffer for diffing agains another branch/commit
function! merginal#openDiffFilesBuffer(diffBranch,...) function! merginal#openDiffFilesBuffer(diffTarget,...)
if merginal#openTuiBuffer('Merginal:Diff',get(a:000,1,bufwinnr('Merginal:'))) if merginal#openTuiBuffer('Merginal:Diff',get(a:000,1,bufwinnr('Merginal:')))
doautocmd User Merginal_DiffFiles doautocmd User Merginal_DiffFiles
endif endif
let b:merginal_diffBranch=a:diffBranch let b:merginal_diffTarget=a:diffTarget
"At any rate, refresh the buffer: "At any rate, refresh the buffer:
call merginal#tryRefreshDiffFilesBuffer() call merginal#tryRefreshDiffFilesBuffer()
endfunction endfunction
augroup merginal augroup merginal
autocmd User Merginal_DiffFiles nnoremap <buffer> q <C-w>q
autocmd User Merginal_DiffFiles nnoremap <buffer> R :call merginal#tryRefreshDiffFilesBuffer()<Cr> autocmd User Merginal_DiffFiles nnoremap <buffer> R :call merginal#tryRefreshDiffFilesBuffer()<Cr>
autocmd User Merginal_DiffFiles nnoremap <buffer> <Cr> :call <SID>openDiffFileUnderCursor()<Cr> autocmd User Merginal_DiffFiles nnoremap <buffer> <Cr> :call <SID>openDiffFileUnderCursor()<Cr>
autocmd User Merginal_DiffFiles nnoremap <buffer> ds :call <SID>openDiffFileUnderCursorAndDiff('s')<Cr> autocmd User Merginal_DiffFiles nnoremap <buffer> ds :call <SID>openDiffFileUnderCursorAndDiff('s')<Cr>
@ -859,16 +930,16 @@ function! merginal#diffFileDetails(lineNumber)
return l:result return l:result
endfunction endfunction
"If the current buffer is a branch list buffer - refresh it! "If the current buffer is a diff files buffer - refresh it!
function! merginal#tryRefreshDiffFilesBuffer() function! merginal#tryRefreshDiffFilesBuffer()
if 'Merginal:Diff'==bufname('') if 'Merginal:Diff'==bufname('')
let l:diffBranch=b:merginal_diffBranch let l:diffTarget=b:merginal_diffTarget
let l:diffFiles=merginal#runGitCommandInTreeReturnResultLines(b:merginal_repo,'diff --name-status '.shellescape(l:diffBranch.handle)) let l:diffFiles=merginal#runGitCommandInTreeReturnResultLines(b:merginal_repo,'diff --name-status '.shellescape(l:diffTarget))
let l:currentLine=line('.') let l:currentLine=line('.')
setlocal modifiable setlocal modifiable
"Clear the buffer: "Clear the buffer:
normal ggdG silent normal! gg"_dG
"Write the diff files list: "Write the diff files list:
call setline(1,l:diffFiles) call setline(1,l:diffFiles)
setlocal nomodifiable setlocal nomodifiable
@ -903,7 +974,7 @@ function! s:openDiffFileUnderCursorAndDiff(diffType)
endif endif
let l:repo=b:merginal_repo let l:repo=b:merginal_repo
let l:diffBranch=b:merginal_diffBranch let l:diffTarget=b:merginal_diffTarget
"Close currently open git diffs "Close currently open git diffs
let l:currentWindowBuffer=winbufnr('.') let l:currentWindowBuffer=winbufnr('.')
@ -919,11 +990,11 @@ function! s:openDiffFileUnderCursorAndDiff(diffType)
call merginal#openFileDecidedWindow(l:repo,l:diffFile.fileFullPath) call merginal#openFileDecidedWindow(l:repo,l:diffFile.fileFullPath)
execute ':G'.a:diffType.'diff '.fnameescape(l:diffBranch.handle) execute ':G'.a:diffType.'diff '.fnameescape(l:diffTarget)
endif endif
endfunction endfunction
"Checks out the file from the other branch to the current branch "Checks out the file from the diff target to the current branch
function! s:checkoutDiffFileUnderCursor() function! s:checkoutDiffFileUnderCursor()
if 'Merginal:Diff'==bufname('') if 'Merginal:Diff'==bufname('')
let l:diffFile=merginal#diffFileDetails('.') let l:diffFile=merginal#diffFileDetails('.')
@ -934,16 +1005,16 @@ function! s:checkoutDiffFileUnderCursor()
let l:answer=1 let l:answer=1
if !empty(glob(l:diffFile.fileFullPath)) if !empty(glob(l:diffFile.fileFullPath))
let l:answer='yes'==input('Override `'.l:diffFile.fileInTree.'`?(type "yes" to confirm) ') let l:answer='yes'==input('Override `'.l:diffFile.fileInTree.'`? (type "yes" to confirm) ')
endif endif
if l:answer if l:answer
echo merginal#runGitCommandInTreeReturnResult(b:merginal_repo,'--no-pager checkout '.shellescape(b:merginal_diffBranch.handle) call merginal#runGitCommandInTreeEcho(b:merginal_repo,'--no-pager checkout '.shellescape(b:merginal_diffTarget)
\.' -- '.shellescape(l:diffFile.fileFullPath)) \.' -- '.shellescape(l:diffFile.fileFullPath))
call merginal#reloadBuffers() call merginal#reloadBuffers()
call merginal#tryRefreshDiffFilesBuffer() call merginal#tryRefreshDiffFilesBuffer()
else else
echo ' ' echo
echo 'File checkout canceled by the user' echom 'File checkout canceled by user.'
endif endif
endif endif
endfunction endfunction
@ -961,6 +1032,7 @@ function! merginal#openRebaseConflictsBuffer(...)
endfunction endfunction
augroup merginal augroup merginal
autocmd User Merginal_RebaseConflicts nnoremap <buffer> q <C-w>q
autocmd User Merginal_RebaseConflicts nnoremap <buffer> R :call merginal#tryRefreshRebaseConflictsBuffer(0)<Cr> autocmd User Merginal_RebaseConflicts nnoremap <buffer> R :call merginal#tryRefreshRebaseConflictsBuffer(0)<Cr>
autocmd User Merginal_RebaseConflicts nnoremap <buffer> <Cr> :call <SID>openMergeConflictUnderCursor()<Cr> autocmd User Merginal_RebaseConflicts nnoremap <buffer> <Cr> :call <SID>openMergeConflictUnderCursor()<Cr>
autocmd User Merginal_RebaseConflicts nnoremap <buffer> A :call <SID>addConflictedFileToStagingArea()<Cr> autocmd User Merginal_RebaseConflicts nnoremap <buffer> A :call <SID>addConflictedFileToStagingArea()<Cr>
@ -986,7 +1058,7 @@ endfunction
function! s:rebaseAction(remoteAction) function! s:rebaseAction(remoteAction)
if 'Merginal:Rebase'==bufname('') if 'Merginal:Rebase'==bufname('')
\|| 'Merginal:RebaseAmend'==bufname('') \|| 'Merginal:RebaseAmend'==bufname('')
echo merginal#runGitCommandInTreeReturnResult(b:merginal_repo,'--no-pager rebase --'.a:remoteAction) call merginal#runGitCommandInTreeEcho(b:merginal_repo,'--no-pager rebase --'.a:remoteAction)
call merginal#reloadBuffers() call merginal#reloadBuffers()
if merginal#isRebaseMode() if merginal#isRebaseMode()
call merginal#tryRefreshRebaseConflictsBuffer(0) call merginal#tryRefreshRebaseConflictsBuffer(0)
@ -1012,15 +1084,18 @@ function! merginal#openRebaseAmendBuffer(...)
call merginal#tryRefreshRebaseAmendBuffer() call merginal#tryRefreshRebaseAmendBuffer()
endfunction endfunction
autocmd User Merginal_RebaseAmend nnoremap <buffer> R :call merginal#tryRefreshRebaseAmendBuffer()<Cr> augroup merginal
autocmd User Merginal_RebaseAmend nnoremap <buffer> ra :call <SID>rebaseAction('abort')<Cr> autocmd User Merginal_RebaseAmend nnoremap <buffer> q <C-w>q
autocmd User Merginal_RebaseAmend nnoremap <buffer> rs :call <SID>rebaseAction('skip')<Cr> autocmd User Merginal_RebaseAmend nnoremap <buffer> R :call merginal#tryRefreshRebaseAmendBuffer()<Cr>
autocmd User Merginal_RebaseAmend nnoremap <buffer> rc :call <SID>rebaseAction('continue')<Cr> autocmd User Merginal_RebaseAmend nnoremap <buffer> ra :call <SID>rebaseAction('abort')<Cr>
autocmd User Merginal_RebaseAmend nnoremap <buffer> gd :call <SID>diffWithBranchUnderCursor()<Cr> autocmd User Merginal_RebaseAmend nnoremap <buffer> rs :call <SID>rebaseAction('skip')<Cr>
autocmd User Merginal_RebaseAmend nnoremap <buffer> rc :call <SID>rebaseAction('continue')<Cr>
autocmd User Merginal_RebaseAmend nnoremap <buffer> gd :call <SID>diffWithBranchUnderCursor()<Cr>
autocmd User Merginal_RebaseAmend nnoremap <buffer> gl :call <SID>historyLogForBranchUnderCursor()<Cr>
augroup END
function! merginal#tryRefreshRebaseAmendBuffer() function! merginal#tryRefreshRebaseAmendBuffer()
if 'Merginal:RebaseAmend'==bufname('') if 'Merginal:RebaseAmend'==bufname('')
"let l:gitStatusOutput=split(merginal#system(b:merginal_repo.git_command('status','--all')),'\r\n\|\n\|\r')
let l:currentLine=line('.') let l:currentLine=line('.')
let l:newBufferLines=[] let l:newBufferLines=[]
let l:amendedCommit=readfile(b:merginal_repo.dir('rebase-merge','amend')) let l:amendedCommit=readfile(b:merginal_repo.dir('rebase-merge','amend'))
@ -1042,7 +1117,7 @@ function! merginal#tryRefreshRebaseAmendBuffer()
setlocal modifiable setlocal modifiable
"Clear the buffer: "Clear the buffer:
normal ggdG silent normal! gg"_dG
"Write the new buffer lines: "Write the new buffer lines:
call setline(1,l:newBufferLines) call setline(1,l:newBufferLines)
"call setline(1,l:branchList) "call setline(1,l:branchList)
@ -1050,3 +1125,113 @@ function! merginal#tryRefreshRebaseAmendBuffer()
endif endif
return 0 return 0
endfunction endfunction
"Open the history log buffer
function! merginal#openHistoryLogBuffer(logBranch,...)
let l:currentFile=expand('%:~:.')
if merginal#openTuiBuffer('Merginal:HistoryLog',get(a:000,1,bufwinnr('Merginal:')))
doautocmd User Merginal_HistoryLog
endif
let b:merginal_branch=a:logBranch
"At any rate, refresh the buffer:
call merginal#tryRefreshHistoryLogBuffer()
endfunction
augroup merginal
autocmd User Merginal_HistoryLog nnoremap <buffer> q <C-w>q
autocmd User Merginal_HistoryLog nnoremap <buffer> R :call merginal#tryRefreshHistoryLogBuffer()<Cr>
autocmd User Merginal_HistoryLog nnoremap <buffer> <C-p> :call <SID>moveToNextOrPreviousCommit(-1)<Cr>
autocmd User Merginal_HistoryLog nnoremap <buffer> <C-n> :call <SID>moveToNextOrPreviousCommit(1)<Cr>
autocmd User Merginal_HistoryLog nnoremap <buffer> S :call <SID>printCommitUnderCurosr('fuller')<Cr>
autocmd User Merginal_HistoryLog nnoremap <buffer> ss :call <SID>printCommitUnderCurosr('fuller')<Cr>
autocmd User Merginal_HistoryLog nnoremap <buffer> C :call <SID>checkoutCommitUnderCurosr()<Cr>
autocmd User Merginal_HistoryLog nnoremap <buffer> cc :call <SID>checkoutCommitUnderCurosr()<Cr>
autocmd User Merginal_HistoryLog nnoremap <buffer> gd :call <SID>diffWithCommitUnderCursor()<Cr>
augroup END
function! merginal#tryRefreshHistoryLogBuffer()
if 'Merginal:HistoryLog'==bufname('')
let l:entryFormat = '%h %aN%n%ai%n%s%n'
let l:logLines = split(
\ merginal#system(b:merginal_repo.git_command(
\ '--no-pager', 'log', '--format='.l:entryFormat, b:merginal_branch.handle)),
\ '\r\n\|\n\|\r')
if empty(l:logLines[len(l:logLines) - 1])
call remove(l:logLines, len(l:logLines) - 1)
endif
let l:currentLine=line('.')
setlocal modifiable
"Clear the buffer:
silent normal! gg"_dG
"Write the log lines:
call setline(1,l:logLines)
setlocal nomodifiable
execute l:currentLine
endif
return 0
endfunction
"Exactly what it says on tin
function! s:printCommitUnderCurosr(format)
if 'Merginal:HistoryLog'==bufname('')
let l:commitHash = merginal#commitHash('.')
"Not using merginal#runGitCommandInTreeEcho because we are insterested
"in the result as more than just git command output. Also - using
"git-log with -1 instead of git-show because for some reason git-show
"ignores the --format flag...
echo merginal#system(b:merginal_repo.git_command('--no-pager', 'log', '-1', '--format='.a:format, l:commitHash))
endif
endfunction
"Exactly what it says on tin
function! s:checkoutCommitUnderCurosr()
if 'Merginal:HistoryLog'==bufname('')
let l:commitHash = merginal#commitHash('.')
call merginal#runGitCommandInTreeEcho(b:merginal_repo,'--no-pager checkout '.shellescape(l:commitHash))
call merginal#reloadBuffers()
call merginal#tryRefreshBranchListBuffer(0)
endif
endfunction
"Opens the diff files buffer
function! s:diffWithCommitUnderCursor()
if 'Merginal:HistoryLog'==bufname('')
let l:commitHash = merginal#commitHash('.')
call merginal#openDiffFilesBuffer(l:commitHash)
endif
endfunction
function! s:moveToNextOrPreviousCommit(direction)
if 'Merginal:HistoryLog'==bufname('')
let l:line = line('.')
"Find the first line of the current commit
while !empty(getline(l:line - 1))
let l:line -= 1
endwhile
"Find the first line of the next/prev commit
let l:line += a:direction
while !empty(getline(l:line - 1))
let l:line += a:direction
endwhile
if l:line <= 0 || line('$') <= l:line
"We reached past the first/last commit - go back!
let l:line -= a:direction
while !empty(getline(l:line - 1))
let l:line -= a:direction
endwhile
endif
execute l:line
endif
endfunction

View File

@ -4,7 +4,7 @@
Author: Idan Arye <https://github.com/idanarye/> Author: Idan Arye <https://github.com/idanarye/>
License: Same terms as Vim itself (see |license|) License: Same terms as Vim itself (see |license|)
Version: 1.4.0 Version: 1.5.0
INTRODUCTION *merginal* INTRODUCTION *merginal*
@ -19,6 +19,7 @@ offers interactive TUI for:
* Rebasing branches * Rebasing branches
* Solving merge conflicts * Solving merge conflicts
* Renaming branches * Renaming branches
* Viewing git history for branches
REQUIREMENTS *merginal-requirements* REQUIREMENTS *merginal-requirements*
@ -51,7 +52,8 @@ THE BRANCH LIST *merginal-branch-list*
The branch list shows a list of branches. While in that list, you can use the The branch list shows a list of branches. While in that list, you can use the
following keymaps to interact with the branches: following keymaps to interact with the branches:
R Refresh the buffer list. q Close the branch list.
R Refresh the branch list.
cc Checkout the branch under the cursor. cc Checkout the branch under the cursor.
ct Track the remote branch under the cursor. ct Track the remote branch under the cursor.
cT Track the remote branch under the cursor, prompting for a name. cT Track the remote branch under the cursor, prompting for a name.
@ -74,10 +76,13 @@ rb Rebase the currently checked out branch against the branch under the
ps Prompt to choose a remote to push the branch under the cursor. ps Prompt to choose a remote to push the branch under the cursor.
pS Prompt to choose a remote to force push the branch under the cursor. pS Prompt to choose a remote to force push the branch under the cursor.
pl Prompt to choose a remote to pull the branch under the cursor. pl Prompt to choose a remote to pull the branch under the cursor.
pr Prompt to choose a remote to pull-rebase the branch under the cursor.
pf Prompt to choose a remote to fetch the branch under the cursor. pf Prompt to choose a remote to fetch the branch under the cursor.
gd Open |merginal-diff-files| buffer to diff against the branch under the gd Open |merginal-diff-files| buffer to diff against the branch under the
cursor. cursor.
rn Prompt to rename the branch under the cursor. rn Prompt to rename the branch under the cursor.
gl Open |merginal-history-log| buffer to view the history of the branch
under the cursor.
MERGE CONFLICTS *merginal-merge-conflicts* MERGE CONFLICTS *merginal-merge-conflicts*
@ -85,6 +90,7 @@ MERGE CONFLICTS *merginal-merge-conflicts*
The merge conflicts buffer is used to solve merge conflicts. It shows all the The merge conflicts buffer is used to solve merge conflicts. It shows all the
files that have merge conflicts and offers the following keymaps: files that have merge conflicts and offers the following keymaps:
q Close the merge conflicts list.
R Refresh the merge conflicts list. R Refresh the merge conflicts list.
<Cr> Open the conflicted file under the cursor. <Cr> Open the conflicted file under the cursor.
aa Add the conflicted file under the cursor to the staging area. If that aa Add the conflicted file under the cursor to the staging area. If that
@ -105,7 +111,7 @@ aa Add the conflicted file under the cursor to the staging area. If that
was the last conflicted file, prompt the user to continue to the next was the last conflicted file, prompt the user to continue to the next
patch. patch.
A Same as aa. A Same as aa.
ra Abort the rebase ra Abort the rebase.
rc Continue to the next patch. rc Continue to the next patch.
rs Skip the current patch rs Skip the current patch
@ -114,29 +120,51 @@ REBASE AMEND *merginal-rebase-amend*
The rebase amend buffer is shown when you amend a patch during a rebase. It The rebase amend buffer is shown when you amend a patch during a rebase. It
shows the amended commit's shortened hash and commit message. Additionally, it shows the amended commit's shortened hash and commit message. Additionally, it
shows all the branches so you can diff against them while the patch. If offers shows all the branches so you can diff against them. If offers the following
the folloing keymaps: keymaps:
q Close the rebase amend buffer.
R Refresh the rebase amend buffer. R Refresh the rebase amend buffer.
gd Open |merginal-diff-files| buffer to diff against the branch under the gd Open |merginal-diff-files| buffer to diff against the branch under the
cursor. cursor.
ra Abort the rebase gl Open |merginal-history-log| buffer to view the history of the branch
under the cursor.
ra Abort the rebase.
rc Continue to the next patch. rc Continue to the next patch.
rs Skip the current patch rs Skip the current patch.
DIFF FILES *merginal-diff-files* DIFF FILES *merginal-diff-files*
The diff files buffer is used to diff against another branch. It displays all The diff files buffer is used to diff against another branch. It displays all
the differences between the currently checked out branch and the branch it was the differences between the currently checked out branch and the branch it was
opened against, and offerts the following keymaps: opened against, and offers the following keymaps:
q Close the diff files list.
R Refresh the diff files list. R Refresh the diff files list.
<Cr> Open the file under the cursor(if it exists in the currently checked <Cr> Open the file under the cursor (if it exists in the currently checked
out branch). out branch).
ds Split-diff against the file under the cursor(if it exists in the other ds Split-diff against the file under the cursor (if it exists in the other
branch) branch).
ds VSplit-diff against the file under the cursor(if it exists in the other dv VSplit-diff against the file under the cursor (if it exists in the other
branch) branch).
co Check out the file under the cursor(if it exists in the other branch) co Check out the file under the cursor (if it exists in the other branch)
into the current branch. into the current branch.
HISTORY LOG *merginal-history-log*
The history log buffer is used to show the history of a branch. It shows, for
each commit in the branch, the author, date and commit message subject, and
offers the following keymaps:
q Close the history log buffer.
R Refresh the history log buffer.
<C-p> Move the cursor to the previous commit.
<C-n> Move the cursor to the next commit.
ss Echo the commit details(using git's --format=fuller).
S Same as ss.
cc Checkout the commit under the cursor.
C Same as cc.
gd Open |merginal-diff-files| buffer to diff against the commit under the
cursor.

View File

@ -65,11 +65,17 @@
" "OND" : E.W.Myers, "An O(ND) Difference Algorithm and Its Variations" " "OND" : E.W.Myers, "An O(ND) Difference Algorithm and Its Variations"
" "Basic" : basic algorithm using edit graph and shortest edit distance " "Basic" : basic algorithm using edit graph and shortest edit distance
" "
" DiffCharExpr(iet, exd) function for the diffexpr option " DiffCharExpr(mxi, exd) function for the diffexpr option
" iet: 1 = internal algorithm, 0 = external diff command, " mxi: the maximum number of total lines of both windows to apply internal
" else = threshold value of differences to apply either " algorithm, apply external diff command when more lines
" exd: 1 = initially show exact differences, 0 = vim original ones " exd: 1 = initially show exact differences, 0 = vim original ones
" "
" Update : 4.9
" * Fixed DiffCharExpr() to check the number of total lines, not different
" lines only, of both windows and apply either internal algorithm or
" external diff command, in order to keep the appropriate performance
" for large files.
"
" Update : 4.81 " Update : 4.81
" * Enhanced to make DiffCharExpr() a bit faster by using uniq() or so. " * Enhanced to make DiffCharExpr() a bit faster by using uniq() or so.
" "
@ -209,15 +215,15 @@
" the initial version. " the initial version.
" "
" Author: Rick Howe " Author: Rick Howe
" Last Change: 2015/01/13 " Last Change: 2015/02/18
" Created: " Created:
" Requires: " Requires:
" Version: 4.81 " Version: 4.9
if exists("g:loaded_diffchar") if exists("g:loaded_diffchar")
finish finish
endif endif
let g:loaded_diffchar = 4.81 let g:loaded_diffchar = 4.9
let s:save_cpo = &cpo let s:save_cpo = &cpo
set cpo&vim set cpo&vim
@ -294,15 +300,12 @@ endif
" Set a diff expression " Set a diff expression
if !exists("g:DiffExpr") || g:DiffExpr if !exists("g:DiffExpr") || g:DiffExpr
if empty(&diffexpr) if empty(&diffexpr)
let &diffexpr = "DiffCharExpr(200, 1)" " set threshold and show exact diffs let &diffexpr = "DiffCharExpr(200, 1)" " set # of lines and show exact diffs
" let &diffexpr = "DiffCharExpr(1, 1)" " apply int algo and show exact diffs
" let &diffexpr = "DiffCharExpr(1, 0)" " apply int algo and show vim original
" let &diffexpr = "DiffCharExpr(0, 1)" " apply ext cmd and show exact diffs
" let &diffexpr = "DiffCharExpr(0, 0)" " apply ext cmd and show vim original " let &diffexpr = "DiffCharExpr(0, 0)" " apply ext cmd and show vim original
endif endif
endif endif
function! DiffCharExpr(iet, exd) function! DiffCharExpr(mxi, exd)
" read both files to be diff traced " read both files to be diff traced
let [f1, f2] = [readfile(v:fname_in), readfile(v:fname_new)] let [f1, f2] = [readfile(v:fname_in), readfile(v:fname_new)]
@ -312,35 +315,8 @@ function! DiffCharExpr(iet, exd)
return return
endif endif
" decide either internal or external
if a:iet > 1
" too short to check
if len(f1 + f2) < a:iet
let int = 1
else
" check how many differences and compare with a:iet
if exists("*uniq")
let int = (2 * len(uniq(sort(f1 + f2))) -
\len(uniq(sort(copy(f1)))) -
\len(uniq(sort(copy(f2))))) < a:iet
else
for k in [1, 2]
let d{k} = {}
for s in f{k}
let d{k}[empty(s) ?
\nr2char(10) : s] = 0
endfor
endfor
let int = (- len(d1) - len(d2) + 2 *
\len(extend(d1, d2, "keep"))) < a:iet
endif
endif
else
let int = a:iet
endif
" get a list of diff commands " get a list of diff commands
let dfcmd = int ? let dfcmd = (len(f1 + f2) <= a:mxi) ?
\s:ApplyIntDiffAlgorithm(f1, f2) : s:ApplyExtDiffCommand() \s:ApplyIntDiffAlgorithm(f1, f2) : s:ApplyExtDiffCommand()
" write to output file " write to output file