1 # Copyright (C) 2009 Canonical Ltd
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.
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.
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
17 """An object that updates a bunch of branches based on data imported."""
19 from operator import itemgetter
21 from bzrlib import bzrdir, errors, osutils
22 from bzrlib.trace import error, note
28 class BranchUpdater(object):
30 def __init__(self, repo, branch, cache_mgr, heads_by_ref, last_ref, tags):
31 """Create an object responsible for updating branches.
33 :param heads_by_ref: a dictionary where
34 names are git-style references like refs/heads/master;
35 values are one item lists of commits marks.
39 self.cache_mgr = cache_mgr
40 self.heads_by_ref = heads_by_ref
41 self.last_ref = last_ref
43 self.name_mapper = branch_mapper.BranchMapper()
44 self._branch_format = \
45 helpers.best_format_for_objects_in_a_repository(repo)
48 """Update the Bazaar branches and tips matching the heads.
50 If the repository is shared, this routine creates branches
51 as required. If it isn't, warnings are produced about the
54 :return: updated, lost_heads where
55 updated = the list of branches updated ('trunk' is first)
56 lost_heads = a list of (bazaar-name,revision) for branches that
57 would have been created had the repository been shared
60 branch_tips, lost_heads = self._get_matching_branches()
61 for br, tip in branch_tips:
62 if self._update_branch(br, tip):
64 return updated, lost_heads
66 def _get_matching_branches(self):
67 """Get the Bazaar branches.
69 :return: branch_tips, lost_heads where
70 branch_tips = a list of (branch,tip) tuples for branches. The
71 first tip is the 'trunk'.
72 lost_heads = a list of (bazaar-name,revision) for branches that
73 would have been created had the repository been shared and
78 ref_names = self.heads_by_ref.keys()
79 if self.branch is not None:
80 trunk = self.select_trunk(ref_names)
81 default_tip = self.heads_by_ref[trunk][0]
82 branch_tips.append((self.branch, default_tip))
83 ref_names.remove(trunk)
85 # Convert the reference names into Bazaar speak. If we haven't
86 # already put the 'trunk' first, do it now.
87 git_to_bzr_map = self.name_mapper.git_to_bzr(ref_names)
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]
94 git_bzr_items.extend(sorted(git_to_bzr_map.items(), key=itemgetter(1)))
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
107 dir_policy = dir_under_current
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]
114 location = dir_policy(name, ref_name)
116 br = self.make_branch(location)
117 branch_tips.append((br,tip))
119 except errors.BzrError, ex:
120 error("ERROR: failed to create branch %s: %s",
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
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:
132 # Use the last reference in the import stream
135 def make_branch(self, location):
136 """Make a branch in the repository if not already there."""
138 return bzrdir.BzrDir.open(location).open_branch()
139 except errors.NotBranchError, ex:
140 return bzrdir.BzrDir.create_branch_convenience(location,
141 format=self._branch_format)
143 def _update_branch(self, br, last_mark):
144 """Update a branch with last revision and tag information.
146 :return: whether the branch was changed or not
148 last_rev_id = self.cache_mgr.revision_ids[last_mark]
149 revs = list(self.repo.iter_reverse_revision_history(last_rev_id))
151 existing_revno, existing_last_rev_id = br.last_revision_info()
153 if revno != existing_revno or last_rev_id != existing_last_rev_id:
154 br.set_last_revision_info(revno, last_rev_id)
156 # apply tags known in this branch
159 for tag,rev in self.tags.items():
163 br.tags._set_tag_dict(my_tags)
167 note("\t branch %s now has %d %s and %d %s", br.nick,
168 revno, helpers.single_plural(revno, "revision", "revisions"),
169 tagno, helpers.single_plural(tagno, "tag", "tags"))