[PATCH] convert/bzr: correctly handle divergent nested renames (issue3089)

Patrick Mezard pmezard at gmail.com
Tue Nov 8 10:43:00 CST 2011


# HG changeset patch
# User Patrick Mezard <pmezard at gmail.com>
# Date 1320768538 -3600
# Branch stable
# Node ID b4274167128fb95f170e952329c0e30fc7dca717
# Parent  54c0517c0fe8af4f8851a1bbb5bb229f0e7dd853
convert/bzr: correctly handle divergent nested renames (issue3089)

With renames like:

  a   -> b
  a/c -> a/c

We were ignoring or duplicating the second one instead of leaving files
unchanged or moving them to their proper destination only.

To avoid this, we process the files in reverse lexicographic order, from most
to least specific change, and ignore files already processed.

diff --git a/hgext/convert/bzr.py b/hgext/convert/bzr.py
--- a/hgext/convert/bzr.py
+++ b/hgext/convert/bzr.py
@@ -173,8 +173,14 @@
         revid = current._revision_id
         changes = []
         renames = {}
+        seen = set()
+        # Process the entries by reverse lexicographic name order to
+        # handle nested renames correctly, most specific first.
+        curchanges = sorted(current.iter_changes(origin),
+                            key=lambda c: c[1][0] or c[1][1],
+                            reverse=1)
         for (fileid, paths, changed_content, versioned, parent, name,
-            kind, executable) in current.iter_changes(origin):
+            kind, executable) in curchanges:
 
             if paths[0] == u'' or paths[1] == u'':
                 # ignore changes to tree root
@@ -188,7 +194,8 @@
                     # so it can be removed.
                     changes.append((self.recode(paths[0]), revid))
 
-                if None not in paths and paths[0] != paths[1]:
+                if kind[0] == 'directory' and None not in paths:
+                    renaming = paths[0] != paths[1]
                     # neither an add nor an delete - a move
                     # rename all directory contents manually
                     subdir = origin.inventory.path2id(paths[0])
@@ -198,6 +205,16 @@
                         if entry.kind == 'directory':
                             continue
                         frompath = self.recode(paths[0] + '/' + name)
+                        if frompath in seen:
+                            # Already handled by a more specific change entry
+                            # This is important when you have:
+                            # a => b
+                            # a/c => a/c
+                            # Here a/c must not be renamed into b/c
+                            continue
+                        seen.add(frompath)
+                        if not renaming:
+                            continue
                         topath = self.recode(paths[1] + '/' + name)
                         # register the files as changed
                         changes.append((frompath, revid))
@@ -215,6 +232,7 @@
 
             # we got unicode paths, need to convert them
             path, topath = [self.recode(part) for part in paths]
+            seen.add(path or topath)
 
             if topath is None:
                 # file deleted


More information about the Mercurial-devel mailing list