Index: vcpx/tests/darcs.py
===================================================================
--- vcpx/tests/darcs.py	(revision 1223)
+++ vcpx/tests/darcs.py	(revision 1241)
@@ -169,13 +169,9 @@
         cset = csets[2]
         self.assertEqual(cset.revision, 'moved')
-        self.assertEqual(len(cset.entries), 2)
-
-        entry = cset.entries[0]
-        self.assertEqual(entry.name, 'bdir')
-        self.assertEqual(entry.action_kind, entry.ADDED)
-
-        entry = cset.entries[1]
+        self.assertEqual(len(cset.entries), 1)
+
+        entry = cset.entries[0]
         self.assertEqual(entry.name, 'dir')
-        self.assertEqual(entry.action_kind, entry.RENAMED)
+        self.assertEqual(entry.action_kind, entry.ADDED)
 
         cset = csets[3]
@@ -358,6 +354,4 @@
                             if ((e.name.startswith(n.name+'/') or (e.old_name==n.name)) and
                                 (n.action_kind==n.ADDED or n.action_kind==n.RENAMED))]
-                for ee in postadds:
-                    print ee
                 self.assertEqual(postadds, [])
 
@@ -398,4 +392,88 @@
 """
 
+    MIXED_TEST = """
+<changelog>
+<patch author='esj@harvee.org' date='20050104213401' local_date='Tue Jan  4 22:34:01 CET 2005' inverted='False' hash='20050104213401-fab45-49c3d772521e523fa84be43883b235dbcbf9d61c.gz'>
+        <name>feedback and logging </name>
+        <comment>this patch has three major changes.  First is the addition of the
+false negative feedback so that spam that leaks through
+can be identified and corrected.
+
+second is the logging changes minimizing information
+dumped at the highest levels (1) in order to speed up message processing
+
+third is updating portalocker for modern pythons.
+</comment>
+    <summary>
+    <move from="ancillary/mbox2rpc.py" to="ancillary/fnsource.py"/>
+    <move from="ancillary/rpc2mbox.py" to="web-ui/cgi-exec/fnsink.py"/>
+    <modify_file>
+    ancillary/fnsource.py<added_lines num='335'/>
+    </modify_file>
+    <modify_file>
+    ancillary/global_configuration<added_lines num='8'/>
+    </modify_file>
+    <add_file>
+    ancillary/mbox2rpc.py
+    </add_file>
+    <modify_file>
+    ancillary/mbox2spamtrap.py<removed_lines num='1'/><added_lines num='1'/>
+    </modify_file>
+    <add_file>
+    ancillary/rpc2mbox.py
+    </add_file>
+    <modify_file>
+    modules/camram_email.py<removed_lines num='17'/><added_lines num='33'/>
+    </modify_file>
+    <modify_file>
+    modules/camram_utils.py<removed_lines num='2'/><added_lines num='5'/>
+    </modify_file>
+    <modify_file>
+    modules/configuration.py<removed_lines num='15'/><added_lines num='15'/>
+    </modify_file>
+    <modify_file>
+    modules/dbm_utils.py<removed_lines num='4'/><added_lines num='4'/>
+    </modify_file>
+    <modify_file>
+    modules/log.py<removed_lines num='1'/><added_lines num='1'/>
+    </modify_file>
+    <modify_file>
+    modules/portalocker.py<removed_lines num='2'/><added_lines num='2'/>
+    </modify_file>
+    <modify_file>
+    sgid/build.sh<removed_lines num='1'/><added_lines num='4'/>
+    </modify_file>
+    <modify_file>
+    src/core_filter.py<removed_lines num='50'/><added_lines num='55'/>
+    </modify_file>
+    <modify_file>
+    src/postfix_filter.py<removed_lines num='35'/><added_lines num='49'/>
+    </modify_file>
+    <modify_file>
+    src/postfix_stamper.py<removed_lines num='17'/><added_lines num='18'/>
+    </modify_file>
+    <modify_file>
+    web-ui/cgi-exec/correct.py<removed_lines num='17'/><added_lines num='17'/>
+    </modify_file>
+    <modify_file>
+    web-ui/cgi-exec/edit_config.py<removed_lines num='23'/><added_lines num='26'/>
+    </modify_file>
+    <modify_file>
+    web-ui/cgi-exec/fnsink.py<added_lines num='154'/>
+    </modify_file>
+    <modify_file>
+    web-ui/cgi-exec/recover.py<removed_lines num='5'/><added_lines num='5'/>
+    </modify_file>
+    <modify_file>
+    web-ui/cgi-exec/spamtrap_display.py<removed_lines num='4'/><added_lines num='4'/>
+    </modify_file>
+    <modify_file>
+    web-ui/templates/correct.html<removed_lines num='1'/><added_lines num='1'/>
+    </modify_file>
+    </summary>
+</patch>
+</changelog>
+"""
+
     def testAddAndRename(self):
         "Verify if the parser degrades (add A)+(rename A B) to (add B)"
@@ -405,7 +483,13 @@
 
         cset = csets.next()
-        for e in cset.entries: print e
 
         entry = cset.entries[2]
         self.assertEqual(entry.name, 'vcpx/repository/git/__init__.py')
-        self.assertEqual(entry.action_kind, entry.ADD)
+        self.assertEqual(entry.action_kind, entry.ADDED)
+
+        log = StringIO(self.MIXED_TEST)
+        csets = changesets_from_darcschanges(log)
+
+        cset = csets.next()
+        self.assertEqual([], [e for e in cset.entries if e.name == 'ancillary/mbox2rpc.py'])
+        self.assertEqual([], [e for e in cset.entries if e.action_kind == e.RENAMED])
Index: vcpx/repository/darcs/source.py
===================================================================
--- vcpx/repository/darcs/source.py	(revision 1222)
+++ vcpx/repository/darcs/source.py	(revision 1241)
@@ -14,4 +14,5 @@
 import re
 
+from vcpx.changes import ChangesetEntry, Changeset
 from vcpx.shwrap import ExternalCommand, PIPE, STDOUT
 from vcpx.source import UpdatableSourceWorkingDir, ChangesetApplicationFailure, \
@@ -21,4 +22,91 @@
 
 
+class DarcsChangeset(Changeset):
+    """
+    Fixup darcs idiosyncrasies:
+
+    - collapse "add A; rename A B" into "add B"
+    - collapse "rename A B; remove B" into "remove A"
+    """
+
+    def __init__(self, revision, date, author, log, entries=None, **other):
+        """
+        Initialize a new DarcsChangeset.
+        """
+
+        super(DarcsChangeset, self).__init__(revision, date, author, log, entries=None, **other)
+        if entries is not None:
+            for e in entries:
+                self.addEntry(e, revision)
+
+    def addEntry(self, entry, revision):
+        """
+        Fixup darcs idiosyncrasies:
+        
+        - collapse "add A; rename A B" into "add B"
+        - collapse "rename A B; remove B" into "remove A"
+        """
+
+        # This should not happen, since the parser feeds us an already built
+        # list of ChangesetEntries, anyway...
+        if not isinstance(entry, ChangesetEntry):
+            return super(DarcsChangeset, self).addEntry(entry, revision)
+
+        # Ok, before adding this entry, check it against already
+        # known: if this is an add, and there's a rename (such as "add
+        # A; rename A B; ") then...
+        
+        if entry.action_kind == entry.ADDED:
+            # ... we have to check existings, because of a bug in
+            # darcs: `changes --xml` (as of 1.0.7) emits the changes
+            # in the wrong order, that is, it prefers to start with
+            # renames, *always*, even when they obviously follows the
+            # add of the same entry (even, it should apply this "fix"
+            # by its own).
+            #
+            # So, if there's a rename of this entry there, change that
+            # to an addition instead, and don't insert any other entry
+
+            dirname = entry.name+'/' # darcs hopefully use forward slashes also under win
+            
+            for i,e in enumerate(self.entries):
+                if e.action_kind == e.RENAMED and e.old_name == entry.name:
+                    # Luckily enough (since removes are the first entries
+                    # in the list, that is) by anticipating the add we
+                    # cure also the case below, when addition follows
+                    # edit.
+                    e.action_kind = e.ADDED
+                    e.old_name = None
+                    return e
+
+                # Assert also that add_dir events must preceeds any
+                # add_file and ren_file that have that dir as target,
+                # and that add_file preceeds any edit.
+
+                if ((e.name == entry.name or e.name.startswith(dirname))
+                    or (e.action_kind == e.RENAMED and e.old_name.startswith(dirname))):
+                    self.entries.insert(i, entry)
+                    return entry
+
+        # Likewise, if this is a deletion, and there is a rename of this
+        # entry (such as "rename A B; remove B") then ...
+        
+        elif entry.action_kind == entry.DELETED:
+            # turn the existing rename into a deletion instead
+            
+            for i,e in enumerate(self.entries):
+                if e.action_kind == e.RENAMED and e.name == entry.name:
+                    e.action_kind = e.DELETED
+                    e.name = e.old_name
+                    e.old_name = None
+                    return e
+
+        # Ok, it must be either an edit or a rename: the former goes
+        # obviously to the end, and since the latter, as said, come
+        # in very early, appending is just good.
+        self.entries.append(entry)
+        return entry
+
+        
 def changesets_from_darcschanges(changes, unidiff=False, repodir=None,
                                  chunksize=2**15):
@@ -55,5 +143,4 @@
     from xml.sax.handler import ContentHandler, ErrorHandler
     from datetime import datetime
-    from vcpx.changes import ChangesetEntry, Changeset
 
     class DarcsXMLChangesHandler(ContentHandler):
@@ -97,56 +184,10 @@
         def endElement(self, name):
             if name == 'patch':
-                entries = []
-                todo = self.current['entries']
-                # Darcs allows "rename A B; remove B": collapse those
-                # into "remove A"
-                while todo:
-                    e = todo.pop(0)
-                    if e.action_kind == e.RENAMED:
-                        lookfor = e.name
-                        forget = []
-                        for i,n in enumerate(todo):
-                            if n.action_kind == n.DELETED and n.name == lookfor:
-                                e.action_kind = e.DELETED
-                                e.name = e.old_name
-                                e.old_name = None
-                                entries.append(e)
-                                forget.append(i)
-                                forget.reverse()
-                                for i in forget:
-                                    del todo[i]
-                                break
-                            elif n.action_kind == n.RENAMED and n.old_name == lookfor:
-                                forget.append(i)
-                                lookfor = n.name
-                        if not forget:
-                            entries.append(e)
-                    else:
-                        entries.append(e)
-
-                # Darcs changes --xml (as of 1.0.7) emits bad ordered hunks: it
-                # begins with file moves, apparently for no good reason. Do as
-                # little reordering as needed to adjust the meaning, ie moving all
-                # add_dirs before add_file and ren_file that have that dir as
-                # target
-
-                sorted = False
-                while not sorted:
-                    sorted = True
-                    for i,e in enumerate(entries):
-                        if e.action_kind == e.RENAMED:
-                            for j,n in enumerate(entries[i+1:]):
-                                if ((e.name.startswith(n.name+'/') or e.old_name==n.name) and
-                                    (n.action_kind == n.ADDED or n.action_kind == n.RENAMED)):
-                                    m = entries.pop(i+1+j)
-                                    entries.insert(i, m)
-                                    sorted = False
-
-                cset = Changeset(self.current['name'],
-                                 self.current['date'],
-                                 self.current['author'],
-                                 self.current['comment'],
-                                 entries,
-                                 tags=self.current.get('tags',[]))
+                cset = DarcsChangeset(self.current['name'],
+                                      self.current['date'],
+                                      self.current['author'],
+                                      self.current['comment'],
+                                      self.current['entries'],
+                                      tags=self.current.get('tags',[]))
                 cset.darcs_hash = self.current['hash']
                 if self.darcsdiff:
Index: vcpx/repository/bzr.py
===================================================================
--- vcpx/repository/bzr.py	(revision 1239)
+++ vcpx/repository/bzr.py	(revision 1242)
@@ -326,4 +326,14 @@
 
     def _prepareTargetRepository(self):
+        from bzrlib import version_info
+        from vcpx.dualwd import IGNORED_METADIRS
+
         if self._working_tree is None:
             self._working_tree = self.repository.create()
+
+        if version_info > (0,9):
+            from bzrlib.ignores import add_runtime_ignores
+            add_runtime_ignores(IGNORED_METADIRS)
+        else:
+            from bzrlib import DEFAULT_IGNORE
+            DEFAULT_IGNORE.extend(IGNORED_METADIRS)
