/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 breezy/git/dir.py

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007 Canonical Ltd
2
 
# Copyright (C) 2010-2018 Jelmer Vernooij
3
 
#
4
 
# This program is free software; you can redistribute it and/or modify
5
 
# it under the terms of the GNU General Public License as published by
6
 
# the Free Software Foundation; either version 2 of the License, or
7
 
# (at your option) any later version.
8
 
#
9
 
# This program is distributed in the hope that it will be useful,
10
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 
# GNU General Public License for more details.
13
 
#
14
 
# You should have received a copy of the GNU General Public License
15
 
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
 
 
18
 
"""An adapter between a Git control dir and a Bazaar ControlDir."""
19
 
 
20
 
from __future__ import absolute_import
21
 
 
22
 
from .. import (
23
 
    branch as _mod_branch,
24
 
    errors as bzr_errors,
25
 
    trace,
26
 
    osutils,
27
 
    repository as _mod_repository,
28
 
    revision as _mod_revision,
29
 
    urlutils,
30
 
    )
31
 
from ..sixish import (
32
 
    PY3,
33
 
    viewitems,
34
 
    )
35
 
from ..transport import (
36
 
    do_catching_redirections,
37
 
    get_transport_from_path,
38
 
    )
39
 
 
40
 
from ..controldir import (
41
 
    BranchReferenceLoop,
42
 
    ControlDir,
43
 
    ControlDirFormat,
44
 
    format_registry,
45
 
    RepositoryAcquisitionPolicy,
46
 
    )
47
 
from .object_store import (
48
 
    get_object_store,
49
 
    )
50
 
 
51
 
from .push import (
52
 
    GitPushResult,
53
 
    )
54
 
from .transportgit import (
55
 
    OBJECTDIR,
56
 
    TransportObjectStore,
57
 
    )
58
 
 
59
 
 
60
 
class GitDirConfig(object):
61
 
 
62
 
    def get_default_stack_on(self):
63
 
        return None
64
 
 
65
 
    def set_default_stack_on(self, value):
66
 
        raise bzr_errors.BzrError("Cannot set configuration")
67
 
 
68
 
 
69
 
class GitControlDirFormat(ControlDirFormat):
70
 
 
71
 
    colocated_branches = True
72
 
    fixed_components = True
73
 
 
74
 
    def __eq__(self, other):
75
 
        return type(self) == type(other)
76
 
 
77
 
    def is_supported(self):
78
 
        return True
79
 
 
80
 
    def network_name(self):
81
 
        return b"git"
82
 
 
83
 
 
84
 
class UseExistingRepository(RepositoryAcquisitionPolicy):
85
 
    """A policy of reusing an existing repository"""
86
 
 
87
 
    def __init__(self, repository, stack_on=None, stack_on_pwd=None,
88
 
                 require_stacking=False):
89
 
        """Constructor.
90
 
 
91
 
        :param repository: The repository to use.
92
 
        :param stack_on: A location to stack on
93
 
        :param stack_on_pwd: If stack_on is relative, the location it is
94
 
            relative to.
95
 
        """
96
 
        super(UseExistingRepository, self).__init__(
97
 
                stack_on, stack_on_pwd, require_stacking)
98
 
        self._repository = repository
99
 
 
100
 
    def acquire_repository(self, make_working_trees=None, shared=False,
101
 
            possible_transports=None):
102
 
        """Implementation of RepositoryAcquisitionPolicy.acquire_repository
103
 
 
104
 
        Returns an existing repository to use.
105
 
        """
106
 
        return self._repository, False
107
 
 
108
 
 
109
 
class GitDir(ControlDir):
110
 
    """An adapter to the '.git' dir used by git."""
111
 
 
112
 
    def is_supported(self):
113
 
        return True
114
 
 
115
 
    def can_convert_format(self):
116
 
        return False
117
 
 
118
 
    def break_lock(self):
119
 
        # There are no global locks, so nothing to break.
120
 
        raise NotImplementedError(self.break_lock)
121
 
 
122
 
    def cloning_metadir(self, stacked=False):
123
 
        return format_registry.make_controldir("git")
124
 
 
125
 
    def checkout_metadir(self, stacked=False):
126
 
        return format_registry.make_controldir("git")
127
 
 
128
 
    def _get_selected_ref(self, branch, ref=None):
129
 
        if ref is not None and branch is not None:
130
 
            raise bzr_errors.BzrError("can't specify both ref and branch")
131
 
        if ref is not None:
132
 
            return ref
133
 
        if branch is not None:
134
 
            from .refs import branch_name_to_ref
135
 
            return branch_name_to_ref(branch)
136
 
        segment_parameters = getattr(
137
 
            self.user_transport, "get_segment_parameters", lambda: {})()
138
 
        ref = segment_parameters.get("ref")
139
 
        if ref is not None:
140
 
            return urlutils.unquote_to_bytes(ref)
141
 
        if branch is None and getattr(self, "_get_selected_branch", False):
142
 
            branch = self._get_selected_branch()
143
 
            if branch is not None:
144
 
                from .refs import branch_name_to_ref
145
 
                return branch_name_to_ref(branch)
146
 
        return b"HEAD"
147
 
 
148
 
    def get_config(self):
149
 
        return GitDirConfig()
150
 
 
151
 
    def _available_backup_name(self, base):
152
 
        return osutils.available_backup_name(base, self.root_transport.has)
153
 
 
154
 
    def sprout(self, url, revision_id=None, force_new_repo=False,
155
 
               recurse='down', possible_transports=None,
156
 
               accelerator_tree=None, hardlink=False, stacked=False,
157
 
               source_branch=None, create_tree_if_local=True):
158
 
        from ..repository import InterRepository
159
 
        from ..transport.local import LocalTransport
160
 
        from ..transport import get_transport
161
 
        target_transport = get_transport(url, possible_transports)
162
 
        target_transport.ensure_base()
163
 
        cloning_format = self.cloning_metadir()
164
 
        # Create/update the result branch
165
 
        try:
166
 
            result = ControlDir.open_from_transport(target_transport)
167
 
        except bzr_errors.NotBranchError:
168
 
            result = cloning_format.initialize_on_transport(target_transport)
169
 
        source_branch = self.open_branch()
170
 
        source_repository = self.find_repository()
171
 
        try:
172
 
            result_repo = result.find_repository()
173
 
        except bzr_errors.NoRepositoryPresent:
174
 
            result_repo = result.create_repository()
175
 
            target_is_empty = True
176
 
        else:
177
 
            target_is_empty = None # Unknown
178
 
        if stacked:
179
 
            raise _mod_branch.UnstackableBranchFormat(self._format, self.user_url)
180
 
        interrepo = InterRepository.get(source_repository, result_repo)
181
 
 
182
 
        if revision_id is not None:
183
 
            determine_wants = interrepo.get_determine_wants_revids(
184
 
                [revision_id], include_tags=True)
185
 
        else:
186
 
            determine_wants = interrepo.determine_wants_all
187
 
        interrepo.fetch_objects(determine_wants=determine_wants,
188
 
            mapping=source_branch.mapping)
189
 
        result_branch = source_branch.sprout(result,
190
 
            revision_id=revision_id, repository=result_repo)
191
 
        if (create_tree_if_local
192
 
            and isinstance(target_transport, LocalTransport)
193
 
            and (result_repo is None or result_repo.make_working_trees())):
194
 
            wt = result.create_workingtree(accelerator_tree=accelerator_tree,
195
 
                hardlink=hardlink, from_branch=result_branch)
196
 
        return result
197
 
 
198
 
    def clone_on_transport(self, transport, revision_id=None,
199
 
        force_new_repo=False, preserve_stacking=False, stacked_on=None,
200
 
        create_prefix=False, use_existing_dir=True, no_tree=False):
201
 
        """See ControlDir.clone_on_transport."""
202
 
        from ..repository import InterRepository
203
 
        from .mapping import default_mapping
204
 
        if stacked_on is not None:
205
 
            raise _mod_branch.UnstackableBranchFormat(self._format, self.user_url)
206
 
        if no_tree:
207
 
            format = BareLocalGitControlDirFormat()
208
 
        else:
209
 
            format = LocalGitControlDirFormat()
210
 
        (target_repo, target_controldir, stacking,
211
 
                repo_policy) = format.initialize_on_transport_ex(
212
 
                        transport, use_existing_dir=use_existing_dir,
213
 
                        create_prefix=create_prefix,
214
 
                        force_new_repo=force_new_repo)
215
 
        target_repo = target_controldir.find_repository()
216
 
        target_git_repo = target_repo._git
217
 
        source_repo = self.find_repository()
218
 
        source_git_repo = source_repo._git
219
 
        interrepo = InterRepository.get(source_repo, target_repo)
220
 
        if revision_id is not None:
221
 
            determine_wants = interrepo.get_determine_wants_revids([revision_id], include_tags=True)
222
 
        else:
223
 
            determine_wants = interrepo.determine_wants_all
224
 
        (pack_hint, _, refs) = interrepo.fetch_objects(determine_wants,
225
 
            mapping=default_mapping)
226
 
        for name, val in viewitems(refs):
227
 
            target_git_repo.refs[name] = val
228
 
        result_dir = self.__class__(transport, target_git_repo, format)
229
 
        if revision_id is not None:
230
 
            result_dir.open_branch().set_last_revision(revision_id)
231
 
        try:
232
 
            # Cheaper to check if the target is not local, than to try making
233
 
            # the tree and fail.
234
 
            result_dir.root_transport.local_abspath('.')
235
 
            if result_dir.open_repository().make_working_trees():
236
 
                self.open_workingtree().clone(result_dir, revision_id=revision_id)
237
 
        except (bzr_errors.NoWorkingTree, bzr_errors.NotLocalUrl):
238
 
            pass
239
 
 
240
 
        return result_dir
241
 
 
242
 
    def find_repository(self):
243
 
        """Find the repository that should be used.
244
 
 
245
 
        This does not require a branch as we use it to find the repo for
246
 
        new branches as well as to hook existing branches up to their
247
 
        repository.
248
 
        """
249
 
        return self._gitrepository_class(self._find_commondir())
250
 
 
251
 
    def get_refs_container(self):
252
 
        """Retrieve the refs container.
253
 
        """
254
 
        raise NotImplementedError(self.get_refs_container)
255
 
 
256
 
    def determine_repository_policy(self, force_new_repo=False, stack_on=None,
257
 
                                    stack_on_pwd=None, require_stacking=False):
258
 
        """Return an object representing a policy to use.
259
 
 
260
 
        This controls whether a new repository is created, and the format of
261
 
        that repository, or some existing shared repository used instead.
262
 
 
263
 
        If stack_on is supplied, will not seek a containing shared repo.
264
 
 
265
 
        :param force_new_repo: If True, require a new repository to be created.
266
 
        :param stack_on: If supplied, the location to stack on.  If not
267
 
            supplied, a default_stack_on location may be used.
268
 
        :param stack_on_pwd: If stack_on is relative, the location it is
269
 
            relative to.
270
 
        """
271
 
        return UseExistingRepository(self.find_repository())
272
 
 
273
 
    def get_branches(self):
274
 
        from .refs import ref_to_branch_name
275
 
        ret = {}
276
 
        for ref in self.get_refs_container().keys():
277
 
            try:
278
 
                branch_name = ref_to_branch_name(ref)
279
 
            except ValueError:
280
 
                continue
281
 
            except UnicodeDecodeError:
282
 
                trace.warning("Ignoring branch %r with unicode error ref", ref)
283
 
                continue
284
 
            ret[branch_name] = self.open_branch(ref=ref)
285
 
        return ret
286
 
 
287
 
    def list_branches(self):
288
 
        return list(self.get_branches().values())
289
 
 
290
 
    def push_branch(self, source, revision_id=None, overwrite=False,
291
 
                    remember=False, create_prefix=False, lossy=False,
292
 
                    name=None):
293
 
        """Push the source branch into this ControlDir."""
294
 
        push_result = GitPushResult()
295
 
        push_result.workingtree_updated = None
296
 
        push_result.master_branch = None
297
 
        push_result.source_branch = source
298
 
        push_result.stacked_on = None
299
 
        repo = self.find_repository()
300
 
        refname = self._get_selected_ref(name)
301
 
        from .branch import GitBranch
302
 
        if isinstance(source, GitBranch) and lossy:
303
 
            raise bzr_errors.LossyPushToSameVCS(source.controldir, self)
304
 
        target = self.open_branch(name, nascent_ok=True)
305
 
        push_result.branch_push_result = source.push(
306
 
                target, overwrite=overwrite, stop_revision=revision_id,
307
 
                lossy=lossy)
308
 
        push_result.new_revid = push_result.branch_push_result.new_revid
309
 
        push_result.old_revid = push_result.branch_push_result.old_revid
310
 
        push_result.target_branch = self.open_branch(name)
311
 
        if source.get_push_location() is None or remember:
312
 
            source.set_push_location(push_result.target_branch.base)
313
 
        return push_result
314
 
 
315
 
 
316
 
class LocalGitControlDirFormat(GitControlDirFormat):
317
 
    """The .git directory control format."""
318
 
 
319
 
    bare = False
320
 
 
321
 
    @classmethod
322
 
    def _known_formats(self):
323
 
        return set([LocalGitControlDirFormat()])
324
 
 
325
 
    @property
326
 
    def repository_format(self):
327
 
        from .repository import GitRepositoryFormat
328
 
        return GitRepositoryFormat()
329
 
 
330
 
    @property
331
 
    def workingtree_format(self):
332
 
        from .workingtree import GitWorkingTreeFormat
333
 
        return GitWorkingTreeFormat()
334
 
 
335
 
    def get_branch_format(self):
336
 
        from .branch import LocalGitBranchFormat
337
 
        return LocalGitBranchFormat()
338
 
 
339
 
    def open(self, transport, _found=None):
340
 
        """Open this directory.
341
 
 
342
 
        """
343
 
        from .transportgit import TransportRepo
344
 
        def _open(transport):
345
 
            return TransportRepo(transport, self.bare,
346
 
                refs_text=getattr(self, "_refs_text", None))
347
 
        def redirected(transport, e, redirection_notice):
348
 
            trace.note(redirection_notice)
349
 
            return transport._redirected_to(e.source, e.target)
350
 
        gitrepo = do_catching_redirections(_open, transport, redirected)
351
 
        if not gitrepo._controltransport.has('HEAD'):
352
 
            raise bzr_errors.NotBranchError(path=transport.base)
353
 
        return LocalGitDir(transport, gitrepo, self)
354
 
 
355
 
    def get_format_description(self):
356
 
        return "Local Git Repository"
357
 
 
358
 
    def initialize_on_transport(self, transport):
359
 
        from .transportgit import TransportRepo
360
 
        repo = TransportRepo.init(transport, bare=self.bare)
361
 
        return self.open(transport)
362
 
 
363
 
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
364
 
        create_prefix=False, force_new_repo=False, stacked_on=None,
365
 
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
366
 
        shared_repo=False, vfs_only=False):
367
 
        def make_directory(transport):
368
 
            transport.mkdir('.')
369
 
            return transport
370
 
        def redirected(transport, e, redirection_notice):
371
 
            trace.note(redirection_notice)
372
 
            return transport._redirected_to(e.source, e.target)
373
 
        try:
374
 
            transport = do_catching_redirections(make_directory, transport,
375
 
                redirected)
376
 
        except bzr_errors.FileExists:
377
 
            if not use_existing_dir:
378
 
                raise
379
 
        except bzr_errors.NoSuchFile:
380
 
            if not create_prefix:
381
 
                raise
382
 
            transport.create_prefix()
383
 
        controldir = self.initialize_on_transport(transport)
384
 
        if repo_format_name:
385
 
            result_repo = controldir.find_repository()
386
 
            repository_policy = UseExistingRepository(result_repo)
387
 
            result_repo.lock_write()
388
 
        else:
389
 
            result_repo = None
390
 
            repository_policy = None
391
 
        return (result_repo, controldir, False,
392
 
                repository_policy)
393
 
 
394
 
    def is_supported(self):
395
 
        return True
396
 
 
397
 
    def supports_transport(self, transport):
398
 
        try:
399
 
            external_url = transport.external_url()
400
 
        except bzr_errors.InProcessTransport:
401
 
            raise bzr_errors.NotBranchError(path=transport.base)
402
 
        return external_url.startswith("file:")
403
 
 
404
 
 
405
 
class BareLocalGitControlDirFormat(LocalGitControlDirFormat):
406
 
 
407
 
    bare = True
408
 
    supports_workingtrees = False
409
 
 
410
 
    def get_format_description(self):
411
 
        return "Local Git Repository (bare)"
412
 
 
413
 
 
414
 
class LocalGitDir(GitDir):
415
 
    """An adapter to the '.git' dir used by git."""
416
 
 
417
 
    def _get_gitrepository_class(self):
418
 
        from .repository import LocalGitRepository
419
 
        return LocalGitRepository
420
 
 
421
 
    def __repr__(self):
422
 
        return "<%s at %r>" % (
423
 
            self.__class__.__name__, self.root_transport.base)
424
 
 
425
 
    _gitrepository_class = property(_get_gitrepository_class)
426
 
 
427
 
    @property
428
 
    def user_transport(self):
429
 
        return self.root_transport
430
 
 
431
 
    @property
432
 
    def control_transport(self):
433
 
        return self._git._controltransport
434
 
 
435
 
    def __init__(self, transport, gitrepo, format):
436
 
        self._format = format
437
 
        self.root_transport = transport
438
 
        self._mode_check_done = False
439
 
        self._git = gitrepo
440
 
        if gitrepo.bare:
441
 
            self.transport = transport
442
 
        else:
443
 
            self.transport = transport.clone('.git')
444
 
        self._mode_check_done = None
445
 
 
446
 
    def is_control_filename(self, filename):
447
 
        return (filename == '.git' or
448
 
                filename.startswith('.git/') or
449
 
                filename.startswith('.git\\'))
450
 
 
451
 
    def _get_symref(self, ref):
452
 
        from dulwich.repo import SYMREF
453
 
        ref_chain, unused_sha = self._git.refs.follow(ref)
454
 
        if len(ref_chain) == 1:
455
 
            return None
456
 
        return ref_chain[1]
457
 
 
458
 
    def set_branch_reference(self, target_branch, name=None):
459
 
        ref = self._get_selected_ref(name)
460
 
        if self.control_transport.base == target_branch.controldir.control_transport.base:
461
 
            if ref == target_branch.ref:
462
 
                raise BranchReferenceLoop(target_branch)
463
 
            self._git.refs.set_symbolic_ref(ref, target_branch.ref)
464
 
        else:
465
 
            try:
466
 
                target_path = target_branch.controldir.control_transport.local_abspath('.')
467
 
            except bzr_errors.NotLocalUrl:
468
 
                raise bzr_errors.IncompatibleFormat(target_branch._format, self._format)
469
 
            # TODO(jelmer): Do some consistency checking across branches..
470
 
            self.control_transport.put_bytes('commondir', target_path.encode('utf-8'))
471
 
            # TODO(jelmer): Urgh, avoid mucking about with internals.
472
 
            self._git._commontransport = target_branch.repository._git._commontransport.clone()
473
 
            self._git.object_store = TransportObjectStore(self._git._commontransport.clone(OBJECTDIR))
474
 
            self._git.refs.transport = self._git._commontransport
475
 
            target_ref_chain, unused_sha = target_branch.controldir._git.refs.follow(target_branch.ref)
476
 
            for target_ref in target_ref_chain:
477
 
                if target_ref == b'HEAD':
478
 
                    continue
479
 
                break
480
 
            else:
481
 
                # Can't create a reference to something that is not a in a repository.
482
 
                raise bzr_errors.IncompatibleFormat(self.set_branch_reference, self)
483
 
            self._git.refs.set_symbolic_ref(ref, target_ref)
484
 
 
485
 
    def get_branch_reference(self, name=None):
486
 
        ref = self._get_selected_ref(name)
487
 
        target_ref = self._get_symref(ref)
488
 
        if target_ref is not None:
489
 
            from .refs import ref_to_branch_name
490
 
            try:
491
 
                branch_name = ref_to_branch_name(target_ref)
492
 
            except ValueError:
493
 
                params = {'ref': urlutils.quote(target_ref.decode('utf-8'), '')}
494
 
            else:
495
 
                if branch_name != '':
496
 
                    params = {'branch': urlutils.quote(branch_name, '')}
497
 
                else:
498
 
                    params = {}
499
 
            try:
500
 
                commondir = self.control_transport.get_bytes('commondir')
501
 
            except bzr_errors.NoSuchFile:
502
 
                base_url = self.user_url.rstrip('/')
503
 
            else:
504
 
                base_url = urlutils.local_path_to_url(commondir.decode(osutils._fs_enc)).rstrip('/.git/')+'/'
505
 
            if not PY3:
506
 
                params = {k: v.encode('utf-8') for (k, v) in viewitems(params)}
507
 
            return urlutils.join_segment_parameters(base_url, params)
508
 
        return None
509
 
 
510
 
    def find_branch_format(self, name=None):
511
 
        from .branch import (
512
 
            LocalGitBranchFormat,
513
 
            )
514
 
        ref = self._get_selected_ref(name)
515
 
        return LocalGitBranchFormat()
516
 
 
517
 
    def get_branch_transport(self, branch_format, name=None):
518
 
        if branch_format is None:
519
 
            return self.transport
520
 
        if isinstance(branch_format, LocalGitControlDirFormat):
521
 
            return self.transport
522
 
        raise bzr_errors.IncompatibleFormat(branch_format, self._format)
523
 
 
524
 
    def get_repository_transport(self, format):
525
 
        if format is None:
526
 
            return self.transport
527
 
        if isinstance(format, LocalGitControlDirFormat):
528
 
            return self.transport
529
 
        raise bzr_errors.IncompatibleFormat(format, self._format)
530
 
 
531
 
    def get_workingtree_transport(self, format):
532
 
        if format is None:
533
 
            return self.transport
534
 
        if isinstance(format, LocalGitControlDirFormat):
535
 
            return self.transport
536
 
        raise bzr_errors.IncompatibleFormat(format, self._format)
537
 
 
538
 
    def open_branch(self, name=None, unsupported=False, ignore_fallbacks=None,
539
 
            ref=None, possible_transports=None, nascent_ok=False):
540
 
        """'create' a branch for this dir."""
541
 
        repo = self.find_repository()
542
 
        from .branch import LocalGitBranch
543
 
        ref = self._get_selected_ref(name, ref)
544
 
        if not nascent_ok and ref not in self._git.refs:
545
 
            raise bzr_errors.NotBranchError(self.root_transport.base,
546
 
                    controldir=self)
547
 
        ref_chain, unused_sha = self._git.refs.follow(ref)
548
 
        if ref_chain[-1] == b'HEAD':
549
 
            controldir = self
550
 
        else:
551
 
            controldir = self._find_commondir()
552
 
        return LocalGitBranch(controldir, repo, ref_chain[-1])
553
 
 
554
 
    def destroy_branch(self, name=None):
555
 
        refname = self._get_selected_ref(name)
556
 
        if refname == b'HEAD':
557
 
            # HEAD can't be removed
558
 
            raise bzr_errors.UnsupportedOperation(
559
 
                self.destroy_branch, self)
560
 
        try:
561
 
            del self._git.refs[refname]
562
 
        except KeyError:
563
 
            raise bzr_errors.NotBranchError(self.root_transport.base,
564
 
                    controldir=self)
565
 
 
566
 
    def destroy_repository(self):
567
 
        raise bzr_errors.UnsupportedOperation(self.destroy_repository, self)
568
 
 
569
 
    def destroy_workingtree(self):
570
 
        raise bzr_errors.UnsupportedOperation(self.destroy_workingtree, self)
571
 
 
572
 
    def destroy_workingtree_metadata(self):
573
 
        raise bzr_errors.UnsupportedOperation(self.destroy_workingtree_metadata, self)
574
 
 
575
 
    def needs_format_conversion(self, format=None):
576
 
        return not isinstance(self._format, format.__class__)
577
 
 
578
 
    def open_repository(self):
579
 
        """'open' a repository for this dir."""
580
 
        if self.control_transport.has('commondir'):
581
 
            raise bzr_errors.NoRepositoryPresent(self)
582
 
        return self._gitrepository_class(self)
583
 
 
584
 
    def has_workingtree(self):
585
 
        return not self._git.bare
586
 
 
587
 
    def open_workingtree(self, recommend_upgrade=True, unsupported=False):
588
 
        if not self._git.bare:
589
 
            from dulwich.errors import NoIndexPresent
590
 
            repo = self.find_repository()
591
 
            from .workingtree import GitWorkingTree
592
 
            branch = self.open_branch(ref=b'HEAD', nascent_ok=True)
593
 
            return GitWorkingTree(self, repo, branch)
594
 
        loc = urlutils.unescape_for_display(self.root_transport.base, 'ascii')
595
 
        raise bzr_errors.NoWorkingTree(loc)
596
 
 
597
 
    def create_repository(self, shared=False):
598
 
        from .repository import GitRepositoryFormat
599
 
        if shared:
600
 
            raise bzr_errors.IncompatibleFormat(GitRepositoryFormat(), self._format)
601
 
        return self.find_repository()
602
 
 
603
 
    def create_branch(self, name=None, repository=None,
604
 
                      append_revisions_only=None, ref=None):
605
 
        refname = self._get_selected_ref(name, ref)
606
 
        if refname != b'HEAD' and refname in self._git.refs:
607
 
            raise bzr_errors.AlreadyBranchError(self.user_url)
608
 
        repo = self.open_repository()
609
 
        if refname in self._git.refs:
610
 
            ref_chain, unused_sha = self._git.refs.follow(self._get_selected_ref(None))
611
 
            if ref_chain[0] == b'HEAD':
612
 
                refname = ref_chain[1]
613
 
        from .branch import LocalGitBranch
614
 
        branch = LocalGitBranch(self, repo, refname)
615
 
        if append_revisions_only:
616
 
            branch.set_append_revisions_only(append_revisions_only)
617
 
        return branch
618
 
 
619
 
    def backup_bzrdir(self):
620
 
        if not self._git.bare:
621
 
            self.root_transport.copy_tree(".git", ".git.backup")
622
 
            return (self.root_transport.abspath(".git"),
623
 
                    self.root_transport.abspath(".git.backup"))
624
 
        else:
625
 
            basename = urlutils.basename(self.root_transport.base)
626
 
            parent = self.root_transport.clone('..')
627
 
            parent.copy_tree(basename, basename + ".backup")
628
 
 
629
 
    def create_workingtree(self, revision_id=None, from_branch=None,
630
 
        accelerator_tree=None, hardlink=False):
631
 
        if self._git.bare:
632
 
            raise bzr_errors.UnsupportedOperation(self.create_workingtree, self)
633
 
        if from_branch is None:
634
 
            from_branch = self.open_branch(nascent_ok=True)
635
 
        if revision_id is None:
636
 
            revision_id = from_branch.last_revision()
637
 
        repo = self.find_repository()
638
 
        from .workingtree import GitWorkingTree
639
 
        wt = GitWorkingTree(self, repo, from_branch)
640
 
        wt.set_last_revision(revision_id)
641
 
        wt._build_checkout_with_index()
642
 
        return wt
643
 
 
644
 
    def _find_or_create_repository(self, force_new_repo=None):
645
 
        return self.create_repository(shared=False)
646
 
 
647
 
    def _find_creation_modes(self):
648
 
        """Determine the appropriate modes for files and directories.
649
 
 
650
 
        They're always set to be consistent with the base directory,
651
 
        assuming that this transport allows setting modes.
652
 
        """
653
 
        # TODO: Do we need or want an option (maybe a config setting) to turn
654
 
        # this off or override it for particular locations? -- mbp 20080512
655
 
        if self._mode_check_done:
656
 
            return
657
 
        self._mode_check_done = True
658
 
        try:
659
 
            st = self.transport.stat('.')
660
 
        except bzr_errors.TransportNotPossible:
661
 
            self._dir_mode = None
662
 
            self._file_mode = None
663
 
        else:
664
 
            # Check the directory mode, but also make sure the created
665
 
            # directories and files are read-write for this user. This is
666
 
            # mostly a workaround for filesystems which lie about being able to
667
 
            # write to a directory (cygwin & win32)
668
 
            if (st.st_mode & 0o7777 == 0o0000):
669
 
                # FTP allows stat but does not return dir/file modes
670
 
                self._dir_mode = None
671
 
                self._file_mode = None
672
 
            else:
673
 
                self._dir_mode = (st.st_mode & 0o7777) | 0o0700
674
 
                # Remove the sticky and execute bits for files
675
 
                self._file_mode = self._dir_mode & ~0o7111
676
 
 
677
 
    def _get_file_mode(self):
678
 
        """Return Unix mode for newly created files, or None.
679
 
        """
680
 
        if not self._mode_check_done:
681
 
            self._find_creation_modes()
682
 
        return self._file_mode
683
 
 
684
 
    def _get_dir_mode(self):
685
 
        """Return Unix mode for newly created directories, or None.
686
 
        """
687
 
        if not self._mode_check_done:
688
 
            self._find_creation_modes()
689
 
        return self._dir_mode
690
 
 
691
 
    def get_refs_container(self):
692
 
        return self._git.refs
693
 
 
694
 
    def get_peeled(self, ref):
695
 
        return self._git.get_peeled(ref)
696
 
 
697
 
    def _find_commondir(self):
698
 
        try:
699
 
            commondir = self.control_transport.get_bytes('commondir')
700
 
        except bzr_errors.NoSuchFile:
701
 
            return self
702
 
        else:
703
 
            commondir = commondir.rstrip(b'/.git/').decode(osutils._fs_enc)
704
 
            return ControlDir.open_from_transport(get_transport_from_path(commondir))