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

  • Committer: Jelmer Vernooij
  • Date: 2020-08-10 15:00:17 UTC
  • mfrom: (7490.40.99 work)
  • mto: This revision was merged to the branch mainline in revision 7521.
  • Revision ID: jelmer@jelmer.uk-20200810150017-vs7xnrd1vat4iktg
Merge lp:brz/3.1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from __future__ import absolute_import
 
17
import contextlib
18
18
 
19
19
from .lazy_import import lazy_import
20
20
lazy_import(globals(), """
22
22
 
23
23
from breezy import (
24
24
    branch as _mod_branch,
25
 
    cleanup,
26
25
    conflicts as _mod_conflicts,
27
26
    debug,
28
27
    graph as _mod_graph,
31
30
    revision as _mod_revision,
32
31
    textfile,
33
32
    trace,
34
 
    transform,
35
33
    tree as _mod_tree,
36
34
    tsort,
37
35
    ui,
38
36
    workingtree,
39
37
    )
40
38
from breezy.bzr import (
 
39
    conflicts as _mod_bzr_conflicts,
41
40
    generate_ids,
42
41
    versionedfile,
43
42
    )
44
43
from breezy.i18n import gettext
45
44
""")
 
45
from breezy.bzr.conflicts import Conflict as BzrConflict
46
46
from . import (
47
47
    decorators,
48
48
    errors,
49
49
    hooks,
50
50
    registry,
51
 
    )
52
 
from .sixish import (
53
 
    viewitems,
 
51
    transform,
54
52
    )
55
53
# TODO: Report back as changes are merged in
56
54
 
447
445
    def _add_parent(self):
448
446
        new_parents = self.this_tree.get_parent_ids() + [self.other_rev_id]
449
447
        new_parent_trees = []
450
 
        with cleanup.ExitStack() as stack:
 
448
        with contextlib.ExitStack() as stack:
451
449
            for revision_id in new_parents:
452
450
                try:
453
451
                    tree = self.revision_tree(revision_id)
654
652
        return merge
655
653
 
656
654
    def do_merge(self):
657
 
        with cleanup.ExitStack() as stack:
 
655
        with contextlib.ExitStack() as stack:
658
656
            stack.enter_context(self.this_tree.lock_tree_write())
659
657
            if self.base_tree is not None:
660
658
                stack.enter_context(self.base_tree.lock_read())
757
755
            self.do_merge()
758
756
 
759
757
    def do_merge(self):
760
 
        with cleanup.ExitStack() as stack:
 
758
        with contextlib.ExitStack() as stack:
761
759
            stack.enter_context(self.working_tree.lock_tree_write())
762
760
            stack.enter_context(self.this_tree.lock_read())
763
761
            stack.enter_context(self.base_tree.lock_read())
780
778
 
781
779
    def _compute_transform(self):
782
780
        if self._lca_trees is None:
783
 
            entries = self._entries3()
 
781
            entries = list(self._entries3())
784
782
            resolver = self._three_way
785
783
        else:
786
 
            entries = self._entries_lca()
 
784
            entries = list(self._entries_lca())
787
785
            resolver = self._lca_multi_way
788
786
        # Prepare merge hooks
789
787
        factories = Merger.hooks['merge_file_content']
794
792
            for num, (file_id, changed, paths3, parents3, names3,
795
793
                      executable3) in enumerate(entries):
796
794
                trans_id = self.tt.trans_id_file_id(file_id)
797
 
 
798
795
                # Try merging each entry
799
796
                child_pb.update(gettext('Preparing file merge'),
800
797
                                num, len(entries))
835
832
        other and this.  names3 is a tuple of names for base, other and this.
836
833
        executable3 is a tuple of execute-bit values for base, other and this.
837
834
        """
838
 
        result = []
839
835
        iterator = self.other_tree.iter_changes(self.base_tree,
840
836
                                                specific_files=self.interesting_files,
841
837
                                                extra_trees=[self.this_tree])
863
859
            names3 = change.name + (this_name,)
864
860
            paths3 = change.path + (this_path, )
865
861
            executable3 = change.executable + (this_executable,)
866
 
            result.append(
 
862
            yield (
867
863
                (change.file_id, change.changed_content, paths3,
868
864
                 parents3, names3, executable3))
869
 
        return result
870
865
 
871
866
    def _entries_lca(self):
872
867
        """Gather data about files modified between multiple trees.
895
890
                self.interesting_files, lookup_trees)
896
891
        else:
897
892
            interesting_files = None
898
 
        result = []
899
893
        from .multiwalker import MultiWalker
900
894
        walker = MultiWalker(self.other_tree, self._lca_trees)
901
895
 
1039
1033
                    raise AssertionError('unhandled kind: %s' % other_ie.kind)
1040
1034
 
1041
1035
            # If we have gotten this far, that means something has changed
1042
 
            result.append((file_id, content_changed,
 
1036
            yield (file_id, content_changed,
1043
1037
                           ((base_path, lca_paths),
1044
1038
                            other_path, this_path),
1045
1039
                           ((base_ie.parent_id, lca_parent_ids),
1048
1042
                            other_ie.name, this_ie.name),
1049
1043
                           ((base_ie.executable, lca_executable),
1050
1044
                            other_ie.executable, this_ie.executable)
1051
 
                           ))
1052
 
        return result
 
1045
                           )
1053
1046
 
1054
1047
    def write_modified(self, results):
1055
1048
        if not self.working_tree.supports_merge_modified():
1311
1304
                # This is a contents conflict, because none of the available
1312
1305
                # functions could merge it.
1313
1306
                file_group = self._dump_conflicts(
1314
 
                    name, (base_path, other_path, this_path), parent_id,
1315
 
                    file_id, set_version=True)
 
1307
                    name, (base_path, other_path, this_path), parent_id)
 
1308
                for tid in file_group:
 
1309
                    self.tt.version_file(tid, file_id=file_id)
 
1310
                    break
1316
1311
                self._raw_conflicts.append(('contents conflict', file_group))
1317
1312
        elif hook_status == 'success':
1318
1313
            self.tt.create_file(lines, trans_id)
1324
1319
            name = self.tt.final_name(trans_id)
1325
1320
            parent_id = self.tt.final_parent(trans_id)
1326
1321
            self._dump_conflicts(
1327
 
                name, (base_path, other_path, this_path), parent_id, file_id)
 
1322
                name, (base_path, other_path, this_path), parent_id)
1328
1323
        elif hook_status == 'delete':
1329
1324
            self.tt.unversion_file(trans_id)
1330
1325
            result = "deleted"
1430
1425
            self._raw_conflicts.append(('text conflict', trans_id))
1431
1426
            name = self.tt.final_name(trans_id)
1432
1427
            parent_id = self.tt.final_parent(trans_id)
1433
 
            file_id = self.tt.final_file_id(trans_id)
1434
 
            file_group = self._dump_conflicts(name, paths, parent_id, file_id,
1435
 
                                              this_lines, base_lines,
1436
 
                                              other_lines)
 
1428
            file_group = self._dump_conflicts(
 
1429
                name, paths, parent_id,
 
1430
                lines=(base_lines, other_lines, this_lines))
1437
1431
            file_group.append(trans_id)
1438
1432
 
1439
1433
    def _get_filter_tree_path(self, path):
1450
1444
        # Skip the lookup for older formats
1451
1445
        return None
1452
1446
 
1453
 
    def _dump_conflicts(self, name, paths, parent_id, file_id, this_lines=None,
1454
 
                        base_lines=None, other_lines=None, set_version=False,
 
1447
    def _dump_conflicts(self, name, paths, parent_id, lines=None,
1455
1448
                        no_base=False):
1456
1449
        """Emit conflict files.
1457
1450
        If this_lines, base_lines, or other_lines are omitted, they will be
1459
1452
        or .BASE (in that order) will be created as versioned files.
1460
1453
        """
1461
1454
        base_path, other_path, this_path = paths
 
1455
        if lines:
 
1456
            base_lines, other_lines, this_lines = lines
 
1457
        else:
 
1458
            base_lines = other_lines = this_lines = None
1462
1459
        data = [('OTHER', self.other_tree, other_path, other_lines),
1463
1460
                ('THIS', self.this_tree, this_path, this_lines)]
1464
1461
        if not no_base:
1465
1462
            data.append(('BASE', self.base_tree, base_path, base_lines))
1466
1463
 
1467
1464
        # We need to use the actual path in the working tree of the file here,
1468
 
        # ignoring the conflict suffixes
1469
 
        wt = self.this_tree
1470
 
        if wt.supports_content_filtering():
1471
 
            try:
1472
 
                filter_tree_path = wt.id2path(file_id)
1473
 
            except errors.NoSuchId:
1474
 
                # file has been deleted
1475
 
                filter_tree_path = None
 
1465
        if self.this_tree.supports_content_filtering():
 
1466
            filter_tree_path = this_path
1476
1467
        else:
1477
1468
            # Skip the id2path lookup for older formats
1478
1469
            filter_tree_path = None
1479
1470
 
1480
 
        versioned = False
1481
1471
        file_group = []
1482
1472
        for suffix, tree, path, lines in data:
1483
1473
            if path is not None:
1485
1475
                    name, parent_id, path, tree, suffix, lines,
1486
1476
                    filter_tree_path)
1487
1477
                file_group.append(trans_id)
1488
 
                if set_version and not versioned:
1489
 
                    self.tt.version_file(trans_id, file_id=file_id)
1490
 
                    versioned = True
1491
1478
        return file_group
1492
1479
 
1493
1480
    def _conflict_file(self, name, parent_id, path, tree, suffix,
1534
1521
 
1535
1522
    def cook_conflicts(self, fs_conflicts):
1536
1523
        """Convert all conflicts into a form that doesn't depend on trans_id"""
1537
 
        content_conflict_file_ids = set()
1538
 
        cooked_conflicts = transform.cook_conflicts(fs_conflicts, self.tt)
1539
 
        fp = transform.FinalPaths(self.tt)
1540
 
        for conflict in self._raw_conflicts:
1541
 
            conflict_type = conflict[0]
1542
 
            if conflict_type == 'path conflict':
1543
 
                (trans_id, file_id,
1544
 
                 this_parent, this_name,
1545
 
                 other_parent, other_name) = conflict[1:]
1546
 
                if this_parent is None or this_name is None:
1547
 
                    this_path = '<deleted>'
1548
 
                else:
1549
 
                    parent_path = fp.get_path(
1550
 
                        self.tt.trans_id_file_id(this_parent))
1551
 
                    this_path = osutils.pathjoin(parent_path, this_name)
1552
 
                if other_parent is None or other_name is None:
1553
 
                    other_path = '<deleted>'
1554
 
                else:
1555
 
                    if other_parent == self.other_tree.path2id(''):
1556
 
                        # The tree transform doesn't know about the other root,
1557
 
                        # so we special case here to avoid a NoFinalPath
1558
 
                        # exception
1559
 
                        parent_path = ''
1560
 
                    else:
1561
 
                        parent_path = fp.get_path(
1562
 
                            self.tt.trans_id_file_id(other_parent))
1563
 
                    other_path = osutils.pathjoin(parent_path, other_name)
1564
 
                c = _mod_conflicts.Conflict.factory(
1565
 
                    'path conflict', path=this_path,
1566
 
                    conflict_path=other_path,
1567
 
                    file_id=file_id)
1568
 
            elif conflict_type == 'contents conflict':
1569
 
                for trans_id in conflict[1]:
1570
 
                    file_id = self.tt.final_file_id(trans_id)
1571
 
                    if file_id is not None:
1572
 
                        # Ok we found the relevant file-id
1573
 
                        break
1574
 
                path = fp.get_path(trans_id)
1575
 
                for suffix in ('.BASE', '.THIS', '.OTHER'):
1576
 
                    if path.endswith(suffix):
1577
 
                        # Here is the raw path
1578
 
                        path = path[:-len(suffix)]
1579
 
                        break
1580
 
                c = _mod_conflicts.Conflict.factory(conflict_type,
1581
 
                                                    path=path, file_id=file_id)
1582
 
                content_conflict_file_ids.add(file_id)
1583
 
            elif conflict_type == 'text conflict':
1584
 
                trans_id = conflict[1]
1585
 
                path = fp.get_path(trans_id)
1586
 
                file_id = self.tt.final_file_id(trans_id)
1587
 
                c = _mod_conflicts.Conflict.factory(conflict_type,
1588
 
                                                    path=path, file_id=file_id)
1589
 
            else:
1590
 
                raise AssertionError('bad conflict type: %r' % (conflict,))
1591
 
            cooked_conflicts.append(c)
1592
 
 
1593
 
        self.cooked_conflicts = []
1594
 
        # We want to get rid of path conflicts when a corresponding contents
1595
 
        # conflict exists. This can occur when one branch deletes a file while
1596
 
        # the other renames *and* modifies it. In this case, the content
1597
 
        # conflict is enough.
1598
 
        for c in cooked_conflicts:
1599
 
            if (c.typestring == 'path conflict'
1600
 
                    and c.file_id in content_conflict_file_ids):
1601
 
                continue
1602
 
            self.cooked_conflicts.append(c)
1603
 
        self.cooked_conflicts.sort(key=_mod_conflicts.Conflict.sort_key)
 
1524
        self.cooked_conflicts = list(self.tt.cook_conflicts(
 
1525
            list(fs_conflicts) + self._raw_conflicts))
1604
1526
 
1605
1527
 
1606
1528
class WeaveMerger(Merge3Merger):
1657
1579
            self._raw_conflicts.append(('text conflict', trans_id))
1658
1580
            name = self.tt.final_name(trans_id)
1659
1581
            parent_id = self.tt.final_parent(trans_id)
1660
 
            file_id = self.tt.final_file_id(trans_id)
1661
 
            file_group = self._dump_conflicts(name, paths, parent_id, file_id,
1662
 
                                              no_base=False,
1663
 
                                              base_lines=base_lines)
 
1582
            file_group = self._dump_conflicts(name, paths, parent_id,
 
1583
                                              (base_lines, None, None),
 
1584
                                              no_base=False)
1664
1585
            file_group.append(trans_id)
1665
1586
 
1666
1587
 
1710
1631
            if status == 1:
1711
1632
                name = self.tt.final_name(trans_id)
1712
1633
                parent_id = self.tt.final_parent(trans_id)
1713
 
                file_id = self.tt.final_file_id(trans_id)
1714
 
                self._dump_conflicts(name, paths, parent_id, file_id)
 
1634
                self._dump_conflicts(name, paths, parent_id)
1715
1635
                self._raw_conflicts.append(('text conflict', trans_id))
1716
1636
        finally:
1717
1637
            osutils.rmtree(temp_dir)
2255
2175
        filtered_parent_map = {}
2256
2176
        child_map = {}
2257
2177
        tails = []
2258
 
        for key, parent_keys in viewitems(parent_map):
 
2178
        for key, parent_keys in parent_map.items():
2259
2179
            culled_parent_keys = [p for p in parent_keys if p in parent_map]
2260
2180
            if not culled_parent_keys:
2261
2181
                tails.append(key)