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/merge-3.1 |
Merge into: | lp:brz |
Diff against target: |
1001 lines (+319/-93) 27 files modified
SECURITY.md (+12/-0) breezy/builtins.py (+2/-2) breezy/bzr/workingtree.py (+3/-3) breezy/git/branch.py (+20/-7) breezy/git/interrepo.py (+2/-2) breezy/git/mapping.py (+2/-0) breezy/git/remote.py (+1/-1) breezy/git/tests/test_mapping.py (+20/-0) breezy/git/tests/test_transform.py (+14/-1) breezy/git/transform.py (+1/-1) breezy/git/workingtree.py (+3/-3) breezy/merge.py (+1/-1) breezy/plugins/github/hoster.py (+17/-3) breezy/plugins/gitlab/hoster.py (+58/-23) breezy/plugins/launchpad/hoster.py (+2/-1) breezy/plugins/pypi/__init__.py (+29/-0) breezy/plugins/pypi/directory.py (+78/-0) breezy/plugins/quilt/tests/test_merge.py (+2/-2) breezy/propose.py (+1/-1) breezy/tests/per_workingtree/test_commit.py (+3/-3) breezy/tests/per_workingtree/test_merge_from_branch.py (+9/-9) breezy/tests/per_workingtree/test_unversion.py (+3/-3) breezy/tests/test_merge.py (+18/-18) breezy/tests/test_merge_core.py (+9/-7) breezy/transform.py (+1/-1) breezy/workspace.py (+1/-1) doc/en/release-notes/brz-3.1.txt (+7/-0) |
To merge this branch: | bzr merge lp:~jelmer/brz/merge-3.1 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jelmer Vernooij | Approve | ||
Review via email: mp+400002@code.launchpad.net |
Commit message
Merge lp:brz/3.1.
Description of the change
Merge lp:brz/3.1.
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 | === added file 'SECURITY.md' |
2 | --- SECURITY.md 1970-01-01 00:00:00 +0000 |
3 | +++ SECURITY.md 2021-03-22 21:17:36 +0000 |
4 | @@ -0,0 +1,12 @@ |
5 | +# Security Policy |
6 | + |
7 | +## Supported Versions |
8 | + |
9 | +| Version | Supported | |
10 | +| -------- | ------------------ | |
11 | +| 3.1.x | :white_check_mark: | |
12 | +| 3.0.x | :x: | |
13 | + |
14 | +## Reporting a Vulnerability |
15 | + |
16 | +Please report security issues by e-mail to breezy-core@googlegroups.com. |
17 | |
18 | === modified file 'breezy/builtins.py' |
19 | --- breezy/builtins.py 2020-08-10 15:00:17 +0000 |
20 | +++ breezy/builtins.py 2021-03-22 21:17:36 +0000 |
21 | @@ -4584,7 +4584,7 @@ |
22 | |
23 | def _do_merge(self, merger, change_reporter, allow_pending, verified): |
24 | merger.change_reporter = change_reporter |
25 | - conflict_count = merger.do_merge() |
26 | + conflict_count = len(merger.do_merge()) |
27 | if allow_pending: |
28 | merger.set_pending() |
29 | if verified == 'failed': |
30 | @@ -4842,7 +4842,7 @@ |
31 | conflicts = merger.do_merge() |
32 | finally: |
33 | tree.set_parent_ids(parents) |
34 | - if conflicts > 0: |
35 | + if len(conflicts) > 0: |
36 | return 1 |
37 | else: |
38 | return 0 |
39 | |
40 | === modified file 'breezy/bzr/workingtree.py' |
41 | --- breezy/bzr/workingtree.py 2020-11-18 02:15:43 +0000 |
42 | +++ breezy/bzr/workingtree.py 2021-03-22 21:17:36 +0000 |
43 | @@ -1932,7 +1932,7 @@ |
44 | # local work is unreferenced and will appear to have been lost. |
45 | # |
46 | with self.lock_tree_write(): |
47 | - nb_conflicts = 0 |
48 | + nb_conflicts = [] |
49 | try: |
50 | last_rev = self.get_parent_ids()[0] |
51 | except IndexError: |
52 | @@ -1953,7 +1953,7 @@ |
53 | show_base=show_base) |
54 | if nb_conflicts: |
55 | self.add_parent_tree((old_tip, other_tree)) |
56 | - return nb_conflicts |
57 | + return len(nb_conflicts) |
58 | |
59 | if last_rev != _mod_revision.ensure_null(revision): |
60 | # the working tree is up to date with the branch |
61 | @@ -1995,7 +1995,7 @@ |
62 | (old_tip, self.branch.repository.revision_tree(old_tip))) |
63 | self.set_parent_trees(parent_trees) |
64 | last_rev = parent_trees[0][0] |
65 | - return nb_conflicts |
66 | + return len(nb_conflicts) |
67 | |
68 | |
69 | class WorkingTreeFormatMetaDir(bzrdir.BzrFormat, WorkingTreeFormat): |
70 | |
71 | === modified file 'breezy/git/branch.py' |
72 | --- breezy/git/branch.py 2021-01-10 00:25:52 +0000 |
73 | +++ breezy/git/branch.py 2021-03-22 21:17:36 +0000 |
74 | @@ -1450,13 +1450,26 @@ |
75 | for k, v in self.source.tags.get_tag_dict().items(): |
76 | ret.append((None, v)) |
77 | ret.append((None, stop_revision)) |
78 | - try: |
79 | - revidmap = self.interrepo.fetch_revs(ret, lossy=lossy, limit=limit) |
80 | - except NoPushSupport: |
81 | - raise errors.NoRoundtrippingSupport(self.source, self.target) |
82 | - return _mod_repository.FetchResult(revidmap={ |
83 | - old_revid: new_revid |
84 | - for (old_revid, (new_sha, new_revid)) in revidmap.items()}) |
85 | + if getattr(self.interrepo, 'fetch_revs', None): |
86 | + try: |
87 | + revidmap = self.interrepo.fetch_revs(ret, lossy=lossy, limit=limit) |
88 | + except NoPushSupport: |
89 | + raise errors.NoRoundtrippingSupport(self.source, self.target) |
90 | + return _mod_repository.FetchResult(revidmap={ |
91 | + old_revid: new_revid |
92 | + for (old_revid, (new_sha, new_revid)) in revidmap.items()}) |
93 | + else: |
94 | + def determine_wants(refs): |
95 | + wants = [] |
96 | + for git_sha, revid in ret: |
97 | + if git_sha is None: |
98 | + git_sha, mapping = self.target.lookup_bzr_revision_id(revid) |
99 | + wants.append(git_sha) |
100 | + return wants |
101 | + |
102 | + self.interrepo.fetch_objects( |
103 | + determine_wants, lossy=lossy, limit=limit) |
104 | + return _mod_repository.FetchResult() |
105 | |
106 | def pull(self, overwrite=False, stop_revision=None, local=False, |
107 | possible_transports=None, run_hooks=True, _stop_revno=None, |
108 | |
109 | === modified file 'breezy/git/interrepo.py' |
110 | --- breezy/git/interrepo.py 2021-01-10 00:25:52 +0000 |
111 | +++ breezy/git/interrepo.py 2021-03-22 21:17:36 +0000 |
112 | @@ -801,9 +801,9 @@ |
113 | def git_update_refs(old_refs): |
114 | ret = {} |
115 | self.old_refs = { |
116 | - k: (v, None) for (k, v) in viewitems(old_refs)} |
117 | + k: (v, None) for (k, v) in old_refs.items()} |
118 | new_refs = update_refs(self.old_refs) |
119 | - for name, (gitid, revid) in viewitems(new_refs): |
120 | + for name, (gitid, revid) in new_refs.items(): |
121 | if gitid is None: |
122 | gitid = self.source_store._lookup_revision_sha1(revid) |
123 | if not overwrite: |
124 | |
125 | === modified file 'breezy/git/mapping.py' |
126 | --- breezy/git/mapping.py 2020-08-22 22:46:24 +0000 |
127 | +++ breezy/git/mapping.py 2021-03-22 21:17:36 +0000 |
128 | @@ -411,9 +411,11 @@ |
129 | rev.properties[u'author'] = commit.author.decode(encoding) |
130 | rev.message, rev.git_metadata = self._decode_commit_message( |
131 | rev, commit.message, encoding) |
132 | + |
133 | if commit.encoding is not None: |
134 | rev.properties[u'git-explicit-encoding'] = commit.encoding.decode( |
135 | 'ascii') |
136 | + if commit.encoding is not None and commit.encoding != b'false': |
137 | decode_using_encoding(rev, commit, commit.encoding.decode('ascii')) |
138 | else: |
139 | for encoding in ('utf-8', 'latin1'): |
140 | |
141 | === modified file 'breezy/git/remote.py' |
142 | --- breezy/git/remote.py 2020-08-10 15:00:17 +0000 |
143 | +++ breezy/git/remote.py 2021-03-22 21:17:36 +0000 |
144 | @@ -630,8 +630,8 @@ |
145 | except errors.NoSuchRevision: |
146 | raise errors.NoRoundtrippingSupport( |
147 | source, self.open_branch(name=name, nascent_ok=True)) |
148 | + old_sha = remote_refs.get(actual_refname) |
149 | if not overwrite: |
150 | - old_sha = remote_refs.get(actual_refname) |
151 | if remote_divergence(old_sha, new_sha, source_store): |
152 | raise DivergedBranches( |
153 | source, self.open_branch(name, nascent_ok=True)) |
154 | |
155 | === modified file 'breezy/git/tests/test_mapping.py' |
156 | --- breezy/git/tests/test_mapping.py 2020-06-10 23:47:24 +0000 |
157 | +++ breezy/git/tests/test_mapping.py 2021-03-22 21:17:36 +0000 |
158 | @@ -140,6 +140,26 @@ |
159 | self.assertEqual("iso8859-1", rev.properties[u"git-explicit-encoding"]) |
160 | self.assertTrue(u"git-implicit-encoding" not in rev.properties) |
161 | |
162 | + def test_explicit_encoding_false(self): |
163 | + c = Commit() |
164 | + c.tree = b"cc9462f7f8263ef5adfbeff2fb936bb36b504cba" |
165 | + c.message = b"Some message" |
166 | + c.committer = b"Committer" |
167 | + c.commit_time = 4 |
168 | + c.author_time = 5 |
169 | + c.commit_timezone = 60 * 5 |
170 | + c.author_timezone = 60 * 3 |
171 | + c.author = u"Authér".encode("utf-8") |
172 | + c.encoding = b"false" |
173 | + mapping = BzrGitMappingv1() |
174 | + rev, roundtrip_revid, verifiers = mapping.import_commit( |
175 | + c, mapping.revision_id_foreign_to_bzr) |
176 | + self.assertEqual(None, roundtrip_revid) |
177 | + self.assertEqual({}, verifiers) |
178 | + self.assertEqual(u"Authér", rev.properties[u'author']) |
179 | + self.assertEqual("false", rev.properties[u"git-explicit-encoding"]) |
180 | + self.assertTrue(u"git-implicit-encoding" not in rev.properties) |
181 | + |
182 | def test_implicit_encoding_fallback(self): |
183 | c = Commit() |
184 | c.tree = b"cc9462f7f8263ef5adfbeff2fb936bb36b504cba" |
185 | |
186 | === modified file 'breezy/git/tests/test_transform.py' |
187 | --- breezy/git/tests/test_transform.py 2020-09-02 16:35:18 +0000 |
188 | +++ breezy/git/tests/test_transform.py 2021-03-22 21:17:36 +0000 |
189 | @@ -20,7 +20,7 @@ |
190 | |
191 | import os |
192 | |
193 | -from ...transform import ROOT_PARENT, conflict_pass, resolve_conflicts |
194 | +from ...transform import ROOT_PARENT, conflict_pass, resolve_conflicts, revert |
195 | from . import TestCaseWithTransport |
196 | |
197 | |
198 | @@ -39,3 +39,16 @@ |
199 | self.assertEqual([], list(conflicts)) |
200 | tt.apply() |
201 | self.assertEqual(set(['name1', 'name2']), set(os.listdir('dir'))) |
202 | + |
203 | + def test_revert_does_not_remove(self): |
204 | + tree = self.make_branch_and_tree('.', format='git') |
205 | + tt = tree.transform() |
206 | + dir1 = tt.new_directory('dir', ROOT_PARENT) |
207 | + tid = tt.new_file('name1', dir1, [b'content1']) |
208 | + tt.version_file(tid) |
209 | + tt.apply() |
210 | + tree.commit('start') |
211 | + with open('dir/name1', 'wb') as f: |
212 | + f.write(b'new content2') |
213 | + revert(tree, tree.basis_tree()) |
214 | + self.assertEqual([], list(tree.iter_changes(tree.basis_tree()))) |
215 | |
216 | === modified file 'breezy/git/transform.py' |
217 | --- breezy/git/transform.py 2020-11-19 17:40:49 +0000 |
218 | +++ breezy/git/transform.py 2021-03-22 21:17:36 +0000 |
219 | @@ -1440,7 +1440,7 @@ |
220 | changes = {} |
221 | changed_ids = set() |
222 | for id_set in [self._new_name, self._new_parent, |
223 | - self._new_executability]: |
224 | + self._new_executability, self._new_contents]: |
225 | changed_ids.update(id_set) |
226 | for id_set in [self._new_name, self._new_parent]: |
227 | removed_id.update(id_set) |
228 | |
229 | === modified file 'breezy/git/workingtree.py' |
230 | --- breezy/git/workingtree.py 2020-11-18 02:15:43 +0000 |
231 | +++ breezy/git/workingtree.py 2021-03-22 21:17:36 +0000 |
232 | @@ -1518,7 +1518,7 @@ |
233 | # |
234 | with self.lock_tree_write(): |
235 | from .. import merge |
236 | - nb_conflicts = 0 |
237 | + nb_conflicts = [] |
238 | try: |
239 | last_rev = self.get_parent_ids()[0] |
240 | except IndexError: |
241 | @@ -1539,7 +1539,7 @@ |
242 | show_base=show_base) |
243 | if nb_conflicts: |
244 | self.add_parent_tree((old_tip, other_tree)) |
245 | - return nb_conflicts |
246 | + return len(nb_conflicts) |
247 | |
248 | if last_rev != _mod_revision.ensure_null(revision): |
249 | to_tree = self.branch.repository.revision_tree(revision) |
250 | @@ -1572,7 +1572,7 @@ |
251 | (old_tip, self.branch.repository.revision_tree(old_tip))) |
252 | self.set_parent_trees(parent_trees) |
253 | last_rev = parent_trees[0][0] |
254 | - return nb_conflicts |
255 | + return len(nb_conflicts) |
256 | |
257 | |
258 | class GitWorkingTreeFormat(workingtree.WorkingTreeFormat): |
259 | |
260 | === modified file 'breezy/merge.py' |
261 | --- breezy/merge.py 2020-08-22 22:46:24 +0000 |
262 | +++ breezy/merge.py 2021-03-22 21:17:36 +0000 |
263 | @@ -666,7 +666,7 @@ |
264 | trace.note(gettext("%d conflicts encountered.") |
265 | % len(merge.cooked_conflicts)) |
266 | |
267 | - return len(merge.cooked_conflicts) |
268 | + return merge.cooked_conflicts |
269 | |
270 | |
271 | class _InventoryNoneEntry(object): |
272 | |
273 | === modified file 'breezy/plugins/github/hoster.py' |
274 | --- breezy/plugins/github/hoster.py 2020-11-18 02:15:43 +0000 |
275 | +++ breezy/plugins/github/hoster.py 2021-03-22 21:17:36 +0000 |
276 | @@ -456,7 +456,7 @@ |
277 | repo = self._get_repo(owner, project) |
278 | return github_url_to_bzr_url(repo['ssh_url'], branch_name) |
279 | |
280 | - def get_derived_branch(self, base_branch, name, project=None, owner=None): |
281 | + def get_derived_branch(self, base_branch, name, project=None, owner=None, preferred_schemes=None): |
282 | base_owner, base_project, base_branch_name = parse_github_branch_url(base_branch) |
283 | base_repo = self._get_repo(base_owner, base_project) |
284 | if owner is None: |
285 | @@ -465,10 +465,24 @@ |
286 | project = base_repo['name'] |
287 | try: |
288 | remote_repo = self._get_repo(owner, project) |
289 | - full_url = github_url_to_bzr_url(remote_repo['ssh_url'], name) |
290 | - return _mod_branch.Branch.open(full_url) |
291 | except NoSuchProject: |
292 | raise errors.NotBranchError('%s/%s/%s' % (WEB_GITHUB_URL, owner, project)) |
293 | + if preferred_schemes is None: |
294 | + preferred_schemes = ['git+ssh'] |
295 | + for scheme in preferred_schemes: |
296 | + if scheme == 'git+ssh': |
297 | + github_url = remote_repo['ssh_url'] |
298 | + break |
299 | + if scheme == 'https': |
300 | + github_url = remote_repo['clone_url'] |
301 | + break |
302 | + if scheme == 'git': |
303 | + github_url = remote_repo['git_url'] |
304 | + break |
305 | + else: |
306 | + raise AssertionError |
307 | + full_url = github_url_to_bzr_url(github_url, name) |
308 | + return _mod_branch.Branch.open(full_url) |
309 | |
310 | def get_proposer(self, source_branch, target_branch): |
311 | return GitHubMergeProposalBuilder(self, source_branch, target_branch) |
312 | |
313 | === modified file 'breezy/plugins/gitlab/hoster.py' |
314 | --- breezy/plugins/gitlab/hoster.py 2020-11-18 02:15:43 +0000 |
315 | +++ breezy/plugins/gitlab/hoster.py 2021-03-22 21:17:36 +0000 |
316 | @@ -81,7 +81,8 @@ |
317 | _fmt = "GitLab can not process request: %(error)s." |
318 | |
319 | def __init__(self, error): |
320 | - errors.BzrError.__init__(self, error=error) |
321 | + errors.BzrError.__init__(self) |
322 | + self.error = error |
323 | |
324 | |
325 | class DifferentGitLabInstances(errors.BzrError): |
326 | @@ -112,7 +113,8 @@ |
327 | _fmt = "Conflict during operation: %(reason)s" |
328 | |
329 | def __init__(self, reason): |
330 | - errors.BzrError(self, reason=reason) |
331 | + errors.BzrError(self) |
332 | + self.reason = reason |
333 | |
334 | |
335 | class ForkingDisabled(errors.BzrError): |
336 | @@ -228,7 +230,13 @@ |
337 | self._mr = mr |
338 | |
339 | def _update(self, **kwargs): |
340 | - self.gl._update_merge_request(self._mr['project_id'], self._mr['iid'], kwargs) |
341 | + try: |
342 | + self.gl._update_merge_request( |
343 | + self._mr['project_id'], self._mr['iid'], kwargs) |
344 | + except GitLabConflict as e: |
345 | + self.gl._handle_merge_request_conflict( |
346 | + e.reason, self.get_source_branch_url(), |
347 | + self._mr['target_project_id']) |
348 | |
349 | def __repr__(self): |
350 | return "<%s at %r>" % (type(self).__name__, self._mr['web_url']) |
351 | @@ -302,7 +310,7 @@ |
352 | elif self._mr['merge_status'] == 'can_be_merged': |
353 | return True |
354 | elif self._mr['merge_status'] in ( |
355 | - 'unchecked', 'cannot_be_merged_recheck'): |
356 | + 'unchecked', 'cannot_be_merged_recheck', 'checking'): |
357 | # See https://gitlab.com/gitlab-org/gitlab/-/commit/7517105303c for |
358 | # an explanation of the distinction between unchecked and |
359 | # cannot_be_merged_recheck |
360 | @@ -407,7 +415,17 @@ |
361 | return json.loads(response.data) |
362 | _unexpected_status(path, response) |
363 | |
364 | - def _fork_project(self, project_name, timeout=50, interval=5, owner=None): |
365 | + def create_project(self, project_name): |
366 | + fields = {'name': project_name} |
367 | + response = self._api_request('POST', 'projects', fields=fields) |
368 | + if response.status == 403: |
369 | + raise errors.PermissionDenied(response.text) |
370 | + if response.status not in (200, 201): |
371 | + _unexpected_status('projects', response) |
372 | + project = json.loads(response.data) |
373 | + return project |
374 | + |
375 | + def fork_project(self, project_name, timeout=50, interval=5, owner=None): |
376 | path = 'projects/%s/fork' % urlutils.quote(str(project_name), '') |
377 | fields = {} |
378 | if owner is not None: |
379 | @@ -434,6 +452,18 @@ |
380 | project = self._get_project(project['path_with_namespace']) |
381 | return project |
382 | |
383 | + def _handle_merge_request_conflict(self, message, source_url, target_project): |
384 | + m = re.fullmatch( |
385 | + r'Another open merge request already exists for ' |
386 | + r'this source branch: \!([0-9]+)', |
387 | + message[0]) |
388 | + if m: |
389 | + merge_id = int(m.group(1)) |
390 | + mr = self._get_merge_request(target_project, merge_id) |
391 | + raise MergeProposalExists( |
392 | + source_url, GitLabMergeProposal(self, mr)) |
393 | + raise MergeRequestConflict(reason) |
394 | + |
395 | def get_current_user(self): |
396 | return self._current_user['username'] |
397 | |
398 | @@ -493,6 +523,8 @@ |
399 | response = self._api_request('PUT', path, fields=mr) |
400 | if response.status == 200: |
401 | return json.loads(response.data) |
402 | + if response.status == 409: |
403 | + raise GitLabConflict(json.loads(response.data).get('message')) |
404 | if response.status == 403: |
405 | raise errors.PermissionDenied(response.text) |
406 | _unexpected_status(path, response) |
407 | @@ -527,7 +559,7 @@ |
408 | if response.status == 403: |
409 | raise errors.PermissionDenied(response.text) |
410 | if response.status == 409: |
411 | - raise MergeRequestConflict(json.loads(response.data)) |
412 | + raise GitLabConflict(json.loads(response.data).get('message')) |
413 | if response.status == 422: |
414 | data = json.loads(response.data) |
415 | raise GitLabUnprocessable(data['error']) |
416 | @@ -555,7 +587,7 @@ |
417 | try: |
418 | target_project = self._get_project('%s/%s' % (owner, project)) |
419 | except NoSuchProject: |
420 | - target_project = self._fork_project( |
421 | + target_project = self.fork_project( |
422 | base_project['path_with_namespace'], owner=owner) |
423 | remote_repo_url = git_url_to_bzr_url(target_project['ssh_url_to_repo']) |
424 | remote_dir = controldir.ControlDir.open(remote_repo_url) |
425 | @@ -573,7 +605,7 @@ |
426 | target_project['http_url_to_repo'], name) |
427 | return push_result.target_branch, public_url |
428 | |
429 | - def get_derived_branch(self, base_branch, name, project=None, owner=None): |
430 | + def get_derived_branch(self, base_branch, name, project=None, owner=None, preferred_schemes=None): |
431 | (host, base_project, base_branch_name) = parse_gitlab_branch_url(base_branch) |
432 | if owner is None: |
433 | owner = self.get_current_user() |
434 | @@ -583,8 +615,19 @@ |
435 | target_project = self._get_project('%s/%s' % (owner, project)) |
436 | except NoSuchProject: |
437 | raise errors.NotBranchError('%s/%s/%s' % (self.base_url, owner, project)) |
438 | - return _mod_branch.Branch.open(gitlab_url_to_bzr_url( |
439 | - target_project['ssh_url_to_repo'], name)) |
440 | + if preferred_schemes is None: |
441 | + preferred_schemes = ['git+ssh'] |
442 | + for scheme in preferred_schemes: |
443 | + if scheme == 'git+ssh': |
444 | + gitlab_url = target_project['ssh_url_to_repo'] |
445 | + break |
446 | + elif scheme == 'https': |
447 | + gitlab_url = target_project['http_url_to_repo'] |
448 | + break |
449 | + else: |
450 | + raise AssertionError |
451 | + return _mod_branch.Branch.open( |
452 | + gitlab_url_to_bzr_url(gitlab_url, name)) |
453 | |
454 | def get_proposer(self, source_branch, target_branch): |
455 | return GitlabMergeProposalBuilder(self, source_branch, target_branch) |
456 | @@ -657,7 +700,7 @@ |
457 | yield GitLabMergeProposal(self, mp) |
458 | |
459 | def iter_my_forks(self, owner=None): |
460 | - if owner is not None: |
461 | + if owner is None: |
462 | owner = self.get_current_user() |
463 | for project in self._list_projects(owner=owner): |
464 | base_project = project.get('forked_from_project') |
465 | @@ -754,18 +797,10 @@ |
466 | kwargs['assignee_ids'].append(user['id']) |
467 | try: |
468 | merge_request = self.gl._create_mergerequest(**kwargs) |
469 | - except MergeRequestConflict as e: |
470 | - m = re.fullmatch( |
471 | - r'Another open merge request already exists for ' |
472 | - r'this source branch: \!([0-9]+)', |
473 | - e.reason['message'][0]) |
474 | - if m: |
475 | - merge_id = int(m.group(1)) |
476 | - mr = self.gl._get_merge_request( |
477 | - target_project['path_with_namespace'], merge_id) |
478 | - raise MergeProposalExists( |
479 | - self.source_branch.user_url, GitLabMergeProposal(self.gl, mr)) |
480 | - raise Exception('conflict: %r' % e.reason) |
481 | + except GitLabConflict as e: |
482 | + self.gl._handle_merge_request_conflict( |
483 | + e.reason, self.source_branch.user_url, |
484 | + target_project['path_with_namespace']) |
485 | except GitLabUnprocessable as e: |
486 | if e.error == [ |
487 | "Source project is not a fork of the target project"]: |
488 | |
489 | === modified file 'breezy/plugins/launchpad/hoster.py' |
490 | --- breezy/plugins/launchpad/hoster.py 2020-11-18 02:15:43 +0000 |
491 | +++ breezy/plugins/launchpad/hoster.py 2021-03-22 21:17:36 +0000 |
492 | @@ -408,7 +408,8 @@ |
493 | else: |
494 | raise AssertionError('not a valid Launchpad URL') |
495 | |
496 | - def get_derived_branch(self, base_branch, name, project=None, owner=None): |
497 | + def get_derived_branch(self, base_branch, name, project=None, owner=None, preferred_schemes=None): |
498 | + # TODO(jelmer): honor preferred_schemes |
499 | if owner is None: |
500 | owner = self.launchpad.me.name |
501 | (base_vcs, base_user, base_password, base_path, |
502 | |
503 | === added directory 'breezy/plugins/pypi' |
504 | === added file 'breezy/plugins/pypi/__init__.py' |
505 | --- breezy/plugins/pypi/__init__.py 1970-01-01 00:00:00 +0000 |
506 | +++ breezy/plugins/pypi/__init__.py 2021-03-22 21:17:36 +0000 |
507 | @@ -0,0 +1,29 @@ |
508 | +# Copyright (C) 2021 Breezy Developers |
509 | +# |
510 | +# This program is free software; you can redistribute it and/or modify |
511 | +# it under the terms of the GNU General Public License as published by |
512 | +# the Free Software Foundation; either version 2 of the License, or |
513 | +# (at your option) any later version. |
514 | +# |
515 | +# This program is distributed in the hope that it will be useful, |
516 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
517 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
518 | +# GNU General Public License for more details. |
519 | +# |
520 | +# You should have received a copy of the GNU General Public License |
521 | +# along with this program; if not, write to the Free Software |
522 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
523 | + |
524 | +"""Support for looking up URLs from pypi. |
525 | +""" |
526 | + |
527 | +from __future__ import absolute_import |
528 | + |
529 | +from ... import ( |
530 | + version_info, # noqa: F401 |
531 | + ) |
532 | +from ...directory_service import directories |
533 | + |
534 | +directories.register_lazy('pypi:', __name__ + '.directory', |
535 | + 'PypiDirectory', |
536 | + 'Pypi-based directory service',) |
537 | |
538 | === added file 'breezy/plugins/pypi/directory.py' |
539 | --- breezy/plugins/pypi/directory.py 1970-01-01 00:00:00 +0000 |
540 | +++ breezy/plugins/pypi/directory.py 2021-03-22 21:17:36 +0000 |
541 | @@ -0,0 +1,78 @@ |
542 | +# Copyright (C) 2021 Breezy Developers |
543 | +# |
544 | +# This program is free software; you can redistribute it and/or modify |
545 | +# it under the terms of the GNU General Public License as published by |
546 | +# the Free Software Foundation; either version 2 of the License, or |
547 | +# (at your option) any later version. |
548 | +# |
549 | +# This program is distributed in the hope that it will be useful, |
550 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
551 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
552 | +# GNU General Public License for more details. |
553 | +# |
554 | +# You should have received a copy of the GNU General Public License |
555 | +# along with this program; if not, write to the Free Software |
556 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
557 | + |
558 | +"""Directory lookup that uses pypi.""" |
559 | + |
560 | +from __future__ import absolute_import |
561 | + |
562 | +from breezy.errors import BzrError |
563 | +from breezy.trace import note |
564 | +from breezy.urlutils import InvalidURL |
565 | + |
566 | +import json |
567 | + |
568 | +try: |
569 | + from urllib.request import urlopen |
570 | + from urllib.parse import urlparse |
571 | + from urllib.error import HTTPError |
572 | +except ImportError: # python < 3 |
573 | + from urllib import urlopen, HTTPError |
574 | + from urlparse import urlparse |
575 | + |
576 | + |
577 | +class PypiProjectWithoutRepositoryURL(InvalidURL): |
578 | + |
579 | + _fmt = "No repository URL set for pypi project %(name)s" |
580 | + |
581 | + def __init__(self, name, url=None): |
582 | + BzrError.__init__(self, name=name, url=url) |
583 | + |
584 | + |
585 | +class NoSuchPypiProject(InvalidURL): |
586 | + |
587 | + _fmt = "No pypi project with name %(name)s" |
588 | + |
589 | + def __init__(self, name, url=None): |
590 | + BzrError.__init__(self, name=name, url=url) |
591 | + |
592 | + |
593 | +def find_repo_url(data): |
594 | + for key, value in data['info']['project_urls'].items(): |
595 | + if key == 'Repository': |
596 | + note('Found repository URL %s for pypi project %s', |
597 | + value, name) |
598 | + return value |
599 | + parsed_url = urlparse(value) |
600 | + if (parsed_url.hostname == 'github.com' and |
601 | + parsed_url.path.strip('/').count('/') == 1): |
602 | + return value |
603 | + |
604 | + |
605 | +class PypiDirectory(object): |
606 | + |
607 | + def look_up(self, name, url, purpose=None): |
608 | + """See DirectoryService.look_up""" |
609 | + try: |
610 | + with urlopen('https://pypi.org/pypi/%s/json' % name) as f: |
611 | + data = json.load(f) |
612 | + except HTTPError as e: |
613 | + if e.status == 404: |
614 | + raise NoSuchPypiProject(name, url=url) |
615 | + raise |
616 | + url = find_repo_url(data) |
617 | + if url is None: |
618 | + raise PypiProjectWithoutRepositoryURL(name, url=url) |
619 | + return url |
620 | |
621 | === modified file 'breezy/plugins/quilt/tests/test_merge.py' |
622 | --- breezy/plugins/quilt/tests/test_merge.py 2020-02-18 01:57:45 +0000 |
623 | +++ breezy/plugins/quilt/tests/test_merge.py 2021-03-22 21:17:36 +0000 |
624 | @@ -149,7 +149,7 @@ |
625 | """, "a/debian/patches/patch1") |
626 | # "a" should be unapplied again |
627 | self.assertPathDoesNotExist("a/a") |
628 | - self.assertEquals(1, conflicts) |
629 | + self.assertEquals(1, len(conflicts)) |
630 | |
631 | def test_auto_apply_patches_after_checkout(self): |
632 | self.enable_hooks() |
633 | @@ -286,7 +286,7 @@ |
634 | c |
635 | >>>>>>> MERGE-SOURCE |
636 | """, "a/a") |
637 | - self.assertEquals(2, conflicts) |
638 | + self.assertEquals(2, len(conflicts)) |
639 | |
640 | |
641 | |
642 | |
643 | === modified file 'breezy/propose.py' |
644 | --- breezy/propose.py 2020-11-18 02:15:43 +0000 |
645 | +++ breezy/propose.py 2021-03-22 21:17:36 +0000 |
646 | @@ -286,7 +286,7 @@ |
647 | """ |
648 | raise NotImplementedError(self.publish_derived) |
649 | |
650 | - def get_derived_branch(self, base_branch, name, project=None, owner=None): |
651 | + def get_derived_branch(self, base_branch, name, project=None, owner=None, preferred_schemes=None): |
652 | """Get a derived branch ('a fork'). |
653 | """ |
654 | raise NotImplementedError(self.get_derived_branch) |
655 | |
656 | === modified file 'breezy/tests/per_workingtree/test_commit.py' |
657 | --- breezy/tests/per_workingtree/test_commit.py 2020-08-15 22:46:49 +0000 |
658 | +++ breezy/tests/per_workingtree/test_commit.py 2021-03-22 21:17:36 +0000 |
659 | @@ -101,11 +101,11 @@ |
660 | |
661 | # Merging from A should introduce conflicts because 'n' was modified |
662 | # (in A) and removed (in B), so 'a' needs to be restored. |
663 | - num_conflicts = tree_b.merge_from_branch(tree_a.branch) |
664 | + conflicts = tree_b.merge_from_branch(tree_a.branch) |
665 | if tree_b.has_versioned_directories(): |
666 | - self.assertEqual(3, num_conflicts) |
667 | + self.assertEqual(3, len(conflicts)) |
668 | else: |
669 | - self.assertEqual(2, num_conflicts) |
670 | + self.assertEqual(2, len(conflicts)) |
671 | |
672 | self.assertThat( |
673 | tree_b, HasPathRelations( |
674 | |
675 | === modified file 'breezy/tests/per_workingtree/test_merge_from_branch.py' |
676 | --- breezy/tests/per_workingtree/test_merge_from_branch.py 2020-08-15 22:46:49 +0000 |
677 | +++ breezy/tests/per_workingtree/test_merge_from_branch.py 2021-03-22 21:17:36 +0000 |
678 | @@ -224,9 +224,9 @@ |
679 | outer.commit('delete file3') |
680 | nb_conflicts = outer.merge_from_branch(inner, to_revision=revs[2]) |
681 | if outer.supports_rename_tracking(): |
682 | - self.assertEqual(4, nb_conflicts) |
683 | + self.assertEqual(4, len(nb_conflicts)) |
684 | else: |
685 | - self.assertEqual(1, nb_conflicts) |
686 | + self.assertEqual(1, len(nb_conflicts)) |
687 | self.assertTreeLayout(['dir-outer', |
688 | 'dir-outer/dir', |
689 | 'dir-outer/dir/file1', |
690 | @@ -245,9 +245,9 @@ |
691 | # file4 could not be added to its original root, so it gets added to |
692 | # the new root with a conflict. |
693 | if outer.supports_rename_tracking(): |
694 | - self.assertEqual(1, nb_conflicts) |
695 | + self.assertEqual(1, len(nb_conflicts)) |
696 | else: |
697 | - self.assertEqual(0, nb_conflicts) |
698 | + self.assertEqual(0, len(nb_conflicts)) |
699 | self.assertTreeLayout(['dir-outer', |
700 | 'dir-outer/dir', |
701 | 'dir-outer/dir/file1', |
702 | @@ -261,9 +261,9 @@ |
703 | # 1 conflict, because file4 can't be put into the old root |
704 | nb_conflicts = outer.merge_from_branch(inner, to_revision=revs[3]) |
705 | if outer.supports_rename_tracking(): |
706 | - self.assertEqual(1, nb_conflicts) |
707 | + self.assertEqual(1, len(nb_conflicts)) |
708 | else: |
709 | - self.assertEqual(0, nb_conflicts) |
710 | + self.assertEqual(0, len(nb_conflicts)) |
711 | try: |
712 | outer.set_conflicts([]) |
713 | except errors.UnsupportedOperation: |
714 | @@ -275,7 +275,7 @@ |
715 | # And now file4 gets renamed into an existing dir |
716 | nb_conflicts = outer.merge_from_branch(inner, to_revision=revs[4]) |
717 | if outer.supports_rename_tracking(): |
718 | - self.assertEqual(1, nb_conflicts) |
719 | + self.assertEqual(1, len(nb_conflicts)) |
720 | self.assertTreeLayout(['dir-outer', |
721 | 'dir-outer/dir', |
722 | 'dir-outer/dir/file1', |
723 | @@ -285,9 +285,9 @@ |
724 | outer) |
725 | else: |
726 | if outer.has_versioned_directories(): |
727 | - self.assertEqual(2, nb_conflicts) |
728 | + self.assertEqual(2, len(nb_conflicts)) |
729 | else: |
730 | - self.assertEqual(1, nb_conflicts) |
731 | + self.assertEqual(1, len(nb_conflicts)) |
732 | self.assertTreeLayout(['dir', |
733 | 'dir-outer', |
734 | 'dir-outer/dir', |
735 | |
736 | === modified file 'breezy/tests/per_workingtree/test_unversion.py' |
737 | --- breezy/tests/per_workingtree/test_unversion.py 2020-08-15 17:47:31 +0000 |
738 | +++ breezy/tests/per_workingtree/test_unversion.py 2021-03-22 21:17:36 +0000 |
739 | @@ -175,11 +175,11 @@ |
740 | # Merging from A should introduce conflicts because 'n' was modified |
741 | # and removed, so 'a' needs to be restored. We also have a conflict |
742 | # because 'a' is still an existing directory |
743 | - num_conflicts = tree_b.merge_from_branch(tree_a.branch) |
744 | + conflicts = tree_b.merge_from_branch(tree_a.branch) |
745 | if tree_b.has_versioned_directories(): |
746 | - self.assertEqual(4, num_conflicts) |
747 | + self.assertEqual(4, len(conflicts)) |
748 | else: |
749 | - self.assertEqual(1, num_conflicts) |
750 | + self.assertEqual(1, len(conflicts)) |
751 | |
752 | self.assertThat( |
753 | tree_b, |
754 | |
755 | === modified file 'breezy/tests/test_merge.py' |
756 | --- breezy/tests/test_merge.py 2020-08-22 22:46:24 +0000 |
757 | +++ breezy/tests/test_merge.py 2021-03-22 21:17:36 +0000 |
758 | @@ -434,8 +434,8 @@ |
759 | first_rev) |
760 | merger.merge_type = _mod_merge.Merge3Merger |
761 | merger.interesting_files = 'a' |
762 | - conflict_count = merger.do_merge() |
763 | - self.assertEqual(0, conflict_count) |
764 | + conflicts = merger.do_merge() |
765 | + self.assertEqual([], conflicts) |
766 | |
767 | self.assertPathDoesNotExist("a") |
768 | tree.revert() |
769 | @@ -515,8 +515,8 @@ |
770 | _mod_revision.NULL_REVISION, |
771 | first_rev) |
772 | merger.merge_type = _mod_merge.Merge3Merger |
773 | - conflict_count = merger.do_merge() |
774 | - self.assertEqual(0, conflict_count) |
775 | + conflicts = merger.do_merge() |
776 | + self.assertEqual([], conflicts) |
777 | self.assertEqual({''}, set(tree.all_versioned_paths())) |
778 | tree.set_parent_ids([]) |
779 | |
780 | @@ -2190,7 +2190,7 @@ |
781 | [('modify', ('a', b'a\nb\nc\nd\ne\nf\n'))], |
782 | revision_id=b'D-id') |
783 | wt, conflicts = self.do_merge(builder, b'E-id') |
784 | - self.assertEqual(0, conflicts) |
785 | + self.assertEqual([], conflicts) |
786 | # The merge should have simply update the contents of 'a' |
787 | self.assertEqual(b'a\nb\nc\nd\ne\nf\n', wt.get_file_text('a')) |
788 | |
789 | @@ -2220,7 +2220,7 @@ |
790 | [('rename', ('bar', 'baz'))], revision_id=b'F-id') |
791 | builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id') |
792 | wt, conflicts = self.do_merge(builder, b'F-id') |
793 | - self.assertEqual(0, conflicts) |
794 | + self.assertEqual([], conflicts) |
795 | # The merge should simply recognize that the final rename takes |
796 | # precedence |
797 | self.assertEqual('baz', wt.id2path(b'foo-id')) |
798 | @@ -2251,7 +2251,7 @@ |
799 | [('unversion', 'bar')], revision_id=b'F-id') |
800 | builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id') |
801 | wt, conflicts = self.do_merge(builder, b'F-id') |
802 | - self.assertEqual(0, conflicts) |
803 | + self.assertEqual([], conflicts) |
804 | self.assertRaises(errors.NoSuchId, wt.id2path, b'foo-id') |
805 | |
806 | def test_executable_changes(self): |
807 | @@ -2286,7 +2286,7 @@ |
808 | wt.revert() |
809 | self.assertFalse(wt.is_executable('foo')) |
810 | conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id') |
811 | - self.assertEqual(0, conflicts) |
812 | + self.assertEqual(0, len(conflicts)) |
813 | self.assertTrue(wt.is_executable('foo')) |
814 | |
815 | def test_create_symlink(self): |
816 | @@ -2322,7 +2322,7 @@ |
817 | wt.revert() |
818 | self.assertFalse(wt.is_versioned('foo')) |
819 | conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id') |
820 | - self.assertEqual(0, conflicts) |
821 | + self.assertEqual(0, len(conflicts)) |
822 | self.assertEqual(b'foo-id', wt.path2id('foo')) |
823 | self.assertEqual('bar', wt.get_symlink_target('foo')) |
824 | |
825 | @@ -2352,7 +2352,7 @@ |
826 | builder.build_snapshot([b'B-id', b'C-id'], [], |
827 | revision_id=b'D-id') |
828 | wt, conflicts = self.do_merge(builder, b'E-id') |
829 | - self.assertEqual(1, conflicts) |
830 | + self.assertEqual(1, len(conflicts)) |
831 | self.assertEqualDiff(b'<<<<<<< TREE\n' |
832 | b'B content\n' |
833 | b'=======\n' |
834 | @@ -2402,7 +2402,7 @@ |
835 | wt.merge_from_branch(wt.branch, b'C-id') |
836 | wt.commit('D merges B & C', rev_id=b'D-id') |
837 | conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id') |
838 | - self.assertEqual(0, conflicts) |
839 | + self.assertEqual(0, len(conflicts)) |
840 | self.assertEqual('bing', wt.get_symlink_target('foo')) |
841 | |
842 | def test_renamed_symlink(self): |
843 | @@ -2459,7 +2459,7 @@ |
844 | False), |
845 | ], entries) |
846 | conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id') |
847 | - self.assertEqual(0, conflicts) |
848 | + self.assertEqual(0, len(conflicts)) |
849 | self.assertEqual('blah', wt.id2path(b'foo-id')) |
850 | |
851 | def test_symlink_no_content_change(self): |
852 | @@ -2509,7 +2509,7 @@ |
853 | self.assertEqual([], list(merge_obj._entries_lca())) |
854 | # Now do a real merge, just to test the rest of the stack |
855 | conflicts = wt.merge_from_branch(wt.branch, to_revision=b'E-id') |
856 | - self.assertEqual(0, conflicts) |
857 | + self.assertEqual(0, len(conflicts)) |
858 | self.assertEqual('bing', wt.get_symlink_target('foo')) |
859 | |
860 | def test_symlink_this_changed_kind(self): |
861 | @@ -2652,7 +2652,7 @@ |
862 | [('rename', ('bar', 'foo'))], revision_id=b'F-id') # Rename back to BASE |
863 | builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id') |
864 | wt, conflicts = self.do_merge(builder, b'F-id') |
865 | - self.assertEqual(0, conflicts) |
866 | + self.assertEqual([], conflicts) |
867 | self.assertEqual('foo', wt.id2path(b'foo-id')) |
868 | |
869 | def test_other_reverted_content_to_base(self): |
870 | @@ -2673,7 +2673,7 @@ |
871 | revision_id=b'F-id') # Revert back to BASE |
872 | builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id') |
873 | wt, conflicts = self.do_merge(builder, b'F-id') |
874 | - self.assertEqual(0, conflicts) |
875 | + self.assertEqual([], conflicts) |
876 | # TODO: We need to use the per-file graph to properly select a BASE |
877 | # before this will work. Or at least use the LCA trees to find |
878 | # the appropriate content base. (which is B, not A). |
879 | @@ -2697,7 +2697,7 @@ |
880 | revision_id=b'F-id') # Override B content |
881 | builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id') |
882 | wt, conflicts = self.do_merge(builder, b'F-id') |
883 | - self.assertEqual(0, conflicts) |
884 | + self.assertEqual([], conflicts) |
885 | self.assertEqual(b'F content\n', wt.get_file_text('foo')) |
886 | |
887 | def test_all_wt(self): |
888 | @@ -3302,7 +3302,7 @@ |
889 | dest_wt = self.setup_simple_branch('dest', ['dir/', 'dir/file.txt']) |
890 | self.setup_simple_branch('src', ['README']) |
891 | conflicts = self.do_merge_into('src', 'dest/dir') |
892 | - self.assertEqual(1, conflicts) |
893 | + self.assertEqual(1, len(conflicts)) |
894 | dest_wt.lock_read() |
895 | self.addCleanup(dest_wt.unlock) |
896 | # The r1-lib1 revision should be merged into this one |
897 | @@ -3330,7 +3330,7 @@ |
898 | # This is an edge case that shouldn't happen to users very often. So |
899 | # we don't care really about the exact presentation of the conflict, |
900 | # just that there is one. |
901 | - self.assertEqual(1, conflicts) |
902 | + self.assertEqual(1, len(conflicts)) |
903 | |
904 | def test_only_subdir(self): |
905 | """When the location points to just part of a tree, merge just that |
906 | |
907 | === modified file 'breezy/tests/test_merge_core.py' |
908 | --- breezy/tests/test_merge_core.py 2020-08-09 18:10:01 +0000 |
909 | +++ breezy/tests/test_merge_core.py 2021-03-22 21:17:36 +0000 |
910 | @@ -503,13 +503,13 @@ |
911 | self.build_tree_contents([('b/file', b'this contents contents\n')]) |
912 | wtb = d_b.open_workingtree() |
913 | wtb.commit('this revision', allow_pointless=False) |
914 | - self.assertEqual(1, wtb.merge_from_branch(wta.branch)) |
915 | + self.assertEqual(1, len(wtb.merge_from_branch(wta.branch))) |
916 | self.assertPathExists('b/file.THIS') |
917 | self.assertPathExists('b/file.BASE') |
918 | self.assertPathExists('b/file.OTHER') |
919 | wtb.revert() |
920 | - self.assertEqual(1, wtb.merge_from_branch(wta.branch, |
921 | - merge_type=WeaveMerger)) |
922 | + self.assertEqual(1, len(wtb.merge_from_branch(wta.branch, |
923 | + merge_type=WeaveMerger))) |
924 | self.assertPathExists('b/file') |
925 | self.assertPathExists('b/file.THIS') |
926 | self.assertPathExists('b/file.BASE') |
927 | @@ -544,9 +544,9 @@ |
928 | revision_id=b'E-id') |
929 | builder.finish_series() |
930 | tree = builder.get_branch().create_checkout('tree', lightweight=True) |
931 | - self.assertEqual(1, tree.merge_from_branch(tree.branch, |
932 | + self.assertEqual(1, len(tree.merge_from_branch(tree.branch, |
933 | to_revision=b'D-id', |
934 | - merge_type=WeaveMerger)) |
935 | + merge_type=WeaveMerger))) |
936 | self.assertPathExists('tree/foo.THIS') |
937 | self.assertPathExists('tree/foo.OTHER') |
938 | self.expectFailure('fail to create .BASE in some criss-cross merges', |
939 | @@ -640,8 +640,10 @@ |
940 | b_wt.rename_one('deux', 'un') |
941 | b_wt.rename_one('tmp', 'deux') |
942 | b_wt.commit('r1', rev_id=b'r1') |
943 | - self.assertEqual(0, a_wt.merge_from_branch(b_wt.branch, |
944 | - b_wt.branch.last_revision(), b_wt.branch.get_rev_id(1))) |
945 | + self.assertEqual( |
946 | + 0, len(a_wt.merge_from_branch( |
947 | + b_wt.branch, b_wt.branch.last_revision(), |
948 | + b_wt.branch.get_rev_id(1)))) |
949 | self.assertPathExists('a/un') |
950 | self.assertTrue('a/deux') |
951 | self.assertFalse(os.path.exists('a/tmp')) |
952 | |
953 | === modified file 'breezy/transform.py' |
954 | --- breezy/transform.py 2020-11-19 18:28:52 +0000 |
955 | +++ breezy/transform.py 2021-03-22 21:17:36 +0000 |
956 | @@ -729,7 +729,7 @@ |
957 | return conflicts, merge_modified |
958 | |
959 | |
960 | -def revert(working_tree, target_tree, filenames, backups=False, |
961 | +def revert(working_tree, target_tree, filenames=None, backups=False, |
962 | pb=None, change_reporter=None, merge_modified=None, basis_tree=None): |
963 | """Revert a working tree's contents to those of a target tree.""" |
964 | with contextlib.ExitStack() as es: |
965 | |
966 | === modified file 'breezy/workspace.py' |
967 | --- breezy/workspace.py 2020-08-06 22:27:46 +0000 |
968 | +++ breezy/workspace.py 2021-03-22 21:17:36 +0000 |
969 | @@ -52,7 +52,7 @@ |
970 | subpath: Subpath to operate on |
971 | """ |
972 | revert(local_tree, local_tree.branch.basis_tree(), |
973 | - [subpath] if subpath not in ('.', '') else None) |
974 | + [subpath] if subpath else None) |
975 | deletables = list(iter_deletables( |
976 | local_tree, unknown=True, ignored=False, detritus=False)) |
977 | delete_items(deletables) |
978 | |
979 | === modified file 'doc/en/release-notes/brz-3.1.txt' |
980 | --- doc/en/release-notes/brz-3.1.txt 2020-11-22 15:50:02 +0000 |
981 | +++ doc/en/release-notes/brz-3.1.txt 2021-03-22 21:17:36 +0000 |
982 | @@ -54,6 +54,9 @@ |
983 | but it prevents the DWIM revision specifier from treating "svn:" |
984 | as a URL. (Jelmer Vernooij) |
985 | |
986 | + * New `pypi` directory that can be used to access remote repositories |
987 | + declared in pypi. (Jelmer Vernooij) |
988 | + |
989 | Bug Fixes |
990 | ********* |
991 | |
992 | @@ -96,6 +99,10 @@ |
993 | * File ids are no longer returned in ``Tree.walkdirs``. |
994 | (Jelmer Vernooij) |
995 | |
996 | + * ``WorkingTree.merge_from_branch``, ``Merge.do_merge`` and |
997 | + ``merge_inner`` now return a list of conflicts rather than number of |
998 | + conflicts. (Jelmer Vernooij) |
999 | + |
1000 | Internals |
1001 | ********* |
1002 |
Running landing tests failed /ci.breezy- vcs.org/ job/brz/ job/brz- land/806/
https:/