/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.5.17 by John Arbash Meinel
adding apply-changset, plus more meta information.
1
#!/usr/bin/env python
2
"""\
3
This contains the apply changset function for bzr
4
"""
5
6
import bzrlib
0.5.67 by John Arbash Meinel
Working on apply_changeset
7
import os
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
8
import sys
9
from cStringIO import StringIO
10
import tempfile
11
import shutil
0.5.67 by John Arbash Meinel
Working on apply_changeset
12
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
13
from bzrlib.xml5 import serializer_v5
0.5.80 by John Arbash Meinel
Starting to write tests for changeset, discovering some errors as I go.
14
from bzrlib.trace import mutter, warning
0.5.110 by Aaron Bentley
Switched to using the common ancestor for changeset merge base
15
from bzrlib.revision import common_ancestor
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
16
from bzrlib.merge import merge_inner
1185.82.62 by Aaron Bentley
Don't fail if some data is already installed
17
from bzrlib.errors import BzrCommandError, RevisionAlreadyPresent
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
18
from bzrlib.diff import compare_trees
19
from bzrlib.osutils import sha_string, split_lines
1185.82.63 by Aaron Bentley
Added install revision progress bar
20
import bzrlib.ui
1185.82.43 by Aaron Bentley
Get single-revision case working
21
from bzrlib.tree import EmptyTree
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
22
0.5.80 by John Arbash Meinel
Starting to write tests for changeset, discovering some errors as I go.
23
1185.82.17 by Aaron Bentley
More API updates
24
def _install_info(repository, cset_info, cset_tree):
0.5.67 by John Arbash Meinel
Working on apply_changeset
25
    """Make sure that there is a text entry for each 
26
    file in the changeset.
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
27
28
    TODO: This might be supplanted by some sort of Commit() object, though
29
          some of the side-effects should not occur
30
    TODO: The latest code assumes that if you have the Revision information
31
          then you have to have everything else.
32
          So there may be no point in adding older revision information to
33
          the bottom of a changeset (though I would like to add them all
34
          as ghost revisions)
35
    """
1185.82.17 by Aaron Bentley
More API updates
36
    if not repository.has_revision(cset_info.target):
1185.82.40 by Aaron Bentley
Started work on testing install_revisions/handling empty changesets
37
        rev = cset_info.real_revisions[-1]
1185.82.17 by Aaron Bentley
More API updates
38
        repository.lock_write()
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
39
        try:
1185.82.40 by Aaron Bentley
Started work on testing install_revisions/handling empty changesets
40
            return install_revision(repository, rev, cset_tree)
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
41
        finally:
1185.82.29 by Aaron Bentley
Got merge test working
42
            repository.unlock()
0.5.67 by John Arbash Meinel
Working on apply_changeset
43
1185.82.40 by Aaron Bentley
Started work on testing install_revisions/handling empty changesets
44
def install_changeset(repository, changeset_reader):
1185.82.63 by Aaron Bentley
Added install revision progress bar
45
    pb = bzrlib.ui.ui_factory.nested_progress_bar()
46
    try:
47
        real_revisions = changeset_reader.info.real_revisions
48
        for i, revision in enumerate(reversed(real_revisions)):
49
            pb.update("Install revisions",i, len(real_revisions))
50
            if repository.has_revision(revision.revision_id):
51
                continue
52
            cset_tree = changeset_reader.revision_tree(repository,
53
                                                       revision.revision_id)
54
            install_revision(repository, revision, cset_tree)
55
    finally:
56
        pb.finished()
1185.82.40 by Aaron Bentley
Started work on testing install_revisions/handling empty changesets
57
58
def install_revision(repository, rev, cset_tree):
59
    # install the inventory
60
    # TODO: Figure out how to handle ghosted revisions
61
    present_parents = []
1185.82.43 by Aaron Bentley
Get single-revision case working
62
    parent_trees = {}
1185.82.40 by Aaron Bentley
Started work on testing install_revisions/handling empty changesets
63
    for p_id in rev.parent_ids:
64
        if repository.has_revision(p_id):
65
            present_parents.append(p_id)
1185.82.43 by Aaron Bentley
Get single-revision case working
66
            parent_trees[p_id] = repository.revision_tree(p_id)
67
        else:
68
            parent_trees[p_id] = EmptyTree()
1185.82.40 by Aaron Bentley
Started work on testing install_revisions/handling empty changesets
69
70
    inv = cset_tree.inventory
71
    
72
    # Add the texts that are not already present
73
    for path, ie in inv.iter_entries():
74
        w = repository.weave_store.get_weave_or_empty(ie.file_id,
75
                repository.get_transaction())
76
        if ie.revision not in w:
1185.82.57 by Aaron Bentley
Fix text parent handling
77
            text_parents = []
78
            for revision, tree in parent_trees.iteritems():
79
                if ie.file_id not in tree:
80
                    continue
81
                parent_id = tree.inventory[ie.file_id].revision
82
                if parent_id in text_parents:
83
                    continue
84
                text_parents.append(parent_id)
85
                    
1185.82.40 by Aaron Bentley
Started work on testing install_revisions/handling empty changesets
86
            repository.weave_store.add_text(ie.file_id, 
87
                                            rev.revision_id,
88
            cset_tree.get_file(ie.file_id).readlines(),
1185.82.43 by Aaron Bentley
Get single-revision case working
89
            text_parents, repository.get_transaction())
1185.82.62 by Aaron Bentley
Don't fail if some data is already installed
90
    try:
91
        repository.add_inventory(rev.revision_id, inv, present_parents)
92
    except RevisionAlreadyPresent:
93
        pass
1185.82.40 by Aaron Bentley
Started work on testing install_revisions/handling empty changesets
94
    repository.add_revision(rev.revision_id, rev, inv)
95
0.5.69 by John Arbash Meinel
Applying patch from Robey Pointer to clean up apply_changeset.
96
def apply_changeset(branch, from_file, reverse=False, auto_commit=False):
0.5.80 by John Arbash Meinel
Starting to write tests for changeset, discovering some errors as I go.
97
    """Read in a changeset from the given file, and apply it to
98
    the supplied branch.
0.5.86 by John Arbash Meinel
Updated the auto-commit functionality, and adding to pending-merges, more testing.
99
100
    :return: True if the changeset was automatically committed to the
101
             ancestry, False otherwise.
0.5.80 by John Arbash Meinel
Starting to write tests for changeset, discovering some errors as I go.
102
    """
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
103
    import read_changeset
0.5.17 by John Arbash Meinel
adding apply-changset, plus more meta information.
104
0.5.69 by John Arbash Meinel
Applying patch from Robey Pointer to clean up apply_changeset.
105
    if reverse:
106
        raise Exception('reverse not implemented yet')
0.5.80 by John Arbash Meinel
Starting to write tests for changeset, discovering some errors as I go.
107
1185.82.17 by Aaron Bentley
More API updates
108
    cset = read_changeset.read_changeset(from_file, branch.repository)
0.5.80 by John Arbash Meinel
Starting to write tests for changeset, discovering some errors as I go.
109
0.5.86 by John Arbash Meinel
Updated the auto-commit functionality, and adding to pending-merges, more testing.
110
    return _apply_cset(branch, cset, reverse=reverse, auto_commit=auto_commit)
0.5.69 by John Arbash Meinel
Applying patch from Robey Pointer to clean up apply_changeset.
111
        
1185.82.17 by Aaron Bentley
More API updates
112
def _apply_cset(tree, cset, reverse=False, auto_commit=False):
0.5.80 by John Arbash Meinel
Starting to write tests for changeset, discovering some errors as I go.
113
    """Apply an in-memory changeset to a given branch.
114
    """
115
0.5.83 by John Arbash Meinel
Tests pass. Now ChangesetTree has it's own inventory.
116
    cset_info, cset_tree = cset
0.5.67 by John Arbash Meinel
Working on apply_changeset
117
1185.82.17 by Aaron Bentley
More API updates
118
    _install_info(tree.branch.repository, cset_info, cset_tree)
0.5.67 by John Arbash Meinel
Working on apply_changeset
119
120
    # We could technically optimize more, by using the ChangesetTree
121
    # we already have in memory, but after installing revisions
122
    # this code can work the way merge should work in the
123
    # future.
124
    #
125
    # TODO:
126
    #   This shouldn't use the base of the changeset as the base
127
    #   for the merge, the merge code should pick the best merge
128
    #   based on the ancestry of both trees.
129
    #
1185.82.17 by Aaron Bentley
More API updates
130
    if tree.last_revision() is None:
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
131
        base = None
132
    else:
1185.82.17 by Aaron Bentley
More API updates
133
        base = common_ancestor(tree.last_revision(), cset_info.target, 
134
                               tree.branch.repository)
135
    merge_inner(tree.branch, 
136
                tree.branch.repository.revision_tree(cset_info.target),
137
                tree.branch.repository.revision_tree(base), this_tree=tree)
0.5.17 by John Arbash Meinel
adding apply-changset, plus more meta information.
138
0.5.86 by John Arbash Meinel
Updated the auto-commit functionality, and adding to pending-merges, more testing.
139
    auto_committed = False
140
    
141
    # There are 2 cases where I am allowing automatic committing.
142
    # 1) If the final revision has a parent of the current last revision
143
    #    (branch.last_patch() in cset.target.parents)
144
    #    that means that the changeset target has already merged the current
145
    #    tree.
146
    # 2) A cset contains a list of revisions. If the first entry has a parent
147
    #    of branch.last_patch(), then we can start merging there, and add the
148
    #    rest of the revisions. But it gets better. Some of the entries in the
149
    #    list might already be in the revision list, so we keep going until
150
    #    we find the first revision *not* in the list. If it's parent is
151
    #    branch.last_patch(), then we can also append history from there.
152
    #    This second part is a little more controversial, because the cset
153
    #    probably does not include all of the inventories. So you would have
154
    #    entries in branch.revision_history() without an associated inventory.
155
    #    we could just explicitly disable this. But if we had the inventory
156
    #    entries available, it is what 'bzr merge' would do.
157
    #    If we disable this, the target will just show up as a pending_merge
0.5.17 by John Arbash Meinel
adding apply-changset, plus more meta information.
158
    if auto_commit:
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
159
        raise NotImplementedError('automatic committing has not been implemented after the changes')
0.5.67 by John Arbash Meinel
Working on apply_changeset
160
        # When merging, if the revision to be merged has a parent
161
        # of the current revision, then it can be installed
162
        # directly.
163
        #
164
        # TODO: 
165
        #   There is actually a slightly stronger statement
166
        #   whereby if the current revision is in the ancestry
167
        #   of the merged revisions, it doesn't need to be the
168
        #   immediate ancestry, but that requires searching
169
        #   a potentially branching history.
170
        #
0.5.86 by John Arbash Meinel
Updated the auto-commit functionality, and adding to pending-merges, more testing.
171
        rh = branch.revision_history()
172
        revs_to_merge = None
173
        found_parent = False
174
        if len(rh) == 0 and len(cset_info.real_revisions[0].parents) == 0:
175
            found_parent = True
176
            revs_to_merge = cset_info.real_revisions
177
        else:
178
            for rev_num, rev in enumerate(cset_info.real_revisions):
179
                if rev.revision_id not in rh:
180
                    for parent in rev.parents:
181
                        if parent.revision_id == rh[-1]:
182
                            found_parent = True
183
                    if found_parent:
184
                        # All revisions up until now already
185
                        # existed in the target history
186
                        # and this last one is a child of the
187
                        # last entry in the history.
188
                        # so we can add the rest
189
                        revs_to_merge = cset_info.real_revisions[rev_num:]
190
                    # Even if we don't find anything, we need to
191
                    # stop here
192
                    break
193
194
        if found_parent:
195
            rev_ids = [r.revision_id for r in revs_to_merge]
196
            branch.append_revision(*rev_ids)
197
            auto_committed = True
198
        else:
199
            # We can also merge if the *last* revision has an
200
            # appropriate parent.
201
            target_has_parent = False
202
            target_rev = branch.get_revision(cset_info.target)
203
            lastrev_id = branch.last_patch()
204
            for parent in target_rev.parents:
205
                if parent.revision_id == lastrev_id:
206
                    target_has_parent = True
207
208
            if target_has_parent:
209
                branch.append_revision(target_rev.revision_id)
210
            else:
211
                print '** Could not auto-commit.'
212
213
    if not auto_committed:
1185.82.17 by Aaron Bentley
More API updates
214
        tree.add_pending_merge(cset_info.target)
0.5.86 by John Arbash Meinel
Updated the auto-commit functionality, and adding to pending-merges, more testing.
215
216
    return auto_committed
0.5.17 by John Arbash Meinel
adding apply-changset, plus more meta information.
217