1from __future__ import division, absolute_import, unicode_literals 2import os 3 4from . import cmds 5from . import core 6from . import difftool 7from . import gitcmds 8from . import icons 9from . import qtutils 10from .i18n import N_ 11from .interaction import Interaction 12from .widgets import completion 13from .widgets import editremotes 14from .widgets.browse import BrowseBranch 15from .widgets.selectcommits import select_commits 16from .widgets.selectcommits import select_commits_and_output 17 18 19def delete_branch(context): 20 """Launch the 'Delete Branch' dialog.""" 21 icon = icons.discard() 22 branch = choose_branch(context, N_('Delete Branch'), N_('Delete'), icon=icon) 23 if not branch: 24 return 25 cmds.do(cmds.DeleteBranch, context, branch) 26 27 28def delete_remote_branch(context): 29 """Launch the 'Delete Remote Branch' dialog.""" 30 remote_branch = choose_remote_branch( 31 context, N_('Delete Remote Branch'), N_('Delete'), icon=icons.discard() 32 ) 33 if not remote_branch: 34 return 35 remote, branch = gitcmds.parse_remote_branch(remote_branch) 36 if remote and branch: 37 cmds.do(cmds.DeleteRemoteBranch, context, remote, branch) 38 39 40def browse_current(context): 41 """Launch the 'Browse Current Branch' dialog.""" 42 branch = gitcmds.current_branch(context) 43 BrowseBranch.browse(context, branch) 44 45 46def browse_other(context): 47 """Prompt for a branch and inspect content at that point in time.""" 48 # Prompt for a branch to browse 49 branch = choose_ref(context, N_('Browse Commits...'), N_('Browse')) 50 if not branch: 51 return 52 BrowseBranch.browse(context, branch) 53 54 55def checkout_branch(context): 56 """Launch the 'Checkout Branch' dialog.""" 57 branch = choose_potential_branch(context, N_('Checkout Branch'), N_('Checkout')) 58 if not branch: 59 return 60 cmds.do(cmds.CheckoutBranch, context, branch) 61 62 63def cherry_pick(context): 64 """Launch the 'Cherry-Pick' dialog.""" 65 revs, summaries = gitcmds.log_helper(context, all=True) 66 commits = select_commits( 67 context, N_('Cherry-Pick Commit'), revs, summaries, multiselect=False 68 ) 69 if not commits: 70 return 71 cmds.do(cmds.CherryPick, context, commits) 72 73 74def new_repo(context): 75 """Prompt for a new directory and create a new Git repository 76 77 :returns str: repository path or None if no repository was created. 78 79 """ 80 git = context.git 81 path = qtutils.opendir_dialog(N_('New Repository...'), core.getcwd()) 82 if not path: 83 return None 84 # Avoid needlessly calling `git init`. 85 if git.is_git_repository(path): 86 # We could prompt here and confirm that they really didn't 87 # mean to open an existing repository, but I think 88 # treating it like an "Open" is a sensible DWIM answer. 89 return path 90 91 status, out, err = git.init(path) 92 if status == 0: 93 return path 94 95 title = N_('Error Creating Repository') 96 Interaction.command_error(title, 'git init', status, out, err) 97 return None 98 99 100def open_new_repo(context): 101 dirname = new_repo(context) 102 if not dirname: 103 return 104 cmds.do(cmds.OpenRepo, context, dirname) 105 106 107def new_bare_repo(context): 108 result = None 109 repo = prompt_for_new_bare_repo() 110 if not repo: 111 return result 112 # Create bare repo 113 ok = cmds.do(cmds.NewBareRepo, context, repo) 114 if not ok: 115 return result 116 # Add a new remote pointing to the bare repo 117 parent = qtutils.active_window() 118 add_remote = editremotes.add_remote( 119 context, parent, name=os.path.basename(repo), url=repo, readonly_url=True 120 ) 121 if add_remote: 122 result = repo 123 124 return result 125 126 127def prompt_for_new_bare_repo(): 128 path = qtutils.opendir_dialog(N_('Select Directory...'), core.getcwd()) 129 if not path: 130 return None 131 132 bare_repo = None 133 default = os.path.basename(core.getcwd()) 134 if not default.endswith('.git'): 135 default += '.git' 136 while not bare_repo: 137 name, ok = qtutils.prompt( 138 N_('Enter a name for the new bare repo'), 139 title=N_('New Bare Repository...'), 140 text=default, 141 ) 142 if not name or not ok: 143 return None 144 if not name.endswith('.git'): 145 name += '.git' 146 repo = os.path.join(path, name) 147 if core.isdir(repo): 148 Interaction.critical(N_('Error'), N_('"%s" already exists') % repo) 149 else: 150 bare_repo = repo 151 152 return bare_repo 153 154 155def export_patches(context): 156 """Run 'git format-patch' on a list of commits.""" 157 revs, summaries = gitcmds.log_helper(context) 158 to_export_and_output = select_commits_and_output( 159 context, N_('Export Patches'), revs, summaries 160 ) 161 if not to_export_and_output['to_export']: 162 return 163 164 cmds.do( 165 cmds.FormatPatch, 166 context, 167 reversed(to_export_and_output['to_export']), 168 reversed(revs), 169 to_export_and_output['output'], 170 ) 171 172 173def diff_expression(context): 174 """Diff using an arbitrary expression.""" 175 tracked = gitcmds.tracked_branch(context) 176 current = gitcmds.current_branch(context) 177 if tracked and current: 178 ref = tracked + '..' + current 179 else: 180 ref = '@{upstream}..' 181 difftool.diff_expression(context, qtutils.active_window(), ref) 182 183 184def open_repo(context): 185 model = context.model 186 dirname = qtutils.opendir_dialog(N_('Open Git Repository...'), model.getcwd()) 187 if not dirname: 188 return 189 cmds.do(cmds.OpenRepo, context, dirname) 190 191 192def open_repo_in_new_window(context): 193 """Spawn a new cola session.""" 194 model = context.model 195 dirname = qtutils.opendir_dialog(N_('Open Git Repository...'), model.getcwd()) 196 if not dirname: 197 return 198 cmds.do(cmds.OpenNewRepo, context, dirname) 199 200 201def load_commitmsg(context): 202 """Load a commit message from a file.""" 203 model = context.model 204 filename = qtutils.open_file(N_('Load Commit Message'), directory=model.getcwd()) 205 if filename: 206 cmds.do(cmds.LoadCommitMessageFromFile, context, filename) 207 208 209def choose_from_dialog(get, context, title, button_text, default, icon=None): 210 parent = qtutils.active_window() 211 return get(context, title, button_text, parent, default=default, icon=icon) 212 213 214def choose_ref(context, title, button_text, default=None, icon=None): 215 return choose_from_dialog( 216 completion.GitRefDialog.get, context, title, button_text, default, icon=icon 217 ) 218 219 220def choose_branch(context, title, button_text, default=None, icon=None): 221 return choose_from_dialog( 222 completion.GitBranchDialog.get, context, title, button_text, default, icon=icon 223 ) 224 225 226def choose_potential_branch(context, title, button_text, default=None, icon=None): 227 return choose_from_dialog( 228 completion.GitCheckoutBranchDialog.get, 229 context, 230 title, 231 button_text, 232 default, 233 icon=icon, 234 ) 235 236 237def choose_remote_branch(context, title, button_text, default=None, icon=None): 238 return choose_from_dialog( 239 completion.GitRemoteBranchDialog.get, 240 context, 241 title, 242 button_text, 243 default, 244 icon=icon, 245 ) 246 247 248def review_branch(context): 249 """Diff against an arbitrary revision, branch, tag, etc.""" 250 branch = choose_ref(context, N_('Select Branch to Review'), N_('Review')) 251 if not branch: 252 return 253 merge_base = gitcmds.merge_base_parent(context, branch) 254 difftool.diff_commits(context, qtutils.active_window(), merge_base, branch) 255 256 257def rename_branch(context): 258 """Launch the 'Rename Branch' dialogs.""" 259 branch = choose_branch(context, N_('Rename Existing Branch'), N_('Select')) 260 if not branch: 261 return 262 new_branch = choose_branch(context, N_('Enter New Branch Name'), N_('Rename')) 263 if not new_branch: 264 return 265 cmds.do(cmds.RenameBranch, context, branch, new_branch) 266 267 268def reset_soft(context): 269 title = N_('Reset Branch (Soft)') 270 ok_text = N_('Reset Branch') 271 ref = choose_ref(context, title, ok_text, default='HEAD^') 272 if ref: 273 cmds.do(cmds.ResetSoft, context, ref) 274 275 276def reset_mixed(context): 277 title = N_('Reset Branch and Stage (Mixed)') 278 ok_text = N_('Reset') 279 ref = choose_ref(context, title, ok_text, default='HEAD^') 280 if ref: 281 cmds.do(cmds.ResetMixed, context, ref) 282 283 284def reset_keep(context): 285 title = N_('Reset All (Keep Unstaged Changes)') 286 ref = choose_ref(context, title, N_('Reset and Restore')) 287 if ref: 288 cmds.do(cmds.ResetKeep, context, ref) 289 290 291def reset_merge(context): 292 title = N_('Restore Worktree and Reset All (Merge)') 293 ok_text = N_('Reset and Restore') 294 ref = choose_ref(context, title, ok_text, default='HEAD^') 295 if ref: 296 cmds.do(cmds.ResetMerge, context, ref) 297 298 299def reset_hard(context): 300 title = N_('Restore Worktree and Reset All (Hard)') 301 ok_text = N_('Reset and Restore') 302 ref = choose_ref(context, title, ok_text, default='HEAD^') 303 if ref: 304 cmds.do(cmds.ResetHard, context, ref) 305 306 307def restore_worktree(context): 308 title = N_('Restore Worktree') 309 ok_text = N_('Restore Worktree') 310 ref = choose_ref(context, title, ok_text, default='HEAD^') 311 if ref: 312 cmds.do(cmds.RestoreWorktree, context, ref) 313 314 315def install(): 316 """Install the GUI-model interaction hooks""" 317 Interaction.choose_ref = staticmethod(choose_ref) 318