~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/conflicts.py

  • Committer: Rory Yorke
  • Date: 2010-10-20 14:38:53 UTC
  • mto: This revision was merged to the branch mainline in revision 5519.
  • Revision ID: rory.yorke@gmail.com-20101020143853-9kfd2ldcjfroh8jw
Show missing files in bzr status (bug 134168).

"bzr status" will now show missing files, that is, those added with "bzr
add" and then removed by non bzr means (e.g., rm).

Blackbox tests were added for this case, and tests were also added to
test_delta, since the implementation change is in bzrlib.delta.

Might also affect bug 189709.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2009, 2010, 2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2009, 2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
117
117
            ResolveActionOption(),
118
118
            ]
119
119
    _see_also = ['conflicts']
120
 
    def run(self, file_list=None, all=False, action=None, directory=None):
 
120
    def run(self, file_list=None, all=False, action=None, directory=u'.'):
121
121
        if all:
122
122
            if file_list:
123
123
                raise errors.BzrCommandError("If --all is specified,"
124
124
                                             " no FILE may be provided")
125
 
            if directory is None:
126
 
                directory = u'.'
127
125
            tree = workingtree.WorkingTree.open_containing(directory)[0]
128
126
            if action is None:
129
127
                action = 'done'
130
128
        else:
131
129
            tree, file_list = workingtree.WorkingTree.open_containing_paths(
132
 
                file_list, directory)
 
130
                file_list)
133
131
            if file_list is None:
134
132
                if action is None:
135
133
                    # FIXME: There is a special case here related to the option
159
157
                # conflict.auto(tree) --vila 091242
160
158
                pass
161
159
        else:
162
 
            before, after = resolve(tree, file_list, action=action)
163
 
            trace.note('%d conflict(s) resolved, %d remaining'
164
 
                       % (before - after, after))
 
160
            resolve(tree, file_list, action=action)
165
161
 
166
162
 
167
163
def resolve(tree, paths=None, ignore_misses=False, recursive=False,
180
176
    :param action: How the conflict should be resolved,
181
177
    """
182
178
    tree.lock_tree_write()
183
 
    nb_conflicts_after = None
184
179
    try:
185
180
        tree_conflicts = tree.conflicts()
186
 
        nb_conflicts_before = len(tree_conflicts)
187
181
        if paths is None:
188
182
            new_conflicts = ConflictList()
189
183
            to_process = tree_conflicts
197
191
            except NotImplementedError:
198
192
                new_conflicts.append(conflict)
199
193
        try:
200
 
            nb_conflicts_after = len(new_conflicts)
201
194
            tree.set_conflicts(new_conflicts)
202
195
        except errors.UnsupportedOperation:
203
196
            pass
204
197
    finally:
205
198
        tree.unlock()
206
 
    if nb_conflicts_after is None:
207
 
        nb_conflicts_after = nb_conflicts_before
208
 
    return nb_conflicts_before, nb_conflicts_after
209
199
 
210
200
 
211
201
def restore(filename):
514
504
        # Adjust the path for the retained file id
515
505
        tid = tt.trans_id_file_id(file_id)
516
506
        parent_tid = tt.get_tree_parent(tid)
517
 
        tt.adjust_path(osutils.basename(path), parent_tid, tid)
 
507
        tt.adjust_path(path, parent_tid, tid)
518
508
        tt.apply()
519
509
 
520
510
    def _revision_tree(self, tree, revid):
600
590
        # 'item.suffix_to_remove' has been deleted, this is a no-op)
601
591
        this_tid = tt.trans_id_file_id(self.file_id)
602
592
        parent_tid = tt.get_tree_parent(this_tid)
603
 
        tt.adjust_path(osutils.basename(self.path), parent_tid, this_tid)
 
593
        tt.adjust_path(self.path, parent_tid, this_tid)
604
594
        tt.apply()
605
595
 
606
596
    def action_take_this(self, tree):
610
600
        self._resolve_with_cleanups(tree, 'THIS')
611
601
 
612
602
 
 
603
# FIXME: TextConflict is about a single file-id, there never is a conflict_path
 
604
# attribute so we shouldn't inherit from PathConflict but simply from Conflict
 
605
 
613
606
# TODO: There should be a base revid attribute to better inform the user about
614
607
# how the conflicts were generated.
615
 
class TextConflict(Conflict):
 
608
class TextConflict(PathConflict):
616
609
    """The merge algorithm could not resolve all differences encountered."""
617
610
 
618
611
    has_files = True
621
614
 
622
615
    format = 'Text conflict in %(path)s'
623
616
 
624
 
    rformat = '%(class)s(%(path)r, %(file_id)r)'
625
 
 
626
617
    def associated_filenames(self):
627
618
        return [self.path + suffix for suffix in CONFLICT_SUFFIXES]
628
619
 
629
 
    def _resolve(self, tt, winner_suffix):
630
 
        """Resolve the conflict by copying one of .THIS or .OTHER into file.
631
 
 
632
 
        :param tt: The TreeTransform where the conflict is resolved.
633
 
        :param winner_suffix: Either 'THIS' or 'OTHER'
634
 
 
635
 
        The resolution is symmetric, when taking THIS, item.THIS is renamed
636
 
        into item and vice-versa. This takes one of the files as a whole
637
 
        ignoring every difference that could have been merged cleanly.
638
 
        """
639
 
        # To avoid useless copies, we switch item and item.winner_suffix, only
640
 
        # item will exist after the conflict has been resolved anyway.
641
 
        item_tid = tt.trans_id_file_id(self.file_id)
642
 
        item_parent_tid = tt.get_tree_parent(item_tid)
643
 
        winner_path = self.path + '.' + winner_suffix
644
 
        winner_tid = tt.trans_id_tree_path(winner_path)
645
 
        winner_parent_tid = tt.get_tree_parent(winner_tid)
646
 
        # Switch the paths to preserve the content
647
 
        tt.adjust_path(osutils.basename(self.path),
648
 
                       winner_parent_tid, winner_tid)
649
 
        tt.adjust_path(osutils.basename(winner_path), item_parent_tid, item_tid)
650
 
        # Associate the file_id to the right content
651
 
        tt.unversion_file(item_tid)
652
 
        tt.version_file(self.file_id, winner_tid)
653
 
        tt.apply()
654
 
 
655
 
    def action_take_this(self, tree):
656
 
        self._resolve_with_cleanups(tree, 'THIS')
657
 
 
658
 
    def action_take_other(self, tree):
659
 
        self._resolve_with_cleanups(tree, 'OTHER')
660
 
 
661
620
 
662
621
class HandledConflict(Conflict):
663
622
    """A path problem that has been provisionally resolved.
756
715
        pass
757
716
 
758
717
    def action_take_other(self, tree):
 
718
        # FIXME: We shouldn't have to manipulate so many paths here (and there
 
719
        # is probably a bug or two...)
 
720
        base_path = osutils.basename(self.path)
 
721
        conflict_base_path = osutils.basename(self.conflict_path)
759
722
        tt = transform.TreeTransform(tree)
760
723
        try:
761
724
            p_tid = tt.trans_id_file_id(self.file_id)
762
725
            parent_tid = tt.get_tree_parent(p_tid)
763
726
            cp_tid = tt.trans_id_file_id(self.conflict_file_id)
764
727
            cparent_tid = tt.get_tree_parent(cp_tid)
765
 
            tt.adjust_path(osutils.basename(self.path), cparent_tid, cp_tid)
766
 
            tt.adjust_path(osutils.basename(self.conflict_path),
767
 
                           parent_tid, p_tid)
 
728
            tt.adjust_path(base_path, cparent_tid, cp_tid)
 
729
            tt.adjust_path(conflict_base_path, parent_tid, p_tid)
768
730
            tt.apply()
769
731
        finally:
770
732
            tt.finalize()