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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""Utility for create branches with particular contents."""
19
from bzrlib import bzrdir, errors, memorytree
27
22
class BranchBuilder(object):
28
r"""A BranchBuilder aids creating Branches with particular shapes.
23
"""A BranchBuilder aids creating Branches with particular shapes.
30
25
The expected way to use BranchBuilder is to construct a
31
26
BranchBuilder on the transport you want your branch on, and then call
32
27
appropriate build_ methods on it to get the shape of history you want.
39
>>> from bzrlib.transport.memory import MemoryTransport
40
>>> builder = BranchBuilder(MemoryTransport("memory:///"))
41
>>> builder.start_series()
42
>>> builder.build_snapshot('rev-id', None, [
43
... ('add', ('', 'root-id', 'directory', '')),
44
... ('add', ('filename', 'f-id', 'file', 'content\n'))])
46
>>> builder.build_snapshot('rev2-id', ['rev-id'],
47
... [('modify', ('f-id', 'new-content\n'))])
49
>>> builder.finish_series()
50
>>> branch = builder.get_branch()
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()
52
42
:ivar _tree: This is a private member which is not meant to be modified by
53
43
users of this class. While a 'series' is in progress, it should hold a
56
46
a series in progress, it should be None.
59
def __init__(self, transport=None, format=None, branch=None):
49
def __init__(self, transport, format=None):
60
50
"""Construct a BranchBuilder on transport.
62
52
:param transport: The transport the branch should be created on.
63
53
If the path of the transport does not exist but its parent does
64
54
it will be created.
65
55
:param format: Either a BzrDirFormat, or the name of a format in the
66
56
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.
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)
58
if not transport.has('.'):
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)
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
68
def build_commit(self):
69
"""Build a commit on the branch."""
98
70
tree = memorytree.MemoryTree.create_on_branch(self._branch)
102
return self._do_commit(tree, **commit_kwargs)
74
return tree.commit('commit %d' % (self._branch.revno() + 1))
106
def _do_commit(self, tree, message=None, message_callback=None, **kwargs):
107
reporter = commit.NullCommitReporter()
108
if message is None and message_callback is None:
109
message = u'commit %d' % (self._branch.revno() + 1,)
110
return tree.commit(message, message_callback=message_callback,
114
def _move_branch_pointer(self, new_revision_id,
115
allow_leftmost_as_ghost=False):
78
def _move_branch_pointer(self, new_revision_id):
116
79
"""Point self._branch to a different revision id."""
117
80
self._branch.lock_write()
119
82
# We don't seem to have a simple set_last_revision(), so we
120
83
# implement it here.
121
84
cur_revno, cur_revision_id = self._branch.last_revision_info()
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:
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)
132
90
self._branch.unlock()
133
91
if self._tree is not None:
172
129
('add', ('path', 'file-id', 'kind', 'content' or None))
173
130
('modify', ('file-id', 'new-content'))
174
131
('unversion', 'file-id')
175
('rename', ('orig-path', 'new-path'))
132
# not supported yet: ('rename', ('orig-path', 'new-path'))
176
133
:param message: An optional commit message, if not supplied, a default
177
134
commit message will be written.
178
:param message_callback: A message callback to use for the commit, as
179
per mutabletree.commit.
180
:param timestamp: If non-None, set the timestamp of the commit to this
182
:param timezone: An optional timezone for timestamp.
183
:param committer: An optional username to use for commit
184
:param allow_leftmost_as_ghost: True if the leftmost parent should be
185
permitted to be a ghost.
186
135
:return: The revision_id of the new commit
188
137
if parent_ids is not None:
189
138
base_id = parent_ids[0]
190
139
if base_id != self._branch.last_revision():
191
self._move_branch_pointer(base_id,
192
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
140
self._move_branch_pointer(base_id)
194
142
if self._tree is not None:
195
143
tree = self._tree
240
187
tree.add([path], [file_id], ['directory'])
242
189
tree.mkdir(path, file_id)
243
for from_relpath, to_relpath in to_rename:
244
tree.rename_one(from_relpath, to_relpath)
245
190
tree.add(to_add_files, to_add_file_ids, to_add_kinds)
246
191
for file_id, content in new_contents.iteritems():
247
192
tree.put_file_bytes_non_atomic(file_id, content)
248
return self._do_commit(tree, message=message, rev_id=revision_id,
249
timestamp=timestamp, timezone=timezone, committer=committer,
250
message_callback=message_callback)
195
message = u'commit %d' % (self._branch.revno() + 1,)
196
return tree.commit(message, rev_id=revision_id)