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
9 from ..interaction
import Interaction
10 from ..qtutils
import get
11 from .. import gitcmds
13 from .. import qtutils
14 from .standard
import Dialog
15 from .standard
import ProgressDialog
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())
25 view
.set_revision(revision
)
30 class CreateOpts(object):
31 def __init__(self
, context
):
32 self
.context
= context
37 self
.revision
= 'HEAD'
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
)
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
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
))
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
))
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
)
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
)
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'),
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
,
141 self
.checkout_checkbox
,
144 self
.branch_name_layout
= qtutils
.hbox(defs
.margin
, defs
.spacing
,
145 self
.branch_name_label
,
148 self
.rev_radio_group
= qtutils
.buttongroup(self
.local_radio
,
152 self
.rev_radio_layout
= qtutils
.hbox(defs
.margin
, defs
.spacing
,
158 self
.rev_start_textinput_layout
= qtutils
.hbox(defs
.no_margin
,
164 self
.rev_start_layout
= qtutils
.vbox(defs
.no_margin
, defs
.spacing
,
165 self
.rev_radio_layout
,
167 self
.rev_start_textinput_layout
)
169 self
.options_radio_group
= qtutils
.buttongroup(self
.no_update_radio
,
170 self
.ffwd_only_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
,
180 self
.buttons_layout
= qtutils
.hbox(defs
.margin
, defs
.spacing
,
185 self
.main_layout
= qtutils
.vbox(defs
.margin
, defs
.spacing
,
186 self
.branch_name_layout
,
187 self
.rev_start_layout
,
189 self
.options_radio_layout
,
190 self
.options_checkbox_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
)
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
)
212 def set_revision(self
, revision
):
213 self
.revision
.setText(revision
)
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
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
)
234 if not branch
or not revision
:
235 Interaction
.critical(
237 N_('Please provide both a branch '
238 'name and revision expression.'))
240 if branch
in existing_branches
:
242 msg
= N_('Branch "%s" already exists.') % branch
243 Interaction
.critical(N_('Branch Exists'), msg
)
245 # Whether we should prompt the user for lost commits
246 commits
= gitcmds
.rev_list_range(context
, revision
, branch
)
247 check_branch
= bool(commits
)
250 msg
= (N_('Resetting "%(branch)s" to "%(revision)s" '
251 'will lose commits.') %
252 dict(branch
=branch
, revision
=revision
))
254 Interaction
.critical(N_('Branch Exists'), 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]):
261 lines
.append('\t' + commit
[0][:8] + '\t' + subject
)
263 skip
= len(commits
) - 5
264 lines
.append('\t(%s)' % (N_('%d skipped') % skip
))
266 line
= N_('Recovering lost commits may not be easy.')
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()):
277 title
= N_('Create Branch')
278 label
= N_('Updating')
279 self
.progress
= ProgressDialog(title
, label
, self
)
283 def thread_result(self
, results
):
287 for (cmd
, status
, _
, _
) in results
:
289 Interaction
.critical(
290 N_('Error Creating Branch'),
291 (N_('"%(command)s" returned exit status "%(status)d"')
292 % dict(command
='git '+cmd
, status
=status
)))
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
:
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
):
312 branch
= gitcmds
.strip_remote(self
.model
.remotes
, remote_branch
)
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