3
This contains the apply changset function for bzr
9
from cStringIO import StringIO
13
from bzrlib.xml5 import serializer_v5
14
from bzrlib.trace import mutter, warning
15
from bzrlib.revision import common_ancestor
16
from bzrlib.merge import merge_inner
17
from bzrlib.errors import BzrCommandError
18
from bzrlib.diff import compare_trees
19
from bzrlib.osutils import sha_string, split_lines
22
def _install_info(branch, cset_info, cset_tree):
23
"""Make sure that there is a text entry for each
24
file in the changeset.
26
TODO: This might be supplanted by some sort of Commit() object, though
27
some of the side-effects should not occur
28
TODO: The latest code assumes that if you have the Revision information
29
then you have to have everything else.
30
So there may be no point in adding older revision information to
31
the bottom of a changeset (though I would like to add them all
35
if not branch.has_revision(cset_info.target):
38
# install the inventory
39
# TODO: Figure out how to handle ghosted revisions
42
rev = cset_info.real_revisions[-1]
43
for p_id in rev.parent_ids:
44
if branch.has_revision(p_id):
45
present_parents.append(p_id)
46
parent_invs.append(branch.get_inventory(revision))
48
inv = cset_tree.inventory
50
# Add the texts that are not already present
51
for path, ie in inv.iter_entries():
52
w = branch.weave_store.get_weave_or_empty(ie.file_id,
53
branch.get_transaction())
54
if ie.revision not in w._name_map:
55
branch.weave_store.add_text(ie.file_id, rev.revision_id,
56
cset_tree.get_file(ie.file_id).readlines(),
57
present_parents, branch.get_transaction())
59
# Now add the inventory information
60
txt = serializer_v5.write_inventory_to_string(inv)
61
sha1 = sha_string(txt)
62
branch.control_weaves.add_text('inventory',
66
branch.get_transaction())
68
# And finally, insert the revision
70
serializer_v5.write_revision(rev, rev_tmp)
72
branch.revision_store.add(rev_tmp, rev.revision_id)
76
def apply_changeset(branch, from_file, reverse=False, auto_commit=False):
77
"""Read in a changeset from the given file, and apply it to
80
:return: True if the changeset was automatically committed to the
81
ancestry, False otherwise.
86
raise Exception('reverse not implemented yet')
88
cset = read_changeset.read_changeset(from_file, branch)
90
return _apply_cset(branch, cset, reverse=reverse, auto_commit=auto_commit)
92
def _apply_cset(branch, cset, reverse=False, auto_commit=False):
93
"""Apply an in-memory changeset to a given branch.
96
cset_info, cset_tree = cset
98
_install_info(branch, cset_info, cset_tree)
100
# We could technically optimize more, by using the ChangesetTree
101
# we already have in memory, but after installing revisions
102
# this code can work the way merge should work in the
106
# This shouldn't use the base of the changeset as the base
107
# for the merge, the merge code should pick the best merge
108
# based on the ancestry of both trees.
110
if branch.last_revision() is None:
113
base = common_ancestor(branch.last_revision(), cset_info.target, branch)
114
merge_inner(branch, branch.revision_tree(cset_info.target),
115
branch.revision_tree(base))
117
auto_committed = False
119
# There are 2 cases where I am allowing automatic committing.
120
# 1) If the final revision has a parent of the current last revision
121
# (branch.last_patch() in cset.target.parents)
122
# that means that the changeset target has already merged the current
124
# 2) A cset contains a list of revisions. If the first entry has a parent
125
# of branch.last_patch(), then we can start merging there, and add the
126
# rest of the revisions. But it gets better. Some of the entries in the
127
# list might already be in the revision list, so we keep going until
128
# we find the first revision *not* in the list. If it's parent is
129
# branch.last_patch(), then we can also append history from there.
130
# This second part is a little more controversial, because the cset
131
# probably does not include all of the inventories. So you would have
132
# entries in branch.revision_history() without an associated inventory.
133
# we could just explicitly disable this. But if we had the inventory
134
# entries available, it is what 'bzr merge' would do.
135
# If we disable this, the target will just show up as a pending_merge
137
raise NotImplementedError('automatic committing has not been implemented after the changes')
138
# When merging, if the revision to be merged has a parent
139
# of the current revision, then it can be installed
143
# There is actually a slightly stronger statement
144
# whereby if the current revision is in the ancestry
145
# of the merged revisions, it doesn't need to be the
146
# immediate ancestry, but that requires searching
147
# a potentially branching history.
149
rh = branch.revision_history()
152
if len(rh) == 0 and len(cset_info.real_revisions[0].parents) == 0:
154
revs_to_merge = cset_info.real_revisions
156
for rev_num, rev in enumerate(cset_info.real_revisions):
157
if rev.revision_id not in rh:
158
for parent in rev.parents:
159
if parent.revision_id == rh[-1]:
162
# All revisions up until now already
163
# existed in the target history
164
# and this last one is a child of the
165
# last entry in the history.
166
# so we can add the rest
167
revs_to_merge = cset_info.real_revisions[rev_num:]
168
# Even if we don't find anything, we need to
173
rev_ids = [r.revision_id for r in revs_to_merge]
174
branch.append_revision(*rev_ids)
175
auto_committed = True
177
# We can also merge if the *last* revision has an
178
# appropriate parent.
179
target_has_parent = False
180
target_rev = branch.get_revision(cset_info.target)
181
lastrev_id = branch.last_patch()
182
for parent in target_rev.parents:
183
if parent.revision_id == lastrev_id:
184
target_has_parent = True
186
if target_has_parent:
187
branch.append_revision(target_rev.revision_id)
189
print '** Could not auto-commit.'
191
if not auto_committed:
192
branch.add_pending_merge(cset_info.target)
194
return auto_committed