~vmiklos/bzr-fastimport/darcs

« back to all changes in this revision

Viewing changes to branch_updater.py

  • Committer: Jelmer Vernooij
  • Date: 2010-07-27 23:55:20 UTC
  • mfrom: (276.1.13 trunk)
  • Revision ID: jelmer@samba.org-20100727235520-1a4hrbknf1dbfsge
Merge fixes from Miklos for darcs fast import.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2009 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""An object that updates a bunch of branches based on data imported."""
 
18
 
 
19
from operator import itemgetter
 
20
 
 
21
from bzrlib import bzrdir, errors, osutils, transport
 
22
from bzrlib.trace import error, note
 
23
 
 
24
import helpers
 
25
 
 
26
 
 
27
class BranchUpdater(object):
 
28
 
 
29
    def __init__(self, repo, branch, cache_mgr, heads_by_ref, last_ref, tags):
 
30
        """Create an object responsible for updating branches.
 
31
 
 
32
        :param heads_by_ref: a dictionary where
 
33
          names are git-style references like refs/heads/master;
 
34
          values are one item lists of commits marks.
 
35
        """
 
36
        self.repo = repo
 
37
        self.branch = branch
 
38
        self.cache_mgr = cache_mgr
 
39
        self.heads_by_ref = heads_by_ref
 
40
        self.last_ref = last_ref
 
41
        self.tags = tags
 
42
        self._branch_format = \
 
43
            helpers.best_format_for_objects_in_a_repository(repo)
 
44
 
 
45
    def update(self):
 
46
        """Update the Bazaar branches and tips matching the heads.
 
47
 
 
48
        If the repository is shared, this routine creates branches
 
49
        as required. If it isn't, warnings are produced about the
 
50
        lost of information.
 
51
 
 
52
        :return: updated, lost_heads where
 
53
          updated = the list of branches updated ('trunk' is first)
 
54
          lost_heads = a list of (bazaar-name,revision) for branches that
 
55
            would have been created had the repository been shared
 
56
        """
 
57
        updated = []
 
58
        branch_tips, lost_heads = self._get_matching_branches()
 
59
        for br, tip in branch_tips:
 
60
            if self._update_branch(br, tip):
 
61
                updated.append(br)
 
62
        return updated, lost_heads
 
63
 
 
64
    def _get_matching_branches(self):
 
65
        """Get the Bazaar branches.
 
66
 
 
67
        :return: branch_tips, lost_heads where
 
68
          branch_tips = a list of (branch,tip) tuples for branches. The
 
69
            first tip is the 'trunk'.
 
70
          lost_heads = a list of (bazaar-name,revision) for branches that
 
71
            would have been created had the repository been shared and
 
72
            everything succeeded
 
73
        """
 
74
        branch_tips = []
 
75
        lost_heads = []
 
76
        ref_names = self.heads_by_ref.keys()
 
77
        if self.branch is not None:
 
78
            trunk = self.select_trunk(ref_names)
 
79
            default_tip = self.heads_by_ref[trunk][0]
 
80
            branch_tips.append((self.branch, default_tip))
 
81
            ref_names.remove(trunk)
 
82
 
 
83
        # Convert the reference names into Bazaar speak. If we haven't
 
84
        # already put the 'trunk' first, do it now.
 
85
        git_to_bzr_map = {}
 
86
        for ref_name in ref_names:
 
87
            git_to_bzr_map[ref_name] = self.cache_mgr.branch_mapper.git_to_bzr(ref_name)
 
88
        if ref_names and self.branch is None:
 
89
            trunk = self.select_trunk(ref_names)
 
90
            git_bzr_items = [(trunk, git_to_bzr_map[trunk])]
 
91
            del git_to_bzr_map[trunk]
 
92
        else:
 
93
            git_bzr_items = []
 
94
        git_bzr_items.extend(sorted(git_to_bzr_map.items(), key=itemgetter(1)))
 
95
 
 
96
        # Policy for locating branches
 
97
        def dir_under_current(name, ref_name):
 
98
            # Using the Bazaar name, get a directory under the current one
 
99
            repo_base = self.repo.bzrdir.transport.base
 
100
            return osutils.pathjoin(repo_base, "..", name)
 
101
        def dir_sister_branch(name, ref_name):
 
102
            # Using the Bazaar name, get a sister directory to the branch
 
103
            return osutils.pathjoin(self.branch.base, "..", name)
 
104
        if self.branch is not None:
 
105
            dir_policy = dir_sister_branch
 
106
        else:
 
107
            dir_policy = dir_under_current
 
108
 
 
109
        # Create/track missing branches
 
110
        shared_repo = self.repo.is_shared()
 
111
        for ref_name, name in git_bzr_items:
 
112
            tip = self.heads_by_ref[ref_name][0]
 
113
            if shared_repo:
 
114
                location = dir_policy(name, ref_name)
 
115
                try:
 
116
                    br = self.make_branch(location)
 
117
                    branch_tips.append((br,tip))
 
118
                    continue
 
119
                except errors.BzrError, ex:
 
120
                    error("ERROR: failed to create branch %s: %s",
 
121
                        location, ex)
 
122
            lost_head = self.cache_mgr.revision_ids[tip]
 
123
            lost_info = (name, lost_head)
 
124
            lost_heads.append(lost_info)
 
125
        return branch_tips, lost_heads
 
126
 
 
127
    def select_trunk(self, ref_names):
 
128
        """Given a set of ref names, choose one as the trunk."""
 
129
        for candidate in ['refs/heads/master']:
 
130
            if candidate in ref_names:
 
131
                return candidate
 
132
        # Use the last reference in the import stream
 
133
        return self.last_ref
 
134
 
 
135
    def make_branch(self, location):
 
136
        """Make a branch in the repository if not already there."""
 
137
        to_transport = transport.get_transport(location)
 
138
        to_transport.create_prefix()
 
139
        try:
 
140
            return bzrdir.BzrDir.open(location).open_branch()
 
141
        except errors.NotBranchError, ex:
 
142
            return bzrdir.BzrDir.create_branch_convenience(location,
 
143
                format=self._branch_format,
 
144
                possible_transports=[to_transport])
 
145
 
 
146
    def _update_branch(self, br, last_mark):
 
147
        """Update a branch with last revision and tag information.
 
148
        
 
149
        :return: whether the branch was changed or not
 
150
        """
 
151
        last_rev_id = self.cache_mgr.revision_ids[last_mark]
 
152
        revs = list(self.repo.iter_reverse_revision_history(last_rev_id))
 
153
        revno = len(revs)
 
154
        existing_revno, existing_last_rev_id = br.last_revision_info()
 
155
        changed = False
 
156
        if revno != existing_revno or last_rev_id != existing_last_rev_id:
 
157
            br.set_last_revision_info(revno, last_rev_id)
 
158
            changed = True
 
159
        # apply tags known in this branch
 
160
        my_tags = {}
 
161
        if self.tags:
 
162
            ancestry = self.repo.get_ancestry(last_rev_id)
 
163
            for tag,rev in self.tags.items():
 
164
                if rev in ancestry:
 
165
                    my_tags[tag] = rev
 
166
            if my_tags:
 
167
                br.tags._set_tag_dict(my_tags)
 
168
                changed = True
 
169
        if changed:
 
170
            tagno = len(my_tags)
 
171
            note("\t branch %s now has %d %s and %d %s", br.nick,
 
172
                revno, helpers.single_plural(revno, "revision", "revisions"),
 
173
                tagno, helpers.single_plural(tagno, "tag", "tags"))
 
174
        return changed