hgext/convert/subversion.py
changeset 5872 7d4149cccc5d
parent 5871 c1244685425a
child 5873 ecb4eb0cbff2
equal deleted inserted replaced
5871:c1244685425a 5872:7d4149cccc5d
   131             self.transport = transport.SvnRaTransport(url=self.url)
   131             self.transport = transport.SvnRaTransport(url=self.url)
   132             self.ra = self.transport.ra
   132             self.ra = self.transport.ra
   133             self.ctx = self.transport.client
   133             self.ctx = self.transport.client
   134             self.base = svn.ra.get_repos_root(self.ra)
   134             self.base = svn.ra.get_repos_root(self.ra)
   135             self.module = self.url[len(self.base):]
   135             self.module = self.url[len(self.base):]
   136             self.modulemap = {} # revision, module
       
   137             self.commits = {}
   136             self.commits = {}
   138             self.paths = {}
   137             self.paths = {}
   139             self.uuid = svn.ra.get_uuid(self.ra).decode(self.encoding)
   138             self.uuid = svn.ra.get_uuid(self.ra).decode(self.encoding)
   140         except SubversionException, e:
   139         except SubversionException, e:
   141             ui.print_exc()
   140             ui.print_exc()
   398             return None
   397             return None
   399 
   398 
   400         entries = []
   399         entries = []
   401         copyfrom = {} # Map of entrypath, revision for finding source of deleted revisions.
   400         copyfrom = {} # Map of entrypath, revision for finding source of deleted revisions.
   402         copies = {}
   401         copies = {}
   403         revnum = self.revnum(rev)
   402 
   404 
   403         new_module, revnum = self.revsplit(rev)[1:]
   405         if revnum in self.modulemap:
   404         if new_module != self.module:
   406             new_module = self.modulemap[revnum]
   405             self.module = new_module
   407             if new_module != self.module:
   406             self.reparent(self.module)
   408                 self.module = new_module
       
   409                 self.reparent(self.module)
       
   410 
   407 
   411         for path, ent in paths:
   408         for path, ent in paths:
   412             entrypath = get_entry_from_path(path, module=self.module)
   409             entrypath = get_entry_from_path(path, module=self.module)
   413             entry = entrypath.decode(self.encoding)
   410             entry = entrypath.decode(self.encoding)
   414 
   411 
   430             elif kind == 0: # gone, but had better be a deleted *file*
   427             elif kind == 0: # gone, but had better be a deleted *file*
   431                 self.ui.debug("gone from %s\n" % ent.copyfrom_rev)
   428                 self.ui.debug("gone from %s\n" % ent.copyfrom_rev)
   432 
   429 
   433                 # if a branch is created but entries are removed in the same
   430                 # if a branch is created but entries are removed in the same
   434                 # changeset, get the right fromrev
   431                 # changeset, get the right fromrev
   435                 if parents:
   432                 # parents cannot be empty here, you cannot remove things from
   436                     uuid, old_module, fromrev = self.revsplit(parents[0])
   433                 # a root revision.
   437                 else:
   434                 uuid, old_module, fromrev = self.revsplit(parents[0])
   438                     fromrev = revnum - 1
       
   439                     # might always need to be revnum - 1 in these 3 lines?
       
   440                     old_module = self.modulemap.get(fromrev, self.module)
       
   441 
   435 
   442                 basepath = old_module + "/" + get_entry_from_path(path, module=self.module)
   436                 basepath = old_module + "/" + get_entry_from_path(path, module=self.module)
   443                 entrypath = old_module + "/" + get_entry_from_path(path, module=self.module)
   437                 entrypath = old_module + "/" + get_entry_from_path(path, module=self.module)
   444 
   438 
   445                 def lookup_parts(p):
   439                 def lookup_parts(p):
   575         if from_revnum < to_revnum:
   569         if from_revnum < to_revnum:
   576             from_revnum, to_revnum = to_revnum, from_revnum
   570             from_revnum, to_revnum = to_revnum, from_revnum
   577 
   571 
   578         self.child_cset = None
   572         self.child_cset = None
   579         def parselogentry(orig_paths, revnum, author, date, message):
   573         def parselogentry(orig_paths, revnum, author, date, message):
       
   574             """Return the parsed commit object or None, and True if 
       
   575             the revision is a branch root.
       
   576             """
   580             self.ui.debug("parsing revision %d (%d changes)\n" %
   577             self.ui.debug("parsing revision %d (%d changes)\n" %
   581                           (revnum, len(orig_paths)))
   578                           (revnum, len(orig_paths)))
   582 
   579 
   583             if revnum in self.modulemap:
       
   584                 new_module = self.modulemap[revnum]
       
   585                 if new_module != self.module:
       
   586                     self.module = new_module
       
   587                     self.reparent(self.module)
       
   588 
       
   589             rev = self.revid(revnum)
   580             rev = self.revid(revnum)
   590             # branch log might return entries for a parent we already have
   581             # branch log might return entries for a parent we already have
   591 
   582 
   592             if (rev in self.commits or revnum < to_revnum):
   583             if (rev in self.commits or revnum < to_revnum):
   593                 return None
   584                 return None, False
   594 
   585 
   595             parents = []
   586             parents = []
   596             # check whether this revision is the start of a branch
   587             # check whether this revision is the start of a branch
   597             if self.module in orig_paths:
   588             if self.module in orig_paths:
   598                 ent = orig_paths[self.module]
   589                 ent = orig_paths[self.module]
   599                 if ent.copyfrom_path:
   590                 if ent.copyfrom_path:
   600                     # ent.copyfrom_rev may not be the actual last revision
   591                     # ent.copyfrom_rev may not be the actual last revision
   601                     prev = self.latest(ent.copyfrom_path, ent.copyfrom_rev)
   592                     prev = self.latest(ent.copyfrom_path, ent.copyfrom_rev)
   602                     self.modulemap[prev] = ent.copyfrom_path
       
   603                     parents = [self.revid(prev, ent.copyfrom_path)]
   593                     parents = [self.revid(prev, ent.copyfrom_path)]
   604                     self.ui.note('found parent of branch %s at %d: %s\n' % \
   594                     self.ui.note('found parent of branch %s at %d: %s\n' % \
   605                                      (self.module, prev, ent.copyfrom_path))
   595                                      (self.module, prev, ent.copyfrom_path))
   606                 else:
   596                 else:
   607                     self.ui.debug("No copyfrom path, don't know what to do.\n")
   597                     self.ui.debug("No copyfrom path, don't know what to do.\n")
   608 
       
   609             self.modulemap[revnum] = self.module # track backwards in time
       
   610 
   598 
   611             orig_paths = orig_paths.items()
   599             orig_paths = orig_paths.items()
   612             orig_paths.sort()
   600             orig_paths.sort()
   613             paths = []
   601             paths = []
   614             # filter out unrelated paths
   602             # filter out unrelated paths
   616                 if not path.startswith(self.module):
   604                 if not path.startswith(self.module):
   617                     self.ui.debug("boring@%s: %s\n" % (revnum, path))
   605                     self.ui.debug("boring@%s: %s\n" % (revnum, path))
   618                     continue
   606                     continue
   619                 paths.append((path, ent))
   607                 paths.append((path, ent))
   620 
   608 
   621             self.paths[rev] = (paths, parents)
       
   622 
       
   623             # Example SVN datetime. Includes microseconds.
   609             # Example SVN datetime. Includes microseconds.
   624             # ISO-8601 conformant
   610             # ISO-8601 conformant
   625             # '2007-01-04T17:35:00.902377Z'
   611             # '2007-01-04T17:35:00.902377Z'
   626             date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"])
   612             date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"])
   627 
   613 
   640                           parents=parents,
   626                           parents=parents,
   641                           branch=branch,
   627                           branch=branch,
   642                           rev=rev.encode('utf-8'))
   628                           rev=rev.encode('utf-8'))
   643 
   629 
   644             self.commits[rev] = cset
   630             self.commits[rev] = cset
       
   631             # The parents list is *shared* among self.paths and the
       
   632             # commit object. Both will be updated below.
       
   633             self.paths[rev] = (paths, cset.parents)
   645             if self.child_cset and not self.child_cset.parents:
   634             if self.child_cset and not self.child_cset.parents:
   646                 self.child_cset.parents = [rev]
   635                 self.child_cset.parents[:] = [rev]
   647             self.child_cset = cset
   636             self.child_cset = cset
   648             return cset
   637             return cset, len(parents) > 0
   649 
   638 
   650         self.ui.note('fetching revision log for "%s" from %d to %d\n' %
   639         self.ui.note('fetching revision log for "%s" from %d to %d\n' %
   651                      (self.module, from_revnum, to_revnum))
   640                      (self.module, from_revnum, to_revnum))
   652 
   641 
   653         try:
   642         try:
   654             firstcset = None
   643             firstcset = None
       
   644             branched = False
   655             for entry in self.get_log([self.module], from_revnum, to_revnum):
   645             for entry in self.get_log([self.module], from_revnum, to_revnum):
       
   646                 if branched:
       
   647                     # The iterator must be exhausted for the child process
       
   648                     # to terminate cleanly.
       
   649                     continue
   656                 paths, revnum, author, date, message = entry
   650                 paths, revnum, author, date, message = entry
   657                 if self.is_blacklisted(revnum):
   651                 if self.is_blacklisted(revnum):
   658                     self.ui.note('skipping blacklisted revision %d\n' % revnum)
   652                     self.ui.note('skipping blacklisted revision %d\n' % revnum)
   659                     continue
   653                     continue
   660                 if paths is None:
   654                 if paths is None:
   661                     self.ui.debug('revision %d has no entries\n' % revnum)
   655                     self.ui.debug('revision %d has no entries\n' % revnum)
   662                     continue
   656                     continue
   663                 cset = parselogentry(paths, revnum, author, date, message)
   657                 cset, branched = parselogentry(paths, revnum, author, 
       
   658                                                date, message)
   664                 if cset:
   659                 if cset:
   665                     firstcset = cset
   660                     firstcset = cset
   666 
   661 
   667             if firstcset and not firstcset.parents:
   662             if firstcset and not firstcset.parents:
   668                 # The first revision of the sequence (the last fetched one)
   663                 # The first revision of the sequence (the last fetched one)
   684     def _getfile(self, file, rev):
   679     def _getfile(self, file, rev):
   685         io = StringIO()
   680         io = StringIO()
   686         # TODO: ra.get_file transmits the whole file instead of diffs.
   681         # TODO: ra.get_file transmits the whole file instead of diffs.
   687         mode = ''
   682         mode = ''
   688         try:
   683         try:
   689             revnum = self.revnum(rev)
   684             new_module, revnum = self.revsplit(rev)[1:]
   690             if self.module != self.modulemap[revnum]:
   685             if self.module != new_module:
   691                 self.module = self.modulemap[revnum]
   686                 self.module = new_module
   692                 self.reparent(self.module)
   687                 self.reparent(self.module)
   693             info = svn.ra.get_file(self.ra, file, revnum, io)
   688             info = svn.ra.get_file(self.ra, file, revnum, io)
   694             if isinstance(info, list):
   689             if isinstance(info, list):
   695                 info = info[-1]
   690                 info = info[-1]
   696             mode = ("svn:executable" in info) and 'x' or ''
   691             mode = ("svn:executable" in info) and 'x' or ''