def _parse_line(self, line): if not line: if self.commit is not None and self.state == SVNParser.COMMIT \ or self.state == SVNParser.FILES: self.state = SVNParser.MESSAGE elif self.state == SVNParser.MESSAGE: self.__append_message_line() return # Message if self.state == SVNParser.MESSAGE and self.msg_lines > 0: self.__append_message_line(line) return # Invalid commit. Some svn repos like asterisk have commits like this: # r176840 | (no author) | (no date) | 1 line # without any canged path, so I think we can just ignore them if self.patterns['invalid'].match(line): printdbg("SVN Parser: skipping invalid commit: %s", (line, )) self.state = SVNParser.COMMIT self.commit = None return # Separator if self.patterns['separator'].match(line): if self.commit is None or self.state == SVNParser.COMMIT: return elif self.state == SVNParser.MESSAGE \ or self.state == SVNParser.FILES: # We can go directly from FILES to COMMIT # when there is an empty log message if self.msg_lines > 0: printout("Warning (%d): parsing svn log, missing " + \ "lines in commit message!", (self.n_line,)) self.__convert_commit_actions(self.commit) self.handler.commit(self.commit) self.state = SVNParser.COMMIT self.commit = None self.msg_lines = 0 else: printout("Warning (%d): parsing svn log, unexpected separator", (self.n_line, )) return # Commit match = self.patterns['commit'].match(line) if match and self.state == SVNParser.COMMIT: commit = Commit() commit.revision = match.group(1) commit.committer = Person() commit.committer.name = match.group(2) commit.date = datetime.datetime(int(match.group(3)), int(match.group(4)), int(match.group(5)), int(match.group(6)), int(match.group(7)), int(match.group(8))) self.msg_lines = int(match.group(10)) self.commit = commit self.handler.committer(commit.committer) return elif match and self.state == SVNParser.MESSAGE: # It seems a piece of a log message has been copied as # part of the commit message self.commit.message += line + '\n' return elif match and self.state != SVNParser.COMMIT: printout("Warning (%d): parsing svn log, unexpected line %s", (self.n_line, line)) return # Files if self.state == SVNParser.COMMIT: if self.patterns['paths'].match(line): self.state = SVNParser.FILES else: printout("Warning(%d): parsing svn log, unexpected line %s", (self.n_line, line)) return # File moved/copied/replaced match = self.patterns['file-moved'].match(line) if match: if self.state != SVNParser.FILES: printout("Warning (%d): parsing svn log, unexpected line %s", (self.n_line, line)) return action = Action() action.type = match.group(1) action.f1 = match.group(2) action.f2 = match.group(3) action.rev = match.group(4) action.branch_f1 = self.__guess_branch_from_path(action.f1) action.branch_f2 = self.__guess_branch_from_path(action.f2) self.commit.actions.append(action) self.handler.file(action.f1) return # File match = self.patterns['file'].match(line) if match: if self.state != SVNParser.FILES: printout("Warning (%d): parsing svn log, unexpected line %s", (self.n_line, line)) return path = match.group(2) if path != '/': # path == '/' is probably a properties change in / # not interesting for us, ignoring action = Action() action.type = match.group(1) action.f1 = path action.branch_f1 = self.__guess_branch_from_path(path) self.commit.actions.append(action) self.handler.file(path) return
def _parse_line(self, line): if not line: if self.commit is not None and self.state == SVNParser.COMMIT \ or self.state == SVNParser.FILES: self.state = SVNParser.MESSAGE elif self.state == SVNParser.MESSAGE: self.__append_message_line() return # Message if self.state == SVNParser.MESSAGE and self.msg_lines > 0: self.__append_message_line(line) return # Invalid commit. Some svn repos like asterisk have commits like this: # r176840 | (no author) | (no date) | 1 line # without any canged path, so I think we can just ignore them if self.patterns['invalid'].match(line): printdbg("SVN Parser: skipping invalid commit: %s", (line,)) self.state = SVNParser.COMMIT self.commit = None return # Separator if self.patterns['separator'].match(line): if self.commit is None or self.state == SVNParser.COMMIT: return elif self.state == SVNParser.MESSAGE \ or self.state == SVNParser.FILES: # We can go directly from FILES to COMMIT # when there is an empty log message if self.msg_lines > 0: printout("Warning (%d): parsing svn log, missing " + \ "lines in commit message!", (self.n_line,)) self.__convert_commit_actions(self.commit) self.handler.commit(self.commit) self.state = SVNParser.COMMIT self.commit = None self.msg_lines = 0 else: printout("Warning (%d): parsing svn log, unexpected separator", (self.n_line,)) return # Commit match = self.patterns['commit'].match(line) if match and self.state == SVNParser.COMMIT: commit = Commit() commit.revision = match.group(1) commit.committer = Person() commit.committer.name = match.group(2) commit.commit_date = datetime.datetime(int(match.group(3)), int(match.group(4)), int(match.group(5)), int(match.group(6)), int(match.group(7)), int(match.group(8))) self.msg_lines = int(match.group(10)) self.commit = commit self.handler.committer(commit.committer) return elif match and self.state == SVNParser.MESSAGE: # It seems a piece of a log message has been copied as # part of the commit message self.commit.message += line + '\n' return elif match and self.state != SVNParser.COMMIT: printout("Warning (%d): parsing svn log, unexpected line %s", (self.n_line, line)) return # Files if self.state == SVNParser.COMMIT: if self.patterns['paths'].match(line): self.state = SVNParser.FILES else: printout("Warning(%d): parsing svn log, unexpected line %s", (self.n_line, line)) return # File moved/copied/replaced match = self.patterns['file-moved'].match(line) if match: if self.state != SVNParser.FILES: printout("Warning (%d): parsing svn log, unexpected line %s", (self.n_line, line)) return action = Action() action.type = match.group(1) action.f1 = match.group(2) action.f2 = match.group(3) action.rev = match.group(4) action.branch_f1 = self.__guess_branch_from_path(action.f1) action.branch_f2 = self.__guess_branch_from_path(action.f2) self.commit.actions.append(action) self.handler.file(action.f1) return # File match = self.patterns['file'].match(line) if match: if self.state != SVNParser.FILES: printout("Warning (%d): parsing svn log, unexpected line %s", (self.n_line, line)) return path = match.group(2) if path != '/': # path == '/' is probably a properties change in / # not interesting for us, ignoring action = Action() action.type = match.group(1) action.f1 = path action.branch_f1 = self.__guess_branch_from_path(path) self.commit.actions.append(action) self.handler.file(path) return
def _parse_line(self, line): if not line: if self.commit is None: return if self.rev_separator is not None: self.rev_separator += '\n' elif self.file_separator is not None: self.file_separator += '\n' elif self.commit.message is not None: self.commit.message += '\n' return # Revision Separator if self.patterns['rev-separator'].match(line): # Ignore separators so that we don't # include it in the commit message if self.rev_separator is None: self.rev_separator = line else: self.rev_separator += line + '\n' return # File Separator if self.patterns['file-separator'].match(line): # Ignore separators so that we don't # include it in the commit message if self.file_separator is None: self.file_separator = line else: self.file_separator += line + '\n' return # File match = self.patterns['file'].match(line) if match: self.flush() path = match.group(1) path = path[len(self.root_path):] path = path[:path.rfind(',')] self.file = path self.branches = {} self.tags = {} self.commit = None self.file_separator = None return # Branch match = self.patterns['branch'].match(line) if match: self.branches[match.group(2) + match.group(4)] = match.group(1) return # Tag (Keep this always after Branch pattern) match = self.patterns['tag'].match(line) if match: revision = match.group(2) # We are ignoring 1.1.1.1 revisions, # so in case there's a tag pointing to that # revision we have to redirect it to 1.1 revision if revision == '1.1.1.1': revision = '1.1' self.tags.setdefault(revision, []).append(match.group(1)) return # Revision match = self.patterns['revision'].match(line) if match and self.rev_separator is not None: self._handle_commit() revision = match.group(1) commit = Commit() # composed rev: revision + | + file path # to make sure revision is unique commit.composed_rev = True commit.revision = "%s|%s" % (revision, self.file) commit.tags = self.tags.get(revision, None) self.commit = commit self.rev_separator = None return # Commit info (date, author, etc.) match = self.patterns['info'].match(line) if match and self.commit is not None: commit = self.commit revision = commit.revision.split('|')[0] if revision == '1.1.1.1': self.commit = None return commit.committer = Person() commit.committer.name = match.group(8) self.handler.committer(commit.committer) commit.date = datetime.datetime(int(match.group(1)), int(match.group(2)), int(match.group(3)), int(match.group(4)), int(match.group(5)), int(match.group(6))) if match.group(10) is not None: self.lines[commit.revision] = (int(match.group(11)), int(match.group(12))) else: self.lines[commit.revision] = (0, 0) action = Action() act = match.group(9) if act == 'dead': action.type = 'D' self.file = self.file.replace('/Attic', '') commit.revision = commit.revision.replace('/Attic', '') elif revision == '1.1': action.type = 'A' else: action.type = 'M' action.f1 = self.file # Branch try: last_dot = revision.rfind('.') prefix = revision[:last_dot] branch = self.branches[prefix] if self.file_added_on_branch and \ self.file_added_on_branch == prefix and \ revision[last_dot + 1:] == '1': action.type = 'A' self.file_added_on_branch = None except KeyError: branch = 'trunk' commit.branch = branch commit.actions.append(action) return # Branches match = self.patterns['branches'].match(line) if match: if self.commit is None: return action = self.commit.actions[0] revision = self.commit.revision.split('|')[0] if action.type == 'D' and revision == '1.1': # File added on a branch self.file_added_on_branch = match.group(1) # Discard this commit self.commit = None return # Message. if self.commit is not None: if self.rev_separator is not None: # Previous separator was probably a # false positive self.commit.message += self.rev_separator + '\n' self.rev_separator = None if self.file_separator is not None: # Previous separator was probably a # false positive self.commit.message += self.file_separator + '\n' self.file_separator = None self.commit.message += line + '\n'