hgext/convert/hg.py
changeset 28370 c1878afb063a
parent 27863 ed59ae8b025e
child 28861 86db5cb55d46
equal deleted inserted replaced
28369:71176606fa0a 28370:c1878afb063a
    14 #
    14 #
    15 # * Using "--config convert.hg.saverev=true" will make the source
    15 # * Using "--config convert.hg.saverev=true" will make the source
    16 #   identifier to be stored in the converted revision. This will cause
    16 #   identifier to be stored in the converted revision. This will cause
    17 #   the converted revision to have a different identity than the
    17 #   the converted revision to have a different identity than the
    18 #   source.
    18 #   source.
    19 
    19 from __future__ import absolute_import
    20 
    20 
    21 import os, time, cStringIO
    21 import cStringIO
       
    22 import os
       
    23 import re
       
    24 import time
       
    25 
       
    26 from mercurial import (
       
    27     bookmarks,
       
    28     context,
       
    29     error,
       
    30     exchange,
       
    31     hg,
       
    32     lock as lockmod,
       
    33     merge as mergemod,
       
    34     node as nodemod,
       
    35     phases,
       
    36     scmutil,
       
    37     util,
       
    38 )
    22 from mercurial.i18n import _
    39 from mercurial.i18n import _
    23 from mercurial.node import bin, hex, nullid
    40 from . import common
    24 from mercurial import hg, util, context, bookmarks, error, scmutil, exchange
    41 mapfile = common.mapfile
    25 from mercurial import phases
    42 NoRepo = common.NoRepo
    26 from mercurial import lock as lockmod
    43 
    27 from mercurial import merge as mergemod
       
    28 
       
    29 from common import NoRepo, commit, converter_source, converter_sink, mapfile
       
    30 
       
    31 import re
       
    32 sha1re = re.compile(r'\b[0-9a-f]{12,40}\b')
    44 sha1re = re.compile(r'\b[0-9a-f]{12,40}\b')
    33 
    45 
    34 class mercurial_sink(converter_sink):
    46 class mercurial_sink(common.converter_sink):
    35     def __init__(self, ui, path):
    47     def __init__(self, ui, path):
    36         converter_sink.__init__(self, ui, path)
    48         common.converter_sink.__init__(self, ui, path)
    37         self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
    49         self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
    38         self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
    50         self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
    39         self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
    51         self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
    40         self.lastbranch = None
    52         self.lastbranch = None
    41         if os.path.isdir(path) and len(os.listdir(path)) > 0:
    53         if os.path.isdir(path) and len(os.listdir(path)) > 0:
   130             s = line.split(' ', 1)
   142             s = line.split(' ', 1)
   131             if len(s) != 2:
   143             if len(s) != 2:
   132                 continue
   144                 continue
   133             revid = revmap.get(source.lookuprev(s[0]))
   145             revid = revmap.get(source.lookuprev(s[0]))
   134             if not revid:
   146             if not revid:
   135                 if s[0] == hex(nullid):
   147                 if s[0] == nodemod.nullhex:
   136                     revid = s[0]
   148                     revid = s[0]
   137                 else:
   149                 else:
   138                     continue
   150                     continue
   139             fp.write('%s %s\n' % (revid, s[1]))
   151             fp.write('%s %s\n' % (revid, s[1]))
   140         return fp.getvalue()
   152         return fp.getvalue()
   146             if len(s) != 2:
   158             if len(s) != 2:
   147                 continue
   159                 continue
   148 
   160 
   149             revid = s[0]
   161             revid = s[0]
   150             subpath = s[1]
   162             subpath = s[1]
   151             if revid != hex(nullid):
   163             if revid != nodemod.nullhex:
   152                 revmap = self.subrevmaps.get(subpath)
   164                 revmap = self.subrevmaps.get(subpath)
   153                 if revmap is None:
   165                 if revmap is None:
   154                     revmap = mapfile(self.ui,
   166                     revmap = mapfile(self.ui,
   155                                      self.repo.wjoin(subpath, '.hg/shamap'))
   167                                      self.repo.wjoin(subpath, '.hg/shamap'))
   156                     self.subrevmaps[subpath] = revmap
   168                     self.subrevmaps[subpath] = revmap
   248             if p not in pl:
   260             if p not in pl:
   249                 pl.append(p)
   261                 pl.append(p)
   250         parents = pl
   262         parents = pl
   251         nparents = len(parents)
   263         nparents = len(parents)
   252         if self.filemapmode and nparents == 1:
   264         if self.filemapmode and nparents == 1:
   253             m1node = self.repo.changelog.read(bin(parents[0]))[0]
   265             m1node = self.repo.changelog.read(nodemod.bin(parents[0]))[0]
   254             parent = parents[0]
   266             parent = parents[0]
   255 
   267 
   256         if len(parents) < 2:
   268         if len(parents) < 2:
   257             parents.append(nullid)
   269             parents.append(nodemod.nullid)
   258         if len(parents) < 2:
   270         if len(parents) < 2:
   259             parents.append(nullid)
   271             parents.append(nodemod.nullid)
   260         p2 = parents.pop(0)
   272         p2 = parents.pop(0)
   261 
   273 
   262         text = commit.desc
   274         text = commit.desc
   263 
   275 
   264         sha1s = re.findall(sha1re, text)
   276         sha1s = re.findall(sha1re, text)
   281             if node is None:
   293             if node is None:
   282                 continue
   294                 continue
   283 
   295 
   284             # Only transplant stores its reference in binary
   296             # Only transplant stores its reference in binary
   285             if label == 'transplant_source':
   297             if label == 'transplant_source':
   286                 node = hex(node)
   298                 node = nodemod.hex(node)
   287 
   299 
   288             newrev = revmap.get(node)
   300             newrev = revmap.get(node)
   289             if newrev is not None:
   301             if newrev is not None:
   290                 if label == 'transplant_source':
   302                 if label == 'transplant_source':
   291                     newrev = bin(newrev)
   303                     newrev = nodemod.bin(newrev)
   292 
   304 
   293                 extra[label] = newrev
   305                 extra[label] = newrev
   294 
   306 
   295         if self.branchnames and commit.branch:
   307         if self.branchnames and commit.branch:
   296             extra['branch'] = commit.branch
   308             extra['branch'] = commit.branch
   300         while parents:
   312         while parents:
   301             p1 = p2
   313             p1 = p2
   302             p2 = parents.pop(0)
   314             p2 = parents.pop(0)
   303             p1ctx = self.repo[p1]
   315             p1ctx = self.repo[p1]
   304             p2ctx = None
   316             p2ctx = None
   305             if p2 != nullid:
   317             if p2 != nodemod.nullid:
   306                 p2ctx = self.repo[p2]
   318                 p2ctx = self.repo[p2]
   307             fileset = set(files)
   319             fileset = set(files)
   308             if full:
   320             if full:
   309                 fileset.update(self.repo[p1])
   321                 fileset.update(self.repo[p1])
   310                 fileset.update(self.repo[p2])
   322                 fileset.update(self.repo[p2])
   322             # commit, so copy the source's phase for now.
   334             # commit, so copy the source's phase for now.
   323             self.repo.ui.setconfig('phases', 'new-commit',
   335             self.repo.ui.setconfig('phases', 'new-commit',
   324                                    phases.phasenames[commit.phase], 'convert')
   336                                    phases.phasenames[commit.phase], 'convert')
   325 
   337 
   326             with self.repo.transaction("convert") as tr:
   338             with self.repo.transaction("convert") as tr:
   327                 node = hex(self.repo.commitctx(ctx))
   339                 node = nodemod.hex(self.repo.commitctx(ctx))
   328 
   340 
   329                 # If the node value has changed, but the phase is lower than
   341                 # If the node value has changed, but the phase is lower than
   330                 # draft, set it back to draft since it hasn't been exposed
   342                 # draft, set it back to draft since it hasn't been exposed
   331                 # anywhere.
   343                 # anywhere.
   332                 if commit.rev != node:
   344                 if commit.rev != node:
   338             text = "(octopus merge fixup)\n"
   350             text = "(octopus merge fixup)\n"
   339             p2 = node
   351             p2 = node
   340 
   352 
   341         if self.filemapmode and nparents == 1:
   353         if self.filemapmode and nparents == 1:
   342             man = self.repo.manifest
   354             man = self.repo.manifest
   343             mnode = self.repo.changelog.read(bin(p2))[0]
   355             mnode = self.repo.changelog.read(nodemod.bin(p2))[0]
   344             closed = 'close' in commit.extra
   356             closed = 'close' in commit.extra
   345             if not closed and not man.cmp(m1node, man.revision(mnode)):
   357             if not closed and not man.cmp(m1node, man.revision(mnode)):
   346                 self.ui.status(_("filtering out empty revision\n"))
   358                 self.ui.status(_("filtering out empty revision\n"))
   347                 self.repo.rollback(force=True)
   359                 self.repo.rollback(force=True)
   348                 return parent
   360                 return parent
   352         try:
   364         try:
   353             parentctx = self.repo[self.tagsbranch]
   365             parentctx = self.repo[self.tagsbranch]
   354             tagparent = parentctx.node()
   366             tagparent = parentctx.node()
   355         except error.RepoError:
   367         except error.RepoError:
   356             parentctx = None
   368             parentctx = None
   357             tagparent = nullid
   369             tagparent = nodemod.nullid
   358 
   370 
   359         oldlines = set()
   371         oldlines = set()
   360         for branch, heads in self.repo.branchmap().iteritems():
   372         for branch, heads in self.repo.branchmap().iteritems():
   361             for h in heads:
   373             for h in heads:
   362                 if '.hgtags' in self.repo[h]:
   374                 if '.hgtags' in self.repo[h]:
   395         extra = {'branch': self.tagsbranch}
   407         extra = {'branch': self.tagsbranch}
   396         ctx = context.memctx(self.repo, (tagparent, None), "update tags",
   408         ctx = context.memctx(self.repo, (tagparent, None), "update tags",
   397                              [".hgtags"], getfilectx, "convert-repo", date,
   409                              [".hgtags"], getfilectx, "convert-repo", date,
   398                              extra)
   410                              extra)
   399         node = self.repo.commitctx(ctx)
   411         node = self.repo.commitctx(ctx)
   400         return hex(node), hex(tagparent)
   412         return nodemod.hex(node), nodemod.hex(tagparent)
   401 
   413 
   402     def setfilemapmode(self, active):
   414     def setfilemapmode(self, active):
   403         self.filemapmode = active
   415         self.filemapmode = active
   404 
   416 
   405     def putbookmarks(self, updatedbookmark):
   417     def putbookmarks(self, updatedbookmark):
   411             lock = self.repo.lock()
   423             lock = self.repo.lock()
   412             tr = self.repo.transaction('bookmark')
   424             tr = self.repo.transaction('bookmark')
   413             self.ui.status(_("updating bookmarks\n"))
   425             self.ui.status(_("updating bookmarks\n"))
   414             destmarks = self.repo._bookmarks
   426             destmarks = self.repo._bookmarks
   415             for bookmark in updatedbookmark:
   427             for bookmark in updatedbookmark:
   416                 destmarks[bookmark] = bin(updatedbookmark[bookmark])
   428                 destmarks[bookmark] = nodemod.bin(updatedbookmark[bookmark])
   417             destmarks.recordchange(tr)
   429             destmarks.recordchange(tr)
   418             tr.close()
   430             tr.close()
   419         finally:
   431         finally:
   420             lockmod.release(lock, wlock, tr)
   432             lockmod.release(lock, wlock, tr)
   421 
   433 
   428             raise error.Abort(_('revision %s not found in destination '
   440             raise error.Abort(_('revision %s not found in destination '
   429                                'repository (lookups with clonebranches=true '
   441                                'repository (lookups with clonebranches=true '
   430                                'are not implemented)') % rev)
   442                                'are not implemented)') % rev)
   431         return rev in self.repo
   443         return rev in self.repo
   432 
   444 
   433 class mercurial_source(converter_source):
   445 class mercurial_source(common.converter_source):
   434     def __init__(self, ui, path, revs=None):
   446     def __init__(self, ui, path, revs=None):
   435         converter_source.__init__(self, ui, path, revs)
   447         common.converter_source.__init__(self, ui, path, revs)
   436         self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
   448         self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
   437         self.ignored = set()
   449         self.ignored = set()
   438         self.saverev = ui.configbool('convert', 'hg.saverev', False)
   450         self.saverev = ui.configbool('convert', 'hg.saverev', False)
   439         try:
   451         try:
   440             self.repo = hg.repository(self.ui, path)
   452             self.repo = hg.repository(self.ui, path)
   491 
   503 
   492     def _parents(self, ctx):
   504     def _parents(self, ctx):
   493         return [p for p in ctx.parents() if p and self.keep(p.node())]
   505         return [p for p in ctx.parents() if p and self.keep(p.node())]
   494 
   506 
   495     def getheads(self):
   507     def getheads(self):
   496         return [hex(h) for h in self._heads if self.keep(h)]
   508         return [nodemod.hex(h) for h in self._heads if self.keep(h)]
   497 
   509 
   498     def getfile(self, name, rev):
   510     def getfile(self, name, rev):
   499         try:
   511         try:
   500             fctx = self._changectx(rev)[name]
   512             fctx = self._changectx(rev)[name]
   501             return fctx.data(), fctx.flags()
   513             return fctx.data(), fctx.flags()
   570     def getcommit(self, rev):
   582     def getcommit(self, rev):
   571         ctx = self._changectx(rev)
   583         ctx = self._changectx(rev)
   572         parents = [p.hex() for p in self._parents(ctx)]
   584         parents = [p.hex() for p in self._parents(ctx)]
   573         crev = rev
   585         crev = rev
   574 
   586 
   575         return commit(author=ctx.user(),
   587         return common.commit(author=ctx.user(),
   576                       date=util.datestr(ctx.date(), '%Y-%m-%d %H:%M:%S %1%2'),
   588                              date=util.datestr(ctx.date(),
   577                       desc=ctx.description(), rev=crev, parents=parents,
   589                                                '%Y-%m-%d %H:%M:%S %1%2'),
   578                       branch=ctx.branch(), extra=ctx.extra(),
   590                              desc=ctx.description(),
   579                       sortkey=ctx.rev(), saverev=self.saverev,
   591                              rev=crev,
   580                       phase=ctx.phase())
   592                              parents=parents,
       
   593                              branch=ctx.branch(),
       
   594                              extra=ctx.extra(),
       
   595                              sortkey=ctx.rev(),
       
   596                              saverev=self.saverev,
       
   597                              phase=ctx.phase())
   581 
   598 
   582     def gettags(self):
   599     def gettags(self):
   583         # This will get written to .hgtags, filter non global tags out.
   600         # This will get written to .hgtags, filter non global tags out.
   584         tags = [t for t in self.repo.tagslist()
   601         tags = [t for t in self.repo.tagslist()
   585                 if self.repo.tagtype(t[0]) == 'global']
   602                 if self.repo.tagtype(t[0]) == 'global']
   586         return dict([(name, hex(node)) for name, node in tags
   603         return dict([(name, nodemod.hex(node)) for name, node in tags
   587                      if self.keep(node)])
   604                      if self.keep(node)])
   588 
   605 
   589     def getchangedfiles(self, rev, i):
   606     def getchangedfiles(self, rev, i):
   590         ctx = self._changectx(rev)
   607         ctx = self._changectx(rev)
   591         parents = self._parents(ctx)
   608         parents = self._parents(ctx)
   620     def hasnativeclose(self):
   637     def hasnativeclose(self):
   621         return True
   638         return True
   622 
   639 
   623     def lookuprev(self, rev):
   640     def lookuprev(self, rev):
   624         try:
   641         try:
   625             return hex(self.repo.lookup(rev))
   642             return nodemod.hex(self.repo.lookup(rev))
   626         except (error.RepoError, error.LookupError):
   643         except (error.RepoError, error.LookupError):
   627             return None
   644             return None
   628 
   645 
   629     def getbookmarks(self):
   646     def getbookmarks(self):
   630         return bookmarks.listbookmarks(self.repo)
   647         return bookmarks.listbookmarks(self.repo)