Merge lp:~jelmer/brz/merge-3.1 into lp:brz

Proposed by Jelmer Vernooij on 2021-04-03
Status: Merged
Approved by: Jelmer Vernooij on 2021-04-03
Approved revision: 7532
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: 452 lines (+129/-41)
13 files modified
breezy/git/branch.py (+20/-18)
breezy/git/dir.py (+16/-5)
breezy/git/remote.py (+4/-2)
breezy/git/repository.py (+7/-0)
breezy/git/tests/test_remote.py (+10/-0)
breezy/git/transform.py (+1/-1)
breezy/plugins/github/hoster.py (+2/-2)
breezy/plugins/gitlab/hoster.py (+55/-9)
breezy/plugins/propose/cmds.py (+1/-1)
breezy/propose.py (+5/-1)
breezy/tests/per_branch/test_http.py (+1/-1)
breezy/tests/test_propose.py (+6/-0)
breezy/transport/http/urllib.py (+1/-1)
To merge this branch: bzr merge lp:~jelmer/brz/merge-3.1
Reviewer Review Type Date Requested Status
Jelmer Vernooij Approve on 2021-04-03
Review via email: mp+400593@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.
Jelmer Vernooij (jelmer) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/git/branch.py'
2--- breezy/git/branch.py 2021-03-22 21:07:08 +0000
3+++ breezy/git/branch.py 2021-04-03 12:59:06 +0000
4@@ -151,7 +151,7 @@
5 else:
6 conflicts.append(
7 (tag_name,
8- self.repository.lookup_foreign_revision_id(peeled),
9+ self.source.branch.repository.lookup_foreign_revision_id(peeled),
10 self.target.branch.repository.lookup_foreign_revision_id(
11 old_refs[ref_name])))
12 return ret
13@@ -258,7 +258,7 @@
14 return updates, conflicts
15
16 def _merge_to(self, to_tags, source_tag_refs, overwrite=False,
17- selector=None):
18+ selector=None, ignore_master=False):
19 unpeeled_map = defaultdict(set)
20 conflicts = []
21 updates = {}
22@@ -497,8 +497,9 @@
23 if getattr(self.repository, '_git', None):
24 cs = self.repository._git.get_config_stack()
25 try:
26- return cs.get((b"branch", self.name.encode('utf-8')),
27- b"nick").decode("utf-8")
28+ return cs.get(
29+ (b"branch", self.name.encode('utf-8')),
30+ b"nick").decode("utf-8")
31 except KeyError:
32 pass
33 return self.name or u"HEAD"
34@@ -517,6 +518,9 @@
35 return "<%s(%r, %r)>" % (self.__class__.__name__, self.repository.base,
36 self.name)
37
38+ def set_last_revision(self, revid):
39+ raise NotImplementedError(self.set_last_revision)
40+
41 def generate_revision_history(self, revid, last_rev=None,
42 other_branch=None):
43 if last_rev is not None:
44@@ -592,7 +596,7 @@
45 try:
46 ref = cs.get((b"branch", remote), b"merge")
47 except KeyError:
48- ref = self.ref
49+ ref = b'HEAD'
50
51 return git_url_to_bzr_url(location.decode('utf-8'), ref=ref)
52
53@@ -601,11 +605,6 @@
54 cs = self.repository._git.get_config_stack()
55 return self._get_related_merge_branch(cs)
56
57- def _write_git_config(self, cs):
58- f = BytesIO()
59- cs.write_to_file(f)
60- self.repository._git._put_named_file('config', f.getvalue())
61-
62 def set_parent(self, location):
63 cs = self.repository._git.get_config()
64 remote = self._get_origin(cs)
65@@ -613,14 +612,17 @@
66 target_url, branch, ref = bzr_url_to_git_url(location)
67 location = urlutils.relative_url(this_url, target_url)
68 cs.set((b"remote", remote), b"url", location)
69- if branch:
70- cs.set((b"branch", remote), b"merge", branch_name_to_ref(branch))
71- elif ref:
72- cs.set((b"branch", remote), b"merge", ref)
73- else:
74- # TODO(jelmer): Maybe unset rather than setting to HEAD?
75- cs.set((b"branch", remote), b"merge", b'HEAD')
76- self._write_git_config(cs)
77+ cs.set((b"remote", remote), b'fetch',
78+ b'+refs/heads/*:refs/remotes/%s/*' % remote)
79+ if self.name:
80+ if branch:
81+ cs.set((b"branch", self.name.encode()), b"merge", branch_name_to_ref(branch))
82+ elif ref:
83+ cs.set((b"branch", self.name.encode()), b"merge", ref)
84+ else:
85+ # TODO(jelmer): Maybe unset rather than setting to HEAD?
86+ cs.set((b"branch", self.name.encode()), b"merge", b'HEAD')
87+ self.repository._write_git_config(cs)
88
89 def break_lock(self):
90 raise NotImplementedError(self.break_lock)
91
92=== modified file 'breezy/git/dir.py'
93--- breezy/git/dir.py 2020-07-28 02:11:05 +0000
94+++ breezy/git/dir.py 2021-04-03 12:59:06 +0000
95@@ -233,13 +233,13 @@
96 from ..repository import InterRepository
97 from .mapping import default_mapping
98 from ..transport.local import LocalTransport
99- if stacked_on is not None:
100- raise _mod_branch.UnstackableBranchFormat(
101- self._format, self.user_url)
102+ from .refs import is_peeled
103 if no_tree:
104 format = BareLocalGitControlDirFormat()
105 else:
106 format = LocalGitControlDirFormat()
107+ if stacked_on is not None:
108+ raise _mod_branch.UnstackableBranchFormat(format, self.user_url)
109 (target_repo, target_controldir, stacking,
110 repo_policy) = format.initialize_on_transport_ex(
111 transport, use_existing_dir=use_existing_dir,
112@@ -257,10 +257,21 @@
113 (pack_hint, _, refs) = interrepo.fetch_objects(determine_wants,
114 mapping=default_mapping)
115 for name, val in refs.items():
116- target_git_repo.refs[name] = val
117+ if is_peeled(name):
118+ continue
119+ if val in target_git_repo.object_store:
120+ target_git_repo.refs[name] = val
121 result_dir = LocalGitDir(transport, target_git_repo, format)
122+ result_branch = result_dir.open_branch()
123+ try:
124+ parent = self.open_branch().get_parent()
125+ except brz_errors.InaccessibleParent:
126+ pass
127+ else:
128+ if parent:
129+ result_branch.set_parent(parent)
130 if revision_id is not None:
131- result_dir.open_branch().set_last_revision(revision_id)
132+ result_branch.set_last_revision(revision_id)
133 if not no_tree and isinstance(result_dir.root_transport, LocalTransport):
134 if result_dir.open_repository().make_working_trees():
135 try:
136
137=== modified file 'breezy/git/remote.py'
138--- breezy/git/remote.py 2021-03-22 21:07:08 +0000
139+++ breezy/git/remote.py 2021-04-03 12:59:06 +0000
140@@ -201,6 +201,8 @@
141 return PermissionDenied(url, message)
142 if message.endswith(' does not appear to be a git repository'):
143 return NotBranchError(url, message)
144+ if message == 'A repository for this project does not exist yet.':
145+ return NotBranchError(url, message)
146 if message == 'pre-receive hook declined':
147 return PermissionDenied(url, message)
148 if re.match('(.+) is not a valid repository name',
149@@ -384,7 +386,7 @@
150
151 def progress(self, text):
152 text = text.rstrip(b"\r\n")
153- text = text.decode('utf-8')
154+ text = text.decode('utf-8', 'surrogateescape')
155 if text.lower().startswith('error: '):
156 trace.show_error('git: %s', text[len(b'error: '):])
157 else:
158@@ -499,7 +501,7 @@
159 raise AlreadyBranchError(self.user_url)
160 ref_chain, unused_sha = self.get_refs_container().follow(
161 self._get_selected_ref(name))
162- if ref_chain and ref_chain[0] == b'HEAD':
163+ if ref_chain and ref_chain[0] == b'HEAD' and len(ref_chain) > 1:
164 refname = ref_chain[1]
165 repo = self.open_repository()
166 return RemoteGitBranch(self, repo, refname)
167
168=== modified file 'breezy/git/repository.py'
169--- breezy/git/repository.py 2021-01-10 00:25:52 +0000
170+++ breezy/git/repository.py 2021-04-03 12:59:06 +0000
171@@ -17,6 +17,8 @@
172
173 """An adapter between a Git Repository and a Bazaar Branch"""
174
175+from io import BytesIO
176+
177 from .. import (
178 check,
179 errors,
180@@ -258,6 +260,11 @@
181 self.start_write_group()
182 return builder
183
184+ def _write_git_config(self, cs):
185+ f = BytesIO()
186+ cs.write_to_file(f)
187+ self._git._put_named_file('config', f.getvalue())
188+
189 def get_file_graph(self):
190 return _mod_graph.Graph(GitFileParentProvider(
191 self._file_change_scanner))
192
193=== modified file 'breezy/git/tests/test_remote.py'
194--- breezy/git/tests/test_remote.py 2020-08-10 15:00:17 +0000
195+++ breezy/git/tests/test_remote.py 2021-04-03 12:59:06 +0000
196@@ -205,6 +205,16 @@
197 [b'=======',
198 b'You are not allowed to push code to this project.', b'', b'======'])))
199
200+ def test_notbrancherror_yet(self):
201+ self.assertEqual(
202+ NotBranchError('http://', 'A repository for this project does not exist yet.'),
203+ parse_git_hangup(
204+ 'http://',
205+ HangupException(
206+ [b'=======',
207+ b'',
208+ b'A repository for this project does not exist yet.', b'', b'======'])))
209+
210
211 class TestRemoteGitBranchFormat(TestCase):
212
213
214=== modified file 'breezy/git/transform.py'
215--- breezy/git/transform.py 2021-03-22 21:07:08 +0000
216+++ breezy/git/transform.py 2021-04-03 12:59:06 +0000
217@@ -1497,7 +1497,7 @@
218
219 def __init__(self, tree, pb=None, case_sensitive=True):
220 tree.lock_read()
221- limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
222+ limbodir = osutils.mkdtemp(prefix='git-limbo-')
223 DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
224
225 def canonical_path(self, path):
226
227=== modified file 'breezy/plugins/github/hoster.py'
228--- breezy/plugins/github/hoster.py 2021-03-22 21:07:08 +0000
229+++ breezy/plugins/github/hoster.py 2021-04-03 12:59:06 +0000
230@@ -281,11 +281,11 @@
231 headers=headers, body=body, retries=3)
232 except UnexpectedHttpStatus as e:
233 if e.code == 401:
234- raise GitHubLoginRequired(self)
235+ raise GitHubLoginRequired(self.base_url)
236 else:
237 raise
238 if response.status == 401:
239- raise GitHubLoginRequired(self)
240+ raise GitHubLoginRequired(self.base_url)
241 return response
242
243 def _get_repo(self, owner, repo):
244
245=== modified file 'breezy/plugins/gitlab/hoster.py'
246--- breezy/plugins/gitlab/hoster.py 2021-03-22 21:07:08 +0000
247+++ breezy/plugins/gitlab/hoster.py 2021-04-03 12:59:06 +0000
248@@ -42,6 +42,7 @@
249 PrerequisiteBranchUnsupported,
250 SourceNotDerivedFromTarget,
251 UnsupportedHoster,
252+ HosterLoginRequired,
253 )
254
255
256@@ -76,13 +77,24 @@
257 self.url = url
258
259
260+class GitLabError(errors.BzrError):
261+
262+ _fmt = "GitLab error: %(error)s"
263+
264+ def __init__(self, error, full_response):
265+ errors.BzrError.__init__(self)
266+ self.error = error
267+ self.full_response = full_response
268+
269+
270 class GitLabUnprocessable(errors.BzrError):
271
272 _fmt = "GitLab can not process request: %(error)s."
273
274- def __init__(self, error):
275+ def __init__(self, error, full_response):
276 errors.BzrError.__init__(self)
277 self.error = error
278+ self.full_response = full_response
279
280
281 class DifferentGitLabInstances(errors.BzrError):
282@@ -95,9 +107,9 @@
283 self.target_host = target_host
284
285
286-class GitLabLoginMissing(errors.BzrError):
287+class GitLabLoginMissing(HosterLoginRequired):
288
289- _fmt = ("Please log into GitLab")
290+ _fmt = ("Please log into GitLab instance at %(hoster)s")
291
292
293 class GitlabLoginError(errors.BzrError):
294@@ -416,8 +428,24 @@
295 _unexpected_status(path, response)
296
297 def create_project(self, project_name):
298- fields = {'name': project_name}
299+ if project_name.endswith('.git'):
300+ project_name = project_name[:-4]
301+ if '/' in project_name:
302+ namespace, path = project_name.rsplit('/', 1)
303+ else:
304+ namespace = None
305+ path = project_name
306+ fields = {
307+ 'path': path,
308+ 'name': path.replace('-', '_'),
309+ 'namespace_path': namespace,
310+ }
311 response = self._api_request('POST', 'projects', fields=fields)
312+ if response.status == 400:
313+ ret = json.loads(response.data)
314+ if ret.get("message", {}).get("path") == ["has already been taken"]:
315+ raise errors.AlreadyControlDirError(project_name)
316+ raise
317 if response.status == 403:
318 raise errors.PermissionDenied(response.text)
319 if response.status not in (200, 201):
320@@ -556,13 +584,15 @@
321 if labels:
322 fields['labels'] = labels
323 response = self._api_request('POST', path, fields=fields)
324+ if response.status == 400:
325+ raise GitLabError(data.get('message'), data)
326 if response.status == 403:
327 raise errors.PermissionDenied(response.text)
328 if response.status == 409:
329 raise GitLabConflict(json.loads(response.data).get('message'))
330 if response.status == 422:
331 data = json.loads(response.data)
332- raise GitLabUnprocessable(data['error'])
333+ raise GitLabUnprocessable(data.get('error'), data)
334 if response.status != 201:
335 _unexpected_status(path, response)
336 return json.loads(response.data)
337@@ -659,13 +689,18 @@
338 return self.base_hostname == host
339
340 def check(self):
341- response = self._api_request('GET', 'user')
342+ try:
343+ response = self._api_request('GET', 'user')
344+ except errors.UnexpectedHttpStatus as e:
345+ if e.code == 401:
346+ raise GitLabLoginMissing(self.base_url)
347+ raise
348 if response.status == 200:
349 self._current_user = json.loads(response.data)
350 return
351- if response == 401:
352+ if response.status == 401:
353 if json.loads(response.data) == {"message": "401 Unauthorized"}:
354- raise GitLabLoginMissing()
355+ raise GitLabLoginMissing(self.base_url)
356 else:
357 raise GitlabLoginError(response.text)
358 raise UnsupportedHoster(self.base_url)
359@@ -681,7 +716,17 @@
360 credentials = get_credentials_by_url(transport.base)
361 if credentials is not None:
362 return cls(transport, credentials.get('private_token'))
363- raise UnsupportedHoster(url)
364+ try:
365+ resp = transport.request(
366+ 'GET', 'https://%s/api/v4/projects/%s' % (host, urlutils.quote(str(project), '')))
367+ except errors.UnexpectedHttpStatus as e:
368+ raise UnsupportedHoster(url)
369+ else:
370+ if not resp.getheader('X-Gitlab-Feature-Category'):
371+ raise UnsupportedHoster(url)
372+ if resp.status in (200, 401):
373+ raise GitLabLoginMissing('https://%s/' % host)
374+ raise UnsupportedHoster(url)
375
376 @classmethod
377 def iter_instances(cls):
378@@ -806,6 +851,7 @@
379 "Source project is not a fork of the target project"]:
380 raise SourceNotDerivedFromTarget(
381 self.source_branch, self.target_branch)
382+ raise
383 return GitLabMergeProposal(self.gl, merge_request)
384
385
386
387=== modified file 'breezy/plugins/propose/cmds.py'
388--- breezy/plugins/propose/cmds.py 2020-11-18 02:15:43 +0000
389+++ breezy/plugins/propose/cmds.py 2021-04-03 12:59:06 +0000
390@@ -300,7 +300,7 @@
391 for l in description.splitlines()])
392 self.outf.write('\n')
393 except _mod_propose.HosterLoginRequired as e:
394- warning('Skipping %r, login required.', instance)
395+ warning('Skipping %s, login required.', instance)
396
397
398 class cmd_land_merge_proposal(Command):
399
400=== modified file 'breezy/propose.py'
401--- breezy/propose.py 2021-03-22 21:07:08 +0000
402+++ breezy/propose.py 2021-04-03 12:59:06 +0000
403@@ -388,7 +388,11 @@
404
405 def determine_title(description):
406 """Determine the title for a merge proposal based on full description."""
407- firstline = description.splitlines()[0]
408+ for firstline in description.splitlines():
409+ if firstline.strip():
410+ break
411+ else:
412+ raise ValueError
413 try:
414 i = firstline.index('. ')
415 except ValueError:
416
417=== modified file 'breezy/tests/per_branch/test_http.py'
418--- breezy/tests/per_branch/test_http.py 2018-11-11 04:08:32 +0000
419+++ breezy/tests/per_branch/test_http.py 2021-04-03 12:59:06 +0000
420@@ -76,4 +76,4 @@
421 # from, even if that branch has an invalid parent.
422 branch_b = self.get_branch_with_invalid_parent()
423 branch_c = branch_b.controldir.sprout('c').open_branch()
424- self.assertEqual(branch_b.user_url, branch_c.get_parent())
425+ self.assertEqual(branch_b.base, branch_c.get_parent())
426
427=== modified file 'breezy/tests/test_propose.py'
428--- breezy/tests/test_propose.py 2020-10-22 22:14:42 +0000
429+++ breezy/tests/test_propose.py 2021-04-03 12:59:06 +0000
430@@ -127,3 +127,9 @@
431
432 And here are some more details.
433 """))
434+ self.assertEqual('Release version 5.1', determine_title("""\
435+
436+Release version 5.1
437+
438+And here are some more details.
439+"""))
440
441=== modified file 'breezy/transport/http/urllib.py'
442--- breezy/transport/http/urllib.py 2021-01-10 01:22:28 +0000
443+++ breezy/transport/http/urllib.py 2021-04-03 12:59:06 +0000
444@@ -176,7 +176,7 @@
445 """
446
447 # Some responses have bodies in which we have no interest
448- _body_ignored_responses = [301, 302, 303, 307, 308, 400, 401, 403, 404, 501]
449+ _body_ignored_responses = [301, 302, 303, 307, 308, 403, 404, 501]
450
451 # in finish() below, we may have to discard several MB in the worst
452 # case. To avoid buffering that much, we read and discard by chunks

Subscribers

People subscribed via source and target branches