~ubuntu-branches/debian/sid/bzr-builddeb/sid

5.1.5 by James Westby, Jelmer Vernooij, James Westby
[ Jelmer Vernooij ]
1
#    merge_package.py -- The plugin for bzr
2
#    Copyright (C) 2009 Canonical Ltd.
3
#
4
#    :Author: Muharem Hrnjadovic <muharem@ubuntu.com>
5
#
6
#    This file is part of bzr-builddeb.
7
#
8
#    bzr-builddeb is free software; you can redistribute it and/or modify
9
#    it under the terms of the GNU General Public License as published by
10
#    the Free Software Foundation; either version 2 of the License, or
11
#    (at your option) any later version.
12
#
13
#    bzr-builddeb is distributed in the hope that it will be useful,
14
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
15
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
#    GNU General Public License for more details.
17
#
18
#    You should have received a copy of the GNU General Public License
19
#    along with bzr-builddeb; if not, write to the Free Software
20
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21
#
22
23
import os
24
import shutil
25
import tempfile
26
11 by Jelmer Vernooij, Colin Watson, Jelmer Vernooij, Robert Collins, James Westby
[ Colin Watson ]
27
try:
28
    from debian.changelog import Version
29
except ImportError:
30
    # Prior to 0.1.15 the debian module was called debian_bundle
31
    from debian_bundle.changelog import Version
7 by Jelmer Vernooij
* Upload to unstable.
32
5.1.5 by James Westby, Jelmer Vernooij, James Westby
[ Jelmer Vernooij ]
33
from bzrlib.plugins.builddeb.errors import (
34
    SharedUpstreamConflictsWithTargetPackaging)
35
from bzrlib.plugins.builddeb.import_dsc import DistributionBranch
36
from bzrlib.plugins.builddeb.util import find_changelog
37
38
39
def _latest_version(branch):
40
    """Version of the most recent source package upload in the given `branch`.
41
    
42
    :param branch: A Branch object containing the source upload of interest.
43
    """
44
    changelog, _ignore = find_changelog(branch.basis_tree(), False)
45
46
    return changelog.version
47
48
49
def _upstream_version_data(source, target):
50
    """Most recent upstream versions/revision IDs of the merge source/target.
51
52
    Please note: both packaging branches must have been read-locked
53
    beforehand.
54
55
    :param source: The merge source branch.
56
    :param target: The merge target branch.
57
    """
58
    results = list()
59
    for branch in (source, target):
60
        db = DistributionBranch(branch, branch)
61
        uver = _latest_version(branch).upstream_version
7 by Jelmer Vernooij
* Upload to unstable.
62
        results.append((Version(uver),
18 by Jelmer Vernooij, Jelmer Vernooij, Jonathan Riddell, Scott Kitterman
[ Jelmer Vernooij ]
63
                    db.pristine_upstream_source.version_as_revision(None, uver)))
5.1.5 by James Westby, Jelmer Vernooij, James Westby
[ Jelmer Vernooij ]
64
65
    return results
66
67
68
def fix_ancestry_as_needed(tree, source):
69
    """Manipulate the merge target's ancestry to avoid upstream conflicts.
70
71
    Merging J->I given the following ancestry tree is likely to result in
72
    upstream merge conflicts:
73
74
    debian-upstream                 ,------------------H
75
                       A-----------B                    \
76
    ubuntu-upstream     \           \`-------G           \
77
                         \           \        \           \
78
    debian-packaging      \ ,---------D--------\-----------J
79
                           C           \        \
80
    ubuntu-packaging        `----E------F--------I
81
82
    Here there was a new upstream release (G) that Ubuntu packaged (I), and
83
    then another one that Debian packaged, skipping G, at H and J.
84
85
    Now, the way to solve this is to introduce the missing link.
86
87
    debian-upstream                 ,------------------H------.
88
                       A-----------B                    \      \
89
    ubuntu-upstream     \           \`-------G-----------\------K
90
                         \           \        \           \
91
    debian-packaging      \ ,---------D--------\-----------J
92
                           C           \        \
93
    ubuntu-packaging        `----E------F--------I
94
95
    at K, which isn't a real merge, as we just use the tree from H, but add
96
    G as a parent and then we merge that in to Ubuntu.
97
98
    debian-upstream                 ,------------------H------.
99
                       A-----------B                    \      \
100
    ubuntu-upstream     \           \`-------G-----------\------K
101
                         \           \        \           \      \
102
    debian-packaging      \ ,---------D--------\-----------J      \
103
                           C           \        \                  \
104
    ubuntu-packaging        `----E------F--------I------------------L
105
106
    At this point we can merge J->L to merge the Debian and Ubuntu changes.
107
108
    :param tree: The `WorkingTree` of the merge target branch.
109
    :param source: The merge source (packaging) branch.
110
    """
111
    upstreams_diverged = False
112
    t_upstream_reverted = False
113
    target = tree.branch
114
115
    source.lock_read()
116
    try:
117
        tree.lock_write()
118
        try:
119
            # "Unpack" the upstream versions and revision ids for the merge
120
            # source and target branch respectively.
121
            [(us_ver, us_revid), (ut_ver, ut_revid)] = _upstream_version_data(source, target)
122
123
            # Did the upstream branches of the merge source/target diverge?
124
            graph = source.repository.get_graph(target.repository)
125
            upstreams_diverged = (len(graph.heads([us_revid, ut_revid])) > 1)
126
127
            # No, we're done!
128
            if not upstreams_diverged:
129
                return (upstreams_diverged, t_upstream_reverted)
130
131
            # Instantiate a `DistributionBranch` object for the merge target
132
            # (packaging) branch.
133
            db = DistributionBranch(tree.branch, tree.branch)
134
            tempdir = tempfile.mkdtemp(dir=os.path.join(tree.basedir, '..'))
135
136
            try:
137
                # Extract the merge target's upstream tree into a temporary
138
                # directory.
139
                db.extract_upstream_tree(ut_revid, tempdir)
18 by Jelmer Vernooij, Jelmer Vernooij, Jonathan Riddell, Scott Kitterman
[ Jelmer Vernooij ]
140
                tmp_target_utree = db.pristine_upstream_tree
5.1.5 by James Westby, Jelmer Vernooij, James Westby
[ Jelmer Vernooij ]
141
142
                # Merge upstream branch tips to obtain a shared upstream parent.
143
                # This will add revision K (see graph above) to a temporary merge
144
                # target upstream tree.
145
                tmp_target_utree.lock_write()
146
                try:
147
                    if us_ver > ut_ver:
148
                        # The source upstream tree is more recent and the
149
                        # temporary target tree needs to be reshaped to match it.
150
                        tmp_target_utree.revert(
151
                            None, source.repository.revision_tree(us_revid))
152
                        t_upstream_reverted = True
153
154
                    tmp_target_utree.set_parent_ids((ut_revid, us_revid))
7 by Jelmer Vernooij
* Upload to unstable.
155
                    new_revid = tmp_target_utree.commit(
5.1.5 by James Westby, Jelmer Vernooij, James Westby
[ Jelmer Vernooij ]
156
                        'Prepared upstream tree for merging into target branch.')
7 by Jelmer Vernooij
* Upload to unstable.
157
5.1.5 by James Westby, Jelmer Vernooij, James Westby
[ Jelmer Vernooij ]
158
                    # Repository updates during a held lock are not visible,
159
                    # hence the call to refresh the data in the /target/ repo.
160
                    tree.branch.repository.refresh_data()
161
7 by Jelmer Vernooij
* Upload to unstable.
162
                    tree.branch.fetch(source, last_revision=us_revid)
163
                    tree.branch.fetch(tmp_target_utree.branch,
164
                            last_revision=new_revid)
165
5.1.5 by James Westby, Jelmer Vernooij, James Westby
[ Jelmer Vernooij ]
166
                    # Merge shared upstream parent into the target merge branch. This
167
                    # creates revison L in the digram above.
168
                    conflicts = tree.merge_from_branch(tmp_target_utree.branch)
169
                    if conflicts > 0:
170
                        raise SharedUpstreamConflictsWithTargetPackaging()
171
                    else:
172
                        tree.commit('Merging shared upstream rev into target branch.')
173
174
                finally:
175
                    tmp_target_utree.unlock()
176
            finally:
177
                shutil.rmtree(tempdir)
178
        finally:
179
            tree.unlock()
180
    finally:
181
        source.unlock()
182
183
    return (upstreams_diverged, t_upstream_reverted)