/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/changeset/apply_changeset.py

  • Committer: John Arbash Meinel
  • Date: 2005-11-18 04:43:52 UTC
  • mfrom: (0.5.121)
  • mto: (1185.82.108 w-changeset)
  • mto: This revision was merged to the branch mainline in revision 1738.
  • Revision ID: john@arbash-meinel.com-20051118044352-e71e51fcb843025b
merging in the changeset code

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
"""\
 
3
This contains the apply changset function for bzr
 
4
"""
 
5
 
 
6
import bzrlib
 
7
import os
 
8
import sys
 
9
from cStringIO import StringIO
 
10
import tempfile
 
11
import shutil
 
12
 
 
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
 
20
 
 
21
 
 
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.
 
25
 
 
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
 
32
          as ghost revisions)
 
33
    """
 
34
 
 
35
    if not branch.has_revision(cset_info.target):
 
36
        branch.lock_write()
 
37
        try:
 
38
            # install the inventory
 
39
            # TODO: Figure out how to handle ghosted revisions
 
40
            present_parents = []
 
41
            parent_invs = []
 
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))
 
47
 
 
48
            inv = cset_tree.inventory
 
49
            
 
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())
 
58
 
 
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', 
 
63
                rev.revision_id, 
 
64
                split_lines(txt), 
 
65
                present_parents,
 
66
                branch.get_transaction())
 
67
 
 
68
            # And finally, insert the revision
 
69
            rev_tmp = StringIO()
 
70
            serializer_v5.write_revision(rev, rev_tmp)
 
71
            rev_tmp.seek(0)
 
72
            branch.revision_store.add(rev_tmp, rev.revision_id)
 
73
        finally:
 
74
            branch.unlock()
 
75
 
 
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
 
78
    the supplied branch.
 
79
 
 
80
    :return: True if the changeset was automatically committed to the
 
81
             ancestry, False otherwise.
 
82
    """
 
83
    import read_changeset
 
84
 
 
85
    if reverse:
 
86
        raise Exception('reverse not implemented yet')
 
87
 
 
88
    cset = read_changeset.read_changeset(from_file, branch)
 
89
 
 
90
    return _apply_cset(branch, cset, reverse=reverse, auto_commit=auto_commit)
 
91
        
 
92
def _apply_cset(branch, cset, reverse=False, auto_commit=False):
 
93
    """Apply an in-memory changeset to a given branch.
 
94
    """
 
95
 
 
96
    cset_info, cset_tree = cset
 
97
 
 
98
    _install_info(branch, cset_info, cset_tree)
 
99
 
 
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
 
103
    # future.
 
104
    #
 
105
    # TODO:
 
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.
 
109
    #
 
110
    if branch.last_revision() is None:
 
111
        base = None
 
112
    else:
 
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))
 
116
 
 
117
    auto_committed = False
 
118
    
 
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
 
123
    #    tree.
 
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
 
136
    if auto_commit:
 
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
 
140
        # directly.
 
141
        #
 
142
        # TODO: 
 
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.
 
148
        #
 
149
        rh = branch.revision_history()
 
150
        revs_to_merge = None
 
151
        found_parent = False
 
152
        if len(rh) == 0 and len(cset_info.real_revisions[0].parents) == 0:
 
153
            found_parent = True
 
154
            revs_to_merge = cset_info.real_revisions
 
155
        else:
 
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]:
 
160
                            found_parent = True
 
161
                    if found_parent:
 
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
 
169
                    # stop here
 
170
                    break
 
171
 
 
172
        if found_parent:
 
173
            rev_ids = [r.revision_id for r in revs_to_merge]
 
174
            branch.append_revision(*rev_ids)
 
175
            auto_committed = True
 
176
        else:
 
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
 
185
 
 
186
            if target_has_parent:
 
187
                branch.append_revision(target_rev.revision_id)
 
188
            else:
 
189
                print '** Could not auto-commit.'
 
190
 
 
191
    if not auto_committed:
 
192
        branch.add_pending_merge(cset_info.target)
 
193
 
 
194
    return auto_committed
 
195