guicmds: pass icon through choose_ref()
[git-cola.git] / cola / guicmds.py
blob568257fc09ae27ecc0f4285709d47276cc9ac5e2
1 from __future__ import division, absolute_import, unicode_literals
3 import os
4 import re
6 from PyQt4 import QtGui
8 from cola import cmds
9 from cola import core
10 from cola import difftool
11 from cola import gitcmds
12 from cola import icons
13 from cola import qtutils
14 from cola import utils
15 from cola.git import git
16 from cola.i18n import N_
17 from cola.interaction import Interaction
18 from cola.models import main
19 from cola.widgets import completion
20 from cola.widgets.browse import BrowseDialog
21 from cola.widgets.selectcommits import select_commits
22 from cola.compat import ustr
25 def delete_branch():
26 """Launch the 'Delete Branch' dialog."""
27 icon = icons.discard()
28 branch = choose_branch(N_('Delete Branch'), N_('Delete'), icon=icon)
29 if not branch:
30 return
31 cmds.do(cmds.DeleteBranch, branch)
34 def delete_remote_branch():
35 """Launch the 'Delete Remote Branch' dialog."""
36 branch = choose_remote_branch(N_('Delete Remote Branch'), N_('Delete'),
37 icon=icons.discard())
38 if not branch:
39 return
40 rgx = re.compile(r'^(?P<remote>[^/]+)/(?P<branch>.+)$')
41 match = rgx.match(branch)
42 if match:
43 remote = match.group('remote')
44 branch = match.group('branch')
45 cmds.do(cmds.DeleteRemoteBranch, remote, branch)
48 def browse_current():
49 """Launch the 'Browse Current Branch' dialog."""
50 branch = gitcmds.current_branch()
51 BrowseDialog.browse(branch)
54 def browse_other():
55 """Prompt for a branch and inspect content at that point in time."""
56 # Prompt for a branch to browse
57 branch = choose_ref(N_('Browse Commits...'), N_('Browse'))
58 if not branch:
59 return
60 BrowseDialog.browse(branch)
63 def checkout_branch():
64 """Launch the 'Checkout Branch' dialog."""
65 branch = choose_branch(N_('Checkout Branch'), N_('Checkout'))
66 if not branch:
67 return
68 cmds.do(cmds.CheckoutBranch, branch)
71 def cherry_pick():
72 """Launch the 'Cherry-Pick' dialog."""
73 revs, summaries = gitcmds.log_helper(all=True)
74 commits = select_commits(N_('Cherry-Pick Commit'),
75 revs, summaries, multiselect=False)
76 if not commits:
77 return
78 cmds.do(cmds.CherryPick, commits)
81 def new_repo():
82 """Prompt for a new directory and create a new Git repository
84 :returns str: repository path or None if no repository was created.
86 """
87 dlg = QtGui.QFileDialog()
88 dlg.setFileMode(QtGui.QFileDialog.Directory)
89 dlg.setOption(QtGui.QFileDialog.ShowDirsOnly)
90 dlg.show()
91 dlg.raise_()
92 if dlg.exec_() != QtGui.QFileDialog.Accepted:
93 return None
94 paths = dlg.selectedFiles()
95 if not paths:
96 return None
97 path = ustr(paths[0])
98 if not path:
99 return None
100 # Avoid needlessly calling `git init`.
101 if git.is_git_dir(path):
102 # We could prompt here and confirm that they really didn't
103 # mean to open an existing repository, but I think
104 # treating it like an "Open" is a sensible DWIM answer.
105 return path
107 status, out, err = core.run_command(['git', 'init', path])
108 if status == 0:
109 return path
110 else:
111 title = N_('Error Creating Repository')
112 msg = (N_('"%(command)s" returned exit status %(status)d') %
113 dict(command='git init %s' % path, status=status))
114 details = N_('Output:\n%s') % out
115 if err:
116 details += '\n\n'
117 details += N_('Errors: %s') % err
118 qtutils.critical(title, msg, details)
119 return None
122 def open_new_repo():
123 dirname = new_repo()
124 if not dirname:
125 return
126 cmds.do(cmds.OpenRepo, dirname)
129 def prompt_for_clone():
131 Present a GUI for cloning a repository.
133 Returns the target directory and URL
136 url, ok = qtutils.prompt(N_('Path or URL to clone (Env. $VARS okay)'))
137 url = utils.expandpath(url)
138 if not ok or not url:
139 return None
140 try:
141 # Pick a suitable basename by parsing the URL
142 newurl = url.replace('\\', '/').rstrip('/')
143 default = newurl.rsplit('/', 1)[-1]
144 if default == '.git':
145 # The end of the URL is /.git, so assume it's a file path
146 default = os.path.basename(os.path.dirname(newurl))
147 if default.endswith('.git'):
148 # The URL points to a bare repo
149 default = default[:-4]
150 if url == '.':
151 # The URL is the current repo
152 default = os.path.basename(core.getcwd())
153 if not default:
154 raise
155 except:
156 Interaction.information(
157 N_('Error Cloning'),
158 N_('Could not parse Git URL: "%s"') % url)
159 Interaction.log(N_('Could not parse Git URL: "%s"') % url)
160 return None
162 # Prompt the user for a directory to use as the parent directory
163 msg = N_('Select a parent directory for the new clone')
164 dirname = qtutils.opendir_dialog(msg, main.model().getcwd())
165 if not dirname:
166 return None
167 count = 1
168 destdir = os.path.join(dirname, default)
169 olddestdir = destdir
170 if core.exists(destdir):
171 # An existing path can be specified
172 msg = (N_('"%s" already exists, cola will create a new directory') %
173 destdir)
174 Interaction.information(N_('Directory Exists'), msg)
176 # Make sure the new destdir doesn't exist
177 while core.exists(destdir):
178 destdir = olddestdir + str(count)
179 count += 1
181 return url, destdir
184 def export_patches():
185 """Run 'git format-patch' on a list of commits."""
186 revs, summaries = gitcmds.log_helper()
187 to_export = select_commits(N_('Export Patches'), revs, summaries)
188 if not to_export:
189 return
190 cmds.do(cmds.FormatPatch, reversed(to_export), reversed(revs))
193 def diff_expression():
194 """Diff using an arbitrary expression."""
195 tracked = gitcmds.tracked_branch()
196 current = gitcmds.current_branch()
197 if tracked and current:
198 ref = tracked + '..' + current
199 else:
200 ref = 'origin/master..'
201 difftool.diff_expression(qtutils.active_window(), ref)
204 def open_repo():
205 dirname = qtutils.opendir_dialog(N_('Open Git Repository...'),
206 main.model().getcwd())
207 if not dirname:
208 return
209 cmds.do(cmds.OpenRepo, dirname)
212 def open_repo_in_new_window():
213 """Spawn a new cola session."""
214 dirname = qtutils.opendir_dialog(N_('Open Git Repository...'),
215 main.model().getcwd())
216 if not dirname:
217 return
218 cmds.do(cmds.OpenNewRepo, dirname)
221 def load_commitmsg():
222 """Load a commit message from a file."""
223 filename = qtutils.open_file(N_('Load Commit Message'),
224 directory=main.model().getcwd())
225 if filename:
226 cmds.do(cmds.LoadCommitMessageFromFile, filename)
229 def choose_from_dialog(get, title, button_text, default, icon=None):
230 parent = qtutils.active_window()
231 return get(title, button_text, parent, default=default, icon=icon)
234 def choose_ref(title, button_text, default=None, icon=None):
235 return choose_from_dialog(completion.GitRefDialog.get,
236 title, button_text, default, icon=icon)
239 def choose_branch(title, button_text, default=None, icon=None):
240 return choose_from_dialog(completion.GitBranchDialog.get,
241 title, button_text, default, icon=icon)
244 def choose_remote_branch(title, button_text, default=None, icon=None):
245 return choose_from_dialog(completion.GitRemoteBranchDialog.get,
246 title, button_text, default, icon=icon)
249 def review_branch():
250 """Diff against an arbitrary revision, branch, tag, etc."""
251 branch = choose_ref(N_('Select Branch to Review'), N_('Review'))
252 if not branch:
253 return
254 merge_base = gitcmds.merge_base_parent(branch)
255 difftool.diff_commits(qtutils.active_window(), merge_base, branch)
258 class CloneTask(qtutils.Task):
259 """Clones a Git repository"""
261 def __init__(self, url, destdir, spawn, parent):
262 qtutils.Task.__init__(self, parent)
263 self.url = url
264 self.destdir = destdir
265 self.spawn = spawn
266 self.cmd = None
268 def task(self):
269 """Runs the model action and captures the result"""
270 self.cmd = cmds.do(cmds.Clone, self.url, self.destdir, spawn=self.spawn)
271 return self.cmd
274 def clone_repo(parent, runtask, progress, finish, spawn):
275 """Clone a repostiory asynchronously with progress animation"""
276 result = prompt_for_clone()
277 if result is None:
278 return
279 # Use a thread to update in the background
280 url, destdir = result
281 progress.set_details(N_('Clone Repository'),
282 N_('Cloning repository at %s') % url)
283 task = CloneTask(url, destdir, spawn, parent)
284 runtask.start(task, finish=finish, progress=progress)
287 def report_clone_repo_errors(task):
288 """Report errors from the clone task if they exist"""
289 if task.cmd is None or task.cmd.ok:
290 return
291 Interaction.critical(task.cmd.error_message,
292 message=task.cmd.error_message,
293 details=task.cmd.error_details)
295 def rename_branch():
296 """Launch the 'Rename Branch' dialogs."""
297 branch = choose_branch(N_('Rename Existing Branch'), N_('Select'))
298 if not branch:
299 return
300 new_branch = choose_branch(N_('Enter Branch New Name'), N_('Rename'))
301 if not new_branch:
302 return
303 cmds.do(cmds.RenameBranch, branch, new_branch)