/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 breezy/workingtree.py

  • Committer: Jelmer Vernooij
  • Date: 2017-07-23 22:06:41 UTC
  • mfrom: (6738 trunk)
  • mto: This revision was merged to the branch mainline in revision 6739.
  • Revision ID: jelmer@jelmer.uk-20170723220641-69eczax9bmv8d6kk
Merge trunk, address review comments.

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
WorkingTree.open(dir).
28
28
"""
29
29
 
30
 
import contextlib
 
30
from __future__ import absolute_import
 
31
 
31
32
import errno
32
33
import os
 
34
import re
33
35
import sys
34
36
 
35
37
import breezy
36
38
 
37
39
from .lazy_import import lazy_import
38
40
lazy_import(globals(), """
39
 
import shutil
 
41
from bisect import bisect_left
 
42
import itertools
 
43
import operator
40
44
import stat
41
45
 
42
46
from breezy import (
 
47
    branch,
43
48
    conflicts as _mod_conflicts,
 
49
    controldir,
 
50
    errors,
44
51
    filters as _mod_filters,
 
52
    generate_ids,
45
53
    merge,
46
54
    revision as _mod_revision,
 
55
    shelf,
47
56
    transform,
48
57
    transport,
 
58
    ui,
49
59
    views,
50
60
    )
51
 
from breezy.bzr import (
52
 
    generate_ids,
53
 
    )
54
61
""")
55
62
 
56
63
from . import (
57
 
    errors,
58
 
    )
59
 
from .controldir import (
60
 
    ControlComponent,
61
 
    ControlComponentFormatRegistry,
62
 
    ControlComponentFormat,
63
 
    ControlDir,
64
 
    ControlDirFormat,
65
 
    )
66
 
from . import (
67
64
    osutils,
68
65
    )
 
66
from .decorators import needs_read_lock, needs_write_lock
69
67
from .i18n import gettext
70
68
from . import mutabletree
71
 
from .symbol_versioning import deprecated_method, deprecated_in
 
69
from .mutabletree import needs_tree_write_lock
 
70
from .sixish import (
 
71
    text_type,
 
72
    )
72
73
from .trace import mutter, note
73
74
 
74
75
 
75
 
class SettingFileIdUnsupported(errors.BzrError):
76
 
 
77
 
    _fmt = "This format does not support setting file ids."
78
 
 
79
 
 
80
 
class ShelvingUnsupported(errors.BzrError):
81
 
 
82
 
    _fmt = "This format does not support shelving changes."
83
 
 
84
 
 
85
 
class WorkingTree(mutabletree.MutableTree, ControlComponent):
 
76
ERROR_PATH_NOT_FOUND = 3    # WindowsError errno code, equivalent to ENOENT
 
77
 
 
78
 
 
79
class TreeEntry(object):
 
80
    """An entry that implements the minimum interface used by commands.
 
81
 
 
82
    This needs further inspection, it may be better to have
 
83
    InventoryEntries without ids - though that seems wrong. For now,
 
84
    this is a parallel hierarchy to InventoryEntry, and needs to become
 
85
    one of several things: decorates to that hierarchy, children of, or
 
86
    parents of it.
 
87
    Another note is that these objects are currently only used when there is
 
88
    no InventoryEntry available - i.e. for unversioned objects.
 
89
    Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
 
90
    """
 
91
 
 
92
    def __eq__(self, other):
 
93
        # yes, this us ugly, TODO: best practice __eq__ style.
 
94
        return (isinstance(other, TreeEntry)
 
95
                and other.__class__ == self.__class__)
 
96
 
 
97
    def kind_character(self):
 
98
        return "???"
 
99
 
 
100
 
 
101
class TreeDirectory(TreeEntry):
 
102
    """See TreeEntry. This is a directory in a working tree."""
 
103
 
 
104
    def __eq__(self, other):
 
105
        return (isinstance(other, TreeDirectory)
 
106
                and other.__class__ == self.__class__)
 
107
 
 
108
    def kind_character(self):
 
109
        return "/"
 
110
 
 
111
 
 
112
class TreeFile(TreeEntry):
 
113
    """See TreeEntry. This is a regular file in a working tree."""
 
114
 
 
115
    def __eq__(self, other):
 
116
        return (isinstance(other, TreeFile)
 
117
                and other.__class__ == self.__class__)
 
118
 
 
119
    def kind_character(self):
 
120
        return ''
 
121
 
 
122
 
 
123
class TreeLink(TreeEntry):
 
124
    """See TreeEntry. This is a symlink in a working tree."""
 
125
 
 
126
    def __eq__(self, other):
 
127
        return (isinstance(other, TreeLink)
 
128
                and other.__class__ == self.__class__)
 
129
 
 
130
    def kind_character(self):
 
131
        return ''
 
132
 
 
133
 
 
134
class WorkingTree(mutabletree.MutableTree,
 
135
    controldir.ControlComponent):
86
136
    """Working copy tree.
87
137
 
88
138
    :ivar basedir: The root of the tree on disk. This is a unicode path object
107
157
        self.controldir = _controldir
108
158
        if not _internal:
109
159
            raise errors.BzrError("Please use controldir.open_workingtree or "
110
 
                                  "WorkingTree.open() to obtain a WorkingTree.")
 
160
                "WorkingTree.open() to obtain a WorkingTree.")
111
161
        basedir = osutils.safe_unicode(basedir)
112
162
        mutter("opening working tree %r", basedir)
113
163
        if branch is not None:
127
177
    def control_transport(self):
128
178
        return self._transport
129
179
 
130
 
    def supports_symlinks(self):
131
 
        return osutils.supports_symlinks(self.basedir)
132
 
 
133
180
    def is_control_filename(self, filename):
134
181
        """True if filename is the name of a control file in this tree.
135
182
 
154
201
        """See `Tree.has_versioned_directories`."""
155
202
        return self._format.supports_versioned_directories
156
203
 
157
 
    def supports_merge_modified(self):
158
 
        """Indicate whether this workingtree supports storing merge_modified.
159
 
        """
160
 
        return self._format.supports_merge_modified
161
 
 
162
204
    def _supports_executable(self):
163
 
        return osutils.supports_executable(self.basedir)
 
205
        if sys.platform == 'win32':
 
206
            return False
 
207
        # FIXME: Ideally this should check the file system
 
208
        return True
164
209
 
165
210
    def break_lock(self):
166
211
        """Break a lock if one is present from another instance.
184
229
    def supports_views(self):
185
230
        return self.views.supports_views()
186
231
 
187
 
    def supports_setting_file_ids(self):
188
 
        return self._format.supports_setting_file_ids
189
 
 
190
232
    def get_config_stack(self):
191
233
        """Retrieve the config stack for this tree.
192
234
 
202
244
        """
203
245
        if path is None:
204
246
            path = osutils.getcwd()
205
 
        control = ControlDir.open(path, _unsupported=_unsupported)
 
247
        control = controldir.ControlDir.open(path, _unsupported=_unsupported)
206
248
        return control.open_workingtree(unsupported=_unsupported)
207
249
 
208
250
    @staticmethod
220
262
        """
221
263
        if path is None:
222
264
            path = osutils.getcwd()
223
 
        control, relpath = ControlDir.open_containing(path)
 
265
        control, relpath = controldir.ControlDir.open_containing(path)
224
266
        return control.open_workingtree(), relpath
225
267
 
226
268
    @staticmethod
279
321
        # self.relpath exists as a "thunk" to osutils, but canonical_relpath
280
322
        # doesn't - fix that up here before we enter the loop.
281
323
        if canonicalize:
282
 
            def fixer(p):
283
 
                return osutils.canonical_relpath(self.basedir, p)
 
324
            fixer = lambda p: osutils.canonical_relpath(self.basedir, p)
284
325
        else:
285
326
            fixer = self.relpath
286
327
        for filename in file_list:
287
328
            relpath = fixer(osutils.dereference_path(filename))
288
329
            if view_files and not osutils.is_inside_any(view_files, relpath):
289
 
                raise views.FileOutsideView(filename, view_files)
 
330
                raise errors.FileOutsideView(filename, view_files)
290
331
            new_list.append(relpath)
291
332
        return new_list
292
333
 
298
339
        """
299
340
        return WorkingTree.open(path, _unsupported=True)
300
341
 
 
342
    @staticmethod
 
343
    def find_trees(location):
 
344
        def list_current(transport):
 
345
            return [d for d in transport.list_dir('')
 
346
                    if not controldir.is_control_filename(d)]
 
347
        def evaluate(controldir):
 
348
            try:
 
349
                tree = controldir.open_workingtree()
 
350
            except errors.NoWorkingTree:
 
351
                return True, None
 
352
            else:
 
353
                return True, tree
 
354
        t = transport.get_transport(location)
 
355
        iterator = controldir.ControlDir.find_controldirs(t, evaluate=evaluate,
 
356
                                              list_current=list_current)
 
357
        return [tr for tr in iterator if tr is not None]
 
358
 
301
359
    def __repr__(self):
302
360
        return "<%s of %s>" % (self.__class__.__name__,
303
361
                               getattr(self, 'basedir', None))
319
377
            # in the future this should return the tree for
320
378
            # 'empty:' - the implicit root empty tree.
321
379
            return self.branch.repository.revision_tree(
322
 
                _mod_revision.NULL_REVISION)
 
380
                       _mod_revision.NULL_REVISION)
323
381
        try:
324
382
            return self.revision_tree(revision_id)
325
383
        except errors.NoSuchRevision:
326
384
            pass
327
385
        # No cached copy available, retrieve from the repository.
328
 
        # FIXME? RBC 20060403 should we cache the tree locally
 
386
        # FIXME? RBC 20060403 should we cache the inventory locally
329
387
        # at this point ?
330
388
        try:
331
389
            return self.branch.repository.revision_tree(revision_id)
337
395
                raise
338
396
            # the basis tree is a ghost so return an empty tree.
339
397
            return self.branch.repository.revision_tree(
340
 
                _mod_revision.NULL_REVISION)
 
398
                       _mod_revision.NULL_REVISION)
341
399
 
342
400
    def relpath(self, path):
343
401
        """Return the local path portion from a given path.
350
408
    def has_filename(self, filename):
351
409
        return osutils.lexists(self.abspath(filename))
352
410
 
353
 
    def get_file(self, path, filtered=True):
354
 
        return self.get_file_with_stat(path, filtered=filtered)[0]
 
411
    def get_file(self, file_id, path=None, filtered=True):
 
412
        return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
355
413
 
356
 
    def get_file_with_stat(self, path, filtered=True,
 
414
    def get_file_with_stat(self, file_id, path=None, filtered=True,
357
415
                           _fstat=osutils.fstat):
358
416
        """See Tree.get_file_with_stat."""
359
 
        abspath = self.abspath(path)
360
 
        try:
361
 
            file_obj = open(abspath, 'rb')
362
 
        except EnvironmentError as e:
363
 
            if e.errno == errno.ENOENT:
364
 
                raise errors.NoSuchFile(path)
365
 
            raise
 
417
        if path is None:
 
418
            path = self.id2path(file_id)
 
419
        file_obj = self.get_file_byname(path, filtered=False)
366
420
        stat_value = _fstat(file_obj.fileno())
367
421
        if filtered and self.supports_content_filtering():
368
422
            filters = self._content_filter_stack(path)
369
 
            if filters:
370
 
                file_obj, size = _mod_filters.filtered_input_file(
371
 
                    file_obj, filters)
372
 
                stat_value = _mod_filters.FilteredStat(
373
 
                    stat_value, st_size=size)
 
423
            file_obj = _mod_filters.filtered_input_file(file_obj, filters)
374
424
        return (file_obj, stat_value)
375
425
 
376
 
    def get_file_text(self, path, filtered=True):
377
 
        with self.get_file(path, filtered=filtered) as my_file:
 
426
    def get_file_text(self, file_id, path=None, filtered=True):
 
427
        my_file = self.get_file(file_id, path=path, filtered=filtered)
 
428
        try:
378
429
            return my_file.read()
379
 
 
380
 
    def get_file_lines(self, path, filtered=True):
 
430
        finally:
 
431
            my_file.close()
 
432
 
 
433
    def get_file_byname(self, filename, filtered=True):
 
434
        path = self.abspath(filename)
 
435
        f = file(path, 'rb')
 
436
        if filtered and self.supports_content_filtering():
 
437
            filters = self._content_filter_stack(filename)
 
438
            return _mod_filters.filtered_input_file(f, filters)
 
439
        else:
 
440
            return f
 
441
 
 
442
    def get_file_lines(self, file_id, path=None, filtered=True):
381
443
        """See Tree.get_file_lines()"""
382
 
        with self.get_file(path, filtered=filtered) as file:
 
444
        file = self.get_file(file_id, path, filtered=filtered)
 
445
        try:
383
446
            return file.readlines()
 
447
        finally:
 
448
            file.close()
384
449
 
385
450
    def get_parent_ids(self):
386
451
        """See Tree.get_parent_ids.
399
464
            pass
400
465
        else:
401
466
            for l in osutils.split_lines(merges_bytes):
402
 
                revision_id = l.rstrip(b'\n')
 
467
                revision_id = l.rstrip('\n')
403
468
                parents.append(revision_id)
404
469
        return parents
405
470
 
 
471
    def get_root_id(self):
 
472
        """Return the id of this trees root"""
 
473
        raise NotImplementedError(self.get_root_id)
 
474
 
 
475
    @needs_read_lock
406
476
    def clone(self, to_controldir, revision_id=None):
407
477
        """Duplicate this working tree into to_bzr, including all state.
408
478
 
416
486
            revision, and difference between the source trees last revision
417
487
            and this one merged in.
418
488
        """
419
 
        with self.lock_read():
420
 
            # assumes the target bzr dir format is compatible.
421
 
            result = to_controldir.create_workingtree()
422
 
            self.copy_content_into(result, revision_id)
423
 
            return result
 
489
        # assumes the target bzr dir format is compatible.
 
490
        result = to_controldir.create_workingtree()
 
491
        self.copy_content_into(result, revision_id)
 
492
        return result
424
493
 
 
494
    @needs_read_lock
425
495
    def copy_content_into(self, tree, revision_id=None):
426
496
        """Copy the current content and user files of this tree into tree."""
427
 
        with self.lock_read():
428
 
            tree.set_root_id(self.path2id(''))
429
 
            if revision_id is None:
430
 
                merge.transform_tree(tree, self)
 
497
        tree.set_root_id(self.get_root_id())
 
498
        if revision_id is None:
 
499
            merge.transform_tree(tree, self)
 
500
        else:
 
501
            # TODO now merge from tree.last_revision to revision (to preserve
 
502
            # user local changes)
 
503
            try:
 
504
                other_tree = self.revision_tree(revision_id)
 
505
            except errors.NoSuchRevision:
 
506
                other_tree = self.branch.repository.revision_tree(revision_id)
 
507
 
 
508
            merge.transform_tree(tree, other_tree)
 
509
            if revision_id == _mod_revision.NULL_REVISION:
 
510
                new_parents = []
431
511
            else:
432
 
                # TODO now merge from tree.last_revision to revision (to
433
 
                # preserve user local changes)
434
 
                try:
435
 
                    other_tree = self.revision_tree(revision_id)
436
 
                except errors.NoSuchRevision:
437
 
                    other_tree = self.branch.repository.revision_tree(
438
 
                        revision_id)
439
 
 
440
 
                merge.transform_tree(tree, other_tree)
441
 
                if revision_id == _mod_revision.NULL_REVISION:
442
 
                    new_parents = []
443
 
                else:
444
 
                    new_parents = [revision_id]
445
 
                tree.set_parent_ids(new_parents)
446
 
 
447
 
    def get_file_size(self, path):
 
512
                new_parents = [revision_id]
 
513
            tree.set_parent_ids(new_parents)
 
514
 
 
515
    def id2abspath(self, file_id):
 
516
        return self.abspath(self.id2path(file_id))
 
517
 
 
518
    def get_file_size(self, file_id):
448
519
        """See Tree.get_file_size"""
449
520
        # XXX: this returns the on-disk size; it should probably return the
450
521
        # canonical size
451
522
        try:
452
 
            return os.path.getsize(self.abspath(path))
 
523
            return os.path.getsize(self.id2abspath(file_id))
453
524
        except OSError as e:
454
525
            if e.errno != errno.ENOENT:
455
526
                raise
456
527
            else:
457
528
                return None
458
529
 
 
530
    @needs_tree_write_lock
459
531
    def _gather_kinds(self, files, kinds):
460
532
        """See MutableTree._gather_kinds."""
461
 
        with self.lock_tree_write():
462
 
            for pos, f in enumerate(files):
463
 
                if kinds[pos] is None:
464
 
                    fullpath = osutils.normpath(self.abspath(f))
465
 
                    try:
466
 
                        kinds[pos] = osutils.file_kind(fullpath)
467
 
                    except OSError as e:
468
 
                        if e.errno == errno.ENOENT:
469
 
                            raise errors.NoSuchFile(fullpath)
 
533
        for pos, f in enumerate(files):
 
534
            if kinds[pos] is None:
 
535
                fullpath = osutils.normpath(self.abspath(f))
 
536
                try:
 
537
                    kinds[pos] = osutils.file_kind(fullpath)
 
538
                except OSError as e:
 
539
                    if e.errno == errno.ENOENT:
 
540
                        raise errors.NoSuchFile(fullpath)
470
541
 
 
542
    @needs_write_lock
471
543
    def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
472
544
        """Add revision_id as a parent.
473
545
 
479
551
            added, or the allow_leftmost_as_ghost parameter is set True.
480
552
        :param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
481
553
        """
482
 
        with self.lock_write():
483
 
            parents = self.get_parent_ids() + [revision_id]
484
 
            self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
485
 
                                or allow_leftmost_as_ghost)
 
554
        parents = self.get_parent_ids() + [revision_id]
 
555
        self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
 
556
            or allow_leftmost_as_ghost)
486
557
 
 
558
    @needs_tree_write_lock
487
559
    def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
488
560
        """Add revision_id, tree tuple as a parent.
489
561
 
497
569
            If the revision_id is a ghost, pass None for the tree.
498
570
        :param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
499
571
        """
500
 
        with self.lock_tree_write():
501
 
            parent_ids = self.get_parent_ids() + [parent_tuple[0]]
502
 
            if len(parent_ids) > 1:
503
 
                # the leftmost may have already been a ghost, preserve that if it
504
 
                # was.
505
 
                allow_leftmost_as_ghost = True
506
 
            self.set_parent_ids(parent_ids,
507
 
                                allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
572
        parent_ids = self.get_parent_ids() + [parent_tuple[0]]
 
573
        if len(parent_ids) > 1:
 
574
            # the leftmost may have already been a ghost, preserve that if it
 
575
            # was.
 
576
            allow_leftmost_as_ghost = True
 
577
        self.set_parent_ids(parent_ids,
 
578
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
508
579
 
 
580
    @needs_tree_write_lock
509
581
    def add_pending_merge(self, *revision_ids):
510
 
        with self.lock_tree_write():
511
 
            # TODO: Perhaps should check at this point that the
512
 
            # history of the revision is actually present?
513
 
            parents = self.get_parent_ids()
514
 
            updated = False
515
 
            for rev_id in revision_ids:
516
 
                if rev_id in parents:
517
 
                    continue
518
 
                parents.append(rev_id)
519
 
                updated = True
520
 
            if updated:
521
 
                self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
 
582
        # TODO: Perhaps should check at this point that the
 
583
        # history of the revision is actually present?
 
584
        parents = self.get_parent_ids()
 
585
        updated = False
 
586
        for rev_id in revision_ids:
 
587
            if rev_id in parents:
 
588
                continue
 
589
            parents.append(rev_id)
 
590
            updated = True
 
591
        if updated:
 
592
            self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
522
593
 
523
594
    def path_content_summary(self, path, _lstat=os.lstat,
524
 
                             _mapper=osutils.file_kind_from_stat_mode):
 
595
        _mapper=osutils.file_kind_from_stat_mode):
525
596
        """See Tree.path_content_summary."""
526
597
        abspath = self.abspath(path)
527
598
        try:
563
634
        if len(revision_ids) > 0:
564
635
            leftmost_id = revision_ids[0]
565
636
            if (not allow_leftmost_as_ghost and not
566
 
                    self.branch.repository.has_revision(leftmost_id)):
 
637
                self.branch.repository.has_revision(leftmost_id)):
567
638
                raise errors.GhostRevisionUnusableHere(leftmost_id)
568
639
 
569
640
    def _set_merges_from_parent_ids(self, parent_ids):
570
641
        merges = parent_ids[1:]
571
 
        self._transport.put_bytes('pending-merges', b'\n'.join(merges),
572
 
                                  mode=self.controldir._get_file_mode())
 
642
        self._transport.put_bytes('pending-merges', '\n'.join(merges),
 
643
            mode=self.controldir._get_file_mode())
573
644
 
574
645
    def _filter_parent_ids_by_ancestry(self, revision_ids):
575
646
        """Check that all merged revisions are proper 'heads'.
587
658
                new_revision_ids.append(revision_id)
588
659
        if new_revision_ids != revision_ids:
589
660
            mutter('requested to set revision_ids = %s,'
590
 
                   ' but filtered to %s', revision_ids, new_revision_ids)
 
661
                         ' but filtered to %s', revision_ids, new_revision_ids)
591
662
        return new_revision_ids
592
663
 
 
664
    @needs_tree_write_lock
593
665
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
594
666
        """Set the parent ids to revision_ids.
595
667
 
602
674
        :param revision_ids: The revision_ids to set as the parent ids of this
603
675
            working tree. Any of these may be ghosts.
604
676
        """
605
 
        with self.lock_tree_write():
606
 
            self._check_parents_for_ghosts(revision_ids,
607
 
                                           allow_leftmost_as_ghost=allow_leftmost_as_ghost)
608
 
            for revision_id in revision_ids:
609
 
                _mod_revision.check_not_reserved_id(revision_id)
610
 
 
611
 
            revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
612
 
 
613
 
            if len(revision_ids) > 0:
614
 
                self.set_last_revision(revision_ids[0])
615
 
            else:
616
 
                self.set_last_revision(_mod_revision.NULL_REVISION)
617
 
 
618
 
            self._set_merges_from_parent_ids(revision_ids)
619
 
 
 
677
        self._check_parents_for_ghosts(revision_ids,
 
678
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
679
        for revision_id in revision_ids:
 
680
            _mod_revision.check_not_reserved_id(revision_id)
 
681
 
 
682
        revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
 
683
 
 
684
        if len(revision_ids) > 0:
 
685
            self.set_last_revision(revision_ids[0])
 
686
        else:
 
687
            self.set_last_revision(_mod_revision.NULL_REVISION)
 
688
 
 
689
        self._set_merges_from_parent_ids(revision_ids)
 
690
 
 
691
    @needs_tree_write_lock
620
692
    def set_pending_merges(self, rev_list):
621
 
        with self.lock_tree_write():
622
 
            parents = self.get_parent_ids()
623
 
            leftmost = parents[:1]
624
 
            new_parents = leftmost + rev_list
625
 
            self.set_parent_ids(new_parents)
 
693
        parents = self.get_parent_ids()
 
694
        leftmost = parents[:1]
 
695
        new_parents = leftmost + rev_list
 
696
        self.set_parent_ids(new_parents)
626
697
 
 
698
    @needs_tree_write_lock
627
699
    def set_merge_modified(self, modified_hashes):
628
700
        """Set the merge modified hashes."""
629
701
        raise NotImplementedError(self.set_merge_modified)
638
710
        """
639
711
        return None
640
712
 
 
713
    @needs_write_lock # because merge pulls data into the branch.
641
714
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
642
715
                          merge_type=None, force=False):
643
716
        """Merge from a branch into this working tree.
649
722
            branch.last_revision().
650
723
        """
651
724
        from .merge import Merger, Merge3Merger
652
 
        with self.lock_write():
653
 
            merger = Merger(self.branch, this_tree=self)
654
 
            # check that there are no local alterations
655
 
            if not force and self.has_changes():
656
 
                raise errors.UncommittedChanges(self)
657
 
            if to_revision is None:
658
 
                to_revision = _mod_revision.ensure_null(branch.last_revision())
659
 
            merger.other_rev_id = to_revision
660
 
            if _mod_revision.is_null(merger.other_rev_id):
661
 
                raise errors.NoCommits(branch)
662
 
            self.branch.fetch(branch, stop_revision=merger.other_rev_id)
663
 
            merger.other_basis = merger.other_rev_id
664
 
            merger.other_tree = self.branch.repository.revision_tree(
665
 
                merger.other_rev_id)
666
 
            merger.other_branch = branch
667
 
            if from_revision is None:
668
 
                merger.find_base()
669
 
            else:
670
 
                merger.set_base_revision(from_revision, branch)
671
 
            if merger.base_rev_id == merger.other_rev_id:
672
 
                raise errors.PointlessMerge
673
 
            merger.backup_files = False
674
 
            if merge_type is None:
675
 
                merger.merge_type = Merge3Merger
676
 
            else:
677
 
                merger.merge_type = merge_type
678
 
            merger.set_interesting_files(None)
679
 
            merger.show_base = False
680
 
            merger.reprocess = False
681
 
            conflicts = merger.do_merge()
682
 
            merger.set_pending()
683
 
            return conflicts
 
725
        merger = Merger(self.branch, this_tree=self)
 
726
        # check that there are no local alterations
 
727
        if not force and self.has_changes():
 
728
            raise errors.UncommittedChanges(self)
 
729
        if to_revision is None:
 
730
            to_revision = _mod_revision.ensure_null(branch.last_revision())
 
731
        merger.other_rev_id = to_revision
 
732
        if _mod_revision.is_null(merger.other_rev_id):
 
733
            raise errors.NoCommits(branch)
 
734
        self.branch.fetch(branch, last_revision=merger.other_rev_id)
 
735
        merger.other_basis = merger.other_rev_id
 
736
        merger.other_tree = self.branch.repository.revision_tree(
 
737
            merger.other_rev_id)
 
738
        merger.other_branch = branch
 
739
        if from_revision is None:
 
740
            merger.find_base()
 
741
        else:
 
742
            merger.set_base_revision(from_revision, branch)
 
743
        if merger.base_rev_id == merger.other_rev_id:
 
744
            raise errors.PointlessMerge
 
745
        merger.backup_files = False
 
746
        if merge_type is None:
 
747
            merger.merge_type = Merge3Merger
 
748
        else:
 
749
            merger.merge_type = merge_type
 
750
        merger.set_interesting_files(None)
 
751
        merger.show_base = False
 
752
        merger.reprocess = False
 
753
        conflicts = merger.do_merge()
 
754
        merger.set_pending()
 
755
        return conflicts
684
756
 
685
757
    def merge_modified(self):
686
758
        """Return a dictionary of files modified by a merge.
690
762
        because of a merge.
691
763
 
692
764
        This returns a map of file_id->sha1, containing only files which are
693
 
        still in the working tree and have that text hash.
 
765
        still in the working inventory and have that text hash.
694
766
        """
695
767
        raise NotImplementedError(self.merge_modified)
696
768
 
 
769
    @needs_write_lock
697
770
    def mkdir(self, path, file_id=None):
698
771
        """See MutableTree.mkdir()."""
699
772
        if file_id is None:
700
 
            if self.supports_setting_file_ids():
701
 
                file_id = generate_ids.gen_file_id(os.path.basename(path))
 
773
            file_id = generate_ids.gen_file_id(os.path.basename(path))
 
774
        os.mkdir(self.abspath(path))
 
775
        self.add(path, file_id, 'directory')
 
776
        return file_id
 
777
 
 
778
    def get_symlink_target(self, file_id, path=None):
 
779
        if path is not None:
 
780
            abspath = self.abspath(path)
702
781
        else:
703
 
            if not self.supports_setting_file_ids():
704
 
                raise SettingFileIdUnsupported()
705
 
        with self.lock_write():
706
 
            os.mkdir(self.abspath(path))
707
 
            self.add(path, file_id, 'directory')
708
 
            return file_id
709
 
 
710
 
    def get_symlink_target(self, path):
711
 
        abspath = self.abspath(path)
712
 
        try:
713
 
            return osutils.readlink(abspath)
714
 
        except OSError as e:
715
 
            if getattr(e, 'errno', None) == errno.ENOENT:
716
 
                raise errors.NoSuchFile(path)
717
 
            raise
 
782
            abspath = self.id2abspath(file_id)
 
783
        target = osutils.readlink(abspath)
 
784
        return target
718
785
 
719
786
    def subsume(self, other_tree):
720
787
        raise NotImplementedError(self.subsume)
721
788
 
722
 
    def _directory_is_tree_reference(self, relpath):
723
 
        raise NotImplementedError(self._directory_is_tree_reference)
724
 
 
725
 
    def extract(self, path, format=None):
 
789
    def _setup_directory_is_tree_reference(self):
 
790
        if self._branch.repository._format.supports_tree_reference:
 
791
            self._directory_is_tree_reference = \
 
792
                self._directory_may_be_tree_reference
 
793
        else:
 
794
            self._directory_is_tree_reference = \
 
795
                self._directory_is_never_tree_reference
 
796
 
 
797
    def _directory_is_never_tree_reference(self, relpath):
 
798
        return False
 
799
 
 
800
    def _directory_may_be_tree_reference(self, relpath):
 
801
        # as a special case, if a directory contains control files then
 
802
        # it's a tree reference, except that the root of the tree is not
 
803
        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
 
804
        # TODO: We could ask all the control formats whether they
 
805
        # recognize this directory, but at the moment there's no cheap api
 
806
        # to do that.  Since we probably can only nest bzr checkouts and
 
807
        # they always use this name it's ok for now.  -- mbp 20060306
 
808
        #
 
809
        # FIXME: There is an unhandled case here of a subdirectory
 
810
        # containing .bzr but not a branch; that will probably blow up
 
811
        # when you try to commit it.  It might happen if there is a
 
812
        # checkout in a subdirectory.  This can be avoided by not adding
 
813
        # it.  mbp 20070306
 
814
 
 
815
    def extract(self, file_id, format=None):
726
816
        """Extract a subtree from this tree.
727
817
 
728
818
        A new branch will be created, relative to the path for this tree.
733
823
        """Write the in memory meta data to disk."""
734
824
        raise NotImplementedError(self.flush)
735
825
 
736
 
    def kind(self, relpath):
 
826
    def _kind(self, relpath):
737
827
        return osutils.file_kind(self.abspath(relpath))
738
828
 
739
 
    def list_files(self, include_root=False, from_dir=None, recursive=True,
740
 
                   recurse_nested=False):
 
829
    def list_files(self, include_root=False, from_dir=None, recursive=True):
741
830
        """List all files as (path, class, kind, id, entry).
742
831
 
743
832
        Lists, but does not descend into unversioned directories.
786
875
        """
787
876
        raise NotImplementedError(self.move)
788
877
 
789
 
    def copy_one(self, from_rel, to_rel):
790
 
        """Copy a file in the tree to a new location.
791
 
 
792
 
        This default implementation just copies the file, then
793
 
        adds the target.
794
 
 
795
 
        :param from_rel: From location (relative to tree root)
796
 
        :param to_rel: Target location (relative to tree root)
 
878
    @needs_tree_write_lock
 
879
    def rename_one(self, from_rel, to_rel, after=False):
 
880
        """Rename one file.
 
881
 
 
882
        This can change the directory or the filename or both.
 
883
 
 
884
        rename_one has several 'modes' to work. First, it can rename a physical
 
885
        file and change the file_id. That is the normal mode. Second, it can
 
886
        only change the file_id without touching any physical file.
 
887
 
 
888
        rename_one uses the second mode if 'after == True' and 'to_rel' is
 
889
        either not versioned or newly added, and present in the working tree.
 
890
 
 
891
        rename_one uses the second mode if 'after == False' and 'from_rel' is
 
892
        versioned but no longer in the working tree, and 'to_rel' is not
 
893
        versioned but present in the working tree.
 
894
 
 
895
        rename_one uses the first mode if 'after == False' and 'from_rel' is
 
896
        versioned and present in the working tree, and 'to_rel' is not
 
897
        versioned and not present in the working tree.
 
898
 
 
899
        Everything else results in an error.
797
900
        """
798
 
        shutil.copyfile(self.abspath(from_rel), self.abspath(to_rel))
799
 
        self.add(to_rel)
 
901
        raise NotImplementedError(self.rename_one)
800
902
 
 
903
    @needs_read_lock
801
904
    def unknowns(self):
802
905
        """Return all unknown files.
803
906
 
804
907
        These are files in the working directory that are not versioned or
805
908
        control files or ignored.
806
909
        """
807
 
        with self.lock_read():
808
 
            # force the extras method to be fully executed before returning, to
809
 
            # prevent race conditions with the lock
810
 
            return iter(
811
 
                [subp for subp in self.extras() if not self.is_ignored(subp)])
812
 
 
813
 
    def unversion(self, paths):
814
 
        """Remove the path in pahs from the current versioned set.
815
 
 
816
 
        When a path is unversioned, all of its children are automatically
 
910
        # force the extras method to be fully executed before returning, to
 
911
        # prevent race conditions with the lock
 
912
        return iter(
 
913
            [subp for subp in self.extras() if not self.is_ignored(subp)])
 
914
 
 
915
    def unversion(self, file_ids):
 
916
        """Remove the file ids in file_ids from the current versioned set.
 
917
 
 
918
        When a file_id is unversioned, all of its children are automatically
817
919
        unversioned.
818
920
 
819
 
        :param paths: The paths to stop versioning.
820
 
        :raises NoSuchFile: if any path is not currently versioned.
 
921
        :param file_ids: The file ids to stop versioning.
 
922
        :raises: NoSuchId if any fileid is not currently versioned.
821
923
        """
822
924
        raise NotImplementedError(self.unversion)
823
925
 
 
926
    @needs_write_lock
824
927
    def pull(self, source, overwrite=False, stop_revision=None,
825
928
             change_reporter=None, possible_transports=None, local=False,
826
 
             show_base=False, tag_selector=None):
827
 
        with self.lock_write(), source.lock_read():
 
929
             show_base=False):
 
930
        source.lock_read()
 
931
        try:
828
932
            old_revision_info = self.branch.last_revision_info()
829
933
            basis_tree = self.basis_tree()
830
934
            count = self.branch.pull(source, overwrite, stop_revision,
831
935
                                     possible_transports=possible_transports,
832
 
                                     local=local, tag_selector=tag_selector)
 
936
                                     local=local)
833
937
            new_revision_info = self.branch.last_revision_info()
834
938
            if new_revision_info != old_revision_info:
835
939
                repository = self.branch.repository
838
942
                    if parent_ids:
839
943
                        basis_id = parent_ids[0]
840
944
                        basis_tree = repository.revision_tree(basis_id)
841
 
                with basis_tree.lock_read():
 
945
                basis_tree.lock_read()
 
946
                try:
842
947
                    new_basis_tree = self.branch.basis_tree()
843
948
                    merge.merge_inner(
844
 
                        self.branch,
845
 
                        new_basis_tree,
846
 
                        basis_tree,
847
 
                        this_tree=self,
848
 
                        change_reporter=change_reporter,
849
 
                        show_base=show_base)
850
 
                    basis_root_id = basis_tree.path2id('')
851
 
                    new_root_id = new_basis_tree.path2id('')
 
949
                                self.branch,
 
950
                                new_basis_tree,
 
951
                                basis_tree,
 
952
                                this_tree=self,
 
953
                                change_reporter=change_reporter,
 
954
                                show_base=show_base)
 
955
                    basis_root_id = basis_tree.get_root_id()
 
956
                    new_root_id = new_basis_tree.get_root_id()
852
957
                    if new_root_id is not None and basis_root_id != new_root_id:
853
958
                        self.set_root_id(new_root_id)
 
959
                finally:
 
960
                    basis_tree.unlock()
854
961
                # TODO - dedup parents list with things merged by pull ?
855
962
                # reuse the revisiontree we merged against to set the new
856
963
                # tree data.
866
973
                merges = self.get_parent_ids()[1:]
867
974
                parent_trees.extend([
868
975
                    (parent, repository.revision_tree(parent)) for
869
 
                    parent in merges])
 
976
                     parent in merges])
870
977
                self.set_parent_trees(parent_trees)
871
978
            return count
 
979
        finally:
 
980
            source.unlock()
872
981
 
873
 
    def put_file_bytes_non_atomic(self, path, bytes):
 
982
    @needs_write_lock
 
983
    def put_file_bytes_non_atomic(self, file_id, bytes):
874
984
        """See MutableTree.put_file_bytes_non_atomic."""
875
 
        with self.lock_write(), open(self.abspath(path), 'wb') as stream:
 
985
        stream = file(self.id2abspath(file_id), 'wb')
 
986
        try:
876
987
            stream.write(bytes)
 
988
        finally:
 
989
            stream.close()
877
990
 
878
991
    def extras(self):
879
992
        """Yield all unversioned files in this WorkingTree.
880
993
 
881
 
        If there are any unversioned directories and the file format
882
 
        supports versioning directories, then only the directory is returned,
883
 
        not all its children. But if there are unversioned files under a
884
 
        versioned subdirectory, they are returned.
 
994
        If there are any unversioned directories then only the directory is
 
995
        returned, not all its children.  But if there are unversioned files
 
996
        under a versioned subdirectory, they are returned.
885
997
 
886
998
        Currently returned depth-first, sorted by name within directories.
887
999
        This is the same order used by 'osutils.walkdirs'.
900
1012
        """
901
1013
        raise NotImplementedError(self.is_ignored)
902
1014
 
903
 
    def stored_kind(self, path):
 
1015
    def kind(self, file_id):
 
1016
        return osutils.file_kind(self.id2abspath(file_id))
 
1017
 
 
1018
    def stored_kind(self, file_id):
904
1019
        """See Tree.stored_kind"""
905
1020
        raise NotImplementedError(self.stored_kind)
906
1021
 
924
1039
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
925
1040
        return kind, executable, stat_value
926
1041
 
 
1042
    def _file_size(self, entry, stat_value):
 
1043
        return stat_value.st_size
 
1044
 
927
1045
    def last_revision(self):
928
1046
        """Return the last revision of the branch for this tree.
929
1047
 
934
1052
        """
935
1053
        return self._last_revision()
936
1054
 
 
1055
    @needs_read_lock
937
1056
    def _last_revision(self):
938
1057
        """helper for get_parent_ids."""
939
 
        with self.lock_read():
940
 
            return _mod_revision.ensure_null(self.branch.last_revision())
 
1058
        return _mod_revision.ensure_null(self.branch.last_revision())
941
1059
 
942
1060
    def is_locked(self):
943
1061
        """Check if this tree is locked."""
990
1108
            self.branch._set_revision_history([new_revision])
991
1109
        return True
992
1110
 
 
1111
    @needs_tree_write_lock
993
1112
    def remove(self, files, verbose=False, to_file=None, keep_files=True,
994
 
               force=False):
 
1113
        force=False):
995
1114
        """Remove nominated files from the working tree metadata.
996
1115
 
997
1116
        :files: File paths relative to the basedir.
999
1118
        :force: Delete files and directories, even if they are changed and
1000
1119
            even if the directories are not empty.
1001
1120
        """
1002
 
        raise NotImplementedError(self.remove)
1003
 
 
 
1121
        if isinstance(files, (str, text_type)):
 
1122
            files = [files]
 
1123
 
 
1124
        inv_delta = []
 
1125
 
 
1126
        all_files = set() # specified and nested files 
 
1127
        unknown_nested_files=set()
 
1128
        if to_file is None:
 
1129
            to_file = sys.stdout
 
1130
 
 
1131
        files_to_backup = []
 
1132
 
 
1133
        def recurse_directory_to_add_files(directory):
 
1134
            # Recurse directory and add all files
 
1135
            # so we can check if they have changed.
 
1136
            for parent_info, file_infos in self.walkdirs(directory):
 
1137
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
 
1138
                    # Is it versioned or ignored?
 
1139
                    if self.path2id(relpath):
 
1140
                        # Add nested content for deletion.
 
1141
                        all_files.add(relpath)
 
1142
                    else:
 
1143
                        # Files which are not versioned
 
1144
                        # should be treated as unknown.
 
1145
                        files_to_backup.append(relpath)
 
1146
 
 
1147
        for filename in files:
 
1148
            # Get file name into canonical form.
 
1149
            abspath = self.abspath(filename)
 
1150
            filename = self.relpath(abspath)
 
1151
            if len(filename) > 0:
 
1152
                all_files.add(filename)
 
1153
                recurse_directory_to_add_files(filename)
 
1154
 
 
1155
        files = list(all_files)
 
1156
 
 
1157
        if len(files) == 0:
 
1158
            return # nothing to do
 
1159
 
 
1160
        # Sort needed to first handle directory content before the directory
 
1161
        files.sort(reverse=True)
 
1162
 
 
1163
        # Bail out if we are going to delete files we shouldn't
 
1164
        if not keep_files and not force:
 
1165
            for (file_id, path, content_change, versioned, parent_id, name,
 
1166
                 kind, executable) in self.iter_changes(self.basis_tree(),
 
1167
                     include_unchanged=True, require_versioned=False,
 
1168
                     want_unversioned=True, specific_files=files):
 
1169
                if versioned[0] == False:
 
1170
                    # The record is unknown or newly added
 
1171
                    files_to_backup.append(path[1])
 
1172
                elif (content_change and (kind[1] is not None) and
 
1173
                        osutils.is_inside_any(files, path[1])):
 
1174
                    # Versioned and changed, but not deleted, and still
 
1175
                    # in one of the dirs to be deleted.
 
1176
                    files_to_backup.append(path[1])
 
1177
 
 
1178
        def backup(file_to_backup):
 
1179
            backup_name = self.controldir._available_backup_name(file_to_backup)
 
1180
            osutils.rename(abs_path, self.abspath(backup_name))
 
1181
            return "removed %s (but kept a copy: %s)" % (file_to_backup,
 
1182
                                                         backup_name)
 
1183
 
 
1184
        # Build inv_delta and delete files where applicable,
 
1185
        # do this before any modifications to meta data.
 
1186
        for f in files:
 
1187
            fid = self.path2id(f)
 
1188
            message = None
 
1189
            if not fid:
 
1190
                message = "%s is not versioned." % (f,)
 
1191
            else:
 
1192
                if verbose:
 
1193
                    # having removed it, it must be either ignored or unknown
 
1194
                    if self.is_ignored(f):
 
1195
                        new_status = 'I'
 
1196
                    else:
 
1197
                        new_status = '?'
 
1198
                    # XXX: Really should be a more abstract reporter interface
 
1199
                    kind_ch = osutils.kind_marker(self.kind(fid))
 
1200
                    to_file.write(new_status + '       ' + f + kind_ch + '\n')
 
1201
                # Unversion file
 
1202
                inv_delta.append((f, None, fid, None))
 
1203
                message = "removed %s" % (f,)
 
1204
 
 
1205
            if not keep_files:
 
1206
                abs_path = self.abspath(f)
 
1207
                if osutils.lexists(abs_path):
 
1208
                    if (osutils.isdir(abs_path) and
 
1209
                        len(os.listdir(abs_path)) > 0):
 
1210
                        if force:
 
1211
                            osutils.rmtree(abs_path)
 
1212
                            message = "deleted %s" % (f,)
 
1213
                        else:
 
1214
                            message = backup(f)
 
1215
                    else:
 
1216
                        if f in files_to_backup:
 
1217
                            message = backup(f)
 
1218
                        else:
 
1219
                            osutils.delete_any(abs_path)
 
1220
                            message = "deleted %s" % (f,)
 
1221
                elif message is not None:
 
1222
                    # Only care if we haven't done anything yet.
 
1223
                    message = "%s does not exist." % (f,)
 
1224
 
 
1225
            # Print only one message (if any) per file.
 
1226
            if message is not None:
 
1227
                note(message)
 
1228
        self.apply_inventory_delta(inv_delta)
 
1229
 
 
1230
    @needs_tree_write_lock
1004
1231
    def revert(self, filenames=None, old_tree=None, backups=True,
1005
1232
               pb=None, report_changes=False):
1006
1233
        from .conflicts import resolve
1007
 
        with contextlib.ExitStack() as exit_stack:
1008
 
            exit_stack.enter_context(self.lock_tree_write())
1009
 
            if old_tree is None:
1010
 
                basis_tree = self.basis_tree()
1011
 
                exit_stack.enter_context(basis_tree.lock_read())
1012
 
                old_tree = basis_tree
1013
 
            else:
1014
 
                basis_tree = None
 
1234
        if old_tree is None:
 
1235
            basis_tree = self.basis_tree()
 
1236
            basis_tree.lock_read()
 
1237
            old_tree = basis_tree
 
1238
        else:
 
1239
            basis_tree = None
 
1240
        try:
1015
1241
            conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1016
1242
                                         report_changes)
1017
1243
            if filenames is None and len(self.get_parent_ids()) > 1:
1020
1246
                if last_revision != _mod_revision.NULL_REVISION:
1021
1247
                    if basis_tree is None:
1022
1248
                        basis_tree = self.basis_tree()
1023
 
                        exit_stack.enter_context(basis_tree.lock_read())
 
1249
                        basis_tree.lock_read()
1024
1250
                    parent_trees.append((last_revision, basis_tree))
1025
1251
                self.set_parent_trees(parent_trees)
1026
1252
                resolve(self)
1027
1253
            else:
1028
1254
                resolve(self, filenames, ignore_misses=True, recursive=True)
1029
 
            return conflicts
 
1255
        finally:
 
1256
            if basis_tree is not None:
 
1257
                basis_tree.unlock()
 
1258
        return conflicts
1030
1259
 
 
1260
    @needs_write_lock
1031
1261
    def store_uncommitted(self):
1032
1262
        """Store uncommitted changes from the tree in the branch."""
1033
 
        raise NotImplementedError(self.store_uncommitted)
 
1263
        target_tree = self.basis_tree()
 
1264
        shelf_creator = shelf.ShelfCreator(self, target_tree)
 
1265
        try:
 
1266
            if not shelf_creator.shelve_all():
 
1267
                return
 
1268
            self.branch.store_uncommitted(shelf_creator)
 
1269
            shelf_creator.transform()
 
1270
        finally:
 
1271
            shelf_creator.finalize()
 
1272
        note('Uncommitted changes stored in branch "%s".', self.branch.nick)
1034
1273
 
 
1274
    @needs_write_lock
1035
1275
    def restore_uncommitted(self):
1036
1276
        """Restore uncommitted changes from the branch into the tree."""
1037
 
        raise NotImplementedError(self.restore_uncommitted)
 
1277
        unshelver = self.branch.get_unshelver(self)
 
1278
        if unshelver is None:
 
1279
            return
 
1280
        try:
 
1281
            merger = unshelver.make_merger()
 
1282
            merger.ignore_zero = True
 
1283
            merger.do_merge()
 
1284
            self.branch.store_uncommitted(None)
 
1285
        finally:
 
1286
            unshelver.finalize()
1038
1287
 
1039
1288
    def revision_tree(self, revision_id):
1040
1289
        """See Tree.revision_tree.
1041
1290
 
1042
 
        For trees that can be obtained from the working tree, this
1043
 
        will do so. For other trees, it will fall back to the repository.
 
1291
        WorkingTree can supply revision_trees for the basis revision only
 
1292
        because there is only one cached inventory in the bzr directory.
1044
1293
        """
1045
1294
        raise NotImplementedError(self.revision_tree)
1046
1295
 
 
1296
    @needs_tree_write_lock
1047
1297
    def set_root_id(self, file_id):
1048
1298
        """Set the root id for this tree."""
1049
 
        if not self.supports_setting_file_ids():
1050
 
            raise SettingFileIdUnsupported()
1051
 
        with self.lock_tree_write():
1052
 
            # for compatibility
1053
 
            if file_id is None:
1054
 
                raise ValueError(
1055
 
                    'WorkingTree.set_root_id with fileid=None')
1056
 
            self._set_root_id(file_id)
 
1299
        # for compatability
 
1300
        if file_id is None:
 
1301
            raise ValueError(
 
1302
                'WorkingTree.set_root_id with fileid=None')
 
1303
        file_id = osutils.safe_file_id(file_id)
 
1304
        self._set_root_id(file_id)
1057
1305
 
1058
1306
    def _set_root_id(self, file_id):
1059
1307
        """Set the root id for this tree, in a format specific manner.
1126
1374
        finally:
1127
1375
            self.unlock()
1128
1376
 
 
1377
    @needs_tree_write_lock
1129
1378
    def _update_tree(self, old_tip=None, change_reporter=None, revision=None,
1130
1379
                     show_base=False):
1131
1380
        """Update a tree to the master branch.
1143
1392
        # We MUST save it even if an error occurs, because otherwise the users
1144
1393
        # local work is unreferenced and will appear to have been lost.
1145
1394
        #
1146
 
        with self.lock_tree_write():
1147
 
            nb_conflicts = 0
 
1395
        nb_conflicts = 0
 
1396
        try:
 
1397
            last_rev = self.get_parent_ids()[0]
 
1398
        except IndexError:
 
1399
            last_rev = _mod_revision.NULL_REVISION
 
1400
        if revision is None:
 
1401
            revision = self.branch.last_revision()
 
1402
 
 
1403
        old_tip = old_tip or _mod_revision.NULL_REVISION
 
1404
 
 
1405
        if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
 
1406
            # the branch we are bound to was updated
 
1407
            # merge those changes in first
 
1408
            base_tree  = self.basis_tree()
 
1409
            other_tree = self.branch.repository.revision_tree(old_tip)
 
1410
            nb_conflicts = merge.merge_inner(self.branch, other_tree,
 
1411
                                             base_tree, this_tree=self,
 
1412
                                             change_reporter=change_reporter,
 
1413
                                             show_base=show_base)
 
1414
            if nb_conflicts:
 
1415
                self.add_parent_tree((old_tip, other_tree))
 
1416
                note(gettext('Rerun update after fixing the conflicts.'))
 
1417
                return nb_conflicts
 
1418
 
 
1419
        if last_rev != _mod_revision.ensure_null(revision):
 
1420
            # the working tree is up to date with the branch
 
1421
            # we can merge the specified revision from master
 
1422
            to_tree = self.branch.repository.revision_tree(revision)
 
1423
            to_root_id = to_tree.get_root_id()
 
1424
 
 
1425
            basis = self.basis_tree()
 
1426
            basis.lock_read()
1148
1427
            try:
1149
 
                last_rev = self.get_parent_ids()[0]
1150
 
            except IndexError:
1151
 
                last_rev = _mod_revision.NULL_REVISION
1152
 
            if revision is None:
1153
 
                revision = self.branch.last_revision()
1154
 
 
1155
 
            old_tip = old_tip or _mod_revision.NULL_REVISION
1156
 
 
1157
 
            if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1158
 
                # the branch we are bound to was updated
1159
 
                # merge those changes in first
1160
 
                base_tree = self.basis_tree()
1161
 
                other_tree = self.branch.repository.revision_tree(old_tip)
1162
 
                nb_conflicts = merge.merge_inner(self.branch, other_tree,
1163
 
                                                 base_tree, this_tree=self,
1164
 
                                                 change_reporter=change_reporter,
1165
 
                                                 show_base=show_base)
1166
 
                if nb_conflicts:
1167
 
                    self.add_parent_tree((old_tip, other_tree))
1168
 
                    note(gettext('Rerun update after fixing the conflicts.'))
1169
 
                    return nb_conflicts
1170
 
 
1171
 
            if last_rev != _mod_revision.ensure_null(revision):
1172
 
                # the working tree is up to date with the branch
1173
 
                # we can merge the specified revision from master
1174
 
                to_tree = self.branch.repository.revision_tree(revision)
1175
 
                to_root_id = to_tree.path2id('')
1176
 
 
1177
 
                basis = self.basis_tree()
1178
 
                with basis.lock_read():
1179
 
                    if (basis.path2id('') is None or basis.path2id('') != to_root_id):
1180
 
                        self.set_root_id(to_root_id)
1181
 
                        self.flush()
1182
 
 
1183
 
                # determine the branch point
1184
 
                graph = self.branch.repository.get_graph()
1185
 
                base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
1186
 
                                                    last_rev)
1187
 
                base_tree = self.branch.repository.revision_tree(base_rev_id)
1188
 
 
1189
 
                nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
1190
 
                                                 this_tree=self,
1191
 
                                                 change_reporter=change_reporter,
1192
 
                                                 show_base=show_base)
1193
 
                self.set_last_revision(revision)
1194
 
                # TODO - dedup parents list with things merged by pull ?
1195
 
                # reuse the tree we've updated to to set the basis:
1196
 
                parent_trees = [(revision, to_tree)]
1197
 
                merges = self.get_parent_ids()[1:]
1198
 
                # Ideally we ask the tree for the trees here, that way the working
1199
 
                # tree can decide whether to give us the entire tree or give us a
1200
 
                # lazy initialised tree. dirstate for instance will have the trees
1201
 
                # in ram already, whereas a last-revision + basis-inventory tree
1202
 
                # will not, but also does not need them when setting parents.
1203
 
                for parent in merges:
1204
 
                    parent_trees.append(
1205
 
                        (parent, self.branch.repository.revision_tree(parent)))
1206
 
                if not _mod_revision.is_null(old_tip):
1207
 
                    parent_trees.append(
1208
 
                        (old_tip, self.branch.repository.revision_tree(old_tip)))
1209
 
                self.set_parent_trees(parent_trees)
1210
 
                last_rev = parent_trees[0][0]
1211
 
            return nb_conflicts
 
1428
                if (basis.get_root_id() is None or basis.get_root_id() != to_root_id):
 
1429
                    self.set_root_id(to_root_id)
 
1430
                    self.flush()
 
1431
            finally:
 
1432
                basis.unlock()
 
1433
 
 
1434
            # determine the branch point
 
1435
            graph = self.branch.repository.get_graph()
 
1436
            base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
 
1437
                                                last_rev)
 
1438
            base_tree = self.branch.repository.revision_tree(base_rev_id)
 
1439
 
 
1440
            nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
 
1441
                                             this_tree=self,
 
1442
                                             change_reporter=change_reporter,
 
1443
                                             show_base=show_base)
 
1444
            self.set_last_revision(revision)
 
1445
            # TODO - dedup parents list with things merged by pull ?
 
1446
            # reuse the tree we've updated to to set the basis:
 
1447
            parent_trees = [(revision, to_tree)]
 
1448
            merges = self.get_parent_ids()[1:]
 
1449
            # Ideally we ask the tree for the trees here, that way the working
 
1450
            # tree can decide whether to give us the entire tree or give us a
 
1451
            # lazy initialised tree. dirstate for instance will have the trees
 
1452
            # in ram already, whereas a last-revision + basis-inventory tree
 
1453
            # will not, but also does not need them when setting parents.
 
1454
            for parent in merges:
 
1455
                parent_trees.append(
 
1456
                    (parent, self.branch.repository.revision_tree(parent)))
 
1457
            if not _mod_revision.is_null(old_tip):
 
1458
                parent_trees.append(
 
1459
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
 
1460
            self.set_parent_trees(parent_trees)
 
1461
            last_rev = parent_trees[0][0]
 
1462
        return nb_conflicts
1212
1463
 
1213
1464
    def set_conflicts(self, arg):
1214
1465
        raise errors.UnsupportedOperation(self.set_conflicts, self)
1223
1474
        """Walk the directories of this tree.
1224
1475
 
1225
1476
        returns a generator which yields items in the form:
1226
 
                (current_directory_path,
1227
 
                 [(file1_path, file1_name, file1_kind, (lstat),
 
1477
                ((curren_directory_path, fileid),
 
1478
                 [(file1_path, file1_name, file1_kind, (lstat), file1_id,
1228
1479
                   file1_kind), ... ])
1229
1480
 
1230
1481
        This API returns a generator, which is only valid during the current
1233
1484
        If the tree is not locked, it may cause an error to be raised,
1234
1485
        depending on the tree implementation.
1235
1486
        """
1236
 
        raise NotImplementedError(self.walkdirs)
1237
 
 
1238
 
    @deprecated_method(deprecated_in((3, 0, 1)))
 
1487
        disk_top = self.abspath(prefix)
 
1488
        if disk_top.endswith('/'):
 
1489
            disk_top = disk_top[:-1]
 
1490
        top_strip_len = len(disk_top) + 1
 
1491
        inventory_iterator = self._walkdirs(prefix)
 
1492
        disk_iterator = osutils.walkdirs(disk_top, prefix)
 
1493
        try:
 
1494
            current_disk = next(disk_iterator)
 
1495
            disk_finished = False
 
1496
        except OSError as e:
 
1497
            if not (e.errno == errno.ENOENT or
 
1498
                (sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
 
1499
                raise
 
1500
            current_disk = None
 
1501
            disk_finished = True
 
1502
        try:
 
1503
            current_inv = next(inventory_iterator)
 
1504
            inv_finished = False
 
1505
        except StopIteration:
 
1506
            current_inv = None
 
1507
            inv_finished = True
 
1508
        while not inv_finished or not disk_finished:
 
1509
            if current_disk:
 
1510
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
 
1511
                    cur_disk_dir_content) = current_disk
 
1512
            else:
 
1513
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
 
1514
                    cur_disk_dir_content) = ((None, None), None)
 
1515
            if not disk_finished:
 
1516
                # strip out .bzr dirs
 
1517
                if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
 
1518
                    len(cur_disk_dir_content) > 0):
 
1519
                    # osutils.walkdirs can be made nicer -
 
1520
                    # yield the path-from-prefix rather than the pathjoined
 
1521
                    # value.
 
1522
                    bzrdir_loc = bisect_left(cur_disk_dir_content,
 
1523
                        ('.bzr', '.bzr'))
 
1524
                    if (bzrdir_loc < len(cur_disk_dir_content)
 
1525
                        and self.controldir.is_control_filename(
 
1526
                            cur_disk_dir_content[bzrdir_loc][0])):
 
1527
                        # we dont yield the contents of, or, .bzr itself.
 
1528
                        del cur_disk_dir_content[bzrdir_loc]
 
1529
            if inv_finished:
 
1530
                # everything is unknown
 
1531
                direction = 1
 
1532
            elif disk_finished:
 
1533
                # everything is missing
 
1534
                direction = -1
 
1535
            else:
 
1536
                direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
 
1537
            if direction > 0:
 
1538
                # disk is before inventory - unknown
 
1539
                dirblock = [(relpath, basename, kind, stat, None, None) for
 
1540
                    relpath, basename, kind, stat, top_path in
 
1541
                    cur_disk_dir_content]
 
1542
                yield (cur_disk_dir_relpath, None), dirblock
 
1543
                try:
 
1544
                    current_disk = next(disk_iterator)
 
1545
                except StopIteration:
 
1546
                    disk_finished = True
 
1547
            elif direction < 0:
 
1548
                # inventory is before disk - missing.
 
1549
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
 
1550
                    for relpath, basename, dkind, stat, fileid, kind in
 
1551
                    current_inv[1]]
 
1552
                yield (current_inv[0][0], current_inv[0][1]), dirblock
 
1553
                try:
 
1554
                    current_inv = next(inventory_iterator)
 
1555
                except StopIteration:
 
1556
                    inv_finished = True
 
1557
            else:
 
1558
                # versioned present directory
 
1559
                # merge the inventory and disk data together
 
1560
                dirblock = []
 
1561
                for relpath, subiterator in itertools.groupby(sorted(
 
1562
                    current_inv[1] + cur_disk_dir_content,
 
1563
                    key=operator.itemgetter(0)), operator.itemgetter(1)):
 
1564
                    path_elements = list(subiterator)
 
1565
                    if len(path_elements) == 2:
 
1566
                        inv_row, disk_row = path_elements
 
1567
                        # versioned, present file
 
1568
                        dirblock.append((inv_row[0],
 
1569
                            inv_row[1], disk_row[2],
 
1570
                            disk_row[3], inv_row[4],
 
1571
                            inv_row[5]))
 
1572
                    elif len(path_elements[0]) == 5:
 
1573
                        # unknown disk file
 
1574
                        dirblock.append((path_elements[0][0],
 
1575
                            path_elements[0][1], path_elements[0][2],
 
1576
                            path_elements[0][3], None, None))
 
1577
                    elif len(path_elements[0]) == 6:
 
1578
                        # versioned, absent file.
 
1579
                        dirblock.append((path_elements[0][0],
 
1580
                            path_elements[0][1], 'unknown', None,
 
1581
                            path_elements[0][4], path_elements[0][5]))
 
1582
                    else:
 
1583
                        raise NotImplementedError('unreachable code')
 
1584
                yield current_inv[0], dirblock
 
1585
                try:
 
1586
                    current_inv = next(inventory_iterator)
 
1587
                except StopIteration:
 
1588
                    inv_finished = True
 
1589
                try:
 
1590
                    current_disk = next(disk_iterator)
 
1591
                except StopIteration:
 
1592
                    disk_finished = True
 
1593
 
 
1594
    def _walkdirs(self, prefix=""):
 
1595
        """Walk the directories of this tree.
 
1596
 
 
1597
        :param prefix: is used as the directrory to start with.
 
1598
        :returns: a generator which yields items in the form::
 
1599
 
 
1600
            ((curren_directory_path, fileid),
 
1601
             [(file1_path, file1_name, file1_kind, None, file1_id,
 
1602
               file1_kind), ... ])
 
1603
        """
 
1604
        raise NotImplementedError(self._walkdirs)
 
1605
 
 
1606
    @needs_tree_write_lock
1239
1607
    def auto_resolve(self):
1240
1608
        """Automatically resolve text conflicts according to contents.
1241
1609
 
1244
1612
        into files that have text conflicts.  The corresponding .THIS .BASE and
1245
1613
        .OTHER files are deleted, as per 'resolve'.
1246
1614
 
1247
 
        :return: a tuple of lists: (un_resolved, resolved).
 
1615
        :return: a tuple of ConflictLists: (un_resolved, resolved).
1248
1616
        """
1249
 
        with self.lock_tree_write():
1250
 
            un_resolved = []
1251
 
            resolved = []
1252
 
            for conflict in self.conflicts():
1253
 
                try:
1254
 
                    conflict.action_auto(self)
1255
 
                except NotImplementedError:
1256
 
                    un_resolved.append(conflict)
 
1617
        un_resolved = _mod_conflicts.ConflictList()
 
1618
        resolved = _mod_conflicts.ConflictList()
 
1619
        conflict_re = re.compile('^(<{7}|={7}|>{7})')
 
1620
        for conflict in self.conflicts():
 
1621
            if (conflict.typestring != 'text conflict' or
 
1622
                self.kind(conflict.file_id) != 'file'):
 
1623
                un_resolved.append(conflict)
 
1624
                continue
 
1625
            my_file = open(self.id2abspath(conflict.file_id), 'rb')
 
1626
            try:
 
1627
                for line in my_file:
 
1628
                    if conflict_re.search(line):
 
1629
                        un_resolved.append(conflict)
 
1630
                        break
1257
1631
                else:
1258
 
                    conflict.cleanup(self)
1259
1632
                    resolved.append(conflict)
1260
 
            self.set_conflicts(un_resolved)
1261
 
            return un_resolved, resolved
 
1633
            finally:
 
1634
                my_file.close()
 
1635
        resolved.remove_files(self)
 
1636
        self.set_conflicts(un_resolved)
 
1637
        return un_resolved, resolved
1262
1638
 
1263
1639
    def _validate(self):
1264
1640
        """Validate internal structures.
1287
1663
        """See Tree._get_rules_searcher."""
1288
1664
        if self._rules_searcher is None:
1289
1665
            self._rules_searcher = super(WorkingTree,
1290
 
                                         self)._get_rules_searcher(default_searcher)
 
1666
                self)._get_rules_searcher(default_searcher)
1291
1667
        return self._rules_searcher
1292
1668
 
1293
1669
    def get_shelf_manager(self):
1294
1670
        """Return the ShelfManager for this WorkingTree."""
1295
 
        raise NotImplementedError(self.get_shelf_manager)
1296
 
 
1297
 
    def get_canonical_paths(self, paths):
1298
 
        """Like get_canonical_path() but works on multiple items.
1299
 
 
1300
 
        :param paths: A sequence of paths relative to the root of the tree.
1301
 
        :return: A list of paths, with each item the corresponding input path
1302
 
            adjusted to account for existing elements that match case
1303
 
            insensitively.
1304
 
        """
1305
 
        with self.lock_read():
1306
 
            for path in paths:
1307
 
                yield path
1308
 
 
1309
 
    def get_canonical_path(self, path):
1310
 
        """Returns the first item in the tree that matches a path.
1311
 
 
1312
 
        This is meant to allow case-insensitive path lookups on e.g.
1313
 
        FAT filesystems.
1314
 
 
1315
 
        If a path matches exactly, it is returned. If no path matches exactly
1316
 
        but more than one path matches according to the underlying file system,
1317
 
        it is implementation defined which is returned.
1318
 
 
1319
 
        If no path matches according to the file system, the input path is
1320
 
        returned, but with as many path entries that do exist changed to their
1321
 
        canonical form.
1322
 
 
1323
 
        If you need to resolve many names from the same tree, you should
1324
 
        use get_canonical_paths() to avoid O(N) behaviour.
1325
 
 
1326
 
        :param path: A paths relative to the root of the tree.
1327
 
        :return: The input path adjusted to account for existing elements
1328
 
        that match case insensitively.
1329
 
        """
1330
 
        with self.lock_read():
1331
 
            return next(self.get_canonical_paths([path]))
1332
 
 
1333
 
    def reference_parent(self, path, branch=None, possible_transports=None):
1334
 
        raise errors.UnsupportedOperation(self.reference_parent, self)
1335
 
 
1336
 
    def get_reference_info(self, path, branch=None):
1337
 
        raise errors.UnsupportedOperation(self.get_reference_info, self)
1338
 
 
1339
 
    def set_reference_info(self, tree_path, branch_location):
1340
 
        raise errors.UnsupportedOperation(self.set_reference_info, self)
1341
 
 
1342
 
 
1343
 
class WorkingTreeFormatRegistry(ControlComponentFormatRegistry):
 
1671
        from .shelf import ShelfManager
 
1672
        return ShelfManager(self, self._transport)
 
1673
 
 
1674
 
 
1675
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
1344
1676
    """Registry for working tree formats."""
1345
1677
 
1346
1678
    def __init__(self, other_registry=None):
1351
1683
    def get_default(self):
1352
1684
        """Return the current default format."""
1353
1685
        if (self._default_format_key is not None and
1354
 
                self._default_format is None):
 
1686
            self._default_format is None):
1355
1687
            self._default_format = self.get(self._default_format_key)
1356
1688
        return self._default_format
1357
1689
 
1369
1701
format_registry = WorkingTreeFormatRegistry()
1370
1702
 
1371
1703
 
1372
 
class WorkingTreeFormat(ControlComponentFormat):
 
1704
class WorkingTreeFormat(controldir.ControlComponentFormat):
1373
1705
    """An encapsulation of the initialization and open routines for a format.
1374
1706
 
1375
1707
    Formats provide three things:
1400
1732
 
1401
1733
    supports_versioned_directories = None
1402
1734
 
1403
 
    supports_merge_modified = True
1404
 
    """If this format supports storing merge modified hashes."""
1405
 
 
1406
 
    supports_setting_file_ids = True
1407
 
    """If this format allows setting the file id."""
1408
 
 
1409
 
    supports_store_uncommitted = True
1410
 
    """If this format supports shelve-like functionality."""
1411
 
 
1412
 
    supports_leftmost_parent_id_as_ghost = True
1413
 
 
1414
 
    supports_righthand_parent_id_as_ghost = True
1415
 
 
1416
 
    ignore_filename = None
1417
 
    """Name of file with ignore patterns, if any. """
1418
 
 
1419
1735
    def initialize(self, controldir, revision_id=None, from_branch=None,
1420
1736
                   accelerator_tree=None, hardlink=False):
1421
1737
        """Initialize a new working tree in controldir.
1466
1782
        This is to support testing of working tree formats that can not exist
1467
1783
        in the same control directory as a branch.
1468
1784
        """
1469
 
        return self._matchingcontroldir
1470
 
 
1471
 
 
1472
 
format_registry.register_lazy(b"Bazaar Working Tree Format 4 (bzr 0.15)\n",
1473
 
                              "breezy.bzr.workingtree_4", "WorkingTreeFormat4")
1474
 
format_registry.register_lazy(b"Bazaar Working Tree Format 5 (bzr 1.11)\n",
1475
 
                              "breezy.bzr.workingtree_4", "WorkingTreeFormat5")
1476
 
format_registry.register_lazy(b"Bazaar Working Tree Format 6 (bzr 1.14)\n",
1477
 
                              "breezy.bzr.workingtree_4", "WorkingTreeFormat6")
1478
 
format_registry.register_lazy(b"Bazaar-NG Working Tree format 3",
1479
 
                              "breezy.bzr.workingtree_3", "WorkingTreeFormat3")
1480
 
format_registry.set_default_key(b"Bazaar Working Tree Format 6 (bzr 1.14)\n")
 
1785
        return self._matchingbzrdir
 
1786
 
 
1787
 
 
1788
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
 
1789
    "breezy.bzr.workingtree_4", "WorkingTreeFormat4")
 
1790
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
 
1791
    "breezy.bzr.workingtree_4", "WorkingTreeFormat5")
 
1792
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
 
1793
    "breezy.bzr.workingtree_4", "WorkingTreeFormat6")
 
1794
format_registry.register_lazy("Bazaar-NG Working Tree format 3",
 
1795
    "breezy.bzr.workingtree_3", "WorkingTreeFormat3")
 
1796
format_registry.set_default_key("Bazaar Working Tree Format 6 (bzr 1.14)\n")