Merge lp:~jelmer/brz/upload-symlinks into lp:brz

Proposed by Jelmer Vernooij on 2018-09-17
Status: Merged
Approved by: Jelmer Vernooij on 2018-09-22
Approved revision: 7062
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/upload-symlinks
Merge into: lp:brz
Prerequisite: lp:~jelmer/brz/memorytransport-links
Diff against target: 240 lines (+67/-40)
3 files modified
breezy/plugins/upload/cmds.py (+60/-35)
breezy/plugins/upload/tests/test_upload.py (+5/-3)
breezy/transport/sftp.py (+2/-2)
To merge this branch: bzr merge lp:~jelmer/brz/upload-symlinks
Reviewer Review Type Date Requested Status
Vincent Ladeuil 2018-09-17 Approve on 2018-09-17
Review via email: mp+355061@code.launchpad.net

This proposal supersedes a proposal from 2018-09-17.

Commit message

Add symlink support to 'brz upload'.

Description of the change

Add symlink support to 'brz upload'.

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

Cool, thanks !

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/plugins/upload/cmds.py'
2--- breezy/plugins/upload/cmds.py 2018-03-04 15:59:37 +0000
3+++ breezy/plugins/upload/cmds.py 2018-09-17 00:27:30 +0000
4@@ -24,6 +24,7 @@
5 config,
6 lazy_import,
7 option,
8+ osutils,
9 )
10 lazy_import.lazy_import(globals(), """
11 import stat
12@@ -184,24 +185,47 @@
13 self.outf.write('Uploading %s\n' % old_relpath)
14 self._up_put_bytes(old_relpath, self.tree.get_file_text(new_relpath, id), mode)
15
16+ def _force_clear(self, relpath):
17+ try:
18+ st = self._up_stat(relpath)
19+ if stat.S_ISDIR(st.st_mode):
20+ # A simple rmdir may not be enough
21+ if not self.quiet:
22+ self.outf.write('Clearing %s/%s\n' % (
23+ self.to_transport.external_url(), relpath))
24+ self._up_delete_tree(relpath)
25+ elif stat.S_ISLNK(st.st_mode):
26+ if not self.quiet:
27+ self.outf.write('Clearing %s/%s\n' % (
28+ self.to_transport.external_url(), relpath))
29+ self._up_delete(relpath)
30+ except errors.PathError:
31+ pass
32+
33 def upload_file_robustly(self, relpath, id, mode=None):
34 """Upload a file, clearing the way on the remote side.
35
36 When doing a full upload, it may happen that a directory exists where
37 we want to put our file.
38 """
39- try:
40- st = self._up_stat(relpath)
41- if stat.S_ISDIR(st.st_mode):
42- # A simple rmdir may not be enough
43- if not self.quiet:
44- self.outf.write('Clearing %s/%s\n' % (
45- self.to_transport.external_url(), relpath))
46- self._up_delete_tree(relpath)
47- except errors.PathError:
48- pass
49+ self._force_clear(relpath)
50 self.upload_file(relpath, relpath, id, mode)
51
52+ def upload_symlink(self, relpath, target):
53+ self.to_transport.symlink(target, relpath)
54+
55+ def upload_symlink_robustly(self, relpath, target):
56+ """Handle uploading symlinks.
57+ """
58+ self._force_clear(relpath)
59+ # Target might not be there at this time; dummy file should be
60+ # overwritten at some point, possibly by another upload.
61+ target = osutils.normpath(osutils.pathjoin(
62+ osutils.dirname(relpath),
63+ target)
64+ )
65+ self.upload_symlink(relpath, target)
66+
67 def make_remote_dir(self, relpath, mode=None):
68 if mode is None:
69 mode = 0o775
70@@ -303,13 +327,16 @@
71 continue
72 if ie.kind == 'file':
73 self.upload_file_robustly(relpath, ie.file_id)
74+ elif ie.kind == 'symlink':
75+ try:
76+ self.upload_symlink_robustly(relpath, ie.symlink_target)
77+ except errors.TransportNotPossible:
78+ if not self.quiet:
79+ target = self.tree.path_content_summary(relpath)[3]
80+ self.outf.write('Not uploading symlink %s -> %s\n'
81+ % (relpath, target))
82 elif ie.kind == 'directory':
83 self.make_remote_dir_robustly(relpath)
84- elif ie.kind == 'symlink':
85- if not self.quiet:
86- target = self.tree.path_content_summary(relpath)[3]
87- self.outf.write('Not uploading symlink %s -> %s\n'
88- % (relpath, target))
89 else:
90 raise NotImplementedError
91 self.set_uploaded_revid(self.rev_id)
92@@ -347,10 +374,7 @@
93 elif kind is 'directory':
94 self.delete_remote_dir_maybe(path)
95 elif kind == 'symlink':
96- if not self.quiet:
97- target = self.tree.path_content_summary(path)[3]
98- self.outf.write('Not deleting remote symlink %s -> %s\n'
99- % (path, target))
100+ self.delete_remote_file(path)
101 else:
102 raise NotImplementedError
103
104@@ -365,12 +389,7 @@
105 # We update the old_path content because renames and
106 # deletions are differed.
107 self.upload_file(old_path, new_path, id)
108- if kind == 'symlink':
109- if not self.quiet:
110- self.outf.write('Not renaming remote symlink %s to %s\n'
111- % (old_path, new_path))
112- else:
113- self.rename_remote(old_path, new_path)
114+ self.rename_remote(old_path, new_path)
115 self.finish_renames()
116 self.finish_deletions()
117
118@@ -379,15 +398,18 @@
119 if not self.quiet:
120 self.outf.write('Ignoring %s\n' % path)
121 continue
122- if old_kind == 'file':
123+ if old_kind in ('file', 'symlink'):
124 self.delete_remote_file(path)
125- elif old_kind == 'directory':
126+ elif old_kind == 'directory':
127 self.delete_remote_dir(path)
128 else:
129 raise NotImplementedError
130
131 if new_kind == 'file':
132 self.upload_file(path, path, id)
133+ elif new_kind == 'symlink':
134+ target = self.tree.get_symlink_target(path)
135+ self.upload_symlink(path, target)
136 elif new_kind is 'directory':
137 self.make_remote_dir(path)
138 else:
139@@ -403,10 +425,13 @@
140 elif kind == 'directory':
141 self.make_remote_dir(path)
142 elif kind == 'symlink':
143- if not self.quiet:
144- target = self.tree.path_content_summary(path)[3]
145- self.outf.write('Not uploading symlink %s -> %s\n'
146- % (path, target))
147+ target = self.tree.get_symlink_target(path)
148+ try:
149+ self.upload_symlink(path, target)
150+ except errors.TransportNotPossible:
151+ if not self.quiet:
152+ self.outf.write('Not uploading symlink %s -> %s\n'
153+ % (path, target))
154 else:
155 raise NotImplementedError
156
157@@ -419,6 +444,9 @@
158 continue
159 if kind == 'file':
160 self.upload_file(path, path, id)
161+ elif kind == 'symlink':
162+ target = self.tree.get_symlink_target(path)
163+ self.upload_symlink(path, target)
164 else:
165 raise NotImplementedError
166
167@@ -542,14 +570,11 @@
168 locked.unlock()
169
170 # We uploaded successfully, remember it
171- branch.lock_write()
172- try:
173+ with branch.lock_write():
174 upload_location = conf.get('upload_location')
175 if upload_location is None or remember:
176 conf.set('upload_location',
177 urlutils.unescape(to_transport.base))
178 if auto is not None:
179 conf.set('upload_auto', auto)
180- finally:
181- branch.unlock()
182
183
184=== modified file 'breezy/plugins/upload/tests/test_upload.py'
185--- breezy/plugins/upload/tests/test_upload.py 2018-07-02 23:41:13 +0000
186+++ breezy/plugins/upload/tests/test_upload.py 2018-09-17 00:27:30 +0000
187@@ -47,6 +47,8 @@
188 if features.paramiko.available():
189 from ....transport import sftp
190 usable_classes.add(sftp.SFTPTransport)
191+ from ....transport import local
192+ usable_classes.add(local.LocalTransport)
193 for name, d in basis:
194 t_class = d['transport_class']
195 if t_class in usable_classes:
196@@ -411,19 +413,19 @@
197
198 self.do_upload()
199
200- self.assertUpPathDoesNotExist('link')
201+ self.assertUpPathExists('link')
202
203 def test_rename_symlink(self):
204 self.make_branch_and_working_tree()
205 old_name, new_name = 'old-link', 'new-link'
206 self.add_symlink(old_name, 'target')
207 self.do_full_upload()
208+
209 self.rename_any(old_name, new_name)
210
211 self.do_upload()
212
213- self.assertUpPathDoesNotExist(old_name)
214- self.assertUpPathDoesNotExist(new_name)
215+ self.assertUpPathExists(new_name)
216
217 def get_upload_auto(self):
218 # We need a fresh branch to check what has been saved on disk
219
220=== modified file 'breezy/transport/sftp.py'
221--- breezy/transport/sftp.py 2018-08-31 05:50:14 +0000
222+++ breezy/transport/sftp.py 2018-09-17 00:27:30 +0000
223@@ -849,7 +849,7 @@
224 """See Transport.readlink."""
225 path = self._remote_path(relpath)
226 try:
227- return self._get_sftp().readlink(path)
228+ return self._get_sftp().readlink(self._remote_path(path))
229 except (IOError, paramiko.SSHException) as e:
230 self._translate_io_exception(e, path, ': unable to readlink')
231
232@@ -857,7 +857,7 @@
233 """See Transport.symlink."""
234 try:
235 conn = self._get_sftp()
236- sftp_retval = conn.symlink(source, link_name)
237+ sftp_retval = conn.symlink(source, self._remote_path(link_name))
238 except (IOError, paramiko.SSHException) as e:
239 self._translate_io_exception(e, link_name,
240 ': unable to create symlink to %r' % (source))

Subscribers

People subscribed via source and target branches