13 from .interaction
import Interaction
14 from .widgets
import completion
15 from .widgets
import editremotes
16 from .widgets
import switcher
17 from .widgets
.browse
import BrowseBranch
18 from .widgets
.selectcommits
import select_commits
19 from .widgets
.selectcommits
import select_commits_and_output
22 def delete_branch(context
):
23 """Launch the 'Delete Branch' dialog."""
24 icon
= icons
.discard()
25 branch
= choose_branch(context
, N_('Delete Branch'), N_('Delete'), icon
=icon
)
28 cmds
.do(cmds
.DeleteBranch
, context
, branch
)
31 def delete_remote_branch(context
):
32 """Launch the 'Delete Remote Branch' dialog."""
33 remote_branch
= choose_remote_branch(
34 context
, N_('Delete Remote Branch'), N_('Delete'), icon
=icons
.discard()
38 remote
, branch
= gitcmds
.parse_remote_branch(remote_branch
)
40 cmds
.do(cmds
.DeleteRemoteBranch
, context
, remote
, branch
)
43 def browse_current(context
):
44 """Launch the 'Browse Current Branch' dialog."""
45 branch
= gitcmds
.current_branch(context
)
46 BrowseBranch
.browse(context
, branch
)
49 def browse_other(context
):
50 """Prompt for a branch and inspect content at that point in time."""
51 # Prompt for a branch to browse
52 branch
= choose_ref(context
, N_('Browse Commits...'), N_('Browse'))
55 BrowseBranch
.browse(context
, branch
)
58 def checkout_branch(context
, default
=None):
59 """Launch the 'Checkout Branch' dialog."""
60 branch
= choose_potential_branch(
61 context
, N_('Checkout Branch'), N_('Checkout'), default
=default
65 cmds
.do(cmds
.CheckoutBranch
, context
, branch
)
68 def cherry_pick(context
):
69 """Launch the 'Cherry-Pick' dialog."""
70 revs
, summaries
= gitcmds
.log_helper(context
, all
=True)
71 commits
= select_commits(
72 context
, N_('Cherry-Pick Commit'), revs
, summaries
, multiselect
=False
76 cmds
.do(cmds
.CherryPick
, context
, commits
)
79 def new_repo(context
):
80 """Prompt for a new directory and create a new Git repository
82 :returns str: repository path or None if no repository was created.
86 path
= qtutils
.opendir_dialog(N_('New Repository...'), core
.getcwd())
89 # Avoid needlessly calling `git init`.
90 if git
.is_git_repository(path
):
91 # We could prompt here and confirm that they really didn't
92 # mean to open an existing repository, but I think
93 # treating it like an "Open" is a sensible DWIM answer.
96 status
, out
, err
= git
.init(path
)
100 title
= N_('Error Creating Repository')
101 Interaction
.command_error(title
, 'git init', status
, out
, err
)
105 def open_new_repo(context
):
106 """Create a new repository and open it"""
107 dirname
= new_repo(context
)
110 cmds
.do(cmds
.OpenRepo
, context
, dirname
)
113 def new_bare_repo(context
):
114 """Create a bare repository and configure a remote pointing to it"""
116 repo
= prompt_for_new_bare_repo()
120 ok
= cmds
.do(cmds
.NewBareRepo
, context
, repo
)
123 # Add a new remote pointing to the bare repo
124 parent
= qtutils
.active_window()
125 add_remote
= editremotes
.add_remote(
126 context
, parent
, name
=os
.path
.basename(repo
), url
=repo
, readonly_url
=True
134 def prompt_for_new_bare_repo():
135 """Prompt for a directory and name for a new bare repository"""
136 path
= qtutils
.opendir_dialog(N_('Select Directory...'), core
.getcwd())
141 default
= os
.path
.basename(core
.getcwd())
142 if not default
.endswith('.git'):
145 name
, ok
= qtutils
.prompt(
146 N_('Enter a name for the new bare repo'),
147 title
=N_('New Bare Repository...'),
150 if not name
or not ok
:
152 if not name
.endswith('.git'):
154 repo
= os
.path
.join(path
, name
)
156 Interaction
.critical(N_('Error'), N_('"%s" already exists') % repo
)
163 def export_patches(context
):
164 """Run 'git format-patch' on a list of commits."""
165 revs
, summaries
= gitcmds
.log_helper(context
)
166 to_export_and_output
= select_commits_and_output(
167 context
, N_('Export Patches'), revs
, summaries
169 if not to_export_and_output
['to_export']:
175 reversed(to_export_and_output
['to_export']),
177 output
=to_export_and_output
['output'],
181 def diff_against_commit(context
):
182 """Diff against any commit and checkout changes using the Diff Editor"""
183 icon
= icons
.compare()
184 ref
= choose_ref(context
, N_('Diff Against Commit'), N_('Diff'), icon
=icon
)
187 cmds
.do(cmds
.DiffAgainstCommitMode
, context
, ref
)
190 def diff_expression(context
):
191 """Diff using an arbitrary expression."""
192 tracked
= gitcmds
.tracked_branch(context
)
193 current
= gitcmds
.current_branch(context
)
194 if tracked
and current
:
195 ref
= tracked
+ '..' + current
197 ref
= '@{upstream}..'
198 difftool
.diff_expression(context
, qtutils
.active_window(), ref
)
201 def open_repo(context
):
202 """Open a repository in the current window"""
203 model
= context
.model
204 dirname
= qtutils
.opendir_dialog(N_('Open Git Repository'), model
.getcwd())
207 cmds
.do(cmds
.OpenRepo
, context
, dirname
)
210 def open_repo_in_new_window(context
):
211 """Spawn a new cola session."""
212 model
= context
.model
213 dirname
= qtutils
.opendir_dialog(N_('Open Git Repository'), model
.getcwd())
216 cmds
.do(cmds
.OpenNewRepo
, context
, dirname
)
219 def open_quick_repo_search(context
, parent
=None):
220 """Open a Quick Repository Search dialog"""
222 parent
= qtutils
.active_window()
223 settings
= context
.settings
224 items
= settings
.bookmarks
+ settings
.recent
228 default_repo
= cfg
.get('cola.defaultrepo')
230 entries
= QtGui
.QStandardItemModel()
232 normalize
= display
.normalize_path
233 star_icon
= icons
.star()
234 folder_icon
= icons
.folder()
237 key
= normalize(item
['path'])
242 if default_repo
== item
['path']:
247 entry
= switcher
.switcher_item(key
, icon
, name
)
248 entries
.appendRow(entry
)
251 title
= N_('Quick Open Repository')
252 place_holder
= N_('Search repositories by name...')
253 switcher
.switcher_inner_view(
257 place_holder
=place_holder
,
258 enter_action
=lambda entry
: cmds
.do(cmds
.OpenRepo
, context
, entry
.key
),
263 def load_commitmsg(context
):
264 """Load a commit message from a file."""
265 model
= context
.model
266 filename
= qtutils
.open_file(N_('Load Commit Message'), directory
=model
.getcwd())
268 cmds
.do(cmds
.LoadCommitMessageFromFile
, context
, filename
)
271 def choose_from_dialog(get
, context
, title
, button_text
, default
, icon
=None):
272 """Choose a value from a dialog using the `get` method"""
273 parent
= qtutils
.active_window()
274 return get(context
, title
, button_text
, parent
, default
=default
, icon
=icon
)
277 def choose_ref(context
, title
, button_text
, default
=None, icon
=None):
278 """Choose a Git ref and return it"""
279 return choose_from_dialog(
280 completion
.GitRefDialog
.get
, context
, title
, button_text
, default
, icon
=icon
284 def choose_branch(context
, title
, button_text
, default
=None, icon
=None):
285 """Choose a branch and return either the chosen branch or an empty value"""
286 return choose_from_dialog(
287 completion
.GitBranchDialog
.get
, context
, title
, button_text
, default
, icon
=icon
291 def choose_potential_branch(context
, title
, button_text
, default
=None, icon
=None):
292 """Choose a "potential" branch for checking out.
294 This dialog includes remote branches from which new local branches can be created.
296 return choose_from_dialog(
297 completion
.GitCheckoutBranchDialog
.get
,
306 def choose_remote_branch(context
, title
, button_text
, default
=None, icon
=None):
307 """Choose a remote branch"""
308 return choose_from_dialog(
309 completion
.GitRemoteBranchDialog
.get
,
318 def review_branch(context
):
319 """Diff against an arbitrary revision, branch, tag, etc."""
320 branch
= choose_ref(context
, N_('Select Branch to Review'), N_('Review'))
323 merge_base
= gitcmds
.merge_base_parent(context
, branch
)
324 difftool
.diff_commits(context
, qtutils
.active_window(), merge_base
, branch
)
327 def rename_branch(context
):
328 """Launch the 'Rename Branch' dialogs."""
329 branch
= choose_branch(context
, N_('Rename Existing Branch'), N_('Select'))
332 new_branch
= choose_branch(context
, N_('Enter New Branch Name'), N_('Rename'))
335 cmds
.do(cmds
.RenameBranch
, context
, branch
, new_branch
)
338 def reset_soft(context
):
339 """Run "git reset --soft" to reset the branch HEAD"""
340 title
= N_('Reset Branch (Soft)')
341 ok_text
= N_('Reset Branch')
342 ref
= choose_ref(context
, title
, ok_text
, default
='HEAD^')
344 cmds
.do(cmds
.ResetSoft
, context
, ref
)
347 def reset_mixed(context
):
348 """Run "git reset --mixed" to reset the branch HEAD and staging area"""
349 title
= N_('Reset Branch and Stage (Mixed)')
350 ok_text
= N_('Reset')
351 ref
= choose_ref(context
, title
, ok_text
, default
='HEAD^')
353 cmds
.do(cmds
.ResetMixed
, context
, ref
)
356 def reset_keep(context
):
357 """Run "git reset --keep" safe reset to avoid clobbering local changes"""
358 title
= N_('Reset All (Keep Unstaged Changes)')
359 ref
= choose_ref(context
, title
, N_('Reset and Restore'))
361 cmds
.do(cmds
.ResetKeep
, context
, ref
)
364 def reset_merge(context
):
365 """Run "git reset --merge" to reset the working tree and staging area
367 The staging area is allowed to carry forward unmerged index entries,
368 but if any unstaged changes would be clobbered by the reset then the
371 title
= N_('Restore Worktree and Reset All (Merge)')
372 ok_text
= N_('Reset and Restore')
373 ref
= choose_ref(context
, title
, ok_text
, default
='HEAD^')
375 cmds
.do(cmds
.ResetMerge
, context
, ref
)
378 def reset_hard(context
):
379 """Run "git reset --hard" to fully reset the working tree and staging area"""
380 title
= N_('Restore Worktree and Reset All (Hard)')
381 ok_text
= N_('Reset and Restore')
382 ref
= choose_ref(context
, title
, ok_text
, default
='HEAD^')
384 cmds
.do(cmds
.ResetHard
, context
, ref
)
387 def restore_worktree(context
):
388 """Restore the worktree to the content from the specified commit"""
389 title
= N_('Restore Worktree')
390 ok_text
= N_('Restore Worktree')
391 ref
= choose_ref(context
, title
, ok_text
, default
='HEAD^')
393 cmds
.do(cmds
.RestoreWorktree
, context
, ref
)
397 """Install the GUI-model interaction hooks"""
398 Interaction
.choose_ref
= staticmethod(choose_ref
)