From d45e74f308b4b898b848164e76671eeef8783f7b Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 19 Apr 2020 16:00:48 +0800 Subject: [PATCH] improve dir diff efficiency --- meld/dirdiff.py | 118 ++++++++++++++++++++++++++++++++------------ meld/tree.py | 35 +++++++++---- meld/treehelpers.py | 12 ++--- meld/vcview.py | 2 +- 4 files changed, 119 insertions(+), 48 deletions(-) diff --git a/meld/dirdiff.py b/meld/dirdiff.py index deee301d..bc8eb6e0 100644 --- a/meld/dirdiff.py +++ b/meld/dirdiff.py @@ -173,7 +173,19 @@ def _files_same(files, regexes, comparison_args): regexes = tuple(regexes) if apply_text_filters else () # If all entries are directories, they are considered to be the same - if all([stat.S_ISDIR(s.mode) for s in stats]): + if all([stat.S_ISDIR(s.mode) for s in stats]) : + + #If there are no text filters, unequal sizes imply a difference + same_size = all_same([s.size for s in stats]) + if not need_contents and not same_size : + return Different + #copy code block of Compare files superficially if the options tells us to + if shallow_comparison : + all_same_timestamp = all( + s.shallow_equal(stats[0], time_resolution_ns) for s in stats[1 :]) + + res_diff = DodgySame if all_same_timestamp else Different + return res_diff return Same # If any entries are not regular files, consider them different @@ -525,6 +537,9 @@ class DirDiff(Gtk.VBox, tree.TreeviewCommon, MeldDoc): icon_tint=col_index(tree.COL_TINT, i) ) self.treeview[i].append_column(column) + #hide trigonometry icon + self.treeview[i].set_show_expanders(False) + self.treeview[i].set_level_indentation(20) self.columns_dict[i]["name"] = column # Create file size CellRenderer column = Gtk.TreeViewColumn(_("Size")) @@ -754,10 +769,13 @@ class DirDiff(Gtk.VBox, tree.TreeviewCommon, MeldDoc): """ it = self.model.get_iter(path) child = self.model.iter_children(it) - while child: - self.model.remove(child) - child = self.model.iter_children(it) - self._update_item_state(it) + #judging path depth + if path.get_depth() <= 1 : + while child : + + self.model.remove(child) + child = self.model.iter_children(it) + self._update_item_state(it) self._scan_in_progress += 1 self.scheduler.add_task(self._search_recursively_iter(path)) @@ -779,8 +797,11 @@ class DirDiff(Gtk.VBox, tree.TreeviewCommon, MeldDoc): shadowed_entries = [] invalid_filenames = [] while len(todo): - todo.sort() # depth first + #todo.sort() # depth first path = todo.pop(0) + #one level is OK + if path.get_depth() - rootpath.get_depth() > 1 : + break it = self.model.get_iter(path) roots = self.model.value_paths(it) @@ -868,22 +889,38 @@ class DirDiff(Gtk.VBox, tree.TreeviewCommon, MeldDoc): for pane, f1, f2 in dirs.errors + files.errors: shadowed_entries.append((pane, roots[pane], f1, f2)) - alldirs = self._filter_on_state(roots, dirs.get()) - allfiles = self._filter_on_state(roots, files.get()) - - if alldirs or allfiles: - for names in alldirs: + alldirs, diff_dirs = self._filter_on_state(roots, dirs.get()) + allfiles, diff_files = self._filter_on_state(roots, files.get()) + previous_expansion = self.row_expansions + empty_iters = [t.iter for t in self.model[rootpath].iterchildren() if t] + for empty_iter in empty_iters : + self.model.remove(empty_iter) + self.row_expansions = previous_expansion + if diff_dirs or diff_files : + #If any of the subdirectories or subfiles is different, the subdirectories or subfiles are different. + differences = True + expanded.add(tree_path_as_tuple(path)) + if alldirs or allfiles : + for names in alldirs : entries = [ os.path.join(r, n) for r, n in zip(roots, names)] child = self.model.add_entries(it, entries) differences |= self._update_item_state(child) - todo.append(self.model.get_path(child)) - for names in allfiles: + child_treepath = self.model.get_path(child) + + if rootpath.is_ancestor(child_treepath) and (child_treepath.get_depth() - rootpath.get_depth()) < 2 : + self.model.add_empty(child) + + + for names in allfiles : entries = [ os.path.join(r, n) for r, n in zip(roots, names)] child = self.model.add_entries(it, entries) differences |= self._update_item_state(child) - else: + child_treepath = self.model.get_path(child) + if rootpath.is_ancestor(child_treepath) and (child_treepath.get_depth() - rootpath.get_depth()) < 2 : + expanded.add(tree_path_as_tuple(child_treepath)) + else : # Our subtree is empty, or has been filtered to be empty if (tree.STATE_NORMAL in self.state_filters or not all(os.path.isdir(f) for f in roots)): @@ -913,24 +950,19 @@ class DirDiff(Gtk.VBox, tree.TreeviewCommon, MeldDoc): path.prev() it = parent - - if differences: - expanded.add(tree_path_as_tuple(path)) - - if invalid_filenames or shadowed_entries: + if invalid_filenames or shadowed_entries : self._show_tree_wide_errors(invalid_filenames, shadowed_entries) elif rootpath == Gtk.TreePath.new_first() and not expanded: self._show_identical_status() - - self.treeview[0].expand_to_path(Gtk.TreePath(("0",))) - for path in sorted(expanded): - self.treeview[0].expand_to_path(Gtk.TreePath(path)) + #Traversed through it and its subdirectories and added iter_children to iter_children, just expanding. + self.treeview[0].expand_to_path(Gtk.TreePath(rootpath)) + for child_path in sorted(t for t in expanded if (t.__len__() - rootpath.get_depth()) < 1) : + self.treeview[0].expand_to_path(Gtk.TreePath(child_path))#Expand to the level of the son node. yield _("[%s] Done") % self.label_text self._scan_in_progress -= 1 self.force_cursor_recalculate = True - self.treeview[0].set_cursor(Gtk.TreePath.new_first()) - + self.treeview[0].set_cursor(rootpath) def _show_identical_status(self): primary = _("Folders have no differences") identical_note = _( @@ -1273,8 +1305,21 @@ class DirDiff(Gtk.VBox, tree.TreeviewCommon, MeldDoc): @Gtk.Template.Callback() def on_treeview_row_expanded(self, view, it, path): + expanded_already = True + if not (str(path) in self.row_expansions): + expanded_already = False + + if path.get_depth() >= 2 and any(p.startswith(str(path)) for p in self.row_expansions): + expanded_already = True + self.row_expansions.add(str(path)) - for row in self.model[path].iterchildren(): + if path.get_depth() >= 2 and not expanded_already: + self.recursively_update(path) + + #Obtain the subdirectory path. This subdirectory is added by using the add_entries function. + + for row in self.model[path].iterchildren() : + ## Check whether the subdirectory is expanded. self.expandsions indicates the background set of the expanded rows. if str(row.path) in self.row_expansions: view.expand_row(row.path, False) @@ -1416,6 +1461,8 @@ class DirDiff(Gtk.VBox, tree.TreeviewCommon, MeldDoc): """ assert len(roots) == self.model.ntree ret = [] + #diff return value + diff_ret = [] regexes = [f.byte_filter for f in self.text_filters if f.active] for files in fileslist: curfiles = [os.path.join(r, f) for r, f in zip(roots, files)] @@ -1437,7 +1484,10 @@ class DirDiff(Gtk.VBox, tree.TreeviewCommon, MeldDoc): if (state in self.state_filters or all(os.path.isdir(f) for f in curfiles)): ret.append(files) - return ret + if (state in [tree.STATE_MODIFIED, tree.STATE_NEW]) : + diff_ret.append(files) + + return ret, diff_ret def _update_item_state(self, it): """Update the state of a tree row @@ -1529,8 +1579,12 @@ class DirDiff(Gtk.VBox, tree.TreeviewCommon, MeldDoc): }) # Size is handled independently, because unsafe_set # can't correctly box GObject.TYPE_INT64. - self.model.set( - it, self.model.column_index(COL_SIZE, j), sizes[j]) + if isdir[j]: + self.model.set( + it, self.model.column_index(COL_SIZE, j), -1) + else: + self.model.set( + it, self.model.column_index(COL_SIZE, j), sizes[j]) else: self.model.set_path_state( it, j, tree.STATE_NONEXIST, any(isdir)) @@ -1614,14 +1668,16 @@ class DirDiff(Gtk.VBox, tree.TreeviewCommon, MeldDoc): changed = changed[len(current):] # search the tree one path part at a time for part in changed: - child = model.iter_children(it) + child = self.model.iter_next(child) + #child = model.iter_children(it) while child: child_path = model.value_path(child, pane) # Found the changed path if child_path and part == os.path.basename(child_path): it = child break - child = self.model.iter_next(child) + child = model.iter_children(it) + #child = self.model.iter_next(child) if not it: break # save if found and unique diff --git a/meld/tree.py b/meld/tree.py index b78a4eb7..649de3c7 100644 --- a/meld/tree.py +++ b/meld/tree.py @@ -168,16 +168,31 @@ class DiffTreeStore(SearchableTreeStore): icon = self.icon_details[state][1 if isdir else 0] tint = self.icon_details[state][3 if isdir else 2] fg, style, weight, strike = self.text_attributes[state] - self.unsafe_set(it, pane, { - COL_STATE: str(state), - COL_TEXT: label, - COL_ICON: icon, - COL_TINT: tint, - COL_FG: fg, - COL_STYLE: style, - COL_WEIGHT: weight, - COL_STRIKE: strike - }) + file_path = self.value_path(it,pane) + if file_path and os.path.exists(file_path): + self.unsafe_set(it, pane, { + COL_STATE: str(state), + COL_TEXT: label, + COL_ICON: icon, + COL_TINT: tint, + COL_FG: fg, + COL_STYLE: style, + COL_WEIGHT: weight, + COL_STRIKE: strike + }) + #hide unexist dir + else: + self.unsafe_set(it, pane, { + COL_STATE: str(state), + COL_TEXT: None, + COL_ICON: None, + COL_TINT: None, + COL_FG: fg, + COL_STYLE: style, + COL_WEIGHT: weight, + COL_STRIKE: strike + }) + def get_state(self, it, pane): state_idx = self.column_index(COL_STATE, pane) diff --git a/meld/treehelpers.py b/meld/treehelpers.py index 16c0cb84..9a462bc7 100644 --- a/meld/treehelpers.py +++ b/meld/treehelpers.py @@ -73,13 +73,13 @@ class SearchableTreeStore(Gtk.TreeStore): def inorder_search_down(self, it): while it: - child = self.iter_children(it) - if child: - it = child + next_it = self.iter_next(it) + if next_it : + it = next_it else: - next_it = self.iter_next(it) - if next_it: - it = next_it + child = self.iter_children(it) + if child : + it = child else: while True: it = self.iter_parent(it) diff --git a/meld/vcview.py b/meld/vcview.py index cee2e725..03e6ce65 100644 --- a/meld/vcview.py +++ b/meld/vcview.py @@ -441,7 +441,7 @@ class VcView(Gtk.VBox, tree.TreeviewCommon, MeldDoc): while todo: # This needs to happen sorted and depth-first in order for our row # references to remain valid while we traverse. - todo.sort() + #todo.sort() treepath, path = todo.pop(0) it = self.model.get_iter(treepath) yield _("Scanning %s") % path[display_prefix:] -- GitLab