/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

Only actually fetch tags if "branch.fetch_tags" is set to true.

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