1 from PyQt4
import QtGui
2 from PyQt4
import QtCore
3 from PyQt4
.QtCore
import Qt
4 from PyQt4
.QtCore
import SIGNAL
6 from cola
import gitcmds
7 from cola
import qtutils
9 from cola
.i18n
import N_
10 from cola
.interaction
import Interaction
11 from cola
.models
import main
12 from cola
.widgets
import defs
13 from cola
.widgets
import completion
14 from cola
.widgets
.standard
import Dialog
17 def create_new_branch(revision
=''):
18 """Launches a dialog for creating a new branch"""
19 model
= main
.MainModel()
21 view
= CreateBranchDialog(model
, qtutils
.active_window())
23 view
.set_revision(revision
)
28 class CreateOpts(object):
29 def __init__(self
, model
):
35 self
.revision
= 'HEAD'
39 class CreateThread(QtCore
.QThread
):
40 def __init__(self
, opts
, parent
):
41 QtCore
.QThread
.__init
__(self
, parent
)
45 branch
= self
.opts
.branch
46 revision
= self
.opts
.revision
47 reset
= self
.opts
.reset
48 checkout
= self
.opts
.checkout
49 track
= self
.opts
.track
50 model
= self
.opts
.model
54 if track
and '/' in revision
:
55 remote
= revision
.split('/', 1)[0]
56 status
, out
, err
= model
.git
.fetch(remote
)
57 self
.emit(SIGNAL('command'), status
, out
, err
)
58 results
.append(('fetch', status
, out
, err
))
61 status
, out
, err
= model
.create_branch(branch
, revision
,
64 self
.emit(SIGNAL('command'), status
, out
, err
)
66 results
.append(('branch', status
, out
, err
))
67 if status
== 0 and checkout
:
68 status
, out
, err
= model
.git
.checkout(branch
)
69 self
.emit(SIGNAL('command'), status
, out
, err
)
70 results
.append(('checkout', status
, out
, err
))
72 main
.model().update_status()
73 self
.emit(SIGNAL('done'), results
)
76 class CreateBranchDialog(Dialog
):
77 """A dialog for creating branches."""
79 def __init__(self
, model
, parent
=None):
80 Dialog
.__init
__(self
, parent
=parent
)
81 self
.setAttribute(Qt
.WA_MacMetalStyle
)
82 self
.setWindowTitle(N_('Create Branch'))
83 if parent
is not None:
84 self
.setWindowModality(Qt
.WindowModal
)
87 self
.opts
= CreateOpts(model
)
88 self
.thread
= CreateThread(self
.opts
, self
)
90 self
.progress
= QtGui
.QProgressDialog(self
)
91 self
.progress
.setRange(0, 0)
92 self
.progress
.setCancelButton(None)
93 self
.progress
.setWindowTitle(N_('Create Branch'))
94 self
.progress
.setWindowModality(Qt
.WindowModal
)
96 self
.branch_name_label
= QtGui
.QLabel()
97 self
.branch_name_label
.setText(N_('Branch Name'))
99 self
.branch_name
= QtGui
.QLineEdit()
101 self
.rev_label
= QtGui
.QLabel()
102 self
.rev_label
.setText(N_('Starting Revision'))
104 self
.revision
= completion
.GitRefLineEdit()
105 current
= gitcmds
.current_branch()
107 self
.revision
.setText(current
)
109 self
.local_radio
= QtGui
.QRadioButton()
110 self
.local_radio
.setText(N_('Local branch'))
111 self
.local_radio
.setChecked(True)
113 self
.remote_radio
= QtGui
.QRadioButton()
114 self
.remote_radio
.setText(N_('Tracking branch'))
116 self
.tag_radio
= QtGui
.QRadioButton()
117 self
.tag_radio
.setText(N_('Tag'))
119 self
.branch_list
= QtGui
.QListWidget()
121 self
.update_existing_label
= QtGui
.QLabel()
122 self
.update_existing_label
.setText(N_('Update Existing Branch:'))
124 self
.no_update_radio
= QtGui
.QRadioButton()
125 self
.no_update_radio
.setText(N_('No'))
127 self
.ffwd_only_radio
= QtGui
.QRadioButton()
128 self
.ffwd_only_radio
.setText(N_('Fast Forward Only'))
129 self
.ffwd_only_radio
.setChecked(True)
131 self
.reset_radio
= QtGui
.QRadioButton()
132 self
.reset_radio
.setText(N_('Reset'))
134 self
.options_bottom_layout
= QtGui
.QHBoxLayout()
135 self
.options_checkbox_layout
= QtGui
.QVBoxLayout()
137 self
.fetch_checkbox
= QtGui
.QCheckBox()
138 self
.fetch_checkbox
.setText(N_('Fetch Tracking Branch'))
139 self
.fetch_checkbox
.setChecked(True)
140 self
.options_checkbox_layout
.addWidget(self
.fetch_checkbox
)
142 self
.checkout_checkbox
= QtGui
.QCheckBox()
143 self
.checkout_checkbox
.setText(N_('Checkout After Creation'))
144 self
.checkout_checkbox
.setChecked(True)
145 self
.options_checkbox_layout
.addWidget(self
.checkout_checkbox
)
147 self
.options_bottom_layout
.addLayout(self
.options_checkbox_layout
)
148 self
.options_bottom_layout
.addStretch()
150 self
.create_button
= qtutils
.create_button(text
=N_('Create Branch'),
151 icon
=qtutils
.git_icon())
152 self
.create_button
.setDefault(True)
154 self
.close_button
= qtutils
.create_button(text
=N_('Close'))
156 self
.branch_name_layout
= QtGui
.QHBoxLayout()
157 self
.branch_name_layout
.addWidget(self
.branch_name_label
)
158 self
.branch_name_layout
.addWidget(self
.branch_name
)
160 self
.rev_start_radiobtn_layout
= QtGui
.QHBoxLayout()
161 self
.rev_start_radiobtn_layout
.addWidget(self
.local_radio
)
162 self
.rev_start_radiobtn_layout
.addWidget(self
.remote_radio
)
163 self
.rev_start_radiobtn_layout
.addWidget(self
.tag_radio
)
164 self
.rev_start_radiobtn_layout
.addStretch()
166 self
.rev_start_textinput_layout
= QtGui
.QHBoxLayout()
167 self
.rev_start_textinput_layout
.setMargin(0)
168 self
.rev_start_textinput_layout
.setSpacing(defs
.spacing
)
169 self
.rev_start_textinput_layout
.addWidget(self
.rev_label
)
170 self
.rev_start_textinput_layout
.addWidget(self
.revision
)
172 self
.rev_start_group
= QtGui
.QGroupBox()
173 self
.rev_start_group
.setTitle(N_('Starting Revision'))
175 self
.rev_start_layout
= QtGui
.QVBoxLayout(self
.rev_start_group
)
176 self
.rev_start_layout
.setMargin(defs
.margin
)
177 self
.rev_start_layout
.setSpacing(defs
.spacing
)
178 self
.rev_start_layout
.addLayout(self
.rev_start_radiobtn_layout
)
179 self
.rev_start_layout
.addWidget(self
.branch_list
)
180 self
.rev_start_layout
.addLayout(self
.rev_start_textinput_layout
)
182 self
.options_radio_layout
= QtGui
.QHBoxLayout()
183 self
.options_radio_layout
.addWidget(self
.update_existing_label
)
184 self
.options_radio_layout
.addWidget(self
.no_update_radio
)
185 self
.options_radio_layout
.addWidget(self
.ffwd_only_radio
)
186 self
.options_radio_layout
.addWidget(self
.reset_radio
)
188 self
.option_group
= QtGui
.QGroupBox()
189 self
.option_group
.setTitle(N_('Options'))
191 self
.options_grp_layout
= QtGui
.QVBoxLayout(self
.option_group
)
192 self
.options_grp_layout
.setMargin(defs
.margin
)
193 self
.options_grp_layout
.setSpacing(defs
.spacing
)
194 self
.options_grp_layout
.addLayout(self
.options_radio_layout
)
195 self
.options_grp_layout
.addLayout(self
.options_bottom_layout
)
197 self
.buttons_layout
= QtGui
.QHBoxLayout()
198 self
.buttons_layout
.setMargin(defs
.margin
)
199 self
.buttons_layout
.setSpacing(defs
.spacing
)
200 self
.buttons_layout
.addWidget(self
.create_button
)
201 self
.buttons_layout
.addWidget(self
.close_button
)
203 self
.options_section_layout
= QtGui
.QHBoxLayout()
204 self
.options_section_layout
.setMargin(defs
.margin
)
205 self
.options_section_layout
.setSpacing(defs
.spacing
)
206 self
.options_section_layout
.addWidget(self
.option_group
)
207 self
.options_section_layout
.addLayout(self
.buttons_layout
)
209 self
.main_layout
= QtGui
.QVBoxLayout()
210 self
.main_layout
.setMargin(defs
.margin
)
211 self
.main_layout
.setSpacing(defs
.spacing
)
212 self
.main_layout
.addLayout(self
.branch_name_layout
)
213 self
.main_layout
.addWidget(self
.rev_start_group
)
214 self
.main_layout
.addLayout(self
.options_section_layout
)
215 self
.setLayout(self
.main_layout
)
217 qtutils
.connect_button(self
.close_button
, self
.reject
)
218 qtutils
.connect_button(self
.create_button
, self
.create_branch
)
219 qtutils
.connect_button(self
.local_radio
, self
.display_model
)
220 qtutils
.connect_button(self
.remote_radio
, self
.display_model
)
221 qtutils
.connect_button(self
.tag_radio
, self
.display_model
)
223 self
.connect(self
.branch_list
, SIGNAL('itemSelectionChanged()'),
224 self
.branch_item_changed
)
226 self
.connect(self
.thread
, SIGNAL('command'), self
.thread_command
)
227 self
.connect(self
.thread
, SIGNAL('done'), self
.thread_done
)
229 self
.resize(555, 333)
232 def set_revision(self
, revision
):
233 self
.revision
.setText(revision
)
236 self
.opts
.revision
= self
.revision
.value()
237 self
.opts
.branch
= unicode(self
.branch_name
.text())
238 self
.opts
.checkout
= self
.checkout_checkbox
.isChecked()
239 self
.opts
.reset
= self
.reset_radio
.isChecked()
240 self
.opts
.fetch
= self
.fetch_checkbox
.isChecked()
241 self
.opts
.track
= self
.remote_radio
.isChecked()
243 def create_branch(self
):
244 """Creates a branch; called by the "Create Branch" button"""
246 revision
= self
.opts
.revision
247 branch
= self
.opts
.branch
248 no_update
= self
.no_update_radio
.isChecked()
249 ffwd_only
= self
.ffwd_only_radio
.isChecked()
250 existing_branches
= gitcmds
.branch_list()
253 if not branch
or not revision
:
254 qtutils
.critical(N_('Missing Data'),
255 N_('Please provide both a branch '
256 'name and revision expression.'))
258 if branch
in existing_branches
:
260 msg
= N_('Branch "%s" already exists.') % branch
261 qtutils
.critical(N_('Branch Exists'), msg
)
263 # Whether we should prompt the user for lost commits
264 commits
= gitcmds
.rev_list_range(revision
, branch
)
265 check_branch
= bool(commits
)
268 msg
= (N_('Resetting "%(branch)s" to "%(revision)s" '
269 'will lose commits.') %
270 dict(branch
=branch
, revision
=revision
))
272 qtutils
.critical(N_('Branch Exists'), msg
)
275 for idx
, commit
in enumerate(commits
):
276 subject
= commit
[1][0:min(len(commit
[1]),16)]
277 if len(subject
) < len(commit
[1]):
279 lines
.append('\t' + commit
[0][:8]
282 skip
= len(commits
) - 5
283 lines
.append('\t(%s)' % (N_('%d skipped') % skip
))
285 line
= N_('Recovering lost commits may not be easy.')
287 if not qtutils
.confirm(N_('Reset Branch?'),
289 (N_('Reset "%(branch)s" to "%(revision)s"?') %
290 dict(branch
=branch
, revision
=revision
)),
293 icon
=qtutils
.icon('undo.svg')):
295 self
.setEnabled(False)
296 self
.progress
.setEnabled(True)
297 QtGui
.QApplication
.setOverrideCursor(Qt
.WaitCursor
)
299 # Show a nice progress bar
300 self
.progress
.setLabelText(N_('Updating...'))
304 def thread_command(self
, status
, out
, err
):
305 Interaction
.log_status(status
, out
, err
)
307 def thread_done(self
, results
):
308 self
.setEnabled(True)
309 self
.progress
.close()
310 QtGui
.QApplication
.restoreOverrideCursor()
313 for (cmd
, status
, out
, err
) in results
:
315 Interaction
.critical(
316 N_('Error Creating Branch'),
317 (N_('"%(command)s" returned exit status "%(status)d"') %
318 dict(command
='git '+cmd
, status
=status
)))
320 line
= '"git %s" returned exit status %d' % (cmd
, status
)
321 detail_lines
.append(line
)
323 detail_lines
.append(out
)
325 detail_lines
.append(err
)
326 detail_lines
.append('')
327 details
= '\n'.join(detail_lines
)
328 qtutils
.information(N_('Create Branch'),
329 N_('Branch created'),
333 def branch_item_changed(self
, *rest
):
334 """This callback is called when the branch selection changes"""
335 # When the branch selection changes then we should update
336 # the "Revision Expression" accordingly.
337 qlist
= self
.branch_list
338 (row
, selected
) = qtutils
.selected_row(qlist
)
341 # Update the model with the selection
342 sources
= self
.branch_sources()
344 self
.revision
.setText(rev
)
346 # Set the branch field if we're branching from a remote branch.
347 if not self
.remote_radio
.isChecked():
349 branch
= utils
.basename(rev
)
352 # Signal that we've clicked on a remote branch
353 self
.branch_name
.setText(branch
)
355 def display_model(self
):
356 """Sets the branch list to the available branches
358 branches
= self
.branch_sources()
359 qtutils
.set_items(self
.branch_list
, branches
)
361 def branch_sources(self
):
362 """Get the list of items for populating the branch root list.
364 if self
.local_radio
.isChecked():
365 return self
.model
.local_branches
366 elif self
.remote_radio
.isChecked():
367 return self
.model
.remote_branches
368 elif self
.tag_radio
.isChecked():
369 return self
.model
.tags