Beispiel #1
0
 def blame(self, commit, path):
     """Return a 'git blame' list for the file at `path`: For each line in
     the file, the list contains the commit that last changed that line.
     """
     # XXX see comment in `.history()`
     cmd = ['git', 'blame', '-ls', '--root', decode_from_git(commit.id), '--', path]
     output = subprocess.check_output(cmd, cwd=os.path.abspath(self.path))
     sha1_sums = [line[:40] for line in output.strip().split(b'\n')]
     return [None if self[sha1] is None else decode_from_git(self[sha1].id) for sha1 in sha1_sums]
Beispiel #2
0
 def blame(self, commit, path):
     """Return a 'git blame' list for the file at `path`: For each line in
     the file, the list contains the commit that last changed that line.
     """
     # XXX see comment in `.history()`
     cmd = ['git', 'blame', '-ls', '--root', decode_from_git(commit.id), '--', path]
     output = subprocess.check_output(cmd, cwd=os.path.abspath(self.path))
     sha1_sums = [line[:40] for line in output.strip().split(b'\n')]
     return [None if self[sha1] is None else decode_from_git(self[sha1].id) for sha1 in sha1_sums]
Beispiel #3
0
    def listdir(self, commit, path):
        """Return a list of submodules, directories and files in given
        directory: Lists of (link name, target path) tuples.
        """
        submodules, dirs, files = [], [], []
        for entry_rel in self.get_blob_or_tree(commit, path).items():
            # entry_rel: Entry('foo.txt', ...)
            # entry_abs: Entry('spam/eggs/foo.txt', ...)
            entry_abs = entry_rel.in_path(encode_for_git(path))
            path_str = decode_from_git(entry_abs.path)
            item = (os.path.basename(path_str), path_str)
            if S_ISGITLINK(entry_abs.mode):
                submodules.append(item)
            elif stat.S_ISDIR(entry_abs.mode):
                dirs.append(item)
            else:
                files.append(item)

        keyfunc = lambda tpl: tpl[0].lower()
        submodules.sort(key=keyfunc)
        files.sort(key=keyfunc)
        dirs.sort(key=keyfunc)

        if path:
            dirs.insert(0, ('..', parent_directory(path)))

        return {'submodules': submodules, 'dirs' : dirs, 'files' : files}
Beispiel #4
0
    def listdir(self, commit, path):
        """Return a list of submodules, directories and files in given
        directory: Lists of (link name, target path) tuples.
        """
        submodules, dirs, files = [], [], []
        for entry_rel in self.get_blob_or_tree(commit, path).items():
            # entry_rel: Entry('foo.txt', ...)
            # entry_abs: Entry('spam/eggs/foo.txt', ...)
            entry_abs = entry_rel.in_path(encode_for_git(path))
            path_str = decode_from_git(entry_abs.path)
            item = (os.path.basename(path_str), path_str)
            if S_ISGITLINK(entry_abs.mode):
                submodules.append(item)
            elif stat.S_ISDIR(entry_abs.mode):
                dirs.append(item)
            else:
                files.append(item)

        keyfunc = lambda tpl: tpl[0].lower()
        submodules.sort(key=keyfunc)
        files.sort(key=keyfunc)
        dirs.sort(key=keyfunc)

        if path:
            dirs.insert(0, ('..', parent_directory(path)))

        return {'submodules': submodules, 'dirs': dirs, 'files': files}
Beispiel #5
0
    def history(self, commit, path=None, max_commits=None, skip=0):
        """Return a list of all commits that affected `path`, starting at branch
        or commit `commit`. `skip` can be used for pagination, `max_commits`
        to limit the number of commits returned.

        Similar to `git log [branch/commit] [--skip skip] [-n max_commits]`.
        """
        # XXX The pure-Python/dulwich code is very slow compared to `git log`
        #     at the time of this writing (mid-2012).
        #     For instance, `git log .tx` in the Django root directory takes
        #     about 0.15s on my machine whereas the history() method needs 5s.
        #     Therefore we use `git log` here until dulwich gets faster.
        #     For the pure-Python implementation, see the 'purepy-hist' branch.

        cmd = ['git', 'log', '--format=%H']
        if skip:
            cmd.append('--skip=%d' % skip)
        if max_commits:
            cmd.append('--max-count=%d' % max_commits)
        cmd.append(decode_from_git(commit.id))
        if path:
            cmd.extend(['--', path])

        output = subprocess.check_output(cmd, cwd=os.path.abspath(self.path))
        sha1_sums = output.strip().split(b'\n')
        return [self[sha1] for sha1 in sha1_sums]
Beispiel #6
0
    def history(self, commit, path=None, max_commits=None, skip=0):
        """Return a list of all commits that affected `path`, starting at branch
        or commit `commit`. `skip` can be used for pagination, `max_commits`
        to limit the number of commits returned.

        Similar to `git log [branch/commit] [--skip skip] [-n max_commits]`.
        """
        # XXX The pure-Python/dulwich code is very slow compared to `git log`
        #     at the time of this writing (mid-2012).
        #     For instance, `git log .tx` in the Django root directory takes
        #     about 0.15s on my machine whereas the history() method needs 5s.
        #     Therefore we use `git log` here until dulwich gets faster.
        #     For the pure-Python implementation, see the 'purepy-hist' branch.

        cmd = ['git', 'log', '--format=%H']
        if skip:
            cmd.append('--skip=%d' % skip)
        if max_commits:
            cmd.append('--max-count=%d' % max_commits)
        cmd.append(decode_from_git(commit.id))
        if path:
            cmd.extend(['--', path])

        output = subprocess.check_output(cmd, cwd=os.path.abspath(self.path))
        sha1_sums = output.strip().split(b'\n')
        return [self[sha1] for sha1 in sha1_sums]
Beispiel #7
0
    def commit_diff(self, commit):
        """Return the list of changes introduced by `commit`."""
        from klaus.utils import guess_is_binary

        if commit.parents:
            parent_tree = self[commit.parents[0]].tree
        else:
            parent_tree = None

        summary = {'nfiles': 0, 'nadditions': 0, 'ndeletions': 0}
        file_changes = []  # the changes in detail

        dulwich_changes = self.object_store.tree_changes(
            parent_tree, commit.tree)
        for (oldpath, newpath), (oldmode,
                                 newmode), (oldsha, newsha) in dulwich_changes:
            summary['nfiles'] += 1

            try:
                # Check for binary files -- can't show diffs for these
                if newsha and guess_is_binary(self[newsha]) or \
                   oldsha and guess_is_binary(self[oldsha]):
                    file_changes.append({
                        'is_binary': True,
                        'old_filename': oldpath or '/dev/null',
                        'new_filename': newpath or '/dev/null',
                        'chunks': None
                    })
                    continue
            except KeyError:
                # newsha/oldsha are probably related to submodules.
                # Dulwich will handle that.
                pass

            bytesio = io.BytesIO()
            dulwich.patch.write_object_diff(bytesio, self.object_store,
                                            (oldpath, oldmode, oldsha),
                                            (newpath, newmode, newsha))
            files = prepare_udiff(decode_from_git(bytesio.getvalue()),
                                  want_header=False)
            if not files:
                # the diff module doesn't handle deletions/additions
                # of empty files correctly.
                file_changes.append({
                    'old_filename': oldpath or '/dev/null',
                    'new_filename': newpath or '/dev/null',
                    'chunks': [],
                    'additions': 0,
                    'deletions': 0,
                })
            else:
                change = files[0]
                summary['nadditions'] += change['additions']
                summary['ndeletions'] += change['deletions']
                file_changes.append(change)

        return summary, file_changes
Beispiel #8
0
 def blame(self, commit, path):
     """Return a 'git blame' list for the file at `path`: For each line in
     the file, the list contains the commit that last changed that line.
     """
     # XXX see comment in `.history()`
     cmd = [
         'git', 'blame', '-ls', '--root',
         decode_from_git(commit.id), '--', path
     ]
     output = subprocess.check_output(cmd, cwd=os.path.abspath(self.path))
     sha1_sums = [line[:40] for line in output.strip().split(b'\n')]
     lines = []
     for sha1 in sha1_sums:
         obj = self.getref(sha1, None)
         if obj is not None:
             obj = decode_from_git(obj.id)
         lines.append(obj)
     return lines
Beispiel #9
0
def walk_tree(repo, tree, root=''):
    """Recursively walk a dulwich Tree, yielding tuples of
    (absolute path, TreeEntry) along the way.
    """
    for entry in tree.iteritems():
        entry_abspath = os.path.join(root, decode_from_git(entry.path))
        if stat.S_ISDIR(entry.mode):
            for _ in walk_tree(repo, repo[entry.sha], entry_abspath):
                yield _
        else:
            yield (entry_abspath, entry)
Beispiel #10
0
def walk_tree(repo, tree, root=''):
    """Recursively walk a dulwich Tree, yielding tuples of
    (absolute path, TreeEntry) along the way.
    """
    for entry in tree.iteritems():
        entry_abspath = os.path.join(root, decode_from_git(entry.path))
        if stat.S_ISDIR(entry.mode):
            for _ in walk_tree(repo, repo[entry.sha], entry_abspath):
                yield _
        else:
            yield (entry_abspath, entry)
Beispiel #11
0
    def commit_diff(self, commit):
        """Return the list of changes introduced by `commit`."""
        from klaus.utils import guess_is_binary

        if commit.parents:
            parent_tree = self[commit.parents[0]].tree
        else:
            parent_tree = None

        summary = {'nfiles': 0, 'nadditions':  0, 'ndeletions':  0}
        file_changes = []  # the changes in detail

        dulwich_changes = self.object_store.tree_changes(parent_tree, commit.tree)
        for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in dulwich_changes:
            summary['nfiles'] += 1

            try:
                # Check for binary files -- can't show diffs for these
                if newsha and guess_is_binary(self[newsha]) or \
                   oldsha and guess_is_binary(self[oldsha]):
                    file_changes.append({
                        'is_binary': True,
                        'old_filename': oldpath or '/dev/null',
                        'new_filename': newpath or '/dev/null',
                        'chunks': None
                    })
                    continue
            except KeyError:
                # newsha/oldsha are probably related to submodules.
                # Dulwich will handle that.
                pass

            bytesio = io.BytesIO()
            dulwich.patch.write_object_diff(bytesio, self.object_store,
                                            (oldpath, oldmode, oldsha),
                                            (newpath, newmode, newsha))
            files = prepare_udiff(decode_from_git(bytesio.getvalue()), want_header=False)
            if not files:
                # the diff module doesn't handle deletions/additions
                # of empty files correctly.
                file_changes.append({
                    'old_filename': oldpath or '/dev/null',
                    'new_filename': newpath or '/dev/null',
                    'chunks': [],
                    'additions': 0,
                    'deletions': 0,
                })
            else:
                change = files[0]
                summary['nadditions'] += change['additions']
                summary['ndeletions'] += change['deletions']
                file_changes.append(change)

        return summary, file_changes
Beispiel #12
0
    def get_sorted_ref_names(self, prefix, exclude=None):
        refs = self.refs.as_dict(encode_for_git(prefix))
        if exclude:
            refs.pop(prefix + exclude, None)

        def get_commit_time(refname):
            obj = self[refs[refname]]
            if isinstance(obj, dulwich.objects.Tag):
                return obj.tag_time
            return obj.commit_time

        return [decode_from_git(ref) for ref in
                sorted(refs.keys(), key=get_commit_time, reverse=True)]
Beispiel #13
0
    def get_sorted_ref_names(self, prefix, exclude=None):
        refs = self.refs.as_dict(encode_for_git(prefix))
        if exclude:
            refs.pop(prefix + exclude, None)

        def get_commit_time(refname):
            obj = self[refs[refname]]
            if isinstance(obj, dulwich.objects.Tag):
                return obj.tag_time
            return obj.commit_time

        return [decode_from_git(ref) for ref in
                sorted(refs.keys(), key=get_commit_time, reverse=True)]
Beispiel #14
0
    def get_ref_names_ordered_by_last_commit(self, prefix, exclude=None):
        """Return a list of ref names that begin with `prefix`, ordered by the
        time they have been committed to last.
        """
        def get_commit_time(refname):
            obj = self[refs[refname]]
            if isinstance(obj, dulwich.objects.Tag):
                return obj.tag_time
            return obj.commit_time

        refs = self.refs.as_dict(encode_for_git(prefix))
        if exclude:
            refs.pop(prefix + exclude, None)
        sorted_names = sorted(refs.keys(), key=get_commit_time, reverse=True)
        return [decode_from_git(ref) for ref in sorted_names]
Beispiel #15
0
    def get_ref_names_ordered_by_last_commit(self, prefix, exclude=None):
        """Return a list of ref names that begin with `prefix`, ordered by the
        time they have been committed to last.
        """
        def get_commit_time(refname):
            obj = self[refs[refname]]
            if isinstance(obj, dulwich.objects.Tag):
                return obj.tag_time
            return obj.commit_time

        refs = self.refs.as_dict(encode_for_git(prefix))
        if exclude:
            refs.pop(prefix + exclude, None)
        sorted_names = sorted(refs.keys(), key=get_commit_time, reverse=True)
        return [decode_from_git(ref) for ref in sorted_names]
Beispiel #16
0
    def listdir(self, commit, path):
        """Return a list of directories and files in given directory."""
        submodules, dirs, files = [], [], []
        for entry in self.get_blob_or_tree(commit, path).items():
            entry_path = decode_from_git(entry.path)
            name, entry = entry_path, entry.in_path(encode_for_git(path))
            if S_ISGITLINK(entry.mode):
                submodules.append((name.lower(), name, entry_path, entry.sha))
            elif stat.S_ISDIR(entry.mode):
                dirs.append((name.lower(), name, entry_path))
            else:
                files.append((name.lower(), name, entry_path))
        files.sort()
        dirs.sort()

        if path:
            dirs.insert(0, (None, '..', parent_directory(path)))

        return {'submodules': submodules, 'dirs': dirs, 'files': files}
Beispiel #17
0
    def get_ref_names_ordered_by_last_commit(self, prefix, exclude=None):
        """Return a list of ref names that begin with `prefix`, ordered by the
        time they have been committed to last.
        """
        def get_commit_time(obj):
            if obj is None:
                # Put refs that point to non-existent objects last.
                return 0
            elif isinstance(obj, dulwich.objects.Tag):
                return obj.tag_time
            else:
                return obj.commit_time

        refs = self.get_resolved_refs_as_dict(encode_for_git(prefix),
                                              resolve_default=None)
        if exclude:
            refs.pop(prefix + exclude, None)
        sorted_refs = sorted(refs.items(),
                             key=lambda item: get_commit_time(item[1]),
                             reverse=True)
        return [decode_from_git(name) for name, _ in sorted_refs]