1 from __future__
import division
, absolute_import
, unicode_literals
3 from PyQt4
import QtGui
4 from PyQt4
import QtCore
5 from PyQt4
.QtCore
import Qt
6 from PyQt4
.QtCore
import SIGNAL
8 from cola
import gitcmds
10 from cola
import qtutils
11 from cola
.i18n
import N_
12 from cola
.interaction
import Interaction
13 from cola
.models
import main
14 from cola
.widgets
import defs
15 from cola
.widgets
import completion
16 from cola
.widgets
.standard
import Dialog
17 from cola
.compat
import ustr
20 COMMAND_SIGNAL
= 'command(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)'
23 def create_new_branch(revision
='', settings
=None):
24 """Launches a dialog for creating a new branch"""
25 model
= main
.MainModel()
27 view
= CreateBranchDialog(model
, settings
=settings
,
28 parent
=qtutils
.active_window())
30 view
.set_revision(revision
)
35 class CreateOpts(object):
36 def __init__(self
, model
):
42 self
.revision
= 'HEAD'
46 class CreateThread(QtCore
.QThread
):
47 def __init__(self
, opts
, parent
):
48 QtCore
.QThread
.__init
__(self
, parent
)
52 branch
= self
.opts
.branch
53 revision
= self
.opts
.revision
54 reset
= self
.opts
.reset
55 checkout
= self
.opts
.checkout
56 track
= self
.opts
.track
57 model
= self
.opts
.model
61 if track
and '/' in revision
:
62 remote
= revision
.split('/', 1)[0]
63 status
, out
, err
= model
.git
.fetch(remote
)
64 self
.emit(SIGNAL(COMMAND_SIGNAL
), status
, out
, err
)
65 results
.append(('fetch', status
, out
, err
))
68 status
, out
, err
= model
.create_branch(branch
, revision
,
71 self
.emit(SIGNAL(COMMAND_SIGNAL
), status
, out
, err
)
73 results
.append(('branch', status
, out
, err
))
74 if status
== 0 and checkout
:
75 status
, out
, err
= model
.git
.checkout(branch
)
76 self
.emit(SIGNAL(COMMAND_SIGNAL
), status
, out
, err
)
77 results
.append(('checkout', status
, out
, err
))
79 main
.model().update_status()
80 self
.emit(SIGNAL('done(PyQt_PyObject)'), results
)
83 class CreateBranchDialog(Dialog
):
84 """A dialog for creating branches."""
86 def __init__(self
, model
, settings
=None, parent
=None):
87 Dialog
.__init
__(self
, parent
=parent
)
88 self
.setAttribute(Qt
.WA_MacMetalStyle
)
89 self
.setWindowTitle(N_('Create Branch'))
90 if parent
is not None:
91 self
.setWindowModality(Qt
.WindowModal
)
94 self
.opts
= CreateOpts(model
)
95 self
.thread
= CreateThread(self
.opts
, self
)
97 self
.progress
= QtGui
.QProgressDialog(self
)
98 self
.progress
.setRange(0, 0)
99 self
.progress
.setCancelButton(None)
100 self
.progress
.setWindowTitle(N_('Create Branch'))
101 self
.progress
.setWindowModality(Qt
.WindowModal
)
103 self
.branch_name_label
= QtGui
.QLabel()
104 self
.branch_name_label
.setText(N_('Branch Name'))
106 self
.branch_name
= QtGui
.QLineEdit()
108 self
.rev_label
= QtGui
.QLabel()
109 self
.rev_label
.setText(N_('Starting Revision'))
111 self
.revision
= completion
.GitRefLineEdit()
112 current
= gitcmds
.current_branch()
114 self
.revision
.setText(current
)
116 self
.local_radio
= qtutils
.radio(text
=N_('Local branch'), checked
=True)
117 self
.remote_radio
= qtutils
.radio(text
=N_('Tracking branch'))
118 self
.tag_radio
= qtutils
.radio(text
=N_('Tag'))
120 self
.branch_list
= QtGui
.QListWidget()
122 self
.update_existing_label
= QtGui
.QLabel()
123 self
.update_existing_label
.setText(N_('Update Existing Branch:'))
125 self
.no_update_radio
= qtutils
.radio(text
=N_('No'))
126 self
.ffwd_only_radio
= qtutils
.radio(text
=N_('Fast Forward Only'),
128 self
.reset_radio
= qtutils
.radio(text
=N_('Reset'))
130 text
= N_('Fetch Tracking Branch')
131 self
.fetch_checkbox
= qtutils
.checkbox(text
=text
, checked
=True)
133 text
= N_('Checkout After Creation')
134 self
.checkout_checkbox
= qtutils
.checkbox(text
=text
, checked
=True)
136 icon
= icons
.branch()
137 self
.create_button
= qtutils
.create_button(text
=N_('Create Branch'),
138 icon
=icon
, default
=True)
139 self
.close_button
= qtutils
.close_button()
141 self
.options_checkbox_layout
= qtutils
.hbox(defs
.margin
, defs
.spacing
,
143 self
.checkout_checkbox
,
146 self
.branch_name_layout
= qtutils
.hbox(defs
.margin
, defs
.spacing
,
147 self
.branch_name_label
,
150 self
.rev_radio_group
= qtutils
.buttongroup(self
.local_radio
,
154 self
.rev_radio_layout
= qtutils
.hbox(defs
.margin
, defs
.spacing
,
160 self
.rev_start_textinput_layout
= qtutils
.hbox(defs
.no_margin
,
166 self
.rev_start_layout
= qtutils
.vbox(defs
.no_margin
, defs
.spacing
,
167 self
.rev_radio_layout
,
169 self
.rev_start_textinput_layout
)
171 self
.options_radio_group
= qtutils
.buttongroup(self
.no_update_radio
,
172 self
.ffwd_only_radio
,
175 self
.options_radio_layout
= qtutils
.hbox(defs
.no_margin
, defs
.spacing
,
176 self
.update_existing_label
,
177 self
.no_update_radio
,
178 self
.ffwd_only_radio
,
182 self
.buttons_layout
= qtutils
.hbox(defs
.margin
, defs
.spacing
,
187 self
.main_layout
= qtutils
.vbox(defs
.margin
, defs
.spacing
,
188 self
.branch_name_layout
,
189 self
.rev_start_layout
,
191 self
.options_radio_layout
,
192 self
.options_checkbox_layout
,
194 self
.setLayout(self
.main_layout
)
196 qtutils
.add_close_action(self
)
197 qtutils
.connect_button(self
.close_button
, self
.close
)
198 qtutils
.connect_button(self
.create_button
, self
.create_branch
)
199 qtutils
.connect_button(self
.local_radio
, self
.display_model
)
200 qtutils
.connect_button(self
.remote_radio
, self
.display_model
)
201 qtutils
.connect_button(self
.tag_radio
, self
.display_model
)
203 self
.connect(self
.branch_list
, SIGNAL('itemSelectionChanged()'),
204 self
.branch_item_changed
)
206 self
.connect(self
.thread
, SIGNAL(COMMAND_SIGNAL
),
207 self
.thread_command
, Qt
.QueuedConnection
)
209 self
.connect(self
.thread
, SIGNAL('done(PyQt_PyObject)'),
210 self
.thread_done
, Qt
.QueuedConnection
)
212 if not self
.restore_state(settings
=settings
):
213 self
.resize(555, 333)
217 def set_revision(self
, revision
):
218 self
.revision
.setText(revision
)
221 self
.opts
.revision
= self
.revision
.value()
222 self
.opts
.branch
= ustr(self
.branch_name
.text())
223 self
.opts
.checkout
= self
.checkout_checkbox
.isChecked()
224 self
.opts
.reset
= self
.reset_radio
.isChecked()
225 self
.opts
.fetch
= self
.fetch_checkbox
.isChecked()
226 self
.opts
.track
= self
.remote_radio
.isChecked()
228 def create_branch(self
):
229 """Creates a branch; called by the "Create Branch" button"""
231 revision
= self
.opts
.revision
232 branch
= self
.opts
.branch
233 no_update
= self
.no_update_radio
.isChecked()
234 ffwd_only
= self
.ffwd_only_radio
.isChecked()
235 existing_branches
= gitcmds
.branch_list()
238 if not branch
or not revision
:
239 qtutils
.critical(N_('Missing Data'),
240 N_('Please provide both a branch '
241 'name and revision expression.'))
243 if branch
in existing_branches
:
245 msg
= N_('Branch "%s" already exists.') % branch
246 qtutils
.critical(N_('Branch Exists'), msg
)
248 # Whether we should prompt the user for lost commits
249 commits
= gitcmds
.rev_list_range(revision
, branch
)
250 check_branch
= bool(commits
)
253 msg
= (N_('Resetting "%(branch)s" to "%(revision)s" '
254 'will lose commits.') %
255 dict(branch
=branch
, revision
=revision
))
257 qtutils
.critical(N_('Branch Exists'), msg
)
260 for idx
, commit
in enumerate(commits
):
261 subject
= commit
[1][0:min(len(commit
[1]),16)]
262 if len(subject
) < len(commit
[1]):
264 lines
.append('\t' + commit
[0][:8]
267 skip
= len(commits
) - 5
268 lines
.append('\t(%s)' % (N_('%d skipped') % skip
))
270 line
= N_('Recovering lost commits may not be easy.')
272 if not qtutils
.confirm(N_('Reset Branch?'),
274 (N_('Reset "%(branch)s" to "%(revision)s"?') %
275 dict(branch
=branch
, revision
=revision
)),
280 self
.setEnabled(False)
281 self
.progress
.setEnabled(True)
282 QtGui
.QApplication
.setOverrideCursor(Qt
.WaitCursor
)
284 # Show a nice progress bar
285 self
.progress
.setLabelText(N_('Updating...'))
289 def thread_command(self
, status
, out
, err
):
290 Interaction
.log_status(status
, out
, err
)
292 def thread_done(self
, results
):
293 self
.setEnabled(True)
294 self
.progress
.close()
295 QtGui
.QApplication
.restoreOverrideCursor()
297 for (cmd
, status
, out
, err
) in results
:
299 Interaction
.critical(
300 N_('Error Creating Branch'),
301 (N_('"%(command)s" returned exit status "%(status)d"') %
302 dict(command
='git '+cmd
, status
=status
)))
307 def branch_item_changed(self
, *rest
):
308 """This callback is called when the branch selection changes"""
309 # When the branch selection changes then we should update
310 # the "Revision Expression" accordingly.
311 qlist
= self
.branch_list
312 remote_branch
= qtutils
.selected_item(qlist
, self
.branch_sources())
313 if not remote_branch
:
315 # Update the model with the selection
316 self
.revision
.setText(remote_branch
)
318 # Set the branch field if we're branching from a remote branch.
319 if not self
.remote_radio
.isChecked():
321 branch
= gitcmds
.strip_remote(self
.model
.remotes
, remote_branch
)
324 # Signal that we've clicked on a remote branch
325 self
.branch_name
.setText(branch
)
327 def display_model(self
):
328 """Sets the branch list to the available branches
330 branches
= self
.branch_sources()
331 qtutils
.set_items(self
.branch_list
, branches
)
333 def branch_sources(self
):
334 """Get the list of items for populating the branch root list.
336 if self
.local_radio
.isChecked():
337 return self
.model
.local_branches
338 elif self
.remote_radio
.isChecked():
339 return self
.model
.remote_branches
340 elif self
.tag_radio
.isChecked():
341 return self
.model
.tags