/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

TranslateĀ moreĀ strings.

Show diffs side-by-side

added added

removed removed

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