Merge lp:~vila/brz/trunk into lp:brz

Proposed by Vincent Ladeuil on 2019-03-06
Status: Merged
Approved by: Vincent Ladeuil on 2019-03-06
Approved revision: no longer in the source branch.
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~vila/brz/trunk
Merge into: lp:brz
Diff against target: 1624 lines (+483/-193)
42 files modified
.bzrignore (+1/-0)
breezy/__init__.py (+1/-1)
breezy/builtins.py (+35/-26)
breezy/git/cache.py (+1/-3)
breezy/git/git-remote-bzr (+7/-2)
breezy/git/git_remote_helper.py (+1/-1)
breezy/git/memorytree.py (+4/-0)
breezy/git/object_store.py (+2/-2)
breezy/git/tests/test_blackbox.py (+9/-0)
breezy/git/tests/test_branch.py (+7/-1)
breezy/git/tests/test_git_remote_helper.py (+31/-2)
breezy/git/tests/test_object_store.py (+30/-2)
breezy/globbing.py (+3/-1)
breezy/ignores.py (+1/-1)
breezy/memorytree.py (+18/-4)
breezy/plugins/propose/launchpad.py (+1/-0)
breezy/tests/blackbox/test_push.py (+20/-0)
breezy/tests/blackbox/test_uncommit.py (+14/-0)
breezy/tests/features.py (+14/-0)
breezy/tests/test_memorytree.py (+65/-70)
breezy/tests/test_urlutils.py (+3/-0)
breezy/transport/memory.py (+50/-32)
breezy/urlutils.py (+4/-1)
brz (+1/-1)
byov.conf (+4/-2)
doc/developers/apport.txt (+1/-1)
doc/developers/configuration.txt (+0/-14)
doc/developers/contribution-quickstart.txt (+1/-1)
doc/developers/plugin-development.txt (+1/-1)
doc/en/admin-guide/hooks-plugins.txt (+4/-4)
doc/en/admin-guide/migration.txt (+1/-1)
doc/en/index.txt (+1/-1)
doc/en/release-notes/brz-3.0.txt (+16/-6)
doc/en/release-notes/brz-3.1.txt (+62/-0)
doc/en/user-guide/controlling_registration.txt (+3/-3)
doc/en/user-guide/git_limitations.txt (+18/-0)
doc/en/user-guide/index-plain.txt (+1/-0)
doc/en/user-guide/installing_breezy.txt (+1/-1)
doc/en/user-guide/plugins.txt (+2/-2)
doc/en/whats-new/template.txt (+3/-3)
doc/en/whats-new/whats-new-in-3.1.txt (+22/-0)
setup.py (+19/-3)
To merge this branch: bzr merge lp:~vila/brz/trunk
Reviewer Review Type Date Requested Status
Vincent Ladeuil Approve on 2019-03-06
Review via email: mp+364041@code.launchpad.net

Commit message

Open trunk again as 3.1.0dev1

To post a comment you must log in.
Vincent Ladeuil (vila) wrote :

RM self-approval

review: Approve
lp:~vila/brz/trunk updated on 2019-03-06
7295. By Vincent Ladeuil on 2019-03-06

Open trunk again as 3.1.0dev1

Merged from https://code.launchpad.net/~vila/brz/trunk/+merge/364041

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2018-11-18 18:55:35 +0000
3+++ .bzrignore 2019-03-06 14:35:55 +0000
4@@ -2,6 +2,7 @@
5 # These are created as byproducts of our test suite
6 ./test*.tmp
7 ./.python-eggs
8+./breezy.egg-info
9 ./.bzr.log
10 # Generated files
11 CHANGELOG
12
13=== modified file 'breezy/__init__.py'
14--- breezy/__init__.py 2019-01-14 01:27:55 +0000
15+++ breezy/__init__.py 2019-03-06 14:35:55 +0000
16@@ -50,7 +50,7 @@
17 # Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a
18 # releaselevel of 'dev' for unreleased under-development code.
19
20-version_info = (3, 0, 0, 'beta', 1)
21+version_info = (3, 1, 0, 'dev', 1)
22
23
24 def _format_version_tuple(version_info):
25
26=== modified file 'breezy/builtins.py'
27--- breezy/builtins.py 2019-02-09 03:23:20 +0000
28+++ breezy/builtins.py 2019-03-06 14:35:55 +0000
29@@ -348,7 +348,7 @@
30 Not versioned and not matching an ignore pattern.
31
32 Additionally for directories, symlinks and files with a changed
33- executable bit, Bazaar indicates their type using a trailing
34+ executable bit, Breezy indicates their type using a trailing
35 character: '/', '@' or '*' respectively. These decorations can be
36 disabled using the '--no-classify' option.
37
38@@ -454,11 +454,11 @@
39
40 b = controldir.ControlDir.open_containing_tree_or_branch(directory)[1]
41
42- revisions = b.repository.revisions
43+ revisions = getattr(b.repository, "revisions", None)
44 if revisions is None:
45 raise errors.BzrCommandError(
46 gettext('Repository %r does not support '
47- 'access to raw revision texts'))
48+ 'access to raw revision texts') % b.repository)
49
50 with b.repository.lock_read():
51 # TODO: jam 20060112 should cat-revision always output utf-8?
52@@ -1344,6 +1344,7 @@
53 use_existing_dir=False, directory=None, stacked_on=None,
54 stacked=False, strict=None, no_tree=False,
55 overwrite_tags=False, lossy=False):
56+ from .location import location_to_url
57 from .push import _show_push_branch
58
59 if overwrite:
60@@ -1371,6 +1372,7 @@
61 more_warning='Uncommitted changes will not be pushed.')
62 # Get the stacked_on branch, if any
63 if stacked_on is not None:
64+ stacked_on = location_to_url(stacked_on, 'read')
65 stacked_on = urlutils.normalize_url(stacked_on)
66 elif stacked:
67 parent_url = br_from.get_parent()
68@@ -1728,7 +1730,7 @@
69 If the tree's branch is bound to a master branch, brz will also update
70 the branch from the master.
71
72- You cannot update just a single file or directory, because each Bazaar
73+ You cannot update just a single file or directory, because each Breezy
74 working tree has just a single basis revision. If you want to restore a
75 file that has been removed locally, use 'brz revert' instead of 'brz
76 update'. If you want to restore a file to its state in a previous
77@@ -1871,11 +1873,11 @@
78 class cmd_remove(Command):
79 __doc__ = """Remove files or directories.
80
81- This makes Bazaar stop tracking changes to the specified files. Bazaar will
82+ This makes Breezy stop tracking changes to the specified files. Breezy will
83 delete them if they can easily be recovered using revert otherwise they
84 will be backed up (adding an extension of the form .~#~). If no options or
85- parameters are given Bazaar will scan for files that are being tracked by
86- Bazaar but missing in your tree and stop tracking them for you.
87+ parameters are given Breezy will scan for files that are being tracked by
88+ Breezy but missing in your tree and stop tracking them for you.
89 """
90 takes_args = ['file*']
91 takes_options = ['verbose',
92@@ -3091,9 +3093,10 @@
93 using this command or directly by using an editor, be sure to commit
94 it.
95
96- Bazaar also supports a global ignore file ~/.bazaar/ignore. On Windows
97- the global ignore file can be found in the application data directory as
98- C:\\Documents and Settings\\<user>\\Application Data\\Bazaar\\2.0\\ignore.
99+ Breezy also supports a global ignore file ~/.config/breezy/ignore. On
100+ Windows the global ignore file can be found in the application data
101+ directory as
102+ C:\\Documents and Settings\\<user>\\Application Data\\Breezy\\3.0\\ignore.
103 Global ignores are not touched by this command. The global ignore file
104 can be edited directly using an editor.
105
106@@ -3732,7 +3735,7 @@
107 unreferenced ancestors
108 Texts that are ancestors of other texts, but
109 are not properly referenced by the revision ancestry. This is a
110- subtle problem that Bazaar can work around.
111+ subtle problem that Breezy can work around.
112
113 unique file texts
114 This is the total number of unique file contents
115@@ -3744,7 +3747,7 @@
116 entries are modified, but the file contents are not. It does not
117 indicate a problem.
118
119- If no restrictions are specified, all Bazaar data that is found at the given
120+ If no restrictions are specified, all data that is found at the given
121 location will be checked.
122
123 :Examples:
124@@ -3786,10 +3789,10 @@
125 __doc__ = """Upgrade a repository, branch or working tree to a newer format.
126
127 When the default format has changed after a major new release of
128- Bazaar, you may be informed during certain operations that you
129+ Bazaar/Breezy, you may be informed during certain operations that you
130 should upgrade. Upgrading to a newer format may improve performance
131 or make new features available. It may however limit interoperability
132- with older repositories or with older versions of Bazaar.
133+ with older repositories or with older versions of Bazaar or Breezy.
134
135 If you wish to upgrade to a particular format rather than the
136 current default, that can be specified using the --format option.
137@@ -3811,7 +3814,7 @@
138 If the conversion of a branch fails, remaining branches are still
139 tried.
140
141- For more information on upgrades, see the Bazaar Upgrade Guide,
142+ For more information on upgrades, see the Breezy Upgrade Guide,
143 https://www.breezy-vcs.org/doc/en/upgrade-guide/.
144 """
145
146@@ -4312,7 +4315,7 @@
147 through OTHER, excluding BASE but including OTHER, will be merged. If this
148 causes some revisions to be skipped, i.e. if the destination branch does
149 not already contain revision BASE, such a merge is commonly referred to as
150- a "cherrypick". Unlike a normal merge, Bazaar does not currently track
151+ a "cherrypick". Unlike a normal merge, Breezy does not currently track
152 cherrypicks. The changes look like a normal commit, and the history of the
153 changes from the other branch is not stored in the commit.
154
155@@ -5133,8 +5136,8 @@
156
157 --verbose shows the path where each plugin is located.
158
159- A plugin is an external component for Bazaar that extends the
160- revision control system, by adding or replacing code in Bazaar.
161+ A plugin is an external component for Breezy that extends the
162+ revision control system, by adding or replacing code in Breezy.
163 Plugins can do a variety of things, including overriding commands,
164 adding new commands, providing additional network transports and
165 customizing log output.
166@@ -5420,10 +5423,10 @@
167 else:
168 self.add_cleanup(b.lock_write().unlock)
169 return self._run(b, tree, dry_run, verbose, revision, force,
170- local, keep_tags)
171+ local, keep_tags, location)
172
173 def _run(self, b, tree, dry_run, verbose, revision, force, local,
174- keep_tags):
175+ keep_tags, location):
176 from .log import log_formatter, show_log
177 from .uncommit import uncommit
178
179@@ -5477,10 +5480,16 @@
180 last_rev_id, rev_id)
181 uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose,
182 revno=revno, local=local, keep_tags=keep_tags)
183- self.outf.write(
184- gettext('You can restore the old tip by running:\n'
185- ' brz pull . -r revid:%s\n')
186- % last_rev_id.decode('utf-8'))
187+ if location != '.':
188+ self.outf.write(
189+ gettext('You can restore the old tip by running:\n'
190+ ' brz pull -d %s %s -r revid:%s\n')
191+ % (location, location, last_rev_id.decode('utf-8')))
192+ else:
193+ self.outf.write(
194+ gettext('You can restore the old tip by running:\n'
195+ ' brz pull . -r revid:%s\n')
196+ % last_rev_id.decode('utf-8'))
197
198
199 class cmd_break_lock(Command):
200@@ -5498,7 +5507,7 @@
201 :Examples:
202 brz break-lock
203 brz break-lock brz+ssh://example.com/brz/foo
204- brz break-lock --conf ~/.bazaar
205+ brz break-lock --conf ~/.config/breezy
206 """
207
208 takes_args = ['location?']
209@@ -5789,7 +5798,7 @@
210 branch containing submit_branch in its ancestory without needing access to
211 the source branch.
212
213- If --no-bundle is specified, then Bazaar doesn't send the contents of the
214+ If --no-bundle is specified, then Breezy doesn't send the contents of the
215 revisions, but only a structured request to merge from the
216 public_location. In that case the public_branch is needed and it must be
217 up-to-date and accessible to the recipient. The public_branch is always
218
219=== modified file 'breezy/git/cache.py'
220--- breezy/git/cache.py 2018-11-12 01:41:38 +0000
221+++ breezy/git/cache.py 2019-03-06 14:35:55 +0000
222@@ -613,11 +613,9 @@
223
224 def open(self, transport):
225 try:
226- basepath = transport.local_abspath(".").encode(osutils._fs_enc)
227+ basepath = transport.local_abspath(".")
228 except bzr_errors.NotLocalUrl:
229 basepath = get_cache_dir()
230- if not isinstance(basepath, str):
231- raise TypeError(basepath)
232 try:
233 return TdbBzrGitCache(os.path.join(basepath, "idmap.tdb"))
234 except ImportError:
235
236=== modified file 'breezy/git/git-remote-bzr'
237--- breezy/git/git-remote-bzr 2018-03-26 22:28:24 +0000
238+++ breezy/git/git-remote-bzr 2019-03-06 14:35:55 +0000
239@@ -32,10 +32,12 @@
240 import breezy
241 breezy.initialize()
242
243+from breezy.sixish import PY3
244+
245 from breezy.plugin import load_plugins
246 load_plugins()
247
248-from breezy.plugins.git.git_remote_helper import (
249+from breezy.git.git_remote_helper import (
250 RemoteHelper,
251 open_local_dir,
252 open_remote_dir,
253@@ -46,4 +48,7 @@
254 (shortname, url) = args
255
256 helper = RemoteHelper(open_local_dir(), shortname, open_remote_dir(url))
257-helper.process(sys.stdin, sys.stdout)
258+if PY3:
259+ helper.process(sys.stdin.buffer, sys.stdout.buffer)
260+else:
261+ helper.process(sys.stdin, sys.stdout)
262
263=== modified file 'breezy/git/git_remote_helper.py'
264--- breezy/git/git_remote_helper.py 2018-11-11 14:23:06 +0000
265+++ breezy/git/git_remote_helper.py 2019-03-06 14:35:55 +0000
266@@ -186,7 +186,7 @@
267 self.batchcmd = None
268 else:
269 try:
270- self.commands[argv[0]](self, outf, argv)
271+ self.commands[argv[0].decode()](self, outf, argv)
272 except KeyError:
273 raise Exception("Unknown remote command %r" % argv)
274 outf.flush()
275
276=== modified file 'breezy/git/memorytree.py'
277--- breezy/git/memorytree.py 2018-11-18 00:25:19 +0000
278+++ breezy/git/memorytree.py 2019-03-06 14:35:55 +0000
279@@ -258,3 +258,7 @@
280 def kind(self, p):
281 stat_value = self._file_transport.stat(p)
282 return osutils.file_kind_from_stat_mode(stat_value.st_mode)
283+
284+ def get_symlink_target(self, path):
285+ with self.lock_read():
286+ return self._file_transport.readlink(path)
287
288=== modified file 'breezy/git/object_store.py'
289--- breezy/git/object_store.py 2019-01-01 21:23:40 +0000
290+++ breezy/git/object_store.py 2019-03-06 14:35:55 +0000
291@@ -359,7 +359,7 @@
292 continue
293
294 if tree.kind(path) != 'directory':
295- raise AssertionError
296+ continue
297
298 obj = Tree()
299 for value in tree.iter_child_entries(path):
300@@ -589,7 +589,7 @@
301 for (file_id, revision, expected_sha), chunks in stream:
302 blob = Blob()
303 blob.chunked = chunks
304- if blob.id != expected_sha and blob.data == "":
305+ if blob.id != expected_sha and blob.data == b"":
306 # Perhaps it's a symlink ?
307 tree = self.tree_cache.revision_tree(revision)
308 path = tree.id2path(file_id)
309
310=== modified file 'breezy/git/tests/test_blackbox.py'
311--- breezy/git/tests/test_blackbox.py 2019-02-14 04:20:37 +0000
312+++ breezy/git/tests/test_blackbox.py 2019-03-06 14:35:55 +0000
313@@ -78,6 +78,15 @@
314 self.assertEqual(output, '')
315 self.assertFileEqual("foo\n", ".gitignore")
316
317+ def test_cat_revision(self):
318+ self.simple_commit()
319+ output, error = self.run_bzr(['cat-revision', '-r-1'], retcode=3)
320+ self.assertContainsRe(
321+ error,
322+ 'brz: ERROR: Repository .* does not support access to raw '
323+ 'revision texts')
324+ self.assertEqual(output, '')
325+
326 def test_branch(self):
327 os.mkdir("gitbranch")
328 GitRepo.init(os.path.join(self.test_dir, "gitbranch"))
329
330=== modified file 'breezy/git/tests/test_branch.py'
331--- breezy/git/tests/test_branch.py 2019-01-19 17:13:53 +0000
332+++ breezy/git/tests/test_branch.py 2019-03-06 14:35:55 +0000
333@@ -112,6 +112,7 @@
334 reva = self.simple_commit_a()
335 self.build_tree(['b'])
336 r = GitRepo(".")
337+ self.addCleanup(r.close)
338 r.stage("b")
339 revb = r.do_commit(b"b", committer=b"Somebody <foo@example.com>")
340
341@@ -129,6 +130,7 @@
342 o.tag_timezone = 0
343 o.tag_time = 42
344 r = GitRepo(".")
345+ self.addCleanup(r.close)
346 r.object_store.add_object(o)
347 r[b'refs/tags/foo'] = o.id
348 thebranch = Branch.open('.')
349@@ -138,6 +140,7 @@
350 def test_tag(self):
351 reva = self.simple_commit_a()
352 r = GitRepo(".")
353+ self.addCleanup(r.close)
354 r.refs[b"refs/tags/foo"] = reva
355 thebranch = Branch.open('.')
356 self.assertEqual({"foo": default_mapping.revision_id_foreign_to_bzr(reva)},
357@@ -221,6 +224,7 @@
358 def test_sprouted_tags(self):
359 path, gitsha = self.make_onerev_branch()
360 r = GitRepo(path)
361+ self.addCleanup(r.close)
362 r.refs[b"refs/tags/lala"] = r.head()
363 oldrepo = Repository.open(path)
364 revid = oldrepo.get_mapping().revision_id_foreign_to_bzr(gitsha)
365@@ -231,6 +235,7 @@
366 def test_sprouted_ghost_tags(self):
367 path, gitsha = self.make_onerev_branch()
368 r = GitRepo(path)
369+ self.addCleanup(r.close)
370 r.refs[b"refs/tags/lala"] = b"aa" * 20
371 oldrepo = Repository.open(path)
372 revid = oldrepo.get_mapping().revision_id_foreign_to_bzr(gitsha)
373@@ -238,7 +243,7 @@
374 self.clone_git_branch, path, "f")
375 self.assertEqual({}, newbranch.tags.get_tag_dict())
376 # Dulwich raises a UserWarning for tags with invalid target
377- self.assertEqual(1, len(warnings))
378+ self.assertIn(('ref refs/tags/lala points at non-present sha ' + ("aa" * 20), ), [w.args for w in warnings])
379
380 def test_interbranch_pull(self):
381 path, (gitsha1, gitsha2) = self.make_tworev_branch()
382@@ -272,6 +277,7 @@
383 def test_interbranch_pull_with_tags(self):
384 path, (gitsha1, gitsha2) = self.make_tworev_branch()
385 gitrepo = GitRepo(path)
386+ self.addCleanup(gitrepo.close)
387 gitrepo.refs[b"refs/tags/sometag"] = gitsha2
388 oldrepo = Repository.open(path)
389 revid1 = oldrepo.get_mapping().revision_id_foreign_to_bzr(gitsha1)
390
391=== modified file 'breezy/git/tests/test_git_remote_helper.py'
392--- breezy/git/tests/test_git_remote_helper.py 2018-11-21 03:39:28 +0000
393+++ breezy/git/tests/test_git_remote_helper.py 2019-03-06 14:35:55 +0000
394@@ -23,12 +23,15 @@
395
396 from io import BytesIO
397 import os
398+import subprocess
399+import sys
400
401 from dulwich.repo import Repo
402
403 from ...tests import (
404 TestCaseWithTransport,
405 )
406+from ...tests.features import PathFeature
407
408 from ..object_store import get_object_store
409 from ..git_remote_helper import (
410@@ -46,6 +49,11 @@
411 return object_store._lookup_revision_sha1(bzr_revid)
412
413
414+git_remote_bzr_path = os.path.abspath(
415+ os.path.join(os.path.dirname(__file__), '..', 'git-remote-bzr'))
416+git_remote_bzr_feature = PathFeature(git_remote_bzr_path)
417+
418+
419 class OpenLocalDirTests(TestCaseWithTransport):
420
421 def test_from_env_dir(self):
422@@ -86,8 +94,29 @@
423 self.assertEqual(out, b"\n")
424 r = Repo('local')
425 self.assertTrue(git_sha1 in r.object_store)
426- self.assertEqual({
427- }, r.get_refs())
428+ self.assertEqual({}, r.get_refs())
429+
430+
431+class ExecuteRemoteHelperTests(TestCaseWithTransport):
432+
433+ def test_run(self):
434+ self.requireFeature(git_remote_bzr_feature)
435+ local_dir = self.make_branch_and_tree('local', format='git').controldir
436+ local_path = local_dir.control_transport.local_abspath('.')
437+ remote_tree = self.make_branch_and_tree('remote')
438+ remote_dir = remote_tree.controldir
439+ shortname = 'bzr'
440+ env = dict(os.environ)
441+ env['GIT_DIR'] = local_path
442+ env['PYTHONPATH'] = ':'.join(sys.path)
443+ p = subprocess.Popen(
444+ [sys.executable, git_remote_bzr_path, local_path, remote_dir.user_url],
445+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
446+ stderr=subprocess.PIPE, env=env)
447+ (out, err) = p.communicate(b'capabilities\n')
448+ lines = out.splitlines()
449+ self.assertIn(b'push', lines, "no 'push' in %r, error: %r" % (lines, err))
450+ self.assertEqual(b'', err)
451
452
453 class RemoteHelperTests(TestCaseWithTransport):
454
455=== modified file 'breezy/git/tests/test_object_store.py'
456--- breezy/git/tests/test_object_store.py 2018-11-11 04:08:32 +0000
457+++ breezy/git/tests/test_object_store.py 2019-03-06 14:35:55 +0000
458@@ -18,6 +18,9 @@
459
460 from __future__ import absolute_import
461
462+import os
463+import shutil
464+
465 from dulwich.objects import (
466 Blob,
467 Tree,
468@@ -144,11 +147,11 @@
469 def setUp(self):
470 super(BazaarObjectStoreTests, self).setUp()
471 self.branch = self.make_branch(".")
472- self.branch.lock_write()
473- self.addCleanup(self.branch.unlock)
474 self.store = BazaarObjectStore(self.branch.repository)
475
476 def test_get_blob(self):
477+ self.branch.lock_write()
478+ self.addCleanup(self.branch.unlock)
479 b = Blob()
480 b.data = b'a\nb\nc\nd\ne\n'
481 self.store.lock_read()
482@@ -167,7 +170,30 @@
483 self.store.lock_read()
484 self.assertEqual(b, self.store[b.id])
485
486+ def test_directory_converted_to_symlink(self):
487+ b = Blob()
488+ b.data = b'trgt'
489+ self.store.lock_read()
490+ self.addCleanup(self.store.unlock)
491+ self.assertRaises(KeyError, self.store.__getitem__, b.id)
492+ tree = self.branch.controldir.create_workingtree()
493+ self.build_tree_contents([
494+ ('foo/', ),
495+ ('foo/bar', b'a\nb\nc\nd\ne\n')])
496+ tree.add(['foo', 'foo/bar'])
497+ revid1 = tree.commit('commit 1')
498+ shutil.rmtree('foo')
499+ os.symlink('trgt', 'foo')
500+ revid2 = tree.commit('commit 2')
501+ # read locks cache
502+ self.assertRaises(KeyError, self.store.__getitem__, b.id)
503+ self.store.unlock()
504+ self.store.lock_read()
505+ self.assertEqual(b, self.store[b.id])
506+
507 def test_get_raw(self):
508+ self.branch.lock_write()
509+ self.addCleanup(self.branch.unlock)
510 b = Blob()
511 b.data = b'a\nb\nc\nd\ne\n'
512 self.store.lock_read()
513@@ -187,6 +213,8 @@
514 self.assertEqual(b.as_raw_string(), self.store.get_raw(b.id)[1])
515
516 def test_contains(self):
517+ self.branch.lock_write()
518+ self.addCleanup(self.branch.unlock)
519 b = Blob()
520 b.data = b'a\nb\nc\nd\ne\n'
521 self.store.lock_read()
522
523=== modified file 'breezy/globbing.py'
524--- breezy/globbing.py 2018-11-18 19:48:57 +0000
525+++ breezy/globbing.py 2019-03-06 14:35:55 +0000
526@@ -248,7 +248,9 @@
527 # the combined pattern we sent to regex. Instead we indicate to
528 # the user that an ignore file needs fixing.
529 mutter('Invalid pattern found in regex: %s.', e.msg)
530- e.msg = "File ~/.bazaar/ignore or .bzrignore contains error(s)."
531+ e.msg = (
532+ "File ~/.config/breezy/ignore or "
533+ ".bzrignore contains error(s).")
534 bad_patterns = ''
535 for _, patterns in self._regex_patterns:
536 for p in patterns:
537
538=== modified file 'breezy/ignores.py'
539--- breezy/ignores.py 2018-11-12 01:41:38 +0000
540+++ breezy/ignores.py 2019-03-06 14:35:55 +0000
541@@ -33,7 +33,7 @@
542 )
543 """)
544
545-# ~/.bazaar/ignore will be filled out using
546+# ~/.config/breezy/ignore will be filled out using
547 # this ignore list, if it does not exist
548 # please keep these sorted (in C locale order) to aid merging
549 USER_DEFAULTS = [
550
551=== modified file 'breezy/memorytree.py'
552--- breezy/memorytree.py 2018-11-18 00:25:19 +0000
553+++ breezy/memorytree.py 2019-03-06 14:35:55 +0000
554@@ -22,6 +22,7 @@
555 from __future__ import absolute_import
556
557 import os
558+import stat
559
560 from . import (
561 errors,
562@@ -62,7 +63,15 @@
563 with self.lock_tree_write():
564 for f, file_id, kind in zip(files, ids, kinds):
565 if kind is None:
566- kind = 'file'
567+ st_mode = self._file_transport.stat(f).st_mode
568+ if stat.S_ISREG(st_mode):
569+ kind = 'file'
570+ elif stat.S_ISLNK(st_mode):
571+ kind = 'symlink'
572+ elif stat.S_ISDIR(st_mode):
573+ kind = 'directory'
574+ else:
575+ raise AssertionError('Unknown file kind')
576 if file_id is None:
577 self._inventory.add_path(f, kind=kind)
578 else:
579@@ -127,7 +136,7 @@
580 # memory tree does not support nested trees yet.
581 return kind, None, None, None
582 elif kind == 'symlink':
583- raise NotImplementedError('symlink support')
584+ return kind, None, None, self._inventory[id].symlink_target
585 else:
586 raise NotImplementedError('unknown kind')
587
588@@ -148,8 +157,7 @@
589 return self._inventory.get_entry_by_path(path).executable
590
591 def kind(self, path):
592- file_id = self.path2id(path)
593- return self._inventory[file_id].kind
594+ return self._inventory.get_entry_by_path(path).kind
595
596 def mkdir(self, path, file_id=None):
597 """See MutableTree.mkdir()."""
598@@ -227,6 +235,8 @@
599 continue
600 if entry.kind == 'directory':
601 self._file_transport.mkdir(path)
602+ elif entry.kind == 'symlink':
603+ self._file_transport.symlink(entry.symlink_target, path)
604 elif entry.kind == 'file':
605 self._file_transport.put_file(
606 path, self._basis_tree.get_file(path))
607@@ -302,6 +312,10 @@
608 else:
609 raise
610
611+ def get_symlink_target(self, path):
612+ with self.lock_read():
613+ return self._file_transport.readlink(path)
614+
615 def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
616 """See MutableTree.set_parent_trees()."""
617 if len(parents_list) == 0:
618
619=== modified file 'breezy/plugins/propose/launchpad.py'
620--- breezy/plugins/propose/launchpad.py 2019-02-10 18:37:58 +0000
621+++ breezy/plugins/propose/launchpad.py 2019-03-06 14:35:55 +0000
622@@ -140,6 +140,7 @@
623
624 def set_description(self, description):
625 self._mp.description = description
626+ self._mp.lp_save()
627
628 def close(self):
629 self._mp.setStatus(status='Rejected')
630
631=== modified file 'breezy/tests/blackbox/test_push.py'
632--- breezy/tests/blackbox/test_push.py 2018-11-16 12:08:41 +0000
633+++ breezy/tests/blackbox/test_push.py 2019-03-06 14:35:55 +0000
634@@ -22,6 +22,7 @@
635 from breezy import (
636 branch,
637 controldir,
638+ directory_service,
639 errors,
640 osutils,
641 tests,
642@@ -498,6 +499,25 @@
643 self.assertPublished(branch_tree.last_revision(),
644 trunk_tree.branch.base)
645
646+ def test_push_new_branch_stacked_on(self):
647+ """Pushing a new branch with --stacked-on can use directory URLs."""
648+ trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
649+ class FooDirectory(object):
650+ def look_up(self, name, url, purpose=None):
651+ if url == 'foo:':
652+ return trunk_tree.branch.base
653+ return url
654+ directory_service.directories.register('foo:', FooDirectory, 'Foo directory')
655+ self.addCleanup(directory_service.directories.remove, 'foo:')
656+ # we publish branch_tree with a reference to the mainline.
657+ out, err = self.run_bzr(['push', '--stacked-on', 'foo:',
658+ self.get_url('published')], working_dir='branch')
659+ self.assertEqual('', out)
660+ self.assertEqual('Created new stacked branch referring to %s.\n' %
661+ trunk_tree.branch.base, err)
662+ self.assertPublished(branch_tree.last_revision(),
663+ trunk_tree.branch.base)
664+
665 def test_push_new_branch_stacked_uses_parent_when_no_public_url(self):
666 """When the parent has no public url the parent is used as-is."""
667 trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
668
669=== modified file 'breezy/tests/blackbox/test_uncommit.py'
670--- breezy/tests/blackbox/test_uncommit.py 2018-11-18 19:48:57 +0000
671+++ breezy/tests/blackbox/test_uncommit.py 2019-03-06 14:35:55 +0000
672@@ -246,6 +246,20 @@
673 brz pull . -r revid:a2
674 """)
675
676+ def test_uncommit_shows_pull_with_location(self):
677+ wt = self.create_simple_tree()
678+
679+ script = ScriptRunner()
680+ script.run_script(self, """
681+$ brz uncommit --force tree
682+ 2 ...
683+ second commit
684+...
685+The above revision(s) will be removed.
686+You can restore the old tip by running:
687+ brz pull -d tree tree -r revid:a2
688+""")
689+
690 def test_uncommit_octopus_merge(self):
691 # Check that uncommit keeps the pending merges in the same order
692 # though it will also filter out ones in the ancestry
693
694=== modified file 'breezy/tests/features.py'
695--- breezy/tests/features.py 2019-01-19 01:44:20 +0000
696+++ breezy/tests/features.py 2019-03-06 14:35:55 +0000
697@@ -568,3 +568,17 @@
698
699
700 BackslashFilenameFeature = _BackslashFilenameFeature()
701+
702+
703+class PathFeature(Feature):
704+ """Feature testing whether a particular path exists."""
705+
706+ def __init__(self, path):
707+ super(PathFeature, self).__init__()
708+ self.path = path
709+
710+ def _probe(self):
711+ return os.path.exists(self.path)
712+
713+ def feature_name(self):
714+ return "%s exists" % self.path
715
716=== modified file 'breezy/tests/test_memorytree.py'
717--- breezy/tests/test_memorytree.py 2018-11-11 04:08:32 +0000
718+++ breezy/tests/test_memorytree.py 2019-03-06 14:35:55 +0000
719@@ -46,21 +46,17 @@
720 rev_id = tree.commit('first post')
721 tree.unlock()
722 tree = MemoryTree.create_on_branch(branch)
723- tree.lock_read()
724- self.assertEqual([rev_id], tree.get_parent_ids())
725- with tree.get_file('foo') as f:
726- self.assertEqual(b'contents of foo\n', f.read())
727- tree.unlock()
728+ with tree.lock_read():
729+ self.assertEqual([rev_id], tree.get_parent_ids())
730+ with tree.get_file('foo') as f:
731+ self.assertEqual(b'contents of foo\n', f.read())
732
733 def test_get_root_id(self):
734 branch = self.make_branch('branch')
735 tree = MemoryTree.create_on_branch(branch)
736- tree.lock_write()
737- try:
738+ with tree.lock_write():
739 tree.add([''])
740 self.assertIsNot(None, tree.get_root_id())
741- finally:
742- tree.unlock()
743
744 def test_lock_tree_write(self):
745 """Check we can lock_tree_write and unlock MemoryTrees."""
746@@ -73,9 +69,8 @@
747 """Check that we error when trying to upgrade a read lock to write."""
748 branch = self.make_branch('branch')
749 tree = MemoryTree.create_on_branch(branch)
750- tree.lock_read()
751- self.assertRaises(errors.ReadOnlyError, tree.lock_tree_write)
752- tree.unlock()
753+ with tree.lock_read():
754+ self.assertRaises(errors.ReadOnlyError, tree.lock_tree_write)
755
756 def test_lock_write(self):
757 """Check we can lock_write and unlock MemoryTrees."""
758@@ -88,58 +83,63 @@
759 """Check that we error when trying to upgrade a read lock to write."""
760 branch = self.make_branch('branch')
761 tree = MemoryTree.create_on_branch(branch)
762- tree.lock_read()
763- self.assertRaises(errors.ReadOnlyError, tree.lock_write)
764- tree.unlock()
765+ with tree.lock_read():
766+ self.assertRaises(errors.ReadOnlyError, tree.lock_write)
767
768 def test_add_with_kind(self):
769 branch = self.make_branch('branch')
770 tree = MemoryTree.create_on_branch(branch)
771- tree.lock_write()
772- tree.add(['', 'afile', 'adir'], None,
773- ['directory', 'file', 'directory'])
774- self.assertEqual('afile', tree.id2path(tree.path2id('afile')))
775- self.assertEqual('adir', tree.id2path(tree.path2id('adir')))
776- self.assertFalse(tree.has_filename('afile'))
777- self.assertFalse(tree.has_filename('adir'))
778- tree.unlock()
779+ with tree.lock_write():
780+ tree.add(['', 'afile', 'adir'], None,
781+ ['directory', 'file', 'directory'])
782+ self.assertEqual('afile', tree.id2path(tree.path2id('afile')))
783+ self.assertEqual('adir', tree.id2path(tree.path2id('adir')))
784+ self.assertFalse(tree.has_filename('afile'))
785+ self.assertFalse(tree.has_filename('adir'))
786
787 def test_put_new_file(self):
788 branch = self.make_branch('branch')
789 tree = MemoryTree.create_on_branch(branch)
790- tree.lock_write()
791- tree.add(['', 'foo'], ids=[b'root-id', b'foo-id'],
792- kinds=['directory', 'file'])
793- tree.put_file_bytes_non_atomic('foo', b'barshoom')
794- self.assertEqual(b'barshoom', tree.get_file('foo').read())
795- tree.unlock()
796+ with tree.lock_write():
797+ tree.add(['', 'foo'], ids=[b'root-id', b'foo-id'],
798+ kinds=['directory', 'file'])
799+ tree.put_file_bytes_non_atomic('foo', b'barshoom')
800+ with tree.get_file('foo') as f:
801+ self.assertEqual(b'barshoom', f.read())
802
803 def test_put_existing_file(self):
804 branch = self.make_branch('branch')
805 tree = MemoryTree.create_on_branch(branch)
806- tree.lock_write()
807- tree.add(['', 'foo'], ids=[b'root-id', b'foo-id'],
808- kinds=['directory', 'file'])
809- tree.put_file_bytes_non_atomic('foo', b'first-content')
810- tree.put_file_bytes_non_atomic('foo', b'barshoom')
811- self.assertEqual(b'barshoom', tree.get_file('foo').read())
812- tree.unlock()
813+ with tree.lock_write():
814+ tree.add(['', 'foo'], ids=[b'root-id', b'foo-id'],
815+ kinds=['directory', 'file'])
816+ tree.put_file_bytes_non_atomic('foo', b'first-content')
817+ tree.put_file_bytes_non_atomic('foo', b'barshoom')
818+ self.assertEqual(b'barshoom', tree.get_file('foo').read())
819
820 def test_add_in_subdir(self):
821 branch = self.make_branch('branch')
822 tree = MemoryTree.create_on_branch(branch)
823- tree.lock_write()
824- self.addCleanup(tree.unlock)
825- tree.add([''], [b'root-id'], ['directory'])
826- # Unfortunately, the only way to 'mkdir' is to call 'tree.mkdir', but
827- # that *always* adds the directory as well. So if you want to create a
828- # file in a subdirectory, you have to split out the 'mkdir()' calls
829- # from the add and put_file_bytes_non_atomic calls. :(
830- tree.mkdir('adir', b'dir-id')
831- tree.add(['adir/afile'], [b'file-id'], ['file'])
832- self.assertEqual('adir/afile', tree.id2path(b'file-id'))
833- self.assertEqual('adir', tree.id2path(b'dir-id'))
834- tree.put_file_bytes_non_atomic('adir/afile', b'barshoom')
835+ with tree.lock_write():
836+ tree.add([''], [b'root-id'], ['directory'])
837+ # Unfortunately, the only way to 'mkdir' is to call 'tree.mkdir', but
838+ # that *always* adds the directory as well. So if you want to create a
839+ # file in a subdirectory, you have to split out the 'mkdir()' calls
840+ # from the add and put_file_bytes_non_atomic calls. :(
841+ tree.mkdir('adir', b'dir-id')
842+ tree.add(['adir/afile'], [b'file-id'], ['file'])
843+ self.assertEqual('adir/afile', tree.id2path(b'file-id'))
844+ self.assertEqual('adir', tree.id2path(b'dir-id'))
845+ tree.put_file_bytes_non_atomic('adir/afile', b'barshoom')
846+
847+ def test_add_symlink(self):
848+ branch = self.make_branch('branch')
849+ tree = MemoryTree.create_on_branch(branch)
850+ with tree.lock_write():
851+ tree._file_transport.symlink('bar', 'foo')
852+ tree.add(['', 'foo'])
853+ self.assertEqual('symlink', tree.kind('foo'))
854+ self.assertEqual('bar', tree.get_symlink_target('foo'))
855
856 def test_commit_trivial(self):
857 """Smoke test for commit on a MemoryTree.
858@@ -149,40 +149,35 @@
859 """
860 branch = self.make_branch('branch')
861 tree = MemoryTree.create_on_branch(branch)
862- tree.lock_write()
863- tree.add(['', 'foo'], ids=[b'root-id', b'foo-id'],
864- kinds=['directory', 'file'])
865- tree.put_file_bytes_non_atomic('foo', b'barshoom')
866- revision_id = tree.commit('message baby')
867- # the parents list for the tree should have changed.
868- self.assertEqual([revision_id], tree.get_parent_ids())
869- tree.unlock()
870+ with tree.lock_write():
871+ tree.add(['', 'foo'], ids=[b'root-id', b'foo-id'],
872+ kinds=['directory', 'file'])
873+ tree.put_file_bytes_non_atomic('foo', b'barshoom')
874+ revision_id = tree.commit('message baby')
875+ # the parents list for the tree should have changed.
876+ self.assertEqual([revision_id], tree.get_parent_ids())
877 # and we should have a revision that is accessible outside the tree lock
878 revtree = tree.branch.repository.revision_tree(revision_id)
879- revtree.lock_read()
880- self.addCleanup(revtree.unlock)
881- with revtree.get_file('foo') as f:
882+ with revtree.lock_read(), revtree.get_file('foo') as f:
883 self.assertEqual(b'barshoom', f.read())
884
885 def test_unversion(self):
886 """Some test for unversion of a memory tree."""
887 branch = self.make_branch('branch')
888 tree = MemoryTree.create_on_branch(branch)
889- tree.lock_write()
890- tree.add(['', 'foo'], ids=[b'root-id', b'foo-id'],
891- kinds=['directory', 'file'])
892- tree.unversion(['foo'])
893- self.assertFalse(tree.is_versioned('foo'))
894- self.assertFalse(tree.has_id(b'foo-id'))
895- tree.unlock()
896+ with tree.lock_write():
897+ tree.add(['', 'foo'], ids=[b'root-id', b'foo-id'],
898+ kinds=['directory', 'file'])
899+ tree.unversion(['foo'])
900+ self.assertFalse(tree.is_versioned('foo'))
901+ self.assertFalse(tree.has_id(b'foo-id'))
902
903 def test_last_revision(self):
904 """There should be a last revision method we can call."""
905 tree = self.make_branch_and_memory_tree('branch')
906- tree.lock_write()
907- tree.add('')
908- rev_id = tree.commit('first post')
909- tree.unlock()
910+ with tree.lock_write():
911+ tree.add('')
912+ rev_id = tree.commit('first post')
913 self.assertEqual(rev_id, tree.last_revision())
914
915 def test_rename_file(self):
916
917=== modified file 'breezy/tests/test_urlutils.py'
918--- breezy/tests/test_urlutils.py 2018-11-23 03:06:01 +0000
919+++ breezy/tests/test_urlutils.py 2019-03-06 14:35:55 +0000
920@@ -599,6 +599,9 @@
921 self.assertEqual(("foo/base,key1=val1/other/elements",
922 {"key2": "val2"}), split_segment_parameters(
923 "foo/base,key1=val1/other/elements,key2=val2"))
924+ self.assertRaises(
925+ urlutils.InvalidURL, split_segment_parameters,
926+ "foo/base,key1")
927 # TODO: Check full URLs as well as relative references
928
929 def test_win32_strip_local_trailing_slash(self):
930
931=== modified file 'breezy/transport/memory.py'
932--- breezy/transport/memory.py 2018-11-17 16:53:10 +0000
933+++ breezy/transport/memory.py 2019-03-06 14:35:55 +0000
934@@ -26,9 +26,10 @@
935 from io import (
936 BytesIO,
937 )
938+import itertools
939 import os
940 import errno
941-from stat import S_IFREG, S_IFDIR, S_IFLNK
942+from stat import S_IFREG, S_IFDIR, S_IFLNK, S_ISDIR
943
944 from .. import (
945 transport,
946@@ -50,20 +51,16 @@
947
948 class MemoryStat(object):
949
950- def __init__(self, size, kind, perms):
951+ def __init__(self, size, kind, perms=None):
952 self.st_size = size
953- if kind == 'file':
954+ if not S_ISDIR(kind):
955 if perms is None:
956 perms = 0o644
957- self.st_mode = S_IFREG | perms
958- elif kind == 'directory':
959+ self.st_mode = kind | perms
960+ else:
961 if perms is None:
962 perms = 0o755
963- self.st_mode = S_IFDIR | perms
964- elif kind == 'symlink':
965- self.st_mode = S_IFLNK | 0o644
966- else:
967- raise AssertionError('unknown kind %r' % kind)
968+ self.st_mode = kind | perms
969
970
971 class MemoryTransport(transport.Transport):
972@@ -111,7 +108,7 @@
973
974 def append_file(self, relpath, f, mode=None):
975 """See Transport.append_file()."""
976- _abspath = self._abspath(relpath)
977+ _abspath = self._resolve_symlinks(relpath)
978 self._check_parent(_abspath)
979 orig_content, orig_mode = self._files.get(_abspath, (b"", None))
980 if mode is None:
981@@ -128,16 +125,20 @@
982 def has(self, relpath):
983 """See Transport.has()."""
984 _abspath = self._abspath(relpath)
985- return ((_abspath in self._files)
986- or (_abspath in self._dirs)
987- or (_abspath in self._symlinks))
988+ for container in (self._files, self._dirs, self._symlinks):
989+ if _abspath in container.keys():
990+ return True
991+ return False
992
993 def delete(self, relpath):
994 """See Transport.delete()."""
995 _abspath = self._abspath(relpath)
996- if _abspath not in self._files:
997+ if _abspath in self._files:
998+ del self._files[_abspath]
999+ elif _abspath in self._symlinks:
1000+ del self._symlinks[_abspath]
1001+ else:
1002 raise NoSuchFile(relpath)
1003- del self._files[_abspath]
1004
1005 def external_url(self):
1006 """See breezy.transport.Transport.external_url."""
1007@@ -147,7 +148,7 @@
1008
1009 def get(self, relpath):
1010 """See Transport.get()."""
1011- _abspath = self._abspath(relpath)
1012+ _abspath = self._resolve_symlinks(relpath)
1013 if _abspath not in self._files:
1014 if _abspath in self._dirs:
1015 return LateReadError(relpath)
1016@@ -157,15 +158,20 @@
1017
1018 def put_file(self, relpath, f, mode=None):
1019 """See Transport.put_file()."""
1020- _abspath = self._abspath(relpath)
1021+ _abspath = self._resolve_symlinks(relpath)
1022 self._check_parent(_abspath)
1023 raw_bytes = f.read()
1024 self._files[_abspath] = (raw_bytes, mode)
1025 return len(raw_bytes)
1026
1027+ def symlink(self, source, target):
1028+ _abspath = self._resolve_symlinks(target)
1029+ self._check_parent(_abspath)
1030+ self._symlinks[_abspath] = self._abspath(source)
1031+
1032 def mkdir(self, relpath, mode=None):
1033 """See Transport.mkdir()."""
1034- _abspath = self._abspath(relpath)
1035+ _abspath = self._resolve_symlinks(relpath)
1036 self._check_parent(_abspath)
1037 if _abspath in self._dirs:
1038 raise FileExists(relpath)
1039@@ -183,13 +189,13 @@
1040 return True
1041
1042 def iter_files_recursive(self):
1043- for file in self._files:
1044+ for file in itertools.chain(self._files, self._symlinks):
1045 if file.startswith(self._cwd):
1046 yield urlutils.escape(file[len(self._cwd):])
1047
1048 def list_dir(self, relpath):
1049 """See Transport.list_dir()."""
1050- _abspath = self._abspath(relpath)
1051+ _abspath = self._resolve_symlinks(relpath)
1052 if _abspath != '/' and _abspath not in self._dirs:
1053 raise NoSuchFile(relpath)
1054 result = []
1055@@ -197,7 +203,7 @@
1056 if not _abspath.endswith('/'):
1057 _abspath += '/'
1058
1059- for path_group in self._files, self._dirs:
1060+ for path_group in self._files, self._dirs, self._symlinks:
1061 for path in path_group:
1062 if path.startswith(_abspath):
1063 trailing = path[len(_abspath):]
1064@@ -207,8 +213,8 @@
1065
1066 def rename(self, rel_from, rel_to):
1067 """Rename a file or directory; fail if the destination exists"""
1068- abs_from = self._abspath(rel_from)
1069- abs_to = self._abspath(rel_to)
1070+ abs_from = self._resolve_symlinks(rel_from)
1071+ abs_to = self._resolve_symlinks(rel_to)
1072
1073 def replace(x):
1074 if x == abs_from:
1075@@ -233,21 +239,25 @@
1076 # fail differently depending on dict order. So work on copy, fail on
1077 # error on only replace dicts if all goes well.
1078 renamed_files = self._files.copy()
1079+ renamed_symlinks = self._symlinks.copy()
1080 renamed_dirs = self._dirs.copy()
1081 do_renames(renamed_files)
1082+ do_renames(renamed_symlinks)
1083 do_renames(renamed_dirs)
1084 # We may have been cloned so modify in place
1085 self._files.clear()
1086 self._files.update(renamed_files)
1087+ self._symlinks.clear()
1088+ self._symlinks.update(renamed_symlinks)
1089 self._dirs.clear()
1090 self._dirs.update(renamed_dirs)
1091
1092 def rmdir(self, relpath):
1093 """See Transport.rmdir."""
1094- _abspath = self._abspath(relpath)
1095+ _abspath = self._resolve_symlinks(relpath)
1096 if _abspath in self._files:
1097 self._translate_error(IOError(errno.ENOTDIR, relpath), relpath)
1098- for path in self._files:
1099+ for path in itertools.chain(self._files, self._symlinks):
1100 if path.startswith(_abspath + '/'):
1101 self._translate_error(IOError(errno.ENOTEMPTY, relpath),
1102 relpath)
1103@@ -262,13 +272,13 @@
1104 def stat(self, relpath):
1105 """See Transport.stat()."""
1106 _abspath = self._abspath(relpath)
1107- if _abspath in self._files:
1108- return MemoryStat(len(self._files[_abspath][0]), 'file',
1109+ if _abspath in self._files.keys():
1110+ return MemoryStat(len(self._files[_abspath][0]), S_IFREG,
1111 self._files[_abspath][1])
1112- elif _abspath in self._dirs:
1113- return MemoryStat(0, 'directory', self._dirs[_abspath])
1114- elif _abspath in self._symlinks:
1115- return MemoryStat(0, 'symlink', 0)
1116+ elif _abspath in self._dirs.keys():
1117+ return MemoryStat(0, S_IFDIR, self._dirs[_abspath])
1118+ elif _abspath in self._symlinks.keys():
1119+ return MemoryStat(0, S_IFLNK)
1120 else:
1121 raise NoSuchFile(_abspath)
1122
1123@@ -280,6 +290,12 @@
1124 """See Transport.lock_write()."""
1125 return _MemoryLock(self._abspath(relpath), self)
1126
1127+ def _resolve_symlinks(self, relpath):
1128+ path = self._abspath(relpath)
1129+ while path in self._symlinks.keys():
1130+ path = self._symlinks[path]
1131+ return path
1132+
1133 def _abspath(self, relpath):
1134 """Generate an internal absolute path."""
1135 relpath = urlutils.unescape(relpath)
1136@@ -336,6 +352,7 @@
1137 def start_server(self):
1138 self._dirs = {'/': None}
1139 self._files = {}
1140+ self._symlinks = {}
1141 self._locks = {}
1142 self._scheme = "memory+%s:///" % id(self)
1143
1144@@ -344,6 +361,7 @@
1145 result = memory.MemoryTransport(url)
1146 result._dirs = self._dirs
1147 result._files = self._files
1148+ result._symlinks = self._symlinks
1149 result._locks = self._locks
1150 return result
1151 self._memory_factory = memory_factory
1152
1153=== modified file 'breezy/urlutils.py'
1154--- breezy/urlutils.py 2018-11-16 23:19:12 +0000
1155+++ breezy/urlutils.py 2019-03-06 14:35:55 +0000
1156@@ -561,7 +561,10 @@
1157 (base_url, subsegments) = split_segment_parameters_raw(url)
1158 parameters = {}
1159 for subsegment in subsegments:
1160- (key, value) = subsegment.split("=", 1)
1161+ try:
1162+ (key, value) = subsegment.split("=", 1)
1163+ except ValueError:
1164+ raise InvalidURL(url, "missing = in subsegment")
1165 if not isinstance(key, str):
1166 raise TypeError(key)
1167 if not isinstance(value, str):
1168
1169=== modified file 'brz'
1170--- brz 2018-11-18 12:41:12 +0000
1171+++ brz 2019-03-06 14:35:55 +0000
1172@@ -25,7 +25,7 @@
1173 import warnings
1174
1175 # update this on each release
1176-_script_version = (3, 0, 0)
1177+_script_version = (3, 1, 0)
1178
1179 NEED_VERS = (2, 7)
1180
1181
1182=== modified file 'byov.conf'
1183--- byov.conf 2018-12-19 01:30:58 +0000
1184+++ byov.conf 2019-03-06 14:35:55 +0000
1185@@ -13,6 +13,8 @@
1186 sphinx_epytext.install3 = (pip3 install sphinx_epytext)
1187 flake8.install = (pip install flake8)
1188 flake8.install3 = (pip3 install flake8)
1189+cython.install = (pip install cython)
1190+cython.install3 = (pip3 install cython)
1191
1192 [brz]
1193 # FIXME: we're stuck on xenial
1194@@ -25,12 +27,12 @@
1195
1196 # FIXME: Arguably this should be vm.build_deps=brz but it requires either an
1197 # available package or at least a debian/ dir ? -- vila 2018-02-23
1198-brz.build_deps = gcc, debhelper, python, python-all-dev, python3-all-dev, python-configobj, python3-configobj, python-docutils, python3-docutils, python-paramiko, python3-paramiko, python-subunit, python3-subunit, python-testtools, python3-testtools, subunit, cython, cython3, python-pip, python3-pip, python-setuptools, python3-setuptools, python-flake8, python3-flake8, python-sphinx, python3-sphinx, python-launchpadlib, python3-launchpadlib
1199+brz.build_deps = gcc, debhelper, python, python-all-dev, python3-all-dev, python-configobj, python3-configobj, python-docutils, python3-docutils, python-paramiko, python3-paramiko, python-subunit, python3-subunit, python-testtools, python3-testtools, subunit, python-pip, python3-pip, python-setuptools, python3-setuptools, python-flake8, python3-flake8, python-sphinx, python3-sphinx, python-launchpadlib, python3-launchpadlib
1200 subunit.build_deps = python-testscenarios, python3-testscenarios, python-testtools, python3-testtools
1201 vm.packages = {brz.build_deps}, {subunit.build_deps}, bzr, git, python-junitxml
1202 [brz-xenial]
1203 vm.release = xenial
1204-byoci.setup.command = ({dulwich.clone} && {dulwich.install} && {dulwich.install3} && {fastimport.clone} && {fastimport.install} && {fastimport.install3} && {subunit.clone} && {flake8.install} && {flake8.install3} && {sphinx_epytext.install} && {sphinx_epytext.install3})
1205+byoci.setup.command = ({dulwich.clone} && {dulwich.install} && {dulwich.install3} && {fastimport.clone} && {fastimport.install} && {fastimport.install3} && {subunit.clone} && {flake8.install} && {flake8.install3} && {sphinx_epytext.install} && {sphinx_epytext.install3} && {cython.install} && {cython.install3})
1206 # FIXME: bzr log -l2 should be by default -- vila 2018-03-09
1207 byoci.tests.command = bash -o pipefail -c "bzr log -l2 && PYTHONPATH=../subunit/python:$PYTHONPATH PATH=../subunit/filters:$PATH make check-ci | subunit2junitxml -o ../results.xml -f | subunit2pyunit"
1208 [brz-bionic]
1209
1210=== modified file 'doc/developers/apport.txt'
1211--- doc/developers/apport.txt 2009-12-02 20:34:07 +0000
1212+++ doc/developers/apport.txt 2019-03-06 14:35:55 +0000
1213@@ -41,7 +41,7 @@
1214
1215 #. An exception reaches the top-level handler.
1216
1217-#. We log it in apport-format to a file in ~/.bazaar/crash.
1218+#. We log it in apport-format to a file in ~/.config/breezy/crash.
1219
1220 #. We tell the user where that file is, and invite them to file a bug
1221 report.
1222
1223=== modified file 'doc/developers/configuration.txt'
1224--- doc/developers/configuration.txt 2017-07-30 16:59:50 +0000
1225+++ doc/developers/configuration.txt 2019-03-06 14:35:55 +0000
1226@@ -154,20 +154,6 @@
1227 though). The ``bzr-bookmarks`` plugin defines a ``BOOKMARKS`` section there
1228 for example.
1229
1230-pkgimport.conf
1231-==============
1232-
1233-The Ubuntu package importer defines a store and two stacks involving
1234-``pkgimport.conf``. A no-name section contains the options common to all
1235-packages and sections named after their corresponding package can also be
1236-defined.
1237-
1238-The ``ImporterStack`` uses ``locations.conf`` and the no-name section in
1239-``pkgimport.conf`` for the importer options.
1240-
1241-The ``PackageStack`` uses only ``pkgimport.conf`` and uses the section name
1242-after the package followed by the no-name section.
1243-
1244 location.conf
1245 =============
1246
1247
1248=== modified file 'doc/developers/contribution-quickstart.txt'
1249--- doc/developers/contribution-quickstart.txt 2019-01-01 21:38:07 +0000
1250+++ doc/developers/contribution-quickstart.txt 2019-03-06 14:35:55 +0000
1251@@ -46,7 +46,7 @@
1252 closely-related cluster of bugs per branch, to make reviews and merges
1253 flow more smoothly.
1254
1255-You probably want this configuration in ``~/.bazaar/locations.conf``::
1256+You probably want this configuration in ``~/.config/breezy/locations.conf``::
1257
1258 [/home/USER/brz]
1259 push_location = lp:~LAUNCHPAD_USER/brz/{branchname}
1260
1261=== modified file 'doc/developers/plugin-development.txt'
1262--- doc/developers/plugin-development.txt 2018-11-22 02:23:26 +0000
1263+++ doc/developers/plugin-development.txt 2019-03-06 14:35:55 +0000
1264@@ -31,7 +31,7 @@
1265 To ensure your plugin under development is available to Breezy, set
1266 the ``BRZ_PLUGIN_PATH`` environment variable to its parent directory.
1267 Alternatively, you may wish to develop your plugin within a directory
1268-under your personal plugins area (``~/.bazaar/plugins`` on GNU/Linux)
1269+under your personal plugins area (``~/.config/breezy/plugins`` on GNU/Linux)
1270 or put a symbolic link in that area pointing to your plugin under
1271 test. Finally you can use ``BRZ_PLUGINS_AT`` to point to a specific
1272 directory for a specific plugin (separated by your platform's value of
1273
1274=== modified file 'doc/en/admin-guide/hooks-plugins.txt'
1275--- doc/en/admin-guide/hooks-plugins.txt 2018-11-18 13:20:01 +0000
1276+++ doc/en/admin-guide/hooks-plugins.txt 2019-03-06 14:35:55 +0000
1277@@ -25,10 +25,10 @@
1278 http://doc.bazaar.canonical.com/plugins/en/. For purposes of installation,
1279 plugins are simply python packages. They can be installed alongside Bazaar in
1280 the ``bzrlib.plugins`` package using each plugin's ``setup.py``. They can
1281-also be installed in the plugin path which is the user's ``~/.bazaar/plugins``
1282-directory or can be specified with the ``BZR_PLUGIN_PATH`` environment
1283-variable. See ``bzr help configuration`` for more on specifying the location
1284-of plugins.
1285+also be installed in the plugin path which is the user's
1286+``~/.config/breezy/plugins`` directory or can be specified with the
1287+``BZR_PLUGIN_PATH`` environment variable. See ``bzr help configuration`` for
1288+more on specifying the location of plugins.
1289
1290
1291 Email Notification
1292
1293=== modified file 'doc/en/admin-guide/migration.txt'
1294--- doc/en/admin-guide/migration.txt 2018-11-18 13:20:01 +0000
1295+++ doc/en/admin-guide/migration.txt 2019-03-06 14:35:55 +0000
1296@@ -56,7 +56,7 @@
1297 This command has flexible ways to specify what paths within the Subversion
1298 repository contain branches and which contain tags. For example, the
1299 recommended layout for Subversion projects (called ``trunk`` by the svn
1300-plugin) could be specified in ``~/.bazaar/subversion.conf`` as
1301+plugin) could be specified in ``~/.config/breezy/subversion.conf`` as
1302
1303 ::
1304
1305
1306=== modified file 'doc/en/index.txt'
1307--- doc/en/index.txt 2018-09-14 09:31:24 +0000
1308+++ doc/en/index.txt 2019-03-06 14:35:55 +0000
1309@@ -10,7 +10,7 @@
1310 .. toctree::
1311 :maxdepth: 1
1312
1313- whats-new/whats-new-in-3.0
1314+ whats-new/whats-new-in-3.1
1315 user-guide/index
1316 tutorials/index
1317 quick-reference/index
1318
1319=== modified file 'doc/en/release-notes/brz-3.0.txt'
1320--- doc/en/release-notes/brz-3.0.txt 2019-03-02 22:31:28 +0000
1321+++ doc/en/release-notes/brz-3.0.txt 2019-03-06 14:35:55 +0000
1322@@ -5,16 +5,16 @@
1323 .. toctree::
1324 :maxdepth: 1
1325
1326+brz 3.0.0
1327+#########
1328+
1329+:Codename: Pelican
1330+:3.0.0: 2019-03-06
1331+
1332 brz 3.0.0 is the first release of ``Breezy``, a fork of Bazaar. For more
1333 information, see our release announcement on the Bazaar mailing list and
1334 README.
1335
1336-brz 3.0.0
1337-#########
1338-
1339-:Codename: Pelican
1340-:3.0.0: NOT RELEASED YET
1341-
1342 External Compatibility Breaks
1343 *****************************
1344
1345@@ -85,6 +85,9 @@
1346 longer performed, since there are no longer
1347 any automated imports. (Jelmer Vernooij)
1348
1349+ * ``setuptools`` is now required to build and install Breezy.
1350+ (Jelmer Vernooij)
1351+
1352 New Features
1353 ************
1354
1355@@ -234,6 +237,13 @@
1356 * Don't report .git files as unknown files.
1357 (Jelmer Vernooij, Debian Bug #921240)
1358
1359+* Raise better error when path subsegments lack =.
1360+ (Jelmer Vernooij, #891483)
1361+
1362+* Display correct pull location argument in
1363+ output of ``brz uncommit``.
1364+ (Jelmer Vernooij, #386577)
1365+
1366 Documentation
1367 *************
1368
1369
1370=== added file 'doc/en/release-notes/brz-3.1.txt'
1371--- doc/en/release-notes/brz-3.1.txt 1970-01-01 00:00:00 +0000
1372+++ doc/en/release-notes/brz-3.1.txt 2019-03-06 14:35:55 +0000
1373@@ -0,0 +1,62 @@
1374+####################
1375+Breezy Release Notes
1376+####################
1377+
1378+.. toctree::
1379+ :maxdepth: 1
1380+
1381+brz 3.1.0
1382+#########
1383+
1384+:Codename: Nirvana
1385+:3.1.0: NOT RELEASED YET
1386+
1387+External Compatibility Breaks
1388+*****************************
1389+
1390+.. These may require users to change the way they use Breezy.
1391+
1392+New Features
1393+************
1394+
1395+.. New commands, options, etc that users may wish to try out.
1396+
1397+Improvements
1398+************
1399+
1400+.. Improvements to existing commands, especially improved performance
1401+ or memory usage, or better results.
1402+
1403+Bug Fixes
1404+*********
1405+
1406+.. Fixes for situations where brz would previously crash or give incorrect
1407+ or undesirable results.
1408+
1409+Documentation
1410+*************
1411+
1412+.. Improved or updated documentation.
1413+
1414+API Changes
1415+***********
1416+
1417+.. Changes that may require updates in plugins or other code that uses
1418+ breezy.
1419+
1420+Internals
1421+*********
1422+
1423+.. Major internal changes, unlikely to be visible to users or plugin
1424+ developers, but interesting for brz developers.
1425+
1426+Testing
1427+*******
1428+
1429+.. Fixes and changes that are only relevant to brz's test framework and
1430+ suite. This can include new facilities for writing tests, fixes to
1431+ spurious test failures and changes to the way things should be tested.
1432+
1433+
1434+..
1435+ vim: tw=74 ft=rst ff=unix
1436
1437=== modified file 'doc/en/user-guide/controlling_registration.txt'
1438--- doc/en/user-guide/controlling_registration.txt 2017-05-21 13:41:54 +0000
1439+++ doc/en/user-guide/controlling_registration.txt 2019-03-06 14:35:55 +0000
1440@@ -95,12 +95,12 @@
1441 There are some ignored files which are not project specific, but more user
1442 specific. Things like editor temporary files, or personal temporary files.
1443 Rather than add these ignores to every project, brz supports a global
1444-ignore file in ``~/.bazaar/ignore`` [#]_. It has the same syntax as the
1445+ignore file in ``~/.config/breezy/ignore`` [#]_. It has the same syntax as the
1446 per-project ignore file.
1447
1448 .. [#] On Windows, the users configuration files can be found in the
1449- application data directory. So instead of ``~/.bazaar/branch.conf``
1450+ application data directory. So instead of ``~/.config/breezy/branch.conf``
1451 the configuration file can be found as:
1452- ``C:\Documents and Settings\<username>\Application Data\Breezy\2.0\branch.conf``.
1453+ ``C:\Documents and Settings\<username>\Application Data\Breezy\3.0\branch.conf``.
1454 The same is true for ``locations.conf``, ``ignore``, and the
1455 ``plugins`` directory.
1456
1457=== added file 'doc/en/user-guide/git_limitations.txt'
1458--- doc/en/user-guide/git_limitations.txt 1970-01-01 00:00:00 +0000
1459+++ doc/en/user-guide/git_limitations.txt 2019-03-06 14:35:55 +0000
1460@@ -0,0 +1,18 @@
1461+Git Limitations
1462+===============
1463+
1464+Breezy's support for Git repositories currently has the following limitations:
1465+
1466+* No support for creating annotated tags. pad.lv/1758185
1467+* No support for gitattributes and the features that depend on it (lfs, eol, etc). pad.lv/1802797
1468+* Limited support for submodules. pad.lv/402814
1469+* No rename/copy inference. pad.lv/1760740
1470+* No support for creating shallow branches (pad.lv/1760151)
1471+* No good way to refer to git remotes from the command-line (pad.lv/1702283)
1472+* No mailmap support (pad.lv/544031)
1473+
1474+Functionality similar to git that's missing:
1475+
1476+* No rebase command. pad.lv/1708046
1477+* No --amend option for commit. pad.lv/507529
1478+* No clone/fetch commands. pad.lv/831939
1479
1480=== modified file 'doc/en/user-guide/index-plain.txt'
1481--- doc/en/user-guide/index-plain.txt 2018-11-24 15:56:05 +0000
1482+++ doc/en/user-guide/index-plain.txt 2019-03-06 14:35:55 +0000
1483@@ -114,6 +114,7 @@
1484 .. include:: setting_up_email.txt
1485 .. include:: http_smart_server.txt
1486 .. include:: writing_a_plugin.txt
1487+.. include:: git_limitations.txt
1488 .. include:: licence.txt
1489
1490 .. |--| unicode:: U+2014
1491
1492=== modified file 'doc/en/user-guide/installing_breezy.txt'
1493--- doc/en/user-guide/installing_breezy.txt 2018-11-18 13:43:04 +0000
1494+++ doc/en/user-guide/installing_breezy.txt 2019-03-06 14:35:55 +0000
1495@@ -90,7 +90,7 @@
1496 3. Put the created directory on your PATH.
1497
1498 Advanced users may also wish to build the optional C extensions for greater
1499-speed. This can be done using ``make`` and requires ``pyrex`` and a C compiler.
1500+speed. This can be done using ``make`` and requires ``cython`` and a C compiler.
1501 Please contact us on email or IRC if you need assistance with this.
1502
1503
1504
1505=== modified file 'doc/en/user-guide/plugins.txt'
1506--- doc/en/user-guide/plugins.txt 2017-05-21 13:41:54 +0000
1507+++ doc/en/user-guide/plugins.txt 2019-03-06 14:35:55 +0000
1508@@ -35,7 +35,7 @@
1509
1510 Installing a plugin is very easy! If not already created, create a
1511 ``plugins`` directory under your Breezy configuration directory,
1512-``~/.bazaar/`` on Unix and
1513+``~/.config/breezy/`` on Unix and
1514 ``C:\Documents and Settings\<username>\Application Data\Breezy\2.0\``
1515 on Windows. Within this directory (referred to as $BZR_HOME below),
1516 each plugin is placed in its own subdirectory.
1517@@ -45,7 +45,7 @@
1518 one can perform the following::
1519
1520 brz branch http://panoramicfeedback.com/opensource/brz/brztools
1521- ~/.bazaar/plugins/brztools
1522+ ~/.config/breezy/plugins/brztools
1523
1524 When installing plugins, the directories that you install them in must
1525 be valid python identifiers. This means that they can only contain
1526
1527=== modified file 'doc/en/whats-new/template.txt'
1528--- doc/en/whats-new/template.txt 2011-07-18 15:58:50 +0000
1529+++ doc/en/whats-new/template.txt 2019-03-06 14:35:55 +0000
1530@@ -1,8 +1,8 @@
1531 *************************
1532-What's New in Bazaar x.y?
1533+What's New in Breezy x.y?
1534 *************************
1535
1536-Bazaar x.y is still under development, and will be released in Month Year.
1537+Breezy x.y is still under development, and will be released in Month Year.
1538 This document accumulates a high level summary of what's changed. See the
1539 :doc:`../release-notes/index` for a full list.
1540
1541@@ -14,7 +14,7 @@
1542 For more detailed information on the changes made, see the the
1543 :doc:`../release-notes/index` for:
1544
1545-* the interim bzr `milestones <https://launchpad.net/bzr/x.y>`_
1546+* the interim brz `milestones <https://launchpad.net/bzr/x.y>`_
1547 * the plugins you use.
1548
1549 For a summary of changes made in earlier releases, see:
1550
1551=== added file 'doc/en/whats-new/whats-new-in-3.1.txt'
1552--- doc/en/whats-new/whats-new-in-3.1.txt 1970-01-01 00:00:00 +0000
1553+++ doc/en/whats-new/whats-new-in-3.1.txt 2019-03-06 14:35:55 +0000
1554@@ -0,0 +1,22 @@
1555+*************************
1556+What's New in Breezy 3.1?
1557+*************************
1558+
1559+Breezy 3.1 is still under development, and will be released when it is
1560+ready. This document accumulates a high level summary of what's changed.
1561+See the :doc:`../release-notes/index` for a full list.
1562+
1563+<topics of interest here>
1564+
1565+Further information
1566+*******************
1567+
1568+For more detailed information on the changes made, see the the
1569+:doc:`../release-notes/index` for:
1570+
1571+* the interim brz `milestones <https://launchpad.net/brz/3.1>`_
1572+* the plugins you use.
1573+
1574+For a summary of changes made in earlier releases, see:
1575+
1576+* :doc:`whats-new-in-3.0`
1577
1578=== modified file 'setup.py'
1579--- setup.py 2019-02-06 05:44:37 +0000
1580+++ setup.py 2019-03-06 14:35:55 +0000
1581@@ -16,6 +16,14 @@
1582 sys.stderr.write("[ERROR] Not a supported Python version. Need 2.7+\n")
1583 sys.exit(1)
1584
1585+
1586+try:
1587+ import setuptools
1588+except ImportError:
1589+ sys.stderr.write("[ERROR] Please install setuptools\n")
1590+ sys.exit(1)
1591+
1592+
1593 # NOTE: The directory containing setup.py, whether run by 'python setup.py' or
1594 # './setup.py' or the equivalent with another path, should always be at the
1595 # start of the path, so this should find the right one...
1596@@ -118,8 +126,7 @@
1597 BREEZY['packages'] = get_breezy_packages()
1598
1599
1600-from distutils import log
1601-from distutils.core import setup
1602+from setuptools import setup
1603 from distutils.version import LooseVersion
1604 from distutils.command.install_scripts import install_scripts
1605 from distutils.command.install_data import install_data
1606@@ -211,8 +218,17 @@
1607 print("")
1608 from distutils.command.build_ext import build_ext
1609 else:
1610- have_cython = True
1611+ minimum_cython_version = '0.29'
1612 cython_version_info = LooseVersion(cython_version)
1613+ if cython_version_info < LooseVersion(minimum_cython_version):
1614+ print("Version of Cython is too old. "
1615+ "Current is %s, need at least %s."
1616+ % (cython_version, minimum_cython_version))
1617+ print("If the .c files are available, they will be built,"
1618+ " but modifying the .pyx files will not rebuild them.")
1619+ have_cython = False
1620+ else:
1621+ have_cython = True
1622
1623
1624 class build_ext_if_possible(build_ext):

Subscribers

People subscribed via source and target branches