223
225
child_branch.branch.unlock()
228
def nest_part_branch(op, child_branch, tree_to, br_to, subpath,
230
"""Merge the branch subdirectory specified by child_branch.
232
:param child_branch: the RecipeBranch to retrieve the branch and revision to
234
:param tree_to: the WorkingTree to merge in to.
235
:param br_to: the Branch to merge in to.
236
:param subpath: only merge files from branch that are from this path.
237
e.g. subpath='/debian' will only merge changes from that directory.
238
:param target_subdir: (optional) directory in target to merge that
239
subpath into. Defaults to basename of subpath.
241
merge_from = branch.Branch.open(child_branch.url)
242
merge_from.lock_read()
243
op.add_cleanup(merge_from.unlock)
244
if child_branch.revspec is not None:
245
merge_revspec = revisionspec.RevisionSpec.from_string(
246
child_branch.revspec)
247
merge_revid = merge_revspec.as_revision_id(merge_from)
249
merge_revid = merge_from.last_revision()
250
child_branch.revid = merge_revid
251
other_tree = merge_from.basis_tree()
252
other_tree.lock_read()
253
op.add_cleanup(other_tree.unlock)
254
if target_subdir is None:
255
target_subdir = os.path.basename(subpath)
256
merger = merge.MergeIntoMerger(this_tree=tree_to, other_tree=other_tree,
257
other_branch=merge_from, target_subdir=target_subdir,
258
source_subpath=subpath, other_rev_id=merge_revid)
259
merger.set_base_revision(revision.NULL_REVISION, merge_from)
260
conflict_count = merger.do_merge()
263
# FIXME: better reporting
264
raise errors.BzrCommandError("Conflicts from merge")
265
tree_to.commit("Merge %s of %s" %
266
(subpath, urlutils.unescape_for_display(child_branch.url, 'utf-8')))
226
269
def update_branch(base_branch, tree_to, br_to, to_transport,
227
270
possible_transports=None):
228
271
if base_branch.branch is None:
422
465
possible_transports=possible_transports)
468
class NestPartInstruction(ChildBranch):
470
def __init__(self, recipe_branch, subpath, target_subdir):
471
ChildBranch.__init__(self, recipe_branch)
472
self.subpath = subpath
473
self.target_subdir = target_subdir
475
def apply(self, target_path, tree_to, br_to):
476
from bzrlib.cleanup import OperationWithCleanups
477
op = OperationWithCleanups(nest_part_branch)
478
op.run(self.recipe_branch, tree_to, br_to, self.subpath,
425
482
class NestInstruction(ChildBranch):
427
484
def apply(self, target_path, tree_to, br_to, possible_transports=None):
440
497
The child_branches attribute is a list of tuples of ChildBranch objects.
441
498
The revid attribute records the revid that the url and revspec resolved
442
499
to when the RecipeBranch was built, or None if it has not been built.
501
:ivar revid: after this recipe branch has been built this is set to the
502
revision ID that was merged/nested from the branch at self.url.
445
505
def __init__(self, name, url, revspec=None):
465
525
self.child_branches.append(MergeInstruction(branch))
527
def nest_part_branch(self, branch, subpath=None, target_subdir=None):
528
"""Merge subdir of a child branch into this one.
530
:param branch: the RecipeBranch to merge.
531
:param subpath: only merge files from branch that are from this path.
532
e.g. subpath='/debian' will only merge changes from that directory.
533
:param target_subdir: (optional) directory in target to merge that
534
subpath into. Defaults to basename of subpath.
536
self.child_branches.append(
537
NestPartInstruction(branch, subpath, target_subdir))
467
539
def nest_branch(self, location, branch):
468
540
"""Nest a child branch in to this one.
664
736
digit_chars = ("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")
668
740
def __init__(self, f, filename=None):
669
741
"""Create a RecipeParser.
741
813
url = self.parse_branch_url()
742
814
if instruction == NEST_INSTRUCTION:
743
815
location = self.parse_branch_location()
816
if instruction == NEST_PART_INSTRUCTION:
817
path = self.parse_subpath()
818
target_subdir = self.parse_optional_path()
819
if target_subdir == '':
744
821
revspec = self.parse_optional_revspec()
746
823
last_branch = RecipeBranch(branch_id, url, revspec=revspec)
747
824
if instruction == NEST_INSTRUCTION:
748
825
active_branches[-1].nest_branch(location, last_branch)
826
elif instruction == MERGE_INSTRUCTION:
750
827
active_branches[-1].merge_branch(last_branch)
828
elif instruction == NEST_PART_INSTRUCTION:
829
active_branches[-1].nest_part_branch(
830
last_branch, path, target_subdir)
751
831
last_instruction = instruction
752
832
if len(active_branches) == 0:
753
833
self.throw_parse_error("Empty recipe")
771
851
if self.version < 0.2:
772
852
options = (MERGE_INSTRUCTION, NEST_INSTRUCTION)
773
853
options_str = "'%s' or '%s'" % options
854
elif self.version < 0.3:
775
855
options = (MERGE_INSTRUCTION, NEST_INSTRUCTION, RUN_INSTRUCTION)
776
856
options_str = "'%s', '%s' or '%s'" % options
858
options = (MERGE_INSTRUCTION, NEST_INSTRUCTION,
859
NEST_PART_INSTRUCTION, RUN_INSTRUCTION)
860
options_str = "'%s', '%s', '%s' or '%s'" % options
777
861
instruction = self.peek_to_whitespace()
778
862
if instruction is None:
779
863
self.throw_parse_error("End of line while looking for %s"
830
914
self.seen_paths[norm_location] = self.line_index + 1
917
def parse_subpath(self):
918
self.parse_whitespace("the subpath to merge")
919
location = self.take_to_whitespace("the subpath to merge")
922
def parse_revspec(self):
923
self.parse_whitespace("the revspec")
924
revspec = self.take_to_whitespace("the revspec")
833
927
def parse_optional_revspec(self):
834
928
self.parse_whitespace(None, require=False)
835
929
revspec = self.peek_to_whitespace()
837
931
self.take_chars(len(revspec))
934
def parse_optional_path(self):
935
self.parse_whitespace(None, require=False)
936
path = self.peek_to_whitespace()
938
self.take_chars(len(path))
840
941
def throw_parse_error(self, problem, cls=None, **kwargs):
842
943
cls = RecipeParseError