~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/store/versioned/weave.py

  • Committer: Robert Collins
  • Date: 2006-03-01 08:40:35 UTC
  • mto: (1594.2.4 integration)
  • mto: This revision was merged to the branch mainline in revision 1596.
  • Revision ID: robertc@robertcollins.net-20060301084035-ce00abd11fe4da31
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/python
 
2
 
 
3
# Copyright (C) 2005 Canonical Ltd
 
4
 
 
5
# This program is free software; you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation; either version 2 of the License, or
 
8
# (at your option) any later version.
 
9
 
 
10
# This program is distributed in the hope that it will be useful,
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
# GNU General Public License for more details.
 
14
 
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with this program; if not, write to the Free Software
 
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
18
 
 
19
# XXX: Some consideration of the problems that might occur if there are
 
20
# files whose id differs only in case.  That should probably be forbidden.
 
21
 
 
22
 
 
23
import errno
 
24
import os
 
25
from cStringIO import StringIO
 
26
import urllib
 
27
 
 
28
from bzrlib.weavefile import read_weave, write_weave_v5
 
29
from bzrlib.weave import Weave, WeaveFile
 
30
from bzrlib.store import TransportStore, hash_prefix
 
31
from bzrlib.atomicfile import AtomicFile
 
32
from bzrlib.errors import NoSuchFile, FileExists
 
33
from bzrlib.symbol_versioning import *
 
34
from bzrlib.trace import mutter
 
35
 
 
36
 
 
37
class WeaveStore(TransportStore):
 
38
    """Collection of several weave files in a directory.
 
39
 
 
40
    This has some shortcuts for reading and writing them.
 
41
    """
 
42
    FILE_SUFFIX = '.weave'
 
43
 
 
44
    def __init__(self, transport, prefixed=False, precious=False,
 
45
                 dir_mode=None, file_mode=None):
 
46
        super(WeaveStore, self).__init__(transport,
 
47
                dir_mode=dir_mode, file_mode=file_mode,
 
48
                prefixed=prefixed, compressed=False)
 
49
        self._precious = precious
 
50
 
 
51
    def filename(self, file_id):
 
52
        """Return the path relative to the transport root."""
 
53
        if self._prefixed:
 
54
            return hash_prefix(file_id) + urllib.quote(file_id) + WeaveStore.FILE_SUFFIX
 
55
        else:
 
56
            return urllib.quote(file_id) + WeaveStore.FILE_SUFFIX
 
57
 
 
58
    def __iter__(self):
 
59
        l = len(WeaveStore.FILE_SUFFIX)
 
60
        for relpath in self._iter_files_recursive():
 
61
            if relpath.endswith(WeaveStore.FILE_SUFFIX):
 
62
                yield os.path.basename(relpath[:-l])
 
63
 
 
64
    def has_id(self, fileid):
 
65
        return self._transport.has(self.filename(fileid))
 
66
 
 
67
    def _get(self, file_id):
 
68
        return self._transport.get(self.filename(file_id))
 
69
 
 
70
    def _put(self, file_id, f):
 
71
        # less round trips to mkdir on failure than mkdir always
 
72
        try:
 
73
            return self._transport.put(self.filename(file_id), f, mode=self._file_mode)
 
74
        except NoSuchFile:
 
75
            if not self._prefixed:
 
76
                raise
 
77
            self._transport.mkdir(hash_prefix(file_id), mode=self._dir_mode)
 
78
            return self._transport.put(self.filename(file_id), f, mode=self._file_mode)
 
79
 
 
80
    def get_weave(self, file_id, transaction):
 
81
        weave = transaction.map.find_weave(file_id)
 
82
        if weave:
 
83
            mutter("cache hit in %s for %s", self, file_id)
 
84
            return weave
 
85
        w = WeaveFile(self.filename(file_id), self._transport, self._file_mode)
 
86
        transaction.map.add_weave(file_id, w)
 
87
        transaction.register_clean(w, precious=self._precious)
 
88
        # TODO: jam 20051219 This should check if there is a prelude
 
89
        #       which is already cached, and if so, should remove it
 
90
        #       But transaction doesn't seem to have a 'remove'
 
91
        #       One workaround would be to re-add the object with
 
92
        #       the PRELUDE marker.
 
93
        return w
 
94
 
 
95
    def get_weave_prelude(self, file_id, transaction):
 
96
        weave_id = file_id
 
97
        weave = transaction.map.find_weave(weave_id)
 
98
        if weave:
 
99
            mutter("cache hit in %s for %s", self, weave_id)
 
100
            return weave
 
101
        w = read_weave(self._get(file_id), prelude=True)
 
102
        # no point caching the prelude: any repeat action will need the real 
 
103
        # thing
 
104
        return w
 
105
 
 
106
    def get_lines(self, file_id, rev_id, transaction):
 
107
        """Return text from a particular version of a weave.
 
108
 
 
109
        Returned as a list of lines."""
 
110
        w = self.get_weave(file_id, transaction)
 
111
        return w.get(rev_id)
 
112
    
 
113
    def get_weave_prelude_or_empty(self, file_id, transaction):
 
114
        """cheap version that reads the prelude but not the lines
 
115
        """
 
116
        try:
 
117
            return self.get_weave_prelude(file_id, transaction)
 
118
        except NoSuchFile:
 
119
            # returns more than needed - harmless as its empty.
 
120
            return self._new_weave(file_id, transaction)
 
121
 
 
122
    def _new_weave(self, file_id, transaction):
 
123
        """Make a new weave for file_id and return it."""
 
124
        weave = WeaveFile(self.filename(file_id), self._transport, self._file_mode)
 
125
        # ensure that the directories are created.
 
126
        # this is so that weave does not encounter ENOTDIR etc.
 
127
        weave_stream = self._weave_to_stream(weave)
 
128
        self._put(file_id, weave_stream)
 
129
        transaction.map.add_weave(file_id, weave)
 
130
        transaction.register_clean(weave, precious=self._precious)
 
131
        return weave
 
132
 
 
133
    def get_weave_or_empty(self, file_id, transaction):
 
134
        """Return a weave, or an empty one if it doesn't exist.""" 
 
135
        try:
 
136
            return self.get_weave(file_id, transaction)
 
137
        except NoSuchFile:
 
138
            return self._new_weave(file_id, transaction)
 
139
 
 
140
    @deprecated_method(zero_eight)
 
141
    def put_weave(self, file_id, weave, transaction):
 
142
        """This is a deprecated API: It writes an entire collection of ids out.
 
143
        
 
144
        This became inappropriate when we made a versioned file api which
 
145
        tracks the state of the collection of versions for a single id.
 
146
        
 
147
        Its maintained for backwards compatability but will only work on
 
148
        weave stores - pre 0.8 repositories.
 
149
        """
 
150
        self._put_weave(self, file_id, weave, transaction)
 
151
 
 
152
    def _put_weave(self, file_id, weave, transaction):
 
153
        """Preserved here for upgrades-to-weaves to use."""
 
154
        transaction.register_dirty(weave)
 
155
        weave_stream = self._weave_to_stream(weave)
 
156
        self._put(file_id, weave_stream)
 
157
 
 
158
    def _weave_to_stream(self, weave):
 
159
        """Make a stream from a weave."""
 
160
        sio = StringIO()
 
161
        write_weave_v5(weave, sio)
 
162
        sio.seek(0)
 
163
        return sio
 
164
 
 
165
    @deprecated_method(zero_eight)
 
166
    def add_text(self, file_id, rev_id, new_lines, parents, transaction):
 
167
        """This method was a shorthand for 
 
168
 
 
169
        vfile = self.get_weave_or_empty(file_id, transaction)
 
170
        vfile.add_lines(rev_id, parents, new_lines)
 
171
        """
 
172
        vfile = self.get_weave_or_empty(file_id, transaction)
 
173
        vfile.add_lines(rev_id, parents, new_lines)
 
174
        
 
175
    @deprecated_method(zero_eight)
 
176
    def add_identical_text(self, file_id, old_rev_id, new_rev_id, parents,
 
177
                           transaction):
 
178
        """This method was a shorthand for
 
179
 
 
180
        vfile = self.get_weave_or_empty(file_id, transaction)
 
181
        vfile.clone_text(new_rev_id, old_rev_id, parents)
 
182
        """
 
183
        vfile = self.get_weave_or_empty(file_id, transaction)
 
184
        vfile.clone_text(new_rev_id, old_rev_id, parents)
 
185
     
 
186
    def copy_multi(self, from_store, file_ids, pb=None, from_transaction=None):
 
187
        assert isinstance(from_store, WeaveStore)
 
188
        for count, f in enumerate(file_ids):
 
189
            mutter("copy weave {%s} into %s", f, self)
 
190
            if pb:
 
191
                pb.update('copy', count, len(file_ids))
 
192
            # if we have it in cache, its faster.
 
193
            if from_transaction and from_transaction.map.find_weave(f):
 
194
                mutter("cache hit in %s for %s", from_store, f)
 
195
                weave = from_transaction.map.find_weave(f)
 
196
                weave_stream = self._weave_to_stream(weave)
 
197
                self._put(f, weave_stream)
 
198
            else:
 
199
                self._put(f, from_store._get(f))
 
200
        if pb:
 
201
            pb.clear()