hgext/convert/subversion.py
changeset 4786 62e1b6412b62
parent 4785 a67f185d0474
child 4787 c5dd8e184279
equal deleted inserted replaced
4785:a67f185d0474 4786:62e1b6412b62
   216 
   216 
   217             copyfrom = {} # Map of entrypath, revision for finding source of deleted revisions.
   217             copyfrom = {} # Map of entrypath, revision for finding source of deleted revisions.
   218             copies = {}
   218             copies = {}
   219             entries = []
   219             entries = []
   220             self.ui.debug("Parsing revision %d\n" % revnum)
   220             self.ui.debug("Parsing revision %d\n" % revnum)
   221             if orig_paths is not None:
   221             if orig_paths is None:
   222                 rev = self.rev(revnum)
   222                 return
   223                 try:
   223 
   224                     branch = self.module.split("/")[-1]
   224             rev = self.rev(revnum)
   225                     if branch == 'trunk':
   225             try:
   226                         branch = ''
   226                 branch = self.module.split("/")[-1]
   227                 except IndexError:
   227                 if branch == 'trunk':
   228                     branch = None
   228                     branch = ''
       
   229             except IndexError:
       
   230                 branch = None
   229                 
   231                 
   230                 for path in orig_paths:
   232             for path in orig_paths:
   231                     # self.ui.write("path %s\n" % path)
   233                 # self.ui.write("path %s\n" % path)
   232                     if path == self.module: # Follow branching back in history
   234                 if path == self.module: # Follow branching back in history
   233                         ent = orig_paths[path]
       
   234                         if ent:
       
   235                             if ent.copyfrom_path:
       
   236                                 self.modulemap[ent.copyfrom_rev] = ent.copyfrom_path
       
   237                             else:
       
   238                                 self.ui.debug("No copyfrom path, don't know what to do.\n")
       
   239                                 # Maybe it was added and there is no more history.
       
   240                     entrypath = get_entry_from_path(path, module=self.module)
       
   241                     # self.ui.write("entrypath %s\n" % entrypath)
       
   242                     if entrypath is None:
       
   243                         # Outside our area of interest
       
   244                         self.ui.debug("boring@%s: %s\n" % (revnum, path))
       
   245                         continue
       
   246                     entry = entrypath.decode(self.encoding)
       
   247                     ent = orig_paths[path]
   235                     ent = orig_paths[path]
   248                     if not entrypath:
   236                     if ent:
   249                         # TODO: branch creation event
       
   250                         pass
       
   251 
       
   252                     kind = svn.ra.check_path(self.ra, entrypath, revnum)
       
   253                     if kind == svn.core.svn_node_file:
       
   254                         if ent.copyfrom_path:
   237                         if ent.copyfrom_path:
   255                             copyfrom_path = get_entry_from_path(ent.copyfrom_path)
   238                             self.modulemap[ent.copyfrom_rev] = ent.copyfrom_path
   256                             if copyfrom_path:
   239                         else:
   257                                 self.ui.debug("Copied to %s from %s@%s\n" % (entry, copyfrom_path, ent.copyfrom_rev))
   240                             self.ui.debug("No copyfrom path, don't know what to do.\n")
   258                                 # It's probably important for hg that the source
   241                             # Maybe it was added and there is no more history.
   259                                 # exists in the revision's parent, not just the
   242                 entrypath = get_entry_from_path(path, module=self.module)
   260                                 # ent.copyfrom_rev
   243                 # self.ui.write("entrypath %s\n" % entrypath)
   261                                 fromkind = svn.ra.check_path(self.ra, copyfrom_path, ent.copyfrom_rev)
   244                 if entrypath is None:
   262                                 if fromkind != 0:
   245                     # Outside our area of interest
   263                                     copies[self.recode(entry)] = self.recode(copyfrom_path)
   246                     self.ui.debug("boring@%s: %s\n" % (revnum, path))
       
   247                     continue
       
   248                 entry = entrypath.decode(self.encoding)
       
   249                 ent = orig_paths[path]
       
   250                 if not entrypath:
       
   251                     # TODO: branch creation event
       
   252                     pass
       
   253 
       
   254                 kind = svn.ra.check_path(self.ra, entrypath, revnum)
       
   255                 if kind == svn.core.svn_node_file:
       
   256                     if ent.copyfrom_path:
       
   257                         copyfrom_path = get_entry_from_path(ent.copyfrom_path)
       
   258                         if copyfrom_path:
       
   259                             self.ui.debug("Copied to %s from %s@%s\n" % (entry, copyfrom_path, ent.copyfrom_rev))
       
   260                             # It's probably important for hg that the source
       
   261                             # exists in the revision's parent, not just the
       
   262                             # ent.copyfrom_rev
       
   263                             fromkind = svn.ra.check_path(self.ra, copyfrom_path, ent.copyfrom_rev)
       
   264                             if fromkind != 0:
       
   265                                 copies[self.recode(entry)] = self.recode(copyfrom_path)
       
   266                     entries.append(self.recode(entry))
       
   267                 elif kind == 0: # gone, but had better be a deleted *file*
       
   268                     self.ui.debug("gone from %s\n" % ent.copyfrom_rev)
       
   269 
       
   270                     fromrev = revnum - 1
       
   271                     # might always need to be revnum - 1 in these 3 lines?
       
   272                     old_module = self.modulemap.get(fromrev, self.module)
       
   273                     basepath = old_module + "/" + get_entry_from_path(path, module=self.module)
       
   274                     entrypath = old_module + "/" + get_entry_from_path(path, module=self.module)
       
   275 
       
   276                     def lookup_parts(p):
       
   277                         rc = None
       
   278                         parts = p.split("/")
       
   279                         for i in range(len(parts)):
       
   280                             part = "/".join(parts[:i])
       
   281                             info = part, copyfrom.get(part, None)
       
   282                             if info[1] is not None:
       
   283                                 self.ui.debug("Found parent directory %s\n" % info)
       
   284                                 rc = info
       
   285                         return rc
       
   286 
       
   287                     self.ui.debug("base, entry %s %s\n" % (basepath, entrypath))
       
   288 
       
   289                     frompath, froment = lookup_parts(entrypath) or (None, revnum - 1)
       
   290 
       
   291                     # need to remove fragment from lookup_parts and replace with copyfrom_path
       
   292                     if frompath is not None:
       
   293                         self.ui.debug("munge-o-matic\n")
       
   294                         self.ui.debug(entrypath + '\n')
       
   295                         self.ui.debug(entrypath[len(frompath):] + '\n')
       
   296                         entrypath = froment.copyfrom_path + entrypath[len(frompath):]
       
   297                         fromrev = froment.copyfrom_rev
       
   298                         self.ui.debug("Info: %s %s %s %s\n" % (frompath, froment, ent, entrypath))
       
   299 
       
   300                     fromkind = svn.ra.check_path(self.ra, entrypath, fromrev)
       
   301                     if fromkind == svn.core.svn_node_file:   # a deleted file
   264                         entries.append(self.recode(entry))
   302                         entries.append(self.recode(entry))
   265                     elif kind == 0: # gone, but had better be a deleted *file*
   303                     else:
   266                         self.ui.debug("gone from %s\n" % ent.copyfrom_rev)
   304                         # print "Deleted/moved non-file:", revnum, path, ent
   267 
   305                         # children = self._find_children(path, revnum - 1)
   268                         fromrev = revnum - 1
   306                         # print "find children %s@%d from %d action %s" % (path, revnum, ent.copyfrom_rev, ent.action)
   269                         # might always need to be revnum - 1 in these 3 lines?
   307                         # Sometimes this is tricky. For example: in
   270                         old_module = self.modulemap.get(fromrev, self.module)
   308                         # The Subversion Repository revision 6940 a dir
   271                         basepath = old_module + "/" + get_entry_from_path(path, module=self.module)
   309                         # was copied and one of its files was deleted 
   272                         entrypath = old_module + "/" + get_entry_from_path(path, module=self.module)
   310                         # from the new location in the same commit. This
   273 
   311                         # code can't deal with that yet.
   274                         def lookup_parts(p):
   312                         if ent.action == 'C':
   275                             rc = None
   313                             children = self._find_children(path, fromrev)
   276                             parts = p.split("/")
       
   277                             for i in range(len(parts)):
       
   278                                 part = "/".join(parts[:i])
       
   279                                 info = part, copyfrom.get(part, None)
       
   280                                 if info[1] is not None:
       
   281                                     self.ui.debug("Found parent directory %s\n" % info)
       
   282                                     rc = info
       
   283                             return rc
       
   284 
       
   285                         self.ui.debug("base, entry %s %s\n" % (basepath, entrypath))
       
   286 
       
   287                         frompath, froment = lookup_parts(entrypath) or (None, revnum - 1)
       
   288 
       
   289                         # need to remove fragment from lookup_parts and replace with copyfrom_path
       
   290                         if frompath is not None:
       
   291                             self.ui.debug("munge-o-matic\n")
       
   292                             self.ui.debug(entrypath + '\n')
       
   293                             self.ui.debug(entrypath[len(frompath):] + '\n')
       
   294                             entrypath = froment.copyfrom_path + entrypath[len(frompath):]
       
   295                             fromrev = froment.copyfrom_rev
       
   296                             self.ui.debug("Info: %s %s %s %s\n" % (frompath, froment, ent, entrypath))
       
   297 
       
   298                         fromkind = svn.ra.check_path(self.ra, entrypath, fromrev)
       
   299                         if fromkind == svn.core.svn_node_file:   # a deleted file
       
   300                             entries.append(self.recode(entry))
       
   301                         else:
   314                         else:
   302                             # print "Deleted/moved non-file:", revnum, path, ent
   315                             oroot = entrypath.strip('/')
   303                             # children = self._find_children(path, revnum - 1)
   316                             nroot = path.strip('/')
   304                             # print "find children %s@%d from %d action %s" % (path, revnum, ent.copyfrom_rev, ent.action)
   317                             children = self._find_children(oroot, fromrev)
   305                             # Sometimes this is tricky. For example: in
   318                             children = [s.replace(oroot,nroot) for s in children]
   306                             # The Subversion Repository revision 6940 a dir
   319                         # Mark all [files, not directories] as deleted.
   307                             # was copied and one of its files was deleted 
       
   308                             # from the new location in the same commit. This
       
   309                             # code can't deal with that yet.
       
   310                             if ent.action == 'C':
       
   311                                 children = self._find_children(path, fromrev)
       
   312                             else:
       
   313                                 oroot = entrypath.strip('/')
       
   314                                 nroot = path.strip('/')
       
   315                                 children = self._find_children(oroot, fromrev)
       
   316                                 children = [s.replace(oroot,nroot) for s in children]
       
   317                             # Mark all [files, not directories] as deleted.
       
   318                             for child in children:
       
   319                                 # Can we move a child directory and its
       
   320                                 # parent in the same commit? (probably can). Could
       
   321                                 # cause problems if instead of revnum -1, 
       
   322                                 # we have to look in (copyfrom_path, revnum - 1)
       
   323                                 entrypath = get_entry_from_path("/" + child, module=old_module)
       
   324                                 if entrypath:
       
   325                                     entry = self.recode(entrypath.decode(self.encoding))
       
   326                                     if entry in copies:
       
   327                                         # deleted file within a copy
       
   328                                         del copies[entry]
       
   329                                     else:
       
   330                                         entries.append(entry)
       
   331                     elif kind == svn.core.svn_node_dir:
       
   332                         # Should probably synthesize normal file entries
       
   333                         # and handle as above to clean up copy/rename handling.
       
   334 
       
   335                         # If the directory just had a prop change,
       
   336                         # then we shouldn't need to look for its children.
       
   337                         # Also this could create duplicate entries. Not sure
       
   338                         # whether this will matter. Maybe should make entries a set.
       
   339                         # print "Changed directory", revnum, path, ent.action, ent.copyfrom_path, ent.copyfrom_rev
       
   340                         # This will fail if a directory was copied
       
   341                         # from another branch and then some of its files
       
   342                         # were deleted in the same transaction.
       
   343                         children = self._find_children(path, revnum)
       
   344                         children.sort()
       
   345                         for child in children:
   320                         for child in children:
   346                             # Can we move a child directory and its
   321                             # Can we move a child directory and its
   347                             # parent in the same commit? (probably can). Could
   322                             # parent in the same commit? (probably can). Could
   348                             # cause problems if instead of revnum -1, 
   323                             # cause problems if instead of revnum -1, 
   349                             # we have to look in (copyfrom_path, revnum - 1)
   324                             # we have to look in (copyfrom_path, revnum - 1)
   350                             entrypath = get_entry_from_path("/" + child, module=self.module)
   325                             entrypath = get_entry_from_path("/" + child, module=old_module)
   351                             # print child, self.module, entrypath
       
   352                             if entrypath:
   326                             if entrypath:
   353                                 # Need to filter out directories here...
   327                                 entry = self.recode(entrypath.decode(self.encoding))
   354                                 kind = svn.ra.check_path(self.ra, entrypath, revnum)
   328                                 if entry in copies:
   355                                 if kind != svn.core.svn_node_dir:
   329                                     # deleted file within a copy
   356                                     entries.append(self.recode(entrypath))
   330                                     del copies[entry]
   357 
   331                                 else:
   358                         # Copies here (must copy all from source)
   332                                     entries.append(entry)
   359                         # Probably not a real problem for us if
   333                 elif kind == svn.core.svn_node_dir:
   360                         # source does not exist
   334                     # Should probably synthesize normal file entries
   361 
   335                     # and handle as above to clean up copy/rename handling.
   362                         # Can do this with the copy command "hg copy"
   336 
   363                         # if ent.copyfrom_path:
   337                     # If the directory just had a prop change,
   364                         #     copyfrom_entry = get_entry_from_path(ent.copyfrom_path.decode(self.encoding),
   338                     # then we shouldn't need to look for its children.
   365                         #             module=self.module)
   339                     # Also this could create duplicate entries. Not sure
   366                         #     copyto_entry = entrypath
   340                     # whether this will matter. Maybe should make entries a set.
   367                         #
   341                     # print "Changed directory", revnum, path, ent.action, ent.copyfrom_path, ent.copyfrom_rev
   368                         #     print "copy directory", copyfrom_entry, 'to', copyto_entry
   342                     # This will fail if a directory was copied
   369                         #
   343                     # from another branch and then some of its files
   370                         #     copies.append((copyfrom_entry, copyto_entry))
   344                     # were deleted in the same transaction.
   371                         
   345                     children = self._find_children(path, revnum)
   372                         if ent.copyfrom_path:
   346                     children.sort()
   373                             copyfrom_path = ent.copyfrom_path.decode(self.encoding)
   347                     for child in children:
   374                             copyfrom_entry = get_entry_from_path(copyfrom_path, module=self.module)
   348                         # Can we move a child directory and its
   375                             if copyfrom_entry:
   349                         # parent in the same commit? (probably can). Could
   376                                 copyfrom[path] = ent
   350                         # cause problems if instead of revnum -1, 
   377                                 self.ui.debug("mark %s came from %s\n" % (path, copyfrom[path]))
   351                         # we have to look in (copyfrom_path, revnum - 1)
   378 
   352                         entrypath = get_entry_from_path("/" + child, module=self.module)
   379                                 # Good, /probably/ a regular copy. Really should check
   353                         # print child, self.module, entrypath
   380                                 # to see whether the parent revision actually contains
   354                         if entrypath:
   381                                 # the directory in question.
   355                             # Need to filter out directories here...
   382                                 children = self._find_children(self.recode(copyfrom_path), ent.copyfrom_rev)
   356                             kind = svn.ra.check_path(self.ra, entrypath, revnum)
   383                                 children.sort()
   357                             if kind != svn.core.svn_node_dir:
   384                                 for child in children:
   358                                 entries.append(self.recode(entrypath))
   385                                     entrypath = get_entry_from_path("/" + child, module=self.module)
   359 
   386                                     if entrypath:
   360                     # Copies here (must copy all from source)
   387                                         entry = entrypath.decode(self.encoding)
   361                     # Probably not a real problem for us if
   388                                         # print "COPY COPY From", copyfrom_entry, entry
   362                     # source does not exist
   389                                         copyto_path = path + entry[len(copyfrom_entry):]
   363 
   390                                         copyto_entry =  get_entry_from_path(copyto_path, module=self.module)
   364                     # Can do this with the copy command "hg copy"
   391                                         # print "COPY", entry, "COPY To", copyto_entry
   365                     # if ent.copyfrom_path:
   392                                         copies[self.recode(copyto_entry)] = self.recode(entry)
   366                     #     copyfrom_entry = get_entry_from_path(ent.copyfrom_path.decode(self.encoding),
   393                                         # copy from quux splort/quuxfile
   367                     #             module=self.module)
   394               
   368                     #     copyto_entry = entrypath
   395                 self.modulemap[revnum] = self.module # track backwards in time
   369                     #
   396                 # a list of (filename, id) where id lets us retrieve the file.
   370                     #     print "copy directory", copyfrom_entry, 'to', copyto_entry
   397                 # eg in git, id is the object hash. for svn it'll be the 
   371                     #
   398                 self.files[rev] = zip(entries, [rev] * len(entries))
   372                     #     copies.append((copyfrom_entry, copyto_entry))
   399                 if not entries:
   373 
   400                     return
   374                     if ent.copyfrom_path:
   401 
   375                         copyfrom_path = ent.copyfrom_path.decode(self.encoding)
   402                 # Example SVN datetime. Includes microseconds.
   376                         copyfrom_entry = get_entry_from_path(copyfrom_path, module=self.module)
   403                 # ISO-8601 conformant
   377                         if copyfrom_entry:
   404                 # '2007-01-04T17:35:00.902377Z'
   378                             copyfrom[path] = ent
   405                 date = util.parsedate(date[:18] + " UTC", ["%Y-%m-%dT%H:%M:%S"])
   379                             self.ui.debug("mark %s came from %s\n" % (path, copyfrom[path]))
   406 
   380 
   407                 log = message and self.recode(message)
   381                             # Good, /probably/ a regular copy. Really should check
   408                 author = author and self.recode(author) or ''
   382                             # to see whether the parent revision actually contains
   409 
   383                             # the directory in question.
   410                 cset = commit(author=author,
   384                             children = self._find_children(self.recode(copyfrom_path), ent.copyfrom_rev)
   411                               date=util.datestr(date), 
   385                             children.sort()
   412                               desc=log, 
   386                             for child in children:
   413                               parents=[],
   387                                 entrypath = get_entry_from_path("/" + child, module=self.module)
   414                               copies=copies,
   388                                 if entrypath:
   415                               branch=branch)
   389                                     entry = entrypath.decode(self.encoding)
   416 
   390                                     # print "COPY COPY From", copyfrom_entry, entry
   417                 if self.child_cset and self.child_rev != rev:
   391                                     copyto_path = path + entry[len(copyfrom_entry):]
   418                     self.child_cset.parents = [rev]
   392                                     copyto_entry =  get_entry_from_path(copyto_path, module=self.module)
   419                     self.commits[self.child_rev] = self.child_cset
   393                                     # print "COPY", entry, "COPY To", copyto_entry
   420                 self.child_cset = cset
   394                                     copies[self.recode(copyto_entry)] = self.recode(entry)
   421                 self.child_rev = rev
   395                                     # copy from quux splort/quuxfile
       
   396 
       
   397             self.modulemap[revnum] = self.module # track backwards in time
       
   398             # a list of (filename, id) where id lets us retrieve the file.
       
   399             # eg in git, id is the object hash. for svn it'll be the 
       
   400             self.files[rev] = zip(entries, [rev] * len(entries))
       
   401             if not entries:
       
   402                 return
       
   403 
       
   404             # Example SVN datetime. Includes microseconds.
       
   405             # ISO-8601 conformant
       
   406             # '2007-01-04T17:35:00.902377Z'
       
   407             date = util.parsedate(date[:18] + " UTC", ["%Y-%m-%dT%H:%M:%S"])
       
   408 
       
   409             log = message and self.recode(message)
       
   410             author = author and self.recode(author) or ''
       
   411 
       
   412             cset = commit(author=author,
       
   413                           date=util.datestr(date), 
       
   414                           desc=log, 
       
   415                           parents=[],
       
   416                           copies=copies,
       
   417                           branch=branch)
       
   418 
       
   419             if self.child_cset and self.child_rev != rev:
       
   420                 self.child_cset.parents = [rev]
       
   421                 self.commits[self.child_rev] = self.child_cset
       
   422             self.child_cset = cset
       
   423             self.child_rev = rev
   422 
   424 
   423         try:
   425         try:
   424             discover_changed_paths = True
   426             discover_changed_paths = True
   425             strict_node_history = False
   427             strict_node_history = False
   426             svn.ra.get_log(self.ra, [self.module], from_revnum, to_revnum, 
   428             svn.ra.get_log(self.ra, [self.module], from_revnum, to_revnum,