Beispiel #1
0
    def get_changes(self):
        # Force population of the file history for the files modified in this
        # changelist.
        self._log.debug("PerforceChangeset(%i).get_changes()" % self._change)
        self._repos.precacheFileInformationForChanges([self._change])

        for node in self._changelist.nodes:
            nodePath = node.nodePath
            self._log.debug(
                'Change %i contains %s%s [%s]' %
                (self._change, nodePath.path, nodePath.rev, node.action))

            if node.action in [u'add', u'branch', u'import']:
                if node.integrations:
                    otherNodePath, how = node.integrations[0]
                    otherNode = self._repos.getNode(otherNodePath)
                    yield (normalisePath(nodePath.path),
                           Node.FILE, Changeset.COPY,
                           normalisePath(otherNodePath.path), otherNode.change)
                else:
                    yield (normalisePath(nodePath.path), Node.FILE,
                           Changeset.ADD, None, None)
            elif node.action in [u'edit', u'integrate']:
                if node.integrations and node.integrations[0][1] == 'copy':
                    otherNodePath, how = node.integrations[0]
                    otherNode = self._repos.getNode(otherNodePath)

                    # A 'copy from' operation
                    yield (normalisePath(nodePath.path),
                           Node.FILE, Changeset.COPY,
                           normalisePath(otherNodePath.path), otherNode.change)
                else:
                    if node.fileRevision > 1:
                        from p4trac.repos import P4NodePath
                        otherNode = self._repos.getNode(
                            P4NodePath(nodePath.path,
                                       '#%i' % (node.fileRevision - 1)))

                        # A basic edit operation
                        yield (normalisePath(nodePath.path),
                               Node.FILE, Changeset.EDIT,
                               normalisePath(nodePath.path), otherNode.change)
                    else:
                        yield (normalisePath(nodePath.path), Node.FILE,
                               Changeset.EDIT, None, None)
            elif node.action in [u'delete']:
                # The file was deleted
                from p4trac.repos import P4NodePath
                otherNodePath = P4NodePath(nodePath.path,
                                           '#%i' % (node.fileRevision - 1))
                otherNode = self._repos.getNode(otherNodePath)
                yield (normalisePath(nodePath.path), Node.FILE,
                       Changeset.DELETE, normalisePath(nodePath.path),
                       otherNode.change)
Beispiel #2
0
 def get_path_history(self, path, rev=None, limit=None):
     # TODO: This doesn't handle the case where the head node has been
     # deleted or a file has changed to a directory or vica versa.
     from p4trac.repos import P4NodePath
     nodePath = P4NodePath(P4NodePath.normalisePath(path), rev)
     node = PerforceNode(nodePath, self._repos, self.log)
     return node.get_history(limit)
Beispiel #3
0
def normaliseRev(rev):
    """Normalise a Perforce revision and return it as a Trac-compatible rev.

    Basically converts revisions to '@<label/client/date>' or '#<rev>' but
    returns '@<change>' as an integer value.
    """
    from p4trac.repos import P4NodePath
    rev = P4NodePath.normaliseRevision(rev)
    if rev is None:
        return rev
    elif rev.startswith(u'@') and rev[1:].isdigit():
        return int(rev[1:])
    else:
        return rev
Beispiel #4
0
def normaliseRev(rev):
    """Normalise a Perforce revision and return it as a Trac-compatible rev.

    Basically converts revisions to '@<label/client/date>' or '#<rev>' but
    returns '@<change>' as an integer value.
    """
    from p4trac.repos import P4NodePath
    rev = P4NodePath.normaliseRevision(rev)
    if rev is None:
        return rev
    elif rev.startswith(u'@') and rev[1:].isdigit():
        return int(rev[1:])
    else:
        return rev
Beispiel #5
0
def normalisePath(path):
    """Normalise a Perforce path and return it as a Trac-compatible path.

    If None or the empty string is passed then the root path is returned.
    The path is returned with a single leading slash rather than the Perforce
    depot notation which uses two leading slashes.

    @return: The normalised Perforce path.
    @rtype: C{unicode}
    """

    from p4trac.repos import P4NodePath
    path = P4NodePath.normalisePath(path)
    if path is None:
        return u'/'
    else:
        return path[1:]
Beispiel #6
0
def normalisePath(path):
    """Normalise a Perforce path and return it as a Trac-compatible path.

    If None or the empty string is passed then the root path is returned.
    The path is returned with a single leading slash rather than the Perforce
    depot notation which uses two leading slashes.

    @return: The normalised Perforce path.
    @rtype: C{unicode}
    """

    from p4trac.repos import P4NodePath
    path = P4NodePath.normalisePath(path)
    if path is None:
        return u'/'
    else:
        return path[1:]
Beispiel #7
0
    def get_changes(self, old_path, old_rev, new_path, new_rev, ignore_ancestry=0):
        self.log.debug('PerforceRepository.get_changes(%r,%r,%r,%r)' % (
                       old_path, old_rev, new_path, new_rev))

        from p4trac.repos import P4NodePath
        oldNodePath = P4NodePath(P4NodePath.normalisePath(old_path), old_rev)
        oldNode = self._repos.getNode(oldNodePath)

        newNodePath = P4NodePath(P4NodePath.normalisePath(new_path), new_rev)
        newNode = self._repos.getNode(newNodePath)

        if (newNode.isFile and oldNode.isDirectory) or \
           (newNode.isDirectory and oldNode.isFile):
            raise TracError("Cannot view changes between directory and file")

        if newNode.isDirectory or oldNode.isDirectory:
            if oldNodePath.isRoot:
                oldQueryPath = u'%s...%s' % (rootPath(self._connection),
                                          oldNodePath.rev)
            else:
                oldQueryPath = u'%s/...%s' % (oldNodePath.path,
                                             oldNodePath.rev)
            if newNodePath.isRoot:
                newQueryPath = u'%s...%s' % (rootPath(self._connection),
                                          newNodePath.rev)
            else:
                newQueryPath = u'%s/...%s' % (newNodePath.path,
                                             newNodePath.rev)
        elif newNode.isFile or oldNode.isFile:
            oldQueryPath = oldNodePath.fullPath
            newQueryPath = newNodePath.fullPath
        else:
            raise TracError("Cannot diff two non-existant nodes")

        from p4trac.repos import P4Diff2OutputConsumer
        output = P4Diff2OutputConsumer(self._repos)

        self.log.debug('p4 diff2 -ds %s %s' % (oldQueryPath, newQueryPath))
        
        self._connection.run('diff2', '-ds',
                             self._repos.fromUnicode(oldQueryPath),
                             self._repos.fromUnicode(newQueryPath),
                             output=output)
        if output.errors:
            from p4trac.repos import PerforceError
            raise PerforceError(output.errors)

        for change in output.changes:
            oldFileNodePath, newFileNodePath = change
            if oldFileNodePath is not None:
                oldFileNode = PerforceNode(oldFileNodePath,
                                           self._repos,
                                           self.log)
            else:
                oldFileNode = None

            if newFileNodePath is not None:
                newFileNode = PerforceNode(newFileNodePath,
                                           self._repos,
                                           self.log)
            else:
                newFileNode = None

            if newFileNode and oldFileNode:
                yield oldFileNode, newFileNode, Node.FILE, Changeset.EDIT
            elif newFileNode:
                yield oldFileNode, newFileNode, Node.FILE, Changeset.ADD
            elif oldFileNode:
                yield oldFileNode, newFileNode, Node.FILE, Changeset.DELETE
Beispiel #8
0
    def next_rev(self, rev, path=''):
        # Finding the next revision is a little more difficult in Perforce
        # as we can only ask for the n most recent changes according to a
        # given criteria. We query batches of changes using a binary search
        # technique so that the number of changes queried is of the order of
        # log N where N is the number of changes greater than rev. This way
        # it is still fairly efficient if the next change is 1 or 1000 changes
        # later.
        self.log.debug('next_rev(%r,%r)' % (rev, path))

        from p4trac.repos import P4NodePath
        if not path:
            queryPath = u'...'
        else:
            path = P4NodePath.normalisePath(path)
            node = self._repos.getNode(P4NodePath(path, rev))
            self.log.debug(u'node : %i %i %s' % (node.isDirectory, node.nodePath.isRoot, node.nodePath.path))

            if node.isDirectory:
                if node.nodePath.isRoot:
                    queryPath = u'...'
                else:
                    queryPath = u'%s/...' % node.nodePath.path
            else:
                queryPath = node.nodePath.path

        queryPath = self._repos.fromUnicode(queryPath)
        self.log.debug(u'Looking for next_rev after change %i for %s' % (rev, path))

        # Perform a binary-search of sorts for the next revision
        batchSize = 50
        lowerBound = rev + 1
        upperBound = self.youngest_rev

        while lowerBound <= upperBound:
            if lowerBound + batchSize > upperBound:
                batchUpperBound = upperBound
            else:
                middle = (upperBound + lowerBound) / 2
                if middle - lowerBound < batchSize:
                    batchUpperBound = lowerBound + batchSize
                else:
                    batchUpperBound = middle
            self.log.debug(
                'Looking for changes in range [%i, %i]' % (lowerBound,
                                                           batchUpperBound))

            from p4trac.repos import P4ChangesOutputConsumer
            output = P4ChangesOutputConsumer(self._repos)
            depot_path = '%s%s@>=%i,@<=%i' % (rootPath(self._connection),
                                              queryPath, lowerBound, batchUpperBound)
            self.log.debug(u'p4 changes -l -s submitted -m %s %s' % (batchSize, depot_path))
            self._connection.run('changes', '-l', '-s', 'submitted',
                                 '-m', str(batchSize),
                                 depot_path, output=output)

            if output.errors:
                from p4trac.repos import PerforceError
                raise PerforceError(output.errors)

            if output.changes:
                lowest = min(output.changes)
                assert lowest >= lowerBound
                assert lowest <= batchUpperBound

                if lowerBound + batchSize >= batchUpperBound:
                    # There are no earlier changes
                    self.log.debug('next_rev is %i' % lowest)
                    return lowest
                else:
                    # There may be another earlier changes but we know it
                    # can't be any later than lowest.
                    upperBound = lowest
            else:
                # Didn't find any changes in (lowerBound, batchUpperBound)
                # Try searching from batchUpperBound + 1 onwards
                lowerBound = batchUpperBound + 1
        return None
Beispiel #9
0
 def get_node(self, path, rev=None):
     self.log.debug('get_node(%s, %s) called' % (path, rev))
     from p4trac.repos import P4NodePath
     nodePath = P4NodePath(P4NodePath.normalisePath(path), rev)
     return PerforceNode(nodePath, self._repos, self.log)
Beispiel #10
0
 def has_node(self, path, rev=None):
     from p4trac.repos import P4NodePath
     path = P4NodePath.normalisePath(path)
     return self._repos.getNode(P4NodePath(path, rev)).exists
Beispiel #11
0
    def get_history(self, limit=None):
        self._log.debug('PerforceNode.get_history(%r)' % limit)
        if self._node.isFile:
            # Force population of the filelog history for efficiency
            self._repos._runFileLog(self._nodePath, limit)

            from p4trac.repos import P4NodePath

            currentNode = self._node
            i = 0
            while i < limit and currentNode is not None:
                if currentNode.action in [u'add', u'branch', u'import']:
                    if currentNode.integrations:
                        nodePath, how = currentNode.integrations[0]

                        # TODO: Detect whether the copy was really a move
                        yield (normalisePath(currentNode.nodePath.path),
                               currentNode.change, Changeset.COPY)
                        currentNode = self._repos.getNode(nodePath)
                    else:
                        yield (normalisePath(currentNode.nodePath.path),
                               currentNode.change, Changeset.ADD)
                        if currentNode.fileRevision > 1:
                            # Get the previous revision
                            nodePath = P4NodePath(
                                currentNode.nodePath.path,
                                '#%i' % (currentNode.fileRevision - 1))
                            currentNode = self._repos.getNode(nodePath)
                        else:
                            currentNode = None
                elif currentNode.action in [u'edit', u'integrate']:
                    nextNode = None
                    if currentNode.integrations:
                        nodePath, how = currentNode.integrations[0]
                        if how == 'copy':
                            yield (normalisePath(currentNode.nodePath.path),
                                   currentNode.change, Changeset.COPY)
                            nextNode = self._repos.getNode(nodePath)
                        else:
                            yield (normalisePath(currentNode.nodePath.path),
                                   currentNode.change, Changeset.EDIT)
                    else:
                        yield (normalisePath(currentNode.nodePath.path),
                               currentNode.change, Changeset.EDIT)
                    if nextNode is None:
                        if currentNode.fileRevision > 1:
                            currentNode = self._repos.getNode(
                                P4NodePath(
                                    currentNode.nodePath.path,
                                    '#%i' % (currentNode.fileRevision - 1)))
                        else:
                            currentNode = None
                    else:
                        currentNode = nextNode
                elif currentNode.action in [u'delete']:
                    yield (normalisePath(currentNode.nodePath.path),
                           currentNode.change, Changeset.DELETE)
                    if currentNode.fileRevision > 1:
                        currentNode = self._repos.getNode(
                            P4NodePath(currentNode.nodePath.path,
                                       '#%i' % (currentNode.fileRevision - 1)))
                    else:
                        currentNode = None
                i += 1
        elif self._node.isDirectory:
            # List all changelists that have affected this directory
            from p4trac.repos import P4ChangesOutputConsumer
            output = P4ChangesOutputConsumer(self._repos)

            if self._nodePath.isRoot:
                queryPath = '%s%s' % (rootPath(
                    self._repos._connection), self._nodePath.rev)
            else:
                queryPath = '%s/...%s' % (self._nodePath.path,
                                          self._nodePath.rev)

            if limit is None:
                self._repos._connection.run('changes',
                                            '-l',
                                            '-s',
                                            'submitted',
                                            self._repos.fromUnicode(queryPath),
                                            output=output)
            else:
                self._repos._connection.run('changes',
                                            '-l',
                                            '-s',
                                            'submitted',
                                            '-m',
                                            str(limit),
                                            self._repos.fromUnicode(queryPath),
                                            output=output)
            if output.errors:
                raise PerforceError(output.errors)
            changes = output.changes

            self._repos._runDescribe(changes)

            from p4trac.repos import P4NodePath
            for i in xrange(len(changes)):
                change = changes[i]
                nodePath = P4NodePath(self._nodePath.path, change)
                if i < len(changes) - 1:
                    prevChange = changes[i + 1]
                else:
                    prevChange = change - 1

                prevNodePath = P4NodePath(self._nodePath.path, prevChange)
                node = self._repos.getNode(nodePath)
                prevNode = self._repos.getNode(prevNodePath)
                if node.isDirectory:
                    if prevNode.isDirectory:
                        yield (normalisePath(self._nodePath.path), change,
                               Changeset.EDIT)
                    else:
                        yield (normalisePath(self._nodePath.path), change,
                               Changeset.ADD)
                elif prevNode.isDirectory:
                    yield (normalisePath(self._nodePath.path), change,
                           Changeset.DELETE)
        else:
            raise NoSuchNode(self._nodePath.path, self._nodePath.rev)
Beispiel #12
0
    def get_changes(self,
                    old_path,
                    old_rev,
                    new_path,
                    new_rev,
                    ignore_ancestry=0):
        self.log.debug('PerforceRepository.get_changes(%r,%r,%r,%r)' %
                       (old_path, old_rev, new_path, new_rev))

        from p4trac.repos import P4NodePath
        oldNodePath = P4NodePath(P4NodePath.normalisePath(old_path), old_rev)
        oldNode = self._repos.getNode(oldNodePath)

        newNodePath = P4NodePath(P4NodePath.normalisePath(new_path), new_rev)
        newNode = self._repos.getNode(newNodePath)

        if (newNode.isFile and oldNode.isDirectory) or \
           (newNode.isDirectory and oldNode.isFile):
            raise TracError("Cannot view changes between directory and file")

        if newNode.isDirectory or oldNode.isDirectory:
            if oldNodePath.isRoot:
                oldQueryPath = u'%s%s' % (rootPath(
                    self._connection), oldNodePath.rev)
            else:
                oldQueryPath = u'%s/...%s' % (oldNodePath.path,
                                              oldNodePath.rev)
            if newNodePath.isRoot:
                newQueryPath = u'%s%s' % (rootPath(
                    self._connection), newNodePath.rev)
            else:
                newQueryPath = u'%s/...%s' % (newNodePath.path,
                                              newNodePath.rev)
        elif newNode.isFile or oldNode.isFile:
            oldQueryPath = oldNodePath.fullPath
            newQueryPath = newNodePath.fullPath
        else:
            raise TracError("Cannot diff two non-existant nodes")

        from p4trac.repos import P4Diff2OutputConsumer
        output = P4Diff2OutputConsumer(self._repos)

        self._connection.run('diff2',
                             '-ds',
                             self._repos.fromUnicode(oldQueryPath),
                             self._repos.fromUnicode(newQueryPath),
                             output=output)
        if output.errors:
            from p4trac.repos import PerforceError
            raise PerforceError(output.errors)

        for change in output.changes:
            oldFileNodePath, newFileNodePath = change
            if oldFileNodePath is not None:
                oldFileNode = PerforceNode(oldFileNodePath, self, self.log)
            else:
                oldFileNode = None

            if newFileNodePath is not None:
                newFileNode = PerforceNode(newFileNodePath, self, self.log)
            else:
                newFileNode = None

            if newFileNode and oldFileNode:
                yield oldFileNode, newFileNode, Node.FILE, Changeset.EDIT
            elif newFileNode:
                yield oldFileNode, newFileNode, Node.FILE, Changeset.ADD
            elif oldFileNode:
                yield oldFileNode, newFileNode, Node.FILE, Changeset.DELETE
Beispiel #13
0
    def next_rev(self, rev, path=''):
        # Finding the next revision is a little more difficult in Perforce
        # as we can only ask for the n most recent changes according to a
        # given criteria. We query batches of changes using a binary search
        # technique so that the number of changes queried is of the order of
        # log N where N is the number of changes greater than rev. This way
        # it is still fairly efficient if the next change is 1 or 1000 changes
        # later.
        self.log.debug('next_rev(%r,%r)' % (rev, path))

        from p4trac.repos import P4NodePath
        if not path:
            path = u'//'
        else:
            path = P4NodePath.normalisePath(path)
        node = self._repos.getNode(P4NodePath(path, rev))

        if node.isDirectory:
            if node.nodePath.isRoot:
                # Handle the root path specially since it encompasses all
                # changes and so can use the repository's internal cache.
                return self._repos.getNextChange(int(rev))
            else:
                queryPath = u'%s/...' % node.nodePath.path
        else:
            queryPath = node.nodePath.path

        queryPath = self._repos.fromUnicode(queryPath)
        self.log.debug(u'Looking for next_rev after change %i for %s' %
                       (rev, path))

        # Perform a binary-search of sorts for the next revision
        batchSize = 50
        lowerBound = rev + 1
        upperBound = self.youngest_rev

        while lowerBound <= upperBound:
            if lowerBound + batchSize > upperBound:
                batchUpperBound = upperBound
            else:
                middle = (upperBound + lowerBound) / 2
                if middle - lowerBound < batchSize:
                    batchUpperBound = lowerBound + batchSize
                else:
                    batchUpperBound = middle
            self.log.debug('Looking for changes in range [%i, %i]' %
                           (lowerBound, batchUpperBound))

            from p4trac.repos import P4ChangesOutputConsumer
            output = P4ChangesOutputConsumer(self._repos)
            depot_path = '%s%s@>=%i,@<=%i' % (rootPath(
                self._connection), queryPath, lowerBound, batchUpperBound)
            self._connection.run('changes',
                                 '-l',
                                 '-s',
                                 'submitted',
                                 '-m',
                                 str(batchSize),
                                 depot_path,
                                 output=output)

            if output.errors:
                from p4trac.repos import PerforceError
                raise PerforceError(output.errors)

            if output.changes:
                lowest = min(output.changes)
                assert lowest >= lowerBound
                assert lowest <= batchUpperBound

                if lowerBound + batchSize >= batchUpperBound:
                    # There are no earlier changes
                    self.log.debug('next_rev is %i' % lowest)
                    return lowest
                else:
                    # There may be another earlier changes but we know it
                    # can't be any later than lowest.
                    upperBound = lowest
            else:
                # Didn't find any changes in (lowerBound, batchUpperBound)
                # Try searching from batchUpperBound + 1 onwards
                lowerBound = batchUpperBound + 1
        return None
Beispiel #14
0
 def get_node(self, path, rev=None):
     self.log.debug('get_node(%s, %s) called' % (path, rev))
     from p4trac.repos import P4NodePath
     nodePath = P4NodePath(P4NodePath.normalisePath(path), rev)
     return PerforceNode(nodePath, self, self.log)
Beispiel #15
0
 def has_node(self, path, rev=None):
     from p4trac.repos import P4NodePath
     path = P4NodePath.normalisePath(path)
     return self._repos.getNode(P4NodePath(path, rev)).exists