Re: [PATCH] auto merge action



Hi,

Thanks a lot for all the comments! I will try to improve the patch based on your input.
However, I am going to keep merge_2_files() as it is because:
1) I pass the original linediffer to Merger so the changes applied should be exactly the same as the changes shown to the user
2) it is about 10 times faster than calling replace_chunk in a loop on my machine
I am attaching a patch that can be used to compare performance of both approaches and two test files:
- run meld meldapp_1.py meldapp_1.py meldapp_2.py
- right click middle pane, select Pull all non-conflicting from right (see console)
- right click middle pane again, select Pull all non-conflicting from left (see console)

Cheers,
Piotr

Attachment: test_files.tar.gz
Description: GNU Zip compressed data

From aaa4033a7f985b5ba9d431afef1d05951552e41e Mon Sep 17 00:00:00 2001
From: Piotr Piastucki <leech miranda gmail com>
Date: Wed, 28 Apr 2010 14:41:06 +0200
Subject: [PATCH] Add bulk merge actions to 3-way diff.
 This commit adds 3 new actions: Merge all non-conflicting changes, pull all non-conflicting changes from left and pull all non-conflicting changes from right to the context menu.

---
 data/ui/filediff-ui.xml |    4 ++++
 meld/filediff.py        |   41 +++++++++++++++++++++++++++++++++++++++--
 meld/merge.py           |   45 ++++++++++++++++++++++++++++++++++-----------
 3 files changed, 77 insertions(+), 13 deletions(-)

diff --git a/data/ui/filediff-ui.xml b/data/ui/filediff-ui.xml
index 2df3e7f..1eef17c 100644
--- a/data/ui/filediff-ui.xml
+++ b/data/ui/filediff-ui.xml
@@ -39,6 +39,10 @@
     <menuitem action="CopyAllLeft" />
     <menuitem action="CopyAllRight" />
     <separator/>
+    <menuitem action="MergeNonConflicting"/>
+    <menuitem action="PullNonConflictingLeft"/>
+    <menuitem action="PullNonConflictingRight"/>
+    <separator/>
     <menuitem action="FileOpen" />
   </popup>
 
diff --git a/meld/filediff.py b/meld/filediff.py
index f630721..41232ba 100644
--- a/meld/filediff.py
+++ b/meld/filediff.py
@@ -34,6 +34,7 @@ import misc
 import melddoc
 import paths
 import cairo
+import merge
 
 from util.sourceviewer import srcviewer
 
@@ -183,6 +184,9 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             ("Delete",    gtk.STOCK_DELETE,     _("Delete"),     "<Alt>Delete", _("Delete change"), self.delete_change),
             ("CopyAllLeft",       gtk.STOCK_GOTO_FIRST, _("Copy To Left"),  None, _("Copy all changes from right pane to left pane"), lambda x: self.copy_selected(-1)),
             ("CopyAllRight",      gtk.STOCK_GOTO_LAST,  _("Copy To Right"), None, _("Copy all changes from left pane to right pane"), lambda x: self.copy_selected(1)),
+            ("MergeNonConflicting",      None,  _("Merge all non-conflicting"), None, _("Merge all non-conflicting changes from left and right pane"), lambda x: self.merge_all_non_conflicting_changes()),
+            ("PullNonConflictingLeft",   None, _("Pull all non-conflicting from left"),  None, _("Pull all non-conflicting changes from right pane to left pane"), lambda x: self.pull_all_non_conflicting_changes(-1)),
+            ("PullNonConflictingRight",  None,  _("Pull all non-conflicting from right"), None, _("Pull all non-conflicting changes from left pane to right pane"), lambda x: self.pull_all_non_conflicting_changes(1)),
         )
 
         self.ui_file = paths.ui_dir("filediff-ui.xml")
@@ -321,6 +325,33 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
         assert(chunk is not None)
         self.replace_chunk(src, dst, chunk)
 
+    def pull_all_non_conflicting_changes(self, direction):
+        assert direction in (-1,1)
+        dst = self._get_focused_pane()
+        src = dst + direction
+        assert src in range(self.num_panes)
+        merger = merge.Merger()
+        merger.differ = self.linediffer
+        merger.texts = [t for t in self._get_texts(raw=1)]
+        for mergedfile in merger.merge_2_files(src, dst):
+            pass
+        self.on_textbuffer__begin_user_action()
+        self.textbuffer[dst].set_text(mergedfile)
+        self.on_textbuffer__end_user_action()
+        self.scheduler.add_task( lambda : self._sync_vscroll( self.scrolledwindow[src].get_vadjustment(), src ) and None )
+
+    def merge_all_non_conflicting_changes(self):
+        dst = 1
+        merger = merge.Merger()
+        merger.differ = self.linediffer
+        merger.texts = [t for t in self._get_texts(raw=1)]
+        for mergedfile in merger.merge_3_files(False):
+            pass
+        self.on_textbuffer__begin_user_action()
+        self.textbuffer[dst].set_text(mergedfile)
+        self.on_textbuffer__end_user_action()
+        self.scheduler.add_task( lambda : self._sync_vscroll( self.scrolledwindow[0].get_vadjustment(), 0 ) and None )
+
     def delete_change(self, widget):
         pane = self._get_focused_pane()
         chunk = self.linediffer.get_chunk(self.cursor.chunk, pane)
@@ -362,6 +393,8 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
                 if not line_end.ends_line():
                     line_end.forward_to_line_end()
                 return self.buf.get_text(line_start, line_end, False)
+            def __len__(self):
+                return self.buf.get_line_count()
 
         class FakeTextArray(object):
             def __init__(self, bufs, textfilter):
@@ -582,8 +615,12 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
             self.findbar.hide()
 
     def popup_in_pane(self, pane):
-        self.actiongroup.get_action("CopyAllLeft").set_sensitive(pane > 0)
-        self.actiongroup.get_action("CopyAllRight").set_sensitive(pane+1 < self.num_panes)
+        self.actiongroup.get_action("CopyAllLeft").set_sensitive(pane > 0 and self.textview[pane-1].get_editable())
+        self.actiongroup.get_action("CopyAllRight").set_sensitive(pane+1 < self.num_panes and self.textview[pane+1].get_editable())
+        editable = self.textview[pane].get_editable();
+        self.actiongroup.get_action("MergeNonConflicting").set_sensitive(self.num_panes == 3 and pane == 1 and editable)
+        self.actiongroup.get_action("PullNonConflictingLeft").set_sensitive(self.num_panes == 3 and pane > 0 and editable)
+        self.actiongroup.get_action("PullNonConflictingRight").set_sensitive(self.num_panes == 3 and pane+1 < self.num_panes and editable)
         self.popup_menu.popup(None, None, None, 3, gtk.get_current_event_time())
 
     def on_scrolledwindow__size_allocate(self, scrolledwindow, allocation):
diff --git a/meld/merge.py b/meld/merge.py
index 3333f86..6583ae1 100644
--- a/meld/merge.py
+++ b/meld/merge.py
@@ -188,7 +188,7 @@ class Merger(diffutil.Differ):
         else:
             return change[HI] - change[LO]
 
-    def merge_3_files(self):
+    def merge_3_files(self, mark_conflicts = True):
         LO, HI = 1, 2
         self.unresolved = []
         lastline = 0
@@ -208,18 +208,19 @@ class Merger(diffutil.Differ):
             lastline = low_mark
             if change[0] != None and change[1] != None and change[0][0] == 'conflict':
                 high_mark = max(change[0][HI], change[1][HI])
-                if low_mark < high_mark:
-                    for i in range(low_mark, high_mark):
-                        mergedtext.append("(??)" + self.texts[1][i])
+                if mark_conflicts:
+                    if low_mark < high_mark:
+                        for i in range(low_mark, high_mark):
+                            mergedtext.append("(??)" + self.texts[1][i])
+                            self.unresolved.append(mergedline)
+                            mergedline += 1
+                    else:
+                        #conflictsize = min(1, max(change[0][HI + 2] - change[0][LO + 2], change[1][HI + 2] - change[1][LO + 2]))
+                        #for i in range(conflictsize):
+                        mergedtext.append("(??)")
                         self.unresolved.append(mergedline)
                         mergedline += 1
-                else:
-                    #conflictsize = min(1, max(change[0][HI + 2] - change[0][LO + 2], change[1][HI + 2] - change[1][LO + 2]))
-                    #for i in range(conflictsize):
-                    mergedtext.append("(??)")
-                    self.unresolved.append(mergedline)
-                    mergedline += 1
-                lastline = high_mark
+                    lastline = high_mark
             elif change[0] != None:
                 lastline += self._apply_change(self.texts[0], change[0], mergedtext)
                 mergedline += change[0][HI + 2] - change[0][LO + 2]
@@ -231,3 +232,25 @@ class Merger(diffutil.Differ):
             mergedtext.append(self.texts[1][i])
 
         yield "\n".join(mergedtext)
+
+    def merge_2_files(self, fromindex, toindex):
+        LO, HI = 1, 2
+        self.unresolved = []
+        lastline = 0
+        mergedtext = []
+        for change in self.differ.pair_changes(toindex, fromindex):
+            yield None
+            if change[0] == 'conflict':
+                low_mark = change[HI]
+            else:
+                low_mark = change[LO]
+            for i in range(lastline, low_mark, 1):
+                mergedtext.append(self.texts[toindex][i])
+            lastline = low_mark
+            if change[0] != 'conflict':
+                lastline += self._apply_change(self.texts[fromindex], change, mergedtext)
+        baselen = len(self.texts[toindex])
+        for i in range(lastline, baselen, 1):
+            mergedtext.append(self.texts[toindex][i])
+
+        yield "\n".join(mergedtext)
-- 
1.7.0.4



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]