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(self
.thread_command
, type=Qt
.QueuedConnection
)
206 thread
.result
.connect(self
.thread_result
, type=Qt
.QueuedConnection
)
208 self
.init_size(settings
=settings
, parent
=parent
)
211 def set_revision(self
, revision
):
212 self
.revision
.setText(revision
)
215 self
.opts
.revision
= get(self
.revision
)
216 self
.opts
.branch
= get(self
.branch_name
)
217 self
.opts
.checkout
= get(self
.checkout_checkbox
)
218 self
.opts
.reset
= get(self
.reset_radio
)
219 self
.opts
.fetch
= get(self
.fetch_checkbox
)
220 self
.opts
.track
= get(self
.remote_radio
)
222 def create_branch(self
):
223 """Creates a branch; called by the "Create Branch" button"""
224 context
= self
.context
226 revision
= self
.opts
.revision
227 branch
= self
.opts
.branch
228 no_update
= get(self
.no_update_radio
)
229 ffwd_only
= get(self
.ffwd_only_radio
)
230 existing_branches
= gitcmds
.branch_list(context
)
233 if not branch
or not revision
:
234 Interaction
.critical(
236 N_('Please provide both a branch '
237 'name and revision expression.'))
239 if branch
in existing_branches
:
241 msg
= N_('Branch "%s" already exists.') % branch
242 Interaction
.critical(N_('Branch Exists'), msg
)
244 # Whether we should prompt the user for lost commits
245 commits
= gitcmds
.rev_list_range(context
, revision
, branch
)
246 check_branch
= bool(commits
)
249 msg
= (N_('Resetting "%(branch)s" to "%(revision)s" '
250 'will lose commits.') %
251 dict(branch
=branch
, revision
=revision
))
253 Interaction
.critical(N_('Branch Exists'), msg
)
256 for idx
, commit
in enumerate(commits
):
257 subject
= commit
[1][0:min(len(commit
[1]), 16)]
258 if len(subject
) < len(commit
[1]):
260 lines
.append('\t' + commit
[0][:8] + '\t' + subject
)
262 skip
= len(commits
) - 5
263 lines
.append('\t(%s)' % (N_('%d skipped') % skip
))
265 line
= N_('Recovering lost commits may not be easy.')
268 info_text
= (N_('Reset "%(branch)s" to "%(revision)s"?') %
269 dict(branch
=branch
, revision
=revision
))
271 if not Interaction
.confirm(
272 N_('Reset Branch?'), '\n'.join(lines
), info_text
,
273 N_('Reset Branch'), default
=False, icon
=icons
.undo()):
276 title
= N_('Create Branch')
277 label
= N_('Updating')
278 self
.progress
= ProgressDialog(title
, label
, self
)
282 def thread_command(self
, status
, out
, err
):
283 Interaction
.log_status(status
, out
, err
)
285 def thread_result(self
, results
):
289 for (cmd
, status
, _
, _
) in results
:
291 Interaction
.critical(
292 N_('Error Creating Branch'),
293 (N_('"%(command)s" returned exit status "%(status)d"')
294 % dict(command
='git '+cmd
, status
=status
)))
299 # pylint: disable=unused-argument
300 def branch_item_changed(self
, *rest
):
301 """This callback is called when the branch selection changes"""
302 # When the branch selection changes then we should update
303 # the "Revision Expression" accordingly.
304 qlist
= self
.branch_list
305 remote_branch
= qtutils
.selected_item(qlist
, self
.branch_sources())
306 if not remote_branch
:
308 # Update the model with the selection
309 self
.revision
.setText(remote_branch
)
311 # Set the branch field if we're branching from a remote branch.
312 if not get(self
.remote_radio
):
314 branch
= gitcmds
.strip_remote(self
.model
.remotes
, remote_branch
)
317 # Signal that we've clicked on a remote branch
318 self
.branch_name
.set_value(branch
)
320 def display_model(self
):
321 """Sets the branch list to the available branches
323 branches
= self
.branch_sources()
324 qtutils
.set_items(self
.branch_list
, branches
)
326 def branch_sources(self
):
327 """Get the list of items for populating the branch root list.
329 if get(self
.local_radio
):
330 value
= self
.model
.local_branches
331 elif get(self
.remote_radio
):
332 value
= self
.model
.remote_branches
333 elif get(self
.tag_radio
):
334 value
= self
.model
.tags