1
# Copyright (C) 2007, 2008 Canonical Ltd
1
# Copyright (C) 2007, 2008, 2009 Canonical Ltd
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
13
13
# You should have received a copy of the GNU General Public License
14
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
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Utility for create branches with particular contents."""
19
19
from bzrlib import (
27
27
class BranchBuilder(object):
28
28
r"""A BranchBuilder aids creating Branches with particular shapes.
30
30
The expected way to use BranchBuilder is to construct a
31
31
BranchBuilder on the transport you want your branch on, and then call
32
32
appropriate build_ methods on it to get the shape of history you want.
56
56
a series in progress, it should be None.
59
def __init__(self, transport, format=None):
59
def __init__(self, transport=None, format=None, branch=None):
60
60
"""Construct a BranchBuilder on transport.
62
62
:param transport: The transport the branch should be created on.
63
63
If the path of the transport does not exist but its parent does
64
64
it will be created.
65
65
:param format: Either a BzrDirFormat, or the name of a format in the
66
66
bzrdir format registry for the branch to be built.
67
:param branch: An already constructed branch to use. This param is
68
mutually exclusive with the transport and format params.
68
if not transport.has('.'):
72
if isinstance(format, str):
73
format = bzrdir.format_registry.make_bzrdir(format)
74
self._branch = bzrdir.BzrDir.create_branch_convenience(transport.base,
75
format=format, force_new_tree=False)
70
if branch is not None:
71
if format is not None:
73
"branch and format kwargs are mutually exclusive")
74
if transport is not None:
76
"branch and transport kwargs are mutually exclusive")
79
if not transport.has('.'):
83
if isinstance(format, str):
84
format = bzrdir.format_registry.make_bzrdir(format)
85
self._branch = bzrdir.BzrDir.create_branch_convenience(
86
transport.base, format=format, force_new_tree=False)
78
def build_commit(self):
79
"""Build a commit on the branch."""
89
def build_commit(self, **commit_kwargs):
90
"""Build a commit on the branch.
92
This makes a commit with no real file content for when you only want
93
to look at the revision graph structure.
95
:param commit_kwargs: Arguments to pass through to commit, such as
80
98
tree = memorytree.MemoryTree.create_on_branch(self._branch)
84
return self._do_commit(tree)
102
return self._do_commit(tree, **commit_kwargs)
93
111
reporter=reporter,
96
def _move_branch_pointer(self, new_revision_id):
114
def _move_branch_pointer(self, new_revision_id,
115
allow_leftmost_as_ghost=False):
97
116
"""Point self._branch to a different revision id."""
98
117
self._branch.lock_write()
100
119
# We don't seem to have a simple set_last_revision(), so we
101
120
# implement it here.
102
121
cur_revno, cur_revision_id = self._branch.last_revision_info()
103
g = self._branch.repository.get_graph()
104
new_revno = g.find_distance_to_null(new_revision_id,
105
[(cur_revision_id, cur_revno)])
106
self._branch.set_last_revision_info(new_revno, new_revision_id)
123
g = self._branch.repository.get_graph()
124
new_revno = g.find_distance_to_null(new_revision_id,
125
[(cur_revision_id, cur_revno)])
126
self._branch.set_last_revision_info(new_revno, new_revision_id)
127
except errors.GhostRevisionsHaveNoRevno:
128
if not allow_leftmost_as_ghost:
108
132
self._branch.unlock()
109
133
if self._tree is not None:
137
161
self._tree = None
139
163
def build_snapshot(self, revision_id, parent_ids, actions,
164
message=None, timestamp=None, allow_leftmost_as_ghost=False,
165
committer=None, timezone=None):
141
166
"""Build a commit, shaped in a specific way.
143
168
:param revision_id: The handle for the new commit, can be None
150
175
('rename', ('orig-path', 'new-path'))
151
176
:param message: An optional commit message, if not supplied, a default
152
177
commit message will be written.
178
:param timestamp: If non-None, set the timestamp of the commit to this
180
:param timezone: An optional timezone for timestamp.
181
:param committer: An optional username to use for commit
182
:param allow_leftmost_as_ghost: True if the leftmost parent should be
183
permitted to be a ghost.
153
184
:return: The revision_id of the new commit
155
186
if parent_ids is not None:
156
187
base_id = parent_ids[0]
157
188
if base_id != self._branch.last_revision():
158
self._move_branch_pointer(base_id)
189
self._move_branch_pointer(base_id,
190
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
160
192
if self._tree is not None:
161
193
tree = self._tree
164
196
tree.lock_write()
166
198
if parent_ids is not None:
167
tree.set_parent_ids(parent_ids)
199
tree.set_parent_ids(parent_ids,
200
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
168
201
# Unfortunately, MemoryTree.add(directory) just creates an
169
202
# inventory entry. And the only public function to create a
170
203
# directory is MemoryTree.mkdir() which creates the directory, but
210
243
tree.add(to_add_files, to_add_file_ids, to_add_kinds)
211
244
for file_id, content in new_contents.iteritems():
212
245
tree.put_file_bytes_non_atomic(file_id, content)
213
return self._do_commit(tree, message=message, rev_id=revision_id)
246
return self._do_commit(tree, message=message, rev_id=revision_id,
247
timestamp=timestamp, timezone=timezone, committer=committer)