Kiln » TortoiseHg » TortoiseHg
Clone URL:  
Pushed to one repository · View In Graph Contained in 0.8, 0.8.1, and 0.8.2

merge: merge refactor

The merge dialog is intended to be used from the changelog tool, so
remove revision finding code. Show details of local and other changesets
so user has clear idea of what is being merged (often helpful while
merging). Toolbar replaced with bottom button bar with buttons Close,
Undo, Commit, Merge. Button states are modified by actions. Still uses
hgcmd to do hg operations (perhaps in 0.9 we can make hgcmd a pluggable
widget). Keyboard accelerators for merge (ctrl-enter) and close (esc).
Likely action buttons are given focus().

Fixes #28, #54, #141

Changeset eb5f6f185d32

Parent 71d7e381eca0

by Steve Borho

Changes to 3 files · Browse files at eb5f6f185d32 Showing diff from parent 71d7e381eca0 Diff from another changeset...

Change 1 of 1 Show Entire File hggtk/​hgtk.py Stacked
 
576
577
578
579
 
 
 
580
581
582
 
576
577
578
 
579
580
581
582
583
584
@@ -576,7 +576,9 @@
  "^log|history": (log,   [('l', 'limit', '', _('limit number of changes displayed'))],   _('hgtk log [OPTIONS] [FILE]')), - "^merge": (merge, [], _('hgtk merge')), + "^merge": (merge, + [('r', 'rev', '', _('revision to update'))], + _('hgtk merge')),   "^recovery|rollback|verify": (recovery, [], _('hgtk recovery')),   "^shelve|unshelve": (shelve, [], _('hgtk shelve')),   "^synch|pull|push|incoming|outgoing|email": (synch, [], _('hgtk synch')),
Change 1 of 1 Show Entire File hggtk/​merge.py Stacked
 
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
 
 
205
206
207
208
209
210
211
 
 
 
 
212
213
214
215
 
 
 
 
 
216
217
218
219
220
221
222
223
 
 
 
224
225
226
227
228
229
230
 
231
232
233
234
235
236
237
 
 
 
 
238
239
240
 
 
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
159
160
 
161
@@ -4,237 +4,158 @@
 # Copyright (C) 2007 TK Soh <teekaysoh@gmail.com>  #   +import os  import gtk +import gobject +import re  import sys -from dialog import error_dialog +  from mercurial.node import short, nullrev +from mercurial.hgweb import webutil  from mercurial.i18n import _  from mercurial import util, hg, ui +  import gdialog  import hgcmd  import shlib -import histselect  import hglib    class MergeDialog(gtk.Window):   """ Dialog to merge revisions of a Mercurial repo """ - def __init__(self, root='', rev=''): + def __init__(self, rev=None):   """ Initialize the Dialog """   gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)   shlib.set_tortoise_icon(self, 'menumerge.ico')   shlib.set_tortoise_keys(self) + self.notify_func = None   - # set dialog title - title = "hg merge" - if root: title += " - %s" % root + if not rev: + gdialog.Prompt(_('Unable to merge'), + _('Must supply a target revision'), self).run() + gobject.idle_add(self.destroy) + return + + try: + repo = hg.repository(ui.ui(), path=hglib.rootpath()) + except hglib.RepoError: + gobject.idle_add(self.destroy) + return + + title = _("Merging in ") + hglib.toutf(os.path.basename(repo.root))   self.set_title(title) + self.set_default_size(350, 120)   - self.root = root or hglib.rootpath() - self.rev = rev - self.repo = None - self.notify_func = None - self._create() + vbox = gtk.VBox() + self.add(vbox) + + frame = gtk.Frame(_('Merge target (other)')) + lbl = gtk.Label() + other, desc = self.revdesc(repo, rev) + lbl.set_markup(desc) + lbl.set_alignment(0, 0) + frame.add(lbl) + frame.set_border_width(5) + vbox.pack_start(frame, False, False, 2) + + frame = gtk.Frame(_('Current revision (local)')) + lbl = gtk.Label() + local, desc = self.revdesc(repo, '.') + lbl.set_markup(desc) + lbl.set_alignment(0, 0) + frame.add(lbl) + frame.set_border_width(5) + vbox.pack_start(frame, False, False, 2) + + accelgroup = gtk.AccelGroup() + self.add_accel_group(accelgroup) + mod = shlib.get_thg_modifier() + + hbbox = gtk.HButtonBox() + hbbox.set_layout(gtk.BUTTONBOX_END) + vbox.pack_start(hbbox, False, False, 2) + + close = gtk.Button(_('Close')) + close.connect('clicked', lambda x: self.destroy()) + key, modifier = gtk.accelerator_parse('Escape') + close.add_accelerator('clicked', accelgroup, key, 0, + gtk.ACCEL_VISIBLE) + hbbox.add(close) + + undo = gtk.Button(_('Undo')) + hbbox.add(undo) + undo.set_sensitive(False) + + commit = gtk.Button(_('Commit')) + hbbox.add(commit) + commit.set_sensitive(False) + + merge = gtk.Button(_('Merge')) + key, modifier = gtk.accelerator_parse(mod+'Return') + merge.add_accelerator('clicked', accelgroup, key, modifier, + gtk.ACCEL_VISIBLE) + hbbox.add(merge) + merge.grab_focus() + + undo.connect('clicked', self.undo, local, merge, commit) + merge.connect('clicked', self.merge, other, commit, undo) + commit.connect('clicked', self.commit) + + def revdesc(self, repo, revid): + ctx = repo.changectx(revid) + revstr = str(ctx.rev()) + summary = ctx.description().replace('\0', '') + summary = summary.split('\n')[0] + escape = gobject.markup_escape_text + desc = '<b>rev</b>\t\t: %s\n' % escape(revstr) + desc += '<b>summary</b>\t: %s\n' % escape(summary[:80]) + desc += '<b>user</b>\t\t: %s\n' % escape(ctx.user()) + desc += '<b>date</b>\t\t: %s\n' % escape(hglib.displaytime(ctx.date())) + node = repo.lookup(revid) + tags = repo.nodetags(node) + desc += '<b>branch</b>\t: ' + escape(ctx.branch()) + if tags: + desc += '\n<b>tags</b>\t\t: ' + escape(', '.join(tags)) + if node not in repo.heads(): + desc += '\n<b>Not a head revision!</b>' + return revstr, hglib.toutf(desc)     def set_notify_func(self, func, *args):   self.notify_func = func   self.notify_args = args   - def _create(self): - self.set_default_size(350, 120) - - # add toolbar with tooltips - self.tbar = gtk.Toolbar() - self.tips = gtk.Tooltips() - - self._btn_merge = self._toolbutton( - gtk.STOCK_CONNECT, - _('merge'), - self._btn_merge_clicked, - menu=self._merge_menu(), - tip=_('Merge working revision with selected revision')) - self._btn_unmerge = self._toolbutton( - gtk.STOCK_DISCONNECT, - _('unmerge'), - self._btn_unmerge_clicked, - tip=_('Undo merging and return working directory to' - ' one of it parent revision')) - tbuttons = [ - self._btn_merge, - gtk.SeparatorToolItem(), - self._btn_unmerge - ] - for btn in tbuttons: - self.tbar.insert(btn, -1) - vbox = gtk.VBox() - self.add(vbox) - vbox.pack_start(self.tbar, False, False, 2) - - # repo parent revisions - parentbox = gtk.HBox() - lbl = gtk.Label(_('Parent revision(s):')) - lbl.set_property('width-chars', 18) - lbl.set_alignment(0, 0.5) - self._parent_revs = gtk.Entry() - parentbox.pack_start(lbl, False, False) - parentbox.pack_start(self._parent_revs, True, True) - vbox.pack_start(parentbox, False, False, 2) - - # revision input - revbox = gtk.HBox() - self._rev_lbl = gtk.Label(_('Merge with revision:')) - self._rev_lbl.set_property('width-chars', 18) - self._rev_lbl.set_alignment(0, 0.5) - - # revisions combo box - self._revlist = gtk.ListStore(str, str) - self._revbox = gtk.ComboBoxEntry(self._revlist, 0) - - # add extra column to droplist for type of changeset - cell = gtk.CellRendererText() - self._revbox.pack_start(cell) - self._revbox.add_attribute(cell, 'text', 1) - self._rev_input = self._revbox.get_child() - - self._btn_rev_browse = gtk.Button(_('Browse...')) - self._btn_rev_browse.connect('clicked', self._btn_rev_clicked) - revbox.pack_start(self._rev_lbl, False, False) - revbox.pack_start(self._revbox, False, False) - revbox.pack_start(self._btn_rev_browse, False, False, 5) - vbox.pack_start(revbox, False, False, 2) - - # show them all - self._refresh() - - def _toolbutton(self, stock, label, handler, - menu=None, userdata=None, tip=None): - if menu: - tbutton = gtk.MenuToolButton(stock) - tbutton.set_menu(menu) - else: - tbutton = gtk.ToolButton(stock) - - tbutton.set_label(label) - if tip: - tbutton.set_tooltip(self.tips, tip) - tbutton.connect('clicked', handler, userdata) - return tbutton - - def _refresh(self): - """ update display on dialog with recent repo data """ - try: - # FIXME: force hg to refresh parents info - del self.repo - self.repo = hg.repository(ui.ui(), path=self.root) - except hglib.RepoError: - return None - - # populate parent rev data - self._parents = [x.node() for x in self.repo.changectx(None).parents()] - self._parent_revs.set_sensitive(True) - self._parent_revs.set_text(", ".join([short(x) for x in self._parents])) - self._parent_revs.set_sensitive(False) - - # condition some widgets per state of working directory - is_merged = len(self._parents) > 1 - if is_merged: - self._btn_merge.set_sensitive(False) - self._btn_unmerge.set_sensitive(True) - self._rev_lbl.set_text(_('Unmerge to revision:')) - self._btn_rev_browse.set_sensitive(False) - else: - self._btn_merge.set_sensitive(True) - self._btn_unmerge.set_sensitive(False) - self._rev_lbl.set_text(_('Merge with revision:')) - self._btn_rev_browse.set_sensitive(True) - - # populate revision data - heads = self.repo.heads() - tip = self.repo.changelog.node(nullrev+len(self.repo.changelog)) - self._revlist.clear() - self._rev_input.set_text("") - for i, node in enumerate(heads): - if node in self._parents and not is_merged: - continue - - status = "head %d" % (i+1) - if node == tip: - status += ", tip" - - self._revlist.append([short(node), "(%s)" %status]) - self._rev_input.set_text(short(node)) - - if self.rev: - self._rev_input.set_text(str(self.rev)) - - def _merge_menu(self): - menu = gtk.Menu() - - self._chbox_force = gtk.CheckMenuItem(_('Allow merge with uncommited changes')) - menu.append(self._chbox_force) - - menu.show_all() - return menu - - def _btn_rev_clicked(self, button): - """ select revision from history dialog """ - rev = histselect.select(self.root) - if rev is not None: - self._rev_input.set_text(rev) - - def _btn_merge_clicked(self, toolbutton, data=None): - self._do_merge() - - def _btn_unmerge_clicked(self, toolbutton, data=None): - self._do_unmerge() - - def _do_unmerge(self): - rev = self._rev_input.get_text() - - if not rev: - error_dialog(self, _('Cannot unmerge'), - _('please select revision to unmerge')) - return - - response = gdialog.Confirm(_('undo merge'), [], self, - _('and checkout revision %s?') % rev).run() - if response != gtk.RESPONSE_YES: - return - - cmdline = ['hg', 'update', '-R', self.root, '--rev', rev, - '--clean', '--verbose'] + def merge(self, button, other, commit, undo): + cmdline = ['hg', 'merge', '--rev', other]   dlg = hgcmd.CmdDialog(cmdline)   dlg.run()   dlg.hide()   if self.notify_func:   self.notify_func(self.notify_args) - shlib.shell_notify([self.root]) - self._refresh() + button.set_sensitive(False) + undo.set_sensitive(True) + commit.set_sensitive(True) + commit.grab_focus()   - def _do_merge(self): - rev = self._rev_input.get_text() - force = self._chbox_force.get_active() + def commit(self, button): + import commit + dlg = commit.run(ui.ui()) + dlg.set_modal(True) + dlg.display()   - if not rev: - error_dialog(self, _('Cannot merge'), - _('please enter revision to merge')) - return - - response = gdialog.Confirm(_('merge'), [], self, - _('with revision %s') % rev).run() + def undo(self, button, local, merge, commit): + response = gdialog.Confirm(_('undo merge'), [], self, + _('Clean checkout of original revision?')).run()   if response != gtk.RESPONSE_YES:   return - - cmdline = ['hg', 'merge', '-R', self.root, '--rev', rev] - if force: - cmdline.append("--force") - + cmdline = ['hg', 'update', '--rev', local, '--clean']   dlg = hgcmd.CmdDialog(cmdline)   dlg.run()   dlg.hide() - shlib.shell_notify([self.root])   if self.notify_func:   self.notify_func(self.notify_args) - self._refresh() + button.set_sensitive(False) + commit.set_sensitive(False) + merge.set_sensitive(True) + merge.grab_focus()    def run(ui, *pats, **opts): - return MergeDialog() + return MergeDialog(opts.get('rev'))
Change 1 of 2 Show Entire File hggtk/​update.py Stacked
 
11
12
13
14
 
15
16
17
 
30
31
32
33
34
35
 
 
 
 
36
37
 
38
39
40
 
11
12
13
 
14
15
16
17
 
30
31
32
 
 
 
33
34
35
36
37
 
38
39
40
41
@@ -11,7 +11,7 @@
 from mercurial import util, hg, ui  from mercurial.node import short, nullrev  from mercurial.i18n import _ -from hglib import rootpath, toutf, RepoError +import hglib  import shlib  import hgcmd  import gdialog @@ -30,11 +30,12 @@
  self.notify_func = None     try: - repo = hg.repository(ui.ui()) - except RepoError: - self.destroy() + repo = hg.repository(ui.ui(), path=hglib.rootpath()) + except hglib.RepoError: + gobject.idle_add(self.destroy) + return   - title = "hg update - %s" % toutf(os.path.basename(repo.root)) + title = "hg update - %s" % hglib.toutf(os.path.basename(repo.root))   self.set_title(title)     vbox = gtk.VBox()