maint: prefer functions over methods
[git-cola.git] / cola / widgets / createbranch.py
blob92e98af0c80ec949f0308a7f8dcf6678b6c696e0
1 from __future__ import division, absolute_import, unicode_literals
3 from qtpy import QtWidgets
4 from qtpy import QtCore
5 from qtpy.QtCore import Qt
6 from qtpy.QtCore import Signal
8 from ..i18n import N_
9 from ..interaction import Interaction
10 from ..qtutils import get
11 from .. import gitcmds
12 from .. import icons
13 from .. import qtutils
14 from .standard import Dialog
15 from .standard import ProgressDialog
16 from . import defs
17 from . import completion
20 def create_new_branch(context, revision='', settings=None):
21 """Launches a dialog for creating a new branch"""
22 view = CreateBranchDialog(context, settings=settings,
23 parent=qtutils.active_window())
24 if revision:
25 view.set_revision(revision)
26 view.show()
27 return view
30 class CreateOpts(object):
31 def __init__(self, context):
32 self.context = context
33 self.reset = False
34 self.track = False
35 self.fetch = True
36 self.checkout = True
37 self.revision = 'HEAD'
38 self.branch = ''
41 class CreateThread(QtCore.QThread):
42 command = Signal(object, object, object)
43 result = Signal(object)
45 def __init__(self, opts, parent):
46 QtCore.QThread.__init__(self, parent)
47 self.opts = opts
49 def run(self):
50 branch = self.opts.branch
51 revision = self.opts.revision
52 reset = self.opts.reset
53 checkout = self.opts.checkout
54 track = self.opts.track
55 context = self.opts.context
56 git = context.git
57 model = context.model
58 results = []
59 status = 0
61 if track and '/' in revision:
62 remote = revision.split('/', 1)[0]
63 status, out, err = git.fetch(remote)
64 self.command.emit(status, out, err)
65 results.append(('fetch', status, out, err))
67 if status == 0:
68 status, out, err = model.create_branch(
69 branch, revision, force=reset, track=track)
70 self.command.emit(status, out, err)
72 results.append(('branch', status, out, err))
73 if status == 0 and checkout:
74 status, out, err = git.checkout(branch)
75 self.command.emit(status, out, err)
76 results.append(('checkout', status, out, err))
78 model.update_status()
79 self.result.emit(results)
82 class CreateBranchDialog(Dialog):
83 """A dialog for creating branches."""
85 def __init__(self, context, settings=None, parent=None):
86 Dialog.__init__(self, parent=parent)
87 self.setWindowTitle(N_('Create Branch'))
88 if parent is not None:
89 self.setWindowModality(Qt.WindowModal)
91 self.context = context
92 self.model = context.model
93 self.opts = CreateOpts(context)
94 self.thread = CreateThread(self.opts, self)
96 self.progress = None
98 self.branch_name_label = QtWidgets.QLabel()
99 self.branch_name_label.setText(N_('Branch Name'))
101 self.branch_name = completion.GitCreateBranchLineEdit(context)
102 self.branch_validator = completion.BranchValidator(
103 context.git, parent=self.branch_name)
104 self.branch_name.setValidator(self.branch_validator)
106 self.rev_label = QtWidgets.QLabel()
107 self.rev_label.setText(N_('Starting Revision'))
109 self.revision = completion.GitRefLineEdit(context)
110 current = gitcmds.current_branch(context)
111 if current:
112 self.revision.setText(current)
114 self.local_radio = qtutils.radio(text=N_('Local branch'), checked=True)
115 self.remote_radio = qtutils.radio(text=N_('Tracking branch'))
116 self.tag_radio = qtutils.radio(text=N_('Tag'))
118 self.branch_list = QtWidgets.QListWidget()
120 self.update_existing_label = QtWidgets.QLabel()
121 self.update_existing_label.setText(N_('Update Existing Branch:'))
123 self.no_update_radio = qtutils.radio(text=N_('No'))
124 self.ffwd_only_radio = qtutils.radio(text=N_('Fast Forward Only'),
125 checked=True)
126 self.reset_radio = qtutils.radio(text=N_('Reset'))
128 text = N_('Fetch Tracking Branch')
129 self.fetch_checkbox = qtutils.checkbox(text=text, checked=True)
131 text = N_('Checkout After Creation')
132 self.checkout_checkbox = qtutils.checkbox(text=text, checked=True)
134 icon = icons.branch()
135 self.create_button = qtutils.create_button(text=N_('Create Branch'),
136 icon=icon, default=True)
137 self.close_button = qtutils.close_button()
139 self.options_checkbox_layout = qtutils.hbox(defs.margin, defs.spacing,
140 self.fetch_checkbox,
141 self.checkout_checkbox,
142 qtutils.STRETCH)
144 self.branch_name_layout = qtutils.hbox(defs.margin, defs.spacing,
145 self.branch_name_label,
146 self.branch_name)
148 self.rev_radio_group = qtutils.buttongroup(self.local_radio,
149 self.remote_radio,
150 self.tag_radio)
152 self.rev_radio_layout = qtutils.hbox(defs.margin, defs.spacing,
153 self.local_radio,
154 self.remote_radio,
155 self.tag_radio,
156 qtutils.STRETCH)
158 self.rev_start_textinput_layout = qtutils.hbox(defs.no_margin,
159 defs.spacing,
160 self.rev_label,
161 defs.spacing,
162 self.revision)
164 self.rev_start_layout = qtutils.vbox(defs.no_margin, defs.spacing,
165 self.rev_radio_layout,
166 self.branch_list,
167 self.rev_start_textinput_layout)
169 self.options_radio_group = qtutils.buttongroup(self.no_update_radio,
170 self.ffwd_only_radio,
171 self.reset_radio)
173 self.options_radio_layout = qtutils.hbox(defs.no_margin, defs.spacing,
174 self.update_existing_label,
175 self.no_update_radio,
176 self.ffwd_only_radio,
177 self.reset_radio,
178 qtutils.STRETCH)
180 self.buttons_layout = qtutils.hbox(defs.margin, defs.spacing,
181 self.close_button,
182 qtutils.STRETCH,
183 self.create_button)
185 self.main_layout = qtutils.vbox(defs.margin, defs.spacing,
186 self.branch_name_layout,
187 self.rev_start_layout,
188 defs.button_spacing,
189 self.options_radio_layout,
190 self.options_checkbox_layout,
191 self.buttons_layout)
192 self.setLayout(self.main_layout)
194 qtutils.add_close_action(self)
195 qtutils.connect_button(self.close_button, self.close)
196 qtutils.connect_button(self.create_button, self.create_branch)
197 qtutils.connect_toggle(self.local_radio, self.display_model)
198 qtutils.connect_toggle(self.remote_radio, self.display_model)
199 qtutils.connect_toggle(self.tag_radio, self.display_model)
201 branches = self.branch_list
202 branches.itemSelectionChanged.connect(self.branch_item_changed)
204 thread = self.thread
205 thread.command.connect(Interaction.log_status,
206 type=Qt.QueuedConnection)
207 thread.result.connect(self.thread_result, type=Qt.QueuedConnection)
209 self.init_size(settings=settings, parent=parent)
210 self.display_model()
212 def set_revision(self, revision):
213 self.revision.setText(revision)
215 def getopts(self):
216 self.opts.revision = get(self.revision)
217 self.opts.branch = get(self.branch_name)
218 self.opts.checkout = get(self.checkout_checkbox)
219 self.opts.reset = get(self.reset_radio)
220 self.opts.fetch = get(self.fetch_checkbox)
221 self.opts.track = get(self.remote_radio)
223 def create_branch(self):
224 """Creates a branch; called by the "Create Branch" button"""
225 context = self.context
226 self.getopts()
227 revision = self.opts.revision
228 branch = self.opts.branch
229 no_update = get(self.no_update_radio)
230 ffwd_only = get(self.ffwd_only_radio)
231 existing_branches = gitcmds.branch_list(context)
232 check_branch = False
234 if not branch or not revision:
235 Interaction.critical(
236 N_('Missing Data'),
237 N_('Please provide both a branch '
238 'name and revision expression.'))
239 return
240 if branch in existing_branches:
241 if no_update:
242 msg = N_('Branch "%s" already exists.') % branch
243 Interaction.critical(N_('Branch Exists'), msg)
244 return
245 # Whether we should prompt the user for lost commits
246 commits = gitcmds.rev_list_range(context, revision, branch)
247 check_branch = bool(commits)
249 if check_branch:
250 msg = (N_('Resetting "%(branch)s" to "%(revision)s" '
251 'will lose commits.') %
252 dict(branch=branch, revision=revision))
253 if ffwd_only:
254 Interaction.critical(N_('Branch Exists'), msg)
255 return
256 lines = [msg]
257 for idx, commit in enumerate(commits):
258 subject = commit[1][0:min(len(commit[1]), 16)]
259 if len(subject) < len(commit[1]):
260 subject += '...'
261 lines.append('\t' + commit[0][:8] + '\t' + subject)
262 if idx >= 5:
263 skip = len(commits) - 5
264 lines.append('\t(%s)' % (N_('%d skipped') % skip))
265 break
266 line = N_('Recovering lost commits may not be easy.')
267 lines.append(line)
269 info_text = (N_('Reset "%(branch)s" to "%(revision)s"?') %
270 dict(branch=branch, revision=revision))
272 if not Interaction.confirm(
273 N_('Reset Branch?'), '\n'.join(lines), info_text,
274 N_('Reset Branch'), default=False, icon=icons.undo()):
275 return
277 title = N_('Create Branch')
278 label = N_('Updating')
279 self.progress = ProgressDialog(title, label, self)
280 self.progress.show()
281 self.thread.start()
283 def thread_result(self, results):
284 self.progress.hide()
285 del self.progress
287 for (cmd, status, _, _) in results:
288 if status != 0:
289 Interaction.critical(
290 N_('Error Creating Branch'),
291 (N_('"%(command)s" returned exit status "%(status)d"')
292 % dict(command='git '+cmd, status=status)))
293 return
295 self.accept()
297 # pylint: disable=unused-argument
298 def branch_item_changed(self, *rest):
299 """This callback is called when the branch selection changes"""
300 # When the branch selection changes then we should update
301 # the "Revision Expression" accordingly.
302 qlist = self.branch_list
303 remote_branch = qtutils.selected_item(qlist, self.branch_sources())
304 if not remote_branch:
305 return
306 # Update the model with the selection
307 self.revision.setText(remote_branch)
309 # Set the branch field if we're branching from a remote branch.
310 if not get(self.remote_radio):
311 return
312 branch = gitcmds.strip_remote(self.model.remotes, remote_branch)
313 if branch == 'HEAD':
314 return
315 # Signal that we've clicked on a remote branch
316 self.branch_name.set_value(branch)
318 def display_model(self):
319 """Sets the branch list to the available branches
321 branches = self.branch_sources()
322 qtutils.set_items(self.branch_list, branches)
324 def branch_sources(self):
325 """Get the list of items for populating the branch root list.
327 if get(self.local_radio):
328 value = self.model.local_branches
329 elif get(self.remote_radio):
330 value = self.model.remote_branches
331 elif get(self.tag_radio):
332 value = self.model.tags
333 else:
334 value = []
335 return value