Example #1
0
    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
Example #2
0
    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
Example #3
0
    def _parse_line (self, line):
        if line is None or line == '':
            return

        # Ignore
        for patt in self.patterns['ignore']:
            if patt.match (line):
                return

        # Commit
        match = self.patterns['commit'].match (line)
        if match:
            if self.commit is not None and self.branch.is_remote ():
                if self.branch.tail.svn_tag is None: # Skip commits on svn tags
                    self.handler.commit (self.branch.tail.commit)

            self.commit = Commit ()
            self.commit.revision = match.group (1)

            parents = match.group (3)
            if parents:
                parents = parents.split ()
            git_commit = self.GitCommit (self.commit, parents)

            decorate = match.group (5)
            branch = None
            if decorate:
                # Remote branch
                m = re.search (self.patterns['branch'], decorate)
                if m:
                    branch = self.GitBranch (self.GitBranch.REMOTE, m.group (1), git_commit)
                    printdbg ("Branch '%s' head at acommit %s", (branch.name, self.commit.revision))
                else:
                    # Local Branch
                    m = re.search (self.patterns['local-branch'], decorate)
                    if m:
                        branch = self.GitBranch (self.GitBranch.LOCAL, m.group (1), git_commit)
                        printdbg ("Commit %s on local branch '%s'", (self.commit.revision, branch.name))
                        # If local branch was merged we just ignore this decoration
                        if self.branch and self.branch.is_my_parent (git_commit):
                            printdbg ("Local branch '%s' was merged", (branch.name,))
                            branch = None
                    else:
                        # Stash
                        m = re.search (self.patterns['stash'], decorate)
                        if m:
                            branch = self.GitBranch (self.GitBranch.STASH, "stash", git_commit)
                            printdbg ("Commit %s on stash", (self.commit.revision,))
                # Tag
                m = re.search (self.patterns['tag'], decorate)
                if m:
                    self.commit.tags = [m.group (1)]
                    printdbg ("Commit %s tagged as '%s'", (self.commit.revision, self.commit.tags[0]))

            if branch is not None and self.branch is not None:
                # Detect empty branches. Ideally, the head of a branch
                # can't have children. When this happens is because the
                # branch is empty, so we just ignore such branch
                if self.branch.is_my_parent (git_commit):
                    printout ("Warning: Detected empty branch '%s', it'll be ignored", (branch.name,))
                    branch = None

            if len (self.branches) >= 2:
                # If current commit is the start point of a new branch
                # we have to look at all the current branches since
                # we haven't inserted the new branch yet.
                # If not, look at all other branches excluding the current one
                for i, b in enumerate (self.branches):
                    if i == 0 and branch is None:
                        continue

                    if b.is_my_parent (git_commit):
                        # We assume current branch is always the last one
                        # AFAIK there's no way to make sure this is right
                        printdbg ("Start point of branch '%s' at commit %s", (self.branches[0].name, self.commit.revision))
                        self.branches.pop (0)
                        self.branch = b

            if self.branch and self.branch.tail.svn_tag is not None and self.branch.is_my_parent (git_commit):
                # There's a pending tag in previous commit
                pending_tag = self.branch.tail.svn_tag
                printdbg ("Move pending tag '%s' from previous commit %s to current %s", (pending_tag,
                                                                                          self.branch.tail.commit.revision,
                                                                                          self.commit.revision))
                if self.commit.tags and pending_tag not in self.commit.tags:
                    self.commit.tags.append (pending_tag)
                else:
                    self.commit.tags = [pending_tag]
                self.branch.tail.svn_tag = None

            if branch is not None:
                self.branch = branch

                # Insert master always at the end
                if branch.is_remote () and branch.name == 'master':
                    self.branches.append (self.branch)
                else:
                    self.branches.insert (0, self.branch)
            else:
                self.branch.set_tail (git_commit)

            return

        # Committer
        match = self.patterns['committer'].match (line)
        if match:
            self.commit.committer = Person ()
            self.commit.committer.name = match.group (1)
            self.commit.committer.email = match.group (2)
            self.handler.committer (self.commit.committer)

            return

        # Author
        match = self.patterns['author'].match (line)
        if match:
            self.commit.author = Person ()
            self.commit.author.name = match.group (1)
            self.commit.author.email = match.group (2)
            self.handler.author (self.commit.author)

            return

        # Date
        match = self.patterns['date'].match (line)
        if match:
            self.commit.date = datetime.datetime (* (time.strptime (match.group (1).strip (" "), "%a %b %d %H:%M:%S %Y")[0:6]))
            # datetime.datetime.strptime not supported by Python2.4
            #self.commit.date = datetime.datetime.strptime (match.group (1).strip (" "), "%a %b %d %H:%M:%S %Y")
            
            return

        # File
        match = self.patterns['file'].match (line)
        if match:
            action = Action ()
            action.type = match.group (1)
            action.f1 = match.group (2)

            self.commit.actions.append (action)
            self.handler.file (action.f1)
        
            return

        # File moved/copied
        match = self.patterns['file-moved'].match (line)
        if match:
            action = Action ()
            type = match.group (1)
            if type == 'R':
                action.type = 'V'
            else:
                action.type = type
            action.f1 = match.group (3)
            action.f2 = match.group (2)
            action.rev = self.commit.revision

            self.commit.actions.append (action)
            self.handler.file (action.f1)

            return

        # This is a workaround for a bug in the GNOME Git migration
        # There are commits on tags not correctly detected like this one:
        # http://git.gnome.org/cgit/evolution/commit/?id=b8e52acac2b9fc5414a7795a73c74f7ee4eeb71f
        # We want to ignore commits on tags since it doesn't make any sense in Git
        if self.is_gnome:
            match = self.patterns['svn-tag'].match (line.strip ())
            if match:
                printout ("Warning: detected a commit on a svn tag: %s", (match.group (0),))
                tag = match.group (1)
                if self.commit.tags and tag in self.commit.tags:
                    # The commit will be ignored, so move the tag
                    # to the next (previous in history) commit
                    self.branch.tail.svn_tag = tag

        # Message
        self.commit.message += line + '\n'

        assert True, "Not match for line %s" % (line)
Example #4
0
    def _parse_line(self, line):
        if line is None or line == '':
            return

        # Separator
        match = self.patterns['separator'].match(line)
        if match:
            self.flush()

            return

        # Ignore details about merges
        match = self.patterns['ignore'].match(line)
        if match:
            self.state = BzrParser.UNKNOWN

            return
        
        # Commit
        match = self.patterns['commit'].match(line)
        if match:
            self.flush()
            self.commit = Commit()
            self.commit.revision = match.group(1)
            
            self.state = BzrParser.COMMIT

            return

        # Committer
        match = self.patterns['committer'].match(line)
        if match:
            self.commit.committer = Person()
            self.commit.committer.name = match.group(1)
            self.commit.committer.email = match.group(2)
            self.handler.committer(self.commit.committer)

            return

        # Author
        match = self.patterns['author'].match(line)
        if match:
            self.commit.author = Person()
            self.commit.author.name = match.group(1)
            self.commit.author.email = match.group(2)
            self.handler.author(self.commit.author)

            return        

        # Date
        match = self.patterns['date'].match(line)
        if match:
            self.commit.date = datetime.datetime(*(time.strptime
                                                   (match.group(1).strip(" "), 
                                                    "%Y-%m-%d %H:%M:%S")[0:6]))
            # datetime.datetime.strptime not supported by Python2.4
            #self.commit.date = datetime.datetime.strptime(\
            #    match.group(1).strip(" "), "%a %b %d %H:%M:%S %Y")
            
            return

        # Message
        match = self.patterns['message'].match(line)
        if match:
            self.state = BzrParser.MESSAGE

            return
        
        # Added files
        match = self.patterns['added'].match(line)
        if match:
            self.state = BzrParser.ADDED

            return

        # Modified files
        match = self.patterns['modified'].match(line)
        if match:
            self.state = BzrParser.MODIFIED

            return

        # Removed files
        match = self.patterns['removed'].match(line)
        if match:
            self.state = BzrParser.REMOVED

            return

        # Renamed files
        match = self.patterns['renamed'].match(line)
        if match:
            self.state = BzrParser.RENAMED

            return

        if self.state == BzrParser.MESSAGE:
            self.commit.message += line.lstrip() + '\n'
        elif self.state == BzrParser.ADDED or \
             self.state == BzrParser.MODIFIED or \
             self.state == BzrParser.REMOVED:
            action = Action()
            if self.state == BzrParser.ADDED:
                action.type = 'A'
            elif self.state == BzrParser.MODIFIED:
                action.type = 'M'
            elif self.state == BzrParser.REMOVED:
                action.type = 'D'
            action.f1 = line.strip()

            self.commit.actions.append(action)
            self.handler.file(action.f1)
        elif self.state == BzrParser.RENAMED:
            m = re.compile("^[ \t]+(.*) => (.*)$").match(line)
            if not m:
                return

            action = Action()
            action.type = 'V'
            action.f1 = m.group(2)
            action.f2 = m.group(1)
            
            self.commit.actions.append(action)
            self.handler.file(action.f1)
        else:
            self.state = BzrParser.UNKNOWN
Example #5
0
    def _parse_line(self, line):
        if line is None or line == '':
            return

        # Ignore
        for patt in self.patterns['ignore']:
            if patt.match(line):
                return

        # Commit
        match = self.patterns['commit'].match(line)
        if match:
            if self.commit is not None:
                # Skip commits on svn tags
                if self.branch.tail.svn_tag is None:
                    self.handler.commit(self.branch.tail.commit)

            if self.patterns['replace-commit'].search(line):
                printdbg("Skipping commit, because it's a replacement")
                self.commit = None

                return

            self.commit = Commit()
            self.commit.revision = match.group(1)

            parents = match.group(3)
            if parents:
                parents = parents.split()
            git_commit = self.GitCommit(self.commit, parents)

            # If a specific branch has been configured, there
            # won't be any decoration, so a branch needs to be
            # created
            if Config().branch is not None:
                self.branch = self.GitBranch(self.GitBranch.LOCAL,
                                             Config().branch, git_commit)

            decorate = match.group(5)
            branch = None
            if decorate:
                # Remote branch
                m = re.search(self.patterns['branch'], decorate)
                if m:
                    branch = self.GitBranch(self.GitBranch.REMOTE, m.group(2),
                                            git_commit)
                    printdbg("Branch '%s' head at acommit %s",
                             (branch.name, self.commit.revision))
                else:
                    # Local Branch
                    m = re.search(self.patterns['local-branch'], decorate)
                    if m:
                        branch = self.GitBranch(self.GitBranch.LOCAL,
                                                m.group(1), git_commit)
                        printdbg("Commit %s on local branch '%s'",
                                 (self.commit.revision, branch.name))
                        # If local branch was merged we just ignore this
                        # decoration
                        if self.branch and \
                        self.branch.is_my_parent(git_commit):
                            printdbg("Local branch '%s' was merged",
                                     (branch.name, ))
                            branch = None
                    else:
                        # Stash
                        m = re.search(self.patterns['stash'], decorate)
                        if m:
                            branch = self.GitBranch(self.GitBranch.STASH,
                                                    "stash", git_commit)
                            printdbg("Commit %s on stash",
                                     (self.commit.revision, ))
                # Tag
                m = re.search(self.patterns['tag'], decorate)
                if m:
                    self.commit.tags = [m.group(1)]
                    printdbg("Commit %s tagged as '%s'",
                             (self.commit.revision, self.commit.tags[0]))

            if branch is not None and self.branch is not None:
                # Detect empty branches. Ideally, the head of a branch
                # can't have children. When this happens is because the
                # branch is empty, so we just ignore such branch
                if self.branch.is_my_parent(git_commit):
                    printout("Warning: Detected empty branch '%s', " + \
                             "it'll be ignored", (branch.name,))
                    branch = None

            if len(self.branches) >= 2:
                # If current commit is the start point of a new branch
                # we have to look at all the current branches since
                # we haven't inserted the new branch yet.
                # If not, look at all other branches excluding the current one
                for i, b in enumerate(self.branches):
                    if i == 0 and branch is None:
                        continue

                    if b.is_my_parent(git_commit):
                        # We assume current branch is always the last one
                        # AFAIK there's no way to make sure this is right
                        printdbg("Start point of branch '%s' at commit %s",
                                 (self.branches[0].name, self.commit.revision))
                        self.branches.pop(0)
                        self.branch = b

            if self.branch and self.branch.tail.svn_tag is not None and \
            self.branch.is_my_parent(git_commit):
                # There's a pending tag in previous commit
                pending_tag = self.branch.tail.svn_tag
                printdbg("Move pending tag '%s' from previous commit %s " + \
                         "to current %s", (pending_tag,
                                           self.branch.tail.commit.revision,
                                           self.commit.revision))
                if self.commit.tags and pending_tag not in self.commit.tags:
                    self.commit.tags.append(pending_tag)
                else:
                    self.commit.tags = [pending_tag]
                self.branch.tail.svn_tag = None

            if branch is not None:
                self.branch = branch

                # Insert master always at the end
                if branch.is_remote() and branch.name == 'master':
                    self.branches.append(self.branch)
                else:
                    self.branches.insert(0, self.branch)
            else:
                self.branch.set_tail(git_commit)

            if parents and len(parents) > 1 and not Config().analyze_merges:
                #Skip merge commits
                self.commit = None

            return
        elif self.commit is None:
            return

        # Committer
        match = self.patterns['committer'].match(line)
        if match:
            self.commit.committer = Person()
            self.commit.committer.name = match.group(1)
            self.commit.committer.email = match.group(2)
            self.handler.committer(self.commit.committer)

            return

        # Author
        match = self.patterns['author'].match(line)
        if match:
            self.commit.author = Person()
            self.commit.author.name = match.group(1)
            self.commit.author.email = match.group(2)
            self.handler.author(self.commit.author)

            return

        # Commit Date
        match = self.patterns['commit-date'].match(line)
        if match:
            self.commit.commit_date = datetime.datetime(*(time.strptime(\
                match.group(1).strip(" "), "%a %b %d %H:%M:%S %Y")[0:6]))

            return

        # Author Date
        match = self.patterns['author-date'].match(line)
        if match:
            self.commit.author_date = datetime.datetime(*(time.strptime(\
                match.group(1).strip(" "), "%a %b %d %H:%M:%S %Y")[0:6]))

            return

        # File
        match = self.patterns['file'].match(line)
        if match:
            action = Action()
            action.type = match.group(1)
            action.f1 = match.group(2)

            self.commit.actions.append(action)
            self.handler.file(action.f1)

            return

        # File moved/copied
        match = self.patterns['file-moved'].match(line)
        if match:
            action = Action()
            type = match.group(1)
            if type == 'R':
                action.type = 'V'
            else:
                action.type = type
            action.f1 = match.group(3)
            action.f2 = match.group(2)
            action.rev = self.commit.revision

            self.commit.actions.append(action)
            self.handler.file(action.f1)

            return

        # Message
        self.commit.message += line + '\n'

        assert True, "Not match for line %s" % (line)
Example #6
0
    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'
Example #7
0
    def _parse_line(self, line):
        if line is None or line == '':
            return

        # Ignore
        for patt in self.patterns['ignore']:
            if patt.match(line):
                return

        # Commit
        match = self.patterns['commit'].match(line)
        if match:
            if self.commit is not None and self.branch is not None:
                if self.branch.tail.svn_tag is None:  # Skip commits on svn tags
                    self.handler.commit(self.branch.tail.commit)

            self.commit = Commit()
            self.commit.revision = match.group(1)

            parents = match.group(3)
            if parents:
                parents = parents.split()
                self.commit.parents = parents
            git_commit = self.GitCommit(self.commit, parents)

            decorate = match.group(5)
            branch = None
            if decorate:
                # Remote branch
                m = re.search(self.patterns['branch'], decorate)
                if m:
                    branch = self.GitBranch(self.GitBranch.REMOTE, m.group(1), git_commit)
                    printdbg("Branch '%s' head at acommit %s", (branch.name, self.commit.revision))
                else:
                    # Local Branch
                    m = re.search(self.patterns['local-branch'], decorate)
                    if m:
                        branch = self.GitBranch(self.GitBranch.LOCAL, m.group(1), git_commit)
                        printdbg("Commit %s on local branch '%s'", (self.commit.revision, branch.name))
                        # If local branch was merged we just ignore this decoration
                        if self.branch and self.branch.is_my_parent(git_commit):
                            printdbg("Local branch '%s' was merged", (branch.name,))
                            branch = None
                    else:
                        # Stash
                        m = re.search(self.patterns['stash'], decorate)
                        if m:
                            branch = self.GitBranch(self.GitBranch.STASH, "stash", git_commit)
                            printdbg("Commit %s on stash", (self.commit.revision,))
                # Tag
                m = re.search(self.patterns['tag'], decorate)
                if m:
                    self.commit.tags = [m.group(1)]
                    printdbg("Commit %s tagged as '%s'", (self.commit.revision, self.commit.tags[0]))

            if not branch and not self.branch:
                branch = self.GitBranch(self.GitBranch.LOCAL, "(no-branch)", git_commit)
                printdbg("Commit %s on unknown local branch '%s'", (self.commit.revision, branch.name))

            # This part of code looks wired at first time so here is a small description what it does:
            #
            # * self.branch is the branch to which the last inspected commit belonged to
            # * branch is the branch of the current parsed commit
            #
            # This check is only to find branches which are fully merged into a already analyzed branch
            #
            # For more detailed information see https://github.com/MetricsGrimoire/CVSAnalY/issues/64
            if branch is not None and self.branch is not None:
                # Detect empty branches.
                # Ideally, the head of a branch can't have children.
                # When this happens is because the branch is empty, so we just ignore such branch.
                if self.branch.is_my_parent(git_commit):
                    printout(
                        "Info: Branch '%s' will be ignored, because it was already merged in an active one.",
                        (branch.name,)
                    )
                    branch = None

            if len(self.branches) >= 2:
                # If current commit is the start point of a new branch
                # we have to look at all the current branches since
                # we haven't inserted the new branch yet.
                # If not, look at all other branches excluding the current one
                for i, b in enumerate(self.branches):
                    if i == 0 and branch is None:
                        continue

                    if b.is_my_parent(git_commit):
                        # We assume current branch is always the last one
                        # AFAIK there's no way to make sure this is right
                        printdbg("Start point of branch '%s' at commit %s",
                                 (self.branches[0].name, self.commit.revision))
                        self.branches.pop(0)
                        self.branch = b

            if self.branch and self.branch.tail.svn_tag is not None and self.branch.is_my_parent(git_commit):
                # There's a pending tag in previous commit
                pending_tag = self.branch.tail.svn_tag
                printdbg("Move pending tag '%s' from previous commit %s to current %s", (pending_tag,
                                                                                         self.branch.tail.commit.revision,
                                                                                         self.commit.revision))
                if self.commit.tags and pending_tag not in self.commit.tags:
                    self.commit.tags.append(pending_tag)
                else:
                    self.commit.tags = [pending_tag]
                self.branch.tail.svn_tag = None

            if branch is not None:
                self.branch = branch

                # Insert master always at the end
                if branch.name == 'master':
                    self.branches.append(self.branch)
                else:
                    self.branches.insert(0, self.branch)
            else:
                if self.branch is not None:
                    self.branch.set_tail(git_commit)
            return

        # Committer
        match = self.patterns['committer'].match(line)
        if match:
            self.commit.committer = Person()
            self.commit.committer.name = match.group(1)
            self.commit.committer.email = match.group(2)
            self.handler.committer(self.commit.committer)
            return

        # Author
        match = self.patterns['author'].match(line)
        if match:
            self.commit.author = Person()
            self.commit.author.name = match.group(1)
            self.commit.author.email = match.group(2)
            self.handler.author(self.commit.author)
            return

        # Commit date
        match = self.patterns['date'].match(line)
        if match:
            self.commit.date = datetime.datetime(
                *(time.strptime(match.group(1).strip(" "), "%a %b %d %H:%M:%S %Y")[0:6]))
            # datetime.datetime.strptime not supported by Python2.4
            #self.commit.date = datetime.datetime.strptime (match.group (1).strip (" "), "%a %b %d %H:%M:%S %Y")

            # match.group(2) represents the timezone. E.g. -0300, +0200, +0430 (Afghanistan)
            # This string will be parsed to int and recalculated into seconds (60 * 60)
            self.commit.date_tz = (((int(match.group(2))) * 60 * 60) / 100)
            return

        # Author date
        match = self.patterns['author_date'].match(line)
        if match:
            self.commit.author_date = datetime.datetime(
                *(time.strptime(match.group(1).strip(" "), "%a %b %d %H:%M:%S %Y")[0:6]))
            # datetime.datetime.strptime not supported by Python2.4
            #self.commit.author_date = datetime.datetime.strptime (match.group (1).strip (" "), "%a %b %d %H:%M:%S %Y")

            # match.group(2) represents the timezone. E.g. -0300, +0200, +0430 (Afghanistan)
            # This string will be parsed to int and recalculated into seconds (60 * 60)
            self.commit.author_date_tz = (((int(match.group(2))) * 60 * 60) / 100)
            return

        # File
        match = self.patterns['file'].match(line)
        if match:
            action = Action()
            type = match.group(1)
            if len(type) > 1:
                # merge actions
                if 'M' in type:
                    type = 'M'
                else:
                    # ignore merge actions without 'M'
                    return

            action.type = type
            action.f1 = match.group(2)

            self.commit.actions.append(action)
            self.handler.file(action.f1)
            return

        # File moved/copied
        match = self.patterns['file-moved'].match(line)
        if match:
            action = Action()
            type = match.group(1)
            if type == 'R':
                action.type = 'V'
            else:
                action.type = type
            action.f1 = match.group(3)
            action.f2 = match.group(2)
            action.rev = self.commit.revision

            self.commit.actions.append(action)
            self.handler.file(action.f1)

            return

        # This is a workaround for a bug in the GNOME Git migration
        # There are commits on tags not correctly detected like this one:
        # http://git.gnome.org/cgit/evolution/commit/?id=b8e52acac2b9fc5414a7795a73c74f7ee4eeb71f
        # We want to ignore commits on tags since it doesn't make any sense in Git
        if self.is_gnome:
            match = self.patterns['svn-tag'].match(line.strip())
            if match:
                printout("Warning: detected a commit on a svn tag: %s", (match.group(0),))
                tag = match.group(1)
                if self.commit.tags and tag in self.commit.tags:
                    # The commit will be ignored, so move the tag
                    # to the next (previous in history) commit
                    self.branch.tail.svn_tag = tag

        # Message
        self.commit.message += line + '\n'

        assert True, "Not match for line %s" % (line)