/breezy/unstable

To get this branch, use:
bzr branch https://code.breezy-vcs.org/breezy/unstable

« back to all changes in this revision

Viewing changes to breezy/plugins/fastimport/branch_updater.py

  • Committer: Jelmer Vernooij
  • Date: 2017-05-24 01:39:33 UTC
  • mfrom: (3815.3776.6)
  • Revision ID: jelmer@jelmer.uk-20170524013933-ir4y4tqtrsiz2ka2
New upstream snapshot.

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, see <http://www.gnu.org/licenses/>.
 
15
 
 
16
"""An object that updates a bunch of branches based on data imported."""
 
17
 
 
18
from __future__ import absolute_import
 
19
 
 
20
from operator import itemgetter
 
21
 
 
22
from ... import bzrdir, errors, osutils, transport
 
23
from ...trace import show_error, note
 
24
 
 
25
from .helpers import (
 
26
    best_format_for_objects_in_a_repository,
 
27
    single_plural,
 
28
    )
 
29
 
 
30
 
 
31
class BranchUpdater(object):
 
32
 
 
33
    def __init__(self, repo, branch, cache_mgr, heads_by_ref, last_ref, tags):
 
34
        """Create an object responsible for updating branches.
 
35
 
 
36
        :param heads_by_ref: a dictionary where
 
37
          names are git-style references like refs/heads/master;
 
38
          values are one item lists of commits marks.
 
39
        """
 
40
        self.repo = repo
 
41
        self.branch = branch
 
42
        self.cache_mgr = cache_mgr
 
43
        self.heads_by_ref = heads_by_ref
 
44
        self.last_ref = last_ref
 
45
        self.tags = tags
 
46
        self._branch_format = \
 
47
            best_format_for_objects_in_a_repository(repo)
 
48
 
 
49
    def update(self):
 
50
        """Update the Bazaar branches and tips matching the heads.
 
51
 
 
52
        If the repository is shared, this routine creates branches
 
53
        as required. If it isn't, warnings are produced about the
 
54
        lost of information.
 
55
 
 
56
        :return: updated, lost_heads where
 
57
          updated = the list of branches updated ('trunk' is first)
 
58
          lost_heads = a list of (bazaar-name,revision) for branches that
 
59
            would have been created had the repository been shared
 
60
        """
 
61
        updated = []
 
62
        branch_tips, lost_heads = self._get_matching_branches()
 
63
        for br, tip in branch_tips:
 
64
            if self._update_branch(br, tip):
 
65
                updated.append(br)
 
66
        return updated, lost_heads
 
67
 
 
68
    def _get_matching_branches(self):
 
69
        """Get the Bazaar branches.
 
70
 
 
71
        :return: branch_tips, lost_heads where
 
72
          branch_tips = a list of (branch,tip) tuples for branches. The
 
73
            first tip is the 'trunk'.
 
74
          lost_heads = a list of (bazaar-name,revision) for branches that
 
75
            would have been created had the repository been shared and
 
76
            everything succeeded
 
77
        """
 
78
        branch_tips = []
 
79
        lost_heads = []
 
80
        ref_names = self.heads_by_ref.keys()
 
81
        if self.branch is not None:
 
82
            trunk = self.select_trunk(ref_names)
 
83
            default_tip = self.heads_by_ref[trunk][0]
 
84
            branch_tips.append((self.branch, default_tip))
 
85
            ref_names.remove(trunk)
 
86
 
 
87
        # Convert the reference names into Bazaar speak. If we haven't
 
88
        # already put the 'trunk' first, do it now.
 
89
        git_to_bzr_map = {}
 
90
        for ref_name in ref_names:
 
91
            git_to_bzr_map[ref_name] = self.cache_mgr.branch_mapper.git_to_bzr(ref_name)
 
92
        if ref_names and self.branch is None:
 
93
            trunk = self.select_trunk(ref_names)
 
94
            git_bzr_items = [(trunk, git_to_bzr_map[trunk])]
 
95
            del git_to_bzr_map[trunk]
 
96
        else:
 
97
            git_bzr_items = []
 
98
        git_bzr_items.extend(sorted(git_to_bzr_map.items(), key=itemgetter(1)))
 
99
 
 
100
        # Policy for locating branches
 
101
        def dir_under_current(name, ref_name):
 
102
            # Using the Bazaar name, get a directory under the current one
 
103
            repo_base = self.repo.bzrdir.transport.base
 
104
            return osutils.pathjoin(repo_base, "..", name)
 
105
        def dir_sister_branch(name, ref_name):
 
106
            # Using the Bazaar name, get a sister directory to the branch
 
107
            return osutils.pathjoin(self.branch.base, "..", name)
 
108
        if self.branch is not None:
 
109
            dir_policy = dir_sister_branch
 
110
        else:
 
111
            dir_policy = dir_under_current
 
112
 
 
113
        # Create/track missing branches
 
114
        shared_repo = self.repo.is_shared()
 
115
        for ref_name, name in git_bzr_items:
 
116
            tip = self.heads_by_ref[ref_name][0]
 
117
            if shared_repo:
 
118
                location = dir_policy(name, ref_name)
 
119
                try:
 
120
                    br = self.make_branch(location)
 
121
                    branch_tips.append((br,tip))
 
122
                    continue
 
123
                except errors.BzrError, ex:
 
124
                    show_error("ERROR: failed to create branch %s: %s",
 
125
                        location, ex)
 
126
            lost_head = self.cache_mgr.lookup_committish(tip)
 
127
            lost_info = (name, lost_head)
 
128
            lost_heads.append(lost_info)
 
129
        return branch_tips, lost_heads
 
130
 
 
131
    def select_trunk(self, ref_names):
 
132
        """Given a set of ref names, choose one as the trunk."""
 
133
        for candidate in ['refs/heads/master']:
 
134
            if candidate in ref_names:
 
135
                return candidate
 
136
        # Use the last reference in the import stream
 
137
        return self.last_ref
 
138
 
 
139
    def make_branch(self, location):
 
140
        """Make a branch in the repository if not already there."""
 
141
        to_transport = transport.get_transport(location)
 
142
        to_transport.create_prefix()
 
143
        try:
 
144
            return bzrdir.BzrDir.open(location).open_branch()
 
145
        except errors.NotBranchError, ex:
 
146
            return bzrdir.BzrDir.create_branch_convenience(location,
 
147
                format=self._branch_format,
 
148
                possible_transports=[to_transport])
 
149
 
 
150
    def _update_branch(self, br, last_mark):
 
151
        """Update a branch with last revision and tag information.
 
152
 
 
153
        :return: whether the branch was changed or not
 
154
        """
 
155
        last_rev_id = self.cache_mgr.lookup_committish(last_mark)
 
156
        self.repo.lock_read()
 
157
        try:
 
158
            graph = self.repo.get_graph()
 
159
            revno = graph.find_distance_to_null(last_rev_id, [])
 
160
        finally:
 
161
            self.repo.unlock()
 
162
        existing_revno, existing_last_rev_id = br.last_revision_info()
 
163
        changed = False
 
164
        if revno != existing_revno or last_rev_id != existing_last_rev_id:
 
165
            br.set_last_revision_info(revno, last_rev_id)
 
166
            changed = True
 
167
        # apply tags known in this branch
 
168
        my_tags = {}
 
169
        if self.tags:
 
170
            graph = self.repo.get_graph()
 
171
            ancestry = [r for (r, ps) in graph.iter_ancestry([last_rev_id]) if ps is not None]
 
172
            for tag,rev in self.tags.items():
 
173
                if rev in ancestry:
 
174
                    my_tags[tag] = rev
 
175
            if my_tags:
 
176
                br.tags._set_tag_dict(my_tags)
 
177
                changed = True
 
178
        if changed:
 
179
            tagno = len(my_tags)
 
180
            note("\t branch %s now has %d %s and %d %s", br.nick,
 
181
                revno, single_plural(revno, "revision", "revisions"),
 
182
                tagno, single_plural(tagno, "tag", "tags"))
 
183
        return changed