17
17
"""Transport for the local filesystem.
19
This is a fairly thin wrapper on regular file IO.
19
This is a fairly thin wrapper on regular file IO."""
25
23
from stat import ST_MODE, S_ISDIR, ST_SIZE
28
from bzrlib.osutils import (abspath, realpath, normpath, pathjoin, rename,
29
check_legal_path, rmtree)
30
from bzrlib.symbol_versioning import warn
31
27
from bzrlib.trace import mutter
32
28
from bzrlib.transport import Transport, Server
33
import bzrlib.urlutils as urlutils
29
from bzrlib.osutils import abspath, realpath, normpath, pathjoin, rename
36
32
class LocalTransport(Transport):
39
35
def __init__(self, base):
40
36
"""Set the base path where files will be stored."""
41
if not base.startswith('file://'):
42
warn("Instantiating LocalTransport with a filesystem path"
43
" is deprecated as of bzr 0.8."
44
" Please use bzrlib.transport.get_transport()"
45
" or pass in a file:// url.",
49
base = urlutils.local_path_to_url(base)
37
if base.startswith('file://'):
39
# realpath is incompatible with symlinks. When we traverse
40
# up we might be able to normpath stuff. RBC 20051003
41
base = normpath(abspath(base))
50
42
if base[-1] != '/':
52
44
super(LocalTransport, self).__init__(base)
53
self._local_base = urlutils.local_path_from_url(base)
55
46
def should_cache(self):
66
57
return LocalTransport(self.abspath(offset))
68
def _abspath(self, relative_reference):
69
"""Return a path for use in os calls.
71
Several assumptions are made:
72
- relative_reference does not contain '..'
73
- relative_reference is url escaped.
75
if relative_reference in ('.', ''):
76
return self._local_base
77
return self._local_base + urlutils.unescape(relative_reference)
79
59
def abspath(self, relpath):
80
"""Return the full url to the given relative URL."""
81
# TODO: url escape the result. RBC 20060523.
60
"""Return the full url to the given relative URL.
61
This can be supplied with a string or a list
82
63
assert isinstance(relpath, basestring), (type(relpath), relpath)
83
# jam 20060426 Using normpath on the real path, because that ensures
84
# proper handling of stuff like
85
path = normpath(pathjoin(self._local_base, urlutils.unescape(relpath)))
86
return urlutils.local_path_to_url(path)
88
def local_abspath(self, relpath):
89
"""Transform the given relative path URL into the actual path on disk
91
This function only exists for the LocalTransport, since it is
92
the only one that has direct local access.
93
This is mostly for stuff like WorkingTree which needs to know
94
the local working directory.
96
This function is quite expensive: it calls realpath which resolves
99
absurl = self.abspath(relpath)
100
# mutter(u'relpath %s => base: %s, absurl %s', relpath, self.base, absurl)
101
return urlutils.local_path_from_url(absurl)
64
return pathjoin(self.base, urllib.unquote(relpath))
103
66
def relpath(self, abspath):
104
67
"""Return the local path portion from a given absolute path.
69
from bzrlib.osutils import relpath
106
70
if abspath is None:
109
return urlutils.file_relpath(
110
urlutils.strip_trailing_slash(self.base),
111
urlutils.strip_trailing_slash(abspath))
72
if abspath.endswith('/'):
73
abspath = abspath[:-1]
74
return relpath(self.base[:-1], abspath)
113
76
def has(self, relpath):
114
return os.access(self._abspath(relpath), os.F_OK)
77
return os.access(self.abspath(relpath), os.F_OK)
116
79
def get(self, relpath):
117
80
"""Get the file at the given relative path.
161
123
"""Create a directory at the given path."""
164
path = self._abspath(relpath)
126
path = self.abspath(relpath)
166
128
if mode is not None:
167
129
os.chmod(path, mode)
168
130
except (IOError, OSError),e:
169
131
self._translate_error(e, path)
171
def append(self, relpath, f, mode=None):
172
"""Append the text in the file-like object into the final location."""
173
abspath = self._abspath(relpath)
133
def append(self, relpath, f):
134
"""Append the text in the file-like object into the final
175
fp = open(abspath, 'ab')
176
# FIXME should we really be chmodding every time ? RBC 20060523
178
os.chmod(abspath, mode)
138
fp = open(self.abspath(relpath), 'ab')
179
139
except (IOError, OSError),e:
180
140
self._translate_error(e, relpath)
181
# win32 workaround (tell on an unwritten file returns 0)
184
141
self._pump(f, fp)
187
143
def copy(self, rel_from, rel_to):
188
144
"""Copy the item at rel_from to the location at rel_to"""
189
path_from = self._abspath(rel_from)
190
path_to = self._abspath(rel_to)
146
path_from = self.abspath(rel_from)
147
path_to = self.abspath(rel_to)
192
149
shutil.copy(path_from, path_to)
193
150
except (IOError, OSError),e:
194
151
# TODO: What about path_to?
195
152
self._translate_error(e, path_from)
197
def rename(self, rel_from, rel_to):
198
path_from = self._abspath(rel_from)
200
# *don't* call bzrlib.osutils.rename, because we want to
201
# detect errors on rename
202
os.rename(path_from, self._abspath(rel_to))
203
except (IOError, OSError),e:
204
# TODO: What about path_to?
205
self._translate_error(e, path_from)
207
154
def move(self, rel_from, rel_to):
208
155
"""Move the item at rel_from to the location at rel_to"""
209
path_from = self._abspath(rel_from)
210
path_to = self._abspath(rel_to)
156
path_from = self.abspath(rel_from)
157
path_to = self.abspath(rel_to)
213
# this version will delete the destination if necessary
214
160
rename(path_from, path_to)
215
161
except (IOError, OSError),e:
216
162
# TODO: What about path_to?
220
166
"""Delete the item at relpath"""
223
path = self._abspath(relpath)
169
path = self.abspath(relpath)
225
171
except (IOError, OSError),e:
172
# TODO: What about path_to?
226
173
self._translate_error(e, path)
228
175
def copy_to(self, relpaths, other, mode=None, pb=None):
234
181
# Both from & to are on the local filesystem
235
182
# Unfortunately, I can't think of anything faster than just
236
183
# copying them across, one by one :(
237
186
total = self._get_total(relpaths)
239
188
for path in relpaths:
240
189
self._update_pb(pb, 'copy-to', count, total)
242
mypath = self._abspath(path)
243
otherpath = other._abspath(path)
191
mypath = self.abspath(path)
192
otherpath = other.abspath(path)
244
193
shutil.copy(mypath, otherpath)
245
194
if mode is not None:
246
195
os.chmod(otherpath, mode)
260
209
WARNING: many transports do not support this, so trying avoid using
261
210
it if at all possible.
263
path = self._abspath(relpath)
265
return [urlutils.escape(entry) for entry in os.listdir(path)]
266
except (IOError, OSError), e:
214
path = self.abspath(relpath)
215
return os.listdir(path)
216
except (IOError, OSError),e:
267
217
self._translate_error(e, path)
269
219
def stat(self, relpath):
295
245
:return: A lock object, which should be passed to Transport.unlock()
297
247
from bzrlib.lock import WriteLock
298
return WriteLock(self._abspath(relpath))
248
return WriteLock(self.abspath(relpath))
300
250
def rmdir(self, relpath):
301
251
"""See Transport.rmdir."""
304
path = self._abspath(relpath)
254
path = self.abspath(relpath)
306
256
except (IOError, OSError),e:
307
257
self._translate_error(e, path)
309
def _can_roundtrip_unix_modebits(self):
310
if sys.platform == 'win32':
317
259
class ScratchTransport(LocalTransport):
318
260
"""A transport that works in a temporary dir and cleans up after itself.