Skip to content

Instantly share code, notes, and snippets.

@AndrewRadev
Last active March 9, 2016 22:58
Show Gist options
  • Save AndrewRadev/1478115 to your computer and use it in GitHub Desktop.
Save AndrewRadev/1478115 to your computer and use it in GitHub Desktop.
Start a github pull request from Vim
" Usage:
"
" :Ginitpull [remote-name] [branch-name]
"
" Initiates a github pull request in the default browser, from the given
" branch name to master, on the given remote. Tab-completes both arguments.
"
" If called without any arguments, defaults to the "origin" remote and the
" current branch name, which is probably what you usually want.
"
command! -complete=custom,s:GinitpullComplete -nargs=* Ginitpull call s:Ginitpull(<f-args>)
function! s:Ginitpull(...)
let remote_name = get(a:000, 0, 'origin')
let branch_name = get(a:000, 1, '')
try
if branch_name == ''
let branch_name = s:CurrentGitBranch()
endif
let github_path = s:GithubPath(remote_name)
catch /./
echoerr v:exception
endtry
call s:OpenURL('https://github.com/'.github_path.'/pull/new/'.branch_name)
endfunction
function! s:GithubPath(remote_name)
for remote in split(system('git remote -v'), "\n")
if remote =~ '^'.a:remote_name
if remote =~ '[email protected]'
return substitute(remote, '.*[email protected]:\(.*\)\.git.*', '\1', '')
elseif remote =~ 'https\?://github\.com'
return substitute(remote, '.*https\?://github\.com/\(.*\)\.git.*', '\1', '')
endif
endif
endfor
throw 'No remote "'.a:remote_name.'" was found, or remote is not from github.'
endfunction
function! s:CurrentGitBranch()
" Search upwards for relevant files
let head_file = findfile('.git/HEAD', ';')
let dot_git_file = findfile('.git', ';')
if filereadable(head_file)
let head_ref = readfile(head_file)[0]
elseif filereadable(dot_git_file)
let module_file = readfile(dot_git_file)[0]
let module_file = substitute(module_file, 'gitdir: \(.*\)', '\1/HEAD', '')
let head_ref = readfile(module_file)[0]
else
throw 'This doesn''t look like a git repository, neither .git/HEAD nor .git were found.'
endif
return substitute(head_ref, 'ref: refs/heads/\(.*\)', '\1', '')
endfunction
function! s:GinitpullComplete(argument_lead, command_line, cursor_position)
if a:argument_lead != ''
" then we're in the middle of an argument, remove it from the command-line
let start_of_line = substitute(a:command_line, a:argument_lead.'$', '', '')
else
" just take the entire command-line
let start_of_line = a:command_line
end
let arg_count = len(split(start_of_line, '\s\+'))
if arg_count <= 1
" first argument: remote
let completions = s:TrimLines(system('git remote'))
elseif arg_count == 2
" second argument: branch
let completions = substitute(system('git branch'), '\*\s*', '', 'g')
let completions = s:TrimLines(completions)
else
" can't handle more than 2 arguments
let completions = ''
endif
return completions
endfunction
function! s:OpenURL(url)
let url = shellescape(a:url)
if has('mac')
silent call system('open '.url)
elseif has('unix')
if executable('xdg-open')
silent call system('xdg-open '.url.' 2>&1 > /dev/null &')
else
echoerr "You need to install xdg-open to be able to open urls"
return
end
else
echoerr "Don't know how to open a URL on this system"
return
end
echo "Opening ".url
endfunction
function! s:Trim(s)
let s = a:s
let s = substitute(s, '^\_s\+', '', '')
let s = substitute(s, '\_s\+$', '', '')
return s
endfunction
" Trim a list of items
function! s:TrimList(ss)
return map(a:ss, 's:Trim(v:val)')
endfunction
" Trim each line in the given string
function! s:TrimLines(s)
return join(s:TrimList(split(a:s, "\n")), "\n")
endfunction
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment