~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

Nearly complete .bzr/checkout splitout.

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 WorkingTree(dir[, branch])
 
28
To get a WorkingTree, call bzrdir.open_workingtree() or
 
29
WorkingTree.open(dir).
29
30
"""
30
31
 
31
32
 
186
187
    not listed in the Inventory and vice versa.
187
188
    """
188
189
 
189
 
    def __init__(self, basedir='.', branch=None, _inventory=None, _control_files=None):
 
190
    def __init__(self, basedir='.', branch=None, _inventory=None, _control_files=None, _internal=False):
190
191
        """Construct a WorkingTree for basedir.
191
192
 
192
193
        If the branch is not supplied, it is opened automatically.
194
195
        (branch.base is not cross checked, because for remote branches that
195
196
        would be meaningless).
196
197
        """
 
198
        if not _internal:
 
199
            # created via open etc.
 
200
            wt = WorkingTree.open(basedir, branch)
 
201
            self.branch = wt.branch
 
202
            self.basedir = wt.basedir
 
203
            self._control_files = wt._control_files
 
204
            self._hashcache = wt._hashcache
 
205
            self._set_inventory(wt._inventory)
197
206
        from bzrlib.hashcache import HashCache
198
207
        from bzrlib.trace import note, mutter
199
208
        assert isinstance(basedir, basestring), \
244
253
        self.path2id = self._inventory.path2id
245
254
 
246
255
    @staticmethod
 
256
    def open(path=None, _unsupported=False):
 
257
        """Open an existing working tree at path.
 
258
 
 
259
        """
 
260
        if path is None:
 
261
            path = os.path.getcwdu()
 
262
        control = bzrdir.BzrDir.open(path, _unsupported)
 
263
        return control.open_workingtree(_unsupported)
 
264
        
 
265
    @staticmethod
247
266
    def open_containing(path=None):
248
267
        """Open an existing working tree which has its root about path.
249
268
        
255
274
        If there is one, it is returned, along with the unused portion of path.
256
275
        """
257
276
        if path is None:
258
 
            path = getcwd()
259
 
        else:
260
 
            # sanity check.
261
 
            if path.find('://') != -1:
262
 
                raise NotBranchError(path=path)
263
 
        path = abspath(path)
264
 
        orig_path = path[:]
265
 
        tail = u''
266
 
        while True:
267
 
            try:
268
 
                return WorkingTree(path), tail
269
 
            except NotBranchError:
270
 
                pass
271
 
            if tail:
272
 
                tail = pathjoin(os.path.basename(path), tail)
273
 
            else:
274
 
                tail = os.path.basename(path)
275
 
            lastpath = path
276
 
            path = os.path.dirname(path)
277
 
            if lastpath == path:
278
 
                # reached the root, whatever that may be
279
 
                raise NotBranchError(path=orig_path)
 
277
            path = os.getcwdu()
 
278
        control, relpath = bzrdir.BzrDir.open_containing(path)
 
279
        return control.open_workingtree(), relpath
 
280
 
 
281
    @staticmethod
 
282
    def open_downlevel(path=None):
 
283
        """Open an unsupported working tree.
 
284
 
 
285
        Only intended for advanced situations like upgrading part of a bzrdir.
 
286
        """
 
287
        return WorkingTree.open(path, _unsupported=True)
280
288
 
281
289
    def __iter__(self):
282
290
        """Iterate through file_ids for this tree.
325
333
        XXX: When BzrDir is present, these should be created through that 
326
334
        interface instead.
327
335
        """
 
336
        transport = get_transport(directory)
 
337
        if branch.bzrdir.transport.clone('..').base == transport.base:
 
338
            # same dir 
 
339
            return branch.bzrdir.create_workingtree()
 
340
        # different directory, 
 
341
        # create a branch reference
 
342
        # and now a working tree.
 
343
        raise NotImplementedError
328
344
        try:
329
345
            os.mkdir(directory)
330
346
        except OSError, e:
337
353
                raise
338
354
        revision_tree = branch.repository.revision_tree(branch.last_revision())
339
355
        inv = revision_tree.inventory
340
 
        wt = WorkingTree(directory, branch, inv)
 
356
        wt = WorkingTree(directory, branch, inv, _internal=True)
341
357
        wt._write_inventory(inv)
342
358
        wt.set_root_id(revision_tree.inventory.root.file_id)
343
359
        if branch.last_revision() is not None:
347
363
        return wt
348
364
 
349
365
    @staticmethod
 
366
    @deprecated_method(zero_eight)
350
367
    def create_standalone(directory):
351
368
        """Create a checkout and a branch and a repo at directory.
352
369
 
353
370
        Directory must exist and be empty.
354
371
 
355
 
        XXX: When BzrDir is present, these should be created through that 
356
 
        interface instead.
 
372
        please use BzrDir.create_standalone_workingtree
357
373
        """
358
 
        directory = safe_unicode(directory)
359
 
        b = bzrdir.BzrDir.create_branch_and_repo(directory)
360
 
        return WorkingTree.create(b, directory)
 
374
        return bzrdir.BzrDir.create_standalone_workingtree(directory)
361
375
 
362
376
    def relpath(self, abs):
363
377
        """Return the local path portion from a given absolute path."""
1127
1141
    object will be created every time regardless.
1128
1142
    """
1129
1143
 
 
1144
    _default_format = None
 
1145
    """The default format used for new trees."""
 
1146
 
 
1147
    _formats = {}
 
1148
    """The known formats."""
 
1149
 
 
1150
    @classmethod
 
1151
    def find_format(klass, a_bzrdir):
 
1152
        """Return the format for the working tree object in a_bzrdir."""
 
1153
        try:
 
1154
            transport = a_bzrdir.get_workingtree_transport(None)
 
1155
            format_string = transport.get("format").read()
 
1156
            return klass._formats[format_string]
 
1157
        except NoSuchFile:
 
1158
            raise errors.NotBranchError(path=transport.base)
 
1159
        except KeyError:
 
1160
            raise errors.UnknownFormatError(format_string)
 
1161
 
 
1162
    @classmethod
 
1163
    def get_default_format(klass):
 
1164
        """Return the current default format."""
 
1165
        return klass._default_format
 
1166
 
1130
1167
    def get_format_string(self):
1131
1168
        """Return the ASCII format string that identifies this format."""
1132
1169
        raise NotImplementedError(self.get_format_string)
1133
1170
 
 
1171
    def is_supported(self):
 
1172
        """Is this format supported?
 
1173
 
 
1174
        Supported formats can be initialized and opened.
 
1175
        Unsupported formats may not support initialization or committing or 
 
1176
        some other features depending on the reason for not being supported.
 
1177
        """
 
1178
        return True
 
1179
 
 
1180
    @classmethod
 
1181
    def register_format(klass, format):
 
1182
        klass._formats[format.get_format_string()] = format
 
1183
 
 
1184
    @classmethod
 
1185
    def set_default_format(klass, format):
 
1186
        klass._default_format = format
 
1187
 
 
1188
    @classmethod
 
1189
    def unregister_format(klass, format):
 
1190
        assert klass._formats[format.get_format_string()] is format
 
1191
        del klass._formats[format.get_format_string()]
 
1192
 
1134
1193
 
1135
1194
 
1136
1195
class WorkingTreeFormat2(WorkingTreeFormat):
1143
1202
        """See WorkingTreeFormat.initialize()."""
1144
1203
        if not isinstance(a_bzrdir.transport, LocalTransport):
1145
1204
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1146
 
        result = WorkingTree.create(a_bzrdir.open_branch(),
1147
 
                                  a_bzrdir.transport.clone('..').base)
1148
 
        result.bzrdir = a_bzrdir
1149
 
        result._format = self
1150
 
        return result
 
1205
        b = a_bzrdir.open_branch()
 
1206
        r = a_bzrdir.open_repository()
 
1207
        revision = b.last_revision()
 
1208
        basis_tree = r.revision_tree(revision)
 
1209
        inv = basis_tree.inventory
 
1210
        wt = WorkingTree(a_bzrdir.transport.clone('..').base, b, inv, _internal=True)
 
1211
        wt._write_inventory(inv)
 
1212
        wt.set_root_id(inv.root.file_id)
 
1213
        wt.set_last_revision(revision)
 
1214
        wt.set_pending_merges([])
 
1215
        wt.revert([])
 
1216
        wt.bzrdir = a_bzrdir
 
1217
        wt._format = self
 
1218
        return wt
 
1219
 
 
1220
    def __init__(self):
 
1221
        super(WorkingTreeFormat2, self).__init__()
 
1222
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
1151
1223
 
1152
1224
    def open(self, a_bzrdir, _found=False):
1153
1225
        """Return the WorkingTree object for a_bzrdir
1158
1230
        if not _found:
1159
1231
            # we are being called directly and must probe.
1160
1232
            raise NotImplementedError
1161
 
        result = WorkingTree(a_bzrdir.transport.clone('..').base)
 
1233
        if not isinstance(a_bzrdir.transport, LocalTransport):
 
1234
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
 
1235
        result = WorkingTree(a_bzrdir.transport.clone('..').base, _internal=True)
1162
1236
        result.bzrdir = a_bzrdir
1163
1237
        result._format = self
1164
1238
        return result
1178
1252
        """See WorkingTreeFormat.initialize()."""
1179
1253
        if not isinstance(a_bzrdir.transport, LocalTransport):
1180
1254
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1181
 
        result = WorkingTree.create(a_bzrdir.open_branch(),
1182
 
                                  a_bzrdir.transport.clone('..').base)
1183
 
        result.bzrdir = a_bzrdir
1184
 
        result._format = self
1185
 
        return result
 
1255
        transport = a_bzrdir.get_workingtree_transport(self)
 
1256
        control_files = LockableFiles(transport, 'lock')
 
1257
        control_files.put_utf8('format', self.get_format_string())
 
1258
        b = a_bzrdir.open_branch()
 
1259
        r = a_bzrdir.open_repository()
 
1260
        revision = b.last_revision()
 
1261
        basis_tree = r.revision_tree(revision)
 
1262
        inv = basis_tree.inventory
 
1263
        wt = WorkingTree(a_bzrdir.transport.clone('..').base, b, inv, _internal=True)
 
1264
        wt._write_inventory(inv)
 
1265
        wt.set_root_id(inv.root.file_id)
 
1266
        wt.set_last_revision(revision)
 
1267
        wt.set_pending_merges([])
 
1268
        wt.revert([])
 
1269
        wt.bzrdir = a_bzrdir
 
1270
        wt._format = self
 
1271
        return wt
 
1272
 
 
1273
    def __init__(self):
 
1274
        super(WorkingTreeFormat3, self).__init__()
 
1275
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1186
1276
 
1187
1277
    def open(self, a_bzrdir, _found=False):
1188
1278
        """Return the WorkingTree object for a_bzrdir
1193
1283
        if not _found:
1194
1284
            # we are being called directly and must probe.
1195
1285
            raise NotImplementedError
1196
 
        result = WorkingTree(a_bzrdir.transport.clone('..').base)
 
1286
        if not isinstance(a_bzrdir.transport, LocalTransport):
 
1287
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
 
1288
        result = WorkingTree(a_bzrdir.transport.clone('..').base, _internal=True)
1197
1289
        result.bzrdir = a_bzrdir
1198
1290
        result._format = self
1199
1291
        return result
 
1292
 
 
1293
 
 
1294
# formats which have no format string are not discoverable
 
1295
# and not independently creatable, so are not registered.
 
1296
__default_format = WorkingTreeFormat3()
 
1297
WorkingTreeFormat.register_format(__default_format)
 
1298
WorkingTreeFormat.set_default_format(__default_format)
 
1299
_legacy_formats = [WorkingTreeFormat2(),
 
1300
                   ]
 
1301
 
 
1302
 
 
1303
class WorkingTreeTestProviderAdapter(object):
 
1304
    """A tool to generate a suite testing multiple workingtree formats at once.
 
1305
 
 
1306
    This is done by copying the test once for each transport and injecting
 
1307
    the transport_server, transport_readonly_server, and workingtree_format
 
1308
    classes into each copy. Each copy is also given a new id() to make it
 
1309
    easy to identify.
 
1310
    """
 
1311
 
 
1312
    def __init__(self, transport_server, transport_readonly_server, formats):
 
1313
        self._transport_server = transport_server
 
1314
        self._transport_readonly_server = transport_readonly_server
 
1315
        self._formats = formats
 
1316
    
 
1317
    def adapt(self, test):
 
1318
        from bzrlib.tests import TestSuite
 
1319
        result = TestSuite()
 
1320
        for workingtree_format, bzrdir_format in self._formats:
 
1321
            new_test = deepcopy(test)
 
1322
            new_test.transport_server = self._transport_server
 
1323
            new_test.transport_readonly_server = self._transport_readonly_server
 
1324
            new_test.bzrdir_format = bzrdir_format
 
1325
            new_test.workingtree_format = workingtree_format
 
1326
            def make_new_test_id():
 
1327
                new_id = "%s(%s)" % (new_test.id(), workingtree_format.__class__.__name__)
 
1328
                return lambda: new_id
 
1329
            new_test.id = make_new_test_id()
 
1330
            result.addTest(new_test)
 
1331
        return result