~bzr-pqm/bzr/bzr.dev

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/usr/bin/env python
"""\
This contains the apply changset function for bzr
"""

import bzrlib
import os

def _install_info(branch, cset_info, cset_tree, cset_inv):
    """Make sure that there is a text entry for each 
    file in the changeset.
    """
    from bzrlib.xml import pack_xml
    from cStringIO import StringIO

    # First, install all required texts
    for file_id, text_id in cset_info.text_ids.iteritems():
        if text_id not in branch.text_store:
            branch.text_store.add(cset_tree.get_file(file_id), text_id)

    # Now install the final inventory
    if cset_info.target not in branch.inventory_store:
        # bzrlib.commit uses a temporary file, but store.add
        # reads in the entire file anyway
        sio = StringIO()
        pack_xml(cset_inv, sio)
        branch.inventory_store.add(sio.getvalue(), cset_info.target)
        del sio

    # Now that we have installed the inventory and texts
    # install the revision entries.
    for rev in cset_info.real_revisions:
        if rev.revision_id not in branch.revision_store:
            sio = StringIO()
            pack_xml(rev, sio)
            branch.inventory_store.add(sio.getvalue(), rev.revision_id)
            del sio

def get_tree(treespec, temp_root, label):
    location, revno = treespec
    branch = find_branch(location)
    if revno is None:
        base_tree = branch.working_tree()
    elif revno == -1:
        base_tree = branch.basis_tree()
    else:
        base_tree = branch.revision_tree(branch.lookup_revision(revno))
    temp_path = os.path.join(temp_root, label)
    os.mkdir(temp_path)
    return branch, MergeTree(base_tree, temp_path)

def merge_revs(branch, rev_base, rev_other,
        ignore_zero=False, check_clean=True):
    """This will merge the tree of rev_other into 
    the working tree of branch using the base given by rev_base.
    All the revision XML should be inside branch.
    """
    import tempfile, shutil
    from bzrlib.merge import merge_inner, MergeTree
    from bzrlib.errors import BzrCommandError

    tempdir = tempfile.mkdtemp(prefix='bzr-')
    try:
        if check_clean:
            from bzrlib.diff import compare_trees
            changes = compare_trees(branch.working_tree(), 
                                    branch.basis_tree(), False)

            if changes.has_changed():
                raise BzrCommandError("Working tree has uncommitted changes.")

        other_dir = os.path.join(tempdir, 'other')
        other_tree = MergeTree(branch.revision_tree(rev_other), other_dir)

        base_dir = os.path.join(tempdir, 'base')
        base_tree = MergeTree(branch.revision_tree(rev_base), base_dir)

        merge_inner(branch, other_tree, base_tree, tempdir,
            ignore_zero=ignore_zero)
    finally:
        shutil.rmtree(tempdir)


def apply_changeset(branch, from_file, auto_commit=False):
    import sys, read_changeset

    cset_info, cset_tree, cset_inv = \
            read_changeset.read_changeset(from_file)

    _install_info(branch, cset_info, cset_tree, cset_inv)

    # We could technically optimize more, by using the ChangesetTree
    # we already have in memory, but after installing revisions
    # this code can work the way merge should work in the
    # future.
    #
    # TODO:
    #   This shouldn't use the base of the changeset as the base
    #   for the merge, the merge code should pick the best merge
    #   based on the ancestry of both trees.
    #
    merge_revs(branch, cset_info.base, cset_info.target)

    if auto_commit:
        from bzrlib.commit import commit

        # When merging, if the revision to be merged has a parent
        # of the current revision, then it can be installed
        # directly.
        #
        # TODO: 
        #   There is actually a slightly stronger statement
        #   whereby if the current revision is in the ancestry
        #   of the merged revisions, it doesn't need to be the
        #   immediate ancestry, but that requires searching
        #   a potentially branching history.
        #
        target_has_parent = False
        target_rev = branch.get_revision(cset_info.target)
        lastrev_id = branch.last_patch()
        for parent in target_rev.parents:
            if parent.revision_id == lastrev_id:
                target_has_parent = True

        if target_has_parent:
            branch.append_revision(target_rev.revision_id)
        else:
            print '** Could not auto-commit.'