diff --git a/autoload/magit/git.vim b/autoload/magit/git.vim index 27cd507..cfc581d 100644 --- a/autoload/magit/git.vim +++ b/autoload/magit/git.vim @@ -339,3 +339,9 @@ function! magit#git#get_remote_branch(ref, type) return "none" endtry endfunction + + +function! magit#git#get_branches() + return magit#sys#system(g:magit_git_cmd . " branch -a") +endfunction + diff --git a/autoload/magit/mapping.vim b/autoload/magit/mapping.vim index 6bffbf8..50fbcfe 100644 --- a/autoload/magit/mapping.vim +++ b/autoload/magit/mapping.vim @@ -9,6 +9,9 @@ let g:magit_commit_fixup_mapping = get(g:, 'magit_commit_fixup_mapping', let g:magit_close_commit_mapping = get(g:, 'magit_close_commit_mapping', 'CU' ) let g:magit_reload_mapping = get(g:, 'magit_reload_mapping', 'R' ) let g:magit_edit_mapping = get(g:, 'magit_edit_mapping', 'E' ) +let g:magit_checkout_mapping = get(g:, 'magit_checkout_mapping', 'CBB') +let g:magit_checkout_last_mapping = get(g:, 'magit_checkout_last_mapping', 'CB-') +let g:magit_checkout_force_mapping = get(g:, 'magit_checkout_force_mapping', 'CBF') let g:magit_jump_next_hunk = get(g:, 'magit_jump_next_hunk', '') let g:magit_jump_prev_hunk = get(g:, 'magit_jump_prev_hunk', '') @@ -157,6 +160,13 @@ function! magit#mapping#set_default() call s:mg_set_mapping('n', g:magit_jump_prev_hunk, \ "magit#jump_hunk('P')") + call s:mg_set_mapping('n', g:magit_checkout_mapping, + \ "magit#checkout_branch('B')") + call s:mg_set_mapping('n', g:magit_checkout_last_mapping, + \ "magit#checkout_branch('-')") + call s:mg_set_mapping('n', g:magit_checkout_force_mapping, + \ "magit#checkout_branch('F')") + for mapping in g:magit_folding_toggle_mapping " trick to pass '' in a mapping command without being interpreted let func_arg = ( mapping ==? "" ) ? '+' : mapping @@ -227,6 +237,14 @@ function! magit#mapping#set_default() \' modifying the previous commit message', \g:magit_close_commit_mapping \. ' commit undo, cancel and close current commit message', +\g:magit_checkout_mapping +\. ' From stage mode: set branch mode in normal flavor', +\' From branch mode: checkout branch on line cursor is on.', +\g:magit_checkout_force_mapping +\. ' From stage mode: set branch mode in force flavor', +\' From branch mode: checkout/reset branch on line cursor is on.', +\g:magit_checkout_last_mapping +\. ' From stage mode: checkout previous branch.', \g:magit_reload_mapping \.' refresh magit buffer', \g:magit_diff_shrink.','.g:magit_diff_enlarge.','.g:magit_diff_reset diff --git a/common/magit_common.vim b/common/magit_common.vim index f1f8123..e0bb6ae 100644 --- a/common/magit_common.vim +++ b/common/magit_common.vim @@ -7,7 +7,8 @@ let g:magit_sections = { \ 'staged': 'Staged changes', \ 'unstaged': 'Unstaged changes', \ 'commit': 'Commit message', - \ 'stash': 'Stash list' + \ 'stash': 'Stash list', + \ 'branches': 'Branch list' \ } let g:magit_section_info = { diff --git a/doc/vimagit.txt b/doc/vimagit.txt index e0ef71e..0c87a00 100644 --- a/doc/vimagit.txt +++ b/doc/vimagit.txt @@ -100,15 +100,26 @@ Commit mode has two flavors. By the way, you can also perform all stage mode actions in commit mode. +BRANCH MODE *vimagit-branch-mode* + +In this mode, the `Branch list` section is open, where you can choose any +existing local or remote branch as well as create a new local branch. +There are two flavors to Branch mode: + +* `normal`: selected branch will be created/checked out normally. +* `force`: selected branch will be created if nonexistant, or reset if existant. + SECTIONS *vimagit-sections* IMPORTANT: mappings can have different meanings regarding the cursor position. -There are 5 sections: +There are 6 sections: * Info: this section display some information about the git repository, like the current branch and the HEAD commit. * Commit message: this section appears in commit mode (see below). It contains the message to be committed. +* Branch list: this section contains all of the current local and remote + brances. * Staged changes: this sections contains all staged files/hunks, ready to commit. * Unstaged changes: this section contains all unstaged and untracked @@ -218,6 +229,27 @@ section only. + *vimagit-CBB* *magit#checkout_branch('B')* + *vimagit-g:magit_checkout_mapping* + From `stage mode`, set branch mode in `normal` flavor and show + "Branches" section. When used in `branch mode`, the branch name on + the line the cursor is on will be checked out. Created locally if + needed. This is equivalent to `git checkout -b [remote]` + + *vimagit-CBF* *magit#checkout_branch('F')* + *vimagit-g:magit_checkout_force_mapping* + From `stage mode` or `branch mode`, set branching to `force` mode, + and checkout any new or existing branches with while the cursor + is on the line of the desired branch. This is equivalent to + `git checkout -B [remote]`. + + *vimagit-CB-* *magit#checkout_branch('-')* + *vimagit-g:magit_checkout_last_mapping* + From `stage mode`, instantly checkout the previous branch. Any + changes are carried along the checkout. This is equivalent to + `git checkout -`. + + *vimagit-* *magit#jump_hunk()* *vimagit-* *vimagit-g:magit_jump_next_hunk* diff --git a/plugin/magit.vim b/plugin/magit.vim index 1455382..b6d5cf9 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -28,7 +28,7 @@ let g:magit_default_show_all_files = get(g:, 'magit_default_show_all_files', let g:magit_default_fold_level = get(g:, 'magit_default_fold_level', 1) let g:magit_auto_close = get(g:, 'magit_auto_close', 0) let g:magit_auto_foldopen = get(g:, 'magit_auto_foldopen', 1) -let g:magit_default_sections = get(g:, 'magit_default_sections', ['info', 'global_help', 'commit', 'staged', 'unstaged']) +let g:magit_default_sections = get(g:, 'magit_default_sections', ['info', 'global_help', 'branch', 'commit', 'staged', 'unstaged']) let g:magit_discard_untracked_do_delete = get(g:, 'magit_discard_untracked_do_delete', 0) let g:magit_refresh_gutter = get(g:, 'magit_refresh_gutter' , 1) @@ -249,6 +249,25 @@ function! s:mg_get_commit_section() endif endfunction +" s:mg_get_branches_section: this function writes in current buffer the commit +" section. It is a commit message, depending on b:magit_current_commit_mode +" WARNING: this function writes in file, it should only be called through +" protected functions like magit#update_buffer +" param[in] b:magit_show_branches: +function! s:mg_get_branches_section() + if ( b:magit_show_branches == 1 ) + silent put =g:magit_sections.branches + silent put =magit#utils#underline(g:magit_sections.branches) + + let branch_list = magit#git#get_branches() + silent put ='*Close*' + silent put ='New Branch:' + silent put =branch_list + silent put ='' + silent put ='' + endif +endfunction + " s:mg_search_block: helper function, to get start and end line of a block, " giving a start and multiple end pattern " a "pattern parameter" is a List: @@ -390,6 +409,42 @@ function! s:mg_git_commit(mode) abort let b:magit_just_commited = 1 endfunction +" s:mg_git_checkout_branch: checks out a branch (local or remote) +" param[in]: branch_name specifies the name of the branch +" remote specifies the name of the remote +" = '' specifies a local branch +" return none +function! s:mg_git_checkout_branch(branch_name, remote) + " Update the name of the remote branch (format: 'origin/master') + if ( a:remote != '' ) + let remote_name = " " . a:remote . "/" . a:branch_name + endif + + if ( b:magit_current_checkout_flag == 'B' && a:remote != '' ) + let checkout_cmd = g:magit_git_cmd . " checkout -b " . + \ a:branch_name . " " . remote_name + elseif ( b:magit_current_checkout_flag == 'B' && b:magit_creating_new_branch == 1) + let checkout_cmd = g:magit_git_cmd . " checkout -b " . + \ a:branch_name + elseif ( b:magit_current_checkout_flag == 'B' || b:magit_current_checkout_flag == '-' ) + let checkout_cmd = g:magit_git_cmd . " checkout " . + \ a:branch_name + elseif ( b:magit_current_checkout_flag == 'F' && a:remote != '' && b:magit_creating_new_branch == 0 ) + let checkout_cmd = g:magit_git_cmd . " checkout -B " . + \ a:branch_name . remote_name + elseif ( b:magit_current_checkout_flag == 'F' && b:magit_creating_new_branch == 1 ) + let checkout_cmd = g:magit_git_cmd . " checkout -B " . + \ a:branch_name + endif + + try + silent! let git_result = magit#sys#system(checkout_cmd) + catch 'shell error' + call magit#sys#print_shell_error() + echoerr "Could not checkout branch ''" . a:branch_name . "'" + endtry +endfunction + " s:mg_select_file_block: select the whole diff file, relative to the current " cursor position " nota: if the cursor is not in a diff file when the function is called, this @@ -531,6 +586,7 @@ let g:magit_last_updated_buffer = '' let s:mg_display_functions = { \ 'info': { 'fn': function("s:mg_get_info"), 'arg': []}, \ 'global_help': { 'fn': function("magit#mapping#get_section_help"), 'arg': ['global']}, + \ 'branch': { 'fn': function("s:mg_get_branches_section"), 'arg': []}, \ 'commit': { 'fn': function("s:mg_get_commit_section"), 'arg': []}, \ 'staged': { 'fn': function("s:mg_get_staged_section"), 'arg': ['staged']}, \ 'unstaged': { 'fn': function("s:mg_get_staged_section"), 'arg': ['unstaged']}, @@ -851,6 +907,9 @@ function! magit#show_magit(display, ...) let b:magit_current_commit_mode='' let b:magit_commit_newly_open=0 + let b:magit_show_branches=0 + let b:magit_current_branch_mode='' + let b:magit_diff_context=3 let b:magit_just_commited = 0 @@ -1273,6 +1332,73 @@ function! magit#jump_hunk(dir) endif endfunction +" magit#checkout_branch: entry function for branch checkout +" param[in]: opt specifies the option for checkout +" 'B' is for '-b' +" 'F' is for '-B' +" '-' is to checkout previous branch +function! magit#checkout_branch(opt) + let b:magit_current_checkout_flag = a:opt + if ( b:magit_show_branches == 0 && a:opt != '-' ) + let b:magit_show_branches = 1 + else + call magit#select_branch() + endif + + if ( a:opt == '-' ) + call mg_git_checkout_branch('-', '') + endif + + call magit#update_buffer() +endfunction + +" magit#select_branch: handles the parsing and passing of the selected branch +" return no +function! magit#select_branch() + let line = trim(getline(".")) + + " cannot checkout HEAD + if ( match(line, "HEAD") != -1 ) + let b:magit_show_branches = 0 + call magit#update_buffer() + return + endif + + " close buffer if requested + if ( match(line, "*Close*") != -1 ) + let b:magit_show_branches = 0 + call magit#update_buffer() + return + endif + + " check whether attempting to checkout remote branch + " and pass to s:mg_git_checkout_branch() with correct + clean options + let is_remote = match(line, "remotes") + if ( is_remote != -1 ) + let line = substitute(line, "^remotes/", "", "") + let remote = matchstr(line, "[^/]*") + let branch = matchstr(line, ".*", len(remote)+1) + call mg_git_checkout_branch(branch, remote) + else + " disregard if attempting to checkout current branch + if ( match(line, "*") != -1 ) + echo "You are already checked out on this branch..." + return + else + " handle custom input for new branch + if ( match(line, "^New Branch:") != -1 ) + let anti_line = matchstr(line, "[^:]*") + let line = trim(matchstr(line, ".*", len(anti_line)+1)) + let b:magit_creating_new_branch = 1 + else + let b:magit_creating_new_branch = 0 + endif + + call mg_git_checkout_branch(line, '') + endif + endif +endfunction + " magit#get_staged_files: function returning an array with staged files names " return: an array with staged files names function! magit#get_staged_files() @@ -1348,6 +1474,10 @@ function! magit#get_current_mode() return "COMMIT" elseif ( b:magit_current_commit_mode == 'CA' ) return "AMEND" + elseif ( b:magit_current_checkout_flag == 'B' ) + return "BRANCH" + elseif ( b:magit_current_checkout_flag == 'F' ) + return "FORCE" endif endfunction