1
# merge_package.py -- The plugin for bzr
2
# Copyright (C) 2009 Canonical Ltd.
4
# :Author: Muharem Hrnjadovic <muharem@ubuntu.com>
6
# This file is part of bzr-builddeb.
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.
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.
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
27
from debian_bundle.changelog import Version
29
from bzrlib.plugins.builddeb.errors import (
30
SharedUpstreamConflictsWithTargetPackaging)
31
from bzrlib.plugins.builddeb.import_dsc import DistributionBranch
32
from bzrlib.plugins.builddeb.util import find_changelog
35
def _latest_version(branch):
36
"""Version of the most recent source package upload in the given `branch`.
38
:param branch: A Branch object containing the source upload of interest.
40
changelog, _ignore = find_changelog(branch.basis_tree(), False)
42
return changelog.version
45
def _upstream_version_data(source, target):
46
"""Most recent upstream versions/revision IDs of the merge source/target.
48
Please note: both packaging branches must have been read-locked
51
:param source: The merge source branch.
52
:param target: The merge target branch.
55
for branch in (source, target):
56
db = DistributionBranch(branch, branch)
57
uver = _latest_version(branch).upstream_version
58
results.append((Version(uver),
59
db.revid_of_upstream_version_from_branch(uver)))
64
def fix_ancestry_as_needed(tree, source):
65
"""Manipulate the merge target's ancestry to avoid upstream conflicts.
67
Merging J->I given the following ancestry tree is likely to result in
68
upstream merge conflicts:
70
debian-upstream ,------------------H
72
ubuntu-upstream \ \`-------G \
74
debian-packaging \ ,---------D--------\-----------J
76
ubuntu-packaging `----E------F--------I
78
Here there was a new upstream release (G) that Ubuntu packaged (I), and
79
then another one that Debian packaged, skipping G, at H and J.
81
Now, the way to solve this is to introduce the missing link.
83
debian-upstream ,------------------H------.
85
ubuntu-upstream \ \`-------G-----------\------K
87
debian-packaging \ ,---------D--------\-----------J
89
ubuntu-packaging `----E------F--------I
91
at K, which isn't a real merge, as we just use the tree from H, but add
92
G as a parent and then we merge that in to Ubuntu.
94
debian-upstream ,------------------H------.
96
ubuntu-upstream \ \`-------G-----------\------K
98
debian-packaging \ ,---------D--------\-----------J \
100
ubuntu-packaging `----E------F--------I------------------L
102
At this point we can merge J->L to merge the Debian and Ubuntu changes.
104
:param tree: The `WorkingTree` of the merge target branch.
105
:param source: The merge source (packaging) branch.
107
upstreams_diverged = False
108
t_upstream_reverted = False
115
# "Unpack" the upstream versions and revision ids for the merge
116
# source and target branch respectively.
117
[(us_ver, us_revid), (ut_ver, ut_revid)] = _upstream_version_data(source, target)
119
# Did the upstream branches of the merge source/target diverge?
120
graph = source.repository.get_graph(target.repository)
121
upstreams_diverged = (len(graph.heads([us_revid, ut_revid])) > 1)
124
if not upstreams_diverged:
125
return (upstreams_diverged, t_upstream_reverted)
127
# Instantiate a `DistributionBranch` object for the merge target
128
# (packaging) branch.
129
db = DistributionBranch(tree.branch, tree.branch)
130
tempdir = tempfile.mkdtemp(dir=os.path.join(tree.basedir, '..'))
133
# Extract the merge target's upstream tree into a temporary
135
db.extract_upstream_tree(ut_revid, tempdir)
136
tmp_target_utree = db.upstream_tree
138
# Merge upstream branch tips to obtain a shared upstream parent.
139
# This will add revision K (see graph above) to a temporary merge
140
# target upstream tree.
141
tmp_target_utree.lock_write()
144
# The source upstream tree is more recent and the
145
# temporary target tree needs to be reshaped to match it.
146
tmp_target_utree.revert(
147
None, source.repository.revision_tree(us_revid))
148
t_upstream_reverted = True
150
tmp_target_utree.set_parent_ids((ut_revid, us_revid))
151
new_revid = tmp_target_utree.commit(
152
'Prepared upstream tree for merging into target branch.')
154
# Repository updates during a held lock are not visible,
155
# hence the call to refresh the data in the /target/ repo.
156
tree.branch.repository.refresh_data()
158
tree.branch.fetch(source, last_revision=us_revid)
159
tree.branch.fetch(tmp_target_utree.branch,
160
last_revision=new_revid)
162
# Merge shared upstream parent into the target merge branch. This
163
# creates revison L in the digram above.
164
conflicts = tree.merge_from_branch(tmp_target_utree.branch)
166
raise SharedUpstreamConflictsWithTargetPackaging()
168
tree.commit('Merging shared upstream rev into target branch.')
171
tmp_target_utree.unlock()
173
shutil.rmtree(tempdir)
179
return (upstreams_diverged, t_upstream_reverted)