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

  • Committer: Michael Ellerman
  • Date: 2006-03-09 00:24:48 UTC
  • mto: (1610.1.8 bzr.mbp.integration)
  • mto: This revision was merged to the branch mainline in revision 1616.
  • Revision ID: michael@ellerman.id.au-20060309002448-70cce15e3d605130
Make the "ignore line" in the commit message editor the "right" width, so
that if you make your message that wide it won't wrap in bzr log output.
Just as a visual aid.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
                           ReusingTransform, NotVersionedError, CantMoveRoot,
23
23
                           ExistingLimbo, ImmortalLimbo)
24
24
from bzrlib.inventory import InventoryEntry
25
 
from bzrlib.osutils import (file_kind, supports_executable, pathjoin, lexists,
26
 
                            delete_any)
27
 
from bzrlib.progress import DummyProgress, ProgressPhase
 
25
from bzrlib.osutils import file_kind, supports_executable, pathjoin
 
26
from bzrlib.progress import DummyProgress
28
27
from bzrlib.trace import mutter, warning
29
 
import bzrlib.ui 
30
28
 
31
29
 
32
30
ROOT_PARENT = "root-parent"
38
36
    map[key] = value
39
37
 
40
38
 
41
 
class _TransformResults(object):
42
 
    def __init__(self, modified_paths):
43
 
        object.__init__(self)
44
 
        self.modified_paths = modified_paths
45
 
 
46
 
 
47
39
class TreeTransform(object):
48
40
    """Represent a tree transformation.
49
41
    
287
279
        os.symlink(target, self._limbo_name(trans_id))
288
280
        unique_add(self._new_contents, trans_id, 'symlink')
289
281
 
 
282
    @staticmethod
 
283
    def delete_any(full_path):
 
284
        """Delete a file or directory."""
 
285
        try:
 
286
            os.unlink(full_path)
 
287
        except OSError, e:
 
288
        # We may be renaming a dangling inventory id
 
289
            if e.errno not in (errno.EISDIR, errno.EACCES, errno.EPERM):
 
290
                raise
 
291
            os.rmdir(full_path)
 
292
 
290
293
    def cancel_creation(self, trans_id):
291
294
        """Cancel the creation of new file contents."""
292
295
        del self._new_contents[trans_id]
293
 
        delete_any(self._limbo_name(trans_id))
 
296
        self.delete_any(self._limbo_name(trans_id))
294
297
 
295
298
    def delete_contents(self, trans_id):
296
299
        """Schedule the contents of a path entry for deletion"""
428
431
        except KeyError:
429
432
            return os.path.basename(self._tree_id_paths[trans_id])
430
433
 
431
 
    def by_parent(self):
 
434
    def _by_parent(self):
432
435
        """Return a map of parent: children for known parents.
433
436
        
434
437
        Only new paths and parents of tree files with assigned ids are used.
455
458
        # ensure all children of all existent parents are known
456
459
        # all children of non-existent parents are known, by definition.
457
460
        self._add_tree_children()
458
 
        by_parent = self.by_parent()
 
461
        by_parent = self._by_parent()
459
462
        conflicts.extend(self._unversioned_parents(by_parent))
460
463
        conflicts.extend(self._parent_loops())
461
464
        conflicts.extend(self._duplicate_entries(by_parent))
472
475
        Active parents are those which gain children, and those which are
473
476
        removed.  This is a necessary first step in detecting conflicts.
474
477
        """
475
 
        parents = self.by_parent().keys()
 
478
        parents = self._by_parent().keys()
476
479
        parents.extend([t for t in self._removed_contents if 
477
480
                        self.tree_kind(t) == 'directory'])
478
481
        for trans_id in self._removed_id:
504
507
                continue
505
508
            yield self.trans_id_tree_path(childpath)
506
509
 
507
 
    def has_named_child(self, by_parent, parent_id, name):
508
 
        try:
509
 
            children = by_parent[parent_id]
510
 
        except KeyError:
511
 
            children = []
512
 
        for child in children:
513
 
            if self.final_name(child) == name:
514
 
                return True
515
 
        try:
516
 
            path = self._tree_id_paths[parent_id]
517
 
        except KeyError:
518
 
            return False
519
 
        childpath = joinpath(path, name)
520
 
        child_id = self._tree_path_ids.get(childpath)
521
 
        if child_id is None:
522
 
            return lexists(self._tree.abspath(childpath))
523
 
        else:
524
 
            if tt.final_parent(child_id) != parent_id:
525
 
                return False
526
 
            if child_id in tt._removed_contents:
527
 
                # XXX What about dangling file-ids?
528
 
                return False
529
 
            else:
530
 
                return True
531
 
 
532
510
    def _parent_loops(self):
533
511
        """No entry should be its own ancestor"""
534
512
        conflicts = []
620
598
                if name == last_name:
621
599
                    conflicts.append(('duplicate', last_trans_id, trans_id,
622
600
                    name))
623
 
                try:
624
 
                    kind = self.final_kind(trans_id)
625
 
                except NoSuchFile:
626
 
                    kind = None
627
 
                file_id = self.final_file_id(trans_id)
628
 
                if kind is not None or file_id is not None:
629
 
                    last_name = name
630
 
                    last_trans_id = trans_id
 
601
                last_name = name
 
602
                last_trans_id = trans_id
631
603
        return conflicts
632
604
 
633
605
    def _duplicate_ids(self):
687
659
            raise MalformedTransform(conflicts=conflicts)
688
660
        limbo_inv = {}
689
661
        inv = self._tree.inventory
690
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
691
 
        try:
692
 
            child_pb.update('Apply phase', 0, 2)
693
 
            self._apply_removals(inv, limbo_inv)
694
 
            child_pb.update('Apply phase', 1, 2)
695
 
            modified_paths = self._apply_insertions(inv, limbo_inv)
696
 
        finally:
697
 
            child_pb.finished()
 
662
        self._apply_removals(inv, limbo_inv)
 
663
        self._apply_insertions(inv, limbo_inv)
698
664
        self._tree._write_inventory(inv)
699
665
        self.__done = True
700
666
        self.finalize()
701
 
        return _TransformResults(modified_paths)
702
667
 
703
668
    def _limbo_name(self, trans_id):
704
669
        """Generate the limbo name of a file"""
713
678
        """
714
679
        tree_paths = list(self._tree_path_ids.iteritems())
715
680
        tree_paths.sort(reverse=True)
716
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
717
 
        try:
718
 
            for num, data in enumerate(tree_paths):
719
 
                path, trans_id = data
720
 
                child_pb.update('removing file', num, len(tree_paths))
721
 
                full_path = self._tree.abspath(path)
722
 
                if trans_id in self._removed_contents:
723
 
                    delete_any(full_path)
724
 
                elif trans_id in self._new_name or trans_id in \
725
 
                    self._new_parent:
726
 
                    try:
727
 
                        os.rename(full_path, self._limbo_name(trans_id))
728
 
                    except OSError, e:
729
 
                        if e.errno != errno.ENOENT:
730
 
                            raise
731
 
                if trans_id in self._removed_id:
732
 
                    if trans_id == self._new_root:
733
 
                        file_id = self._tree.inventory.root.file_id
734
 
                    else:
735
 
                        file_id = self.tree_file_id(trans_id)
 
681
        for num, data in enumerate(tree_paths):
 
682
            path, trans_id = data
 
683
            self._pb.update('removing file', num+1, len(tree_paths))
 
684
            full_path = self._tree.abspath(path)
 
685
            if trans_id in self._removed_contents:
 
686
                self.delete_any(full_path)
 
687
            elif trans_id in self._new_name or trans_id in self._new_parent:
 
688
                try:
 
689
                    os.rename(full_path, self._limbo_name(trans_id))
 
690
                except OSError, e:
 
691
                    if e.errno != errno.ENOENT:
 
692
                        raise
 
693
            if trans_id in self._removed_id:
 
694
                if trans_id == self._new_root:
 
695
                    file_id = self._tree.inventory.root.file_id
 
696
                else:
 
697
                    file_id = self.tree_file_id(trans_id)
 
698
                del inv[file_id]
 
699
            elif trans_id in self._new_name or trans_id in self._new_parent:
 
700
                file_id = self.tree_file_id(trans_id)
 
701
                if file_id is not None:
 
702
                    limbo_inv[trans_id] = inv[file_id]
736
703
                    del inv[file_id]
737
 
                elif trans_id in self._new_name or trans_id in self._new_parent:
738
 
                    file_id = self.tree_file_id(trans_id)
739
 
                    if file_id is not None:
740
 
                        limbo_inv[trans_id] = inv[file_id]
741
 
                        del inv[file_id]
742
 
        finally:
743
 
            child_pb.finished()
 
704
        self._pb.clear()
744
705
 
745
706
    def _apply_insertions(self, inv, limbo_inv):
746
707
        """Perform tree operations that insert directory/inventory names.
750
711
        parent-to-child order.
751
712
        """
752
713
        new_paths = self.new_paths()
753
 
        modified_paths = []
754
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
755
 
        try:
756
 
            for num, (path, trans_id) in enumerate(new_paths):
757
 
                child_pb.update('adding file', num, len(new_paths))
 
714
        for num, (path, trans_id) in enumerate(new_paths):
 
715
            self._pb.update('adding file', num+1, len(new_paths))
 
716
            try:
 
717
                kind = self._new_contents[trans_id]
 
718
            except KeyError:
 
719
                kind = contents = None
 
720
            if trans_id in self._new_contents or self.path_changed(trans_id):
 
721
                full_path = self._tree.abspath(path)
758
722
                try:
759
 
                    kind = self._new_contents[trans_id]
760
 
                except KeyError:
761
 
                    kind = contents = None
762
 
                if trans_id in self._new_contents or \
763
 
                    self.path_changed(trans_id):
764
 
                    full_path = self._tree.abspath(path)
765
 
                    try:
766
 
                        os.rename(self._limbo_name(trans_id), full_path)
767
 
                    except OSError, e:
768
 
                        # We may be renaming a dangling inventory id
769
 
                        if e.errno != errno.ENOENT:
770
 
                            raise
771
 
                    if trans_id in self._new_contents:
772
 
                        modified_paths.append(full_path)
773
 
                        del self._new_contents[trans_id]
774
 
 
775
 
                if trans_id in self._new_id:
776
 
                    if kind is None:
777
 
                        kind = file_kind(self._tree.abspath(path))
778
 
                    inv.add_path(path, kind, self._new_id[trans_id])
779
 
                elif trans_id in self._new_name or trans_id in\
780
 
                    self._new_parent:
781
 
                    entry = limbo_inv.get(trans_id)
782
 
                    if entry is not None:
783
 
                        entry.name = self.final_name(trans_id)
784
 
                        parent_path = os.path.dirname(path)
785
 
                        entry.parent_id = \
786
 
                            self._tree.inventory.path2id(parent_path)
787
 
                        inv.add(entry)
788
 
 
789
 
                # requires files and inventory entries to be in place
790
 
                if trans_id in self._new_executability:
791
 
                    self._set_executability(path, inv, trans_id)
792
 
        finally:
793
 
            child_pb.finished()
794
 
        return modified_paths
 
723
                    os.rename(self._limbo_name(trans_id), full_path)
 
724
                except OSError, e:
 
725
                    # We may be renaming a dangling inventory id
 
726
                    if e.errno != errno.ENOENT:
 
727
                        raise
 
728
                if trans_id in self._new_contents:
 
729
                    del self._new_contents[trans_id]
 
730
 
 
731
            if trans_id in self._new_id:
 
732
                if kind is None:
 
733
                    kind = file_kind(self._tree.abspath(path))
 
734
                inv.add_path(path, kind, self._new_id[trans_id])
 
735
            elif trans_id in self._new_name or trans_id in self._new_parent:
 
736
                entry = limbo_inv.get(trans_id)
 
737
                if entry is not None:
 
738
                    entry.name = self.final_name(trans_id)
 
739
                    parent_path = os.path.dirname(path)
 
740
                    entry.parent_id = self._tree.inventory.path2id(parent_path)
 
741
                    inv.add(entry)
 
742
 
 
743
            # requires files and inventory entries to be in place
 
744
            if trans_id in self._new_executability:
 
745
                self._set_executability(path, inv, trans_id)
 
746
        self._pb.clear()
795
747
 
796
748
    def _set_executability(self, path, inv, trans_id):
797
749
        """Set the executability of versioned files """
905
857
def build_tree(tree, wt):
906
858
    """Create working tree for a branch, using a Transaction."""
907
859
    file_trans_id = {}
908
 
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
909
 
    pp = ProgressPhase("Build phase", 2, top_pb)
910
860
    tt = TreeTransform(wt)
911
861
    try:
912
 
        pp.next_phase()
913
862
        file_trans_id[wt.get_root_id()] = tt.trans_id_tree_file_id(wt.get_root_id())
914
863
        file_ids = topology_sorted_ids(tree)
915
 
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
916
 
        try:
917
 
            for num, file_id in enumerate(file_ids):
918
 
                pb.update("Building tree", num, len(file_ids))
919
 
                entry = tree.inventory[file_id]
920
 
                if entry.parent_id is None:
921
 
                    continue
922
 
                if entry.parent_id not in file_trans_id:
923
 
                    raise repr(entry.parent_id)
924
 
                parent_id = file_trans_id[entry.parent_id]
925
 
                file_trans_id[file_id] = new_by_entry(tt, entry, parent_id, 
926
 
                                                      tree)
927
 
        finally:
928
 
            pb.finished()
929
 
        pp.next_phase()
 
864
        for file_id in file_ids:
 
865
            entry = tree.inventory[file_id]
 
866
            if entry.parent_id is None:
 
867
                continue
 
868
            if entry.parent_id not in file_trans_id:
 
869
                raise repr(entry.parent_id)
 
870
            parent_id = file_trans_id[entry.parent_id]
 
871
            file_trans_id[file_id] = new_by_entry(tt, entry, parent_id, tree)
930
872
        tt.apply()
931
873
    finally:
932
874
        tt.finalize()
933
 
        top_pb.finished()
934
875
 
935
876
def new_by_entry(tt, entry, parent_id, tree):
936
877
    """Create a new file according to its inventory entry"""
971
912
    else:
972
913
        interesting_ids = set()
973
914
        for tree_path in filenames:
974
 
            not_found = True
975
915
            for tree in (working_tree, target_tree):
 
916
                not_found = True
976
917
                file_id = tree.inventory.path2id(tree_path)
977
918
                if file_id is not None:
978
919
                    interesting_ids.add(file_id)
979
920
                    not_found = False
980
 
            if not_found:
981
 
                raise NotVersionedError(path=tree_path)
 
921
                if not_found:
 
922
                    raise NotVersionedError(path=tree_path)
982
923
    return interesting_ids
983
924
 
984
925
 
985
926
def change_entry(tt, file_id, working_tree, target_tree, 
986
 
                 trans_id_file_id, backups, trans_id, by_parent):
 
927
                 trans_id_file_id, backups, trans_id):
987
928
    """Replace a file_id's contents with those from a target tree."""
988
929
    e_trans_id = trans_id_file_id(file_id)
989
930
    entry = target_tree.inventory[file_id]
996
937
                tt.delete_contents(e_trans_id)
997
938
            else:
998
939
                parent_trans_id = trans_id_file_id(entry.parent_id)
999
 
                backup_name = get_backup_name(entry, by_parent,
1000
 
                                              parent_trans_id, tt)
1001
 
                tt.adjust_path(backup_name, parent_trans_id, e_trans_id)
 
940
                tt.adjust_path(entry.name+"~", parent_trans_id, e_trans_id)
1002
941
                tt.unversion_file(e_trans_id)
1003
942
                e_trans_id = tt.create_path(entry.name, parent_trans_id)
1004
943
                tt.version_file(file_id, e_trans_id)
1022
961
        tt.adjust_path(entry.name, parent_trans_id, e_trans_id)
1023
962
 
1024
963
 
1025
 
def get_backup_name(entry, by_parent, parent_trans_id, tt):
1026
 
    """Produce a backup-style name that appears to be available"""
1027
 
    def name_gen():
1028
 
        counter = 1
1029
 
        while True:
1030
 
            yield "%s.~%d~" % (entry.name, counter)
1031
 
            counter += 1
1032
 
    for name in name_gen():
1033
 
        if not tt.has_named_child(by_parent, parent_trans_id, name):
1034
 
            return name
1035
 
 
1036
964
def _entry_changes(file_id, entry, working_tree):
1037
965
    """Determine in which ways the inventory entry has changed.
1038
966
 
1074
1002
 
1075
1003
    tt = TreeTransform(working_tree, pb)
1076
1004
    try:
1077
 
        merge_modified = working_tree.merge_modified()
1078
1005
        trans_id = {}
1079
1006
        def trans_id_file_id(file_id):
1080
1007
            try:
1082
1009
            except KeyError:
1083
1010
                return tt.trans_id_tree_file_id(file_id)
1084
1011
 
1085
 
        pp = ProgressPhase("Revert phase", 4, pb)
1086
 
        pp.next_phase()
1087
1012
        sorted_interesting = [i for i in topology_sorted_ids(target_tree) if
1088
1013
                              interesting(i)]
1089
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1090
 
        try:
1091
 
            by_parent = tt.by_parent()
1092
 
            for id_num, file_id in enumerate(sorted_interesting):
1093
 
                child_pb.update("Reverting file", id_num+1, 
1094
 
                                len(sorted_interesting))
1095
 
                if file_id not in working_tree.inventory:
1096
 
                    entry = target_tree.inventory[file_id]
1097
 
                    parent_id = trans_id_file_id(entry.parent_id)
1098
 
                    e_trans_id = new_by_entry(tt, entry, parent_id, target_tree)
1099
 
                    trans_id[file_id] = e_trans_id
1100
 
                else:
1101
 
                    backup_this = backups
1102
 
                    if file_id in merge_modified:
1103
 
                        backup_this = False
1104
 
                        del merge_modified[file_id]
1105
 
                    change_entry(tt, file_id, working_tree, target_tree, 
1106
 
                                 trans_id_file_id, backup_this, trans_id,
1107
 
                                 by_parent)
1108
 
        finally:
1109
 
            child_pb.finished()
1110
 
        pp.next_phase()
 
1014
        for id_num, file_id in enumerate(sorted_interesting):
 
1015
            pb.update("Reverting file", id_num+1, len(sorted_interesting))
 
1016
            if file_id not in working_tree.inventory:
 
1017
                entry = target_tree.inventory[file_id]
 
1018
                parent_id = trans_id_file_id(entry.parent_id)
 
1019
                e_trans_id = new_by_entry(tt, entry, parent_id, target_tree)
 
1020
                trans_id[file_id] = e_trans_id
 
1021
            else:
 
1022
                change_entry(tt, file_id, working_tree, target_tree, 
 
1023
                             trans_id_file_id, backups, trans_id)
1111
1024
        wt_interesting = [i for i in working_tree.inventory if interesting(i)]
1112
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1113
 
        try:
1114
 
            for id_num, file_id in enumerate(wt_interesting):
1115
 
                child_pb.update("New file check", id_num+1, 
1116
 
                                len(sorted_interesting))
1117
 
                if file_id not in target_tree:
1118
 
                    trans_id = tt.trans_id_tree_file_id(file_id)
1119
 
                    tt.unversion_file(trans_id)
1120
 
                    if file_id in merge_modified:
1121
 
                        tt.delete_contents(trans_id)
1122
 
                        del merge_modified[file_id]
1123
 
        finally:
1124
 
            child_pb.finished()
1125
 
        pp.next_phase()
1126
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1127
 
        try:
1128
 
            raw_conflicts = resolve_conflicts(tt, child_pb)
1129
 
        finally:
1130
 
            child_pb.finished()
1131
 
        conflicts = cook_conflicts(raw_conflicts, tt)
1132
 
        for conflict in conflicts:
1133
 
            warning(conflict)
1134
 
        pp.next_phase()
 
1025
        for id_num, file_id in enumerate(wt_interesting):
 
1026
            pb.update("New file check", id_num+1, len(sorted_interesting))
 
1027
            if file_id not in target_tree:
 
1028
                tt.unversion_file(tt.trans_id_tree_file_id(file_id))
 
1029
        raw_conflicts = resolve_conflicts(tt, pb)
 
1030
        for line in conflicts_strings(cook_conflicts(raw_conflicts, tt)):
 
1031
            warning(line)
1135
1032
        tt.apply()
1136
 
        working_tree.set_merge_modified({})
1137
1033
    finally:
1138
1034
        tt.finalize()
1139
1035
        pb.clear()
1140
 
    return conflicts
1141
1036
 
1142
1037
 
1143
1038
def resolve_conflicts(tt, pb=DummyProgress()):
1200
1095
def cook_conflicts(raw_conflicts, tt):
1201
1096
    """Generate a list of cooked conflicts, sorted by file path"""
1202
1097
    def key(conflict):
1203
 
        if conflict.path is not None:
1204
 
            return conflict.path, conflict.typestring
1205
 
        elif getattr(conflict, "conflict_path", None) is not None:
1206
 
            return conflict.conflict_path, conflict.typestring
 
1098
        if conflict[2] is not None:
 
1099
            return conflict[2], conflict[0]
 
1100
        elif len(conflict) == 6:
 
1101
            return conflict[4], conflict[0]
1207
1102
        else:
1208
 
            return None, conflict.typestring
 
1103
            return None, conflict[0]
1209
1104
 
1210
1105
    return sorted(list(iter_cook_conflicts(raw_conflicts, tt)), key=key)
1211
1106
 
1212
1107
def iter_cook_conflicts(raw_conflicts, tt):
1213
 
    from bzrlib.conflicts import Conflict
 
1108
    cooked_conflicts = []
1214
1109
    fp = FinalPaths(tt)
1215
1110
    for conflict in raw_conflicts:
1216
1111
        c_type = conflict[0]
1218
1113
        modified_path = fp.get_path(conflict[2])
1219
1114
        modified_id = tt.final_file_id(conflict[2])
1220
1115
        if len(conflict) == 3:
1221
 
            yield Conflict.factory(c_type, action=action, path=modified_path,
1222
 
                                     file_id=modified_id)
1223
 
             
 
1116
            yield c_type, action, modified_path, modified_id
1224
1117
        else:
1225
1118
            conflicting_path = fp.get_path(conflict[3])
1226
1119
            conflicting_id = tt.final_file_id(conflict[3])
1227
 
            yield Conflict.factory(c_type, action=action, path=modified_path,
1228
 
                                   file_id=modified_id, 
1229
 
                                   conflict_path=conflicting_path,
1230
 
                                   conflict_file_id=conflicting_id)
 
1120
            yield (c_type, action, modified_path, modified_id, 
 
1121
                   conflicting_path, conflicting_id)
 
1122
 
 
1123
 
 
1124
def conflicts_strings(conflicts):
 
1125
    """Generate strings for the provided conflicts"""
 
1126
    for conflict in conflicts:
 
1127
        conflict_type = conflict[0]
 
1128
        if conflict_type == 'text conflict':
 
1129
            yield 'Text conflict in %s' % conflict[2]
 
1130
        elif conflict_type == 'contents conflict':
 
1131
            yield 'Contents conflict in %s' % conflict[2]
 
1132
        elif conflict_type == 'path conflict':
 
1133
            yield 'Path conflict: %s / %s' % conflict[2:]
 
1134
        elif conflict_type == 'duplicate id':
 
1135
            vals = (conflict[4], conflict[1], conflict[2])
 
1136
            yield 'Conflict adding id to %s.  %s %s.' % vals
 
1137
        elif conflict_type == 'duplicate':
 
1138
            vals = (conflict[4], conflict[1], conflict[2])
 
1139
            yield 'Conflict adding file %s.  %s %s.' % vals
 
1140
        elif conflict_type == 'parent loop':
 
1141
            vals = (conflict[4], conflict[2], conflict[1])
 
1142
            yield 'Conflict moving %s into %s.  %s.' % vals
 
1143
        elif conflict_type == 'unversioned parent':
 
1144
            vals = (conflict[2], conflict[1])
 
1145
            yield 'Conflict adding versioned files to %s.  %s.' % vals
 
1146
        elif conflict_type == 'missing parent':
 
1147
            vals = (conflict[2], conflict[1])
 
1148
            yield 'Conflict adding files to %s.  %s.' % vals