~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/store/weave.py

Adding sha1 check when weave extracts a text.

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
        if self._prefixed:
 
71
            try:
 
72
                self._transport.mkdir(hash_prefix(file_id), mode=self._dir_mode)
 
73
            except FileExists:
 
74
                pass
 
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
        return w
 
86
 
 
87
    def get_lines(self, file_id, rev_id, transaction):
 
88
        """Return text from a particular version of a weave.
 
89
 
 
90
        Returned as a list of lines."""
 
91
        w = self.get_weave(file_id, transaction)
 
92
        return w.get(w.lookup(rev_id))
 
93
    
 
94
    def get_weave_prelude_or_empty(self, file_id, transaction):
 
95
        """cheap version that reads the prelude but not the lines
 
96
        """
 
97
        weave = transaction.map.find_weave(file_id)
 
98
        if weave:
 
99
            mutter("cache hit in %s for %s", self, file_id)
 
100
            return weave
 
101
        try:
 
102
            return read_weave(self._get(file_id),prelude=True)
 
103
        except NoSuchFile:
 
104
            return Weave(weave_name=file_id)
 
105
 
 
106
    def get_weave_or_empty(self, file_id, transaction):
 
107
        """Return a weave, or an empty one if it doesn't exist.""" 
 
108
        try:
 
109
            return self.get_weave(file_id, transaction)
 
110
        except NoSuchFile:
 
111
            weave = Weave(weave_name=file_id)
 
112
            transaction.map.add_weave(file_id, weave)
 
113
            transaction.register_clean(weave, precious=self._precious)
 
114
            return weave
 
115
 
 
116
    def put_weave(self, file_id, weave, transaction):
 
117
        """Write back a modified weave"""
 
118
        transaction.register_dirty(weave)
 
119
        # TODO FOR WRITE TRANSACTIONS: this should be done in a callback
 
120
        # from the transaction, when it decides to save.
 
121
        sio = StringIO()
 
122
        write_weave_v5(weave, sio)
 
123
        sio.seek(0)
 
124
        self._put(file_id, sio)
 
125
 
 
126
    def add_text(self, file_id, rev_id, new_lines, parents, transaction):
 
127
        w = self.get_weave_or_empty(file_id, transaction)
 
128
        parent_idxs = map(w.lookup, parents)
 
129
        w.add(rev_id, parent_idxs, new_lines)
 
130
        self.put_weave(file_id, w, transaction)
 
131
        
 
132
    def add_identical_text(self, file_id, old_rev_id, new_rev_id, parents,
 
133
                           transaction):
 
134
        w = self.get_weave_or_empty(file_id, transaction)
 
135
        parent_idxs = map(w.lookup, parents)
 
136
        w.add_identical(old_rev_id, new_rev_id, parent_idxs)
 
137
        self.put_weave(file_id, w, transaction)
 
138
     
 
139
    def copy_multi(self, from_store, file_ids):
 
140
        assert isinstance(from_store, WeaveStore)
 
141
        for f in file_ids:
 
142
            mutter("copy weave {%s} into %s", f, self)
 
143
            self._put(f, from_store._get(f))