git-cola v2.0.0
[git-cola.git] / cola / guicmds.py
blob6a2ce0d1429fadadf0240bb5b325765d58ec1726
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 qtutils
13 from cola.git import git
14 from cola.i18n import N_
15 from cola.interaction import Interaction
16 from cola.models import main
17 from cola.widgets import completion
18 from cola.widgets.browse import BrowseDialog
19 from cola.widgets.selectcommits import select_commits
20 from cola.compat import ustr
23 def delete_branch():
24 """Launch the 'Delete Branch' dialog."""
25 branch = choose_branch(N_('Delete Branch'), N_('Delete'))
26 if not branch:
27 return
28 cmds.do(cmds.DeleteBranch, branch)
31 def delete_remote_branch():
32 """Launch the 'Delete Remote Branch' dialog."""
33 branch = choose_remote_branch(N_('Delete Remote Branch'), N_('Delete'))
34 if not branch:
35 return
36 rgx = re.compile(r'^(?P<remote>[^/]+)/(?P<branch>.+)$')
37 match = rgx.match(branch)
38 if match:
39 remote = match.group('remote')
40 branch = match.group('branch')
41 cmds.do(cmds.DeleteRemoteBranch, remote, branch)
44 def browse_current():
45 """Launch the 'Browse Current Branch' dialog."""
46 branch = gitcmds.current_branch()
47 BrowseDialog.browse(branch)
50 def browse_other():
51 """Prompt for a branch and inspect content at that point in time."""
52 # Prompt for a branch to browse
53 branch = choose_ref(N_('Browse Commits...'), N_('Browse'))
54 if not branch:
55 return
56 BrowseDialog.browse(branch)
59 def checkout_branch():
60 """Launch the 'Checkout Branch' dialog."""
61 branch = choose_branch(N_('Checkout Branch'), N_('Checkout'))
62 if not branch:
63 return
64 cmds.do(cmds.CheckoutBranch, branch)
67 def cherry_pick():
68 """Launch the 'Cherry-Pick' dialog."""
69 revs, summaries = gitcmds.log_helper(all=True)
70 commits = select_commits(N_('Cherry-Pick Commit'),
71 revs, summaries, multiselect=False)
72 if not commits:
73 return
74 cmds.do(cmds.CherryPick, commits)
77 def new_repo():
78 """Prompt for a new directory and create a new Git repository
80 :returns str: repository path or None if no repository was created.
82 """
83 dlg = QtGui.QFileDialog()
84 dlg.setFileMode(QtGui.QFileDialog.Directory)
85 dlg.setOption(QtGui.QFileDialog.ShowDirsOnly)
86 dlg.show()
87 dlg.raise_()
88 if dlg.exec_() != QtGui.QFileDialog.Accepted:
89 return None
90 paths = dlg.selectedFiles()
91 if not paths:
92 return None
93 path = ustr(paths[0])
94 if not path:
95 return None
96 # Avoid needlessly calling `git init`.
97 if git.is_git_dir(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.
101 return path
103 status, out, err = core.run_command(['git', 'init', path])
104 if status == 0:
105 return path
106 else:
107 title = N_('Error Creating Repository')
108 msg = (N_('"%(command)s" returned exit status %(status)d') %
109 dict(command='git init %s' % path, status=status))
110 details = N_('Output:\n%s') % out
111 if err:
112 details += '\n\n'
113 details += N_('Errors: %s') % err
114 qtutils.critical(title, msg, details)
115 return None
118 def open_new_repo():
119 dirname = new_repo()
120 if not dirname:
121 return
122 cmds.do(cmds.OpenRepo, dirname)
125 def clone_repo(spawn=True):
127 Present GUI controls for cloning a repository
129 A new cola session is invoked when 'spawn' is True.
132 url, ok = qtutils.prompt(N_('Path or URL to clone (Env. $VARS okay)'))
133 url = os.path.expandvars(url)
134 if not ok or not url:
135 return None
136 try:
137 # Pick a suitable basename by parsing the URL
138 newurl = url.replace('\\', '/').rstrip('/')
139 default = newurl.rsplit('/', 1)[-1]
140 if default == '.git':
141 # The end of the URL is /.git, so assume it's a file path
142 default = os.path.basename(os.path.dirname(newurl))
143 if default.endswith('.git'):
144 # The URL points to a bare repo
145 default = default[:-4]
146 if url == '.':
147 # The URL is the current repo
148 default = os.path.basename(core.getcwd())
149 if not default:
150 raise
151 except:
152 Interaction.information(
153 N_('Error Cloning'),
154 N_('Could not parse Git URL: "%s"') % url)
155 Interaction.log(N_('Could not parse Git URL: "%s"') % url)
156 return None
158 # Prompt the user for a directory to use as the parent directory
159 msg = N_('Select a parent directory for the new clone')
160 dirname = qtutils.opendir_dialog(msg, main.model().getcwd())
161 if not dirname:
162 return None
163 count = 1
164 destdir = os.path.join(dirname, default)
165 olddestdir = destdir
166 if core.exists(destdir):
167 # An existing path can be specified
168 msg = (N_('"%s" already exists, cola will create a new directory') %
169 destdir)
170 Interaction.information('Directory Exists', msg)
172 # Make sure the new destdir doesn't exist
173 while core.exists(destdir):
174 destdir = olddestdir + str(count)
175 count += 1
176 if cmds.do(cmds.Clone, url, destdir, spawn=spawn):
177 return destdir
178 return None
181 def export_patches():
182 """Run 'git format-patch' on a list of commits."""
183 revs, summaries = gitcmds.log_helper()
184 to_export = select_commits(N_('Export Patches'), revs, summaries)
185 if not to_export:
186 return
187 cmds.do(cmds.FormatPatch, reversed(to_export), reversed(revs))
190 def diff_expression():
191 """Diff using an arbitrary expression."""
192 tracked = gitcmds.tracked_branch()
193 current = gitcmds.current_branch()
194 if tracked and current:
195 ref = tracked + '..' + current
196 else:
197 ref = 'origin/master..'
198 difftool.diff_expression(qtutils.active_window(), ref)
201 def open_repo():
202 dirname = qtutils.opendir_dialog(N_('Open Git Repository...'),
203 main.model().getcwd())
204 if not dirname:
205 return
206 cmds.do(cmds.OpenRepo, dirname)
209 def open_repo_in_new_window():
210 """Spawn a new cola session."""
211 dirname = qtutils.opendir_dialog(N_('Open Git Repository...'),
212 main.model().getcwd())
213 if not dirname:
214 return
215 cmds.do(cmds.OpenNewRepo, dirname)
218 def load_commitmsg():
219 """Load a commit message from a file."""
220 filename = qtutils.open_file(N_('Load Commit Message'),
221 directory=main.model().getcwd())
222 if filename:
223 cmds.do(cmds.LoadCommitMessageFromFile, filename)
226 def choose_from_dialog(get, title, button_text, default):
227 parent = qtutils.active_window()
228 return get(title, button_text, parent, default=default)
231 def choose_ref(title, button_text, default=None):
232 return choose_from_dialog(completion.GitRefDialog.get,
233 title, button_text, default)
236 def choose_branch(title, button_text, default=None):
237 return choose_from_dialog(completion.GitBranchDialog.get,
238 title, button_text, default)
241 def choose_remote_branch(title, button_text, default=None):
242 return choose_from_dialog(completion.GitRemoteBranchDialog.get,
243 title, button_text, default)
246 def review_branch():
247 """Diff against an arbitrary revision, branch, tag, etc."""
248 branch = choose_ref(N_('Select Branch to Review'), N_('Review'))
249 if not branch:
250 return
251 merge_base = gitcmds.merge_base_parent(branch)
252 difftool.diff_commits(qtutils.active_window(), merge_base, branch)