hgext/convert/convcmd.py
changeset 50181 02fe65f74be5
parent 48946 642e31cb55f0
child 50887 8edfd28a01d1
equal deleted inserted replaced
50176:829aa604d71a 50181:02fe65f74be5
     4 #
     4 #
     5 # This software may be used and distributed according to the terms of the
     5 # This software may be used and distributed according to the terms of the
     6 # GNU General Public License version 2 or any later version.
     6 # GNU General Public License version 2 or any later version.
     7 
     7 
     8 import collections
     8 import collections
       
     9 import heapq
     9 import os
    10 import os
    10 import shutil
    11 import shutil
    11 
    12 
    12 from mercurial.i18n import _
    13 from mercurial.i18n import _
    13 from mercurial.pycompat import open
    14 from mercurial.pycompat import open
   194     def lookuprev(self, rev):
   195     def lookuprev(self, rev):
   195         return self.source.lookuprev(rev)
   196         return self.source.lookuprev(rev)
   196 
   197 
   197     def close(self):
   198     def close(self):
   198         self.progress.complete()
   199         self.progress.complete()
       
   200 
       
   201 
       
   202 # Sorters are used by the `toposort` function to maintain a set of revisions
       
   203 # which can be converted immediately and pick one
       
   204 class branchsorter:
       
   205     """If the previously converted revision has a child in the
       
   206     eligible revisions list, pick it. Return the list head
       
   207     otherwise. Branch sort attempts to minimize branch
       
   208     switching, which is harmful for Mercurial backend
       
   209     compression.
       
   210     """
       
   211 
       
   212     def __init__(self, parents):
       
   213         self.nodes = []
       
   214         self.parents = parents
       
   215         self.prev = None
       
   216 
       
   217     def picknext(self):
       
   218         next = self.nodes[0]
       
   219         for n in self.nodes:
       
   220             if self.prev in self.parents[n]:
       
   221                 next = n
       
   222                 break
       
   223         self.prev = next
       
   224         self.nodes.remove(next)
       
   225         return next
       
   226 
       
   227     def insert(self, node):
       
   228         self.nodes.insert(0, node)
       
   229 
       
   230     def __len__(self):
       
   231         return self.nodes.__len__()
       
   232 
       
   233 
       
   234 class keysorter:
       
   235     """Key-based sort, ties broken by insertion order"""
       
   236 
       
   237     def __init__(self, keyfn):
       
   238         self.heap = []
       
   239         self.keyfn = keyfn
       
   240         self.counter = 0
       
   241 
       
   242     def picknext(self):
       
   243         return heapq.heappop(self.heap)[2]
       
   244 
       
   245     def insert(self, node):
       
   246         counter = self.counter
       
   247         self.counter = counter + 1
       
   248         key = self.keyfn(node)
       
   249         heapq.heappush(self.heap, (key, counter, node))
       
   250 
       
   251     def __len__(self):
       
   252         return self.heap.__len__()
   199 
   253 
   200 
   254 
   201 class converter:
   255 class converter:
   202     def __init__(self, ui, source, dest, revmapfile, opts):
   256     def __init__(self, ui, source, dest, revmapfile, opts):
   203 
   257 
   362                 if not hasparent:
   416                 if not hasparent:
   363                     roots.append(n)
   417                     roots.append(n)
   364 
   418 
   365             return children, roots
   419             return children, roots
   366 
   420 
   367         # Sort functions are supposed to take a list of revisions which
       
   368         # can be converted immediately and pick one
       
   369 
       
   370         def makebranchsorter():
       
   371             """If the previously converted revision has a child in the
       
   372             eligible revisions list, pick it. Return the list head
       
   373             otherwise. Branch sort attempts to minimize branch
       
   374             switching, which is harmful for Mercurial backend
       
   375             compression.
       
   376             """
       
   377             prev = [None]
       
   378 
       
   379             def picknext(nodes):
       
   380                 next = nodes[0]
       
   381                 for n in nodes:
       
   382                     if prev[0] in parents[n]:
       
   383                         next = n
       
   384                         break
       
   385                 prev[0] = next
       
   386                 return next
       
   387 
       
   388             return picknext
       
   389 
       
   390         def makesourcesorter():
   421         def makesourcesorter():
   391             """Source specific sort."""
   422             """Source specific sort."""
   392             keyfn = lambda n: self.commitcache[n].sortkey
   423             keyfn = lambda n: self.commitcache[n].sortkey
   393 
   424             return keysorter(keyfn)
   394             def picknext(nodes):
       
   395                 return sorted(nodes, key=keyfn)[0]
       
   396 
       
   397             return picknext
       
   398 
   425 
   399         def makeclosesorter():
   426         def makeclosesorter():
   400             """Close order sort."""
   427             """Close order sort."""
   401             keyfn = lambda n: (
   428             keyfn = lambda n: (
   402                 b'close' not in self.commitcache[n].extra,
   429                 b'close' not in self.commitcache[n].extra,
   403                 self.commitcache[n].sortkey,
   430                 self.commitcache[n].sortkey,
   404             )
   431             )
   405 
   432             return keysorter(keyfn)
   406             def picknext(nodes):
       
   407                 return sorted(nodes, key=keyfn)[0]
       
   408 
       
   409             return picknext
       
   410 
   433 
   411         def makedatesorter():
   434         def makedatesorter():
   412             """Sort revisions by date."""
   435             """Sort revisions by date."""
   413             dates = {}
       
   414 
   436 
   415             def getdate(n):
   437             def getdate(n):
   416                 if n not in dates:
   438                 return dateutil.parsedate(self.commitcache[n].date)
   417                     dates[n] = dateutil.parsedate(self.commitcache[n].date)
   439 
   418                 return dates[n]
   440             return keysorter(getdate)
   419 
       
   420             def picknext(nodes):
       
   421                 return min([(getdate(n), n) for n in nodes])[1]
       
   422 
       
   423             return picknext
       
   424 
   441 
   425         if sortmode == b'branchsort':
   442         if sortmode == b'branchsort':
   426             picknext = makebranchsorter()
   443             sorter = branchsorter(parents)
   427         elif sortmode == b'datesort':
   444         elif sortmode == b'datesort':
   428             picknext = makedatesorter()
   445             sorter = makedatesorter()
   429         elif sortmode == b'sourcesort':
   446         elif sortmode == b'sourcesort':
   430             picknext = makesourcesorter()
   447             sorter = makesourcesorter()
   431         elif sortmode == b'closesort':
   448         elif sortmode == b'closesort':
   432             picknext = makeclosesorter()
   449             sorter = makeclosesorter()
   433         else:
   450         else:
   434             raise error.Abort(_(b'unknown sort mode: %s') % sortmode)
   451             raise error.Abort(_(b'unknown sort mode: %s') % sortmode)
   435 
   452 
   436         children, actives = mapchildren(parents)
   453         children, roots = mapchildren(parents)
       
   454 
       
   455         for node in roots:
       
   456             sorter.insert(node)
   437 
   457 
   438         s = []
   458         s = []
   439         pendings = {}
   459         pendings = {}
   440         while actives:
   460         while sorter:
   441             n = picknext(actives)
   461             n = sorter.picknext()
   442             actives.remove(n)
       
   443             s.append(n)
   462             s.append(n)
   444 
   463 
   445             # Update dependents list
   464             # Update dependents list
   446             for c in children.get(n, []):
   465             for c in children.get(n, []):
   447                 if c not in pendings:
   466                 if c not in pendings:
   453                         _(b'cycle detected between %s and %s')
   472                         _(b'cycle detected between %s and %s')
   454                         % (recode(c), recode(n))
   473                         % (recode(c), recode(n))
   455                     )
   474                     )
   456                 if not pendings[c]:
   475                 if not pendings[c]:
   457                     # Parents are converted, node is eligible
   476                     # Parents are converted, node is eligible
   458                     actives.insert(0, c)
   477                     sorter.insert(c)
   459                     pendings[c] = None
   478                     pendings[c] = None
   460 
   479 
   461         if len(s) != len(parents):
   480         if len(s) != len(parents):
   462             raise error.Abort(_(b"not all revisions were sorted"))
   481             raise error.Abort(_(b"not all revisions were sorted"))
   463 
   482