~ubuntu-branches/ubuntu/trusty/bzr-builder/trusty

« back to all changes in this revision

Viewing changes to bzrlibbackports.py

  • Committer: Jelmer Vernooij
  • Date: 2011-11-10 13:34:01 UTC
  • mfrom: (1.1.5)
  • Revision ID: jelmer@samba.org-20111110133401-0rsutmixovsm6nt0
* New upstream release.
 + Prints clearer error message if upstream tag is missing. LP: #864339
 + Fixes testsuite when run with precise dpkg. LP: #886728
 + Only change source format to "3.0 (native)" if upstream tarball
   is missing when --allow-fallback-to-native is used. LP: #886730
* Run test suite during build.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# bzr-builder: a bzr plugin to constuct trees based on recipes
2
 
# Copyright 2010 Canonical Ltd.
3
 
 
4
 
# This program is free software: you can redistribute it and/or modify it 
5
 
# under the terms of the GNU General Public License version 3, as published 
6
 
# by the Free Software Foundation.
7
 
 
8
 
# This program is distributed in the hope that it will be useful, but 
9
 
# WITHOUT ANY WARRANTY; without even the implied warranties of 
10
 
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
11
 
# PURPOSE.  See the GNU General Public License for more details.
12
 
 
13
 
# You should have received a copy of the GNU General Public License along 
14
 
# with this program.  If not, see <http://www.gnu.org/licenses/>.
15
 
 
16
 
"""Copies/backports features from recent bzrlib versions.
17
 
 
18
 
This allows bzr-builder to continue to work with older bzrlib versions while
19
 
using features from newer versions.
20
 
"""
21
 
 
22
 
# NOTE FOR DEVELOPERS: with each backport please include a comment saying which
23
 
# version of bzr you are backporting from, to make it easier to copy bugfixes
24
 
# made in bzr (and so that we know which things we can drop from this module if
25
 
# bzr-builder ever raises which version of bzr it depends on).
26
 
 
27
 
from bzrlib.merge import (
28
 
    Merger,
29
 
    Merge3Merger,
30
 
    )
31
 
from bzrlib import (
32
 
    errors,
33
 
    generate_ids,
34
 
    osutils,
35
 
    revision as _mod_revision,
36
 
    transform,
37
 
    ui,
38
 
    )
39
 
 
40
 
# Backport of bzrlib.merge.MergeIntoMerger, introduced in bzr 2.2rc1.
41
 
# (Also backports PathNotInTree, _MergeTypeParameterizer, MergeIntoMergeType)
42
 
class PathNotInTree(errors.BzrError):
43
 
 
44
 
    _fmt = """Merge-into failed because %(tree)s does not contain %(path)s."""
45
 
 
46
 
    def __init__(self, path, tree):
47
 
        errors.BzrError.__init__(self, path=path, tree=tree)
48
 
 
49
 
 
50
 
class MergeIntoMerger(Merger):
51
 
    """Merger that understands other_tree will be merged into a subdir.
52
 
 
53
 
    This also changes the Merger api so that it uses real Branch, revision_id,
54
 
    and RevisonTree objects, rather than using revision specs.
55
 
    """
56
 
 
57
 
    def __init__(self, this_tree, other_branch, other_tree, target_subdir,
58
 
            source_subpath, other_rev_id=None):
59
 
        """Create a new MergeIntoMerger object.
60
 
 
61
 
        source_subpath in other_tree will be effectively copied to
62
 
        target_subdir in this_tree.
63
 
 
64
 
        :param this_tree: The tree that we will be merging into.
65
 
        :param other_branch: The Branch we will be merging from.
66
 
        :param other_tree: The RevisionTree object we want to merge.
67
 
        :param target_subdir: The relative path where we want to merge
68
 
            other_tree into this_tree
69
 
        :param source_subpath: The relative path specifying the subtree of
70
 
            other_tree to merge into this_tree.
71
 
        """
72
 
        # It is assumed that we are merging a tree that is not in our current
73
 
        # ancestry, which means we are using the "EmptyTree" as our basis.
74
 
        null_ancestor_tree = this_tree.branch.repository.revision_tree(
75
 
                                _mod_revision.NULL_REVISION)
76
 
        super(MergeIntoMerger, self).__init__(
77
 
            this_branch=this_tree.branch,
78
 
            this_tree=this_tree,
79
 
            other_tree=other_tree,
80
 
            base_tree=null_ancestor_tree,
81
 
            )
82
 
        self._target_subdir = target_subdir
83
 
        self._source_subpath = source_subpath
84
 
        self.other_branch = other_branch
85
 
        if other_rev_id is None:
86
 
            other_rev_id = other_tree.get_revision_id()
87
 
        self.other_rev_id = self.other_basis = other_rev_id
88
 
        self.base_is_ancestor = True
89
 
        self.backup_files = True
90
 
        self.merge_type = Merge3Merger
91
 
        self.show_base = False
92
 
        self.reprocess = False
93
 
        self.interesting_ids = None
94
 
        self.merge_type = _MergeTypeParameterizer(MergeIntoMergeType,
95
 
              target_subdir=self._target_subdir,
96
 
              source_subpath=self._source_subpath)
97
 
        if self._source_subpath != '':
98
 
            # If this isn't a partial merge make sure the revisions will be
99
 
            # present.
100
 
            self._maybe_fetch(self.other_branch, self.this_branch,
101
 
                self.other_basis)
102
 
 
103
 
    def set_pending(self):
104
 
        if self._source_subpath != '':
105
 
            return
106
 
        Merger.set_pending(self)
107
 
 
108
 
 
109
 
class _MergeTypeParameterizer(object):
110
 
    """Wrap a merge-type class to provide extra parameters.
111
 
    
112
 
    This is hack used by MergeIntoMerger to pass some extra parameters to its
113
 
    merge_type.  Merger.do_merge() sets up its own set of parameters to pass to
114
 
    the 'merge_type' member.  It is difficult override do_merge without
115
 
    re-writing the whole thing, so instead we create a wrapper which will pass
116
 
    the extra parameters.
117
 
    """
118
 
 
119
 
    def __init__(self, merge_type, **kwargs):
120
 
        self._extra_kwargs = kwargs
121
 
        self._merge_type = merge_type
122
 
 
123
 
    def __call__(self, *args, **kwargs):
124
 
        kwargs.update(self._extra_kwargs)
125
 
        return self._merge_type(*args, **kwargs)
126
 
 
127
 
    def __getattr__(self, name):
128
 
        return getattr(self._merge_type, name)
129
 
 
130
 
 
131
 
class MergeIntoMergeType(Merge3Merger):
132
 
    """Merger that incorporates a tree (or part of a tree) into another."""
133
 
 
134
 
    # Backport note: the definition of _finish_computing_transform is copied
135
 
    # from Merge3Merger in bzr 2.2 (it is supposed to be inherited from
136
 
    # Merge3Merger, but was only introduced in 2.2).
137
 
    def _finish_computing_transform(self):
138
 
        """Finalize the transform and report the changes.
139
 
 
140
 
        This is the second half of _compute_transform.
141
 
        """
142
 
        child_pb = ui.ui_factory.nested_progress_bar()
143
 
        try:
144
 
            fs_conflicts = transform.resolve_conflicts(self.tt, child_pb,
145
 
                lambda t, c: transform.conflict_pass(t, c, self.other_tree))
146
 
        finally:
147
 
            child_pb.finished()
148
 
        if self.change_reporter is not None:
149
 
            from bzrlib import delta
150
 
            delta.report_changes(
151
 
                self.tt.iter_changes(), self.change_reporter)
152
 
        self.cook_conflicts(fs_conflicts)
153
 
        from bzrlib import trace
154
 
        for conflict in self.cooked_conflicts:
155
 
            trace.warning(conflict)
156
 
 
157
 
    def __init__(self, *args, **kwargs):
158
 
        """Initialize the merger object.
159
 
 
160
 
        :param args: See Merge3Merger.__init__'s args.
161
 
        :param kwargs: See Merge3Merger.__init__'s keyword args, except for
162
 
            source_subpath and target_subdir.
163
 
        :keyword source_subpath: The relative path specifying the subtree of
164
 
            other_tree to merge into this_tree.
165
 
        :keyword target_subdir: The relative path where we want to merge
166
 
            other_tree into this_tree
167
 
        """
168
 
        # All of the interesting work happens during Merge3Merger.__init__(),
169
 
        # so we have have to hack in to get our extra parameters set.
170
 
        self._source_subpath = kwargs.pop('source_subpath')
171
 
        self._target_subdir = kwargs.pop('target_subdir')
172
 
        super(MergeIntoMergeType, self).__init__(*args, **kwargs)
173
 
 
174
 
    def _compute_transform(self):
175
 
        child_pb = ui.ui_factory.nested_progress_bar()
176
 
        try:
177
 
            entries = self._entries_to_incorporate()
178
 
            entries = list(entries)
179
 
            for num, (entry, parent_id) in enumerate(entries):
180
 
                child_pb.update('Preparing file merge', num, len(entries))
181
 
                parent_trans_id = self.tt.trans_id_file_id(parent_id)
182
 
                trans_id = transform.new_by_entry(self.tt, entry,
183
 
                    parent_trans_id, self.other_tree)
184
 
        finally:
185
 
            child_pb.finished()
186
 
        self._finish_computing_transform()
187
 
 
188
 
    def _entries_to_incorporate(self):
189
 
        """Yields pairs of (inventory_entry, new_parent)."""
190
 
        other_inv = self.other_tree.inventory
191
 
        subdir_id = other_inv.path2id(self._source_subpath)
192
 
        if subdir_id is None:
193
 
            # XXX: The error would be clearer if it gave the URL of the source
194
 
            # branch, but we don't have a reference to that here.
195
 
            raise PathNotInTree(self._source_subpath, "Source tree")
196
 
        subdir = other_inv[subdir_id]
197
 
        parent_in_target = osutils.dirname(self._target_subdir)
198
 
        target_id = self.this_tree.inventory.path2id(parent_in_target)
199
 
        if target_id is None:
200
 
            raise PathNotInTree(self._target_subdir, "Target tree")
201
 
        name_in_target = osutils.basename(self._target_subdir)
202
 
        merge_into_root = subdir.copy()
203
 
        merge_into_root.name = name_in_target
204
 
        if merge_into_root.file_id in self.this_tree.inventory:
205
 
            # Give the root a new file-id.
206
 
            # This can happen fairly easily if the directory we are
207
 
            # incorporating is the root, and both trees have 'TREE_ROOT' as
208
 
            # their root_id.  Users will expect this to Just Work, so we
209
 
            # change the file-id here.
210
 
            # Non-root file-ids could potentially conflict too.  That's really
211
 
            # an edge case, so we don't do anything special for those.  We let
212
 
            # them cause conflicts.
213
 
            merge_into_root.file_id = generate_ids.gen_file_id(name_in_target)
214
 
        yield (merge_into_root, target_id)
215
 
        if subdir.kind != 'directory':
216
 
            # No children, so we are done.
217
 
            return
218
 
        for ignored_path, entry in other_inv.iter_entries_by_dir(subdir_id):
219
 
            parent_id = entry.parent_id
220
 
            if parent_id == subdir.file_id:
221
 
                # The root's parent ID has changed, so make sure children of
222
 
                # the root refer to the new ID.
223
 
                parent_id = merge_into_root.file_id
224
 
            yield (entry, parent_id)
225
 
 
226
 
 
227