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
19 COMMAND_SIGNAL
= 'command(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)'
22 def create_new_branch(revision
='', settings
=None):
23 """Launches a dialog for creating a new branch"""
24 model
= main
.MainModel()
26 view
= CreateBranchDialog(model
, settings
=settings
,
27 parent
=qtutils
.active_window())
29 view
.set_revision(revision
)
34 class CreateOpts(object):
35 def __init__(self
, model
):
41 self
.revision
= 'HEAD'
45 class CreateThread(QtCore
.QThread
):
46 def __init__(self
, opts
, parent
):
47 QtCore
.QThread
.__init
__(self
, parent
)
51 branch
= self
.opts
.branch
52 revision
= self
.opts
.revision
53 reset
= self
.opts
.reset
54 checkout
= self
.opts
.checkout
55 track
= self
.opts
.track
56 model
= self
.opts
.model
60 if track
and '/' in revision
:
61 remote
= revision
.split('/', 1)[0]
62 status
, out
, err
= model
.git
.fetch(remote
)
63 self
.emit(SIGNAL(COMMAND_SIGNAL
), status
, out
, err
)
64 results
.append(('fetch', status
, out
, err
))
67 status
, out
, err
= model
.create_branch(branch
, revision
,
70 self
.emit(SIGNAL(COMMAND_SIGNAL
), status
, out
, err
)
72 results
.append(('branch', status
, out
, err
))
73 if status
== 0 and checkout
:
74 status
, out
, err
= model
.git
.checkout(branch
)
75 self
.emit(SIGNAL(COMMAND_SIGNAL
), status
, out
, err
)
76 results
.append(('checkout', status
, out
, err
))
78 main
.model().update_status()
79 self
.emit(SIGNAL('done(PyQt_PyObject)'), results
)
82 class CreateBranchDialog(Dialog
):
83 """A dialog for creating branches."""
85 def __init__(self
, model
, settings
=None, parent
=None):
86 Dialog
.__init
__(self
, parent
=parent
)
87 self
.setAttribute(Qt
.WA_MacMetalStyle
)
88 self
.setWindowTitle(N_('Create Branch'))
89 if parent
is not None:
90 self
.setWindowModality(Qt
.WindowModal
)
93 self
.opts
= CreateOpts(model
)
94 self
.thread
= CreateThread(self
.opts
, self
)
96 self
.progress
= QtGui
.QProgressDialog(self
)
97 self
.progress
.setRange(0, 0)
98 self
.progress
.setCancelButton(None)
99 self
.progress
.setWindowTitle(N_('Create Branch'))
100 self
.progress
.setWindowModality(Qt
.WindowModal
)
102 self
.branch_name_label
= QtGui
.QLabel()
103 self
.branch_name_label
.setText(N_('Branch Name'))
105 self
.branch_name
= QtGui
.QLineEdit()
107 self
.rev_label
= QtGui
.QLabel()
108 self
.rev_label
.setText(N_('Starting Revision'))
110 self
.revision
= completion
.GitRefLineEdit()
111 current
= gitcmds
.current_branch()
113 self
.revision
.setText(current
)
115 self
.local_radio
= qtutils
.radio(text
=N_('Local branch'), checked
=True)
116 self
.remote_radio
= qtutils
.radio(text
=N_('Tracking branch'))
117 self
.tag_radio
= qtutils
.radio(text
=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
= qtutils
.radio(text
=N_('No'))
125 self
.ffwd_only_radio
= qtutils
.radio(text
=N_('Fast Forward Only'),
127 self
.reset_radio
= qtutils
.radio(text
=N_('Reset'))
129 text
= N_('Fetch Tracking Branch')
130 self
.fetch_checkbox
= qtutils
.checkbox(text
=text
, checked
=True)
132 text
= N_('Checkout After Creation')
133 self
.checkout_checkbox
= qtutils
.checkbox(text
=text
, checked
=True)
135 icon
= icons
.branch()
136 self
.create_button
= qtutils
.create_button(text
=N_('Create Branch'),
137 icon
=icon
, default
=True)
138 self
.close_button
= qtutils
.close_button()
140 self
.options_checkbox_layout
= qtutils
.hbox(defs
.margin
, defs
.spacing
,
142 self
.checkout_checkbox
,
145 self
.branch_name_layout
= qtutils
.hbox(defs
.margin
, defs
.spacing
,
146 self
.branch_name_label
,
149 self
.rev_radio_group
= qtutils
.buttongroup(self
.local_radio
,
153 self
.rev_radio_layout
= qtutils
.hbox(defs
.margin
, defs
.spacing
,
159 self
.rev_start_textinput_layout
= qtutils
.hbox(defs
.no_margin
,
165 self
.rev_start_layout
= qtutils
.vbox(defs
.no_margin
, defs
.spacing
,
166 self
.rev_radio_layout
,
168 self
.rev_start_textinput_layout
)
170 self
.options_radio_group
= qtutils
.buttongroup(self
.no_update_radio
,
171 self
.ffwd_only_radio
,
174 self
.options_radio_layout
= qtutils
.hbox(defs
.no_margin
, defs
.spacing
,
175 self
.update_existing_label
,
176 self
.no_update_radio
,
177 self
.ffwd_only_radio
,
181 self
.buttons_layout
= qtutils
.hbox(defs
.margin
, defs
.spacing
,
186 self
.main_layout
= qtutils
.vbox(defs
.margin
, defs
.spacing
,
187 self
.branch_name_layout
,
188 self
.rev_start_layout
,
190 self
.options_radio_layout
,
191 self
.options_checkbox_layout
,
193 self
.setLayout(self
.main_layout
)
195 qtutils
.add_close_action(self
)
196 qtutils
.connect_button(self
.close_button
, self
.close
)
197 qtutils
.connect_button(self
.create_button
, self
.create_branch
)
198 qtutils
.connect_button(self
.local_radio
, self
.display_model
)
199 qtutils
.connect_button(self
.remote_radio
, self
.display_model
)
200 qtutils
.connect_button(self
.tag_radio
, self
.display_model
)
202 self
.connect(self
.branch_list
, SIGNAL('itemSelectionChanged()'),
203 self
.branch_item_changed
)
205 self
.connect(self
.thread
, SIGNAL(COMMAND_SIGNAL
),
206 self
.thread_command
, Qt
.QueuedConnection
)
208 self
.connect(self
.thread
, SIGNAL('done(PyQt_PyObject)'),
209 self
.thread_done
, Qt
.QueuedConnection
)
211 if not self
.restore_state(settings
=settings
):
212 self
.resize(555, 333)
216 def set_revision(self
, revision
):
217 self
.revision
.setText(revision
)
220 self
.opts
.revision
= self
.revision
.value()
221 self
.opts
.branch
= self
.branch_name
.text()
222 self
.opts
.checkout
= self
.checkout_checkbox
.isChecked()
223 self
.opts
.reset
= self
.reset_radio
.isChecked()
224 self
.opts
.fetch
= self
.fetch_checkbox
.isChecked()
225 self
.opts
.track
= self
.remote_radio
.isChecked()
227 def create_branch(self
):
228 """Creates a branch; called by the "Create Branch" button"""
230 revision
= self
.opts
.revision
231 branch
= self
.opts
.branch
232 no_update
= self
.no_update_radio
.isChecked()
233 ffwd_only
= self
.ffwd_only_radio
.isChecked()
234 existing_branches
= gitcmds
.branch_list()
237 if not branch
or not revision
:
238 qtutils
.critical(N_('Missing Data'),
239 N_('Please provide both a branch '
240 'name and revision expression.'))
242 if branch
in existing_branches
:
244 msg
= N_('Branch "%s" already exists.') % branch
245 qtutils
.critical(N_('Branch Exists'), msg
)
247 # Whether we should prompt the user for lost commits
248 commits
= gitcmds
.rev_list_range(revision
, branch
)
249 check_branch
= bool(commits
)
252 msg
= (N_('Resetting "%(branch)s" to "%(revision)s" '
253 'will lose commits.') %
254 dict(branch
=branch
, revision
=revision
))
256 qtutils
.critical(N_('Branch Exists'), msg
)
259 for idx
, commit
in enumerate(commits
):
260 subject
= commit
[1][0:min(len(commit
[1]), 16)]
261 if len(subject
) < len(commit
[1]):
263 lines
.append('\t' + commit
[0][:8] + '\t' + subject
)
265 skip
= len(commits
) - 5
266 lines
.append('\t(%s)' % (N_('%d skipped') % skip
))
268 line
= N_('Recovering lost commits may not be easy.')
271 info_text
= (N_('Reset "%(branch)s" to "%(revision)s"?') %
272 dict(branch
=branch
, revision
=revision
))
274 if not qtutils
.confirm(N_('Reset Branch?'),
281 self
.setEnabled(False)
282 self
.progress
.setEnabled(True)
283 QtGui
.QApplication
.setOverrideCursor(Qt
.WaitCursor
)
285 # Show a nice progress bar
286 self
.progress
.setLabelText(N_('Updating...'))
290 def thread_command(self
, status
, out
, err
):
291 Interaction
.log_status(status
, out
, err
)
293 def thread_done(self
, results
):
294 self
.setEnabled(True)
295 self
.progress
.close()
296 QtGui
.QApplication
.restoreOverrideCursor()
298 for (cmd
, status
, out
, err
) in results
:
300 Interaction
.critical(
301 N_('Error Creating Branch'),
302 (N_('"%(command)s" returned exit status "%(status)d"') %
303 dict(command
='git '+cmd
, status
=status
)))
308 def branch_item_changed(self
, *rest
):
309 """This callback is called when the branch selection changes"""
310 # When the branch selection changes then we should update
311 # the "Revision Expression" accordingly.
312 qlist
= self
.branch_list
313 remote_branch
= qtutils
.selected_item(qlist
, self
.branch_sources())
314 if not remote_branch
:
316 # Update the model with the selection
317 self
.revision
.setText(remote_branch
)
319 # Set the branch field if we're branching from a remote branch.
320 if not self
.remote_radio
.isChecked():
322 branch
= gitcmds
.strip_remote(self
.model
.remotes
, remote_branch
)
325 # Signal that we've clicked on a remote branch
326 self
.branch_name
.setText(branch
)
328 def display_model(self
):
329 """Sets the branch list to the available branches
331 branches
= self
.branch_sources()
332 qtutils
.set_items(self
.branch_list
, branches
)
334 def branch_sources(self
):
335 """Get the list of items for populating the branch root list.
337 if self
.local_radio
.isChecked():
338 return self
.model
.local_branches
339 elif self
.remote_radio
.isChecked():
340 return self
.model
.remote_branches
341 elif self
.tag_radio
.isChecked():
342 return self
.model
.tags