/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/plugins/propose/cmds.py

  • Committer: Jelmer Vernooij
  • Date: 2017-06-08 23:30:31 UTC
  • mto: This revision was merged to the branch mainline in revision 6690.
  • Revision ID: jelmer@jelmer.uk-20170608233031-3qavls2o7a1pqllj
Update imports.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2018 Jelmer Vernooij <jelmer@jelmer.uk>
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, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
"""Propose command implementations."""
18
 
 
19
 
from io import StringIO
20
 
 
21
 
from ... import (
22
 
    branch as _mod_branch,
23
 
    controldir,
24
 
    errors,
25
 
    log as _mod_log,
26
 
    missing as _mod_missing,
27
 
    msgeditor,
28
 
    urlutils,
29
 
    )
30
 
from ...i18n import gettext
31
 
from ...commands import Command
32
 
from ...option import (
33
 
    ListOption,
34
 
    Option,
35
 
    RegistryOption,
36
 
    )
37
 
from ...trace import note
38
 
from ... import (
39
 
    propose as _mod_propose,
40
 
    )
41
 
 
42
 
 
43
 
def branch_name(branch):
44
 
    if branch.name:
45
 
        return branch.name
46
 
    return urlutils.basename(branch.user_url)
47
 
 
48
 
 
49
 
def _check_already_merged(branch, target):
50
 
    # TODO(jelmer): Check entire ancestry rather than just last revision?
51
 
    if branch.last_revision() == target.last_revision():
52
 
        raise errors.CommandError(gettext(
53
 
            'All local changes are already present in target.'))
54
 
 
55
 
 
56
 
class cmd_publish_derived(Command):
57
 
    __doc__ = """Publish a derived branch.
58
 
 
59
 
    Try to create a public copy of a local branch on a hosting site,
60
 
    derived from the specified base branch.
61
 
 
62
 
    Reasonable defaults are picked for owner name, branch name and project
63
 
    name, but they can also be overridden from the command-line.
64
 
    """
65
 
 
66
 
    takes_options = [
67
 
        'directory',
68
 
        Option('owner', help='Owner of the new remote branch.', type=str),
69
 
        Option('project', help='Project name for the new remote branch.',
70
 
               type=str),
71
 
        Option('name', help='Name of the new remote branch.', type=str),
72
 
        Option('no-allow-lossy',
73
 
               help='Allow fallback to lossy push, if necessary.'),
74
 
        Option('overwrite', help="Overwrite existing commits."),
75
 
        ]
76
 
    takes_args = ['submit_branch?']
77
 
 
78
 
    def run(self, submit_branch=None, owner=None, name=None, project=None,
79
 
            no_allow_lossy=False, overwrite=False, directory='.'):
80
 
        local_branch = _mod_branch.Branch.open_containing(directory)[0]
81
 
        self.add_cleanup(local_branch.lock_write().unlock)
82
 
        if submit_branch is None:
83
 
            submit_branch = local_branch.get_submit_branch()
84
 
            note(gettext('Using submit branch %s') % submit_branch)
85
 
        if submit_branch is None:
86
 
            submit_branch = local_branch.get_parent()
87
 
            note(gettext('Using parent branch %s') % submit_branch)
88
 
        submit_branch = _mod_branch.Branch.open(submit_branch)
89
 
        _check_already_merged(local_branch, submit_branch)
90
 
        if name is None:
91
 
            name = branch_name(local_branch)
92
 
        hoster = _mod_propose.get_hoster(submit_branch)
93
 
        remote_branch, public_url = hoster.publish_derived(
94
 
            local_branch, submit_branch, name=name, project=project,
95
 
            owner=owner, allow_lossy=not no_allow_lossy,
96
 
            overwrite=overwrite)
97
 
        local_branch.set_push_location(remote_branch.user_url)
98
 
        local_branch.set_public_branch(public_url)
99
 
        local_branch.set_submit_branch(submit_branch.user_url)
100
 
        note(gettext("Pushed to %s") % public_url)
101
 
 
102
 
 
103
 
def summarize_unmerged(local_branch, remote_branch, target,
104
 
                       prerequisite_branch=None):
105
 
    """Generate a text description of the unmerged revisions in branch.
106
 
 
107
 
    :param branch: The proposed branch
108
 
    :param target: Target branch
109
 
    :param prerequisite_branch: Optional prerequisite branch
110
 
    :return: A string
111
 
    """
112
 
    log_format = _mod_log.log_formatter_registry.get_default(local_branch)
113
 
    to_file = StringIO()
114
 
    lf = log_format(to_file=to_file, show_ids=False, show_timezone='original')
115
 
    if prerequisite_branch:
116
 
        local_extra = _mod_missing.find_unmerged(
117
 
            remote_branch, prerequisite_branch, restrict='local')[0]
118
 
    else:
119
 
        local_extra = _mod_missing.find_unmerged(
120
 
            remote_branch, target, restrict='local')[0]
121
 
 
122
 
    if remote_branch.supports_tags():
123
 
        rev_tag_dict = remote_branch.tags.get_reverse_tag_dict()
124
 
    else:
125
 
        rev_tag_dict = {}
126
 
 
127
 
    for revision in _mod_missing.iter_log_revisions(
128
 
            local_extra, local_branch.repository, False, rev_tag_dict):
129
 
        lf.log_revision(revision)
130
 
    return to_file.getvalue()
131
 
 
132
 
 
133
 
class cmd_propose_merge(Command):
134
 
    __doc__ = """Propose a branch for merging.
135
 
 
136
 
    This command creates a merge proposal for the local
137
 
    branch to the target branch. The format of the merge
138
 
    proposal depends on the submit branch.
139
 
    """
140
 
 
141
 
    takes_options = [
142
 
        'directory',
143
 
        RegistryOption(
144
 
            'hoster',
145
 
            help='Use the hoster.',
146
 
            lazy_registry=('breezy.plugins.propose.propose', 'hosters')),
147
 
        ListOption('reviewers', short_name='R', type=str,
148
 
                   help='Requested reviewers.'),
149
 
        Option('name', help='Name of the new remote branch.', type=str),
150
 
        Option('description', help='Description of the change.', type=str),
151
 
        Option('prerequisite', help='Prerequisite branch.', type=str),
152
 
        Option('wip', help='Mark merge request as work-in-progress'),
153
 
        Option(
154
 
            'commit-message',
155
 
            help='Set commit message for merge, if supported', type=str),
156
 
        ListOption('labels', short_name='l', type=str,
157
 
                   help='Labels to apply.'),
158
 
        Option('no-allow-lossy',
159
 
               help='Allow fallback to lossy push, if necessary.'),
160
 
        Option('allow-collaboration',
161
 
               help='Allow collaboration from target branch maintainer(s)'),
162
 
        Option('allow-empty',
163
 
               help='Do not prevent empty merge proposals.'),
164
 
        ]
165
 
    takes_args = ['submit_branch?']
166
 
 
167
 
    aliases = ['propose']
168
 
 
169
 
    def run(self, submit_branch=None, directory='.', hoster=None,
170
 
            reviewers=None, name=None, no_allow_lossy=False, description=None,
171
 
            labels=None, prerequisite=None, commit_message=None, wip=False,
172
 
            allow_collaboration=False, allow_empty=False):
173
 
        tree, branch, relpath = (
174
 
            controldir.ControlDir.open_containing_tree_or_branch(directory))
175
 
        if submit_branch is None:
176
 
            submit_branch = branch.get_submit_branch()
177
 
        if submit_branch is None:
178
 
            submit_branch = branch.get_parent()
179
 
        if submit_branch is None:
180
 
            raise errors.CommandError(
181
 
                gettext("No target location specified or remembered"))
182
 
        target = _mod_branch.Branch.open(submit_branch)
183
 
        if not allow_empty:
184
 
            _check_already_merged(branch, target)
185
 
        if hoster is None:
186
 
            hoster = _mod_propose.get_hoster(target)
187
 
        else:
188
 
            hoster = hoster.probe(target)
189
 
        if name is None:
190
 
            name = branch_name(branch)
191
 
        remote_branch, public_branch_url = hoster.publish_derived(
192
 
            branch, target, name=name, allow_lossy=not no_allow_lossy)
193
 
        branch.set_push_location(remote_branch.user_url)
194
 
        branch.set_submit_branch(target.user_url)
195
 
        note(gettext('Published branch to %s') % public_branch_url)
196
 
        if prerequisite is not None:
197
 
            prerequisite_branch = _mod_branch.Branch.open(prerequisite)
198
 
        else:
199
 
            prerequisite_branch = None
200
 
        proposal_builder = hoster.get_proposer(remote_branch, target)
201
 
        if description is None:
202
 
            body = proposal_builder.get_initial_body()
203
 
            info = proposal_builder.get_infotext()
204
 
            info += "\n\n" + summarize_unmerged(
205
 
                branch, remote_branch, target, prerequisite_branch)
206
 
            description = msgeditor.edit_commit_message(
207
 
                info, start_message=body)
208
 
        try:
209
 
            proposal = proposal_builder.create_proposal(
210
 
                description=description, reviewers=reviewers,
211
 
                prerequisite_branch=prerequisite_branch, labels=labels,
212
 
                commit_message=commit_message,
213
 
                work_in_progress=wip, allow_collaboration=allow_collaboration)
214
 
        except _mod_propose.MergeProposalExists as e:
215
 
            note(gettext('There is already a branch merge proposal: %s'), e.url)
216
 
        else:
217
 
            note(gettext('Merge proposal created: %s') % proposal.url)
218
 
 
219
 
 
220
 
class cmd_find_merge_proposal(Command):
221
 
    __doc__ = """Find a merge proposal.
222
 
 
223
 
    """
224
 
 
225
 
    takes_options = ['directory']
226
 
    takes_args = ['submit_branch?']
227
 
    aliases = ['find-proposal']
228
 
 
229
 
    def run(self, directory='.', submit_branch=None):
230
 
        tree, branch, relpath = controldir.ControlDir.open_containing_tree_or_branch(
231
 
            directory)
232
 
        public_location = branch.get_public_branch()
233
 
        if public_location:
234
 
            branch = _mod_branch.Branch.open(public_location)
235
 
        if submit_branch is None:
236
 
            submit_branch = branch.get_submit_branch()
237
 
        if submit_branch is None:
238
 
            submit_branch = branch.get_parent()
239
 
        if submit_branch is None:
240
 
            raise errors.CommandError(
241
 
                gettext("No target location specified or remembered"))
242
 
        else:
243
 
            target = _mod_branch.Branch.open(submit_branch)
244
 
        hoster = _mod_propose.get_hoster(branch)
245
 
        for mp in hoster.iter_proposals(branch, target):
246
 
            self.outf.write(gettext('Merge proposal: %s\n') % mp.url)
247
 
 
248
 
 
249
 
class cmd_my_merge_proposals(Command):
250
 
    __doc__ = """List all merge proposals owned by the logged-in user.
251
 
 
252
 
    """
253
 
 
254
 
    hidden = True
255
 
 
256
 
    takes_options = [
257
 
        'verbose',
258
 
        RegistryOption.from_kwargs(
259
 
            'status',
260
 
            title='Proposal Status',
261
 
            help='Only include proposals with specified status.',
262
 
            value_switches=True,
263
 
            enum_switch=True,
264
 
            all='All merge proposals',
265
 
            open='Open merge proposals',
266
 
            merged='Merged merge proposals',
267
 
            closed='Closed merge proposals')]
268
 
 
269
 
    def run(self, status='open', verbose=False):
270
 
        for instance in _mod_propose.iter_hoster_instances():
271
 
            for mp in instance.iter_my_proposals(status=status):
272
 
                self.outf.write('%s\n' % mp.url)
273
 
                if verbose:
274
 
                    self.outf.write(
275
 
                        '(Merging %s into %s)\n' %
276
 
                        (mp.get_source_branch_url(),
277
 
                         mp.get_target_branch_url()))
278
 
                    description = mp.get_description()
279
 
                    if description:
280
 
                        self.outf.writelines(
281
 
                            ['\t%s\n' % l
282
 
                             for l in description.splitlines()])
283
 
                    self.outf.write('\n')
284
 
 
285
 
 
286
 
class cmd_land_merge_proposal(Command):
287
 
    __doc__ = """Land a merge proposal."""
288
 
 
289
 
    takes_args = ['url']
290
 
    takes_options = [
291
 
        Option('message', help='Commit message to use.', type=str)]
292
 
 
293
 
    def run(self, url, message=None):
294
 
        proposal = _mod_propose.get_proposal_by_url(url)
295
 
        proposal.merge(commit_message=message)
296
 
 
297
 
 
298
 
class cmd_hosters(Command):
299
 
    __doc__ = """List all known hosting sites and user details."""
300
 
 
301
 
    hidden = True
302
 
 
303
 
    def run(self):
304
 
        for instance in _mod_propose.iter_hoster_instances():
305
 
            current_user = instance.get_current_user()
306
 
            self.outf.write(
307
 
                gettext('%s (%s) - user: %s (%s)\n') % (
308
 
                    instance.name, instance.base_url,
309
 
                    current_user, instance.get_user_url(current_user)))