Mercurial > hg > hg-fastimport
comparison hgfastimport/hgimport.py @ 74:a4f13dc5e3f7
Support Mercurial 5.6 and Python-3.6
This change the minimum Mercurial supported version 4.1
| author | Roy Marples <roy@marples.name> |
|---|---|
| date | Mon, 18 Jan 2021 18:04:38 +0000 |
| parents | a99e5c6c8e1c |
| children |
comparison
equal
deleted
inserted
replaced
| 73:a99e5c6c8e1c | 74:a4f13dc5e3f7 |
|---|---|
| 111 | 111 |
| 112 def _parse(self): | 112 def _parse(self): |
| 113 if self.parsed: | 113 if self.parsed: |
| 114 return | 114 return |
| 115 for source in self.sources: | 115 for source in self.sources: |
| 116 if source == "-": | 116 if source == b"-": |
| 117 infile = sys.stdin | 117 infile = sys.stdin |
| 118 else: | 118 else: |
| 119 infile = open(source, 'rb') | 119 infile = open(source, 'rb') |
| 120 try: | 120 try: |
| 121 p = parser.ImportParser(infile) | 121 p = parser.ImportParser(infile) |
| 126 self.parsed = True | 126 self.parsed = True |
| 127 | 127 |
| 128 | 128 |
| 129 class HgImportProcessor(processor.ImportProcessor): | 129 class HgImportProcessor(processor.ImportProcessor): |
| 130 | 130 |
| 131 tagprefix = "refs/tags/" | 131 tagprefix = b"refs/tags/" |
| 132 | 132 |
| 133 def __init__(self, ui, repo): | 133 def __init__(self, ui, repo): |
| 134 super(HgImportProcessor, self).__init__() | 134 super(HgImportProcessor, self).__init__() |
| 135 self.ui = ui | 135 self.ui = ui |
| 136 self.repo = repo | 136 self.repo = repo |
| 153 pass | 153 pass |
| 154 | 154 |
| 155 def teardown(self): | 155 def teardown(self): |
| 156 """Cleanup after processing all streams.""" | 156 """Cleanup after processing all streams.""" |
| 157 if self.blobdir and os.path.exists(self.blobdir): | 157 if self.blobdir and os.path.exists(self.blobdir): |
| 158 self.ui.status("Removing blob dir %r ...\n" % self.blobdir) | 158 self.ui.debug(b"Removing blob dir %s ...\n" % self.blobdir) |
| 159 shutil.rmtree(self.blobdir) | 159 shutil.rmtree(self.blobdir) |
| 160 | 160 |
| 161 def progress_handler(self, cmd): | 161 def progress_handler(self, cmd): |
| 162 self.ui.write("Progress: %s\n" % cmd.message) | 162 self.ui.write(b"Progress: %s\n" % cmd.message) |
| 163 | 163 |
| 164 def blob_handler(self, cmd): | 164 def blob_handler(self, cmd): |
| 165 self.writeblob(cmd.id, cmd.data) | 165 self.writeblob(cmd.id, cmd.data) |
| 166 | 166 |
| 167 def _getblobfilename(self, blobid): | 167 def _getblobfilename(self, blobid): |
| 168 if self.blobdir is None: | 168 if self.blobdir is None: |
| 169 raise RuntimeError("no blobs seen, so no blob directory created") | 169 raise RuntimeError("no blobs seen, so no blob directory created") |
| 170 # XXX should escape ":" for windows | 170 # XXX should escape ":" for windows |
| 171 return os.path.join(self.blobdir, "blob-" + blobid) | 171 return os.path.join(self.blobdir, b"blob-" + blobid) |
| 172 | 172 |
| 173 def getblob(self, fileid): | 173 def getblob(self, fileid): |
| 174 (commitid, blobid) = fileid | 174 (commitid, blobid) = fileid |
| 175 f = open(self._getblobfilename(blobid), "rb") | 175 f = open(self._getblobfilename(blobid), "rb") |
| 176 try: | 176 try: |
| 178 finally: | 178 finally: |
| 179 f.close() | 179 f.close() |
| 180 | 180 |
| 181 def writeblob(self, blobid, data): | 181 def writeblob(self, blobid, data): |
| 182 if self.blobdir is None: # no blobs seen yet | 182 if self.blobdir is None: # no blobs seen yet |
| 183 self.blobdir = os.path.join(self.repo.root, ".hg", "blobs") | 183 self.blobdir = os.path.join(self.repo.root, b".hg", b"blobs") |
| 184 os.mkdir(self.blobdir) | 184 os.mkdir(self.blobdir) |
| 185 | 185 |
| 186 fn = self._getblobfilename(blobid) | 186 fn = self._getblobfilename(blobid) |
| 187 blobfile = open(fn, "wb") | 187 blobfile = open(fn, "wb") |
| 188 #self.ui.debug("writing blob %s to %s (%d bytes)\n" | 188 #self.ui.debug("writing blob %s to %s (%d bytes)\n" |
| 190 blobfile.write(data) | 190 blobfile.write(data) |
| 191 blobfile.close() | 191 blobfile.close() |
| 192 | 192 |
| 193 self.numblobs += 1 | 193 self.numblobs += 1 |
| 194 if self.numblobs % 500 == 0: | 194 if self.numblobs % 500 == 0: |
| 195 self.ui.status("%d blobs read\n" % self.numblobs) | 195 self.ui.status(b"%d blobs read\n" % self.numblobs) |
| 196 | 196 |
| 197 def getmode(self, name, fileid): | 197 def getmode(self, name, fileid): |
| 198 (commitid, blobid) = fileid | 198 (commitid, blobid) = fileid |
| 199 return self.filemodes[commitid][name] | 199 return self.filemodes[commitid][name] |
| 200 | 200 |
| 206 """Given a mark reference or a branch name, return the | 206 """Given a mark reference or a branch name, return the |
| 207 appropriate commit object. Return None if commitref is a tag | 207 appropriate commit object. Return None if commitref is a tag |
| 208 or a branch with no commits. Raises KeyError if anything else | 208 or a branch with no commits. Raises KeyError if anything else |
| 209 is out of whack. | 209 is out of whack. |
| 210 """ | 210 """ |
| 211 if commitref.startswith(":"): | 211 if commitref.startswith(b":"): |
| 212 # KeyError here indicates the input stream is broken. | 212 # KeyError here indicates the input stream is broken. |
| 213 return self.commitmap[commitref] | 213 return self.commitmap[commitref] |
| 214 elif commitref.startswith(self.tagprefix): | 214 elif commitref.startswith(self.tagprefix): |
| 215 return None | 215 return None |
| 216 else: | 216 else: |
| 217 branch = self._getbranch(commitref) | 217 branch = self._getbranch(commitref) |
| 218 if branch is None: | 218 if branch is None: |
| 219 raise ValueError("invalid commit ref: %r" % commitref) | 219 raise ValueError(b"invalid commit ref: %s" % commitref) |
| 220 | 220 |
| 221 heads = self.branchmap.get(branch) | 221 heads = self.branchmap.get(branch) |
| 222 if heads is None: | 222 if heads is None: |
| 223 return None | 223 return None |
| 224 else: | 224 else: |
| 229 """Translate a Git head ref to corresponding Mercurial branch | 229 """Translate a Git head ref to corresponding Mercurial branch |
| 230 name. E.g. \"refs/heads/foo\" is translated to \"foo\". | 230 name. E.g. \"refs/heads/foo\" is translated to \"foo\". |
| 231 Special case: \"refs/heads/master\" becomes \"default\". If | 231 Special case: \"refs/heads/master\" becomes \"default\". If |
| 232 'ref' is not a head ref, return None. | 232 'ref' is not a head ref, return None. |
| 233 """ | 233 """ |
| 234 prefix = "refs/heads/" | 234 prefix = b"refs/heads/" |
| 235 if ref.startswith(prefix): | 235 if ref.startswith(prefix): |
| 236 branch = ref[len(prefix):] | 236 branch = ref[len(prefix):] |
| 237 if branch == "master": | 237 if branch == b"master": |
| 238 return "default" | 238 return b"default" |
| 239 else: | 239 else: |
| 240 return branch | 240 return branch |
| 241 else: | 241 else: |
| 242 return None | 242 return None |
| 243 | 243 |
| 245 # XXX this assumes the fixup branch name used by cvs2git. In | 245 # XXX this assumes the fixup branch name used by cvs2git. In |
| 246 # contrast, git-fast-import(1) recommends "TAG_FIXUP" (not under | 246 # contrast, git-fast-import(1) recommends "TAG_FIXUP" (not under |
| 247 # refs/heads), and implies that it can be called whatever the | 247 # refs/heads), and implies that it can be called whatever the |
| 248 # creator of the fastimport dump wants to call it. So the name | 248 # creator of the fastimport dump wants to call it. So the name |
| 249 # of the fixup branch should be configurable! | 249 # of the fixup branch should be configurable! |
| 250 fixup = (cmd.ref == "refs/heads/TAG.FIXUP") | 250 fixup = (cmd.ref == b"refs/heads/TAG.FIXUP") |
| 251 | 251 |
| 252 if cmd.ref.startswith(self.tagprefix) and cmd.mark: | 252 if cmd.ref.startswith(self.tagprefix) and cmd.mark: |
| 253 tag = cmd.ref[len(self.tagprefix):] | 253 tag = cmd.ref[len(self.tagprefix):] |
| 254 self.tags.append((tag, ':' + cmd.mark)) | 254 self.tags.append((tag, b':' + cmd.mark)) |
| 255 | 255 |
| 256 if cmd.from_: | 256 if cmd.from_: |
| 257 first_parent = cmd.from_ | 257 first_parent = cmd.from_ |
| 258 else: | 258 else: |
| 259 first_parent = self._getcommit(cmd.ref) # commit object | 259 first_parent = self._getcommit(cmd.ref) # commit object |
| 274 # time. | 274 # time. |
| 275 first_parent = second_parent | 275 first_parent = second_parent |
| 276 second_parent = None | 276 second_parent = None |
| 277 no_files = True # XXX this is ignored... | 277 no_files = True # XXX this is ignored... |
| 278 | 278 |
| 279 self.ui.debug("commit %s: first_parent = %r, second_parent = %r\n" | 279 bfirst_parent = first_parent or b'' |
| 280 % (cmd, first_parent, second_parent)) | 280 bsecond_parent = second_parent or b'' |
| 281 self.ui.debug(b"commit %s: first_parent = %s, second_parent = %s\n" | |
| 282 % (cmd, bfirst_parent, bsecond_parent)) | |
| 281 assert ((first_parent != second_parent) or | 283 assert ((first_parent != second_parent) or |
| 282 (first_parent is second_parent is None)), \ | 284 (first_parent is second_parent is None)), \ |
| 283 ("commit %s: first_parent == second parent = %r" | 285 (b"commit %s: first_parent == second parent = %s" |
| 284 % (cmd, first_parent)) | 286 % (cmd, bfirst_parent)) |
| 285 | 287 |
| 286 # Figure out the Mercurial branch name. | 288 # Figure out the Mercurial branch name. |
| 287 if fixup and first_parent is not None: | 289 if fixup and first_parent is not None: |
| 288 # If this is a fixup commit, pretend it happened on the same | 290 # If this is a fixup commit, pretend it happened on the same |
| 289 # branch as its first parent. (We don't want a Mercurial | 291 # branch as its first parent. (We don't want a Mercurial |
| 307 # In order to conform to fastimport syntax, cvs2git with no | 309 # In order to conform to fastimport syntax, cvs2git with no |
| 308 # authormap produces author names like "jsmith <jsmith>"; if | 310 # authormap produces author names like "jsmith <jsmith>"; if |
| 309 # we see that, revert to plain old "jsmith". | 311 # we see that, revert to plain old "jsmith". |
| 310 user = userinfo[0] | 312 user = userinfo[0] |
| 311 else: | 313 else: |
| 312 user = "%s <%s>" % (userinfo[0], userinfo[1]) | 314 user = b"%s <%s>" % (userinfo[0], userinfo[1]) |
| 313 | 315 |
| 314 text = cmd.message | 316 text = cmd.message |
| 315 date = self.convert_date(userinfo) | 317 date = self.convert_date(userinfo) |
| 316 | 318 parents = [] |
| 317 parents = filter(None, [first_parent, second_parent]) | 319 if first_parent: |
| 320 parents.append(first_parent) | |
| 321 if second_parent: | |
| 322 parents.append(second_parent) | |
| 323 | |
| 318 commit = common.commit(user, date, text, parents, branch, | 324 commit = common.commit(user, date, text, parents, branch, |
| 319 rev=cmd.id, sortkey=int(cmd.id[1:])) | 325 rev=cmd.id, sortkey=int(cmd.id[1:])) |
| 320 | 326 |
| 321 self.commitmap[cmd.id] = commit | 327 self.commitmap[cmd.id] = commit |
| 322 heads = self.branchmap.get(branch) | 328 heads = self.branchmap.get(branch) |
| 328 heads.remove(first_parent) | 334 heads.remove(first_parent) |
| 329 except ValueError: # first parent not a head: no problem | 335 except ValueError: # first parent not a head: no problem |
| 330 pass | 336 pass |
| 331 heads.append(cmd.id) # at end means this is tipmost | 337 heads.append(cmd.id) # at end means this is tipmost |
| 332 self.branchmap[branch] = heads | 338 self.branchmap[branch] = heads |
| 333 self.ui.debug("processed commit %s\n" % cmd) | 339 self.ui.debug(b"processed commit %s\n" % cmd) |
| 334 | 340 |
| 335 def convert_date(self, c): | 341 def convert_date(self, c): |
| 336 res = (int(c[2]), -int(c[3])) | 342 res = (int(c[2]), -int(c[3])) |
| 337 #print c, res | 343 #print c, res |
| 338 #print type((0, 0)), type(res), len(res), type(res) is type((0, 0)) | 344 #print type((0, 0)), type(res), len(res), type(res) is type((0, 0)) |
| 339 #if type(res) is type((0, 0)) and len(res) == 2: | 345 #if type(res) is type((0, 0)) and len(res) == 2: |
| 340 # print "go for it" | 346 # print "go for it" |
| 341 #return res | 347 #return res |
| 342 return "%d %d" % res | 348 return b"%d %d" % res |
| 343 | 349 |
| 344 def reset_handler(self, cmd): | 350 def reset_handler(self, cmd): |
| 345 branch = self._getbranch(cmd.ref) | 351 branch = self._getbranch(cmd.ref) |
| 346 if branch: | 352 if branch: |
| 347 # The usual case for 'reset': (re)create the named branch. | 353 # The usual case for 'reset': (re)create the named branch. |
| 398 | 404 |
| 399 def modify_handler(self, filecmd): | 405 def modify_handler(self, filecmd): |
| 400 if filecmd.dataref: | 406 if filecmd.dataref: |
| 401 blobid = filecmd.dataref # blobid is the mark of the blob | 407 blobid = filecmd.dataref # blobid is the mark of the blob |
| 402 else: | 408 else: |
| 403 blobid = "%s-inline:%d" % (self.command.id, self.inlinecount) | 409 blobid = b"%s-inline:%d" % (self.command.id, self.inlinecount) |
| 404 assert filecmd.data is not None | 410 assert filecmd.data is not None |
| 405 self.parent.writeblob(blobid, filecmd.data) | 411 self.parent.writeblob(blobid, filecmd.data) |
| 406 self.inlinecount += 1 | 412 self.inlinecount += 1 |
| 407 | 413 |
| 408 fileid = (self.command.id, blobid) | 414 fileid = (self.command.id, blobid) |
| 409 | 415 |
| 410 self.modified.append((filecmd.path, fileid)) | 416 self.modified.append((filecmd.path, fileid)) |
| 411 if stat.S_ISLNK(filecmd.mode): # link | 417 if stat.S_ISLNK(filecmd.mode): # link |
| 412 mode = 'l' | 418 mode = b'l' |
| 413 elif filecmd.mode & 0111: # executable | 419 elif filecmd.mode & 0o111: # executable |
| 414 mode = 'x' | 420 mode = b'x' |
| 415 elif stat.S_ISREG(filecmd.mode): # regular file | 421 elif stat.S_ISREG(filecmd.mode): # regular file |
| 416 mode = '' | 422 mode = b'' |
| 417 else: | 423 else: |
| 418 raise RuntimeError("mode %r unsupported" % filecmd.mode) | 424 raise RuntimeError(b"mode %s unsupported" % filecmd.mode) |
| 419 | 425 |
| 420 self.mode[filecmd.path] = mode | 426 self.mode[filecmd.path] = mode |
| 421 | 427 |
| 422 def delete_handler(self, filecmd): | 428 def delete_handler(self, filecmd): |
| 423 self.modified.append((filecmd.path, None)) | 429 self.modified.append((filecmd.path, None)) |
