1
# dep3.py -- DEP-3 compatible patch formatting
2
# Copyright (C) 2011 Canonical Ltd.
4
# This file is part of bzr-builddeb.
6
# bzr-builddeb is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
11
# bzr-builddeb is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU General Public License for more details.
16
# You should have received a copy of the GNU General Public License
17
# along with bzr-builddeb; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
"""DEP-3 style patch formatting."""
23
from bzrlib import diff
28
def write_dep3_bug_line(f, bug_url, status):
29
"""Write a DEP-3 compatible line with a bug link.
31
:param f: File-like object to write to
32
:param bug_url: Bug URL
33
:param status: Bug status (e.g. "fixed")
35
# For the moment, we only care about fixed bugs
38
if bug_url.startswith("http://bugs.debian.org/"):
39
f.write("Bug-Debian: %s\n" % bug_url)
41
# FIXME: Filter out Ubuntu bugs on Launchpad
42
f.write("Bug: %s\n" % bug_url)
45
def write_dep3_patch_header(f, description=None, origin=None, forwarded=None,
46
bugs=None, authors=None, revision_id=None, last_update=None,
47
applied_upstream=None):
48
"""Write a DEP3 patch header.
50
:param f: File-like object to write to
51
:param description: Description of the patch
52
:param origin: Single line describing the origin of the patch
53
:param forwarded: Single line describing whether and how the patch was
55
:param bugs: Set of bugs fixed in this patch
56
:param authors: Authors of the patch
57
:param revision_id: Relevant bzr revision id
58
:param last_update: Last update timestamp
59
:param applied_upstream: If the patch is applied upstream,
60
an informal string describing where it was merged
62
# FIXME: Handle line breaks, etc sensibly
63
if description is not None:
64
description = description.strip("\n")
65
description = description.replace("\n\n", "\n.\n")
66
description = description.replace("\n", "\n ")
67
f.write("Description: %s\n" % description)
68
if origin is not None:
69
f.write("Origin: %s\n" % origin)
70
if forwarded is not None:
71
f.write("Forwarded: %s\n" % forwarded)
72
if authors is not None:
73
for author in authors:
74
f.write("Author: %s\n" % author)
76
for bug_url, status in bugs:
77
write_dep3_bug_line(f, bug_url, status)
78
if last_update is not None:
79
f.write("Last-Update: %s\n" % time.strftime("%Y-%m-%d",
80
time.gmtime(last_update)))
81
if applied_upstream is not None:
82
f.write("Applied-Upstream: %s\n" % applied_upstream)
83
if revision_id is not None:
84
f.write("X-Bzr-Revision-Id: %s\n" % revision_id)
88
def gather_bugs_and_authors(repository, interesting_revision_ids):
89
"""Gather bug and author information from revisions.
91
:param interesting_revision_ids: Iterable of revision ids to check
92
:return: Tuple of bugs, authors and highest found commit timestamp
97
for rev in repository.get_revisions(interesting_revision_ids):
98
last_update = max(rev.timestamp, last_update)
99
authors.update(rev.get_apparent_authors())
100
bugs.update(rev.iter_bugs())
101
return (bugs, authors, last_update)
104
def determine_applied_upstream(upstream_branch, feature_branch, feature_revid=None):
105
"""Check if a particular revision has been merged upstream.
107
:param upstream_branch: Upstream branch object
108
:param feature_branch: Feature branch
109
:param feature_revid: Revision id in feature branch to check,
110
defaults to feature_branch tip.
111
:return: String that can be used for Applied-Upstream field
113
if feature_revid is None:
114
feature_revid = feature_branch.last_revision()
115
upstream_graph = feature_branch.repository.get_graph(upstream_branch.repository)
116
merger = upstream_graph.find_lefthand_merger(feature_revid,
117
upstream_branch.last_revision())
118
if merger is not None:
119
return "merged in revision %s" % (
120
".".join(str(x) for x in upstream_branch.revision_id_to_dotted_revno(merger)), )
125
def determine_forwarded(upstream_branch, feature_branch, feature_revid):
126
"""See if a branch has been forwarded to upstream.
128
:param upstream_branch: Upstream branch object
129
:param feature_branch: Feature branch
130
:param feature_revid: Revision id in feature branch to check
131
:return: String that can be used for Applied-Upstream field
133
# FIXME: Check for Launchpad merge proposals from feature_branch (or its
134
# public_branch) to upstream_branch
136
# Are there any other ways to see that a patch has been forwarded upstream?
140
def describe_origin(branch, revid):
141
"""Describe a tree for use in the origin field.
143
:param branch: Branch to retrieve the revision from
144
:param revid: Revision id
146
public_branch_url = branch.get_public_branch()
147
if public_branch_url is not None:
148
return "commit, %s, revision: %s" % (
150
".".join(str(x) for x in branch.revision_id_to_dotted_revno(revid)), )
152
return "commit, revision id: %s" % revid
155
def write_dep3_patch(f, branch, base_revid, target_revid, description=None,
156
origin=None, forwarded=None, applied_upstream=None, bugs=None,
157
authors=None, last_update=None):
158
"""Write a DEP-3 compliant patch.
160
:param f: File-like object to write to
161
:param repository: Repository to retrieve revisions from
162
:param base_revid: Base revision id
163
:param target_revid: Target revision id
164
:param description: Optional description
165
:param forwarded: Optional information on if/how the patch was forwarded
166
:param applied_upstream: Optional information on how whether the patch
168
:param bugs: Sequence of bug reports related to this patch
169
:param authors: Sequence of authors of this patch
170
:param last_update: Timestamp for last time this patch was updated
172
write_dep3_patch_header(f, bugs=bugs, authors=authors, last_update=last_update,
173
description=description, revision_id=target_revid, origin=origin,
174
applied_upstream=applied_upstream, forwarded=forwarded)
175
old_tree = branch.repository.revision_tree(base_revid)
176
new_tree = branch.repository.revision_tree(target_revid)
177
diff.show_diff_trees(old_tree, new_tree, f, old_label='old/', new_label='new/')