Merge lp:~jelmer/brz/trunk-merge-3.1 into lp:brz
- trunk-merge-3.1
- Merge into trunk
Proposed by
Jelmer Vernooij
Status: | Merged |
---|---|
Approved by: | Jelmer Vernooij |
Approved revision: | no longer in the source branch. |
Merge reported by: | The Breezy Bot |
Merged at revision: | not available |
Proposed branch: | lp:~jelmer/brz/trunk-merge-3.1 |
Merge into: | lp:brz |
Diff against target: |
1947 lines (+738/-158) 45 files modified
.gitignore (+11/-0) breezy/__main__.py (+79/-0) breezy/branch.py (+3/-3) breezy/builtins.py (+37/-2) breezy/bzr/branch.py (+3/-3) breezy/bzr/bzrdir.py (+29/-14) breezy/bzr/remote.py (+23/-4) breezy/bzr/smart/bzrdir.py (+10/-3) breezy/config.py (+3/-3) breezy/controldir.py (+14/-2) breezy/git/__init__.py (+1/-1) breezy/git/branch.py (+1/-1) breezy/git/dir.py (+25/-9) breezy/git/mapping.py (+11/-4) breezy/git/object_store.py (+22/-7) breezy/git/remote.py (+19/-5) breezy/git/server.py (+10/-3) breezy/git/tests/test_blackbox.py (+3/-3) breezy/git/tests/test_remote.py (+2/-0) breezy/git/transportgit.py (+54/-4) breezy/git/tree.py (+3/-1) breezy/git/workingtree.py (+6/-2) breezy/info.py (+1/-1) breezy/plugins/darcs/__init__.py (+5/-3) breezy/plugins/fastimport/tests/test_head_tracking.py (+1/-1) breezy/plugins/propose/__init__.py (+8/-0) breezy/plugins/propose/gitlabs.py (+28/-8) breezy/plugins/propose/tests/__init__.py (+26/-0) breezy/plugins/propose/tests/test_gitlab.py (+52/-0) breezy/plugins/weave_fmt/bzrdir.py (+1/-1) breezy/tests/blackbox/__init__.py (+1/-0) breezy/tests/blackbox/test_branch.py (+11/-0) breezy/tests/blackbox/test_clone.py (+70/-0) breezy/tests/blackbox/test_switch.py (+1/-1) breezy/tests/per_bzrdir/test_bzrdir.py (+1/-1) breezy/tests/per_controldir/test_controldir.py (+62/-40) breezy/tests/per_controldir_colo/test_supported.py (+5/-0) breezy/tests/per_workingtree/test_changes_from.py (+18/-0) breezy/tests/test_bzrdir.py (+14/-0) breezy/tests/test_config.py (+16/-1) breezy/tests/test_smart.py (+13/-0) breezy/transport/http/__init__.py (+5/-1) brz (+2/-24) doc/en/release-notes/brz-3.1.txt (+24/-0) doc/en/user-guide/setting_up_email.txt (+4/-2) |
To merge this branch: | bzr merge lp:~jelmer/brz/trunk-merge-3.1 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jelmer Vernooij | Approve | ||
Review via email: mp+383481@code.launchpad.net |
Commit message
Merge the 3.1 branch.
Description of the change
Merge the 3.1 branch.
To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) : | # |
review:
Approve
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.gitignore' |
2 | --- .gitignore 2019-10-13 14:45:08 +0000 |
3 | +++ .gitignore 2020-05-06 02:56:30 +0000 |
4 | @@ -1,1 +1,12 @@ |
5 | __pycache__ |
6 | +*.pyc |
7 | +build/ |
8 | +*_pyx.so |
9 | +*_pyx.c |
10 | +*_pyx.h |
11 | +*_pyx_api.h |
12 | +*_pyx.cpython-*.so |
13 | +*_c.cpython-*.so |
14 | +*_c.so |
15 | +*_pyx.cpython-*.c |
16 | +*~ |
17 | |
18 | === added file 'breezy/__main__.py' |
19 | --- breezy/__main__.py 1970-01-01 00:00:00 +0000 |
20 | +++ breezy/__main__.py 2020-05-06 02:56:30 +0000 |
21 | @@ -0,0 +1,79 @@ |
22 | +#! /usr/bin/env python3 |
23 | + |
24 | +# Copyright (C) 2005-2013, 2016, 2017 Canonical Ltd |
25 | +# Copyright (C) 2018-2020 Breezy Developers |
26 | +# |
27 | +# This program is free software; you can redistribute it and/or modify |
28 | +# it under the terms of the GNU General Public License as published by |
29 | +# the Free Software Foundation; either version 2 of the License, or |
30 | +# (at your option) any later version. |
31 | +# |
32 | +# This program is distributed in the hope that it will be useful, |
33 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
34 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
35 | +# GNU General Public License for more details. |
36 | +# |
37 | +# You should have received a copy of the GNU General Public License |
38 | +# along with this program; if not, write to the Free Software |
39 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
40 | + |
41 | +from __future__ import absolute_import |
42 | + |
43 | +"""Breezy -- a free distributed version-control tool""" |
44 | + |
45 | +import os |
46 | +import sys |
47 | +import warnings |
48 | + |
49 | + |
50 | +profiling = False |
51 | +if '--profile-imports' in sys.argv: |
52 | + import profile_imports |
53 | + profile_imports.install() |
54 | + profiling = True |
55 | + |
56 | + |
57 | +if os.name == "posix": |
58 | + import locale |
59 | + try: |
60 | + locale.setlocale(locale.LC_ALL, '') |
61 | + except locale.Error as e: |
62 | + sys.stderr.write( |
63 | + 'brz: warning: %s\n' |
64 | + ' bzr could not set the application locale.\n' |
65 | + ' Although this should be no problem for bzr itself, it might\n' |
66 | + ' cause problems with some plugins. To investigate the issue,\n' |
67 | + ' look at the output of the locale(1p) tool.\n' % e) |
68 | + # Use better default than ascii with posix filesystems that deal in bytes |
69 | + # natively even when the C locale or no locale at all is given. Note that |
70 | + # we need an immortal string for the hack, hence the lack of a hyphen. |
71 | + sys._brz_default_fs_enc = "utf8" |
72 | + |
73 | + |
74 | +def main(): |
75 | + import breezy.breakin |
76 | + breezy.breakin.hook_debugger_to_signal() |
77 | + |
78 | + import breezy.commands |
79 | + import breezy.trace |
80 | + |
81 | + with breezy.initialize(): |
82 | + exit_val = breezy.commands.main() |
83 | + if profiling: |
84 | + profile_imports.log_stack_info(sys.stderr) |
85 | + |
86 | + # By this point we really have completed everything we want to do, and |
87 | + # there's no point doing any additional cleanup. Abruptly exiting here |
88 | + # stops any background threads getting into trouble as code is unloaded, |
89 | + # and it may also be slightly faster, through avoiding gc of objects that |
90 | + # are just about to be discarded anyhow. This does mean that atexit hooks |
91 | + # won't run but we don't use them. Also file buffers won't be flushed, |
92 | + # but our policy is to always close files from a finally block. -- mbp 20070215 |
93 | + exitfunc = getattr(sys, "exitfunc", None) |
94 | + if exitfunc is not None: |
95 | + exitfunc() |
96 | + os._exit(exit_val) |
97 | + |
98 | + |
99 | +if __name__ == '__main__': |
100 | + main() |
101 | |
102 | === modified file 'breezy/branch.py' |
103 | --- breezy/branch.py 2020-02-21 03:58:42 +0000 |
104 | +++ breezy/branch.py 2020-05-06 02:56:30 +0000 |
105 | @@ -1188,8 +1188,8 @@ |
106 | if revno < 1 or revno > self.revno(): |
107 | raise errors.InvalidRevisionNumber(revno) |
108 | |
109 | - def clone(self, to_controldir, revision_id=None, repository_policy=None, |
110 | - tag_selector=None): |
111 | + def clone(self, to_controldir, revision_id=None, name=None, |
112 | + repository_policy=None, tag_selector=None): |
113 | """Clone this branch into to_controldir preserving all semantic values. |
114 | |
115 | Most API users will want 'create_clone_on_transport', which creates a |
116 | @@ -1198,7 +1198,7 @@ |
117 | revision_id: if not None, the revision history in the new branch will |
118 | be truncated to end with revision_id. |
119 | """ |
120 | - result = to_controldir.create_branch() |
121 | + result = to_controldir.create_branch(name=name) |
122 | with self.lock_read(), result.lock_write(): |
123 | if repository_policy is not None: |
124 | repository_policy.configure_branch(result) |
125 | |
126 | === modified file 'breezy/builtins.py' |
127 | --- breezy/builtins.py 2020-02-18 01:57:45 +0000 |
128 | +++ breezy/builtins.py 2020-05-06 02:56:30 +0000 |
129 | @@ -1434,6 +1434,7 @@ |
130 | parameter, as in "branch foo/bar -r 5". |
131 | """ |
132 | |
133 | + aliase = ['sprout'] |
134 | _see_also = ['checkout'] |
135 | takes_args = ['from_location', 'to_location?'] |
136 | takes_options = ['revision', |
137 | @@ -1461,15 +1462,17 @@ |
138 | help="Bind new branch to from location."), |
139 | Option('no-recurse-nested', |
140 | help='Do not recursively check out nested trees.'), |
141 | + Option('colocated-branch', short_name='b', |
142 | + type=str, help='Name of colocated branch to sprout.'), |
143 | ] |
144 | |
145 | def run(self, from_location, to_location=None, revision=None, |
146 | hardlink=False, stacked=False, standalone=False, no_tree=False, |
147 | use_existing_dir=False, switch=False, bind=False, |
148 | - files_from=None, no_recurse_nested=False): |
149 | + files_from=None, no_recurse_nested=False, colocated_branch=None): |
150 | from breezy import switch as _mod_switch |
151 | accelerator_tree, br_from = controldir.ControlDir.open_tree_or_branch( |
152 | - from_location) |
153 | + from_location, name=colocated_branch) |
154 | if no_recurse_nested: |
155 | recurse = 'none' |
156 | else: |
157 | @@ -1695,6 +1698,38 @@ |
158 | accelerator_tree, hardlink) |
159 | |
160 | |
161 | +class cmd_clone(Command): |
162 | + __doc__ = """Clone a control directory. |
163 | + """ |
164 | + |
165 | + takes_args = ['from_location', 'to_location?'] |
166 | + takes_options = ['revision', |
167 | + Option('no-recurse-nested', |
168 | + help='Do not recursively check out nested trees.'), |
169 | + ] |
170 | + |
171 | + def run(self, from_location, to_location=None, revision=None, no_recurse_nested=False): |
172 | + accelerator_tree, br_from = controldir.ControlDir.open_tree_or_branch( |
173 | + from_location) |
174 | + if no_recurse_nested: |
175 | + recurse = 'none' |
176 | + else: |
177 | + recurse = 'down' |
178 | + revision = _get_one_revision('branch', revision) |
179 | + self.enter_context(br_from.lock_read()) |
180 | + if revision is not None: |
181 | + revision_id = revision.as_revision_id(br_from) |
182 | + else: |
183 | + # FIXME - wt.last_revision, fallback to branch, fall back to |
184 | + # None or perhaps NULL_REVISION to mean copy nothing |
185 | + # RBC 20060209 |
186 | + revision_id = br_from.last_revision() |
187 | + if to_location is None: |
188 | + to_location = urlutils.derive_to_location(from_location) |
189 | + target_controldir = br_from.controldir.clone(to_location, revision_id=revision_id) |
190 | + note(gettext('Created new control directory.')) |
191 | + |
192 | + |
193 | class cmd_renames(Command): |
194 | __doc__ = """Show list of renamed files. |
195 | """ |
196 | |
197 | === modified file 'breezy/bzr/branch.py' |
198 | --- breezy/bzr/branch.py 2020-02-21 03:58:42 +0000 |
199 | +++ breezy/bzr/branch.py 2020-05-06 02:56:30 +0000 |
200 | @@ -1023,10 +1023,10 @@ |
201 | |
202 | def _make_reference_clone_function(format, a_branch): |
203 | """Create a clone() routine for a branch dynamically.""" |
204 | - def clone(to_bzrdir, revision_id=None, |
205 | - repository_policy=None, tag_selector=None): |
206 | + def clone(to_bzrdir, revision_id=None, repository_policy=None, name=None, |
207 | + tag_selector=None): |
208 | """See Branch.clone().""" |
209 | - return format.initialize(to_bzrdir, target_branch=a_branch) |
210 | + return format.initialize(to_bzrdir, target_branch=a_branch, name=name) |
211 | # cannot obey revision_id limits when cloning a reference ... |
212 | # FIXME RBC 20060210 either nuke revision_id for clone, or |
213 | # emit some sort of warning/error to the caller ?! |
214 | |
215 | === modified file 'breezy/bzr/bzrdir.py' |
216 | --- breezy/bzr/bzrdir.py 2020-02-21 03:58:42 +0000 |
217 | +++ breezy/bzr/bzrdir.py 2020-05-06 02:56:30 +0000 |
218 | @@ -172,17 +172,18 @@ |
219 | local_repo = self.find_repository() |
220 | except errors.NoRepositoryPresent: |
221 | local_repo = None |
222 | + local_branches = self.get_branches() |
223 | try: |
224 | - local_branch = self.open_branch() |
225 | - except errors.NotBranchError: |
226 | - local_branch = None |
227 | + local_active_branch = local_branches[''] |
228 | + except KeyError: |
229 | + pass |
230 | else: |
231 | # enable fallbacks when branch is not a branch reference |
232 | - if local_branch.repository.has_same_location(local_repo): |
233 | - local_repo = local_branch.repository |
234 | + if local_active_branch.repository.has_same_location(local_repo): |
235 | + local_repo = local_active_branch.repository |
236 | if preserve_stacking: |
237 | try: |
238 | - stacked_on = local_branch.get_stacked_on_url() |
239 | + stacked_on = local_active_branch.get_stacked_on_url() |
240 | except (_mod_branch.UnstackableBranchFormat, |
241 | errors.UnstackableRepositoryFormat, |
242 | errors.NotStacked): |
243 | @@ -231,11 +232,11 @@ |
244 | # 1 if there is a branch present |
245 | # make sure its content is available in the target repository |
246 | # clone it. |
247 | - if local_branch is not None: |
248 | + for name, local_branch in local_branches.items(): |
249 | local_branch.clone( |
250 | - result, revision_id=revision_id, |
251 | + result, revision_id=(None if name != '' else revision_id), |
252 | repository_policy=repository_policy, |
253 | - tag_selector=tag_selector) |
254 | + name=name, tag_selector=tag_selector) |
255 | try: |
256 | # Cheaper to check if the target is not local, than to try making |
257 | # the tree and fail. |
258 | @@ -1014,6 +1015,18 @@ |
259 | pass |
260 | return self.transport.clone('checkout') |
261 | |
262 | + def branch_names(self): |
263 | + """See ControlDir.branch_names.""" |
264 | + ret = [] |
265 | + try: |
266 | + self.get_branch_reference() |
267 | + except errors.NotBranchError: |
268 | + pass |
269 | + else: |
270 | + ret.append("") |
271 | + ret.extend(self._read_branch_list()) |
272 | + return ret |
273 | + |
274 | def get_branches(self): |
275 | """See ControlDir.get_branches.""" |
276 | ret = {} |
277 | @@ -1312,11 +1325,13 @@ |
278 | remote_dir_format = RemoteBzrDirFormat() |
279 | remote_dir_format._network_name = self.network_name() |
280 | self._supply_sub_formats_to(remote_dir_format) |
281 | - return remote_dir_format.initialize_on_transport_ex(transport, |
282 | - use_existing_dir=use_existing_dir, create_prefix=create_prefix, |
283 | - force_new_repo=force_new_repo, stacked_on=stacked_on, |
284 | - stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name, |
285 | - make_working_trees=make_working_trees, shared_repo=shared_repo) |
286 | + return remote_dir_format.initialize_on_transport_ex( |
287 | + transport, use_existing_dir=use_existing_dir, |
288 | + create_prefix=create_prefix, force_new_repo=force_new_repo, |
289 | + stacked_on=stacked_on, stack_on_pwd=stack_on_pwd, |
290 | + repo_format_name=repo_format_name, |
291 | + make_working_trees=make_working_trees, |
292 | + shared_repo=shared_repo) |
293 | # XXX: Refactor the create_prefix/no_create_prefix code into a |
294 | # common helper function |
295 | # The destination may not exist - if so make it according to policy. |
296 | |
297 | === modified file 'breezy/bzr/remote.py' |
298 | --- breezy/bzr/remote.py 2020-02-21 03:58:42 +0000 |
299 | +++ breezy/bzr/remote.py 2020-05-06 02:56:30 +0000 |
300 | @@ -698,6 +698,23 @@ |
301 | b = self.open_branch(name=name) |
302 | return b._format |
303 | |
304 | + def branch_names(self): |
305 | + path = self._path_for_remote_call(self._client) |
306 | + try: |
307 | + response, handler = self._call_expecting_body( |
308 | + b'BzrDir.get_branches', path) |
309 | + except errors.UnknownSmartMethod: |
310 | + self._ensure_real() |
311 | + return self._real_bzrdir.branch_names() |
312 | + if response[0] != b"success": |
313 | + raise errors.UnexpectedSmartServerResponse(response) |
314 | + body = bencode.bdecode(handler.read_body_bytes()) |
315 | + ret = [] |
316 | + for name, value in body.items(): |
317 | + name = name.decode('utf-8') |
318 | + ret.append(name) |
319 | + return ret |
320 | + |
321 | def get_branches(self, possible_transports=None, ignore_fallbacks=False): |
322 | path = self._path_for_remote_call(self._client) |
323 | try: |
324 | @@ -712,9 +729,10 @@ |
325 | ret = {} |
326 | for name, value in body.items(): |
327 | name = name.decode('utf-8') |
328 | - ret[name] = self._open_branch(name, value[0], value[1], |
329 | - possible_transports=possible_transports, |
330 | - ignore_fallbacks=ignore_fallbacks) |
331 | + ret[name] = self._open_branch( |
332 | + name, value[0].decode('ascii'), value[1], |
333 | + possible_transports=possible_transports, |
334 | + ignore_fallbacks=ignore_fallbacks) |
335 | return ret |
336 | |
337 | def set_branch_reference(self, target_branch, name=None): |
338 | @@ -783,8 +801,9 @@ |
339 | if kind == 'ref': |
340 | # a branch reference, use the existing BranchReference logic. |
341 | format = BranchReferenceFormat() |
342 | + ref_loc = urlutils.join(self.user_url, location_or_format.decode('utf-8')) |
343 | return format.open(self, name=name, _found=True, |
344 | - location=location_or_format.decode('utf-8'), |
345 | + location=ref_loc, |
346 | ignore_fallbacks=ignore_fallbacks, |
347 | possible_transports=possible_transports) |
348 | branch_format_name = location_or_format |
349 | |
350 | === modified file 'breezy/bzr/smart/bzrdir.py' |
351 | --- breezy/bzr/smart/bzrdir.py 2020-02-18 01:57:45 +0000 |
352 | +++ breezy/bzr/smart/bzrdir.py 2020-05-06 02:56:30 +0000 |
353 | @@ -440,12 +440,19 @@ |
354 | The body is a bencoded dictionary, with values similar to the return |
355 | value of the open branch request. |
356 | """ |
357 | - branches = self._bzrdir.get_branches() |
358 | + branch_names = self._bzrdir.branch_names() |
359 | ret = {} |
360 | - for name, b in branches.items(): |
361 | + for name in branch_names: |
362 | if name is None: |
363 | name = b"" |
364 | - ret[name.encode('utf-8')] = (b"branch", b._format.network_name()) |
365 | + branch_ref = self._bzrdir.get_branch_reference(name=name) |
366 | + if branch_ref is not None: |
367 | + branch_ref = urlutils.relative_url(self._bzrdir.user_url, branch_ref) |
368 | + value = (b"ref", branch_ref.encode('utf-8')) |
369 | + else: |
370 | + b = self._bzrdir.open_branch(name=name, ignore_fallbacks=True) |
371 | + value = (b"branch", b._format.network_name()) |
372 | + ret[name.encode('utf-8')] = value |
373 | return SuccessfulSmartServerResponse( |
374 | (b"success", ), bencode.bencode(ret)) |
375 | |
376 | |
377 | === modified file 'breezy/config.py' |
378 | --- breezy/config.py 2020-02-18 01:57:45 +0000 |
379 | +++ breezy/config.py 2020-05-06 02:56:30 +0000 |
380 | @@ -530,12 +530,12 @@ |
381 | |
382 | Something similar to 'Martin Pool <mbp@sourcefrog.net>' |
383 | |
384 | - $BRZ_EMAIL can be set to override this, then |
385 | + $BRZ_EMAIL or $BZR_EMAIL can be set to override this, then |
386 | the concrete policy type is checked, and finally |
387 | $EMAIL is examined. |
388 | If no username can be found, NoWhoami exception is raised. |
389 | """ |
390 | - v = os.environ.get('BRZ_EMAIL') |
391 | + v = os.environ.get('BRZ_EMAIL') or os.environ.get('BZR_EMAIL') |
392 | if v: |
393 | return v |
394 | v = self._get_user_id() |
395 | @@ -2514,7 +2514,7 @@ |
396 | Option('editor', |
397 | help='The command called to launch an editor to enter a message.')) |
398 | option_registry.register( |
399 | - Option('email', override_from_env=['BRZ_EMAIL'], |
400 | + Option('email', override_from_env=['BRZ_EMAIL', 'BZR_EMAIL'], |
401 | default=bedding.default_email, help='The users identity')) |
402 | option_registry.register( |
403 | Option('gpg_signing_key', |
404 | |
405 | === modified file 'breezy/controldir.py' |
406 | --- breezy/controldir.py 2020-02-21 03:58:42 +0000 |
407 | +++ breezy/controldir.py 2020-05-06 02:56:30 +0000 |
408 | @@ -127,6 +127,18 @@ |
409 | """ |
410 | return list(self.get_branches().values()) |
411 | |
412 | + def branch_names(self): |
413 | + """List all branch names in this control directory. |
414 | + |
415 | + :return: List of branch names |
416 | + """ |
417 | + try: |
418 | + self.get_branch_reference() |
419 | + except (errors.NotBranchError, errors.NoRepositoryPresent): |
420 | + return [] |
421 | + else: |
422 | + return [""] |
423 | + |
424 | def get_branches(self): |
425 | """Get all branches in this control directory, as a dictionary. |
426 | |
427 | @@ -794,7 +806,7 @@ |
428 | a_transport = new_t |
429 | |
430 | @classmethod |
431 | - def open_tree_or_branch(klass, location): |
432 | + def open_tree_or_branch(klass, location, name=None): |
433 | """Return the branch and working tree at a location. |
434 | |
435 | If there is no tree at the location, tree will be None. |
436 | @@ -803,7 +815,7 @@ |
437 | :return: (tree, branch) |
438 | """ |
439 | controldir = klass.open(location) |
440 | - return controldir._get_tree_branch() |
441 | + return controldir._get_tree_branch(name=name) |
442 | |
443 | @classmethod |
444 | def open_containing_tree_or_branch(klass, location, |
445 | |
446 | === modified file 'breezy/git/__init__.py' |
447 | --- breezy/git/__init__.py 2020-02-18 01:57:45 +0000 |
448 | +++ breezy/git/__init__.py 2020-05-06 02:56:30 +0000 |
449 | @@ -147,7 +147,7 @@ |
450 | def is_github_url(url): |
451 | (scheme, user, password, host, port, |
452 | path) = urlutils.parse_url(url) |
453 | - return host == "github.com" |
454 | + return host in ("github.com", "gopkg.in") |
455 | |
456 | |
457 | class RemoteGitProber(Prober): |
458 | |
459 | === modified file 'breezy/git/branch.py' |
460 | --- breezy/git/branch.py 2020-02-21 03:58:42 +0000 |
461 | +++ breezy/git/branch.py 2020-05-06 02:56:30 +0000 |
462 | @@ -1198,7 +1198,7 @@ |
463 | return refs |
464 | self.target.repository.send_pack( |
465 | get_changed_refs, |
466 | - self.source.repository._git.object_store.generate_pack_data) |
467 | + self.source.repository._git.generate_pack_data) |
468 | return result |
469 | |
470 | |
471 | |
472 | === modified file 'breezy/git/dir.py' |
473 | --- breezy/git/dir.py 2020-02-21 03:58:42 +0000 |
474 | +++ breezy/git/dir.py 2020-05-06 02:56:30 +0000 |
475 | @@ -223,6 +223,7 @@ |
476 | """See ControlDir.clone_on_transport.""" |
477 | from ..repository import InterRepository |
478 | from .mapping import default_mapping |
479 | + from ..transport.local import LocalTransport |
480 | if stacked_on is not None: |
481 | raise _mod_branch.UnstackableBranchFormat( |
482 | self._format, self.user_url) |
483 | @@ -248,18 +249,19 @@ |
484 | mapping=default_mapping) |
485 | for name, val in refs.items(): |
486 | target_git_repo.refs[name] = val |
487 | - result_dir = self.__class__(transport, target_git_repo, format) |
488 | + result_dir = LocalGitDir(transport, target_git_repo, format) |
489 | if revision_id is not None: |
490 | result_dir.open_branch().set_last_revision(revision_id) |
491 | - try: |
492 | - # Cheaper to check if the target is not local, than to try making |
493 | - # the tree and fail. |
494 | - result_dir.root_transport.local_abspath('.') |
495 | + if not no_tree and isinstance(result_dir.root_transport, LocalTransport): |
496 | if result_dir.open_repository().make_working_trees(): |
497 | - self.open_workingtree().clone( |
498 | - result_dir, revision_id=revision_id) |
499 | - except (brz_errors.NoWorkingTree, brz_errors.NotLocalUrl): |
500 | - pass |
501 | + try: |
502 | + local_wt = self.open_workingtree() |
503 | + except brz_errors.NoWorkingTree: |
504 | + pass |
505 | + except brz_errors.NotLocalUrl: |
506 | + result_dir.create_workingtree(revision_id=revision_id) |
507 | + else: |
508 | + local_wt.clone(result_dir, revision_id=revision_id) |
509 | |
510 | return result_dir |
511 | |
512 | @@ -294,6 +296,20 @@ |
513 | """ |
514 | return UseExistingRepository(self.find_repository()) |
515 | |
516 | + def branch_names(self): |
517 | + from .refs import ref_to_branch_name |
518 | + ret = [] |
519 | + for ref in self.get_refs_container().keys(): |
520 | + try: |
521 | + branch_name = ref_to_branch_name(ref) |
522 | + except UnicodeDecodeError: |
523 | + trace.warning("Ignoring branch %r with unicode error ref", ref) |
524 | + continue |
525 | + except ValueError: |
526 | + continue |
527 | + ret.append(branch_name) |
528 | + return ret |
529 | + |
530 | def get_branches(self): |
531 | from .refs import ref_to_branch_name |
532 | ret = {} |
533 | |
534 | === modified file 'breezy/git/mapping.py' |
535 | --- breezy/git/mapping.py 2020-02-18 01:57:45 +0000 |
536 | +++ breezy/git/mapping.py 2020-05-06 02:56:30 +0000 |
537 | @@ -324,7 +324,9 @@ |
538 | commit.author_timezone = commit.commit_timezone |
539 | if u'git-gpg-signature' in rev.properties: |
540 | commit.gpgsig = rev.properties[u'git-gpg-signature'].encode( |
541 | - 'ascii') |
542 | + 'utf-8') |
543 | + if u'git-gpg-signature-b64' in rev.properties: |
544 | + commit.gpgsig = base64.b64decode(rev.properties[u'git-gpg-signature-b64']) |
545 | commit.message = self._encode_commit_message(rev, rev.message, |
546 | encoding) |
547 | if not isinstance(commit.message, bytes): |
548 | @@ -337,7 +339,8 @@ |
549 | mapping_properties = set( |
550 | [u'author', u'author-timezone', u'author-timezone-neg-utc', |
551 | u'commit-timezone-neg-utc', u'git-implicit-encoding', |
552 | - u'git-gpg-signature', u'git-explicit-encoding', |
553 | + u'git-gpg-signature', u'git-gpg-signature-b64', |
554 | + u'git-explicit-encoding', |
555 | u'author-timestamp', u'file-modes']) |
556 | for k, v in rev.properties.items(): |
557 | if k not in mapping_properties: |
558 | @@ -419,8 +422,12 @@ |
559 | if commit._commit_timezone_neg_utc: |
560 | rev.properties[u'commit-timezone-neg-utc'] = "" |
561 | if commit.gpgsig: |
562 | - rev.properties[u'git-gpg-signature'] = commit.gpgsig.decode( |
563 | - 'ascii') |
564 | + try: |
565 | + rev.properties[u'git-gpg-signature'] = commit.gpgsig.decode( |
566 | + 'utf-8') |
567 | + except UnicodeDecodeError: |
568 | + rev.properties[u'git-gpg-signature-b64'] = base64.b64encode( |
569 | + commit.gpgsig) |
570 | if commit.mergetag: |
571 | for i, tag in enumerate(commit.mergetag): |
572 | rev.properties[u'git-mergetag-%d' % i] = tag.as_raw_string() |
573 | |
574 | === modified file 'breezy/git/object_store.py' |
575 | --- breezy/git/object_store.py 2020-02-18 01:57:45 +0000 |
576 | +++ breezy/git/object_store.py 2020-05-06 02:56:30 +0000 |
577 | @@ -132,7 +132,7 @@ |
578 | self._cache[tree.get_revision_id()] = tree |
579 | |
580 | |
581 | -def _find_missing_bzr_revids(graph, want, have): |
582 | +def _find_missing_bzr_revids(graph, want, have, shallow=None): |
583 | """Find the revisions that have to be pushed. |
584 | |
585 | :param get_parent_map: Function that returns the parents for a sequence |
586 | @@ -142,13 +142,17 @@ |
587 | :return: Set of revisions to fetch |
588 | """ |
589 | handled = set(have) |
590 | + if shallow: |
591 | + # Shallows themselves still need to be fetched, but let's exclude their |
592 | + # parents. |
593 | + for ps in graph.get_parent_map(shallow).values(): |
594 | + handled.update(ps) |
595 | + handled.add(NULL_REVISION) |
596 | todo = set() |
597 | for rev in want: |
598 | extra_todo = graph.find_unique_ancestors(rev, handled) |
599 | todo.update(extra_todo) |
600 | handled.update(extra_todo) |
601 | - if NULL_REVISION in todo: |
602 | - todo.remove(NULL_REVISION) |
603 | return todo |
604 | |
605 | |
606 | @@ -755,13 +759,15 @@ |
607 | else: |
608 | raise KeyError(sha) |
609 | |
610 | - def generate_lossy_pack_data(self, have, want, progress=None, |
611 | + def generate_lossy_pack_data(self, have, want, shallow=None, |
612 | + progress=None, |
613 | get_tagged=None, ofs_delta=False): |
614 | return pack_objects_to_data( |
615 | - self.generate_pack_contents(have, want, progress, get_tagged, |
616 | + self.generate_pack_contents(have, want, progress=progress, |
617 | + shallow=shallow, get_tagged=get_tagged, |
618 | lossy=True)) |
619 | |
620 | - def generate_pack_contents(self, have, want, progress=None, |
621 | + def generate_pack_contents(self, have, want, shallow=None, progress=None, |
622 | ofs_delta=False, get_tagged=None, lossy=False): |
623 | """Iterate over the contents of a pack file. |
624 | |
625 | @@ -790,9 +796,18 @@ |
626 | pending.add(type_data[0]) |
627 | except KeyError: |
628 | pass |
629 | + shallows = set() |
630 | + for commit_sha in shallow or set(): |
631 | + try: |
632 | + for (type, type_data) in ret[commit_sha]: |
633 | + if type != "commit": |
634 | + raise AssertionError("Type was %s, not commit" % type) |
635 | + shallows.add(type_data[0]) |
636 | + except KeyError: |
637 | + pass |
638 | |
639 | graph = self.repository.get_graph() |
640 | - todo = _find_missing_bzr_revids(graph, pending, processed) |
641 | + todo = _find_missing_bzr_revids(graph, pending, processed, shallow) |
642 | ret = PackTupleIterable(self) |
643 | with ui.ui_factory.nested_progress_bar() as pb: |
644 | for i, revid in enumerate(graph.iter_topo_order(todo)): |
645 | |
646 | === modified file 'breezy/git/remote.py' |
647 | --- breezy/git/remote.py 2020-03-07 14:46:08 +0000 |
648 | +++ breezy/git/remote.py 2020-05-06 02:56:30 +0000 |
649 | @@ -606,10 +606,24 @@ |
650 | ret[tag_name_to_ref(tagname)] = new_sha |
651 | return ret |
652 | with source_store.lock_read(): |
653 | - if lossy: |
654 | - generate_pack_data = source_store.generate_lossy_pack_data |
655 | - else: |
656 | - generate_pack_data = source_store.generate_pack_data |
657 | + def generate_pack_data(have, want, progress=None, |
658 | + ofs_delta=True): |
659 | + git_repo = getattr(source.repository, '_git', None) |
660 | + if git_repo: |
661 | + shallow = git_repo.get_shallow() |
662 | + else: |
663 | + shallow = None |
664 | + if lossy: |
665 | + return source_store.generate_lossy_pack_data( |
666 | + have, want, shallow=shallow, |
667 | + progress=progress, ofs_delta=ofs_delta) |
668 | + elif shallow: |
669 | + return source_store.generate_pack_data( |
670 | + have, want, shallow=shallow, |
671 | + progress=progress, ofs_delta=ofs_delta) |
672 | + else: |
673 | + return source_store.generate_pack_data( |
674 | + have, want, progress=progress, ofs_delta=ofs_delta) |
675 | new_refs = self.send_pack(get_changed_refs, generate_pack_data) |
676 | push_result.new_revid = repo.lookup_foreign_revision_id( |
677 | new_refs[actual_refname]) |
678 | @@ -712,7 +726,7 @@ |
679 | raise NotGitRepository() |
680 | elif response.status != 200: |
681 | raise GitProtocolError("unexpected http resp %d for %s" % |
682 | - (response.code, url)) |
683 | + (response.status, url)) |
684 | |
685 | # TODO: Optimization available by adding `preload_content=False` to the |
686 | # request and just passing the `read` method on instead of going via |
687 | |
688 | === modified file 'breezy/git/server.py' |
689 | --- breezy/git/server.py 2020-02-18 01:57:45 +0000 |
690 | +++ breezy/git/server.py 2020-05-06 02:56:30 +0000 |
691 | @@ -90,12 +90,19 @@ |
692 | have = self.object_store.find_common_revisions(graph_walker) |
693 | if wants is None: |
694 | return |
695 | + shallows = getattr(graph_walker, 'shallow', frozenset()) |
696 | if isinstance(self.object_store, BazaarObjectStore): |
697 | return self.object_store.generate_pack_contents( |
698 | - have, wants, progress, get_tagged=get_tagged, lossy=True) |
699 | + have, wants, shallow=shallows, |
700 | + progress=progress, get_tagged=get_tagged, lossy=True) |
701 | else: |
702 | - return self.object_store.generate_pack_contents( |
703 | - have, wants, progress) |
704 | + if shallows: |
705 | + return self.object_store.generate_pack_contents( |
706 | + have, wants, shallow=shallows, progress=progress) |
707 | + else: |
708 | + return self.object_store.generate_pack_contents( |
709 | + have, wants, progress=progress) |
710 | + |
711 | |
712 | |
713 | class BzrTCPGitServer(TCPGitServer): |
714 | |
715 | === modified file 'breezy/git/tests/test_blackbox.py' |
716 | --- breezy/git/tests/test_blackbox.py 2020-03-22 01:35:14 +0000 |
717 | +++ breezy/git/tests/test_blackbox.py 2020-05-06 02:56:30 +0000 |
718 | @@ -347,7 +347,7 @@ |
719 | self.run_bzr(["git-import", "--colocated", "a", "b"]) |
720 | self.assertEqual(set([".bzr"]), set(os.listdir("b"))) |
721 | self.assertEqual(set(["abranch", "bbranch"]), |
722 | - set(ControlDir.open("b").get_branches().keys())) |
723 | + set(ControlDir.open("b").branch_names())) |
724 | |
725 | def test_git_import_incremental(self): |
726 | r = GitRepo.init("a", mkdir=True) |
727 | @@ -359,7 +359,7 @@ |
728 | self.run_bzr(["git-import", "--colocated", "a", "b"]) |
729 | self.assertEqual(set([".bzr"]), set(os.listdir("b"))) |
730 | b = ControlDir.open("b") |
731 | - self.assertEqual(["abranch"], list(b.get_branches().keys())) |
732 | + self.assertEqual(["abranch"], b.branch_names()) |
733 | |
734 | def test_git_import_tags(self): |
735 | r = GitRepo.init("a", mkdir=True) |
736 | @@ -371,7 +371,7 @@ |
737 | self.run_bzr(["git-import", "--colocated", "a", "b"]) |
738 | self.assertEqual(set([".bzr"]), set(os.listdir("b"))) |
739 | b = ControlDir.open("b") |
740 | - self.assertEqual(["abranch"], list(b.get_branches().keys())) |
741 | + self.assertEqual(["abranch"], b.branch_names()) |
742 | self.assertEqual(["atag"], |
743 | list(b.open_branch("abranch").tags.get_tag_dict().keys())) |
744 | |
745 | |
746 | === modified file 'breezy/git/tests/test_remote.py' |
747 | --- breezy/git/tests/test_remote.py 2020-02-18 01:57:45 +0000 |
748 | +++ breezy/git/tests/test_remote.py 2020-05-06 02:56:30 +0000 |
749 | @@ -565,6 +565,8 @@ |
750 | self.assertEqual( |
751 | {'': 'master', 'blah': 'blah', 'master': 'master'}, |
752 | {n: b.name for (n, b) in remote.get_branches().items()}) |
753 | + self.assertEqual( |
754 | + set(['', 'blah', 'master']), set(remote.branch_names())) |
755 | |
756 | def test_remove_tag(self): |
757 | c1 = self.remote_real.do_commit( |
758 | |
759 | === modified file 'breezy/git/transportgit.py' |
760 | --- breezy/git/transportgit.py 2020-02-18 01:57:45 +0000 |
761 | +++ breezy/git/transportgit.py 2020-05-06 02:56:30 +0000 |
762 | @@ -424,8 +424,10 @@ |
763 | _mod_transport.get_transport_from_path(commondir) |
764 | else: |
765 | self._commontransport = self._controltransport |
766 | - object_store = TransportObjectStore( |
767 | - self._commontransport.clone(OBJECTDIR)) |
768 | + config = self.get_config() |
769 | + object_store = TransportObjectStore.from_config( |
770 | + self._commontransport.clone(OBJECTDIR), |
771 | + config) |
772 | if refs_text is not None: |
773 | refs_container = InfoRefsContainer(BytesIO(refs_text)) |
774 | try: |
775 | @@ -512,6 +514,25 @@ |
776 | backends.extend(StackedConfig.default_backends()) |
777 | return StackedConfig(backends, writable=writable) |
778 | |
779 | + # Here for compatibility with dulwich < 0.19.17 |
780 | + def generate_pack_data(self, have, want, progress=None, ofs_delta=None): |
781 | + """Generate pack data objects for a set of wants/haves. |
782 | + |
783 | + Args: |
784 | + have: List of SHA1s of objects that should not be sent |
785 | + want: List of SHA1s of objects that should be sent |
786 | + ofs_delta: Whether OFS deltas can be included |
787 | + progress: Optional progress reporting method |
788 | + """ |
789 | + shallow = self.get_shallow() |
790 | + if shallow: |
791 | + return self.object_store.generate_pack_data( |
792 | + have, want, shallow=shallow, |
793 | + progress=progress, ofs_delta=ofs_delta) |
794 | + else: |
795 | + return self.object_store.generate_pack_data( |
796 | + have, want, progress=progress, ofs_delta=ofs_delta) |
797 | + |
798 | def __repr__(self): |
799 | return "<%s for %r>" % (self.__class__.__name__, self.transport) |
800 | |
801 | @@ -544,16 +565,38 @@ |
802 | class TransportObjectStore(PackBasedObjectStore): |
803 | """Git-style object store that exists on disk.""" |
804 | |
805 | - def __init__(self, transport): |
806 | + def __init__(self, transport, |
807 | + loose_compression_level=-1, pack_compression_level=-1): |
808 | """Open an object store. |
809 | |
810 | :param transport: Transport to open data from |
811 | """ |
812 | super(TransportObjectStore, self).__init__() |
813 | + self.pack_compression_level = pack_compression_level |
814 | + self.loose_compression_level = loose_compression_level |
815 | self.transport = transport |
816 | self.pack_transport = self.transport.clone(PACKDIR) |
817 | self._alternates = None |
818 | |
819 | + @classmethod |
820 | + def from_config(cls, path, config): |
821 | + try: |
822 | + default_compression_level = int(config.get( |
823 | + (b'core', ), b'compression').decode()) |
824 | + except KeyError: |
825 | + default_compression_level = -1 |
826 | + try: |
827 | + loose_compression_level = int(config.get( |
828 | + (b'core', ), b'looseCompression').decode()) |
829 | + except KeyError: |
830 | + loose_compression_level = default_compression_level |
831 | + try: |
832 | + pack_compression_level = int(config.get( |
833 | + (b'core', ), 'packCompression').decode()) |
834 | + except KeyError: |
835 | + pack_compression_level = default_compression_level |
836 | + return cls(path, loose_compression_level, pack_compression_level) |
837 | + |
838 | def __eq__(self, other): |
839 | if not isinstance(other, TransportObjectStore): |
840 | return False |
841 | @@ -686,7 +729,14 @@ |
842 | path = urlutils.quote_from_bytes(osutils.pathjoin(dir, file)) |
843 | if self.transport.has(path): |
844 | return # Already there, no need to write again |
845 | - self.transport.put_bytes(path, obj.as_legacy_object()) |
846 | + # Backwards compatibility with Dulwich < 0.20, which doesn't support |
847 | + # the compression_level parameter. |
848 | + if self.loose_compression_level not in (-1, None): |
849 | + raw_string = obj.as_legacy_object( |
850 | + compression_level=self.loose_compression_level) |
851 | + else: |
852 | + raw_string = obj.as_legacy_object() |
853 | + self.transport.put_bytes(path, raw_string) |
854 | |
855 | def move_in_pack(self, f): |
856 | """Move a specific file containing a pack into the pack directory. |
857 | |
858 | === modified file 'breezy/git/tree.py' |
859 | --- breezy/git/tree.py 2020-02-18 01:57:45 +0000 |
860 | +++ breezy/git/tree.py 2020-05-06 02:56:30 +0000 |
861 | @@ -1670,9 +1670,11 @@ |
862 | e, osutils._fs_enc) |
863 | if stat.S_ISDIR(st.st_mode): |
864 | blob = Tree() |
865 | - else: |
866 | + elif stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode): |
867 | blob = blob_from_path_and_stat( |
868 | target.abspath(e).encode(osutils._fs_enc), st) |
869 | + else: |
870 | + continue |
871 | store.add_object(blob) |
872 | np = np.encode('utf-8') |
873 | blobs[np] = (blob.id, cleanup_mode(st.st_mode)) |
874 | |
875 | === modified file 'breezy/git/workingtree.py' |
876 | --- breezy/git/workingtree.py 2020-02-21 03:58:42 +0000 |
877 | +++ breezy/git/workingtree.py 2020-05-06 02:56:30 +0000 |
878 | @@ -124,7 +124,7 @@ |
879 | index_path = os.path.join(self.basedir, relpath.decode('utf-8'), '.git', 'index') |
880 | else: |
881 | index_path = self.control_transport.local_abspath( |
882 | - posixpath.join('modules', info[1], 'index')) |
883 | + posixpath.join('modules', info[1].decode('utf-8'), 'index')) |
884 | return Index(index_path) |
885 | |
886 | def lock_read(self): |
887 | @@ -814,7 +814,11 @@ |
888 | ie = self._get_file_ie(name, path, value, dir_ids[parent]) |
889 | yield (posixpath.relpath(path, from_dir), "V", ie.kind, ie) |
890 | else: |
891 | - ie = fk_entries[kind]() |
892 | + try: |
893 | + ie = fk_entries[kind]() |
894 | + except KeyError: |
895 | + # unsupported kind |
896 | + continue |
897 | yield (posixpath.relpath(path, from_dir), |
898 | ("I" if self.is_ignored(path) else "?"), kind, ie) |
899 | |
900 | |
901 | === modified file 'breezy/info.py' |
902 | --- breezy/info.py 2020-02-18 01:57:45 +0000 |
903 | +++ breezy/info.py 2020-05-06 02:56:30 +0000 |
904 | @@ -455,7 +455,7 @@ |
905 | extra = [] |
906 | if repository.make_working_trees(): |
907 | extra.append('trees') |
908 | - if len(control.get_branches()) > 0: |
909 | + if len(control.branch_names()) > 0: |
910 | extra.append('colocated branches') |
911 | if extra: |
912 | phrase += ' with ' + " and ".join(extra) |
913 | |
914 | === modified file 'breezy/plugins/darcs/__init__.py' |
915 | --- breezy/plugins/darcs/__init__.py 2020-02-18 01:57:45 +0000 |
916 | +++ breezy/plugins/darcs/__init__.py 2020-05-06 02:56:30 +0000 |
917 | @@ -57,11 +57,11 @@ |
918 | |
919 | def open(self, transport, _found=False): |
920 | """Open this directory.""" |
921 | - raise DarcsUnsupportedError(self) |
922 | + raise DarcsUnsupportedError() |
923 | |
924 | def check_support_status(self, allow_unsupported, recommend_upgrade=True, |
925 | basedir=None): |
926 | - raise DarcsUnsupportedError(self) |
927 | + raise DarcsUnsupportedError() |
928 | |
929 | def open(self, transport): |
930 | # Raise NotBranchError if there is nothing there |
931 | @@ -73,11 +73,13 @@ |
932 | |
933 | @classmethod |
934 | def priority(klass, transport): |
935 | + if 'darcs' in transport.base: |
936 | + return 90 |
937 | return 100 |
938 | |
939 | @classmethod |
940 | def probe_transport(klass, transport): |
941 | - if transport.has('_darcs'): |
942 | + if transport.has('_darcs/format'): |
943 | return DarcsDirFormat() |
944 | raise errors.NotBranchError(path=transport.base) |
945 | |
946 | |
947 | === modified file 'breezy/plugins/fastimport/tests/test_head_tracking.py' |
948 | --- breezy/plugins/fastimport/tests/test_head_tracking.py 2020-02-18 01:57:45 +0000 |
949 | +++ breezy/plugins/fastimport/tests/test_head_tracking.py 2020-05-06 02:56:30 +0000 |
950 | @@ -24,7 +24,7 @@ |
951 | |
952 | import testtools |
953 | |
954 | -from .reftracker import ( |
955 | +from fastimport.reftracker import ( |
956 | RefTracker, |
957 | ) |
958 | |
959 | |
960 | === modified file 'breezy/plugins/propose/__init__.py' |
961 | --- breezy/plugins/propose/__init__.py 2020-02-18 01:57:45 +0000 |
962 | +++ breezy/plugins/propose/__init__.py 2020-05-06 02:56:30 +0000 |
963 | @@ -40,3 +40,11 @@ |
964 | hosters.register_lazy( |
965 | "gitlab", "breezy.plugins.propose.gitlabs", |
966 | "GitLab") |
967 | + |
968 | + |
969 | +def test_suite(): |
970 | + from unittest import TestSuite |
971 | + from .tests import test_suite |
972 | + result = TestSuite() |
973 | + result.addTest(test_suite()) |
974 | + return result |
975 | |
976 | === modified file 'breezy/plugins/propose/gitlabs.py' |
977 | --- breezy/plugins/propose/gitlabs.py 2020-03-22 01:35:14 +0000 |
978 | +++ breezy/plugins/propose/gitlabs.py 2020-05-06 02:56:30 +0000 |
979 | @@ -166,9 +166,15 @@ |
980 | raise NotGitLabUrl(url) |
981 | path = path.strip('/') |
982 | parts = path.split('/') |
983 | + if len(parts) < 2: |
984 | + raise NotMergeRequestUrl(host, url) |
985 | if parts[-2] != 'merge_requests': |
986 | raise NotMergeRequestUrl(host, url) |
987 | - return host, '/'.join(parts[:-2]), int(parts[-1]) |
988 | + if parts[-3] == '-': |
989 | + project_name = '/'.join(parts[:-3]) |
990 | + else: |
991 | + project_name = '/'.join(parts[:-2]) |
992 | + return host, project_name, int(parts[-1]) |
993 | |
994 | |
995 | class GitLabMergeProposal(MergeProposal): |
996 | @@ -280,6 +286,10 @@ |
997 | def base_url(self): |
998 | return self.transport.base |
999 | |
1000 | + @property |
1001 | + def base_hostname(self): |
1002 | + return urlutils.parse_url(self.base_url)[3] |
1003 | + |
1004 | def _api_request(self, method, path, fields=None, body=None): |
1005 | return self.transport.request( |
1006 | method, urlutils.join(self.base_url, 'api', 'v4', path), |
1007 | @@ -374,6 +384,15 @@ |
1008 | parameters['owner_id'] = urlutils.quote(owner, '') |
1009 | return self._list_paged(path, parameters, per_page=DEFAULT_PAGE_SIZE) |
1010 | |
1011 | + def _get_merge_request(self, project, merge_id): |
1012 | + path = 'projects/%s/merge_requests/%d' % (urlutils.quote(str(project), ''), merge_id) |
1013 | + response = self._api_request('GET', path) |
1014 | + if response.status == 403: |
1015 | + raise errors.PermissionDenied(response.text) |
1016 | + if response.status != 200: |
1017 | + raise errors.InvalidHttpResponse(path, response.text) |
1018 | + return json.loads(response.data) |
1019 | + |
1020 | def _list_projects(self, owner): |
1021 | path = 'users/%s/projects' % urlutils.quote(str(owner), '') |
1022 | parameters = {} |
1023 | @@ -390,7 +409,7 @@ |
1024 | def _create_mergerequest( |
1025 | self, title, source_project_id, target_project_id, |
1026 | source_branch_name, target_branch_name, description, |
1027 | - labels=None): |
1028 | + labels=None, allow_collaboration=False): |
1029 | path = 'projects/%s/merge_requests' % source_project_id |
1030 | fields = { |
1031 | 'title': title, |
1032 | @@ -398,6 +417,7 @@ |
1033 | 'target_branch': target_branch_name, |
1034 | 'target_project_id': target_project_id, |
1035 | 'description': description, |
1036 | + 'allow_collaboration': allow_collaboration, |
1037 | } |
1038 | if labels: |
1039 | fields['labels'] = labels |
1040 | @@ -484,7 +504,7 @@ |
1041 | (host, project, branch_name) = parse_gitlab_branch_url(branch) |
1042 | except NotGitLabUrl: |
1043 | return False |
1044 | - return (self.base_url == ('https://%s' % host)) |
1045 | + return self.base_hostname == host |
1046 | |
1047 | def check(self): |
1048 | response = self._api_request('GET', 'user') |
1049 | @@ -539,15 +559,15 @@ |
1050 | except NotGitLabUrl: |
1051 | raise UnsupportedHoster(url) |
1052 | except NotMergeRequestUrl as e: |
1053 | - if self.base_url == ('https://%s' % e.host): |
1054 | + if self.base_hostname == e.host: |
1055 | raise |
1056 | else: |
1057 | raise UnsupportedHoster(url) |
1058 | - if self.base_url != ('https://%s' % host): |
1059 | + if self.base_hostname != host: |
1060 | raise UnsupportedHoster(url) |
1061 | project = self._get_project(project) |
1062 | - mr = project.mergerequests.get(merge_id) |
1063 | - return GitLabMergeProposal(mr) |
1064 | + mr = self._get_merge_request(project['path_with_namespace'], merge_id) |
1065 | + return GitLabMergeProposal(self, mr) |
1066 | |
1067 | def delete_project(self, project): |
1068 | path = 'projects/%s' % urlutils.quote(str(project), '') |
1069 | @@ -623,7 +643,7 @@ |
1070 | try: |
1071 | merge_request = self.gl._create_mergerequest(**kwargs) |
1072 | except MergeRequestExists: |
1073 | - raise ProposalExists(self.source_branch.user_url) |
1074 | + raise MergeProposalExists(self.source_branch.user_url) |
1075 | return GitLabMergeProposal(self.gl, merge_request) |
1076 | |
1077 | |
1078 | |
1079 | === added directory 'breezy/plugins/propose/tests' |
1080 | === added file 'breezy/plugins/propose/tests/__init__.py' |
1081 | --- breezy/plugins/propose/tests/__init__.py 1970-01-01 00:00:00 +0000 |
1082 | +++ breezy/plugins/propose/tests/__init__.py 2020-05-06 02:56:30 +0000 |
1083 | @@ -0,0 +1,26 @@ |
1084 | +# Copyright (C) 2020 Jelmer Vernooij <jelmer@jelmer.uk> |
1085 | +# |
1086 | +# This program is free software; you can redistribute it and/or modify |
1087 | +# it under the terms of the GNU General Public License as published by |
1088 | +# the Free Software Foundation; either version 2 of the License, or |
1089 | +# (at your option) any later version. |
1090 | +# |
1091 | +# This program is distributed in the hope that it will be useful, |
1092 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1093 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1094 | +# GNU General Public License for more details. |
1095 | +# |
1096 | +# You should have received a copy of the GNU General Public License |
1097 | +# along with this program; if not, write to the Free Software |
1098 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
1099 | + |
1100 | +from unittest import TestLoader, TestSuite |
1101 | + |
1102 | + |
1103 | +def test_suite(): |
1104 | + result = TestSuite() |
1105 | + from . import test_gitlab |
1106 | + |
1107 | + loader = TestLoader() |
1108 | + result.addTests(loader.loadTestsFromModule(test_gitlab)) |
1109 | + return result |
1110 | |
1111 | === added file 'breezy/plugins/propose/tests/test_gitlab.py' |
1112 | --- breezy/plugins/propose/tests/test_gitlab.py 1970-01-01 00:00:00 +0000 |
1113 | +++ breezy/plugins/propose/tests/test_gitlab.py 2020-05-06 02:56:30 +0000 |
1114 | @@ -0,0 +1,52 @@ |
1115 | +# Copyright (C) 2020 Jelmer Vernooij <jelmer@jelmer.uk> |
1116 | +# |
1117 | +# This program is free software; you can redistribute it and/or modify |
1118 | +# it under the terms of the GNU General Public License as published by |
1119 | +# the Free Software Foundation; either version 2 of the License, or |
1120 | +# (at your option) any later version. |
1121 | +# |
1122 | +# This program is distributed in the hope that it will be useful, |
1123 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1124 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1125 | +# GNU General Public License for more details. |
1126 | +# |
1127 | +# You should have received a copy of the GNU General Public License |
1128 | +# along with this program; if not, write to the Free Software |
1129 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
1130 | + |
1131 | +from breezy.tests import TestCase |
1132 | + |
1133 | +from breezy.plugins.propose.gitlabs import ( |
1134 | + parse_gitlab_merge_request_url, |
1135 | + NotMergeRequestUrl, |
1136 | + NotGitLabUrl, |
1137 | + ) |
1138 | + |
1139 | + |
1140 | +class ParseGitLabMergeRequestUrlTests(TestCase): |
1141 | + |
1142 | + def test_invalid(self): |
1143 | + self.assertRaises( |
1144 | + NotMergeRequestUrl, parse_gitlab_merge_request_url, |
1145 | + "https://salsa.debian.org/") |
1146 | + self.assertRaises( |
1147 | + NotGitLabUrl, parse_gitlab_merge_request_url, |
1148 | + "bzr://salsa.debian.org/") |
1149 | + self.assertRaises( |
1150 | + NotGitLabUrl, parse_gitlab_merge_request_url, |
1151 | + "https:///salsa.debian.org/") |
1152 | + self.assertRaises( |
1153 | + NotMergeRequestUrl, parse_gitlab_merge_request_url, |
1154 | + "https://salsa.debian.org/jelmer/salsa") |
1155 | + |
1156 | + def test_old_style(self): |
1157 | + self.assertEqual( |
1158 | + ('salsa.debian.org', 'jelmer/salsa', 4), |
1159 | + parse_gitlab_merge_request_url( |
1160 | + 'https://salsa.debian.org/jelmer/salsa/merge_requests/4')) |
1161 | + |
1162 | + def test_new_style(self): |
1163 | + self.assertEqual( |
1164 | + ('salsa.debian.org', 'jelmer/salsa', 4), |
1165 | + parse_gitlab_merge_request_url( |
1166 | + 'https://salsa.debian.org/jelmer/salsa/-/merge_requests/4')) |
1167 | |
1168 | === modified file 'breezy/plugins/weave_fmt/bzrdir.py' |
1169 | --- breezy/plugins/weave_fmt/bzrdir.py 2020-02-21 03:58:42 +0000 |
1170 | +++ breezy/plugins/weave_fmt/bzrdir.py 2020-05-06 02:56:30 +0000 |
1171 | @@ -840,7 +840,7 @@ |
1172 | |
1173 | def get_branch_transport(self, branch_format, name=None): |
1174 | """See BzrDir.get_branch_transport().""" |
1175 | - if name is not None: |
1176 | + if name: |
1177 | raise errors.NoColocatedBranchSupport(self) |
1178 | if branch_format is None: |
1179 | return self.transport |
1180 | |
1181 | === modified file 'breezy/tests/blackbox/__init__.py' |
1182 | --- breezy/tests/blackbox/__init__.py 2019-06-15 17:06:40 +0000 |
1183 | +++ breezy/tests/blackbox/__init__.py 2020-05-06 02:56:30 +0000 |
1184 | @@ -51,6 +51,7 @@ |
1185 | 'test_check', |
1186 | 'test_checkout', |
1187 | 'test_clean_tree', |
1188 | + 'test_clone', |
1189 | 'test_command_encoding', |
1190 | 'test_commit', |
1191 | 'test_config', |
1192 | |
1193 | === modified file 'breezy/tests/blackbox/test_branch.py' |
1194 | --- breezy/tests/blackbox/test_branch.py 2020-01-19 14:49:03 +0000 |
1195 | +++ breezy/tests/blackbox/test_branch.py 2020-05-06 02:56:30 +0000 |
1196 | @@ -107,6 +107,17 @@ |
1197 | self.assertEqual('Branched 0 revisions.\n', err) |
1198 | self.assertPathExists('a') |
1199 | |
1200 | + def test_from_name(self): |
1201 | + """Branch from a colocated branch into a regular branch.""" |
1202 | + os.mkdir('b') |
1203 | + tree = self.example_branch('b/a', format='development-colo') |
1204 | + tree.controldir.create_branch(name='somecolo') |
1205 | + out, err = self.run_bzr('branch -b somecolo %s' % |
1206 | + local_path_to_url('b/a')) |
1207 | + self.assertEqual('', out) |
1208 | + self.assertEqual('Branched 0 revisions.\n', err) |
1209 | + self.assertPathExists('a') |
1210 | + |
1211 | def test_branch_broken_pack(self): |
1212 | """branching with a corrupted pack file.""" |
1213 | self.example_branch('a') |
1214 | |
1215 | === added file 'breezy/tests/blackbox/test_clone.py' |
1216 | --- breezy/tests/blackbox/test_clone.py 1970-01-01 00:00:00 +0000 |
1217 | +++ breezy/tests/blackbox/test_clone.py 2020-05-06 02:56:30 +0000 |
1218 | @@ -0,0 +1,70 @@ |
1219 | +# Copyright (C) 2006-2012, 2016 Canonical Ltd |
1220 | +# |
1221 | +# This program is free software; you can redistribute it and/or modify |
1222 | +# it under the terms of the GNU General Public License as published by |
1223 | +# the Free Software Foundation; either version 2 of the License, or |
1224 | +# (at your option) any later version. |
1225 | +# |
1226 | +# This program is distributed in the hope that it will be useful, |
1227 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1228 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1229 | +# GNU General Public License for more details. |
1230 | +# |
1231 | +# You should have received a copy of the GNU General Public License |
1232 | +# along with this program; if not, write to the Free Software |
1233 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
1234 | + |
1235 | + |
1236 | +"""Black-box tests for brz branch.""" |
1237 | + |
1238 | +import os |
1239 | + |
1240 | +from breezy import ( |
1241 | + branch, |
1242 | + controldir, |
1243 | + tests, |
1244 | + ) |
1245 | +from breezy.urlutils import local_path_to_url |
1246 | + |
1247 | + |
1248 | +class TestClone(tests.TestCaseWithTransport): |
1249 | + |
1250 | + def example_dir(self, path='.', format=None): |
1251 | + tree = self.make_branch_and_tree(path, format=format) |
1252 | + self.build_tree_contents([(path + '/hello', b'foo')]) |
1253 | + tree.add('hello') |
1254 | + tree.commit(message='setup') |
1255 | + self.build_tree_contents([(path + '/goodbye', b'baz')]) |
1256 | + tree.add('goodbye') |
1257 | + tree.commit(message='setup') |
1258 | + return tree |
1259 | + |
1260 | + def test_clone(self): |
1261 | + """Branch from one branch to another.""" |
1262 | + self.example_dir('a') |
1263 | + self.run_bzr('clone a b') |
1264 | + b = branch.Branch.open('b') |
1265 | + self.run_bzr('clone a c -r 1') |
1266 | + # previously was erroneously created by branching |
1267 | + self.assertFalse(b._transport.has('branch-name')) |
1268 | + b.controldir.open_workingtree().commit(message='foo', allow_pointless=True) |
1269 | + |
1270 | + def test_clone_no_to_location(self): |
1271 | + """The to_location is derived from the source branch name.""" |
1272 | + os.mkdir("something") |
1273 | + a = self.example_dir('something/a').branch |
1274 | + self.run_bzr('clone something/a') |
1275 | + b = branch.Branch.open('a') |
1276 | + self.assertEqual(b.last_revision_info(), a.last_revision_info()) |
1277 | + |
1278 | + def test_from_colocated(self): |
1279 | + """Branch from a colocated branch into a regular branch.""" |
1280 | + os.mkdir('b') |
1281 | + tree = self.example_dir('b/a') |
1282 | + tree.controldir.create_branch(name='somecolo') |
1283 | + out, err = self.run_bzr('clone %s' % local_path_to_url('b/a')) |
1284 | + self.assertEqual('', out) |
1285 | + self.assertEqual('Created new control directory.\n', err) |
1286 | + self.assertPathExists('a') |
1287 | + target = controldir.ControlDir.open('a') |
1288 | + self.assertEqual(['', 'somecolo'], target.branch_names()) |
1289 | |
1290 | === modified file 'breezy/tests/blackbox/test_switch.py' |
1291 | --- breezy/tests/blackbox/test_switch.py 2019-09-01 15:33:59 +0000 |
1292 | +++ breezy/tests/blackbox/test_switch.py 2020-05-06 02:56:30 +0000 |
1293 | @@ -207,7 +207,7 @@ |
1294 | self.run_bzr(['switch', '-b', 'anotherbranch']) |
1295 | self.assertEqual( |
1296 | {'', 'anotherbranch'}, |
1297 | - set(tree.branch.controldir.get_branches().keys())) |
1298 | + set(tree.branch.controldir.branch_names())) |
1299 | |
1300 | def test_switch_into_unrelated_colocated(self): |
1301 | # Create a new colocated branch from an existing non-colocated branch. |
1302 | |
1303 | === modified file 'breezy/tests/per_bzrdir/test_bzrdir.py' |
1304 | --- breezy/tests/per_bzrdir/test_bzrdir.py 2018-11-11 04:08:32 +0000 |
1305 | +++ breezy/tests/per_bzrdir/test_bzrdir.py 2020-05-06 02:56:30 +0000 |
1306 | @@ -688,4 +688,4 @@ |
1307 | target_branch = repo.controldir.create_branch(name='foo') |
1308 | repo.controldir.set_branch_reference(target_branch) |
1309 | self.assertEqual({"", 'foo'}, |
1310 | - set(repo.controldir.get_branches().keys())) |
1311 | + set(repo.controldir.branch_names())) |
1312 | |
1313 | === modified file 'breezy/tests/per_controldir/test_controldir.py' |
1314 | --- breezy/tests/per_controldir/test_controldir.py 2019-05-29 03:22:34 +0000 |
1315 | +++ breezy/tests/per_controldir/test_controldir.py 2020-05-06 02:56:30 +0000 |
1316 | @@ -199,7 +199,7 @@ |
1317 | "control directories without working tree") |
1318 | self.assertRaises(errors.NoWorkingTree, dir.open_workingtree) |
1319 | |
1320 | - def test_clone_bzrdir_repository_under_shared(self): |
1321 | + def test_clone_controldir_repository_under_shared(self): |
1322 | tree = self.make_branch_and_tree('commit_tree') |
1323 | self.build_tree( |
1324 | ['foo'], transport=tree.controldir.transport.clone('..')) |
1325 | @@ -221,7 +221,7 @@ |
1326 | self.assertNotEqual(dir.transport.base, target.transport.base) |
1327 | self.assertRaises(errors.NoRepositoryPresent, target.open_repository) |
1328 | |
1329 | - def test_clone_bzrdir_repository_branch_both_under_shared(self): |
1330 | + def test_clone_controldir_repository_branch_both_under_shared(self): |
1331 | # Create a shared repository |
1332 | try: |
1333 | shared_repo = self.make_repository('shared', shared=True) |
1334 | @@ -249,7 +249,7 @@ |
1335 | dir.create_branch() |
1336 | # Clone 'source' to 'target', also inside the shared repository. |
1337 | target = dir.clone(self.get_url('shared/target')) |
1338 | - # 'source', 'target', and the shared repo all have distinct bzrdirs. |
1339 | + # 'source', 'target', and the shared repo all have distinct controldirs. |
1340 | self.assertNotEqual(dir.transport.base, target.transport.base) |
1341 | self.assertNotEqual( |
1342 | dir.transport.base, shared_repo.controldir.transport.base) |
1343 | @@ -258,7 +258,7 @@ |
1344 | # 'commit_tree' branch. |
1345 | self.assertTrue(shared_repo.has_revision(rev1)) |
1346 | |
1347 | - def test_clone_bzrdir_repository_branch_only_source_under_shared(self): |
1348 | + def test_clone_controldir_repository_branch_only_source_under_shared(self): |
1349 | try: |
1350 | shared_repo = self.make_repository('shared', shared=True) |
1351 | except errors.IncompatibleFormat: |
1352 | @@ -291,7 +291,7 @@ |
1353 | self.assertFalse(branch.repository.make_working_trees()) |
1354 | self.assertTrue(branch.repository.is_shared()) |
1355 | |
1356 | - def test_clone_bzrdir_repository_revision(self): |
1357 | + def test_clone_controldir_repository_revision(self): |
1358 | # test for revision limiting, [smoke test, not corner case checks]. |
1359 | # make a repository with some revisions, |
1360 | # and clone it with a revision limit. |
1361 | @@ -310,7 +310,7 @@ |
1362 | dir.clone(self.get_url('target'), revision_id=rev2) |
1363 | raise TestSkipped('revision limiting not strict yet') |
1364 | |
1365 | - def test_clone_bzrdir_branch_and_repo_fixed_user_id(self): |
1366 | + def test_clone_controldir_branch_and_repo_fixed_user_id(self): |
1367 | # Bug #430868 is about an email containing '.sig' |
1368 | self.overrideEnv('BRZ_EMAIL', 'murphy@host.sighup.org') |
1369 | tree = self.make_branch_and_tree('commit_tree') |
1370 | @@ -330,7 +330,7 @@ |
1371 | tree_repo.get_signature_text(rev1), |
1372 | target.repository.get_signature_text(rev1)) |
1373 | |
1374 | - def test_clone_bzrdir_branch_and_repo_into_shared_repo(self): |
1375 | + def test_clone_controldir_branch_and_repo_into_shared_repo(self): |
1376 | # by default cloning into a shared repo uses the shared repo. |
1377 | tree = self.make_branch_and_tree('commit_tree') |
1378 | self.build_tree(['commit_tree/foo']) |
1379 | @@ -354,7 +354,7 @@ |
1380 | self.assertEqual(source.last_revision(), |
1381 | target.open_branch().last_revision()) |
1382 | |
1383 | - def test_clone_bzrdir_branch_revision(self): |
1384 | + def test_clone_controldir_branch_revision(self): |
1385 | # test for revision limiting, [smoke test, not corner case checks]. |
1386 | # make a branch with some revisions, |
1387 | # and clone it with a revision limit. |
1388 | @@ -371,6 +371,23 @@ |
1389 | target = dir.clone(self.get_url('target'), revision_id=rev1) |
1390 | self.assertEqual(rev1, target.open_branch().last_revision()) |
1391 | |
1392 | + def test_clone_controldir_with_colocated(self): |
1393 | + if not self.bzrdir_format.colocated_branches: |
1394 | + raise TestNotApplicable( |
1395 | + 'format does not supported colocated branches') |
1396 | + tree = self.make_branch_and_tree('commit_tree') |
1397 | + self.build_tree(['commit_tree/foo']) |
1398 | + tree.add('foo') |
1399 | + rev1 = tree.commit('revision 1') |
1400 | + rev2 = tree.commit('revision 2', allow_pointless=True) |
1401 | + rev3 = tree.commit('revision 2', allow_pointless=True) |
1402 | + dir = tree.branch.controldir |
1403 | + colo = dir.create_branch(name='colo') |
1404 | + colo.pull(tree.branch, stop_revision=rev1) |
1405 | + target = dir.clone(self.get_url('target'), revision_id=rev2) |
1406 | + self.assertEqual(rev2, target.open_branch().last_revision()) |
1407 | + self.assertEqual(rev1, target.open_branch(name='colo').last_revision()) |
1408 | + |
1409 | def test_clone_on_transport_preserves_repo_format(self): |
1410 | if self.bzrdir_format == controldir.format_registry.make_controldir('default'): |
1411 | format = 'knit' |
1412 | @@ -381,8 +398,8 @@ |
1413 | a_dir = breezy.branch.Branch.open_from_transport( |
1414 | self.get_transport('source')).controldir |
1415 | target_transport = self.get_transport('target') |
1416 | - target_bzrdir = a_dir.clone_on_transport(target_transport) |
1417 | - target_repo = target_bzrdir.open_repository() |
1418 | + target_controldir = a_dir.clone_on_transport(target_transport) |
1419 | + target_repo = target_controldir.open_repository() |
1420 | source_branch = breezy.branch.Branch.open( |
1421 | self.get_vfs_only_url('source')) |
1422 | if isinstance(target_repo, RemoteRepository): |
1423 | @@ -390,7 +407,7 @@ |
1424 | target_repo = target_repo._real_repository |
1425 | self.assertEqual(target_repo._format, source_branch.repository._format) |
1426 | |
1427 | - def test_clone_bzrdir_tree_revision(self): |
1428 | + def test_clone_controldir_tree_revision(self): |
1429 | # test for revision limiting, [smoke test, not corner case checks]. |
1430 | # make a tree with a revision with a last-revision |
1431 | # and clone it with a revision limit. |
1432 | @@ -406,7 +423,7 @@ |
1433 | self.skipIfNoWorkingTree(target) |
1434 | self.assertEqual([rev1], target.open_workingtree().get_parent_ids()) |
1435 | |
1436 | - def test_clone_bzrdir_into_notrees_repo(self): |
1437 | + def test_clone_controldir_into_notrees_repo(self): |
1438 | """Cloning into a no-trees repo should not create a working tree""" |
1439 | tree = self.make_branch_and_tree('source') |
1440 | self.build_tree(['source/foo']) |
1441 | @@ -514,12 +531,12 @@ |
1442 | """get_branch_reference should not mask NotBranchErrors.""" |
1443 | dir = self.make_controldir('source') |
1444 | if dir.has_branch(): |
1445 | - # this format does not support branchless bzrdirs. |
1446 | + # this format does not support branchless controldirs. |
1447 | raise TestNotApplicable("format does not support " |
1448 | "branchless control directories") |
1449 | self.assertRaises(errors.NotBranchError, dir.get_branch_reference) |
1450 | |
1451 | - def test_sprout_bzrdir_empty(self): |
1452 | + def test_sprout_controldir_empty(self): |
1453 | dir = self.make_controldir('source') |
1454 | target = dir.sprout(self.get_url('target')) |
1455 | self.assertNotEqual(dir.control_transport.base, |
1456 | @@ -529,7 +546,7 @@ |
1457 | target.open_branch() |
1458 | self.openWorkingTreeIfLocal(target) |
1459 | |
1460 | - def test_sprout_bzrdir_empty_under_shared_repo(self): |
1461 | + def test_sprout_controldir_empty_under_shared_repo(self): |
1462 | # sprouting an empty dir into a repo uses the repo |
1463 | dir = self.make_controldir('source') |
1464 | try: |
1465 | @@ -543,13 +560,13 @@ |
1466 | try: |
1467 | target.open_workingtree() |
1468 | except errors.NoWorkingTree: |
1469 | - # Some bzrdirs can never have working trees. |
1470 | + # Some controldirs can never have working trees. |
1471 | repo = target.find_repository() |
1472 | self.assertFalse(repo.controldir._format.supports_workingtrees) |
1473 | |
1474 | - def test_sprout_bzrdir_empty_under_shared_repo_force_new(self): |
1475 | + def test_sprout_controldir_empty_under_shared_repo_force_new(self): |
1476 | # the force_new_repo parameter should force use of a new repo in an empty |
1477 | - # bzrdir's sprout logic |
1478 | + # controldir's sprout logic |
1479 | dir = self.make_controldir('source') |
1480 | try: |
1481 | self.make_repository('target', shared=True) |
1482 | @@ -561,7 +578,7 @@ |
1483 | target.open_branch() |
1484 | self.openWorkingTreeIfLocal(target) |
1485 | |
1486 | - def test_sprout_bzrdir_with_repository_to_shared(self): |
1487 | + def test_sprout_controldir_with_repository_to_shared(self): |
1488 | tree = self.make_branch_and_tree('commit_tree') |
1489 | self.build_tree(['commit_tree/foo']) |
1490 | tree.add('foo') |
1491 | @@ -583,7 +600,7 @@ |
1492 | target.user_transport.base) |
1493 | self.assertTrue(shared_repo.has_revision(rev1)) |
1494 | |
1495 | - def test_sprout_bzrdir_repository_branch_both_under_shared(self): |
1496 | + def test_sprout_controldir_repository_branch_both_under_shared(self): |
1497 | try: |
1498 | shared_repo = self.make_repository('shared', shared=True) |
1499 | except errors.IncompatibleFormat: |
1500 | @@ -609,7 +626,7 @@ |
1501 | shared_repo.controldir.transport.base) |
1502 | self.assertTrue(shared_repo.has_revision(rev1)) |
1503 | |
1504 | - def test_sprout_bzrdir_repository_branch_only_source_under_shared(self): |
1505 | + def test_sprout_controldir_repository_branch_only_source_under_shared(self): |
1506 | try: |
1507 | shared_repo = self.make_repository('shared', shared=True) |
1508 | except errors.IncompatibleFormat: |
1509 | @@ -639,7 +656,7 @@ |
1510 | dir.transport.base, |
1511 | shared_repo.controldir.transport.base) |
1512 | branch = target.open_branch() |
1513 | - # The sprouted bzrdir has a branch, so only revisions referenced by |
1514 | + # The sprouted controldir has a branch, so only revisions referenced by |
1515 | # that branch are copied, rather than the whole repository. It's an |
1516 | # empty branch, so none are copied. |
1517 | self.assertEqual([], branch.repository.all_revision_ids()) |
1518 | @@ -647,7 +664,7 @@ |
1519 | self.assertTrue(branch.repository.make_working_trees()) |
1520 | self.assertFalse(branch.repository.is_shared()) |
1521 | |
1522 | - def test_sprout_bzrdir_repository_under_shared_force_new_repo(self): |
1523 | + def test_sprout_controldir_repository_under_shared_force_new_repo(self): |
1524 | tree = self.make_branch_and_tree('commit_tree') |
1525 | self.build_tree(['commit_tree/foo']) |
1526 | tree.add('foo') |
1527 | @@ -670,7 +687,7 @@ |
1528 | target.control_transport.base) |
1529 | self.assertFalse(shared_repo.has_revision(rev1)) |
1530 | |
1531 | - def test_sprout_bzrdir_repository_revision(self): |
1532 | + def test_sprout_controldir_repository_revision(self): |
1533 | # test for revision limiting, [smoke test, not corner case checks]. |
1534 | # make a repository with some revisions, |
1535 | # and sprout it with a revision limit. |
1536 | @@ -689,7 +706,7 @@ |
1537 | self.sproutOrSkip(dir, self.get_url('target'), revision_id=rev2) |
1538 | raise TestSkipped('revision limiting not strict yet') |
1539 | |
1540 | - def test_sprout_bzrdir_branch_and_repo_shared(self): |
1541 | + def test_sprout_controldir_branch_and_repo_shared(self): |
1542 | # sprouting a branch with a repo into a shared repo uses the shared |
1543 | # repo |
1544 | tree = self.make_branch_and_tree('commit_tree') |
1545 | @@ -708,7 +725,7 @@ |
1546 | dir.sprout(self.get_url('target/child')) |
1547 | self.assertTrue(shared_repo.has_revision(rev1)) |
1548 | |
1549 | - def test_sprout_bzrdir_branch_and_repo_shared_force_new_repo(self): |
1550 | + def test_sprout_controldir_branch_and_repo_shared_force_new_repo(self): |
1551 | # sprouting a branch with a repo into a shared repo uses the shared |
1552 | # repo |
1553 | tree = self.make_branch_and_tree('commit_tree') |
1554 | @@ -729,7 +746,7 @@ |
1555 | dir.control_transport.base, target.control_transport.base) |
1556 | self.assertFalse(shared_repo.has_revision(rev1)) |
1557 | |
1558 | - def test_sprout_bzrdir_branch_reference(self): |
1559 | + def test_sprout_controldir_branch_reference(self): |
1560 | # sprouting should create a repository if needed and a sprouted branch. |
1561 | referenced_branch = self.make_branch('referenced') |
1562 | dir = self.make_controldir('source') |
1563 | @@ -747,7 +764,7 @@ |
1564 | # place |
1565 | target.open_repository() |
1566 | |
1567 | - def test_sprout_bzrdir_branch_reference_shared(self): |
1568 | + def test_sprout_controldir_branch_reference_shared(self): |
1569 | # sprouting should create a repository if needed and a sprouted branch. |
1570 | referenced_tree = self.make_branch_and_tree('referenced') |
1571 | rev1 = referenced_tree.commit('1', allow_pointless=True) |
1572 | @@ -773,7 +790,7 @@ |
1573 | # and we want revision '1' in the shared repo |
1574 | self.assertTrue(shared_repo.has_revision(rev1)) |
1575 | |
1576 | - def test_sprout_bzrdir_branch_reference_shared_force_new_repo(self): |
1577 | + def test_sprout_controldir_branch_reference_shared_force_new_repo(self): |
1578 | # sprouting should create a repository if needed and a sprouted branch. |
1579 | referenced_tree = self.make_branch_and_tree('referenced') |
1580 | rev1 = referenced_tree.commit('1', allow_pointless=True) |
1581 | @@ -799,7 +816,7 @@ |
1582 | # but not the shared one |
1583 | self.assertFalse(shared_repo.has_revision(rev1)) |
1584 | |
1585 | - def test_sprout_bzrdir_branch_revision(self): |
1586 | + def test_sprout_controldir_branch_revision(self): |
1587 | # test for revision limiting, [smoke test, not corner case checks]. |
1588 | # make a repository with some revisions, |
1589 | # and sprout it with a revision limit. |
1590 | @@ -816,7 +833,7 @@ |
1591 | target = dir.sprout(self.get_url('target'), revision_id=rev1) |
1592 | self.assertEqual(rev1, target.open_branch().last_revision()) |
1593 | |
1594 | - def test_sprout_bzrdir_branch_with_tags(self): |
1595 | + def test_sprout_controldir_branch_with_tags(self): |
1596 | # when sprouting a branch all revisions named in the tags are copied |
1597 | # too. |
1598 | builder = self.make_branch_builder('source') |
1599 | @@ -835,7 +852,7 @@ |
1600 | self.assertEqual(rev2, new_branch.tags.lookup_tag('tag-a')) |
1601 | new_branch.repository.get_revision(rev2) |
1602 | |
1603 | - def test_sprout_bzrdir_branch_with_absent_tag(self): |
1604 | + def test_sprout_controldir_branch_with_absent_tag(self): |
1605 | # tags referencing absent revisions are copied (and those absent |
1606 | # revisions do not prevent the sprout.) |
1607 | builder = self.make_branch_builder('source') |
1608 | @@ -855,7 +872,7 @@ |
1609 | new_branch = target.open_branch() |
1610 | self.assertEqual(b'missing-rev', new_branch.tags.lookup_tag('tag-a')) |
1611 | |
1612 | - def test_sprout_bzrdir_passing_source_branch_with_absent_tag(self): |
1613 | + def test_sprout_controldir_passing_source_branch_with_absent_tag(self): |
1614 | # tags referencing absent revisions are copied (and those absent |
1615 | # revisions do not prevent the sprout.) |
1616 | builder = self.make_branch_builder('source') |
1617 | @@ -875,9 +892,9 @@ |
1618 | new_branch = target.open_branch() |
1619 | self.assertEqual(b'missing-rev', new_branch.tags.lookup_tag('tag-a')) |
1620 | |
1621 | - def test_sprout_bzrdir_passing_rev_not_source_branch_copies_tags(self): |
1622 | + def test_sprout_controldir_passing_rev_not_source_branch_copies_tags(self): |
1623 | # dir.sprout(..., revision_id=b'rev1') copies rev1, and all the tags of |
1624 | - # the branch at that bzrdir, the ancestry of all of those, but no other |
1625 | + # the branch at that controldir, the ancestry of all of those, but no other |
1626 | # revs (not even the tip of the source branch). |
1627 | builder = self.make_branch_builder('source') |
1628 | base_rev = builder.build_commit(message="Base") |
1629 | @@ -927,7 +944,7 @@ |
1630 | sorted([base_rev, rev_b1, rev_b2, rev_c1, rev_c2]), |
1631 | sorted(new_branch.repository.all_revision_ids())) |
1632 | |
1633 | - def test_sprout_bzrdir_tree_branch_reference(self): |
1634 | + def test_sprout_controldir_tree_branch_reference(self): |
1635 | # sprouting should create a repository if needed and a sprouted branch. |
1636 | # the tree state should not be copied. |
1637 | referenced_branch = self.make_branch('referencced') |
1638 | @@ -952,7 +969,7 @@ |
1639 | result_tree = target.open_workingtree() |
1640 | self.assertFalse(result_tree.has_filename('subdir')) |
1641 | |
1642 | - def test_sprout_bzrdir_tree_branch_reference_revision(self): |
1643 | + def test_sprout_controldir_tree_branch_reference_revision(self): |
1644 | # sprouting should create a repository if needed and a sprouted branch. |
1645 | # the tree state should not be copied but the revision changed, |
1646 | # and the likewise the new branch should be truncated too |
1647 | @@ -982,7 +999,7 @@ |
1648 | self.assertEqual([rev1], target.open_workingtree().get_parent_ids()) |
1649 | self.assertEqual(rev1, target.open_branch().last_revision()) |
1650 | |
1651 | - def test_sprout_bzrdir_tree_revision(self): |
1652 | + def test_sprout_controldir_tree_revision(self): |
1653 | # test for revision limiting, [smoke test, not corner case checks]. |
1654 | # make a tree with a revision with a last-revision |
1655 | # and sprout it with a revision limit. |
1656 | @@ -1034,13 +1051,13 @@ |
1657 | rev3 = builder.build_commit(message='Rev 3.') |
1658 | builder.finish_series() |
1659 | stack_on = builder.get_branch() |
1660 | - # Make a bzrdir with a default stacking policy to stack on that branch. |
1661 | + # Make a controldir with a default stacking policy to stack on that branch. |
1662 | config = self.make_controldir('policy-dir').get_config() |
1663 | try: |
1664 | config.set_default_stack_on(self.get_url('stack-on')) |
1665 | except errors.BzrError: |
1666 | raise TestNotApplicable('Only relevant for stackable formats.') |
1667 | - # Sprout the stacked-on branch into the bzrdir. |
1668 | + # Sprout the stacked-on branch into the controldir. |
1669 | sprouted = stack_on.controldir.sprout( |
1670 | self.get_url('policy-dir/sprouted'), revision_id=rev3) |
1671 | # Not all revisions are copied into the sprouted repository. |
1672 | @@ -1288,6 +1305,11 @@ |
1673 | repo.controldir.create_branch() |
1674 | self.assertEqual([""], list(repo.controldir.get_branches())) |
1675 | |
1676 | + def test_branch_names(self): |
1677 | + repo = self.make_repository('branch-1') |
1678 | + repo.controldir.create_branch() |
1679 | + self.assertEqual([""], repo.controldir.branch_names()) |
1680 | + |
1681 | def test_create_repository(self): |
1682 | # a bzrdir can construct a repository for itself. |
1683 | if not self.bzrdir_format.is_initializable(): |
1684 | |
1685 | === modified file 'breezy/tests/per_controldir_colo/test_supported.py' |
1686 | --- breezy/tests/per_controldir_colo/test_supported.py 2019-04-13 21:37:04 +0000 |
1687 | +++ breezy/tests/per_controldir_colo/test_supported.py 2020-05-06 02:56:30 +0000 |
1688 | @@ -184,6 +184,11 @@ |
1689 | self.assertEqual(target_branch.base, |
1690 | repo.controldir.get_branches()['foo'].base) |
1691 | |
1692 | + def test_branch_names(self): |
1693 | + repo = self.make_repository('branch-1') |
1694 | + target_branch = self.create_branch(repo.controldir, name='foo') |
1695 | + self.assertIn('foo', repo.controldir.branch_names()) |
1696 | + |
1697 | def test_branch_name_with_slash(self): |
1698 | repo = self.make_repository('branch-1') |
1699 | try: |
1700 | |
1701 | === modified file 'breezy/tests/per_workingtree/test_changes_from.py' |
1702 | --- breezy/tests/per_workingtree/test_changes_from.py 2019-06-30 12:59:05 +0000 |
1703 | +++ breezy/tests/per_workingtree/test_changes_from.py 2020-05-06 02:56:30 +0000 |
1704 | @@ -16,6 +16,8 @@ |
1705 | |
1706 | """Test Tree.changes_from() for WorkingTree specific scenarios.""" |
1707 | |
1708 | +import socket |
1709 | + |
1710 | from breezy import revision |
1711 | from breezy.tests.per_workingtree import TestCaseWithWorkingTree |
1712 | |
1713 | @@ -54,3 +56,19 @@ |
1714 | self.assertEqual([], d.renamed) |
1715 | self.assertEqual([], d.copied) |
1716 | self.assertEqual([], d.modified) |
1717 | + |
1718 | + def test_socket(self): |
1719 | + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
1720 | + s.bind('tree/socketpath') |
1721 | + s.listen(1) |
1722 | + empty_tree = self.tree.branch.repository.revision_tree( |
1723 | + revision.NULL_REVISION) |
1724 | + d = self.tree.changes_from( |
1725 | + empty_tree, specific_files=['socketpath'], |
1726 | + want_unversioned=True) |
1727 | + self.assertEqual([], d.added) |
1728 | + self.assertEqual([], d.removed) |
1729 | + self.assertEqual([], d.renamed) |
1730 | + self.assertEqual([], d.copied) |
1731 | + self.assertEqual([], d.modified) |
1732 | + self.assertIn([x.path[1] for x in d.unversioned], [['socketpath'], []]) |
1733 | |
1734 | === modified file 'breezy/tests/test_bzrdir.py' |
1735 | --- breezy/tests/test_bzrdir.py 2020-01-19 03:22:04 +0000 |
1736 | +++ breezy/tests/test_bzrdir.py 2020-05-06 02:56:30 +0000 |
1737 | @@ -821,6 +821,20 @@ |
1738 | self.assertEqual(os.path.realpath('topdir/foo'), |
1739 | self.local_branch_path(branch)) |
1740 | |
1741 | + def test_open_tree_or_branch_named(self): |
1742 | + tree = self.make_branch_and_tree('topdir') |
1743 | + self.assertRaises( |
1744 | + NotBranchError, |
1745 | + bzrdir.BzrDir.open_tree_or_branch, 'topdir', name='missing') |
1746 | + tree.branch.controldir.create_branch('named') |
1747 | + tree, branch = bzrdir.BzrDir.open_tree_or_branch('topdir', name='named') |
1748 | + self.assertEqual(os.path.realpath('topdir'), |
1749 | + os.path.realpath(tree.basedir)) |
1750 | + self.assertEqual(os.path.realpath('topdir'), |
1751 | + self.local_branch_path(branch)) |
1752 | + self.assertEqual(branch.name, 'named') |
1753 | + self.assertIs(tree.controldir, branch.controldir) |
1754 | + |
1755 | def test_open_from_transport(self): |
1756 | # transport pointing at bzrdir should give a bzrdir with root transport |
1757 | # set to the given transport |
1758 | |
1759 | === modified file 'breezy/tests/test_config.py' |
1760 | --- breezy/tests/test_config.py 2020-02-07 02:14:30 +0000 |
1761 | +++ breezy/tests/test_config.py 2020-05-06 02:56:30 +0000 |
1762 | @@ -1481,6 +1481,13 @@ |
1763 | self.assertEqual("Robert Collins <robertc@example.org>", |
1764 | my_config.username()) |
1765 | |
1766 | + def test_BRZ_EMAIL_OVERRIDES(self): |
1767 | + self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>") |
1768 | + branch = FakeBranch() |
1769 | + my_config = config.BranchConfig(branch) |
1770 | + self.assertEqual("Robert Collins <robertc@example.org>", |
1771 | + my_config.username()) |
1772 | + |
1773 | def test_get_user_option_global(self): |
1774 | my_config = self.get_branch_config(global_config=sample_config_text) |
1775 | self.assertEqual('something', |
1776 | @@ -4645,8 +4652,16 @@ |
1777 | |
1778 | def test_default_email_uses_BRZ_EMAIL(self): |
1779 | conf = config.MemoryStack(b'email=jelmer@debian.org') |
1780 | - # BRZ_EMAIL takes precedence over EMAIL |
1781 | + # BRZ_EMAIL takes precedence over BZR_EMAIL and EMAIL |
1782 | self.overrideEnv('BRZ_EMAIL', 'jelmer@samba.org') |
1783 | + self.overrideEnv('BZR_EMAIL', 'jelmer@jelmer.uk') |
1784 | + self.overrideEnv('EMAIL', 'jelmer@apache.org') |
1785 | + self.assertEqual('jelmer@samba.org', conf.get('email')) |
1786 | + |
1787 | + def test_default_email_uses_BZR_EMAIL(self): |
1788 | + conf = config.MemoryStack(b'email=jelmer@debian.org') |
1789 | + # BZR_EMAIL takes precedence over EMAIL |
1790 | + self.overrideEnv('BZR_EMAIL', 'jelmer@samba.org') |
1791 | self.overrideEnv('EMAIL', 'jelmer@apache.org') |
1792 | self.assertEqual('jelmer@samba.org', conf.get('email')) |
1793 | |
1794 | |
1795 | === modified file 'breezy/tests/test_smart.py' |
1796 | --- breezy/tests/test_smart.py 2020-01-18 02:42:17 +0000 |
1797 | +++ breezy/tests/test_smart.py 2020-05-06 02:56:30 +0000 |
1798 | @@ -485,6 +485,19 @@ |
1799 | (b"success", ), local_result) |
1800 | self.assertEqual(expected, request.execute(b'')) |
1801 | |
1802 | + def test_ref(self): |
1803 | + backing = self.get_transport() |
1804 | + dir = self.make_controldir('foo') |
1805 | + b = self.make_branch('bar') |
1806 | + dir.set_branch_reference(b) |
1807 | + request_class = smart_dir.SmartServerBzrDirRequestGetBranches |
1808 | + request = request_class(backing) |
1809 | + local_result = bencode.bencode( |
1810 | + {b"": (b"ref", b'../bar/')}) |
1811 | + expected = smart_req.SuccessfulSmartServerResponse( |
1812 | + (b"success", ), local_result) |
1813 | + self.assertEqual(expected, request.execute(b'foo')) |
1814 | + |
1815 | def test_empty(self): |
1816 | backing = self.get_transport() |
1817 | self.make_controldir('.') |
1818 | |
1819 | === modified file 'breezy/transport/http/__init__.py' |
1820 | --- breezy/transport/http/__init__.py 2020-02-18 01:57:45 +0000 |
1821 | +++ breezy/transport/http/__init__.py 2020-05-06 02:56:30 +0000 |
1822 | @@ -1811,10 +1811,14 @@ |
1823 | report_activity=self._report_activity, ca_certs=ca_certs) |
1824 | |
1825 | def request(self, method, url, fields=None, headers=None, **urlopen_kw): |
1826 | + body = urlopen_kw.pop('body', None) |
1827 | if fields is not None: |
1828 | data = urlencode(fields).encode() |
1829 | + if body is not None: |
1830 | + raise ValueError( |
1831 | + 'body and fields are mutually exclusive') |
1832 | else: |
1833 | - data = urlopen_kw.pop('body', None) |
1834 | + data = body |
1835 | if headers is None: |
1836 | headers = {} |
1837 | request = Request(method, url, data, headers) |
1838 | |
1839 | === modified file 'brz' |
1840 | --- brz 2020-02-18 01:57:45 +0000 |
1841 | +++ brz 2020-05-06 02:56:30 +0000 |
1842 | @@ -78,30 +78,8 @@ |
1843 | breezy._format_version_tuple(_script_version), |
1844 | __file__)) |
1845 | |
1846 | - |
1847 | -import breezy.breakin |
1848 | -breezy.breakin.hook_debugger_to_signal() |
1849 | - |
1850 | -import breezy.commands |
1851 | -import breezy.trace |
1852 | - |
1853 | - |
1854 | if __name__ == '__main__': |
1855 | - with breezy.initialize(): |
1856 | - exit_val = breezy.commands.main() |
1857 | - if profiling: |
1858 | - profile_imports.log_stack_info(sys.stderr) |
1859 | - |
1860 | - # By this point we really have completed everything we want to do, and |
1861 | - # there's no point doing any additional cleanup. Abruptly exiting here |
1862 | - # stops any background threads getting into trouble as code is unloaded, |
1863 | - # and it may also be slightly faster, through avoiding gc of objects that |
1864 | - # are just about to be discarded anyhow. This does mean that atexit hooks |
1865 | - # won't run but we don't use them. Also file buffers won't be flushed, |
1866 | - # but our policy is to always close files from a finally block. -- mbp 20070215 |
1867 | - exitfunc = getattr(sys, "exitfunc", None) |
1868 | - if exitfunc is not None: |
1869 | - exitfunc() |
1870 | - os._exit(exit_val) |
1871 | + from breezy.__main__ import main |
1872 | + main() |
1873 | else: |
1874 | raise ImportError("The brz script cannot be imported.") |
1875 | |
1876 | === modified file 'doc/en/release-notes/brz-3.1.txt' |
1877 | --- doc/en/release-notes/brz-3.1.txt 2020-03-21 20:56:33 +0000 |
1878 | +++ doc/en/release-notes/brz-3.1.txt 2020-05-06 02:56:30 +0000 |
1879 | @@ -73,6 +73,20 @@ |
1880 | * When pushing to Git repositories, symrefs are now followed. |
1881 | (Jelmer Vernooij, #1800393) |
1882 | |
1883 | + * New ``brz clone`` command, which clones everything under |
1884 | + a control directory. I.e. all colocated branches, like |
1885 | + ``git clone``. (Jelmer Vernooij, #831939) |
1886 | + |
1887 | + * ``brz sprout`` is now an alias for ``brz branch``. |
1888 | + (Jelmer Vernooij) |
1889 | + |
1890 | + * ``brz branch`` now accepts a ``-b`` flag with the |
1891 | + name of the colocated branch to sprout. |
1892 | + (Jelmer Vernooij, #1869977) |
1893 | + |
1894 | + * Add a ``breezy.__main__`` module so that |
1895 | + ``python3 -m breezy`` works. (Jelmer Vernooij) |
1896 | + |
1897 | Improvements |
1898 | ************ |
1899 | |
1900 | @@ -117,6 +131,10 @@ |
1901 | * ``.git/config`` is now consulted to determine the users' identity |
1902 | for commits, and the gpg_signing_key. (Jelmer Vernooij) |
1903 | |
1904 | + * Ignore special files (fifos, block/character devices, sockets) |
1905 | + when finding changes in Git working trees. (Jelmer Vernooij, #1857244) |
1906 | + |
1907 | + |
1908 | Bug Fixes |
1909 | ********* |
1910 | |
1911 | @@ -154,6 +172,12 @@ |
1912 | * Fix ``setup_ui=False`` when initializing Breezy. |
1913 | (Jelmer Vernooij, #1852647) |
1914 | |
1915 | +* Fix backwards compatibility with Bazaar by supporting the |
1916 | + $BZR_EMAIL variable. (Jelmer Vernooij, #1869178) |
1917 | + |
1918 | +* Cope with non-ascii characters in Git signatures. |
1919 | + (Jelmer Vernooij, #1869533) |
1920 | + |
1921 | Documentation |
1922 | ************* |
1923 | |
1924 | |
1925 | === modified file 'doc/en/user-guide/setting_up_email.txt' |
1926 | --- doc/en/user-guide/setting_up_email.txt 2017-07-30 16:59:50 +0000 |
1927 | +++ doc/en/user-guide/setting_up_email.txt 2020-05-06 02:56:30 +0000 |
1928 | @@ -30,7 +30,9 @@ |
1929 | |
1930 | The order of precedence is |
1931 | |
1932 | - 1. If the ``BZR_EMAIL`` environment variable is set. |
1933 | + 1. If the ``BRZ_EMAIL`` environment variable is set. |
1934 | + #. If the ``BZR_EMAIL`` environment variable is set, for backwards |
1935 | + compatibility with Bazaar. |
1936 | #. If an email is set for your current branch in the ``locations.conf`` |
1937 | file. |
1938 | #. If an email is set four your current branch in the |
1939 | @@ -90,7 +92,7 @@ |
1940 | |
1941 | Setting email via environment variable |
1942 | -------------------------------------- |
1943 | -The final method Breezy will use is checking for the ``BZR_EMAIL`` |
1944 | +The final method Breezy will use is checking for the ``BRZ_EMAIL`` |
1945 | and ``EMAIL`` environment variables. Generally, you would use this |
1946 | method to override the email in a script context. If you would like |
1947 | to set a general default, then please see the ini methods above. |
Running landing tests failed /ci.breezy- vcs.org/ job/brz/ job/brz- land/734/
https:/