~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

[merge] land Robert's branch-formats branch

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
At the moment every WorkingTree has its own branch.  Remote
26
26
WorkingTrees aren't supported.
27
27
 
28
 
To get a WorkingTree, call Branch.working_tree():
 
28
To get a WorkingTree, call WorkingTree(dir[, branch])
29
29
"""
30
30
 
31
31
 
32
 
# TODO: Don't allow WorkingTrees to be constructed for remote branches if 
33
 
# they don't work.
34
 
 
35
32
# FIXME: I don't know if writing out the cache from the destructor is really a
36
33
# good idea, because destructors are considered poor taste in Python, and it's
37
34
# not predictable when it will be written out.
43
40
# At the momenthey may alias the inventory and have old copies of it in memory.
44
41
 
45
42
from copy import deepcopy
 
43
from cStringIO import StringIO
 
44
import errno
 
45
import fnmatch
46
46
import os
47
47
import stat
48
 
import fnmatch
49
48
 
 
49
 
 
50
from bzrlib.atomicfile import AtomicFile
50
51
from bzrlib.branch import (Branch,
 
52
                           BzrBranchFormat4,
 
53
                           BzrBranchFormat5,
 
54
                           BzrBranchFormat6,
51
55
                           is_control_file,
52
56
                           quotefn)
 
57
from bzrlib.decorators import needs_read_lock, needs_write_lock
53
58
from bzrlib.errors import (BzrCheckError,
54
59
                           BzrError,
55
60
                           DivergedBranches,
58
63
                           NoSuchFile,
59
64
                           NotVersionedError)
60
65
from bzrlib.inventory import InventoryEntry
 
66
from bzrlib.lockable_files import LockableFiles
61
67
from bzrlib.osutils import (appendpath,
62
68
                            compact_date,
63
69
                            file_kind,
65
71
                            getcwd,
66
72
                            pathjoin,
67
73
                            pumpfile,
 
74
                            safe_unicode,
68
75
                            splitpath,
69
76
                            rand_bytes,
70
77
                            abspath,
72
79
                            realpath,
73
80
                            relpath,
74
81
                            rename)
 
82
from bzrlib.symbol_versioning import *
75
83
from bzrlib.textui import show_status
76
84
import bzrlib.tree
77
85
from bzrlib.trace import mutter
 
86
from bzrlib.transport import get_transport
78
87
import bzrlib.xml5
79
 
from bzrlib.decorators import needs_read_lock, needs_write_lock
80
88
 
81
89
 
82
90
def gen_file_id(name):
178
186
    not listed in the Inventory and vice versa.
179
187
    """
180
188
 
181
 
    def __init__(self, basedir=u'.', branch=None):
 
189
    def __init__(self, basedir='.', branch=None, _inventory=None, _control_files=None):
182
190
        """Construct a WorkingTree for basedir.
183
191
 
184
192
        If the branch is not supplied, it is opened automatically.
190
198
        from bzrlib.trace import note, mutter
191
199
        assert isinstance(basedir, basestring), \
192
200
            "base directory %r is not a string" % basedir
 
201
        basedir = safe_unicode(basedir)
 
202
        mutter("openeing working tree %r", basedir)
193
203
        if branch is None:
194
204
            branch = Branch.open(basedir)
195
205
        assert isinstance(branch, Branch), \
196
206
            "branch %r is not a Branch" % branch
197
207
        self.branch = branch
198
208
        self.basedir = realpath(basedir)
 
209
        # if branch is at our basedir and is a format 6 or less
 
210
        if (isinstance(self.branch._branch_format,
 
211
                       (BzrBranchFormat4, BzrBranchFormat5, BzrBranchFormat6))
 
212
            # might be able to share control object
 
213
            and self.branch.base.split('/')[-2] == self.basedir.split('/')[-1]):
 
214
            self._control_files = self.branch.control_files
 
215
        elif _control_files is not None:
 
216
            assert False, "not done yet"
 
217
#            self._control_files = _control_files
 
218
        else:
 
219
            self._control_files = LockableFiles(
 
220
                get_transport(self.basedir).clone(bzrlib.BZRDIR), 'branch-lock')
199
221
 
200
222
        # update the whole cache up front and write to disk if anything changed;
201
223
        # in the future we might want to do this more selectively
211
233
            mutter("write hc")
212
234
            hc.write()
213
235
 
214
 
        self._set_inventory(self.read_working_inventory())
 
236
        if _inventory is None:
 
237
            self._set_inventory(self.read_working_inventory())
 
238
        else:
 
239
            self._set_inventory(_inventory)
215
240
 
216
241
    def _set_inventory(self, inv):
217
242
        self._inventory = inv
270
295
    def abspath(self, filename):
271
296
        return pathjoin(self.basedir, filename)
272
297
 
 
298
    @staticmethod
 
299
    def create(branch, directory):
 
300
        """Create a workingtree for branch at directory.
 
301
 
 
302
        If existing_directory already exists it must have a .bzr directory.
 
303
        If it does not exist, it will be created.
 
304
 
 
305
        This returns a new WorkingTree object for the new checkout.
 
306
 
 
307
        TODO FIXME RBC 20060124 when we have checkout formats in place this
 
308
        should accept an optional revisionid to checkout [and reject this if
 
309
        checking out into the same dir as a pre-checkout-aware branch format.]
 
310
 
 
311
        XXX: When BzrDir is present, these should be created through that 
 
312
        interface instead.
 
313
        """
 
314
        try:
 
315
            os.mkdir(directory)
 
316
        except OSError, e:
 
317
            if e.errno != errno.EEXIST:
 
318
                raise
 
319
        try:
 
320
            os.mkdir(pathjoin(directory, '.bzr'))
 
321
        except OSError, e:
 
322
            if e.errno != errno.EEXIST:
 
323
                raise
 
324
        inv = branch.repository.revision_tree(branch.last_revision()).inventory
 
325
        wt = WorkingTree(directory, branch, inv)
 
326
        wt._write_inventory(inv)
 
327
        if branch.last_revision() is not None:
 
328
            wt.set_last_revision(branch.last_revision())
 
329
        wt.set_pending_merges([])
 
330
        wt.revert([])
 
331
        return wt
 
332
 
 
333
    @staticmethod
 
334
    def create_standalone(directory):
 
335
        """Create a checkout and a branch at directory.
 
336
 
 
337
        Directory must exist and be empty.
 
338
 
 
339
        XXX: When BzrDir is present, these should be created through that 
 
340
        interface instead.
 
341
        """
 
342
        directory = safe_unicode(directory)
 
343
        b = Branch.create(directory)
 
344
        return WorkingTree.create(b, directory)
 
345
 
273
346
    def relpath(self, abs):
274
347
        """Return the local path portion from a given absolute path."""
275
348
        return relpath(self.basedir, abs)
293
366
        return self.abspath(self.id2path(file_id))
294
367
 
295
368
    @needs_write_lock
296
 
    def commit(self, *args, **kw):
 
369
    def commit(self, *args, **kwargs):
297
370
        from bzrlib.commit import Commit
298
 
        Commit().commit(self.branch, *args, **kw)
 
371
        # args for wt.commit start at message from the Commit.commit method,
 
372
        # but with branch a kwarg now, passing in args as is results in the
 
373
        #message being used for the branch
 
374
        args = (DEPRECATED_PARAMETER, ) + args
 
375
        Commit().commit(working_tree=self, *args, **kwargs)
299
376
        self._set_inventory(self.read_working_inventory())
300
377
 
301
378
    def id2abspath(self, file_id):
419
496
        directory but not yet committed.
420
497
        """
421
498
        try:
422
 
            f = self.branch.control_files.get_utf8('pending-merges')
423
 
        except NoSuchFile:
 
499
            merges_file = self._control_files.get_utf8('pending-merges')
 
500
        except OSError, e:
 
501
            if e.errno != errno.ENOENT:
 
502
                raise
424
503
            return []
425
504
        p = []
426
 
        for l in f.readlines():
 
505
        for l in merges_file.readlines():
427
506
            p.append(l.rstrip('\n'))
428
507
        return p
429
508
 
430
509
    @needs_write_lock
431
510
    def set_pending_merges(self, rev_list):
432
 
        self.branch.control_files.put_utf8('pending-merges', '\n'.join(rev_list))
 
511
        self._control_files.put_utf8('pending-merges', '\n'.join(rev_list))
433
512
 
434
513
    def get_symlink_target(self, file_id):
435
514
        return os.readlink(self.id2abspath(file_id))
681
760
                repository = self.branch.repository
682
761
                merge_inner(self.branch,
683
762
                            self.branch.basis_tree(), 
684
 
                            repository.revision_tree(other_revision))
 
763
                            repository.revision_tree(other_revision),
 
764
                            this_tree=self)
 
765
                self.set_last_revision(self.branch.last_revision())
685
766
            return count
686
767
        finally:
687
768
            source.unlock()
816
897
        """Read the working inventory."""
817
898
        # ElementTree does its own conversion from UTF-8, so open in
818
899
        # binary.
819
 
        f = self.branch.control_files.get('inventory')
820
 
        return bzrlib.xml5.serializer_v5.read_inventory(f)
 
900
        return bzrlib.xml5.serializer_v5.read_inventory(
 
901
            self._control_files.get('inventory'))
821
902
 
822
903
    @needs_write_lock
823
904
    def remove(self, files, verbose=False):
868
949
        merge_inner(self.branch, old_tree,
869
950
                    self, ignore_zero=True,
870
951
                    backup_files=backups, 
871
 
                    interesting_files=filenames)
 
952
                    interesting_files=filenames,
 
953
                    this_tree=self)
872
954
        if not len(filenames):
873
955
            self.set_pending_merges([])
874
956
 
930
1012
    @needs_write_lock
931
1013
    def _write_inventory(self, inv):
932
1014
        """Write inventory as the current inventory."""
933
 
        from cStringIO import StringIO
934
 
        from bzrlib.atomicfile import AtomicFile
935
1015
        sio = StringIO()
936
1016
        bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
937
1017
        sio.seek(0)
938
 
        f = AtomicFile(self.branch.control_files.controlfilename('inventory'))
939
 
        try:
940
 
            pumpfile(sio, f)
941
 
            f.commit()
942
 
        finally:
943
 
            f.close()
 
1018
        self._control_files.put('inventory', sio)
944
1019
        self._set_inventory(inv)
945
1020
        mutter('wrote working inventory')
946
1021