Kiln » TortoiseHg » TortoiseHg
Clone URL:  
Pushed to one repository · View In Graph Contained in 0.9, 0.9.1, and 0.9.1.1

merge: merge workflow improvements

* hgtk merge from command line should work if repo already has two parents
* any two mergable changesets can be selected for merge in changelog tool
* cleanup 'mergable changeset' and backout decision logic

Fixes #496

Changeset 4636e451b517

Parent 33a1a07adece

by Steve Borho

Changes to 2 files · Browse files at 4636e451b517 Showing diff from parent 33a1a07adece Diff from another changeset...

 
50
51
52
 
53
54
55
 
643
644
645
646
647
648
649
650
651
652
 
 
 
 
 
 
 
 
 
 
653
 
654
655
656
 
688
689
690
 
691
692
693
 
713
714
715
716
717
 
 
 
 
 
 
 
718
719
720
 
1609
1610
1611
1612
1613
1614
1615
1616
1617
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
 
50
51
52
53
54
55
56
 
644
645
646
 
 
 
 
 
 
 
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
 
693
694
695
696
697
698
699
 
719
720
721
 
 
722
723
724
725
726
727
728
729
730
731
 
1620
1621
1622
 
 
 
 
 
 
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
 
 
 
 
 
 
 
 
 
 
 
 
1645
1646
1647
@@ -50,6 +50,7 @@
  self.npreviews = 0   self.outgoing = []   self.useproxy = None + self.revrange = None   self.forcepush = False   os.chdir(self.repo.root)   @@ -643,14 +644,18 @@
  m.append(create_menu(_('_Archive...'), self.archive))     # disable/enable menus as required - parents = [x.rev() for x in self.repo.parents()] - can_merge = self.currevid not in parents and len(parents) < 2 - - op1, op2 = self.repo.dirstate.parents() - node = self.repo[self.currevid].node() - a = self.repo.changelog.ancestor(op1, node) - cmenu_backout.set_sensitive(a == node) + parents = self.repo.parents() + if len(parents) > 1: + can_merge = False + can_backout = False + else: + pctx = parents[0] + cctx = self.repo[self.currevid] + actx = cctx.ancestor(pctx) + can_merge = actx != pctx or pctx.branch() != cctx.branch() + can_backout = actx == cctx   cmenu_merge.set_sensitive(can_merge) + cmenu_backout.set_sensitive(can_backout)     # need transplant extension for transplant command   if 'transplant' in self.exs: @@ -688,6 +693,7 @@
  def restore_original_selection(self, widget, *args):   self.tree.get_selection().set_mode(gtk.SELECTION_SINGLE)   self.tree.get_selection().select_path(self.origsel) + self.revrange = None     def tree_diff_context_menu(self):   m = gtklib.MenuItems() @@ -713,8 +719,13 @@
  m.append_sep()     # disable/enable menus as required - parents = [x.rev() for x in self.repo.parents()] - can_merge = self.currevid in parents and len(parents) < 2 + parents = self.repo.parents() + if len(parents) > 1: + can_merge = False + else: + rev0, rev1 = self.revrange + c0, c1 = self.repo[rev0], self.repo[rev1] + can_merge = c0.branch() != c1.branch() or c0.ancestor(c1) != c1   cmenu_merge.set_sensitive(can_merge)     # need transplant extension for transplant command @@ -1609,26 +1620,28 @@
  self.refresh_model()     def domerge(self, menuitem): - rev = self.currevid - parents = [x.node() for x in self.repo.parents()] - if rev == self.repo.parents()[0].rev(): - rev = self.revrange[1] - dlg = merge.MergeDialog(rev) - dlg.set_notify_func(self.merge_completed, parents, len(self.repo)) + def merge_notify(args): + oldparents, repolen = args + self.repo.invalidate() + self.repo.dirstate.invalidate() + if len(self.repo) != repolen: + self.reload_log() + elif not oldparents == self.repo.parents(): + self.refresh_model() + # update parents for the next nofifying + args[0] = self.repo.parents() + + if self.revrange: + rev0, rev1 = self.revrange + else: + rev0, rev1 = self.repo['.'].rev(), self.currevid + + args = [self.repo.parents(), len(self.repo)] + dlg = merge.MergeDialog(rev0, rev1) + dlg.set_notify_func(merge_notify, *args) + merge_notify(args) # could have immediately switched parents   self.show_dialog(dlg)   - def merge_completed(self, args): - self.repo.invalidate() - self.repo.dirstate.invalidate() - oldparents, repolen = args - newparents = [x.node() for x in self.repo.parents()] - if len(self.repo) != repolen: - self.reload_log() - elif not oldparents == newparents: - self.refresh_model() - # update parents for the next nofifying - args[0] = newparents -   def archive(self, menuitem):   rev = self.currevid   parents = [x.node() for x in self.repo.parents()]
 
10
11
12
13
 
14
15
16
 
26
27
28
29
 
30
31
32
 
36
37
38
39
40
41
42
43
44
45
46
47
 
49
50
51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
53
 
54
55
56
57
58
59
60
 
61
62
63
 
91
92
93
94
95
96
 
 
 
 
 
 
 
 
 
 
 
97
98
99
 
254
255
256
257
 
 
10
11
12
 
13
14
15
16
 
26
27
28
 
29
30
31
32
 
36
37
38
 
 
 
 
 
 
39
40
41
 
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
 
110
111
112
 
 
 
113
114
115
116
117
118
119
120
121
122
123
124
125
126
 
281
282
283
 
284
@@ -10,7 +10,7 @@
 import gtk  import gobject   -from mercurial import hg, ui +from mercurial import hg, ui, commands    from tortoisehg.util.i18n import _  from tortoisehg.util import hglib, paths @@ -26,7 +26,7 @@
   class MergeDialog(gtk.Dialog):   """ Dialog to merge revisions of a Mercurial repo """ - def __init__(self, rev=None): + def __init__(self, rev0, rev1):   """ Initialize the Dialog """   gtk.Dialog.__init__(self)   gtklib.set_tortoise_icon(self, 'menumerge.ico') @@ -36,12 +36,6 @@
  self.connect('response', self.dialog_response)   self.notify_func = None   - if not rev: - gdialog.Prompt(_('Unable to merge'), - _('Must supply a target revision'), self).run() - gtklib.idle_add_single_call(self.destroy) - return -   try:   repo = hg.repository(ui.ui(), path=paths.find_root())   except hglib.RepoError: @@ -49,15 +43,40 @@
  return   self.set_title(_('Merging in %s') % hglib.get_reponame(repo))   + prevs = [ctx.rev() for ctx in repo.parents()] + if len(prevs) > 1: + rev0, rev1 = prevs + elif not rev1: + gdialog.Prompt(_('Unable to merge'), + _('Must supply a target revision'), self).run() + gtklib.idle_add_single_call(self.destroy) + return + elif not rev0: + rev0 = prevs[0] + elif rev1 == prevs[0]: + # selected pair was backwards + rev0, rev1 = rev1, rev0 + elif rev0 != prevs[0]: + # working parent not in selected revision pair + modified, added, removed, deleted = repo.status()[:4] + if modified or added or removed or deleted: + gdialog.Prompt(_('Unable to merge'), + _('Outstanding uncommitted changes'), self).run() + gtklib.idle_add_single_call(self.destroy) + return + repo.ui.quiet = True + commands.update(repo.ui, repo, rev=rev0, check=True) + repo.ui.quiet = False +   frame = gtk.Frame(_('Merge target (other)')) - self.otherrev, desc = changesetinfo.changesetinfo(repo, rev, True) + self.otherrev, desc = changesetinfo.changesetinfo(repo, rev1, True)   frame.add(desc)   frame.set_border_width(5)   self.vbox.pack_start(frame, False, False)   self.otherframe = frame     frame = gtk.Frame(_('Current revision (local)')) - self.localrev, desc = changesetinfo.changesetinfo(repo, '.', True) + self.localrev, desc = changesetinfo.changesetinfo(repo, rev0, True)   frame.add(desc)   frame.set_border_width(5)   self.vbox.pack_start(frame, False, False) @@ -91,9 +110,17 @@
  combo.child.set_text('')     # prepare to show - self.mergebtn.grab_focus() - self.commitbtn.set_sensitive(False) - self.undobtn.set_sensitive(False) + if len(repo.parents()) == 2: + self.mergetool.set_sensitive(False) + self.mergelabel.set_sensitive(False) + self.mergebtn.set_sensitive(False) + self.undobtn.set_sensitive(True) + self.commitbtn.set_sensitive(True) + self.commitbtn.grab_focus() + else: + self.mergebtn.grab_focus() + self.commitbtn.set_sensitive(False) + self.undobtn.set_sensitive(False)   gtklib.idle_add_single_call(self.after_init)     def after_init(self): @@ -254,4 +281,4 @@
  self.cmd.execute(cmdline, cmd_done)    def run(ui, *pats, **opts): - return MergeDialog(opts.get('rev')) + return MergeDialog(None, opts.get('rev'))