hgext/convert/cvs.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43085 eef9a2d67051
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    37 
    37 
    38 class convert_cvs(converter_source):
    38 class convert_cvs(converter_source):
    39     def __init__(self, ui, repotype, path, revs=None):
    39     def __init__(self, ui, repotype, path, revs=None):
    40         super(convert_cvs, self).__init__(ui, repotype, path, revs=revs)
    40         super(convert_cvs, self).__init__(ui, repotype, path, revs=revs)
    41 
    41 
    42         cvs = os.path.join(path, "CVS")
    42         cvs = os.path.join(path, b"CVS")
    43         if not os.path.exists(cvs):
    43         if not os.path.exists(cvs):
    44             raise NoRepo(_("%s does not look like a CVS checkout") % path)
    44             raise NoRepo(_(b"%s does not look like a CVS checkout") % path)
    45 
    45 
    46         checktool('cvs')
    46         checktool(b'cvs')
    47 
    47 
    48         self.changeset = None
    48         self.changeset = None
    49         self.files = {}
    49         self.files = {}
    50         self.tags = {}
    50         self.tags = {}
    51         self.lastbranch = {}
    51         self.lastbranch = {}
    52         self.socket = None
    52         self.socket = None
    53         self.cvsroot = open(os.path.join(cvs, "Root"), 'rb').read()[:-1]
    53         self.cvsroot = open(os.path.join(cvs, b"Root"), b'rb').read()[:-1]
    54         self.cvsrepo = open(os.path.join(cvs, "Repository"), 'rb').read()[:-1]
    54         self.cvsrepo = open(os.path.join(cvs, b"Repository"), b'rb').read()[:-1]
    55         self.encoding = encoding.encoding
    55         self.encoding = encoding.encoding
    56 
    56 
    57         self._connect()
    57         self._connect()
    58 
    58 
    59     def _parse(self):
    59     def _parse(self):
    63 
    63 
    64         maxrev = 0
    64         maxrev = 0
    65         if self.revs:
    65         if self.revs:
    66             if len(self.revs) > 1:
    66             if len(self.revs) > 1:
    67                 raise error.Abort(
    67                 raise error.Abort(
    68                     _('cvs source does not support specifying ' 'multiple revs')
    68                     _(
       
    69                         b'cvs source does not support specifying '
       
    70                         b'multiple revs'
       
    71                     )
    69                 )
    72                 )
    70             # TODO: handle tags
    73             # TODO: handle tags
    71             try:
    74             try:
    72                 # patchset number?
    75                 # patchset number?
    73                 maxrev = int(self.revs[0])
    76                 maxrev = int(self.revs[0])
    74             except ValueError:
    77             except ValueError:
    75                 raise error.Abort(
    78                 raise error.Abort(
    76                     _('revision %s is not a patchset number') % self.revs[0]
    79                     _(b'revision %s is not a patchset number') % self.revs[0]
    77                 )
    80                 )
    78 
    81 
    79         d = encoding.getcwd()
    82         d = encoding.getcwd()
    80         try:
    83         try:
    81             os.chdir(self.path)
    84             os.chdir(self.path)
    82 
    85 
    83             cache = 'update'
    86             cache = b'update'
    84             if not self.ui.configbool('convert', 'cvsps.cache'):
    87             if not self.ui.configbool(b'convert', b'cvsps.cache'):
    85                 cache = None
    88                 cache = None
    86             db = cvsps.createlog(self.ui, cache=cache)
    89             db = cvsps.createlog(self.ui, cache=cache)
    87             db = cvsps.createchangeset(
    90             db = cvsps.createchangeset(
    88                 self.ui,
    91                 self.ui,
    89                 db,
    92                 db,
    90                 fuzz=int(self.ui.config('convert', 'cvsps.fuzz')),
    93                 fuzz=int(self.ui.config(b'convert', b'cvsps.fuzz')),
    91                 mergeto=self.ui.config('convert', 'cvsps.mergeto'),
    94                 mergeto=self.ui.config(b'convert', b'cvsps.mergeto'),
    92                 mergefrom=self.ui.config('convert', 'cvsps.mergefrom'),
    95                 mergefrom=self.ui.config(b'convert', b'cvsps.mergefrom'),
    93             )
    96             )
    94 
    97 
    95             for cs in db:
    98             for cs in db:
    96                 if maxrev and cs.id > maxrev:
    99                 if maxrev and cs.id > maxrev:
    97                     break
   100                     break
    98                 id = b"%d" % cs.id
   101                 id = b"%d" % cs.id
    99                 cs.author = self.recode(cs.author)
   102                 cs.author = self.recode(cs.author)
   100                 self.lastbranch[cs.branch] = id
   103                 self.lastbranch[cs.branch] = id
   101                 cs.comment = self.recode(cs.comment)
   104                 cs.comment = self.recode(cs.comment)
   102                 if self.ui.configbool('convert', 'localtimezone'):
   105                 if self.ui.configbool(b'convert', b'localtimezone'):
   103                     cs.date = makedatetimestamp(cs.date[0])
   106                     cs.date = makedatetimestamp(cs.date[0])
   104                 date = dateutil.datestr(cs.date, '%Y-%m-%d %H:%M:%S %1%2')
   107                 date = dateutil.datestr(cs.date, b'%Y-%m-%d %H:%M:%S %1%2')
   105                 self.tags.update(dict.fromkeys(cs.tags, id))
   108                 self.tags.update(dict.fromkeys(cs.tags, id))
   106 
   109 
   107                 files = {}
   110                 files = {}
   108                 for f in cs.entries:
   111                 for f in cs.entries:
   109                     files[f.file] = "%s%s" % (
   112                     files[f.file] = b"%s%s" % (
   110                         '.'.join([(b"%d" % x) for x in f.revision]),
   113                         b'.'.join([(b"%d" % x) for x in f.revision]),
   111                         ['', '(DEAD)'][f.dead],
   114                         [b'', b'(DEAD)'][f.dead],
   112                     )
   115                     )
   113 
   116 
   114                 # add current commit to set
   117                 # add current commit to set
   115                 c = commit(
   118                 c = commit(
   116                     author=cs.author,
   119                     author=cs.author,
   117                     date=date,
   120                     date=date,
   118                     parents=[(b"%d" % p.id) for p in cs.parents],
   121                     parents=[(b"%d" % p.id) for p in cs.parents],
   119                     desc=cs.comment,
   122                     desc=cs.comment,
   120                     branch=cs.branch or '',
   123                     branch=cs.branch or b'',
   121                 )
   124                 )
   122                 self.changeset[id] = c
   125                 self.changeset[id] = c
   123                 self.files[id] = files
   126                 self.files[id] = files
   124 
   127 
   125             self.heads = self.lastbranch.values()
   128             self.heads = self.lastbranch.values()
   128 
   131 
   129     def _connect(self):
   132     def _connect(self):
   130         root = self.cvsroot
   133         root = self.cvsroot
   131         conntype = None
   134         conntype = None
   132         user, host = None, None
   135         user, host = None, None
   133         cmd = ['cvs', 'server']
   136         cmd = [b'cvs', b'server']
   134 
   137 
   135         self.ui.status(_("connecting to %s\n") % root)
   138         self.ui.status(_(b"connecting to %s\n") % root)
   136 
   139 
   137         if root.startswith(":pserver:"):
   140         if root.startswith(b":pserver:"):
   138             root = root[9:]
   141             root = root[9:]
   139             m = re.match(
   142             m = re.match(
   140                 r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)', root
   143                 r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)', root
   141             )
   144             )
   142             if m:
   145             if m:
   143                 conntype = "pserver"
   146                 conntype = b"pserver"
   144                 user, passw, serv, port, root = m.groups()
   147                 user, passw, serv, port, root = m.groups()
   145                 if not user:
   148                 if not user:
   146                     user = "anonymous"
   149                     user = b"anonymous"
   147                 if not port:
   150                 if not port:
   148                     port = 2401
   151                     port = 2401
   149                 else:
   152                 else:
   150                     port = int(port)
   153                     port = int(port)
   151                 format0 = ":pserver:%s@%s:%s" % (user, serv, root)
   154                 format0 = b":pserver:%s@%s:%s" % (user, serv, root)
   152                 format1 = ":pserver:%s@%s:%d%s" % (user, serv, port, root)
   155                 format1 = b":pserver:%s@%s:%d%s" % (user, serv, port, root)
   153 
   156 
   154                 if not passw:
   157                 if not passw:
   155                     passw = "A"
   158                     passw = b"A"
   156                     cvspass = os.path.expanduser("~/.cvspass")
   159                     cvspass = os.path.expanduser(b"~/.cvspass")
   157                     try:
   160                     try:
   158                         pf = open(cvspass, 'rb')
   161                         pf = open(cvspass, b'rb')
   159                         for line in pf.read().splitlines():
   162                         for line in pf.read().splitlines():
   160                             part1, part2 = line.split(' ', 1)
   163                             part1, part2 = line.split(b' ', 1)
   161                             # /1 :pserver:user@example.com:2401/cvsroot/foo
   164                             # /1 :pserver:user@example.com:2401/cvsroot/foo
   162                             # Ah<Z
   165                             # Ah<Z
   163                             if part1 == '/1':
   166                             if part1 == b'/1':
   164                                 part1, part2 = part2.split(' ', 1)
   167                                 part1, part2 = part2.split(b' ', 1)
   165                                 format = format1
   168                                 format = format1
   166                             # :pserver:user@example.com:/cvsroot/foo Ah<Z
   169                             # :pserver:user@example.com:/cvsroot/foo Ah<Z
   167                             else:
   170                             else:
   168                                 format = format0
   171                                 format = format0
   169                             if part1 == format:
   172                             if part1 == format:
   177                             raise
   180                             raise
   178 
   181 
   179                 sck = socket.socket()
   182                 sck = socket.socket()
   180                 sck.connect((serv, port))
   183                 sck.connect((serv, port))
   181                 sck.send(
   184                 sck.send(
   182                     "\n".join(
   185                     b"\n".join(
   183                         [
   186                         [
   184                             "BEGIN AUTH REQUEST",
   187                             b"BEGIN AUTH REQUEST",
   185                             root,
   188                             root,
   186                             user,
   189                             user,
   187                             passw,
   190                             passw,
   188                             "END AUTH REQUEST",
   191                             b"END AUTH REQUEST",
   189                             "",
   192                             b"",
   190                         ]
   193                         ]
   191                     )
   194                     )
   192                 )
   195                 )
   193                 if sck.recv(128) != "I LOVE YOU\n":
   196                 if sck.recv(128) != b"I LOVE YOU\n":
   194                     raise error.Abort(_("CVS pserver authentication failed"))
   197                     raise error.Abort(_(b"CVS pserver authentication failed"))
   195 
   198 
   196                 self.writep = self.readp = sck.makefile('r+')
   199                 self.writep = self.readp = sck.makefile(b'r+')
   197 
   200 
   198         if not conntype and root.startswith(":local:"):
   201         if not conntype and root.startswith(b":local:"):
   199             conntype = "local"
   202             conntype = b"local"
   200             root = root[7:]
   203             root = root[7:]
   201 
   204 
   202         if not conntype:
   205         if not conntype:
   203             # :ext:user@host/home/user/path/to/cvsroot
   206             # :ext:user@host/home/user/path/to/cvsroot
   204             if root.startswith(":ext:"):
   207             if root.startswith(b":ext:"):
   205                 root = root[5:]
   208                 root = root[5:]
   206             m = re.match(br'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
   209             m = re.match(br'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
   207             # Do not take Windows path "c:\foo\bar" for a connection strings
   210             # Do not take Windows path "c:\foo\bar" for a connection strings
   208             if os.path.isdir(root) or not m:
   211             if os.path.isdir(root) or not m:
   209                 conntype = "local"
   212                 conntype = b"local"
   210             else:
   213             else:
   211                 conntype = "rsh"
   214                 conntype = b"rsh"
   212                 user, host, root = m.group(1), m.group(2), m.group(3)
   215                 user, host, root = m.group(1), m.group(2), m.group(3)
   213 
   216 
   214         if conntype != "pserver":
   217         if conntype != b"pserver":
   215             if conntype == "rsh":
   218             if conntype == b"rsh":
   216                 rsh = encoding.environ.get("CVS_RSH") or "ssh"
   219                 rsh = encoding.environ.get(b"CVS_RSH") or b"ssh"
   217                 if user:
   220                 if user:
   218                     cmd = [rsh, '-l', user, host] + cmd
   221                     cmd = [rsh, b'-l', user, host] + cmd
   219                 else:
   222                 else:
   220                     cmd = [rsh, host] + cmd
   223                     cmd = [rsh, host] + cmd
   221 
   224 
   222             # popen2 does not support argument lists under Windows
   225             # popen2 does not support argument lists under Windows
   223             cmd = [procutil.shellquote(arg) for arg in cmd]
   226             cmd = [procutil.shellquote(arg) for arg in cmd]
   224             cmd = procutil.quotecommand(' '.join(cmd))
   227             cmd = procutil.quotecommand(b' '.join(cmd))
   225             self.writep, self.readp = procutil.popen2(cmd)
   228             self.writep, self.readp = procutil.popen2(cmd)
   226 
   229 
   227         self.realroot = root
   230         self.realroot = root
   228 
   231 
   229         self.writep.write("Root %s\n" % root)
   232         self.writep.write(b"Root %s\n" % root)
   230         self.writep.write(
   233         self.writep.write(
   231             "Valid-responses ok error Valid-requests Mode"
   234             b"Valid-responses ok error Valid-requests Mode"
   232             " M Mbinary E Checked-in Created Updated"
   235             b" M Mbinary E Checked-in Created Updated"
   233             " Merged Removed\n"
   236             b" Merged Removed\n"
   234         )
   237         )
   235         self.writep.write("valid-requests\n")
   238         self.writep.write(b"valid-requests\n")
   236         self.writep.flush()
   239         self.writep.flush()
   237         r = self.readp.readline()
   240         r = self.readp.readline()
   238         if not r.startswith("Valid-requests"):
   241         if not r.startswith(b"Valid-requests"):
   239             raise error.Abort(
   242             raise error.Abort(
   240                 _(
   243                 _(
   241                     'unexpected response from CVS server '
   244                     b'unexpected response from CVS server '
   242                     '(expected "Valid-requests", but got %r)'
   245                     b'(expected "Valid-requests", but got %r)'
   243                 )
   246                 )
   244                 % r
   247                 % r
   245             )
   248             )
   246         if "UseUnchanged" in r:
   249         if b"UseUnchanged" in r:
   247             self.writep.write("UseUnchanged\n")
   250             self.writep.write(b"UseUnchanged\n")
   248             self.writep.flush()
   251             self.writep.flush()
   249             self.readp.readline()
   252             self.readp.readline()
   250 
   253 
   251     def getheads(self):
   254     def getheads(self):
   252         self._parse()
   255         self._parse()
   260             output = stringio()
   263             output = stringio()
   261             while count > 0:
   264             while count > 0:
   262                 data = fp.read(min(count, chunksize))
   265                 data = fp.read(min(count, chunksize))
   263                 if not data:
   266                 if not data:
   264                     raise error.Abort(
   267                     raise error.Abort(
   265                         _("%d bytes missing from remote file") % count
   268                         _(b"%d bytes missing from remote file") % count
   266                     )
   269                     )
   267                 count -= len(data)
   270                 count -= len(data)
   268                 output.write(data)
   271                 output.write(data)
   269             return output.getvalue()
   272             return output.getvalue()
   270 
   273 
   271         self._parse()
   274         self._parse()
   272         if rev.endswith("(DEAD)"):
   275         if rev.endswith(b"(DEAD)"):
   273             return None, None
   276             return None, None
   274 
   277 
   275         args = ("-N -P -kk -r %s --" % rev).split()
   278         args = (b"-N -P -kk -r %s --" % rev).split()
   276         args.append(self.cvsrepo + '/' + name)
   279         args.append(self.cvsrepo + b'/' + name)
   277         for x in args:
   280         for x in args:
   278             self.writep.write("Argument %s\n" % x)
   281             self.writep.write(b"Argument %s\n" % x)
   279         self.writep.write("Directory .\n%s\nco\n" % self.realroot)
   282         self.writep.write(b"Directory .\n%s\nco\n" % self.realroot)
   280         self.writep.flush()
   283         self.writep.flush()
   281 
   284 
   282         data = ""
   285         data = b""
   283         mode = None
   286         mode = None
   284         while True:
   287         while True:
   285             line = self.readp.readline()
   288             line = self.readp.readline()
   286             if line.startswith("Created ") or line.startswith("Updated "):
   289             if line.startswith(b"Created ") or line.startswith(b"Updated "):
   287                 self.readp.readline()  # path
   290                 self.readp.readline()  # path
   288                 self.readp.readline()  # entries
   291                 self.readp.readline()  # entries
   289                 mode = self.readp.readline()[:-1]
   292                 mode = self.readp.readline()[:-1]
   290                 count = int(self.readp.readline()[:-1])
   293                 count = int(self.readp.readline()[:-1])
   291                 data = chunkedread(self.readp, count)
   294                 data = chunkedread(self.readp, count)
   292             elif line.startswith(" "):
   295             elif line.startswith(b" "):
   293                 data += line[1:]
   296                 data += line[1:]
   294             elif line.startswith("M "):
   297             elif line.startswith(b"M "):
   295                 pass
   298                 pass
   296             elif line.startswith("Mbinary "):
   299             elif line.startswith(b"Mbinary "):
   297                 count = int(self.readp.readline()[:-1])
   300                 count = int(self.readp.readline()[:-1])
   298                 data = chunkedread(self.readp, count)
   301                 data = chunkedread(self.readp, count)
   299             else:
   302             else:
   300                 if line == "ok\n":
   303                 if line == b"ok\n":
   301                     if mode is None:
   304                     if mode is None:
   302                         raise error.Abort(_('malformed response from CVS'))
   305                         raise error.Abort(_(b'malformed response from CVS'))
   303                     return (data, "x" in mode and "x" or "")
   306                     return (data, b"x" in mode and b"x" or b"")
   304                 elif line.startswith("E "):
   307                 elif line.startswith(b"E "):
   305                     self.ui.warn(_("cvs server: %s\n") % line[2:])
   308                     self.ui.warn(_(b"cvs server: %s\n") % line[2:])
   306                 elif line.startswith("Remove"):
   309                 elif line.startswith(b"Remove"):
   307                     self.readp.readline()
   310                     self.readp.readline()
   308                 else:
   311                 else:
   309                     raise error.Abort(_("unknown CVS response: %s") % line)
   312                     raise error.Abort(_(b"unknown CVS response: %s") % line)
   310 
   313 
   311     def getchanges(self, rev, full):
   314     def getchanges(self, rev, full):
   312         if full:
   315         if full:
   313             raise error.Abort(_("convert from cvs does not support --full"))
   316             raise error.Abort(_(b"convert from cvs does not support --full"))
   314         self._parse()
   317         self._parse()
   315         return sorted(self.files[rev].iteritems()), {}, set()
   318         return sorted(self.files[rev].iteritems()), {}, set()
   316 
   319 
   317     def getcommit(self, rev):
   320     def getcommit(self, rev):
   318         self._parse()
   321         self._parse()