Changeset 886 in tailor
- Timestamp:
- 10/05/05 10:40:19 (8 years ago)
- Hash name:
- 20051005084019-d21f0-26289ac884f2cb7cb8dc68d8e63c4aff569bbe50
- File:
-
- 1 edited
-
vcpx/cvs.py (modified) (13 diffs)
Legend:
- Unmodified
- Added
- Removed
-
vcpx/cvs.py
r885 r886 46 46 return cmp(r1, r2) 47 47 48 49 def changesets_from_cvslog(log, module): 48 def cvs_revs_same_branch(rev1, rev2): 49 """True iff the two normalized revision numbers are on the same branch.""" 50 51 # Odd-length revisions are branch numbers, even-length ones 52 # are revision numbers. 53 54 # Two branch numbers can't be on the same branch unless they're identical. 55 if len(rev1) % 2 and len(rev2) % 2: 56 return rev1 == rev2 57 58 # Two revision numbers are on the same branch if they 59 # agree up to the last number. 60 if len(rev1) % 2 == 0 and len(rev2) % 2 == 0: 61 return rev1[0:-1] == rev2[0:-1] 62 63 # One branch number, one revision number. If by removing the last number 64 # of one you get the other, then they're on the same branch, regardless of 65 # which is longer. E.g. revision 1.2 is the root of the branch 1.2.2; 66 # revision 1.2.2.2 is directly on the branch 1.2.2. 67 if rev1[0:-1] == rev2: 68 return True 69 70 if rev2[0:-1] == rev1: 71 return True 72 73 return False 74 75 def is_branch(rev): 76 """True iff the given (normalized) revision number is a branch number""" 77 if len(rev) % 2: 78 return True 79 80 def rev2branch(rev): 81 """Return the branch on which this (normalized) revision lies""" 82 assert not is_branch(rev) 83 return rev[0:-1] 84 85 86 def changesets_from_cvslog(log, module, branch, entries, since): 50 87 """ 51 88 Parse CVS log. … … 54 91 from datetime import timedelta 55 92 56 collected = ChangeSetCollector(log, module )93 collected = ChangeSetCollector(log, module, branch, entries, since) 57 94 collapsed = [] 58 95 … … 69 106 if (last and last.author == cs.author and last.log == cs.log and 70 107 abs(lastts - cs.date) < threshold and 108 not last.tags and 71 109 not [e for e in cs.entries 72 110 if e.name in [n.name for n in last.entries … … 125 163 inter_sep = '=' * 77 + '\n' 126 164 127 def __init__(self, log, module ):165 def __init__(self, log, module, branch, entries, since): 128 166 """ 129 167 Initialize a ChangeSetCollector instance. … … 141 179 """The CVS module name.""" 142 180 143 self.__parseCvsLog( )181 self.__parseCvsLog(entries, since, branch) 144 182 145 183 def __iter__(self): … … 262 300 return (date, author, changelog, entry, rev, state, newentry) 263 301 264 def __parseCvsLog(self ):302 def __parseCvsLog(self, entries, since, branch): 265 303 """Parse a complete CVS log.""" 266 304 305 from changes import Changeset 267 306 from os.path import split, join 268 307 import sre 308 from datetime import timedelta 309 from time import strptime 310 from datetime import datetime 269 311 270 312 revcount_regex = sre.compile('\\bselected revisions:\\s*(\\d+)\\b') … … 272 314 self.__currentdir = None 273 315 316 file2rev2tags = {} 317 tagcounts = {} 318 branchnum = None 274 319 while 1: 275 320 l = self.__readline() … … 284 329 285 330 entry = join(self.__currentdir, split(l[10:-1])[1][:-2]) 331 while l and not l.startswith('head: '): 332 l = self.__readline() 333 assert l, "Missed 'head:' line" 334 if branch is None: 335 branchnum = normalize_cvs_rev(l[6:-1]) 336 branchnum = rev2branch(branchnum) 337 338 while l and not l == 'symbolic names:\n': 339 l = self.__readline() 340 341 assert l, "Missed 'symbolic names:' line" 342 343 l = self.__readline() 344 rev2tags = {} 345 while l.startswith('\t'): 346 tag,revision = l[1:-1].split(': ') 347 tagcounts[tag] = tagcounts.get(tag,0) + 1 348 revision = normalize_cvs_rev(revision) 349 rev2tags.setdefault(revision,[]).append(tag) 350 if tag == branch: 351 branchnum = revision 352 353 l = self.__readline() 354 355 # branchnum may still be None, if this file doesn't exist 356 # on the requested branch. 357 358 # filter out branch tags, and tags for revisions that are 359 # on other branches. 360 for revision in rev2tags.keys(): 361 if is_branch(revision) or \ 362 not branchnum or \ 363 not cvs_revs_same_branch(revision,branchnum): 364 del rev2tags[revision] 365 366 file2rev2tags[entry] = rev2tags 286 367 287 368 expected_revisions = None 288 while 1: 289 l = self.__readline() 290 if l in (self.inter_sep, self.intra_sep): 291 break 292 369 while l not in (self.inter_sep, self.intra_sep): 293 370 m = revcount_regex.search(l) 294 371 if m is not None: 295 372 expected_revisions = int(m.group(1)) 373 l = self.__readline() 296 374 297 375 last = previous = None … … 328 406 ( expected_revisions, found_revisions ) 329 407 330 # end of __parseCvsLog() 408 # Determine the current revision of each live 409 # (i.e. non-deleted) entry. 410 state = dict(entries.getFileVersions()) 411 412 # before stepping through changes, see if the initial state is 413 # taggable. If so, add an initial changeset that does nothing 414 # but tag, using the date of the last revision tailor imported 415 # on its previous run. There's no way to tell when the tag 416 # was really applied, so we don't know if it was seen on the 417 # last run or not. Before applying the tag on the other end, 418 # we'll have to check whether it's already been applied. 419 tags = self.__getApplicableTags(state, file2rev2tags, tagcounts) 420 if tags: 421 if since == None: 422 # I think this could only happen if the CVS repo was 423 # tagged before any files were added to it. We could 424 # probably get a better date by looking at when the 425 # files were added, but who cares. 426 timestamp = datetime(1900,1,1) 427 else: 428 # "since" is a revision name read from the state file, 429 # which means it was originally generated by 430 # getGlobalCVSRevision. The format string "%Y-%m-%d 431 # %H:%M:%S" matches the format generated by the implicit 432 # call to timestamp.__str__() in getGlobalCVSRevision. 433 y,m,d,hh,mm,ss,d1,d2,d3 = strptime(since, "%Y-%m-%d %H:%M:%S") 434 timestamp = datetime(y,m,d,hh,mm,ss) 435 author = "unknown tagger" 436 changelog = "tag %s %s" % (timestamp, tags) 437 key = (timestamp, author, changelog) 438 self.changesets[key] = Changeset(_getGlobalCVSRevision(timestamp, 439 author), 440 timestamp,author,changelog, 441 tags=tags) 442 443 # Walk through the changesets, identifying ones that result in 444 # a state with a tag. Add that info to the changeset. 445 for cs in self.__iter__(): 446 self.__updateState(state, cs) 447 cs.tags = self.__getApplicableTags(state, file2rev2tags, tagcounts) 448 449 def __getApplicableTags(self,state,taginfo,expectedcounts): 450 # state: a dictionary mapping filename->revision 451 # 452 # taginfo: a two-level dictionary mapping 453 # tagname->revision->list of tags. 454 # 455 # expectedcounts: a dictionary mapping tagname->number of 456 # files tagged with that name. 457 observedcounts = {} 458 possibletags = [] 459 for filename, revno in state.iteritems(): 460 filetags = taginfo[filename].get(revno,[]) 461 if len(possibletags) == 0: 462 # first iteration of loop 463 possibletags = filetags 464 465 # Intersection of possibletags and filetags. I'm 466 # avoiding using python sets to preserve python 2.3 467 # compatibility. 468 possibletags = [t for t in possibletags if t in filetags] 469 for t in filetags: 470 observedcounts[t] = observedcounts.get(t,0) + 1 471 472 if len(possibletags) == 0: 473 break 474 475 # All currently existing files carry the tags in possibletags. 476 # But that doesn't mean that the tags correspond to this 477 # state--we might need to create additional files before 478 # tagging. 479 possibletags = [t for t in possibletags if 480 observedcounts[t] == expectedcounts[t]] 481 482 return possibletags 483 484 def __updateState(self,state, changeset): 485 for e in changeset.entries: 486 if e.action_kind in (e.ADDED, e.UPDATED): 487 state[e.name] = normalize_cvs_rev(e.new_revision) 488 elif e.action_kind == e.DELETED: 489 if state.has_key(e.name): 490 del state[e.name] 491 elif e.action_kind == e.RENAMED: 492 if state.has_key(e.name): 493 del state[e.old_name] 494 state[e.name] = normalize_cvs_rev(e.new_revision) 331 495 332 496 … … 355 519 self.repository.name)) 356 520 357 branch = ''521 branch = None 358 522 fname = join(self.basedir, 'CVS', 'Tag') 359 523 if exists(fname): … … 362 526 branch=tag[1:-1] 363 527 364 cmd = self.repository.command("-f", "-d", "%(repository)s", "rlog", 365 "-N") 528 cmd = self.repository.command("-f", "-d", "%(repository)s", "rlog") 366 529 367 530 if not sincerev or sincerev in ("INITIAL", "HEAD"): … … 423 586 424 587 log = reader(log) 425 return changesets_from_cvslog(log, self.repository.module) 588 return changesets_from_cvslog(log, self.repository.module, branch, 589 CvsEntries(self.repository.rootdir), 590 since) 426 591 427 592 def _checkoutUpstreamRevision(self, revision): … … 560 725 561 726 return latest 727 728 def getFileVersions(self, prefix=''): 729 """Return a set of (entry name, version number) pairs.""" 730 731 from os.path import join 732 733 pairs = [(prefix+e.filename, normalize_cvs_rev(e.cvs_version)) 734 for e in self.files.values()] 735 736 for dirname, entries in self.directories.iteritems(): 737 pairs += [(prefix+filename, version) 738 for filename, version in 739 entries.getFileVersions("%s/" % dirname)] 740 741 return pairs
Note: See TracChangeset
for help on using the changeset viewer.
