~james-w/bzr-builder/fix-text-of-nest-parts

« back to all changes in this revision

Viewing changes to recipe.py

  • Committer: James Westby
  • Date: 2010-08-06 19:24:38 UTC
  • mfrom: (52.2.12 working)
  • Revision ID: james.westby@canonical.com-20100806192438-dzv1bfgpqfyj877s
Add nest-part instruction. Thanks Andrew.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
        bzrdir,
23
23
        errors,
24
24
        merge,
 
25
        revision,
25
26
        revisionspec,
26
27
        tag,
27
28
        trace,
41
42
 
42
43
 
43
44
MERGE_INSTRUCTION = "merge"
 
45
NEST_PART_INSTRUCTION = "nest-part"
44
46
NEST_INSTRUCTION = "nest"
45
47
RUN_INSTRUCTION = "run"
46
48
 
223
225
        child_branch.branch.unlock()
224
226
 
225
227
 
 
228
def nest_part_branch(op, child_branch, tree_to, br_to, subpath,
 
229
        target_subdir=None):
 
230
    """Merge the branch subdirectory specified by child_branch.
 
231
 
 
232
    :param child_branch: the RecipeBranch to retrieve the branch and revision to
 
233
            merge from.
 
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.
 
240
    """
 
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)
 
248
    else:
 
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()
 
261
    merger.set_pending()
 
262
    if conflict_count:
 
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')))
 
267
 
 
268
 
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)
423
466
 
424
467
 
 
468
class NestPartInstruction(ChildBranch):
 
469
 
 
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
 
474
 
 
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,
 
479
            self.target_subdir)
 
480
 
 
481
 
425
482
class NestInstruction(ChildBranch):
426
483
 
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.
 
500
 
 
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.
443
503
    """
444
504
 
445
505
    def __init__(self, name, url, revspec=None):
464
524
        """
465
525
        self.child_branches.append(MergeInstruction(branch))
466
526
 
 
527
    def nest_part_branch(self, branch, subpath=None, target_subdir=None):
 
528
        """Merge subdir of a child branch into this one.
 
529
 
 
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.
 
535
        """
 
536
        self.child_branches.append(
 
537
            NestPartInstruction(branch, subpath, target_subdir))
 
538
 
467
539
    def nest_branch(self, location, branch):
468
540
        """Nest a child branch in to this one.
469
541
 
663
735
    eol_char = "\n"
664
736
    digit_chars = ("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")
665
737
 
666
 
    NEWEST_VERSION = 0.2
 
738
    NEWEST_VERSION = 0.3
667
739
 
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 == '':
 
820
                            target_subdir = None
744
821
                    revspec = self.parse_optional_revspec()
745
822
                    self.new_line()
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)
749
 
                    else:
 
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
774
 
        else:
 
854
        elif self.version < 0.3:
775
855
            options = (MERGE_INSTRUCTION, NEST_INSTRUCTION, RUN_INSTRUCTION)
776
856
            options_str = "'%s', '%s' or '%s'" % options
 
857
        else:
 
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
831
915
        return location
832
916
 
 
917
    def parse_subpath(self):
 
918
        self.parse_whitespace("the subpath to merge")
 
919
        location = self.take_to_whitespace("the subpath to merge")
 
920
        return location
 
921
 
 
922
    def parse_revspec(self):
 
923
        self.parse_whitespace("the revspec")
 
924
        revspec = self.take_to_whitespace("the revspec")
 
925
        return revspec
 
926
 
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))
838
932
        return revspec
839
933
 
 
934
    def parse_optional_path(self):
 
935
        self.parse_whitespace(None, require=False)
 
936
        path = self.peek_to_whitespace()
 
937
        if path is not None:
 
938
            self.take_chars(len(path))
 
939
        return path
 
940
 
840
941
    def throw_parse_error(self, problem, cls=None, **kwargs):
841
942
        if cls is None:
842
943
            cls = RecipeParseError