git-cola v4.3.1
[git-cola.git] / cola / widgets / merge.py
blob9c19033573c28718eb2dd8916664eec0be2ba1c4
1 from __future__ import division, absolute_import, unicode_literals
3 from qtpy import QtWidgets
4 from qtpy.QtCore import Qt
6 from ..i18n import N_
7 from ..interaction import Interaction
8 from ..qtutils import get
9 from .. import cmds
10 from .. import icons
11 from .. import qtutils
12 from . import completion
13 from . import standard
14 from . import defs
17 def local_merge(context):
18 """Provides a dialog for merging branches"""
19 view = Merge(context, qtutils.active_window())
20 view.show()
21 view.raise_()
22 return view
25 class Merge(standard.Dialog):
26 """Provides a dialog for merging branches."""
28 def __init__(self, context, parent=None, ref=None):
29 standard.Dialog.__init__(self, parent=parent)
30 self.context = context
31 self.cfg = cfg = context.cfg
32 self.model = context.model
33 if parent is not None:
34 self.setWindowModality(Qt.WindowModal)
36 # Widgets
37 self.title_label = QtWidgets.QLabel()
38 self.revision_label = QtWidgets.QLabel()
39 self.revision_label.setText(N_('Revision to Merge'))
41 self.revision = completion.GitRefLineEdit(context)
42 self.revision.setToolTip(N_('Revision to Merge'))
43 if ref:
44 self.revision.set_value(ref)
46 self.radio_local = qtutils.radio(text=N_('Local Branch'), checked=True)
47 self.radio_remote = qtutils.radio(text=N_('Tracking Branch'))
48 self.radio_tag = qtutils.radio(text=N_('Tag'))
50 self.revisions = QtWidgets.QListWidget()
51 self.revisions.setAlternatingRowColors(True)
53 self.button_viz = qtutils.create_button(
54 text=N_('Visualize'), icon=icons.visualize()
57 tooltip = N_('Squash the merged commits into a single commit')
58 self.checkbox_squash = qtutils.checkbox(text=N_('Squash'), tooltip=tooltip)
60 tooltip = N_(
61 'Always create a merge commit when enabled, '
62 'even when the merge is a fast-forward update'
64 self.checkbox_noff = qtutils.checkbox(
65 text=N_('No fast forward'), tooltip=tooltip, checked=False
67 self.checkbox_noff_state = False
69 tooltip = N_(
70 'Commit the merge if there are no conflicts. '
71 'Uncheck to leave the merge uncommitted'
73 self.checkbox_commit = qtutils.checkbox(
74 text=N_('Commit'), tooltip=tooltip, checked=True
76 self.checkbox_commit_state = True
78 text = N_('Create Signed Commit')
79 checked = cfg.get('cola.signcommits', False)
80 tooltip = N_('GPG-sign the merge commit')
81 self.checkbox_sign = qtutils.checkbox(
82 text=text, checked=checked, tooltip=tooltip
84 self.button_close = qtutils.close_button()
86 icon = icons.merge()
87 self.button_merge = qtutils.create_button(
88 text=N_('Merge'), icon=icon, default=True
91 # Layouts
92 self.revlayt = qtutils.hbox(
93 defs.no_margin,
94 defs.spacing,
95 self.revision_label,
96 self.revision,
97 qtutils.STRETCH,
98 self.title_label,
101 self.radiolayt = qtutils.hbox(
102 defs.no_margin,
103 defs.spacing,
104 self.radio_local,
105 self.radio_remote,
106 self.radio_tag,
109 self.buttonlayt = qtutils.hbox(
110 defs.no_margin,
111 defs.button_spacing,
112 self.button_viz,
113 self.checkbox_squash,
114 self.checkbox_noff,
115 self.checkbox_commit,
116 self.checkbox_sign,
117 qtutils.STRETCH,
118 self.button_close,
119 self.button_merge,
122 self.mainlayt = qtutils.vbox(
123 defs.margin,
124 defs.spacing,
125 self.radiolayt,
126 self.revisions,
127 self.revlayt,
128 self.buttonlayt,
130 self.setLayout(self.mainlayt)
132 # Signal/slot connections
133 # pylint: disable=no-member
134 self.revision.textChanged.connect(self.update_title)
135 self.revision.enter.connect(self.merge_revision)
136 self.revisions.itemSelectionChanged.connect(self.revision_selected)
138 qtutils.connect_released(self.radio_local, self.update_revisions)
139 qtutils.connect_released(self.radio_remote, self.update_revisions)
140 qtutils.connect_released(self.radio_tag, self.update_revisions)
141 qtutils.connect_button(self.button_merge, self.merge_revision)
142 qtutils.connect_button(self.checkbox_squash, self.toggle_squash)
143 qtutils.connect_button(self.button_viz, self.viz_revision)
144 qtutils.connect_button(self.button_close, self.reject)
146 # Observer messages
147 self.model.updated.connect(self.update_all)
148 self.update_all()
150 self.init_size(parent=parent)
151 self.revision.setFocus()
153 def update_all(self):
154 """Set the branch name for the window title and label."""
155 self.update_title()
156 self.update_revisions()
158 def update_title(self, _txt=None):
159 branch = self.model.currentbranch
160 revision = self.revision.text()
161 if revision:
162 txt = N_('Merge "%(revision)s" into "%(branch)s"') % {
163 'revision': revision,
164 'branch': branch,
166 else:
167 txt = N_('Merge into "%s"') % branch
168 self.button_merge.setEnabled(bool(revision))
169 self.title_label.setText(txt)
170 self.setWindowTitle(txt)
172 def toggle_squash(self):
173 """Toggles the commit checkbox based on the squash checkbox."""
174 if get(self.checkbox_squash):
175 self.checkbox_commit_state = self.checkbox_commit.checkState()
176 self.checkbox_commit.setCheckState(Qt.Unchecked)
177 self.checkbox_commit.setDisabled(True)
178 self.checkbox_noff_state = self.checkbox_noff.checkState()
179 self.checkbox_noff.setCheckState(Qt.Unchecked)
180 self.checkbox_noff.setDisabled(True)
181 else:
182 self.checkbox_noff.setDisabled(False)
183 oldstateff = self.checkbox_noff_state
184 self.checkbox_noff.setCheckState(oldstateff)
185 self.checkbox_commit.setDisabled(False)
186 oldstate = self.checkbox_commit_state
187 self.checkbox_commit.setCheckState(oldstate)
189 def update_revisions(self):
190 """Update the revision list whenever a radio button is clicked"""
191 self.revisions.clear()
192 self.revisions.addItems(self.current_revisions())
194 def revision_selected(self):
195 """Update the revision field when a list item is selected"""
196 revlist = self.current_revisions()
197 widget = self.revisions
198 revision = qtutils.selected_item(widget, revlist)
199 if revision is not None:
200 self.revision.setText(revision)
202 def current_revisions(self):
203 """Retrieve candidate items to merge"""
204 if get(self.radio_local):
205 return self.model.local_branches
206 if get(self.radio_remote):
207 return self.model.remote_branches
208 if get(self.radio_tag):
209 return self.model.tags
210 return []
212 def viz_revision(self):
213 """Launch a gitk-like viewer on the selection revision"""
214 revision = self.revision.text()
215 if not revision:
216 Interaction.information(
217 N_('No Revision Specified'), N_('You must specify a revision to view.')
219 return
220 cmds.do(cmds.VisualizeRevision, self.context, revision)
222 def merge_revision(self):
223 """Merge the selected revision/branch"""
224 revision = self.revision.text()
225 if not revision:
226 Interaction.information(
227 N_('No Revision Specified'), N_('You must specify a revision to merge.')
229 return
231 noff = get(self.checkbox_noff)
232 no_commit = not get(self.checkbox_commit)
233 squash = get(self.checkbox_squash)
234 sign = get(self.checkbox_sign)
235 context = self.context
236 cmds.do(cmds.Merge, context, revision, no_commit, squash, noff, sign)
237 self.accept()
239 def export_state(self):
240 """Export persistent settings"""
241 state = super(Merge, self).export_state()
242 state['no-ff'] = get(self.checkbox_noff)
243 state['sign'] = get(self.checkbox_sign)
244 state['commit'] = get(self.checkbox_commit)
245 return state
247 def apply_state(self, state):
248 """Apply persistent settings"""
249 result = super(Merge, self).apply_state(state)
250 self.checkbox_noff.setChecked(state.get('no-ff', False))
251 self.checkbox_sign.setChecked(state.get('sign', False))
252 self.checkbox_commit.setChecked(state.get('commit', True))
253 return result