Kiln » TortoiseHg » TortoiseHg
Clone URL:  
Pushed to one repository · View In Graph Contained in 1.9, 1.9.1, and 1.9.2

rebase: introduce a rebase dialog

Rebase process can now handle merge conclicts and resume. It also allows the
user to select '--keep' and/or '--detach'. The next step is to use this dialog
to finish pull --rebase operations that require conflicts to be resolved.

Changeset 890ca5486db7

Parent fdcf85d1c0d8

by Steve Borho

Changes to 3 files · Browse files at 890ca5486db7 Showing diff from parent fdcf85d1c0d8 Diff from another changeset...

Change 1 of 1 Show Entire File tortoisehg/​hgqt/​rebase.py Stacked
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
@@ -0,0 +1,158 @@
+# rebase.py - Rebase dialog for TortoiseHg +# +# Copyright 2010 Steve Borho <steve@borho.org> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from PyQt4.QtCore import * +from PyQt4.QtGui import * + +import os + +from mercurial import util, merge as mergemod + +from tortoisehg.util import hglib +from tortoisehg.hgqt.i18n import _ +from tortoisehg.hgqt import qtlib, csinfo, cmdui, resolve + +BB = QDialogButtonBox + +class RebaseDialog(QDialog): + + def __init__(self, repo, parent, **opts): + super(RebaseDialog, self).__init__(parent) + f = self.windowFlags() + self.setWindowFlags(f & ~Qt.WindowContextHelpButtonHint) + self.repo = repo + self.opts = opts + + box = QVBoxLayout() + box.setSpacing(8) + box.setContentsMargins(*(6,)*4) + self.setLayout(box) + + style = csinfo.panelstyle(selectable=True) + + srcb = QGroupBox( _('Rebase changeset and descendants')) + srcb.setLayout(QVBoxLayout()) + srcb.layout().setContentsMargins(*(2,)*4) + s = opts.get('source', '.') + source = csinfo.create(self.repo, s, style, withupdate=True) + srcb.layout().addWidget(source) + self.layout().addWidget(srcb) + + destb = QGroupBox( _('To rebase destination')) + destb.setLayout(QVBoxLayout()) + destb.layout().setContentsMargins(*(2,)*4) + d = opts.get('dest', '.') + dest = csinfo.create(self.repo, d, style, withupdate=True) + destb.layout().addWidget(dest) + self.layout().addWidget(destb) + + sep = qtlib.LabeledSeparator(_('Options')) + self.layout().addWidget(sep) + + self.keepchk = QCheckBox(_('Keep original changesets')) + self.keepchk.setChecked(opts.get('keep', False)) + self.layout().addWidget(self.keepchk) + + self.detachchk = QCheckBox(_('Force detach of rebased changesets ' + 'from their original branch')) + self.detachchk.setChecked(opts.get('detach', True)) + self.layout().addWidget(self.detachchk) + + sep = qtlib.LabeledSeparator(_('Status')) + self.layout().addWidget(sep) + + self.reslabel = QLabel() + self.reslabel.linkActivated.connect(self.linkActivated) + self.layout().addWidget(self.reslabel) + + self.cmd = cmdui.Widget() + self.cmd.commandFinished.connect(self.commandFinished) + self.layout().addWidget(self.cmd, 2) + + bbox = QDialogButtonBox(BB.Cancel|BB.Ok) + bbox.button(BB.Ok).setText('Rebase') + bbox.button(BB.Cancel).setText('Abort') + bbox.button(BB.Cancel).setEnabled(False) + bbox.accepted.connect(self.rebase) + bbox.rejected.connect(self.abort) + self.layout().addWidget(bbox) + self.bbox = bbox + + if self.checkResolve() or not (s or d): + for w in (srb, destb, sep, self.keepchk, self.detachchk): + w.setHidden(True) + + self.setMinimumWidth(480) + self.setMaximumHeight(800) + self.resize(0, 340) + self.setWindowTitle(_('Rebase - %s') % self.repo.displayname) + + def rebase(self): + self.keepchk.setEnabled(False) + self.detachchk.setEnabled(False) + cmdline = ['rebase', '--repository', self.repo.root] + cmdline += ['--config', 'ui.merge=internal:fail'] + if os.path.exists(self.repo.join('rebasestate')): + cmdline += ['--continue'] + else: + if self.keepchk.isChecked(): + cmdline += ['--keep'] + if self.detachchk.isChecked(): + cmdline += ['--detach'] + source = self.opts.get('source') + dest = self.opts.get('dest') + cmdline += ['--source', str(source), '--dest', str(dest)] + self.repo.incrementBusyCount() + self.cmd.run(cmdline) + + def abort(self): + cmdline = ['rebase', '--repository', self.repo.root, '--abort'] + self.repo.incrementBusyCount() + self.cmd.run(cmdline) + + def commandFinished(self, ret): + self.repo.decrementBusyCount() + if self.checkResolve() is False: + self.reslabel.setText(_('<h3>Rebase is complete</h3>')) + self.bbox.button(BB.Ok).setText(_('Close')) + self.bbox.accepted.disconnect(self.rebase) + self.bbox.accepted.connect(self.accept) + + def checkResolve(self): + ms = mergemod.mergestate(self.repo) + for path in ms: + if ms[path] == 'u': + txt = _('<h3>Rebase generated merge <b>conflicts</b> that must ' + 'be <a href="resolve"><b>resolved</b></a></h3>') + self.bbox.button(BB.Ok).setEnabled(False) + break + else: + self.bbox.button(BB.Ok).setEnabled(True) + txt = _('<h3>You may continue the rebase</h3>') + self.reslabel.setText(txt) + + if os.path.exists(self.repo.join('rebasestate')): + self.bbox.button(BB.Cancel).setEnabled(True) + self.bbox.button(BB.Ok).setText('Continue') + return True + else: + self.bbox.button(BB.Cancel).setEnabled(False) + return False + + def linkActivated(self, cmd): + if cmd == 'resolve': + dlg = resolve.ResolveDialog(self.repo, self) + dlg.exec_() + self.checkResolve() + +def run(ui, *pats, **opts): + from tortoisehg.util import paths + from tortoisehg.hgqt import thgrepo + repo = thgrepo.repository(ui, path=paths.find_root()) + if not opts['source'] or not opts['dest']: + raise util.Abort('source and dest must be supplied') + return RebaseDialog(repo, None, **opts)
 
21
22
23
24
 
25
26
27
 
723
724
725
726
727
728
729
730
731
732
733
734
735
736
 
 
 
 
737
738
739
 
21
22
23
 
24
25
26
27
 
723
724
725
 
 
 
 
 
 
 
 
 
 
 
726
727
728
729
730
731
732
@@ -21,7 +21,7 @@
 from tortoisehg.hgqt.repomodel import HgRepoListModel  from tortoisehg.hgqt import cmdui, update, tag, backout, merge, visdiff  from tortoisehg.hgqt import archive, thgimport, thgstrip, run, thgrepo, purge -from tortoisehg.hgqt import bisect, resolve +from tortoisehg.hgqt import bisect, rebase, resolve    from tortoisehg.hgqt.repofilter import RepoFilterBar  from tortoisehg.hgqt.repoview import HgRepoView @@ -723,17 +723,10 @@
    def rebaseRevision(self):   """Rebase selected revision on top of working directory parent""" - srcrev = self.rev - dstrev = self.repo['.'].rev() - main = _("Confirm Rebase Revision") - text = _("Rebase revision %d on top of %d?") % (srcrev, dstrev) - labels = ((QMessageBox.Yes, _('&Yes')), - (QMessageBox.No, _('&No'))) - cmdline = ['rebase', '--source', str(srcrev), '--dest', str(dstrev), - '--repository', self.repo.root] - if QuestionMsgBox(_('Confirm Rebase'), main, text, labels=labels, - parent=self): - self.runCommand(_('Rebase - TortoiseHg'), cmdline) + opts = {'source' : self.rev, 'dest': self.repo['.'].rev()} + dlg = rebase.RebaseDialog(self.repo, self, **opts) + dlg.finished.connect(dlg.deleteLater) + dlg.exec_()     def qimportRevision(self):   """QImport revision and all descendents to MQ"""
 
649
650
651
 
 
 
 
 
652
653
654
 
960
961
962
 
 
 
 
 
 
 
 
 
963
964
965
 
649
650
651
652
653
654
655
656
657
658
659
 
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
@@ -649,6 +649,11 @@
  from tortoisehg.hgqt.thgstrip import run   qtrun(run, ui, *pats, **opts)   +def rebase(ui, *pats, **opts): + """rebase dialog""" + from tortoisehg.hgqt.rebase import run + qtrun(run, ui, *pats, **opts) +  def thgimport(ui, *pats, **opts):   """import an ordered set of patches"""   from tortoisehg.hgqt.thgimport import run @@ -960,6 +965,15 @@
  ('n', 'nobackup', None, _('do not back up stripped revisions')),   ('r', 'rev', '', _('revision to strip')),],   _('thg strip [-f] [-n] [[-r] REV]')), + "^rebase": (rebase, + [('', 'keep', False, _('keep original changesets')), + ('', 'detach', False, _('force detaching of source from its original ' + 'branch')), + ('s', 'source', '', + _('rebase from the specified changeset')), + ('d', 'dest', '', + _('rebase onto the specified changeset'))], + _('thg rebase -s REV -d REV [--keep] [--detach]')),   "^tag":   (tag,   [('f', 'force', None, _('replace existing tag')),