/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to dir.py

Cope with files disappearing during commit.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2007 Canonical Ltd
 
2
# Copyright (C) 2010 Jelmer Vernooij
2
3
#
3
4
# This program is free software; you can redistribute it and/or modify
4
5
# it under the terms of the GNU General Public License as published by
14
15
# along with this program; if not, write to the Free Software
15
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
 
17
 
"""An adapter between a Git control dir and a Bazaar BzrDir"""
18
 
 
19
 
import os
20
 
 
21
 
import bzrlib
22
 
from bzrlib.lazy_import import lazy_import
 
18
"""An adapter between a Git control dir and a Bazaar ControlDir."""
 
19
 
23
20
from bzrlib import (
24
 
    bzrdir,
 
21
    errors as bzr_errors,
25
22
    lockable_files,
 
23
    trace,
 
24
    osutils,
26
25
    urlutils,
27
 
    )
28
 
 
29
 
lazy_import(globals(), """
30
 
from bzrlib.lockable_files import TransportLock
31
 
from bzrlib.plugins.git import (
32
 
    errors,
33
 
    branch,
34
 
    repository,
35
 
    workingtree,
36
 
    )
37
 
""")
38
 
 
 
26
    version_info as bzrlib_version,
 
27
    )
 
28
from bzrlib.bzrdir import CreateRepository
 
29
from bzrlib.transport import do_catching_redirections
 
30
 
 
31
LockWarner = getattr(lockable_files, "_LockWarner", None)
 
32
 
 
33
from bzrlib.controldir import (
 
34
    ControlDir,
 
35
    ControlDirFormat,
 
36
    format_registry,
 
37
    )
39
38
 
40
39
 
41
40
class GitLock(object):
42
41
    """A lock that thunks through to Git."""
43
42
 
 
43
    def __init__(self):
 
44
        self.lock_name = "git lock"
 
45
 
44
46
    def lock_write(self, token=None):
45
47
        pass
46
48
 
56
58
    def validate_token(self, token):
57
59
        pass
58
60
 
 
61
    def break_lock(self):
 
62
        raise NotImplementedError(self.break_lock)
 
63
 
 
64
    def dont_leave_in_place(self):
 
65
        raise NotImplementedError(self.dont_leave_in_place)
 
66
 
 
67
    def leave_in_place(self):
 
68
        raise NotImplementedError(self.leave_in_place)
 
69
 
59
70
 
60
71
class GitLockableFiles(lockable_files.LockableFiles):
61
72
    """Git specific lockable files abstraction."""
64
75
        self._lock = lock
65
76
        self._transaction = None
66
77
        self._lock_mode = None
67
 
        self._lock_count = 0
68
78
        self._transport = transport
69
 
 
70
 
 
71
 
class GitDir(bzrdir.BzrDir):
 
79
        if LockWarner is None:
 
80
            # Bzr 1.13
 
81
            self._lock_count = 0
 
82
        else:
 
83
            self._lock_warner = LockWarner(repr(self))
 
84
 
 
85
 
 
86
class GitDirConfig(object):
 
87
 
 
88
    def get_default_stack_on(self):
 
89
        return None
 
90
 
 
91
    def set_default_stack_on(self, value):
 
92
        raise bzr_errors.BzrError("Cannot set configuration")
 
93
 
 
94
 
 
95
class GitControlDirFormat(ControlDirFormat):
 
96
 
 
97
    _lock_class = lockable_files.TransportLock
 
98
 
 
99
    colocated_branches = True
 
100
    fixed_components = True
 
101
 
 
102
    def __eq__(self, other):
 
103
        return type(self) == type(other)
 
104
 
 
105
    def is_supported(self):
 
106
        return True
 
107
 
 
108
    def network_name(self):
 
109
        return "git"
 
110
 
 
111
 
 
112
class GitDir(ControlDir):
72
113
    """An adapter to the '.git' dir used by git."""
73
114
 
74
115
    def is_supported(self):
75
116
        return True
76
117
 
 
118
    def can_convert_format(self):
 
119
        return False
 
120
 
 
121
    def break_lock(self):
 
122
        pass
 
123
 
77
124
    def cloning_metadir(self, stacked=False):
 
125
        return format_registry.make_bzrdir("default")
 
126
 
 
127
    def checkout_metadir(self, stacked=False):
 
128
        return format_registry.make_bzrdir("default")
 
129
 
 
130
    def _branch_name_to_ref(self, name):
 
131
        raise NotImplementedError(self._branch_name_to_ref)
 
132
 
 
133
    def get_config(self):
 
134
        return GitDirConfig()
 
135
 
 
136
    def _available_backup_name(self, base):
 
137
        return osutils.available_backup_name(base, self.root_transport.has)
 
138
 
 
139
    def sprout(self, url, revision_id=None, force_new_repo=False,
 
140
               recurse='down', possible_transports=None,
 
141
               accelerator_tree=None, hardlink=False, stacked=False,
 
142
               source_branch=None, create_tree_if_local=True):
 
143
        from bzrlib.repository import InterRepository
 
144
        from bzrlib.transport.local import LocalTransport
 
145
        from bzrlib.transport import get_transport
 
146
        target_transport = get_transport(url, possible_transports)
 
147
        target_transport.ensure_base()
 
148
        cloning_format = self.cloning_metadir()
 
149
        # Create/update the result branch
 
150
        result = cloning_format.initialize_on_transport(target_transport)
 
151
        source_branch = self.open_branch()
 
152
        source_repository = self.find_repository()
 
153
        try:
 
154
            result_repo = result.find_repository()
 
155
        except bzr_errors.NoRepositoryPresent:
 
156
            result_repo = result.create_repository()
 
157
            target_is_empty = True
 
158
        else:
 
159
            target_is_empty = None # Unknown
78
160
        if stacked:
79
 
            return bzrlib.bzrdir.format_registry.make_bzrdir("pack-0.92")
80
 
        else:
81
 
            return bzrlib.bzrdir.format_registry.make_bzrdir("1.6")
 
161
            raise bzr_errors.IncompatibleRepositories(source_repository, result_repo)
 
162
        interrepo = InterRepository.get(source_repository, result_repo)
 
163
 
 
164
        if revision_id is not None:
 
165
            determine_wants = interrepo.get_determine_wants_revids(
 
166
                [revision_id], include_tags=True)
 
167
        else:
 
168
            determine_wants = interrepo.determine_wants_all
 
169
        interrepo.fetch_objects(determine_wants=determine_wants,
 
170
            mapping=source_branch.mapping)
 
171
        result_branch = source_branch.sprout(result,
 
172
            revision_id=revision_id, repository=result_repo)
 
173
        if (create_tree_if_local and isinstance(target_transport, LocalTransport)
 
174
            and (result_repo is None or result_repo.make_working_trees())):
 
175
            wt = result.create_workingtree(accelerator_tree=accelerator_tree,
 
176
                hardlink=hardlink, from_branch=result_branch)
 
177
            wt.lock_write()
 
178
            try:
 
179
                if wt.path2id('') is None:
 
180
                    try:
 
181
                        wt.set_root_id(self.open_workingtree.get_root_id())
 
182
                    except bzr_errors.NoWorkingTree:
 
183
                        pass
 
184
            finally:
 
185
                wt.unlock()
 
186
        return result
 
187
 
 
188
    def clone_on_transport(self, transport, revision_id=None,
 
189
        force_new_repo=False, preserve_stacking=False, stacked_on=None,
 
190
        create_prefix=False, use_existing_dir=True, no_tree=False):
 
191
        """See ControlDir.clone_on_transport."""
 
192
        from bzrlib.repository import InterRepository
 
193
        from bzrlib.plugins.git.mapping import default_mapping
 
194
        if no_tree:
 
195
            format = BareLocalGitControlDirFormat()
 
196
        else:
 
197
            format = LocalGitControlDirFormat()
 
198
        (target_repo, target_controldir, stacking, repo_policy) = format.initialize_on_transport_ex(transport, use_existing_dir=use_existing_dir, create_prefix=create_prefix, force_new_repo=force_new_repo)
 
199
        target_git_repo = target_repo._git
 
200
        source_repo = self.open_repository()
 
201
        source_git_repo = source_repo._git
 
202
        interrepo = InterRepository.get(source_repo, target_repo)
 
203
        if revision_id is not None:
 
204
            determine_wants = interrepo.get_determine_wants_revids([revision_id], include_tags=True)
 
205
        else:
 
206
            determine_wants = interrepo.determine_wants_all
 
207
        (pack_hint, _, refs) = interrepo.fetch_objects(determine_wants,
 
208
            mapping=default_mapping)
 
209
        for name, val in refs.iteritems():
 
210
            target_git_repo.refs[name] = val
 
211
        lockfiles = GitLockableFiles(transport, GitLock())
 
212
        return self.__class__(transport, lockfiles, target_git_repo, format)
 
213
 
 
214
    def find_repository(self):
 
215
        """Find the repository that should be used.
 
216
 
 
217
        This does not require a branch as we use it to find the repo for
 
218
        new branches as well as to hook existing branches up to their
 
219
        repository.
 
220
        """
 
221
        return self.open_repository()
 
222
 
 
223
 
 
224
class LocalGitControlDirFormat(GitControlDirFormat):
 
225
    """The .git directory control format."""
 
226
 
 
227
    bare = False
 
228
 
 
229
    @classmethod
 
230
    def _known_formats(self):
 
231
        return set([LocalGitControlDirFormat()])
 
232
 
 
233
    @property
 
234
    def repository_format(self):
 
235
        from bzrlib.plugins.git.repository import GitRepositoryFormat
 
236
        return GitRepositoryFormat()
 
237
 
 
238
    def get_branch_format(self):
 
239
        from bzrlib.plugins.git.branch import GitBranchFormat
 
240
        return GitBranchFormat()
 
241
 
 
242
    def open(self, transport, _found=None):
 
243
        """Open this directory.
 
244
 
 
245
        """
 
246
        from bzrlib.plugins.git.transportgit import TransportRepo
 
247
        gitrepo = TransportRepo(transport)
 
248
        lockfiles = GitLockableFiles(transport, GitLock())
 
249
        return LocalGitDir(transport, lockfiles, gitrepo, self)
 
250
 
 
251
    def get_format_description(self):
 
252
        return "Local Git Repository"
 
253
 
 
254
    def initialize_on_transport(self, transport):
 
255
        from bzrlib.plugins.git.transportgit import TransportRepo
 
256
        TransportRepo.init(transport, bare=self.bare)
 
257
        return self.open(transport)
 
258
 
 
259
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
 
260
        create_prefix=False, force_new_repo=False, stacked_on=None,
 
261
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
 
262
        shared_repo=False, vfs_only=False):
 
263
        def make_directory(transport):
 
264
            transport.mkdir('.')
 
265
            return transport
 
266
        def redirected(transport, e, redirection_notice):
 
267
            trace.note(redirection_notice)
 
268
            return transport._redirected_to(e.source, e.target)
 
269
        try:
 
270
            transport = do_catching_redirections(make_directory, transport,
 
271
                redirected)
 
272
        except bzr_errors.FileExists:
 
273
            if not use_existing_dir:
 
274
                raise
 
275
        except bzr_errors.NoSuchFile:
 
276
            if not create_prefix:
 
277
                raise
 
278
            transport.create_prefix()
 
279
        controldir = self.initialize_on_transport(transport)
 
280
        repository = controldir.open_repository()
 
281
        repository.lock_write()
 
282
        return (repository, controldir, False, CreateRepository(controldir))
 
283
 
 
284
    def is_supported(self):
 
285
        return True
 
286
 
 
287
 
 
288
class BareLocalGitControlDirFormat(LocalGitControlDirFormat):
 
289
 
 
290
    bare = True
 
291
    supports_workingtrees = False
 
292
 
 
293
    def get_format_description(self):
 
294
        return "Local Git Repository (bare)"
82
295
 
83
296
 
84
297
class LocalGitDir(GitDir):
85
298
    """An adapter to the '.git' dir used by git."""
86
299
 
87
 
    _gitrepository_class = repository.LocalGitRepository
 
300
    def _get_gitrepository_class(self):
 
301
        from bzrlib.plugins.git.repository import LocalGitRepository
 
302
        return LocalGitRepository
 
303
 
 
304
    _gitrepository_class = property(_get_gitrepository_class)
 
305
 
 
306
    @property
 
307
    def user_transport(self):
 
308
        return self.root_transport
 
309
 
 
310
    @property
 
311
    def control_transport(self):
 
312
        return self.transport
88
313
 
89
314
    def __init__(self, transport, lockfiles, gitrepo, format):
90
315
        self._format = format
91
316
        self.root_transport = transport
 
317
        self._mode_check_done = False
92
318
        self._git = gitrepo
93
319
        if gitrepo.bare:
94
320
            self.transport = transport
95
321
        else:
96
322
            self.transport = transport.clone('.git')
97
323
        self._lockfiles = lockfiles
98
 
 
99
 
    def get_branch_transport(self, branch_format):
 
324
        self._mode_check_done = None
 
325
 
 
326
    def _branch_name_to_ref(self, name):
 
327
        from bzrlib.plugins.git.refs import branch_name_to_ref
 
328
        ref = branch_name_to_ref(name, None)
 
329
        if ref == "HEAD":
 
330
            from dulwich.repo import SYMREF
 
331
            refcontents = self._git.refs.read_ref(ref)
 
332
            if refcontents.startswith(SYMREF):
 
333
                ref = refcontents[len(SYMREF):]
 
334
        return ref
 
335
 
 
336
    def is_control_filename(self, filename):
 
337
        return (filename == '.git' or filename.startswith('.git/'))
 
338
 
 
339
    def get_branch_transport(self, branch_format, name=None):
100
340
        if branch_format is None:
101
341
            return self.transport
102
 
        if isinstance(branch_format, LocalGitBzrDirFormat):
103
 
            return self.transport
104
 
        raise errors.bzr_errors.IncompatibleFormat(branch_format, self._format)
105
 
 
106
 
    get_repository_transport = get_branch_transport
107
 
    get_workingtree_transport = get_branch_transport
108
 
 
109
 
    def open_branch(self, ignored=None):
 
342
        if isinstance(branch_format, LocalGitControlDirFormat):
 
343
            return self.transport
 
344
        raise bzr_errors.IncompatibleFormat(branch_format, self._format)
 
345
 
 
346
    def get_repository_transport(self, format):
 
347
        if format is None:
 
348
            return self.transport
 
349
        if isinstance(format, LocalGitControlDirFormat):
 
350
            return self.transport
 
351
        raise bzr_errors.IncompatibleFormat(format, self._format)
 
352
 
 
353
    def get_workingtree_transport(self, format):
 
354
        if format is None:
 
355
            return self.transport
 
356
        if isinstance(format, LocalGitControlDirFormat):
 
357
            return self.transport
 
358
        raise bzr_errors.IncompatibleFormat(format, self._format)
 
359
 
 
360
    def open_branch(self, name=None, unsupported=False, ignore_fallbacks=None):
110
361
        """'create' a branch for this dir."""
111
362
        repo = self.open_repository()
112
 
        return branch.LocalGitBranch(self, repo, "HEAD", repo._git.head(), self._lockfiles)
113
 
 
114
 
    def open_repository(self, shared=False):
 
363
        from bzrlib.plugins.git.branch import LocalGitBranch
 
364
        return LocalGitBranch(self, repo, self._branch_name_to_ref(name),
 
365
            self._lockfiles)
 
366
 
 
367
    def destroy_branch(self, name=None):
 
368
        refname = self._branch_name_to_ref(name)
 
369
        if not refname in self._git.refs:
 
370
            raise bzr_errors.NotBranchError(self.root_transport.base,
 
371
                    bzrdir=self)
 
372
        del self._git.refs[refname]
 
373
 
 
374
    def destroy_repository(self):
 
375
        raise bzr_errors.UnsupportedOperation(self.destroy_repository, self)
 
376
 
 
377
    def destroy_workingtree(self):
 
378
        raise bzr_errors.UnsupportedOperation(self.destroy_workingtree, self)
 
379
 
 
380
    def needs_format_conversion(self, format=None):
 
381
        return not isinstance(self._format, format.__class__)
 
382
 
 
383
    def list_branches(self):
 
384
        ret = []
 
385
        for name in self._git.get_refs():
 
386
            if name.startswith("refs/heads/"):
 
387
                ret.append(self.open_branch(name=name))
 
388
        return ret
 
389
 
 
390
    def open_repository(self):
115
391
        """'open' a repository for this dir."""
116
392
        return self._gitrepository_class(self, self._lockfiles)
117
393
 
118
394
    def open_workingtree(self, recommend_upgrade=True):
119
 
        if self._git.bare:
120
 
            loc = urlutils.unescape_for_display(self.root_transport.base, 'ascii')
121
 
            raise errors.bzr_errors.NoWorkingTree(loc)
122
 
        else:
123
 
            return workingtree.GitWorkingTree(self, self.open_repository(), 
124
 
                                                  self.open_branch())
 
395
        if not self._git.bare:
 
396
            from dulwich.errors import NoIndexPresent
 
397
            repo = self.open_repository()
 
398
            try:
 
399
                index = repo._git.open_index()
 
400
            except NoIndexPresent:
 
401
                pass
 
402
            else:
 
403
                from bzrlib.plugins.git.workingtree import GitWorkingTree
 
404
                try:
 
405
                    branch = self.open_branch()
 
406
                except bzr_errors.NotBranchError:
 
407
                    pass
 
408
                else:
 
409
                    return GitWorkingTree(self, repo, branch, index)
 
410
        loc = urlutils.unescape_for_display(self.root_transport.base, 'ascii')
 
411
        raise bzr_errors.NoWorkingTree(loc)
125
412
 
126
413
    def create_repository(self, shared=False):
 
414
        from bzrlib.plugins.git.repository import GitRepositoryFormat
 
415
        if shared:
 
416
            raise bzr_errors.IncompatibleFormat(GitRepositoryFormat(), self._format)
127
417
        return self.open_repository()
128
418
 
129
 
 
130
 
class GitBzrDirFormat(bzrdir.BzrDirFormat):
131
 
    _lock_class = TransportLock
132
 
 
133
 
    def is_supported(self):
134
 
        return True
135
 
 
136
 
 
137
 
class LocalGitBzrDirFormat(GitBzrDirFormat):
138
 
    """The .git directory control format."""
139
 
 
140
 
    _gitdir_class = LocalGitDir
141
 
 
142
 
    @classmethod
143
 
    def _known_formats(self):
144
 
        return set([LocalGitBzrDirFormat()])
145
 
 
146
 
    def open(self, transport, _found=None):
147
 
        """Open this directory.
148
 
 
149
 
        """
150
 
        from bzrlib.plugins.git import git
151
 
        # we dont grok readonly - git isn't integrated with transport.
152
 
        url = transport.base
153
 
        if url.startswith('readonly+'):
154
 
            url = url[len('readonly+'):]
155
 
 
156
 
        try:
157
 
            gitrepo = git.repo.Repo(transport.local_abspath("."))
158
 
        except errors.bzr_errors.NotLocalUrl:
159
 
            raise errors.bzr_errors.NotBranchError(path=transport.base)
160
 
        lockfiles = GitLockableFiles(transport, GitLock())
161
 
        return self._gitdir_class(transport, lockfiles, gitrepo, self)
162
 
 
163
 
    @classmethod
164
 
    def probe_transport(klass, transport):
165
 
        """Our format is present if the transport ends in '.not/'."""
166
 
        from bzrlib.plugins.git import git
167
 
        # little ugly, but works
168
 
        format = klass()
169
 
        # delegate to the main opening code. This pays a double rtt cost at the
170
 
        # moment, so perhaps we want probe_transport to return the opened thing
171
 
        # rather than an openener ? or we could return a curried thing with the
172
 
        # dir to open already instantiated ? Needs more thought.
173
 
        try:
174
 
            format.open(transport)
175
 
            return format
176
 
        except git.errors.NotGitRepository, e:
177
 
            raise errors.bzr_errors.NotBranchError(path=transport.base)
178
 
        raise errors.bzr_errors.NotBranchError(path=transport.base)
179
 
 
180
 
    def get_format_description(self):
181
 
        return "Local Git Repository"
182
 
 
183
 
    def get_format_string(self):
184
 
        return "Local Git Repository"
185
 
 
186
 
    def initialize_on_transport(self, transport):
187
 
        from bzrlib.transport.local import LocalTransport
188
 
        from bzrlib.plugins.git import git
189
 
 
190
 
        if not isinstance(transport, LocalTransport):
191
 
            raise NotImplementedError(self.initialize, 
192
 
                "Can't create Git Repositories/branches on "
193
 
                "non-local transports")
194
 
 
195
 
        git.repo.Repo.create(transport.local_abspath(".")) 
196
 
        return self.open(transport)
197
 
 
198
 
    def is_supported(self):
199
 
        return True
200
 
 
201
 
 
202
 
class RemoteGitBzrDirFormat(GitBzrDirFormat):
203
 
    """The .git directory control format."""
204
 
 
205
 
    @classmethod
206
 
    def _known_formats(self):
207
 
        return set([RemoteGitBzrDirFormat()])
208
 
 
209
 
    def open(self, transport, _found=None):
210
 
        """Open this directory.
211
 
 
212
 
        """
213
 
        from bzrlib.plugins.git.remote import RemoteGitDir, GitSmartTransport
214
 
        if not isinstance(transport, GitSmartTransport):
215
 
            raise errors.bzr_errors.NotBranchError(transport.base)
216
 
        # we dont grok readonly - git isn't integrated with transport.
217
 
        url = transport.base
218
 
        if url.startswith('readonly+'):
219
 
            url = url[len('readonly+'):]
220
 
 
221
 
        lockfiles = GitLockableFiles(transport, GitLock())
222
 
        return RemoteGitDir(transport, lockfiles, self)
223
 
 
224
 
    @classmethod
225
 
    def probe_transport(klass, transport):
226
 
        """Our format is present if the transport ends in '.not/'."""
227
 
        # little ugly, but works
228
 
        format = klass()
229
 
        from bzrlib.plugins.git.remote import GitSmartTransport
230
 
        if not isinstance(transport, GitSmartTransport):
231
 
            raise errors.bzr_errors.NotBranchError(transport.base)
232
 
        # The only way to know a path exists and contains a valid repository 
233
 
        # is to do a request against it:
234
 
        try:
235
 
            transport.fetch_pack(lambda x: [], None, lambda x: None, 
236
 
                                 lambda x: mutter("git: %s" % x))
237
 
        except GitProtocolException, e:
238
 
            raise errors.bzr_errors.NotBranchError(path=transport.base)
239
 
        else:
240
 
            return format
241
 
        raise errors.bzr_errors.NotBranchError(path=transport.base)
242
 
 
243
 
    def get_format_description(self):
244
 
        return "Remote Git Repository"
245
 
 
246
 
    def get_format_string(self):
247
 
        return "Remote Git Repository"
248
 
 
249
 
    def initialize_on_transport(self, transport):
250
 
        raise errors.bzr_errors.UninitializableFormat(self)
 
419
    def create_branch(self, name=None, repository=None):
 
420
        refname = self._branch_name_to_ref(name)
 
421
        from dulwich.protocol import ZERO_SHA
 
422
        self._git.refs[refname or "HEAD"] = ZERO_SHA
 
423
        return self.open_branch(name)
 
424
 
 
425
    def backup_bzrdir(self):
 
426
        if self._git.bare:
 
427
            self.root_transport.copy_tree(".git", ".git.backup")
 
428
            return (self.root_transport.abspath(".git"),
 
429
                    self.root_transport.abspath(".git.backup"))
 
430
        else:
 
431
            raise bzr_errors.BzrError("Unable to backup bare repositories")
 
432
 
 
433
    def create_workingtree(self, revision_id=None, from_branch=None,
 
434
        accelerator_tree=None, hardlink=False):
 
435
        if self._git.bare:
 
436
            raise bzr_errors.UnsupportedOperation(self.create_workingtree, self)
 
437
        from dulwich.index import write_index
 
438
        from dulwich.pack import SHA1Writer
 
439
        f = open(self.transport.local_abspath("index"), 'w+')
 
440
        try:
 
441
            f = SHA1Writer(f)
 
442
            write_index(f, [])
 
443
        finally:
 
444
            f.close()
 
445
        return self.open_workingtree()
 
446
 
 
447
    def _find_or_create_repository(self, force_new_repo=None):
 
448
        return self.create_repository(shared=False)
 
449
 
 
450
    def _find_creation_modes(self):
 
451
        """Determine the appropriate modes for files and directories.
 
452
 
 
453
        They're always set to be consistent with the base directory,
 
454
        assuming that this transport allows setting modes.
 
455
        """
 
456
        # TODO: Do we need or want an option (maybe a config setting) to turn
 
457
        # this off or override it for particular locations? -- mbp 20080512
 
458
        if self._mode_check_done:
 
459
            return
 
460
        self._mode_check_done = True
 
461
        try:
 
462
            st = self.transport.stat('.')
 
463
        except bzr_errors.TransportNotPossible:
 
464
            self._dir_mode = None
 
465
            self._file_mode = None
 
466
        else:
 
467
            # Check the directory mode, but also make sure the created
 
468
            # directories and files are read-write for this user. This is
 
469
            # mostly a workaround for filesystems which lie about being able to
 
470
            # write to a directory (cygwin & win32)
 
471
            if (st.st_mode & 07777 == 00000):
 
472
                # FTP allows stat but does not return dir/file modes
 
473
                self._dir_mode = None
 
474
                self._file_mode = None
 
475
            else:
 
476
                self._dir_mode = (st.st_mode & 07777) | 00700
 
477
                # Remove the sticky and execute bits for files
 
478
                self._file_mode = self._dir_mode & ~07111
 
479
 
 
480
    def _get_file_mode(self):
 
481
        """Return Unix mode for newly created files, or None.
 
482
        """
 
483
        if not self._mode_check_done:
 
484
            self._find_creation_modes()
 
485
        return self._file_mode
 
486
 
 
487
    def _get_dir_mode(self):
 
488
        """Return Unix mode for newly created directories, or None.
 
489
        """
 
490
        if not self._mode_check_done:
 
491
            self._find_creation_modes()
 
492
        return self._dir_mode
251
493