~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/store/weave.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-02-22 07:59:56 UTC
  • mfrom: (1553.5.33 bzr.mbp.locks)
  • Revision ID: pqm@pqm.ubuntu.com-20060222075956-fb281c427e571da6
add LockDir and related fixes

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