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, transport
22
from bzrlib.trace import error, note
27
class BranchUpdater(object):
29
def __init__(self, repo, branch, cache_mgr, heads_by_ref, last_ref, tags):
30
"""Create an object responsible for updating branches.
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.
38
self.cache_mgr = cache_mgr
39
self.heads_by_ref = heads_by_ref
40
self.last_ref = last_ref
42
self._branch_format = \
43
helpers.best_format_for_objects_in_a_repository(repo)
46
"""Update the Bazaar branches and tips matching the heads.
48
If the repository is shared, this routine creates branches
49
as required. If it isn't, warnings are produced about the
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
58
branch_tips, lost_heads = self._get_matching_branches()
59
for br, tip in branch_tips:
60
if self._update_branch(br, tip):
62
return updated, lost_heads
64
def _get_matching_branches(self):
65
"""Get the Bazaar branches.
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
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)
83
# Convert the reference names into Bazaar speak. If we haven't
84
# already put the 'trunk' first, do it now.
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]
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."""
137
to_transport = transport.get_transport(location)
138
to_transport.create_prefix()
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])
146
def _update_branch(self, br, last_mark):
147
"""Update a branch with last revision and tag information.
149
:return: whether the branch was changed or not
151
last_rev_id = self.cache_mgr.revision_ids[last_mark]
152
revs = list(self.repo.iter_reverse_revision_history(last_rev_id))
154
existing_revno, existing_last_rev_id = br.last_revision_info()
156
if revno != existing_revno or last_rev_id != existing_last_rev_id:
157
br.set_last_revision_info(revno, last_rev_id)
159
# apply tags known in this branch
162
ancestry = self.repo.get_ancestry(last_rev_id)
163
for tag,rev in self.tags.items():
167
br.tags._set_tag_dict(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"))