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))