/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to model.py

Ultra-experimental support for "bzr pull". No test. No sanity.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2007 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""The model for interacting with the git process, etc."""
 
18
 
 
19
import os
 
20
import subprocess
 
21
import tarfile
 
22
 
 
23
from bzrlib.plugins.git import errors
 
24
 
 
25
 
 
26
class GitModel(object):
 
27
    """API that follows GIT model closely"""
 
28
 
 
29
    def __init__(self, git_dir):
 
30
        self.git_dir = git_dir
 
31
 
 
32
    def git_command(self, command, args):
 
33
        return ['git', command] + args
 
34
 
 
35
    def git_lines(self, command, args):
 
36
        cmd = self.git_command(command, args)
 
37
        env = os.environ.copy()
 
38
        env['GIT_DIR'] = self.git_dir
 
39
        p = subprocess.Popen(cmd,
 
40
                             stdout=subprocess.PIPE,
 
41
                             stderr=subprocess.PIPE,
 
42
                             env=env)
 
43
        lines = p.stdout.readlines()
 
44
        if p.wait() != 0:
 
45
            raise errors.GitCommandError(cmd, p.returncode,
 
46
                                         p.stderr.read().strip())
 
47
        return lines
 
48
 
 
49
    def git_line(self, command, args):
 
50
        lines = self.git_lines(command, args)
 
51
        return lines[0]
 
52
 
 
53
    def cat_file(self, type, object_id, pretty=False):
 
54
        args = []
 
55
        if pretty:
 
56
            args.append('-p')
 
57
        else:
 
58
            args.append(type)
 
59
        args.append(object_id)
 
60
        return self.git_lines('cat-file', args)
 
61
 
 
62
    def rev_list(self, heads, max_count=None, header=False, parents=False,
 
63
                 topo_order=False, paths=None):
 
64
        args = []
 
65
        if max_count is not None:
 
66
            args.append('--max-count=%d' % max_count)
 
67
        if header:
 
68
            args.append('--header')
 
69
        if parents:
 
70
            args.append('--parents')
 
71
        if topo_order:
 
72
            args.append('--topo-order')
 
73
        if heads is None:
 
74
            args.append('--all')
 
75
        else:
 
76
            args.extend(heads)
 
77
        if paths is not None:
 
78
            args.append('--')
 
79
            args.extend(paths)
 
80
        return self.git_lines('rev-list', args)
 
81
 
 
82
    def rev_parse(self, git_id):
 
83
        args = ['--verify', git_id]
 
84
        return self.git_line('rev-parse', args).strip()
 
85
 
 
86
    def get_head(self):
 
87
        try:
 
88
            return self.rev_parse('HEAD')
 
89
        except errors.GitCommandError, e:
 
90
            # Most likely, this is a null branch, so treat it as such
 
91
            if e.stderr == 'fatal: Needed a single revision':
 
92
                return None
 
93
            raise
 
94
 
 
95
    def get_revision_graph(self, revisions):
 
96
        ancestors = {}
 
97
        for line in self.rev_list(revisions, parents=True):
 
98
            entries = line.split()
 
99
            ancestors[entries[0]] = entries[1:]
 
100
        return ancestors
 
101
 
 
102
    def get_ancestry(self, revisions):
 
103
        args = ['--topo-order', '--reverse'] + revisions
 
104
        return [line[:-1] for line in self.git_lines('rev-list', args)]
 
105
 
 
106
    def ancestor_lines(self, revisions):
 
107
        revision_lines = []
 
108
        for line in self.rev_list(revisions, header=True):
 
109
            if line.startswith('\x00'):
 
110
                yield revision_lines
 
111
                revision_lines = [line[1:].decode('latin-1')]
 
112
            else:
 
113
                revision_lines.append(line.decode('latin-1'))
 
114
        assert revision_lines == ['']
 
115
 
 
116
    def get_inventory(self, tree_id):
 
117
        for line in self.git_lines('ls-tree', ['-r', '-t', tree_id]):
 
118
            # Ideally, we would use -z so we would not have to handle escaped
 
119
            # file names. But then we could not use readlines() to split the
 
120
            # data as it is read.
 
121
            permissions, type, hash_and_path = line.split(' ', 2)
 
122
            hash, name = hash_and_path.split('\t', 1)
 
123
            name = name[:-1] # strip trailing newline
 
124
            if name.startswith('"'):
 
125
                name = name[1:-1].decode('string_escape')
 
126
            name = name.decode('utf-8')
 
127
            yield permissions, type, hash, name
 
128
 
 
129
    def get_tarpipe(self, tree_id):
 
130
        cmd = self.git_command('archive', [tree_id])
 
131
        env = os.environ.copy()
 
132
        env['GIT_DIR'] = self.git_dir
 
133
        p = subprocess.Popen(cmd,
 
134
                             stdin=subprocess.PIPE,
 
135
                             stdout=subprocess.PIPE,
 
136
                             stderr=subprocess.PIPE,
 
137
                             env=env)
 
138
        p.stdin.close()
 
139
        tarpipe = TarPipe.open(mode='r|', fileobj=p.stdout)
 
140
        def close_callback():
 
141
            if p.wait() != 0:
 
142
                raise errors.GitCommandError(cmd, p.returncode,
 
143
                                             p.stderr.read().strip())
 
144
        tarpipe.set_close_callback(close_callback)
 
145
        return tarpipe
 
146
 
 
147
 
 
148
class TarPipe(tarfile.TarFile):
 
149
 
 
150
    def set_close_callback(self, close_callback):
 
151
        self.__close_callback = close_callback
 
152
 
 
153
    def close(self):
 
154
        super(TarPipe, self).close()
 
155
        self.__close_callback()