/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Robert Collins
  • Date: 2005-10-17 23:13:00 UTC
  • mto: This revision was merged to the branch mainline in revision 1462.
  • Revision ID: robertc@robertcollins.net-20051017231300-e1c9e931bcfacd6a
Branch.open_containing now returns a tuple (Branch, relative-path).

This allows direct access to the common case of 'get me this file
from its branch'. (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
# it's not predictable when it will be written out.
22
22
 
23
23
import os
24
 
    
 
24
import stat
 
25
import fnmatch
 
26
 
 
27
from bzrlib.branch import Branch
25
28
import bzrlib.tree
26
 
from errors import BzrCheckError
27
 
from trace import mutter
 
29
from bzrlib.osutils import appendpath, file_kind, isdir, splitpath, relpath
 
30
from bzrlib.errors import BzrCheckError
 
31
from bzrlib.trace import mutter
 
32
 
 
33
class TreeEntry(object):
 
34
    """An entry that implements the minium interface used by commands.
 
35
 
 
36
    This needs further inspection, it may be better to have 
 
37
    InventoryEntries without ids - though that seems wrong. For now,
 
38
    this is a parallel hierarchy to InventoryEntry, and needs to become
 
39
    one of several things: decorates to that hierarchy, children of, or
 
40
    parents of it.
 
41
    Another note is that these objects are currently only used when there is
 
42
    no InventoryEntry available - i.e. for unversioned objects.
 
43
    Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
 
44
    """
 
45
 
 
46
    def __eq__(self, other):
 
47
        # yes, this us ugly, TODO: best practice __eq__ style.
 
48
        return (isinstance(other, TreeEntry)
 
49
                and other.__class__ == self.__class__)
 
50
 
 
51
    def kind_character(self):
 
52
        return "???"
 
53
 
 
54
 
 
55
class TreeDirectory(TreeEntry):
 
56
    """See TreeEntry. This is a directory in a working tree."""
 
57
 
 
58
    def __eq__(self, other):
 
59
        return (isinstance(other, TreeDirectory)
 
60
                and other.__class__ == self.__class__)
 
61
 
 
62
    def kind_character(self):
 
63
        return "/"
 
64
 
 
65
 
 
66
class TreeFile(TreeEntry):
 
67
    """See TreeEntry. This is a regular file in a working tree."""
 
68
 
 
69
    def __eq__(self, other):
 
70
        return (isinstance(other, TreeFile)
 
71
                and other.__class__ == self.__class__)
 
72
 
 
73
    def kind_character(self):
 
74
        return ''
 
75
 
 
76
 
 
77
class TreeLink(TreeEntry):
 
78
    """See TreeEntry. This is a symlink in a working tree."""
 
79
 
 
80
    def __eq__(self, other):
 
81
        return (isinstance(other, TreeLink)
 
82
                and other.__class__ == self.__class__)
 
83
 
 
84
    def kind_character(self):
 
85
        return ''
 
86
 
28
87
 
29
88
class WorkingTree(bzrlib.tree.Tree):
30
89
    """Working copy tree.
35
94
    It is possible for a `WorkingTree` to have a filename which is
36
95
    not listed in the Inventory and vice versa.
37
96
    """
38
 
    def __init__(self, basedir, inv):
 
97
    def __init__(self, basedir, branch=None):
 
98
        """Construct a WorkingTree for basedir.
 
99
 
 
100
        If the branch is not supplied, it is opened automatically.
 
101
        If the branch is supplied, it must be the branch for this basedir.
 
102
        (branch.base is not cross checked, because for remote branches that
 
103
        would be meaningless).
 
104
        """
39
105
        from bzrlib.hashcache import HashCache
40
106
        from bzrlib.trace import note, mutter
41
107
 
42
 
        self._inventory = inv
 
108
        if branch is None:
 
109
            branch = Branch.open(basedir)
 
110
        self._inventory = branch.inventory
 
111
        self.path2id = self._inventory.path2id
 
112
        self.branch = branch
43
113
        self.basedir = basedir
44
 
        self.path2id = inv.path2id
45
114
 
46
115
        # update the whole cache up front and write to disk if anything changed;
47
116
        # in the future we might want to do this more selectively
67
136
        """
68
137
        inv = self._inventory
69
138
        for path, ie in inv.iter_entries():
70
 
            if os.path.exists(self.abspath(path)):
 
139
            if bzrlib.osutils.lexists(self.abspath(path)):
71
140
                yield ie.file_id
72
141
 
73
142
 
80
149
    def abspath(self, filename):
81
150
        return os.path.join(self.basedir, filename)
82
151
 
 
152
    def relpath(self, abspath):
 
153
        """Return the local path portion from a given absolute path."""
 
154
        return relpath(self.basedir, abspath)
 
155
 
83
156
    def has_filename(self, filename):
84
 
        return os.path.exists(self.abspath(filename))
 
157
        return bzrlib.osutils.lexists(self.abspath(filename))
85
158
 
86
159
    def get_file(self, file_id):
87
160
        return self.get_file_byname(self.id2path(file_id))
93
166
        ## XXX: badly named; this isn't in the store at all
94
167
        return self.abspath(self.id2path(file_id))
95
168
 
 
169
 
 
170
    def id2abspath(self, file_id):
 
171
        return self.abspath(self.id2path(file_id))
 
172
 
96
173
                
97
174
    def has_id(self, file_id):
98
175
        # files that have been deleted are excluded
100
177
        if not inv.has_id(file_id):
101
178
            return False
102
179
        path = inv.id2path(file_id)
103
 
        return os.path.exists(self.abspath(path))
 
180
        return bzrlib.osutils.lexists(self.abspath(path))
104
181
 
 
182
    def has_or_had_id(self, file_id):
 
183
        if file_id == self.inventory.root.file_id:
 
184
            return True
 
185
        return self.inventory.has_id(file_id)
105
186
 
106
187
    __contains__ = has_id
107
188
    
108
189
 
109
190
    def get_file_size(self, file_id):
110
 
        # is this still called?
111
 
        raise NotImplementedError()
112
 
 
 
191
        return os.path.getsize(self.id2abspath(file_id))
113
192
 
114
193
    def get_file_sha1(self, file_id):
115
194
        path = self._inventory.id2path(file_id)
116
195
        return self._hashcache.get_sha1(path)
117
196
 
118
197
 
 
198
    def is_executable(self, file_id):
 
199
        if os.name == "nt":
 
200
            return self._inventory[file_id].executable
 
201
        else:
 
202
            path = self._inventory.id2path(file_id)
 
203
            mode = os.lstat(self.abspath(path)).st_mode
 
204
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC&mode)
 
205
 
 
206
    def get_symlink_target(self, file_id):
 
207
        return os.readlink(self.id2abspath(file_id))
 
208
 
119
209
    def file_class(self, filename):
120
210
        if self.path2id(filename):
121
211
            return 'V'
135
225
 
136
226
        Skips the control directory.
137
227
        """
138
 
        from osutils import appendpath, file_kind
139
 
        import os
140
 
 
141
228
        inv = self._inventory
142
229
 
143
230
        def descend(from_dir_relpath, from_dir_id, dp):
172
259
                                            "now of kind %r"
173
260
                                            % (fap, f_ie.kind, f_ie.file_id, fk))
174
261
 
175
 
                yield fp, c, fk, (f_ie and f_ie.file_id)
 
262
                # make a last minute entry
 
263
                if f_ie:
 
264
                    entry = f_ie
 
265
                else:
 
266
                    if fk == 'directory':
 
267
                        entry = TreeDirectory()
 
268
                    elif fk == 'file':
 
269
                        entry = TreeFile()
 
270
                    elif fk == 'symlink':
 
271
                        entry = TreeLink()
 
272
                    else:
 
273
                        entry = TreeEntry()
 
274
                
 
275
                yield fp, c, fk, (f_ie and f_ie.file_id), entry
176
276
 
177
277
                if fk != 'directory':
178
278
                    continue
194
294
            if not self.is_ignored(subp):
195
295
                yield subp
196
296
 
 
297
    def iter_conflicts(self):
 
298
        conflicted = set()
 
299
        for path in (s[0] for s in self.list_files()):
 
300
            stem = get_conflicted_stem(path)
 
301
            if stem is None:
 
302
                continue
 
303
            if stem not in conflicted:
 
304
                conflicted.add(stem)
 
305
                yield stem
197
306
 
198
307
    def extras(self):
199
308
        """Yield all unknown files in this WorkingTree.
205
314
        Currently returned depth-first, sorted by name within directories.
206
315
        """
207
316
        ## TODO: Work from given directory downwards
208
 
        from osutils import isdir, appendpath
209
 
        
210
317
        for path, dir_entry in self.inventory.directories():
211
318
            mutter("search for unknowns in %r" % path)
212
319
            dirabs = self.abspath(path)
269
376
        # Eventually it should be replaced with something more
270
377
        # accurate.
271
378
        
272
 
        import fnmatch
273
 
        from osutils import splitpath
274
 
        
275
379
        for pat in self.get_ignore_list():
276
380
            if '/' in pat or '\\' in pat:
277
381
                
290
394
                    return pat
291
395
        else:
292
396
            return None
293
 
        
 
 
b'\\ No newline at end of file'
 
397
 
 
398
    def kind(self, file_id):
 
399
        return file_kind(self.id2abspath(file_id))
 
400
 
 
401
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
 
402
def get_conflicted_stem(path):
 
403
    for suffix in CONFLICT_SUFFIXES:
 
404
        if path.endswith(suffix):
 
405
            return path[:-len(suffix)]