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 copy_commit_id_to_clipboard(context
):
23 """Copy the current commit ID to the clipboard"""
24 status
, commit_id
, _
= context
.git
.rev_parse('HEAD')
25 if status
== 0 and commit_id
:
26 qtutils
.set_clipboard(commit_id
)
29 def delete_branch(context
):
30 """Launch the 'Delete Branch' dialog."""
31 icon
= icons
.discard()
32 branch
= choose_branch(context
, N_('Delete Branch'), N_('Delete'), icon
=icon
)
35 cmds
.do(cmds
.DeleteBranch
, context
, branch
)
38 def delete_remote_branch(context
):
39 """Launch the 'Delete Remote Branch' dialog."""
40 remote_branch
= choose_remote_branch(
41 context
, N_('Delete Remote Branch'), N_('Delete'), icon
=icons
.discard()
45 remote
, branch
= gitcmds
.parse_remote_branch(remote_branch
)
47 cmds
.do(cmds
.DeleteRemoteBranch
, context
, remote
, branch
)
50 def browse_current(context
):
51 """Launch the 'Browse Current Branch' dialog."""
52 branch
= gitcmds
.current_branch(context
)
53 BrowseBranch
.browse(context
, branch
)
56 def browse_other(context
):
57 """Prompt for a branch and inspect content at that point in time."""
58 # Prompt for a branch to browse
59 branch
= choose_ref(context
, N_('Browse Commits...'), N_('Browse'))
62 BrowseBranch
.browse(context
, branch
)
65 def checkout_branch(context
, default
=None):
66 """Launch the 'Checkout Branch' dialog."""
67 branch
= choose_potential_branch(
68 context
, N_('Checkout Branch'), N_('Checkout'), default
=default
72 cmds
.do(cmds
.CheckoutBranch
, context
, branch
)
75 def cherry_pick(context
):
76 """Launch the 'Cherry-Pick' dialog."""
77 revs
, summaries
= gitcmds
.log_helper(context
, all
=True)
78 commits
= select_commits(
79 context
, N_('Cherry-Pick Commit'), revs
, summaries
, multiselect
=False
83 cmds
.do(cmds
.CherryPick
, context
, commits
)
86 def new_repo(context
):
87 """Prompt for a new directory and create a new Git repository
89 :returns str: repository path or None if no repository was created.
93 path
= qtutils
.opendir_dialog(N_('New Repository...'), core
.getcwd())
96 # Avoid needlessly calling `git init`.
97 if git
.is_git_repository(path
):
98 # We could prompt here and confirm that they really didn't
99 # mean to open an existing repository, but I think
100 # treating it like an "Open" is a sensible DWIM answer.
103 status
, out
, err
= git
.init(path
)
107 title
= N_('Error Creating Repository')
108 Interaction
.command_error(title
, 'git init', status
, out
, err
)
112 def open_new_repo(context
):
113 """Create a new repository and open it"""
114 dirname
= new_repo(context
)
117 cmds
.do(cmds
.OpenRepo
, context
, dirname
)
120 def new_bare_repo(context
):
121 """Create a bare repository and configure a remote pointing to it"""
123 repo
= prompt_for_new_bare_repo()
127 ok
= cmds
.do(cmds
.NewBareRepo
, context
, repo
)
130 # Add a new remote pointing to the bare repo
131 parent
= qtutils
.active_window()
132 add_remote
= editremotes
.add_remote(
133 context
, parent
, name
=os
.path
.basename(repo
), url
=repo
, readonly_url
=True
141 def prompt_for_new_bare_repo():
142 """Prompt for a directory and name for a new bare repository"""
143 path
= qtutils
.opendir_dialog(N_('Select Directory...'), core
.getcwd())
148 default
= os
.path
.basename(core
.getcwd())
149 if not default
.endswith('.git'):
152 name
, ok
= qtutils
.prompt(
153 N_('Enter a name for the new bare repo'),
154 title
=N_('New Bare Repository...'),
157 if not name
or not ok
:
159 if not name
.endswith('.git'):
161 repo
= os
.path
.join(path
, name
)
163 Interaction
.critical(N_('Error'), N_('"%s" already exists') % repo
)
170 def export_patches(context
):
171 """Run 'git format-patch' on a list of commits."""
172 revs
, summaries
= gitcmds
.log_helper(context
)
173 to_export_and_output
= select_commits_and_output(
174 context
, N_('Export Patches'), revs
, summaries
176 if not to_export_and_output
['to_export']:
182 reversed(to_export_and_output
['to_export']),
184 output
=to_export_and_output
['output'],
188 def diff_against_commit(context
):
189 """Diff against any commit and checkout changes using the Diff Editor"""
190 icon
= icons
.compare()
191 ref
= choose_ref(context
, N_('Diff Against Commit'), N_('Diff'), icon
=icon
)
194 cmds
.do(cmds
.DiffAgainstCommitMode
, context
, ref
)
197 def diff_expression(context
):
198 """Diff using an arbitrary expression."""
199 tracked
= gitcmds
.tracked_branch(context
)
200 current
= gitcmds
.current_branch(context
)
201 if tracked
and current
:
202 ref
= tracked
+ '..' + current
204 ref
= '@{upstream}..'
205 difftool
.diff_expression(context
, qtutils
.active_window(), ref
)
208 def open_repo(context
):
209 """Open a repository in the current window"""
210 model
= context
.model
211 dirname
= qtutils
.opendir_dialog(N_('Open Git Repository'), model
.getcwd())
214 cmds
.do(cmds
.OpenRepo
, context
, dirname
)
217 def open_repo_in_new_window(context
):
218 """Spawn a new cola session."""
219 model
= context
.model
220 dirname
= qtutils
.opendir_dialog(N_('Open Git Repository'), model
.getcwd())
223 cmds
.do(cmds
.OpenNewRepo
, context
, dirname
)
226 def open_quick_repo_search(context
, parent
=None):
227 """Open a Quick Repository Search dialog"""
229 parent
= qtutils
.active_window()
230 settings
= context
.settings
231 items
= settings
.bookmarks
+ settings
.recent
235 default_repo
= cfg
.get('cola.defaultrepo')
237 entries
= QtGui
.QStandardItemModel()
239 normalize
= display
.normalize_path
240 star_icon
= icons
.star()
241 folder_icon
= icons
.folder()
244 key
= normalize(item
['path'])
249 if default_repo
== item
['path']:
254 entry
= switcher
.switcher_item(key
, icon
, name
)
255 entries
.appendRow(entry
)
258 title
= N_('Quick Open Repository')
259 place_holder
= N_('Search repositories by name...')
260 switcher
.switcher_inner_view(
264 place_holder
=place_holder
,
265 enter_action
=lambda entry
: cmds
.do(cmds
.OpenRepo
, context
, entry
.key
),
270 def load_commitmsg(context
):
271 """Load a commit message from a file."""
272 model
= context
.model
273 filename
= qtutils
.open_file(N_('Load Commit Message'), directory
=model
.getcwd())
275 cmds
.do(cmds
.LoadCommitMessageFromFile
, context
, filename
)
278 def choose_from_dialog(get
, context
, title
, button_text
, default
, icon
=None):
279 """Choose a value from a dialog using the `get` method"""
280 parent
= qtutils
.active_window()
281 return get(context
, title
, button_text
, parent
, default
=default
, icon
=icon
)
284 def choose_ref(context
, title
, button_text
, default
=None, icon
=None):
285 """Choose a Git ref and return it"""
286 return choose_from_dialog(
287 completion
.GitRefDialog
.get
, context
, title
, button_text
, default
, icon
=icon
291 def choose_branch(context
, title
, button_text
, default
=None, icon
=None):
292 """Choose a branch and return either the chosen branch or an empty value"""
293 return choose_from_dialog(
294 completion
.GitBranchDialog
.get
, context
, title
, button_text
, default
, icon
=icon
298 def choose_potential_branch(context
, title
, button_text
, default
=None, icon
=None):
299 """Choose a "potential" branch for checking out.
301 This dialog includes remote branches from which new local branches can be created.
303 return choose_from_dialog(
304 completion
.GitCheckoutBranchDialog
.get
,
313 def choose_remote_branch(context
, title
, button_text
, default
=None, icon
=None):
314 """Choose a remote branch"""
315 return choose_from_dialog(
316 completion
.GitRemoteBranchDialog
.get
,
325 def review_branch(context
):
326 """Diff against an arbitrary revision, branch, tag, etc."""
327 branch
= choose_ref(context
, N_('Select Branch to Review'), N_('Review'))
330 merge_base
= gitcmds
.merge_base_parent(context
, branch
)
331 difftool
.diff_commits(context
, qtutils
.active_window(), merge_base
, branch
)
334 def rename_branch(context
):
335 """Launch the 'Rename Branch' dialogs."""
336 branch
= choose_branch(context
, N_('Rename Existing Branch'), N_('Select'))
339 new_branch
= choose_branch(context
, N_('Enter New Branch Name'), N_('Rename'))
342 cmds
.do(cmds
.RenameBranch
, context
, branch
, new_branch
)
345 def reset_soft(context
):
346 """Run "git reset --soft" to reset the branch HEAD"""
347 title
= N_('Reset Branch (Soft)')
348 ok_text
= N_('Reset Branch')
349 default
= context
.settings
.get_value('reset::soft', 'ref', default
='HEAD^')
350 ref
= choose_ref(context
, title
, ok_text
, default
=default
)
352 cmds
.do(cmds
.ResetSoft
, context
, ref
)
353 context
.settings
.set_value('reset::soft', 'ref', ref
)
356 def reset_mixed(context
):
357 """Run "git reset --mixed" to reset the branch HEAD and staging area"""
358 title
= N_('Reset Branch and Stage (Mixed)')
359 ok_text
= N_('Reset')
360 default
= context
.settings
.get_value('reset::mixed', 'ref', default
='HEAD^')
361 ref
= choose_ref(context
, title
, ok_text
, default
=default
)
363 cmds
.do(cmds
.ResetMixed
, context
, ref
)
364 context
.settings
.set_value('reset::mixed', 'ref', ref
)
367 def reset_keep(context
):
368 """Run "git reset --keep" safe reset to avoid clobbering local changes"""
369 title
= N_('Reset All (Keep Unstaged Changes)')
370 default
= context
.settings
.get_value('reset::keep', 'ref', default
='HEAD^')
371 ref
= choose_ref(context
, title
, N_('Reset and Restore'), default
=default
)
373 cmds
.do(cmds
.ResetKeep
, context
, ref
)
374 context
.settings
.set_value('reset::keep', 'ref', ref
)
377 def reset_merge(context
):
378 """Run "git reset --merge" to reset the working tree and staging area
380 The staging area is allowed to carry forward unmerged index entries,
381 but if any unstaged changes would be clobbered by the reset then the
384 title
= N_('Restore Worktree and Reset All (Merge)')
385 ok_text
= N_('Reset and Restore')
386 default
= context
.settings
.get_value('reset::merge', 'ref', default
='HEAD^')
387 ref
= choose_ref(context
, title
, ok_text
, default
=default
)
389 cmds
.do(cmds
.ResetMerge
, context
, ref
)
390 context
.settings
.set_value('reset::merge', 'ref', ref
)
393 def reset_hard(context
):
394 """Run "git reset --hard" to fully reset the working tree and staging area"""
395 title
= N_('Restore Worktree and Reset All (Hard)')
396 ok_text
= N_('Reset and Restore')
397 default
= context
.settings
.get_value('reset::hard', 'ref', default
='HEAD^')
398 ref
= choose_ref(context
, title
, ok_text
, default
=default
)
400 cmds
.do(cmds
.ResetHard
, context
, ref
)
401 context
.settings
.set_value('reset::hard', 'ref', ref
)
404 def restore_worktree(context
):
405 """Restore the worktree to the content from the specified commit"""
406 title
= N_('Restore Worktree')
407 ok_text
= N_('Restore Worktree')
408 default
= context
.settings
.get_value('restore::worktree', 'ref', default
='HEAD^')
409 ref
= choose_ref(context
, title
, ok_text
, default
=default
)
411 cmds
.do(cmds
.RestoreWorktree
, context
, ref
)
412 context
.settings
.set_value('restore::worktree', 'ref', ref
)
416 """Install the GUI-model interaction hooks"""
417 Interaction
.choose_ref
= staticmethod(choose_ref
)