~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

- stub for revision properties

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
# TODO: Don't allow WorkingTrees to be constructed for remote branches.
18
18
 
 
19
# FIXME: I don't know if writing out the cache from the destructor is really a
 
20
# good idea, because destructors are considered poor taste in Python, and
 
21
# it's not predictable when it will be written out.
 
22
 
19
23
import os
20
 
    
 
24
import stat
 
25
import fnmatch
 
26
        
21
27
import bzrlib.tree
22
 
from errors import BzrCheckError
23
 
from trace import mutter
 
28
from bzrlib.osutils import appendpath, file_kind, isdir, splitpath
 
29
from bzrlib.errors import BzrCheckError
 
30
from bzrlib.trace import mutter
 
31
 
 
32
class TreeEntry(object):
 
33
    """An entry that implements the minium interface used by commands.
 
34
 
 
35
    This needs further inspection, it may be better to have 
 
36
    InventoryEntries without ids - though that seems wrong. For now,
 
37
    this is a parallel hierarchy to InventoryEntry, and needs to become
 
38
    one of several things: decorates to that hierarchy, children of, or
 
39
    parents of it.
 
40
    Another note is that these objects are currently only used when there is
 
41
    no InventoryEntry available - i.e. for unversioned objects.
 
42
    Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
 
43
    """
 
44
 
 
45
    def __eq__(self, other):
 
46
        # yes, this us ugly, TODO: best practice __eq__ style.
 
47
        return (isinstance(other, TreeEntry)
 
48
                and other.__class__ == self.__class__)
 
49
 
 
50
    def kind_character(self):
 
51
        return "???"
 
52
 
 
53
 
 
54
class TreeDirectory(TreeEntry):
 
55
    """See TreeEntry. This is a directory in a working tree."""
 
56
 
 
57
    def __eq__(self, other):
 
58
        return (isinstance(other, TreeDirectory)
 
59
                and other.__class__ == self.__class__)
 
60
 
 
61
    def kind_character(self):
 
62
        return "/"
 
63
 
 
64
 
 
65
class TreeFile(TreeEntry):
 
66
    """See TreeEntry. This is a regular file in a working tree."""
 
67
 
 
68
    def __eq__(self, other):
 
69
        return (isinstance(other, TreeFile)
 
70
                and other.__class__ == self.__class__)
 
71
 
 
72
    def kind_character(self):
 
73
        return ''
 
74
 
 
75
 
 
76
class TreeLink(TreeEntry):
 
77
    """See TreeEntry. This is a symlink in a working tree."""
 
78
 
 
79
    def __eq__(self, other):
 
80
        return (isinstance(other, TreeLink)
 
81
                and other.__class__ == self.__class__)
 
82
 
 
83
    def kind_character(self):
 
84
        return ''
 
85
 
24
86
 
25
87
class WorkingTree(bzrlib.tree.Tree):
26
88
    """Working copy tree.
43
105
        # in the future we might want to do this more selectively
44
106
        hc = self._hashcache = HashCache(basedir)
45
107
        hc.read()
46
 
        for path, ie in inv.iter_entries():
47
 
            hc.get_sha1(path)
 
108
        hc.scan()
48
109
 
49
110
        if hc.needs_write:
50
111
            mutter("write hc")
51
112
            hc.write()
52
 
 
 
113
            
 
114
            
 
115
    def __del__(self):
 
116
        if self._hashcache.needs_write:
 
117
            self._hashcache.write()
53
118
 
54
119
 
55
120
    def __iter__(self):
60
125
        """
61
126
        inv = self._inventory
62
127
        for path, ie in inv.iter_entries():
63
 
            if os.path.exists(self.abspath(path)):
 
128
            if bzrlib.osutils.lexists(self.abspath(path)):
64
129
                yield ie.file_id
65
130
 
66
131
 
67
132
    def __repr__(self):
68
133
        return "<%s of %s>" % (self.__class__.__name__,
69
 
                               self.basedir)
 
134
                               getattr(self, 'basedir', None))
70
135
 
71
136
 
72
137
 
74
139
        return os.path.join(self.basedir, filename)
75
140
 
76
141
    def has_filename(self, filename):
77
 
        return os.path.exists(self.abspath(filename))
 
142
        return bzrlib.osutils.lexists(self.abspath(filename))
78
143
 
79
144
    def get_file(self, file_id):
80
145
        return self.get_file_byname(self.id2path(file_id))
86
151
        ## XXX: badly named; this isn't in the store at all
87
152
        return self.abspath(self.id2path(file_id))
88
153
 
 
154
 
 
155
    def id2abspath(self, file_id):
 
156
        return self.abspath(self.id2path(file_id))
 
157
 
89
158
                
90
159
    def has_id(self, file_id):
91
160
        # files that have been deleted are excluded
93
162
        if not inv.has_id(file_id):
94
163
            return False
95
164
        path = inv.id2path(file_id)
96
 
        return os.path.exists(self.abspath(path))
 
165
        return bzrlib.osutils.lexists(self.abspath(path))
97
166
 
98
167
 
99
168
    __contains__ = has_id
100
169
    
101
170
 
102
171
    def get_file_size(self, file_id):
103
 
        # is this still called?
104
 
        raise NotImplementedError()
105
 
 
 
172
        return os.path.getsize(self.id2abspath(file_id))
106
173
 
107
174
    def get_file_sha1(self, file_id):
108
175
        path = self._inventory.id2path(file_id)
109
176
        return self._hashcache.get_sha1(path)
110
177
 
111
178
 
 
179
    def is_executable(self, file_id):
 
180
        if os.name == "nt":
 
181
            return self._inventory[file_id].executable
 
182
        else:
 
183
            path = self._inventory.id2path(file_id)
 
184
            mode = os.lstat(self.abspath(path)).st_mode
 
185
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC&mode)
 
186
 
 
187
    def get_symlink_target(self, file_id):
 
188
        return os.readlink(self.id2abspath(file_id))
 
189
 
112
190
    def file_class(self, filename):
113
191
        if self.path2id(filename):
114
192
            return 'V'
128
206
 
129
207
        Skips the control directory.
130
208
        """
131
 
        from osutils import appendpath, file_kind
132
 
        import os
133
 
 
134
209
        inv = self._inventory
135
210
 
136
211
        def descend(from_dir_relpath, from_dir_id, dp):
165
240
                                            "now of kind %r"
166
241
                                            % (fap, f_ie.kind, f_ie.file_id, fk))
167
242
 
168
 
                yield fp, c, fk, (f_ie and f_ie.file_id)
 
243
                # make a last minute entry
 
244
                if f_ie:
 
245
                    entry = f_ie
 
246
                else:
 
247
                    if fk == 'directory':
 
248
                        entry = TreeDirectory()
 
249
                    elif fk == 'file':
 
250
                        entry = TreeFile()
 
251
                    elif fk == 'symlink':
 
252
                        entry = TreeLink()
 
253
                    else:
 
254
                        entry = TreeEntry()
 
255
                
 
256
                yield fp, c, fk, (f_ie and f_ie.file_id), entry
169
257
 
170
258
                if fk != 'directory':
171
259
                    continue
187
275
            if not self.is_ignored(subp):
188
276
                yield subp
189
277
 
 
278
    def iter_conflicts(self):
 
279
        conflicted = set()
 
280
        for path in (s[0] for s in self.list_files()):
 
281
            stem = get_conflicted_stem(path)
 
282
            if stem is None:
 
283
                continue
 
284
            if stem not in conflicted:
 
285
                conflicted.add(stem)
 
286
                yield stem
190
287
 
191
288
    def extras(self):
192
289
        """Yield all unknown files in this WorkingTree.
198
295
        Currently returned depth-first, sorted by name within directories.
199
296
        """
200
297
        ## TODO: Work from given directory downwards
201
 
        from osutils import isdir, appendpath
202
 
        
203
298
        for path, dir_entry in self.inventory.directories():
204
299
            mutter("search for unknowns in %r" % path)
205
300
            dirabs = self.abspath(path)
262
357
        # Eventually it should be replaced with something more
263
358
        # accurate.
264
359
        
265
 
        import fnmatch
266
 
        from osutils import splitpath
267
 
        
268
360
        for pat in self.get_ignore_list():
269
361
            if '/' in pat or '\\' in pat:
270
362
                
283
375
                    return pat
284
376
        else:
285
377
            return None
286
 
        
287
 
 
288
 
        
289
 
        
290
 
 
 
378
 
 
379
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
 
380
def get_conflicted_stem(path):
 
381
    for suffix in CONFLICT_SUFFIXES:
 
382
        if path.endswith(suffix):
 
383
            return path[:-len(suffix)]