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

  • Committer: John Arbash Meinel
  • Date: 2008-08-18 22:34:21 UTC
  • mto: (3606.5.6 1.6)
  • mto: This revision was merged to the branch mainline in revision 3641.
  • Revision ID: john@arbash-meinel.com-20080818223421-todjny24vj4faj4t
Add tests for the fetching behavior.

The proper parameter passed is 'unordered' add an assert for it, and
fix callers that were passing 'unsorted' instead.
Add tests that we make the right get_record_stream call based
on the value of _fetch_uses_deltas.
Fix the fetch request for signatures.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2007, 2008 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Utility for create branches with particular contents."""
 
18
 
 
19
from bzrlib import bzrdir, errors, memorytree
 
20
 
 
21
 
 
22
class BranchBuilder(object):
 
23
    """A BranchBuilder aids creating Branches with particular shapes.
 
24
    
 
25
    The expected way to use BranchBuilder is to construct a
 
26
    BranchBuilder on the transport you want your branch on, and then call
 
27
    appropriate build_ methods on it to get the shape of history you want.
 
28
 
 
29
    This is meant as a helper for the test suite, not as a general class for
 
30
    real data.
 
31
 
 
32
    For instance:
 
33
      builder = BranchBuilder(self.get_transport().clone('relpath'))
 
34
      builder.start_series()
 
35
      builder.build_snapshot('rev-id', [],
 
36
        [('add', ('filename', 'f-id', 'file', 'content\n'))])
 
37
      builder.build_snapshot('rev2-id', ['rev-id'],
 
38
        [('modify', ('f-id', 'new-content\n'))])
 
39
      builder.finish_series()
 
40
      branch = builder.get_branch()
 
41
 
 
42
    :ivar _tree: This is a private member which is not meant to be modified by
 
43
        users of this class. While a 'series' is in progress, it should hold a
 
44
        MemoryTree with the contents of the last commit (ready to be modified
 
45
        by the next build_snapshot command) with a held write lock. Outside of
 
46
        a series in progress, it should be None.
 
47
    """
 
48
 
 
49
    def __init__(self, transport, format=None):
 
50
        """Construct a BranchBuilder on transport.
 
51
        
 
52
        :param transport: The transport the branch should be created on.
 
53
            If the path of the transport does not exist but its parent does
 
54
            it will be created.
 
55
        :param format: Either a BzrDirFormat, or the name of a format in the
 
56
            bzrdir format registry for the branch to be built.
 
57
        """
 
58
        if not transport.has('.'):
 
59
            transport.mkdir('.')
 
60
        if format is None:
 
61
            format = 'default'
 
62
        if isinstance(format, str):
 
63
            format = bzrdir.format_registry.make_bzrdir(format)
 
64
        self._branch = bzrdir.BzrDir.create_branch_convenience(transport.base,
 
65
            format=format, force_new_tree=False)
 
66
        self._tree = None
 
67
 
 
68
    def build_commit(self):
 
69
        """Build a commit on the branch."""
 
70
        tree = memorytree.MemoryTree.create_on_branch(self._branch)
 
71
        tree.lock_write()
 
72
        try:
 
73
            tree.add('')
 
74
            return tree.commit('commit %d' % (self._branch.revno() + 1))
 
75
        finally:
 
76
            tree.unlock()
 
77
 
 
78
    def _move_branch_pointer(self, new_revision_id):
 
79
        """Point self._branch to a different revision id."""
 
80
        self._branch.lock_write()
 
81
        try:
 
82
            # We don't seem to have a simple set_last_revision(), so we
 
83
            # implement it here.
 
84
            cur_revno, cur_revision_id = self._branch.last_revision_info()
 
85
            g = self._branch.repository.get_graph()
 
86
            new_revno = g.find_distance_to_null(new_revision_id,
 
87
                                                [(cur_revision_id, cur_revno)])
 
88
            self._branch.set_last_revision_info(new_revno, new_revision_id)
 
89
        finally:
 
90
            self._branch.unlock()
 
91
        if self._tree is not None:
 
92
            # We are currently processing a series, but when switching branch
 
93
            # pointers, it is easiest to just create a new memory tree.
 
94
            # That way we are sure to have the right files-on-disk
 
95
            # We are cheating a little bit here, and locking the new tree
 
96
            # before the old tree is unlocked. But that way the branch stays
 
97
            # locked throughout.
 
98
            new_tree = memorytree.MemoryTree.create_on_branch(self._branch)
 
99
            new_tree.lock_write()
 
100
            self._tree.unlock()
 
101
            self._tree = new_tree
 
102
 
 
103
    def start_series(self):
 
104
        """We will be creating a series of commits.
 
105
 
 
106
        This allows us to hold open the locks while we are processing.
 
107
 
 
108
        Make sure to call 'finish_series' when you are done.
 
109
        """
 
110
        if self._tree is not None:
 
111
            raise AssertionError('You cannot start a new series while a'
 
112
                                 ' series is already going.')
 
113
        self._tree = memorytree.MemoryTree.create_on_branch(self._branch)
 
114
        self._tree.lock_write()
 
115
 
 
116
    def finish_series(self):
 
117
        """Call this after start_series to unlock the various objects."""
 
118
        self._tree.unlock()
 
119
        self._tree = None
 
120
 
 
121
    def build_snapshot(self, revision_id, parent_ids, actions,
 
122
                       message=None):
 
123
        """Build a commit, shaped in a specific way.
 
124
 
 
125
        :param revision_id: The handle for the new commit, can be None
 
126
        :param parent_ids: A list of parent_ids to use for the commit.
 
127
            It can be None, which indicates to use the last commit.
 
128
        :param actions: A list of actions to perform. Supported actions are:
 
129
            ('add', ('path', 'file-id', 'kind', 'content' or None))
 
130
            ('modify', ('file-id', 'new-content'))
 
131
            ('unversion', 'file-id')
 
132
            # not supported yet: ('rename', ('orig-path', 'new-path'))
 
133
        :param message: An optional commit message, if not supplied, a default
 
134
            commit message will be written.
 
135
        :return: The revision_id of the new commit
 
136
        """
 
137
        if parent_ids is not None:
 
138
            base_id = parent_ids[0]
 
139
            if base_id != self._branch.last_revision():
 
140
                self._move_branch_pointer(base_id)
 
141
 
 
142
        if self._tree is not None:
 
143
            tree = self._tree
 
144
        else:
 
145
            tree = memorytree.MemoryTree.create_on_branch(self._branch)
 
146
        tree.lock_write()
 
147
        try:
 
148
            if parent_ids is not None:
 
149
                tree.set_parent_ids(parent_ids)
 
150
            # Unfortunately, MemoryTree.add(directory) just creates an
 
151
            # inventory entry. And the only public function to create a
 
152
            # directory is MemoryTree.mkdir() which creates the directory, but
 
153
            # also always adds it. So we have to use a multi-pass setup.
 
154
            to_add_directories = []
 
155
            to_add_files = []
 
156
            to_add_file_ids = []
 
157
            to_add_kinds = []
 
158
            new_contents = {}
 
159
            to_unversion_ids = []
 
160
            # TODO: MemoryTree doesn't support rename() or
 
161
            #       apply_inventory_delta, so we'll postpone allowing renames
 
162
            #       for now
 
163
            # to_rename = []
 
164
            for action, info in actions:
 
165
                if action == 'add':
 
166
                    path, file_id, kind, content = info
 
167
                    if kind == 'directory':
 
168
                        to_add_directories.append((path, file_id))
 
169
                    else:
 
170
                        to_add_files.append(path)
 
171
                        to_add_file_ids.append(file_id)
 
172
                        to_add_kinds.append(kind)
 
173
                        if content is not None:
 
174
                            new_contents[file_id] = content
 
175
                elif action == 'modify':
 
176
                    file_id, content = info
 
177
                    new_contents[file_id] = content
 
178
                elif action == 'unversion':
 
179
                    to_unversion_ids.append(info)
 
180
                else:
 
181
                    raise ValueError('Unknown build action: "%s"' % (action,))
 
182
            if to_unversion_ids:
 
183
                tree.unversion(to_unversion_ids)
 
184
            for path, file_id in to_add_directories:
 
185
                if path == '':
 
186
                    # Special case, because the path already exists
 
187
                    tree.add([path], [file_id], ['directory'])
 
188
                else:
 
189
                    tree.mkdir(path, file_id)
 
190
            tree.add(to_add_files, to_add_file_ids, to_add_kinds)
 
191
            for file_id, content in new_contents.iteritems():
 
192
                tree.put_file_bytes_non_atomic(file_id, content)
 
193
 
 
194
            if message is None:
 
195
                message = u'commit %d' % (self._branch.revno() + 1,)
 
196
            return tree.commit(message, rev_id=revision_id)
 
197
        finally:
 
198
            tree.unlock()
 
199
 
 
200
    def get_branch(self):
 
201
        """Return the branch created by the builder."""
 
202
        return self._branch