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