/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
453 by Martin Pool
- Split WorkingTree into its own file
1
# Copyright (C) 2005 Canonical Ltd
2
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
1185.16.72 by Martin Pool
[merge] from robert and fix up tests
17
"""WorkingTree object and friends.
18
19
A WorkingTree represents the editable working copy of a branch.
20
Operations which represent the WorkingTree are also done here, 
21
such as renaming or adding files.  The WorkingTree has an inventory 
22
which is updated by these operations.  A commit produces a 
23
new revision based on the workingtree and its inventory.
24
25
At the moment every WorkingTree has its own branch.  Remote
26
WorkingTrees aren't supported.
27
28
To get a WorkingTree, call Branch.working_tree():
29
"""
30
31
32
# TODO: Don't allow WorkingTrees to be constructed for remote branches if 
33
# they don't work.
453 by Martin Pool
- Split WorkingTree into its own file
34
956 by Martin Pool
doc
35
# FIXME: I don't know if writing out the cache from the destructor is really a
1185.16.72 by Martin Pool
[merge] from robert and fix up tests
36
# good idea, because destructors are considered poor taste in Python, and it's
37
# not predictable when it will be written out.
38
39
# TODO: Give the workingtree sole responsibility for the working inventory;
40
# remove the variable and references to it from the branch.  This may require
41
# updating the commit code so as to update the inventory within the working
42
# copy, and making sure there's only one WorkingTree for any directory on disk.
43
# At the momenthey may alias the inventory and have old copies of it in memory.
956 by Martin Pool
doc
44
1508.1.8 by Robert Collins
move move() from Branch to WorkingTree.
45
from copy import deepcopy
453 by Martin Pool
- Split WorkingTree into its own file
46
import os
1398 by Robert Collins
integrate in Gustavos x-bit patch
47
import stat
1140 by Martin Pool
- lift out import statements within WorkingTree
48
import fnmatch
1457.1.1 by Robert Collins
rather than getting the branch inventory, WorkingTree can use the whole Branch, or make its own.
49
 
1508.1.5 by Robert Collins
Move add from Branch to WorkingTree.
50
from bzrlib.branch import (Branch,
51
                           is_control_file,
52
                           needs_read_lock,
53
                           needs_write_lock,
54
                           quotefn)
55
from bzrlib.errors import (BzrCheckError,
1508.1.7 by Robert Collins
Move rename_one from Branch to WorkingTree. (Robert Collins).
56
                           BzrError,
1508.1.5 by Robert Collins
Move add from Branch to WorkingTree.
57
                           DivergedBranches,
1185.33.59 by Martin Pool
[patch] keep a cached basis inventory (Johan Rydberg)
58
                           WeaveRevisionNotPresent,
1508.1.5 by Robert Collins
Move add from Branch to WorkingTree.
59
                           NotBranchError,
60
                           NotVersionedError)
61
from bzrlib.inventory import InventoryEntry
1457.1.11 by Robert Collins
Move _write_inventory to WorkingTree.
62
from bzrlib.osutils import (appendpath,
1508.1.5 by Robert Collins
Move add from Branch to WorkingTree.
63
                            compact_date,
1457.1.11 by Robert Collins
Move _write_inventory to WorkingTree.
64
                            file_kind,
65
                            isdir,
66
                            pumpfile,
67
                            splitpath,
1508.1.5 by Robert Collins
Move add from Branch to WorkingTree.
68
                            rand_bytes,
1508.1.10 by Robert Collins
bzrlib.add.smart_add_branch is now smart_add_tree. (Robert Collins)
69
                            realpath,
1508.1.7 by Robert Collins
Move rename_one from Branch to WorkingTree. (Robert Collins).
70
                            relpath,
71
                            rename)
1185.33.92 by Martin Pool
[patch] fix for 'bzr rm -v' (Wouter van Heyst)
72
from bzrlib.textui import show_status
1508.1.5 by Robert Collins
Move add from Branch to WorkingTree.
73
import bzrlib.tree
1140 by Martin Pool
- lift out import statements within WorkingTree
74
from bzrlib.trace import mutter
1497 by Robert Collins
Move Branch.read_working_inventory to WorkingTree.
75
import bzrlib.xml5
453 by Martin Pool
- Split WorkingTree into its own file
76
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
77
1508.1.5 by Robert Collins
Move add from Branch to WorkingTree.
78
def gen_file_id(name):
79
    """Return new file id.
80
81
    This should probably generate proper UUIDs, but for the moment we
82
    cope with just randomness because running uuidgen every time is
83
    slow."""
84
    import re
85
    from binascii import hexlify
86
    from time import time
87
88
    # get last component
89
    idx = name.rfind('/')
90
    if idx != -1:
91
        name = name[idx+1 : ]
92
    idx = name.rfind('\\')
93
    if idx != -1:
94
        name = name[idx+1 : ]
95
96
    # make it not a hidden file
97
    name = name.lstrip('.')
98
99
    # remove any wierd characters; we don't escape them but rather
100
    # just pull them out
101
    name = re.sub(r'[^\w.]', '', name)
102
103
    s = hexlify(rand_bytes(8))
104
    return '-'.join((name, compact_date(time()), s))
105
106
107
def gen_root_id():
108
    """Return a new tree-root file id."""
109
    return gen_file_id('TREE_ROOT')
110
111
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
112
class TreeEntry(object):
113
    """An entry that implements the minium interface used by commands.
114
115
    This needs further inspection, it may be better to have 
116
    InventoryEntries without ids - though that seems wrong. For now,
117
    this is a parallel hierarchy to InventoryEntry, and needs to become
118
    one of several things: decorates to that hierarchy, children of, or
119
    parents of it.
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
120
    Another note is that these objects are currently only used when there is
121
    no InventoryEntry available - i.e. for unversioned objects.
122
    Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
123
    """
124
 
125
    def __eq__(self, other):
126
        # yes, this us ugly, TODO: best practice __eq__ style.
127
        return (isinstance(other, TreeEntry)
128
                and other.__class__ == self.__class__)
129
 
130
    def kind_character(self):
131
        return "???"
132
133
134
class TreeDirectory(TreeEntry):
135
    """See TreeEntry. This is a directory in a working tree."""
136
137
    def __eq__(self, other):
138
        return (isinstance(other, TreeDirectory)
139
                and other.__class__ == self.__class__)
140
141
    def kind_character(self):
142
        return "/"
143
144
145
class TreeFile(TreeEntry):
146
    """See TreeEntry. This is a regular file in a working tree."""
147
148
    def __eq__(self, other):
149
        return (isinstance(other, TreeFile)
150
                and other.__class__ == self.__class__)
151
152
    def kind_character(self):
153
        return ''
154
155
156
class TreeLink(TreeEntry):
157
    """See TreeEntry. This is a symlink in a working tree."""
158
159
    def __eq__(self, other):
160
        return (isinstance(other, TreeLink)
161
                and other.__class__ == self.__class__)
162
163
    def kind_character(self):
164
        return ''
165
166
453 by Martin Pool
- Split WorkingTree into its own file
167
class WorkingTree(bzrlib.tree.Tree):
168
    """Working copy tree.
169
170
    The inventory is held in the `Branch` working-inventory, and the
171
    files are in a directory on disk.
172
173
    It is possible for a `WorkingTree` to have a filename which is
174
    not listed in the Inventory and vice versa.
175
    """
1442.1.65 by Robert Collins
Branch.remove has been moved to WorkingTree.
176
1185.33.66 by Martin Pool
[patch] use unicode literals for all hardcoded paths (Alexander Belchenko)
177
    def __init__(self, basedir=u'.', branch=None):
1457.1.1 by Robert Collins
rather than getting the branch inventory, WorkingTree can use the whole Branch, or make its own.
178
        """Construct a WorkingTree for basedir.
179
180
        If the branch is not supplied, it is opened automatically.
181
        If the branch is supplied, it must be the branch for this basedir.
182
        (branch.base is not cross checked, because for remote branches that
183
        would be meaningless).
184
        """
866 by Martin Pool
- use new path-based hashcache for WorkingTree- squash mtime/ctime to whole seconds- update and if necessary write out hashcache when WorkingTree object is created.
185
        from bzrlib.hashcache import HashCache
186
        from bzrlib.trace import note, mutter
1185.16.72 by Martin Pool
[merge] from robert and fix up tests
187
        assert isinstance(basedir, basestring), \
188
            "base directory %r is not a string" % basedir
1457.1.1 by Robert Collins
rather than getting the branch inventory, WorkingTree can use the whole Branch, or make its own.
189
        if branch is None:
190
            branch = Branch.open(basedir)
1185.16.72 by Martin Pool
[merge] from robert and fix up tests
191
        assert isinstance(branch, Branch), \
192
            "branch %r is not a Branch" % branch
1457.1.1 by Robert Collins
rather than getting the branch inventory, WorkingTree can use the whole Branch, or make its own.
193
        self.branch = branch
1508.1.10 by Robert Collins
bzrlib.add.smart_add_branch is now smart_add_tree. (Robert Collins)
194
        self.basedir = realpath(basedir)
195
196
        self._set_inventory(self.read_working_inventory())
866 by Martin Pool
- use new path-based hashcache for WorkingTree- squash mtime/ctime to whole seconds- update and if necessary write out hashcache when WorkingTree object is created.
197
198
        # update the whole cache up front and write to disk if anything changed;
199
        # in the future we might want to do this more selectively
1467 by Robert Collins
WorkingTree.__del__ has been removed.
200
        # two possible ways offer themselves : in self._unlock, write the cache
201
        # if needed, or, when the cache sees a change, append it to the hash
202
        # cache file, and have the parser take the most recent entry for a
203
        # given path only.
866 by Martin Pool
- use new path-based hashcache for WorkingTree- squash mtime/ctime to whole seconds- update and if necessary write out hashcache when WorkingTree object is created.
204
        hc = self._hashcache = HashCache(basedir)
205
        hc.read()
954 by Martin Pool
- separate out code that just scans the hash cache to find files that are possibly
206
        hc.scan()
866 by Martin Pool
- use new path-based hashcache for WorkingTree- squash mtime/ctime to whole seconds- update and if necessary write out hashcache when WorkingTree object is created.
207
208
        if hc.needs_write:
209
            mutter("write hc")
210
            hc.write()
453 by Martin Pool
- Split WorkingTree into its own file
211
1508.1.10 by Robert Collins
bzrlib.add.smart_add_branch is now smart_add_tree. (Robert Collins)
212
    def _set_inventory(self, inv):
213
        self._inventory = inv
214
        self.path2id = self._inventory.path2id
215
1508.1.1 by Robert Collins
Provide a open_containing for WorkingTree.
216
    @staticmethod
217
    def open_containing(path=None):
218
        """Open an existing working tree which has its root about path.
219
        
220
        This probes for a working tree at path and searches upwards from there.
221
222
        Basically we keep looking up until we find the control directory or
223
        run into /.  If there isn't one, raises NotBranchError.
224
        TODO: give this a new exception.
225
        If there is one, it is returned, along with the unused portion of path.
226
        """
227
        if path is None:
228
            path = os.getcwdu()
1508.1.3 by Robert Collins
Do not consider urls to be relative paths within working trees.
229
        else:
230
            # sanity check.
231
            if path.find('://') != -1:
232
                raise NotBranchError(path=path)
1508.1.2 by Robert Collins
Use abspath not realpath in WorkingTree.open_containing, to avoid symlink resolution lies.
233
        path = os.path.abspath(path)
1185.33.66 by Martin Pool
[patch] use unicode literals for all hardcoded paths (Alexander Belchenko)
234
        tail = u''
1508.1.1 by Robert Collins
Provide a open_containing for WorkingTree.
235
        while True:
236
            try:
237
                return WorkingTree(path), tail
238
            except NotBranchError:
239
                pass
240
            if tail:
241
                tail = os.path.join(os.path.basename(path), tail)
242
            else:
243
                tail = os.path.basename(path)
244
            path = os.path.dirname(path)
245
            # FIXME: top in windows is indicated how ???
246
            if path == os.path.sep:
247
                # reached the root, whatever that may be
248
                raise NotBranchError(path=path)
249
462 by Martin Pool
- New form 'file_id in tree' to check if the file is present
250
    def __iter__(self):
251
        """Iterate through file_ids for this tree.
252
253
        file_ids are in a WorkingTree if they are in the working inventory
254
        and the working file exists.
255
        """
256
        inv = self._inventory
866 by Martin Pool
- use new path-based hashcache for WorkingTree- squash mtime/ctime to whole seconds- update and if necessary write out hashcache when WorkingTree object is created.
257
        for path, ie in inv.iter_entries():
1092.2.6 by Robert Collins
symlink support updated to work
258
            if bzrlib.osutils.lexists(self.abspath(path)):
866 by Martin Pool
- use new path-based hashcache for WorkingTree- squash mtime/ctime to whole seconds- update and if necessary write out hashcache when WorkingTree object is created.
259
                yield ie.file_id
462 by Martin Pool
- New form 'file_id in tree' to check if the file is present
260
453 by Martin Pool
- Split WorkingTree into its own file
261
    def __repr__(self):
262
        return "<%s of %s>" % (self.__class__.__name__,
954 by Martin Pool
- separate out code that just scans the hash cache to find files that are possibly
263
                               getattr(self, 'basedir', None))
453 by Martin Pool
- Split WorkingTree into its own file
264
265
    def abspath(self, filename):
266
        return os.path.join(self.basedir, filename)
267
1457.1.3 by Robert Collins
make Branch.relpath delegate to the working tree.
268
    def relpath(self, abspath):
269
        """Return the local path portion from a given absolute path."""
270
        return relpath(self.basedir, abspath)
271
453 by Martin Pool
- Split WorkingTree into its own file
272
    def has_filename(self, filename):
1092.2.6 by Robert Collins
symlink support updated to work
273
        return bzrlib.osutils.lexists(self.abspath(filename))
453 by Martin Pool
- Split WorkingTree into its own file
274
275
    def get_file(self, file_id):
276
        return self.get_file_byname(self.id2path(file_id))
277
278
    def get_file_byname(self, filename):
279
        return file(self.abspath(filename), 'rb')
280
1497 by Robert Collins
Move Branch.read_working_inventory to WorkingTree.
281
    def get_root_id(self):
282
        """Return the id of this trees root"""
283
        inv = self.read_working_inventory()
284
        return inv.root.file_id
285
        
453 by Martin Pool
- Split WorkingTree into its own file
286
    def _get_store_filename(self, file_id):
1508.1.1 by Robert Collins
Provide a open_containing for WorkingTree.
287
        ## XXX: badly named; this is not in the store at all
453 by Martin Pool
- Split WorkingTree into its own file
288
        return self.abspath(self.id2path(file_id))
289
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
290
    @needs_write_lock
291
    def commit(self, *args, **kw):
292
        from bzrlib.commit import Commit
293
        Commit().commit(self.branch, *args, **kw)
1508.1.10 by Robert Collins
bzrlib.add.smart_add_branch is now smart_add_tree. (Robert Collins)
294
        self._set_inventory(self.read_working_inventory())
1248 by Martin Pool
- new weave based cleanup [broken]
295
296
    def id2abspath(self, file_id):
297
        return self.abspath(self.id2path(file_id))
298
1185.12.39 by abentley
Propogated has_or_had_id to Tree
299
    def has_id(self, file_id):
453 by Martin Pool
- Split WorkingTree into its own file
300
        # files that have been deleted are excluded
1185.12.39 by abentley
Propogated has_or_had_id to Tree
301
        inv = self._inventory
866 by Martin Pool
- use new path-based hashcache for WorkingTree- squash mtime/ctime to whole seconds- update and if necessary write out hashcache when WorkingTree object is created.
302
        if not inv.has_id(file_id):
453 by Martin Pool
- Split WorkingTree into its own file
303
            return False
866 by Martin Pool
- use new path-based hashcache for WorkingTree- squash mtime/ctime to whole seconds- update and if necessary write out hashcache when WorkingTree object is created.
304
        path = inv.id2path(file_id)
1092.2.6 by Robert Collins
symlink support updated to work
305
        return bzrlib.osutils.lexists(self.abspath(path))
462 by Martin Pool
- New form 'file_id in tree' to check if the file is present
306
1185.12.39 by abentley
Propogated has_or_had_id to Tree
307
    def has_or_had_id(self, file_id):
308
        if file_id == self.inventory.root.file_id:
309
            return True
310
        return self.inventory.has_id(file_id)
462 by Martin Pool
- New form 'file_id in tree' to check if the file is present
311
312
    __contains__ = has_id
313
453 by Martin Pool
- Split WorkingTree into its own file
314
    def get_file_size(self, file_id):
1248 by Martin Pool
- new weave based cleanup [broken]
315
        return os.path.getsize(self.id2abspath(file_id))
453 by Martin Pool
- Split WorkingTree into its own file
316
317
    def get_file_sha1(self, file_id):
866 by Martin Pool
- use new path-based hashcache for WorkingTree- squash mtime/ctime to whole seconds- update and if necessary write out hashcache when WorkingTree object is created.
318
        path = self._inventory.id2path(file_id)
319
        return self._hashcache.get_sha1(path)
453 by Martin Pool
- Split WorkingTree into its own file
320
1398 by Robert Collins
integrate in Gustavos x-bit patch
321
    def is_executable(self, file_id):
322
        if os.name == "nt":
323
            return self._inventory[file_id].executable
324
        else:
325
            path = self._inventory.id2path(file_id)
326
            mode = os.lstat(self.abspath(path)).st_mode
327
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC&mode)
328
1457.1.16 by Robert Collins
Move set_pending_merges to WorkingTree.
329
    @needs_write_lock
1508.1.5 by Robert Collins
Move add from Branch to WorkingTree.
330
    def add(self, files, ids=None):
331
        """Make files versioned.
332
333
        Note that the command line normally calls smart_add instead,
334
        which can automatically recurse.
335
336
        This adds the files to the inventory, so that they will be
337
        recorded by the next commit.
338
339
        files
340
            List of paths to add, relative to the base of the tree.
341
342
        ids
343
            If set, use these instead of automatically generated ids.
344
            Must be the same length as the list of files, but may
345
            contain None for ids that are to be autogenerated.
346
347
        TODO: Perhaps have an option to add the ids even if the files do
348
              not (yet) exist.
349
350
        TODO: Perhaps callback with the ids and paths as they're added.
351
        """
352
        # TODO: Re-adding a file that is removed in the working copy
353
        # should probably put it back with the previous ID.
354
        if isinstance(files, basestring):
355
            assert(ids is None or isinstance(ids, basestring))
356
            files = [files]
357
            if ids is not None:
358
                ids = [ids]
359
360
        if ids is None:
361
            ids = [None] * len(files)
362
        else:
363
            assert(len(ids) == len(files))
364
365
        inv = self.read_working_inventory()
366
        for f,file_id in zip(files, ids):
367
            if is_control_file(f):
368
                raise BzrError("cannot add control file %s" % quotefn(f))
369
370
            fp = splitpath(f)
371
372
            if len(fp) == 0:
373
                raise BzrError("cannot add top-level %r" % f)
374
375
            fullpath = os.path.normpath(self.abspath(f))
376
377
            try:
378
                kind = file_kind(fullpath)
379
            except OSError:
380
                # maybe something better?
381
                raise BzrError('cannot add: not a regular file, symlink or directory: %s' % quotefn(f))
382
383
            if not InventoryEntry.versionable_kind(kind):
384
                raise BzrError('cannot add: not a versionable file ('
385
                               'i.e. regular file, symlink or directory): %s' % quotefn(f))
386
387
            if file_id is None:
388
                file_id = gen_file_id(f)
389
            inv.add_path(f, kind=kind, file_id=file_id)
390
391
            mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
392
        self._write_inventory(inv)
393
394
    @needs_write_lock
1457.1.15 by Robert Collins
Move add_pending_merge to WorkingTree.
395
    def add_pending_merge(self, *revision_ids):
396
        # TODO: Perhaps should check at this point that the
397
        # history of the revision is actually present?
398
        p = self.pending_merges()
399
        updated = False
400
        for rev_id in revision_ids:
401
            if rev_id in p:
402
                continue
403
            p.append(rev_id)
404
            updated = True
405
        if updated:
1457.1.16 by Robert Collins
Move set_pending_merges to WorkingTree.
406
            self.set_pending_merges(p)
1457.1.15 by Robert Collins
Move add_pending_merge to WorkingTree.
407
1457.1.14 by Robert Collins
Move pending_merges() to WorkingTree.
408
    def pending_merges(self):
409
        """Return a list of pending merges.
410
411
        These are revisions that have been merged into the working
412
        directory but not yet committed.
413
        """
1185.65.13 by Robert Collins
Merge from integration
414
        cfn = self.branch.control_files._rel_controlfilename('pending-merges')
415
        if not self.branch.control_files._transport.has(cfn):
1457.1.14 by Robert Collins
Move pending_merges() to WorkingTree.
416
            return []
417
        p = []
1185.65.13 by Robert Collins
Merge from integration
418
        for l in self.branch.control_files.controlfile('pending-merges', 'r').readlines():
1457.1.14 by Robert Collins
Move pending_merges() to WorkingTree.
419
            p.append(l.rstrip('\n'))
420
        return p
421
1457.1.16 by Robert Collins
Move set_pending_merges to WorkingTree.
422
    @needs_write_lock
423
    def set_pending_merges(self, rev_list):
1185.65.13 by Robert Collins
Merge from integration
424
        self.branch.control_files.put_utf8('pending-merges', '\n'.join(rev_list))
1457.1.16 by Robert Collins
Move set_pending_merges to WorkingTree.
425
1092.2.6 by Robert Collins
symlink support updated to work
426
    def get_symlink_target(self, file_id):
1185.15.10 by Scott James Remnant
Fix WorkingTree.get_symlink_target() to read the absolute path of the
427
        return os.readlink(self.id2abspath(file_id))
453 by Martin Pool
- Split WorkingTree into its own file
428
429
    def file_class(self, filename):
430
        if self.path2id(filename):
431
            return 'V'
432
        elif self.is_ignored(filename):
433
            return 'I'
434
        else:
435
            return '?'
436
437
438
    def list_files(self):
439
        """Recursively list all files as (path, class, kind, id).
440
441
        Lists, but does not descend into unversioned directories.
442
443
        This does not include files that have been deleted in this
444
        tree.
445
446
        Skips the control directory.
447
        """
866 by Martin Pool
- use new path-based hashcache for WorkingTree- squash mtime/ctime to whole seconds- update and if necessary write out hashcache when WorkingTree object is created.
448
        inv = self._inventory
453 by Martin Pool
- Split WorkingTree into its own file
449
450
        def descend(from_dir_relpath, from_dir_id, dp):
451
            ls = os.listdir(dp)
452
            ls.sort()
453
            for f in ls:
454
                ## TODO: If we find a subdirectory with its own .bzr
455
                ## directory, then that is a separate tree and we
456
                ## should exclude it.
457
                if bzrlib.BZRDIR == f:
458
                    continue
459
460
                # path within tree
461
                fp = appendpath(from_dir_relpath, f)
462
463
                # absolute path
464
                fap = appendpath(dp, f)
465
                
466
                f_ie = inv.get_child(from_dir_id, f)
467
                if f_ie:
468
                    c = 'V'
469
                elif self.is_ignored(fp):
470
                    c = 'I'
471
                else:
472
                    c = '?'
473
474
                fk = file_kind(fap)
475
476
                if f_ie:
477
                    if f_ie.kind != fk:
478
                        raise BzrCheckError("file %r entered as kind %r id %r, "
479
                                            "now of kind %r"
480
                                            % (fap, f_ie.kind, f_ie.file_id, fk))
481
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
482
                # make a last minute entry
483
                if f_ie:
484
                    entry = f_ie
485
                else:
486
                    if fk == 'directory':
487
                        entry = TreeDirectory()
488
                    elif fk == 'file':
489
                        entry = TreeFile()
490
                    elif fk == 'symlink':
491
                        entry = TreeLink()
492
                    else:
493
                        entry = TreeEntry()
494
                
495
                yield fp, c, fk, (f_ie and f_ie.file_id), entry
453 by Martin Pool
- Split WorkingTree into its own file
496
497
                if fk != 'directory':
498
                    continue
499
500
                if c != 'V':
501
                    # don't descend unversioned directories
502
                    continue
503
                
504
                for ff in descend(fp, f_ie.file_id, fap):
505
                    yield ff
506
1185.33.66 by Martin Pool
[patch] use unicode literals for all hardcoded paths (Alexander Belchenko)
507
        for f in descend(u'', inv.root.file_id, self.basedir):
453 by Martin Pool
- Split WorkingTree into its own file
508
            yield f
1508.1.7 by Robert Collins
Move rename_one from Branch to WorkingTree. (Robert Collins).
509
510
    @needs_write_lock
1508.1.8 by Robert Collins
move move() from Branch to WorkingTree.
511
    def move(self, from_paths, to_name):
512
        """Rename files.
513
514
        to_name must exist in the inventory.
515
516
        If to_name exists and is a directory, the files are moved into
517
        it, keeping their old names.  
518
519
        Note that to_name is only the last component of the new name;
520
        this doesn't change the directory.
521
522
        This returns a list of (from_path, to_path) pairs for each
523
        entry that is moved.
524
        """
525
        result = []
526
        ## TODO: Option to move IDs only
527
        assert not isinstance(from_paths, basestring)
528
        inv = self.inventory
529
        to_abs = self.abspath(to_name)
530
        if not isdir(to_abs):
531
            raise BzrError("destination %r is not a directory" % to_abs)
532
        if not self.has_filename(to_name):
533
            raise BzrError("destination %r not in working directory" % to_abs)
534
        to_dir_id = inv.path2id(to_name)
535
        if to_dir_id == None and to_name != '':
536
            raise BzrError("destination %r is not a versioned directory" % to_name)
537
        to_dir_ie = inv[to_dir_id]
538
        if to_dir_ie.kind not in ('directory', 'root_directory'):
539
            raise BzrError("destination %r is not a directory" % to_abs)
540
541
        to_idpath = inv.get_idpath(to_dir_id)
542
543
        for f in from_paths:
544
            if not self.has_filename(f):
545
                raise BzrError("%r does not exist in working tree" % f)
546
            f_id = inv.path2id(f)
547
            if f_id == None:
548
                raise BzrError("%r is not versioned" % f)
549
            name_tail = splitpath(f)[-1]
550
            dest_path = appendpath(to_name, name_tail)
551
            if self.has_filename(dest_path):
552
                raise BzrError("destination %r already exists" % dest_path)
553
            if f_id in to_idpath:
554
                raise BzrError("can't move %r to a subdirectory of itself" % f)
555
556
        # OK, so there's a race here, it's possible that someone will
557
        # create a file in this interval and then the rename might be
558
        # left half-done.  But we should have caught most problems.
559
        orig_inv = deepcopy(self.inventory)
560
        try:
561
            for f in from_paths:
562
                name_tail = splitpath(f)[-1]
563
                dest_path = appendpath(to_name, name_tail)
564
                result.append((f, dest_path))
565
                inv.rename(inv.path2id(f), to_dir_id, name_tail)
566
                try:
567
                    rename(self.abspath(f), self.abspath(dest_path))
568
                except OSError, e:
569
                    raise BzrError("failed to rename %r to %r: %s" %
570
                                   (f, dest_path, e[1]),
571
                            ["rename rolled back"])
572
        except:
573
            # restore the inventory on error
1508.1.10 by Robert Collins
bzrlib.add.smart_add_branch is now smart_add_tree. (Robert Collins)
574
            self._set_inventory(orig_inv)
1508.1.8 by Robert Collins
move move() from Branch to WorkingTree.
575
            raise
576
        self._write_inventory(inv)
577
        return result
578
579
    @needs_write_lock
1508.1.7 by Robert Collins
Move rename_one from Branch to WorkingTree. (Robert Collins).
580
    def rename_one(self, from_rel, to_rel):
581
        """Rename one file.
582
583
        This can change the directory or the filename or both.
584
        """
585
        inv = self.inventory
586
        if not self.has_filename(from_rel):
587
            raise BzrError("can't rename: old working file %r does not exist" % from_rel)
588
        if self.has_filename(to_rel):
589
            raise BzrError("can't rename: new working file %r already exists" % to_rel)
590
591
        file_id = inv.path2id(from_rel)
592
        if file_id == None:
593
            raise BzrError("can't rename: old name %r is not versioned" % from_rel)
594
595
        entry = inv[file_id]
596
        from_parent = entry.parent_id
597
        from_name = entry.name
598
        
599
        if inv.path2id(to_rel):
600
            raise BzrError("can't rename: new name %r is already versioned" % to_rel)
601
602
        to_dir, to_tail = os.path.split(to_rel)
603
        to_dir_id = inv.path2id(to_dir)
604
        if to_dir_id == None and to_dir != '':
605
            raise BzrError("can't determine destination directory id for %r" % to_dir)
606
607
        mutter("rename_one:")
608
        mutter("  file_id    {%s}" % file_id)
609
        mutter("  from_rel   %r" % from_rel)
610
        mutter("  to_rel     %r" % to_rel)
611
        mutter("  to_dir     %r" % to_dir)
612
        mutter("  to_dir_id  {%s}" % to_dir_id)
613
614
        inv.rename(file_id, to_dir_id, to_tail)
615
616
        from_abs = self.abspath(from_rel)
617
        to_abs = self.abspath(to_rel)
618
        try:
619
            rename(from_abs, to_abs)
620
        except OSError, e:
621
            inv.rename(file_id, from_parent, from_name)
622
            raise BzrError("failed to rename %r to %r: %s"
623
                    % (from_abs, to_abs, e[1]),
624
                    ["rename rolled back"])
625
        self._write_inventory(inv)
626
627
    @needs_read_lock
453 by Martin Pool
- Split WorkingTree into its own file
628
    def unknowns(self):
1508.1.6 by Robert Collins
Move Branch.unknowns() to WorkingTree.
629
        """Return all unknown files.
630
631
        These are files in the working directory that are not versioned or
632
        control files or ignored.
633
        
634
        >>> from bzrlib.branch import ScratchBranch
635
        >>> b = ScratchBranch(files=['foo', 'foo~'])
636
        >>> tree = WorkingTree(b.base, b)
637
        >>> map(str, tree.unknowns())
638
        ['foo']
639
        >>> tree.add('foo')
640
        >>> list(b.unknowns())
641
        []
642
        >>> tree.remove('foo')
643
        >>> list(b.unknowns())
644
        [u'foo']
645
        """
453 by Martin Pool
- Split WorkingTree into its own file
646
        for subp in self.extras():
647
            if not self.is_ignored(subp):
648
                yield subp
649
1185.14.6 by Aaron Bentley
Made iter_conflicts a WorkingTree method
650
    def iter_conflicts(self):
651
        conflicted = set()
652
        for path in (s[0] for s in self.list_files()):
653
            stem = get_conflicted_stem(path)
654
            if stem is None:
655
                continue
656
            if stem not in conflicted:
657
                conflicted.add(stem)
658
                yield stem
453 by Martin Pool
- Split WorkingTree into its own file
659
1442.1.67 by Robert Collins
Factor out the guts of 'pull' from the command into WorkingTree.pull().
660
    @needs_write_lock
1490 by Robert Collins
Implement a 'bzr push' command, with saved locations; update diff to return 1.
661
    def pull(self, source, overwrite=False):
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
662
        from bzrlib.merge import merge_inner
1442.1.67 by Robert Collins
Factor out the guts of 'pull' from the command into WorkingTree.pull().
663
        source.lock_read()
664
        try:
665
            old_revision_history = self.branch.revision_history()
1185.33.44 by Martin Pool
[patch] show number of revisions pushed/pulled/merged (Robey Pointer)
666
            count = self.branch.pull(source, overwrite)
1442.1.67 by Robert Collins
Factor out the guts of 'pull' from the command into WorkingTree.pull().
667
            new_revision_history = self.branch.revision_history()
668
            if new_revision_history != old_revision_history:
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
669
                if len(old_revision_history):
670
                    other_revision = old_revision_history[-1]
671
                else:
672
                    other_revision = None
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
673
                repository = self.branch.repository
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
674
                merge_inner(self.branch,
675
                            self.branch.basis_tree(), 
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
676
                            repository.revision_tree(other_revision))
1185.33.44 by Martin Pool
[patch] show number of revisions pushed/pulled/merged (Robey Pointer)
677
            return count
1442.1.67 by Robert Collins
Factor out the guts of 'pull' from the command into WorkingTree.pull().
678
        finally:
679
            source.unlock()
680
453 by Martin Pool
- Split WorkingTree into its own file
681
    def extras(self):
682
        """Yield all unknown files in this WorkingTree.
683
684
        If there are any unknown directories then only the directory is
685
        returned, not all its children.  But if there are unknown files
686
        under a versioned subdirectory, they are returned.
687
688
        Currently returned depth-first, sorted by name within directories.
689
        """
690
        ## TODO: Work from given directory downwards
691
        for path, dir_entry in self.inventory.directories():
1185.31.4 by John Arbash Meinel
Fixing mutter() calls to not have to do string processing.
692
            mutter("search for unknowns in %r", path)
453 by Martin Pool
- Split WorkingTree into its own file
693
            dirabs = self.abspath(path)
694
            if not isdir(dirabs):
695
                # e.g. directory deleted
696
                continue
697
698
            fl = []
699
            for subf in os.listdir(dirabs):
700
                if (subf != '.bzr'
701
                    and (subf not in dir_entry.children)):
702
                    fl.append(subf)
703
            
704
            fl.sort()
705
            for subf in fl:
706
                subp = appendpath(path, subf)
707
                yield subp
708
709
710
    def ignored_files(self):
711
        """Yield list of PATH, IGNORE_PATTERN"""
712
        for subp in self.extras():
713
            pat = self.is_ignored(subp)
714
            if pat != None:
715
                yield subp, pat
716
717
718
    def get_ignore_list(self):
719
        """Return list of ignore patterns.
720
721
        Cached in the Tree object after the first call.
722
        """
723
        if hasattr(self, '_ignorelist'):
724
            return self._ignorelist
725
726
        l = bzrlib.DEFAULT_IGNORE[:]
727
        if self.has_filename(bzrlib.IGNORE_FILENAME):
728
            f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
729
            l.extend([line.rstrip("\n\r") for line in f.readlines()])
730
        self._ignorelist = l
731
        return l
732
733
734
    def is_ignored(self, filename):
735
        r"""Check whether the filename matches an ignore pattern.
736
737
        Patterns containing '/' or '\' need to match the whole path;
738
        others match against only the last component.
739
740
        If the file is ignored, returns the pattern which caused it to
741
        be ignored, otherwise None.  So this can simply be used as a
742
        boolean if desired."""
743
744
        # TODO: Use '**' to match directories, and other extended
745
        # globbing stuff from cvs/rsync.
746
747
        # XXX: fnmatch is actually not quite what we want: it's only
748
        # approximately the same as real Unix fnmatch, and doesn't
749
        # treat dotfiles correctly and allows * to match /.
750
        # Eventually it should be replaced with something more
751
        # accurate.
752
        
753
        for pat in self.get_ignore_list():
754
            if '/' in pat or '\\' in pat:
755
                
756
                # as a special case, you can put ./ at the start of a
757
                # pattern; this is good to match in the top-level
758
                # only;
759
                
760
                if (pat[:2] == './') or (pat[:2] == '.\\'):
761
                    newpat = pat[2:]
762
                else:
763
                    newpat = pat
764
                if fnmatch.fnmatchcase(filename, newpat):
765
                    return pat
766
            else:
767
                if fnmatch.fnmatchcase(splitpath(filename)[-1], pat):
768
                    return pat
769
        else:
770
            return None
1185.14.6 by Aaron Bentley
Made iter_conflicts a WorkingTree method
771
1185.12.28 by Aaron Bentley
Removed use of readonly path for executability test
772
    def kind(self, file_id):
773
        return file_kind(self.id2abspath(file_id))
774
1442.1.65 by Robert Collins
Branch.remove has been moved to WorkingTree.
775
    def lock_read(self):
776
        """See Branch.lock_read, and WorkingTree.unlock."""
777
        return self.branch.lock_read()
778
779
    def lock_write(self):
780
        """See Branch.lock_write, and WorkingTree.unlock."""
781
        return self.branch.lock_write()
782
1185.33.59 by Martin Pool
[patch] keep a cached basis inventory (Johan Rydberg)
783
    def _basis_inventory_name(self, revision_id):
784
        return 'basis-inventory.%s' % revision_id
785
786
    def set_last_revision(self, new_revision, old_revision=None):
787
        if old_revision:
788
            try:
789
                path = self._basis_inventory_name(old_revision)
790
                path = self.branch._rel_controlfilename(path)
791
                self.branch._transport.delete(path)
792
            except:
793
                pass
794
        try:
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
795
            xml = self.branch.repository.get_inventory_xml(new_revision)
1185.33.59 by Martin Pool
[patch] keep a cached basis inventory (Johan Rydberg)
796
            path = self._basis_inventory_name(new_revision)
1185.65.15 by Robert Collins
Merge from integration.
797
            self.branch.control_files.put_utf8(path, xml)
1185.33.59 by Martin Pool
[patch] keep a cached basis inventory (Johan Rydberg)
798
        except WeaveRevisionNotPresent:
799
            pass
800
801
    def read_basis_inventory(self, revision_id):
802
        """Read the cached basis inventory."""
803
        path = self._basis_inventory_name(revision_id)
1185.65.15 by Robert Collins
Merge from integration.
804
        return self.branch.control_files.controlfile(path, 'r').read()
1185.33.59 by Martin Pool
[patch] keep a cached basis inventory (Johan Rydberg)
805
        
1497 by Robert Collins
Move Branch.read_working_inventory to WorkingTree.
806
    @needs_read_lock
807
    def read_working_inventory(self):
808
        """Read the working inventory."""
809
        # ElementTree does its own conversion from UTF-8, so open in
810
        # binary.
1185.65.11 by Robert Collins
Disable inheritance for getting at LockableFiles, rather use composition.
811
        f = self.branch.control_files.controlfile('inventory', 'rb')
1497 by Robert Collins
Move Branch.read_working_inventory to WorkingTree.
812
        return bzrlib.xml5.serializer_v5.read_inventory(f)
813
1442.1.65 by Robert Collins
Branch.remove has been moved to WorkingTree.
814
    @needs_write_lock
815
    def remove(self, files, verbose=False):
816
        """Remove nominated files from the working inventory..
817
818
        This does not remove their text.  This does not run on XXX on what? RBC
819
820
        TODO: Refuse to remove modified files unless --force is given?
821
822
        TODO: Do something useful with directories.
823
824
        TODO: Should this remove the text or not?  Tough call; not
825
        removing may be useful and the user can just use use rm, and
826
        is the opposite of add.  Removing it is consistent with most
827
        other tools.  Maybe an option.
828
        """
829
        ## TODO: Normalize names
830
        ## TODO: Remove nested loops; better scalability
831
        if isinstance(files, basestring):
832
            files = [files]
833
834
        inv = self.inventory
835
836
        # do this before any modifications
837
        for f in files:
838
            fid = inv.path2id(f)
839
            if not fid:
1185.16.72 by Martin Pool
[merge] from robert and fix up tests
840
                # TODO: Perhaps make this just a warning, and continue?
841
                # This tends to happen when 
842
                raise NotVersionedError(path=f)
1185.31.4 by John Arbash Meinel
Fixing mutter() calls to not have to do string processing.
843
            mutter("remove inventory entry %s {%s}", quotefn(f), fid)
1442.1.65 by Robert Collins
Branch.remove has been moved to WorkingTree.
844
            if verbose:
845
                # having remove it, it must be either ignored or unknown
846
                if self.is_ignored(f):
847
                    new_status = 'I'
848
                else:
849
                    new_status = '?'
850
                show_status(new_status, inv[fid].kind, quotefn(f))
851
            del inv[fid]
852
1457.1.11 by Robert Collins
Move _write_inventory to WorkingTree.
853
        self._write_inventory(inv)
1442.1.65 by Robert Collins
Branch.remove has been moved to WorkingTree.
854
1497 by Robert Collins
Move Branch.read_working_inventory to WorkingTree.
855
    @needs_write_lock
1501 by Robert Collins
Move revert from Branch to WorkingTree.
856
    def revert(self, filenames, old_tree=None, backups=True):
1457.1.8 by Robert Collins
Replace the WorkingTree.revert method algorithm with a call to merge_inner.
857
        from bzrlib.merge import merge_inner
1501 by Robert Collins
Move revert from Branch to WorkingTree.
858
        if old_tree is None:
859
            old_tree = self.branch.basis_tree()
1457.1.8 by Robert Collins
Replace the WorkingTree.revert method algorithm with a call to merge_inner.
860
        merge_inner(self.branch, old_tree,
861
                    self, ignore_zero=True,
862
                    backup_files=backups, 
863
                    interesting_files=filenames)
864
        if not len(filenames):
1457.1.16 by Robert Collins
Move set_pending_merges to WorkingTree.
865
            self.set_pending_merges([])
1501 by Robert Collins
Move revert from Branch to WorkingTree.
866
867
    @needs_write_lock
1497 by Robert Collins
Move Branch.read_working_inventory to WorkingTree.
868
    def set_inventory(self, new_inventory_list):
869
        from bzrlib.inventory import (Inventory,
870
                                      InventoryDirectory,
871
                                      InventoryEntry,
872
                                      InventoryFile,
873
                                      InventoryLink)
874
        inv = Inventory(self.get_root_id())
875
        for path, file_id, parent, kind in new_inventory_list:
876
            name = os.path.basename(path)
877
            if name == "":
878
                continue
879
            # fixme, there should be a factory function inv,add_?? 
880
            if kind == 'directory':
881
                inv.add(InventoryDirectory(file_id, name, parent))
882
            elif kind == 'file':
883
                inv.add(InventoryFile(file_id, name, parent))
884
            elif kind == 'symlink':
885
                inv.add(InventoryLink(file_id, name, parent))
886
            else:
887
                raise BzrError("unknown kind %r" % kind)
1457.1.11 by Robert Collins
Move _write_inventory to WorkingTree.
888
        self._write_inventory(inv)
1497 by Robert Collins
Move Branch.read_working_inventory to WorkingTree.
889
1457.1.10 by Robert Collins
Move set_root_id to WorkingTree.
890
    @needs_write_lock
891
    def set_root_id(self, file_id):
892
        """Set the root id for this tree."""
893
        inv = self.read_working_inventory()
894
        orig_root_id = inv.root.file_id
895
        del inv._byid[inv.root.file_id]
896
        inv.root.file_id = file_id
897
        inv._byid[inv.root.file_id] = inv.root
898
        for fid in inv:
899
            entry = inv[fid]
900
            if entry.parent_id in (None, orig_root_id):
901
                entry.parent_id = inv.root.file_id
1457.1.11 by Robert Collins
Move _write_inventory to WorkingTree.
902
        self._write_inventory(inv)
1457.1.10 by Robert Collins
Move set_root_id to WorkingTree.
903
1442.1.65 by Robert Collins
Branch.remove has been moved to WorkingTree.
904
    def unlock(self):
905
        """See Branch.unlock.
906
        
907
        WorkingTree locking just uses the Branch locking facilities.
908
        This is current because all working trees have an embedded branch
909
        within them. IF in the future, we were to make branch data shareable
910
        between multiple working trees, i.e. via shared storage, then we 
911
        would probably want to lock both the local tree, and the branch.
912
        """
913
        return self.branch.unlock()
914
1457.1.11 by Robert Collins
Move _write_inventory to WorkingTree.
915
    @needs_write_lock
916
    def _write_inventory(self, inv):
917
        """Write inventory as the current inventory."""
918
        from cStringIO import StringIO
919
        from bzrlib.atomicfile import AtomicFile
920
        sio = StringIO()
921
        bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
922
        sio.seek(0)
1185.65.13 by Robert Collins
Merge from integration
923
        f = AtomicFile(self.branch.control_files.controlfilename('inventory'))
1457.1.11 by Robert Collins
Move _write_inventory to WorkingTree.
924
        try:
925
            pumpfile(sio, f)
926
            f.commit()
927
        finally:
928
            f.close()
1508.1.10 by Robert Collins
bzrlib.add.smart_add_branch is now smart_add_tree. (Robert Collins)
929
        self._set_inventory(inv)
1457.1.11 by Robert Collins
Move _write_inventory to WorkingTree.
930
        mutter('wrote working inventory')
931
            
1442.1.65 by Robert Collins
Branch.remove has been moved to WorkingTree.
932
1185.14.6 by Aaron Bentley
Made iter_conflicts a WorkingTree method
933
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
934
def get_conflicted_stem(path):
935
    for suffix in CONFLICT_SUFFIXES:
936
        if path.endswith(suffix):
937
            return path[:-len(suffix)]