~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/store/weave.py

  • Committer: Martin Pool
  • Date: 2006-03-09 03:28:52 UTC
  • mto: This revision was merged to the branch mainline in revision 1602.
  • Revision ID: mbp@sourcefrog.net-20060309032852-1097eb1947d9bceb
doc

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 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
# XXX: Some consideration of the problems that might occur if there are
 
18
# files whose id differs only in case.  That should probably be forbidden.
 
19
 
 
20
 
 
21
import errno
 
22
import os
 
23
from cStringIO import StringIO
 
24
import urllib
 
25
 
 
26
from bzrlib.weavefile import read_weave, write_weave_v5
 
27
from bzrlib.weave import Weave
 
28
from bzrlib.store import TransportStore, hash_prefix
 
29
from bzrlib.atomicfile import AtomicFile
 
30
from bzrlib.errors import NoSuchFile, FileExists
 
31
from bzrlib.trace import mutter
 
32
 
 
33
 
 
34
class WeaveStore(TransportStore):
 
35
    """Collection of several weave files in a directory.
 
36
 
 
37
    This has some shortcuts for reading and writing them.
 
38
    """
 
39
    FILE_SUFFIX = '.weave'
 
40
 
 
41
    def __init__(self, transport, prefixed=False, precious=False,
 
42
                 dir_mode=None, file_mode=None):
 
43
        super(WeaveStore, self).__init__(transport,
 
44
                dir_mode=dir_mode, file_mode=file_mode,
 
45
                prefixed=prefixed, compressed=False)
 
46
        self._precious = precious
 
47
 
 
48
    def filename(self, file_id):
 
49
        """Return the path relative to the transport root."""
 
50
        if self._prefixed:
 
51
            return hash_prefix(file_id) + urllib.quote(file_id) + WeaveStore.FILE_SUFFIX
 
52
        else:
 
53
            return urllib.quote(file_id) + WeaveStore.FILE_SUFFIX
 
54
 
 
55
    def __iter__(self):
 
56
        l = len(WeaveStore.FILE_SUFFIX)
 
57
        for relpath in self._iter_files_recursive():
 
58
            if relpath.endswith(WeaveStore.FILE_SUFFIX):
 
59
                yield os.path.basename(relpath[:-l])
 
60
 
 
61
    def has_id(self, fileid):
 
62
        return self._transport.has(self.filename(fileid))
 
63
 
 
64
    def _get(self, file_id):
 
65
        return self._transport.get(self.filename(file_id))
 
66
 
 
67
    def _put(self, file_id, f):
 
68
        # less round trips to mkdir on failure than mkdir always
 
69
        try:
 
70
            return self._transport.put(self.filename(file_id), f, mode=self._file_mode)
 
71
        except NoSuchFile:
 
72
            if not self._prefixed:
 
73
                raise
 
74
            self._transport.mkdir(hash_prefix(file_id), mode=self._dir_mode)
 
75
            return self._transport.put(self.filename(file_id), f, mode=self._file_mode)
 
76
 
 
77
    def get_weave(self, file_id, transaction):
 
78
        weave = transaction.map.find_weave(file_id)
 
79
        if weave:
 
80
            mutter("cache hit in %s for %s", self, file_id)
 
81
            return weave
 
82
        w = read_weave(self._get(file_id))
 
83
        transaction.map.add_weave(file_id, w)
 
84
        transaction.register_clean(w, precious=self._precious)
 
85
        # TODO: jam 20051219 This should check if there is a prelude
 
86
        #       which is already cached, and if so, should remove it
 
87
        #       But transaction doesn't seem to have a 'remove'
 
88
        #       One workaround would be to re-add the object with
 
89
        #       the PRELUDE marker.
 
90
        return w
 
91
 
 
92
    def get_weave_prelude(self, file_id, transaction):
 
93
        weave_id = file_id
 
94
        weave = transaction.map.find_weave(weave_id)
 
95
        if weave:
 
96
            mutter("cache hit in %s for %s", self, weave_id)
 
97
            return weave
 
98
        # We want transactions to also cache preludes if that
 
99
        # is all that we are loading. So we need a unique
 
100
        # identifier, so that someone who wants the whole text
 
101
        # won't get just the prelude
 
102
        weave_id = 'PRELUDE-' + file_id
 
103
        weave = transaction.map.find_weave(weave_id)
 
104
        if weave:
 
105
            mutter("cache hit in %s for %s", self, weave_id)
 
106
            return weave
 
107
        w = read_weave(self._get(file_id), prelude=True)
 
108
        transaction.map.add_weave(weave_id, w)
 
109
        transaction.register_clean(w, precious=self._precious)
 
110
        return w
 
111
 
 
112
    def get_lines(self, file_id, rev_id, transaction):
 
113
        """Return text from a particular version of a weave.
 
114
 
 
115
        Returned as a list of lines."""
 
116
        w = self.get_weave(file_id, transaction)
 
117
        return w.get(w.lookup(rev_id))
 
118
    
 
119
    def get_weave_prelude_or_empty(self, file_id, transaction):
 
120
        """cheap version that reads the prelude but not the lines
 
121
        """
 
122
        try:
 
123
            return self.get_weave_prelude(file_id, transaction)
 
124
        except NoSuchFile:
 
125
            # We can cache here, because we know that there
 
126
            # is no complete object, since we got NoSuchFile
 
127
            weave = Weave(weave_name=file_id)
 
128
            transaction.map.add_weave(file_id, weave)
 
129
            transaction.register_clean(weave, precious=self._precious)
 
130
            return weave
 
131
 
 
132
    def get_weave_or_empty(self, file_id, transaction):
 
133
        """Return a weave, or an empty one if it doesn't exist.""" 
 
134
        try:
 
135
            return self.get_weave(file_id, transaction)
 
136
        except NoSuchFile:
 
137
            weave = Weave(weave_name=file_id)
 
138
            transaction.map.add_weave(file_id, weave)
 
139
            transaction.register_clean(weave, precious=self._precious)
 
140
            return weave
 
141
 
 
142
    def put_weave(self, file_id, weave, transaction):
 
143
        """Write back a modified weave"""
 
144
        transaction.register_dirty(weave)
 
145
        # TODO FOR WRITE TRANSACTIONS: this should be done in a callback
 
146
        # from the transaction, when it decides to save.
 
147
        sio = StringIO()
 
148
        write_weave_v5(weave, sio)
 
149
        sio.seek(0)
 
150
        self._put(file_id, sio)
 
151
 
 
152
    def add_text(self, file_id, rev_id, new_lines, parents, transaction):
 
153
        w = self.get_weave_or_empty(file_id, transaction)
 
154
        parent_idxs = map(w.lookup, parents)
 
155
        w.add(rev_id, parent_idxs, new_lines)
 
156
        self.put_weave(file_id, w, transaction)
 
157
        
 
158
    def add_identical_text(self, file_id, old_rev_id, new_rev_id, parents,
 
159
                           transaction):
 
160
        w = self.get_weave_or_empty(file_id, transaction)
 
161
        parent_idxs = map(w.lookup, parents)
 
162
        w.add_identical(old_rev_id, new_rev_id, parent_idxs)
 
163
        self.put_weave(file_id, w, transaction)
 
164
     
 
165
    def copy_multi(self, from_store, file_ids, pb=None):
 
166
        assert isinstance(from_store, WeaveStore)
 
167
        for count, f in enumerate(file_ids):
 
168
            mutter("copy weave {%s} into %s", f, self)
 
169
            if pb:
 
170
                pb.update('copy', count, len(file_ids))
 
171
            self._put(f, from_store._get(f))
 
172
        if pb:
 
173
            pb.clear()