/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/mutabletree.py

  • Committer: Martin Pool
  • Date: 2007-08-20 05:53:39 UTC
  • mfrom: (2727 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2730.
  • Revision ID: mbp@sourcefrog.net-20070820055339-uzei7f7i7jo6tugg
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
"""
21
21
 
22
22
 
 
23
from bzrlib.lazy_import import lazy_import
 
24
lazy_import(globals(), """
 
25
import os
 
26
 
 
27
from bzrlib import (
 
28
    add,
 
29
    bzrdir,
 
30
    )
 
31
from bzrlib.osutils import dirname
 
32
from bzrlib.trace import mutter, warning
 
33
""")
 
34
 
23
35
from bzrlib import (
24
36
    errors,
25
37
    osutils,
189
201
            revprops = {}
190
202
        if not 'branch-nick' in revprops:
191
203
            revprops['branch-nick'] = self.branch.nick
 
204
        author = kwargs.pop('author', None)
 
205
        if author is not None:
 
206
            assert 'author' not in revprops
 
207
            revprops['author'] = author
192
208
        # args for wt.commit start at message from the Commit.commit method,
193
209
        args = (message, ) + args
194
210
        committed_id = commit.Commit().commit(working_tree=self,
260
276
            parent tree - i.e. a ghost.
261
277
        """
262
278
        raise NotImplementedError(self.set_parent_trees)
 
279
 
 
280
    @needs_tree_write_lock
 
281
    def smart_add(self, file_list, recurse=True, action=None, save=True):
 
282
        """Version file_list, optionally recursing into directories.
 
283
 
 
284
        This is designed more towards DWIM for humans than API clarity.
 
285
        For the specific behaviour see the help for cmd_add().
 
286
 
 
287
        :param action: A reporter to be called with the inventory, parent_ie,
 
288
            path and kind of the path being added. It may return a file_id if 
 
289
            a specific one should be used.
 
290
        :param save: Save the inventory after completing the adds. If False
 
291
            this provides dry-run functionality by doing the add and not saving
 
292
            the inventory.
 
293
        :return: A tuple - files_added, ignored_files. files_added is the count
 
294
            of added files, and ignored_files is a dict mapping files that were
 
295
            ignored to the rule that caused them to be ignored.
 
296
        """
 
297
        # not in an inner loop; and we want to remove direct use of this,
 
298
        # so here as a reminder for now. RBC 20070703
 
299
        from bzrlib.inventory import InventoryEntry
 
300
        assert isinstance(recurse, bool)
 
301
        if action is None:
 
302
            action = add.AddAction()
 
303
        
 
304
        if not file_list:
 
305
            # no paths supplied: add the entire tree.
 
306
            file_list = [u'.']
 
307
        # mutter("smart add of %r")
 
308
        inv = self.inventory
 
309
        added = []
 
310
        ignored = {}
 
311
        dirs_to_add = []
 
312
        user_dirs = set()
 
313
 
 
314
        # validate user file paths and convert all paths to tree 
 
315
        # relative : it's cheaper to make a tree relative path an abspath
 
316
        # than to convert an abspath to tree relative.
 
317
        for filepath in file_list:
 
318
            rf = _FastPath(self.relpath(filepath))
 
319
            # validate user parameters. Our recursive code avoids adding new files
 
320
            # that need such validation 
 
321
            if self.is_control_filename(rf.raw_path):
 
322
                raise errors.ForbiddenControlFileError(filename=rf.raw_path)
 
323
            
 
324
            abspath = self.abspath(rf.raw_path)
 
325
            kind = osutils.file_kind(abspath)
 
326
            if kind == 'directory':
 
327
                # schedule the dir for scanning
 
328
                user_dirs.add(rf)
 
329
            else:
 
330
                if not InventoryEntry.versionable_kind(kind):
 
331
                    raise errors.BadFileKindError(filename=abspath, kind=kind)
 
332
            # ensure the named path is added, so that ignore rules in the later directory
 
333
            # walk dont skip it.
 
334
            # we dont have a parent ie known yet.: use the relatively slower inventory 
 
335
            # probing method
 
336
            versioned = inv.has_filename(rf.raw_path)
 
337
            if versioned:
 
338
                continue
 
339
            added.extend(_add_one_and_parent(self, inv, None, rf, kind, action))
 
340
 
 
341
        if not recurse:
 
342
            # no need to walk any directories at all.
 
343
            if len(added) > 0 and save:
 
344
                self._write_inventory(inv)
 
345
            return added, ignored
 
346
 
 
347
        # only walk the minimal parents needed: we have user_dirs to override
 
348
        # ignores.
 
349
        prev_dir = None
 
350
 
 
351
        is_inside = osutils.is_inside_or_parent_of_any
 
352
        for path in sorted(user_dirs):
 
353
            if (prev_dir is None or not is_inside([prev_dir], path.raw_path)):
 
354
                dirs_to_add.append((path, None))
 
355
            prev_dir = path.raw_path
 
356
 
 
357
        # dirs_to_add is initialised to a list of directories, but as we scan
 
358
        # directories we append files to it.
 
359
        # XXX: We should determine kind of files when we scan them rather than
 
360
        # adding to this list. RBC 20070703
 
361
        for directory, parent_ie in dirs_to_add:
 
362
            # directory is tree-relative
 
363
            abspath = self.abspath(directory.raw_path)
 
364
 
 
365
            # get the contents of this directory.
 
366
 
 
367
            # find the kind of the path being added.
 
368
            kind = osutils.file_kind(abspath)
 
369
 
 
370
            if not InventoryEntry.versionable_kind(kind):
 
371
                warning("skipping %s (can't add file of kind '%s')", abspath, kind)
 
372
                continue
 
373
 
 
374
            if parent_ie is not None:
 
375
                versioned = directory.base_path in parent_ie.children
 
376
            else:
 
377
                # without the parent ie, use the relatively slower inventory 
 
378
                # probing method
 
379
                versioned = inv.has_filename(directory.raw_path)
 
380
 
 
381
            if kind == 'directory':
 
382
                try:
 
383
                    sub_branch = bzrdir.BzrDir.open(abspath)
 
384
                    sub_tree = True
 
385
                except errors.NotBranchError:
 
386
                    sub_tree = False
 
387
                except errors.UnsupportedFormatError:
 
388
                    sub_tree = True
 
389
            else:
 
390
                sub_tree = False
 
391
 
 
392
            if directory.raw_path == '':
 
393
                # mutter("tree root doesn't need to be added")
 
394
                sub_tree = False
 
395
            elif versioned:
 
396
                pass
 
397
                # mutter("%r is already versioned", abspath)
 
398
            elif sub_tree:
 
399
                # XXX: This is wrong; people *might* reasonably be trying to add
 
400
                # subtrees as subtrees.  This should probably only be done in formats 
 
401
                # which can represent subtrees, and even then perhaps only when
 
402
                # the user asked to add subtrees.  At the moment you can add them
 
403
                # specially through 'join --reference', which is perhaps
 
404
                # reasonable: adding a new reference is a special operation and
 
405
                # can have a special behaviour.  mbp 20070306
 
406
                mutter("%r is a nested bzr tree", abspath)
 
407
            else:
 
408
                _add_one(self, inv, parent_ie, directory, kind, action)
 
409
                added.append(directory.raw_path)
 
410
 
 
411
            if kind == 'directory' and not sub_tree:
 
412
                if parent_ie is not None:
 
413
                    # must be present:
 
414
                    this_ie = parent_ie.children[directory.base_path]
 
415
                else:
 
416
                    # without the parent ie, use the relatively slower inventory 
 
417
                    # probing method
 
418
                    this_id = inv.path2id(directory.raw_path)
 
419
                    if this_id is None:
 
420
                        this_ie = None
 
421
                    else:
 
422
                        this_ie = inv[this_id]
 
423
 
 
424
                for subf in sorted(os.listdir(abspath)):
 
425
                    # here we could use TreeDirectory rather than 
 
426
                    # string concatenation.
 
427
                    subp = osutils.pathjoin(directory.raw_path, subf)
 
428
                    # TODO: is_control_filename is very slow. Make it faster. 
 
429
                    # TreeDirectory.is_control_filename could also make this 
 
430
                    # faster - its impossible for a non root dir to have a 
 
431
                    # control file.
 
432
                    if self.is_control_filename(subp):
 
433
                        mutter("skip control directory %r", subp)
 
434
                    elif subf in this_ie.children:
 
435
                        # recurse into this already versioned subdir.
 
436
                        dirs_to_add.append((_FastPath(subp, subf), this_ie))
 
437
                    else:
 
438
                        # user selection overrides ignoes
 
439
                        # ignore while selecting files - if we globbed in the
 
440
                        # outer loop we would ignore user files.
 
441
                        ignore_glob = self.is_ignored(subp)
 
442
                        if ignore_glob is not None:
 
443
                            # mutter("skip ignored sub-file %r", subp)
 
444
                            ignored.setdefault(ignore_glob, []).append(subp)
 
445
                        else:
 
446
                            #mutter("queue to add sub-file %r", subp)
 
447
                            dirs_to_add.append((_FastPath(subp, subf), this_ie))
 
448
 
 
449
        if len(added) > 0:
 
450
            if save:
 
451
                self._write_inventory(inv)
 
452
            else:
 
453
                self.read_working_inventory()
 
454
        return added, ignored
 
455
 
 
456
 
 
457
class _FastPath(object):
 
458
    """A path object with fast accessors for things like basename."""
 
459
 
 
460
    __slots__ = ['raw_path', 'base_path']
 
461
 
 
462
    def __init__(self, path, base_path=None):
 
463
        """Construct a FastPath from path."""
 
464
        if base_path is None:
 
465
            self.base_path = osutils.basename(path)
 
466
        else:
 
467
            self.base_path = base_path
 
468
        self.raw_path = path
 
469
 
 
470
    def __cmp__(self, other):
 
471
        return cmp(self.raw_path, other.raw_path)
 
472
 
 
473
    def __hash__(self):
 
474
        return hash(self.raw_path)
 
475
 
 
476
 
 
477
def _add_one_and_parent(tree, inv, parent_ie, path, kind, action):
 
478
    """Add a new entry to the inventory and automatically add unversioned parents.
 
479
 
 
480
    :param inv: Inventory which will receive the new entry.
 
481
    :param parent_ie: Parent inventory entry if known, or None.  If
 
482
        None, the parent is looked up by name and used if present, otherwise it
 
483
        is recursively added.
 
484
    :param kind: Kind of new entry (file, directory, etc)
 
485
    :param action: callback(inv, parent_ie, path, kind); return ignored.
 
486
    :return: A list of paths which have been added.
 
487
    """
 
488
    # Nothing to do if path is already versioned.
 
489
    # This is safe from infinite recursion because the tree root is
 
490
    # always versioned.
 
491
    if parent_ie is not None:
 
492
        # we have a parent ie already
 
493
        added = []
 
494
    else:
 
495
        # slower but does not need parent_ie
 
496
        if inv.has_filename(path.raw_path):
 
497
            return []
 
498
        # its really not there : add the parent
 
499
        # note that the dirname use leads to some extra str copying etc but as
 
500
        # there are a limited number of dirs we can be nested under, it should
 
501
        # generally find it very fast and not recurse after that.
 
502
        added = _add_one_and_parent(tree, inv, None,
 
503
            _FastPath(dirname(path.raw_path)), 'directory', action)
 
504
        parent_id = inv.path2id(dirname(path.raw_path))
 
505
        parent_ie = inv[parent_id]
 
506
    _add_one(tree, inv, parent_ie, path, kind, action)
 
507
    return added + [path.raw_path]
 
508
 
 
509
 
 
510
def _add_one(tree, inv, parent_ie, path, kind, file_id_callback):
 
511
    """Add a new entry to the inventory.
 
512
 
 
513
    :param inv: Inventory which will receive the new entry.
 
514
    :param parent_ie: Parent inventory entry.
 
515
    :param kind: Kind of new entry (file, directory, etc)
 
516
    :param file_id_callback: callback(inv, parent_ie, path, kind); return a
 
517
        file_id or None to generate a new file id
 
518
    :returns: None
 
519
    """
 
520
    file_id = file_id_callback(inv, parent_ie, path, kind)
 
521
    entry = inv.make_entry(kind, path.base_path, parent_ie.file_id,
 
522
        file_id=file_id)
 
523
    inv.add(entry)