/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
1
# Copyright (C) 2010, 2011 Canonical Ltd
0.431.27 by Jelmer Vernooij
Catch 503 errors.
2
# Copyright (C) 2018 Breezy Developers
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
0.434.1 by Jelmer Vernooij
Use absolute_import.
18
"""Support for Launchpad."""
19
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
20
from __future__ import absolute_import
21
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
22
import re
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
23
import shutil
24
import tempfile
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
25
7408.3.1 by Jelmer Vernooij
Move propose module into core.
26
from ...propose import (
0.432.2 by Jelmer Vernooij
Publish command sort of works.
27
    Hoster,
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
28
    LabelsUnsupported,
0.431.3 by Jelmer Vernooij
Add a MergeProposal object.
29
    MergeProposal,
0.432.2 by Jelmer Vernooij
Publish command sort of works.
30
    MergeProposalBuilder,
0.431.3 by Jelmer Vernooij
Add a MergeProposal object.
31
    MergeProposalExists,
0.431.12 by Jelmer Vernooij
Fix Launchpad probing.
32
    UnsupportedHoster,
0.431.3 by Jelmer Vernooij
Add a MergeProposal object.
33
    )
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
34
35
from ... import (
0.432.2 by Jelmer Vernooij
Publish command sort of works.
36
    branch as _mod_branch,
37
    controldir,
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
38
    errors,
39
    hooks,
40
    urlutils,
41
    )
7408.2.1 by Jelmer Vernooij
Use standard functions for creating Git URLs.
42
from ...git.urls import git_url_to_bzr_url
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
43
from ...lazy_import import lazy_import
44
lazy_import(globals(), """
45
from breezy.plugins.launchpad import (
46
    lp_api,
47
    )
7253 by Jelmer Vernooij
Fix default launchpadlib API URL.
48
49
from launchpadlib import uris
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
50
""")
0.432.2 by Jelmer Vernooij
Publish command sort of works.
51
from ...transport import get_transport
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
52
53
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
54
# TODO(jelmer): Make selection of launchpad staging a configuration option.
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
55
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
56
def status_to_lp_mp_statuses(status):
57
    statuses = []
58
    if status in ('open', 'all'):
59
        statuses.extend([
60
            'Work in progress',
61
            'Needs review',
62
            'Approved',
63
            'Code failed to merge',
64
            'Queued'])
65
    if status in ('closed', 'all'):
66
        statuses.extend(['Rejected', 'Superseded'])
67
    if status in ('merged', 'all'):
68
        statuses.append('Merged')
69
    return statuses
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
70
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
71
72
def plausible_launchpad_url(url):
73
    if url is None:
74
        return False
75
    if url.startswith('lp:'):
76
        return True
7322.2.1 by Martin
Fix invalid escape sequence warnings in regexps
77
    regex = re.compile(r'([a-z]*\+)*(bzr\+ssh|http|ssh|git|https)'
78
                       r'://(bazaar|git).*\.launchpad\.net')
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
79
    return bool(regex.match(url))
80
81
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
82
class WebserviceFailure(Exception):
83
84
    def __init__(self, message):
85
        self.message = message
86
87
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
88
def _call_webservice(call, *args, **kwargs):
89
    """Make a call to the webservice, wrapping failures.
90
91
    :param call: The call to make.
92
    :param *args: *args for the call.
93
    :param **kwargs: **kwargs for the call.
94
    :return: The result of calling call(*args, *kwargs).
95
    """
96
    from lazr.restfulclient import errors as restful_errors
97
    try:
98
        return call(*args, **kwargs)
99
    except restful_errors.HTTPError as e:
100
        error_lines = []
101
        for line in e.content.splitlines():
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
102
            if line.startswith(b'Traceback (most recent call last):'):
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
103
                break
104
            error_lines.append(line)
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
105
        raise WebserviceFailure(b''.join(error_lines))
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
106
107
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
108
class LaunchpadMergeProposal(MergeProposal):
109
110
    def __init__(self, mp):
111
        self._mp = mp
112
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
113
    def get_source_branch_url(self):
114
        if self._mp.source_branch:
115
            return self._mp.source_branch.bzr_identity
116
        else:
7408.2.1 by Jelmer Vernooij
Use standard functions for creating Git URLs.
117
            return git_url_to_bzr_url(
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
118
                self._mp.source_git_repository.git_identity,
7408.2.1 by Jelmer Vernooij
Use standard functions for creating Git URLs.
119
                ref=self._mp.source_git_path.encode('utf-8'))
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
120
121
    def get_target_branch_url(self):
122
        if self._mp.target_branch:
123
            return self._mp.target_branch.bzr_identity
124
        else:
7408.2.1 by Jelmer Vernooij
Use standard functions for creating Git URLs.
125
            return git_url_to_bzr_url(
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
126
                self._mp.target_git_repository.git_identity,
7408.2.1 by Jelmer Vernooij
Use standard functions for creating Git URLs.
127
                ref=self._mp.target_git_path.encode('utf-8'))
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
128
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
129
    @property
130
    def url(self):
131
        return lp_api.canonical_url(self._mp)
132
133
    def is_merged(self):
134
        return (self._mp.queue_status == 'Merged')
135
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
136
    def is_closed(self):
137
        return (self._mp.queue_status in ('Rejected', 'Superseded'))
138
139
    def reopen(self):
140
        self._mp.setStatus(status='Needs review')
141
0.431.70 by Jelmer Vernooij
Implement get_description/set_description for LaunchpadMergeProposal.
142
    def get_description(self):
143
        return self._mp.description
144
145
    def set_description(self, description):
146
        self._mp.description = description
7290.5.1 by Jelmer Vernooij
Actually send API call to Launchpad when updating description.
147
        self._mp.lp_save()
0.431.70 by Jelmer Vernooij
Implement get_description/set_description for LaunchpadMergeProposal.
148
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
149
    def get_commit_message(self):
150
        return self._mp.commit_message
151
152
    def set_commit_message(self, commit_message):
153
        self._mp.commit_message = commit_message
154
        self._mp.lp_save()
155
7260.2.1 by Jelmer Vernooij
Implement .close on merge proposals.
156
    def close(self):
157
        self._mp.setStatus(status='Rejected')
158
7381.2.1 by Jelmer Vernooij
Implement LaunchpadMergeProposal.can_be_merged.
159
    def can_be_merged(self):
160
        if not self._mp.preview_diff:
161
            # Maybe?
162
            return True
163
        return not bool(self._mp.preview_diff.conflicts)
164
7414.4.1 by Jelmer Vernooij
Add a MergeProposal.get_merged_by method.
165
    def get_merged_by(self):
166
        merge_reporter = self._mp.merge_reporter
167
        if merge_reporter is None:
168
            return None
169
        return merge_reporter.name
170
7414.4.3 by Jelmer Vernooij
Add MergeProposal.get_merged_at.
171
    def get_merged_at(self):
172
        return self._mp.date_merged
173
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
174
    def merge(self, commit_message=None):
175
        target_branch = _mod_branch.Branch.open(
176
            self.get_target_branch_url())
177
        source_branch = _mod_branch.Branch.open(
178
            self.get_source_branch_url())
179
        # TODO(jelmer): Ideally this would use a memorytree, but merge doesn't
180
        # support that yet.
181
        # tree = target_branch.create_memorytree()
182
        tmpdir = tempfile.mkdtemp()
183
        try:
184
            tree = target_branch.create_checkout(
185
                to_location=tmpdir, lightweight=True)
186
            tree.merge_from_branch(source_branch)
187
            tree.commit(commit_message or self._mp.commit_message)
188
        finally:
189
            shutil.rmtree(tmpdir)
190
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
191
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
192
class Launchpad(Hoster):
193
    """The Launchpad hosting service."""
194
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
195
    name = 'launchpad'
196
0.431.29 by Jelmer Vernooij
Fix github branch name, add bug URL.
197
    # https://bugs.launchpad.net/launchpad/+bug/397676
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
198
    supports_merge_proposal_labels = False
199
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
200
    supports_merge_proposal_commit_message = True
201
7445.1.1 by Jelmer Vernooij
Add Hoster.merge_proposal_description_format and common function for determining title.
202
    merge_proposal_description_format = 'plain'
203
7254.1.2 by Jelmer Vernooij
Fix propose Launchpad.
204
    def __init__(self, staging=False):
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
205
        self._staging = staging
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
206
        if staging:
7253 by Jelmer Vernooij
Fix default launchpadlib API URL.
207
            lp_base_url = uris.STAGING_SERVICE_ROOT
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
208
        else:
7253 by Jelmer Vernooij
Fix default launchpadlib API URL.
209
            lp_base_url = uris.LPNET_SERVICE_ROOT
7371.4.4 by Jelmer Vernooij
Pull in more fixes from janitor.
210
        self.launchpad = lp_api.connect_launchpad(lp_base_url, version='devel')
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
211
7260.1.1 by Jelmer Vernooij
Add .base_url property to Hoster.
212
    @property
213
    def base_url(self):
214
        return lp_api.uris.web_root_for_service_root(
215
            str(self.launchpad._root_uri))
216
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
217
    def __repr__(self):
218
        return "Launchpad(staging=%s)" % self._staging
219
220
    def hosts(self, branch):
221
        # TODO(jelmer): staging vs non-staging?
222
        return plausible_launchpad_url(branch.user_url)
223
0.432.2 by Jelmer Vernooij
Publish command sort of works.
224
    @classmethod
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
225
    def probe_from_url(cls, url, possible_transports=None):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
226
        if plausible_launchpad_url(url):
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
227
            return Launchpad()
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
228
        raise UnsupportedHoster(url)
0.432.2 by Jelmer Vernooij
Publish command sort of works.
229
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
230
    def _get_lp_git_ref_from_branch(self, branch):
231
        url, params = urlutils.split_segment_parameters(branch.user_url)
232
        (scheme, user, password, host, port, path) = urlutils.parse_url(
233
            url)
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
234
        repo_lp = self.launchpad.git_repositories.getByPath(
235
            path=path.strip('/'))
0.431.26 by Jelmer Vernooij
Fix handling of proposals for Launchapd Git URLs.
236
        try:
237
            ref_path = params['ref']
238
        except KeyError:
239
            branch_name = params.get('branch', branch.name)
240
            if branch_name:
241
                ref_path = 'refs/heads/%s' % branch_name
242
            else:
243
                ref_path = repo_lp.default_branch
244
        ref_lp = repo_lp.getRefByPath(path=ref_path)
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
245
        return (repo_lp, ref_lp)
246
247
    def _get_lp_bzr_branch_from_branch(self, branch):
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
248
        return self.launchpad.branches.getByUrl(
249
            url=urlutils.unescape(branch.user_url))
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
250
0.431.23 by Jelmer Vernooij
Launchpad fixes.
251
    def _get_derived_git_path(self, base_path, owner, project):
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
252
        base_repo = self.launchpad.git_repositories.getByPath(path=base_path)
253
        if project is None:
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
254
            project = urlutils.parse_url(base_repo.git_ssh_url)[-1].strip('/')
255
        if project.startswith('~'):
256
            project = '/'.join(base_path.split('/')[1:])
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
257
        # TODO(jelmer): Surely there is a better way of creating one of these
258
        # URLs?
0.431.23 by Jelmer Vernooij
Launchpad fixes.
259
        return "~%s/%s" % (owner, project)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
260
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
261
    def _publish_git(self, local_branch, base_path, name, owner, project=None,
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
262
                     revision_id=None, overwrite=False, allow_lossy=True):
0.431.23 by Jelmer Vernooij
Launchpad fixes.
263
        to_path = self._get_derived_git_path(base_path, owner, project)
264
        to_transport = get_transport("git+ssh://git.launchpad.net/" + to_path)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
265
        try:
266
            dir_to = controldir.ControlDir.open_from_transport(to_transport)
267
        except errors.NotBranchError:
268
            # Didn't find anything
269
            dir_to = None
270
271
        if dir_to is None:
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
272
            try:
273
                br_to = local_branch.create_clone_on_transport(
7233.3.2 by Jelmer Vernooij
Merge lp:brz-propose.
274
                    to_transport, revision_id=revision_id, name=name)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
275
            except errors.NoRoundtrippingSupport:
276
                br_to = local_branch.create_clone_on_transport(
7211.13.7 by Jelmer Vernooij
Fix formatting.
277
                    to_transport, revision_id=revision_id, name=name,
7233.3.2 by Jelmer Vernooij
Merge lp:brz-propose.
278
                    lossy=True)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
279
        else:
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
280
            try:
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
281
                dir_to = dir_to.push_branch(
282
                    local_branch, revision_id, overwrite=overwrite, name=name)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
283
            except errors.NoRoundtrippingSupport:
284
                if not allow_lossy:
285
                    raise
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
286
                dir_to = dir_to.push_branch(
287
                    local_branch, revision_id, overwrite=overwrite, name=name,
288
                    lossy=True)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
289
            br_to = dir_to.target_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
290
        return br_to, (
291
            "https://git.launchpad.net/%s/+ref/%s" % (to_path, name))
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
292
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
293
    def _get_derived_bzr_path(self, base_branch, name, owner, project):
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
294
        if project is None:
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
295
            base_branch_lp = self._get_lp_bzr_branch_from_branch(base_branch)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
296
            project = '/'.join(base_branch_lp.unique_name.split('/')[1:-1])
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
297
        # TODO(jelmer): Surely there is a better way of creating one of these
298
        # URLs?
0.431.23 by Jelmer Vernooij
Launchpad fixes.
299
        return "~%s/%s/%s" % (owner, project, name)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
300
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
301
    def get_push_url(self, branch):
302
        (vcs, user, password, path, params) = self._split_url(branch.user_url)
303
        if vcs == 'bzr':
304
            branch_lp = self._get_lp_bzr_branch_from_branch(branch)
305
            return branch_lp.bzr_identity
306
        elif vcs == 'git':
307
            return urlutils.join_segment_parameters(
308
                "git+ssh://git.launchpad.net/" + path, params)
309
        else:
310
            raise AssertionError
311
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
312
    def _publish_bzr(self, local_branch, base_branch, name, owner,
313
                     project=None, revision_id=None, overwrite=False,
314
                     allow_lossy=True):
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
315
        to_path = self._get_derived_bzr_path(base_branch, name, owner, project)
0.431.23 by Jelmer Vernooij
Launchpad fixes.
316
        to_transport = get_transport("lp:" + to_path)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
317
        try:
318
            dir_to = controldir.ControlDir.open_from_transport(to_transport)
319
        except errors.NotBranchError:
320
            # Didn't find anything
321
            dir_to = None
322
323
        if dir_to is None:
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
324
            br_to = local_branch.create_clone_on_transport(
325
                to_transport, revision_id=revision_id)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
326
        else:
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
327
            br_to = dir_to.push_branch(
328
                local_branch, revision_id, overwrite=overwrite).target_branch
0.431.23 by Jelmer Vernooij
Launchpad fixes.
329
        return br_to, ("https://code.launchpad.net/" + to_path)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
330
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
331
    def _split_url(self, url):
332
        url, params = urlutils.split_segment_parameters(url)
333
        (scheme, user, password, host, port, path) = urlutils.parse_url(url)
334
        path = path.strip('/')
335
        if host.startswith('bazaar.'):
336
            vcs = 'bzr'
337
        elif host.startswith('git.'):
338
            vcs = 'git'
339
        else:
340
            raise ValueError("unknown host %s" % host)
341
        return (vcs, user, password, path, params)
342
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
343
    def publish_derived(self, local_branch, base_branch, name, project=None,
344
                        owner=None, revision_id=None, overwrite=False,
345
                        allow_lossy=True):
0.432.2 by Jelmer Vernooij
Publish command sort of works.
346
        """Publish a branch to the site, derived from base_branch.
347
348
        :param base_branch: branch to derive the new branch from
349
        :param new_branch: branch to publish
350
        :param name: Name of the new branch on the remote host
351
        :param project: Optional project name
352
        :param owner: Optional owner
353
        :return: resulting branch
354
        """
355
        if owner is None:
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
356
            owner = self.launchpad.me.name
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
357
        (base_vcs, base_user, base_password, base_path,
358
            base_params) = self._split_url(base_branch.user_url)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
359
        # TODO(jelmer): Prevent publishing to development focus
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
360
        if base_vcs == 'bzr':
7211.13.7 by Jelmer Vernooij
Fix formatting.
361
            return self._publish_bzr(
362
                local_branch, base_branch, name, project=project, owner=owner,
363
                revision_id=revision_id, overwrite=overwrite,
364
                allow_lossy=allow_lossy)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
365
        elif base_vcs == 'git':
7211.13.7 by Jelmer Vernooij
Fix formatting.
366
            return self._publish_git(
367
                local_branch, base_path, name, project=project, owner=owner,
368
                revision_id=revision_id, overwrite=overwrite,
369
                allow_lossy=allow_lossy)
0.432.2 by Jelmer Vernooij
Publish command sort of works.
370
        else:
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
371
            raise AssertionError('not a valid Launchpad URL')
0.432.2 by Jelmer Vernooij
Publish command sort of works.
372
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
373
    def get_derived_branch(self, base_branch, name, project=None, owner=None):
374
        if owner is None:
375
            owner = self.launchpad.me.name
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
376
        (base_vcs, base_user, base_password, base_path,
377
            base_params) = self._split_url(base_branch.user_url)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
378
        if base_vcs == 'bzr':
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
379
            to_path = self._get_derived_bzr_path(
380
                base_branch, name, owner, project)
0.431.23 by Jelmer Vernooij
Launchpad fixes.
381
            return _mod_branch.Branch.open("lp:" + to_path)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
382
        elif base_vcs == 'git':
7211.13.7 by Jelmer Vernooij
Fix formatting.
383
            to_path = self._get_derived_git_path(
384
                base_path.strip('/'), owner, project)
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
385
            to_url = urlutils.join_segment_parameters(
7240.4.1 by Jelmer Vernooij
Merge lp:brz-propose.
386
                "git+ssh://git.launchpad.net/" + to_path,
387
                {'branch': name})
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
388
            return _mod_branch.Branch.open(to_url)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
389
        else:
390
            raise AssertionError('not a valid Launchpad URL')
391
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
392
    def iter_proposals(self, source_branch, target_branch, status='open'):
393
        (base_vcs, base_user, base_password, base_path,
394
            base_params) = self._split_url(target_branch.user_url)
395
        statuses = status_to_lp_mp_statuses(status)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
396
        if base_vcs == 'bzr':
7211.13.7 by Jelmer Vernooij
Fix formatting.
397
            target_branch_lp = self.launchpad.branches.getByUrl(
398
                url=target_branch.user_url)
399
            source_branch_lp = self.launchpad.branches.getByUrl(
400
                url=source_branch.user_url)
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
401
            for mp in target_branch_lp.getMergeProposals(status=statuses):
402
                if mp.source_branch_link != source_branch_lp.self_link:
403
                    continue
404
                yield LaunchpadMergeProposal(mp)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
405
        elif base_vcs == 'git':
7211.13.7 by Jelmer Vernooij
Fix formatting.
406
            (source_repo_lp, source_branch_lp) = (
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
407
                self._get_lp_git_ref_from_branch(source_branch))
7211.13.7 by Jelmer Vernooij
Fix formatting.
408
            (target_repo_lp, target_branch_lp) = (
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
409
                self._get_lp_git_ref_from_branch(target_branch))
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
410
            for mp in target_branch_lp.getMergeProposals(status=statuses):
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
411
                if (target_branch_lp.path != mp.target_git_path or
7211.13.7 by Jelmer Vernooij
Fix formatting.
412
                        target_repo_lp != mp.target_git_repository or
413
                        source_branch_lp.path != mp.source_git_path or
414
                        source_repo_lp != mp.source_git_repository):
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
415
                    continue
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
416
                yield LaunchpadMergeProposal(mp)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
417
        else:
418
            raise AssertionError('not a valid Launchpad URL')
419
0.432.2 by Jelmer Vernooij
Publish command sort of works.
420
    def get_proposer(self, source_branch, target_branch):
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
421
        (base_vcs, base_user, base_password, base_path,
422
            base_params) = self._split_url(target_branch.user_url)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
423
        if base_vcs == 'bzr':
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
424
            return LaunchpadBazaarMergeProposalBuilder(
7211.13.7 by Jelmer Vernooij
Fix formatting.
425
                self, source_branch, target_branch)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
426
        elif base_vcs == 'git':
0.431.24 by Jelmer Vernooij
Support git merge proposals.
427
            return LaunchpadGitMergeProposalBuilder(
7211.13.7 by Jelmer Vernooij
Fix formatting.
428
                self, source_branch, target_branch)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
429
        else:
430
            raise AssertionError('not a valid Launchpad URL')
0.432.2 by Jelmer Vernooij
Publish command sort of works.
431
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
432
    @classmethod
433
    def iter_instances(cls):
434
        yield cls()
435
0.431.66 by Jelmer Vernooij
Add support for status argument.
436
    def iter_my_proposals(self, status='open'):
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
437
        statuses = status_to_lp_mp_statuses(status)
0.431.66 by Jelmer Vernooij
Add support for status argument.
438
        for mp in self.launchpad.me.getMergeProposals(status=statuses):
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
439
            yield LaunchpadMergeProposal(mp)
440
7414.5.2 by Jelmer Vernooij
Change iter_my_projects to iter_my_forks.
441
    def iter_my_forks(self):
442
        # Launchpad doesn't really have the concept of "forks"
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
443
        return iter([])
444
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
445
    def get_proposal_by_url(self, url):
446
        # Launchpad doesn't have a way to find a merge proposal by URL.
447
        (scheme, user, password, host, port, path) = urlutils.parse_url(
448
            url)
7296.9.2 by Jelmer Vernooij
Fix detection of launchpad hosts.
449
        LAUNCHPAD_CODE_DOMAINS = [
450
            ('code.%s' % domain) for domain in lp_api.LAUNCHPAD_DOMAINS.values()]
451
        if host not in LAUNCHPAD_CODE_DOMAINS:
452
            raise UnsupportedHoster(url)
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
453
        # TODO(jelmer): Check if this is a launchpad URL. Otherwise, raise
454
        # UnsupportedHoster
455
        # See https://api.launchpad.net/devel/#branch_merge_proposal
456
        # the syntax is:
457
        # https://api.launchpad.net/devel/~<author.name>/<project.name>/<branch.name>/+merge/<id>
458
        api_url = str(self.launchpad._root_uri) + path
459
        mp = self.launchpad.load(api_url)
460
        return LaunchpadMergeProposal(mp)
461
0.432.2 by Jelmer Vernooij
Publish command sort of works.
462
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
463
class LaunchpadBazaarMergeProposalBuilder(MergeProposalBuilder):
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
464
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
465
    def __init__(self, lp_host, source_branch, target_branch,
0.432.2 by Jelmer Vernooij
Publish command sort of works.
466
                 staging=None, approve=None, fixes=None):
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
467
        """Constructor.
468
469
        :param source_branch: The branch to propose for merging.
470
        :param target_branch: The branch to merge into.
471
        :param staging: If True, propose the merge against staging instead of
472
            production.
473
        :param approve: If True, mark the new proposal as approved immediately.
474
            This is useful when a project permits some things to be approved
475
            by the submitter (e.g. merges between release and deployment
476
            branches).
477
        """
0.431.26 by Jelmer Vernooij
Fix handling of proposals for Launchapd Git URLs.
478
        self.lp_host = lp_host
479
        self.launchpad = lp_host.launchpad
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
480
        self.source_branch = source_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
481
        self.source_branch_lp = self.launchpad.branches.getByUrl(
482
            url=source_branch.user_url)
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
483
        if target_branch is None:
0.431.24 by Jelmer Vernooij
Support git merge proposals.
484
            self.target_branch_lp = self.source_branch_lp.get_target()
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
485
            self.target_branch = _mod_branch.Branch.open(
486
                self.target_branch_lp.bzr_identity)
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
487
        else:
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
488
            self.target_branch = target_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
489
            self.target_branch_lp = self.launchpad.branches.getByUrl(
490
                url=target_branch.user_url)
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
491
        self.approve = approve
492
        self.fixes = fixes
493
494
    def get_infotext(self):
495
        """Determine the initial comment for the merge proposal."""
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
496
        info = ["Source: %s\n" % self.source_branch_lp.bzr_identity]
497
        info.append("Target: %s\n" % self.target_branch_lp.bzr_identity)
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
498
        return ''.join(info)
499
500
    def get_initial_body(self):
501
        """Get a body for the proposal for the user to modify.
502
503
        :return: a str or None.
504
        """
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
505
        if not self.hooks['merge_proposal_body']:
506
            return None
507
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
508
        def list_modified_files():
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
509
            lca_tree = self.source_branch_lp.find_lca_tree(
510
                self.target_branch_lp)
511
            source_tree = self.source_branch.basis_tree()
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
512
            files = modified_files(lca_tree, source_tree)
513
            return list(files)
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
514
        with self.target_branch.lock_read(), \
515
                self.source_branch.lock_read():
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
516
            body = None
517
            for hook in self.hooks['merge_proposal_body']:
518
                body = hook({
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
519
                    'target_branch': self.target_branch_lp.bzr_identity,
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
520
                    'modified_files_callback': list_modified_files,
521
                    'old_body': body,
522
                })
523
            return body
524
525
    def check_proposal(self):
526
        """Check that the submission is sensible."""
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
527
        if self.source_branch_lp.self_link == self.target_branch_lp.self_link:
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
528
            raise errors.BzrCommandError(
529
                'Source and target branches must be different.')
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
530
        for mp in self.source_branch_lp.landing_targets:
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
531
            if mp.queue_status in ('Merged', 'Rejected'):
532
                continue
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
533
            if mp.target_branch.self_link == self.target_branch_lp.self_link:
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
534
                raise MergeProposalExists(lp_api.canonical_url(mp))
535
536
    def approve_proposal(self, mp):
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
537
        with self.source_branch.lock_read():
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
538
            _call_webservice(
539
                mp.createComment,
540
                vote=u'Approve',
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
541
                subject='',  # Use the default subject.
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
542
                content=u"Rubberstamp! Proposer approves of own proposal.")
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
543
            _call_webservice(mp.setStatus, status=u'Approved',
544
                             revid=self.source_branch.last_revision())
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
545
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
546
    def create_proposal(self, description, reviewers=None, labels=None,
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
547
                        prerequisite_branch=None, commit_message=None):
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
548
        """Perform the submission."""
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
549
        if labels:
7295.2.1 by Jelmer Vernooij
Fix some issues reported by lgtm.com.
550
            raise LabelsUnsupported(self)
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
551
        if prerequisite_branch is not None:
552
            prereq = self.launchpad.branches.getByUrl(
553
                url=prerequisite_branch.user_url)
554
        else:
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
555
            prereq = None
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
556
        if reviewers is None:
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
557
            reviewer_objs = []
558
        else:
559
            reviewer_objs = []
560
            for reviewer in reviewers:
561
                if '@' in reviewer:
562
                    reviewer_obj = self.launchpad.people.getByEmail(email=reviewer)
563
                else:
564
                    reviewer_obj = self.launchpad.people[reviewer]
565
                reviewer_objs.append(reviewer_obj)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
566
        try:
567
            mp = _call_webservice(
568
                self.source_branch_lp.createMergeProposal,
569
                target_branch=self.target_branch_lp,
570
                prerequisite_branch=prereq,
0.431.42 by Jelmer Vernooij
Remove unnecessary encode (breaks on Python 3).
571
                initial_comment=description.strip(),
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
572
                commit_message=commit_message,
7381.5.5 by Jelmer Vernooij
Review feedback.
573
                reviewers=[reviewer.self_link for reviewer in reviewer_objs],
574
                review_types=['' for reviewer in reviewer_objs])
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
575
        except WebserviceFailure as e:
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
576
            # Urgh.
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
577
            if (b'There is already a branch merge proposal '
7211.13.7 by Jelmer Vernooij
Fix formatting.
578
                    b'registered for branch ') in e.message:
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
579
                raise MergeProposalExists(self.source_branch.user_url)
580
            raise
581
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
582
        if self.approve:
583
            self.approve_proposal(mp)
584
        if self.fixes:
585
            if self.fixes.startswith('lp:'):
586
                self.fixes = self.fixes[3:]
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
587
            _call_webservice(
588
                mp.linkBug,
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
589
                bug=self.launchpad.bugs[int(self.fixes)])
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
590
        return LaunchpadMergeProposal(mp)
0.431.24 by Jelmer Vernooij
Support git merge proposals.
591
592
593
class LaunchpadGitMergeProposalBuilder(MergeProposalBuilder):
594
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
595
    def __init__(self, lp_host, source_branch, target_branch,
0.431.24 by Jelmer Vernooij
Support git merge proposals.
596
                 staging=None, approve=None, fixes=None):
597
        """Constructor.
598
599
        :param source_branch: The branch to propose for merging.
600
        :param target_branch: The branch to merge into.
601
        :param staging: If True, propose the merge against staging instead of
602
            production.
603
        :param approve: If True, mark the new proposal as approved immediately.
604
            This is useful when a project permits some things to be approved
605
            by the submitter (e.g. merges between release and deployment
606
            branches).
607
        """
0.431.26 by Jelmer Vernooij
Fix handling of proposals for Launchapd Git URLs.
608
        self.lp_host = lp_host
609
        self.launchpad = lp_host.launchpad
0.431.24 by Jelmer Vernooij
Support git merge proposals.
610
        self.source_branch = source_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
611
        (self.source_repo_lp,
612
            self.source_branch_lp) = self.lp_host._get_lp_git_ref_from_branch(
613
                source_branch)
0.431.24 by Jelmer Vernooij
Support git merge proposals.
614
        if target_branch is None:
615
            self.target_branch_lp = self.source_branch.get_target()
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
616
            self.target_branch = _mod_branch.Branch.open(
617
                self.target_branch_lp.git_https_url)
0.431.24 by Jelmer Vernooij
Support git merge proposals.
618
        else:
619
            self.target_branch = target_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
620
            (self.target_repo_lp, self.target_branch_lp) = (
621
                self.lp_host._get_lp_git_ref_from_branch(target_branch))
0.431.24 by Jelmer Vernooij
Support git merge proposals.
622
        self.approve = approve
623
        self.fixes = fixes
624
625
    def get_infotext(self):
626
        """Determine the initial comment for the merge proposal."""
627
        info = ["Source: %s\n" % self.source_branch.user_url]
628
        info.append("Target: %s\n" % self.target_branch.user_url)
629
        return ''.join(info)
630
631
    def get_initial_body(self):
632
        """Get a body for the proposal for the user to modify.
633
634
        :return: a str or None.
635
        """
636
        if not self.hooks['merge_proposal_body']:
637
            return None
638
639
        def list_modified_files():
640
            lca_tree = self.source_branch_lp.find_lca_tree(
641
                self.target_branch_lp)
642
            source_tree = self.source_branch.basis_tree()
643
            files = modified_files(lca_tree, source_tree)
644
            return list(files)
645
        with self.target_branch.lock_read(), \
646
                self.source_branch.lock_read():
647
            body = None
648
            for hook in self.hooks['merge_proposal_body']:
649
                body = hook({
650
                    'target_branch': self.target_branch,
651
                    'modified_files_callback': list_modified_files,
652
                    'old_body': body,
653
                })
654
            return body
655
656
    def check_proposal(self):
657
        """Check that the submission is sensible."""
658
        if self.source_branch_lp.self_link == self.target_branch_lp.self_link:
659
            raise errors.BzrCommandError(
660
                'Source and target branches must be different.')
661
        for mp in self.source_branch_lp.landing_targets:
662
            if mp.queue_status in ('Merged', 'Rejected'):
663
                continue
664
            if mp.target_branch.self_link == self.target_branch_lp.self_link:
665
                raise MergeProposalExists(lp_api.canonical_url(mp))
666
667
    def approve_proposal(self, mp):
668
        with self.source_branch.lock_read():
669
            _call_webservice(
670
                mp.createComment,
671
                vote=u'Approve',
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
672
                subject='',  # Use the default subject.
0.431.24 by Jelmer Vernooij
Support git merge proposals.
673
                content=u"Rubberstamp! Proposer approves of own proposal.")
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
674
            _call_webservice(
675
                mp.setStatus, status=u'Approved',
676
                revid=self.source_branch.last_revision())
0.431.24 by Jelmer Vernooij
Support git merge proposals.
677
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
678
    def create_proposal(self, description, reviewers=None, labels=None,
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
679
                        prerequisite_branch=None, commit_message=None):
0.431.24 by Jelmer Vernooij
Support git merge proposals.
680
        """Perform the submission."""
681
        if labels:
7295.2.1 by Jelmer Vernooij
Fix some issues reported by lgtm.com.
682
            raise LabelsUnsupported(self)
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
683
        if prerequisite_branch is not None:
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
684
            (prereq_repo_lp, prereq_branch_lp) = (
685
                self.lp_host._get_lp_git_ref_from_branch(prerequisite_branch))
0.431.24 by Jelmer Vernooij
Support git merge proposals.
686
        else:
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
687
            prereq_branch_lp = None
0.431.24 by Jelmer Vernooij
Support git merge proposals.
688
        if reviewers is None:
689
            reviewers = []
690
        try:
691
            mp = _call_webservice(
692
                self.source_branch_lp.createMergeProposal,
693
                merge_target=self.target_branch_lp,
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
694
                merge_prerequisite=prereq_branch_lp,
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
695
                initial_comment=description.strip(),
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
696
                commit_message=commit_message,
0.431.24 by Jelmer Vernooij
Support git merge proposals.
697
                needs_review=True,
698
                reviewers=[self.launchpad.people[reviewer].self_link
699
                           for reviewer in reviewers],
700
                review_types=[None for reviewer in reviewers])
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
701
        except WebserviceFailure as e:
0.431.24 by Jelmer Vernooij
Support git merge proposals.
702
            # Urgh.
703
            if ('There is already a branch merge proposal '
7211.13.7 by Jelmer Vernooij
Fix formatting.
704
                    'registered for branch ') in e.message:
0.431.24 by Jelmer Vernooij
Support git merge proposals.
705
                raise MergeProposalExists(self.source_branch.user_url)
706
            raise
707
        if self.approve:
708
            self.approve_proposal(mp)
709
        if self.fixes:
710
            if self.fixes.startswith('lp:'):
711
                self.fixes = self.fixes[3:]
712
            _call_webservice(
713
                mp.linkBug,
714
                bug=self.launchpad.bugs[int(self.fixes)])
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
715
        return LaunchpadMergeProposal(mp)
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
716
717
718
def modified_files(old_tree, new_tree):
719
    """Return a list of paths in the new tree with modified contents."""
7322.1.6 by Jelmer Vernooij
Use the new attributes on TreeChange.
720
    for change in new_tree.iter_changes(old_tree):
721
        if change.changed_content and change.kind[1] == 'file':
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
722
            yield str(path)